Commit bf6636ba authored by Marc G. Fournier's avatar Marc G. Fournier

From: Michael Meskes <Michael_Meskes@topmail.de>

See Changes file...
parent 3eb22085
......@@ -400,3 +400,60 @@ Tue Feb 2 07:40:52 CET 1999
- Brought preproc.y in sync again with gram.y.
- Set ecpg version to 2.4.9
Wed Feb 3 18:28:46 CET 1999
- Started working on PREPARE statement.
- Fixed typo in preproc that cause CREATE statement to not work
anymore.
Thu Feb 4 19:43:39 CET 1999
- Some parts of the PREPARE statement work now.
- Added EXECUTE command
- Added DEALLOCATE PREPARE command
Fri Feb 5 18:25:07 CET 1999
- PREPARE seems to be working okay now.
- Fixed some minor bugs.
- Renamed y.tab.* to preproc.*
Mon Feb 8 07:57:29 CET 1999
- Synced preproc.y with gram.y again.
- Allow ':<name>' as positional variable in prepare statement also.
You can still specify ';;' instead of course.
- Added TYPE statement.
- Set library version to 2.7.0
Tue Feb 9 07:07:11 CET 1999
- Synced preproc.y with gram.y.
Tue Feb 9 20:21:44 CET 1999
- Added FREE statement.
Wed Feb 10 07:51:09 CET 1999
- Synced keyword.c.
Sat Feb 13 10:44:43 CET 1999
- Added DECLARE STATEMENT for compatibility with Oracle. De facto
this statement does nothing.
- Added VAR statement.
Son Feb 14 11:36:04 CET 1999
- Added type 'enum' to TYPE and VAR statement.
- Allow ecpg keywords as datatypes.
Thu Feb 18 08:35:35 CET 1999
- Make sure indicator for array is array too.
Fri Feb 19 18:38:45 CET 1999
- Finished type aliasing for structures.
- Set ecpg version to 2.5.0
......@@ -11,10 +11,10 @@ DESCRIPTOR statement will be ignored.
it would be nice to be able to use :var[:index] as cvariable
'at DB connection' is missing for several commands (is this standard?)
support for unions
Missing statements:
- exec sql type
- exec sql prepare
- exec sql allocate
- exqc sql free
- SQLSTATE
- exec sql whenever sqlwarning
......@@ -28,6 +28,8 @@
#define ECPG_NO_CONN -220
#define ECPG_NOT_CONN -221
#define ECPG_INVALID_STMT -230
/* finally the backend error messages, they start at 400 */
#define ECPG_PGSQL -400
#define ECPG_TRANS -401
......
......@@ -11,6 +11,9 @@ extern "C"
bool ECPGdo(int, char *,...);
bool ECPGtrans(int, const char *);
bool ECPGdisconnect(int, const char *);
bool ECPGprepare(int, char *, char *);
bool ECPGdeallocate(int, char *);
char *ECPGprepared_statement(char *);
void ECPGlog(const char *format,...);
......
......@@ -45,7 +45,8 @@ extern "C"
ECPGt_struct,
ECPGt_EOIT, /* End of insert types. */
ECPGt_EORT, /* End of result types. */
ECPGt_NO_INDICATOR /* no indicator */
ECPGt_NO_INDICATOR, /* no indicator */
ECPGt_char_variable
};
#define IS_SIMPLE_TYPE(type) ((type) >= ECPGt_char && (type) <= ECPGt_varchar2)
......
......@@ -6,13 +6,13 @@
# Copyright (c) 1994, Regents of the University of California
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/Makefile.in,v 1.40 1999/01/21 20:01:32 scrappy Exp $
# $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/Makefile.in,v 1.41 1999/02/20 07:00:53 scrappy Exp $
#
#-------------------------------------------------------------------------
NAME= ecpg
SO_MAJOR_VERSION= 2
SO_MINOR_VERSION= 6.3
SO_MINOR_VERSION= 7.0
SRCDIR= @top_srcdir@
include $(SRCDIR)/Makefile.global
......
......@@ -81,6 +81,13 @@ struct statement
struct variable *outlist;
};
struct prepared_statement
{
char *name;
struct statement *stmt;
struct prepared_statement *next;
} *prep_stmts = NULL;
static int simple_debug = 0;
static FILE *debugstream = NULL;
static int committed = true;
......@@ -196,6 +203,41 @@ quote_postgres(char *arg, int lineno)
return res;
}
/* This function returns a newly malloced string that has the \
in the strings inside the argument quoted with another \.
*/
static
char *
quote_strings(char *arg, int lineno)
{
char *res = (char *) ecpg_alloc(2 * strlen(arg) + 1, lineno);
int i,
ri;
bool string = false;
if (!res)
return (res);
for (i = 0, ri = 0; arg[i]; i++, ri++)
{
switch (arg[i])
{
case '\'':
string = string ? false : true;
break;
case '\\':
res[ri++] = '\\';
default:
;
}
res[ri] = arg[i];
}
res[ri] = '\0';
return res;
}
/* create a list of variables */
static bool
create_statement(int lineno, struct statement ** stmt, char *query, va_list ap)
......@@ -237,6 +279,14 @@ create_statement(int lineno, struct statement ** stmt, char *query, va_list ap)
var->ind_offset = va_arg(ap, long);
var->next = NULL;
if (var->value == NULL)
{
ECPGlog("create_statement: invalid statement name\n");
register_error(ECPG_INVALID_STMT, "Invalid statement name in line %d", lineno);
free(var);
return false;
}
for (ptr = *list; ptr && ptr->next; ptr = ptr->next);
if (ptr == NULL)
......@@ -251,6 +301,19 @@ create_statement(int lineno, struct statement ** stmt, char *query, va_list ap)
return (true);
}
static char *
next_insert(char *text)
{
char *ptr = text;
bool string = false;
for (; ptr[1] != '\0' && (ptr[0] != ';' || ptr[1] != ';' || string); ptr++)
if (ptr[0] == '\'')
string = string ? false : true;
return (ptr[1] == '\0') ? NULL : ptr;
}
static bool
ECPGexecute(struct statement * stmt)
{
......@@ -379,7 +442,30 @@ ECPGexecute(struct statement * stmt)
tobeinserted = mallocedval;
}
break;
case ECPGt_char_variable:
{
/* set slen to string length if type is char * */
int slen = (var->varcharsize == 0) ? strlen((char *) var->value) : var->varcharsize;
char *tmp;
if (!(newcopy = ecpg_alloc(slen + 1, stmt->lineno)))
return false;
strncpy(newcopy, (char *) var->value, slen);
newcopy[slen] = '\0';
if (!(mallocedval = (char *) ecpg_alloc(2 * strlen(newcopy) + 1, stmt->lineno)))
return false;
tmp = quote_strings(newcopy, stmt->lineno);
if (!tmp)
return false;
strcat(mallocedval, tmp);
free(newcopy);
tobeinserted = mallocedval;
}
break;
case ECPGt_varchar:
{
struct ECPGgeneric_varchar *variable =
......@@ -428,7 +514,7 @@ ECPGexecute(struct statement * stmt)
return false;
strcpy(newcopy, copiedquery);
if ((p = strstr(newcopy, ";;")) == NULL)
if ((p = next_insert(newcopy)) == NULL)
{
/*
......@@ -449,7 +535,7 @@ ECPGexecute(struct statement * stmt)
strcat(newcopy,
copiedquery
+ (p - newcopy)
+ 2 /* Length of ;; */ );
+ sizeof(";;") - 1 /* don't count the '\0' */);
}
/*
......@@ -470,7 +556,7 @@ ECPGexecute(struct statement * stmt)
}
/* Check if there are unmatched things left. */
if (strstr(copiedquery, ";;") != NULL)
if (next_insert(copiedquery) != NULL)
{
register_error(ECPG_TOO_FEW_ARGUMENTS, "Too few arguments line %d.", stmt->lineno);
return false;
......@@ -898,7 +984,21 @@ ECPGtrans(int lineno, const char *transaction)
PQclear(res);
}
if (strcmp(transaction, "commit") == 0 || strcmp(transaction, "rollback") == 0)
{
struct prepared_statement *this;
committed = 1;
/* deallocate all prepared statements */
for (this = prep_stmts; this != NULL; this = this->next)
{
bool b = ECPGdeallocate(lineno, this->name);
if (!b)
return false;
}
}
return TRUE;
}
......@@ -1033,3 +1133,109 @@ sqlprint(void)
sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0';
printf("sql error %s\n", sqlca.sqlerrm.sqlerrmc);
}
static void
replace_variables(char *text)
{
char *ptr = text;
bool string = false;
for (; *ptr != '\0'; ptr++)
{
if (*ptr == '\'')
string = string ? false : true;
if (!string && *ptr == ':')
{
ptr[0] = ptr[1] = ';';
for (ptr += 2; *ptr && *ptr != ' '; ptr++)
*ptr = ' ';
}
}
}
/* handle the EXEC SQL PREPARE statement */
bool
ECPGprepare(int lineno, char *name, char *variable)
{
struct statement *stmt;
struct prepared_statement *this;
/* check if we already have prepared this statement */
for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next);
if (this)
{
bool b = ECPGdeallocate(lineno, name);
if (!b)
return false;
}
this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
if (!this)
return false;
stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
if (!stmt)
{
free(this);
return false;
}
/* create statement */
stmt->lineno = lineno;
stmt->command = ecpg_strdup(variable, lineno);
stmt->inlist = stmt->outlist = NULL;
/* if we have C variables in our statment replace them with ';;' */
replace_variables(stmt->command);
/* add prepared statement to our list */
this->name = ecpg_strdup(name, lineno);
this->stmt = stmt;
if (prep_stmts == NULL)
this->next = NULL;
else
this->next = prep_stmts;
prep_stmts = this;
return true;
}
/* handle the EXEC SQL DEALLOCATE PREPARE statement */
bool
ECPGdeallocate(int lineno, char *name)
{
struct prepared_statement *this, *prev;
/* check if we really have prepared this statement */
for (this = prep_stmts, prev = NULL; this != NULL && strcmp(this->name, name) != 0; prev = this, this = this->next);
if (this)
{
/* okay, free all the resources */
free(this->name);
free(this->stmt->command);
free(this->stmt);
if (prev != NULL)
prev->next = this->next;
else
prep_stmts = this->next;
return true;
}
ECPGlog("deallocate_prepare: invalid statement name %s\n", name);
register_error(ECPG_INVALID_STMT, "Invalid statement name %s in line %d", name, lineno);
return false;
}
/* return the prepared statement */
char *
ECPGprepared_statement(char *name)
{
struct prepared_statement *this;
for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next);
return (this) ? this->stmt->command : NULL;
}
......@@ -32,6 +32,8 @@ ECPGtype_name(enum ECPGttype typ)
return "bool";
case ECPGt_varchar:
return "varchar";
case ECPGt_char_variable:
return "char";
default:
abort();
}
......
......@@ -2,20 +2,25 @@ SRCDIR= ../../..
include $(SRCDIR)/Makefile.global
MAJOR_VERSION=2
MINOR_VERSION=4
PATCHLEVEL=9
MINOR_VERSION=5
PATCHLEVEL=0
CFLAGS+=-I../include -DMAJOR_VERSION=$(MAJOR_VERSION) \
-DMINOR_VERSION=$(MINOR_VERSION) -DPATCHLEVEL=$(PATCHLEVEL) \
-DINCLUDE_PATH=\"$(DESTDIR)$(HEADERDIR)\"
OBJ=y.tab.o pgc.o type.o ecpg.o ecpg_keywords.o ../../../backend/parser/scansup.o \
OBJ=preproc.o pgc.o type.o ecpg.o ecpg_keywords.o ../../../backend/parser/scansup.o \
keywords.o c_keywords.o ../lib/typename.o
all:: ecpg
preproc.c preproc.h: preproc.y
$(YACC) $(YFLAGS) $<
mv y.tab.c preproc.c
mv y.tab.h preproc.h
clean:
rm -f *.o core a.out ecpg$(X) y.tab.h y.tab.c pgc.c *~
rm -f *.o core a.out ecpg$(X) *~
install: all
$(INSTALL) $(INSTL_EXE_OPTS) ecpg$(X) $(DESTDIR)$(BINDIR)
......@@ -31,13 +36,10 @@ pgc.c: pgc.l
$(LEX) $<
mv lex.yy.c pgc.c
y.tab.h y.tab.c: preproc.y
$(YACC) $(YFLAGS) $<
y.tab.o : y.tab.h ../include/ecpgtype.h keywords.c c_keywords.c ecpg_keywords.c
preproc.o : preproc.h ../include/ecpgtype.h keywords.c c_keywords.c ecpg_keywords.c
type.o : ../include/ecpgtype.h
pgc.o : ../include/ecpgtype.h keywords.c c_keywords.c ecpg_keywords.c y.tab.h
keywords.o: ../include/ecpgtype.h y.tab.h
c_keywords.o: ../include/ecpgtype.h y.tab.h
ecpg_keywords.o: ../include/ecpgtype.h y.tab.h
pgc.o : ../include/ecpgtype.h keywords.c c_keywords.c ecpg_keywords.c preproc.h
keywords.o: ../include/ecpgtype.h preproc.h
c_keywords.o: ../include/ecpgtype.h preproc.h
ecpg_keywords.o: ../include/ecpgtype.h preproc.h
......@@ -9,9 +9,8 @@
#include <string.h>
#include "postgres.h"
#include "type.h"
#include "y.tab.h"
#include "extern.h"
#include "preproc.h"
/*
* List of (keyword-name, keyword-token-value) pairs.
......
......@@ -25,6 +25,7 @@ extern char *optarg;
struct _include_path *include_paths;
int no_auto_trans = 0;
struct cursor *cur = NULL;
struct typedefs *types = NULL;
static void
usage(char *progname)
......@@ -155,22 +156,22 @@ main(int argc, char *const argv[])
{
struct cursor *ptr;
struct _defines *defptr;
struct typedefs *typeptr;
/* remove old cursor definitions if any are still there */
for (ptr = cur; ptr != NULL;)
{
struct cursor *this = ptr;
struct arguments *l1,
*l2;
struct arguments *l1, *l2;
free(ptr->command);
free(ptr->name);
for (l1 = argsinsert; l1; l1 = l2)
for (l1 = ptr->argsinsert; l1; l1 = l2)
{
l2 = l1->next;
free(l1);
}
for (l1 = argsresult; l1; l1 = l2)
for (l1 = ptr->argsresult; l1; l1 = l2)
{
l2 = l1->next;
free(l1);
......@@ -190,6 +191,18 @@ main(int argc, char *const argv[])
free(this);
}
/* and old typedefs */
for (typeptr = types; typeptr != NULL;)
{
struct typedefs *this = typeptr;
free(typeptr->name);
free(typeptr->type);
ECPGfree_struct_member(typeptr->struct_member_list);
typeptr = typeptr->next;
free(this);
}
/* initialize lex */
lex_init();
......
......@@ -9,9 +9,8 @@
#include <string.h>
#include "postgres.h"
#include "type.h"
#include "extern.h"
#include "y.tab.h"
#include "preproc.h"
/*
* List of (keyword-name, keyword-token-value) pairs.
......@@ -21,25 +20,38 @@
*/
static ScanKeyword ScanKeywords[] = {
/* name value */
{"bool", SQL_BOOL},
{"break", SQL_BREAK},
{"call", SQL_CALL},
{"connect", SQL_CONNECT},
{"connection", SQL_CONNECTION},
{"continue", SQL_CONTINUE},
{"deallocate", SQL_DEALLOCATE},
{"disconnect", SQL_DISCONNECT},
{"enum", SQL_ENUM},
{"found", SQL_FOUND},
{"free", SQL_FREE},
{"go", SQL_GO},
{"goto", SQL_GOTO},
{"identified", SQL_IDENTIFIED},
{"immediate", SQL_IMMEDIATE},
{"indicator", SQL_INDICATOR},
{"int", SQL_INT},
{"long", SQL_LONG},
{"open", SQL_OPEN},
{"prepare", SQL_PREPARE},
{"reference", SQL_REFERENCE},
{"release", SQL_RELEASE},
{"section", SQL_SECTION},
{"short", SQL_SHORT},
{"signed", SQL_SIGNED},
{"sqlerror", SQL_SQLERROR},
{"sqlprint", SQL_SQLPRINT},
{"sqlwarning", SQL_SQLWARNING},
{"stop", SQL_STOP},
{"struct", SQL_STRUCT},
{"unsigned", SQL_UNSIGNED},
{"var", SQL_VAR},
{"whenever", SQL_WHENEVER},
};
......
#include "parser/keywords.h"
#include "type.h"
#include <errno.h>
/* variables */
extern int braces_open,
no_auto_trans;
no_auto_trans, struct_level;
extern char *yytext;
extern int yylineno,
yyleng;
extern FILE *yyin,
*yyout;
struct _include_path
{
char *path;
struct _include_path *next;
};
extern struct _include_path *include_paths;
struct cursor
{
char *name;
char *command;
struct arguments *argsinsert;
struct arguments *argsresult;
struct cursor *next;
};
extern struct cursor *cur;
struct _defines
{
char *old;
char *new;
struct _defines *next;
};
extern struct typedefs *types;
extern struct _defines *defines;
/* This is a linked list of the variable names and types. */
struct variable
{
char *name;
struct ECPGtype *type;
int brace_level;
struct variable *next;
};
extern struct ECPGtype ecpg_no_indicator;
extern struct variable no_indicator;
struct arguments
{
struct variable *variable;
struct variable *indicator;
struct arguments *next;
};
extern struct arguments *argsinsert;
extern struct arguments *argsresult;
......@@ -77,6 +37,7 @@ extern void yyerror(char *);
#define OK 0
#define PARSE_ERROR -1
#define ILLEGAL_OPTION -2
#define INDICATOR_NOT_ARRAY -3
#define NO_INCLUDE_FILE ENOENT
#define OUT_OF_MEMORY ENOMEM
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.10 1999/02/13 23:22:35 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.11 1999/02/20 07:01:00 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -18,7 +18,7 @@
#include "nodes/parsenodes.h"
#include "nodes/pg_list.h"
#include "type.h"
#include "y.tab.h"
#include "preproc.h"
#include "parser/keywords.h"
#include "utils/elog.h"
......@@ -69,9 +69,6 @@ static ScanKeyword ScanKeywords[] = {
{"createdb", CREATEDB},
{"createuser", CREATEUSER},
{"cross", CROSS},
{"current", CURRENT}, /* 6.4 to 6.5 is migration time! CURRENT
* will be removed in 6.5! Use OLD keyword
* in rules. Jan */
{"current_date", CURRENT_DATE},
{"current_time", CURRENT_TIME},
{"current_timestamp", CURRENT_TIMESTAMP},
......@@ -96,6 +93,7 @@ static ScanKeyword ScanKeywords[] = {
{"end", END_TRANS},
/***S*I***/
{"except", EXCEPT},
{"execute", EXECUTE},
{"exists", EXISTS},
{"explain", EXPLAIN},
......@@ -125,6 +123,7 @@ static ScanKeyword ScanKeywords[] = {
{"instead", INSTEAD},
/***S*I***/
{"intersect", INTERSECT},
{"interval", INTERVAL},
{"into", INTO},
{"is", IS},
......@@ -138,6 +137,7 @@ static ScanKeyword ScanKeywords[] = {
{"left", LEFT},
{"level", LEVEL},
{"like", LIKE},
{"limit", LIMIT},
{"listen", LISTEN},
{"load", LOAD},
{"local", LOCAL},
......@@ -167,6 +167,7 @@ static ScanKeyword ScanKeywords[] = {
{"nullif", NULLIF},
{"numeric", NUMERIC},
{"of", OF},
{"offset", OFFSET},
{"oids", OIDS},
{"old", CURRENT},
{"on", ON},
......
......@@ -24,9 +24,8 @@
#include "nodes/parsenodes.h"
#include "parser/gramparse.h"
#include "parser/scansup.h"
#include "type.h"
#include "extern.h"
#include "y.tab.h"
#include "preproc.h"
#include "utils/builtins.h"
/* some versions of lex define this as a macro */
......@@ -241,7 +240,7 @@ cppline {space}*#.*(\\{space}*\n)*\n*
}
<xq>{xqstop} {
BEGIN(SQL);
yylval.str = strdup(scanstr(literal));
yylval.str = mm_strdup(scanstr(literal));
return SCONST;
}
<xq>{xqdouble} |
......@@ -276,7 +275,7 @@ cppline {space}*#.*(\\{space}*\n)*\n*
}
<xd>{xdstop} {
BEGIN(SQL);
yylval.str = strdup(literal);
yylval.str = mm_strdup(literal);
return CSTRING;
}
<xd>{xdinside} {
......@@ -292,7 +291,7 @@ cppline {space}*#.*(\\{space}*\n)*\n*
}
<xdc>{xdstop} {
BEGIN(C);
yylval.str = strdup(literal);
yylval.str = mm_strdup(literal);
return CSTRING;
}
<xdc>{xdinside} {
......@@ -316,14 +315,14 @@ cppline {space}*#.*(\\{space}*\n)*\n*
}
<SQL>{self} { return yytext[0]; }
<SQL>{operator}/-[\.0-9] {
yylval.str = strdup((char*)yytext);
yylval.str = mm_strdup((char*)yytext);
return Op;
}
<SQL>{operator} {
if (strcmp((char*)yytext,"!=") == 0)
yylval.str = strdup("<>"); /* compatability */
yylval.str = mm_strdup("<>"); /* compatability */
else
yylval.str = strdup((char*)yytext);
yylval.str = mm_strdup((char*)yytext);
return Op;
}
<SQL>{param} {
......@@ -342,7 +341,6 @@ cppline {space}*#.*(\\{space}*\n)*\n*
if (isascii((unsigned char)lower_text[i]) && isupper(lower_text[i]))
lower_text[i] = tolower(lower_text[i]);
printf("yyt= %s, lt = %s\n", yytext, lower_text);
keyword = ScanKeywordLookup((char*)lower_text);
if (keyword != NULL) {
return keyword->value;
......@@ -367,7 +365,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text);
yb->buffer = YY_CURRENT_BUFFER;
yb->lineno = yylineno;
yb->filename = strdup(input_filename);
yb->filename = mm_strdup(input_filename);
yb->next = yy_buffer;
yy_buffer = yb;
......@@ -378,7 +376,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text);
}
if (ptr == NULL)
{
yylval.str = strdup((char*)yytext);
yylval.str = mm_strdup((char*)yytext);
return IDENT;
}
}
......@@ -470,7 +468,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text);
return ICONST;
}
<SQL>:{identifier}(("->"|\.){identifier})* {
yylval.str = strdup((char*)yytext+1);
yylval.str = mm_strdup((char*)yytext+1);
return(CVARIABLE);
}
<SQL>{identifier} {
......@@ -484,7 +482,6 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text);
if (isascii((unsigned char)lower_text[i]) && isupper(lower_text[i]))
lower_text[i] = tolower(lower_text[i]);
printf("yyt= %s, lt = %s\n", yytext, lower_text);
keyword = ScanKeywordLookup((char*)lower_text);
if (keyword != NULL) {
return keyword->value;
......@@ -509,7 +506,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text);
yb->buffer = YY_CURRENT_BUFFER;
yb->lineno = yylineno;
yb->filename = strdup(input_filename);
yb->filename = mm_strdup(input_filename);
yb->next = yy_buffer;
yy_buffer = yb;
......@@ -520,19 +517,26 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text);
}
if (ptr == NULL)
{
yylval.str = strdup((char*)yytext);
yylval.str = mm_strdup((char*)yytext);
return IDENT;
}
}
}
}
<SQL>{space} { /* ignore */ }
<SQL>";" { BEGIN C; return SQL_SEMI; }
<SQL>";" { /*
* We may find a ';' inside a structure
* definition in a TYPE or VAR statement.
* This is not a EOL marker.
*/
if (struct_level == 0)
BEGIN C;
return SQL_SEMI; }
<SQL>{other} { return yytext[0]; }
<C>{exec}{space}{sql} { BEGIN SQL; return SQL_START; }
<C>{ccomment} { /* ignore */ }
<C>{cppline} {
yylval.str = strdup((char*)yytext);
yylval.str = mm_strdup((char*)yytext);
return(CPP_LINE);
}
<C>{identifier} {
......@@ -556,7 +560,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text);
yb->buffer = YY_CURRENT_BUFFER;
yb->lineno = yylineno;
yb->filename = strdup(input_filename);
yb->filename = mm_strdup(input_filename);
yb->next = yy_buffer;
yy_buffer = yb;
......@@ -567,7 +571,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text);
}
if (ptr == NULL)
{
yylval.str = strdup((char*)yytext);
yylval.str = mm_strdup((char*)yytext);
return IDENT;
}
}
......@@ -585,7 +589,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text);
<C>{exec}{space}{sql}{space}{define} {BEGIN(def_ident);}
<def_ident>{space} {}
<def_ident>{identifier} {
old = strdup(yytext);
old = mm_strdup(yytext);
BEGIN(def);
llen = 0;
*literal = '\0';
......@@ -599,7 +603,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text);
if (strcmp(old, ptr->old) == 0)
{
free(ptr->new);
ptr->new = strdup(scanstr(literal));
ptr->new = mm_strdup(scanstr(literal));
}
}
if (ptr == NULL)
......@@ -608,7 +612,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text);
/* initial definition */
this->old = old;
this->new = strdup(scanstr(literal));
this->new = mm_strdup(scanstr(literal));
this->next = defines;
defines = this;
}
......@@ -666,7 +670,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text);
exit(NO_INCLUDE_FILE);
}
input_filename = strdup(inc_file);
input_filename = mm_strdup(inc_file);
yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE ));
yylineno = 0;
......
......@@ -6,7 +6,6 @@
#include "catalog/catname.h"
#include "utils/numeric.h"
#include "type.h"
#include "extern.h"
#ifdef MULTIBYTE
......@@ -18,10 +17,10 @@
/*
* Variables containing simple states.
*/
static int struct_level = 0;
int struct_level = 0;
static char errortext[128];
static int QueryIsRule = 0, ForUpdateNotAllowed = 0;
static enum ECPGttype actual_type[STRUCT_DEPTH];
static struct this_type actual_type[STRUCT_DEPTH];
static char *actual_storage[STRUCT_DEPTH];
/* temporarily store struct members while creating the data structure */
......@@ -30,6 +29,8 @@ struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = { NULL };
struct ECPGtype ecpg_no_indicator = {ECPGt_NO_INDICATOR, 0L, {NULL}};
struct variable no_indicator = {"no_indicator", &ecpg_no_indicator, 0, NULL};
struct ECPGtype ecpg_query = {ECPGt_char_variable, 0L, {NULL}};
/*
* Handle the filename and line numbering.
*/
......@@ -505,6 +506,98 @@ output_statement(char * stmt, int mode)
free(stmt);
}
static struct typedefs *
get_typedef(char *name)
{
struct typedefs *this;
for (this = types; this && strcmp(this->name, name); this = this->next);
if (!this)
{
sprintf(errortext, "invalid datatype '%s'", name);
yyerror(errortext);
}
return(this);
}
static void
adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dimension, int type_index, bool pointer)
{
if (type_index >= 0)
{
if (*length >= 0)
yyerror("No multi-dimensional array support");
*length = type_index;
}
if (type_dimension >= 0)
{
if (*dimension >= 0 && *length >= 0)
yyerror("No multi-dimensional array support");
if (*dimension >= 0)
*length = *dimension;
*dimension = type_dimension;
}
switch (type_enum)
{
case ECPGt_struct:
/* pointer has to get dimension 0 */
if (pointer)
{
*length = *dimension;
*dimension = 0;
}
if (*length >= 0)
yyerror("No multi-dimensional array support for structures");
break;
case ECPGt_varchar:
/* pointer has to get length 0 */
if (pointer)
*length=0;
/* one index is the string length */
if (*length < 0)
{
*length = *dimension;
*dimension = -1;
}
break;
case ECPGt_char:
case ECPGt_unsigned_char:
/* pointer has to get length 0 */
if (pointer)
*length=0;
/* one index is the string length */
if (*length < 0)
{
*length = (*dimension < 0) ? 1 : *dimension;
*dimension = -1;
}
break;
default:
/* a pointer has dimension = 0 */
if (pointer) {
*length = *dimension;
*dimension = 0;
}
if (*length >= 0)
yyerror("No multi-dimensional array support for simple data types");
break;
}
}
%}
%union {
......@@ -519,12 +612,15 @@ output_statement(char * stmt, int mode)
}
/* special embedded SQL token */
%token SQL_BREAK SQL_CALL SQL_CONNECT SQL_CONNECTION SQL_CONTINUE
%token SQL_DISCONNECT SQL_FOUND SQL_GO SQL_GOTO
%token SQL_IDENTIFIED SQL_IMMEDIATE SQL_INDICATOR SQL_OPEN
%token SQL_PREPARE SQL_RELEASE
%token SQL_SECTION SQL_SEMI SQL_SQLERROR SQL_SQLPRINT SQL_START
%token SQL_STOP SQL_WHENEVER SQL_SQLWARNING
%token SQL_BOOL SQL_BREAK
%token SQL_CALL SQL_CONNECT SQL_CONNECTION SQL_CONTINUE
%token SQL_DEALLOCATE SQL_DISCONNECT SQL_ENUM
%token SQL_FOUND SQL_FREE SQL_GO SQL_GOTO
%token SQL_IDENTIFIED SQL_IMMEDIATE SQL_INDICATOR SQL_INT SQL_LONG
%token SQL_OPEN SQL_PREPARE SQL_RELEASE SQL_REFERENCE
%token SQL_SECTION SQL_SEMI SQL_SHORT SQL_SIGNED SQL_SQLERROR SQL_SQLPRINT
%token SQL_SQLWARNING SQL_START SQL_STOP SQL_STRUCT SQL_UNSIGNED
%token SQL_VAR SQL_WHENEVER
/* C token */
%token S_ANYTHING S_AUTO S_BOOL S_CHAR S_CONST S_DOUBLE S_ENUM S_EXTERN
......@@ -577,9 +673,9 @@ output_statement(char * stmt, int mode)
DATABASE, DELIMITERS, DO, EACH, ENCODING, EXPLAIN, EXTEND,
FORWARD, FUNCTION, HANDLER,
INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
LANCOMPILER, LISTEN, UNLISTEN, LOAD, LOCATION, LOCK_P, MAXVALUE, MINVALUE, MOVE,
LANCOMPILER, LIMIT, LISTEN, UNLISTEN, LOAD, LOCATION, LOCK_P, MAXVALUE, MINVALUE, MOVE,
NEW, NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL,
OIDS, OPERATOR, PASSWORD, PROCEDURAL,
OFFSET, OIDS, OPERATOR, PASSWORD, PROCEDURAL,
RECIPE, RENAME, RESET, RETURNS, ROW, RULE,
SERIAL, SEQUENCE, SETOF, SHOW, START, STATEMENT, STDIN, STDOUT, TRUSTED,
UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
......@@ -654,8 +750,9 @@ output_statement(char * stmt, int mode)
%type <str> index_opt_unique IndexStmt set_opt func_return def_rest
%type <str> func_args_list func_args opt_with ProcedureStmt def_arg
%type <str> def_elem def_list definition def_name def_type DefineStmt
%type <str> opt_instead event event_object OptStmtMulti OptStmtBlock
%type <str> OptStmtList RuleStmt opt_column opt_name oper_argtypes
%type <str> opt_instead event event_object RuleActionList,
%type <str> RuleActionBlock RuleActionMulti
%type <str> RuleStmt opt_column opt_name oper_argtypes
%type <str> MathOp RemoveFuncStmt aggr_argtype for_update_clause
%type <str> RemoveAggrStmt remove_type RemoveStmt ExtendStmt RecipeStmt
%type <str> RemoveOperStmt RenameStmt all_Op user_valid_clause
......@@ -665,7 +762,7 @@ output_statement(char * stmt, int mode)
%type <str> user_createuser_clause user_group_list user_group_clause
%type <str> CreateUserStmt AlterUserStmt CreateSeqStmt OptSeqList
%type <str> OptSeqElem TriggerForSpec TriggerForOpt TriggerForType
%type <str> DropTrigStmt TriggerOneEvent TriggerEvents
%type <str> DropTrigStmt TriggerOneEvent TriggerEvents RuleActionStmt
%type <str> TriggerActionTime CreateTrigStmt DropPLangStmt PLangTrusted
%type <str> CreatePLangStmt IntegerOnly TriggerFuncArgs TriggerFuncArg
%type <str> ViewStmt LoadStmt CreatedbStmt opt_database1 opt_database2 location
......@@ -673,27 +770,32 @@ output_statement(char * stmt, int mode)
%type <str> GrantStmt privileges operation_commalist operation
%type <str> cursor_clause opt_cursor opt_readonly opt_of opt_lmode
%type <str> case_expr when_clause_list case_default case_arg when_clause
%type <str> select_w_o_sort
%type <str> select_w_o_sort opt_select_limit select_limit_value,
%type <str> select_offset_value
%type <str> ECPGWhenever ECPGConnect connection_target ECPGOpen open_opts
%type <str> indicator ECPGExecute ecpg_expr dotext
%type <str> ECPGWhenever ECPGConnect connection_target ECPGOpen opt_using
%type <str> indicator ECPGExecute ecpg_expr dotext ECPGPrepare
%type <str> storage_clause opt_initializer vartext c_anything blockstart
%type <str> blockend variable_list variable var_anything do_anything
%type <str> opt_pointer cvariable ECPGDisconnect dis_name
%type <str> stmt symbol opt_symbol ECPGRelease execstring server_name
%type <str> connection_object opt_server opt_port c_thing
%type <str> connection_object opt_server opt_port c_thing opt_reference
%type <str> user_name opt_user char_variable ora_user ident
%type <str> db_prefix server opt_options opt_connection_name
%type <str> ECPGSetConnection c_line cpp_line s_enum
%type <str> enum_type
%type <str> ECPGSetConnection c_line cpp_line s_enum ECPGTypedef
%type <str> enum_type civariableonly ECPGCursorStmt ECPGDeallocate
%type <str> ECPGFree ECPGDeclare ECPGVar sql_variable_declarations
%type <str> sql_declaration sql_variable_list sql_variable
%type <str> struct_type s_struct declaration variable_declarations
%type <type_enum> simple_type
%type <type_enum> simple_type varchar_type
%type <type> type
%type <type> type ctype
%type <action> action
%type <index> opt_array_bounds nest_array_bounds
%type <index> opt_array_bounds nest_array_bounds opt_type_array_bounds
%type <index> nest_type_array_bounds
%%
prog: statements;
......@@ -769,13 +871,29 @@ stmt: AddAttrStmt { output_statement($1, 0); }
whenever_action(0);
free($1);
}
| ECPGCursorStmt {
fputs($1, yyout);
free($1);
}
| ECPGDeallocate {
fputs($1, yyout);
whenever_action(0);
free($1);
}
| ECPGDeclare {
fputs($1, yyout);
free($1);
}
| ECPGDisconnect {
fprintf(yyout, "ECPGdisconnect(__LINE__, \"%s\");", $1);
whenever_action(0);
free($1);
}
| ECPGExecute {
fprintf(yyout, "ECPGdo(__LINE__, %s, ECPGt_EOIT, ECPGt_EORT);", $1);
output_statement($1, 0);
}
| ECPGFree {
fprintf(yyout, "ECPGdeallocate(__LINE__, \"%s\");", $1);
whenever_action(0);
free($1);
}
......@@ -797,26 +915,39 @@ stmt: AddAttrStmt { output_statement($1, 0); }
fprintf(yyout, "ECPGdo(__LINE__, \"%s\",", ptr->command);
/* dump variables to C file*/
dump_variables(ptr->argsinsert, 0);
dump_variables(argsinsert, 0);
fputs("ECPGt_EOIT, ", yyout);
dump_variables(ptr->argsresult, 0);
fputs("ECPGt_EORT);", yyout);
whenever_action(0);
free($1);
}
| ECPGPrepare {
fprintf(yyout, "ECPGprepare(__LINE__, %s);", $1);
whenever_action(0);
free($1);
}
| ECPGRelease { /* output already done */ }
| ECPGSetConnection {
fprintf(yyout, "ECPGsetconn(__LINE__, %s);", $1);
whenever_action(0);
free($1);
}
| ECPGTypedef {
fputs($1, yyout);
free($1);
}
| ECPGVar {
fputs($1, yyout);
free($1);
}
| ECPGWhenever {
fputs($1, yyout);
output_line_number();
free($1);
}
| ECPGPrepare {
yyerror("PREPARE is not supported yet.");
}
;
/*
* We start with a lot of stuff that's very similar to the backend's parsing
......@@ -1098,7 +1229,7 @@ copy_delimiter: USING DELIMITERS Sconst { $$ = cat2_str(make1_str("using delim
CreateStmt: CREATE OptTemp TABLE relation_name '(' OptTableElementList ')'
OptInherit
{
$$ = cat5_str(make1_str("create"), $2, make1_str("table"), make3_str(make1_str("("), $6, make1_str(")")), $8);
$$ = cat3_str(cat4_str(make1_str("create"), $2, make1_str("table"), $4), make3_str(make1_str("("), $6, make1_str(")")), $8);
}
;
......@@ -2127,40 +2258,37 @@ opt_column: COLUMN { $$ = make1_str("colmunn"); }
RuleStmt: CREATE RULE name AS
{ QueryIsRule=1; }
ON event TO event_object where_clause
DO opt_instead OptStmtList
DO opt_instead RuleActionList
{
$$ = cat2_str(cat5_str(cat5_str(make1_str("create rule"), $3, make1_str("as on"), $7, make1_str("to")), $9, $10, make1_str("do"), $12), $13);
}
;
OptStmtList: NOTHING { $$ = make1_str("nothing"); }
| OptimizableStmt { $$ = $1; }
| '[' OptStmtBlock ']' { $$ = cat3_str(make1_str("["), $2, make1_str("]")); }
/***S*I*D***/
/* We comment this out because it produces a shift / reduce conflict
* with the select_w_o_sort rule */
/* | '(' OptStmtBlock ')' { $$ = cat3_str(make1_str("("), $2, make1_str(")")); }*/
RuleActionList: NOTHING { $$ = make1_str("nothing"); }
| SelectStmt { $$ = $1; }
| RuleActionStmt { $$ = $1; }
| '[' RuleActionBlock ']' { $$ = cat3_str(make1_str("["), $2, make1_str("]")); }
| '(' RuleActionBlock ')' { $$ = cat3_str(make1_str("("), $2, make1_str(")")); }
;
OptStmtBlock: OptStmtMulti
{ $$ = $1; }
| OptimizableStmt
{ $$ = $1; }
RuleActionBlock: RuleActionMulti { $$ = $1; }
| RuleActionStmt { $$ = $1; }
;
OptStmtMulti: OptStmtMulti OptimizableStmt ';'
RuleActionMulti: RuleActionMulti RuleActionStmt
{ $$ = cat2_str($1, $2); }
| RuleActionMulti RuleActionStmt ';'
{ $$ = cat3_str($1, $2, make1_str(";")); }
/***S*I***/
/* We comment the next rule because it seems to be redundant
* and produces 16 shift/reduce conflicts with the new SelectStmt rule
* needed for EXCEPT and INTERSECT. So far I did not notice any
* violations by removing the rule! */
/* | OptStmtMulti OptimizableStmt
{ $$ = cat2_str($1, $2); }*/
| OptimizableStmt ';'
| RuleActionStmt ';'
{ $$ = cat2_str($1, make1_str(";")); }
;
RuleActionStmt: InsertStmt
| UpdateStmt
| DeleteStmt
| NotifyStmt
;
event_object: relation_name '.' attr_name
{
$$ = make3_str($1, make1_str("."), $3);
......@@ -2588,7 +2716,7 @@ CursorStmt: DECLARE name opt_cursor CURSOR FOR SelectStmt cursor_clause
{
if (strcmp($2, ptr->name) == 0)
{
/* re-definition is a bug*/
/* re-definition is a bug */
sprintf(errortext, "cursor %s already defined", $2);
yyerror(errortext);
}
......@@ -2642,13 +2770,13 @@ opt_of: OF columnList { $$ = make2_str(make1_str("of"), $2); }
/* The new 'SelectStmt' rule adapted for the optional use of INTERSECT EXCEPT a nd UNION
* accepts the use of '(' and ')' to select an order of set operations.
*/
SelectStmt: select_w_o_sort sort_clause for_update_clause
SelectStmt: select_w_o_sort sort_clause for_update_clause opt_select_limit
{
if (strlen($3) > 0 && ForUpdateNotAllowed != 0)
yyerror("SELECT FOR UPDATE is not allowed in this context");
ForUpdateNotAllowed = 0;
$$ = cat3_str($1, $2, $3);
$$ = cat4_str($1, $2, $3, $4);
}
/***S*I***/
......@@ -2736,6 +2864,29 @@ OptUseOp: USING Op { $$ = cat2_str(make1_str("using"), $2); }
| /*EMPTY*/ { $$ = make1_str(""); }
;
opt_select_limit: LIMIT select_limit_value ',' select_offset_value
{ $$ = cat4_str(make1_str("limit"), $2, make1_str(","), $4); }
| LIMIT select_limit_value OFFSET select_offset_value
{ $$ = cat4_str(make1_str("limit"), $2, make1_str("offset"), $4); }
| LIMIT select_limit_value
{ $$ = cat2_str(make1_str("limit"), $2);; }
| OFFSET select_offset_value LIMIT select_limit_value
{ $$ = cat4_str(make1_str("offset"), $2, make1_str("limit"), $4); }
| OFFSET select_offset_value
{ $$ = cat2_str(make1_str("offset"), $2); }
| /* EMPTY */
{ $$ = make1_str(""); }
;
select_limit_value: Iconst { $$ = $1; }
| ALL { $$ = make1_str("all"); }
| PARAM { $$ = make_name(); }
;
select_offset_value: Iconst { $$ = $1; }
| PARAM { $$ = make_name(); }
;
/*
* jimmy bell-style recursive queries aren't supported in the
* current system.
......@@ -2952,6 +3103,36 @@ Generic: generic
generic: ident { $$ = $1; }
| TYPE_P { $$ = make1_str("type"); }
| SQL_BOOL { $$ = make1_str("bool"); }
| SQL_BREAK { $$ = make1_str("break"); }
| SQL_CALL { $$ = make1_str("call"); }
| SQL_CONNECT { $$ = make1_str("connect"); }
| SQL_CONNECTION { $$ = make1_str("connection"); }
| SQL_CONTINUE { $$ = make1_str("continue"); }
| SQL_DEALLOCATE { $$ = make1_str("deallocate"); }
| SQL_DISCONNECT { $$ = make1_str("disconnect"); }
| SQL_FOUND { $$ = make1_str("found"); }
| SQL_GO { $$ = make1_str("go"); }
| SQL_GOTO { $$ = make1_str("goto"); }
| SQL_IDENTIFIED { $$ = make1_str("identified"); }
| SQL_IMMEDIATE { $$ = make1_str("immediate"); }
| SQL_INDICATOR { $$ = make1_str("indicator"); }
| SQL_INT { $$ = make1_str("int"); }
| SQL_LONG { $$ = make1_str("long"); }
| SQL_OPEN { $$ = make1_str("open"); }
| SQL_PREPARE { $$ = make1_str("prepare"); }
| SQL_RELEASE { $$ = make1_str("release"); }
| SQL_SECTION { $$ = make1_str("section"); }
| SQL_SHORT { $$ = make1_str("short"); }
| SQL_SIGNED { $$ = make1_str("signed"); }
| SQL_SQLERROR { $$ = make1_str("sqlerror"); }
| SQL_SQLPRINT { $$ = make1_str("sqlprint"); }
| SQL_SQLWARNING { $$ = make1_str("sqlwarning"); }
| SQL_STOP { $$ = make1_str("stop"); }
| SQL_STRUCT { $$ = make1_str("struct"); }
| SQL_UNSIGNED { $$ = make1_str("unsigned"); }
| SQL_VAR { $$ = make1_str("var"); }
| SQL_WHENEVER { $$ = make1_str("whenever"); }
;
/* SQL92 numeric data types
......@@ -3638,7 +3819,7 @@ b_expr: attr opt_indirection
$$ = make3_str(make1_str("trim("), $3, make1_str(")"));
}
| civariableonly
{ $$ = make1_str(";;"); }
{ $$ = $1; }
;
opt_indirection: '[' ecpg_expr ']' opt_indirection
......@@ -4125,6 +4306,36 @@ ColId: ident { $$ = $1; }
| VALID { $$ = make1_str("valid"); }
| VERSION { $$ = make1_str("version"); }
| ZONE { $$ = make1_str("zone"); }
| SQL_BOOL { $$ = make1_str("bool"); }
| SQL_BREAK { $$ = make1_str("break"); }
| SQL_CALL { $$ = make1_str("call"); }
| SQL_CONNECT { $$ = make1_str("connect"); }
| SQL_CONNECTION { $$ = make1_str("connection"); }
| SQL_CONTINUE { $$ = make1_str("continue"); }
| SQL_DEALLOCATE { $$ = make1_str("deallocate"); }
| SQL_DISCONNECT { $$ = make1_str("disconnect"); }
| SQL_FOUND { $$ = make1_str("found"); }
| SQL_GO { $$ = make1_str("go"); }
| SQL_GOTO { $$ = make1_str("goto"); }
| SQL_IDENTIFIED { $$ = make1_str("identified"); }
| SQL_IMMEDIATE { $$ = make1_str("immediate"); }
| SQL_INDICATOR { $$ = make1_str("indicator"); }
| SQL_INT { $$ = make1_str("int"); }
| SQL_LONG { $$ = make1_str("long"); }
| SQL_OPEN { $$ = make1_str("open"); }
| SQL_PREPARE { $$ = make1_str("prepare"); }
| SQL_RELEASE { $$ = make1_str("release"); }
| SQL_SECTION { $$ = make1_str("section"); }
| SQL_SHORT { $$ = make1_str("short"); }
| SQL_SIGNED { $$ = make1_str("signed"); }
| SQL_SQLERROR { $$ = make1_str("sqlerror"); }
| SQL_SQLPRINT { $$ = make1_str("sqlprint"); }
| SQL_SQLWARNING { $$ = make1_str("sqlwarning"); }
| SQL_STOP { $$ = make1_str("stop"); }
| SQL_STRUCT { $$ = make1_str("struct"); }
| SQL_UNSIGNED { $$ = make1_str("unsigned"); }
| SQL_VAR { $$ = make1_str("var"); }
| SQL_WHENEVER { $$ = make1_str("whenever"); }
;
/* Column label
* Allowed labels in "AS" clauses.
......@@ -4198,202 +4409,423 @@ SpecialRuleRelation: CURRENT
*/
/*
* variable declaration inside the exec sql declare block
* the exec sql connect statement: connect to the given database
*/
ECPGDeclaration: sql_startdeclare variable_declarations sql_enddeclare {}
sql_startdeclare : ecpgstart BEGIN_TRANS DECLARE SQL_SECTION SQL_SEMI {
fputs("/* exec sql begin declare section */\n", yyout);
output_line_number();
}
sql_enddeclare: ecpgstart END_TRANS DECLARE SQL_SECTION SQL_SEMI {
fputs("/* exec sql end declare section */\n", yyout);
output_line_number();
}
variable_declarations: /* empty */
| declaration variable_declarations;
declaration: storage_clause type
ECPGConnect: SQL_CONNECT TO connection_target opt_connection_name opt_user
{
actual_storage[struct_level] = $1;
actual_type[struct_level] = $2.type_enum;
if ($2.type_enum != ECPGt_varchar && $2.type_enum != ECPGt_struct)
fprintf(yyout, "%s %s", $1, $2.type_str);
free($2.type_str);
$$ = make5_str($3, make1_str(","), $5, make1_str(","), $4);
}
variable_list ';' { fputc(';', yyout); }
storage_clause : S_EXTERN { $$ = "extern"; }
| S_STATIC { $$ = "static"; }
| S_SIGNED { $$ = "signed"; }
| S_CONST { $$ = "const"; }
| S_REGISTER { $$ = "register"; }
| S_AUTO { $$ = "auto"; }
| /* empty */ { $$ = ""; }
type: simple_type
| SQL_CONNECT TO DEFAULT
{
$$.type_enum = $1;
$$.type_str = mm_strdup(ECPGtype_name($1));
$$ = make1_str("NULL,NULL,NULL,\"DEFAULT\"");
}
| struct_type
/* also allow ORACLE syntax */
| SQL_CONNECT ora_user
{
$$.type_enum = ECPGt_struct;
$$.type_str = make1_str("");
$$ = make3_str(make1_str("NULL,"), $2, make1_str(",NULL"));
}
| enum_type
connection_target: database_name opt_server opt_port
{
$$.type_str = $1;
$$.type_enum = ECPGt_int;
/* old style: dbname[@server][:port] */
if (strlen($2) > 0 && *($2) != '@')
{
sprintf(errortext, "parse error at or near '%s'", $2);
yyerror(errortext);
}
enum_type: s_enum '{' c_line '}'
$$ = make5_str(make1_str("\""), $1, $2, $3, make1_str("\""));
}
| db_prefix server opt_port '/' database_name opt_options
{
$$ = cat4_str($1, make1_str("{"), $3, make1_str("}"));
/* new style: <tcp|unix>:postgresql://server[:port][/dbname] */
if (strncmp($2, "://", 3) != 0)
{
sprintf(errortext, "parse error at or near '%s'", $2);
yyerror(errortext);
}
s_enum: S_ENUM opt_symbol { $$ = cat2_str(make1_str("enum"), $2); }
struct_type: s_struct '{' variable_declarations '}'
if (strncmp($1, "unix", 4) == 0 && strncmp($2 + 3, "localhost", 9) != 0)
{
ECPGfree_struct_member(struct_member_list[struct_level]);
free(actual_storage[struct_level--]);
fputs("} ", yyout);
sprintf(errortext, "unix domain sockets only work on 'localhost' but not on '%9.9s'", $2);
yyerror(errortext);
}
s_struct : S_STRUCT opt_symbol
if (strncmp($1, "unix", 4) != 0 && strncmp($1, "tcp", 3) != 0)
{
struct_member_list[struct_level++] = NULL;
if (struct_level >= STRUCT_DEPTH)
yyerror("Too many levels in nested structure definition");
fprintf(yyout, "struct %s {", $2);
free($2);
sprintf(errortext, "only protocols 'tcp' and 'unix' are supported");
yyerror(errortext);
}
opt_symbol: /* empty */ { $$ = make1_str(""); }
| symbol { $$ = $1; }
simple_type: S_SHORT { $$ = ECPGt_short; }
| S_UNSIGNED S_SHORT { $$ = ECPGt_unsigned_short; }
| S_INT { $$ = ECPGt_int; }
| S_UNSIGNED S_INT { $$ = ECPGt_unsigned_int; }
| S_LONG { $$ = ECPGt_long; }
| S_UNSIGNED S_LONG { $$ = ECPGt_unsigned_long; }
| S_FLOAT { $$ = ECPGt_float; }
| S_DOUBLE { $$ = ECPGt_double; }
| S_BOOL { $$ = ECPGt_bool; };
| S_CHAR { $$ = ECPGt_char; }
| S_UNSIGNED S_CHAR { $$ = ECPGt_unsigned_char; }
| S_VARCHAR { $$ = ECPGt_varchar; }
variable_list: variable
| variable_list ','
$$ = make4_str(make5_str(make1_str("\""), $1, $2, $3, make1_str("/")), $5, $6, make1_str("\""));
}
| char_variable
{
if (actual_type[struct_level] != ECPGt_varchar)
fputs(", ", yyout);
else
fputs(";\n ", yyout);
} variable
variable: opt_pointer symbol opt_array_bounds opt_initializer
$$ = $1;
}
| Sconst
{
struct ECPGtype * type;
int dimension = $3.index1; /* dimension of array */
int length = $3.index2; /* lenght of string */
char dim[14L];
$$ = mm_strdup($1);
$$[0] = '\"';
$$[strlen($$) - 1] = '\"';
free($1);
}
switch (actual_type[struct_level])
db_prefix: ident cvariable
{
case ECPGt_struct:
/* pointer has to get dimension 0 */
if (strlen($1) > 0)
if (strcmp($2, "postgresql") != 0 && strcmp($2, "postgres") != 0)
{
length = dimension;
dimension = 0;
sprintf(errortext, "parse error at or near '%s'", $2);
yyerror(errortext);
}
if (length >= 0)
yyerror("No multi-dimensional array support for structures");
if (dimension == 1 || dimension < 0)
type = ECPGmake_struct_type(struct_member_list[struct_level]);
else
type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level]), dimension);
fprintf(yyout, "%s%s%s%s", $1, $2, $3.str, $4);
break;
case ECPGt_varchar:
/* pointer has to get length 0 */
if (strlen($1) > 0)
length=0;
/* one index is the string length */
if (length < 0)
if (strcmp($1, "tcp") != 0 && strcmp($1, "unix") != 0)
{
length = dimension;
dimension = 1;
sprintf(errortext, "Illegal connection type %s", $1);
yyerror(errortext);
}
if (dimension == 1)
type = ECPGmake_simple_type(actual_type[struct_level], length);
else
type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level], length), dimension);
switch(dimension)
{
case 0:
strcpy(dim, "[]");
break;
case 1:
*dim = '\0';
break;
default:
sprintf(dim, "[%d]", dimension);
break;
$$ = make3_str($1, make1_str(":"), $2);
}
if (length > 0)
fprintf(yyout, "%s struct varchar_%s { int len; char arr[%d]; } %s%s", actual_storage[struct_level], $2, length, $2, dim);
else
fprintf(yyout, "%s struct varchar_%s { int len; char *arr; } %s%s", actual_storage[struct_level], $2, $2, dim);
break;
case ECPGt_char:
case ECPGt_unsigned_char:
/* pointer has to get length 0 */
if (strlen($1) > 0)
length=0;
/* one index is the string length */
if (length < 0)
server: Op server_name
{
if (strcmp($1, "@") != 0 && strcmp($1, "://") != 0)
{
length = (dimension < 0) ? 1 : dimension;
dimension = 1;
sprintf(errortext, "parse error at or near '%s'", $1);
yyerror(errortext);
}
if (dimension == 1)
type = ECPGmake_simple_type(actual_type[struct_level], length);
else
type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level], length), dimension);
fprintf(yyout, "%s%s%s%s", $1, $2, $3.str, $4);
break;
default:
/* a pointer has dimension = 0 */
if (strlen($1) > 0) {
length = dimension;
dimension = 0;
$$ = make2_str($1, $2);
}
if (length >= 0)
yyerror("No multi-dimensional array support for simple data types");
opt_server: server { $$ = $1; }
| /* empty */ { $$ = make1_str(""); }
if (dimension == 1 || dimension < 0)
type = ECPGmake_simple_type(actual_type[struct_level], 1);
else
type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level], 1), dimension);
server_name: ColId { $$ = $1; }
| ColId '.' server_name { $$ = make3_str($1, make1_str("."), $3); }
opt_port: ':' Iconst { $$ = make2_str(make1_str(":"), $2); }
| /* empty */ { $$ = make1_str(""); }
opt_connection_name: AS connection_target { $$ = $2; }
| /* empty */ { $$ = make1_str("NULL"); }
opt_user: USER ora_user { $$ = $2; }
| /* empty */ { $$ = make1_str("NULL,NULL"); }
ora_user: user_name
{
$$ = make2_str($1, make1_str(",NULL"));
}
| user_name '/' ColId
{
$$ = make3_str($1, make1_str(","), $3);
}
| user_name SQL_IDENTIFIED BY user_name
{
$$ = make3_str($1, make1_str(","), $4);
}
| user_name USING user_name
{
$$ = make3_str($1, make1_str(","), $3);
}
user_name: UserId { if ($1[0] == '\"')
$$ = $1;
else
$$ = make3_str(make1_str("\""), $1, make1_str("\""));
}
| char_variable { $$ = $1; }
| SCONST { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); }
char_variable: cvariable
{ /* check if we have a char variable */
struct variable *p = find_variable($1);
enum ECPGttype typ = p->type->typ;
/* if array see what's inside */
if (typ == ECPGt_array)
typ = p->type->u.element->typ;
switch (typ)
{
case ECPGt_char:
case ECPGt_unsigned_char:
$$ = $1;
break;
case ECPGt_varchar:
$$ = make2_str($1, make1_str(".arr"));
break;
default:
yyerror("invalid datatype");
break;
}
}
opt_options: Op ColId
{
if (strlen($1) == 0)
yyerror("parse error");
if (strcmp($1, "?") != 0)
{
sprintf(errortext, "parse error at or near %s", $1);
yyerror(errortext);
}
$$ = make2_str(make1_str("?"), $2);
}
| /* empty */ { $$ = make1_str(""); }
/*
* Declare a prepared cursor. The syntax is different from the standard
* declare statement, so we create a new rule.
*/
ECPGCursorStmt: DECLARE name opt_cursor CURSOR FOR ident cursor_clause
{
struct cursor *ptr, *this;
struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
for (ptr = cur; ptr != NULL; ptr = ptr->next)
{
if (strcmp($2, ptr->name) == 0)
{
/* re-definition is a bug */
sprintf(errortext, "cursor %s already defined", $2);
yyerror(errortext);
}
}
this = (struct cursor *) mm_alloc(sizeof(struct cursor));
/* initial definition */
this->next = cur;
this->name = $2;
this->command = cat5_str(make1_str("declare"), mm_strdup($2), $3, make1_str("cursor for ;;"), $7);
this->argsresult = NULL;
thisquery->type = &ecpg_query;
thisquery->brace_level = 0;
thisquery->next = NULL;
thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(\"\")") + strlen($6));
sprintf(thisquery->name, "ECPGprepared_statement(\"%s\")", $6);
this->argsinsert = NULL;
add_variable(&(this->argsinsert), thisquery, &no_indicator);
cur = this;
$$ = cat3_str(make1_str("/*"), mm_strdup(this->command), make1_str("*/"));
}
;
/*
* the exec sql deallocate prepare command to deallocate a previously
* prepared statement
*/
ECPGDeallocate: SQL_DEALLOCATE SQL_PREPARE ident { $$ = make3_str(make1_str("ECPGdeallocate(__LINE__, \""), $3, make1_str("\");")); }
/*
* variable declaration inside the exec sql declare block
*/
ECPGDeclaration: sql_startdeclare
{
fputs("/* exec sql begin declare section */", yyout);
output_line_number();
}
variable_declarations sql_enddeclare
{
fprintf(yyout, "%s/* exec sql end declare section */", $3);
free($3);
output_line_number();
}
sql_startdeclare : ecpgstart BEGIN_TRANS DECLARE SQL_SECTION SQL_SEMI {}
sql_enddeclare: ecpgstart END_TRANS DECLARE SQL_SECTION SQL_SEMI {}
variable_declarations: /* empty */
{
$$ = make1_str("");
}
| declaration variable_declarations
{
$$ = cat2_str($1, $2);
}
declaration: storage_clause
{
actual_storage[struct_level] = mm_strdup($1);
}
type
{
actual_type[struct_level].type_enum = $3.type_enum;
actual_type[struct_level].type_dimension = $3.type_dimension;
actual_type[struct_level].type_index = $3.type_index;
}
variable_list ';'
{
$$ = cat4_str($1, $3.type_str, $5, make1_str(";\n"));
}
storage_clause : S_EXTERN { $$ = make1_str("extern"); }
| S_STATIC { $$ = make1_str("static"); }
| S_SIGNED { $$ = make1_str("signed"); }
| S_CONST { $$ = make1_str("const"); }
| S_REGISTER { $$ = make1_str("register"); }
| S_AUTO { $$ = make1_str("auto"); }
| /* empty */ { $$ = make1_str(""); }
type: simple_type
{
$$.type_enum = $1;
$$.type_str = mm_strdup(ECPGtype_name($1));
$$.type_dimension = -1;
$$.type_index = -1;
}
| varchar_type
{
$$.type_enum = ECPGt_varchar;
$$.type_str = make1_str("");
$$.type_dimension = -1;
$$.type_index = -1;
}
| struct_type
{
$$.type_enum = ECPGt_struct;
$$.type_str = $1;
$$.type_dimension = -1;
$$.type_index = -1;
}
| enum_type
{
$$.type_str = $1;
$$.type_enum = ECPGt_int;
$$.type_dimension = -1;
$$.type_index = -1;
}
| symbol
{
/* this is for typedef'ed types */
struct typedefs *this = get_typedef($1);
$$.type_str = mm_strdup(this->name);
$$.type_enum = this->type->type_enum;
$$.type_dimension = this->type->type_dimension;
$$.type_index = this->type->type_index;
struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
}
enum_type: s_enum '{' c_line '}'
{
$$ = cat4_str($1, make1_str("{"), $3, make1_str("}"));
}
s_enum: S_ENUM opt_symbol { $$ = cat2_str(make1_str("enum"), $2); }
struct_type: s_struct '{' variable_declarations '}'
{
ECPGfree_struct_member(struct_member_list[struct_level]);
free(actual_storage[struct_level--]);
$$ = cat4_str($1, make1_str("{"), $3, make1_str("}"));
}
s_struct : S_STRUCT opt_symbol
{
struct_member_list[struct_level++] = NULL;
if (struct_level >= STRUCT_DEPTH)
yyerror("Too many levels in nested structure definition");
$$ = cat2_str(make1_str("struct"), $2);
}
opt_symbol: /* empty */ { $$ = make1_str(""); }
| symbol { $$ = $1; }
simple_type: S_SHORT { $$ = ECPGt_short; }
| S_UNSIGNED S_SHORT { $$ = ECPGt_unsigned_short; }
| S_INT { $$ = ECPGt_int; }
| S_UNSIGNED S_INT { $$ = ECPGt_unsigned_int; }
| S_LONG { $$ = ECPGt_long; }
| S_UNSIGNED S_LONG { $$ = ECPGt_unsigned_long; }
| S_FLOAT { $$ = ECPGt_float; }
| S_DOUBLE { $$ = ECPGt_double; }
| S_BOOL { $$ = ECPGt_bool; };
| S_CHAR { $$ = ECPGt_char; }
| S_UNSIGNED S_CHAR { $$ = ECPGt_unsigned_char; }
varchar_type: S_VARCHAR { $$ = ECPGt_varchar; }
variable_list: variable
{
$$ = $1;
}
| variable_list ',' variable
{
$$ = cat3_str($1, make1_str(","), $3);
}
fprintf(yyout, "%s%s%s%s", $1, $2, $3.str, $4);
variable: opt_pointer symbol opt_array_bounds opt_initializer
{
struct ECPGtype * type;
int dimension = $3.index1; /* dimension of array */
int length = $3.index2; /* lenght of string */
char dim[14L], ascii_len[12];
adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen($1));
switch (actual_type[struct_level].type_enum)
{
case ECPGt_struct:
if (dimension < 0)
type = ECPGmake_struct_type(struct_member_list[struct_level]);
else
type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level]), dimension);
$$ = make4_str($1, mm_strdup($2), $3.str, $4);
break;
case ECPGt_varchar:
if (dimension == -1)
type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length);
else
type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension);
switch(dimension)
{
case 0:
strcpy(dim, "[]");
break;
case -1:
case 1:
*dim = '\0';
break;
default:
sprintf(dim, "[%d]", dimension);
break;
}
sprintf(ascii_len, "%d", length);
if (length > 0)
$$ = make4_str(make5_str(mm_strdup(actual_storage[struct_level]), make1_str(" struct varchar_"), mm_strdup($2), make1_str(" { int len; char arr["), mm_strdup(ascii_len)), make1_str("]; } "), mm_strdup($2), mm_strdup(dim));
else
$$ = make4_str(make3_str(mm_strdup(actual_storage[struct_level]), make1_str(" struct varchar_"), mm_strdup($2)), make1_str(" { int len; char *arr; } "), mm_strdup($2), mm_strdup(dim));
break;
case ECPGt_char:
case ECPGt_unsigned_char:
if (dimension == -1)
type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length);
else
type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension);
$$ = make4_str($1, mm_strdup($2), $3.str, $4);
break;
default:
if (dimension < 0)
type = ECPGmake_simple_type(actual_type[struct_level].type_enum, 1);
else
type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, 1), dimension);
$$ = make4_str($1, mm_strdup($2), $3.str, $4);
break;
}
......@@ -4402,10 +4834,7 @@ variable: opt_pointer symbol opt_array_bounds opt_initializer
else
ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1]));
free($1);
free($2);
free($3.str);
free($4);
}
opt_initializer: /* empty */ { $$ = make1_str(""); }
......@@ -4414,234 +4843,490 @@ opt_initializer: /* empty */ { $$ = make1_str(""); }
opt_pointer: /* empty */ { $$ = make1_str(""); }
| '*' { $$ = make1_str("*"); }
/*
* the exec sql connect statement: connect to the given database
*/
ECPGConnect: SQL_CONNECT TO connection_target opt_connection_name opt_user
/*
* As long as the prepare statement is not supported by the backend, we will
* try to simulate it here so we get dynamic SQL
*/
ECPGDeclare: DECLARE STATEMENT ident
{
/* this is only supported for compatibility */
$$ = cat3_str(make1_str("/* declare statement"), $3, make1_str("*/"));
}
/*
* the exec sql disconnect statement: disconnect from the given database
*/
ECPGDisconnect: SQL_DISCONNECT dis_name { $$ = $2; }
dis_name: connection_object { $$ = $1; }
| CURRENT { $$ = make1_str("CURRENT"); }
| ALL { $$ = make1_str("ALL"); }
| /* empty */ { $$ = make1_str("CURRENT"); }
connection_object: connection_target { $$ = $1; }
| DEFAULT { $$ = make1_str("DEFAULT"); }
/*
* execute a given string as sql command
*/
ECPGExecute : EXECUTE SQL_IMMEDIATE execstring
{
struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
thisquery->type = &ecpg_query;
thisquery->brace_level = 0;
thisquery->next = NULL;
thisquery->name = $3;
add_variable(&argsinsert, thisquery, &no_indicator);
$$ = make1_str(";;");
}
| EXECUTE ident
{
struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
thisquery->type = &ecpg_query;
thisquery->brace_level = 0;
thisquery->next = NULL;
thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(\"\")") + strlen($2));
sprintf(thisquery->name, "ECPGprepared_statement(\"%s\")", $2);
add_variable(&argsinsert, thisquery, &no_indicator);
} opt_using
{
$$ = make1_str(";;");
}
execstring: char_variable |
CSTRING { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); };
/*
* the exec sql free command to deallocate a previously
* prepared statement
*/
ECPGFree: SQL_FREE ident { $$ = $2; }
/*
* open is an open cursor, at the moment this has to be removed
*/
ECPGOpen: SQL_OPEN name opt_using {
$$ = $2;
};
opt_using: /* empty */ { $$ = make1_str(""); }
| USING variablelist {
/* yyerror ("open cursor with variables not implemented yet"); */
$$ = make1_str("");
}
variablelist: cinputvariable | cinputvariable ',' variablelist
/*
* As long as the prepare statement is not supported by the backend, we will
* try to simulate it here so we get dynamic SQL
*/
ECPGPrepare: SQL_PREPARE ident FROM char_variable
{
$$ = make4_str(make1_str("\""), $2, make1_str("\", "), $4);
}
/*
* for compatibility with ORACLE we will also allow the keyword RELEASE
* after a transaction statement to disconnect from the database.
*/
ECPGRelease: TransactionStmt SQL_RELEASE
{
if (strncmp($1, "begin", 5) == 0)
yyerror("RELEASE does not make sense when beginning a transaction");
fprintf(yyout, "ECPGtrans(__LINE__, \"%s\");", $1);
whenever_action(0);
fprintf(yyout, "ECPGdisconnect(\"\");");
whenever_action(0);
free($1);
}
/*
* set the actual connection, this needs a differnet handling as the other
* set commands
*/
ECPGSetConnection: SET SQL_CONNECTION connection_object
{
$$ = $3;
}
/*
* define a new type for embedded SQL
*/
ECPGTypedef: TYPE_P symbol IS ctype opt_type_array_bounds opt_reference
{
/* add entry to list */
struct typedefs *ptr, *this;
int dimension = $5.index1;
int length = $5.index2;
for (ptr = types; ptr != NULL; ptr = ptr->next)
{
if (strcmp($2, ptr->name) == 0)
{
/* re-definition is a bug */
sprintf(errortext, "type %s already defined", $2);
yyerror(errortext);
}
}
adjust_array($4.type_enum, &dimension, &length, $4.type_dimension, $4.type_index, strlen($6));
this = (struct typedefs *) mm_alloc(sizeof(struct typedefs));
/* initial definition */
this->next = types;
this->name = $2;
this->type = (struct this_type *) mm_alloc(sizeof(struct this_type));
this->type->type_enum = $4.type_enum;
this->type->type_str = mm_strdup($2);
this->type->type_dimension = dimension; /* dimension of array */
this->type->type_index = length; /* lenght of string */
this->struct_member_list = struct_member_list[struct_level];
if ($4.type_enum != ECPGt_varchar &&
$4.type_enum != ECPGt_char &&
$4.type_enum != ECPGt_unsigned_char &&
this->type->type_index >= 0)
yyerror("No multi-dimensional array support for simple data types");
types = this;
$$ = cat5_str(cat3_str(make1_str("/* exec sql type"), mm_strdup($2), make1_str("is")), mm_strdup($4.type_str), mm_strdup($5.str), $6, make1_str("*/"));
}
opt_type_array_bounds: '[' ']' nest_type_array_bounds
{
$$.index1 = 0;
$$.index2 = $3.index1;
$$.str = cat2_str(make1_str("[]"), $3.str);
}
| '(' ')' nest_type_array_bounds
{
$$.index1 = 0;
$$.index2 = $3.index1;
$$.str = cat2_str(make1_str("[]"), $3.str);
}
| '[' Iconst ']' nest_type_array_bounds
{
$$.index1 = atol($2);
$$.index2 = $4.index1;
$$.str = cat4_str(make1_str("["), $2, make1_str("]"), $4.str);
}
| '(' Iconst ')' nest_type_array_bounds
{
$$.index1 = atol($2);
$$.index2 = $4.index1;
$$.str = cat4_str(make1_str("["), $2, make1_str("]"), $4.str);
}
| /* EMPTY */
{
$$.index1 = -1;
$$.index2 = -1;
$$.str= make1_str("");
}
;
nest_type_array_bounds: '[' ']' nest_type_array_bounds
{
$$ = make5_str($3, make1_str(","), $5, make1_str(","), $4);
$$.index1 = 0;
$$.index2 = $3.index1;
$$.str = cat2_str(make1_str("[]"), $3.str);
}
| SQL_CONNECT TO DEFAULT
| '(' ')' nest_type_array_bounds
{
$$ = make1_str("NULL,NULL,NULL,\"DEFAULT\"");
$$.index1 = 0;
$$.index2 = $3.index1;
$$.str = cat2_str(make1_str("[]"), $3.str);
}
/* also allow ORACLE syntax */
| SQL_CONNECT ora_user
| '[' Iconst ']' nest_type_array_bounds
{
$$ = make3_str(make1_str("NULL,"), $2, make1_str(",NULL"));
$$.index1 = atol($2);
$$.index2 = $4.index1;
$$.str = cat4_str(make1_str("["), $2, make1_str("]"), $4.str);
}
connection_target: database_name opt_server opt_port
| '(' Iconst ')' nest_type_array_bounds
{
/* old style: dbname[@server][:port] */
if (strlen($2) > 0 && *($2) != '@')
$$.index1 = atol($2);
$$.index2 = $4.index1;
$$.str = cat4_str(make1_str("["), $2, make1_str("]"), $4.str);
}
| /* EMPTY */
{
sprintf(errortext, "parse error at or near '%s'", $2);
yyerror(errortext);
$$.index1 = -1;
$$.index2 = -1;
$$.str= make1_str("");
}
;
opt_reference: SQL_REFERENCE { $$ = make1_str("reference"); }
| /* empty */ { $$ = make1_str(""); }
$$ = make5_str(make1_str("\""), $1, $2, $3, make1_str("\""));
ctype: CHAR
{
$$.type_str = make1_str("char");
$$.type_enum = ECPGt_char;
$$.type_index = -1;
$$.type_dimension = -1;
}
| db_prefix server opt_port '/' database_name opt_options
| VARCHAR
{
/* new style: <tcp|unix>:postgresql://server[:port][/dbname] */
if (strncmp($2, "://", 3) != 0)
$$.type_str = make1_str("varchar");
$$.type_enum = ECPGt_varchar;
$$.type_index = -1;
$$.type_dimension = -1;
}
| FLOAT
{
sprintf(errortext, "parse error at or near '%s'", $2);
yyerror(errortext);
$$.type_str = make1_str("float");
$$.type_enum = ECPGt_float;
$$.type_index = -1;
$$.type_dimension = -1;
}
if (strncmp($1, "unix", 4) == 0 && strncmp($2 + 3, "localhost", 9) != 0)
| DOUBLE
{
sprintf(errortext, "unix domain sockets only work on 'localhost' but not on '%9.9s'", $2);
yyerror(errortext);
$$.type_str = make1_str("double");
$$.type_enum = ECPGt_double;
$$.type_index = -1;
$$.type_dimension = -1;
}
if (strncmp($1, "unix", 4) != 0 && strncmp($1, "tcp", 3) != 0)
| opt_signed SQL_INT
{
sprintf(errortext, "only protocols 'tcp' and 'unix' are supported");
yyerror(errortext);
$$.type_str = make1_str("int");
$$.type_enum = ECPGt_int;
$$.type_index = -1;
$$.type_dimension = -1;
}
$$ = make4_str(make5_str(make1_str("\""), $1, $2, $3, make1_str("/")), $5, $6, make1_str("\""));
| SQL_ENUM
{
$$.type_str = make1_str("int");
$$.type_enum = ECPGt_int;
$$.type_index = -1;
$$.type_dimension = -1;
}
| char_variable
| opt_signed SQL_SHORT
{
$$ = $1;
$$.type_str = make1_str("short");
$$.type_enum = ECPGt_short;
$$.type_index = -1;
$$.type_dimension = -1;
}
| Sconst
| opt_signed SQL_LONG
{
$$ = mm_strdup($1);
$$[0] = '\"';
$$[strlen($$) - 1] = '\"';
free($1);
$$.type_str = make1_str("long");
$$.type_enum = ECPGt_long;
$$.type_index = -1;
$$.type_dimension = -1;
}
db_prefix: ident cvariable
| SQL_BOOL
{
if (strcmp($2, "postgresql") != 0 && strcmp($2, "postgres") != 0)
$$.type_str = make1_str("bool");
$$.type_enum = ECPGt_bool;
$$.type_index = -1;
$$.type_dimension = -1;
}
| SQL_UNSIGNED SQL_INT
{
sprintf(errortext, "parse error at or near '%s'", $2);
yyerror(errortext);
$$.type_str = make1_str("unsigned int");
$$.type_enum = ECPGt_unsigned_int;
$$.type_index = -1;
$$.type_dimension = -1;
}
if (strcmp($1, "tcp") != 0 && strcmp($1, "unix") != 0)
| SQL_UNSIGNED SQL_SHORT
{
sprintf(errortext, "Illegal connection type %s", $1);
yyerror(errortext);
$$.type_str = make1_str("unsigned short");
$$.type_enum = ECPGt_unsigned_short;
$$.type_index = -1;
$$.type_dimension = -1;
}
$$ = make3_str($1, make1_str(":"), $2);
| SQL_UNSIGNED SQL_LONG
{
$$.type_str = make1_str("unsigned long");
$$.type_enum = ECPGt_unsigned_long;
$$.type_index = -1;
$$.type_dimension = -1;
}
server: Op server_name
| SQL_STRUCT
{
if (strcmp($1, "@") != 0 && strcmp($1, "://") != 0)
struct_member_list[struct_level++] = NULL;
if (struct_level >= STRUCT_DEPTH)
yyerror("Too many levels in nested structure definition");
} '{' sql_variable_declarations '}'
{
sprintf(errortext, "parse error at or near '%s'", $1);
yyerror(errortext);
ECPGfree_struct_member(struct_member_list[struct_level--]);
$$.type_str = cat3_str(make1_str("struct {"), $4, make1_str("}"));
$$.type_enum = ECPGt_struct;
$$.type_index = -1;
$$.type_dimension = -1;
}
| symbol
{
struct typedefs *this = get_typedef($1);
$$ = make2_str($1, $2);
$$.type_str = mm_strdup($1);
$$.type_enum = this->type->type_enum;
$$.type_dimension = this->type->type_dimension;
$$.type_index = this->type->type_index;
struct_member_list[struct_level] = this->struct_member_list;
}
opt_server: server { $$ = $1; }
| /* empty */ { $$ = make1_str(""); }
server_name: ColId { $$ = $1; }
| ColId '.' server_name { $$ = make3_str($1, make1_str("."), $3); }
opt_port: ':' Iconst { $$ = make2_str(make1_str(":"), $2); }
| /* empty */ { $$ = make1_str(""); }
opt_connection_name: AS connection_target { $$ = $2; }
| /* empty */ { $$ = make1_str("NULL"); }
opt_signed: SQL_SIGNED | /* empty */
opt_user: USER ora_user { $$ = $2; }
| /* empty */ { $$ = make1_str("NULL,NULL"); }
ora_user: user_name
sql_variable_declarations: /* empty */
{
$$ = make2_str($1, make1_str(",NULL"));
$$ = make1_str("");
}
| user_name '/' ColId
| sql_declaration sql_variable_declarations
{
$$ = make3_str($1, make1_str(","), $3);
$$ = cat2_str($1, $2);
}
| user_name SQL_IDENTIFIED BY user_name
;
sql_declaration: ctype
{
$$ = make3_str($1, make1_str(","), $4);
actual_type[struct_level].type_enum = $1.type_enum;
actual_type[struct_level].type_dimension = $1.type_dimension;
actual_type[struct_level].type_index = $1.type_index;
}
| user_name USING user_name
sql_variable_list SQL_SEMI
{
$$ = make3_str($1, make1_str(","), $3);
$$ = cat3_str($1.type_str, $3, make1_str(";"));
}
user_name: UserId { if ($1[0] == '\"')
sql_variable_list: sql_variable
{
$$ = $1;
else
$$ = make3_str(make1_str("\""), $1, make1_str("\""));
}
| char_variable { $$ = $1; }
| SCONST { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); }
| sql_variable_list ',' sql_variable
{
$$ = make3_str($1, make1_str(","), $3);
}
char_variable: cvariable
{ /* check if we have a char variable */
struct variable *p = find_variable($1);
enum ECPGttype typ = p->type->typ;
sql_variable: opt_pointer symbol opt_array_bounds
{
int dimension = $3.index1;
int length = $3.index2;
struct ECPGtype * type;
char dim[14L];
/* if array see what's inside */
if (typ == ECPGt_array)
typ = p->type->u.element->typ;
adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen($1));
switch (typ)
switch (actual_type[struct_level].type_enum)
{
case ECPGt_char:
case ECPGt_unsigned_char:
$$ = $1;
case ECPGt_struct:
if (dimension < 0)
type = ECPGmake_struct_type(struct_member_list[struct_level]);
else
type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level]), dimension);
break;
case ECPGt_varchar:
$$ = make2_str($1, make1_str(".arr"));
if (dimension == -1)
type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length);
else
type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension);
switch(dimension)
{
case 0:
strcpy(dim, "[]");
break;
case -1:
case 1:
*dim = '\0';
break;
default:
yyerror("invalid datatype");
sprintf(dim, "[%d]", dimension);
break;
}
}
opt_options: Op ColId
{
if (strlen($1) == 0)
yyerror("parse error");
break;
case ECPGt_char:
case ECPGt_unsigned_char:
if (dimension == -1)
type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length);
else
type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension);
if (strcmp($1, "?") != 0)
{
sprintf(errortext, "parse error at or near %s", $1);
yyerror(errortext);
break;
default:
if (length >= 0)
yyerror("No multi-dimensional array support for simple data types");
if (dimension < 0)
type = ECPGmake_simple_type(actual_type[struct_level].type_enum, 1);
else
type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, 1), dimension);
break;
}
$$ = make2_str(make1_str("?"), $2);
if (struct_level == 0)
new_variable($2, type);
else
ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1]));
$$ = cat3_str($1, $2, $3.str);
}
| /* empty */ { $$ = make1_str(""); }
/*
* the exec sql disconnect statement: disconnect from the given database
* define the type of one variable for embedded SQL
*/
ECPGDisconnect: SQL_DISCONNECT dis_name { $$ = $2; }
ECPGVar: SQL_VAR symbol IS ctype opt_type_array_bounds opt_reference
{
struct variable *p = find_variable($2);
int dimension = $5.index1;
int length = $5.index2;
struct ECPGtype * type;
dis_name: connection_object { $$ = $1; }
| CURRENT { $$ = make1_str("CURRENT"); }
| ALL { $$ = make1_str("ALL"); }
| /* empty */ { $$ = make1_str("CURRENT"); }
adjust_array($4.type_enum, &dimension, &length, $4.type_dimension, $4.type_index, strlen($6));
connection_object: connection_target { $$ = $1; }
| DEFAULT { $$ = make1_str("DEFAULT"); }
switch ($4.type_enum)
{
case ECPGt_struct:
if (dimension < 0)
type = ECPGmake_struct_type(struct_member_list[struct_level]);
else
type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level]), dimension);
break;
case ECPGt_varchar:
if (dimension == -1)
type = ECPGmake_simple_type($4.type_enum, length);
else
type = ECPGmake_array_type(ECPGmake_simple_type($4.type_enum, length), dimension);
/*
* execute a given string as sql command
*/
ECPGExecute : EXECUTE SQL_IMMEDIATE execstring { $$ = $3; };
break;
case ECPGt_char:
case ECPGt_unsigned_char:
if (dimension == -1)
type = ECPGmake_simple_type($4.type_enum, length);
else
type = ECPGmake_array_type(ECPGmake_simple_type($4.type_enum, length), dimension);
execstring: cvariable |
CSTRING { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); };
break;
default:
if (length >= 0)
yyerror("No multi-dimensional array support for simple data types");
/*
* open is an open cursor, at the moment this has to be removed
*/
ECPGOpen: SQL_OPEN name open_opts {
$$ = $2;
};
if (dimension < 0)
type = ECPGmake_simple_type($4.type_enum, 1);
else
type = ECPGmake_array_type(ECPGmake_simple_type($4.type_enum, 1), dimension);
open_opts: /* empty */ { $$ = make1_str(""); }
| USING cvariable {
yyerror ("open cursor with variables not implemented yet");
break;
}
/*
* for compatibility with ORACLE we will also allow the keyword RELEASE
* after a transaction statement to disconnect from the database.
*/
ECPGRelease: TransactionStmt SQL_RELEASE
{
if (strncmp($1, "begin", 5) == 0)
yyerror("RELEASE does not make sense when beginning a transaction");
ECPGfree_type(p->type);
p->type = type;
fprintf(yyout, "ECPGtrans(__LINE__, \"%s\");", $1);
whenever_action(0);
fprintf(yyout, "ECPGdisconnect(\"\");");
whenever_action(0);
free($1);
$$ = cat5_str(cat3_str(make1_str("/* exec sql var"), mm_strdup($2), make1_str("is")), mm_strdup($4.type_str), mm_strdup($5.str), $6, make1_str("*/"));
}
/*
* set the actual connection, this needs a differnet handling as the other
* set commands
*/
ECPGSetConnection: SET SQL_CONNECTION connection_object
{
$$ = $3;
}
/*
* whenever statement: decide what to do in case of error/no data found
* according to SQL standards we lack: SQLSTATE, CONSTRAINT and SQLEXCEPTION
......@@ -4703,14 +5388,6 @@ action : SQL_CONTINUE {
$<action>$.str = cat2_str(make1_str("call"), mm_strdup($<action>$.command));
}
/*
* As long as the prepare statement in not supported by the backend, we will
* try to simulate it here so we get dynamic SQL
*/
ECPGPrepare: SQL_PREPARE name FROM name
{
}
/* some other stuff for ecpg */
ecpg_expr: attr opt_indirection
{
......@@ -4987,7 +5664,7 @@ ecpg_expr: attr opt_indirection
| NOT ecpg_expr
{ $$ = cat2_str(make1_str("not"), $2); }
| civariableonly
{ $$ = make1_str(";;"); }
{ $$ = $1; }
;
into_list : coutputvariable | into_list ',' coutputvariable;
......@@ -5010,6 +5687,7 @@ cinputvariable : cvariable indicator {
civariableonly : cvariable {
add_variable(&argsinsert, find_variable($1), &no_indicator);
$$ = make1_str(";;");
}
cvariable: CVARIABLE { $$ = $1; }
......
......@@ -2,7 +2,6 @@
#include <string.h>
#include <stdlib.h>
#include "type.h"
#include "extern.h"
/* malloc + error check */
......@@ -36,8 +35,8 @@ mm_strdup(const char *string)
}
/* duplicate memberlist */
static struct ECPGstruct_member *
struct_member_dup(struct ECPGstruct_member * rm)
struct ECPGstruct_member *
ECPGstruct_member_dup(struct ECPGstruct_member * rm)
{
struct ECPGstruct_member *new = NULL;
......@@ -71,7 +70,8 @@ void
ECPGmake_struct_member(char *name, struct ECPGtype * type, struct ECPGstruct_member ** start)
{
struct ECPGstruct_member *ptr,
*ne = (struct ECPGstruct_member *) mm_alloc(sizeof(struct ECPGstruct_member));
*ne =
(struct ECPGstruct_member *) mm_alloc(sizeof(struct ECPGstruct_member));
ne->name = strdup(name);
ne->typ = type;
......@@ -112,7 +112,7 @@ ECPGmake_struct_type(struct ECPGstruct_member * rm)
{
struct ECPGtype *ne = ECPGmake_simple_type(ECPGt_struct, 1);
ne->u.members = struct_member_dup(rm);
ne->u.members = ECPGstruct_member_dup(rm);
return ne;
}
......@@ -160,6 +160,9 @@ get_type(enum ECPGttype typ)
case ECPGt_NO_INDICATOR: /* no indicator */
return ("ECPGt_NO_INDICATOR");
break;
case ECPGt_char_variable: /* string that should not be quoted */
return ("ECPGt_char_variable");
break;
default:
abort();
}
......@@ -202,23 +205,30 @@ ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *in
{
ECPGdump_a_simple(o, name, typ->u.element->typ,
typ->u.element->size, typ->size, NULL, prefix);
if (ind_typ == &ecpg_no_indicator)
if (ind_typ->typ == ECPGt_NO_INDICATOR)
ECPGdump_a_simple(o, ind_name, ind_typ->typ, ind_typ->size, -1, NULL, ind_prefix);
else
{
if (ind_typ->typ != ECPGt_array)
{
fprintf(stderr, "Indicator for an array has to be array too.\n");
exit(INDICATOR_NOT_ARRAY);
}
ECPGdump_a_simple(o, ind_name, ind_typ->u.element->typ,
ind_typ->u.element->size, ind_typ->size, NULL, prefix);
}
}
else if (typ->u.element->typ == ECPGt_array)
{
yyerror("No nested arrays allowed (except strings)"); /* Array of array, */
yyerror("No nested arrays allowed (except strings)"); /* array of array */
}
else if (typ->u.element->typ == ECPGt_struct)
{
/* Array of structs. */
/* Array of structs */
ECPGdump_a_struct(o, name, ind_name, typ->size, typ->u.element, ind_typ->u.element, NULL, prefix, ind_prefix);
}
else
yyerror("Internal error: unknown datatype, pleqase inform pgsql-bugs@postgresql.org");
yyerror("Internal error: unknown datatype, please inform pgsql-bugs@postgresql.org");
break;
case ECPGt_struct:
ECPGdump_a_struct(o, name, ind_name, 1, typ, ind_typ, NULL, prefix, ind_prefix);
......@@ -260,6 +270,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ,
break;
case ECPGt_char:
case ECPGt_unsigned_char:
case ECPGt_char_variable:
sprintf(offset, "%ld*sizeof(char)", varcharsize);
break;
default:
......
......@@ -30,6 +30,7 @@ struct ECPGtype *ECPGmake_simple_type(enum ECPGttype, long);
struct ECPGtype *ECPGmake_varchar_type(enum ECPGttype, long);
struct ECPGtype *ECPGmake_array_type(struct ECPGtype *, long);
struct ECPGtype *ECPGmake_struct_type(struct ECPGstruct_member *);
struct ECPGstruct_member * ECPGstruct_member_dup(struct ECPGstruct_member *);
/* Frees a type. */
void ECPGfree_struct_member(struct ECPGstruct_member *);
......@@ -86,4 +87,52 @@ struct this_type
{
enum ECPGttype type_enum;
char *type_str;
int type_dimension;
int type_index;
};
struct _include_path
{
char *path;
struct _include_path *next;
};
struct cursor
{
char *name;
char *command;
struct arguments *argsinsert;
struct arguments *argsresult;
struct cursor *next;
};
struct typedefs
{
char *name;
struct this_type *type;
struct ECPGstruct_member *struct_member_list;
struct typedefs *next;
};
struct _defines
{
char *old;
char *new;
struct _defines *next;
};
/* This is a linked list of the variable names and types. */
struct variable
{
char *name;
struct ECPGtype *type;
int brace_level;
struct variable *next;
};
struct arguments
{
struct variable *variable;
struct variable *indicator;
struct arguments *next;
};
......@@ -4,16 +4,25 @@ exec sql whenever sqlerror sqlprint;
exec sql include sqlca;
exec sql define AMOUNT 5;
exec sql define AMOUNT 8;
exec sql type intarray is int[AMOUNT];
exec sql type string is char(6);
typedef int intarray[AMOUNT];
int
main ()
{
exec sql begin declare section;
int amount[AMOUNT];
char name[AMOUNT][8];
intarray amount;
int increment=100;
char name[AMOUNT][6];
char letter[AMOUNT][1];
char command[128];
exec sql end declare section;
char msg[128], command[128];
exec sql var name is string(AMOUNT);
char msg[128];
FILE *dbgs;
int i,j;
......@@ -24,30 +33,40 @@ exec sql end declare section;
exec sql connect to mm;
strcpy(msg, "create");
exec sql create table test(name char(8), amount int);
exec sql create table test(name char(6), amount int, letter char(1));
strcpy(msg, "commit");
exec sql commit;
strcpy(msg, "execute insert 1");
sprintf(command, "insert into test(name, amount) values ('foobar', 1)");
sprintf(command, "insert into test(name, amount, letter) values ('foobar', 1, 'f')");
exec sql execute immediate :command;
strcpy(msg, "excute insert 2");
sprintf(command, "insert into test(name, amount) select name, amount+1 from test");
strcpy(msg, "execute insert 2");
sprintf(command, "insert into test(name, amount, letter) select name, amount+1, letter from test");
exec sql execute immediate :command;
strcpy(msg, "excute insert 3");
sprintf(command, "insert into test(name, amount) select name, amount+10 from test");
strcpy(msg, "execute insert 3");
sprintf(command, "insert into test(name, amount, letter) select name, amount+10, letter from test");
exec sql execute immediate :command;
printf("Inserted %d tuples via execute immediate\n", sqlca.sqlerrd[2]);
strcpy(msg, "execute insert 4");
sprintf(command, "insert into test(name, amount, letter) select name, amount+;;, letter from test");
exec sql prepare I from :command;
exec sql execute I using :increment;
printf("Inserted %d tuples via prepared execute\n", sqlca.sqlerrd[2]);
strcpy(msg, "commit");
exec sql commit;
strcpy(msg, "select");
exec sql select name, amount into :name, :amount from test;
exec sql select name, amount, letter into :name, :amount, :letter from test;
for (i=0, j=sqlca.sqlerrd[2]; i<j; i++)
printf("name[%d]=%8.8s, amount[%d]=%d\n", i, name[i], i, amount[i]);
printf("name[%d]=%6.6s\tamount[%d]=%d\tletter[%d]=%c\n", i, name[i], i, amount[i],i, letter[i][0]);
strcpy(msg, "drop");
exec sql drop table test;
......
......@@ -2,25 +2,30 @@
exec sql include header_test;
exec sql type c is char reference;
typedef char* c;
int
main ()
{
typedef struct { long born; short age; } birthinfo;
exec sql type birthinfo is struct { long born; short age; };
exec sql begin declare section;
struct personal_struct { varchar name[8];
struct birth_struct { long born;
short age;
} birth;
birthinfo birth;
} personal;
struct personal_indicator { short ind_name;
struct birth_indicator { short ind_born;
int ind_age;
} ind_birth;
struct personal_indicator { int ind_name;
birthinfo ind_birth;
} ind_personal;
long ind_married;
int ind_married;
char married[9];
c testname="Petra";
char *query="select name, born, age, married from meskes where name = :var1";
exec sql end declare section;
exec sql declare cur cursor for
exec sql var ind_married is long;
exec sql declare cur cursor for
select name, born, age, married from meskes;
char msg[128], command[128];
......@@ -36,11 +41,11 @@ exec sql declare cur cursor for
exec sql create table meskes(name char(8), born integer, age smallint, married char(8));
strcpy(msg, "insert");
exec sql insert into meskes(name, born, age, married) values ('Petra', 19661202, 32, '19900404');
exec sql insert into meskes(name, married) values ('Petra', '19900404');
exec sql insert into meskes(name, born, age, married) values ('Michael', 19660117, 33, '19900404');
exec sql insert into meskes(name, born, age) values ('Carsten', 19910103, 7);
exec sql insert into meskes(name, born, age) values ('Marc', 19930907, 4);
exec sql insert into meskes(name, born, age) values ('Chris', 19970923, 0);
exec sql insert into meskes(name, born, age) values ('Carsten', 19910103, 8);
exec sql insert into meskes(name, born, age) values ('Marc', 19930907, 5);
exec sql insert into meskes(name, born, age) values ('Chris', 19970923, 1);
strcpy(msg, "commit");
exec sql commit;
......@@ -53,12 +58,44 @@ exec sql declare cur cursor for
while (1) {
strcpy(msg, "fetch");
exec sql fetch in cur into :personal:ind_personal, :married:ind_married;
printf ("%8.8s was born %d (age = %d) %s%s\n", personal.name.arr, personal.birth.born, personal.birth.age, ind_married ? "" : "and married ", ind_married ? "" : married);
printf("%8.8s", personal.name.arr);
if (!ind_personal.ind_birth.born)
printf(", born %d", personal.birth.born);
if (!ind_personal.ind_birth.age)
printf(", age = %d", personal.birth.age);
if (!ind_married)
printf(", married %s", married);
putchar('\n');
}
strcpy(msg, "close");
exec sql close cur;
/* and now the same query with prepare */
exec sql prepare MM from :query;
exec sql declare prep cursor for MM;
strcpy(msg, "open");
exec sql open prep using :testname;
exec sql whenever not found do break;
while (1) {
strcpy(msg, "fetch");
exec sql fetch in prep into :personal:ind_personal, :married:ind_married;
printf("%8.8s", personal.name.arr);
if (!ind_personal.ind_birth.born)
printf(", born %d", personal.birth.born);
if (!ind_personal.ind_birth.age)
printf(", age = %d", personal.birth.age);
if (!ind_married)
printf(", married %s", married);
putchar('\n');
}
strcpy(msg, "close");
exec sql close prep;
strcpy(msg, "drop");
exec sql drop table meskes;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment