Commit 64e35e14 authored by Marc G. Fournier's avatar Marc G. Fournier

Upgrade ECPG to 2.0

Michael Meskes <meskes@topsystem.de>
parent 5e6b0a57
...@@ -86,3 +86,43 @@ Fri Mar 13 13:35:13 CET 1998 ...@@ -86,3 +86,43 @@ Fri Mar 13 13:35:13 CET 1998
Mon Mar 16 15:09:10 CET 1998 Mon Mar 16 15:09:10 CET 1998
- fixed parser to print correct filename and line number - fixed parser to print correct filename and line number
Wed Mar 18 14:28:49 CET 1998
- started working on indicator variables
Mon Mar 23 13:49:15 CET 1998
- fixed some bugs in indicator variable handling
- completely rewrote parser for fetch and insert statements
- indicator variables are also allowed in insert statements now
Mon Mar 23 16:09:05 CET 1998
- fixed whenever command goto to only allow valid lables
Thu Mar 26 13:33:02 MEZ 1998
- some minor bugfixes
Mon Apr 20 13:06:09 CEST 1998
- database name no longer has to entered as string constant, i.e.
just remove the '...' around the name
Mon Apr 20 14:38:45 CEST 1998
- both test cases compile cleanly
Mon Apr 20 16:13:25 CEST 1998
- Phew! Finally finished parser rewriting.
Mon Apr 20 16:39:23 CEST 1998
- Cursor is opened when the open command is issued, not at declare time.
Tue Apr 21 12:53:49 CEST 1998
- Set indicator to amount of data really written (truncation).
...@@ -2,15 +2,6 @@ This list is still from Linus. MM ...@@ -2,15 +2,6 @@ This list is still from Linus. MM
The variables should be static. The variables should be static.
Preprocessor cannot do syntax checking on your SQL statements Whatever you
write is copied more or less exactly to the PostgreSQL and you will not be
able to locate your errors until run-time.
No restriction to strings only The PQ interface, and most of all the PQexec
function, that is used by the ecpg relies on that the request is built up as
a string. In some cases, like when the data contains the null character,
this will be a serious problem.
There should be different error numbers for the different errors instead of There should be different error numbers for the different errors instead of
just -1 for them all. just -1 for them all.
...@@ -21,12 +12,6 @@ ecpg it is done for compatibility reasons only. For them to improve speed ...@@ -21,12 +12,6 @@ ecpg it is done for compatibility reasons only. For them to improve speed
would require a lot more insight in the postgres internal mechanisms than I would require a lot more insight in the postgres internal mechanisms than I
possess. possess.
Oracle has indicator variables that tell if a value is null or if it is
empty. This largely simplifies array operations and provides for a way to
hack around some design flaws in the handling of VARCHAR2 (like that an
empty string isn't distinguishable from a null value). I am not sure if this
is an Oracle extension or part of the ANSI standard.
As well as complex types like records and arrays, typedefs would be a good As well as complex types like records and arrays, typedefs would be a good
thing to take care of. thing to take care of.
...@@ -43,8 +28,6 @@ Now comes my list (MM): ...@@ -43,8 +28,6 @@ Now comes my list (MM):
The return code is alway -1 in case of an error. You cannot see which error The return code is alway -1 in case of an error. You cannot see which error
occured by examining the return code. occured by examining the return code.
The cursor is opened when the declare statement is issued.
ecpg does not understand enum datatypes. ecpg does not understand enum datatypes.
There is no exec sql prepare statement. There is no exec sql prepare statement.
...@@ -59,7 +42,16 @@ There is no way yet to fill a complete array with one call except arrays of ...@@ -59,7 +42,16 @@ There is no way yet to fill a complete array with one call except arrays of
ecpg cannot use pointer variables except [unsigned] char * ecpg cannot use pointer variables except [unsigned] char *
List all commands as sqlcommand, not just S_SYMBOL or even better rewrite give back the number of tuples affected via sqlca
pareser to be equivalent to backend´s parser.
exec sql disconnect {current|default|all|connectionname|connection_hostvar};
oder <disconnect statement> ::=
DISCONNECT <disconnect object>
<disconnect object> ::=
<connection object>
| ALL
| CURRENT
commit release|commit work release auch disconnect
Set standard include paths. It is not neccessary to check for sql not found after all commands.
...@@ -6,13 +6,13 @@ all clean:: ...@@ -6,13 +6,13 @@ all clean::
@echo Nothing to be done. @echo Nothing to be done.
install:: install::
$(INSTALL) $(INSTLOPTS) ecpglib.h $(HEADERDIR) $(INSTALL) $(INSTLOPTS) ecpglib.h $(DESTDIR)$(HEADERDIR)
$(INSTALL) $(INSTLOPTS) ecpgtype.h $(HEADERDIR) $(INSTALL) $(INSTLOPTS) ecpgtype.h $(DESTDIR)$(HEADERDIR)
$(INSTALL) $(INSTLOPTS) sqlca.h $(HEADERDIR) $(INSTALL) $(INSTLOPTS) sqlca.h $(DESTDIR)$(HEADERDIR)
uninstall:: uninstall::
rm -f $(HEADERDIR)/ecpglib.h rm -f $(DESTDIR)$(HEADERDIR)/ecpglib.h
rm -f $(HEADERDIR)/ecpgtype.h rm -f $(DESTDIR)$(HEADERDIR)/ecpgtype.h
rm -f $(HEADERDIR)/sqlca.h rm -f $(DESTDIR)$(HEADERDIR)/sqlca.h
dep depend: dep depend:
...@@ -13,11 +13,6 @@ bool ECPGstatus(void); ...@@ -13,11 +13,6 @@ bool ECPGstatus(void);
void ECPGlog(const char *format,...); void ECPGlog(const char *format,...);
/* These functions are only kept for compatibility reasons. */
/* Use ECPGtrans instead. */
bool ECPGcommit(int);
bool ECPGrollback(int);
#ifdef LIBPQ_FE_H #ifdef LIBPQ_FE_H
bool ECPGsetdb(PGconn *); bool ECPGsetdb(PGconn *);
......
...@@ -43,7 +43,8 @@ enum ECPGttype ...@@ -43,7 +43,8 @@ enum ECPGttype
ECPGt_array, ECPGt_array,
ECPGt_record, ECPGt_record,
ECPGt_EOIT, /* End of insert types. */ ECPGt_EOIT, /* End of insert types. */
ECPGt_EORT /* End of result types. */ ECPGt_EORT, /* End of result types. */
ECPGt_NO_INDICATOR /* no indicator */
}; };
#define IS_SIMPLE_TYPE(type) ((type) >= ECPGt_char && (type) <= ECPGt_varchar2) #define IS_SIMPLE_TYPE(type) ((type) >= ECPGt_char && (type) <= ECPGt_varchar2)
......
...@@ -3,8 +3,8 @@ include $(SRCDIR)/Makefile.global ...@@ -3,8 +3,8 @@ include $(SRCDIR)/Makefile.global
PQ_INCLUDE=-I$(SRCDIR)/interfaces/libpq PQ_INCLUDE=-I$(SRCDIR)/interfaces/libpq
SO_MAJOR_VERSION=1 SO_MAJOR_VERSION=2
SO_MINOR_VERSION=1 SO_MINOR_VERSION=0
PORTNAME=@PORTNAME@ PORTNAME=@PORTNAME@
...@@ -16,6 +16,7 @@ endif ...@@ -16,6 +16,7 @@ endif
shlib := shlib :=
install-shlib-dep := install-shlib-dep :=
ifeq ($(PORTNAME), linux) ifeq ($(PORTNAME), linux)
LINUX_ELF=@LINUX_ELF@
ifdef LINUX_ELF ifdef LINUX_ELF
install-shlib-dep := install-shlib install-shlib-dep := install-shlib
shlib := libecpg.so.$(SO_MAJOR_VERSION).$(SO_MINOR_VERSION) shlib := libecpg.so.$(SO_MAJOR_VERSION).$(SO_MINOR_VERSION)
......
...@@ -96,10 +96,11 @@ ECPGdo(int lineno, char *query,...) ...@@ -96,10 +96,11 @@ ECPGdo(int lineno, char *query,...)
*/ */
while (type != ECPGt_EOIT) while (type != ECPGt_EOIT)
{ {
void *value = NULL; void *value = NULL, *ind_value;
long varcharsize; long varcharsize, ind_varcharsize;
long size; long size, ind_size;
long arrsize; long arrsize, ind_arrsize;
enum ECPGttype ind_type;
char *newcopy; char *newcopy;
char *mallocedval = NULL; char *mallocedval = NULL;
...@@ -117,7 +118,38 @@ ECPGdo(int lineno, char *query,...) ...@@ -117,7 +118,38 @@ ECPGdo(int lineno, char *query,...)
varcharsize = va_arg(ap, long); varcharsize = va_arg(ap, long);
size = va_arg(ap, long); size = va_arg(ap, long);
arrsize = va_arg(ap, long); arrsize = va_arg(ap, long);
ind_type = va_arg(ap, enum ECPGttype);
ind_value = va_arg(ap, void *);
ind_varcharsize = va_arg(ap, long);
ind_size = va_arg(ap, long);
ind_arrsize = va_arg(ap, long);
buff[0] = '\0';
/* check for null value and set input buffer accordingly */
switch (ind_type)
{
case ECPGt_short:
case ECPGt_unsigned_short:
if (*(short *) ind_value < 0)
strcpy(buff, "null");
break;
case ECPGt_int:
case ECPGt_unsigned_int:
if (*(int *) ind_value < 0)
strcpy(buff, "null");
break;
case ECPGt_long:
case ECPGt_unsigned_long:
if (*(long *) ind_value < 0L)
strcpy(buff, "null");
break;
default:
break;
}
if (*buff == '\0')
{
switch (type) switch (type)
{ {
case ECPGt_short: case ECPGt_short:
...@@ -206,6 +238,9 @@ ECPGdo(int lineno, char *query,...) ...@@ -206,6 +238,9 @@ ECPGdo(int lineno, char *query,...)
return false; return false;
break; break;
} }
}
else
tobeinserted = buff;
/* /*
* Now tobeinserted points to an area that is to be inserted at * Now tobeinserted points to an area that is to be inserted at
...@@ -266,7 +301,7 @@ ECPGdo(int lineno, char *query,...) ...@@ -266,7 +301,7 @@ ECPGdo(int lineno, char *query,...)
if (committed) if (committed)
{ {
if ((results = PQexec(simple_connection, "begin")) == NULL) if ((results = PQexec(simple_connection, "begin transaction")) == NULL)
{ {
register_error(-1, "Error starting transaction line %d.", lineno); register_error(-1, "Error starting transaction line %d.", lineno);
return false; return false;
...@@ -324,10 +359,11 @@ ECPGdo(int lineno, char *query,...) ...@@ -324,10 +359,11 @@ ECPGdo(int lineno, char *query,...)
for (x = 0; x < m && status; x++) for (x = 0; x < m && status; x++)
{ {
void *value = NULL; void *value = NULL, *ind_value;
long varcharsize; long varcharsize, ind_varcharsize;
long size; long size, ind_size;
long arrsize; long arrsize, ind_arrsize;
enum ECPGttype ind_type;
char *pval = PQgetvalue(results, 0, x); char *pval = PQgetvalue(results, 0, x);
...@@ -339,13 +375,37 @@ ECPGdo(int lineno, char *query,...) ...@@ -339,13 +375,37 @@ ECPGdo(int lineno, char *query,...)
ECPGlog("ECPGdo line %d: RESULT: %s\n", lineno, pval ? pval : ""); ECPGlog("ECPGdo line %d: RESULT: %s\n", lineno, pval ? pval : "");
/* No the pval is a pointer to the value. */ /* Now the pval is a pointer to the value. */
/* We will have to decode the value */ /* We will have to decode the value */
type = va_arg(ap, enum ECPGttype); type = va_arg(ap, enum ECPGttype);
value = va_arg(ap, void *); value = va_arg(ap, void *);
varcharsize = va_arg(ap, long); varcharsize = va_arg(ap, long);
size = va_arg(ap, long); size = va_arg(ap, long);
arrsize = va_arg(ap, long); arrsize = va_arg(ap, long);
ind_type = va_arg(ap, enum ECPGttype);
ind_value = va_arg(ap, void *);
ind_varcharsize = va_arg(ap, long);
ind_size = va_arg(ap, long);
ind_arrsize = va_arg(ap, long);
/* check for null value and set indicator accordingly */
switch (ind_type)
{
case ECPGt_short:
case ECPGt_unsigned_short:
*(short *) ind_value = -PQgetisnull(results, 0, x);
break;
case ECPGt_int:
case ECPGt_unsigned_int:
*(int *) ind_value = -PQgetisnull(results, 0, x);
break;
case ECPGt_long:
case ECPGt_unsigned_long:
*(long *) ind_value = -PQgetisnull(results, 0, x);
break;
default:
break;
}
switch (type) switch (type)
{ {
...@@ -486,7 +546,30 @@ ECPGdo(int lineno, char *query,...) ...@@ -486,7 +546,30 @@ ECPGdo(int lineno, char *query,...)
((char *) value)[strlen(pval)] = '\0'; ((char *) value)[strlen(pval)] = '\0';
} }
else else
{
strncpy((char *) value, pval, varcharsize); strncpy((char *) value, pval, varcharsize);
if (varcharsize < strlen(pval))
{
/* truncation */
switch (ind_type)
{
case ECPGt_short:
case ECPGt_unsigned_short:
*(short *) ind_value = varcharsize;
break;
case ECPGt_int:
case ECPGt_unsigned_int:
*(int *) ind_value = varcharsize;
break;
case ECPGt_long:
case ECPGt_unsigned_long:
*(long *) ind_value = varcharsize;
break;
default:
break;
}
}
}
} }
break; break;
...@@ -498,8 +581,29 @@ ECPGdo(int lineno, char *query,...) ...@@ -498,8 +581,29 @@ ECPGdo(int lineno, char *query,...)
strncpy(var->arr, pval, varcharsize); strncpy(var->arr, pval, varcharsize);
var->len = strlen(pval); var->len = strlen(pval);
if (var->len > varcharsize) if (var->len > varcharsize)
{
/* truncation */
switch (ind_type)
{
case ECPGt_short:
case ECPGt_unsigned_short:
*(short *) ind_value = varcharsize;
break;
case ECPGt_int:
case ECPGt_unsigned_int:
*(int *) ind_value = varcharsize;
break;
case ECPGt_long:
case ECPGt_unsigned_long:
*(long *) ind_value = varcharsize;
break;
default:
break;
}
var->len = varcharsize; var->len = varcharsize;
} }
}
break; break;
case ECPGt_EORT: case ECPGt_EORT:
...@@ -587,19 +691,6 @@ ECPGtrans(int lineno, const char * transaction) ...@@ -587,19 +691,6 @@ ECPGtrans(int lineno, const char * transaction)
return (TRUE); return (TRUE);
} }
/* include these for compatibility */
bool
ECPGcommit(int lineno)
{
return(ECPGtrans(lineno, "end"));
}
bool
ECPGrollback(int lineno)
{
return(ECPGtrans(lineno, "abort"));
}
bool bool
ECPGsetdb(PGconn *newcon) ECPGsetdb(PGconn *newcon)
{ {
......
SRCDIR= ../../.. SRCDIR= ../../..
include $(SRCDIR)/Makefile.global include $(SRCDIR)/Makefile.global
MAJOR_VERSION=1 MAJOR_VERSION=2
MINOR_VERSION=1 MINOR_VERSION=0
PATCHLEVEL=0 PATCHLEVEL=0
CFLAGS+=-I../include -DMAJOR_VERSION=$(MAJOR_VERSION) \ CFLAGS+=-I../include -DMAJOR_VERSION=$(MAJOR_VERSION) \
-DMINOR_VERSION=$(MINOR_VERSION) -DPATCHLEVEL=$(PATCHLEVEL) \ -DMINOR_VERSION=$(MINOR_VERSION) -DPATCHLEVEL=$(PATCHLEVEL) \
-DINCLUDE_PATH=\"$(HEADERDIR)\" -DINCLUDE_PATH=\"$(DESTDIR)$(HEADERDIR)\"
OBJ=y.tab.o pgc.o type.o ecpg.o ecpg_keywords.o ../../../backend/parser/scansup.o \
keywords.o c_keywords.o ../lib/typename.o
all:: ecpg all:: ecpg
...@@ -15,21 +18,22 @@ clean: ...@@ -15,21 +18,22 @@ clean:
rm -f *.o core a.out ecpg y.tab.h y.tab.c pgc.c *~ rm -f *.o core a.out ecpg y.tab.h y.tab.c pgc.c *~
install: all install: all
$(INSTALL) $(INSTL_EXE_OPTS) ecpg $(BINDIR) $(INSTALL) $(INSTL_EXE_OPTS) ecpg $(DESTDIR)$(BINDIR)
uninstall: uninstall:
rm -f $(BINDIR)/ecpg rm -f $(DESTDIR)$(BINDIR)/ecpg
dep depend:
$(CC) -MM $(CFLAGS) *.c > depend
# Rule that really do something. # Rule that really do something.
ecpg: y.tab.o pgc.o type.o ecpg.o ../lib/typename.o ecpg: $(OBJ)
$(CC) -o ecpg y.tab.o pgc.o type.o ecpg.o ../lib/typename.o $(LEXLIB) $(LDFLAGS) $(CC) -o ecpg $(OBJ) $(LEXLIB)
y.tab.h y.tab.c: preproc.y y.tab.h y.tab.c: preproc.y
$(YACC) $(YFLAGS) $< $(YACC) $(YFLAGS) $<
y.tab.o : y.tab.h ../include/ecpgtype.h y.tab.o : y.tab.h ../include/ecpgtype.h keywords.c c_keywords.c ecpg_keywords.c
type.o : ../include/ecpgtype.h type.o : ../include/ecpgtype.h
pgc.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
/*-------------------------------------------------------------------------
*
* keywords.c--
* lexical token lookup for reserved words in postgres embedded SQL
*
*-------------------------------------------------------------------------
*/
#include <ctype.h>
#include <string.h>
#include "postgres.h"
#include "type.h"
#include "y.tab.h"
#include "extern.h"
/*
* List of (keyword-name, keyword-token-value) pairs.
*
* !!WARNING!!: This list must be sorted, because binary
* search is used to locate entries.
*/
static ScanKeyword ScanKeywords[] = {
/* name value */
{"auto", S_AUTO},
{"bool", S_BOOL},
{"char", S_CHAR},
{"const", S_CONST},
{"double", S_DOUBLE},
{"extern", S_EXTERN},
{"float", S_FLOAT},
{"int", S_INT},
{"long", S_LONG},
{"register", S_REGISTER},
{"short", S_SHORT},
{"signed", S_SIGNED},
{"static", S_STATIC},
{"struct", S_STRUCT},
{"unsigned", S_UNSIGNED},
{"varchar", S_VARCHAR},
};
ScanKeyword *
ScanCKeywordLookup(char *text)
{
ScanKeyword *low = &ScanKeywords[0];
ScanKeyword *high = endof(ScanKeywords) - 1;
ScanKeyword *middle;
int difference;
while (low <= high)
{
middle = low + (high - low) / 2;
difference = strcmp(middle->name, text);
if (difference == 0)
return (middle);
else if (difference < 0)
low = middle + 1;
else
high = middle - 1;
}
return (NULL);
}
...@@ -91,7 +91,7 @@ main(int argc, char *const argv[]) ...@@ -91,7 +91,7 @@ main(int argc, char *const argv[])
/* after the options there must not be anything but filenames */ /* after the options there must not be anything but filenames */
for (fnr = optind; fnr < argc; fnr++) for (fnr = optind; fnr < argc; fnr++)
{ {
char *ptr2ext; char *output_filename = NULL, *ptr2ext;
input_filename = mm_alloc(strlen(argv[fnr]) + 5); input_filename = mm_alloc(strlen(argv[fnr]) + 5);
...@@ -113,7 +113,7 @@ main(int argc, char *const argv[]) ...@@ -113,7 +113,7 @@ main(int argc, char *const argv[])
if (out_option == 0)/* calculate the output name */ if (out_option == 0)/* calculate the output name */
{ {
char *output_filename = strdup(input_filename); output_filename = strdup(input_filename);
ptr2ext = strrchr(output_filename, '.'); ptr2ext = strrchr(output_filename, '.');
/* make extension = .c */ /* make extension = .c */
...@@ -128,7 +128,6 @@ main(int argc, char *const argv[]) ...@@ -128,7 +128,6 @@ main(int argc, char *const argv[])
free(input_filename); free(input_filename);
continue; continue;
} }
free(output_filename);
} }
yyin = fopen(input_filename, "r"); yyin = fopen(input_filename, "r");
...@@ -136,9 +135,25 @@ main(int argc, char *const argv[]) ...@@ -136,9 +135,25 @@ main(int argc, char *const argv[])
perror(argv[fnr]); perror(argv[fnr]);
else else
{ {
struct cursor *ptr;
/* initialize lex */ /* initialize lex */
lex_init(); lex_init();
/* initialize cursor list */
for (ptr = cur; ptr != NULL;)
{
struct cursor *c;
free(ptr->name);
free(ptr->command);
c = ptr;
ptr = ptr->next;
free(c);
}
cur = NULL;
/* we need two includes */ /* we need two includes */
fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n/*These two include files are added by the preprocessor */\n#include <ecpgtype.h>\n#include <ecpglib.h>\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL); fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n/*These two include files are added by the preprocessor */\n#include <ecpgtype.h>\n#include <ecpglib.h>\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL);
...@@ -150,6 +165,10 @@ main(int argc, char *const argv[]) ...@@ -150,6 +165,10 @@ main(int argc, char *const argv[])
if (out_option == 0) if (out_option == 0)
fclose(yyout); fclose(yyout);
} }
if (output_filename)
free(output_filename);
free(input_filename); free(input_filename);
} }
} }
......
/*-------------------------------------------------------------------------
*
* keywords.c--
* lexical token lookup for reserved words in postgres embedded SQL
*
*-------------------------------------------------------------------------
*/
#include <ctype.h>
#include <string.h>
#include "postgres.h"
#include "type.h"
#include "y.tab.h"
#include "extern.h"
/*
* List of (keyword-name, keyword-token-value) pairs.
*
* !!WARNING!!: This list must be sorted, because binary
* search is used to locate entries.
*/
static ScanKeyword ScanKeywords[] = {
/* name value */
{"connect", SQL_CONNECT},
{"continue", SQL_CONTINUE},
{"found", SQL_FOUND},
{"go", SQL_GO},
{"goto", SQL_GOTO},
{"immediate", SQL_IMMEDIATE},
{"indicator", SQL_INDICATOR},
{"open", SQL_OPEN},
{"section", SQL_SECTION},
{"sqlerror", SQL_SQLERROR},
{"sqlprint", SQL_SQLPRINT},
{"stop", SQL_STOP},
{"whenever", SQL_WHENEVER},
};
ScanKeyword *
ScanECPGKeywordLookup(char *text)
{
ScanKeyword *low = &ScanKeywords[0];
ScanKeyword *high = endof(ScanKeywords) - 1;
ScanKeyword *middle;
int difference;
while (low <= high)
{
middle = low + (high - low) / 2;
difference = strcmp(middle->name, text);
if (difference == 0)
return (middle);
else if (difference < 0)
low = middle + 1;
else
high = middle - 1;
}
return (NULL);
}
#include "parser/keywords.h"
/* variables */ /* variables */
extern int debugging, extern int debugging,
...@@ -14,9 +16,19 @@ struct _include_path { char * path; ...@@ -14,9 +16,19 @@ struct _include_path { char * path;
extern struct _include_path *include_paths; extern struct _include_path *include_paths;
struct cursor { char *name;
char *command;
struct cursor *next;
};
extern struct cursor *cur;
/* functions */ /* functions */
extern void lex_init(void); extern void lex_init(void);
extern char *input_filename; extern char *input_filename;
extern int yyparse(void); extern int yyparse(void);
extern void *mm_alloc(size_t), *mm_realloc(void *, size_t); extern void *mm_alloc(size_t), *mm_realloc(void *, size_t);
ScanKeyword * ScanECPGKeywordLookup(char *);
ScanKeyword * ScanCKeywordLookup(char *);
extern void yyerror(char *);
/*-------------------------------------------------------------------------
*
* keywords.c--
* lexical token lookup for reserved words in postgres SQL
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.1 1998/04/21 13:23:06 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include <ctype.h>
#include <string.h>
#include "postgres.h"
#include "nodes/parsenodes.h"
#include "nodes/pg_list.h"
#include "type.h"
#include "y.tab.h"
#include "parser/keywords.h"
#include "utils/elog.h"
/*
* List of (keyword-name, keyword-token-value) pairs.
*
* !!WARNING!!: This list must be sorted, because binary
* search is used to locate entries.
*/
static ScanKeyword ScanKeywords[] = {
/* name value */
{"abort", ABORT_TRANS},
{"action", ACTION},
{"add", ADD},
{"after", AFTER},
{"aggregate", AGGREGATE},
{"all", ALL},
{"alter", ALTER},
{"analyze", ANALYZE},
{"and", AND},
{"any", ANY},
{"archive", ARCHIVE},
{"as", AS},
{"asc", ASC},
{"backward", BACKWARD},
{"before", BEFORE},
{"begin", BEGIN_TRANS},
{"between", BETWEEN},
{"binary", BINARY},
{"both", BOTH},
{"by", BY},
{"cache", CACHE},
{"cascade", CASCADE},
{"cast", CAST},
{"char", CHAR},
{"character", CHARACTER},
{"check", CHECK},
{"close", CLOSE},
{"cluster", CLUSTER},
{"collate", COLLATE},
{"column", COLUMN},
{"commit", COMMIT},
{"constraint", CONSTRAINT},
{"copy", COPY},
{"create", CREATE},
{"createdb", CREATEDB},
{"createuser", CREATEUSER},
{"cross", CROSS},
{"current", CURRENT},
{"current_date", CURRENT_DATE},
{"current_time", CURRENT_TIME},
{"current_timestamp", CURRENT_TIMESTAMP},
{"current_user", CURRENT_USER},
{"cursor", CURSOR},
{"cycle", CYCLE},
{"database", DATABASE},
{"day", DAY_P},
{"decimal", DECIMAL},
{"declare", DECLARE},
{"default", DEFAULT},
{"delete", DELETE},
{"delimiters", DELIMITERS},
{"desc", DESC},
{"distinct", DISTINCT},
{"do", DO},
{"double", DOUBLE},
{"drop", DROP},
{"each", EACH},
{"end", END_TRANS},
{"execute", EXECUTE},
{"exists", EXISTS},
{"explain", EXPLAIN},
{"extend", EXTEND},
{"extract", EXTRACT},
{"false", FALSE_P},
{"fetch", FETCH},
{"float", FLOAT},
{"for", FOR},
{"foreign", FOREIGN},
{"forward", FORWARD},
{"from", FROM},
{"full", FULL},
{"function", FUNCTION},
{"grant", GRANT},
{"group", GROUP},
{"handler", HANDLER},
{"having", HAVING},
{"hour", HOUR_P},
{"in", IN},
{"increment", INCREMENT},
{"index", INDEX},
{"inherits", INHERITS},
{"inner", INNER_P},
{"insert", INSERT},
{"instead", INSTEAD},
{"interval", INTERVAL},
{"into", INTO},
{"is", IS},
{"isnull", ISNULL},
{"join", JOIN},
{"key", KEY},
{"lancompiler", LANCOMPILER},
{"language", LANGUAGE},
{"leading", LEADING},
{"left", LEFT},
{"like", LIKE},
{"listen", LISTEN},
{"load", LOAD},
{"local", LOCAL},
{"location", LOCATION},
{"lock", LOCK_P},
{"match", MATCH},
{"maxvalue", MAXVALUE},
{"minute", MINUTE_P},
{"minvalue", MINVALUE},
{"month", MONTH_P},
{"move", MOVE},
{"national", NATIONAL},
{"natural", NATURAL},
{"nchar", NCHAR},
{"new", NEW},
{"no", NO},
{"nocreatedb", NOCREATEDB},
{"nocreateuser", NOCREATEUSER},
{"none", NONE},
{"not", NOT},
{"nothing", NOTHING},
{"notify", NOTIFY},
{"notnull", NOTNULL},
{"null", NULL_P},
{"numeric", NUMERIC},
{"oids", OIDS},
{"on", ON},
{"operator", OPERATOR},
{"option", OPTION},
{"or", OR},
{"order", ORDER},
{"outer", OUTER_P},
{"partial", PARTIAL},
{"password", PASSWORD},
{"position", POSITION},
{"precision", PRECISION},
{"primary", PRIMARY},
{"privileges", PRIVILEGES},
{"procedural", PROCEDURAL},
{"procedure", PROCEDURE},
{"public", PUBLIC},
{"recipe", RECIPE},
{"references", REFERENCES},
{"rename", RENAME},
{"reset", RESET},
{"returns", RETURNS},
{"revoke", REVOKE},
{"right", RIGHT},
{"rollback", ROLLBACK},
{"row", ROW},
{"rule", RULE},
{"second", SECOND_P},
{"select", SELECT},
{"sequence", SEQUENCE},
{"set", SET},
{"setof", SETOF},
{"show", SHOW},
{"start", START},
{"statement", STATEMENT},
{"stdin", STDIN},
{"stdout", STDOUT},
{"substring", SUBSTRING},
{"table", TABLE},
{"time", TIME},
{"to", TO},
{"trailing", TRAILING},
{"transaction", TRANSACTION},
{"trigger", TRIGGER},
{"trim", TRIM},
{"true", TRUE_P},
{"trusted", TRUSTED},
{"type", TYPE_P},
{"union", UNION},
{"unique", UNIQUE},
{"until", UNTIL},
{"update", UPDATE},
{"user", USER},
{"using", USING},
{"vacuum", VACUUM},
{"valid", VALID},
{"values", VALUES},
{"varchar", VARCHAR},
{"varying", VARYING},
{"verbose", VERBOSE},
{"version", VERSION},
{"view", VIEW},
{"where", WHERE},
{"with", WITH},
{"work", WORK},
{"year", YEAR_P},
{"zone", ZONE},
};
ScanKeyword *
ScanKeywordLookup(char *text)
{
ScanKeyword *low = &ScanKeywords[0];
ScanKeyword *high = endof(ScanKeywords) - 1;
ScanKeyword *middle;
int difference;
while (low <= high)
{
middle = low + (high - low) / 2;
difference = strcmp(middle->name, text);
if (difference == 0)
return (middle);
else if (difference < 0)
low = middle + 1;
else
high = middle - 1;
}
return (NULL);
}
/* Copyright comment! */ /* This is a modified version of src/backend/parser/scan.l */
%{ %{
#include "config.h" #include "config.h"
#include <ctype.h>
#include <sys/types.h> #include <sys/types.h>
#include <limits.h> #include <limits.h>
#if defined(HAVE_STRING_H) #if defined(HAVE_STRING_H)
...@@ -8,101 +10,428 @@ ...@@ -8,101 +10,428 @@
#else #else
#include <strings.h> #include <strings.h>
#endif #endif
#include <errno.h>
#include "postgres.h"
#include "miscadmin.h"
#include "nodes/pg_list.h"
#include "nodes/parsenodes.h"
#include "parser/gramparse.h"
#include "parser/scansup.h"
#include "type.h" #include "type.h"
#include "y.tab.h" #include "y.tab.h"
#include "utils/builtins.h"
#include "extern.h" #include "extern.h"
/* some versions of lex define this as a macro */
#if defined(yywrap)
#undef yywrap
#endif /* yywrap */
int debugging = 0;
extern YYSTYPE yylval;
int llen;
char literal[MAX_PARSE_BUFFER];
struct _yy_buffer { YY_BUFFER_STATE buffer; struct _yy_buffer { YY_BUFFER_STATE buffer;
long lineno; long lineno;
char * filename; char * filename;
struct _yy_buffer * next; struct _yy_buffer * next;
} *yy_buffer = NULL; } *yy_buffer = NULL;
#define dbg(arg) if (debugging) fprintf(stderr, "DEBUG, %d: %s\n", yylineno, #arg);
%} %}
%option yylineno %option yylineno
%s C SQL incl %s C SQL incl
ccomment \/\*([^*]|\*[^/]|\*\*[^/])*\*\/ /* OK, here is a short description of lex/flex rules behavior.
ws ([ \t\n][ \t\n]*|{ccomment})* * The longest pattern which matches an input string is always chosen.
letter [A-Za-z_] * For equal-length patterns, the first occurring in the rules list is chosen.
* INITIAL is the starting condition, to which all non-conditional rules apply.
* When in an exclusive condition, only those rules defined for that condition apply.
*
* Exclusive states change parsing rules while the state is active.
* There are exclusive states for quoted strings, extended comments,
* and to eliminate parsing troubles for numeric strings.
* Exclusive states:
* <xb> binary numeric string - thomas 1997-11-16
* <xc> extended C-style comments - tgl 1997-07-12
* <xd> delimited identifiers (double-quoted identifiers) - tgl 1997-10-27
* <xh> hexadecimal numeric string - thomas 1997-11-16
* <xm> numeric strings with embedded minus sign - tgl 1997-09-05
* <xq> quoted strings - tgl 1997-07-30
*
* The "extended comment" syntax closely resembles allowable operator syntax.
* So, when in condition <xc>, only strings which would terminate the
* "extended comment" trigger any action other than "ignore".
* Be sure to match _any_ candidate comment, including those with appended
* operator-like symbols. - thomas 1997-07-14
*/
%x xb
%x xc
%x xd
%x xh
%x xm
%x xq
/* Binary number
*/
xbstart [bB]{quote}
xbstop {quote}
xbinside [^']*
xbcat {quote}{space}*\n{space}*{quote}
/* Hexadecimal number
*/
xhstart [xX]{quote}
xhstop {quote}
xhinside [^']*
xhcat {quote}{space}*\n{space}*{quote}
/* Extended quote
* xqdouble implements SQL92 embedded quote
* xqcat allows strings to cross input lines
*/
quote '
xqstart {quote}
xqstop {quote}
xqdouble {quote}{quote}
xqinside [^\\']*
xqembedded "\\'"
xqliteral [\\](.|\n)
xqcat {quote}{space}*\n{space}*{quote}
/* Delimited quote
* Allows embedded spaces and other special characters into identifiers.
*/
dquote \"
xdstart {dquote}
xdstop {dquote}
xdinside [^"]*
/* Comments
* Ignored by the scanner and parser.
*/
xcline [\/][\*].*[\*][\/]{space}*\n*
xcstart [\/][\*]{op_and_self}*
xcstop {op_and_self}*[\*][\/]({space}*|\n)
xcinside [^*]*
xcstar [^/]
digit [0-9] digit [0-9]
length {digit}+ number [-+.0-9Ee]
symbol {letter}({letter}|{digit})* letter [\200-\377_A-Za-z]
label ({letter}|{digit})* letter_or_digit [\200-\377_A-Za-z0-9]
string '[^']*'
identifier {letter}{letter_or_digit}*
abort [aA][bB][oO][rR][tT]
begin [bB][eE][gG][iI][nN] typecast "::"
commit [cC][oO][mM][mM][iI][tT]
connect [cC][oO][nN][nN][eE][cC][tT] self [,()\[\].$\:\+\-\*\/\<\>\=\|]
continue [cC][oO][nN][tT][iI][nN][uU][eE] op_and_self [\~\!\@\#\%\^\&\|\`\?\$\:\+\-\*\/\<\>\=]
declare [dD][eE][cC][lL][aA][rR][eE] operator {op_and_self}+
do [dD][oO]
end [eE][nN][dD] xminteger {integer}/-
xmreal {real}/{space}*-{digit}
xmstop -
integer -?{digit}+
real -?{digit}+\.{digit}+([Ee][-+]?{digit}+)?
param \${integer}
comment ("--"|"//").*\n
space [ \t\n\f]
other .
/* some stuff needed for ecpg */
ccomment \/\*([^*]|\*[^/]|\*\*[^/])*\*\/
exec [eE][xX][eE][cC] exec [eE][xX][eE][cC]
execute [eE][xX][eE][cC][uU][tT][eE]
fetch [fF][eE][tT][cC][hH]
found [fF][oO][uU][nN][dD]
from [fF][rR][oO][mM]
go [gG][oO]
goto [gG][oO][tT][oO]
immediate [iI][mM][mM][eE][dD][iI][aA][tT][eE]
include [iI][nN][cC][lL][uU][dD][eE] include [iI][nN][cC][lL][uU][dD][eE]
in [iI][nN]
into [iI][nN][tT][oO]
not [nN][oO][tT]
open [oO][pP][eE][nN]
release [rR][eE][lL][eE][aA][sS][eE]
rollback [rR][oO][lL][lL][bB][aA][cC][kK]
section [sS][eE][cC][tT][iI][oO][nN]
sql [sS][qQ][lL] sql [sS][qQ][lL]
sqlerror [sS][qQ][lL][eE][rR][rR][oO][rR]
sqlprint [sS][qQ][lL][pP][rR][iI][nN][tT] /* DO NOT PUT ANY COMMENTS IN THE FOLLOWING SECTION.
stop [sS][tT][oO][pP] * AT&T lex does not properly handle C-style comments in this second lex block.
transaction [tT][rR][aA][nN][sS][aA][cC][tT][iI][oO][nN] * So, put comments here. tgl - 1997-09-08
to [tT][oO] *
varchar [vV][aA][rR][cC][hH][aA][rR] * Quoted strings must allow some special characters such as single-quote
varchar2 [vV][aA][rR][cC][hH][aA][rR]2 * and newline.
whenever [wW][hH][eE][nN][eE][vV][eE][rR] * Embedded single-quotes are implemented both in the SQL/92-standard
work [wW][oO][rR][kK] * style of two adjacent single quotes "''" and in the Postgres/Java style
vacuum [vV][aA][cC][uU][uU][mM] * of escaped-quote "\'".
* Other embedded escaped characters are matched explicitly and the leading
* backslash is dropped from the string. - thomas 1997-09-24
*/
%% %%
<C>{exec}{ws}{sql} { BEGIN SQL; dbg(SQL_START); return SQL_START; } <SQL>{comment} { /* ignore */ }
<SQL>";" { BEGIN C; dbg(SQL_SEMI); return SQL_SEMI; }
<SQL>{abort} { dbg(SQL_ABORT); return SQL_ABORT; } <SQL>{xcline} { /* ignore */ }
<SQL>{begin} { dbg(SQL_BEGIN); return SQL_BEGIN; }
<SQL>{end} { dbg(SQL_END); return SQL_END; } <xc>{xcstar} |
<SQL>{declare} { dbg(SQL_DECLARE); return SQL_DECLARE; } <SQL>{xcstart} { BEGIN(xc); }
<SQL>{execute} { dbg(SQL_EXECUTE); return SQL_EXECUTE; }
<SQL>{immediate} { dbg(SQL_IMMEDIATE); return SQL_IMMEDIATE; } <xc>{xcstop} { BEGIN(SQL); }
<SQL>{section} { dbg(SQL_SECTION); return SQL_SECTION; }
<SQL>{connect} { dbg(SQL_CONNECT); return SQL_CONNECT; } <xc>{xcinside} { /* ignore */ }
<SQL>{open} { dbg(SQL_OPEN); return SQL_OPEN; }
<SQL>{commit} { dbg(SQL_COMMIT); return SQL_COMMIT; } <SQL>{xbstart} {
<SQL>{release} { dbg(SQL_RELEASE); return SQL_RELEASE; } BEGIN(xb);
<SQL>{work} { dbg(SQL_WORK); return SQL_WORK; } llen = 0;
<SQL>{fetch} { dbg(SQL_FETCH); return SQL_FETCH; } *literal = '\0';
<SQL>{rollback} { dbg(SQL_ROLLBACK); return SQL_ROLLBACK; } }
<SQL>{whenever} { dbg(SQL_WHENEVER); return SQL_WHENEVER; } <xb>{xbstop} {
<SQL>{sqlerror} { dbg(SQL_SQLERROR); return SQL_SQLERROR; } char* endptr;
<SQL>{sqlprint} { dbg(SQL_SQLPRINT); return SQL_SQLPRINT; }
<SQL>{not}{ws}{found} { dbg(SQL_NOT_FOUND); return SQL_NOT_FOUND; } BEGIN(SQL);
<SQL>{continue} { dbg(SQL_CONTINUE); return SQL_CONTINUE; } errno = 0;
<SQL>{into} { dbg(SQL_INTO); return SQL_INTO; } yylval.ival = strtol((char *)literal,&endptr,2);
<SQL>{in} { dbg(SQL_IN); return SQL_IN; } if (*endptr != '\0' || errno == ERANGE)
<SQL>{goto} { dbg(SQL_GOTO); return SQL_GOTO; } yyerror("ERROR: Bad binary integer input!");
<SQL>{go}{ws}{to} { dbg(SQL_GOTO); return SQL_GOTO; } return (ICONST);
<SQL>{stop} { dbg(SQL_STOP); return SQL_STOP; } }
<SQL>{do} { dbg(SQL_DO); return SQL_DO; } <xh>{xhinside} |
<SQL>{from} { dbg(SQL_FROM); return SQL_FROM; } <xb>{xbinside} {
<SQL>{transaction} { dbg(SQL_TRANSACTION); return SQL_TRANSACTION; } if ((llen+yyleng) > (MAX_PARSE_BUFFER - 1))
<SQL>{vacuum} { dbg(SQL_VACUUM); return SQL_VACUUM; } yyerror("ERROR: quoted string parse buffer exceeded");
memcpy(literal+llen, yytext, yyleng+1);
llen += yyleng;
<C>{exec}{ws}{sql}{ws}{include} { BEGIN(incl); } }
<incl>{ws} /* eat the whitespace */ <xh>{xhcat} |
<xb>{xbcat} {
}
<SQL>{xhstart} {
BEGIN(xh);
llen = 0;
*literal = '\0';
}
<xh>{xhstop} {
char* endptr;
BEGIN(SQL);
errno = 0;
yylval.ival = strtol((char *)literal,&endptr,16);
if (*endptr != '\0' || errno == ERANGE)
yyerror("ERROR: Bad hexadecimal integer input");
return (ICONST);
}
<SQL>{xqstart} {
BEGIN(xq);
llen = 0;
*literal = '\0';
}
<xq>{xqstop} {
BEGIN(SQL);
yylval.str = strdup(scanstr(literal));
return (SCONST);
}
<xq>{xqdouble} |
<xq>{xqinside} {
if ((llen+yyleng) > (MAX_PARSE_BUFFER - 1))
yyerror("ERROR: quoted string parse buffer exceeded");
memcpy(literal+llen, yytext, yyleng+1);
llen += yyleng;
}
<xq>{xqembedded} {
if ((llen+yyleng-1) > (MAX_PARSE_BUFFER - 1))
yyerror("ERROR: quoted string parse buffer exceeded");
memcpy(literal+llen, yytext, yyleng+1);
*(literal+llen) = '\'';
llen += yyleng;
}
<xq>{xqliteral} {
if ((llen+yyleng-1) > (MAX_PARSE_BUFFER - 1))
yyerror("ERROR: quoted string parse buffer exceeded");
memcpy(literal+llen, yytext, yyleng+1);
llen += yyleng;
}
<xq>{xqcat} {
}
<SQL>{xdstart} {
BEGIN(xd);
llen = 0;
*literal = '\0';
}
<xd>{xdstop} {
BEGIN(SQL);
yylval.str = strdup(literal);
return (IDENT);
}
<xd>{xdinside} {
if ((llen+yyleng) > (MAX_PARSE_BUFFER - 1))
yyerror("ERROR: quoted string parse buffer exceeded");
memcpy(literal+llen, yytext, yyleng+1);
llen += yyleng;
}
<xm>{space}* { /* ignore */ }
<xm>{xmstop} {
BEGIN(SQL);
return (yytext[0]);
}
<SQL>{typecast} { return TYPECAST; }
<SQL>{self}/-[\.0-9] {
return (yytext[0]);
}
<SQL>{self} { return (yytext[0]); }
<SQL>{operator}/-[\.0-9] {
yylval.str = strdup((char*)yytext);
return (Op);
}
<SQL>{operator} {
if (strcmp((char*)yytext,"!=") == 0)
yylval.str = strdup("<>"); /* compatability */
else
yylval.str = strdup((char*)yytext);
return (Op);
}
<SQL>{param} {
yylval.ival = atoi((char*)&yytext[1]);
return (PARAM);
}
<SQL>{identifier}/{space}*-{number} {
int i;
ScanKeyword *keyword;
BEGIN(xm);
for(i = 0; yytext[i]; i++)
if (isupper(yytext[i]))
yytext[i] = tolower(yytext[i]);
keyword = ScanKeywordLookup((char*)yytext);
if (keyword != NULL) {
return (keyword->value);
}
else
{
keyword = ScanECPGKeywordLookup((char*)yytext);
if (keyword != NULL) {
return (keyword->value);
}
else
{
yylval.str = strdup((char*)yytext);
return (IDENT);
}
}
}
{integer}/{space}*-{number} {
char* endptr;
BEGIN(xm);
errno = 0;
yylval.ival = strtol((char *)yytext,&endptr,10);
if (*endptr != '\0' || errno == ERANGE)
{
errno = 0;
yylval.dval = strtod(((char *)yytext),&endptr);
if (*endptr != '\0' || errno == ERANGE)
yyerror("ERROR: Bad integer input");
yyerror("WARNING: Integer input is out of range; promoted to float");
return (FCONST);
}
return (ICONST);
}
{real}/{space}*-{number} {
char* endptr;
BEGIN(xm);
errno = 0;
yylval.dval = strtod(((char *)yytext),&endptr);
if (*endptr != '\0' || errno == ERANGE)
yyerror("ERROR: Bad float8 input");
return (FCONST);
}
{integer} {
char* endptr;
errno = 0;
yylval.ival = strtol((char *)yytext,&endptr,10);
if (*endptr != '\0' || errno == ERANGE)
{
errno = 0;
yylval.dval = strtod(((char *)yytext),&endptr);
if (*endptr != '\0' || errno == ERANGE)
yyerror("ERROR: Bad integer input");
yyerror("WARNING: Integer input is out of range; promoted to float");
return (FCONST);
}
return (ICONST);
}
{real} {
char* endptr;
errno = 0;
yylval.dval = strtod((char *)yytext,&endptr);
if (*endptr != '\0' || errno == ERANGE)
yyerror("ERROR: Bad float input");
return (FCONST);
}
<SQL>{identifier} {
int i;
ScanKeyword *keyword;
for(i = 0; yytext[i]; i++)
if (isupper(yytext[i]))
yytext[i] = tolower(yytext[i]);
keyword = ScanKeywordLookup((char*)yytext);
if (keyword != NULL) {
return (keyword->value);
}
else
{
keyword = ScanECPGKeywordLookup((char*)yytext);
if (keyword != NULL) {
return (keyword->value);
}
else
{
yylval.str = strdup((char*)yytext);
return (IDENT);
}
}
}
<SQL>{space} { /* ignore */ }
<SQL>";" { BEGIN C; return SQL_SEMI; }
<SQL>{other} { return (yytext[0]); }
<C>{exec}{space}{sql} { BEGIN SQL; return SQL_START; }
<C>{identifier} {
ScanKeyword *keyword;
keyword = ScanCKeywordLookup((char*)yytext);
if (keyword != NULL) {
return (keyword->value);
}
else
{
yylval.str = strdup((char*)yytext);
return (IDENT);
}
}
<C>";" { return(';'); }
<C>{space} { ECHO; }
\{ { return('{'); }
\} { return('}'); }
\[ { return('['); }
\] { return(']'); }
\= { return('='); }
<C>{other} { return (S_ANYTHING); }
<C>{exec}{space}{sql}{space}{include} { BEGIN(incl); }
<incl>{space} /* eat the whitespace */
<incl>[^ \t\n]+ { /* got the include file name */ <incl>[^ \t\n]+ { /* got the include file name */
struct _yy_buffer *yb; struct _yy_buffer *yb;
struct _include_path *ip; struct _include_path *ip;
...@@ -125,7 +454,7 @@ vacuum [vV][aA][cC][uU][uU][mM] ...@@ -125,7 +454,7 @@ vacuum [vV][aA][cC][uU][uU][mM]
{ {
if (strlen(ip->path) + strlen(yytext) + 3 > PATH_MAX) if (strlen(ip->path) + strlen(yytext) + 3 > PATH_MAX)
{ {
fprintf(stderr, "Path %s/%s is too long, skipping.\n", ip->path, yytext); fprintf(stderr, "Error: Path %s/%s is too long in line %d, skipping.\n", ip->path, yytext, yylineno);
continue; continue;
} }
sprintf (inc_file, "%s/%s", ip->path, yytext); sprintf (inc_file, "%s/%s", ip->path, yytext);
...@@ -142,7 +471,7 @@ vacuum [vV][aA][cC][uU][uU][mM] ...@@ -142,7 +471,7 @@ vacuum [vV][aA][cC][uU][uU][mM]
} }
if (!yyin) if (!yyin)
{ {
fprintf(stderr, "Cannot open include file %s\n", yytext); fprintf(stderr, "Error: Cannot open include file %s in line %d\n", yytext, yylineno);
exit(1); exit(1);
} }
...@@ -153,79 +482,6 @@ vacuum [vV][aA][cC][uU][uU][mM] ...@@ -153,79 +482,6 @@ vacuum [vV][aA][cC][uU][uU][mM]
BEGIN C; BEGIN C;
} }
<incl>";" { BEGIN C; } <incl>";" { BEGIN C; }
{length} { dbg(S_LENGTH); return S_LENGTH; }
{varchar} { dbg(S_VARCHAR); return S_VARCHAR; }
{varchar2} { dbg(S_VARCHAR2); return S_VARCHAR2; }
long { dbg(S_LONG); return S_LONG; }
short { dbg(S_SHORT); return S_SHORT; }
int { dbg(S_INT); return S_INT; }
char { dbg(S_CHAR); return S_CHAR; }
float { dbg(S_FLOAT); return S_FLOAT; }
double { dbg(S_DOUBLE); return S_DOUBLE; }
bool { dbg(S_BOOL); return S_BOOL; }
static { dbg(S_STATIC); return S_STATIC; }
signed { dbg(S_SIGNED); return S_SIGNED; }
extern { dbg(S_EXTERN); return S_EXTERN; }
auto { dbg(S_AUTO); return S_AUTO; }
const { dbg(S_CONST); return S_CONST; }
register { dbg(S_REGISTER); return S_REGISTER; }
struct { dbg(S_STRUCT); return S_STRUCT; }
{string} { dbg(SQL_STRING); return SQL_STRING; }
<SQL>{ws} ;
{symbol} { dbg(S_SYMBOL); return S_SYMBOL; }
{label} { dbg(S_LABEL); return S_LABEL; }
<SQL>"!<" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"!>" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"!^" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"!|" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"!~" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"!~*" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"#<" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"#<=" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"#<>" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"#=" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"#>" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"#>=" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"&&" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"&<" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"&>" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"<<" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"<=" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"<===>" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"<>" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"<?>" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"===>" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"===`" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"=|=" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>">=" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>">>" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"@@" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"|/" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"||/" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"~*" { dbg(S_SYMBOL); return S_SYMBOL; }
<SQL>"~=" { dbg(S_SYMBOL); return S_SYMBOL; }
"[" { dbg([); return '['; }
"]" { dbg(]); return ']'; }
";" { dbg(;); return ';'; }
"=" { dbg(=); return '='; }
"," { dbg(komma); return ','; }
\( { dbg(braceopen); return '('; }
\) { dbg(braceclose); return ')'; }
\{ { dbg(blockstart); return '{'; }
\} { dbg(blockend); return '}'; }
\* { dbg(*); return('*'); }
<SQL>":" { dbg(:); return ':'; }
<SQL>"::" { dbg(SQL_CONV); return SQL_CONV; }
{ws} { ECHO; }
. { dbg(.); return S_ANYTHING; }
<<EOF>> { if (yy_buffer == NULL) <<EOF>> { if (yy_buffer == NULL)
yyterminate(); yyterminate();
else else
...@@ -260,4 +516,3 @@ int yywrap(void) ...@@ -260,4 +516,3 @@ int yywrap(void)
{ {
return 1; return 1;
} }
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -123,22 +123,27 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ, ...@@ -123,22 +123,27 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ,
long varcharsize, long varcharsize,
long arrsiz, const char *siz, const char *prefix); long arrsiz, const char *siz, const char *prefix);
void void
ECPGdump_a_record(FILE *o, const char *name, long arrsiz, ECPGdump_a_record(FILE *o, const char *name, const char *ind_name, long arrsiz,
struct ECPGtype * typ, const char *offset, const char *prefix); struct ECPGtype * typ, struct ECPGtype * ind_typ, const char *offset, const char *prefix, const char * ind_prefix);
void void
ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *prefix) ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *ind_name, struct ECPGtype * ind_typ, const char *prefix, const char *ind_prefix)
{ {
if (IS_SIMPLE_TYPE(typ->typ)) if (IS_SIMPLE_TYPE(typ->typ))
{ {
ECPGdump_a_simple(o, name, typ->typ, typ->size, 0, 0, prefix); ECPGdump_a_simple(o, name, typ->typ, typ->size, 0, 0, prefix);
ECPGdump_a_simple(o, ind_name, ind_typ->typ, ind_typ->size, 0, 0, ind_prefix);
} }
else if (typ->typ == ECPGt_array) else if (typ->typ == ECPGt_array)
{ {
if (IS_SIMPLE_TYPE(typ->u.element->typ)) if (IS_SIMPLE_TYPE(typ->u.element->typ))
{
ECPGdump_a_simple(o, name, typ->u.element->typ, ECPGdump_a_simple(o, name, typ->u.element->typ,
typ->u.element->size, typ->size, 0, prefix); typ->u.element->size, typ->size, 0, prefix);
ECPGdump_a_simple(o, ind_name, ind_typ->u.element->typ,
ind_typ->u.element->size, ind_typ->size, 0, prefix);
}
else if (typ->u.element->typ == ECPGt_array) else if (typ->u.element->typ == ECPGt_array)
{ {
abort(); /* Array of array, */ abort(); /* Array of array, */
...@@ -146,7 +151,7 @@ ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *pr ...@@ -146,7 +151,7 @@ ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *pr
else if (typ->u.element->typ == ECPGt_record) else if (typ->u.element->typ == ECPGt_record)
{ {
/* Array of records. */ /* Array of records. */
ECPGdump_a_record(o, name, typ->size, typ->u.element, 0, prefix); ECPGdump_a_record(o, name, ind_name, typ->size, typ->u.element, ind_typ->u.element, 0, prefix, ind_prefix);
} }
else else
{ {
...@@ -155,7 +160,7 @@ ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *pr ...@@ -155,7 +160,7 @@ ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *pr
} }
else if (typ->typ == ECPGt_record) else if (typ->typ == ECPGt_record)
{ {
ECPGdump_a_record(o, name, 0, typ, 0, prefix); ECPGdump_a_record(o, name, ind_name, 0, typ, ind_typ, 0, prefix, ind_prefix);
} }
else else
{ {
...@@ -171,7 +176,8 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ, ...@@ -171,7 +176,8 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ,
long varcharsize, long varcharsize,
long arrsiz, long arrsiz,
const char *siz, const char *siz,
const char *prefix) const char *prefix
)
{ {
switch (typ) switch (typ)
{ {
...@@ -241,15 +247,19 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ, ...@@ -241,15 +247,19 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ,
varcharsize, varcharsize,
arrsiz, siz); arrsiz, siz);
break; break;
case ECPGt_NO_INDICATOR: /* no indicator */
fprintf(o, "\n\tECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ");
break;
default: default:
abort(); abort();
} }
} }
/* Penetrate a record and dump the contents. */ /* Penetrate a record and dump the contents. */
void void
ECPGdump_a_record(FILE *o, const char *name, long arrsiz, struct ECPGtype * typ, const char *offsetarg, const char *prefix) ECPGdump_a_record(FILE *o, const char *name, const char * ind_name, long arrsiz, struct ECPGtype * typ, struct ECPGtype * ind_typ, const char *offsetarg, const char *prefix, const char *ind_prefix)
{ {
/* /*
...@@ -257,9 +267,9 @@ ECPGdump_a_record(FILE *o, const char *name, long arrsiz, struct ECPGtype * typ, ...@@ -257,9 +267,9 @@ ECPGdump_a_record(FILE *o, const char *name, long arrsiz, struct ECPGtype * typ,
* then we are in a record in a record and the offset is used as * then we are in a record in a record and the offset is used as
* offset. * offset.
*/ */
struct ECPGrecord_member *p; struct ECPGrecord_member *p, *ind_p;
char obuf[BUFSIZ]; char obuf[BUFSIZ];
char pbuf[BUFSIZ]; char pbuf[BUFSIZ], ind_pbuf[BUFSIZ];
const char *offset; const char *offset;
if (offsetarg == NULL) if (offsetarg == NULL)
...@@ -275,62 +285,12 @@ ECPGdump_a_record(FILE *o, const char *name, long arrsiz, struct ECPGtype * typ, ...@@ -275,62 +285,12 @@ ECPGdump_a_record(FILE *o, const char *name, long arrsiz, struct ECPGtype * typ,
sprintf(pbuf, "%s%s.", prefix ? prefix : "", name); sprintf(pbuf, "%s%s.", prefix ? prefix : "", name);
prefix = pbuf; prefix = pbuf;
for (p = typ->u.members; p; p = p->next) sprintf(ind_pbuf, "%s%s.", ind_prefix ? ind_prefix : "", ind_name);
{ ind_prefix = ind_pbuf;
#if 0
if (IS_SIMPLE_TYPE(p->typ->typ))
{
sprintf(buf, "%s.%s", name, p->name);
ECPGdump_a_simple(o, buf, p->typ->typ, p->typ->size,
arrsiz, offset);
}
else if (p->typ->typ == ECPGt_array)
{
int i;
for (i = 0; i < p->typ->size; i++) for (p = typ->u.members, ind_p = ind_typ->u.members; p; p = p->next, ind_p = ind_p->next)
{ {
if (IS_SIMPLE_TYPE(p->typ->u.element->typ)) ECPGdump_a_type(o, p->name, p->typ, ind_p->name, ind_p->typ, prefix, ind_prefix);
{
/* sprintf(buf, "%s.%s[%d]", name, p->name, i); */
sprintf(buf, "%s.%s", name, p->name);
ECPGdump_a_simple(o, buf, p->typ->u.element->typ, p->typ->u.element->size,
p->typ->u.element->size, offset);
}
else if (p->typ->u.element->typ == ECPGt_array)
{
/* Array within an array. NOT implemented. */
abort();
}
else if (p->typ->u.element->typ == ECPGt_record)
{
/*
* Record within array within record. NOT implemented
* yet.
*/
abort();
}
else
{
/* Unknown type */
abort();
}
}
}
else if (p->typ->typ == ECPGt_record)
{
/* Record within a record */
sprintf(buf, "%s.%s", name, p->name);
ECPGdump_a_record(o, buf, arrsiz, p->typ, offset);
}
else
{
/* Unknown type */
abort();
}
#endif
ECPGdump_a_type(o, p->name, p->typ, prefix);
} }
} }
......
...@@ -45,7 +45,7 @@ void ECPGfree_type(struct ECPGtype *); ...@@ -45,7 +45,7 @@ void ECPGfree_type(struct ECPGtype *);
size is the maxsize in case it is a varchar. Otherwise it is the size of size is the maxsize in case it is a varchar. Otherwise it is the size of
the variable (required to do array fetches of records). the variable (required to do array fetches of records).
*/ */
void ECPGdump_a_type(FILE *, const char *name, struct ECPGtype *, const char *); void ECPGdump_a_type(FILE *, const char *, struct ECPGtype *, const char *, struct ECPGtype *, const char *, const char *);
/* A simple struct to keep a variable and its type. */ /* A simple struct to keep a variable and its type. */
struct ECPGtemp_type struct ECPGtemp_type
...@@ -71,5 +71,6 @@ enum WHEN ...@@ -71,5 +71,6 @@ enum WHEN
struct when struct when
{ {
enum WHEN code; enum WHEN code;
char *command;
char *str; char *str;
}; };
all: test2 perftest all: test2 perftest
test2: test2.c test2: test2.c
gcc -g -I ../include -I ../../libpq -o test2 test2.c -L../lib -lecpg -L../../libpq -lpq -lcrypt gcc -g -I ../include -I ../../libpq -o test2 test2.c -L../lib -lecpg -L../../libpq -lpq -lcrypt --static
test2.c: test2.pgc test2.c: test2.pgc
ecpg test2.pgc ../preproc/ecpg test2.pgc
perftest: perftest.c perftest: perftest.c
gcc -g -I ../include -I ../../libpq -o perftest perftest.c -L../lib -lecpg -L../../libpq -lpq -lcrypt gcc -g -I ../include -I ../../libpq -o perftest perftest.c -L../lib -lecpg -L../../libpq -lpq -lcrypt --static
perftest.c: perftest.pgc perftest.c: perftest.pgc
ecpg perftest.pgc ../preproc/ecpg perftest.pgc
clean: clean:
/bin/rm test2 test2.c perftest perftest.c log /bin/rm test2 test2.c perftest perftest.c log
dep depend:
...@@ -16,7 +16,8 @@ print_result(long sec, long usec, char *text) ...@@ -16,7 +16,8 @@ print_result(long sec, long usec, char *text)
usec+=1000000; usec+=1000000;
} }
printf("I needed %ld seconds and %ld microseconds for the %s test.\n", sec, usec, text); printf("I needed %ld seconds and %ld microseconds for the %s test.\n", sec, usec, text);
exec sql vacuum analyze; exec sql vacuum;
sleep(1);
} }
int int
...@@ -27,9 +28,9 @@ exec sql begin declare section; ...@@ -27,9 +28,9 @@ exec sql begin declare section;
exec sql end declare section; exec sql end declare section;
struct timeval tvs, tve; struct timeval tvs, tve;
exec sql connect 'mm'; exec sql connect mm;
exec sql create table perftest1(number int4, ascii char16); exec sql create table perftest1(number int4, ascii char(16));
exec sql create unique index number1 on perftest1(number); exec sql create unique index number1 on perftest1(number);
...@@ -100,6 +101,16 @@ exec sql end declare section; ...@@ -100,6 +101,16 @@ exec sql end declare section;
print_result(tve.tv_sec - tvs.tv_sec, tve.tv_usec - tvs.tv_usec, "update"); print_result(tve.tv_sec - tvs.tv_sec, tve.tv_usec - tvs.tv_usec, "update");
gettimeofday(&tvs, NULL);
exec sql delete from perftest2;
exec sql commit;
gettimeofday(&tve, NULL);
print_result(tve.tv_sec - tvs.tv_sec, tve.tv_usec - tvs.tv_usec, "delete");
exec sql drop index number2; exec sql drop index number2;
exec sql drop table perftest2; exec sql drop table perftest2;
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
exec sql include header_test; exec sql include header_test;
extern void ECPGdebug(int n, FILE *dbgs);
static int not_found = 0; static int not_found = 0;
static void static void
set_not_found(void) set_not_found(void)
...@@ -20,37 +18,51 @@ exec sql begin declare section; ...@@ -20,37 +18,51 @@ exec sql begin declare section;
short age; short age;
} birth; } birth;
} personal; } personal;
struct personal_indicator { short name;
struct birth_indicator { short born;
int age;
} ind_birth;
} ind_personal;
long ind_married;
char married[9]="a";
exec sql end declare section; exec sql end declare section;
char msg[128]; char msg[128], command[128];
FILE *dbgs; FILE *dbgs;
if ((dbgs = fopen("log", "w")) != NULL) if ((dbgs = fopen("log", "w")) != NULL)
ECPGdebug(1, dbgs); ECPGdebug(1, dbgs);
strcpy(msg, "connect"); strcpy(msg, "connect");
exec sql connect 'mm'; exec sql connect mm;
strcpy(msg, "create"); strcpy(msg, "create");
exec sql create table meskes(name char8, born int4, age int2); exec sql create table meskes(name char(8), born integer, age smallint, married char(8));
strcpy(msg, "insert"); strcpy(msg, "insert");
exec sql insert into meskes(name, born, age) values ('Petra', 19661202, 31); exec sql insert into meskes(name, born, age, married) values ('Petra', 19661202, 31, '19900404');
exec sql insert into meskes(name, born, age) values ('Michael', 19660117, 32); exec sql insert into meskes(name, born, age, married) values ('Michael', 19660117, 32, '19900404');
exec sql insert into meskes(name, born, age) values ('Carsten', 19910103, 7); 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 ('Marc', 19930907, 4);
exec sql insert into meskes(name, born, age) values ('Chris', 19970923, 0);
sprintf(command, "insert into meskes(name, born, age) values ('Chris', 19970923, 0)");
strcpy(msg, "execute");
exec sql execute immediate :command;
strcpy(msg, "commit");
exec sql commit;
strcpy(msg, "declare"); strcpy(msg, "declare");
exec sql declare cur cursor for exec sql declare cur cursor for
select name, born, age from meskes; select name, born, age, married from meskes;
strcpy(msg, "open");
exec sql open cur; exec sql open cur;
while (not_found == 0) { while (not_found == 0) {
strcpy(msg, "fetch"); strcpy(msg, "fetch");
exec sql fetch cur into :personal; exec sql fetch cur into :personal:ind_personal, :married:ind_married;
if (not_found == 0) if (not_found == 0)
printf ("%8.8s was born %d (age = %d)\n", personal.name.arr, personal.birth.born, personal.birth.age); 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);
} }
strcpy(msg, "close"); strcpy(msg, "close");
......
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