<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
<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>
$$.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;
$$.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;
}
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;
{
/* 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;
| 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"); }
%type <str> ECPGCKeywords
%type <str> ECPGColId
%type <str> ECPGColLabel
-%type <str> ECPGColLabelCommon
%type <str> ECPGConnect
%type <str> ECPGCursorStmt
%type <str> ECPGDeallocateDescr
{
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);
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 *);
}
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
#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;
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
+
-#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)
{
}
{ 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)
{
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)
{
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)
{
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);
[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
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];
{
mmInteger idnum;
mmChar name[21];
- mmSmallInt accs;
+ access accs;
};
EXEC SQL END DECLARE SECTION;
EXEC SQL BEGIN DECLARE SECTION;
struct TBempl empl;
string str;
+ access accs_val = 320;
c ptr = NULL;
struct varchar
{
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);