version 1.38, 2005/01/01 05:43:05
|
version 1.38.4.9, 2010/06/15 16:22:51
|
Line 8
|
Line 8
|
* Darko Prenosil <[email protected]> |
* Darko Prenosil <[email protected]> |
* Shridhar Daithankar <[email protected]> |
* Shridhar Daithankar <[email protected]> |
* |
* |
* Copyright (c) 2001-2005, PostgreSQL Global Development Group |
* Copyright (c) 2001-2006, PostgreSQL Global Development Group |
* ALL RIGHTS RESERVED; |
* ALL RIGHTS RESERVED; |
* |
* |
* Permission to use, copy, modify, and distribute this software and its |
* Permission to use, copy, modify, and distribute this software and its |
Line 34
|
Line 34
|
#include "libpq-fe.h" |
#include "libpq-fe.h" |
#include "fmgr.h" |
#include "fmgr.h" |
#include "funcapi.h" |
#include "funcapi.h" |
|
#include "miscadmin.h" |
#include "access/tupdesc.h" |
#include "access/tupdesc.h" |
#include "access/heapam.h" |
#include "access/heapam.h" |
#include "catalog/catname.h" |
#include "catalog/catname.h" |
Line 47
|
Line 48
|
#include "nodes/execnodes.h" |
#include "nodes/execnodes.h" |
#include "nodes/pg_list.h" |
#include "nodes/pg_list.h" |
#include "parser/parse_type.h" |
#include "parser/parse_type.h" |
|
#include "parser/scansup.h" |
#include "tcop/tcopprot.h" |
#include "tcop/tcopprot.h" |
#include "utils/builtins.h" |
#include "utils/builtins.h" |
#include "utils/fmgroids.h" |
#include "utils/fmgroids.h" |
|
#include "utils/acl.h" |
#include "utils/array.h" |
#include "utils/array.h" |
#include "utils/lsyscache.h" |
#include "utils/lsyscache.h" |
#include "utils/syscache.h" |
#include "utils/syscache.h" |
Line 73 static remoteConn *getConnectionByName(c
|
Line 76 static remoteConn *getConnectionByName(c
|
static HTAB *createConnHash(void); |
static HTAB *createConnHash(void); |
static void createNewConnection(const char *name, remoteConn * con); |
static void createNewConnection(const char *name, remoteConn * con); |
static void deleteConnection(const char *name); |
static void deleteConnection(const char *name); |
static char **get_pkey_attnames(Oid relid, int16 *numatts); |
static char **get_pkey_attnames(Relation rel, int16 *numatts); |
static char *get_sql_insert(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals); |
static char *get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals); |
static char *get_sql_delete(Oid relid, int16 *pkattnums, int16 pknumatts, char **tgt_pkattvals); |
static char *get_sql_delete(Relation rel, int *pkattnums, int pknumatts, char **tgt_pkattvals); |
static char *get_sql_update(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals); |
static char *get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals); |
static char *quote_literal_cstr(char *rawstr); |
static char *quote_literal_cstr(char *rawstr); |
static char *quote_ident_cstr(char *rawstr); |
static char *quote_ident_cstr(char *rawstr); |
static int16 get_attnum_pk_pos(int16 *pkattnums, int16 pknumatts, int16 key); |
static int get_attnum_pk_pos(int *pkattnums, int pknumatts, int key); |
static HeapTuple get_tuple_of_interest(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals); |
static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals); |
static Oid get_relid_from_relname(text *relname_text); |
static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode); |
static char *generate_relation_name(Oid relid); |
static char *generate_relation_name(Relation rel); |
|
static char *connstr_strip_password(const char *connstr); |
|
static void dblink_security_check(PGconn *conn, remoteConn *rcon, const char *connstr); |
|
static void validate_pkattnums(Relation rel, |
|
int16 *pkattnums_arg, int32 pknumatts_arg, |
|
int **pkattnums, int *pknumatts); |
|
|
/* Global */ |
/* Global */ |
List *res_id = NIL; |
List *res_id = NIL; |
Line 166 typedef struct remoteConnHashEnt
|
Line 174 typedef struct remoteConnHashEnt
|
else \ |
else \ |
{ \ |
{ \ |
connstr = conname_or_str; \ |
connstr = conname_or_str; \ |
|
dblink_security_check(conn, rcon, connstr); \ |
conn = PQconnectdb(connstr); \ |
conn = PQconnectdb(connstr); \ |
if (PQstatus(conn) == CONNECTION_BAD) \ |
if (PQstatus(conn) == CONNECTION_BAD) \ |
{ \ |
{ \ |
Line 207 dblink_connect(PG_FUNCTION_ARGS)
|
Line 216 dblink_connect(PG_FUNCTION_ARGS)
|
|
|
if (connname) |
if (connname) |
rcon = (remoteConn *) palloc(sizeof(remoteConn)); |
rcon = (remoteConn *) palloc(sizeof(remoteConn)); |
|
|
|
/* check password used if not superuser */ |
|
dblink_security_check(conn, rcon, connstr); |
conn = PQconnectdb(connstr); |
conn = PQconnectdb(connstr); |
|
|
MemoryContextSwitchTo(oldcontext); |
MemoryContextSwitchTo(oldcontext); |
Line 547 dblink_fetch(PG_FUNCTION_ARGS)
|
Line 559 dblink_fetch(PG_FUNCTION_ARGS)
|
/* got results, keep track of them */ |
/* got results, keep track of them */ |
funcctx->user_fctx = res; |
funcctx->user_fctx = res; |
|
|
/* fast track when no results */ |
|
if (funcctx->max_calls < 1) |
|
{ |
|
if (res) |
|
PQclear(res); |
|
SRF_RETURN_DONE(funcctx); |
|
} |
|
|
|
/* check typtype to see if we have a predetermined return type */ |
/* check typtype to see if we have a predetermined return type */ |
functypeid = get_func_rettype(funcid); |
functypeid = get_func_rettype(funcid); |
functyptype = get_typtype(functypeid); |
functyptype = get_typtype(functypeid); |
Line 577 dblink_fetch(PG_FUNCTION_ARGS)
|
Line 581 dblink_fetch(PG_FUNCTION_ARGS)
|
/* shouldn't happen */ |
/* shouldn't happen */ |
elog(ERROR, "return type must be a row type"); |
elog(ERROR, "return type must be a row type"); |
|
|
|
/* check result and tuple descriptor have the same number of columns */ |
|
if (PQnfields(res) != tupdesc->natts) |
|
ereport(ERROR, |
|
(errcode(ERRCODE_DATATYPE_MISMATCH), |
|
errmsg("remote query result rowtype does not match " |
|
"the specified FROM clause rowtype"))); |
|
|
|
/* fast track when no results */ |
|
if (funcctx->max_calls < 1) |
|
{ |
|
if (res) |
|
PQclear(res); |
|
SRF_RETURN_DONE(funcctx); |
|
} |
|
|
/* store needed metadata for subsequent calls */ |
/* store needed metadata for subsequent calls */ |
attinmeta = TupleDescGetAttInMetadata(tupdesc); |
attinmeta = TupleDescGetAttInMetadata(tupdesc); |
funcctx->attinmeta = attinmeta; |
funcctx->attinmeta = attinmeta; |
Line 749 dblink_record(PG_FUNCTION_ARGS)
|
Line 768 dblink_record(PG_FUNCTION_ARGS)
|
if (freeconn) |
if (freeconn) |
PQfinish(conn); |
PQfinish(conn); |
|
|
/* fast track when no results */ |
|
if (funcctx->max_calls < 1) |
|
{ |
|
if (res) |
|
PQclear(res); |
|
SRF_RETURN_DONE(funcctx); |
|
} |
|
|
|
/* check typtype to see if we have a predetermined return type */ |
/* check typtype to see if we have a predetermined return type */ |
functypeid = get_func_rettype(funcid); |
functypeid = get_func_rettype(funcid); |
functyptype = get_typtype(functypeid); |
functyptype = get_typtype(functypeid); |
Line 782 dblink_record(PG_FUNCTION_ARGS)
|
Line 793 dblink_record(PG_FUNCTION_ARGS)
|
elog(ERROR, "return type must be a row type"); |
elog(ERROR, "return type must be a row type"); |
} |
} |
|
|
|
/* check result and tuple descriptor have the same number of columns */ |
|
if (PQnfields(res) != tupdesc->natts) |
|
ereport(ERROR, |
|
(errcode(ERRCODE_DATATYPE_MISMATCH), |
|
errmsg("remote query result rowtype does not match " |
|
"the specified FROM clause rowtype"))); |
|
|
|
/* fast track when no results */ |
|
if (funcctx->max_calls < 1) |
|
{ |
|
if (res) |
|
PQclear(res); |
|
SRF_RETURN_DONE(funcctx); |
|
} |
|
|
/* store needed metadata for subsequent calls */ |
/* store needed metadata for subsequent calls */ |
attinmeta = TupleDescGetAttInMetadata(tupdesc); |
attinmeta = TupleDescGetAttInMetadata(tupdesc); |
funcctx->attinmeta = attinmeta; |
funcctx->attinmeta = attinmeta; |
Line 961 Datum
|
Line 987 Datum
|
dblink_get_pkey(PG_FUNCTION_ARGS) |
dblink_get_pkey(PG_FUNCTION_ARGS) |
{ |
{ |
int16 numatts; |
int16 numatts; |
Oid relid; |
|
char **results; |
char **results; |
FuncCallContext *funcctx; |
FuncCallContext *funcctx; |
int32 call_cntr; |
int32 call_cntr; |
Line 972 dblink_get_pkey(PG_FUNCTION_ARGS)
|
Line 997 dblink_get_pkey(PG_FUNCTION_ARGS)
|
/* stuff done only on the first call of the function */ |
/* stuff done only on the first call of the function */ |
if (SRF_IS_FIRSTCALL()) |
if (SRF_IS_FIRSTCALL()) |
{ |
{ |
TupleDesc tupdesc = NULL; |
Relation rel; |
|
TupleDesc tupdesc; |
|
|
/* create a function context for cross-call persistence */ |
/* create a function context for cross-call persistence */ |
funcctx = SRF_FIRSTCALL_INIT(); |
funcctx = SRF_FIRSTCALL_INIT(); |
Line 983 dblink_get_pkey(PG_FUNCTION_ARGS)
|
Line 1009 dblink_get_pkey(PG_FUNCTION_ARGS)
|
*/ |
*/ |
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); |
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); |
|
|
/* convert relname to rel Oid */ |
/* open target relation */ |
relid = get_relid_from_relname(PG_GETARG_TEXT_P(0)); |
rel = get_rel_from_relname(PG_GETARG_TEXT_P(0), AccessShareLock, ACL_SELECT); |
if (!OidIsValid(relid)) |
|
ereport(ERROR, |
/* get the array of attnums */ |
(errcode(ERRCODE_UNDEFINED_TABLE), |
results = get_pkey_attnames(rel, &numatts); |
errmsg("relation \"%s\" does not exist", |
|
GET_STR(PG_GETARG_TEXT_P(0))))); |
relation_close(rel, AccessShareLock); |
|
|
/* |
/* |
* need a tuple descriptor representing one INT and one TEXT |
* need a tuple descriptor representing one INT and one TEXT |
Line 1008 dblink_get_pkey(PG_FUNCTION_ARGS)
|
Line 1034 dblink_get_pkey(PG_FUNCTION_ARGS)
|
attinmeta = TupleDescGetAttInMetadata(tupdesc); |
attinmeta = TupleDescGetAttInMetadata(tupdesc); |
funcctx->attinmeta = attinmeta; |
funcctx->attinmeta = attinmeta; |
|
|
/* get an array of attnums */ |
|
results = get_pkey_attnames(relid, &numatts); |
|
|
|
if ((results != NULL) && (numatts > 0)) |
if ((results != NULL) && (numatts > 0)) |
{ |
{ |
funcctx->max_calls = numatts; |
funcctx->max_calls = numatts; |
Line 1066 dblink_get_pkey(PG_FUNCTION_ARGS)
|
Line 1089 dblink_get_pkey(PG_FUNCTION_ARGS)
|
} |
} |
|
|
|
|
#ifndef SHRT_MAX |
|
#define SHRT_MAX (0x7FFF) |
|
#endif |
|
/* |
/* |
* dblink_build_sql_insert |
* dblink_build_sql_insert |
* |
* |
Line 1092 PG_FUNCTION_INFO_V1(dblink_build_sql_ins
|
Line 1112 PG_FUNCTION_INFO_V1(dblink_build_sql_ins
|
Datum |
Datum |
dblink_build_sql_insert(PG_FUNCTION_ARGS) |
dblink_build_sql_insert(PG_FUNCTION_ARGS) |
{ |
{ |
Oid relid; |
Relation rel; |
text *relname_text; |
text *relname_text; |
int16 *pkattnums; |
int16 *pkattnums_arg; |
int pknumatts_tmp; |
int32 pknumatts_arg; |
int16 pknumatts = 0; |
int *pkattnums; |
|
int pknumatts; |
char **src_pkattvals; |
char **src_pkattvals; |
char **tgt_pkattvals; |
char **tgt_pkattvals; |
ArrayType *src_pkattvals_arry; |
ArrayType *src_pkattvals_arry; |
Line 1117 dblink_build_sql_insert(PG_FUNCTION_ARGS
|
Line 1138 dblink_build_sql_insert(PG_FUNCTION_ARGS
|
relname_text = PG_GETARG_TEXT_P(0); |
relname_text = PG_GETARG_TEXT_P(0); |
|
|
/* |
/* |
* Convert relname to rel OID. |
* Open target relation. |
*/ |
*/ |
relid = get_relid_from_relname(relname_text); |
rel = get_rel_from_relname(relname_text, AccessShareLock, ACL_SELECT); |
if (!OidIsValid(relid)) |
|
ereport(ERROR, |
|
(errcode(ERRCODE_UNDEFINED_TABLE), |
|
errmsg("relation \"%s\" does not exist", |
|
GET_STR(relname_text)))); |
|
|
|
pkattnums = (int16 *) PG_GETARG_POINTER(1); |
|
pknumatts_tmp = PG_GETARG_INT32(2); |
|
if (pknumatts_tmp <= SHRT_MAX) |
|
pknumatts = pknumatts_tmp; |
|
else |
|
ereport(ERROR, |
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
|
errmsg("input for number of primary key " \ |
|
"attributes too large"))); |
|
|
|
/* |
/* |
* There should be at least one key attribute |
* Process pkattnums argument. |
*/ |
*/ |
if (pknumatts == 0) |
pkattnums_arg = (int16 *) PG_GETARG_POINTER(1); |
ereport(ERROR, |
pknumatts_arg = PG_GETARG_INT32(2); |
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
validate_pkattnums(rel, pkattnums_arg, pknumatts_arg, |
errmsg("number of key attributes must be > 0"))); |
&pkattnums, &pknumatts); |
|
|
src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); |
src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); |
tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); |
tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); |
Line 1216 dblink_build_sql_insert(PG_FUNCTION_ARGS
|
Line 1222 dblink_build_sql_insert(PG_FUNCTION_ARGS
|
/* |
/* |
* Prep work is finally done. Go get the SQL string. |
* Prep work is finally done. Go get the SQL string. |
*/ |
*/ |
sql = get_sql_insert(relid, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals); |
sql = get_sql_insert(rel, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals); |
|
|
|
/* |
|
* Now we can close the relation. |
|
*/ |
|
relation_close(rel, AccessShareLock); |
|
|
/* |
/* |
* And send it |
* And send it |
Line 1244 PG_FUNCTION_INFO_V1(dblink_build_sql_del
|
Line 1255 PG_FUNCTION_INFO_V1(dblink_build_sql_del
|
Datum |
Datum |
dblink_build_sql_delete(PG_FUNCTION_ARGS) |
dblink_build_sql_delete(PG_FUNCTION_ARGS) |
{ |
{ |
Oid relid; |
Relation rel; |
text *relname_text; |
text *relname_text; |
int16 *pkattnums; |
int16 *pkattnums_arg; |
int pknumatts_tmp; |
int32 pknumatts_arg; |
int16 pknumatts = 0; |
int *pkattnums; |
|
int pknumatts; |
char **tgt_pkattvals; |
char **tgt_pkattvals; |
ArrayType *tgt_pkattvals_arry; |
ArrayType *tgt_pkattvals_arry; |
int tgt_ndim; |
int tgt_ndim; |
Line 1264 dblink_build_sql_delete(PG_FUNCTION_ARGS
|
Line 1276 dblink_build_sql_delete(PG_FUNCTION_ARGS
|
relname_text = PG_GETARG_TEXT_P(0); |
relname_text = PG_GETARG_TEXT_P(0); |
|
|
/* |
/* |
* Convert relname to rel OID. |
* Open target relation. |
*/ |
*/ |
relid = get_relid_from_relname(relname_text); |
rel = get_rel_from_relname(relname_text, AccessShareLock, ACL_SELECT); |
if (!OidIsValid(relid)) |
|
ereport(ERROR, |
|
(errcode(ERRCODE_UNDEFINED_TABLE), |
|
errmsg("relation \"%s\" does not exist", |
|
GET_STR(relname_text)))); |
|
|
|
pkattnums = (int16 *) PG_GETARG_POINTER(1); |
|
pknumatts_tmp = PG_GETARG_INT32(2); |
|
if (pknumatts_tmp <= SHRT_MAX) |
|
pknumatts = pknumatts_tmp; |
|
else |
|
ereport(ERROR, |
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
|
errmsg("input for number of primary key " \ |
|
"attributes too large"))); |
|
|
|
/* |
/* |
* There should be at least one key attribute |
* Process pkattnums argument. |
*/ |
*/ |
if (pknumatts == 0) |
pkattnums_arg = (int16 *) PG_GETARG_POINTER(1); |
ereport(ERROR, |
pknumatts_arg = PG_GETARG_INT32(2); |
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
validate_pkattnums(rel, pkattnums_arg, pknumatts_arg, |
errmsg("number of key attributes must be > 0"))); |
&pkattnums, &pknumatts); |
|
|
tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); |
tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); |
|
|
Line 1329 dblink_build_sql_delete(PG_FUNCTION_ARGS
|
Line 1326 dblink_build_sql_delete(PG_FUNCTION_ARGS
|
/* |
/* |
* Prep work is finally done. Go get the SQL string. |
* Prep work is finally done. Go get the SQL string. |
*/ |
*/ |
sql = get_sql_delete(relid, pkattnums, pknumatts, tgt_pkattvals); |
sql = get_sql_delete(rel, pkattnums, pknumatts, tgt_pkattvals); |
|
|
|
/* |
|
* Now we can close the relation. |
|
*/ |
|
relation_close(rel, AccessShareLock); |
|
|
/* |
/* |
* And send it |
* And send it |
Line 1361 PG_FUNCTION_INFO_V1(dblink_build_sql_upd
|
Line 1363 PG_FUNCTION_INFO_V1(dblink_build_sql_upd
|
Datum |
Datum |
dblink_build_sql_update(PG_FUNCTION_ARGS) |
dblink_build_sql_update(PG_FUNCTION_ARGS) |
{ |
{ |
Oid relid; |
Relation rel; |
text *relname_text; |
text *relname_text; |
int16 *pkattnums; |
int16 *pkattnums_arg; |
int pknumatts_tmp; |
int32 pknumatts_arg; |
int16 pknumatts = 0; |
int *pkattnums; |
|
int pknumatts; |
char **src_pkattvals; |
char **src_pkattvals; |
char **tgt_pkattvals; |
char **tgt_pkattvals; |
ArrayType *src_pkattvals_arry; |
ArrayType *src_pkattvals_arry; |
Line 1386 dblink_build_sql_update(PG_FUNCTION_ARGS
|
Line 1389 dblink_build_sql_update(PG_FUNCTION_ARGS
|
relname_text = PG_GETARG_TEXT_P(0); |
relname_text = PG_GETARG_TEXT_P(0); |
|
|
/* |
/* |
* Convert relname to rel OID. |
* Open target relation. |
*/ |
*/ |
relid = get_relid_from_relname(relname_text); |
rel = get_rel_from_relname(relname_text, AccessShareLock, ACL_SELECT); |
if (!OidIsValid(relid)) |
|
ereport(ERROR, |
|
(errcode(ERRCODE_UNDEFINED_TABLE), |
|
errmsg("relation \"%s\" does not exist", |
|
GET_STR(relname_text)))); |
|
|
|
pkattnums = (int16 *) PG_GETARG_POINTER(1); |
|
pknumatts_tmp = PG_GETARG_INT32(2); |
|
if (pknumatts_tmp <= SHRT_MAX) |
|
pknumatts = pknumatts_tmp; |
|
else |
|
ereport(ERROR, |
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
|
errmsg("input for number of primary key " \ |
|
"attributes too large"))); |
|
|
|
/* |
/* |
* There should be one source array key values for each key attnum |
* Process pkattnums argument. |
*/ |
*/ |
if (pknumatts == 0) |
pkattnums_arg = (int16 *) PG_GETARG_POINTER(1); |
ereport(ERROR, |
pknumatts_arg = PG_GETARG_INT32(2); |
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
validate_pkattnums(rel, pkattnums_arg, pknumatts_arg, |
errmsg("number of key attributes must be > 0"))); |
&pkattnums, &pknumatts); |
|
|
src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); |
src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); |
tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); |
tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); |
Line 1485 dblink_build_sql_update(PG_FUNCTION_ARGS
|
Line 1473 dblink_build_sql_update(PG_FUNCTION_ARGS
|
/* |
/* |
* Prep work is finally done. Go get the SQL string. |
* Prep work is finally done. Go get the SQL string. |
*/ |
*/ |
sql = get_sql_update(relid, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals); |
sql = get_sql_update(rel, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals); |
|
|
|
/* |
|
* Now we can close the relation. |
|
*/ |
|
relation_close(rel, AccessShareLock); |
|
|
/* |
/* |
* And send it |
* And send it |
Line 1519 dblink_current_query(PG_FUNCTION_ARGS)
|
Line 1512 dblink_current_query(PG_FUNCTION_ARGS)
|
* Return NULL, and set numatts = 0, if no primary key exists. |
* Return NULL, and set numatts = 0, if no primary key exists. |
*/ |
*/ |
static char ** |
static char ** |
get_pkey_attnames(Oid relid, int16 *numatts) |
get_pkey_attnames(Relation rel, int16 *numatts) |
{ |
{ |
Relation indexRelation; |
Relation indexRelation; |
ScanKeyData entry; |
ScanKeyData entry; |
Line 1527 get_pkey_attnames(Oid relid, int16 *numa
|
Line 1520 get_pkey_attnames(Oid relid, int16 *numa
|
HeapTuple indexTuple; |
HeapTuple indexTuple; |
int i; |
int i; |
char **result = NULL; |
char **result = NULL; |
Relation rel; |
|
TupleDesc tupdesc; |
TupleDesc tupdesc; |
|
|
/* open relation using relid, get tupdesc */ |
|
rel = relation_open(relid, AccessShareLock); |
|
tupdesc = rel->rd_att; |
|
|
|
/* initialize numatts to 0 in case no primary key exists */ |
/* initialize numatts to 0 in case no primary key exists */ |
*numatts = 0; |
*numatts = 0; |
|
|
|
tupdesc = rel->rd_att; |
|
|
/* use relid to get all related indexes */ |
/* use relid to get all related indexes */ |
indexRelation = heap_openr(IndexRelationName, AccessShareLock); |
indexRelation = heap_openr(IndexRelationName, AccessShareLock); |
ScanKeyInit(&entry, |
ScanKeyInit(&entry, |
Anum_pg_index_indrelid, |
Anum_pg_index_indrelid, |
BTEqualStrategyNumber, F_OIDEQ, |
BTEqualStrategyNumber, F_OIDEQ, |
ObjectIdGetDatum(relid)); |
ObjectIdGetDatum(RelationGetRelid(rel))); |
scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry); |
scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry); |
|
|
while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL) |
while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL) |
Line 1568 get_pkey_attnames(Oid relid, int16 *numa
|
Line 1558 get_pkey_attnames(Oid relid, int16 *numa
|
} |
} |
heap_endscan(scan); |
heap_endscan(scan); |
heap_close(indexRelation, AccessShareLock); |
heap_close(indexRelation, AccessShareLock); |
relation_close(rel, AccessShareLock); |
|
|
|
return result; |
return result; |
} |
} |
|
|
static char * |
static char * |
get_sql_insert(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals) |
get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals) |
{ |
{ |
Relation rel; |
|
char *relname; |
char *relname; |
HeapTuple tuple; |
HeapTuple tuple; |
TupleDesc tupdesc; |
TupleDesc tupdesc; |
Line 1584 get_sql_insert(Oid relid, int16 *pkattnu
|
Line 1572 get_sql_insert(Oid relid, int16 *pkattnu
|
StringInfo str = makeStringInfo(); |
StringInfo str = makeStringInfo(); |
char *sql; |
char *sql; |
char *val; |
char *val; |
int16 key; |
int key; |
int i; |
int i; |
bool needComma; |
bool needComma; |
|
|
/* get relation name including any needed schema prefix and quoting */ |
/* get relation name including any needed schema prefix and quoting */ |
relname = generate_relation_name(relid); |
relname = generate_relation_name(rel); |
|
|
/* |
|
* Open relation using relid |
|
*/ |
|
rel = relation_open(relid, AccessShareLock); |
|
tupdesc = rel->rd_att; |
tupdesc = rel->rd_att; |
natts = tupdesc->natts; |
natts = tupdesc->natts; |
|
|
tuple = get_tuple_of_interest(relid, pkattnums, pknumatts, src_pkattvals); |
tuple = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals); |
if (!tuple) |
if (!tuple) |
ereport(ERROR, |
ereport(ERROR, |
(errcode(ERRCODE_CARDINALITY_VIOLATION), |
(errcode(ERRCODE_CARDINALITY_VIOLATION), |
Line 1635 get_sql_insert(Oid relid, int16 *pkattnu
|
Line 1619 get_sql_insert(Oid relid, int16 *pkattnu
|
appendStringInfo(str, ","); |
appendStringInfo(str, ","); |
|
|
if (tgt_pkattvals != NULL) |
if (tgt_pkattvals != NULL) |
key = get_attnum_pk_pos(pkattnums, pknumatts, i + 1); |
key = get_attnum_pk_pos(pkattnums, pknumatts, i); |
else |
else |
key = -1; |
key = -1; |
|
|
Line 1658 get_sql_insert(Oid relid, int16 *pkattnu
|
Line 1642 get_sql_insert(Oid relid, int16 *pkattnu
|
sql = pstrdup(str->data); |
sql = pstrdup(str->data); |
pfree(str->data); |
pfree(str->data); |
pfree(str); |
pfree(str); |
relation_close(rel, AccessShareLock); |
|
|
|
return (sql); |
return (sql); |
} |
} |
|
|
static char * |
static char * |
get_sql_delete(Oid relid, int16 *pkattnums, int16 pknumatts, char **tgt_pkattvals) |
get_sql_delete(Relation rel, int *pkattnums, int pknumatts, char **tgt_pkattvals) |
{ |
{ |
Relation rel; |
|
char *relname; |
char *relname; |
TupleDesc tupdesc; |
TupleDesc tupdesc; |
int natts; |
int natts; |
Line 1676 get_sql_delete(Oid relid, int16 *pkattnu
|
Line 1658 get_sql_delete(Oid relid, int16 *pkattnu
|
int i; |
int i; |
|
|
/* get relation name including any needed schema prefix and quoting */ |
/* get relation name including any needed schema prefix and quoting */ |
relname = generate_relation_name(relid); |
relname = generate_relation_name(rel); |
|
|
/* |
|
* Open relation using relid |
|
*/ |
|
rel = relation_open(relid, AccessShareLock); |
|
tupdesc = rel->rd_att; |
tupdesc = rel->rd_att; |
natts = tupdesc->natts; |
natts = tupdesc->natts; |
|
|
appendStringInfo(str, "DELETE FROM %s WHERE ", relname); |
appendStringInfo(str, "DELETE FROM %s WHERE ", relname); |
for (i = 0; i < pknumatts; i++) |
for (i = 0; i < pknumatts; i++) |
{ |
{ |
int16 pkattnum = pkattnums[i]; |
int pkattnum = pkattnums[i]; |
|
|
if (i > 0) |
if (i > 0) |
appendStringInfo(str, " AND "); |
appendStringInfo(str, " AND "); |
|
|
appendStringInfo(str, "%s", |
appendStringInfo(str, "%s", |
quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum - 1]->attname))); |
quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum]->attname))); |
|
|
if (tgt_pkattvals != NULL) |
if (tgt_pkattvals != NULL) |
val = pstrdup(tgt_pkattvals[i]); |
val = pstrdup(tgt_pkattvals[i]); |
Line 1714 get_sql_delete(Oid relid, int16 *pkattnu
|
Line 1692 get_sql_delete(Oid relid, int16 *pkattnu
|
sql = pstrdup(str->data); |
sql = pstrdup(str->data); |
pfree(str->data); |
pfree(str->data); |
pfree(str); |
pfree(str); |
relation_close(rel, AccessShareLock); |
|
|
|
return (sql); |
return (sql); |
} |
} |
|
|
static char * |
static char * |
get_sql_update(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals) |
get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals) |
{ |
{ |
Relation rel; |
|
char *relname; |
char *relname; |
HeapTuple tuple; |
HeapTuple tuple; |
TupleDesc tupdesc; |
TupleDesc tupdesc; |
Line 1730 get_sql_update(Oid relid, int16 *pkattnu
|
Line 1706 get_sql_update(Oid relid, int16 *pkattnu
|
StringInfo str = makeStringInfo(); |
StringInfo str = makeStringInfo(); |
char *sql; |
char *sql; |
char *val; |
char *val; |
int16 key; |
int key; |
int i; |
int i; |
bool needComma; |
bool needComma; |
|
|
/* get relation name including any needed schema prefix and quoting */ |
/* get relation name including any needed schema prefix and quoting */ |
relname = generate_relation_name(relid); |
relname = generate_relation_name(rel); |
|
|
/* |
|
* Open relation using relid |
|
*/ |
|
rel = relation_open(relid, AccessShareLock); |
|
tupdesc = rel->rd_att; |
tupdesc = rel->rd_att; |
natts = tupdesc->natts; |
natts = tupdesc->natts; |
|
|
tuple = get_tuple_of_interest(relid, pkattnums, pknumatts, src_pkattvals); |
tuple = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals); |
if (!tuple) |
if (!tuple) |
ereport(ERROR, |
ereport(ERROR, |
(errcode(ERRCODE_CARDINALITY_VIOLATION), |
(errcode(ERRCODE_CARDINALITY_VIOLATION), |
Line 1765 get_sql_update(Oid relid, int16 *pkattnu
|
Line 1737 get_sql_update(Oid relid, int16 *pkattnu
|
quote_ident_cstr(NameStr(tupdesc->attrs[i]->attname))); |
quote_ident_cstr(NameStr(tupdesc->attrs[i]->attname))); |
|
|
if (tgt_pkattvals != NULL) |
if (tgt_pkattvals != NULL) |
key = get_attnum_pk_pos(pkattnums, pknumatts, i + 1); |
key = get_attnum_pk_pos(pkattnums, pknumatts, i); |
else |
else |
key = -1; |
key = -1; |
|
|
Line 1788 get_sql_update(Oid relid, int16 *pkattnu
|
Line 1760 get_sql_update(Oid relid, int16 *pkattnu
|
|
|
for (i = 0; i < pknumatts; i++) |
for (i = 0; i < pknumatts; i++) |
{ |
{ |
int16 pkattnum = pkattnums[i]; |
int pkattnum = pkattnums[i]; |
|
|
if (i > 0) |
if (i > 0) |
appendStringInfo(str, " AND "); |
appendStringInfo(str, " AND "); |
|
|
appendStringInfo(str, "%s", |
appendStringInfo(str, "%s", |
quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum - 1]->attname))); |
quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum]->attname))); |
|
|
if (tgt_pkattvals != NULL) |
if (tgt_pkattvals != NULL) |
val = pstrdup(tgt_pkattvals[i]); |
val = pstrdup(tgt_pkattvals[i]); |
else |
else |
val = SPI_getvalue(tuple, tupdesc, pkattnum); |
val = SPI_getvalue(tuple, tupdesc, pkattnum + 1); |
|
|
if (val != NULL) |
if (val != NULL) |
{ |
{ |
Line 1813 get_sql_update(Oid relid, int16 *pkattnu
|
Line 1785 get_sql_update(Oid relid, int16 *pkattnu
|
sql = pstrdup(str->data); |
sql = pstrdup(str->data); |
pfree(str->data); |
pfree(str->data); |
pfree(str); |
pfree(str); |
relation_close(rel, AccessShareLock); |
|
|
|
return (sql); |
return (sql); |
} |
} |
Line 1854 quote_ident_cstr(char *rawstr)
|
Line 1825 quote_ident_cstr(char *rawstr)
|
return result; |
return result; |
} |
} |
|
|
static int16 |
static int |
get_attnum_pk_pos(int16 *pkattnums, int16 pknumatts, int16 key) |
get_attnum_pk_pos(int *pkattnums, int pknumatts, int key) |
{ |
{ |
int i; |
int i; |
|
|
Line 1870 get_attnum_pk_pos(int16 *pkattnums, int1
|
Line 1841 get_attnum_pk_pos(int16 *pkattnums, int1
|
} |
} |
|
|
static HeapTuple |
static HeapTuple |
get_tuple_of_interest(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals) |
get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals) |
{ |
{ |
Relation rel; |
|
char *relname; |
char *relname; |
TupleDesc tupdesc; |
TupleDesc tupdesc; |
StringInfo str = makeStringInfo(); |
StringInfo str = makeStringInfo(); |
Line 1883 get_tuple_of_interest(Oid relid, int16 *
|
Line 1853 get_tuple_of_interest(Oid relid, int16 *
|
char *val = NULL; |
char *val = NULL; |
|
|
/* get relation name including any needed schema prefix and quoting */ |
/* get relation name including any needed schema prefix and quoting */ |
relname = generate_relation_name(relid); |
relname = generate_relation_name(rel); |
|
|
/* |
tupdesc = rel->rd_att; |
* Open relation using relid |
|
*/ |
|
rel = relation_open(relid, AccessShareLock); |
|
tupdesc = CreateTupleDescCopy(rel->rd_att); |
|
relation_close(rel, AccessShareLock); |
|
|
|
/* |
/* |
* Connect to SPI manager |
* Connect to SPI manager |
Line 1907 get_tuple_of_interest(Oid relid, int16 *
|
Line 1872 get_tuple_of_interest(Oid relid, int16 *
|
|
|
for (i = 0; i < pknumatts; i++) |
for (i = 0; i < pknumatts; i++) |
{ |
{ |
int16 pkattnum = pkattnums[i]; |
int pkattnum = pkattnums[i]; |
|
|
if (i > 0) |
if (i > 0) |
appendStringInfo(str, " AND "); |
appendStringInfo(str, " AND "); |
|
|
appendStringInfo(str, "%s", |
appendStringInfo(str, "%s", |
quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum - 1]->attname))); |
quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum]->attname))); |
|
|
val = pstrdup(src_pkattvals[i]); |
val = pstrdup(src_pkattvals[i]); |
if (val != NULL) |
if (val != NULL) |
Line 1968 get_tuple_of_interest(Oid relid, int16 *
|
Line 1933 get_tuple_of_interest(Oid relid, int16 *
|
return NULL; |
return NULL; |
} |
} |
|
|
static Oid |
/* |
get_relid_from_relname(text *relname_text) |
* Open the relation named by relname_text, acquire specified type of lock, |
|
* verify we have specified permissions. |
|
* Caller must close rel when done with it. |
|
*/ |
|
static Relation |
|
get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode) |
{ |
{ |
RangeVar *relvar; |
RangeVar *relvar; |
Relation rel; |
Relation rel; |
Oid relid; |
AclResult aclresult; |
|
|
relvar = makeRangeVarFromNameList(textToQualifiedNameList(relname_text, "get_relid_from_relname")); |
relvar = makeRangeVarFromNameList(textToQualifiedNameList(relname_text, "get_rel_from_relname")); |
rel = heap_openrv(relvar, AccessShareLock); |
rel = heap_openrv(relvar, lockmode); |
relid = RelationGetRelid(rel); |
|
relation_close(rel, AccessShareLock); |
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), |
|
aclmode); |
|
if (aclresult != ACLCHECK_OK) |
|
aclcheck_error(aclresult, ACL_KIND_CLASS, |
|
RelationGetRelationName(rel)); |
|
|
return relid; |
return rel; |
} |
} |
|
|
/* |
/* |
* generate_relation_name - copied from ruleutils.c |
* generate_relation_name - copied from ruleutils.c |
* Compute the name to display for a relation specified by OID |
* Compute the name to display for a relation |
* |
* |
* The result includes all necessary quoting and schema-prefixing. |
* The result includes all necessary quoting and schema-prefixing. |
*/ |
*/ |
static char * |
static char * |
generate_relation_name(Oid relid) |
generate_relation_name(Relation rel) |
{ |
{ |
HeapTuple tp; |
|
Form_pg_class reltup; |
|
char *nspname; |
char *nspname; |
char *result; |
char *result; |
|
|
tp = SearchSysCache(RELOID, |
|
ObjectIdGetDatum(relid), |
|
0, 0, 0); |
|
if (!HeapTupleIsValid(tp)) |
|
elog(ERROR, "cache lookup failed for relation %u", relid); |
|
|
|
reltup = (Form_pg_class) GETSTRUCT(tp); |
|
|
|
/* Qualify the name if not visible in search path */ |
/* Qualify the name if not visible in search path */ |
if (RelationIsVisible(relid)) |
if (RelationIsVisible(RelationGetRelid(rel))) |
nspname = NULL; |
nspname = NULL; |
else |
else |
nspname = get_namespace_name(reltup->relnamespace); |
nspname = get_namespace_name(rel->rd_rel->relnamespace); |
|
|
result = quote_qualified_identifier(nspname, NameStr(reltup->relname)); |
|
|
|
ReleaseSysCache(tp); |
result = quote_qualified_identifier(nspname, RelationGetRelationName(rel)); |
|
|
return result; |
return result; |
} |
} |
Line 2023 static remoteConn *
|
Line 1985 static remoteConn *
|
getConnectionByName(const char *name) |
getConnectionByName(const char *name) |
{ |
{ |
remoteConnHashEnt *hentry; |
remoteConnHashEnt *hentry; |
char key[NAMEDATALEN]; |
char *key; |
|
|
if (!remoteConnHash) |
if (!remoteConnHash) |
remoteConnHash = createConnHash(); |
remoteConnHash = createConnHash(); |
|
|
MemSet(key, 0, NAMEDATALEN); |
key = pstrdup(name); |
snprintf(key, NAMEDATALEN - 1, "%s", name); |
truncate_identifier(key, strlen(key), true); |
hentry = (remoteConnHashEnt *) hash_search(remoteConnHash, |
hentry = (remoteConnHashEnt *) hash_search(remoteConnHash, |
key, HASH_FIND, NULL); |
key, HASH_FIND, NULL); |
|
|
Line 2055 createNewConnection(const char *name, re
|
Line 2017 createNewConnection(const char *name, re
|
{ |
{ |
remoteConnHashEnt *hentry; |
remoteConnHashEnt *hentry; |
bool found; |
bool found; |
char key[NAMEDATALEN]; |
char *key; |
|
|
if (!remoteConnHash) |
if (!remoteConnHash) |
remoteConnHash = createConnHash(); |
remoteConnHash = createConnHash(); |
|
|
MemSet(key, 0, NAMEDATALEN); |
key = pstrdup(name); |
snprintf(key, NAMEDATALEN - 1, "%s", name); |
truncate_identifier(key, strlen(key), true); |
hentry = (remoteConnHashEnt *) hash_search(remoteConnHash, key, |
hentry = (remoteConnHashEnt *) hash_search(remoteConnHash, key, |
HASH_ENTER, &found); |
HASH_ENTER, &found); |
|
|
Line 2071 createNewConnection(const char *name, re
|
Line 2033 createNewConnection(const char *name, re
|
errmsg("out of memory"))); |
errmsg("out of memory"))); |
|
|
if (found) |
if (found) |
|
{ |
|
PQfinish(con->con); |
|
pfree(con); |
|
|
ereport(ERROR, |
ereport(ERROR, |
(errcode(ERRCODE_DUPLICATE_OBJECT), |
(errcode(ERRCODE_DUPLICATE_OBJECT), |
errmsg("duplicate connection name"))); |
errmsg("duplicate connection name"))); |
|
} |
|
|
hentry->rcon = con; |
hentry->rcon = con; |
strncpy(hentry->name, name, NAMEDATALEN - 1); |
strncpy(hentry->name, name, NAMEDATALEN - 1); |
Line 2084 deleteConnection(const char *name)
|
Line 2051 deleteConnection(const char *name)
|
{ |
{ |
remoteConnHashEnt *hentry; |
remoteConnHashEnt *hentry; |
bool found; |
bool found; |
char key[NAMEDATALEN]; |
char *key; |
|
|
if (!remoteConnHash) |
if (!remoteConnHash) |
remoteConnHash = createConnHash(); |
remoteConnHash = createConnHash(); |
|
|
MemSet(key, 0, NAMEDATALEN); |
key = pstrdup(name); |
snprintf(key, NAMEDATALEN - 1, "%s", name); |
truncate_identifier(key, strlen(key), true); |
|
|
hentry = (remoteConnHashEnt *) hash_search(remoteConnHash, |
hentry = (remoteConnHashEnt *) hash_search(remoteConnHash, |
key, HASH_REMOVE, &found); |
key, HASH_REMOVE, &found); |
|
|
Line 2101 deleteConnection(const char *name)
|
Line 2067 deleteConnection(const char *name)
|
errmsg("undefined connection name"))); |
errmsg("undefined connection name"))); |
|
|
} |
} |
|
|
|
/* |
|
* Modified version of conninfo_parse() from fe-connect.c |
|
* Used to remove any password from the connection string |
|
* in order to test whether the server auth method will |
|
* require it. |
|
*/ |
|
static char * |
|
connstr_strip_password(const char *connstr) |
|
{ |
|
char *pname; |
|
char *pval; |
|
char *buf; |
|
char *cp; |
|
char *cp2; |
|
StringInfoData result; |
|
|
|
/* initialize return value */ |
|
initStringInfo(&result); |
|
|
|
/* Need a modifiable copy of the input string */ |
|
buf = pstrdup(connstr); |
|
cp = buf; |
|
|
|
while (*cp) |
|
{ |
|
/* Skip blanks before the parameter name */ |
|
if (isspace((unsigned char) *cp)) |
|
{ |
|
cp++; |
|
continue; |
|
} |
|
|
|
/* Get the parameter name */ |
|
pname = cp; |
|
while (*cp) |
|
{ |
|
if (*cp == '=') |
|
break; |
|
if (isspace((unsigned char) *cp)) |
|
{ |
|
*cp++ = '\0'; |
|
while (*cp) |
|
{ |
|
if (!isspace((unsigned char) *cp)) |
|
break; |
|
cp++; |
|
} |
|
break; |
|
} |
|
cp++; |
|
} |
|
|
|
/* Check that there is a following '=' */ |
|
if (*cp != '=') |
|
ereport(ERROR, |
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
|
errmsg("missing \"=\" after \"%s\" in connection string", pname))); |
|
*cp++ = '\0'; |
|
|
|
/* Skip blanks after the '=' */ |
|
while (*cp) |
|
{ |
|
if (!isspace((unsigned char) *cp)) |
|
break; |
|
cp++; |
|
} |
|
|
|
/* Get the parameter value */ |
|
pval = cp; |
|
|
|
if (*cp != '\'') |
|
{ |
|
cp2 = pval; |
|
while (*cp) |
|
{ |
|
if (isspace((unsigned char) *cp)) |
|
{ |
|
*cp++ = '\0'; |
|
break; |
|
} |
|
if (*cp == '\\') |
|
{ |
|
cp++; |
|
if (*cp != '\0') |
|
*cp2++ = *cp++; |
|
} |
|
else |
|
*cp2++ = *cp++; |
|
} |
|
*cp2 = '\0'; |
|
} |
|
else |
|
{ |
|
cp2 = pval; |
|
cp++; |
|
for (;;) |
|
{ |
|
if (*cp == '\0') |
|
ereport(ERROR, |
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
|
errmsg("unterminated quoted string in connection string"))); |
|
if (*cp == '\\') |
|
{ |
|
cp++; |
|
if (*cp != '\0') |
|
*cp2++ = *cp++; |
|
continue; |
|
} |
|
if (*cp == '\'') |
|
{ |
|
*cp2 = '\0'; |
|
cp++; |
|
break; |
|
} |
|
*cp2++ = *cp++; |
|
} |
|
} |
|
|
|
/* |
|
* Now we have the name and the value. If it is not a password, |
|
* append to the return connstr. |
|
*/ |
|
if (strcmp("password", pname) != 0) |
|
/* append the value */ |
|
appendStringInfo(&result, " %s='%s'", pname, pval); |
|
} |
|
|
|
return result.data; |
|
} |
|
|
|
static void |
|
dblink_security_check(PGconn *conn, remoteConn *rcon, const char *connstr) |
|
{ |
|
if (!superuser()) |
|
{ |
|
/* this attempt must fail */ |
|
conn = PQconnectdb(connstr_strip_password(connstr)); |
|
|
|
if (PQstatus(conn) == CONNECTION_OK) |
|
{ |
|
PQfinish(conn); |
|
if (rcon) |
|
pfree(rcon); |
|
|
|
ereport(ERROR, |
|
(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), |
|
errmsg("password is required"), |
|
errdetail("Non-superuser cannot connect if the server does not request a password."), |
|
errhint("Target server's authentication method must be changed."))); |
|
} |
|
else |
|
PQfinish(conn); |
|
} |
|
} |
|
|
|
/* |
|
* Validate the PK-attnums argument for dblink_build_sql_insert() and related |
|
* functions, and translate to the internal representation. |
|
* |
|
* The user supplies an int2vector of 1-based physical attnums, plus a count |
|
* argument (the need for the separate count argument is historical, but we |
|
* still check it). We check that each attnum corresponds to a valid, |
|
* non-dropped attribute of the rel. We do *not* prevent attnums from being |
|
* listed twice, though the actual use-case for such things is dubious. |
|
* |
|
* The internal representation is a palloc'd int array of 0-based physical |
|
* attnums. |
|
*/ |
|
static void |
|
validate_pkattnums(Relation rel, |
|
int16 *pkattnums_arg, int32 pknumatts_arg, |
|
int **pkattnums, int *pknumatts) |
|
{ |
|
TupleDesc tupdesc = rel->rd_att; |
|
int natts = tupdesc->natts; |
|
int i; |
|
|
|
/* Must have at least one pk attnum selected */ |
|
if (pknumatts_arg <= 0) |
|
ereport(ERROR, |
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
|
errmsg("number of key attributes must be > 0"))); |
|
|
|
/* Allocate output array */ |
|
*pkattnums = (int *) palloc(pknumatts_arg * sizeof(int)); |
|
*pknumatts = pknumatts_arg; |
|
|
|
/* Validate attnums and convert to internal form */ |
|
for (i = 0; i < pknumatts_arg; i++) |
|
{ |
|
int pkattnum = pkattnums_arg[i]; |
|
|
|
if (pkattnum <= 0 || pkattnum > natts || |
|
tupdesc->attrs[pkattnum - 1]->attisdropped) |
|
ereport(ERROR, |
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
|
errmsg("invalid attribute number %d", pkattnum))); |
|
(*pkattnums)[i] = pkattnum - 1; |
|
} |
|
} |