/* Copyright comment */ /* * The aim is to get a simpler inteface to the database routines. * All the tidieous messing around with tuples is supposed to be hidden * by this function. */ /* Author: Linus Tolke (actually most if the code is "borrowed" from the distribution and just slightly modified) */ /* Taken over as part of PostgreSQL by Michael Meskes on Feb. 5th, 1998 */ #include #include #include #include #include #include #include #include #include #include #include /* variables visible to the programs */ int no_auto_trans; static struct sqlca sqlca_init = { {'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '}, sizeof(struct sqlca), 0, {0, {0}}, {'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0} }; struct sqlca sqlca = { {'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '}, sizeof(struct sqlca), 0, {0, {0}}, {'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0} }; static struct connection { char *name; PGconn *connection; int committed; struct connection *next; } *all_connections = NULL, *actual_connection = NULL; struct variable { enum ECPGttype type; void *value; void *pointer; 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 connection *connection; struct variable *inlist; struct variable *outlist; }; struct prepared_statement { char *name; struct statement *stmt; struct prepared_statement *next; } *prep_stmts = NULL; struct auto_mem { void *pointer; struct auto_mem *next; } *auto_allocs = NULL; static int simple_debug = 0; static FILE *debugstream = NULL; static void register_error(long code, char *fmt,...) { va_list args; struct auto_mem *am; sqlca.sqlcode = code; va_start(args, fmt); vsprintf(sqlca.sqlerrm.sqlerrmc, fmt, args); va_end(args); sqlca.sqlerrm.sqlerrml = strlen(sqlca.sqlerrm.sqlerrmc); /* free all memory we allocate for the user */ for (am = auto_allocs; am;) { struct auto_mem *act = am; am = am->next; free(act->pointer); free(act); } auto_allocs = NULL; } static struct connection * get_connection(const char *connection_name) { struct connection *con = all_connections;; if (connection_name == NULL || strcmp(connection_name, "CURRENT") == 0) return actual_connection; for (; con && strcmp(connection_name, con->name) != 0; con = con->next); if (con) return con; else return NULL; } static void ECPGfinish(struct connection * act) { if (act != NULL) { ECPGlog("ECPGfinish: finishing %s.\n", act->name); PQfinish(act->connection); /* remove act from the list */ if (act == all_connections) { all_connections = act->next; free(act->name); free(act); } else { struct connection *con; for (con = all_connections; con->next && con->next != act; con = con->next); if (con->next) { con->next = act->next; free(act->name); free(act); } } if (actual_connection == act) actual_connection = all_connections; } else ECPGlog("ECPGfinish: called an extra time.\n"); } static char * ecpg_alloc(long size, int lineno) { char *new = (char *) malloc(size); if (!new) { ECPGlog("out of memory\n"); register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); return NULL; } memset(new, '\0', size); return (new); } static char * ecpg_strdup(const char *string, int lineno) { char *new = strdup(string); if (!new) { ECPGlog("out of memory\n"); register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); return NULL; } return (new); } static void add_mem(void *ptr, int lineno) { struct auto_mem *am = (struct auto_mem *) ecpg_alloc(sizeof(struct auto_mem), lineno); am->next = auto_allocs; auto_allocs = am; } /* This function returns a newly malloced string that has the ' and \ in the argument quoted with \. */ static char * quote_postgres(char *arg, int lineno) { char *res = (char *) ecpg_alloc(2 * strlen(arg) + 1, lineno); int i, ri; if (!res) return (res); for (i = 0, ri = 0; arg[i]; i++, ri++) { switch (arg[i]) { case '\'': case '\\': res[ri++] = '\\'; default: ; } res[ri] = arg[i]; } res[ri] = '\0'; return res; } /* This function returns a newly malloced string that has the \ in the strings inside the argument quoted with another \. */ static char * quote_strings(char *arg, int lineno) { char *res = (char *) ecpg_alloc(2 * strlen(arg) + 1, lineno); int i, ri; bool string = false; if (!res) return (res); for (i = 0, ri = 0; arg[i]; i++, ri++) { switch (arg[i]) { case '\'': string = string ? false : true; break; case '\\': res[ri++] = '\\'; default: ; } res[ri] = arg[i]; } res[ri] = '\0'; return res; } /* * create a list of variables * The variables are listed with input variables preceeding outputvariables * The end of each group is marked by an end marker. * per variable we list: * type - as defined in ecpgtype.h * value - where to store the data * varcharsize - length of string in case we have a stringvariable, else 0 * arraysize - 0 for pointer (we don't know the size of the array), * 1 for simple variable, size for arrays * offset - offset between ith and (i+1)th entry in an array, * normally that means sizeof(type) * ind_type - type of indicator variable * ind_value - pointer to indicator variable * ind_varcharsize - empty * ind_arraysize - arraysize of indicator array * ind_offset - indicator offset */ static bool create_statement(int lineno, struct connection *connection, struct statement ** stmt, char *query, va_list ap) { struct variable **list = &((*stmt)->inlist); enum ECPGttype type; if (!(*stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno))) return false; (*stmt)->command = query; (*stmt)->connection = connection; (*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; if (!(var = (struct variable *) ecpg_alloc(sizeof(struct variable), lineno))) return false; var->type = type; var->pointer = va_arg(ap, void *); /* if variable is NULL, the statement hasn't been prepared */ if (var->pointer == NULL) { ECPGlog("create_statement: invalid statement name\n"); register_error(ECPG_INVALID_STMT, "Invalid statement name in line %d", lineno); free(var); return false; } var->varcharsize = va_arg(ap, long); var->arrsize = va_arg(ap, long); var->offset = va_arg(ap, long); if (var->arrsize == 0 || var->varcharsize == 0) var->value = *((void **)(var->pointer)); else var->value = var->pointer; 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 char * next_insert(char *text) { char *ptr = text; bool string = false; for (; ptr[1] != '\0' && (ptr[0] != ';' || ptr[1] != ';' || string); ptr++) if (ptr[0] == '\'') string = string ? false : true; return (ptr[1] == '\0') ? NULL : ptr; } static bool ECPGexecute(struct statement * stmt) { bool status = false; char *copiedquery; PGresult *results; PGnotify *notify; struct variable *var; memcpy((char *) &sqlca, (char *) &sqlca_init, sizeof(sqlca)); copiedquery = ecpg_strdup(stmt->command, stmt->lineno); /* * 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. */ var = stmt->inlist; while (var) { char *newcopy; char *mallocedval = NULL; char *tobeinserted = NULL; char *p; char buff[20]; /* * Some special treatment is needed for records since we want * their contents to arrive in a comma-separated list on insert (I * think). */ buff[0] = '\0'; /* check for null value and set input buffer accordingly */ switch (var->ind_type) { case ECPGt_short: case ECPGt_unsigned_short: if (*(short *) var->ind_value < 0) strcpy(buff, "null"); break; case ECPGt_int: case ECPGt_unsigned_int: if (*(int *) var->ind_value < 0) strcpy(buff, "null"); break; case ECPGt_long: case ECPGt_unsigned_long: if (*(long *) var->ind_value < 0L) strcpy(buff, "null"); break; default: break; } if (*buff == '\0') { switch (var->type) { case ECPGt_short: case ECPGt_int: sprintf(buff, "%d", *(int *) var->value); tobeinserted = buff; break; case ECPGt_unsigned_short: case ECPGt_unsigned_int: sprintf(buff, "%d", *(unsigned int *) var->value); tobeinserted = buff; break; case ECPGt_long: sprintf(buff, "%ld", *(long *) var->value); tobeinserted = buff; break; case ECPGt_unsigned_long: sprintf(buff, "%ld", *(unsigned long *) var->value); tobeinserted = buff; break; case ECPGt_float: sprintf(buff, "%.14g", *(float *) var->value); tobeinserted = buff; break; case ECPGt_double: sprintf(buff, "%.14g", *(double *) var->value); tobeinserted = buff; break; case ECPGt_bool: sprintf(buff, "'%c'", (*(char *) var->value ? 't' : 'f')); tobeinserted = buff; break; case ECPGt_char: case ECPGt_unsigned_char: { /* set slen to string length if type is char * */ int slen = (var->varcharsize == 0) ? strlen((char *) var->value) : var->varcharsize; char *tmp; if (!(newcopy = ecpg_alloc(slen + 1, stmt->lineno))) return false; strncpy(newcopy, (char *) var->value, slen); newcopy[slen] = '\0'; if (!(mallocedval = (char *) ecpg_alloc(2 * strlen(newcopy) + 3, stmt->lineno))) return false; strcpy(mallocedval, "'"); tmp = quote_postgres(newcopy, stmt->lineno); if (!tmp) return false; strcat(mallocedval, tmp); strcat(mallocedval, "'"); free(newcopy); tobeinserted = mallocedval; } break; case ECPGt_char_variable: { int slen = strlen((char *) var->value); char *tmp; if (!(newcopy = ecpg_alloc(slen + 1, stmt->lineno))) return false; strncpy(newcopy, (char *) var->value, slen); newcopy[slen] = '\0'; if (!(mallocedval = (char *) ecpg_alloc(2 * strlen(newcopy) + 1, stmt->lineno))) return false; tmp = quote_strings(newcopy, stmt->lineno); if (!tmp) return false; strcat(mallocedval, tmp); free(newcopy); tobeinserted = mallocedval; } break; case ECPGt_varchar: { struct ECPGgeneric_varchar *variable = (struct ECPGgeneric_varchar *) (var->value); char *tmp; if (!(newcopy = (char *) ecpg_alloc(variable->len + 1, stmt->lineno))) return false; strncpy(newcopy, variable->arr, variable->len); newcopy[variable->len] = '\0'; if (!(mallocedval = (char *) ecpg_alloc(2 * strlen(newcopy) + 3, stmt->lineno))) return false; strcpy(mallocedval, "'"); tmp = quote_postgres(newcopy, stmt->lineno); if (!tmp) return false; strcat(mallocedval, tmp); strcat(mallocedval, "'"); free(newcopy); tobeinserted = mallocedval; } break; default: /* Not implemented yet */ register_error(ECPG_UNSUPPORTED, "Unsupported type %s on line %d.", ECPGtype_name(var->type), stmt->lineno); return false; break; } } else tobeinserted = buff; /* * Now tobeinserted points to an area that is to be inserted at * the first %s */ if (!(newcopy = (char *) ecpg_alloc(strlen(copiedquery) + strlen(tobeinserted) + 1, stmt->lineno))) return false; strcpy(newcopy, copiedquery); if ((p = next_insert(newcopy)) == NULL) { /* * 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.", stmt->lineno); return false; } else { strcpy(p, 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 (mallocedval != NULL) { free(mallocedval); mallocedval = NULL; } free(copiedquery); copiedquery = newcopy; var = var->next; } /* Check if there are unmatched things left. */ if (next_insert(copiedquery) != NULL) { register_error(ECPG_TOO_FEW_ARGUMENTS, "Too few arguments line %d.", stmt->lineno); return false; } /* Now the request is built. */ if (stmt->connection->committed && !no_auto_trans) { if ((results = PQexec(stmt->connection->connection, "begin transaction")) == NULL) { register_error(ECPG_TRANS, "Error starting transaction line %d.", 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); free(copiedquery); if (results == NULL) { ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, PQerrorMessage(stmt->connection->connection)); register_error(ECPG_PGSQL, "Postgres error: %s line %d.", PQerrorMessage(stmt->connection->connection), stmt->lineno); } else { sqlca.sqlerrd[2] = 0; var = stmt->outlist; switch (PQresultStatus(results)) { int nfields, ntuples, act_tuple, act_field; case PGRES_TUPLES_OK: nfields = PQnfields(results); sqlca.sqlerrd[2] = ntuples = PQntuples(results); status = true; if (ntuples < 1) { 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; } for (act_field = 0; act_field < nfields && status; act_field++) { char *pval; char *scan_length; 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 ((var->arrsize > 0 && ntuples > var->arrsize) || (var->ind_arrsize > 0 && ntuples > var->ind_arrsize)) { 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; } /* * allocate memory for NULL pointers */ if (var->arrsize == 0 || var->varcharsize == 0) { switch(var->type) { case ECPGt_char: case ECPGt_unsigned_char: if (var->value == NULL) { var->varcharsize = 0; /* check strlen for each tuple */ for (act_tuple = 0; act_tuple < ntuples; act_tuple++) { int len = strlen(PQgetvalue(results, act_tuple, act_field)); if (len > var->varcharsize) var->varcharsize = len; } var->offset *= var->varcharsize; add_mem((void *)(var->value) = *((void **)(var->pointer)) = (void *) ecpg_alloc(var->offset * ntuples, stmt->lineno), stmt->lineno); } break; #if 0 case ECPGt_varchar: if (((struct ECPGgeneric_varchar *)var->value)->arr == NULL) { var->varcharsize = 0; /* check strlen for each tuple */ for (act_tuple = 0; act_tuple < ntuples; act_tuple++) { int len = strlen(PQgetvalue(results, act_tuple, act_field)); if (len > var->varcharsize) var->varcharsize = len; ((struct ECPGgeneric_varchar *) ((long) var->value + var->offset * act_tuple))->arr = (char *) ecpg_alloc(len, stmt->lineno); } } break; #endif default: if (var->value == NULL) add_mem((void *)(var->value) = *((void **)(var->pointer)) = (void *) ecpg_alloc(var->offset * ntuples, stmt->lineno), stmt->lineno); break; } } for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++) { pval = PQgetvalue(results, act_tuple, act_field); 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 */ /* * check for null value and set indicator * accordingly */ switch (var->ind_type) { case ECPGt_short: case ECPGt_unsigned_short: ((short *) var->ind_value)[act_tuple] = -PQgetisnull(results, act_tuple, act_field); break; case ECPGt_int: case ECPGt_unsigned_int: ((int *) var->ind_value)[act_tuple] = -PQgetisnull(results, act_tuple, act_field); break; case ECPGt_long: case ECPGt_unsigned_long: ((long *) var->ind_value)[act_tuple] = -PQgetisnull(results, act_tuple, act_field); break; default: break; } switch (var->type) { long res; unsigned long ures; double dres; case ECPGt_short: case ECPGt_int: case ECPGt_long: if (pval) { res = strtol(pval, &scan_length, 10); if (*scan_length != '\0') /* Garbage left */ { register_error(ECPG_INT_FORMAT, "Not correctly formatted int type: %s line %d.", pval, stmt->lineno); status = false; res = 0L; } } else res = 0L; /* Again?! Yes */ switch (var->type) { case ECPGt_short: ((short *) var->value)[act_tuple] = (short) res; break; case ECPGt_int: ((int *) var->value)[act_tuple] = (int) res; break; case ECPGt_long: ((long *) var->value)[act_tuple] = res; break; default: /* Cannot happen */ break; } break; case ECPGt_unsigned_short: case ECPGt_unsigned_int: case ECPGt_unsigned_long: if (pval) { ures = strtoul(pval, &scan_length, 10); if (*scan_length != '\0') /* Garbage left */ { register_error(ECPG_UINT_FORMAT, "Not correctly formatted unsigned type: %s line %d.", pval, stmt->lineno); status = false; ures = 0L; } } else ures = 0L; /* Again?! Yes */ switch (var->type) { case ECPGt_unsigned_short: ((unsigned short *) var->value)[act_tuple] = (unsigned short) ures; break; case ECPGt_unsigned_int: ((unsigned int *) var->value)[act_tuple] = (unsigned int) ures; break; case ECPGt_unsigned_long: ((unsigned long *) var->value)[act_tuple] = ures; break; default: /* Cannot happen */ break; } break; case ECPGt_float: case ECPGt_double: if (pval) { dres = strtod(pval, &scan_length); if (*scan_length != '\0') /* Garbage left */ { register_error(ECPG_FLOAT_FORMAT, "Not correctly formatted floating point type: %s line %d.", pval, stmt->lineno); status = false; dres = 0.0; } } else dres = 0.0; /* Again?! Yes */ switch (var->type) { case ECPGt_float: ((float *) var->value)[act_tuple] = dres; break; case ECPGt_double: ((double *) var->value)[act_tuple] = dres; break; default: /* Cannot happen */ break; } break; case ECPGt_bool: if (pval) { if (pval[0] == 'f' && pval[1] == '\0') { ((char *) var->value)[act_tuple] = false; break; } else if (pval[0] == 't' && pval[1] == '\0') { ((char *) var->value)[act_tuple] = true; break; } } register_error(ECPG_CONVERT_BOOL, "Unable to convert %s to bool on line %d.", (pval ? pval : "NULL"), stmt->lineno); status = false; break; case ECPGt_char: case ECPGt_unsigned_char: { strncpy((char *) ((long) var->value + var->offset * act_tuple), pval, var->varcharsize); if (var->varcharsize && var->varcharsize < strlen(pval)) { /* truncation */ switch (var->ind_type) { case ECPGt_short: case ECPGt_unsigned_short: ((short *) var->ind_value)[act_tuple] = var->varcharsize; break; case ECPGt_int: case ECPGt_unsigned_int: ((int *) var->ind_value)[act_tuple] = var->varcharsize; break; case ECPGt_long: case ECPGt_unsigned_long: ((long *) var->ind_value)[act_tuple] = var->varcharsize; break; default: break; } sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W'; } } break; case ECPGt_varchar: { struct ECPGgeneric_varchar *variable = (struct ECPGgeneric_varchar *) ((long) var->value + var->offset * act_tuple); if (var->varcharsize == 0) strncpy(variable->arr, pval, strlen(pval)); else strncpy(variable->arr, pval, var->varcharsize); variable->len = strlen(pval); if (var->varcharsize > 0 && variable->len > var->varcharsize) { /* truncation */ switch (var->ind_type) { case ECPGt_short: case ECPGt_unsigned_short: ((short *) var->ind_value)[act_tuple] = var->varcharsize; break; case ECPGt_int: case ECPGt_unsigned_int: ((int *) var->ind_value)[act_tuple] = var->varcharsize; break; case ECPGt_long: case ECPGt_unsigned_long: ((long *) var->ind_value)[act_tuple] = var->varcharsize; break; default: break; } sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W'; variable->len = var->varcharsize; } } break; default: register_error(ECPG_UNSUPPORTED, "Unsupported type %s on line %d.", ECPGtype_name(var->type), stmt->lineno); status = false; break; } } var = var->next; } if (status && var != NULL) { register_error(ECPG_TOO_MANY_ARGUMENTS, "Too many arguments line %d.", stmt->lineno); status = false; } PQclear(results); break; case PGRES_EMPTY_QUERY: /* do nothing */ register_error(ECPG_EMPTY, "Empty query line %d.", stmt->lineno); break; case PGRES_COMMAND_OK: status = true; sqlca.sqlerrd[2] = atol(PQcmdTuples(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("ECPGexecute line %d: Error: %s", stmt->lineno, PQerrorMessage(stmt->connection->connection)); register_error(ECPG_PGSQL, "Error: %s line %d.", PQerrorMessage(stmt->connection->connection), stmt->lineno); status = false; break; case PGRES_COPY_OUT: ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT ... tossing.\n", stmt->lineno); PQendcopy(stmt->connection->connection); break; 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); register_error(ECPG_PGSQL, "Postgres error line %d.", stmt->lineno); status = false; break; } } /* check for asynchronous returns */ notify = PQnotifies(stmt->connection->connection); if (notify) { ECPGlog("ECPGexecute line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n", stmt->lineno, notify->relname, notify->be_pid); free(notify); } return status; } bool ECPGdo(int lineno, const char *connection_name, char *query,...) { va_list args; struct statement *stmt; struct connection *con = get_connection(connection_name); if (con == NULL) { register_error(ECPG_NO_CONN, "No such connection %s in line %d", connection_name, lineno); return (false); } va_start(args, query); if (create_statement(lineno, con, &stmt, query, args) == false) return (false); va_end(args); /* are we connected? */ if (con == NULL || con->connection == NULL) { ECPGlog("ECPGdo: not connected to %s\n", con->name); register_error(ECPG_NOT_CONN, "Not connected in line %d", lineno); return false; } return (ECPGexecute(stmt)); } bool ECPGtrans(int lineno, const char *connection_name, const char *transaction) { PGresult *res; struct connection *con = get_connection(connection_name); if (con == NULL) { register_error(ECPG_NO_CONN, "No such connection %s in line %d", connection_name, lineno); return (false); } ECPGlog("ECPGtrans line %d action = %s connection = %s\n", lineno, transaction, con->name); /* if we have no connection we just simulate the command */ if (con && con->connection) { if ((res = PQexec(con->connection, transaction)) == NULL) { register_error(ECPG_TRANS, "Error in transaction processing line %d.", lineno); return FALSE; } PQclear(res); } if (strcmp(transaction, "commit") == 0 || strcmp(transaction, "rollback") == 0) { struct prepared_statement *this; con->committed = true; /* deallocate all prepared statements */ for (this = prep_stmts; this != NULL; this = this->next) { bool b = ECPGdeallocate(lineno, this->name); if (!b) return false; } } return true; } bool ECPGsetconn(int lineno, const char *connection_name) { struct connection *con = get_connection(connection_name); if (con) { actual_connection = con; return true; } else { register_error(ECPG_NO_CONN, "No such connection %s in line %d", connection_name, lineno); return false; } } bool ECPGconnect(int lineno, const char *dbname, const char *user, const char *passwd, const char *connection_name) { struct connection *this = (struct connection *) ecpg_alloc(sizeof(struct connection), lineno); if (!this) return false; if (dbname == NULL && connection_name == NULL) connection_name = "DEFAULT"; /* add connection to our list */ if (connection_name != NULL) this->name = ecpg_strdup(connection_name, lineno); else this->name = ecpg_strdup(dbname, lineno); if (all_connections == NULL) this->next = NULL; else this->next = all_connections; actual_connection = all_connections = this; ECPGlog("ECPGconnect: opening database %s %s%s\n", dbname ? dbname : "NULL", user ? "for user " : "", user ? user : ""); sqlca.sqlcode = 0; this->connection = PQsetdbLogin(NULL, NULL, NULL, NULL, dbname, user, passwd); if (PQstatus(this->connection) == CONNECTION_BAD) { ECPGfinish(this); ECPGlog("connect: could not open database %s %s%s in line %d\n", dbname ? dbname : "NULL", user ? "for user " : "", user ? user : "", lineno); register_error(ECPG_CONNECT, "connect: could not open database %s.", dbname ? dbname : "NULL"); return false; } this->committed = true; return true; } bool ECPGdisconnect(int lineno, const char *connection_name) { struct connection *con; if (strcmp(connection_name, "ALL") == 0) { for (con = all_connections; con;) { struct connection *f = con; con = con->next; ECPGfinish(f); } } else { con = get_connection(connection_name); if (con == NULL) { ECPGlog("disconnect: not connected to connection %s\n", connection_name); register_error(ECPG_NO_CONN, "No such connection %s in line %d", connection_name, lineno); return false; } else ECPGfinish(con); } return true; } void ECPGdebug(int n, FILE *dbgs) { simple_debug = n; debugstream = dbgs; ECPGlog("ECPGdebug: set to %d\n", simple_debug); } void ECPGlog(const char *format,...) { va_list ap; if (simple_debug) { char *f = (char *) malloc(strlen(format) + 100); if (!f) return; sprintf(f, "[%d]: %s", (int) getpid(), format); va_start(ap, format); vfprintf(debugstream, f, ap); va_end(ap); free(f); } } /* print out an error message */ void sqlprint(void) { sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0'; printf("sql error %s\n", sqlca.sqlerrm.sqlerrmc); } static bool isvarchar(unsigned char c) { if (isalnum(c)) return true; if (c == '_' || c == '>' || c == '-' || c == '.') return true; if (c >= 128) return true; return(false); } static void replace_variables(char *text) { char *ptr = text; bool string = false; for (; *ptr != '\0'; ptr++) { if (*ptr == '\'') string = string ? false : true; if (!string && *ptr == ':') { ptr[0] = ptr[1] = ';'; for (ptr += 2; *ptr && isvarchar(*ptr); ptr++) *ptr = ' '; } } } /* handle the EXEC SQL PREPARE statement */ bool ECPGprepare(int lineno, char *name, char *variable) { struct statement *stmt; struct prepared_statement *this; /* check if we already have prepared this statement */ for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next); if (this) { bool b = ECPGdeallocate(lineno, name); if (!b) return false; } this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno); if (!this) return false; stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno); if (!stmt) { free(this); return false; } /* create statement */ stmt->lineno = lineno; stmt->connection = NULL; stmt->command = ecpg_strdup(variable, lineno); stmt->inlist = stmt->outlist = NULL; /* if we have C variables in our statment replace them with ';;' */ replace_variables(stmt->command); /* add prepared statement to our list */ this->name = ecpg_strdup(name, lineno); this->stmt = stmt; if (prep_stmts == NULL) this->next = NULL; else this->next = prep_stmts; prep_stmts = this; return true; } /* handle the EXEC SQL DEALLOCATE PREPARE statement */ bool ECPGdeallocate(int lineno, char *name) { struct prepared_statement *this, *prev; /* check if we really have prepared this statement */ for (this = prep_stmts, prev = NULL; this != NULL && strcmp(this->name, name) != 0; prev = this, this = this->next); if (this) { /* okay, free all the resources */ free(this->name); free(this->stmt->command); free(this->stmt); if (prev != NULL) prev->next = this->next; else prep_stmts = this->next; return true; } ECPGlog("deallocate_prepare: invalid statement name %s\n", name); register_error(ECPG_INVALID_STMT, "Invalid statement name %s in line %d", name, lineno); return false; } /* return the prepared statement */ char * ECPGprepared_statement(char *name) { struct prepared_statement *this; for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next); return (this) ? this->stmt->command : NULL; }