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

From: Michael Meskes <meskes@topsystem.de>

Well this is not really a patch. But I mananged to get Linus' old Postgres95
precompiler to compile and work with PostgreSQL. The next step would be to
collect bug/missing feature reports and to put it into the distribution so
that it is made with the standard make procedure.

Warning! So far it is not tested much and it does not install correctly. But
I was able to create a small binary with it.
parent c10908e0
SUBDIRS = src/include src/lib src/preproc
all install uninstall clean::
for i in $(SUBDIRS); do ( cd $$i; make $@ ); done
This list is still from Linus. MM
The variables should be static.
The preprocessor interface is strange, to say the least It would be better
with a consistant unix arguments interface, perhaps builtin default
filenames so they won't have to be given all the time.
Preprocessor cannot do syntax checking on your SQL statements Whatever you
write is copied more or less exactly to the postgres95 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
just -1 for them all.
Missing library functions to_date et al.
Possibility to define records or structs in the declare section in a way
that the record can be filled from one row in the database. This is a
simpler way to handle an entire row at a time.
Oracle has array operations that enhances speed. When implementing it in
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
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
thing to take care of.
To set up a database you need a few scripts with table definitions and other
configuration parameters. If you have these scripts for an old database you
would like to just apply them to get a postgres database that works in the
same way. The functionality could be accomplished with some conversion
scripts. Speed will never be accomplished in this way. To do this you need a
bigger insight in the database construction and the use of the database than
could be realised in a script.
# This file is a shell script that caches the results of configure
# tests run on this system so they can be shared between configure
# scripts and configure runs. It is not useful on other systems.
# If it contains results you don't want to keep, you may remove or edit it.
#
# By default, configure uses ./config.cache as the cache file,
# creating it if it does not exist already. You can give configure
# the --cache-file=FILE option to use a different cache file; that is
# what configure does when it calls configure scripts in
# subdirectories, so they share the cache.
# Giving --cache-file=/dev/null disables caching, for debugging configure.
# config.status only pays attention to the cache file if you give it the
# --recheck option to rerun configure.
#
ac_cv_lib_fl_yywrap=${ac_cv_lib_fl_yywrap=yes}
ac_cv_prog_CC=${ac_cv_prog_CC=gcc}
ac_cv_prog_LEX=${ac_cv_prog_LEX=flex}
ac_cv_prog_YACC=${ac_cv_prog_YACC='bison -y'}
ac_cv_prog_cc_cross=${ac_cv_prog_cc_cross=no}
ac_cv_prog_cc_g=${ac_cv_prog_cc_g=yes}
ac_cv_prog_cc_works=${ac_cv_prog_cc_works=yes}
ac_cv_prog_gcc=${ac_cv_prog_gcc=yes}
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
configure:526: checking for gcc
configure:603: checking whether the C compiler (gcc ) works
configure:617: gcc -o conftest conftest.c 1>&5
configure:637: checking whether the C compiler (gcc ) is a cross-compiler
configure:642: checking whether we are using GNU C
configure:666: checking whether gcc accepts -g
configure:696: checking for flex
configure:729: checking for yywrap in -lfl
configure:775: checking for bison
#! /bin/sh
# Generated automatically by configure.
# Run this file to recreate the current configuration.
# This directory was configured as follows,
# on host gauss:
#
# ./configure
#
# Compiler output produced by configure, useful for debugging
# configure, is in ./config.log if it exists.
ac_cs_usage="Usage: ./config.status [--recheck] [--version] [--help]"
for ac_option
do
case "$ac_option" in
-recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
echo "running ${CONFIG_SHELL-/bin/sh} ./configure --no-create --no-recursion"
exec ${CONFIG_SHELL-/bin/sh} ./configure --no-create --no-recursion ;;
-version | --version | --versio | --versi | --vers | --ver | --ve | --v)
echo "./config.status generated by autoconf version 2.12"
exit 0 ;;
-help | --help | --hel | --he | --h)
echo "$ac_cs_usage"; exit 0 ;;
*) echo "$ac_cs_usage"; exit 1 ;;
esac
done
ac_given_srcdir=.
trap 'rm -fr src/include/Makefile src/lib/Makefile src/preproc/Makefile src/preproc/ecpg conftest*; exit 1' 1 2 15
# Protect against being on the right side of a sed subst in config.status.
sed 's/%@/@@/; s/@%/@@/; s/%g$/@g/; /@g$/s/[\\&%]/\\&/g;
s/@@/%@/; s/@@/@%/; s/@g$/%g/' > conftest.subs <<\CEOF
/^[ ]*VPATH[ ]*=[^:]*$/d
s%@CFLAGS@%-g -O2%g
s%@CPPFLAGS@%%g
s%@CXXFLAGS@%%g
s%@DEFS@% %g
s%@LDFLAGS@%%g
s%@LIBS@%%g
s%@exec_prefix@%${prefix}%g
s%@prefix@%/usr/local%g
s%@program_transform_name@%s,x,x,%g
s%@bindir@%${exec_prefix}/bin%g
s%@sbindir@%${exec_prefix}/sbin%g
s%@libexecdir@%${exec_prefix}/libexec%g
s%@datadir@%${prefix}/share%g
s%@sysconfdir@%${prefix}/etc%g
s%@sharedstatedir@%${prefix}/com%g
s%@localstatedir@%${prefix}/var%g
s%@libdir@%${exec_prefix}/lib%g
s%@includedir@%${prefix}/include%g
s%@oldincludedir@%/usr/include%g
s%@infodir@%${prefix}/info%g
s%@mandir@%${prefix}/man%g
s%@CC@%gcc%g
s%@LEX@%flex%g
s%@LEXLIB@%-lfl%g
s%@YACC@%bison -y%g
s%@TOPSRC@%/home/meskes/data/computer/databases/postgres/pgsql/src/interfaces/ecpg/../..%g
CEOF
# Split the substitutions into bite-sized pieces for seds with
# small command number limits, like on Digital OSF/1 and HP-UX.
ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
ac_file=1 # Number of current file.
ac_beg=1 # First line for current file.
ac_end=$ac_max_sed_cmds # Line after last line for current file.
ac_more_lines=:
ac_sed_cmds=""
while $ac_more_lines; do
if test $ac_beg -gt 1; then
sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
else
sed "${ac_end}q" conftest.subs > conftest.s$ac_file
fi
if test ! -s conftest.s$ac_file; then
ac_more_lines=false
rm -f conftest.s$ac_file
else
if test -z "$ac_sed_cmds"; then
ac_sed_cmds="sed -f conftest.s$ac_file"
else
ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
fi
ac_file=`expr $ac_file + 1`
ac_beg=$ac_end
ac_end=`expr $ac_end + $ac_max_sed_cmds`
fi
done
if test -z "$ac_sed_cmds"; then
ac_sed_cmds=cat
fi
CONFIG_FILES=${CONFIG_FILES-"src/include/Makefile src/lib/Makefile src/preproc/Makefile src/preproc/ecpg"}
for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
# Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
case "$ac_file" in
*:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
*) ac_file_in="${ac_file}.in" ;;
esac
# Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
# Remove last slash and all that follows it. Not all systems have dirname.
ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
# The file is in a subdirectory.
test ! -d "$ac_dir" && mkdir "$ac_dir"
ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
# A "../" for each directory in $ac_dir_suffix.
ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
else
ac_dir_suffix= ac_dots=
fi
case "$ac_given_srcdir" in
.) srcdir=.
if test -z "$ac_dots"; then top_srcdir=.
else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
/*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
*) # Relative path.
srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
top_srcdir="$ac_dots$ac_given_srcdir" ;;
esac
echo creating "$ac_file"
rm -f "$ac_file"
configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
case "$ac_file" in
*Makefile*) ac_comsub="1i\\
# $configure_input" ;;
*) ac_comsub= ;;
esac
ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
sed -e "$ac_comsub
s%@configure_input@%$configure_input%g
s%@srcdir@%$srcdir%g
s%@top_srcdir@%$top_srcdir%g
" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
fi; done
rm -f conftest.s*
exit 0
This diff is collapsed.
dnl Process this file with autoconf to produce a configure script.
AC_INIT(src/preproc/type.c)
AC_PROG_CC
AC_PROG_LEX
AC_PROG_YACC
dnl This is actually not pwd, it is the location of the configure file.
dnl This handling, and the fact that not all Makefiles are created by
dnl this configure script makes it impossible to compile somewhere else.
[TOPSRC=`pwd`/../..]
AC_SUBST(TOPSRC)
AC_OUTPUT(src/include/Makefile src/lib/Makefile src/preproc/Makefile src/preproc/ecpg)
This diff is collapsed.
This diff is collapsed.
# Generated automatically from Makefile.in by configure.
POSTGRESTOP=@POSTGRESERVER@
POSTGRES_INCLUDE=$(POSTGRESTOP)/include
all clean::
@echo Nothing to be done.
install::
install ecpglib.h $(POSTGRES_INCLUDE)
install ecpgtype.h $(POSTGRES_INCLUDE)
install sqlca.h $(POSTGRES_INCLUDE)
uninstall::
rm -f $(POSTGRES_INCLUDE)/ecpglib.h
rm -f $(POSTGRES_INCLUDE)/ecpgtype.h
rm -f $(POSTGRES_INCLUDE)/sqlca.h
POSTGRESTOP=@POSTGRESERVER@
POSTGRES_INCLUDE=$(POSTGRESTOP)/include
all clean::
@echo Nothing to be done.
install::
install ecpglib.h $(POSTGRES_INCLUDE)
install ecpgtype.h $(POSTGRES_INCLUDE)
install sqlca.h $(POSTGRES_INCLUDE)
uninstall::
rm -f $(POSTGRES_INCLUDE)/ecpglib.h
rm -f $(POSTGRES_INCLUDE)/ecpgtype.h
rm -f $(POSTGRES_INCLUDE)/sqlca.h
#include <c.h>
void ECPGdebug(int);
bool ECPGconnect(const char * dbname);
bool ECPGdo(int, char *, ...);
bool ECPGcommit(int);
bool ECPGrollback(int);
bool ECPGfinish();
bool ECPGstatus();
void ECPGlog(const char * format, ...);
#ifdef LIBPQ_FE_H
bool ECPGsetdb(PGconn *);
#endif
/* Here are some methods used by the lib. */
/* Returns a pointer to a string containing a simple type name. */
const char * ECPGtype_name(enum ECPGttype);
/* A generic varchar type. */
struct ECPGgeneric_varchar {
int len;
char arr[1];
};
/*
* This file implements a data structure that is built and maintained by the
* preprocessor.
*
* All types that can be handled for host variable declarations has to
* be handled eventually.
*/
/*
* Here are all the types that we are to handle. Note that it is the type
* that is registered and that has nothing whatsoever to do with the storage
* class.
*
* Simle types
* integers: char, short, int, long (signed and unsigned)
* floats: float, double
*
* Complex types:
* VARCHAR, VARCHAR2 - Strings with length (maxlen is given in the declaration)
* Arrays of simple types and of VARCHAR, VARCHAR2 (size given in declaration)
* Records build of simple types, arrays and other records.
*
* Complicating things:
* typedefs and struct names!
*
* Conclusion:
* This is a typically recursive definition. A structure of typed list elements
* would probably work fine:
*/
#include <stdio.h>
enum ECPGttype {
ECPGt_char = 1, ECPGt_unsigned_char, ECPGt_short, ECPGt_unsigned_short,
ECPGt_int, ECPGt_unsigned_int, ECPGt_long, ECPGt_unsigned_long,
ECPGt_float, ECPGt_double,
ECPGt_varchar, ECPGt_varchar2,
ECPGt_array,
ECPGt_record,
ECPGt_EOIT, /* End of insert types. */
ECPGt_EORT /* End of result types. */
};
#define IS_SIMPLE_TYPE(type) ((type) >= ECPGt_char && (type) <= ECPGt_varchar2)
#ifndef POSTGRES_SQLCA_H
#define POSTGRES_SQLCA_H
struct sqlca {
int sqlcode;
struct {
int sqlerrml;
char sqlerrmc[1000];
} sqlerrm;
} sqlca;
#endif
# Generated automatically from Makefile.in by configure.
TOPDIR=/home/meskes/data/computer/databases/postgres/pgsql/src/interfaces/ecpg/../..
PQ_INCLUDE=-I$(TOPDIR)/include -I$(TOPDIR)/interfaces/libpq
POSTGRES_LIB=$(POSTGRESTOP)/lib
all: lib
lib: libecpg.a
clean::
rm -f *.o *.a core a.out *~
install:: libecpg.a
install -m644 libecpg.a $(POSTGRES_LIB)
uninstall::
rm -f $(POSTGRES_LIB)/libecpg.a
# Rules that do something
libecpg.a : libecpg.a(ecpglib.o) libecpg.a(typename.o)
ecpglib.o : ecpglib.c ../include/ecpglib.h ../include/ecpgtype.h
$(CC) -O2 -g -Wall -I../include $(PQ_INCLUDE) -c ecpglib.c
typename.o : typename.c ../include/ecpgtype.h
$(CC) -g -O2 -Wall -I../include $(PQ_INCLUDE) -c typename.c
TOPDIR=@TOPSRC@
PQ_INCLUDE=-I$(TOPDIR)/include -I$(TOPDIR)/interfaces/libpq
POSTGRES_LIB=$(POSTGRESTOP)/lib
all: lib
lib: libecpg.a
clean::
rm -f *.o *.a core a.out *~
install:: libecpg.a
install -m644 libecpg.a $(POSTGRES_LIB)
uninstall::
rm -f $(POSTGRES_LIB)/libecpg.a
# Rules that do something
libecpg.a : libecpg.a(ecpglib.o) libecpg.a(typename.o)
ecpglib.o : ecpglib.c ../include/ecpglib.h ../include/ecpgtype.h
$(CC) -O2 -g -Wall -I../include $(PQ_INCLUDE) -c ecpglib.c
typename.o : typename.c ../include/ecpgtype.h
$(CC) -g -O2 -Wall -I../include $(PQ_INCLUDE) -c typename.c
This diff is collapsed.
#include <ecpgtype.h>
/*
* This function is used to generate the correct type names.
*/
const char *
ECPGtype_name(enum ECPGttype typ)
{
switch (typ)
{
case ECPGt_char: return "char";
case ECPGt_unsigned_char: return "unsigned char";
case ECPGt_short: return "short";
case ECPGt_unsigned_short: return "unsigned short";
case ECPGt_int: return "int";
case ECPGt_unsigned_int: return "unsigned int";
case ECPGt_long: return "long";
case ECPGt_unsigned_long: return "unsigned long";
case ECPGt_float: return "float";
case ECPGt_double: return "double";
default:
abort();
}
}
# Generated automatically from Makefile.in by configure.
POSTGRESTOP=@POSTGRESERVER@
POSTGRES_BIN=$(POSTGRESTOP)/bin
POSTGRES_LIB=$(POSTGRESTOP)/lib
CC=gcc
LEX=flex
LEXLIB=-lfl
YACC=bison -y
CFLAGS=-I../include -O2 -g -Wall
all:: ecpg
clean::
rm -f *.o core a.out ecpg y.tab.h y.tab.c *~
install:: all
install -c -d -m755 $(POSTGRES_LIB)/ecpg
install -c -m555 preproc $(POSTGRES_LIB)/ecpg
install -c -m555 ecpg $(POSTGRES_BIN)
uninstall::
rm -f $(POSTGRES_BIN)/ecpg
rm -f $(POSTGRES_LIB)/ecpg/preproc
# Rule that really do something.
ecpg: y.tab.o pgc.o type.o ecpg.o
$(CC) -g -O2 -Wall -o ecpg y.tab.o pgc.o type.o ecpg.o -L../lib -lecpg $(LEXLIB)
y.tab.h y.tab.c: preproc.y
$(YACC) -d $<
y.tab.o : y.tab.h ../include/ecpgtype.h
type.o : ../include/ecpgtype.h
pgc.o : ../include/ecpgtype.h
POSTGRESTOP=@POSTGRESERVER@
POSTGRES_BIN=$(POSTGRESTOP)/bin
POSTGRES_LIB=$(POSTGRESTOP)/lib
CC=@CC@
LEX=@LEX@
LEXLIB=@LEXLIB@
YACC=@YACC@
CFLAGS=-I../include -O2 -g -Wall
all:: ecpg
clean::
rm -f *.o core a.out ecpg y.tab.h y.tab.c *~
install:: all
install -c -d -m755 $(POSTGRES_LIB)/ecpg
install -c -m555 preproc $(POSTGRES_LIB)/ecpg
install -c -m555 ecpg $(POSTGRES_BIN)
uninstall::
rm -f $(POSTGRES_BIN)/ecpg
rm -f $(POSTGRES_LIB)/ecpg/preproc
# Rule that really do something.
ecpg: y.tab.o pgc.o type.o ecpg.o
$(CC) -g -O2 -Wall -o ecpg y.tab.o pgc.o type.o ecpg.o -L../lib -lecpg $(LEXLIB)
y.tab.h y.tab.c: preproc.y
$(YACC) -d $<
y.tab.o : y.tab.h ../include/ecpgtype.h
type.o : ../include/ecpgtype.h
pgc.o : ../include/ecpgtype.h
/* New main for ecpg, the PostgreSQL embedded SQL precompiler. */
/* (C) Michael Meskes <meskes@debian.org> Feb 5th, 1998 */
/* Placed under the same copyright as PostgresSQL */
#include <stdio.h>
extern void lex_init(void);
int yyparse (void);
int main(int argc, char *argv[])
{
lex_init();
fprintf(stdout, "/* These two include files are added by the preprocessor */\n#include <ecpgtype.h>\n#include <ecpglib.h>\n");
yyparse();
return(0);
}
#!/bin/sh
INFILE=
OUTFILE=
for arg
do
case "$arg" in
iname=*)
INFILE=`expr substr $arg 7 1000`
;;
oname=*)
OUTFILE=`expr substr $arg 7 1000`
;;
*)
echo Wrong argument $arg
exit 1;
;;
esac
done
if [ -n "$INFILE" -a -n "$OUTFILE" ]
then
exec @POSTGRESERVER@/lib/ecpg/preproc < $INFILE > $OUTFILE
else
echo Missing arguments.
echo usage: $0 iname=file oname=outfile
exit 1;
fi
exit 0;
/* Copyright comment! */
%{
#include "type.h"
#include "y.tab.h"
#define dbg(arg) fprintf(stderr, "DEBUG: %s\n", #arg);
%}
%s C SQL
ccomment \/\*([^*]|\*[^/]|\*\*[^/])*\*\/
ws ([ \t\n][ \t\n]*|{ccomment})*
letter [A-Za-z_]
digit [0-9]
length {digit}+
symbol {letter}({letter}|{digit})*
string '[^']*'
exec [eE][xX][eE][cC]
sql [sS][qQ][lL]
varchar [vV][aA][rR][cC][hH][aA][rR]
varchar2 [vV][aA][rR][cC][hH][aA][rR]2
into [iI][nN][tT][oO]
begin [bB][eE][gG][iI][nN]
end [eE][nN][dD]
declare [dD][eE][cC][lL][aA][rR][eE]
section [sS][eE][cC][tT][iI][oO][nN]
include [iI][nN][cC][lL][uU][dD][eE]
connect [cC][oO][nN][nN][eE][cC][tT]
open [oO][pP][eE][nN]
commit [cC][oO][mM][mM][iI][tT]
rollback [rR][oO][lL][lL][bB][aA][cC][kK]
%%
<C>{exec}{ws}{sql} { BEGIN SQL; dbg(SQL_START); return SQL_START; }
<SQL>";" { BEGIN C; dbg(SQL_SEMI); return SQL_SEMI; }
<SQL>{begin} { dbg(SQL_BEGIN); return SQL_BEGIN; }
<SQL>{end} { dbg(SQL_END); return SQL_END; }
<SQL>{declare} { dbg(SQL_DECLARE); return SQL_DECLARE; }
<SQL>{section} { dbg(SQL_SECTION); return SQL_SECTION; }
<SQL>{include} { dbg(SQL_INCLUDE); return SQL_INCLUDE; }
<SQL>{connect} { dbg(SQL_CONNECT); return SQL_CONNECT; }
<SQL>{open} { dbg(SQL_OPEN); return SQL_OPEN; }
<SQL>{commit} { dbg(SQL_COMMIT); return SQL_COMMIT; }
<SQL>{rollback} { dbg(SQL_ROLLBACK); return SQL_ROLLBACK; }
<SQL>{into} { dbg(SQL_INTO); return SQL_INTO; }
{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; }
{string} { dbg(SQL_STRING); return SQL_STRING; }
<SQL>{ws} ;
{symbol} { 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; }
<SQL>"~=" { dbg(S_SYMBOL); return S_SYMBOL; }
"[" { dbg([); return '['; }
"]" { dbg(]); return ']'; }
";" { dbg(;); return ';'; }
"," { dbg(komma); return ','; }
<SQL>":" { dbg(:); return ':'; }
{ws} { ECHO; }
. { dbg(.); return S_ANYTHING; }
%%
void
lex_init()
{
BEGIN C;
}
int yywrap()
{
return 1;
}
/* Copyright comment */
%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "type.h"
void yyerror(char *);
/*
* Handling of the variables.
*/
/* Since we don't want to keep track of where the functions end we just
* have a list of functions that we search in, most reasently defined
* function. This won't work if we use block scope for variables with the
* same name but different types but in all other cases the c-compiler will
* signal an error (hopefully).
*
* This list is leaked on program exit. This is because I don't think it is
* important enough to spend the extra ten minutes to write the function that
* deletes it. It would be another thing if I would have written in C++.
*/
/* This is a linked list of the variable names and types. */
struct variable
{
char * name;
struct ECPGtype * type;
struct variable * next;
};
static struct variable * allvariables = NULL;
struct variable *
find_variable(char * name)
{
struct variable * p;
for (p = allvariables; p; p = p->next)
{
if (strcmp(p->name, name) == 0)
return p;
}
{
char * errorstring = (char *) malloc(strlen(name) + 100);
sprintf(errorstring, "The variabel :%s is not declared.", name);
yyerror(errorstring);
}
return NULL;
}
void
new_variable(const char * name, struct ECPGtype * type)
{
struct variable * p = (struct variable*) malloc(sizeof(struct variable));
p->name = strdup(name);
p->type = type;
p->next = allvariables;
allvariables = p;
}
/*
* Here is the variables that need to be handled on every request.
* These are of two kinds: input and output.
* I will make two lists for them.
*/
struct arguments {
struct variable * variable;
struct arguments * next;
};
static struct arguments * argsinsert = NULL;
static struct arguments * argsresult = NULL;
void
reset_variables()
{
argsinsert = NULL;
argsresult = NULL;
}
/* Add a variable to a request. */
void
add_variable(struct arguments ** list, struct variable * var)
{
struct arguments * p = (struct arguments *)malloc(sizeof(struct arguments));
p->variable = var;
p->next = *list;
*list = p;
}
/* Dump out a list of all the variable on this list.
This is a recursive function that works from the end of the list and
deletes the list as we go on.
*/
void
dump_variables(struct arguments * list)
{
if (list == NULL)
{
return;
}
/* The list is build up from the beginning so lets first dump the
end of the list:
*/
dump_variables(list->next);
/* Then the current element. */
ECPGdump_a_type(stdout, list->variable->name, list->variable->type);
/* Then release the list element. */
free(list);
}
extern FILE * yyout;
extern char * yytext;
extern int yyleng;
%}
%union {
int tagname;
struct ECPGtemp_type type;
char * symbolname;
int indexsize;
enum ECPGttype type_enum;
}
%token <tagname> SQL_START SQL_SEMI SQL_STRING SQL_INTO
%token <tagname> SQL_BEGIN SQL_END SQL_DECLARE SQL_SECTION SQL_INCLUDE
%token <tagname> SQL_CONNECT SQL_OPEN
%token <tagname> SQL_COMMIT SQL_ROLLBACK
%token <tagname> S_SYMBOL S_LENGTH S_ANYTHING
%token <tagname> S_VARCHAR S_VARCHAR2
%token <tagname> S_EXTERN S_STATIC
%token <tagname> S_UNSIGNED S_SIGNED
%token <tagname> S_LONG S_SHORT S_INT S_CHAR S_FLOAT S_DOUBLE
%token <tagname> '[' ']' ';' ','
%type <type> type type_detailed varchar_type simple_type
%type <symbolname> symbol
%type <tagname> maybe_storage_clause varchar_tag
%type <type_enum> simple_tag
%type <indexsize> index length
%type <tagname> canything sqlanything both_anything
%%
prog : statements;
statements : /* empty */
| statements statement;
statement : sqldeclaration
| sqlinclude
| sqlconnect
| sqlopen
| sqlcommit
| sqlrollback
| sqlstatement
| cthing;
sqldeclaration : sql_startdeclare
variable_declarations
sql_enddeclare;
sql_startdeclare : SQL_START SQL_BEGIN SQL_DECLARE SQL_SECTION SQL_SEMI {
printf("/* exec sql begin declare section */\n");
};
sql_enddeclare : SQL_START SQL_END SQL_DECLARE SQL_SECTION SQL_SEMI {
printf("/* exec sql end declare section */\n");
};
variable_declarations : /* empty */
| variable_declarations variable_declaration ;
/* Here is where we can enter support for typedef. */
variable_declaration : type ';' {
new_variable($<type>1.name, $<type>1.typ);
free($<type>1.name);
fprintf(yyout, ";");
}
symbol : S_SYMBOL {
char * name = (char *)malloc(yyleng + 1);
strncpy(name, yytext, yyleng);
name[yyleng] = '\0';
$<symbolname>$ = name;
}
type : maybe_storage_clause type_detailed { $<type>$ = $<type>2; };
type_detailed : varchar_type { $<type>$ = $<type>1; }
| simple_type { $<type>$ = $<type>1; };
varchar_type : varchar_tag symbol index {
fprintf(yyout, "struct varchar_%s { int len; char arr[%d]; } %s", $<symbolname>2, $<indexsize>3, $<symbolname>2);
$<type>$.name = $<symbolname>2;
$<type>$.typ = ECPGmake_varchar_type(ECPGt_varchar, $<indexsize>3);
}
varchar_tag : S_VARCHAR { $<tagname>$ = $<tagname>1; }
| S_VARCHAR2 { $<tagname>$ = $<tagname>1; };
simple_type : simple_tag symbol {
fprintf(yyout, "%s %s", ECPGtype_name($<type_enum>1), $<symbolname>2);
$<type>$.name = $<symbolname>2;
$<type>$.typ = ECPGmake_simple_type($<type_enum>1);
}
simple_tag : S_CHAR { $<type_enum>$ = ECPGt_char; }
| S_UNSIGNED S_CHAR { $<type_enum>$ = ECPGt_unsigned_char; }
| S_SHORT { $<type_enum>$ = ECPGt_short; }
| S_UNSIGNED S_SHORT { $<type_enum>$ = ECPGt_unsigned_short; }
| S_INT { $<type_enum>$ = ECPGt_int; }
| S_UNSIGNED S_INT { $<type_enum>$ = ECPGt_unsigned_int; }
| S_LONG { $<type_enum>$ = ECPGt_long; }
| S_UNSIGNED S_LONG { $<type_enum>$ = ECPGt_unsigned_long; }
| S_FLOAT { $<type_enum>$ = ECPGt_float; }
| S_DOUBLE { $<type_enum>$ = ECPGt_double; };
maybe_storage_clause : S_EXTERN { fwrite(yytext, yyleng, 1, yyout); }
| S_STATIC { fwrite(yytext, yyleng, 1, yyout); }
| /* empty */ { };
index : '[' length ']' {
$<indexsize>$ = $<indexsize>2;
};
length : S_LENGTH { $<indexsize>$ = atoi(yytext); }
sqlinclude : SQL_START SQL_INCLUDE { fprintf(yyout, "#include \""); }
filename SQL_SEMI { fprintf(yyout, ".h\""); };
filename : cthing
| filename cthing;
sqlconnect : SQL_START SQL_CONNECT { fprintf(yyout, "ECPGconnect(\""); }
SQL_STRING { fwrite(yytext + 1, yyleng - 2, 1, yyout); }
SQL_SEMI { fprintf(yyout, "\");"); };
/* Open is an open cursor. Removed. */
sqlopen : SQL_START SQL_OPEN sqlgarbage SQL_SEMI { };
sqlgarbage : /* Empty */
| sqlgarbage sqlanything;
sqlcommit : SQL_START SQL_COMMIT SQL_SEMI {
fprintf(yyout, "ECPGcommit(__LINE__);");
};
sqlrollback : SQL_START SQL_ROLLBACK SQL_SEMI {
fprintf(yyout, "ECPGrollback(__LINE__);");
};
sqlstatement : SQL_START { /* Reset stack */
reset_variables();
fprintf(yyout, "ECPGdo(__LINE__, \"");
}
sqlstatement_words
SQL_SEMI {
/* Dump */
fprintf(yyout, "\", ");
dump_variables(argsinsert);
fprintf(yyout, "ECPGt_EOIT, ");
dump_variables(argsresult);
fprintf(yyout, "ECPGt_EORT );");
};
sqlstatement_words : sqlstatement_word
| sqlstatement_words sqlstatement_word;
sqlstatement_word : ':' symbol
{
add_variable(&argsinsert, find_variable($2));
fprintf(yyout, " ;; ");
}
| SQL_INTO into_list { }
| sqlanything
{
fwrite(yytext, yyleng, 1, yyout);
fwrite(" ", 1, 1, yyout);
}
| SQL_INTO sqlanything
{
fprintf(yyout, " into ");
fwrite(yytext, yyleng, 1, yyout);
fwrite(" ", 1, 1, yyout);
};
into_list : ':' symbol {
add_variable(&argsresult, find_variable($2));
}
| into_list ',' ':' symbol{
add_variable(&argsresult, find_variable($4));
};
cthing : canything {
fwrite(yytext, yyleng, 1, yyout);
}
canything : both_anything
| SQL_INTO
| ';';
sqlanything : both_anything;
both_anything : S_LENGTH | S_VARCHAR | S_VARCHAR2
| S_LONG | S_SHORT | S_INT | S_CHAR | S_FLOAT | S_DOUBLE
| SQL_OPEN | SQL_CONNECT
| SQL_STRING
| SQL_BEGIN | SQL_END
| SQL_DECLARE | SQL_SECTION
| SQL_INCLUDE
| S_SYMBOL
| '[' | ']' | ','
| S_ANYTHING;
%%
void yyerror(char * error)
{
fprintf(stderr, "%s\n", error);
exit(1);
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "type.h"
/* Constructors
Yes, I mostly write c++-code
*/
/* The NAME argument is copied. The type argument is preserved as a pointer. */
struct ECPGrecord_member *
ECPGmake_record_member(char * name, struct ECPGtype * type)
{
struct ECPGrecord_member * ne =
(struct ECPGrecord_member *)malloc(sizeof(struct ECPGrecord_member));
ne->name = strdup(name);
ne->typ = type;
return ne;
}
struct ECPGtype *
ECPGmake_simple_type(enum ECPGttype typ)
{
struct ECPGtype * ne = (struct ECPGtype *)malloc(sizeof(struct ECPGtype));
ne->typ = typ;
ne->size = 0;
ne->u.element = 0;
return ne;
}
struct ECPGtype *
ECPGmake_varchar_type(enum ECPGttype typ, unsigned short siz)
{
struct ECPGtype * ne = ECPGmake_simple_type(typ);
ne->size = siz;
return ne;
}
struct ECPGtype *
ECPGmake_array_type(struct ECPGtype * typ, unsigned short siz)
{
struct ECPGtype * ne = ECPGmake_simple_type(ECPGt_array);
ne->size = siz;
ne->u.element = typ;
return ne;
}
struct ECPGtype *
ECPGmake_record_type(struct ECPGrecord_member * rm[])
{
struct ECPGtype * ne = ECPGmake_simple_type(ECPGt_record);
ne->u.members = rm;
return ne;
}
/* Dump a type.
The type is dumped as:
type-tag <comma> - enum ECPGttype
reference-to-variable <comma> - void *
size <comma> - short size of this field (if varchar)
arrsize <comma> - short number of elements in the arr
offset <comma> - short offset to the next element
Where:
type-tag is one of the simple types or varchar.
reference-to-variable can be a reference to a struct element.
arrsize is the size of the array in case of array fetches. Otherwise 0.
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).
*/
void ECPGdump_a_simple(FILE * o, const char * name, enum ECPGttype typ,
short varcharsize,
unsigned short arrsiz, const char * siz);
void ECPGdump_a_record(FILE * o, const char * name, unsigned short arrsiz,
struct ECPGtype * typ, const char * offset);
void
ECPGdump_a_type(FILE * o, const char * name, struct ECPGtype * typ)
{
if (IS_SIMPLE_TYPE(typ->typ))
{
ECPGdump_a_simple(o, name, typ->typ, typ->size, 0, 0);
}
else if (typ->typ == ECPGt_array)
{
if (IS_SIMPLE_TYPE(typ->u.element->typ))
ECPGdump_a_simple(o, name, typ->u.element->typ,
typ->u.element->size, typ->size, 0);
else if (typ->u.element->typ == ECPGt_array)
{
abort(); /* Array of array, */
}
else if (typ->u.element->typ == ECPGt_record)
{
/* Array of records. */
ECPGdump_a_record(o, name, typ->size, typ->u.element, 0);
}
else
{
abort();
}
}
else if (typ->typ == ECPGt_record)
{
ECPGdump_a_record(o, name, 0, typ, 0);
}
else
{
abort();
}
}
/* If siz is NULL, then the offset is 0, if not use siz as a
string, it represents the offset needed if we are in an array of records. */
void
ECPGdump_a_simple(FILE * o, const char * name, enum ECPGttype typ,
short varcharsize,
unsigned short arrsiz,
const char * siz)
{
switch (typ)
{
case ECPGt_char:
fprintf(o, "ECPGt_char,&%s,0,%d,%s, ", name, arrsiz,
siz == NULL ? "sizeof(char)" : siz);
break;
case ECPGt_unsigned_char:
fprintf(o, "ECPGt_unsigned_char,&%s,0,%d,%s, ", name, arrsiz,
siz == NULL ? "sizeof(unsigned char)" : siz);
break;
case ECPGt_short:
fprintf(o, "ECPGt_short,&%s,0,%d,%s, ", name, arrsiz,
siz == NULL ? "sizeof(short)" : siz);
break;
case ECPGt_unsigned_short:
fprintf(o,
"ECPGt_unsigned_short,&%s,0,%d,%s, ", name, arrsiz,
siz == NULL ? "sizeof(unsigned short)" : siz);
break;
case ECPGt_int:
fprintf(o, "ECPGt_int,&%s,0,%d,%s, ", name, arrsiz,
siz == NULL ? "sizeof(int)" : siz);
break;
case ECPGt_unsigned_int:
fprintf(o, "ECPGt_unsigned_int,&%s,0,%d,%s, ", name, arrsiz,
siz == NULL ? "sizeof(unsigned int)" : siz);
break;
case ECPGt_long:
fprintf(o, "ECPGt_long,&%s,0,%d,%s, ", name, arrsiz,
siz == NULL ? "sizeof(long)" : siz);
break;
case ECPGt_unsigned_long:
fprintf(o, "ECPGt_unsigned_int,&%s,0,%d,%s, ", name, arrsiz,
siz == NULL ? "sizeof(unsigned int)" : siz);
break;
case ECPGt_float:
fprintf(o, "ECPGt_float,&%s,0,%d,%s, ", name, arrsiz,
siz == NULL ? "sizeof(float)" : siz);
break;
case ECPGt_double:
fprintf(o, "ECPGt_double,&%s,0,%d,%s, ", name, arrsiz,
siz == NULL ? "sizeof(double)" : siz);
break;
case ECPGt_varchar:
case ECPGt_varchar2:
if (siz == NULL)
fprintf(o, "ECPGt_varchar,&%s,%d,%d,sizeof(struct varchar_%s), ",
name,
varcharsize,
arrsiz, name);
else
fprintf(o, "ECPGt_varchar,&%s,%d,%d,%s, ",
name,
varcharsize,
arrsiz, siz);
break;
default:
abort();
}
}
/* Penetrate a record and dump the contents. */
void
ECPGdump_a_record(FILE * o,
const char * name, unsigned short arrsiz,
struct ECPGtype * typ, const char * offsetarg)
{
/* If offset is NULL, then this is the first recursive level. If not then
we are in a record in a record and the offset is used as offset.
*/
struct ECPGrecord_member ** p;
char obuf[BUFSIZ];
char buf[BUFSIZ];
const char * offset;
if (offsetarg == NULL)
{
sprintf(obuf, "sizeof(%s)", name);
offset = obuf;
}
else
{
offset = offsetarg;
}
for (p = typ->u.members; *p; p++)
{
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++)
{
if (IS_SIMPLE_TYPE((*p)->typ->u.element->typ))
{
sprintf(buf, "%s.%s[%d]", name, (*p)->name, i);
ECPGdump_a_simple(o, buf, (*p)->typ->typ, (*p)->typ->size,
arrsiz, offset);
}
else if((*p)->typ->u.element->typ == ECPGt_array)
{
/* Array within an array. NOT implemented yet. */
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();
}
}
}
/* Freeing is not really that important. Since we throw away the process
anyway. Lets implement that last! */
void
ECPGfree_record_member(struct ECPGrecord_member * rm)
{
}
void
ECPGfree_type(struct ECPGtype * typ)
{
}
#include <ecpgtype.h>
struct ECPGtype;
struct ECPGrecord_member {
char * name;
struct ECPGtype * typ;
};
struct ECPGtype {
enum ECPGttype typ;
unsigned short size; /* For array it is the number of elements.
* For varchar it is the maxsize of the area.
*/
union {
struct ECPGtype * element; /* For an array this is the type of the
* element */
struct ECPGrecord_member ** members;
/* A pointer to an array of members. */
} u;
};
/* Everything is malloced. */
struct ECPGrecord_member * ECPGmake_record_member(char *, struct ECPGtype *);
struct ECPGtype * ECPGmake_simple_type(enum ECPGttype);
struct ECPGtype * ECPGmake_varchar_type(enum ECPGttype, unsigned short);
struct ECPGtype * ECPGmake_array_type(struct ECPGtype *, unsigned short);
struct ECPGtype * ECPGmake_record_type(struct ECPGrecord_member *[]);
/* Frees a type. */
void ECPGfree_record_member(struct ECPGrecord_member *);
void ECPGfree_type(struct ECPGtype *);
/* Dump a type.
The type is dumped as:
type-tag <comma> reference-to-variable <comma> arrsize <comma> size <comma>
Where:
type-tag is one of the simple types or varchar.
reference-to-variable can be a reference to a struct element.
arrsize is the size of the array in case of array fetches. Otherwise 0.
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).
*/
void ECPGdump_a_type(FILE *, const char * name, struct ECPGtype *);
/* A simple struct to keep a variable and its type. */
struct ECPGtemp_type {
struct ECPGtype * typ;
const char * name;
};
extern const char * ECPGtype_name(enum ECPGttype typ);
test2: test2.c
gcc -g -I ../include -I ../../../libpq -o test2 test2.c ../lib/libecpg.a ../../../libpq/libpq.a -lcrypt
test2.c: test2.qc
../preproc/ecpg < test2.qc > test2.c
/* These two include files are added by the preprocessor */
#include <ecpgtype.h>
#include <ecpglib.h>
/* exec sql begin declare section */
/* VARSIZE */struct varchar_uid { int len; char arr[200]; } uid;
struct varchar_name { int len; char arr[200]; } name;
short value;
/* exec sql end declare section */
#include "sqlca.h"
#define DBCP(x,y) strcpy(x.arr,y);x.len = strlen(x.arr)
#define LENFIX(x) x.len=strlen(x.arr)
#define STRFIX(x) x.arr[x.len]='\0'
#define SQLCODE sqlca.sqlcode
void
db_error (char *msg)
{
sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0';
printf ("%s: db error %s\n", msg, sqlca.sqlerrm.sqlerrmc);
exit (1);
}
int
main ()
{
strcpy (uid.arr, "test/test");
LENFIX (uid);
ECPGconnect("kom");
if (SQLCODE)
db_error ("connect");
strcpy (name.arr, "opt1");
LENFIX (name);
ECPGdo(__LINE__, "declare cur cursor for select name , value from pace_test ", ECPGt_EOIT, ECPGt_EORT );
if (SQLCODE) db_error ("declare");
if (SQLCODE)
db_error ("open");
while (1) {
ECPGdo(__LINE__, "fetch in cur ", ECPGt_EOIT, ECPGt_varchar,&name,200,0,sizeof(struct varchar_name), ECPGt_short,&value,0,0,sizeof(short), ECPGt_EORT );
if (SQLCODE)
break;
STRFIX (name);
printf ("%s\t%d\n", name.arr, value);
}
if (SQLCODE < 0)
db_error ("fetch");
ECPGdo(__LINE__, "close cur ", ECPGt_EOIT, ECPGt_EORT );
if (SQLCODE) db_error ("close");
ECPGcommit(__LINE__);
if (SQLCODE) db_error ("commit");
return (0);
}
exec sql begin declare section;
VARCHAR uid[200 /* VARSIZE */];
varchar name[200];
short value;
exec sql end declare section;
exec sql include sqlca;
#define DBCP(x,y) strcpy(x.arr,y);x.len = strlen(x.arr)
#define LENFIX(x) x.len=strlen(x.arr)
#define STRFIX(x) x.arr[x.len]='\0'
#define SQLCODE sqlca.sqlcode
void
db_error (char *msg)
{
sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0';
printf ("%s: db error %s\n", msg, sqlca.sqlerrm.sqlerrmc);
exit (1);
}
int
main ()
{
strcpy (uid.arr, "test/test");
LENFIX (uid);
exec sql connect 'kom';
if (SQLCODE)
db_error ("connect");
strcpy (name.arr, "opt1");
LENFIX (name);
exec sql declare cur cursor for
select name, value from pace_test;
if (SQLCODE) db_error ("declare");
exec sql open cur;
if (SQLCODE)
db_error ("open");
while (1) {
exec sql fetch in cur into :name, :value;
if (SQLCODE)
break;
STRFIX (name);
printf ("%s\t%d\n", name.arr, value);
}
if (SQLCODE < 0)
db_error ("fetch");
exec sql close cur;
if (SQLCODE) db_error ("close");
exec sql commit;
if (SQLCODE) db_error ("commit");
return (0);
}
/* These two include files are added by the preprocessor */
#include <ecpgtype.h>
#include <ecpglib.h>
#include "sqlca.h"
#define SQLCODE sqlca.sqlcode
void
db_error (char *msg)
{
sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0';
printf ("%s: db error %s\n", msg, sqlca.sqlerrm.sqlerrmc);
exit (1);
}
int
main ()
{
/* exec sql begin declare section */
struct varchar_text { int len; char arr[8]; } text;
/* exec sql end declare section */
ECPGconnect("mm");
if (SQLCODE)
db_error ("connect");
ECPGdo(__LINE__, "declare cur cursor for select text from test ", ECPGt_EOIT, ECPGt_EORT );
if (SQLCODE) db_error ("declare");
if (SQLCODE)
db_error ("open");
while (1) {
ECPGdo(__LINE__, "fetch in cur ", ECPGt_EOIT, ECPGt_varchar,&text,8,0,sizeof(struct varchar_text), ECPGt_EORT );
if (SQLCODE)
break;
printf ("%8.8s\n", text.arr);
}
if (SQLCODE < 0)
db_error ("fetch");
ECPGdo(__LINE__, "close cur ", ECPGt_EOIT, ECPGt_EORT );
if (SQLCODE) db_error ("close");
ECPGcommit(__LINE__);
if (SQLCODE) db_error ("commit");
return (0);
}
exec sql include sqlca;
#define SQLCODE sqlca.sqlcode
void
db_error (char *msg)
{
sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0';
printf ("%s: db error %s\n", msg, sqlca.sqlerrm.sqlerrmc);
exit (1);
}
int
main ()
{
exec sql begin declare section;
varchar text[8];
exec sql end declare section;
exec sql connect 'mm';
if (SQLCODE)
db_error ("connect");
exec sql declare cur cursor for
select text from test;
if (SQLCODE) db_error ("declare");
exec sql open cur;
if (SQLCODE)
db_error ("open");
while (1) {
exec sql fetch in cur into :text;
if (SQLCODE)
break;
printf ("%8.8s\n", text.arr);
}
if (SQLCODE < 0)
db_error ("fetch");
exec sql close cur;
if (SQLCODE) db_error ("close");
exec sql commit;
if (SQLCODE) db_error ("commit");
return (0);
}
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