]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
This patch allows pg_restore to recognize $-quotes in SQL queries. It
authorBruce Momjian <bruce@momjian.us>
Fri, 20 Aug 2004 16:07:15 +0000 (16:07 +0000)
committerBruce Momjian <bruce@momjian.us>
Fri, 20 Aug 2004 16:07:15 +0000 (16:07 +0000)
will treat any unquoted string that starts with a $ and has no preceding
identifier chars as a potential $-quote tag, it then makes sure that the
tag chars are valid. If so, it processes the $-quote.

Philip Warner

src/bin/pg_dump/pg_backup_archiver.h
src/bin/pg_dump/pg_backup_db.c

index 7466feab0bb4391157bd926bc4a5e94800c0ab61..7f6740376e78ec9fa6e6750774890fa98dc6cccb 100644 (file)
@@ -17,7 +17,7 @@
  *
  *
  * IDENTIFICATION
- *             $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.58 2004/04/22 02:39:10 momjian Exp $
+ *             $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.59 2004/08/20 16:07:15 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -137,7 +137,9 @@ typedef enum
        SQL_SCAN = 0,
        SQL_IN_SQL_COMMENT,
        SQL_IN_EXT_COMMENT,
-       SQL_IN_QUOTE
+       SQL_IN_QUOTE,
+       SQL_IN_DOLLARTAG,
+       SQL_IN_DOLLARQUOTE
 } sqlparseState;
 
 typedef struct
@@ -147,6 +149,7 @@ typedef struct
        char            lastChar;
        char            quoteChar;
        int                     braceDepth;
+       PQExpBuffer     tagBuf;
 } sqlparseInfo;
 
 typedef struct _archiveHandle
index df2f8af3ff2bbc9d03104e4b4e5a24f85a71eab4..984c2f844c3bb0fa10a8000fa65dcbded54939d5 100644 (file)
@@ -5,7 +5,7 @@
  *     Implements the basic DB functions used by the archiver.
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.53 2004/04/22 02:39:10 momjian Exp $
+ *       $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.54 2004/08/20 16:07:15 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,6 +37,8 @@ static void notice_processor(void *arg, const char *message);
 static char *_sendSQLLine(ArchiveHandle *AH, char *qry, char *eos);
 static char *_sendCopyLine(ArchiveHandle *AH, char *qry, char *eos);
 
+static int _isIdentChar(char c);
+static int _isDQChar(char c, int atStart);
 
 static int
 _parse_version(ArchiveHandle *AH, const char *versionString)
@@ -416,6 +418,9 @@ static char *
 _sendSQLLine(ArchiveHandle *AH, char *qry, char *eos)
 {
        int                     pos = 0;                /* Current position */
+       char                    *sqlPtr;
+       int                     consumed;
+       int                     startDT = 0;
 
        /*
         * The following is a mini state machine to assess the end of an SQL
@@ -433,88 +438,174 @@ _sendSQLLine(ArchiveHandle *AH, char *qry, char *eos)
                appendPQExpBufferChar(AH->sqlBuf, qry[pos]);
                /* fprintf(stderr, " %c",qry[pos]); */
 
-               switch (AH->sqlparse.state)
+               /* Loop until character consumed */
+               do
                {
+                       /* If a character needs to be scanned in a different state,
+                        * consumed can be set to 0 to avoid advancing. Care must
+                        * be taken to ensure internal state is not damaged.
+                        */
+                       consumed = 1;
 
-                       case SQL_SCAN:          /* Default state == 0, set in _allocAH */
-                               if (qry[pos] == ';' && AH->sqlparse.braceDepth == 0)
+                       switch (AH->sqlparse.state)
                                {
-                                       /* Send It & reset the buffer */
-
-                                       /*
-                                        * fprintf(stderr, "    sending: '%s'\n\n",
-                                        * AH->sqlBuf->data);
+       
+                               case SQL_SCAN:          /* Default state == 0, set in _allocAH */
+                                       if (qry[pos] == ';' && AH->sqlparse.braceDepth == 0)
+                                       {
+                                               /* We've got the end of a statement.
+                                                * Send It & reset the buffer.
+                                                */
+       
+                                               /*
+                                                * fprintf(stderr, "    sending: '%s'\n\n",
+                                                * AH->sqlBuf->data);
+                                                */
+                                               ExecuteSqlCommand(AH, AH->sqlBuf, "could not execute query", false);
+                                               resetPQExpBuffer(AH->sqlBuf);
+                                               AH->sqlparse.lastChar = '\0';
+       
+                                               /*
+                                                * Remove any following newlines - so that embedded
+                                                * COPY commands don't get a starting newline.
+                                                */
+                                               pos++;
+                                               for (; pos < (eos - qry) && qry[pos] == '\n'; pos++);
+       
+                                               /* We've got our line, so exit */
+                                               return qry + pos;
+                                       }
+                                       else
+                                       {
+                                               /* 
+                                                * Look for normal boring quote chars, or dollar-quotes. We make
+                                                * the assumption that $-quotes will not have an ident character
+                                                * before them in all pg_dump output.
+                                                */
+                                               if (    qry[pos] == '"' 
+                                                       || qry[pos] == '\'' 
+                                                       || ( qry[pos] == '$' && _isIdentChar(AH->sqlparse.lastChar) == 0 )
+                                                  )
+                                               {
+                                                       /* fprintf(stderr,"[startquote]\n"); */
+                                                       AH->sqlparse.state = SQL_IN_QUOTE;
+                                                       AH->sqlparse.quoteChar = qry[pos];
+                                                       AH->sqlparse.backSlash = 0;
+                                                       if (qry[pos] == '$')
+                                                       {
+                                                               /* override the state */
+                                                               AH->sqlparse.state = SQL_IN_DOLLARTAG;
+                                                               /* Used for checking first char of tag */
+                                                               startDT = 1;
+                                                               /* We store the tag for later comparison. */
+                                                               AH->sqlparse.tagBuf = createPQExpBuffer();
+                                                               /* Get leading $ */
+                                                               appendPQExpBufferChar(AH->sqlparse.tagBuf, qry[pos]);
+                                                       }
+                                               }
+                                               else if (qry[pos] == '-' && AH->sqlparse.lastChar == '-')
+                                                       AH->sqlparse.state = SQL_IN_SQL_COMMENT;
+                                               else if (qry[pos] == '*' && AH->sqlparse.lastChar == '/')
+                                                       AH->sqlparse.state = SQL_IN_EXT_COMMENT;
+                                               else if (qry[pos] == '(')
+                                                       AH->sqlparse.braceDepth++;
+                                               else if (qry[pos] == ')')
+                                                       AH->sqlparse.braceDepth--;
+       
+                                               AH->sqlparse.lastChar = qry[pos];
+                                       }
+                                       break;
+       
+                               case SQL_IN_DOLLARTAG:
+       
+                                       /* Like a quote, we look for a closing char *but* we only
+                                        * allow a very limited set of contained chars, and no escape chars.
+                                        * If invalid chars are found, we abort tag processing.
                                         */
-                                       ExecuteSqlCommand(AH, AH->sqlBuf, "could not execute query", false);
-                                       resetPQExpBuffer(AH->sqlBuf);
-                                       AH->sqlparse.lastChar = '\0';
+       
+                                       if (qry[pos] == '$')
+                                       {
+                                               /* fprintf(stderr,"[endquote]\n"); */
+                                               /* Get trailing $ */
+                                               appendPQExpBufferChar(AH->sqlparse.tagBuf, qry[pos]);
+                                               AH->sqlparse.state = SQL_IN_DOLLARQUOTE;
+                                       }
+                                       else
+                                       {
+                                               if ( _isDQChar(qry[pos], startDT) )
+                                               {
+                                                       /* Valid, so add */
+                                                       appendPQExpBufferChar(AH->sqlparse.tagBuf, qry[pos]);
+                                               }
+                                               else
+                                               {
+                                                       /* Jump back to 'scan' state, we're not really in a tag,
+                                                        * and valid tag chars do not include the various chars
+                                                        * we look for in this state machine, so it's safe to just
+                                                        * jump from this state back to SCAN. We set consumed = 0
+                                                        * so that this char gets rescanned in new state.
+                                                        */
+                                                       destroyPQExpBuffer(AH->sqlparse.tagBuf);
+                                                       AH->sqlparse.state = SQL_SCAN;
+                                                       consumed = 0;
+                                               }
+                                       }
+                                       startDT = 0;
+                                       break;
+       
 
+                               case SQL_IN_DOLLARQUOTE:
                                        /*
-                                        * Remove any following newlines - so that embedded
-                                        * COPY commands don't get a starting newline.
+                                        * Comparing the entire string backwards each time is NOT efficient, 
+                                        * but dollar quotes in pg_dump are small and the code is a lot simpler.
                                         */
-                                       pos++;
-                                       for (; pos < (eos - qry) && qry[pos] == '\n'; pos++);
-
-                                       /* We've got our line, so exit */
-                                       return qry + pos;
-                               }
-                               else
-                               {
-                                       if (qry[pos] == '"' || qry[pos] == '\'')
+                                       sqlPtr = AH->sqlBuf->data + AH->sqlBuf->len - AH->sqlparse.tagBuf->len;
+       
+                                       if (strncmp(AH->sqlparse.tagBuf->data, sqlPtr, AH->sqlparse.tagBuf->len) == 0) {
+                                               /* End of $-quote */
+                                               AH->sqlparse.state = SQL_SCAN;
+                                               destroyPQExpBuffer(AH->sqlparse.tagBuf);
+                                       }
+                                       break;
+       
+                               case SQL_IN_SQL_COMMENT:
+                                       if (qry[pos] == '\n')
+                                               AH->sqlparse.state = SQL_SCAN;
+                                       break;
+       
+                               case SQL_IN_EXT_COMMENT:
+                                       if (AH->sqlparse.lastChar == '*' && qry[pos] == '/')
+                                               AH->sqlparse.state = SQL_SCAN;
+                                       break;
+       
+                               case SQL_IN_QUOTE:
+
+                                       if (!AH->sqlparse.backSlash && AH->sqlparse.quoteChar == qry[pos])
                                        {
-                                               /* fprintf(stderr,"[startquote]\n"); */
-                                               AH->sqlparse.state = SQL_IN_QUOTE;
-                                               AH->sqlparse.quoteChar = qry[pos];
-                                               AH->sqlparse.backSlash = 0;
+                                               /* fprintf(stderr,"[endquote]\n"); */
+                                               AH->sqlparse.state = SQL_SCAN;
                                        }
-                                       else if (qry[pos] == '-' && AH->sqlparse.lastChar == '-')
-                                               AH->sqlparse.state = SQL_IN_SQL_COMMENT;
-                                       else if (qry[pos] == '*' && AH->sqlparse.lastChar == '/')
-                                               AH->sqlparse.state = SQL_IN_EXT_COMMENT;
-                                       else if (qry[pos] == '(')
-                                               AH->sqlparse.braceDepth++;
-                                       else if (qry[pos] == ')')
-                                               AH->sqlparse.braceDepth--;
-
-                                       AH->sqlparse.lastChar = qry[pos];
-                               }
-                               break;
-
-                       case SQL_IN_SQL_COMMENT:
-                               if (qry[pos] == '\n')
-                                       AH->sqlparse.state = SQL_SCAN;
-                               break;
-
-                       case SQL_IN_EXT_COMMENT:
-                               if (AH->sqlparse.lastChar == '*' && qry[pos] == '/')
-                                       AH->sqlparse.state = SQL_SCAN;
-                               break;
-
-                       case SQL_IN_QUOTE:
-                               if (!AH->sqlparse.backSlash && AH->sqlparse.quoteChar == qry[pos])
-                               {
-                                       /* fprintf(stderr,"[endquote]\n"); */
-                                       AH->sqlparse.state = SQL_SCAN;
-                               }
-                               else
-                               {
-
-                                       if (qry[pos] == '\\')
+                                       else
                                        {
-                                               if (AH->sqlparse.lastChar == '\\')
-                                                       AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
+       
+                                               if (qry[pos] == '\\')
+                                               {
+                                                       if (AH->sqlparse.lastChar == '\\')
+                                                               AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
+                                                       else
+                                                               AH->sqlparse.backSlash = 1;
+                                               }
                                                else
-                                                       AH->sqlparse.backSlash = 1;
+                                                       AH->sqlparse.backSlash = 0;
                                        }
-                                       else
-                                               AH->sqlparse.backSlash = 0;
-                               }
-                               break;
+                                       break;
+       
+                       }
 
-               }
-               AH->sqlparse.lastChar = qry[pos];
-               /* fprintf(stderr, "\n"); */
+               } while (consumed == 0);
+
+               AH->sqlparse.lastChar = qry[pos];
+               /* fprintf(stderr, "\n"); */
        }
 
        /*
@@ -759,3 +850,38 @@ CommitTransactionXref(ArchiveHandle *AH)
 
        destroyPQExpBuffer(qry);
 }
+
+static int _isIdentChar(char c)
+{
+       if (            (c >= 'a' && c <= 'z')
+               ||      (c >= 'A' && c <= 'Z')
+               ||      (c >= '0' && c <= '9')
+               ||      (c == '_')
+               ||      (c == '$')
+               ||      (c >= '\200' && c <= '\377')
+          )
+       {
+               return 1;
+       }
+       else
+       {
+               return 0;
+       }
+}
+
+static int _isDQChar(char c, int atStart)
+{      
+       if (            (c >= 'a' && c <= 'z')
+               ||      (c >= 'A' && c <= 'Z')
+               ||      (c == '_')
+               ||      (atStart == 0 && c >= '0' && c <= '9')
+               ||      (c >= '\200' && c <= '\377')
+          )
+       {
+               return 1;
+       }
+       else
+       {
+               return 0;
+       }
+}