From c6dd1e63a951ba4b529ed292f88fba28de70df78 Mon Sep 17 00:00:00 2001 From: Bruce Momjian <bruce@momjian.us> Date: Tue, 11 Aug 1998 18:33:37 +0000 Subject: [PATCH] This one cleans the cursor problems ecpg had so far. It is now able to understand cursors with variables. Michael --- src/interfaces/ecpg/ChangeLog | 7 + src/interfaces/ecpg/TODO | 4 +- src/interfaces/ecpg/include/ecpglib.h | 3 - src/interfaces/ecpg/lib/Makefile.in | 2 +- src/interfaces/ecpg/lib/ecpglib.c | 426 ++++++++++++++------------ src/interfaces/ecpg/preproc/ecpg.c | 19 ++ src/interfaces/ecpg/preproc/extern.h | 18 ++ src/interfaces/ecpg/preproc/preproc.y | 73 ++++- src/interfaces/ecpg/test/Makefile | 2 +- src/interfaces/ecpg/test/test2.pgc | 10 +- 10 files changed, 333 insertions(+), 231 deletions(-) diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog index 1907471a731..6ac03ff234f 100644 --- a/src/interfaces/ecpg/ChangeLog +++ b/src/interfaces/ecpg/ChangeLog @@ -270,3 +270,10 @@ Mon Aug 3 17:23:18 CEST 1998 - Fixed cursor handling - Set version to 2.3.5 - Set library version to 2.4 + +Fri Aug 7 12:38:50 CEST 1998 + + - Fixed cursor handling once again + - Added support for variables in cursor + - Set version to 2.3.6 + - Set library version to 2.5 diff --git a/src/interfaces/ecpg/TODO b/src/interfaces/ecpg/TODO index 5811cb767de..908ec619270 100644 --- a/src/interfaces/ecpg/TODO +++ b/src/interfaces/ecpg/TODO @@ -1,9 +1,7 @@ -What happens to a cursor declaration with variables? - The complete structure definition has to be listed inside the declare section of the structure variable for ecpg to be able to understand it. -Variable type bool has to be checked. I never used it so far. +Variable type bool has to be tested. I never used it so far. The error message for "no data" in an exec sql insert select from statement has to be 100. diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h index a81ca7e8b9c..83f2e6efb86 100644 --- a/src/interfaces/ecpg/include/ecpglib.h +++ b/src/interfaces/ecpg/include/ecpglib.h @@ -13,9 +13,6 @@ bool ECPGdisconnect(int, const char *); void ECPGlog(const char *format,...); -bool ECPGdeclare(int, const char *, char *); -bool ECPGopen(int, const char *); - #ifdef LIBPQ_FE_H bool ECPGsetdb(PGconn *); diff --git a/src/interfaces/ecpg/lib/Makefile.in b/src/interfaces/ecpg/lib/Makefile.in index 8fd6b772030..e236487d6aa 100644 --- a/src/interfaces/ecpg/lib/Makefile.in +++ b/src/interfaces/ecpg/lib/Makefile.in @@ -4,7 +4,7 @@ include $(SRCDIR)/Makefile.global PQ_INCLUDE=-I$(SRCDIR)/interfaces/libpq SO_MAJOR_VERSION=2 -SO_MINOR_VERSION=4 +SO_MINOR_VERSION=5 PORTNAME=@PORTNAME@ diff --git a/src/interfaces/ecpg/lib/ecpglib.c b/src/interfaces/ecpg/lib/ecpglib.c index 86021d6ee46..0d3d4fa0e6a 100644 --- a/src/interfaces/ecpg/lib/ecpglib.c +++ b/src/interfaces/ecpg/lib/ecpglib.c @@ -33,6 +33,29 @@ static struct connection struct connection *next; } *all_connections = NULL, *actual_connection = NULL; +struct variable +{ + enum ECPGttype type; + void *value; + long varcharsize; + long arrsize; + long offset; + enum ECPGttype ind_type; + void *ind_value; + long ind_varcharsize; + long ind_arrsize; + long ind_offset; + struct variable *next; +}; + +struct statement +{ + int lineno; + char *command; + struct variable *inlist; + struct variable *outlist; +}; + static int simple_debug = 0; static FILE *debugstream = NULL; static int committed = true; @@ -116,27 +139,87 @@ ECPGfinish(struct connection *act) ECPGlog("ECPGfinish: called an extra time.\n"); } -bool -ECPGdo(int lineno, char *query,...) +/* create a list of variables */ +static bool +create_statement(int lineno, struct statement **stmt, char *query, va_list ap) +{ + struct variable **list = &((*stmt)->inlist); + enum ECPGttype type; + + *stmt = calloc(sizeof(struct statement), 1); + + if (!*stmt) + { + ECPGfinish(actual_connection); + ECPGlog("out of memory\n"); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + return false; + } + + (*stmt)->command = query; + (*stmt)->lineno = lineno; + + list = &((*stmt)->inlist); + + type = va_arg(ap, enum ECPGttype); + + while (type != ECPGt_EORT) + { + if (type == ECPGt_EOIT) + { + list = &((*stmt)->outlist); + } + else + { + struct variable *var, *ptr; + + var = malloc(sizeof(struct variable)); + if (!var) + { + ECPGfinish(actual_connection); + ECPGlog("out of memory\n"); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + return false; + } + + var->type = type; + var->value = va_arg(ap, void *); + var->varcharsize = va_arg(ap, long); + var->arrsize = va_arg(ap, long); + var->offset = va_arg(ap, long); + var->ind_type = va_arg(ap, enum ECPGttype); + var->ind_value = va_arg(ap, void *); + var->ind_varcharsize = va_arg(ap, long); + var->ind_arrsize = va_arg(ap, long); + var->ind_offset = va_arg(ap, long); + var->next = NULL; + + for (ptr = *list; ptr && ptr->next; ptr=ptr->next); + + if (ptr == NULL) + *list = var; + else + ptr->next = var; + } + + type = va_arg(ap, enum ECPGttype); + } + + return(true); +} + +static bool +ECPGexecute(struct statement *stmt) { - va_list ap; bool status = false; char *copiedquery; PGresult *results; PGnotify *notify; - enum ECPGttype type; - void *value = NULL, *ind_value; - long varcharsize, ind_varcharsize; - long arrsize, ind_arrsize; - long offset, ind_offset; - enum ECPGttype ind_type; + struct variable *var; memset((char *) &sqlca, 0, sizeof (sqlca)); - va_start(ap, query); - copiedquery = strdup(query); - - type = va_arg(ap, enum ECPGttype); + copiedquery = strdup(stmt->command); /* * Now, if the type is one of the fill in types then we take the @@ -144,7 +227,8 @@ ECPGdo(int lineno, char *query,...) * Then if there are any more fill in types we fill in at the next and * so on. */ - while (type != ECPGt_EOIT) + var = stmt->inlist; + while (var) { char *newcopy; char *mallocedval = NULL; @@ -158,34 +242,24 @@ ECPGdo(int lineno, char *query,...) * think). */ - value = va_arg(ap, void *); - varcharsize = va_arg(ap, long); - arrsize = va_arg(ap, long); - offset = va_arg(ap, long); - ind_type = va_arg(ap, enum ECPGttype); - ind_value = va_arg(ap, void *); - ind_varcharsize = va_arg(ap, long); - ind_arrsize = va_arg(ap, long); - ind_offset = va_arg(ap, long); - buff[0] = '\0'; /* check for null value and set input buffer accordingly */ - switch (ind_type) + switch (var->ind_type) { case ECPGt_short: case ECPGt_unsigned_short: - if (*(short *) ind_value < 0) + if (*(short *) var->ind_value < 0) strcpy(buff, "null"); break; case ECPGt_int: case ECPGt_unsigned_int: - if (*(int *) ind_value < 0) + if (*(int *) var->ind_value < 0) strcpy(buff, "null"); break; case ECPGt_long: case ECPGt_unsigned_long: - if (*(long *) ind_value < 0L) + if (*(long *) var->ind_value < 0L) strcpy(buff, "null"); break; default: @@ -194,42 +268,42 @@ ECPGdo(int lineno, char *query,...) if (*buff == '\0') { - switch (type) + switch (var->type) { case ECPGt_short: case ECPGt_int: - sprintf(buff, "%d", *(int *) value); + sprintf(buff, "%d", *(int *) var->value); tobeinserted = buff; break; case ECPGt_unsigned_short: case ECPGt_unsigned_int: - sprintf(buff, "%d", *(unsigned int *) value); + sprintf(buff, "%d", *(unsigned int *) var->value); tobeinserted = buff; break; case ECPGt_long: - sprintf(buff, "%ld", *(long *) value); + sprintf(buff, "%ld", *(long *) var->value); tobeinserted = buff; break; case ECPGt_unsigned_long: - sprintf(buff, "%ld", *(unsigned long *) value); + sprintf(buff, "%ld", *(unsigned long *) var->value); tobeinserted = buff; break; case ECPGt_float: - sprintf(buff, "%.14g", *(float *) value); + sprintf(buff, "%.14g", *(float *) var->value); tobeinserted = buff; break; case ECPGt_double: - sprintf(buff, "%.14g", *(double *) value); + sprintf(buff, "%.14g", *(double *) var->value); tobeinserted = buff; break; case ECPGt_bool: - sprintf(buff, "'%c'", (*(char *) value ? 't' : 'f')); + sprintf(buff, "'%c'", (*(char *) var->value ? 't' : 'f')); tobeinserted = buff; break; @@ -237,7 +311,7 @@ ECPGdo(int lineno, char *query,...) case ECPGt_unsigned_char: { /* set slen to string length if type is char * */ - int slen = (varcharsize == 0) ? strlen((char *) value) : varcharsize; + int slen = (var->varcharsize == 0) ? strlen((char *) var->value) : var->varcharsize; char * tmp; newcopy = (char *) malloc(slen + 1); @@ -245,11 +319,11 @@ ECPGdo(int lineno, char *query,...) { ECPGfinish(actual_connection); ECPGlog("out of memory\n"); - register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", stmt->lineno); return false; } - strncpy(newcopy, (char *) value, slen); + strncpy(newcopy, (char *) var->value, slen); newcopy[slen] = '\0'; mallocedval = (char *) malloc(2 * strlen(newcopy) + 3); @@ -257,7 +331,7 @@ ECPGdo(int lineno, char *query,...) { ECPGfinish(actual_connection); ECPGlog("out of memory\n"); - register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", stmt->lineno); return false; } @@ -267,7 +341,7 @@ ECPGdo(int lineno, char *query,...) { ECPGfinish(actual_connection); ECPGlog("out of memory\n"); - register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", stmt->lineno); return false; } @@ -282,28 +356,28 @@ ECPGdo(int lineno, char *query,...) case ECPGt_varchar: { - struct ECPGgeneric_varchar *var = - (struct ECPGgeneric_varchar *) value; + struct ECPGgeneric_varchar *variable = + (struct ECPGgeneric_varchar *) (var->value); char *tmp; - newcopy = (char *) malloc(var->len + 1); + newcopy = (char *) malloc(variable->len + 1); if (!newcopy) { ECPGfinish(actual_connection); ECPGlog("out of memory\n"); - register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", stmt->lineno); return false; } - strncpy(newcopy, var->arr, var->len); - newcopy[var->len] = '\0'; + strncpy(newcopy, variable->arr, variable->len); + newcopy[variable->len] = '\0'; mallocedval = (char *) malloc(2 * strlen(newcopy) + 3); if (!mallocedval) { ECPGfinish(actual_connection); ECPGlog("out of memory\n"); - register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", stmt->lineno); return false; } @@ -313,7 +387,7 @@ ECPGdo(int lineno, char *query,...) { ECPGfinish(actual_connection); ECPGlog("out of memory\n"); - register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", stmt->lineno); return false; } @@ -329,7 +403,7 @@ ECPGdo(int lineno, char *query,...) default: /* Not implemented yet */ register_error(ECPG_UNSUPPORTED, "Unsupported type %s on line %d.", - ECPGtype_name(type), lineno); + ECPGtype_name(var->type), stmt->lineno); return false; break; } @@ -348,7 +422,7 @@ ECPGdo(int lineno, char *query,...) { ECPGfinish(actual_connection); ECPGlog("out of memory\n"); - register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", stmt->lineno); return false; } @@ -360,7 +434,7 @@ ECPGdo(int lineno, char *query,...) * We have an argument but we dont have the matched up string * in the string */ - register_error(ECPG_TOO_MANY_ARGUMENTS, "Too many arguments line %d.", lineno); + register_error(ECPG_TOO_MANY_ARGUMENTS, "Too many arguments line %d.", stmt->lineno); return false; } else @@ -379,7 +453,7 @@ ECPGdo(int lineno, char *query,...) /* * Now everything is safely copied to the newcopy. Lets free the - * oldcopy and let the copiedquery get the value from the newcopy. + * oldcopy and let the copiedquery get the var->value from the newcopy. */ if (mallocedval != NULL) { @@ -390,13 +464,13 @@ ECPGdo(int lineno, char *query,...) free(copiedquery); copiedquery = newcopy; - type = va_arg(ap, enum ECPGttype); + var = var->next; } /* Check if there are unmatched things left. */ if (strstr(copiedquery, ";;") != NULL) { - register_error(ECPG_TOO_FEW_ARGUMENTS, "Too few arguments line %d.", lineno); + register_error(ECPG_TOO_FEW_ARGUMENTS, "Too few arguments line %d.", stmt->lineno); return false; } @@ -406,27 +480,28 @@ ECPGdo(int lineno, char *query,...) { if ((results = PQexec(actual_connection->connection, "begin transaction")) == NULL) { - register_error(ECPG_TRANS, "Error starting transaction line %d.", lineno); + register_error(ECPG_TRANS, "Error starting transaction line %d.", stmt->lineno); return false; } PQclear(results); committed = 0; } - ECPGlog("ECPGdo line %d: QUERY: %s\n", lineno, copiedquery); + ECPGlog("ECPGexecute line %d: QUERY: %s\n", stmt->lineno, copiedquery); results = PQexec(actual_connection->connection, copiedquery); free(copiedquery); if (results == NULL) { - ECPGlog("ECPGdo line %d: error: %s", lineno, + ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, PQerrorMessage(actual_connection->connection)); register_error(ECPG_PGSQL, "Postgres error: %s line %d.", - PQerrorMessage(actual_connection->connection), lineno); + PQerrorMessage(actual_connection->connection), stmt->lineno); } else { sqlca.sqlerrd[2] = 0; + var = stmt->outlist; switch (PQresultStatus(results)) { int nfields, ntuples, act_tuple, act_field; @@ -445,9 +520,9 @@ ECPGdo(int lineno, char *query,...) if (ntuples < 1) { - ECPGlog("ECPGdo line %d: Incorrect number of matches: %d\n", - lineno, ntuples); - register_error(ECPG_NOT_FOUND, "Data not found line %d.", lineno); + ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d\n", + stmt->lineno, ntuples); + register_error(ECPG_NOT_FOUND, "Data not found line %d.", stmt->lineno); status = false; break; } @@ -457,23 +532,19 @@ ECPGdo(int lineno, char *query,...) char *pval; char *scan_length; - type = va_arg(ap, enum ECPGttype); - value = va_arg(ap, void *); - varcharsize = va_arg(ap, long); - arrsize = va_arg(ap, long); - offset = va_arg(ap, long); - ind_type = va_arg(ap, enum ECPGttype); - ind_value = va_arg(ap, void *); - ind_varcharsize = va_arg(ap, long); - ind_arrsize = va_arg(ap, long); - ind_offset = va_arg(ap, long); - + if (var == NULL) + { + ECPGlog("ECPGexecute line %d: Too few arguments.\n", stmt->lineno); + register_error(ECPG_TOO_FEW_ARGUMENTS, "Too few arguments line %d.", stmt->lineno); + return(false); + } + /* if we don't have enough space, we cannot read all tuples */ - if ((arrsize > 0 && ntuples > arrsize) || (ind_arrsize > 0 && ntuples > ind_arrsize)) + if ((var->arrsize > 0 && ntuples > var->arrsize) || (var->ind_arrsize > 0 && ntuples > var->ind_arrsize)) { - ECPGlog("ECPGdo line %d: Incorrect number of matches: %d don't fit into array of %d\n", - lineno, ntuples, arrsize); - register_error(ECPG_TOO_MANY_MATCHES, "Too many matches line %d.", lineno); + ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d don't fit into array of %d\n", + stmt->lineno, ntuples, var->arrsize); + register_error(ECPG_TOO_MANY_MATCHES, "Too many matches line %d.", stmt->lineno); status = false; break; } @@ -481,31 +552,31 @@ ECPGdo(int lineno, char *query,...) { pval = PQgetvalue(results, act_tuple, act_field); - ECPGlog("ECPGdo line %d: RESULT: %s\n", lineno, pval ? pval : ""); + ECPGlog("ECPGexecute line %d: RESULT: %s\n", stmt->lineno, pval ? pval : ""); - /* Now the pval is a pointer to the value. */ - /* We will have to decode the value */ + /* Now the pval is a pointer to the var->value. */ + /* We will have to decode the var->value */ - /* check for null value and set indicator accordingly */ - switch (ind_type) + /* check for null var->value and set indicator accordingly */ + switch (var->ind_type) { case ECPGt_short: case ECPGt_unsigned_short: - ((short *) ind_value)[act_tuple] = -PQgetisnull(results, act_tuple, act_field); + ((short *) var->ind_value)[act_tuple] = -PQgetisnull(results, act_tuple, act_field); break; case ECPGt_int: case ECPGt_unsigned_int: - ((int *) ind_value)[act_tuple] = -PQgetisnull(results, act_tuple, act_field); + ((int *) var->ind_value)[act_tuple] = -PQgetisnull(results, act_tuple, act_field); break; case ECPGt_long: case ECPGt_unsigned_long: - ((long *) ind_value)[act_tuple] = -PQgetisnull(results, act_tuple, act_field); + ((long *) var->ind_value)[act_tuple] = -PQgetisnull(results, act_tuple, act_field); break; default: break; } - switch (type) + switch (var->type) { long res; unsigned long ures; @@ -520,7 +591,7 @@ ECPGdo(int lineno, char *query,...) if (*scan_length != '\0') /* Garbage left */ { register_error(ECPG_INT_FORMAT, "Not correctly formatted int type: %s line %d.", - pval, lineno); + pval, stmt->lineno); status = false; res = 0L; } @@ -529,16 +600,16 @@ ECPGdo(int lineno, char *query,...) res = 0L; /* Again?! Yes */ - switch (type) + switch (var->type) { case ECPGt_short: - ((short *) value)[act_tuple] = (short) res; + ((short *) var->value)[act_tuple] = (short) res; break; case ECPGt_int: - ((int *) value)[act_tuple] = (int) res; + ((int *) var->value)[act_tuple] = (int) res; break; case ECPGt_long: - ((long *) value)[act_tuple] = res; + ((long *) var->value)[act_tuple] = res; break; default: /* Cannot happen */ @@ -555,7 +626,7 @@ ECPGdo(int lineno, char *query,...) if (*scan_length != '\0') /* Garbage left */ { register_error(ECPG_UINT_FORMAT, "Not correctly formatted unsigned type: %s line %d.", - pval, lineno); + pval, stmt->lineno); status = false; ures = 0L; } @@ -564,16 +635,16 @@ ECPGdo(int lineno, char *query,...) ures = 0L; /* Again?! Yes */ - switch (type) + switch (var->type) { case ECPGt_unsigned_short: - ((unsigned short *) value)[act_tuple] = (unsigned short) ures; + ((unsigned short *) var->value)[act_tuple] = (unsigned short) ures; break; case ECPGt_unsigned_int: - ((unsigned int *) value)[act_tuple] = (unsigned int) ures; + ((unsigned int *) var->value)[act_tuple] = (unsigned int) ures; break; case ECPGt_unsigned_long: - ((unsigned long *) value)[act_tuple] = ures; + ((unsigned long *) var->value)[act_tuple] = ures; break; default: /* Cannot happen */ @@ -590,7 +661,7 @@ ECPGdo(int lineno, char *query,...) if (*scan_length != '\0') /* Garbage left */ { register_error(ECPG_FLOAT_FORMAT, "Not correctly formatted floating point type: %s line %d.", - pval, lineno); + pval, stmt->lineno); status = false; dres = 0.0; } @@ -599,13 +670,13 @@ ECPGdo(int lineno, char *query,...) dres = 0.0; /* Again?! Yes */ - switch (type) + switch (var->type) { case ECPGt_float: - ((float *) value)[act_tuple] = dres; + ((float *) var->value)[act_tuple] = dres; break; case ECPGt_double: - ((double *) value)[act_tuple] = dres; + ((double *) var->value)[act_tuple] = dres; break; default: /* Cannot happen */ @@ -618,50 +689,50 @@ ECPGdo(int lineno, char *query,...) { if (pval[0] == 'f' && pval[1] == '\0') { - ((char *) value)[act_tuple] = false; + ((char *) var->value)[act_tuple] = false; break; } else if (pval[0] == 't' && pval[1] == '\0') { - ((char *) value)[act_tuple] = true; + ((char *) var->value)[act_tuple] = true; break; } } register_error(ECPG_CONVERT_BOOL, "Unable to convert %s to bool on line %d.", (pval ? pval : "NULL"), - lineno); + stmt->lineno); status = false; break; case ECPGt_char: case ECPGt_unsigned_char: { - if (varcharsize == 0) + if (var->varcharsize == 0) { /* char* */ - strncpy(((char **) value)[act_tuple], pval, strlen(pval)); - (((char **) value)[act_tuple])[strlen(pval)] = '\0'; + strncpy(((char **) var->value)[act_tuple], pval, strlen(pval)); + (((char **) var->value)[act_tuple])[strlen(pval)] = '\0'; } else { - strncpy((char *) (value + offset * act_tuple), pval, varcharsize); - if (varcharsize < strlen(pval)) + strncpy((char *) (var->value + var->offset * act_tuple), pval, var->varcharsize); + if (var->varcharsize < strlen(pval)) { /* truncation */ - switch (ind_type) + switch (var->ind_type) { case ECPGt_short: case ECPGt_unsigned_short: - ((short *) ind_value)[act_tuple] = varcharsize; + ((short *) var->ind_value)[act_tuple] = var->varcharsize; break; case ECPGt_int: case ECPGt_unsigned_int: - ((int *) ind_value)[act_tuple] = varcharsize; + ((int *) var->ind_value)[act_tuple] = var->varcharsize; break; case ECPGt_long: case ECPGt_unsigned_long: - ((long *) ind_value)[act_tuple] = varcharsize; + ((long *) var->ind_value)[act_tuple] = var->varcharsize; break; default: break; @@ -674,62 +745,55 @@ ECPGdo(int lineno, char *query,...) case ECPGt_varchar: { - struct ECPGgeneric_varchar *var = - (struct ECPGgeneric_varchar *) (value + offset * act_tuple); + struct ECPGgeneric_varchar *variable = + (struct ECPGgeneric_varchar *) (var->value + var->offset * act_tuple); - if (varcharsize == 0) - strncpy(var->arr, pval, strlen(pval)); + if (var->varcharsize == 0) + strncpy(variable->arr, pval, strlen(pval)); else - strncpy(var->arr, pval, varcharsize); + strncpy(variable->arr, pval, var->varcharsize); - var->len = strlen(pval); - if (varcharsize > 0 && var->len > varcharsize) + variable->len = strlen(pval); + if (var->varcharsize > 0 && variable->len > var->varcharsize) { /* truncation */ - switch (ind_type) + switch (var->ind_type) { case ECPGt_short: case ECPGt_unsigned_short: - ((short *) ind_value)[act_tuple] = varcharsize; + ((short *) var->ind_value)[act_tuple] = var->varcharsize; break; case ECPGt_int: case ECPGt_unsigned_int: - ((int *) ind_value)[act_tuple] = varcharsize; + ((int *) var->ind_value)[act_tuple] = var->varcharsize; break; case ECPGt_long: case ECPGt_unsigned_long: - ((long *) ind_value)[act_tuple] = varcharsize; + ((long *) var->ind_value)[act_tuple] = var->varcharsize; break; default: break; } sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W'; - var->len = varcharsize; + variable->len = var->varcharsize; } } break; - case ECPGt_EORT: - ECPGlog("ECPGdo line %d: Too few arguments.\n", lineno); - register_error(ECPG_TOO_FEW_ARGUMENTS, "Too few arguments line %d.", lineno); - status = false; - break; - default: register_error(ECPG_UNSUPPORTED, "Unsupported type %s on line %d.", - ECPGtype_name(type), lineno); + ECPGtype_name(var->type), stmt->lineno); status = false; break; } } + var = var->next; } - type = va_arg(ap, enum ECPGttype); - - if (status && type != ECPGt_EORT) + if (status && var != NULL) { - register_error(ECPG_TOO_MANY_ARGUMENTS, "Too many arguments line %d.", lineno); + register_error(ECPG_TOO_MANY_ARGUMENTS, "Too many arguments line %d.", stmt->lineno); status = false; } @@ -737,35 +801,34 @@ ECPGdo(int lineno, char *query,...) break; case PGRES_EMPTY_QUERY: /* do nothing */ - register_error(ECPG_EMPTY, "Empty query line %d.", lineno); + register_error(ECPG_EMPTY, "Empty query line %d.", stmt->lineno); break; case PGRES_COMMAND_OK: status = true; sqlca.sqlerrd[2] = atol(PQcmdTuples(results)); - ECPGlog("TEST: %s\n", PQcmdTuples(results)); - ECPGlog("ECPGdo line %d Ok: %s\n", lineno, PQcmdStatus(results)); + ECPGlog("ECPGexecute line %d Ok: %s\n", stmt->lineno, PQcmdStatus(results)); break; case PGRES_NONFATAL_ERROR: case PGRES_FATAL_ERROR: case PGRES_BAD_RESPONSE: - ECPGlog("ECPGdo line %d: Error: %s", - lineno, PQerrorMessage(actual_connection->connection)); + ECPGlog("ECPGexecute line %d: Error: %s", + stmt->lineno, PQerrorMessage(actual_connection->connection)); register_error(ECPG_PGSQL, "Error: %s line %d.", - PQerrorMessage(actual_connection->connection), lineno); + PQerrorMessage(actual_connection->connection), stmt->lineno); status = false; break; case PGRES_COPY_OUT: - ECPGlog("ECPGdo line %d: Got PGRES_COPY_OUT ... tossing.\n", lineno); + ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT ... tossing.\n", stmt->lineno); PQendcopy(results->conn); break; case PGRES_COPY_IN: - ECPGlog("ECPGdo line %d: Got PGRES_COPY_IN ... tossing.\n", lineno); + ECPGlog("ECPGexecute line %d: Got PGRES_COPY_IN ... tossing.\n", stmt->lineno); PQendcopy(results->conn); break; default: - ECPGlog("ECPGdo line %d: Got something else, postgres error.\n", - lineno); - register_error(ECPG_PGSQL, "Postgres error line %d.", lineno); + ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n", + stmt->lineno); + register_error(ECPG_PGSQL, "Postgres error line %d.", stmt->lineno); status = false; break; } @@ -775,8 +838,8 @@ ECPGdo(int lineno, char *query,...) notify = PQnotifies(actual_connection->connection); if (notify) { - ECPGlog("ECPGdo line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n", - lineno, notify->relname, notify->be_pid); + ECPGlog("ECPGexecute line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n", + stmt->lineno, notify->relname, notify->be_pid); free(notify); } @@ -784,6 +847,20 @@ ECPGdo(int lineno, char *query,...) return status; } +bool +ECPGdo(int lineno, char *query, ...) +{ + va_list args; + struct statement *stmt; + + va_start(args, query); + if (create_statement(lineno, &stmt, query, args) == false) + return(false); + va_end(args); + + return(ECPGexecute(stmt)); +} + bool ECPGtrans(int lineno, const char * transaction) @@ -940,56 +1017,3 @@ sqlprint(void) sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0'; printf("sql error %s\n", sqlca.sqlerrm.sqlerrmc); } - -/* keep a list of cursors */ -struct cursor *cur = NULL; - -bool ECPGdeclare(int lineno, const char *name, char *command) -{ - struct cursor *ptr; - - for (ptr = cur; ptr != NULL; ptr = ptr->next) - { - if (strcmp(name, ptr->name) == 0) - { - /* re-definition */ - free(ptr->command); - ptr->command = command; - break; - } - } - - if (ptr == NULL) - { - struct cursor *this = (struct cursor *) malloc(sizeof(struct cursor)); - - if (!this) - { - ECPGlog("out of memory\n"); - register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); - return false; - } - /* initial definition */ - this->next = cur; - this->name = name; - this->command = command; - cur = this; - } - - return(true); -} - -bool ECPGopen(int lineno, const char *name) -{ - struct cursor *ptr; - - for (ptr = cur; ptr != NULL; ptr=ptr->next) - { - if (strcmp(ptr->name, name) == 0) - return(ECPGdo(lineno, ptr->command, ECPGt_EOIT, ECPGt_EORT)); - } - - ECPGlog("trying to open undeclared cursor %s\n", name); - register_error(ECPG_UNDECLARED_CURSOR, "trying to open undeclared cursor %s in line %d", name, lineno); - return(false); -} diff --git a/src/interfaces/ecpg/preproc/ecpg.c b/src/interfaces/ecpg/preproc/ecpg.c index f12b799850c..1fe777845b7 100644 --- a/src/interfaces/ecpg/preproc/ecpg.c +++ b/src/interfaces/ecpg/preproc/ecpg.c @@ -23,6 +23,7 @@ extern char *optarg; struct _include_path *include_paths; static int no_auto_trans = 0; +struct cursor *cur = NULL; static void usage(char *progname) @@ -138,6 +139,24 @@ main(int argc, char *const argv[]) { struct cursor *ptr; + /* remove old cursor definitions if any are still there */ + for (ptr = cur; ptr != NULL; ptr=ptr->next) + { + struct arguments *l1, *l2; + + free(ptr->command); + free(ptr->name); + for (l1 = argsinsert; l1; l1 = l2) + { + l2 = l1->next; + free(l1); + } + for (l1 = argsresult; l1; l1 = l2) + { + l2 = l1->next; + free(l1); + } + } /* initialize lex */ lex_init(); diff --git a/src/interfaces/ecpg/preproc/extern.h b/src/interfaces/ecpg/preproc/extern.h index d2eec0c0978..43a15394af3 100644 --- a/src/interfaces/ecpg/preproc/extern.h +++ b/src/interfaces/ecpg/preproc/extern.h @@ -16,6 +16,15 @@ struct _include_path { char * path; extern struct _include_path *include_paths; +struct cursor { char *name; + char *command; + struct arguments * argsinsert; + struct arguments * argsresult; + struct cursor *next; + }; + +extern struct cursor *cur; + /* This is a linked list of the variable names and types. */ struct variable { @@ -28,6 +37,15 @@ struct variable extern struct ECPGtype ecpg_no_indicator; extern struct variable no_indicator; +struct arguments { + struct variable * variable; + struct variable * indicator; + struct arguments * next; +}; + +extern struct arguments * argsinsert; +extern struct arguments * argsresult; + /* functions */ extern void lex_init(void); diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index 290440a39f5..4d6960e406b 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -245,14 +245,9 @@ remove_variables(int brace_level) * These are of two kinds: input and output. * I will make two lists for them. */ -struct arguments { - struct variable * variable; - struct variable * indicator; - struct arguments * next; -}; -static struct arguments * argsinsert = NULL; -static struct arguments * argsresult = NULL; +struct arguments * argsinsert = NULL; +struct arguments * argsresult = NULL; static void reset_variables(void) @@ -279,7 +274,7 @@ add_variable(struct arguments ** list, struct variable * var, struct variable * deletes the list as we go on. */ static void -dump_variables(struct arguments * list) +dump_variables(struct arguments * list, int mode) { if (list == NULL) { @@ -290,7 +285,7 @@ dump_variables(struct arguments * list) end of the list: */ - dump_variables(list->next); + dump_variables(list->next, mode); /* Then the current element and its indicator */ ECPGdump_a_type(yyout, list->variable->name, list->variable->type, @@ -298,7 +293,8 @@ dump_variables(struct arguments * list) (list->indicator->type->typ != ECPGt_NO_INDICATOR) ? list->indicator->type : NULL, NULL, NULL); /* Then release the list element. */ - free(list); + if (mode != 0) + free(list); } static void @@ -494,9 +490,9 @@ output_statement(char * stmt, int mode) fputs("\", ", yyout); /* dump variables to C file*/ - dump_variables(argsinsert); + dump_variables(argsinsert, 1); fputs("ECPGt_EOIT, ", yyout); - dump_variables(argsresult); + dump_variables(argsresult, 1); fputs("ECPGt_EORT);", yyout); whenever_action(mode); free(stmt); @@ -737,10 +733,9 @@ stmt: AddAttrStmt { output_statement($1, 0); } | RenameStmt { output_statement($1, 0); } | RevokeStmt { output_statement($1, 0); } | OptimizableStmt { - if (strncmp($1, "ECPGdeclare" , sizeof("ECPGdeclare")-1) == 0) + if (strncmp($1, "/* " , sizeof("/* ")-1) == 0) { fputs($1, yyout); - whenever_action(0); free($1); } else @@ -775,7 +770,27 @@ stmt: AddAttrStmt { output_statement($1, 0); } whenever_action(0); free($1); } - | ECPGOpen { fprintf(yyout, "ECPGopen(__LINE__, %s);", $1); + | ECPGOpen { + struct cursor *ptr; + + for (ptr = cur; ptr != NULL; ptr=ptr->next) + { + if (strcmp(ptr->name, $1) == 0) + break; + } + + if (ptr == NULL) + { + sprintf(errortext, "trying to open undeclared cursor %s\n", $1); + yyerror(errortext); + } + + fprintf(yyout, "ECPGdo(__LINE__, \"%s\",", ptr->command); + /* dump variables to C file*/ + dump_variables(ptr->argsinsert, 0); + fputs("ECPGt_EOIT, ", yyout); + dump_variables(ptr->argsresult, 0); + fputs("ECPGt_EORT);", yyout); whenever_action(0); free($1); } @@ -2359,7 +2374,31 @@ CursorStmt: DECLARE name opt_binary CURSOR FOR group_clause having_clause union_clause sort_clause { - $$ = make5_str(make1_str("ECPGdeclare(__LINE__, \""), $2, make1_str("\", \""), cat4_str(cat5_str(cat5_str(make1_str("declare"), strdup($2), $3, make1_str("cursor for select"), $7), $8, $9, $10, $11), $12, $13, $14), make1_str("\");")); + struct cursor *ptr, *this; + + for (ptr = cur; ptr != NULL; ptr = ptr->next) + { + if (strcmp($2, ptr->name) == 0) + { + /* re-definition is a bug*/ + sprintf(errortext, "cursor %s already defined", $2); + yyerror(errortext); + } + } + + this = (struct cursor *) mm_alloc(sizeof(struct cursor)); + + /* initial definition */ + this->next = cur; + this->name = $2; + this->command = cat4_str(cat5_str(cat5_str(make1_str("declare"), strdup($2), $3, make1_str("cursor for select"), $7), $8, $9, $10, $11), $12, $13, $14); + this->argsinsert = argsinsert; + this->argsresult = argsresult; + argsinsert = argsresult = NULL; + + cur = this; + + $$ = cat3_str(make1_str("/*"), strdup(this->command), make1_str("*/")); } ; @@ -4221,7 +4260,7 @@ execstring: cvariable | * open is an open cursor, at the moment this has to be removed */ ECPGOpen: SQL_OPEN name open_opts { - $$ = make3_str(make1_str("\""), $2, make1_str("\"")); + $$ = $2; }; open_opts: /* empty */ { $$ = make1_str(""); } diff --git a/src/interfaces/ecpg/test/Makefile b/src/interfaces/ecpg/test/Makefile index 8a7f1f69df4..4e19502fca9 100644 --- a/src/interfaces/ecpg/test/Makefile +++ b/src/interfaces/ecpg/test/Makefile @@ -1,6 +1,6 @@ all: test1 test2 perftest -LDFLAGS=-g -I ../include -I ../../libpq -L../lib -lecpg -L../../libpq -lpq -lcrypt --static +LDFLAGS=-g -I ../include -I ../../libpq -L../lib -lecpg -L../../libpq -lpq -lcrypt test1: test1.c test1.c: test1.pgc diff --git a/src/interfaces/ecpg/test/test2.pgc b/src/interfaces/ecpg/test/test2.pgc index 1506dd7bfa9..89d938730b8 100644 --- a/src/interfaces/ecpg/test/test2.pgc +++ b/src/interfaces/ecpg/test/test2.pgc @@ -19,6 +19,10 @@ exec sql begin declare section; long ind_married; char married[9]; exec sql end declare section; + +exec sql declare cur cursor for + select name, born, age, married from meskes; + char msg[128], command[128]; FILE *dbgs; @@ -26,7 +30,7 @@ exec sql end declare section; ECPGdebug(1, dbgs); strcpy(msg, "connect"); - exec sql connect to tcp:postgresql://localhost:5432/mm; + exec sql connect to tcp:postgresql://localhost:5432/mm; strcpy(msg, "create"); exec sql create table meskes(name char(8), born integer, age smallint, married char(8)); @@ -41,10 +45,6 @@ exec sql end declare section; strcpy(msg, "commit"); exec sql commit; - strcpy(msg, "declare"); - exec sql declare cur cursor for - select name, born, age, married from meskes; - strcpy(msg, "open"); exec sql open cur; -- GitLab