Re: SQL/JSON functions vs. ECPG vs. STRING as a reserved word

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: SQL/JSON functions vs. ECPG vs. STRING as a reserved word
Дата
Msg-id 1277983.1656868116@sss.pgh.pa.us
обсуждение исходный текст
Ответ на SQL/JSON functions vs. ECPG vs. STRING as a reserved word  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
Noah Misch <noah@leadboat.com> writes:
> On Mon, May 30, 2022 at 05:20:15PM -0400, Tom Lane wrote:
>> [allow EXEC SQL TYPE unreserved_keyword IS ...]

> I didn't locate any problems beyond the test and doc gaps that you mentioned,
> so I've marked this Ready for Committer.

Thanks!  Here's a fleshed-out version with doc changes, plus adjustment
of preproc/type.pgc so that it exposes the existing problem.  (No code
changes from v1.)  I'll push this in a few days if there are not
objections.

            regards, tom lane

diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml
index 7f8b4dd5c0..0ba1bf93a6 100644
--- a/doc/src/sgml/ecpg.sgml
+++ b/doc/src/sgml/ecpg.sgml
@@ -1483,6 +1483,10 @@ EXEC SQL END DECLARE SECTION;

     <sect4>
      <title>Typedefs</title>
+     <indexterm>
+      <primary>typedef</primary>
+      <secondary>in ECPG</secondary>
+     </indexterm>

      <para>
       Use the <literal>typedef</literal> keyword to map new types to already
@@ -1497,8 +1501,38 @@ EXEC SQL END DECLARE SECTION;
 <programlisting>
 EXEC SQL TYPE serial_t IS long;
 </programlisting>
-      This declaration does not need to be part of a declare section.
+      This declaration does not need to be part of a declare section;
+      that is, you can also write typedefs as normal C statements.
      </para>
+
+     <para>
+      Any word you declare as a typedef cannot be used as a SQL keyword
+      in <literal>EXEC SQL</literal> commands later in the same program.
+      For example, this won't work:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+    typedef int start;
+EXEC SQL END DECLARE SECTION;
+...
+EXEC SQL START TRANSACTION;
+</programlisting>
+      ECPG will report a syntax error for <literal>START
+      TRANSACTION</literal>, because it no longer
+      recognizes <literal>START</literal> as a SQL keyword,
+      only as a typedef.
+     </para>
+
+     <note>
+      <para>
+       In <productname>PostgreSQL</productname> releases before v16, use
+       of SQL keywords as typedef names was likely to result in syntax
+       errors associated with use of the typedef itself, rather than use
+       of the name as a SQL keyword.  The new behavior is less likely to
+       cause problems when an existing ECPG application is recompiled in
+       a new <productname>PostgreSQL</productname> release with new
+       keywords.
+      </para>
+     </note>
     </sect4>

     <sect4>
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index b95fc44314..fba35f6be6 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -564,8 +564,29 @@ var_type:    simple_type
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        | ECPGColLabelCommon '(' precision opt_scale ')'
+        | NUMERIC '(' precision opt_scale ')'
         {
+            $$.type_enum = ECPGt_numeric;
+            $$.type_str = mm_strdup("numeric");
+            $$.type_dimension = mm_strdup("-1");
+            $$.type_index = mm_strdup("-1");
+            $$.type_sizeof = NULL;
+        }
+        | DECIMAL_P '(' precision opt_scale ')'
+        {
+            $$.type_enum = ECPGt_decimal;
+            $$.type_str = mm_strdup("decimal");
+            $$.type_dimension = mm_strdup("-1");
+            $$.type_index = mm_strdup("-1");
+            $$.type_sizeof = NULL;
+        }
+        | IDENT '(' precision opt_scale ')'
+        {
+            /*
+             * In C parsing mode, NUMERIC and DECIMAL are not keywords, so
+             * they will show up here as a plain identifier, and we need
+             * this duplicate code to recognize them.
+             */
             if (strcmp($1, "numeric") == 0)
             {
                 $$.type_enum = ECPGt_numeric;
@@ -587,15 +608,98 @@ var_type:    simple_type
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        | ECPGColLabelCommon ecpg_interval
+        | VARCHAR
         {
-            if (strlen($2) != 0 && strcmp ($1, "datetime") != 0 && strcmp ($1, "interval") != 0)
-                mmerror (PARSE_ERROR, ET_ERROR, "interval specification not allowed here");
+            $$.type_enum = ECPGt_varchar;
+            $$.type_str = EMPTY; /*mm_strdup("varchar");*/
+            $$.type_dimension = mm_strdup("-1");
+            $$.type_index = mm_strdup("-1");
+            $$.type_sizeof = NULL;
+        }
+        | FLOAT_P
+        {
+            /* Note: DOUBLE is handled in simple_type */
+            $$.type_enum = ECPGt_float;
+            $$.type_str = mm_strdup("float");
+            $$.type_dimension = mm_strdup("-1");
+            $$.type_index = mm_strdup("-1");
+            $$.type_sizeof = NULL;
+        }
+        | NUMERIC
+        {
+            $$.type_enum = ECPGt_numeric;
+            $$.type_str = mm_strdup("numeric");
+            $$.type_dimension = mm_strdup("-1");
+            $$.type_index = mm_strdup("-1");
+            $$.type_sizeof = NULL;
+        }
+        | DECIMAL_P
+        {
+            $$.type_enum = ECPGt_decimal;
+            $$.type_str = mm_strdup("decimal");
+            $$.type_dimension = mm_strdup("-1");
+            $$.type_index = mm_strdup("-1");
+            $$.type_sizeof = NULL;
+        }
+        | TIMESTAMP
+        {
+            $$.type_enum = ECPGt_timestamp;
+            $$.type_str = mm_strdup("timestamp");
+            $$.type_dimension = mm_strdup("-1");
+            $$.type_index = mm_strdup("-1");
+            $$.type_sizeof = NULL;
+        }
+        | INTERVAL ecpg_interval
+        {
+            $$.type_enum = ECPGt_interval;
+            $$.type_str = mm_strdup("interval");
+            $$.type_dimension = mm_strdup("-1");
+            $$.type_index = mm_strdup("-1");
+            $$.type_sizeof = NULL;
+        }
+        | STRING
+        {
+            if (INFORMIX_MODE)
+            {
+                /* In Informix mode, "string" is automatically a typedef */
+                $$.type_enum = ECPGt_string;
+                $$.type_str = mm_strdup("char");
+                $$.type_dimension = mm_strdup("-1");
+                $$.type_index = mm_strdup("-1");
+                $$.type_sizeof = NULL;
+            }
+            else
+            {
+                /* Otherwise, legal only if user typedef'ed it */
+                struct typedefs *this = get_typedef("string", false);
+
+                $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY
:mm_strdup(this->name); 
+                $$.type_enum = this->type->type_enum;
+                $$.type_dimension = this->type->type_dimension;
+                $$.type_index = this->type->type_index;
+                if (this->type->type_sizeof && strlen(this->type->type_sizeof) != 0)
+                    $$.type_sizeof = this->type->type_sizeof;
+                else
+                    $$.type_sizeof = cat_str(3, mm_strdup("sizeof("), mm_strdup(this->name), mm_strdup(")"));

+                struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
+            }
+        }
+        | IDENT ecpg_interval
+        {
             /*
-             * Check for type names that the SQL grammar treats as
-             * unreserved keywords
+             * In C parsing mode, the above SQL type names are not keywords,
+             * so they will show up here as a plain identifier, and we need
+             * this duplicate code to recognize them.
+             *
+             * Note that we also handle the type names bytea, date, and
+             * datetime here, but not above because those are not currently
+             * SQL keywords.  If they ever become so, they must gain duplicate
+             * productions above.
              */
+            if (strlen($2) != 0 && strcmp ($1, "datetime") != 0 && strcmp ($1, "interval") != 0)
+                mmerror (PARSE_ERROR, ET_ERROR, "interval specification not allowed here");
+
             if (strcmp($1, "varchar") == 0)
             {
                 $$.type_enum = ECPGt_varchar;
@@ -686,45 +790,8 @@ var_type:    simple_type
             }
             else
             {
-                /* this is for typedef'ed types */
-                struct typedefs *this = get_typedef($1);
-
-                $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY
:mm_strdup(this->name); 
-                $$.type_enum = this->type->type_enum;
-                $$.type_dimension = this->type->type_dimension;
-                $$.type_index = this->type->type_index;
-                if (this->type->type_sizeof && strlen(this->type->type_sizeof) != 0)
-                    $$.type_sizeof = this->type->type_sizeof;
-                else
-                    $$.type_sizeof = cat_str(3, mm_strdup("sizeof("), mm_strdup(this->name), mm_strdup(")"));
-
-                struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
-            }
-        }
-        | STRING
-        {
-            /*
-             * It's quite horrid that ECPGColLabelCommon excludes
-             * unreserved_keyword, meaning that unreserved keywords can't be
-             * used as type names in var_type.  However, this is hard to avoid
-             * since what follows ecpgstart can be either a random SQL
-             * statement or an ECPGVarDeclaration (beginning with var_type).
-             * Pending a bright idea about how to fix that, we must
-             * special-case STRING (and any other unreserved keywords that are
-             * likely to be needed here).
-             */
-            if (INFORMIX_MODE)
-            {
-                $$.type_enum = ECPGt_string;
-                $$.type_str = mm_strdup("char");
-                $$.type_dimension = mm_strdup("-1");
-                $$.type_index = mm_strdup("-1");
-                $$.type_sizeof = NULL;
-            }
-            else
-            {
-                /* this is for typedef'ed types */
-                struct typedefs *this = get_typedef("string");
+                /* Otherwise, it must be a user-defined typedef name */
+                struct typedefs *this = get_typedef($1, false);

                 $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY
:mm_strdup(this->name); 
                 $$.type_enum = this->type->type_enum;
@@ -751,7 +818,7 @@ var_type:    simple_type
             {
                 /* No */

-                this = get_typedef(name);
+                this = get_typedef(name, false);
                 $$.type_str = mm_strdup(this->name);
                 $$.type_enum = this->type->type_enum;
                 $$.type_dimension = this->type->type_dimension;
@@ -1657,17 +1724,14 @@ ColLabel:  ECPGColLabel                { $$ = $1; }
         | ECPGunreserved_interval    { $$ = $1; }
         ;

-ECPGColLabel:  ECPGColLabelCommon    { $$ = $1; }
+ECPGColLabel:  ecpg_ident            { $$ = $1; }
         | unreserved_keyword        { $$ = $1; }
-        | reserved_keyword            { $$ = $1; }
-        | ECPGKeywords_rest            { $$ = $1; }
-        | CONNECTION                { $$ = mm_strdup("connection"); }
-        ;
-
-ECPGColLabelCommon:  ecpg_ident        { $$ = $1; }
         | col_name_keyword            { $$ = $1; }
         | type_func_name_keyword    { $$ = $1; }
+        | reserved_keyword            { $$ = $1; }
         | ECPGKeywords_vanames        { $$ = $1; }
+        | ECPGKeywords_rest            { $$ = $1; }
+        | CONNECTION                { $$ = mm_strdup("connection"); }
         ;

 ECPGCKeywords: S_AUTO                { $$ = mm_strdup("auto"); }
diff --git a/src/interfaces/ecpg/preproc/ecpg.type b/src/interfaces/ecpg/preproc/ecpg.type
index d1cde691c0..4fe80a5a4b 100644
--- a/src/interfaces/ecpg/preproc/ecpg.type
+++ b/src/interfaces/ecpg/preproc/ecpg.type
@@ -3,7 +3,6 @@
 %type <str> ECPGCKeywords
 %type <str> ECPGColId
 %type <str> ECPGColLabel
-%type <str> ECPGColLabelCommon
 %type <str> ECPGConnect
 %type <str> ECPGCursorStmt
 %type <str> ECPGDeallocateDescr
diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l
index 996718cb8a..c344f8f30f 100644
--- a/src/interfaces/ecpg/preproc/pgc.l
+++ b/src/interfaces/ecpg/preproc/pgc.l
@@ -983,10 +983,19 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     {
                         int        kwvalue;

-                        /* Is it an SQL/ECPG keyword? */
-                        kwvalue = ScanECPGKeywordLookup(yytext);
-                        if (kwvalue >= 0)
-                            return kwvalue;
+                        /*
+                         * User-defined typedefs override SQL keywords, but
+                         * not C keywords.  Currently, a typedef name is just
+                         * reported as IDENT, but someday we might need to
+                         * return a distinct token type.
+                         */
+                        if (get_typedef(yytext, true) == NULL)
+                        {
+                            /* Is it an SQL/ECPG keyword? */
+                            kwvalue = ScanECPGKeywordLookup(yytext);
+                            if (kwvalue >= 0)
+                                return kwvalue;
+                        }

                         /* Is it a C keyword? */
                         kwvalue = ScanCKeywordLookup(yytext);
diff --git a/src/interfaces/ecpg/preproc/preproc_extern.h b/src/interfaces/ecpg/preproc/preproc_extern.h
index 992797b8bb..6be59b7193 100644
--- a/src/interfaces/ecpg/preproc/preproc_extern.h
+++ b/src/interfaces/ecpg/preproc/preproc_extern.h
@@ -93,7 +93,7 @@ extern void add_variable_to_head(struct arguments **, struct variable *, struct
 extern void add_variable_to_tail(struct arguments **, struct variable *, struct variable *);
 extern void remove_variable_from_list(struct arguments **list, struct variable *var);
 extern void dump_variables(struct arguments *, int);
-extern struct typedefs *get_typedef(char *);
+extern struct typedefs *get_typedef(const char *name, bool noerror);
 extern void adjust_array(enum ECPGttype, char **, char **, char *, char *, int, bool);
 extern void reset_variables(void);
 extern void check_indicator(struct ECPGtype *);
diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c
index 887d479e73..2a2b953118 100644
--- a/src/interfaces/ecpg/preproc/variable.c
+++ b/src/interfaces/ecpg/preproc/variable.c
@@ -497,15 +497,20 @@ check_indicator(struct ECPGtype *var)
 }

 struct typedefs *
-get_typedef(char *name)
+get_typedef(const char *name, bool noerror)
 {
     struct typedefs *this;

-    for (this = types; this && strcmp(this->name, name) != 0; this = this->next);
-    if (!this)
+    for (this = types; this != NULL; this = this->next)
+    {
+        if (strcmp(this->name, name) == 0)
+            return this;
+    }
+
+    if (!noerror)
         mmfatal(PARSE_ERROR, "unrecognized data type name \"%s\"", name);

-    return this;
+    return NULL;
 }

 void
diff --git a/src/interfaces/ecpg/test/expected/preproc-type.c b/src/interfaces/ecpg/test/expected/preproc-type.c
index 1968a87574..9323cb66f8 100644
--- a/src/interfaces/ecpg/test/expected/preproc-type.c
+++ b/src/interfaces/ecpg/test/expected/preproc-type.c
@@ -33,20 +33,26 @@ typedef char  mmChar ;

 #line 7 "type.pgc"

-typedef short  mmSmallInt ;
+typedef short  access ;

 #line 8 "type.pgc"

 #line 8 "type.pgc"
+    /* matches an unreserved SQL keyword */
+typedef access  access_renamed ;
+
+#line 9 "type.pgc"
+
+#line 9 "type.pgc"


 /* exec sql type string is char [ 11 ] */
-#line 10 "type.pgc"
+#line 11 "type.pgc"

 typedef char string[11];

 /* exec sql type c is char reference */
-#line 13 "type.pgc"
+#line 14 "type.pgc"

 typedef char* c;

@@ -58,16 +64,16 @@ typedef char* c;


 struct TBempl {
-#line 19 "type.pgc"
+#line 20 "type.pgc"
  mmInteger idnum ;

-#line 20 "type.pgc"
+#line 21 "type.pgc"
  mmChar name [ 21 ] ;

-#line 21 "type.pgc"
- mmSmallInt accs ;
+#line 22 "type.pgc"
+ access accs ;
  } ;/* exec sql end declare section */
-#line 23 "type.pgc"
+#line 24 "type.pgc"


 int
@@ -77,41 +83,45 @@ main (void)



+






-#line 29 "type.pgc"
+#line 30 "type.pgc"
  struct TBempl empl ;

-#line 30 "type.pgc"
+#line 31 "type.pgc"
  string str ;

-#line 31 "type.pgc"
+#line 32 "type.pgc"
+ access accs_val = 320 ;
+
+#line 33 "type.pgc"
  c ptr = NULL ;

-#line 36 "type.pgc"
+#line 38 "type.pgc"
  struct varchar {
-#line 34 "type.pgc"
+#line 36 "type.pgc"
  int len ;

-#line 35 "type.pgc"
+#line 37 "type.pgc"
  char text [ 10 ] ;
  } vc ;
 /* exec sql end declare section */
-#line 37 "type.pgc"
+#line 39 "type.pgc"


   /* exec sql var vc is [ 10 ] */
-#line 39 "type.pgc"
+#line 41 "type.pgc"

   ECPGdebug (1, stderr);

   empl.idnum = 1;
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 43 "type.pgc"
+#line 45 "type.pgc"

   if (sqlca.sqlcode)
     {
@@ -120,7 +130,7 @@ main (void)
     }

   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table empl ( idnum integer , name char ( 20 ) , accs
smallint, string1 char ( 10 ) , string2 char ( 10 ) , string3 char ( 10 ) )", ECPGt_EOIT, ECPGt_EORT);} 
-#line 51 "type.pgc"
+#line 53 "type.pgc"

   if (sqlca.sqlcode)
     {
@@ -128,8 +138,10 @@ main (void)
       exit (sqlca.sqlcode);
     }

-  { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into empl values ( 1 , 'user name' , 320 , 'first str' ,
'secondstr' , 'third str' )", ECPGt_EOIT, ECPGt_EORT);} 
-#line 58 "type.pgc"
+  { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into empl values ( 1 , 'user name' , $1  , 'first str' ,
'secondstr' , 'third str' )",  
+    ECPGt_short,&(accs_val),(long)1,(long)1,sizeof(short),
+    ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);}
+#line 60 "type.pgc"

   if (sqlca.sqlcode)
     {
@@ -152,7 +164,7 @@ main (void)
     ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
     ECPGt_varchar,&(vc),(long)10,(long)1,sizeof(struct varchar),
     ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
-#line 68 "type.pgc"
+#line 70 "type.pgc"

   if (sqlca.sqlcode)
     {
@@ -162,7 +174,7 @@ main (void)
   printf ("id=%ld name='%s' accs=%d str='%s' ptr='%s' vc='%10.10s'\n", empl.idnum, empl.name, empl.accs, str, ptr,
vc.text);

   { ECPGdisconnect(__LINE__, "CURRENT");}
-#line 76 "type.pgc"
+#line 78 "type.pgc"


   free(ptr);
diff --git a/src/interfaces/ecpg/test/expected/preproc-type.stderr
b/src/interfaces/ecpg/test/expected/preproc-type.stderr
index 678eceff70..85e3094ed5 100644
--- a/src/interfaces/ecpg/test/expected/preproc-type.stderr
+++ b/src/interfaces/ecpg/test/expected/preproc-type.stderr
@@ -2,39 +2,41 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ECPGconnect: opening database ecpg1_regression on <DEFAULT> port <DEFAULT>
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 50: query: create table empl ( idnum integer , name char ( 20 ) , accs smallint ,
string1char ( 10 ) , string2 char ( 10 ) , string3 char ( 10 ) ); with 0 parameter(s) on connection ecpg1_regression 
+[NO_PID]: ecpg_execute on line 52: query: create table empl ( idnum integer , name char ( 20 ) , accs smallint ,
string1char ( 10 ) , string2 char ( 10 ) , string3 char ( 10 ) ); with 0 parameter(s) on connection ecpg1_regression 
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 50: using PQexec
+[NO_PID]: ecpg_execute on line 52: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 50: OK: CREATE TABLE
+[NO_PID]: ecpg_process_output on line 52: OK: CREATE TABLE
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 58: query: insert into empl values ( 1 , 'user name' , 320 , 'first str' , 'second str'
,'third str' ); with 0 parameter(s) on connection ecpg1_regression 
+[NO_PID]: ecpg_execute on line 60: query: insert into empl values ( 1 , 'user name' , $1  , 'first str' , 'second str'
,'third str' ); with 1 parameter(s) on connection ecpg1_regression 
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 58: using PQexec
+[NO_PID]: ecpg_execute on line 60: using PQexecParams
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 58: OK: INSERT 0 1
+[NO_PID]: ecpg_free_params on line 60: parameter 1 = 320
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 65: query: select idnum , name , accs , string1 , string2 , string3 from empl where
idnum= $1 ; with 1 parameter(s) on connection ecpg1_regression 
+[NO_PID]: ecpg_process_output on line 60: OK: INSERT 0 1
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 65: using PQexecParams
+[NO_PID]: ecpg_execute on line 67: query: select idnum , name , accs , string1 , string2 , string3 from empl where
idnum= $1 ; with 1 parameter(s) on connection ecpg1_regression 
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_free_params on line 65: parameter 1 = 1
+[NO_PID]: ecpg_execute on line 67: using PQexecParams
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 65: correctly got 1 tuples with 6 fields
+[NO_PID]: ecpg_free_params on line 67: parameter 1 = 1
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 65: RESULT: 1 offset: -1; array: no
+[NO_PID]: ecpg_process_output on line 67: correctly got 1 tuples with 6 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 65: RESULT: user name            offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 67: RESULT: 1 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 65: RESULT: 320 offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 67: RESULT: user name            offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 65: RESULT: first str  offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 67: RESULT: 320 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_store_result on line 65: allocating memory for 1 tuples
+[NO_PID]: ecpg_get_data on line 67: RESULT: first str  offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 65: RESULT: second str offset: -1; array: no
+[NO_PID]: ecpg_store_result on line 67: allocating memory for 1 tuples
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 65: RESULT: third str  offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 67: RESULT: second str offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 67: RESULT: third str  offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection ecpg1_regression closed
 [NO_PID]: sqlca: code: 0, state: 00000
diff --git a/src/interfaces/ecpg/test/preproc/type.pgc b/src/interfaces/ecpg/test/preproc/type.pgc
index 3200c91dc2..12459ccde8 100644
--- a/src/interfaces/ecpg/test/preproc/type.pgc
+++ b/src/interfaces/ecpg/test/preproc/type.pgc
@@ -5,7 +5,8 @@ EXEC SQL include ../regression;

 EXEC SQL typedef long mmInteger;
 EXEC SQL typedef char mmChar;
-EXEC SQL typedef short mmSmallInt;
+EXEC SQL typedef short access;    /* matches an unreserved SQL keyword */
+EXEC SQL typedef access access_renamed;

 exec sql type string is char[11];
 typedef char string[11];
@@ -18,7 +19,7 @@ struct TBempl
 {
   mmInteger idnum;
   mmChar name[21];
-  mmSmallInt accs;
+  access accs;
 };
 EXEC SQL END DECLARE SECTION;

@@ -28,6 +29,7 @@ main (void)
   EXEC SQL BEGIN DECLARE SECTION;
   struct TBempl empl;
   string str;
+  access accs_val = 320;
   c ptr = NULL;
   struct varchar
   {
@@ -55,7 +57,7 @@ main (void)
       exit (sqlca.sqlcode);
     }

-  EXEC SQL insert into empl values (1, 'user name', 320, 'first str', 'second str', 'third str');
+  EXEC SQL insert into empl values (1, 'user name', :accs_val, 'first str', 'second str', 'third str');
   if (sqlca.sqlcode)
     {
       printf ("insert error = %ld\n", sqlca.sqlcode);

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Nathan Bossart
Дата:
Сообщение: Re: O(n) tasks cause lengthy startups and checkpoints
Следующее
От: Andres Freund
Дата:
Сообщение: Re: PSA: Autoconf has risen from the dead