19 Star 39 Fork 46

openGauss / openGauss-connector-odbc

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
execute.c 43.35 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770
/*-------
* Module: execute.c
*
* Description: This module contains routines related to
* preparing and executing an SQL statement.
*
* Classes: n/a
*
* API functions: SQLPrepare, SQLExecute, SQLExecDirect, SQLTransact,
* SQLCancel, SQLNativeSql, SQLParamData, SQLPutData
*
* Comments: See "readme.txt" for copyright and license information.
*-------
*/
#include "psqlodbc.h"
#include "misc.h"
#include <stdio.h>
#include <string.h>
#ifndef WIN32
#include <ctype.h>
#include <sys/time.h>
#include <signal.h>
#endif /* WIN32 */
#include "environ.h"
#include "connection.h"
#include "statement.h"
#include "qresult.h"
#include "convert.h"
#include "bind.h"
#include "pgtypes.h"
#include "lobj.h"
#include "pgapifunc.h"
/*support set the query timeout*/
#ifndef WIN32
StatementClass* outstmt = NULL; //for Recept_timer_signal()
/*set a timer*/
RETCODE
Set_timer(int Interval)
{
RETCODE ret;
if (0 == Interval || 1 == Interval) //set the timer status
{
struct itimerval value, ovalue;
value.it_interval.tv_sec = Interval;
value.it_interval.tv_usec = 0;
value.it_value.tv_sec = Interval;
value.it_value.tv_usec = 0;
ret = setitimer(ITIMER_REAL, &value, &ovalue);
}
else
{
ret = SQL_ERROR;
}
return ret;
}
/*recept the timer signal*/
void
Recept_timer_signal(int sign)
{
RETCODE ret;
if (NULL == outstmt)
return;
if (outstmt->options.timecount < outstmt->options.timeout - 1)
{
outstmt->options.timecount++;
return;
}
else
{
outstmt->options.timecount = 0;
ret = Set_timer(0); //stop the timer
if (!ret)
(void)PGAPI_Cancel(outstmt); //stop the query
}
}
#endif /* WIN32 */
/* Perform a Prepare on the SQL statement */
RETCODE SQL_API
PGAPI_Prepare(HSTMT hstmt,
const SQLCHAR * szSqlStr,
SQLINTEGER cbSqlStr)
{
CSTR func = "PGAPI_Prepare";
StatementClass *self = (StatementClass *) hstmt;
RETCODE retval = SQL_SUCCESS;
BOOL prepared;
MYLOG(0, "entering...\n");
#define return DONT_CALL_RETURN_FROM_HERE???
/*
* According to the ODBC specs it is valid to call SQLPrepare multiple
* times. In that case, the bound SQL statement is replaced by the new
* one
*/
prepared = self->prepared;
SC_set_prepared(self, NOT_YET_PREPARED);
switch (self->status)
{
case STMT_DESCRIBED:
MYLOG(0, "**** STMT_DESCRIBED, recycle\n");
SC_recycle_statement(self); /* recycle the statement, but do
* not remove parameter bindings */
break;
case STMT_FINISHED:
MYLOG(0, "**** STMT_FINISHED, recycle\n");
SC_recycle_statement(self); /* recycle the statement, but do
* not remove parameter bindings */
break;
case STMT_ALLOCATED:
MYLOG(0, "**** STMT_ALLOCATED, copy\n");
self->status = STMT_READY;
break;
case STMT_READY:
MYLOG(0, "**** STMT_READY, change SQL\n");
if (NOT_YET_PREPARED != prepared)
SC_recycle_statement(self); /* recycle the statement */
break;
case STMT_EXECUTING:
MYLOG(0, "**** STMT_EXECUTING, error!\n");
SC_set_error(self, STMT_SEQUENCE_ERROR, "PGAPI_Prepare(): The handle does not point to a statement that is ready to be executed", func);
retval = SQL_ERROR;
goto cleanup;
default:
SC_set_error(self, STMT_INTERNAL_ERROR, "An Internal Error has occured -- Unknown statement status.", func);
retval = SQL_ERROR;
goto cleanup;
}
SC_initialize_stmts(self, TRUE);
if (!szSqlStr)
{
SC_set_error(self, STMT_NO_MEMORY_ERROR, "the query is NULL", func);
retval = SQL_ERROR;
goto cleanup;
}
if (!szSqlStr[0])
self->statement = strdup("");
else
self->statement = make_string(szSqlStr, cbSqlStr, NULL, 0);
if (!self->statement)
{
SC_set_error(self, STMT_NO_MEMORY_ERROR, "No memory available to store statement", func);
retval = SQL_ERROR;
goto cleanup;
}
self->prepare = PREPARE_STATEMENT;
self->statement_type = statement_type(self->statement);
/* Check if connection is onlyread (only selects are allowed) */
if (CC_is_onlyread(SC_get_conn(self)) && STMT_UPDATE(self))
{
SC_set_error(self, STMT_EXEC_ERROR, "Connection is readonly, only select statements are allowed.", func);
retval = SQL_ERROR;
goto cleanup;
}
cleanup:
#undef return
MYLOG(DETAIL_LOG_LEVEL, "leaving %d\n", retval);
return retval;
}
/* Performs the equivalent of SQLPrepare, followed by SQLExecute. */
RETCODE SQL_API
PGAPI_ExecDirect(HSTMT hstmt,
const SQLCHAR * szSqlStr,
SQLINTEGER cbSqlStr,
UWORD flag)
{
StatementClass *stmt = (StatementClass *) hstmt;
RETCODE result;
CSTR func = "PGAPI_ExecDirect";
const ConnectionClass *conn = SC_get_conn(stmt);
MYLOG(0, "entering...%x\n", flag);
if (result = SC_initialize_and_recycle(stmt), SQL_SUCCESS != result)
return result;
/*
* keep a copy of the un-parametrized statement, in case they try to
* execute this statement again
*/
stmt->statement = make_string(szSqlStr, cbSqlStr, NULL, 0);
MYLOG(DETAIL_LOG_LEVEL, "a2\n");
if (!stmt->statement)
{
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "No memory available to store statement", func);
return SQL_ERROR;
}
MYLOG(0, "**** hstmt=%p, statement='%s'\n", hstmt, stmt->statement);
if (0 != (flag & PODBC_WITH_HOLD))
SC_set_with_hold(stmt);
if (0 != (flag & PODBC_RDONLY))
SC_set_readonly(stmt);
/*
* If an SQLPrepare was performed prior to this, but was left in the
* described state because an error occurred prior to SQLExecute then
* set the statement to finished so it can be recycled.
*/
if (stmt->status == STMT_DESCRIBED)
stmt->status = STMT_FINISHED;
stmt->statement_type = statement_type(stmt->statement);
/* Check if connection is onlyread (only selects are allowed) */
if (CC_is_onlyread(conn) && STMT_UPDATE(stmt))
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Connection is readonly, only select statements are allowed.", func);
return SQL_ERROR;
}
MYLOG(0, "calling PGAPI_Execute...\n");
result = PGAPI_Execute(hstmt, flag);
MYLOG(0, "leaving %hd\n", result);
return result;
}
static int
inquireHowToPrepare(const StatementClass *stmt)
{
ConnectionClass *conn;
ConnInfo *ci;
int ret = 0;
conn = SC_get_conn(stmt);
ci = &(conn->connInfo);
if (!ci->use_server_side_prepare)
{
/* Do prepare operations by the driver itself */
return PREPARE_BY_THE_DRIVER;
}
if (NOT_YET_PREPARED == stmt->prepared)
{
SQLSMALLINT num_params;
if (STMT_TYPE_DECLARE == stmt->statement_type &&
PG_VERSION_LT(conn, 8.0))
{
return PREPARE_BY_THE_DRIVER;
}
if (stmt->multi_statement < 0)
PGAPI_NumParams((StatementClass *) stmt, &num_params);
if (stmt->multi_statement > 0)
{
/*
* divide the query into multiple commands and apply V3 parse
* requests for each of them
*/
ret = PARSE_REQ_FOR_INFO;
}
else
{
if (SC_may_use_cursor(stmt))
{
if (ci->drivers.use_declarefetch)
return PARSE_REQ_FOR_INFO;
else if (SQL_CURSOR_FORWARD_ONLY != stmt->options.cursor_type)
ret = PARSE_REQ_FOR_INFO;
else
ret = PARSE_TO_EXEC_ONCE;
}
else
ret = PARSE_TO_EXEC_ONCE;
}
}
if (SC_is_prepare_statement(stmt) && (PARSE_TO_EXEC_ONCE == ret))
ret = NAMED_PARSE_REQUEST;
return ret;
}
int
decideHowToPrepare(StatementClass *stmt, BOOL force)
{
int method = SC_get_prepare_method(stmt);
if (0 != method) /* a method was already determined */
return method;
switch (stmt->prepare)
{
case NON_PREPARE_STATEMENT: /* not a prepare statement */
if (!force)
return method;
break;
}
method = inquireHowToPrepare(stmt);
stmt->prepare |= method;
if (PREPARE_BY_THE_DRIVER == method)
stmt->discard_output_params = 1;
return method;
}
/* dont/should/can send Parse request ? */
enum {
doNothing = 0
,allowParse
,preferParse
,shouldParse
,usingCommand
};
#define ONESHOT_CALL_PARSE allowParse
#define NOPARAM_ONESHOT_CALL_PARSE doNothing
static
int HowToPrepareBeforeExec(StatementClass *stmt, BOOL checkOnly)
{
SQLSMALLINT num_params = stmt->num_params;
ConnectionClass *conn = SC_get_conn(stmt);
ConnInfo *ci = &(conn->connInfo);
int nCallParse = doNothing, how_to_prepare = 0;
BOOL bNeedsTrans = FALSE;
if (num_params < 0)
PGAPI_NumParams(stmt, &num_params);
how_to_prepare = decideHowToPrepare(stmt, checkOnly);
if (checkOnly)
{
if (num_params <= 0)
return doNothing;
}
else
{
switch (how_to_prepare)
{
case NAMED_PARSE_REQUEST:
return shouldParse;
case PARSE_TO_EXEC_ONCE:
switch (stmt->prepared)
{
case PREPARED_TEMPORARILY:
nCallParse = preferParse;
break;
default:
if (num_params <= 0)
nCallParse = NOPARAM_ONESHOT_CALL_PARSE;
else
nCallParse = ONESHOT_CALL_PARSE;
}
break;
default:
return doNothing;
}
}
if (num_params > 0)
{
int param_number = -1;
ParameterInfoClass *apara;
ParameterImplClass *ipara;
OID pgtype;
while (TRUE)
{
SC_param_next(stmt, &param_number, &apara, &ipara);
if (!ipara || !apara)
break;
pgtype = PIC_get_pgtype(*ipara);
if (checkOnly)
{
switch (ipara->SQLType)
{
case SQL_LONGVARBINARY:
if (0 == pgtype)
{
if (ci->bytea_as_longvarbinary &&
0 != conn->lobj_type)
nCallParse = shouldParse;
}
break;
case SQL_CHAR:
if (ci->cvt_null_date_string)
nCallParse = shouldParse;
break;
case SQL_VARCHAR:
if (ci->drivers.bools_as_char &&
PG_WIDTH_OF_BOOLS_AS_CHAR == ipara->column_size)
nCallParse = shouldParse;
break;
}
}
else
{
BOOL bBytea = FALSE;
switch (ipara->SQLType)
{
case SQL_LONGVARBINARY:
if (conn->lobj_type == pgtype || PG_TYPE_OID == pgtype)
bNeedsTrans = TRUE;
else if (PG_TYPE_BYTEA == pgtype)
bBytea = TRUE;
else if (0 == pgtype)
{
if (ci->bytea_as_longvarbinary)
bBytea = TRUE;
else
bNeedsTrans = TRUE;
}
if (bBytea)
if (nCallParse < preferParse)
nCallParse = preferParse;
break;
}
}
}
}
if (bNeedsTrans &&
PARSE_TO_EXEC_ONCE == how_to_prepare)
{
if (!CC_is_in_trans(conn) && CC_does_autocommit(conn))
nCallParse = doNothing;
}
return nCallParse;
}
static
const char *GetSvpName(const ConnectionClass *conn, char *wrk, int wrksize)
{
snprintf(wrk, wrksize, "_EXEC_SVP_%p", conn);
return wrk;
}
static
void SetIPDField(const StatementClass *stmt, const APDFields *apdopts, IPDFields *ipdopts, SQLUSMALLINT status)
{
if ((stmt == NULL) || (apdopts == NULL) || (ipdopts == NULL))
{
return ;
}
if (SC_CanUseBatchProto(stmt))
{
int i = 0;
/* Batch execution means all things done.
* For now gaussdb can not treat each row seperately.
*/
for (i = 0; i < stmt->exec_current_row; i++)
{
if (apdopts->param_operation_ptr != NULL &&
apdopts->param_operation_ptr[i] == SQL_PARAM_IGNORE)
ipdopts->param_status_ptr[i] = SQL_PARAM_UNUSED;
else
ipdopts->param_status_ptr[i] = status;
}
}
else
{
ipdopts->param_status_ptr[stmt->exec_current_row] = status;
}
}
/*
* The execution after all parameters were resolved.
*/
static
RETCODE Exec_with_parameters_resolved(StatementClass *stmt, BOOL *exec_end)
{
CSTR func = "Exec_with_parameters_resolved";
RETCODE retval;
SQLLEN end_row;
SQLINTEGER cursor_type, scroll_concurrency;
#ifndef WIN32
RETCODE timeres = SQL_ERROR;
SQLPOINTER signres = NULL;
#endif /* WIN32 */
ConnectionClass *conn;
QResultClass *res;
APDFields *apdopts;
IPDFields *ipdopts;
BOOL prepare_before_exec = FALSE;
*exec_end = FALSE;
conn = SC_get_conn(stmt);
MYLOG(0, "copying statement params: trans_status=%d, len=" FORMAT_SIZE_T ", stmt='%s'\n", conn->transact_status, strlen(stmt->statement), stmt->statement);
#define return DONT_CALL_RETURN_FROM_HERE???
#define RETURN(code) { retval = code; goto cleanup; }
ENTER_CONN_CS(conn);
/* save the cursor's info before the execution */
cursor_type = stmt->options.cursor_type;
scroll_concurrency = stmt->options.scroll_concurrency;
/* Prepare the statement if possible at backend side */
if (HowToPrepareBeforeExec(stmt, FALSE) >= allowParse)
prepare_before_exec = TRUE;
MYLOG(DETAIL_LOG_LEVEL, "prepare_before_exec=%d srv=%d\n", prepare_before_exec, conn->connInfo.use_server_side_prepare);
/* Create the statement with parameters substituted. */
retval = copy_statement_with_parameters(stmt, prepare_before_exec);
stmt->current_exec_param = -1;
if (retval != SQL_SUCCESS)
{
stmt->exec_current_row = -1;
*exec_end = TRUE;
RETURN(retval) /* error msg is passed from the above */
}
MYLOG(0, " stmt_with_params = '%s'\n", stmt->stmt_with_params);
/*
* The real execution.
*/
MYLOG(0, "about to begin SC_execute\n");
#ifndef WIN32
if (stmt->options.timeout > 0)
{
signres = signal(SIGALRM, Recept_timer_signal); //recept the timer sign
if (SIG_ERR == signres)
RETURN(SQL_ERROR);
stmt->options.timecount = 0;
outstmt = stmt;
timeres = Set_timer(1); //start the timer
if (timeres)
RETURN(SQL_ERROR);
}
#endif /* WIN32 */
retval = SC_execute(stmt);
#ifndef WIN32
if (stmt->options.timeout > 0) //for timeout=0,do nothing
{
stmt->options.timecount = 0;
outstmt = NULL;
timeres = Set_timer(0); //stop the timer
if (timeres)
RETURN(SQL_ERROR);
}
#endif /* WIN32 */
if (retval == SQL_ERROR)
{
stmt->exec_current_row = -1;
*exec_end = TRUE;
RETURN(retval)
}
res = SC_get_Result(stmt);
/* special handling of result for keyset driven cursors */
if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type &&
SQL_CONCUR_READ_ONLY != stmt->options.scroll_concurrency)
{
QResultClass *kres;
if (kres = res->next, kres)
{
QR_set_fields(kres, QR_get_fields(res));
QR_set_fields(res, NULL);
kres->num_fields = res->num_fields;
res->next = NULL;
SC_set_Result(stmt, kres);
res = kres;
}
}
ipdopts = SC_get_IPDF(stmt);
apdopts = SC_get_APDF(stmt);
if (ipdopts->param_status_ptr)
{
switch (retval)
{
case SQL_SUCCESS:
SetIPDField(stmt, apdopts, ipdopts, SQL_PARAM_SUCCESS);
break;
case SQL_SUCCESS_WITH_INFO:
SetIPDField(stmt, apdopts, ipdopts, SQL_PARAM_SUCCESS_WITH_INFO);
break;
default:
SetIPDField(stmt, apdopts, ipdopts, SQL_PARAM_ERROR);
break;
}
}
if (SC_CanUseBatchProto(stmt) && ipdopts->param_processed_ptr)
{
int i = 0;
*(ipdopts->param_processed_ptr) = 0;
for (i = 0; i < stmt->exec_current_row; i++)
{
if (apdopts->param_operation_ptr &&
apdopts->param_operation_ptr[i] == SQL_PARAM_IGNORE)
continue;
*(ipdopts->param_processed_ptr) += 1;
}
}
if (end_row = stmt->exec_end_row, end_row < 0)
{
apdopts = SC_get_APDF(stmt);
end_row = (SQLINTEGER) apdopts->paramset_size - 1;
}
if (stmt->exec_current_row >= end_row)
{
*exec_end = TRUE;
stmt->exec_current_row = -1;
}
else
stmt->exec_current_row++;
if (res)
{
EnvironmentClass *env = (EnvironmentClass *) CC_get_env(conn);
const char *cmd = QR_get_command(res);
SQLLEN start_row;
if (start_row = stmt->exec_start_row, start_row < 0)
start_row = 0;
if (retval == SQL_SUCCESS &&
NULL != cmd &&
start_row >= end_row &&
NULL != env &&
EN_is_odbc3(env))
{
int count;
if (sscanf(cmd , "UPDATE %d", &count) == 1)
;
else if (sscanf(cmd , "DELETE %d", &count) == 1)
;
else
count = -1;
if (0 == count)
retval = SQL_NO_DATA;
}
stmt->diag_row_count = res->recent_processed_row_count;
}
/*
* The cursor's info was changed ?
*/
if (retval == SQL_SUCCESS &&
(stmt->options.cursor_type != cursor_type ||
stmt->options.scroll_concurrency != scroll_concurrency))
{
SC_set_error(stmt, STMT_OPTION_VALUE_CHANGED, "cursor updatability changed", func);
retval = SQL_SUCCESS_WITH_INFO;
}
cleanup:
#undef RETURN
#undef return
LEAVE_CONN_CS(conn);
return retval;
}
int
StartRollbackState(StatementClass *stmt)
{
int ret;
ConnectionClass *conn;
ConnInfo *ci = NULL;
MYLOG(DETAIL_LOG_LEVEL, "entering %p->external=%d\n", stmt, stmt->external);
conn = SC_get_conn(stmt);
if (conn)
ci = &conn->connInfo;
if (!ci || ci->rollback_on_error < 0) /* default */
{
if (conn && PG_VERSION_GE(conn, 8.0))
ret = 2; /* statement rollback */
else
ret = 1; /* transaction rollback */
}
else
{
ret = ci->rollback_on_error;
if (2 == ret && PG_VERSION_LT(conn, 8.0))
ret = 1;
}
switch (ret)
{
case 1:
SC_start_tc_stmt(stmt);
break;
case 2:
SC_start_rb_stmt(stmt);
break;
}
return ret;
}
int
GenerateSvpCommand(ConnectionClass *conn, int type, char *cmd, int buflen)
{
char esavepoint[50];
int rtn = -1;
cmd[0] = '\0';
switch (type)
{
case INTERNAL_SAVEPOINT_OPERATION: /* savepoint */
#ifdef _RELEASE_INTERNAL_SAVEPOINT
if (conn->internal_svp)
rtn = snprintf(cmd, buflen, "RELEASE %s;", GetSvpName(conn, esavepoint, sizeof(esavepoint)));
#endif /* _RELEASE_INTERNAL_SAVEPOINT */
rtn = snprintfcat(cmd, buflen, "SAVEPOINT %s", GetSvpName(conn, esavepoint, sizeof(esavepoint)));
break;
case INTERNAL_ROLLBACK_OPERATION: /* rollback */
if (conn->internal_svp)
rtn = snprintf(cmd, buflen, "ROLLBACK TO %s", GetSvpName(conn, esavepoint, sizeof(esavepoint)));
else
rtn = snprintf(cmd, buflen, "ROLLBACK");
break;
}
return rtn;
}
/*
* Must be in a transaction or the subsequent execution
* invokes a transaction.
*/
RETCODE
SetStatementSvp(StatementClass *stmt, unsigned int option)
{
CSTR func = "SetStatementSvp";
char cmd[128];
ConnectionClass *conn = SC_get_conn(stmt);
QResultClass *res;
RETCODE ret = SQL_SUCCESS_WITH_INFO;
if (NULL == conn->pqconn)
{
SC_set_error(stmt, STMT_COMMUNICATION_ERROR, "The connection has been lost", __FUNCTION__);
return SQL_ERROR;
}
if (CC_is_in_error_trans(conn))
return ret;
if (!stmt->lock_CC_for_rb)
{
ENTER_CONN_CS(conn);
stmt->lock_CC_for_rb = TRUE;
}
MYLOG(DETAIL_LOG_LEVEL, " %p->accessed=%d opt=%u in_progress=%u prev=%u\n", conn, CC_accessed_db(conn), option, conn->opt_in_progress, conn->opt_previous);
conn->opt_in_progress &= option;
switch (stmt->statement_type)
{
case STMT_TYPE_SPECIAL:
case STMT_TYPE_TRANSACTION:
return ret;
}
/* If rbpoint is not yet started and the previous statement was not read-only */
if (!CC_started_rbpoint(conn) && 0 == (conn->opt_previous & SVPOPT_RDONLY))
{
BOOL need_savep = FALSE;
if (SC_is_rb_stmt(stmt))
{
if (CC_is_in_trans(conn) /* needless to issue SAVEPOINT before the 1st command */
&& !conn->connInfo.drivers.for_extension_connector)
{
need_savep = TRUE;
}
}
if (need_savep)
{
if (0 != (option & SVPOPT_REDUCE_ROUNDTRIP))
{
conn->internal_op = PREPEND_IN_PROGRESS;
CC_set_accessed_db(conn);
return ret;
}
GenerateSvpCommand(conn, INTERNAL_SAVEPOINT_OPERATION, cmd, sizeof(cmd));
conn->internal_op = SAVEPOINT_IN_PROGRESS;
res = CC_send_query(conn, cmd, NULL, 0, NULL);
conn->internal_op = 0;
if (QR_command_maybe_successful(res))
ret = SQL_SUCCESS;
else
{
SC_set_error(stmt, STMT_INTERNAL_ERROR, "internal SAVEPOINT failed", func);
ret = SQL_ERROR;
}
QR_Destructor(res);
}
}
CC_set_accessed_db(conn);
MYLOG(DETAIL_LOG_LEVEL, "leaving %p->accessed=%d\n", conn, CC_accessed_db(conn));
return ret;
}
RETCODE
DiscardStatementSvp(StatementClass *stmt, RETCODE ret, BOOL errorOnly)
{
CSTR func = "DiscardStatementSvp";
ConnectionClass *conn = SC_get_conn(stmt);
BOOL start_stmt = FALSE;
MYLOG(DETAIL_LOG_LEVEL, "entering %p->accessed=%d is_in=%d is_rb=%d is_tc=%d\n", conn, CC_accessed_db(conn),
CC_is_in_trans(conn), SC_is_rb_stmt(stmt), SC_is_tc_stmt(stmt));
if (stmt->lock_CC_for_rb)
MYLOG(0, "in_progress=%u previous=%d\n", conn->opt_in_progress, conn->opt_previous);
switch (ret)
{
case SQL_NEED_DATA:
break;
case SQL_ERROR:
start_stmt = TRUE;
break;
default:
if (!errorOnly)
start_stmt = TRUE;
break;
}
if (!CC_accessed_db(conn) || !CC_is_in_trans(conn))
goto cleanup;
if (!SC_is_rb_stmt(stmt) && !SC_is_tc_stmt(stmt))
goto cleanup;
if (SQL_ERROR == ret)
{
if (CC_started_rbpoint(conn) && conn->internal_svp)
{
int cmd_success = CC_internal_rollback(conn, PER_STATEMENT_ROLLBACK, FALSE);
if (!cmd_success)
{
SC_set_error(stmt, STMT_INTERNAL_ERROR, "internal ROLLBACK failed", func);
goto cleanup;
}
}
/* ForExtensionConnector will disable all savepoints. But here raise a rollback
* which will close the transaction user started.
*/
else if (!conn->connInfo.drivers.for_extension_connector)
{
CC_abort(conn);
goto cleanup;
}
}
else if (errorOnly)
return ret;
MYLOG(DETAIL_LOG_LEVEL, "\tret=%d\n", ret);
cleanup:
#ifdef NOT_USED
if (!SC_is_prepare_statement(stmt) && ONCE_DESCRIBED == stmt->prepared)
SC_set_prepared(stmt, NOT_YET_PREPARED);
#endif
if (start_stmt || SQL_ERROR == ret)
{
stmt->execinfo = 0;
if (SQL_ERROR != ret && CC_accessed_db(conn))
{
conn->opt_previous = conn->opt_in_progress;
CC_init_opt_in_progress(conn);
}
if (stmt->lock_CC_for_rb)
{
stmt->lock_CC_for_rb = FALSE;
LEAVE_CONN_CS(conn);
MYLOG(DETAIL_LOG_LEVEL, " release conn_lock\n");
}
CC_start_stmt(conn);
}
MYLOG(DETAIL_LOG_LEVEL, "leaving %d\n", ret);
return ret;
}
/*
* Given a SQL statement, see if it is an INSERT INTO statement and extract
* the name of the table (with schema) of the table that was inserted to.
* (It is needed to resolve any @@identity references in the future.)
*/
void
SC_setInsertedTable(StatementClass *stmt, RETCODE retval)
{
const char *cmd = stmt->statement;
ConnectionClass *conn;
size_t len;
if (STMT_TYPE_INSERT != stmt->statement_type)
return;
if (!SQL_SUCCEEDED(retval))
return;
conn = SC_get_conn(stmt);
#ifdef NOT_USED /* give up the use of lastval() */
if (PG_VERSION_GE(conn, 8.1)) /* lastval() is available */
return;
#endif /* NOT_USED */
/*
* Parse a statement that was just executed. If it looks like an INSERT INTO
* statement, try to extract the table name (and schema) of the table that
* we inserted into.
*
* This is by no means fool-proof, we don't implement the whole backend
* lexer and grammar here, but should handle most simple INSERT statements.
*/
while (isspace((UCHAR) *cmd)) cmd++;
if (!*cmd)
return;
len = 6;
if (strnicmp(cmd, "insert", len))
return;
cmd += len;
while (isspace((UCHAR) *(++cmd)));
if (!*cmd)
return;
len = 4;
if (strnicmp(cmd, "into", len))
return;
cmd += len;
while (isspace((UCHAR) *cmd)) cmd++;
if (!*cmd)
return;
NULL_THE_NAME(conn->schemaIns);
NULL_THE_NAME(conn->tableIns);
eatTableIdentifiers((const UCHAR *) cmd, conn->ccsc, &conn->tableIns, &conn->schemaIns);
if (!NAME_IS_VALID(conn->tableIns))
NULL_THE_NAME(conn->schemaIns);
}
/* Execute a prepared SQL statement */
RETCODE SQL_API
PGAPI_Execute(HSTMT hstmt, UWORD flag)
{
CSTR func = "PGAPI_Execute";
StatementClass *stmt = (StatementClass *) hstmt;
RETCODE retval = SQL_SUCCESS;
ConnectionClass *conn;
APDFields *apdopts;
IPDFields *ipdopts;
SQLLEN i, start_row, end_row;
BOOL exec_end, recycled = FALSE, recycle = TRUE;
SQLSMALLINT num_params;
MYLOG(0, "entering...%x\n", flag);
conn = SC_get_conn(stmt);
apdopts = SC_get_APDF(stmt);
/*
* If the statement was previously described, just recycle the old result
* set that contained just the column information.
*/
if (stmt->prepare && stmt->status == STMT_DESCRIBED)
{
stmt->exec_current_row = -1;
SC_recycle_statement(stmt);
}
MYLOG(0, "clear errors...\n");
SC_clear_error(stmt);
if (!stmt->statement)
{
SC_set_error(stmt, STMT_NO_STMTSTRING, "This handle does not have a SQL statement stored in it", func);
MYLOG(0, "problem with handle\n");
return SQL_ERROR;
}
#define return DONT_CALL_RETURN_FROM_HERE???
if (stmt->exec_current_row > 0)
{
/*
* executing an array of parameters.
* Don't recycle the statement.
*/
recycle = FALSE;
}
else if (PREPARED_PERMANENTLY == stmt->prepared ||
PREPARED_TEMPORARILY == stmt->prepared)
{
/*
* re-executing an prepared statement.
* Don't recycle the statement but
* discard the old result.
*/
recycle = FALSE;
SC_reset_result_for_rerun(stmt);
}
/*
* If SQLExecute is being called again, recycle the statement. Note
* this should have been done by the application in a call to
* SQLFreeStmt(SQL_CLOSE) or SQLCancel.
*/
else if (stmt->status == STMT_FINISHED)
{
MYLOG(0, "recycling statement (should have been done by app)...\n");
/******** Is this really NEEDED ? ******/
SC_recycle_statement(stmt);
recycled = TRUE;
}
/* Check if the statement is in the correct state */
else if ((stmt->prepare && stmt->status != STMT_READY) ||
(stmt->status != STMT_ALLOCATED && stmt->status != STMT_READY))
{
SC_set_error(stmt, STMT_STATUS_ERROR, "The handle does not point to a statement that is ready to be executed", func);
MYLOG(0, "problem with statement\n");
retval = SQL_ERROR;
goto cleanup;
}
if (start_row = stmt->exec_start_row, start_row < 0)
start_row = 0;
if (end_row = stmt->exec_end_row, end_row < 0)
end_row = (SQLINTEGER) apdopts->paramset_size - 1;
if (stmt->exec_current_row < 0)
stmt->exec_current_row = start_row;
ipdopts = SC_get_IPDF(stmt);
num_params = stmt->num_params;
if (num_params < 0)
PGAPI_NumParams(stmt, &num_params);
if (stmt->exec_current_row == start_row)
{
/*
We sometimes need to know about the PG type of binding
parameters even in case of non-prepared statements.
*/
int nCallParse = doNothing;
if (NOT_YET_PREPARED == stmt->prepared)
{
switch (nCallParse = HowToPrepareBeforeExec(stmt, TRUE))
{
case shouldParse:
if (retval = prepareParameters(stmt, FALSE), SQL_ERROR == retval)
goto cleanup;
break;
}
}
MYLOG(0, "prepareParameters was %s called, prepare state:%d\n", shouldParse == nCallParse ? "" : "not", stmt->prepare);
if (shouldParse == nCallParse &&
PREPARE_BY_THE_DRIVER == stmt->prepare)
{
SC_set_Result(stmt, NULL);
}
if (ipdopts->param_processed_ptr)
*ipdopts->param_processed_ptr = 0;
/*
* Initialize param_status_ptr
* if batch protocol used, these params have been set.
*/
if (!SC_CanUseBatchProto(stmt) &&
ipdopts->param_status_ptr)
{
for (i = 0; i <= end_row; i++)
ipdopts->param_status_ptr[i] = SQL_PARAM_UNUSED;
}
if (recycle && !recycled)
SC_recycle_statement(stmt);
if (isSqlServr() &&
stmt->external &&
0 != stmt->prepare &&
PG_VERSION_LT(conn, 8.4) &&
SC_can_parse_statement(stmt))
parse_sqlsvr(stmt);
}
next_param_row:
if (apdopts->param_operation_ptr)
{
while (apdopts->param_operation_ptr[stmt->exec_current_row] == SQL_PARAM_IGNORE)
{
if (stmt->exec_current_row >= end_row)
{
stmt->exec_current_row = -1;
retval = SQL_SUCCESS;
goto cleanup;
}
++stmt->exec_current_row;
}
}
/*
* Initialize the current row status
*/
if (ipdopts->param_status_ptr)
ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR;
/*
* Free any data at exec params before the statement is
* executed again or the next set of parameters is processed.
* If not, then there will be a memory leak when the next
* SQLParamData/SQLPutData is called.
*/
SC_free_params(stmt, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);
/*
* Check if statement has any data-at-execute parameters when it is
* not in SC_pre_execute.
*/
{
/*
* The bound parameters could have possibly changed since the last
* execute of this statement? Therefore check for params and
* re-copy.
*/
SQLULEN offset = apdopts->param_offset_ptr ? *apdopts->param_offset_ptr : 0;
SQLINTEGER bind_size = apdopts->param_bind_type;
SQLLEN current_row = stmt->exec_current_row < 0 ? 0 : stmt->exec_current_row;
Int4 num_p = num_params < apdopts->allocated ? num_params : apdopts->allocated;
/*
* Increment the number of currently processed rows
*/
if (ipdopts->param_processed_ptr)
(*ipdopts->param_processed_ptr)++;
stmt->data_at_exec = -1;
for (i = 0; i < num_p; i++)
{
SQLLEN *pcVal = apdopts->parameters[i].used;
apdopts->parameters[i].data_at_exec = FALSE;
if (pcVal)
{
if (bind_size > 0)
pcVal = LENADDR_SHIFT(pcVal, offset + bind_size * current_row);
else
pcVal = LENADDR_SHIFT(pcVal, offset) + current_row;
if (*pcVal == SQL_DATA_AT_EXEC || *pcVal <= SQL_LEN_DATA_AT_EXEC_OFFSET)
apdopts->parameters[i].data_at_exec = TRUE;
}
/* Check for data at execution parameters */
if (apdopts->parameters[i].data_at_exec)
{
MYLOG(0, "The " FORMAT_LEN "th parameter of " FORMAT_LEN "-row is data at exec(" FORMAT_LEN ")\n", i, current_row, pcVal ? (*pcVal) : -1);
if (stmt->data_at_exec < 0)
stmt->data_at_exec = 1;
else
stmt->data_at_exec++;
}
}
/*
* If there are some data at execution parameters, return need
* data
*/
/*
* SQLParamData and SQLPutData will be used to send params and
* execute the statement.
*/
if (stmt->data_at_exec > 0)
{
retval = SQL_NEED_DATA;
goto cleanup;
}
}
if (0 != (flag & PODBC_WITH_HOLD))
SC_set_with_hold(stmt);
retval = Exec_with_parameters_resolved(stmt, &exec_end);
if (!exec_end)
{
stmt->curr_param_result = 0;
goto next_param_row;
}
cleanup:
MYLOG(0, "leaving retval=%d\n", retval);
SC_setInsertedTable(stmt, retval);
#undef return
return retval;
}
RETCODE SQL_API
PGAPI_Transact(HENV henv,
HDBC hdbc,
SQLUSMALLINT fType)
{
CSTR func = "PGAPI_Transact";
ConnectionClass *conn;
char ok;
int lf;
MYLOG(0, "entering hdbc=%p, henv=%p\n", hdbc, henv);
if (hdbc == SQL_NULL_HDBC && henv == SQL_NULL_HENV)
{
CC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
/*
* If hdbc is null and henv is valid, it means transact all
* connections on that henv.
*/
if (hdbc == SQL_NULL_HDBC && henv != SQL_NULL_HENV)
{
ConnectionClass * const *conns = getConnList();
const int conn_count = getConnCount();
for (lf = 0; lf < conn_count; lf++)
{
conn = conns[lf];
if (conn && CC_get_env(conn) == henv)
if (PGAPI_Transact(henv, (HDBC) conn, fType) != SQL_SUCCESS)
return SQL_ERROR;
}
return SQL_SUCCESS;
}
conn = (ConnectionClass *) hdbc;
if (fType != SQL_COMMIT &&
fType != SQL_ROLLBACK)
{
CC_set_error(conn, CONN_INVALID_ARGUMENT_NO, "PGAPI_Transact can only be called with SQL_COMMIT or SQL_ROLLBACK as parameter", func);
return SQL_ERROR;
}
/* If manual commit and in transaction, then proceed. */
if (CC_loves_visible_trans(conn) && CC_is_in_trans(conn))
{
MYLOG(0, "sending on conn %p '%d'\n", conn, fType);
ok = (SQL_COMMIT == fType) ? CC_commit(conn) : CC_abort(conn);
if (!ok)
{
/* error msg will be in the connection */
CC_on_abort(conn, NO_TRANS);
CC_log_error(func, "", conn);
return SQL_ERROR;
}
}
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_Cancel(HSTMT hstmt) /* Statement to cancel. */
{
CSTR func = "PGAPI_Cancel";
StatementClass *stmt = (StatementClass *) hstmt, *estmt;
ConnectionClass *conn;
RETCODE ret = SQL_SUCCESS;
MYLOG(0, "entering...\n");
/* Check if this can handle canceling in the middle of a SQLPutData? */
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
conn = SC_get_conn(stmt);
if (stmt->execute_delegate)
estmt = stmt->execute_delegate;
else
estmt = stmt;
/*
* SQLCancel works differently depending on what the statement is
* currently doing:
*
* 1. In the middle of SQLParamData / SQLPutData
* -> cancel the statement
*
* 2. Running a query asynchronously. (asynchronous mode is not supported
* by psqlODBC)
*
* 3. Busy running a function in another thread.
* -> Send a query cancel request to the server
*
* 4. Not doing anything.
* -> in ODBC version 2.0, same as SQLFreeStmt(SQL_CLOSE). In version
* 3.5, it has no effect.
*
* XXX: Checking for these conditions is racy. For example, we might
* see that the statement is waiting for SQLParamdata/SQLPutData, but
* before we acquire the lock on the statement, another thread has
* supplied the data and started executing. In that case, we'll block
* on the lock until the execution finishes.
*/
if (estmt->data_at_exec >= 0)
{
/* Waiting for more data from SQLParamData/SQLPutData. Cancel that. */
/*
* Note, any previous data-at-exec buffers will be freed
* if they call SQLExecDirect or SQLExecute again.
*/
ENTER_STMT_CS(stmt);
SC_clear_error(stmt);
estmt->data_at_exec = -1;
estmt->current_exec_param = -1;
estmt->put_data = FALSE;
cancelNeedDataState(estmt);
LEAVE_STMT_CS(stmt);
return ret;
}
else if (estmt->status == STMT_EXECUTING)
{
/*
* Busy executing in a different thread. Send a cancel request to
* the server.
*/
if (!CC_send_cancel_request(conn))
return SQL_ERROR;
else
return SQL_SUCCESS;
}
else
{
/*
* The statement is not executing, and it's not waiting for params
* either. Looks like it's not doing anything.
*/
return SQL_SUCCESS;
}
}
/*
* Returns the SQL string as modified by the driver.
* Currently, just copy the input string without modification
* observing buffer limits and truncation.
*/
RETCODE SQL_API
PGAPI_NativeSql(HDBC hdbc,
const SQLCHAR * szSqlStrIn,
SQLINTEGER cbSqlStrIn,
SQLCHAR * szSqlStr,
SQLINTEGER cbSqlStrMax,
SQLINTEGER * pcbSqlStr)
{
CSTR func = "PGAPI_NativeSql";
size_t len = 0;
char *ptr;
ConnectionClass *conn = (ConnectionClass *) hdbc;
RETCODE result;
MYLOG(0, "entering...cbSqlStrIn=%d\n", cbSqlStrIn);
ptr = (cbSqlStrIn == 0) ? "" : make_string(szSqlStrIn, cbSqlStrIn, NULL, 0);
if (!ptr)
{
CC_set_error(conn, CONN_NO_MEMORY_ERROR, "No memory available to store native sql string", func);
return SQL_ERROR;
}
result = SQL_SUCCESS;
len = strlen(ptr);
if (szSqlStr)
{
strncpy_null((char *) szSqlStr, ptr, cbSqlStrMax);
if (len >= cbSqlStrMax)
{
result = SQL_SUCCESS_WITH_INFO;
CC_set_error(conn, CONN_TRUNCATED, "The buffer was too small for the NativeSQL.", func);
}
}
if (pcbSqlStr)
*pcbSqlStr = (SQLINTEGER) len;
if (cbSqlStrIn)
free(ptr);
return result;
}
/*
* Supplies parameter data at execution time.
* Used in conjuction with SQLPutData.
*/
RETCODE SQL_API
PGAPI_ParamData(HSTMT hstmt,
PTR * prgbValue)
{
CSTR func = "PGAPI_ParamData";
StatementClass *stmt = (StatementClass *) hstmt, *estmt;
APDFields *apdopts;
IPDFields *ipdopts;
RETCODE retval;
int i;
Int2 num_p;
ConnectionClass *conn = NULL;
MYLOG(0, "entering...\n");
conn = SC_get_conn(stmt);
estmt = stmt->execute_delegate ? stmt->execute_delegate : stmt;
apdopts = SC_get_APDF(estmt);
MYLOG(0, "\tdata_at_exec=%d, params_alloc=%d\n", estmt->data_at_exec, apdopts->allocated);
#define return DONT_CALL_RETURN_FROM_HERE???
if (SC_AcceptedCancelRequest(stmt))
{
SC_set_error(stmt, STMT_OPERATION_CANCELLED, "Cancel the statement, sorry", func);
retval = SQL_ERROR;
goto cleanup;
}
if (estmt->data_at_exec < 0)
{
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "No execution-time parameters for this statement", func);
retval = SQL_ERROR;
goto cleanup;
}
if (estmt->data_at_exec > apdopts->allocated)
{
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Too many execution-time parameters were present", func);
retval = SQL_ERROR;
goto cleanup;
}
/* close the large object */
if (estmt->lobj_fd >= 0)
{
odbc_lo_close(conn, estmt->lobj_fd);
/* commit transaction if needed */
if (!CC_cursor_count(conn) && CC_does_autocommit(conn))
{
if (!CC_commit(conn))
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction", func);
retval = SQL_ERROR;
goto cleanup;
}
}
estmt->lobj_fd = -1;
}
/* Done, now copy the params and then execute the statement */
ipdopts = SC_get_IPDF(estmt);
MYLOG(DETAIL_LOG_LEVEL, "ipdopts=%p\n", ipdopts);
if (estmt->data_at_exec == 0)
{
BOOL exec_end;
UWORD flag = SC_is_with_hold(stmt) ? PODBC_WITH_HOLD : 0;
retval = Exec_with_parameters_resolved(estmt, &exec_end);
if (exec_end)
{
/**SC_reset_delegate(retval, stmt);**/
retval = dequeueNeedDataCallback(retval, stmt);
goto cleanup;
}
else
{
stmt->curr_param_result = 0;
}
if (retval = PGAPI_Execute(estmt, flag), SQL_NEED_DATA != retval)
{
goto cleanup;
}
}
/*
* Set beginning param; if first time SQLParamData is called , start
* at 0. Otherwise, start at the last parameter + 1.
*/
i = estmt->current_exec_param >= 0 ? estmt->current_exec_param + 1 : 0;
num_p = estmt->num_params;
if (num_p < 0)
PGAPI_NumParams(estmt, &num_p);
MYLOG(DETAIL_LOG_LEVEL, "i=%d allocated=%d num_p=%d\n", i, apdopts->allocated, num_p);
if (num_p > apdopts->allocated)
num_p = apdopts->allocated;
/* At least 1 data at execution parameter, so Fill in the token value */
for (; i < num_p; i++)
{
MYLOG(DETAIL_LOG_LEVEL, "i=%d", i);
if (apdopts->parameters[i].data_at_exec)
{
MYPRINTF(DETAIL_LOG_LEVEL, " at exec buffer=%p", apdopts->parameters[i].buffer);
estmt->data_at_exec--;
estmt->current_exec_param = i;
estmt->put_data = FALSE;
if (prgbValue)
{
/* returns token here */
if (stmt->execute_delegate)
{
SQLULEN offset = apdopts->param_offset_ptr ? *apdopts->param_offset_ptr : 0;
SQLLEN perrow = apdopts->param_bind_type > 0 ? apdopts->param_bind_type : apdopts->parameters[i].buflen;
MYPRINTF(DETAIL_LOG_LEVEL, " offset=" FORMAT_LEN " perrow=" FORMAT_LEN, offset, perrow);
*prgbValue = apdopts->parameters[i].buffer + offset + estmt->exec_current_row * perrow;
}
else
*prgbValue = apdopts->parameters[i].buffer;
}
break;
}
MYPRINTF(DETAIL_LOG_LEVEL, "\n");
}
retval = SQL_NEED_DATA;
MYLOG(DETAIL_LOG_LEVEL, "return SQL_NEED_DATA\n");
cleanup:
#undef return
if (stmt != NULL)
SC_setInsertedTable(stmt, retval);
MYLOG(0, "leaving %d\n", retval);
return retval;
}
/*
* Supplies parameter data at execution time.
* Used in conjunction with SQLParamData.
*/
RETCODE SQL_API
PGAPI_PutData(HSTMT hstmt,
PTR rgbValue,
SQLLEN cbValue)
{
CSTR func = "PGAPI_PutData";
StatementClass *stmt = (StatementClass *) hstmt, *estmt;
ConnectionClass *conn;
RETCODE retval = SQL_SUCCESS;
APDFields *apdopts;
IPDFields *ipdopts;
PutDataInfo *pdata;
SQLLEN old_pos;
ParameterInfoClass *current_param;
ParameterImplClass *current_iparam;
PutDataClass *current_pdata;
char *putbuf, *allocbuf = NULL;
Int2 ctype;
SQLLEN putlen;
BOOL lenset = FALSE, handling_lo = FALSE;
MYLOG(0, "entering...\n");
#define return DONT_CALL_RETURN_FROM_HERE???
if (SC_AcceptedCancelRequest(stmt))
{
SC_set_error(stmt, STMT_OPERATION_CANCELLED, "Cancel the statement, sorry.", func);
retval = SQL_ERROR;
goto cleanup;
}
estmt = stmt->execute_delegate ? stmt->execute_delegate : stmt;
apdopts = SC_get_APDF(estmt);
if (estmt->current_exec_param < 0)
{
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Previous call was not SQLPutData or SQLParamData", func);
retval = SQL_ERROR;
goto cleanup;
}
current_param = &(apdopts->parameters[estmt->current_exec_param]);
ipdopts = SC_get_IPDF(estmt);
current_iparam = &(ipdopts->parameters[estmt->current_exec_param]);
pdata = SC_get_PDTI(estmt);
current_pdata = &(pdata->pdata[estmt->current_exec_param]);
ctype = current_param->CType;
conn = SC_get_conn(estmt);
if (ctype == SQL_C_DEFAULT)
{
ctype = sqltype_to_default_ctype(conn, current_iparam->SQLType);
#ifdef UNICODE_SUPPORT
if (SQL_C_WCHAR == ctype &&
CC_default_is_c(conn))
ctype = SQL_C_CHAR;
#endif
}
if (SQL_NTS == cbValue)
{
#ifdef UNICODE_SUPPORT
if (SQL_C_WCHAR == ctype)
{
putlen = WCLEN * ucs2strlen((SQLWCHAR *) rgbValue);
lenset = TRUE;
}
else
#endif /* UNICODE_SUPPORT */
if (SQL_C_CHAR == ctype)
{
putlen = strlen(rgbValue);
lenset = TRUE;
}
}
if (!lenset)
{
if (cbValue < 0)
putlen = cbValue;
else
#ifdef UNICODE_SUPPORT
if (ctype == SQL_C_CHAR || ctype == SQL_C_BINARY || ctype == SQL_C_WCHAR)
#else
if (ctype == SQL_C_CHAR || ctype == SQL_C_BINARY)
#endif /* UNICODE_SUPPORT */
putlen = cbValue;
else
putlen = ctype_length(ctype);
}
putbuf = rgbValue;
handling_lo = (PIC_dsp_pgtype(conn, *current_iparam) == conn->lobj_type);
if (handling_lo && SQL_C_CHAR == ctype && putlen >= 0)
{
allocbuf = malloc(putlen / 2 + 1);
if (allocbuf)
{
pg_hex2bin(rgbValue, allocbuf, putlen);
putbuf = allocbuf;
putlen /= 2;
}
}
if (!estmt->put_data)
{ /* first call */
MYLOG(0, "(1) cbValue = " FORMAT_LEN "\n", cbValue);
estmt->put_data = TRUE;
current_pdata->EXEC_used = (SQLLEN *) malloc(sizeof(SQLLEN));
if (!current_pdata->EXEC_used)
{
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory in PGAPI_PutData (1)", func);
retval = SQL_ERROR;
goto cleanup;
}
*current_pdata->EXEC_used = putlen;
if (cbValue == SQL_NULL_DATA)
{
retval = SQL_SUCCESS;
goto cleanup;
}
/* Handle Long Var Binary with Large Objects */
/* if (current_iparam->SQLType == SQL_LONGVARBINARY) */
if (handling_lo)
{
/* begin transaction if needed */
if (!CC_is_in_trans(conn))
{
if (!CC_begin(conn))
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Could not begin (in-line) a transaction", func);
retval = SQL_ERROR;
goto cleanup;
}
}
/* store the oid */
current_pdata->lobj_oid = odbc_lo_creat(conn, INV_READ | INV_WRITE);
if (current_pdata->lobj_oid == 0)
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Couldnt create large object.", func);
retval = SQL_ERROR;
goto cleanup;
}
/*
* major hack -- to allow convert to see somethings there have
* to modify convert to handle this better
*/
/***current_param->EXEC_buffer = (char *) &current_param->lobj_oid;***/
/* store the fd */
estmt->lobj_fd = odbc_lo_open(conn, current_pdata->lobj_oid, INV_WRITE);
if (estmt->lobj_fd < 0)
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Couldnt open large object for writing.", func);
retval = SQL_ERROR;
goto cleanup;
}
retval = odbc_lo_write(conn, estmt->lobj_fd, putbuf, (Int4) putlen);
MYLOG(0, "lo_write: cbValue=" FORMAT_LEN ", wrote %d bytes\n", putlen, retval);
}
else if (putlen > 0)
{
current_pdata->EXEC_buffer = malloc(putlen + 1);
if (!current_pdata->EXEC_buffer)
{
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory in PGAPI_PutData (2)", func);
retval = SQL_ERROR;
goto cleanup;
}
memcpy(current_pdata->EXEC_buffer, putbuf, putlen);
current_pdata->EXEC_buffer[putlen] = '\0';
}
}
else
{
/* calling SQLPutData more than once */
MYLOG(0, "(>1) cbValue = " FORMAT_LEN "\n", cbValue);
/* if (current_iparam->SQLType == SQL_LONGVARBINARY) */
if (handling_lo)
{
/* the large object fd is in EXEC_buffer */
retval = odbc_lo_write(conn, estmt->lobj_fd, putbuf, (Int4) putlen);
MYLOG(0, "lo_write(2): cbValue = " FORMAT_LEN ", wrote %d bytes\n", putlen, retval);
*current_pdata->EXEC_used += putlen;
}
else
{
old_pos = *current_pdata->EXEC_used;
if (putlen > 0)
{
SQLLEN used = *current_pdata->EXEC_used + putlen;
SQLLEN allocsize;
char *buffer;
for (allocsize = (1 << 4); allocsize <= used; allocsize <<= 1) ;
MYLOG(0, " cbValue = " FORMAT_LEN ", old_pos = " FORMAT_LEN ", *used = " FORMAT_LEN "\n", putlen, old_pos, used);
/* dont lose the old pointer in case out of memory */
buffer = realloc(current_pdata->EXEC_buffer, allocsize);
if (!buffer)
{
SC_set_error(stmt, STMT_NO_MEMORY_ERROR,"Out of memory in PGAPI_PutData (3)", func);
retval = SQL_ERROR;
goto cleanup;
}
memcpy(&buffer[old_pos], putbuf, putlen);
buffer[used] = '\0';
/* reassign buffer incase realloc moved it */
*current_pdata->EXEC_used = used;
current_pdata->EXEC_buffer = buffer;
}
else
{
SC_set_error(stmt, STMT_INTERNAL_ERROR, "bad cbValue", func);
retval = SQL_ERROR;
goto cleanup;
}
}
}
retval = SQL_SUCCESS;
cleanup:
#undef return
if (allocbuf)
free(allocbuf);
return retval;
}
C
1
https://gitee.com/opengauss/openGauss-connector-odbc.git
git@gitee.com:opengauss/openGauss-connector-odbc.git
opengauss
openGauss-connector-odbc
openGauss-connector-odbc
master

搜索帮助