5
5
* Implements the basic DB functions used by the archiver.
6
6
*
7
7
* IDENTIFICATION
8
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.53 2004/04/22 02:39:10 momjian Exp $
8
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.54 2004/08/20 16:07:15 momjian Exp $
9
9
*
10
10
*-------------------------------------------------------------------------
11
11
*/
@@ -37,6 +37,8 @@ static void notice_processor(void *arg, const char *message);
37
37
static char * _sendSQLLine (ArchiveHandle * AH , char * qry , char * eos );
38
38
static char * _sendCopyLine (ArchiveHandle * AH , char * qry , char * eos );
39
39
40
+ static int _isIdentChar (char c );
41
+ static int _isDQChar (char c , int atStart );
40
42
41
43
static int
42
44
_parse_version (ArchiveHandle * AH , const char * versionString )
@@ -416,6 +418,9 @@ static char *
416
418
_sendSQLLine (ArchiveHandle * AH , char * qry , char * eos )
417
419
{
418
420
int pos = 0 ; /* Current position */
421
+ char * sqlPtr ;
422
+ int consumed ;
423
+ int startDT = 0 ;
419
424
420
425
/*
421
426
* 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)
433
438
appendPQExpBufferChar (AH -> sqlBuf , qry [pos ]);
434
439
/* fprintf(stderr, " %c",qry[pos]); */
435
440
436
- switch (AH -> sqlparse .state )
441
+ /* Loop until character consumed */
442
+ do
437
443
{
444
+ /* If a character needs to be scanned in a different state,
445
+ * consumed can be set to 0 to avoid advancing. Care must
446
+ * be taken to ensure internal state is not damaged.
447
+ */
448
+ consumed = 1 ;
438
449
439
- case SQL_SCAN : /* Default state == 0, set in _allocAH */
440
- if (qry [pos ] == ';' && AH -> sqlparse .braceDepth == 0 )
450
+ switch (AH -> sqlparse .state )
441
451
{
442
- /* Send It & reset the buffer */
443
-
444
- /*
445
- * fprintf(stderr, " sending: '%s'\n\n",
446
- * AH->sqlBuf->data);
452
+
453
+ case SQL_SCAN : /* Default state == 0, set in _allocAH */
454
+ if (qry [pos ] == ';' && AH -> sqlparse .braceDepth == 0 )
455
+ {
456
+ /* We've got the end of a statement.
457
+ * Send It & reset the buffer.
458
+ */
459
+
460
+ /*
461
+ * fprintf(stderr, " sending: '%s'\n\n",
462
+ * AH->sqlBuf->data);
463
+ */
464
+ ExecuteSqlCommand (AH , AH -> sqlBuf , "could not execute query" , false);
465
+ resetPQExpBuffer (AH -> sqlBuf );
466
+ AH -> sqlparse .lastChar = '\0' ;
467
+
468
+ /*
469
+ * Remove any following newlines - so that embedded
470
+ * COPY commands don't get a starting newline.
471
+ */
472
+ pos ++ ;
473
+ for (; pos < (eos - qry ) && qry [pos ] == '\n' ; pos ++ );
474
+
475
+ /* We've got our line, so exit */
476
+ return qry + pos ;
477
+ }
478
+ else
479
+ {
480
+ /*
481
+ * Look for normal boring quote chars, or dollar-quotes. We make
482
+ * the assumption that $-quotes will not have an ident character
483
+ * before them in all pg_dump output.
484
+ */
485
+ if ( qry [pos ] == '"'
486
+ || qry [pos ] == '\''
487
+ || ( qry [pos ] == '$' && _isIdentChar (AH -> sqlparse .lastChar ) == 0 )
488
+ )
489
+ {
490
+ /* fprintf(stderr,"[startquote]\n"); */
491
+ AH -> sqlparse .state = SQL_IN_QUOTE ;
492
+ AH -> sqlparse .quoteChar = qry [pos ];
493
+ AH -> sqlparse .backSlash = 0 ;
494
+ if (qry [pos ] == '$' )
495
+ {
496
+ /* override the state */
497
+ AH -> sqlparse .state = SQL_IN_DOLLARTAG ;
498
+ /* Used for checking first char of tag */
499
+ startDT = 1 ;
500
+ /* We store the tag for later comparison. */
501
+ AH -> sqlparse .tagBuf = createPQExpBuffer ();
502
+ /* Get leading $ */
503
+ appendPQExpBufferChar (AH -> sqlparse .tagBuf , qry [pos ]);
504
+ }
505
+ }
506
+ else if (qry [pos ] == '-' && AH -> sqlparse .lastChar == '-' )
507
+ AH -> sqlparse .state = SQL_IN_SQL_COMMENT ;
508
+ else if (qry [pos ] == '*' && AH -> sqlparse .lastChar == '/' )
509
+ AH -> sqlparse .state = SQL_IN_EXT_COMMENT ;
510
+ else if (qry [pos ] == '(' )
511
+ AH -> sqlparse .braceDepth ++ ;
512
+ else if (qry [pos ] == ')' )
513
+ AH -> sqlparse .braceDepth -- ;
514
+
515
+ AH -> sqlparse .lastChar = qry [pos ];
516
+ }
517
+ break ;
518
+
519
+ case SQL_IN_DOLLARTAG :
520
+
521
+ /* Like a quote, we look for a closing char *but* we only
522
+ * allow a very limited set of contained chars, and no escape chars.
523
+ * If invalid chars are found, we abort tag processing.
447
524
*/
448
- ExecuteSqlCommand (AH , AH -> sqlBuf , "could not execute query" , false);
449
- resetPQExpBuffer (AH -> sqlBuf );
450
- AH -> sqlparse .lastChar = '\0' ;
525
+
526
+ if (qry [pos ] == '$' )
527
+ {
528
+ /* fprintf(stderr,"[endquote]\n"); */
529
+ /* Get trailing $ */
530
+ appendPQExpBufferChar (AH -> sqlparse .tagBuf , qry [pos ]);
531
+ AH -> sqlparse .state = SQL_IN_DOLLARQUOTE ;
532
+ }
533
+ else
534
+ {
535
+ if ( _isDQChar (qry [pos ], startDT ) )
536
+ {
537
+ /* Valid, so add */
538
+ appendPQExpBufferChar (AH -> sqlparse .tagBuf , qry [pos ]);
539
+ }
540
+ else
541
+ {
542
+ /* Jump back to 'scan' state, we're not really in a tag,
543
+ * and valid tag chars do not include the various chars
544
+ * we look for in this state machine, so it's safe to just
545
+ * jump from this state back to SCAN. We set consumed = 0
546
+ * so that this char gets rescanned in new state.
547
+ */
548
+ destroyPQExpBuffer (AH -> sqlparse .tagBuf );
549
+ AH -> sqlparse .state = SQL_SCAN ;
550
+ consumed = 0 ;
551
+ }
552
+ }
553
+ startDT = 0 ;
554
+ break ;
555
+
451
556
557
+ case SQL_IN_DOLLARQUOTE :
452
558
/*
453
- * Remove any following newlines - so that embedded
454
- * COPY commands don't get a starting newline .
559
+ * Comparing the entire string backwards each time is NOT efficient,
560
+ * but dollar quotes in pg_dump are small and the code is a lot simpler .
455
561
*/
456
- pos ++ ;
457
- for (; pos < (eos - qry ) && qry [pos ] == '\n' ; pos ++ );
458
-
459
- /* We've got our line, so exit */
460
- return qry + pos ;
461
- }
462
- else
463
- {
464
- if (qry [pos ] == '"' || qry [pos ] == '\'' )
562
+ sqlPtr = AH -> sqlBuf -> data + AH -> sqlBuf -> len - AH -> sqlparse .tagBuf -> len ;
563
+
564
+ if (strncmp (AH -> sqlparse .tagBuf -> data , sqlPtr , AH -> sqlparse .tagBuf -> len ) == 0 ) {
565
+ /* End of $-quote */
566
+ AH -> sqlparse .state = SQL_SCAN ;
567
+ destroyPQExpBuffer (AH -> sqlparse .tagBuf );
568
+ }
569
+ break ;
570
+
571
+ case SQL_IN_SQL_COMMENT :
572
+ if (qry [pos ] == '\n' )
573
+ AH -> sqlparse .state = SQL_SCAN ;
574
+ break ;
575
+
576
+ case SQL_IN_EXT_COMMENT :
577
+ if (AH -> sqlparse .lastChar == '*' && qry [pos ] == '/' )
578
+ AH -> sqlparse .state = SQL_SCAN ;
579
+ break ;
580
+
581
+ case SQL_IN_QUOTE :
582
+
583
+ if (!AH -> sqlparse .backSlash && AH -> sqlparse .quoteChar == qry [pos ])
465
584
{
466
- /* fprintf(stderr,"[startquote]\n"); */
467
- AH -> sqlparse .state = SQL_IN_QUOTE ;
468
- AH -> sqlparse .quoteChar = qry [pos ];
469
- AH -> sqlparse .backSlash = 0 ;
585
+ /* fprintf(stderr,"[endquote]\n"); */
586
+ AH -> sqlparse .state = SQL_SCAN ;
470
587
}
471
- else if (qry [pos ] == '-' && AH -> sqlparse .lastChar == '-' )
472
- AH -> sqlparse .state = SQL_IN_SQL_COMMENT ;
473
- else if (qry [pos ] == '*' && AH -> sqlparse .lastChar == '/' )
474
- AH -> sqlparse .state = SQL_IN_EXT_COMMENT ;
475
- else if (qry [pos ] == '(' )
476
- AH -> sqlparse .braceDepth ++ ;
477
- else if (qry [pos ] == ')' )
478
- AH -> sqlparse .braceDepth -- ;
479
-
480
- AH -> sqlparse .lastChar = qry [pos ];
481
- }
482
- break ;
483
-
484
- case SQL_IN_SQL_COMMENT :
485
- if (qry [pos ] == '\n' )
486
- AH -> sqlparse .state = SQL_SCAN ;
487
- break ;
488
-
489
- case SQL_IN_EXT_COMMENT :
490
- if (AH -> sqlparse .lastChar == '*' && qry [pos ] == '/' )
491
- AH -> sqlparse .state = SQL_SCAN ;
492
- break ;
493
-
494
- case SQL_IN_QUOTE :
495
- if (!AH -> sqlparse .backSlash && AH -> sqlparse .quoteChar == qry [pos ])
496
- {
497
- /* fprintf(stderr,"[endquote]\n"); */
498
- AH -> sqlparse .state = SQL_SCAN ;
499
- }
500
- else
501
- {
502
-
503
- if (qry [pos ] == '\\' )
588
+ else
504
589
{
505
- if (AH -> sqlparse .lastChar == '\\' )
506
- AH -> sqlparse .backSlash = !AH -> sqlparse .backSlash ;
590
+
591
+ if (qry [pos ] == '\\' )
592
+ {
593
+ if (AH -> sqlparse .lastChar == '\\' )
594
+ AH -> sqlparse .backSlash = !AH -> sqlparse .backSlash ;
595
+ else
596
+ AH -> sqlparse .backSlash = 1 ;
597
+ }
507
598
else
508
- AH -> sqlparse .backSlash = 1 ;
599
+ AH -> sqlparse .backSlash = 0 ;
509
600
}
510
- else
511
- AH -> sqlparse .backSlash = 0 ;
512
- }
513
- break ;
601
+ break ;
602
+
603
+ }
514
604
515
- }
516
- AH -> sqlparse .lastChar = qry [pos ];
517
- /* fprintf(stderr, "\n"); */
605
+ } while (consumed == 0 );
606
+
607
+ AH -> sqlparse .lastChar = qry [pos ];
608
+ /* fprintf(stderr, "\n"); */
518
609
}
519
610
520
611
/*
@@ -759,3 +850,38 @@ CommitTransactionXref(ArchiveHandle *AH)
759
850
760
851
destroyPQExpBuffer (qry );
761
852
}
853
+
854
+ static int _isIdentChar (char c )
855
+ {
856
+ if ( (c >= 'a' && c <= 'z' )
857
+ || (c >= 'A' && c <= 'Z' )
858
+ || (c >= '0' && c <= '9' )
859
+ || (c == '_' )
860
+ || (c == '$' )
861
+ || (c >= '\200' && c <= '\377' )
862
+ )
863
+ {
864
+ return 1 ;
865
+ }
866
+ else
867
+ {
868
+ return 0 ;
869
+ }
870
+ }
871
+
872
+ static int _isDQChar (char c , int atStart )
873
+ {
874
+ if ( (c >= 'a' && c <= 'z' )
875
+ || (c >= 'A' && c <= 'Z' )
876
+ || (c == '_' )
877
+ || (atStart == 0 && c >= '0' && c <= '9' )
878
+ || (c >= '\200' && c <= '\377' )
879
+ )
880
+ {
881
+ return 1 ;
882
+ }
883
+ else
884
+ {
885
+ return 0 ;
886
+ }
887
+ }
0 commit comments