I have researched your report, and you are right, there are two ecpg
bugs here.  First, dollar quoting uses single-quotes internally to do
the quoting, but it does not double any single-quotes in the
dollar-quoted string.

Second, when a dollar quoted string or single-quoted string spans
multiple lines, ecpg does not escape the newline that is part of the
string.  Some compilers will accept an unescaped newline in a string,
while others will not:

        $ gcc -pedantic -c  -g -Wall tst1.c
        tst1.c:5: warning: string constant runs past end of line

It isn't standard so I think we need to replace newline in a string with
"\n\".

Attached is a patch which fixes both of these issues.  This changes ecpg
behavior so I am thinking this patch would only appear in 8.2.

I am unclear if I fixed the \r case properly.

---------------------------------------------------------------------------

[EMAIL PROTECTED] wrote:
> 
> The following bug has been logged online:
> 
> Bug reference:      2171
> Logged by:          
> Email address:      [EMAIL PROTECTED]
> PostgreSQL version: 8.1.2
> Operating system:   Linux (Debian)
> Description:        Differences compiling plpgsql in ecpg and psql
> Details: 
> 
> There appear to be parsing problems with ecpg.  The following example
> program shows code snippets that allow for the successful creation of a
> function (CREATE FUNCTION) only using two different syntaxes: one when
> entered through psql, and another when compiling with ecpg.
> 
> The expectation (and hints from the documentation) indicate that the exact
> same method of defining a function should succeed in both cases, but such is
> not the case.
> 
> Different quoting and line-wrap behavior is observed between psql and ecpg.
> 
> (Thanks for the attention, I hope this is useful!)
> 
> BEGIN CODE---------------
> /* This file is bug.pgc. */
> /* Compile as shown:
>    ecpg   bug.pgc -o bug.c
>    gcc -c -g -std=c99 -I/usr/local/pgsql/include -L/usr/local/pgsql/lib
> bug.c -o bug.o
>    gcc -I/usr/local/pgsql/include -L/usr/local/pgsql/lib -lecpg bug.o -o bug
> */
> /* Run as: ./bug */
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> 
> int main(int argc, char* argv[]) {
> 
>   EXEC SQL CONNECT TO DEFAULT;
> 
>   EXEC SQL SET AUTOCOMMIT TO ON;
>   EXEC SQL WHENEVER SQLWARNING SQLPRINT;
>   EXEC SQL WHENEVER SQLERROR SQLPRINT;
> 
>   EXEC SQL CREATE TABLE My_Table ( Item1 int, Item2 text );
> 
>   /* Documentation appears to indicate that only single quotes (') are
>      needed, but this will not ecpg-compile without double-single ('')
>      quotes.  When entered through psql, only the single quotes (')
>      are needed. */
>   /* doc/html/sql-syntax.html#SQL-SYNTAX-DOLLAR-QUOTING: "It is
>      particularly useful when representing string constants inside
>      other constants, as is often needed in procedural function
>      definitions." */
>   /* doc/html/sql-createfunction.html: "Without dollar quoting, any
>      single quotes or backslashes in the function definition must be
>      escaped by doubling them." */
> 
>   /* Documentation appears to indicate that the body of the funtion
>      can be extended across multiple lines in the input file (this
>      file) but it will not compile (ecpg) without keeping the function
>      body on one line.  Multiple line input works through psql, but
>      not here.*/
> //bad ecpg,good psql: EXEC SQL CREATE FUNCTION My_Table_Check() RETURNS
> trigger
> //bad ecpg,good psql:   AS $My_Table_Check$
> //bad ecpg,good psql:   BEGIN RAISE NOTICE 'TG_NAME=%, TG WHEN=%', TG_NAME,
> TG_WHEN;
> //bad ecpg,good psql: RETURN NEW;
> //bad ecpg,good psql: END;
> //bad ecpg,good psql: $My_Table_Check$
> //bad ecpg,good psql:   LANGUAGE 'plpgsql';
>   EXEC SQL CREATE FUNCTION My_Table_Check() RETURNS trigger
>     AS $My_Table_Check$ BEGIN RAISE NOTICE ''TG_NAME=%, TG WHEN=%'',
> TG_NAME, TG_WHEN; RETURN NEW; END; $My_Table_Check$
>     LANGUAGE 'plpgsql';
> 
>   EXEC SQL CREATE TRIGGER My_Table_Check_Trigger
>     BEFORE INSERT
>     ON My_Table
>     FOR EACH ROW
>     EXECUTE PROCEDURE My_Table_Check();
> 
>   EXEC SQL INSERT INTO My_Table VALUES (1234, 'Some random text');
>   EXEC SQL INSERT INTO My_Table VALUES (5678, 'The Quick Brown');
> 
>   EXEC SQL DROP TRIGGER My_Table_Check_Trigger ON My_Table;
>   EXEC SQL DROP FUNCTION My_Table_Check();
>   EXEC SQL DROP TABLE My_Table;
> 
>   EXEC SQL DISCONNECT ALL;
> 
>   return 0;
> }
> 
> END CODE------------------
> 
> ---------------------------(end of broadcast)---------------------------
> TIP 9: In versions below 8.0, the planner will ignore your desire to
>        choose an index scan if your joining column's datatypes do not
>        match
> 

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: src/interfaces/ecpg/preproc/pgc.l
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/ecpg/preproc/pgc.l,v
retrieving revision 1.140
diff -c -c -r1.140 pgc.l
*** src/interfaces/ecpg/preproc/pgc.l   2 Feb 2006 03:51:41 -0000       1.140
--- src/interfaces/ecpg/preproc/pgc.l   2 Feb 2006 04:38:28 -0000
***************
*** 134,140 ****
   */
  xqstart                       {quote}
  xqdouble              {quote}{quote}
! xqinside              [^\\']+
  xqescape              [\\][^0-7]
  xqoctesc              [\\][0-7]{1,3}
  xqhexesc              [\\]x[0-9A-Fa-f]{1,2}
--- 134,140 ----
   */
  xqstart                       {quote}
  xqdouble              {quote}{quote}
! xqinside              [^\\'{newline}]
  xqescape              [\\][^0-7]
  xqoctesc              [\\][0-7]{1,3}
  xqhexesc              [\\]x[0-9A-Fa-f]{1,2}
***************
*** 152,158 ****
  dolq_cont             [A-Za-z\200-\377_0-9]
  dolqdelim             \$({dolq_start}{dolq_cont}*)?\$
  dolqfailed            \${dolq_start}{dolq_cont}*
! dolqinside            [^$]+
  
  /* Double quote
   * Allows embedded spaces and other special characters into identifiers.
--- 152,158 ----
  dolq_cont             [A-Za-z\200-\377_0-9]
  dolqdelim             \$({dolq_start}{dolq_cont}*)?\$
  dolqfailed            \${dolq_start}{dolq_cont}*
! dolqinside            [^$'{newline}]
  
  /* Double quote
   * Allows embedded spaces and other special characters into identifiers.
***************
*** 161,167 ****
  xdstart                       {dquote}
  xdstop                        {dquote}
  xddouble              {dquote}{dquote}
! xdinside              [^"]+
  
  /* special stuff for C strings */
  xdcqq                 \\\\
--- 161,167 ----
  xdstart                       {dquote}
  xdstop                        {dquote}
  xddouble              {dquote}{dquote}
! xdinside              [^"]
  
  /* special stuff for C strings */
  xdcqq                 \\\\
***************
*** 423,429 ****
                                return SCONST;
                        }
  <xq>{xqdouble}                { addlitchar('\''); }
! <xq>{xqinside}                { addlit(yytext, yyleng); }
  <xq>{xqescape}        { 
                                check_escape_warning();
                                addlit(yytext, yyleng);
--- 423,433 ----
                                return SCONST;
                        }
  <xq>{xqdouble}                { addlitchar('\''); }
! <xq>{xqinside}                {
!                               if (yytext[0] == '\r' || yytext[0] == '\n')
!                                       addlitchar('\\');       /* C string 
continuation */
!                               addlitchar(yytext[0]);
!                       }
  <xq>{xqescape}        { 
                                check_escape_warning();
                                addlit(yytext, yyleng);
***************
*** 473,482 ****
                                yyless(yyleng-1);
                                }
                        }
! <xdolq>{dolqinside}   { addlit(yytext, yyleng); }
  <xdolq>{dolqfailed}   { addlit(yytext, yyleng); }
  <xdolq>.      {
!                               /* This is only needed for $ inside the quoted 
text */
                                addlitchar(yytext[0]);
                        }
  <xdolq><<EOF>> { yyerror("unterminated dollar-quoted string"); }
--- 477,493 ----
                                yyless(yyleng-1);
                                }
                        }
! <xdolq>{dolqinside}   {
!                               if (yytext[0] == '\r' || yytext[0] == '\n')
!                                       addlitchar('\\');       /* C string 
continuation */
!                               addlitchar(yytext[0]);
!                       }
  <xdolq>{dolqfailed}   { addlit(yytext, yyleng); }
  <xdolq>.      {
!                               /* $$ is implemented as a single-quoted string, 
so double it? */
!                               if (yytext[0] == '\'')
!                                       addlitchar(yytext[0]);
!                               /* single quote or dollar sign */
                                addlitchar(yytext[0]);
                        }
  <xdolq><<EOF>> { yyerror("unterminated dollar-quoted string"); }
---------------------------(end of broadcast)---------------------------
TIP 9: In versions below 8.0, the planner will ignore your desire to
       choose an index scan if your joining column's datatypes do not
       match

Reply via email to