Diff for /pgsql/contrib/dblink/dblink.c between versions 1.38 and 1.38.4.5

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;
   }

Removed from v.1.38  
changed lines
  Added in v.1.38.4.5


PostgreSQL CVSweb <[email protected]>