From 1a9b0613c1b5af012735a81c07dc1e9dd97af945 Mon Sep 17 00:00:00 2001 From: Michael Meskes Date: Thu, 13 Feb 2003 13:11:52 +0000 Subject: [PATCH] - Applied error reporting patch by Matthew Vanecek - Started with an Informix compatibility option. --- src/interfaces/ecpg/ChangeLog | 10 ++++++++++ src/interfaces/ecpg/lib/Makefile | 4 ++-- src/interfaces/ecpg/lib/connect.c | 15 +++++++++----- src/interfaces/ecpg/lib/error.c | 29 ++++++++++++++++++++++++++- src/interfaces/ecpg/lib/execute.c | 30 ++++++++++++++++------------ src/interfaces/ecpg/lib/extern.h | 4 ++++ src/interfaces/ecpg/preproc/ecpg.c | 18 +++++++++++++++-- src/interfaces/ecpg/preproc/extern.h | 3 +++ src/interfaces/ecpg/preproc/pgc.l | 29 ++++++++++++++++++++++++--- 9 files changed, 116 insertions(+), 26 deletions(-) diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog index dd2f52bab9..a7831fb3b1 100644 --- a/src/interfaces/ecpg/ChangeLog +++ b/src/interfaces/ecpg/ChangeLog @@ -1324,3 +1324,13 @@ Tue Jan 21 20:50:58 CET 2003 - Set ecpg version to 2.11.0. - Synced preproc.y with gram.y. + +Thu Feb 13 14:06:28 CET 2003 + + - Applied patch by Matthew Vanecek for better + error reporting. + - Started working on an Informix compatibility mode. With option "-C + INFORMIX" set, ecpg now accepts "$" as alias for "exec sql" and to + denote variables inside SQL statements. + - Set ecpg version to 2.12.0. + diff --git a/src/interfaces/ecpg/lib/Makefile b/src/interfaces/ecpg/lib/Makefile index cfc630537c..20b49ca0af 100644 --- a/src/interfaces/ecpg/lib/Makefile +++ b/src/interfaces/ecpg/lib/Makefile @@ -4,7 +4,7 @@ # # Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/Makefile,v 1.18 2002/12/11 04:07:39 momjian Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/Makefile,v 1.19 2003/02/13 13:11:52 meskes Exp $ # #------------------------------------------------------------------------- @@ -16,7 +16,7 @@ NAME= ecpg SO_MAJOR_VERSION= 3 SO_MINOR_VERSION= 4.2 -override CPPFLAGS := -I$(top_srcdir)/src/interfaces/ecpg/include -I$(libpq_srcdir) $(CPPFLAGS) +override CPPFLAGS := -g -I$(top_srcdir)/src/interfaces/ecpg/include -I$(libpq_srcdir) $(CPPFLAGS) OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \ diff --git a/src/interfaces/ecpg/lib/connect.c b/src/interfaces/ecpg/lib/connect.c index 6cc1f0cb5e..197f9f1caf 100644 --- a/src/interfaces/ecpg/lib/connect.c +++ b/src/interfaces/ecpg/lib/connect.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/connect.c,v 1.19 2002/09/04 20:31:46 momjian Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/connect.c,v 1.20 2003/02/13 13:11:52 meskes Exp $ */ #include "postgres_fe.h" @@ -419,15 +419,20 @@ ECPGconnect(int lineno, const char *name, const char *user, const char *passwd, if (PQstatus(this->connection) == CONNECTION_BAD) { + const char *errmsg = PQerrorMessage(this->connection); + char *db = realname ? realname : ""; + + set_backend_err(errmsg, lineno); ecpg_finish(this); - ECPGlog("connect: could not open database %s on %s port %s %s%s%s%s in line %d\n", - realname ? realname : "", + ECPGlog("connect: could not open database %s on %s port %s %s%s%s%s in line %d\n\t%s\n", + db, host ? host : "", port ? port : "", options ? "with options " : "", options ? options : "", user ? "for user " : "", user ? user : "", - lineno); - ECPGraise(lineno, ECPG_CONNECT, realname ? realname : ""); + lineno, errmsg); + + ECPGraise(lineno, ECPG_CONNECT, db); if (host) ECPGfree(host); if (port) diff --git a/src/interfaces/ecpg/lib/error.c b/src/interfaces/ecpg/lib/error.c index 526634a0f6..d58f6767af 100644 --- a/src/interfaces/ecpg/lib/error.c +++ b/src/interfaces/ecpg/lib/error.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/error.c,v 1.16 2002/09/04 20:31:46 momjian Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/error.c,v 1.17 2003/02/13 13:11:52 meskes Exp $ */ #include "postgres_fe.h" @@ -10,6 +10,10 @@ #include "extern.h" #include "sqlca.h" +/* This should hold the back-end error message from + * the last back-end operation. */ +char *ECPGerr; + void ECPGraise(int line, int code, const char *str) { @@ -162,6 +166,29 @@ ECPGraise(int line, int code, const char *str) ECPGfree_auto_mem(); } +/* Set the error message string from the backend */ +void +set_backend_err(const char *err, int lineno) +{ + if (ECPGerr) + ECPGfree(ECPGerr); + + if (!err) + { + ECPGerr = NULL; + return; + } + + ECPGerr = ECPGstrdup(err, lineno); +} + +/* Retrieve the error message from the backend. */ +char * +ECPGerrmsg(void) +{ + return ECPGerr; +} + /* print out an error message */ void sqlprint(void) diff --git a/src/interfaces/ecpg/lib/execute.c b/src/interfaces/ecpg/lib/execute.c index 70e1d633c8..10f0d8404b 100644 --- a/src/interfaces/ecpg/lib/execute.c +++ b/src/interfaces/ecpg/lib/execute.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/execute.c,v 1.40 2002/10/21 13:09:31 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/execute.c,v 1.41 2003/02/13 13:11:52 meskes Exp $ */ /* * The aim is to get a simpler inteface to the database routines. @@ -850,6 +850,7 @@ ECPGexecute(struct statement * stmt) { bool status = false; char *copiedquery; + char *errmsg, *cmdstat; PGresult *results; PGnotify *notify; struct variable *var; @@ -949,9 +950,10 @@ ECPGexecute(struct statement * stmt) if (results == NULL) { - ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, - PQerrorMessage(stmt->connection->connection)); - ECPGraise(stmt->lineno, ECPG_PGSQL, PQerrorMessage(stmt->connection->connection)); + errmsg = PQerrorMessage(stmt->connection->connection); + ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, errmsg); + ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg); + set_backend_err(errmsg, stmt->lineno); } else @@ -961,7 +963,9 @@ ECPGexecute(struct statement * stmt) */ { bool clear_result = TRUE; - + errmsg = PQresultErrorMessage(results); + set_backend_err(errmsg, stmt->lineno); + var = stmt->outlist; switch (PQresultStatus(results)) { @@ -1027,20 +1031,20 @@ ECPGexecute(struct statement * stmt) break; case PGRES_COMMAND_OK: status = true; + cmdstat = PQcmdStatus(results); sqlca.sqlerrd[1] = PQoidValue(results); sqlca.sqlerrd[2] = atol(PQcmdTuples(results)); - ECPGlog("ECPGexecute line %d Ok: %s\n", stmt->lineno, PQcmdStatus(results)); - if (!sqlca.sqlerrd[2] && (!strncmp(PQcmdStatus(results), "UPDATE", 6) - || !strncmp(PQcmdStatus(results), "INSERT", 6) - || !strncmp(PQcmdStatus(results), "DELETE", 6))) + ECPGlog("ECPGexecute line %d Ok: %s\n", stmt->lineno, cmdstat); + if (!sqlca.sqlerrd[2] && ( !strncmp(cmdstat, "UPDATE", 6) + || !strncmp(cmdstat, "INSERT", 6) + || !strncmp(cmdstat, "DELETE", 6))) ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL); break; case PGRES_NONFATAL_ERROR: case PGRES_FATAL_ERROR: case PGRES_BAD_RESPONSE: - ECPGlog("ECPGexecute line %d: Error: %s", - stmt->lineno, PQerrorMessage(stmt->connection->connection)); - ECPGraise(stmt->lineno, ECPG_PGSQL, PQerrorMessage(stmt->connection->connection)); + ECPGlog("ECPGexecute line %d: Error: %s", stmt->lineno, errmsg); + ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg); status = false; break; case PGRES_COPY_OUT: @@ -1054,7 +1058,7 @@ ECPGexecute(struct statement * stmt) default: ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n", stmt->lineno); - ECPGraise(stmt->lineno, ECPG_PGSQL, PQerrorMessage(stmt->connection->connection)); + ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg); status = false; break; } diff --git a/src/interfaces/ecpg/lib/extern.h b/src/interfaces/ecpg/lib/extern.h index 00a65b94e2..ad0e6b332b 100644 --- a/src/interfaces/ecpg/lib/extern.h +++ b/src/interfaces/ecpg/lib/extern.h @@ -5,6 +5,10 @@ #include "libpq-fe.h" /* Here are some methods used by the lib. */ + +/* Stores the backend error message for client access */ +void set_backend_err(const char *err, int lineon); + /* Returns a pointer to a string containing a simple type name. */ void ECPGadd_mem(void *ptr, int lineno); diff --git a/src/interfaces/ecpg/preproc/ecpg.c b/src/interfaces/ecpg/preproc/ecpg.c index 04c745775d..d09c6d1474 100644 --- a/src/interfaces/ecpg/preproc/ecpg.c +++ b/src/interfaces/ecpg/preproc/ecpg.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/ecpg.c,v 1.58 2002/10/18 22:05:36 petere Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/ecpg.c,v 1.59 2003/02/13 13:11:52 meskes Exp $ */ /* New main for ecpg, the PostgreSQL embedded SQL precompiler. */ /* (C) Michael Meskes Feb 5th, 1998 */ @@ -19,6 +19,9 @@ extern char *optarg; int ret_value = 0, autocommit = false, auto_create_c = false; + +enum COMPAT_MODE compat = ECPG_COMPAT_PGSQL; + struct _include_path *include_paths = NULL; struct cursor *cur = NULL; struct typedefs *types = NULL; @@ -38,6 +41,8 @@ help(const char *progname) #ifdef YYDEBUG printf(" -d generate parser debug output\n"); #endif + printf(" -C set compatibility mode\n" + " mode may be INFORMIX only at the moment\n"); printf(" -D SYMBOL define SYMBOL\n"); printf(" -I DIRECTORY search DIRECTORY for include files\n"); printf(" -o OUTFILE write result to OUTFILE\n"); @@ -107,7 +112,7 @@ main(int argc, char *const argv[]) add_include_path("/usr/local/include"); add_include_path("."); - while ((c = getopt(argc, argv, "vco:I:tD:d")) != -1) + while ((c = getopt(argc, argv, "vco:I:tD:dC:")) != -1) { switch (c) { @@ -130,6 +135,15 @@ main(int argc, char *const argv[]) case 'c': auto_create_c = true; break; + case 'C': + if (strcmp(optarg, "INFORMIX") == 0) + compat = ECPG_COMPAT_INFORMIX; + else + { + fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); + return ILLEGAL_OPTION; + } + break; case 'D': add_preprocessor_define(optarg); break; diff --git a/src/interfaces/ecpg/preproc/extern.h b/src/interfaces/ecpg/preproc/extern.h index 6e49948ffb..3a30225f0c 100644 --- a/src/interfaces/ecpg/preproc/extern.h +++ b/src/interfaces/ecpg/preproc/extern.h @@ -93,4 +93,7 @@ extern ScanKeyword *ScanKeywordLookup(char *text); #define INDICATOR_NOT_STRUCT 6 #define INDICATOR_NOT_SIMPLE 7 +enum COMPAT_MODE { ECPG_COMPAT_PGSQL = 0, ECPG_COMPAT_INFORMIX}; +extern enum COMPAT_MODE compat; + #endif /* _ECPG_PREPROC_EXTERN_H */ diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l index 65bd683b9f..0ff7900c68 100644 --- a/src/interfaces/ecpg/preproc/pgc.l +++ b/src/interfaces/ecpg/preproc/pgc.l @@ -12,7 +12,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/pgc.l,v 1.101 2002/11/07 06:06:17 tgl Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/pgc.l,v 1.102 2003/02/13 13:11:52 meskes Exp $ * *------------------------------------------------------------------------- */ @@ -70,7 +70,6 @@ static struct _if_value %option 8bit %option never-interactive -%option nounput %option noyywrap %option yylineno @@ -247,6 +246,10 @@ whitespace ({space}+|{comment}) horiz_whitespace ({horiz_space}|{comment}) whitespace_with_newline ({horiz_whitespace}*{newline}{whitespace}*) +/* special characters for other dbms */ +/* we have to react differently in compat mode */ +informix_special [\$] + other . /* some stuff needed for ecpg */ @@ -416,6 +419,16 @@ cppline {space}*#(.*\\{space})*.* } {xdcinside} { addlit(yytext, yyleng); } {typecast} { return TYPECAST; } +{informix_special} { + /* are we simulating Informix? */ + if (compat == ECPG_COMPAT_INFORMIX) + { + printf ("unput $\n"); + unput(':'); + } + else + return yytext[0]; + } {self} { /* * We may find a ';' inside a structure * definition in a TYPE or VAR statement. @@ -584,7 +597,17 @@ cppline {space}*#(.*\\{space})*.* } {other} { return yytext[0]; } {exec_sql} { BEGIN SQL; return SQL_START; } -{ccomment} { /* ignore */ } +{informix_special} { + /* are we simulating Informix? */ + if (compat == ECPG_COMPAT_INFORMIX) + { + BEGIN SQL; + return SQL_START; + } + else + return S_ANYTHING; + } +{ccomment} { /* ignore */ } {xch} { char* endptr;