Fix ECPG's handling of type names that match SQL keywords.
Previously, ECPG could only cope with variable declarations whose type names either weren't any SQL keyword, or were at least partially reserved. If you tried to use something in the unreserved_keyword category, you got a syntax error. This is pretty awful, not only because it says right on the tin that those words are not reserved, but because the set of such keywords tends to grow over time. Thus, an ECPG program that was just fine last year could fail when recompiled with a newer SQL grammar. We had to work around this recently when STRING became a keyword, but it's time for an actual fix instead of a band-aid. To fix, borrow a trick from C parsers and make the lexer's behavior change when it sees a word that is known as a typedef. This is not free of downsides: if you try to use such a name as a SQL keyword in EXEC SQL later in the program, it won't be recognized as a SQL keyword, leading to a syntax error there instead. So in a real sense this is just trading one hazard for another. But there is an important difference: with this, whether your ECPG program works depends only on what typedef names and SQL commands are used in the program text. If it compiles today it'll still compile next year, even if more words have become SQL keywords. Discussion: https://postgr.es/m/3661437.1653855582@sss.pgh.pa.us
This commit is contained in:
parent
e64cdab003
commit
83f1c7b742
@ -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,41 @@ 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.
|
||||
(If you have such a conflict, and renaming the typedef
|
||||
seems impractical, you could write the SQL command
|
||||
using <link linkend="ecpg-dynamic">dynamic SQL</link>.)
|
||||
</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>
|
||||
|
@ -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
|
||||
{
|
||||
$$.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
|
||||
{
|
||||
/*
|
||||
* 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");
|
||||
|
||||
/*
|
||||
* Check for type names that the SQL grammar treats as
|
||||
* unreserved keywords
|
||||
*/
|
||||
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"); }
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 *);
|
||||
|
@ -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
|
||||
|
@ -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' , 'second str' , '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' , 'second str' , '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);
|
||||
|
@ -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 , string1 char ( 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 , string1 char ( 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
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user