version 1.38, 2005/01/01 05:43:05
|
version 1.38.4.5, 2010/06/03 09:45:47
|
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" |
Line 83 static int16 get_attnum_pk_pos(int16 *pk
|
Line 85 static int16 get_attnum_pk_pos(int16 *pk
|
static HeapTuple get_tuple_of_interest(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals); |
static HeapTuple get_tuple_of_interest(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals); |
static Oid get_relid_from_relname(text *relname_text); |
static Oid get_relid_from_relname(text *relname_text); |
static char *generate_relation_name(Oid relid); |
static char *generate_relation_name(Oid relid); |
|
static char *connstr_strip_password(const char *connstr); |
|
static void dblink_security_check(PGconn *conn, remoteConn *rcon, const char *connstr); |
|
static int get_nondropped_natts(Oid relid); |
|
|
/* Global */ |
/* Global */ |
List *res_id = NIL; |
List *res_id = NIL; |
Line 166 typedef struct remoteConnHashEnt
|
Line 171 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 213 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 556 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 578 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 765 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 790 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 1113 dblink_build_sql_insert(PG_FUNCTION_ARGS
|
Line 1136 dblink_build_sql_insert(PG_FUNCTION_ARGS
|
int16 typlen; |
int16 typlen; |
bool typbyval; |
bool typbyval; |
char typalign; |
char typalign; |
|
int nondropped_natts; |
|
|
relname_text = PG_GETARG_TEXT_P(0); |
relname_text = PG_GETARG_TEXT_P(0); |
|
|
Line 1148 dblink_build_sql_insert(PG_FUNCTION_ARGS
|
Line 1172 dblink_build_sql_insert(PG_FUNCTION_ARGS
|
tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); |
tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); |
|
|
/* |
/* |
|
* ensure we don't ask for more pk attributes than we have |
|
* non-dropped columns |
|
*/ |
|
nondropped_natts = get_nondropped_natts(relid); |
|
if (pknumatts > nondropped_natts) |
|
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), |
|
errmsg("number of primary key fields exceeds number of specified relation attributes"))); |
|
|
|
/* |
* Source array is made up of key values that will be used to locate |
* Source array is made up of key values that will be used to locate |
* the tuple of interest from the local system. |
* the tuple of interest from the local system. |
*/ |
*/ |
Line 1260 dblink_build_sql_delete(PG_FUNCTION_ARGS
|
Line 1293 dblink_build_sql_delete(PG_FUNCTION_ARGS
|
int16 typlen; |
int16 typlen; |
bool typbyval; |
bool typbyval; |
char typalign; |
char typalign; |
|
int nondropped_natts; |
|
|
relname_text = PG_GETARG_TEXT_P(0); |
relname_text = PG_GETARG_TEXT_P(0); |
|
|
Line 1294 dblink_build_sql_delete(PG_FUNCTION_ARGS
|
Line 1328 dblink_build_sql_delete(PG_FUNCTION_ARGS
|
tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); |
tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); |
|
|
/* |
/* |
|
* ensure we don't ask for more pk attributes than we have |
|
* non-dropped columns |
|
*/ |
|
nondropped_natts = get_nondropped_natts(relid); |
|
if (pknumatts > nondropped_natts) |
|
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), |
|
errmsg("number of primary key fields exceeds number of specified relation attributes"))); |
|
|
|
/* |
* Target array is made up of key values that will be used to build |
* Target array is made up of key values that will be used to build |
* the SQL string for use on the remote system. |
* the SQL string for use on the remote system. |
*/ |
*/ |
Line 1382 dblink_build_sql_update(PG_FUNCTION_ARGS
|
Line 1425 dblink_build_sql_update(PG_FUNCTION_ARGS
|
int16 typlen; |
int16 typlen; |
bool typbyval; |
bool typbyval; |
char typalign; |
char typalign; |
|
int nondropped_natts; |
|
|
relname_text = PG_GETARG_TEXT_P(0); |
relname_text = PG_GETARG_TEXT_P(0); |
|
|
Line 1417 dblink_build_sql_update(PG_FUNCTION_ARGS
|
Line 1461 dblink_build_sql_update(PG_FUNCTION_ARGS
|
tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); |
tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); |
|
|
/* |
/* |
|
* ensure we don't ask for more pk attributes than we have |
|
* non-dropped columns |
|
*/ |
|
nondropped_natts = get_nondropped_natts(relid); |
|
if (pknumatts > nondropped_natts) |
|
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), |
|
errmsg("number of primary key fields exceeds number of specified relation attributes"))); |
|
|
|
/* |
* Source array is made up of key values that will be used to locate |
* Source array is made up of key values that will be used to locate |
* the tuple of interest from the local system. |
* the tuple of interest from the local system. |
*/ |
*/ |
Line 2023 static remoteConn *
|
Line 2076 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 2108 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 2084 deleteConnection(const char *name)
|
Line 2137 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 2153 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); |
|
} |
|
} |
|
|
|
static int |
|
get_nondropped_natts(Oid relid) |
|
{ |
|
int nondropped_natts = 0; |
|
TupleDesc tupdesc; |
|
Relation rel; |
|
int natts; |
|
int i; |
|
|
|
rel = relation_open(relid, AccessShareLock); |
|
tupdesc = rel->rd_att; |
|
natts = tupdesc->natts; |
|
|
|
for (i = 0; i < natts; i++) |
|
{ |
|
if (tupdesc->attrs[i]->attisdropped) |
|
continue; |
|
nondropped_natts++; |
|
} |
|
|
|
relation_close(rel, AccessShareLock); |
|
return nondropped_natts; |
|
} |