*** src/interfaces/ecpg/ecpglib/execute.c.orig 2007-04-26 23:56:11.000000000 -0700 --- src/interfaces/ecpg/ecpglib/execute.c 2007-05-10 14:14:16.000000000 -0700 *************** *** 31,36 **** --- 31,70 ---- #include "pgtypes_timestamp.h" #include "pgtypes_interval.h" + #include + #include + + void ECPGdispCacheStats(void); + void ECPGdispCache(void); + + typedef struct + { char id[8]; + int lineno; + char stmtID[32]; + char *ecpgQuery; + char *pqQuery; + long execs; /* # of executions */ + double execTime; /* time to execute prepared statement */ + PGconn *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 long long stmtCacheExecs = 0; + static long long stmtCachePrepares = 0; + static long long stmtCacheFrees = 0; + static long long stmtCacheInserts = 0; + + static double totalPrepareTime = 0.0; + static double totalExecTime = 0.0; + static double totalSearchTime = 0.0; + static double totalHashTime = 0.0; + static double totalInsertTime = 0.0; + static double totalFreeTime = 0.0; + /* * This function returns a newly malloced string that has ' and \ * escaped. *************** *** 1381,1386 **** --- 1415,2104 ---- return status; } + /* + * display SQL cache + */ + void + ECPGdispCache(void) + { + int bucketNo, ix, entNo; + stmtCacheEntry *entry; + + for(bucketNo = 1; bucketNo < stmtCacheNBuckets + 1; ++bucketNo) + { printf("-- bucket # %5.5d\n", bucketNo); + for(ix = 0; ix < stmtCacheEntPerBucket; ++ix) + { entNo = bucketNo * stmtCacheEntPerBucket + ix; + entry = &stmtCacheEntries[entNo]; + if(entry->stmtID[0]) + printf(" entry # %d: %s\n", entNo, entry->ecpgQuery); + } + } + fflush(stdout); + } + + /* + * display SQL cache statistics + */ + void + ECPGdispCacheStats(void) + { + int bucketNo, ix, entNo, fillCount, fillMax, noCacheEntries, leng, histo[80]; + char timeStr[50]; + time_t clock; + FILE *otFile; + + otFile = stdout; + + /* display title line with date/time */ + clock = time(0); + strcpy(timeStr, ctime(&clock)); + leng = strlen(timeStr); + if(leng && timeStr[leng - 1] < ' ') + timeStr[leng - 1] = '\0'; + fprintf(otFile, "\n"); + fprintf(otFile, "---- %s ----------------------------\n", timeStr); + + /* gather statistics to display */ + noCacheEntries = 0; + fillMax = 0; + memset(histo, 0, sizeof(histo)); + for(bucketNo = 1; bucketNo < stmtCacheNBuckets + 1; ++bucketNo) + { fillCount = 0; + for(ix = 0; ix < stmtCacheEntPerBucket; ++ix) + { entNo = bucketNo * stmtCacheEntPerBucket + ix; + if(stmtCacheEntries[entNo].stmtID[0]) + ++fillCount; + } + ++histo[fillCount]; /* incr count of buckets w/ this many entries */ + noCacheEntries += fillCount; /* incr total # of entries used */ + if(fillCount > fillMax) /* get value of max cache fill */ + fillMax = fillCount; + } + + /* display the statistics */ + fprintf(otFile, "-- # of cache buckets - %d\n", stmtCacheNBuckets); + fprintf(otFile, "-- # of entries per bucket - %d\n", stmtCacheEntPerBucket); + fprintf(otFile, "-- # of SQL statements in cache - %d\n", noCacheEntries); + fprintf(otFile, "-- # 'insert cache entry' execs - %lld\n", stmtCacheInserts); + fprintf(otFile, "-- # 'free cache entry' execs - %lld\n", stmtCacheFrees); + fprintf(otFile, "--\n"); + for(ix = 0; ix <= fillMax; ++ix) + fprintf(otFile, " %6d buckets contain %d entries\n", histo[ix], ix); + + fprintf(otFile, "-- total # of statements executed - %lld\n", stmtCacheExecs); + fprintf(otFile, "-- # of statements prepared - %lld\n", stmtCachePrepares); + + fprintf(otFile, "-- total 'prepare statement' time %16.6f seconds\n", totalPrepareTime); + fprintf(otFile, "-- total 'execute statement' time %16.6f seconds\n", totalExecTime); + fprintf(otFile, "-- total 'hash statement' time %16.6f seconds\n", totalHashTime); + fprintf(otFile, "-- total 'search cache' time %16.6f seconds\n", totalSearchTime); + fprintf(otFile, "-- total 'insert cache entry' time %16.6f seconds\n", totalInsertTime); + fprintf(otFile, "-- total 'free cache entry' time %16.6f seconds\n", totalFreeTime); + } + + /* + * Build a SQL statement for the server - replace '?' variable placeholders in + * a query from ECPG with '$n' placeholders required by the postgres server. + * Returns # of host variables replaced OR negative error code + */ + static int + ECPGbuildPQstmt(int lineno, + char *ecpgQuery, /* input ECPG format query */ + char **retQuery) /* returned new PQ format query */ + { int varNo, varNameLeng; + char *pqQuery, *hv, varName[20]; + + if(!(pqQuery = (char *) ECPGalloc(strlen(ecpgQuery) + 1000, lineno))) + return(-1); + strcpy(pqQuery, ecpgQuery); + + for(varNo = 0; varNo < 10000; ++varNo) + { + if((hv = next_insert(pqQuery)) == 0) + break; + sprintf(varName, "$%d", varNo + 1); + varNameLeng = strlen(varName); + memmove(hv + varNameLeng, hv + 1, strlen(hv + 1)); + memcpy(hv, varName, varNameLeng); + } + *retQuery = pqQuery; + + return(varNo); + } + + /* + * hash a SQL statement - returns entry # of first entry in the bucket + */ + static int + ECPGhashStmt(char *ecpgQuery, /* statement to hash */ + int *retBucketNo) /* returned bucket # */ + { + int stmtIx, bucketNo, entryNo, hashLeng, stmtLeng; + long long hashVal, rotVal; + double lapseHashTime; + struct timeval startHashTime, endHashTime; + + gettimeofday(&startHashTime, (struct timezone *)0); + + 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 */ + entryNo = bucketNo * stmtCacheEntPerBucket; /* get entry # */ + + gettimeofday(&endHashTime, (struct timezone *)0); + lapseHashTime = (((double) endHashTime.tv_sec) + (((double) endHashTime.tv_usec) / 1000000)) + - (((double) startHashTime.tv_sec) + (((double) startHashTime.tv_usec) / 1000000)); + totalHashTime += lapseHashTime; + + *retBucketNo = bucketNo; + return(entryNo); + } + + /* + * 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 + ECPGsearchStmtCache(char *ecpgQuery, /* stmt to search for */ + int *retBucketNo) /* returned bucket # */ + { + int entNo, entIx, bucketNo; + double lapseSearchTime; + struct timeval startSearchTime, endSearchTime; + + gettimeofday(&startSearchTime, (struct timezone *)0); + + /* hash the statement */ + entNo = ECPGhashStmt(ecpgQuery, &bucketNo); + + /* 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; + + /* done - accumulate statistics and return */ + gettimeofday(&endSearchTime, (struct timezone *)0); + lapseSearchTime = (((double) endSearchTime.tv_sec) + (((double) endSearchTime.tv_usec) / 1000000)) + - (((double) startSearchTime.tv_sec) + (((double) startSearchTime.tv_usec) / 1000000)); + totalSearchTime += lapseSearchTime; + + *retBucketNo = bucketNo; + 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; + long resultSts; + PGresult *results; + double lapseFreeTime; + struct timeval startFreeTime, endFreeTime; + char deallocText[100]; + + entry = &stmtCacheEntries[entNo]; + if(!entry->stmtID[0]) /* return if the entry isn't in use */ + return(0); + + gettimeofday(&startFreeTime, (struct timezone *) 0); + ++stmtCacheFrees; /* incr # of cache free's */ + + /* free the server resources for the statement */ + ECPGlog("ECPGfreeStmtCacheEntry line %d: freeing cache entry #%d\n", entry->lineno, entNo); + sprintf(deallocText, "DEALLOCATE PREPARE %s", entry->stmtID); + if((results = PQexec(entry->connection, deallocText)) == NULL) + { + ECPGlog("ECPGfreeStmtCacheEntry line %d: deallocate stmt, cache entry #%d\n", entry->lineno, entNo); + ECPGlog("ECPGfreeStmtCacheEntry results NULL\n"); + /* continue */ + } + else + { + resultSts = PQresultStatus(results); + if(resultSts != PGRES_COMMAND_OK) + { ECPGlog("ECPGfreeStmtCacheEntry line %d: deallocate stmt, cache entry #%d\n", entry->lineno, entNo); + ECPGlog("resultSts = %ld\n", resultSts); + ECPGlog("message = '%s'\n", PQerrorMessage(entry->connection)); + /* continue */ + } + } + PQclear(results); + entry->stmtID[0] = '\0'; + + /* free the memory used by the cache entry */ + if(entry->ecpgQuery) + { + ECPGfree(entry->ecpgQuery); + entry->ecpgQuery = 0; + } + if(entry->pqQuery) + { ECPGfree(entry->pqQuery); + entry->pqQuery = 0; + } + + /* done - accumulate statistics and return */ + gettimeofday(&endFreeTime, (struct timezone *)0); + lapseFreeTime = (((double) endFreeTime.tv_sec) + (((double) endFreeTime.tv_usec) / 1000000)) + - (((double) startFreeTime.tv_sec) + (((double) startFreeTime.tv_usec) / 1000000)); + totalFreeTime += lapseFreeTime; + + return(entNo); + } + + + /* + * add an entry to the statement cache + * returns entry # in cache used OR negative error code + */ + static int + ECPGaddStmtToCache(int lineno, /* line # of statement */ + char *stmtID, /* statement ID */ + PGconn *connection, /* connection */ + char *ecpgQuery, /* query with '?' for vars */ + char *pqQuery) /* query with '$n' for vars */ + { + int ix, initEntNo, luEntNo, entNo, bucketNo; + stmtCacheEntry *entry; + double lapseInsertTime; + struct timeval startInsertTime, endInsertTime; + + gettimeofday(&startInsertTime, (struct timezone *)0); + ++stmtCacheInserts; /* incr # of cache insert's */ + + /* hash the statement */ + initEntNo = ECPGhashStmt(ecpgQuery, &bucketNo); + + /* 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 */ + ECPGfreeStmtCacheEntry(entNo); + + /* add the query to the entry */ + ECPGlog("ECPGaddStmtToCache, line %d: adding stmt to cache, stmt ID: %s\n", lineno, stmtID); + entry = &stmtCacheEntries[entNo]; + memcpy(entry->id, "ECPGSTMT", 8); + entry->lineno = lineno; + entry->ecpgQuery = ecpgQuery; + entry->pqQuery = pqQuery; + entry->connection = connection; + entry->execs = 0; + entry->execTime = 0.0; + memset(entry->stmtID, 0, sizeof(entry->stmtID)); + strncpy(entry->stmtID, stmtID, sizeof(entry->stmtID) - 1); + + gettimeofday(&endInsertTime, (struct timezone *)0); + lapseInsertTime = (((double) endInsertTime.tv_sec) + (((double) endInsertTime.tv_usec) / 1000000)) + - (((double) startInsertTime.tv_sec) + (((double) startInsertTime.tv_usec) / 1000000)); + totalInsertTime += lapseInsertTime; + + return(entNo); + } + + static bool + ECPGexecuteWithPrepare(struct statement *stmt) + { + int noParams, paramNo, entNo, bucketNo; + long resultSts; + bool status = false; + char *ecpgQuery; + char *cmdstat; + PGresult *results; + PGnotify *notify; + struct variable *var; + int desc_counter = 0; + stmtCacheEntry *entry; + double lapsePrepareTime, lapseExecTime; + struct timeval startPrepareTime, endPrepareTime, startExecTime, endExecTime; + char *pqQuery = 0; + char stmtID[40]; + bool paramMalloced[1000]; + const char *paramValues[1000]; + + /* build an array of host variable values */ + var = stmt->inlist; + noParams = 0; + while (var) + { + const char *tobeinserted; + bool malloced = FALSE; + + tobeinserted = NULL; + + /* + * A descriptor is a special case since it contains many variables but + * is listed only once. + */ + if (var->type == ECPGt_descriptor) + { + /* + * We create an additional variable list here, so the same logic + * applies. + */ + struct variable desc_inlist; + struct descriptor *desc; + struct descriptor_item *desc_item; + + for (desc = all_descriptors; desc; desc = desc->next) + { + if (strcmp(var->pointer, desc->name) == 0) + break; + } + + if (desc == NULL) + { + ECPGraise(stmt->lineno, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, var->pointer); + return false; + } + + desc_counter++; + if (desc->count < 0 || desc->count >= desc_counter) + { + for (desc_item = desc->items; desc_item; desc_item = desc_item->next) + { + 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.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, false)) + { + return false; + } + + break; + } + } + + if (!desc_item) /* no more entries found in descriptor */ + desc_counter = 0; + } + else + desc_counter = 0; + } + else + { + if (!ECPGstore_input(stmt->lineno, stmt->force_indicator, var, &tobeinserted, &malloced, false)) + return false; + } + + /* at this point, 'tobeinserted' points to memory that contains the value of the */ + /* current parameter; it has been converted from the caller's type to a string. */ + if (tobeinserted) + { + paramValues[noParams] = tobeinserted; + + /* 'malloced' indicates that the memory identified by 'tobeinserted' must be */ + /* free'd after the query is executed. */ + if (malloced) + paramMalloced[noParams] = true; + else + paramMalloced[noParams] = false; + + /* increment the # of parameters to pass to the server */ + ++noParams; + } + + if (desc_counter == 0) + var = var->next; + } + + /* search the statement cache for this statement */ + entNo = ECPGsearchStmtCache(stmt->command, &bucketNo); + + /* if not found - add the statement to the cache */ + if(entNo) + ECPGlog("ECPGexecute, line %d: stmt found in cache, entry %d\n", stmt->lineno, entNo); + else + { + ECPGlog("ECPGexecute, line %d: stmt not in cache; inserting\n", stmt->lineno); + + /* convert the SQL statement to '$nnn' vars instead of the '?' vars from ECPG */ + ECPGbuildPQstmt(stmt->lineno, stmt->command, &pqQuery); + + /* generate a statement ID -- name must be lower-case; "deallocate " */ + /* converts to lower-case and won't find the name, if it is created as upper case */ + sprintf(stmtID, "ecpg%d", nextStmtID++); + + /* prepare the statement */ + gettimeofday(&startPrepareTime, (struct timezone *)0); + ++stmtCachePrepares; + + ECPGlog("ECPGexecute line %d: PREPARE: %s on connection %s\n", stmt->lineno, stmt->command, stmt->connection->name); + results = PQprepare(stmt->connection->connection, stmtID, pqQuery, 0, 0); + resultSts = PQresultStatus(results); + if(resultSts != PGRES_COMMAND_OK) + { ECPGlog("ECPGexecute line %d: preparing stmt, cache entry #%d\n", stmt->lineno, entNo); + ECPGlog("SQL = '%s'\n", stmt->command); + ECPGlog("resultSts = %ld\n", PQresultStatus(results)); + ECPGlog("message = '%s'\n", PQerrorMessage(stmt->connection->connection)); + PQclear(results); + return(false); + } + PQclear(results); + + gettimeofday(&endPrepareTime, (struct timezone *)0); + lapsePrepareTime = (((double) endPrepareTime.tv_sec) + (((double) endPrepareTime.tv_usec) / 1000000)) + - (((double) startPrepareTime.tv_sec) + (((double) startPrepareTime.tv_usec) / 1000000)); + totalPrepareTime += lapsePrepareTime; + + /* add the statement to the statement cache */ + ecpgQuery = ECPGstrdup(stmt->command, stmt->lineno); + entNo = ECPGaddStmtToCache(stmt->lineno, stmtID, stmt->connection->connection, ecpgQuery, pqQuery); + ECPGlog("ECPGexecute line %d: stmt inserted into cache entry %d\n", stmt->lineno, entNo); + } + + /* The query was either found in the cache or was prepared and put into the cache */ + /* In either case, it is now in the cache: 'entNo' is its entry # */ + entry = &stmtCacheEntries[entNo]; + + /* execute a "BEGIN TRANSACTION" if we've just committed and this isn't autocommit mode */ + if (stmt->connection->committed && !stmt->connection->autocommit) + { + if ((results = PQexec(stmt->connection->connection, "begin transaction")) == NULL) + { + ECPGraise(stmt->lineno, ECPG_TRANS, + ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL); + return false; + } + PQclear(results); + stmt->connection->committed = false; + } + + /* execute the query */ + gettimeofday(&startExecTime, (struct timezone *)0); + ++stmtCacheExecs; + ++entry->execs; + ECPGlog("ECPGexecute line %d: EXEC-PREP: %s on connection %s\n", stmt->lineno, stmt->command, stmt->connection->name); + results = PQexecPrepared(stmt->connection->connection, entry->stmtID, + noParams, paramValues, /* parameters */ + NULL, /* don't need param lengths since text */ + NULL, /* default to all text params */ + 0); /* ask for text results */ + gettimeofday(&endExecTime, (struct timezone *)0); + lapseExecTime = (((double) endExecTime.tv_sec) + (((double) endExecTime.tv_usec) / 1000000)) + - (((double) startExecTime.tv_sec) + (((double) startExecTime.tv_usec) / 1000000)); + totalExecTime += lapseExecTime; + entry->execTime = lapseExecTime; + + /* free any memory allocated to contain parameter values */ + for (paramNo = 0; paramNo < noParams; ++paramNo) + { + if (paramMalloced[paramNo]) + ECPGfree((char *) paramValues[paramNo]); + } + + /* check the results */ + if(results == NULL) + { + ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, PQerrorMessage(stmt->connection->connection)); + ECPGraise_backend(stmt->lineno, NULL, stmt->connection->connection, stmt->compat); + } + else + + /* + * note: since some of the following code is duplicated in + * descriptor.c it should go into a separate function + */ + { + bool clear_result = TRUE; + struct sqlca_t *sqlca = ECPGget_sqlca(); + + 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; + + 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 (var != NULL && var->type == ECPGt_descriptor) + { + PGresult **resultpp = ECPGdescriptor_lvalue(stmt->lineno, (const char *) var->pointer); + + if (resultpp == NULL) + status = false; + 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); + } + var = var->next; + } + else + for (act_field = 0; act_field < nfields && status; act_field++) + { + 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); + } + } + + 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); + 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; + } + 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); + } + + /* 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); + PQfreemem(notify); + } + + return status; + } + bool ECPGdo(int lineno, int compat, int force_indicator, const char *connection_name, const char *query,...) { *************** *** 1389,1397 **** --- 2107,2119 ---- struct connection *con; bool status; char *oldlocale; + char *prep; enum ECPGttype type; struct variable **list; + static int firstTime = 1; + static int usePrepare = 0; + /* Make sure we do NOT honor the locale for numeric input/output */ /* since the database wants the standard decimal point */ oldlocale = ECPGstrdup(setlocale(LC_NUMERIC, NULL), lineno); *************** *** 1549,1555 **** /* initialize auto_mem struct */ ECPGclear_auto_mem(); ! status = ECPGexecute(stmt); free_statement(stmt); /* and reset locale value so our application is not affected */ --- 2271,2288 ---- /* initialize auto_mem struct */ ECPGclear_auto_mem(); ! if(firstTime) ! { ! firstTime = 0; ! prep = getenv("ECPGUSEPREPARE"); ! if(prep && (!strcasecmp(prep, "yes") || !strcasecmp(prep, "y"))) ! usePrepare = 1; ! ECPGlog("ECPGdo line %d: usePrepare=%d\n", stmt->lineno, usePrepare); ! } ! if(usePrepare) ! status = ECPGexecuteWithPrepare(stmt); ! else ! status = ECPGexecute(stmt); free_statement(stmt); /* and reset locale value so our application is not affected */