diff options
author | Michael Meskes <meskes@postgresql.org> | 2007-08-14 10:01:54 +0000 |
---|---|---|
committer | Michael Meskes <meskes@postgresql.org> | 2007-08-14 10:01:54 +0000 |
commit | 635a0b9a8640bb7f2944a3f77ddc370f8dd7b010 (patch) | |
tree | d54146b2416fecd2a544f3bf786108079b879cfc /src/interfaces/ecpg/ecpglib | |
parent | b83bd31bd953b6daa22bcbdaee5ade2a27ec7324 (diff) | |
download | postgresql-635a0b9a8640bb7f2944a3f77ddc370f8dd7b010.tar.gz postgresql-635a0b9a8640bb7f2944a3f77ddc370f8dd7b010.zip |
- Finished major rewrite to use new protocol version
- Really prepare statements
- Added more regression tests
- Added auto-prepare mode
- Use '$n' for positional variables, '?' is still possible via ecpg option
- Cleaned up the sources a little bit
Diffstat (limited to 'src/interfaces/ecpg/ecpglib')
-rw-r--r-- | src/interfaces/ecpg/ecpglib/Makefile | 6 | ||||
-rw-r--r-- | src/interfaces/ecpg/ecpglib/connect.c | 16 | ||||
-rw-r--r-- | src/interfaces/ecpg/ecpglib/descriptor.c | 32 | ||||
-rw-r--r-- | src/interfaces/ecpg/ecpglib/error.c | 56 | ||||
-rw-r--r-- | src/interfaces/ecpg/ecpglib/execute.c | 595 | ||||
-rw-r--r-- | src/interfaces/ecpg/ecpglib/extern.h | 12 | ||||
-rw-r--r-- | src/interfaces/ecpg/ecpglib/misc.c | 12 | ||||
-rw-r--r-- | src/interfaces/ecpg/ecpglib/prepare.c | 367 |
8 files changed, 748 insertions, 348 deletions
diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile index a64f8fa2699..f60207e23c0 100644 --- a/src/interfaces/ecpg/ecpglib/Makefile +++ b/src/interfaces/ecpg/ecpglib/Makefile @@ -4,7 +4,7 @@ # # Copyright (c) 1994, Regents of the University of California # -# $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/Makefile,v 1.46 2007/01/20 17:16:17 petere Exp $ +# $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/Makefile,v 1.47 2007/08/14 10:01:52 meskes Exp $ # #------------------------------------------------------------------------- @@ -13,8 +13,8 @@ top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global NAME= ecpg -SO_MAJOR_VERSION= 5 -SO_MINOR_VERSION= 3 +SO_MAJOR_VERSION= 6 +SO_MINOR_VERSION= 0 DLTYPE= library override CPPFLAGS := -DFRONTEND \ diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c index 650c4e503c0..421155bb923 100644 --- a/src/interfaces/ecpg/ecpglib/connect.c +++ b/src/interfaces/ecpg/ecpglib/connect.c @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/connect.c,v 1.41 2007/03/29 12:02:24 meskes Exp $ */ +/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/connect.c,v 1.42 2007/08/14 10:01:52 meskes Exp $ */ #define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" @@ -185,11 +185,9 @@ ECPGsetcommit(int lineno, const char *mode, const char *connection_name) { if (con->committed) { - if ((results = PQexec(con->connection, "begin transaction")) == NULL) - { - ECPGraise(lineno, ECPG_TRANS, ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL); + results = PQexec(con->connection, "begin transaction"); + if (!ECPGcheck_PQresult(results, lineno, con->connection, ECPG_COMPAT_PGSQL)) return false; - } PQclear(results); con->committed = false; } @@ -199,11 +197,9 @@ ECPGsetcommit(int lineno, const char *mode, const char *connection_name) { if (!con->committed) { - if ((results = PQexec(con->connection, "commit")) == NULL) - { - ECPGraise(lineno, ECPG_TRANS, ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL); + results = PQexec(con->connection, "commit"); + if (!ECPGcheck_PQresult(results, lineno, con->connection, ECPG_COMPAT_PGSQL)) return false; - } PQclear(results); con->committed = true; } @@ -249,7 +245,7 @@ ECPGnoticeReceiver(void *arg, const PGresult *result) if (strncmp(sqlstate, "00", 2) == 0) return; - ECPGlog("%s", message); + ECPGlog("ECPGnoticeReceiver %s\n", message); /* map to SQLCODE for backward compatibility */ if (strcmp(sqlstate, ECPG_SQLSTATE_INVALID_CURSOR_NAME) == 0) diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c index 0a4ecf86fc7..f70aaa42f88 100644 --- a/src/interfaces/ecpg/ecpglib/descriptor.c +++ b/src/interfaces/ecpg/ecpglib/descriptor.c @@ -1,6 +1,6 @@ /* dynamic SQL support routines * - * $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.22 2007/06/11 11:52:08 meskes Exp $ + * $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.23 2007/08/14 10:01:52 meskes Exp $ */ #define POSTGRES_ECPG_INTERNAL @@ -495,6 +495,8 @@ ECPGset_desc(int lineno, const char *desc_name, int index,...) if (!desc_item) return false; desc_item->num = index; + if (desc->count < index) + desc->count = index; desc_item->next = desc->items; desc->items = desc_item; } @@ -508,7 +510,6 @@ ECPGset_desc(int lineno, const char *desc_name, int index,...) { enum ECPGdtype itemtype; const char *tobeinserted = NULL; - bool malloced; itemtype = va_arg(args, enum ECPGdtype); @@ -542,11 +543,12 @@ ECPGset_desc(int lineno, const char *desc_name, int index,...) { case ECPGd_data: { - if (!ECPGstore_input(lineno, true, var, &tobeinserted, &malloced, false)) + if (!ECPGstore_input(lineno, true, var, &tobeinserted, false)) { ECPGfree(var); return false; } + ECPGfree(desc_item->data); /* free() takes care of a potential NULL value */ desc_item->data = (char *) tobeinserted; tobeinserted = NULL; @@ -583,13 +585,7 @@ ECPGset_desc(int lineno, const char *desc_name, int index,...) return false; } } - - /* - * if (itemtype == ECPGd_data) { free(desc_item->data); - * desc_item->data = NULL; } - */ - } - while (true); + } while (true); ECPGfree(var); return true; @@ -598,18 +594,18 @@ ECPGset_desc(int lineno, const char *desc_name, int index,...) bool ECPGdeallocate_desc(int line, const char *name) { - struct descriptor *i; + struct descriptor *desc; struct descriptor **lastptr = &all_descriptors; struct sqlca_t *sqlca = ECPGget_sqlca(); ECPGinit_sqlca(sqlca); - for (i = all_descriptors; i; lastptr = &i->next, i = i->next) + for (desc = all_descriptors; desc; lastptr = &desc->next, desc = desc->next) { - if (!strcmp(name, i->name)) + if (!strcmp(name, desc->name)) { struct descriptor_item *desc_item; - for (desc_item = i->items; desc_item;) + for (desc_item = desc->items; desc_item;) { struct descriptor_item *di; @@ -619,10 +615,10 @@ ECPGdeallocate_desc(int line, const char *name) ECPGfree(di); } - *lastptr = i->next; - ECPGfree(i->name); - PQclear(i->result); - ECPGfree(i); + *lastptr = desc->next; + ECPGfree(desc->name); + PQclear(desc->result); + ECPGfree(desc); return true; } } diff --git a/src/interfaces/ecpg/ecpglib/error.c b/src/interfaces/ecpg/ecpglib/error.c index 12d948b3d17..ef15cccb94a 100644 --- a/src/interfaces/ecpg/ecpglib/error.c +++ b/src/interfaces/ecpg/ecpglib/error.c @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/error.c,v 1.16 2007/05/31 15:13:05 petere Exp $ */ +/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/error.c,v 1.17 2007/08/14 10:01:52 meskes Exp $ */ #define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" @@ -9,7 +9,6 @@ #include "extern.h" #include "sqlca.h" - void ECPGraise(int line, int code, const char *sqlstate, const char *str) { @@ -196,6 +195,59 @@ ECPGraise_backend(int line, PGresult *result, PGconn *conn, int compat) ECPGfree_auto_mem(); } +/* filter out all error codes */ +bool +ECPGcheck_PQresult(PGresult *results, int lineno, PGconn *connection, enum COMPAT_MODE compat) +{ + if (results == NULL) + { + ECPGlog("ECPGcheck_PQresult line %d: error: %s", lineno, PQerrorMessage(connection)); + ECPGraise_backend(lineno, NULL, connection, compat); + return (false); + } + + switch (PQresultStatus(results)) + { + + case PGRES_TUPLES_OK: + return (true); + break; + case PGRES_EMPTY_QUERY: + /* do nothing */ + ECPGraise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL); + PQclear(results); + return (false); + break; + case PGRES_COMMAND_OK: + return (true); + break; + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + case PGRES_BAD_RESPONSE: + ECPGlog("ECPGcheck_PQresult line %d: Error: %s", lineno, PQresultErrorMessage(results)); + ECPGraise_backend(lineno, results, connection, compat); + PQclear(results); + return (false); + break; + case PGRES_COPY_OUT: + return(true); + break; + case PGRES_COPY_IN: + ECPGlog("ECPGcheck_PQresult line %d: Got PGRES_COPY_IN ... tossing.\n", lineno); + PQendcopy(connection); + PQclear(results); + return(false); + break; + default: + ECPGlog("ECPGcheck_PQresult line %d: Got something else, postgres error.\n", + lineno); + ECPGraise_backend(lineno, results, connection, compat); + PQclear(results); + return(false); + break; + } +} + /* print out an error message */ void sqlprint(void) diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c index 5c3ecf6461f..4a9f82cc1ac 100644 --- a/src/interfaces/ecpg/ecpglib/execute.c +++ b/src/interfaces/ecpg/ecpglib/execute.c @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.67 2007/06/11 11:52:08 meskes Exp $ */ +/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.68 2007/08/14 10:01:52 meskes Exp $ */ /* * The aim is to get a simpler inteface to the database routines. @@ -56,7 +56,6 @@ quote_postgres(char *arg, bool quote, int lineno) res = (char *) ECPGalloc(buffer_len + 3, lineno); if (!res) return (res); - escaped_len = PQescapeString(res+1, arg, buffer_len); if (length == escaped_len) { @@ -105,24 +104,43 @@ free_statement(struct statement * stmt) free_variable(stmt->inlist); free_variable(stmt->outlist); ECPGfree(stmt->command); + ECPGfree(stmt->name); ECPGfree(stmt); } -static char * -next_insert(char *text) +static int +next_insert(char *text, int pos, bool questionmarks) { - char *ptr = text; bool string = false; + int p = pos; - for (; *ptr != '\0' && (*ptr != '?' || string); ptr++) + for (; text[p] != '\0'; p++) { - if (*ptr == '\\') /* escape character */ - ptr++; - else if (*ptr == '\'') + if (text[p] == '\\') /* escape character */ + p++; + else if (text[p] == '\'') string = string ? false : true; + else if (!string) + { + if (text[p] == '$' && isdigit(text[p+1])) + { + /* this can be either a dollar quote or a variable */ + int i; + + for (i = p + 1; isdigit(text[i]); i++); + if (!isalpha(text[i]) && isascii(text[i]) && text[i] != '_') + /* not dollar delimeted quote */ + return p; + } + else if (questionmarks && text[p] == '?') + { + /* also allow old style placeholders */ + return p; + } + } } - return (*ptr == '\0') ? NULL : ptr; + return -1; } static bool @@ -257,7 +275,9 @@ ECPGis_type_an_array(int type, const struct statement * stmt, const struct varia sprintf(array_query, "select typlen from pg_type where oid=%d and typelem<>0", type); query = PQexec(stmt->connection->connection, array_query); ECPGfree(array_query); - if (PQresultStatus(query) == PGRES_TUPLES_OK) + if (!ECPGcheck_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat)) + return (ECPG_ARRAY_ERROR); + else if (PQresultStatus(query) == PGRES_TUPLES_OK) { if (PQntuples(query) == 0) isarray = ECPG_ARRAY_NONE; @@ -273,8 +293,11 @@ ECPGis_type_an_array(int type, const struct statement * stmt, const struct varia isarray = ECPG_ARRAY_NONE; } } + PQclear(query); } - PQclear(query); + else + return (ECPG_ARRAY_ERROR); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), type, isarray, stmt->lineno); ECPGlog("ECPGis_type_an_array line %d: TYPE database: %d C: %d array: %s\n", stmt->lineno, type, var->type, isarray ? "Yes" : "No"); return isarray; @@ -428,7 +451,7 @@ ECPGstore_result(const PGresult *results, int act_field, bool ECPGstore_input(const int lineno, const bool force_indicator, const struct variable * var, - const char **tobeinserted_p, bool *malloced_p, bool quote) + const char **tobeinserted_p, bool quote) { char *mallocedval = NULL; char *newcopy = NULL; @@ -450,7 +473,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia * contents to arrive in a comma-separated list on insert (I think). */ - *malloced_p = false; *tobeinserted_p = ""; /* check for null value and set input buffer accordingly */ @@ -459,36 +481,36 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia case ECPGt_short: case ECPGt_unsigned_short: if (*(short *) var->ind_value < 0) - *tobeinserted_p = "null"; + *tobeinserted_p = NULL; break; case ECPGt_int: case ECPGt_unsigned_int: if (*(int *) var->ind_value < 0) - *tobeinserted_p = "null"; + *tobeinserted_p = NULL; break; case ECPGt_long: case ECPGt_unsigned_long: if (*(long *) var->ind_value < 0L) - *tobeinserted_p = "null"; + *tobeinserted_p = NULL; break; #ifdef HAVE_LONG_LONG_INT_64 case ECPGt_long_long: case ECPGt_unsigned_long_long: if (*(long long int *) var->ind_value < (long long) 0) - *tobeinserted_p = "null"; + *tobeinserted_p = NULL; break; #endif /* HAVE_LONG_LONG_INT_64 */ case ECPGt_NO_INDICATOR: if (force_indicator == false) { if (ECPGis_noind_null(var->type, var->value)) - *tobeinserted_p = "null"; + *tobeinserted_p = NULL; } break; default: break; } - if (**tobeinserted_p == '\0') + if (*tobeinserted_p != NULL) { int asize = var->arrsize ? var->arrsize : 1; @@ -513,7 +535,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia sprintf(mallocedval, "%hd", *((short *) var->value)); *tobeinserted_p = mallocedval; - *malloced_p = true; break; case ECPGt_int: @@ -522,18 +543,17 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia if (asize > 1) { - strcpy(mallocedval, "array ["); + strcpy(mallocedval, "{"); for (element = 0; element < asize; element++) sprintf(mallocedval + strlen(mallocedval), "%d,", ((int *) var->value)[element]); - strcpy(mallocedval + strlen(mallocedval) - 1, "]"); + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); } else sprintf(mallocedval, "%d", *((int *) var->value)); *tobeinserted_p = mallocedval; - *malloced_p = true; break; case ECPGt_unsigned_short: @@ -553,7 +573,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia sprintf(mallocedval, "%hu", *((unsigned short *) var->value)); *tobeinserted_p = mallocedval; - *malloced_p = true; break; case ECPGt_unsigned_int: @@ -573,7 +592,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia sprintf(mallocedval, "%u", *((unsigned int *) var->value)); *tobeinserted_p = mallocedval; - *malloced_p = true; break; case ECPGt_long: @@ -593,7 +611,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia sprintf(mallocedval, "%ld", *((long *) var->value)); *tobeinserted_p = mallocedval; - *malloced_p = true; break; case ECPGt_unsigned_long: @@ -613,7 +630,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia sprintf(mallocedval, "%lu", *((unsigned long *) var->value)); *tobeinserted_p = mallocedval; - *malloced_p = true; break; #ifdef HAVE_LONG_LONG_INT_64 case ECPGt_long_long: @@ -633,7 +649,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia sprintf(mallocedval, "%lld", *((long long *) var->value)); *tobeinserted_p = mallocedval; - *malloced_p = true; break; case ECPGt_unsigned_long_long: @@ -653,7 +668,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia sprintf(mallocedval, "%llu", *((unsigned long long *) var->value)); *tobeinserted_p = mallocedval; - *malloced_p = true; break; #endif /* HAVE_LONG_LONG_INT_64 */ case ECPGt_float: @@ -673,7 +687,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia sprintf(mallocedval, "%.14g", *((float *) var->value)); *tobeinserted_p = mallocedval; - *malloced_p = true; break; case ECPGt_double: @@ -693,7 +706,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia sprintf(mallocedval, "%.14g", *((double *) var->value)); *tobeinserted_p = mallocedval; - *malloced_p = true; break; case ECPGt_bool: @@ -722,15 +734,14 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia else { if (var->offset == sizeof(char)) - sprintf(mallocedval, "'%c'", (*((char *) var->value)) ? 't' : 'f'); + sprintf(mallocedval, "%c", (*((char *) var->value)) ? 't' : 'f'); else if (var->offset == sizeof(int)) - sprintf(mallocedval, "'%c'", (*((int *) var->value)) ? 't' : 'f'); + sprintf(mallocedval, "%c", (*((int *) var->value)) ? 't' : 'f'); else ECPGraise(lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, "different size"); } *tobeinserted_p = mallocedval; - *malloced_p = true; break; case ECPGt_char: @@ -750,7 +761,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia return false; *tobeinserted_p = mallocedval; - *malloced_p = true; } break; case ECPGt_const: @@ -765,7 +775,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia mallocedval[slen] = '\0'; *tobeinserted_p = mallocedval; - *malloced_p = true; } break; case ECPGt_varchar: @@ -784,7 +793,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia return false; *tobeinserted_p = mallocedval; - *malloced_p = true; } break; @@ -854,7 +862,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia } *tobeinserted_p = mallocedval; - *malloced_p = true; } break; @@ -881,7 +888,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia if (!element) strcpy(mallocedval, "array ["); - strcpy(mallocedval + strlen(mallocedval), "interval "); strncpy(mallocedval + strlen(mallocedval), str, slen + 1); strcpy(mallocedval + strlen(mallocedval), ","); ECPGfree(str); @@ -901,14 +907,12 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia return false; } - strcpy(mallocedval, "interval "); /* also copy trailing '\0' */ strncpy(mallocedval + strlen(mallocedval), str, slen + 1); ECPGfree(str); } *tobeinserted_p = mallocedval; - *malloced_p = true; } break; @@ -935,7 +939,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia if (!element) strcpy(mallocedval, "array ["); - strcpy(mallocedval + strlen(mallocedval), "date "); strncpy(mallocedval + strlen(mallocedval), str, slen + 1); strcpy(mallocedval + strlen(mallocedval), ","); ECPGfree(str); @@ -955,14 +958,12 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia return false; } - strcpy(mallocedval, "date "); /* also copy trailing '\0' */ strncpy(mallocedval + strlen(mallocedval), str, slen + 1); ECPGfree(str); } *tobeinserted_p = mallocedval; - *malloced_p = true; } break; @@ -990,7 +991,6 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia if (!element) strcpy(mallocedval, "array ["); - strcpy(mallocedval + strlen(mallocedval), "timestamp "); strncpy(mallocedval + strlen(mallocedval), str, slen + 1); strcpy(mallocedval + strlen(mallocedval), ","); ECPGfree(str); @@ -1010,14 +1010,12 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia return false; } - strcpy(mallocedval, "timestamp "); /* also copy trailing '\0' */ strncpy(mallocedval + strlen(mallocedval), str, slen + 1); ECPGfree(str); } *tobeinserted_p = mallocedval; - *malloced_p = true; } break; @@ -1034,33 +1032,45 @@ ECPGstore_input(const int lineno, const bool force_indicator, const struct varia return true; } +static void +free_params(const char **paramValues, int nParams, bool print, int lineno) +{ + int n; + + for (n = 0; n < nParams; n++) + { + if (print) + ECPGlog("ECPGexecute line %d: parameter %d = %s\n", lineno, n + 1, paramValues[n] ? paramValues[n] : "null"); + ECPGfree((void *)(paramValues[n])); + } + ECPGfree(paramValues); +} + static bool ECPGexecute(struct statement * stmt) { bool status = false; - char *copiedquery; char *cmdstat; PGresult *results; PGnotify *notify; struct variable *var; int desc_counter = 0; - - copiedquery = ECPGstrdup(stmt->command, stmt->lineno); + const char * *paramValues = NULL; + int nParams = 0; + int position = 0; + struct sqlca_t *sqlca = ECPGget_sqlca(); + bool clear_result = true; /* - * Now, if the type is one of the fill in types then we take the argument - * and enter that in the string at the first %s position. Then if there - * are any more fill in types we fill in at the next and so on. + * If the type is one of the fill in types then we take the argument + * and enter it to our parameter array at the first position. Then if there + * are any more fill in types we add more parameters. */ var = stmt->inlist; - while (var) { - char *newcopy = NULL; const char *tobeinserted; - char *p; - bool malloced = FALSE; - int hostvarl = 0; + int counter = 1; tobeinserted = NULL; @@ -1087,75 +1097,131 @@ ECPGexecute(struct statement * stmt) if (desc == NULL) { ECPGraise(stmt->lineno, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, var->pointer); - ECPGfree(copiedquery); return false; } desc_counter++; - if (desc->count < 0 || desc->count >= desc_counter) + for (desc_item = desc->items; desc_item; desc_item = desc_item->next) { - for (desc_item = desc->items; desc_item; desc_item = desc_item->next) + if (desc_item->num == desc_counter) { - if (desc_item->num == desc_counter) + desc_inlist.type = ECPGt_char; + desc_inlist.value = desc_item->data; + desc_inlist.pointer = &(desc_item->data); + desc_inlist.varcharsize = strlen(desc_item->data); + desc_inlist.arrsize = 1; + desc_inlist.offset = 0; + if (!desc_item->indicator) { - desc_inlist.type = ECPGt_char; - desc_inlist.value = desc_item->data; - desc_inlist.pointer = &(desc_item->data); - desc_inlist.varcharsize = strlen(desc_item->data); - desc_inlist.arrsize = 1; - desc_inlist.offset = 0; - if (!desc_item->indicator) - { - 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; - } - else - { - desc_inlist.ind_type = ECPGt_int; - desc_inlist.ind_value = &(desc_item->indicator); - desc_inlist.ind_pointer = &(desc_inlist.ind_value); - desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = 1; - desc_inlist.ind_offset = 0; - } - if (!ECPGstore_input(stmt->lineno, stmt->force_indicator, &desc_inlist, &tobeinserted, &malloced, true)) - { - ECPGfree(copiedquery); - return false; - } - - break; + 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; } - } + else + { + desc_inlist.ind_type = ECPGt_int; + desc_inlist.ind_value = &(desc_item->indicator); + desc_inlist.ind_pointer = &(desc_inlist.ind_value); + desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = 1; + desc_inlist.ind_offset = 0; + } + if (!ECPGstore_input(stmt->lineno, stmt->force_indicator, &desc_inlist, &tobeinserted, false)) + return false; - if (!desc_item) /* no more entries found in descriptor */ - desc_counter = 0; + break; + } } - else + if (desc->count == desc_counter) desc_counter = 0; } else { - if (!ECPGstore_input(stmt->lineno, stmt->force_indicator, var, &tobeinserted, &malloced, true)) + if (!ECPGstore_input(stmt->lineno, stmt->force_indicator, var, &tobeinserted, false)) return false; } - if (tobeinserted) + /* + * now tobeinserted points to an area that contains the next parameter + * if var->type=ECPGt_char_variable we have a dynamic cursor + * we have to simulate a dynamic cursor because there is no backend functionality for it + */ + if (var->type != ECPGt_char_variable) { - /* - * Now tobeinserted points to an area that is to be inserted at - * the first %s - */ - if (!(newcopy = (char *) ECPGalloc(strlen(copiedquery) + nParams++; + if (!(paramValues = (const char **) ECPGrealloc(paramValues, sizeof(const char *) * nParams, stmt->lineno))) + { + ECPGfree(paramValues); + return false; + } + + paramValues[nParams - 1] = tobeinserted; + + if ((position = next_insert(stmt->command, position, stmt->questionmarks) + 1) == 0) + { + /* + * We have an argument but we dont have the matched up + * placeholder in the string + */ + ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, + ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, + NULL); + free_params(paramValues, nParams, false, stmt->lineno); + return false; + } + + /* let's see if this was an old style placeholder */ + if (stmt->command[position-1] == '?') + { + /* yes, replace with new style */ + int buffersize = sizeof(int) * CHAR_BIT * 10 / 3; /* a rough guess of the size we need */ + char *buffer, *newcopy; + + if (!(buffer = (char *) ECPGalloc(buffersize, stmt->lineno))) + { + free_params(paramValues, nParams, false, stmt->lineno); + return false; + } + + snprintf(buffer, buffersize, "$%d", counter++); + + if (!(newcopy = (char *) ECPGalloc(strlen(stmt->command) + strlen(buffer) + 1, stmt->lineno))) + { + free_params(paramValues, nParams, false, stmt->lineno); + ECPGfree(buffer); + return false; + } + + strcpy(newcopy, stmt->command); + + /* set positional parameter */ + strcpy(newcopy + position - 1, buffer); + + /* + * The strange thing in the second argument is the rest of the + * string from the old string + */ + strcat(newcopy, + stmt->command + + position + 1); + ECPGfree(buffer); + ECPGfree(stmt->command); + stmt->command = newcopy; + } + } + else + { + char *newcopy; + + if (!(newcopy = (char *) ECPGalloc(strlen(stmt->command) + strlen(tobeinserted) + 1, stmt->lineno))) { - ECPGfree(copiedquery); + free_params(paramValues, nParams, false, stmt->lineno); return false; } - strcpy(newcopy, copiedquery); - if ((p = next_insert(newcopy + hostvarl)) == NULL) + strcpy(newcopy, stmt->command); + if ((position = next_insert(stmt->command, position, stmt->questionmarks) + 1) == 0) { /* * We have an argument but we dont have the matched up string @@ -1164,38 +1230,31 @@ ECPGexecute(struct statement * stmt) ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL); - ECPGfree(copiedquery); + free_params(paramValues, nParams, false, stmt->lineno); ECPGfree(newcopy); return false; } else { - strcpy(p, tobeinserted); - hostvarl = strlen(newcopy); + int ph_len = (stmt->command[position] == '?') ? strlen("?") : strlen("$1"); + + strcpy(newcopy + position - 1, tobeinserted); /* * The strange thing in the second argument is the rest of the * string from the old string */ strcat(newcopy, - copiedquery - + (p - newcopy) - + sizeof("?") - 1 /* don't count the '\0' */ ); - } - - /* - * Now everything is safely copied to the newcopy. Lets free the - * oldcopy and let the copiedquery get the var->value from the - * newcopy. - */ - if (malloced) - { - ECPGfree((char *) tobeinserted); - tobeinserted = NULL; + stmt->command + + position + + ph_len - 1); } - ECPGfree(copiedquery); - copiedquery = newcopy; + ECPGfree(stmt->command); + stmt->command = newcopy; + + ECPGfree((char *)tobeinserted); + tobeinserted = NULL; } if (desc_counter == 0) @@ -1203,171 +1262,161 @@ ECPGexecute(struct statement * stmt) } /* Check if there are unmatched things left. */ - if (next_insert(copiedquery) != NULL) + if (next_insert(stmt->command, position, stmt->questionmarks) >= 0) { ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL); - ECPGfree(copiedquery); + free_params(paramValues, nParams, false, stmt->lineno); return false; } - /* Now the request is built. */ + /* The request has been build. */ if (stmt->connection->committed && !stmt->connection->autocommit) { - if ((results = PQexec(stmt->connection->connection, "begin transaction")) == NULL) + results = PQexec(stmt->connection->connection, "begin transaction"); + if (!ECPGcheck_PQresult(results, stmt->lineno, stmt->connection->connection, stmt->compat)) { - ECPGraise(stmt->lineno, ECPG_TRANS, - ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL); - ECPGfree(copiedquery); + free_params(paramValues, nParams, false, stmt->lineno); return false; } PQclear(results); stmt->connection->committed = false; } - ECPGlog("ECPGexecute line %d: QUERY: %s on connection %s\n", stmt->lineno, copiedquery, stmt->connection->name); - results = PQexec(stmt->connection->connection, copiedquery); - ECPGfree(copiedquery); - - if (results == NULL) + ECPGlog("ECPGexecute line %d: QUERY: %s with %d parameter on connection %s \n", stmt->lineno, stmt->command, nParams, stmt->connection->name); + if (stmt->statement_type == ECPGst_execute) { - ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, PQerrorMessage(stmt->connection->connection)); - ECPGraise_backend(stmt->lineno, NULL, stmt->connection->connection, stmt->compat); + results = PQexecPrepared(stmt->connection->connection, stmt->name, nParams, paramValues, NULL, NULL, 0); + ECPGlog("ECPGexecute line %d: using PQexecPrepared for %s\n", stmt->lineno, stmt->command); } else + { + if (nParams == 0) + { + results = PQexec(stmt->connection->connection, stmt->command); + ECPGlog("ECPGexecute line %d: using PQexec\n", stmt->lineno); + } + else + { + results = PQexecParams(stmt->connection->connection, stmt->command, nParams, NULL, paramValues, NULL, NULL, 0); + ECPGlog("ECPGexecute line %d: using PQexecParams \n", stmt->lineno); + } + } - /* - * note: since some of the following code is duplicated in - * descriptor.c it should go into a separate function - */ + free_params(paramValues, nParams, true, stmt->lineno); + + if (!ECPGcheck_PQresult(results, stmt->lineno, stmt->connection->connection, stmt->compat)) + return (false); + + var = stmt->outlist; + switch (PQresultStatus(results)) { - bool clear_result = TRUE; - struct sqlca_t *sqlca = ECPGget_sqlca(); + int nfields, + ntuples, + act_field; - var = stmt->outlist; - switch (PQresultStatus(results)) - { - int nfields, - ntuples, - act_field; + case PGRES_TUPLES_OK: + nfields = PQnfields(results); + sqlca->sqlerrd[2] = ntuples = PQntuples(results); + ECPGlog("ECPGexecute line %d: Correctly got %d tuples with %d fields\n", stmt->lineno, ntuples, nfields); + status = true; - case PGRES_TUPLES_OK: - nfields = PQnfields(results); - sqlca->sqlerrd[2] = ntuples = PQntuples(results); - ECPGlog("ECPGexecute line %d: Correctly got %d tuples with %d fields\n", stmt->lineno, ntuples, nfields); - status = true; + if (ntuples < 1) + { + if (ntuples) + ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d\n", + stmt->lineno, ntuples); + ECPGraise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); + status = false; + break; + } - if (ntuples < 1) - { - if (ntuples) - ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d\n", - stmt->lineno, ntuples); - ECPGraise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); + if (var != NULL && var->type == ECPGt_descriptor) + { + PGresult **resultpp = ECPGdescriptor_lvalue(stmt->lineno, (const char *) var->pointer); + + if (resultpp == NULL) status = false; - break; + else + { + if (*resultpp) + PQclear(*resultpp); + *resultpp = results; + clear_result = FALSE; + ECPGlog("ECPGexecute putting result (%d tuples) into descriptor '%s'\n", PQntuples(results), (const char *) var->pointer); } - - if (var != NULL && var->type == ECPGt_descriptor) + var = var->next; + } + else + for (act_field = 0; act_field < nfields && status; act_field++) { - PGresult **resultpp = ECPGdescriptor_lvalue(stmt->lineno, (const char *) var->pointer); - - if (resultpp == NULL) - status = false; - else + if (var != NULL) { - if (*resultpp) - PQclear(*resultpp); - *resultpp = results; - clear_result = FALSE; - ECPGlog("ECPGexecute putting result (%d tuples) into descriptor '%s'\n", PQntuples(results), (const char *) var->pointer); + status = ECPGstore_result(results, act_field, stmt, var); + var = var->next; } - var = var->next; - } - else - for (act_field = 0; act_field < nfields && status; act_field++) + else if (!INFORMIX_MODE(stmt->compat)) { - if (var != NULL) - { - status = ECPGstore_result(results, act_field, stmt, var); - var = var->next; - } - else if (!INFORMIX_MODE(stmt->compat)) - { - ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL); - return (false); - } + ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL); + return (false); } - - if (status && var != NULL) - { - ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL); - status = false; } - break; - case PGRES_EMPTY_QUERY: - /* do nothing */ - ECPGraise(stmt->lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL); - 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, cmdstat); - if (stmt->compat != ECPG_COMPAT_INFORMIX_SE && - !sqlca->sqlerrd[2] && - (!strncmp(cmdstat, "UPDATE", 6) - || !strncmp(cmdstat, "INSERT", 6) - || !strncmp(cmdstat, "DELETE", 6))) - ECPGraise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); - break; - case PGRES_NONFATAL_ERROR: - case PGRES_FATAL_ERROR: - case PGRES_BAD_RESPONSE: - ECPGlog("ECPGexecute line %d: Error: %s", stmt->lineno, PQresultErrorMessage(results)); - ECPGraise_backend(stmt->lineno, results, stmt->connection->connection, stmt->compat); + if (status && var != NULL) + { + ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL); status = false; - break; - case PGRES_COPY_OUT: - { - char *buffer; - int res; + } - ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT\n", stmt->lineno); - while ((res = PQgetCopyData(stmt->connection->connection, - &buffer, 0)) > 0) - { - printf("%s", buffer); - PQfreemem(buffer); - } - if (res == -1) - { - /* COPY done */ - PQclear(results); - results = PQgetResult(stmt->connection->connection); - if (PQresultStatus(results) == PGRES_COMMAND_OK) - ECPGlog("ECPGexecute line %d: Got PGRES_COMMAND_OK after PGRES_COPY_OUT\n", stmt->lineno); - else - ECPGlog("ECPGexecute line %d: Got error after PGRES_COPY_OUT: %s", PQresultErrorMessage(results)); - } - break; + 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, cmdstat); + if (stmt->compat != ECPG_COMPAT_INFORMIX_SE && + !sqlca->sqlerrd[2] && + (!strncmp(cmdstat, "UPDATE", 6) + || !strncmp(cmdstat, "INSERT", 6) + || !strncmp(cmdstat, "DELETE", 6))) + ECPGraise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); + break; + case PGRES_COPY_OUT: + { + char *buffer; + int res; + + ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT\n", stmt->lineno); + while ((res = PQgetCopyData(stmt->connection->connection, + &buffer, 0)) > 0) + { + printf("%s", buffer); + PQfreemem(buffer); + } + if (res == -1) + { + /* COPY done */ + PQclear(results); + results = PQgetResult(stmt->connection->connection); + if (PQresultStatus(results) == PGRES_COMMAND_OK) + ECPGlog("ECPGexecute line %d: Got PGRES_COMMAND_OK after PGRES_COPY_OUT\n", stmt->lineno); + else + ECPGlog("ECPGexecute line %d: Got error after PGRES_COPY_OUT: %s", PQresultErrorMessage(results)); } - case PGRES_COPY_IN: - ECPGlog("ECPGexecute line %d: Got PGRES_COPY_IN ... tossing.\n", stmt->lineno); - PQendcopy(stmt->connection->connection); - break; - default: - ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n", - stmt->lineno); - ECPGraise_backend(stmt->lineno, results, stmt->connection->connection, stmt->compat); - status = false; break; - } - if (clear_result) - PQclear(results); + } + default: + /* execution should never reach this code because it is already handled in ECPGcheck_PQresult() */ + ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n", + stmt->lineno); + ECPGraise_backend(stmt->lineno, results, stmt->connection->connection, stmt->compat); + status = false; + break; } + if (clear_result) + PQclear(results); /* check for asynchronous returns */ notify = PQnotifies(stmt->connection->connection); @@ -1382,7 +1431,7 @@ ECPGexecute(struct statement * stmt) } bool -ECPGdo(int lineno, int compat, int force_indicator, const char *connection_name, const char *query,...) +ECPGdo(const int lineno, const int compat, const int force_indicator, const char *connection_name, const bool questionmarks, const enum ECPG_statement_type st, const char *query,...) { va_list args; struct statement *stmt; @@ -1391,6 +1440,14 @@ ECPGdo(int lineno, int compat, int force_indicator, const char *connection_name, char *oldlocale; enum ECPGttype type; struct variable **list; + enum ECPG_statement_type statement_type = st; + char *prepname; + + if (!query) + { + ECPGraise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL); + return(false); + } /* Make sure we do NOT honor the locale for numeric input/output */ /* since the database wants the standard decimal point */ @@ -1435,15 +1492,47 @@ ECPGdo(int lineno, int compat, int force_indicator, const char *connection_name, { setlocale(LC_NUMERIC, oldlocale); ECPGfree(oldlocale); + ECPGfree(prepname); va_end(args); return false; } - stmt->command = ECPGstrdup(query, lineno); + /* If statement type is ECPGst_prepnormal we are supposed to prepare + * the statement before executing them */ + if (statement_type == ECPGst_prepnormal) + { + if (!ECPGauto_prepare(lineno, connection_name, questionmarks, &prepname, query)) + return(false); + + /* statement is now prepared, so instead of the query we have to execute the name */ + stmt->command = prepname; + statement_type = ECPGst_execute; + } + else + stmt->command = ECPGstrdup(query, lineno); + + stmt->name = NULL; + + if (statement_type == ECPGst_execute) + { + /* if we have an EXECUTE command, only the name is send */ + char *command = ECPGprepared(stmt->command, lineno); + + if (command) + { + stmt->name = stmt->command; + stmt->command = ECPGstrdup(command, lineno); + } + else + ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, stmt->command); + } + stmt->connection = con; stmt->lineno = lineno; stmt->compat = compat; stmt->force_indicator = force_indicator; + stmt->questionmarks = questionmarks; + stmt->statement_type = statement_type; list = &(stmt->inlist); @@ -1564,7 +1653,7 @@ bool ECPGdo_descriptor(int line, const char *connection, const char *descriptor, const char *query) { - return ECPGdo(line, ECPG_COMPAT_PGSQL, true, connection, (char *) query, ECPGt_EOIT, + return ECPGdo(line, ECPG_COMPAT_PGSQL, true, connection, '\0', 0, (char *) query, ECPGt_EOIT, ECPGt_descriptor, descriptor, 0L, 0L, 0L, ECPGt_NO_INDICATOR, NULL, 0L, 0L, 0L, ECPGt_EORT); } diff --git a/src/interfaces/ecpg/ecpglib/extern.h b/src/interfaces/ecpg/ecpglib/extern.h index a28e6bff043..7c69e98148d 100644 --- a/src/interfaces/ecpg/ecpglib/extern.h +++ b/src/interfaces/ecpg/ecpglib/extern.h @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/extern.h,v 1.24 2007/04/27 07:55:14 meskes Exp $ */ +/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/extern.h,v 1.25 2007/08/14 10:01:52 meskes Exp $ */ #ifndef _ECPG_LIB_EXTERN_H #define _ECPG_LIB_EXTERN_H @@ -70,9 +70,12 @@ struct statement { int lineno; char *command; + char *name; struct connection *connection; enum COMPAT_MODE compat; bool force_indicator; + enum ECPG_statement_type statement_type; + bool questionmarks; struct variable *inlist; struct variable *outlist; }; @@ -133,7 +136,12 @@ PGresult **ECPGdescriptor_lvalue(int line, const char *descriptor); bool ECPGstore_result(const PGresult *results, int act_field, const struct statement * stmt, struct variable * var); -bool ECPGstore_input(const int, const bool, const struct variable *, const char **, bool *, bool); +bool ECPGstore_input(const int, const bool, const struct variable *, const char **, bool); + +bool ECPGcheck_PQresult(PGresult *, int, PGconn *, enum COMPAT_MODE); +void ECPGraise(int line, int code, const char *sqlstate, const char *str); +void ECPGraise_backend(int line, PGresult *result, PGconn *conn, int compat); +char *ECPGprepared(const char *, int); /* SQLSTATE values generated or processed by ecpglib (intentionally * not exported -- users should refer to the codes directly) */ diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c index 47d924fac86..1da37ec5f77 100644 --- a/src/interfaces/ecpg/ecpglib/misc.c +++ b/src/interfaces/ecpg/ecpglib/misc.c @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/misc.c,v 1.35 2007/03/29 12:02:24 meskes Exp $ */ +/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/misc.c,v 1.36 2007/08/14 10:01:52 meskes Exp $ */ #define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" @@ -213,20 +213,14 @@ ECPGtrans(int lineno, const char *connection_name, const char *transaction) if (con->committed && !con->autocommit && strncmp(transaction, "begin", 5) != 0 && strncmp(transaction, "start", 5) != 0) { res = PQexec(con->connection, "begin transaction"); - if (res == NULL || PQresultStatus(res) != PGRES_COMMAND_OK) - { - ECPGraise(lineno, ECPG_TRANS, ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL); + if (!ECPGcheck_PQresult(res, lineno, con->connection, ECPG_COMPAT_PGSQL)) return FALSE; - } PQclear(res); } res = PQexec(con->connection, transaction); - if (res == NULL || PQresultStatus(res) != PGRES_COMMAND_OK) - { - ECPGraise(lineno, ECPG_TRANS, ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL); + if (!ECPGcheck_PQresult(res, lineno, con->connection, ECPG_COMPAT_PGSQL)) return FALSE; - } PQclear(res); } diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c index f1a9db0729f..bad7b359b45 100644 --- a/src/interfaces/ecpg/ecpglib/prepare.c +++ b/src/interfaces/ecpg/ecpglib/prepare.c @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/prepare.c,v 1.18 2006/10/04 00:30:11 momjian Exp $ */ +/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/prepare.c,v 1.19 2007/08/14 10:01:52 meskes Exp $ */ #define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" @@ -13,11 +13,28 @@ static struct prepared_statement { - char *name; + char *name; + bool prepared; struct statement *stmt; struct prepared_statement *next; } *prep_stmts = NULL; +#define STMTID_SIZE 32 + +typedef struct +{ + int lineno; + char stmtID[STMTID_SIZE]; + char *ecpgQuery; + long execs; /* # of executions */ + char *connection; /* connection for the statement */ +} stmtCacheEntry; + +static int nextStmtID = 1; +static int stmtCacheNBuckets = 2039; /* # buckets - a prime # */ +static int stmtCacheEntPerBucket = 8; /* # entries/bucket */ +static stmtCacheEntry stmtCacheEntries[16384] = {{0,{0},0,0,0}}; + static bool isvarchar(unsigned char c) { @@ -33,43 +50,68 @@ isvarchar(unsigned char c) return (false); } -static void -replace_variables(char *text) +static bool +replace_variables(char **text, int lineno, bool questionmarks) { - char *ptr = text; - bool string = false; + bool string = false; + int counter = 1, ptr = 0; - for (; *ptr != '\0'; ptr++) + for (; (*text)[ptr] != '\0'; ptr++) { - if (*ptr == '\'') + if ((*text)[ptr] == '\'') string = string ? false : true; - if (!string && *ptr == ':') + if (string || (((*text)[ptr] != ':') && ((*text)[ptr] != '?'))) + continue; + + if (((*text)[ptr] == ':') && ((*text)[ptr+1] == ':')) + ptr += 2; /* skip '::' */ + else { - if (ptr[1] == ':') - ptr += 2; /* skip '::' */ - else + int len; + int buffersize = sizeof(int) * CHAR_BIT * 10 / 3; /* a rough guess of the size we need */ + char *buffer, *newcopy; + + if (!(buffer = (char *) ECPGalloc(buffersize, lineno))) + return false; + + snprintf(buffer, buffersize, "$%d", counter++); + + for (len=1; (*text)[ptr+len] && isvarchar((*text)[ptr+len]); len++); + if (!(newcopy = (char *) ECPGalloc(strlen(*text) - len + strlen(buffer) + 1, lineno))) { - *ptr = '?'; - for (++ptr; *ptr && isvarchar(*ptr); ptr++) - *ptr = ' '; - if (*ptr == '\0') /* we reached the end */ - ptr--; /* since we will ptr++ in the top level for - * loop */ + ECPGfree(buffer); + return false; } + + strncpy(newcopy, *text, ptr); + strcpy(newcopy + ptr, buffer); + strcat(newcopy, (*text) + ptr + len); + + ECPGfree(*text); + ECPGfree(buffer); + + *text = newcopy; + + if ((*text)[ptr] == '\0') /* we reached the end */ + ptr--; /* since we will (*text)[ptr]++ in the top level for + * loop */ } } + return true; } /* handle the EXEC SQL PREPARE statement */ bool -ECPGprepare(int lineno, const char *name, const char *variable) +ECPGprepare(int lineno, const char *connection_name, const int questionmarks, const char *name, const char *variable) { struct statement *stmt; struct prepared_statement *this; struct sqlca_t *sqlca = ECPGget_sqlca(); + PGresult *query; ECPGinit_sqlca(sqlca); + /* check if we already have prepared this statement */ for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next); if (this) @@ -93,18 +135,30 @@ ECPGprepare(int lineno, const char *name, const char *variable) /* create statement */ stmt->lineno = lineno; - stmt->connection = NULL; + stmt->connection = ECPGget_connection(connection_name); stmt->command = ECPGstrdup(variable, lineno); stmt->inlist = stmt->outlist = NULL; /* if we have C variables in our statment replace them with '?' */ - replace_variables(stmt->command); + replace_variables(&(stmt->command), lineno, questionmarks); /* add prepared statement to our list */ - this->name = ECPGstrdup(name, lineno); + this->name = (char *) name; this->stmt = stmt; - ECPGlog("ECPGprepare line %d: QUERY: %s\n", stmt->lineno, stmt->command); + /* and finally really prepare the statement */ + query = PQprepare(stmt->connection->connection, name, stmt->command, 0, NULL); + if (!ECPGcheck_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat)) + { + ECPGfree(stmt->command); + ECPGfree(this); + ECPGfree(stmt); + return false; + } + + ECPGlog("ECPGprepare line %d: NAME: %s QUERY: %s\n", stmt->lineno, name, stmt->command); + PQclear(query); + this->prepared = true; if (prep_stmts == NULL) this->next = NULL; @@ -115,30 +169,8 @@ ECPGprepare(int lineno, const char *name, const char *variable) return true; } -/* handle the EXEC SQL DEALLOCATE PREPARE statement */ -bool -ECPGdeallocate(int lineno, int c, const char *name) -{ - bool ret = ECPGdeallocate_one(lineno, name); - enum COMPAT_MODE compat = c; - - if (INFORMIX_MODE(compat)) - { - /* - * Just ignore all errors since we do not know the list of cursors we - * are allowed to free. We have to trust the software. - */ - return true; - } - - if (!ret) - ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name); - - return ret; -} - -bool -ECPGdeallocate_one(int lineno, const char *name) +static bool +deallocate_one(int lineno, const char *name) { struct prepared_statement *this, *prev; @@ -147,8 +179,26 @@ ECPGdeallocate_one(int lineno, const char *name) for (this = prep_stmts, prev = NULL; this != NULL && strcmp(this->name, name) != 0; prev = this, this = this->next); if (this) { + /* first deallocate the statement in the backend */ + if (this->prepared) + { + char *text; + PGresult *query; + + if (!(text = (char *) ECPGalloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno))) + return false; + else + { + sprintf(text, "deallocate \"%s\"", this->name); + query = PQexec(this->stmt->connection->connection, text); + ECPGfree(text); + if (!ECPGcheck_PQresult(query, lineno, this->stmt->connection->connection, this->stmt->compat)) + return false; + PQclear(query); + } + } + /* okay, free all the resources */ - ECPGfree(this->name); ECPGfree(this->stmt->command); ECPGfree(this->stmt); if (prev != NULL) @@ -162,13 +212,36 @@ ECPGdeallocate_one(int lineno, const char *name) return false; } +/* handle the EXEC SQL DEALLOCATE PREPARE statement */ bool -ECPGdeallocate_all(int lineno) +ECPGdeallocate(int lineno, int c, const char *name) +{ + bool ret = deallocate_one(lineno, name); + enum COMPAT_MODE compat = c; + + ECPGlog("ECPGdeallocate line %d: NAME: %s\n", lineno, name); + if (INFORMIX_MODE(compat)) + { + /* + * Just ignore all errors since we do not know the list of cursors we + * are allowed to free. We have to trust the software. + */ + return true; + } + + if (!ret) + ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name); + + return ret; +} + +bool +ECPGdeallocate_all(int lineno, int compat) { /* deallocate all prepared statements */ while (prep_stmts != NULL) { - bool b = ECPGdeallocate(lineno, ECPG_COMPAT_PGSQL, prep_stmts->name); + bool b = ECPGdeallocate(lineno, compat, prep_stmts->name); if (!b) return false; @@ -177,12 +250,204 @@ ECPGdeallocate_all(int lineno) return true; } +char * +ECPGprepared(const char *name, int lineno) +{ + struct prepared_statement *this; + + for (this = prep_stmts; this != NULL && ((strcmp(this->name, name) != 0) || this->prepared == false); this = this->next); + return (this) ? this->stmt->command : NULL; +} + /* return the prepared statement */ char * -ECPGprepared_statement(const char *name) +ECPGprepared_statement(const char *name, int lineno) { struct prepared_statement *this; for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next); return (this) ? this->stmt->command : NULL; } + +/* + * hash a SQL statement - returns entry # of first entry in the bucket + */ +static int +HashStmt(const char *ecpgQuery) +{ + int stmtIx, bucketNo, hashLeng, stmtLeng; + long long hashVal, rotVal; + + stmtLeng = strlen(ecpgQuery); + hashLeng = 50; /* use 1st 50 characters of statement */ + if(hashLeng > stmtLeng) /* if the statement isn't that long */ + hashLeng = stmtLeng; /* use its actual length */ + + hashVal = 0; + for(stmtIx = 0; stmtIx < hashLeng; ++stmtIx) + { + hashVal = hashVal + (int) ecpgQuery[stmtIx]; + hashVal = hashVal << 13; + rotVal = (hashVal & 0x1fff00000000LL) >> 32; + hashVal = (hashVal & 0xffffffffLL) | rotVal; + } + + bucketNo = hashVal % stmtCacheNBuckets; + bucketNo += 1; /* don't use bucket # 0 */ + + return (bucketNo * stmtCacheEntPerBucket); +} + +/* + * search the statement cache - search for entry with matching ECPG-format query + * Returns entry # in cache if found + * OR zero if not present (zero'th entry isn't used) + */ +static int +SearchStmtCache(const char *ecpgQuery) +{ + int entNo, entIx; + +/* hash the statement */ + entNo = HashStmt(ecpgQuery); + +/* search the cache */ + for(entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx) + { + if(stmtCacheEntries[entNo].stmtID[0]) /* check if entry is in use */ + { + if(!strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery)) + break; /* found it */ + } + ++entNo; /* incr entry # */ + } + +/* if entry wasn't found - set entry # to zero */ + if(entIx >= stmtCacheEntPerBucket) + entNo = 0; + + return(entNo); +} + +/* + * free an entry in the statement cache + * Returns entry # in cache used + * OR negative error code + */ +static int +ECPGfreeStmtCacheEntry(int entNo) /* entry # to free */ +{ + stmtCacheEntry *entry; + PGresult *results; + char deallocText[100]; + struct connection *con; + + entry = &stmtCacheEntries[entNo]; + if(!entry->stmtID[0]) /* return if the entry isn't in use */ + return(0); + + con = ECPGget_connection(entry->connection); +/* free the server resources for the statement */ + ECPGlog("ECPGfreeStmtCacheEntry line %d: deallocate %s, cache entry #%d\n", entry->lineno, entry->stmtID, entNo); + sprintf(deallocText, "DEALLOCATE PREPARE %s", entry->stmtID); + results = PQexec(con->connection, deallocText); + + if (!ECPGcheck_PQresult(results, entry->lineno, con->connection, ECPG_COMPAT_PGSQL)) + return(-1); + PQclear(results); + + entry->stmtID[0] = '\0'; + +/* free the memory used by the cache entry */ + if(entry->ecpgQuery) + { + ECPGfree(entry->ecpgQuery); + entry->ecpgQuery = 0; + } + + return(entNo); +} + +/* + * add an entry to the statement cache + * returns entry # in cache used OR negative error code + */ +static int +AddStmtToCache(int lineno, /* line # of statement */ + char *stmtID, /* statement ID */ + const char *connection, /* connection */ + const char *ecpgQuery) /* query */ +{ + int ix, initEntNo, luEntNo, entNo; + stmtCacheEntry *entry; + +/* hash the statement */ + initEntNo = HashStmt(ecpgQuery); + +/* search for an unused entry */ + entNo = initEntNo; /* start with the initial entry # for the bucket */ + luEntNo = initEntNo; /* use it as the initial 'least used' entry */ + for(ix = 0; ix < stmtCacheEntPerBucket; ++ix) + { + entry = &stmtCacheEntries[entNo]; + if(!entry->stmtID[0]) /* unused entry - use it */ + break; + if(entry->execs < stmtCacheEntries[luEntNo].execs) + luEntNo = entNo; /* save new 'least used' entry */ + ++entNo; /* increment entry # */ + } + +/* if no unused entries were found - use the 'least used' entry found in the bucket */ + if(ix >= stmtCacheEntPerBucket) /* if no unused entries were found */ + entNo = luEntNo; /* re-use the 'least used' entry */ + +/* 'entNo' is the entry to use - make sure its free */ + if (ECPGfreeStmtCacheEntry(entNo) < 0) + return (-1); + +/* add the query to the entry */ + entry = &stmtCacheEntries[entNo]; + entry->lineno = lineno; + entry->ecpgQuery = ECPGstrdup(ecpgQuery, lineno); + entry->connection = (char *)connection; + entry->execs = 0; + memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID)); + + return(entNo); +} + +/* handle cache and preparation of statments in auto-prepare mode */ +bool +ECPGauto_prepare(int lineno, const char *connection_name, const int questionmarks, char **name, const char *query) +{ + int entNo; + + /* search the statement cache for this statement */ + entNo = SearchStmtCache(query); + + /* if not found - add the statement to the cache */ + if(entNo) + { + ECPGlog("ECPGauto_prepare line %d: stmt found in cache, entry %d\n", lineno, entNo); + *name = ECPGstrdup(stmtCacheEntries[entNo].stmtID, lineno); + } + else + { + ECPGlog("ECPGauto_prepare line %d: stmt not in cache; inserting\n", lineno); + + /* generate a statement ID */ + *name = (char *) ECPGalloc(STMTID_SIZE, lineno); + sprintf(*name, "ecpg%d", nextStmtID++); + + if (!ECPGprepare(lineno, connection_name, questionmarks, ECPGstrdup(*name, lineno), query)) + return(false); + if (AddStmtToCache(lineno, *name, connection_name, query) < 0) + return(false); + } + + /* increase usage counter */ + stmtCacheEntries[entNo].execs++; + + return(true); +} + |