ECPG dynamic cursor, SQLDA support - Mailing list pgsql-hackers
From | Boszormenyi Zoltan |
---|---|
Subject | ECPG dynamic cursor, SQLDA support |
Date | |
Msg-id | 4A41F73D.7020502@cybertec.at Whole thread Raw |
Responses |
Re: ECPG dynamic cursor, SQLDA support
Re: ECPG dynamic cursor, SQLDA support |
List | pgsql-hackers |
Hi, attached is our latest patch extending ECPG: 1. Support computed cursorname (DECLARE :cursorname ..., etc) No change in that part, it's purely a parser-only change, there's no ECPGdo() change you criticized or any other bad juju. Because of adding support for :cursorname and the dynamically generated ECPG grammar, we needed to modify the main gram.y as well, to make way for computed cursor names. The fetch_direction and FetchStmt change was needed in gram.y because otherwise "FETCH fetch_direction from_in cursor_name" and "MOVE fetch_direction from_in cursor_name" rules caused shift/reduce conflicts in the "fetch_direction" rule in ECPG grammar. This modification was a little sacrifice in gram.y but it allowed keeping the ECPG grammar clean and little change in the automatic grammar generation. 2. Support SQLDA structure in ECPG, both in USING DESCRIPTOR and INTO DESCRIPTOR, if Informix-compatible mode is set. This means the following changes - introduce the pg_sqlda_t and pg_sqlvar_t structures, ECPGt_sqlda type and SQLNNN contants in sqltypes.h - make a distinction between DESCRIPTOR and SQL DESCRIPTOR in Informix mode - Support FETCH ... USING DESCRIPTOR in Informix mode only, that works the same way as FETCH ... INTO DESCRIPTOR. 3. Support DESCRIBE OUTPUT The support was added as a new, exported ECPGdescribe2() function in descriptor.c, the old unsupported ECPGescribe() interface was left intact so the libecpg.so major library version doesn't need to be changed. Best regards, Zoltán Böszörményi -- Bible has answers for everything. Proof: "But let your communication be, Yea, yea; Nay, nay: for whatsoever is more than these cometh of evil." (Matthew 5:37) - basics of digital technology. "May your kingdom come" - superficial description of plate tectonics ---------------------------------- Zoltán Böszörményi Cybertec Schönig & Schönig GmbH http://www.postgresql.at/ diff -dcrpN postgresql-8.4rc2/src/backend/parser/gram.y postgresql-8.4rc2-sqlda/src/backend/parser/gram.y *** postgresql-8.4rc2/src/backend/parser/gram.y 2009-06-18 03:27:02.000000000 +0200 --- postgresql-8.4rc2-sqlda/src/backend/parser/gram.y 2009-06-24 10:33:27.000000000 +0200 *************** static TypeName *TableFuncTypeName(List *** 250,256 **** %type <str> relation_name copy_file_name database_name access_method_clause access_method attr_name ! index_name name file_name cluster_index_specification %type <list> func_name handler_name qual_Op qual_all_Op subquery_Op opt_class opt_validator validator_clause --- 250,256 ---- %type <str> relation_name copy_file_name database_name access_method_clause access_method attr_name ! index_name name cursor_name file_name cluster_index_specification %type <list> func_name handler_name qual_Op qual_all_Op subquery_Op opt_class opt_validator validator_clause *************** reloption_elem: *** 1863,1869 **** *****************************************************************************/ ClosePortalStmt: ! CLOSE name { ClosePortalStmt *n = makeNode(ClosePortalStmt); n->portalname = $2; --- 1863,1869 ---- *****************************************************************************/ ClosePortalStmt: ! CLOSE cursor_name { ClosePortalStmt *n = makeNode(ClosePortalStmt); n->portalname = $2; *************** comment_text: *** 4056,4069 **** * *****************************************************************************/ ! FetchStmt: FETCH fetch_direction from_in name { FetchStmt *n = (FetchStmt *) $2; n->portalname = $4; n->ismove = FALSE; $$ = (Node *)n; } ! | FETCH name { FetchStmt *n = makeNode(FetchStmt); n->direction = FETCH_FORWARD; --- 4056,4087 ---- * *****************************************************************************/ ! FetchStmt: FETCH BACKWARD from_in cursor_name ! { ! FetchStmt *n = makeNode(FetchStmt); ! n->portalname = $4; ! n->ismove = FALSE; ! n->direction = FETCH_BACKWARD; ! n->howMany = 1; ! $$ = (Node *)n; ! } ! | FETCH FORWARD from_in cursor_name ! { ! FetchStmt *n = makeNode(FetchStmt); ! n->portalname = $4; ! n->ismove = FALSE; ! n->direction = FETCH_FORWARD; ! n->howMany = 1; ! $$ = (Node *)n; ! } ! | FETCH fetch_direction from_in cursor_name { FetchStmt *n = (FetchStmt *) $2; n->portalname = $4; n->ismove = FALSE; $$ = (Node *)n; } ! | FETCH cursor_name { FetchStmt *n = makeNode(FetchStmt); n->direction = FETCH_FORWARD; *************** FetchStmt: FETCH fetch_direction from_in *** 4072,4085 **** n->ismove = FALSE; $$ = (Node *)n; } ! | MOVE fetch_direction from_in name { FetchStmt *n = (FetchStmt *) $2; n->portalname = $4; n->ismove = TRUE; $$ = (Node *)n; } ! | MOVE name { FetchStmt *n = makeNode(FetchStmt); n->direction = FETCH_FORWARD; --- 4090,4121 ---- n->ismove = FALSE; $$ = (Node *)n; } ! | MOVE BACKWARD from_in cursor_name ! { ! FetchStmt *n = makeNode(FetchStmt); ! n->portalname = $4; ! n->ismove = TRUE; ! n->direction = FETCH_BACKWARD; ! n->howMany = 1; ! $$ = (Node *)n; ! } ! | MOVE FORWARD from_in cursor_name ! { ! FetchStmt *n = makeNode(FetchStmt); ! n->portalname = $4; ! n->ismove = TRUE; ! n->direction = FETCH_FORWARD; ! n->howMany = 1; ! $$ = (Node *)n; ! } ! | MOVE fetch_direction from_in cursor_name { FetchStmt *n = (FetchStmt *) $2; n->portalname = $4; n->ismove = TRUE; $$ = (Node *)n; } ! | MOVE cursor_name { FetchStmt *n = makeNode(FetchStmt); n->direction = FETCH_FORWARD; *************** fetch_direction: *** 4154,4166 **** n->howMany = FETCH_ALL; $$ = (Node *)n; } - | FORWARD - { - FetchStmt *n = makeNode(FetchStmt); - n->direction = FETCH_FORWARD; - n->howMany = 1; - $$ = (Node *)n; - } | FORWARD SignedIconst { FetchStmt *n = makeNode(FetchStmt); --- 4190,4195 ---- *************** fetch_direction: *** 4175,4187 **** n->howMany = FETCH_ALL; $$ = (Node *)n; } - | BACKWARD - { - FetchStmt *n = makeNode(FetchStmt); - n->direction = FETCH_BACKWARD; - n->howMany = 1; - $$ = (Node *)n; - } | BACKWARD SignedIconst { FetchStmt *n = makeNode(FetchStmt); --- 4204,4209 ---- *************** set_target_list: *** 6770,6776 **** * CURSOR STATEMENTS * *****************************************************************************/ ! DeclareCursorStmt: DECLARE name cursor_options CURSOR opt_hold FOR SelectStmt { DeclareCursorStmt *n = makeNode(DeclareCursorStmt); n->portalname = $2; --- 6792,6798 ---- * CURSOR STATEMENTS * *****************************************************************************/ ! DeclareCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR SelectStmt { DeclareCursorStmt *n = makeNode(DeclareCursorStmt); n->portalname = $2; *************** DeclareCursorStmt: DECLARE name cursor_o *** 6781,6786 **** --- 6803,6811 ---- } ; + cursor_name: name { $$ = $1; } + ; + cursor_options: /*EMPTY*/ { $$ = 0; } | cursor_options NO SCROLL { $$ = $1 | CURSOR_OPT_NO_SCROLL; } | cursor_options SCROLL { $$ = $1 | CURSOR_OPT_SCROLL; } diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/descriptor.c postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/descriptor.c *** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/descriptor.c 2009-06-11 16:49:13.000000000 +0200 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/descriptor.c 2009-06-24 10:33:27.000000000 +0200 *************** *** 13,18 **** --- 13,19 ---- #include "ecpgerrno.h" #include "extern.h" #include "sqlca.h" + #include "sqlda.h" #include "sql3types.h" static void descriptor_free(struct descriptor * desc); *************** get_char_item(int lineno, void *var, enu *** 225,230 **** --- 226,237 ---- return (true); } + #define RETURN_IF_NO_DATA if (ntuples < 1) \ + { \ + ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); \ + return (false); \ + } + bool ECPGget_desc(int lineno, const char *desc_name, int index,...) { *************** ECPGget_desc(int lineno, const char *des *** 243,253 **** return (false); ntuples = PQntuples(ECPGresult); - if (ntuples < 1) - { - ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); - return (false); - } if (index < 1 || index > PQnfields(ECPGresult)) { --- 250,255 ---- *************** ECPGget_desc(int lineno, const char *des *** 282,287 **** --- 284,290 ---- switch (type) { case (ECPGd_indicator): + RETURN_IF_NO_DATA; data_var.ind_type = vartype; data_var.ind_pointer = var; data_var.ind_varcharsize = varcharsize; *************** ECPGget_desc(int lineno, const char *des *** 294,299 **** --- 297,303 ---- break; case ECPGd_data: + RETURN_IF_NO_DATA; data_var.type = vartype; data_var.pointer = var; data_var.varcharsize = varcharsize; *************** ECPGget_desc(int lineno, const char *des *** 376,381 **** --- 380,386 ---- case ECPGd_ret_length: case ECPGd_ret_octet: + RETURN_IF_NO_DATA; /* * this is like ECPGstore_result */ *************** ECPGget_desc(int lineno, const char *des *** 479,484 **** --- 484,490 ---- sqlca->sqlerrd[2] = ntuples; return (true); } + #undef RETURN_IF_NO_DATA bool ECPGset_desc_header(int lineno, const char *desc_name, int count) *************** ecpg_find_desc(int line, const char *nam *** 721,729 **** --- 727,846 ---- return NULL; /* not found */ } + static pg_sqlda_t* + build_sqlda(int lineno, bool input, const char *connection_name, const char *stmt_name) + { + struct connection *con; + PGresult *res; + pg_sqlda_t *sqlda = NULL; + + con = ecpg_get_connection(connection_name); + res = PQdescribePrepared(con->connection, stmt_name); + if (!ecpg_check_PQresult(res, lineno, con->connection, ECPG_COMPAT_INFORMIX)) + return NULL; + + sqlda = ecpg_build_sqlda_for_PGresult(lineno, res); + + PQclear(res); + return sqlda; + } + + /* Old, unsupported interface */ bool ECPGdescribe(int line, bool input, const char *statement,...) { ecpg_log("ECPGdescribe called on line %d for %s: %s\n", line, input ? "input" : "output", statement); return false; } + + /* New interface for DESCRIBE [INPUT|OUTPUT], both for named descriptor and Informix-like SQLDA */ + bool + ECPGdescribe2(int line, bool input, const char *connection_name, const char *stmt_name, ...) + { + bool ret = false; + va_list args; + + /* DESCRIBE INPUT is not yet supported */ + if (input) + return false; + + va_start(args, stmt_name); + + for (;;) + { + enum ECPGttype type, dummy_type; + void *ptr, *dummy_ptr; + long dummy; + + /* variable type */ + type = va_arg(args, enum ECPGttype); + + if (type == ECPGt_EORT) + break; + + /* rest of variable parameters*/ + ptr = va_arg(args, void *); + dummy = va_arg(args, long); + dummy = va_arg(args, long); + dummy = va_arg(args, long); + + /* variable indicator */ + dummy_type = va_arg(args, enum ECPGttype); + dummy_ptr = va_arg(args, void *); + dummy = va_arg(args, long); + dummy = va_arg(args, long); + dummy = va_arg(args, long); + + switch (type) + { + case ECPGt_descriptor: + { + char *name = ptr; + struct connection *con = ecpg_get_connection(connection_name); + struct descriptor *desc = ecpg_find_desc(line, name); + PGresult *res; + ExecStatusType ret; + + if (con == NULL) + break; + if (desc == NULL) + break; + + res = PQdescribePrepared(con->connection, stmt_name); + ret = PQresultStatus(res); + if (ecpg_check_PQresult(res, line, con->connection, ECPG_COMPAT_PGSQL)) + { + if (desc->result != NULL) + PQclear(desc->result); + desc->result = res; + ret = true; + } + break; + } + case ECPGt_sqlda: + { + pg_sqlda_t **sqlda_ptr = ptr; + pg_sqlda_t *sqlda_new; + + sqlda_new = build_sqlda(line, input, connection_name, stmt_name); + if (sqlda_new) + { + /* slight leak */ + #if 0 + pg_sqlda_t *sqlda_old = *sqlda_ptr; + if (sqlda_old) + free(sqlda_old); */ + #endif + *sqlda_ptr = sqlda_new; + ret = true; + } + break; + } + default: + /* nothing else may come */ + ; + } + } + + return ret; + } diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/execute.c postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/execute.c *** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/execute.c 2009-06-11 16:49:13.000000000 +0200 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/execute.c 2009-06-24 10:33:27.000000000 +0200 *************** *** 25,30 **** --- 25,31 ---- #include "ecpgerrno.h" #include "extern.h" #include "sqlca.h" + #include "sqlda.h" #include "sql3types.h" #include "pgtypes_numeric.h" #include "pgtypes_date.h" *************** ecpg_store_input(const int lineno, const *** 1033,1038 **** --- 1034,1042 ---- case ECPGt_descriptor: break; + case ECPGt_sqlda: + break; + default: /* Not implemented yet */ ecpg_raise(lineno, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (char *) ecpg_type_name(var->type)); *************** ecpg_execute(struct statement * stmt) *** 1170,1175 **** --- 1174,1235 ---- if (desc->count == desc_counter) desc_counter = 0; } + else if (var->type == ECPGt_sqlda) + { + pg_sqlda_t **_sqlda = (pg_sqlda_t **)var->pointer; + pg_sqlda_t *sqlda = *_sqlda; + struct variable desc_inlist; + int i; + + if (sqlda == NULL) + return false; + + desc_counter++; + for (i = 0; i < sqlda->sqld; i++) + { + if (i + 1 == desc_counter) + { + desc_inlist.type = ecpg_sqlda_type(sqlda->sqlvar[i].sqltype); + desc_inlist.value = sqlda->sqlvar[i].sqldata; + desc_inlist.pointer = &(sqlda->sqlvar[i].sqldata); + switch (desc_inlist.type) + { + case ECPGt_char: + case ECPGt_varchar: + desc_inlist.varcharsize = strlen(sqlda->sqlvar[i].sqldata); + break; + default: + desc_inlist.varcharsize = 0; + break; + } + desc_inlist.arrsize = 1; + desc_inlist.offset = 0; + if (sqlda->sqlvar[i].sqlind) + { + desc_inlist.ind_type = ECPGt_short; + /* ECPG expects indicator value < 0 */ + if (*(sqlda->sqlvar[i].sqlind)) + *(sqlda->sqlvar[i].sqlind) = -1; + desc_inlist.ind_value = sqlda->sqlvar[i].sqlind; + desc_inlist.ind_pointer = &(sqlda->sqlvar[i].sqlind); + desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = 1; + desc_inlist.ind_offset = 0; + } + else + { + desc_inlist.ind_type = ECPGt_NO_INDICATOR; + desc_inlist.ind_value = desc_inlist.ind_pointer = NULL; + desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = desc_inlist.ind_offset = 0; + } + if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, &desc_inlist, &tobeinserted, false)) + return false; + + break; + } + } + if (sqlda->sqld == desc_counter) + desc_counter = 0; + } else { if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, var, &tobeinserted, false)) *************** ecpg_execute(struct statement * stmt) *** 1351,1356 **** --- 1411,1437 ---- } var = var->next; } + else if (var != NULL && var->type == ECPGt_sqlda) + { + pg_sqlda_t **_sqlda = (pg_sqlda_t **)var->pointer; + pg_sqlda_t *sqlda = *_sqlda; + + if (!sqlda) + { + sqlda = ecpg_build_sqlda_for_PGresult(stmt->lineno, results); + if (!sqlda) + status = false; + else + *_sqlda = sqlda; + } + else if (!ecpg_compare_sqlda_with_PGresult(sqlda, results)) + status = false; + + if (status == true) + ecpg_set_sqlda_from_PGresult(stmt->lineno, _sqlda, results); + + var = var->next; + } else for (act_field = 0; act_field < nfields && status; act_field++) { diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/exports.txt postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/exports.txt *** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/exports.txt 2008-03-25 13:45:25.000000000 +0100 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/exports.txt 2009-06-24 10:33:27.000000000 +0200 *************** ECPGstatus 23 *** 26,28 **** --- 26,29 ---- ECPGtrans 24 sqlprint 25 ECPGget_PGconn 26 + ECPGdescribe2 27 diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/extern.h postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/extern.h *** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/extern.h 2009-05-20 18:13:18.000000000 +0200 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/extern.h 2009-06-24 10:33:27.000000000 +0200 *************** *** 6,11 **** --- 6,12 ---- #include "postgres_fe.h" #include "libpq-fe.h" #include "sqlca.h" + #include "sqlda.h" #include "ecpg_config.h" #ifndef CHAR_BIT #include <limits.h> *************** bool ecpg_init(const struct connection *** 129,134 **** --- 130,137 ---- char *ecpg_strdup(const char *, int); const char *ecpg_type_name(enum ECPGttype); int ecpg_dynamic_type(Oid); + int ecpg_sqlda_type(int); + int ecpg_to_sqlda_type(Oid); void ecpg_free_auto_mem(void); void ecpg_clear_auto_mem(void); *************** void ecpg_log(const char *format,...); *** 149,154 **** --- 152,161 ---- bool ecpg_auto_prepare(int, const char *, const int, char **, const char *); void ecpg_init_sqlca(struct sqlca_t * sqlca); + pg_sqlda_t *ecpg_build_sqlda_for_PGresult(int, PGresult *); + bool ecpg_compare_sqlda_with_PGresult(pg_sqlda_t *sqlda, const PGresult *results); + void ecpg_set_sqlda_from_PGresult(int, pg_sqlda_t **, const PGresult *); + /* SQLSTATE values generated or processed by ecpglib (intentionally * not exported -- users should refer to the codes directly) */ diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/Makefile postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/Makefile *** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/Makefile 2009-02-07 18:17:34.000000000 +0100 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/Makefile 2009-06-24 10:33:27.000000000 +0200 *************** include $(top_builddir)/src/Makefile.glo *** 15,21 **** NAME= ecpg SO_MAJOR_VERSION= 6 ! SO_MINOR_VERSION= 1 override CPPFLAGS := -I../include -I$(top_srcdir)/src/interfaces/ecpg/include \ -I$(libpq_srcdir) -I$(top_builddir)/src/port $(CPPFLAGS) --- 15,21 ---- NAME= ecpg SO_MAJOR_VERSION= 6 ! SO_MINOR_VERSION= 2 override CPPFLAGS := -I../include -I$(top_srcdir)/src/interfaces/ecpg/include \ -I$(libpq_srcdir) -I$(top_builddir)/src/port $(CPPFLAGS) *************** override CFLAGS += $(PTHREAD_CFLAGS) *** 24,30 **** # Need to recompile any libpgport object files LIBS := $(filter-out -lpgport, $(LIBS)) ! OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \ connect.o misc.o path.o pgstrcasecmp.o \ $(filter snprintf.o strlcpy.o, $(LIBOBJS)) --- 24,30 ---- # Need to recompile any libpgport object files LIBS := $(filter-out -lpgport, $(LIBS)) ! OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \ connect.o misc.o path.o pgstrcasecmp.o \ $(filter snprintf.o strlcpy.o, $(LIBOBJS)) *************** ifneq ($(PORTNAME), win32) *** 33,39 **** OBJS += thread.o endif ! SHLIB_LINK = -L../pgtypeslib -lpgtypes $(libpq) $(filter -lintl -lm, $(LIBS)) $(PTHREAD_LIBS) SHLIB_EXPORTS = exports.txt --- 33,39 ---- OBJS += thread.o endif ! SHLIB_LINK = -L../pgtypeslib -lpgtypes $(libpq) $(filter -lintl -lm -ldl, $(LIBS)) $(PTHREAD_LIBS) SHLIB_EXPORTS = exports.txt diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/sqlda.c postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/sqlda.c *** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/sqlda.c 1970-01-01 01:00:00.000000000 +0100 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/sqlda.c 2009-06-24 10:33:27.000000000 +0200 *************** *** 0 **** --- 1,271 ---- + /* + * Crude SQLDA support routines + * Only supports fetching 1 record at a time + * + * The allocated memory area pointed by an sqlda pointer + * contains both the metadata and the data, so freeing up + * is a simple free(sqlda) as expected by the ESQL/C examples. + * + * (C) 2009 Cybertec GmbH + * Zoltán Böszörményi <zb@cybertec.at> + * Hans-Jürgen Schönig <hs@cybertec.at> + */ + + #define POSTGRES_ECPG_INTERNAL + #include "postgres_fe.h" + #include "pg_type.h" + + #include <inttypes.h> + #include <dlfcn.h> + + #include "ecpg-pthread-win32.h" + #include "decimal.h" + #include "ecpgtype.h" + #include "ecpglib.h" + #include "ecpgerrno.h" + #include "extern.h" + #include "sqlca.h" + #include "sqlda.h" + #include "sqltypes.h" + + /* + * Build pg_sqlda_t (metadata only) from PGresult + */ + pg_sqlda_t * + ecpg_build_sqlda_for_PGresult(int line, PGresult *res) + { + pg_sqlda_t *sqlda; + pg_sqlvar_t*sqlvar; + char *fname; + long size; + int i; + + size = sizeof(pg_sqlda_t) + PQnfields(res) * sizeof(pg_sqlvar_t); + for (i = 0; i < PQnfields(res); i++) + size += strlen(PQfname(res, i)) + 1; + /* round allocated size up to the next multiple of 8 */ + if (size % 8) + size += 8 - (size % 8); + + sqlda = (pg_sqlda_t *)ecpg_alloc(size, line); + if (!sqlda) + { + ecpg_raise(line, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); + return NULL; + } + memset(sqlda, 0, size); + sqlvar = (pg_sqlvar_t *)(sqlda + 1); + fname = (char *)(sqlvar + PQnfields(res)); + + sqlda->sqld = PQnfields(res); + sqlda->desc_occ = size; /* cheat here, keep the full allocated size */ + sqlda->sqlvar = sqlvar; + + for (i = 0; i < sqlda->sqld; i++) + { + sqlda->sqlvar[i].sqltype = ecpg_to_sqlda_type(PQftype(res, i)); + strcpy(fname, PQfname(res, i)); + sqlda->sqlvar[i].sqlname = fname; + fname += strlen(sqlda->sqlvar[i].sqlname) + 1; + sqlda->sqlvar[i].sqlformat = (char *)(long)PQfformat(res, i); + sqlda->sqlvar[i].sqlxid = PQftype(res, i); + sqlda->sqlvar[i].sqltypelen = PQfsize(res, i); + } + + return sqlda; + } + + /* + * Check whether the supplied sqlda and PGresult + * both has the same metadata + */ + bool + ecpg_compare_sqlda_with_PGresult(pg_sqlda_t *sqlda, const PGresult *res) + { + int i; + + if (sqlda->sqld != PQnfields(res)) + return false; + + for (i = 0; i < sqlda->sqld; i++) + { + if (sqlda->sqlvar[i].sqltype != ecpg_dynamic_type(PQftype(res, i))) + return false; + if (strcmp(sqlda->sqlvar[i].sqlname, PQfname(res, i))) + return false; + if (sqlda->sqlvar[i].sqlformat != (char *)(long)PQfformat(res, i)) + return false; + if (sqlda->sqlvar[i].sqlxid != PQftype(res, i)) + return false; + if (sqlda->sqlvar[i].sqltypelen != PQfsize(res, i)) + return false; + } + + return true; + } + + static long + ecpg_sqlda_size_round_align(long size, int alignment, int round) + { + if (size % alignment) + size += alignment - (size % alignment); + size += round; + return size; + } + + static long + ecpg_sqlda_size_align(long size, int alignment) + { + if (size % alignment) + size += alignment - (size % alignment); + return size; + } + + /* + * Sets values from PGresult. + * Reallocates the memory area pointed by *_sqlda if needed + */ + void + ecpg_set_sqlda_from_PGresult(int lineno, pg_sqlda_t **_sqlda, const PGresult *res) + { + pg_sqlda_t *sqlda = (*_sqlda); + int i; + long size; + static int2 value_is_null = 1; + static int2 value_is_not_null = 0; + + /* Compute new structure size for allocation */ + size = sizeof(pg_sqlda_t) + sqlda->sqld * sizeof(pg_sqlvar_t); + for (i = 0; i < PQnfields(res); i++) + size += strlen(PQfname(res, i)) + 1; + + for (i = 0; i < sqlda->sqld; i++) + { + switch (sqlda->sqlvar[i].sqltype) + { + case SQLSMINT: + size = ecpg_sqlda_size_round_align(size, sizeof(short), sizeof(short)); + break; + case SQLINT: + case SQLSERIAL: + size = ecpg_sqlda_size_round_align(size, sizeof(int), sizeof(int)); + break; + case SQLFLOAT: + size = ecpg_sqlda_size_round_align(size, sizeof(double), sizeof(double)); + break; + case SQLSMFLOAT: + size = ecpg_sqlda_size_round_align(size, sizeof(float), sizeof(float)); + break; + case SQLDECIMAL: + size = ecpg_sqlda_size_round_align(size, sizeof(int), sizeof(decimal)); + break; + case SQLINT8: + case SQLSERIAL8: + size = ecpg_sqlda_size_round_align(size, sizeof(int64_t), sizeof(int64_t)); + break; + + /* + * These types will be passed as character strings + * until we know what to do with them. + */ + case SQLCHAR: + case SQLTEXT: + case SQLVCHAR: + case SQLNCHAR: + case SQLNVCHAR: + case SQLMONEY: + case SQLDATE: + case SQLDTIME: + case SQLINTERVAL: + default: + break; + } + } + + if (sqlda->desc_occ < size) + { + sqlda = realloc(sqlda, size); + *_sqlda = sqlda; + sqlda->desc_occ = size; + } + + /* + * Set sqlvar[i]->sqldata pointers and convert values to correct format + */ + size = sizeof(pg_sqlda_t) + sqlda->sqld * sizeof(pg_sqlvar_t); + for (i = 0; i < PQnfields(res); i++) + size += strlen(PQfname(res, i)) + 1; + + for (i = 0; i < sqlda->sqld; i++) + { + switch (sqlda->sqlvar[i].sqltype) + { + case SQLSMINT: + size = ecpg_sqlda_size_align(size, sizeof(short)); + sscanf(PQgetvalue(res, 0, i), "%hd", (short *)((char *)sqlda + size)); + sqlda->sqlvar[i].sqldata = (char *)sqlda + size; + size += sizeof(short); + break; + case SQLINT: + case SQLSERIAL: + size = ecpg_sqlda_size_align(size, sizeof(int)); + sscanf(PQgetvalue(res, 0, i), "%d", (int *)((char *)sqlda + size)); + sqlda->sqlvar[i].sqldata = (char *)sqlda + size; + size += sizeof(int); + break; + case SQLFLOAT: + size = ecpg_sqlda_size_align(size, sizeof(double)); + sscanf(PQgetvalue(res, 0, i), "%lf", (double *)((char *)sqlda + size)); + sqlda->sqlvar[i].sqldata = (char *)sqlda + size; + size += sizeof(double); + break; + case SQLSMFLOAT: + size = ecpg_sqlda_size_align(size, sizeof(float)); + sscanf(PQgetvalue(res, 0, i), "%f", (float *)((char *)sqlda + size)); + sqlda->sqlvar[i].sqldata = (char *)sqlda + size; + size += sizeof(float); + break; + case SQLDECIMAL: + { + size = ecpg_sqlda_size_align(size, sizeof(int)); + sqlda->sqlvar[i].sqldata = (char *)sqlda + size; + + ecpg_get_data(res, 0, i, lineno, + ECPGt_decimal, ECPGt_NO_INDICATOR, + sqlda->sqlvar[i].sqldata, NULL, 0, 0, 0, + ECPG_ARRAY_NONE, ECPG_COMPAT_INFORMIX, false); + + size += sizeof(decimal); + break; + } + case SQLINT8: + case SQLSERIAL8: + size = ecpg_sqlda_size_align(size, sizeof(int64_t)); + sscanf(PQgetvalue(res, 0, i), "%" PRId64, (int64_t *)((char *)sqlda + size)); + sqlda->sqlvar[i].sqldata = (char *)sqlda + size; + size += sizeof(int64_t); + break; + + /* + * These types will be passed as character strings until + * it's known what to do with them. We use sqlvar->sqldata + * in all cases regardless of length, don't care about + * sqlvar->sqlilongdata. + */ + case SQLCHAR: + case SQLTEXT: + case SQLVCHAR: + case SQLNCHAR: + case SQLNVCHAR: + case SQLMONEY: + case SQLDATE: + case SQLDTIME: + case SQLINTERVAL: + default: + sqlda->sqlvar[i].sqldata = PQgetvalue(res, 0, i); + break; + } + + sqlda->sqlvar[i].sqlind = PQgetisnull(res, 0, i) ? &value_is_null : &value_is_not_null; + } + } diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/typename.c postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/typename.c *** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/typename.c 2007-11-15 22:14:45.000000000 +0100 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/typename.c 2009-06-24 10:33:27.000000000 +0200 *************** *** 7,12 **** --- 7,13 ---- #include "ecpgtype.h" #include "ecpglib.h" #include "extern.h" + #include "sqltypes.h" #include "sql3types.h" #include "pg_type.h" *************** ecpg_dynamic_type(Oid type) *** 99,101 **** --- 100,189 ---- return -(int) type; } } + + int + ecpg_sqlda_type(int type) + { + switch (type) + { + case SQLCHAR: + case SQLNCHAR: + return ECPGt_char; + case SQLSMINT: + return ECPGt_short; + case SQLINT: + return ECPGt_int; + case SQLFLOAT: + return ECPGt_double; + case SQLSMFLOAT: + return ECPGt_float; + case SQLDECIMAL: + return ECPGt_decimal; + case SQLSERIAL: + return ECPGt_int; + case SQLDATE: + return ECPGt_date; + #if 0 + case SQLMONEY: + return ???; + case SQLNULL: + return ???; + #endif + case SQLDTIME: + return ECPGt_timestamp; + #if 0 + case SQLBYTES: + return ???; + #endif + case SQLTEXT: + return ECPGt_char; + case SQLVCHAR: + case SQLNVCHAR: + return ECPGt_varchar; + case SQLINTERVAL: + return ECPGt_interval; + case SQLINT8: + case SQLSERIAL8: + return ECPGt_long_long; + default: + return (-type); + } + } + + int + ecpg_to_sqlda_type(Oid type) + { + switch (type) + { + case CHAROID: + case BPCHAROID: + return SQLCHAR; + case INT2OID: + return SQLSMINT; + case INT4OID: + return SQLINT; + case FLOAT8OID: + return SQLFLOAT; + case FLOAT4OID: + return SQLSMFLOAT; + case NUMERICOID: + return SQLDECIMAL; + case DATEOID: + return SQLDATE; + case CASHOID: + return SQLMONEY; + case TIMESTAMPOID: + case TIMESTAMPTZOID: + return SQLDTIME; + case TEXTOID: + return SQLTEXT; + case VARCHAROID: + return SQLVCHAR; + case INTERVALOID: + return SQLINTERVAL; + case INT8OID: + return SQLINT8; + default: + return (-type); + } + } diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/include/ecpglib.h postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/ecpglib.h *** postgresql-8.4rc2/src/interfaces/ecpg/include/ecpglib.h 2009-06-11 16:49:13.000000000 +0200 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/ecpglib.h 2009-06-24 10:33:27.000000000 +0200 *************** bool ECPGset_desc(int, const char *, in *** 84,89 **** --- 84,90 ---- void ECPGset_noind_null(enum ECPGttype, void *); bool ECPGis_noind_null(enum ECPGttype, void *); bool ECPGdescribe(int, bool, const char *,...); + bool ECPGdescribe2(int, bool, const char *, const char *, ...); /* dynamic result allocation */ void ECPGfree_auto_mem(void); diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/include/ecpgtype.h postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/ecpgtype.h *** postgresql-8.4rc2/src/interfaces/ecpg/include/ecpgtype.h 2007-08-14 12:01:52.000000000 +0200 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/ecpgtype.h 2009-06-24 10:33:27.000000000 +0200 *************** enum ECPGttype *** 61,67 **** ECPGt_const, /* a constant is needed sometimes */ ECPGt_EOIT, /* End of insert types. */ ECPGt_EORT, /* End of result types. */ ! ECPGt_NO_INDICATOR /* no indicator */ }; /* descriptor items */ --- 61,68 ---- ECPGt_const, /* a constant is needed sometimes */ ECPGt_EOIT, /* End of insert types. */ ECPGt_EORT, /* End of result types. */ ! ECPGt_NO_INDICATOR, /* no indicator */ ! ECPGt_sqlda /* INFORMIX-compatible sqlda_t descriptor */ }; /* descriptor items */ diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/include/sqlda.h postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/sqlda.h *** postgresql-8.4rc2/src/interfaces/ecpg/include/sqlda.h 2009-06-11 16:49:13.000000000 +0200 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/sqlda.h 2009-06-24 10:33:27.000000000 +0200 *************** *** 1,3 **** --- 1,74 ---- /* * $PostgreSQL: pgsql/src/interfaces/ecpg/include/sqlda.h,v 1.4 2009/06/11 14:49:13 momjian Exp $ */ + + #ifndef POSTGRES_SQLDA_H + #define POSTGRES_SQLDA_H + + /* Define Informix "standard" types */ + #ifndef C_H + typedef int int4; + typedef short int2; + #endif + typedef char int1; + + typedef int mint; + typedef long mlong; + + typedef short MSHORT; + typedef char MCHAR; + + typedef unsigned int uint4; + typedef unsigned short uint2; + typedef unsigned char uint1; + + typedef unsigned int muint; + typedef unsigned long mulong; + + typedef unsigned short MUSHORT; + typedef unsigned char MUCHAR; + + #define MI_INT_SIZE (sizeof(int) * 8) + #define MI_LONG_SIZE (sizeof(long) * 8) + #define MI_PTR_SIZE (sizeof(char *) * 8) + + typedef struct sqlvar_struct + { + int2 sqltype; /* variable type */ + int4 sqllen; /* length in bytes */ + char *sqldata; /* pointer to data */ + int2 *sqlind; /* pointer to indicator */ + char *sqlname; /* variable name */ + char *sqlformat; /* reserved for future use */ + int2 sqlitype; /* ind variable type */ + int2 sqlilen; /* ind length in bytes */ + char *sqlidata; /* ind data pointer */ + int4 sqlxid; /* extended id type */ + char *sqltypename; /* extended type name */ + int2 sqltypelen; /* length of extended type name */ + int2 sqlownerlen; /* length of owner name */ + int2 sqlsourcetype; /* source type for distinct of built-ins */ + char *sqlownername; /* owner name */ + int4 sqlsourceid; /* extended id of source type */ + + /* + * sqlilongdata is new. It supports data that exceeds the 32k + * limit. sqlilen and sqlidata are for backward compatibility + * and they have maximum value of <32K. + */ + char *sqlilongdata; /* for data field beyond 32K */ + int4 sqlflags; /* for internal use only */ + void *sqlreserved; /* reserved for future use */ + } pg_sqlvar_t; + + typedef struct sqlda + { + int2 sqld; + pg_sqlvar_t *sqlvar; + char desc_name[19]; /* descriptor name */ + int2 desc_occ; /* size of sqlda structure */ + struct sqlda *desc_next; /* pointer to next sqlda struct */ + void *reserved; /* reserved for future use */ + } pg_sqlda_t; + + #endif /* POSTGRES_SQLDA_H */ diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/include/sqltypes.h postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/sqltypes.h *** postgresql-8.4rc2/src/interfaces/ecpg/include/sqltypes.h 2009-06-11 16:49:13.000000000 +0200 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/sqltypes.h 2009-06-24 10:33:27.000000000 +0200 *************** *** 30,33 **** --- 30,63 ---- #define CLVCHARPTRTYPE 124 #define CTYPEMAX 25 + #define SQLCHAR 0 + #define SQLSMINT 1 + #define SQLINT 2 + #define SQLFLOAT 3 + #define SQLSMFLOAT 4 + #define SQLDECIMAL 5 + #define SQLSERIAL 6 + #define SQLDATE 7 + #define SQLMONEY 8 + #if 0 + #define SQLNULL 9 + #endif + #define SQLDTIME 10 + #define SQLBYTES 11 + #define SQLTEXT 12 + #define SQLVCHAR 13 + #define SQLINTERVAL 14 + #define SQLNCHAR 15 + #define SQLNVCHAR 16 + #define SQLINT8 17 + #define SQLSERIAL8 18 + #if 0 + #define SQLSET 19 + #define SQLMULTISET 20 + #define SQLLIST 21 + #define SQLROW 22 + #define SQLCOLLECTION 23 + #define SQLROWREF 24 + #endif + #endif /* ndef ECPG_SQLTYPES_H */ diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/descriptor.c postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/descriptor.c *** postgresql-8.4rc2/src/interfaces/ecpg/preproc/descriptor.c 2009-01-23 13:43:32.000000000 +0100 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/descriptor.c 2009-06-24 10:33:27.000000000 +0200 *************** descriptor_variable(const char *name, in *** 326,328 **** --- 326,347 ---- strlcpy(descriptor_names[input], name, sizeof(descriptor_names[input])); return (struct variable *) & varspace[input]; } + + struct variable * + sqlda_variable(const char *name) + { + struct variable *p = (struct variable *) mm_alloc(sizeof(struct variable)); + + p->name = mm_strdup(name); + p->type = (struct ECPGtype *) mm_alloc(sizeof(struct ECPGtype)); + p->type->type = ECPGt_sqlda; + p->type->size = NULL; + p->type->struct_sizeof = NULL; + p->type->u.element = NULL; + p->type->lineno = 0; + p->brace_level = 0; + p->next = NULL; + + return p; + } + diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/ecpg.addons postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/ecpg.addons *** postgresql-8.4rc2/src/interfaces/ecpg/preproc/ecpg.addons 2009-01-30 13:53:43.000000000 +0100 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/ecpg.addons 2009-06-24 10:33:27.000000000 +0200 *************** ECPG: stmtViewStmt rule *** 84,90 **** } | ECPGDescribe { ! fprintf(yyout, "{ ECPGdescribe(__LINE__, %s,", $1); dump_variables(argsresult, 1); fputs("ECPGt_EORT);", yyout); fprintf(yyout, "}"); --- 84,90 ---- } | ECPGDescribe { ! fprintf(yyout, "{ ECPGdescribe2(__LINE__, %s,", $1); dump_variables(argsresult, 1); fputs("ECPGt_EORT);", yyout); fprintf(yyout, "}"); *************** ECPG: fetch_directionBACKWARDSignedIcons *** 221,226 **** --- 221,235 ---- free($2); $2 = make_str("$0"); } + ECPG: cursor_namename rule + | char_civar + { + char *curname = mm_alloc(strlen($1) + 2); + sprintf(curname, ":%s", $1); + free($1); + $1 = curname; + $$ = $1; + } ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block { $$.name = $2; *************** ECPG: PrepareStmtPREPAREprepared_namepre *** 235,243 **** } ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block { $$ = $2; } ! ECPG: DeclareCursorStmtDECLAREnamecursor_optionsCURSORopt_holdFORSelectStmt block { struct cursor *ptr, *this; for (ptr = cur; ptr != NULL; ptr = ptr->next) { --- 244,253 ---- } ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block { $$ = $2; } ! ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block { struct cursor *ptr, *this; + char *cursor_marker = $2[0] == ':' ? make_str("$0") : mm_strdup($2); for (ptr = cur; ptr != NULL; ptr = ptr->next) { *************** ECPG: DeclareCursorStmtDECLAREnamecursor *** 251,257 **** this->name = $2; this->connection = connection; this->opened = false; ! this->command = cat_str(7, make_str("declare"), mm_strdup($2), $3, make_str("cursor"), $5, make_str("for"), $7); this->argsinsert = argsinsert; this->argsresult = argsresult; argsinsert = argsresult = NULL; --- 261,267 ---- this->name = $2; this->connection = connection; this->opened = false; ! this->command = cat_str(7, make_str("declare"), cursor_marker, $3, make_str("cursor"), $5, make_str("for"), $7); this->argsinsert = argsinsert; this->argsresult = argsresult; argsinsert = argsresult = NULL; *************** ECPG: DeclareCursorStmtDECLAREnamecursor *** 262,267 **** --- 272,282 ---- else $$ = cat_str(3, make_str("/*"), mm_strdup(this->command), make_str("*/")); } + ECPG: ClosePortalStmtCLOSEcursor_name block + { + char *cursor_marker = $2[0] == ':' ? make_str("$0") : $2; + $$ = cat2_str(make_str("close"), cursor_marker); + } ECPG: opt_hold block { if (compat == ECPG_COMPAT_INFORMIX_SE && autocommit == true) *************** ECPG: VariableShowStmtSHOWALL block *** 326,371 **** mmerror(PARSE_ERROR, ET_ERROR, "SHOW ALL is not implemented"); $$ = EMPTY; } ! ECPG: FetchStmtFETCHfetch_directionfrom_inname block { add_additional_variables($4, false); ! $$ = cat_str(4, make_str("fetch"), $2, $3, $4); } ! ECPG: FetchStmtFETCHname block { add_additional_variables($2, false); ! $$ = cat_str(2, make_str("fetch"), $2); } ! ECPG: FetchStmtMOVEname rule ! | FETCH fetch_direction from_in name ecpg_into { add_additional_variables($4, false); ! $$ = cat_str(4, make_str("fetch"), $2, $3, $4); } ! | FETCH fetch_direction name ecpg_into { add_additional_variables($3, false); ! $$ = cat_str(4, make_str("fetch"), $2, make_str("from"), $3); } ! | FETCH from_in name ecpg_into { add_additional_variables($3, false); ! $$ = cat_str(3, make_str("fetch"), $2, $3); } ! | FETCH name ecpg_into { add_additional_variables($2, false); ! $$ = cat2_str(make_str("fetch"), $2); } ! | FETCH fetch_direction name { add_additional_variables($3, false); ! $$ = cat_str(4, make_str("fetch"), $2, make_str("from"), $3); } ! | FETCH from_in name { add_additional_variables($3, false); ! $$ = cat_str(3, make_str("fetch"), $2, $3); } ECPG: SpecialRuleRelationOLD addon if (!QueryIsRule) --- 341,442 ---- mmerror(PARSE_ERROR, ET_ERROR, "SHOW ALL is not implemented"); $$ = EMPTY; } ! ECPG: FetchStmtFETCHBACKWARDfrom_incursor_name block { + char *cursor_marker = $4[0] == ':' ? make_str("$0") : $4; add_additional_variables($4, false); ! $$ = cat_str(3, make_str("fetch backward"), $3, cursor_marker); } ! ECPG: FetchStmtFETCHFORWARDfrom_incursor_name block { + char *cursor_marker = $4[0] == ':' ? make_str("$0") : $4; + add_additional_variables($4, false); + $$ = cat_str(3, make_str("fetch forward"), $3, cursor_marker); + } + ECPG: FetchStmtFETCHfetch_directionfrom_incursor_name block + { + char *cursor_marker = $4[0] == ':' ? make_str("$0") : $4; + add_additional_variables($4, false); + $$ = cat_str(4, make_str("fetch"), $2, $3, cursor_marker); + } + ECPG: FetchStmtFETCHcursor_name block + { + char *cursor_marker = $2[0] == ':' ? make_str("$0") : $2; add_additional_variables($2, false); ! $$ = cat_str(2, make_str("fetch"), cursor_marker); } ! ECPG: FetchStmtMOVEcursor_name rule ! | FETCH BACKWARD from_in cursor_name ecpg_fetch_into { + char *cursor_marker = $4[0] == ':' ? make_str("$0") : $4; add_additional_variables($4, false); ! $$ = cat_str(3, make_str("fetch backward"), $3, cursor_marker); } ! | FETCH FORWARD from_in cursor_name ecpg_fetch_into { + char *cursor_marker = $4[0] == ':' ? make_str("$0") : $4; + add_additional_variables($4, false); + $$ = cat_str(3, make_str("fetch forward"), $3, cursor_marker); + } + | FETCH fetch_direction from_in cursor_name ecpg_fetch_into + { + char *cursor_marker = $4[0] == ':' ? make_str("$0") : $4; + add_additional_variables($4, false); + $$ = cat_str(4, make_str("fetch"), $2, $3, cursor_marker); + } + | FETCH BACKWARD cursor_name ecpg_fetch_into + { + char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3; add_additional_variables($3, false); ! $$ = cat2_str(make_str("fetch backward from"), cursor_marker); } ! | FETCH FORWARD cursor_name ecpg_fetch_into ! { ! char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3; ! add_additional_variables($3, false); ! $$ = cat2_str(make_str("fetch forward from"), cursor_marker); ! } ! | FETCH fetch_direction cursor_name ecpg_fetch_into { + char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3; add_additional_variables($3, false); ! $$ = cat_str(4, make_str("fetch"), $2, make_str("from"), cursor_marker); } ! | FETCH from_in cursor_name ecpg_fetch_into { + char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3; + add_additional_variables($3, false); + $$ = cat_str(3, make_str("fetch"), $2, cursor_marker); + } + | FETCH cursor_name ecpg_fetch_into + { + char *cursor_marker = $2[0] == ':' ? make_str("$0") : $2; add_additional_variables($2, false); ! $$ = cat2_str(make_str("fetch"), cursor_marker); } ! | FETCH BACKWARD cursor_name { + char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3; add_additional_variables($3, false); ! $$ = cat2_str(make_str("fetch backward from"), cursor_marker); } ! | FETCH FORWARD cursor_name { + char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3; add_additional_variables($3, false); ! $$ = cat2_str(make_str("fetch forward from"), cursor_marker); ! } ! | FETCH fetch_direction cursor_name ! { ! char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3; ! add_additional_variables($3, false); ! $$ = cat_str(4, make_str("fetch"), $2, make_str("from"), cursor_marker); ! } ! | FETCH from_in cursor_name ! { ! char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3; ! add_additional_variables($3, false); ! $$ = cat_str(3, make_str("fetch"), $2, cursor_marker); } ECPG: SpecialRuleRelationOLD addon if (!QueryIsRule) diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/ecpg.trailer postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/ecpg.trailer *** postgresql-8.4rc2/src/interfaces/ecpg/preproc/ecpg.trailer 2009-06-11 01:11:52.000000000 +0200 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/ecpg.trailer 2009-06-24 10:33:27.000000000 +0200 *************** prepared_name: name { *** 284,292 **** * Declare a prepared cursor. The syntax is different from the standard * declare statement, so we create a new rule. */ ! ECPGCursorStmt: DECLARE name cursor_options CURSOR opt_hold FOR prepared_name { struct cursor *ptr, *this; struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable)); const char *con = connection ? connection : "NULL"; --- 284,293 ---- * Declare a prepared cursor. The syntax is different from the standard * declare statement, so we create a new rule. */ ! ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_name { struct cursor *ptr, *this; + char *cursor_marker = $2[0] == ':' ? make_str("$0") : mm_strdup($2); struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable)); const char *con = connection ? connection : "NULL"; *************** ECPGCursorStmt: DECLARE name cursor_opt *** 303,309 **** this->next = cur; this->name = $2; this->connection = connection; ! this->command = cat_str(6, make_str("declare"), mm_strdup($2), $3, make_str("cursor"), $5, make_str("for $1")); this->argsresult = NULL; thisquery->type = &ecpg_query; --- 304,310 ---- this->next = cur; this->name = $2; this->connection = connection; ! this->command = cat_str(6, make_str("declare"), cursor_marker, $3, make_str("cursor"), $5, make_str("for $1")); this->argsresult = NULL; thisquery->type = &ecpg_query; *************** ECPGCursorStmt: DECLARE name cursor_opt *** 313,320 **** sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $7); this->argsinsert = NULL; add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator); - cur = this; $$ = cat_str(3, make_str("/*"), mm_strdup(this->command), make_str("*/")); --- 314,326 ---- sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $7); this->argsinsert = NULL; + if ($2[0] == ':') + { + struct variable *var = find_variable($2 + 1); + remove_variable_from_list(&argsinsert, var); + add_variable_to_head(&(this->argsinsert), var, &no_indicator); + } add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator); cur = this; $$ = cat_str(3, make_str("/*"), mm_strdup(this->command), make_str("*/")); *************** ECPGFree: SQL_FREE name { $$ = $2; } *** 944,950 **** /* * open is an open cursor, at the moment this has to be removed */ ! ECPGOpen: SQL_OPEN name opt_ecpg_using { $$ = $2; }; opt_ecpg_using: /*EMPTY*/ { $$ = EMPTY; } | ecpg_using { $$ = $1; } --- 950,965 ---- /* * open is an open cursor, at the moment this has to be removed */ ! ECPGOpen: SQL_OPEN cursor_name opt_ecpg_using ! { ! if ($2[0] == ':') ! { ! struct variable *var = find_variable($2 + 1); ! remove_variable_from_list(&argsinsert, var); ! } ! $$ = $2; ! } ! ; opt_ecpg_using: /*EMPTY*/ { $$ = EMPTY; } | ecpg_using { $$ = $1; } *************** ecpg_using: USING using_list { $$ = EMP *** 956,974 **** using_descriptor: USING opt_sql SQL_DESCRIPTOR quoted_ident_stringvar { ! add_variable_to_head(&argsinsert, descriptor_variable($4,0), &no_indicator); $$ = EMPTY; } ; into_descriptor: INTO opt_sql SQL_DESCRIPTOR quoted_ident_stringvar { ! add_variable_to_head(&argsresult, descriptor_variable($4,1), &no_indicator); $$ = EMPTY; } ; ! opt_sql: /*EMPTY*/ | SQL_SQL; using_list: UsingValue | UsingValue ',' using_list; --- 971,1025 ---- using_descriptor: USING opt_sql SQL_DESCRIPTOR quoted_ident_stringvar { ! if (strlen($2) || !(INFORMIX_MODE)) ! add_variable_to_head(&argsinsert, descriptor_variable($4,0), &no_indicator); ! else ! { ! if ($4[0] == '\"') ! { ! char *pos; ! ! $4[0] = ' '; ! for (pos = $4; *pos; pos++) ! if (*pos == '\"') ! *pos = ' '; ! } ! add_variable_to_head(&argsinsert, sqlda_variable($4), &no_indicator); ! } $$ = EMPTY; } ; into_descriptor: INTO opt_sql SQL_DESCRIPTOR quoted_ident_stringvar { ! if (strlen($2) || !(INFORMIX_MODE)) ! add_variable_to_head(&argsresult, descriptor_variable($4,1), &no_indicator); ! else ! { ! if ($4[0] == '\"') ! { ! char *pos; ! ! $4[0] = ' '; ! for (pos = $4; *pos; pos++) ! if (*pos == '\"') ! *pos = ' '; ! } ! add_variable_to_head(&argsresult, sqlda_variable($4), &no_indicator); ! } $$ = EMPTY; } ; ! into_sqlda: INTO name ! { ! add_variable_to_head(&argsresult, sqlda_variable($2), &no_indicator); ! $$ = EMPTY; ! } ! ! opt_sql: /*EMPTY*/ { $$ = EMPTY; } ! | SQL_SQL { $$ = make_str("sql"); } ! ; using_list: UsingValue | UsingValue ',' using_list; *************** ECPGDescribe: SQL_DESCRIBE INPUT_P name *** 1001,1022 **** { const char *con = connection ? connection : "NULL"; mmerror(PARSE_ERROR, ET_WARNING, "using unsupported DESCRIBE statement"); ! $$ = (char *) mm_alloc(sizeof("1, ECPGprepared_statement(, \"\", __LINE__)") + strlen(con) + strlen($3)); ! sprintf($$, "1, ECPGprepared_statement(%s, \"%s\", __LINE__)", con, $3); } | SQL_DESCRIBE opt_output name using_descriptor { const char *con = connection ? connection : "NULL"; ! mmerror(PARSE_ERROR, ET_WARNING, "using unsupported DESCRIBE statement"); ! $$ = (char *) mm_alloc(sizeof("0, ECPGprepared_statement(, \"\", __LINE__)") + strlen(con) + strlen($3)); ! sprintf($$, "0, ECPGprepared_statement(%s, \"%s\", __LINE__)", con, $3); } | SQL_DESCRIBE opt_output name into_descriptor { const char *con = connection ? connection : "NULL"; mmerror(PARSE_ERROR, ET_WARNING, "using unsupported DESCRIBE statement"); ! $$ = (char *) mm_alloc(sizeof("0, ECPGprepared_statement(, \"\", __LINE__)") + strlen(con) + strlen($3)); ! sprintf($$, "0, ECPGprepared_statement(%s, \"%s\", __LINE__)", con, $3); } ; --- 1052,1084 ---- { const char *con = connection ? connection : "NULL"; mmerror(PARSE_ERROR, ET_WARNING, "using unsupported DESCRIBE statement"); ! $$ = (char *) mm_alloc(sizeof("1, , \"\"") + strlen(con) + strlen($3)); ! sprintf($$, "1, %s, \"%s\"", con, $3); } | SQL_DESCRIBE opt_output name using_descriptor { const char *con = connection ? connection : "NULL"; ! $$ = (char *) mm_alloc(sizeof("0, , \"\"") + strlen(con) + strlen($3)); ! sprintf($$, "0, %s, \"%s\"", con, $3); } | SQL_DESCRIBE opt_output name into_descriptor { const char *con = connection ? connection : "NULL"; + $$ = (char *) mm_alloc(sizeof("0, , \"\"") + strlen(con) + strlen($3)); + sprintf($$, "0, %s, \"%s\"", con, $3); + } + | SQL_DESCRIBE INPUT_P name into_sqlda + { + const char *con = connection ? connection : "NULL"; mmerror(PARSE_ERROR, ET_WARNING, "using unsupported DESCRIBE statement"); ! $$ = (char *) mm_alloc(sizeof("1, , \"\"") + strlen(con) + strlen($3)); ! sprintf($$, "1, %s, \"%s\"", con, $3); ! } ! | SQL_DESCRIBE opt_output name into_sqlda ! { ! const char *con = connection ? connection : "NULL"; ! $$ = (char *) mm_alloc(sizeof("0, , \"\"") + strlen(con) + strlen($3)); ! sprintf($$, "0, %s, \"%s\"", con, $3); } ; *************** civarind: cvariable indicator *** 1768,1773 **** --- 1830,1845 ---- } ; + char_civar: char_variable + { + char *qm; /* dummy questionmark, we have to pass the real variable name */ + add_variable_to_head(&argsinsert, find_variable($1), &no_indicator); + qm = create_questionmarks($1, false); + free(qm); + $$ = $1; + } + ; + civar: cvariable { add_variable_to_head(&argsinsert, find_variable($1), &no_indicator); *************** ecpg_into: INTO into_list { $$ = EMPTY; *** 1983,1988 **** --- 2055,2075 ---- | into_descriptor { $$ = $1; } ; + ecpg_fetch_into: ecpg_into { $$ = $1; } + | using_descriptor + { + struct variable *var; + + if (!INFORMIX_MODE) + mmerror(PARSE_ERROR, ET_ERROR, "Not in Informix compatibility mode"); + + var = argsinsert->variable; + remove_variable_from_list(&argsinsert, var); + add_variable_to_head(&argsresult, var, &no_indicator); + $$ = $1; + } + ; + %% void base_yyerror(const char *error) diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/ecpg.type postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/ecpg.type *** postgresql-8.4rc2/src/interfaces/ecpg/preproc/ecpg.type 2008-11-14 11:03:33.000000000 +0100 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/ecpg.type 2009-06-24 10:33:27.000000000 +0200 *************** *** 43,48 **** --- 43,49 ---- %type <str> c_term %type <str> c_thing %type <str> char_variable + %type <str> char_civar %type <str> civar %type <str> civarind %type <str> ColLabel *************** *** 60,65 **** --- 61,67 ---- %type <str> ecpg_ident %type <str> ecpg_interval %type <str> ecpg_into + %type <str> ecpg_fetch_into %type <str> ecpg_param %type <str> ecpg_sconst %type <str> ecpg_using *************** *** 70,75 **** --- 72,78 ---- %type <str> execute_rest %type <str> indicator %type <str> into_descriptor + %type <str> into_sqlda %type <str> Iresult %type <str> on_off %type <str> opt_bit_field *************** *** 84,89 **** --- 87,93 ---- %type <str> opt_reference %type <str> opt_scale %type <str> opt_server + %type <str> opt_sql %type <str> opt_user %type <str> opt_opt_value %type <str> ora_user diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/extern.h postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/extern.h *** postgresql-8.4rc2/src/interfaces/ecpg/preproc/extern.h 2009-06-11 16:49:13.000000000 +0200 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/extern.h 2009-06-24 10:33:27.000000000 +0200 *************** extern void add_descriptor(char *, char *** 89,96 **** --- 89,98 ---- extern void drop_descriptor(char *, char *); extern struct descriptor *lookup_descriptor(char *, char *); extern struct variable *descriptor_variable(const char *name, int input); + extern struct variable *sqlda_variable(const char *name); extern void add_variable_to_head(struct arguments **, struct variable *, struct variable *); extern void add_variable_to_tail(struct arguments **, struct variable *, struct variable *); + extern void remove_variable_from_list(struct arguments ** list, struct variable * var); extern void dump_variables(struct arguments *, int); extern struct typedefs *get_typedef(char *); extern void adjust_array(enum ECPGttype, char **, char **, char *, char *, int, bool); diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/type.c postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/type.c *** postgresql-8.4rc2/src/interfaces/ecpg/preproc/type.c 2009-06-11 16:49:13.000000000 +0200 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/type.c 2009-06-24 10:33:27.000000000 +0200 *************** ECPGdump_a_simple(FILE *o, const char *n *** 325,330 **** --- 325,332 ---- else if (type == ECPGt_descriptor) /* remember that name here already contains quotes (if needed) */ fprintf(o, "\n\tECPGt_descriptor, %s, 0L, 0L, 0L, ", name); + else if (type == ECPGt_sqlda) + fprintf(o, "\n\tECPGt_sqlda, &%s, 0L, 0L, 0L, ", name); else { char *variable = (char *) mm_alloc(strlen(name) + ((prefix == NULL) ? 0 : strlen(prefix)) + 4); diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/variable.c postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/variable.c *** postgresql-8.4rc2/src/interfaces/ecpg/preproc/variable.c 2009-06-11 16:49:13.000000000 +0200 --- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/variable.c 2009-06-24 10:33:27.000000000 +0200 *************** add_variable_to_tail(struct arguments ** *** 401,406 **** --- 401,430 ---- *list = new; } + void + remove_variable_from_list(struct arguments ** list, struct variable * var) + { + struct arguments *p, *prev = NULL; + bool found = false; + + for (p = *list; p; p = p->next) + { + if (p->variable == var) + { + found = true; + break; + } + prev = p; + } + if (found) + { + if (prev) + prev->next = p->next; + else + *list = p->next; + } + } + /* 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.
pgsql-hackers by date: