1 Star 0 Fork 46

卢乾辅@swy.com / openGauss-connector-odbc

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
statement.c 84.49 KB
一键复制 编辑 原始数据 按行查看 历史
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296
/*------
* Module: statement.c
*
* Description: This module contains functions related to creating
* and manipulating a statement.
*
* Classes: StatementClass (Functions prefix: "SC_")
*
* API functions: SQLAllocStmt, SQLFreeStmt
*
* Comments: See "readme.txt" for copyright and license information.
*-------
*/
#ifdef WIN_MULTITHREAD_SUPPORT
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif /* _WIN32_WINNT */
#endif /* WIN_MULTITHREAD_SUPPORT */
#include "statement.h"
#include "misc.h" // strncpy_null
#include "bind.h"
#include "connection.h"
#include "multibyte.h"
#include "qresult.h"
#include "convert.h"
#include "environ.h"
#include "loadlib.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "pgapifunc.h"
#define IS_BLANK(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n')
/* Map sql commands to statement types */
static const struct
{
int type;
char *s;
} Statement_Type[] =
{
{
STMT_TYPE_SELECT, "SELECT"
}
,{
STMT_TYPE_INSERT, "INSERT"
}
,{
STMT_TYPE_UPDATE, "UPDATE"
}
,{
STMT_TYPE_DELETE, "DELETE"
}
,{
STMT_TYPE_PROCCALL, "{"
}
,{
STMT_TYPE_SET, "SET"
}
,{
STMT_TYPE_RESET, "RESET"
}
,{
STMT_TYPE_CREATE, "CREATE"
}
,{
STMT_TYPE_DECLARE, "DECLARE"
}
,{
STMT_TYPE_FETCH, "FETCH"
}
,{
STMT_TYPE_MOVE, "MOVE"
}
,{
STMT_TYPE_CLOSE, "CLOSE"
}
,{
STMT_TYPE_PREPARE, "PREPARE"
}
,{
STMT_TYPE_EXECUTE, "EXECUTE"
}
,{
STMT_TYPE_DEALLOCATE, "DEALLOCATE"
}
,{
STMT_TYPE_DROP, "DROP"
}
,{
STMT_TYPE_START, "BEGIN"
}
,{
STMT_TYPE_START, "START"
}
,{
STMT_TYPE_TRANSACTION, "SAVEPOINT"
}
,{
STMT_TYPE_TRANSACTION, "RELEASE"
}
,{
STMT_TYPE_TRANSACTION, "COMMIT"
}
,{
STMT_TYPE_TRANSACTION, "END"
}
,{
STMT_TYPE_TRANSACTION, "ROLLBACK"
}
,{
STMT_TYPE_TRANSACTION, "ABORT"
}
,{
STMT_TYPE_LOCK, "LOCK"
}
,{
STMT_TYPE_ALTER, "ALTER"
}
,{
STMT_TYPE_GRANT, "GRANT"
}
,{
STMT_TYPE_REVOKE, "REVOKE"
}
,{
STMT_TYPE_COPY, "COPY"
}
,{
STMT_TYPE_ANALYZE, "ANALYZE"
}
,{
STMT_TYPE_NOTIFY, "NOTIFY"
}
,{
STMT_TYPE_EXPLAIN, "EXPLAIN"
}
/*
* Special-commands that cannot be run in a transaction block. This isn't
* as granular as it could be. VACUUM can never be run in a transaction
* block, but some variants of REINDEX and CLUSTER can be. CHECKPOINT
* doesn't throw an error if you do, but it cannot be rolled back so
* there's no point in beginning a new transaction for it.
*/
,{
STMT_TYPE_SPECIAL, "VACUUM"
}
,{
STMT_TYPE_SPECIAL, "REINDEX"
}
,{
STMT_TYPE_SPECIAL, "CLUSTER"
}
,{
STMT_TYPE_SPECIAL, "CHECKPOINT"
}
,{
STMT_TYPE_WITH, "WITH"
}
,{
0, NULL
}
};
static QResultClass *libpq_bind_and_exec(StatementClass *stmt);
static void SC_set_errorinfo(StatementClass *self, QResultClass *res, int errkind);
static void SC_set_error_if_not_set(StatementClass *self, int errornumber, const char *errmsg, const char *func);
RETCODE SQL_API
PGAPI_AllocStmt(HDBC hdbc,
HSTMT * phstmt, UDWORD flag)
{
CSTR func = "PGAPI_AllocStmt";
ConnectionClass *conn = (ConnectionClass *) hdbc;
StatementClass *stmt;
ARDFields *ardopts;
MYLOG(0, "entering...\n");
if (!conn)
{
CC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
stmt = SC_Constructor(conn);
MYLOG(0, "**** : hdbc = %p, stmt = %p\n", hdbc, stmt);
if (!stmt)
{
CC_set_error(conn, CONN_STMT_ALLOC_ERROR, "No more memory to allocate a further SQL-statement", func);
*phstmt = SQL_NULL_HSTMT;
return SQL_ERROR;
}
if (!CC_add_statement(conn, stmt))
{
CC_set_error(conn, CONN_STMT_ALLOC_ERROR, "Maximum number of statements exceeded.", func);
SC_Destructor(stmt);
*phstmt = SQL_NULL_HSTMT;
return SQL_ERROR;
}
*phstmt = (HSTMT) stmt;
stmt->iflag = flag;
/* Copy default statement options based from Connection options */
if (0 != (PODBC_INHERIT_CONNECT_OPTIONS & flag))
{
stmt->options = stmt->options_orig = conn->stmtOptions;
stmt->ardi.ardf = conn->ardOptions;
}
else
{
InitializeStatementOptions(&stmt->options_orig);
stmt->options = stmt->options_orig;
InitializeARDFields(&stmt->ardi.ardf);
}
ardopts = SC_get_ARDF(stmt);
ARD_AllocBookmark(ardopts);
/* Save the handle for later */
stmt->phstmt = phstmt;
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_FreeStmt(HSTMT hstmt,
SQLUSMALLINT fOption)
{
CSTR func = "PGAPI_FreeStmt";
StatementClass *stmt = (StatementClass *) hstmt;
MYLOG(0, "entering...hstmt=%p, fOption=%hi\n", hstmt, fOption);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if (fOption == SQL_DROP)
{
ConnectionClass *conn = stmt->hdbc;
/* Remove the statement from the connection's statement list */
if (conn)
{
QResultClass *res;
if (STMT_EXECUTING == stmt->status)
{
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.", func);
return SQL_ERROR; /* stmt may be executing a transaction */
}
if (conn->unnamed_prepared_stmt == stmt)
conn->unnamed_prepared_stmt = NULL;
/*
* Free any cursors and discard any result info.
* Don't detach the statement from the connection
* before freeing the associated cursors. Otherwise
* CC_cursor_count() would get wrong results.
*/
res = SC_get_Result(stmt);
QR_Destructor(res);
SC_init_Result(stmt);
if (!CC_remove_statement(conn, stmt))
{
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.", func);
return SQL_ERROR; /* stmt may be executing a
* transaction */
}
}
if (stmt->execute_delegate)
{
PGAPI_FreeStmt(stmt->execute_delegate, SQL_DROP);
stmt->execute_delegate = NULL;
}
if (stmt->execute_parent)
stmt->execute_parent->execute_delegate = NULL;
/* Destroy the statement and free any results, cursors, etc. */
SC_Destructor(stmt);
}
else if (fOption == SQL_UNBIND)
SC_unbind_cols(stmt);
else if (fOption == SQL_CLOSE)
{
/*
* this should discard all the results, but leave the statement
* itself in place (it can be executed again)
*/
stmt->transition_status = STMT_TRANSITION_ALLOCATED;
if (stmt->execute_delegate)
{
PGAPI_FreeStmt(stmt->execute_delegate, SQL_DROP);
stmt->execute_delegate = NULL;
}
if (!SC_recycle_statement(stmt))
{
return SQL_ERROR;
}
SC_set_Curres(stmt, NULL);
}
else if (fOption == SQL_RESET_PARAMS)
SC_free_params(stmt, STMT_FREE_PARAMS_ALL);
else
{
SC_set_error(stmt, STMT_OPTION_OUT_OF_RANGE_ERROR, "Invalid option passed to PGAPI_FreeStmt.", func);
return SQL_ERROR;
}
return SQL_SUCCESS;
}
/*
* StatementClass implementation
*/
void
InitializeStatementOptions(StatementOptions *opt)
{
memset(opt, 0, sizeof(StatementOptions));
opt->scroll_concurrency = SQL_CONCUR_READ_ONLY;
opt->cursor_type = SQL_CURSOR_FORWARD_ONLY;
opt->retrieve_data = SQL_RD_ON;
opt->use_bookmarks = SQL_UB_OFF;
opt->metadata_id = SQL_FALSE;
}
static void SC_clear_parse_status(StatementClass *self, ConnectionClass *conn)
{
self->parse_status = STMT_PARSE_NONE;
}
static void SC_init_discard_output_params(StatementClass *self)
{
ConnectionClass *conn = SC_get_conn(self);
if (!conn) return;
self->discard_output_params = 0;
if (!conn->connInfo.use_server_side_prepare)
self->discard_output_params = 1;
}
static void SC_init_parse_method(StatementClass *self)
{
ConnectionClass *conn = SC_get_conn(self);
self->parse_method = 0;
if (!conn) return;
if (0 == (PODBC_EXTERNAL_STATEMENT & self->iflag)) return;
if (self->catalog_result) return;
if (conn->connInfo.drivers.parse)
SC_set_parse_forced(self);
}
StatementClass *
SC_Constructor(ConnectionClass *conn)
{
StatementClass *rv;
rv = (StatementClass *) malloc(sizeof(StatementClass));
if (rv)
{
rv->hdbc = conn;
rv->phstmt = NULL;
rv->result = NULL;
rv->curres = NULL;
rv->catalog_result = FALSE;
rv->prepare = NON_PREPARE_STATEMENT;
rv->prepared = NOT_YET_PREPARED;
rv->status = STMT_ALLOCATED;
rv->external = FALSE;
rv->iflag = 0;
rv->plan_name = NULL;
rv->transition_status = STMT_TRANSITION_UNALLOCATED;
rv->multi_statement = -1; /* unknown */
rv->num_params = -1; /* unknown */
rv->processed_statements = NULL;
rv->__error_message = NULL;
rv->__error_number = 0;
rv->pgerror = NULL;
rv->statement = NULL;
rv->stmt_with_params = NULL;
rv->load_statement = NULL;
rv->statement_type = STMT_TYPE_UNKNOWN;
rv->currTuple = -1;
rv->rowset_start = 0;
SC_set_rowset_start(rv, -1, FALSE);
rv->current_col = -1;
rv->bind_row = 0;
rv->from_pos = rv->load_from_pos = rv->where_pos = -1;
rv->last_fetch_count = rv->last_fetch_count_include_ommitted = 0;
rv->save_rowset_size = -1;
rv->data_at_exec = -1;
rv->current_exec_param = -1;
rv->exec_start_row = -1;
rv->exec_end_row = -1;
rv->exec_current_row = -1;
rv->put_data = FALSE;
rv->ref_CC_error = FALSE;
rv->join_info = 0;
rv->curr_param_result = 0;
SC_init_parse_method(rv);
rv->lobj_fd = -1;
INIT_NAME(rv->cursor_name);
/* Parse Stuff */
rv->ti = NULL;
rv->ntab = 0;
rv->num_key_fields = -1; /* unknown */
SC_clear_parse_status(rv, conn);
rv->proc_return = -1;
SC_init_discard_output_params(rv);
rv->cancel_info = 0;
/* Clear Statement Options -- defaults will be set in AllocStmt */
memset(&rv->options, 0, sizeof(StatementOptions));
InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->ardi),
rv, SQL_ATTR_APP_ROW_DESC);
InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->apdi),
rv, SQL_ATTR_APP_PARAM_DESC);
InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->irdi),
rv, SQL_ATTR_IMP_ROW_DESC);
InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->ipdi),
rv, SQL_ATTR_IMP_PARAM_DESC);
rv->miscinfo = 0;
rv->execinfo = 0;
rv->rb_or_tc = 0;
SC_reset_updatable(rv);
rv->diag_row_count = 0;
rv->stmt_time = 0;
rv->execute_delegate = NULL;
rv->execute_parent = NULL;
rv->allocated_callbacks = 0;
rv->num_callbacks = 0;
rv->callbacks = NULL;
GetDataInfoInitialize(SC_get_GDTI(rv));
PutDataInfoInitialize(SC_get_PDTI(rv));
rv->lock_CC_for_rb = FALSE;
INIT_STMT_CS(rv);
}
return rv;
}
char
SC_Destructor(StatementClass *self)
{
CSTR func = "SC_Destructor";
QResultClass *res = SC_get_Result(self);
MYLOG(0, "entering self=%p, self->result=%p, self->hdbc=%p\n", self, res, self->hdbc);
SC_clear_error(self);
if (STMT_EXECUTING == self->status)
{
SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.", func);
return FALSE;
}
if (res)
{
if (!self->hdbc)
res->conn = NULL; /* prevent any dbase activity */
QR_Destructor(res);
}
SC_initialize_stmts(self, TRUE);
/* Free the parsed table information */
SC_initialize_cols_info(self, FALSE, TRUE);
NULL_THE_NAME(self->cursor_name);
/* Free the parsed field information */
DC_Destructor((DescriptorClass *) SC_get_ARDi(self));
DC_Destructor((DescriptorClass *) SC_get_APDi(self));
DC_Destructor((DescriptorClass *) SC_get_IRDi(self));
DC_Destructor((DescriptorClass *) SC_get_IPDi(self));
GDATA_unbind_cols(SC_get_GDTI(self), TRUE);
PDATA_free_params(SC_get_PDTI(self), STMT_FREE_PARAMS_ALL);
if (self->__error_message)
free(self->__error_message);
if (self->pgerror)
ER_Destructor(self->pgerror);
cancelNeedDataState(self);
if (self->callbacks)
free(self->callbacks);
DELETE_STMT_CS(self);
free(self);
MYLOG(0, "leaving\n");
return TRUE;
}
void
SC_init_Result(StatementClass *self)
{
self->result = self->curres = NULL;
self->curr_param_result = 0;
MYLOG(0, "leaving(%p)\n", self);
}
void
SC_set_Result(StatementClass *self, QResultClass *res)
{
if (res != self->result)
{
MYLOG(0, "(%p, %p)\n", self, res);
QR_Destructor(self->result);
self->result = self->curres = res;
if (NULL != res)
self->curr_param_result = 1;
}
}
/*
* Free parameters and free the memory from the
* data-at-execution parameters that was allocated in SQLPutData.
*/
void
SC_free_params(StatementClass *self, char option)
{
if (option != STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY)
{
APD_free_params(SC_get_APDF(self), option);
IPD_free_params(SC_get_IPDF(self), option);
}
PDATA_free_params(SC_get_PDTI(self), option);
self->data_at_exec = -1;
self->current_exec_param = -1;
self->put_data = FALSE;
if (option == STMT_FREE_PARAMS_ALL)
{
self->exec_start_row = -1;
self->exec_end_row = -1;
self->exec_current_row = -1;
}
}
int
statement_type(const char *statement)
{
int i;
/* ignore leading whitespace in query string */
while (*statement && (isspace((UCHAR) *statement) || *statement == '('))
statement++;
for (i = 0; Statement_Type[i].s; i++)
if (!strnicmp(statement, Statement_Type[i].s, strlen(Statement_Type[i].s)))
return Statement_Type[i].type;
return STMT_TYPE_OTHER;
}
void
SC_set_planname(StatementClass *stmt, const char *plan_name)
{
if (stmt->plan_name)
free(stmt->plan_name);
if (plan_name && plan_name[0])
stmt->plan_name = strdup(plan_name);
else
stmt->plan_name = NULL;
}
void
SC_set_rowset_start(StatementClass *stmt, SQLLEN start, BOOL valid_base)
{
QResultClass *res = SC_get_Curres(stmt);
SQLLEN incr = start - stmt->rowset_start;
MYLOG(DETAIL_LOG_LEVEL, "%p->SC_set_rowstart " FORMAT_LEN "->" FORMAT_LEN "(%s) ", stmt, stmt->rowset_start, start, valid_base ? "valid" : "unknown");
if (res != NULL)
{
BOOL valid = QR_has_valid_base(res);
MYPRINTF(DETAIL_LOG_LEVEL, ":(%p)QR is %s", res, QR_has_valid_base(res) ? "valid" : "unknown");
if (valid)
{
if (valid_base)
QR_inc_rowstart_in_cache(res, incr);
else
QR_set_no_valid_base(res);
}
else if (valid_base)
{
QR_set_has_valid_base(res);
if (start < 0)
QR_set_rowstart_in_cache(res, -1);
else
QR_set_rowstart_in_cache(res, start);
}
if (!QR_get_cursor(res))
res->key_base = start;
MYPRINTF(DETAIL_LOG_LEVEL, ":(%p)QR result=" FORMAT_LEN "(%s)", res, QR_get_rowstart_in_cache(res), QR_has_valid_base(res) ? "valid" : "unknown");
}
stmt->rowset_start = start;
MYPRINTF(DETAIL_LOG_LEVEL, ":stmt result=" FORMAT_LEN "\n", stmt->rowset_start);
}
void
SC_inc_rowset_start(StatementClass *stmt, SQLLEN inc)
{
SQLLEN start = stmt->rowset_start + inc;
SC_set_rowset_start(stmt, start, TRUE);
}
int
SC_set_current_col(StatementClass *stmt, int col)
{
if (col == stmt->current_col)
return col;
if (col >= 0)
reset_a_getdata_info(SC_get_GDTI(stmt), col + 1);
stmt->current_col = col;
return stmt->current_col;
}
void
SC_set_prepared(StatementClass *stmt, int prepared)
{
if (prepared == stmt->prepared)
;
else if (NOT_YET_PREPARED == prepared && PREPARED_PERMANENTLY == stmt->prepared)
{
ConnectionClass *conn = SC_get_conn(stmt);
if (conn)
{
ENTER_CONN_CS(conn);
if (CONN_CONNECTED == conn->status)
{
if (CC_is_in_error_trans(conn))
{
CC_mark_a_object_to_discard(conn, 's', stmt->plan_name);
}
else
{
QResultClass *res;
char dealloc_stmt[128];
SPRINTF_FIXED(dealloc_stmt, "DEALLOCATE \"%s\"", stmt->plan_name);
res = CC_send_query(conn, dealloc_stmt, NULL, IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL);
QR_Destructor(res);
}
}
LEAVE_CONN_CS(conn);
}
}
if (NOT_YET_PREPARED == prepared)
SC_set_planname(stmt, NULL);
stmt->prepared = prepared;
}
/*
* Initialize stmt_with_params and load_statement member pointer
* deallocating corresponding prepared plan. Also initialize
* statement member pointer if specified.
*/
RETCODE
SC_initialize_stmts(StatementClass *self, BOOL initializeOriginal)
{
ProcessedStmt *pstmt;
ProcessedStmt *next_pstmt;
if (self->lock_CC_for_rb)
{
LEAVE_CONN_CS(SC_get_conn(self));
self->lock_CC_for_rb = FALSE;
}
if (initializeOriginal)
{
if (self->statement)
{
free(self->statement);
self->statement = NULL;
}
pstmt = self->processed_statements;
while (pstmt)
{
if (pstmt->query)
free(pstmt->query);
next_pstmt = pstmt->next;
free(pstmt);
pstmt = next_pstmt;
}
self->processed_statements = NULL;
self->prepare = NON_PREPARE_STATEMENT;
SC_set_prepared(self, NOT_YET_PREPARED);
self->statement_type = STMT_TYPE_UNKNOWN; /* unknown */
self->multi_statement = -1; /* unknown */
self->num_params = -1; /* unknown */
self->proc_return = -1; /* unknown */
self->join_info = 0;
SC_init_parse_method(self);
SC_init_discard_output_params(self);
}
if (self->stmt_with_params)
{
free(self->stmt_with_params);
self->stmt_with_params = NULL;
}
if (self->load_statement)
{
free(self->load_statement);
self->load_statement = NULL;
}
return 0;
}
BOOL SC_opencheck(StatementClass *self, const char *func)
{
QResultClass *res;
if (!self)
return FALSE;
if (self->status == STMT_EXECUTING)
{
SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.", func);
return TRUE;
}
/*
* We can dispose the result of Describe-only any time.
*/
if (self->prepare && self->status == STMT_DESCRIBED)
{
MYLOG(0, "self->prepare && self->status == STMT_DESCRIBED\n");
return FALSE;
}
if (res = SC_get_Curres(self), NULL != res)
{
if (QR_command_maybe_successful(res) && res->backend_tuples)
{
SC_set_error(self, STMT_SEQUENCE_ERROR, "The cursor is open.", func);
return TRUE;
}
}
return FALSE;
}
RETCODE
SC_initialize_and_recycle(StatementClass *self)
{
SC_initialize_stmts(self, TRUE);
if (!SC_recycle_statement(self))
return SQL_ERROR;
return SQL_SUCCESS;
}
void
SC_reset_result_for_rerun(StatementClass *self)
{
QResultClass *res;
ColumnInfoClass *flds;
if (!self) return;
if (res = SC_get_Result(self), NULL == res)
return;
flds = QR_get_fields(res);
if (NULL == flds ||
0 == CI_get_num_fields(flds))
SC_set_Result(self, NULL);
else
{
QR_reset_for_re_execute(res);
self->curr_param_result = 1;
SC_set_Curres(self, NULL);
}
}
/*
* Called from SQLPrepare if STMT_PREMATURE, or
* from SQLExecute if STMT_FINISHED, or
* from SQLFreeStmt(SQL_CLOSE)
*/
char
SC_recycle_statement(StatementClass *self)
{
CSTR func = "SC_recycle_statement";
ConnectionClass *conn;
MYLOG(0, "entering self=%p\n", self);
SC_clear_error(self);
/* This would not happen */
if (self->status == STMT_EXECUTING)
{
SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.", func);
return FALSE;
}
if (SC_get_conn(self)->unnamed_prepared_stmt == self)
SC_get_conn(self)->unnamed_prepared_stmt = NULL;
conn = SC_get_conn(self);
switch (self->status)
{
case STMT_ALLOCATED:
/* this statement does not need to be recycled */
return TRUE;
case STMT_READY:
break;
case STMT_DESCRIBED:
break;
case STMT_FINISHED:
break;
default:
SC_set_error(self, STMT_INTERNAL_ERROR, "An internal error occured while recycling statements", func);
return FALSE;
}
switch (self->prepared)
{
case NOT_YET_PREPARED:
case PREPARED_TEMPORARILY:
/* Free the parsed table/field information */
SC_initialize_cols_info(self, TRUE, TRUE);
MYLOG(DETAIL_LOG_LEVEL, "SC_clear_parse_status\n");
SC_clear_parse_status(self, conn);
break;
}
/* Free any cursors */
if (SC_get_Result(self))
SC_set_Result(self, NULL);
self->miscinfo = 0;
self->execinfo = 0;
/* self->rbonerr = 0; Never clear the bits here */
/*
* Reset only parameters that have anything to do with results
*/
self->status = STMT_READY;
self->catalog_result = FALSE; /* not very important */
self->currTuple = -1;
SC_set_rowset_start(self, -1, FALSE);
SC_set_current_col(self, -1);
self->bind_row = 0;
MYLOG(DETAIL_LOG_LEVEL, "statement=%p ommitted=0\n", self);
self->last_fetch_count = self->last_fetch_count_include_ommitted = 0;
self->__error_message = NULL;
self->__error_number = 0;
self->lobj_fd = -1;
SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);
SC_initialize_stmts(self, FALSE);
cancelNeedDataState(self);
self->cancel_info = 0;
/*
* reset the current attr setting to the original one.
*/
self->options.scroll_concurrency = self->options_orig.scroll_concurrency;
self->options.cursor_type = self->options_orig.cursor_type;
self->options.keyset_size = self->options_orig.keyset_size;
self->options.maxLength = self->options_orig.maxLength;
self->options.maxRows = self->options_orig.maxRows;
return TRUE;
}
/*
* Scan the query wholly or partially (if the next_cmd param specified).
* Also count the number of parameters respectviely.
*/
void
SC_scanQueryAndCountParams(const char *query, const ConnectionClass *conn,
ssize_t *next_cmd, SQLSMALLINT * pcpar,
po_ind_t *multi_st, po_ind_t *proc_return)
{
const char *tstr, *tag = NULL;
size_t taglen = 0;
char tchar, bchar, escape_in_literal = '\0';
char in_literal = FALSE, in_ident_keyword = FALSE,
in_dquote_identifier = FALSE,
in_dollar_quote = FALSE, in_escape = FALSE,
in_line_comment = FALSE, del_found = FALSE;
int comment_level = 0;
po_ind_t multi = FALSE;
SQLSMALLINT num_p;
encoded_str encstr;
MYLOG(0, "entering...\n");
num_p = 0;
if (proc_return)
*proc_return = 0;
if (next_cmd)
*next_cmd = -1;
tstr = query;
make_encoded_str(&encstr, conn, tstr);
for (bchar = '\0', tchar = encoded_nextchar(&encstr); tchar; tchar = encoded_nextchar(&encstr))
{
if (MBCS_NON_ASCII(encstr)) /* multibyte char */
{
if ((UCHAR) tchar >= 0x80)
bchar = tchar;
if (in_dquote_identifier ||
in_literal ||
in_dollar_quote ||
in_escape ||
in_line_comment ||
comment_level > 0)
;
else
in_ident_keyword = TRUE;
continue;
}
if (!multi && del_found)
{
if (IS_NOT_SPACE(tchar))
{
multi = TRUE;
if (next_cmd)
break;
}
}
if (in_ident_keyword)
{
if (isalnum(tchar) ||
DOLLAR_QUOTE == tchar ||
'_' == tchar)
{
bchar = tchar;
continue;
}
in_ident_keyword = FALSE;
}
if (in_dollar_quote)
{
if (tchar == DOLLAR_QUOTE)
{
if (strncmp((const char *) ENCODE_PTR(encstr), tag, taglen) == 0)
{
in_dollar_quote = FALSE;
tag = NULL;
encoded_position_shift(&encstr, taglen - 1);
}
}
}
else if (in_literal)
{
if (in_escape)
in_escape = FALSE;
else if (tchar == escape_in_literal)
in_escape = TRUE;
else if (tchar == LITERAL_QUOTE)
in_literal = FALSE;
}
else if (in_dquote_identifier)
{
if (tchar == IDENTIFIER_QUOTE)
in_dquote_identifier = FALSE;
}
else if (in_line_comment)
{
if (PG_LINEFEED == tchar)
in_line_comment = FALSE;
}
else if (comment_level > 0)
{
if ('/' == tchar && '*' == ENCODE_PTR(encstr)[1])
{
tchar = encoded_nextchar(&encstr);
comment_level++;
}
else if ('*' == tchar && '/' == ENCODE_PTR(encstr)[1])
{
tchar = encoded_nextchar(&encstr);
comment_level--;
}
}
else if (isalnum(tchar))
in_ident_keyword = TRUE;
else
{
if (tchar == '?')
{
if (0 == num_p && bchar == '{')
{
if (proc_return)
*proc_return = 1;
}
num_p++;
}
else if (tchar == ';')
{
del_found = TRUE;
if (next_cmd)
*next_cmd = encstr.pos;
}
else if (tchar == DOLLAR_QUOTE)
{
const char *ptr = (const char *) ENCODE_PTR(encstr);
taglen = findTag(ptr, encstr.ccsc);
if (taglen > 0)
{
in_dollar_quote = TRUE;
tag = ptr;
encoded_position_shift(&encstr, taglen - 1);
}
}
else if (tchar == LITERAL_QUOTE)
{
in_literal = TRUE;
escape_in_literal = CC_get_escape(conn);
if (!escape_in_literal)
{
if (LITERAL_EXT == ENCODE_PTR(encstr)[-1])
escape_in_literal = ESCAPE_IN_LITERAL;
}
}
else if (tchar == IDENTIFIER_QUOTE)
in_dquote_identifier = TRUE;
else if ('-' == tchar)
{
if ('-' == ENCODE_PTR(encstr)[1])
{
tchar = encoded_nextchar(&encstr);
in_line_comment = TRUE;
}
}
else if ('/' == tchar)
{
if ('*' == ENCODE_PTR(encstr)[1])
{
tchar = encoded_nextchar(&encstr);
comment_level++;
}
}
if (IS_NOT_SPACE(tchar))
bchar = tchar;
}
}
if (pcpar)
*pcpar = num_p;
if (multi_st)
*multi_st = multi;
MYLOG(0, "leaving...num_p=%d multi=%d\n", num_p, multi);
}
/*
* Describe the result set a statement will produce (for
* SQLPrepare/SQLDescribeCol)
*
* returns # of fields if successful, -1 on error.
*/
Int4
SC_describe(StatementClass *self)
{
Int4 num_fields = -1;
QResultClass *res;
MYLOG(0, "entering status = %d\n", self->status);
res = SC_get_Curres(self);
if (NULL != res)
{
num_fields = QR_NumResultCols(res);
if (num_fields > 0 ||
NULL != QR_get_command(res))
return num_fields;
}
if (self->status == STMT_READY)
{
MYLOG(0, " preprocess: status = READY\n");
self->miscinfo = 0;
self->execinfo = 0;
decideHowToPrepare(self, FALSE);
switch (SC_get_prepare_method(self))
{
case NAMED_PARSE_REQUEST:
case PARSE_TO_EXEC_ONCE:
if (SQL_SUCCESS != prepareParameters(self, FALSE))
return num_fields;
break;
case PARSE_REQ_FOR_INFO:
if (SQL_SUCCESS != prepareParameters(self, FALSE))
return num_fields;
self->status = STMT_DESCRIBED;
break;
default:
if (SQL_SUCCESS != prepareParameters(self, TRUE))
return num_fields;
self->status = STMT_DESCRIBED;
break;
}
if (res = SC_get_Curres(self), NULL != res)
{
num_fields = QR_NumResultCols(res);
return num_fields;
}
}
return num_fields;
}
/* This is only called from SQLFreeStmt(SQL_UNBIND) */
char
SC_unbind_cols(StatementClass *self)
{
ARDFields *opts = SC_get_ARDF(self);
GetDataInfo *gdata = SC_get_GDTI(self);
BindInfoClass *bookmark;
ARD_unbind_cols(opts, FALSE);
GDATA_unbind_cols(gdata, FALSE);
if (bookmark = opts->bookmark, bookmark != NULL)
{
bookmark->buffer = NULL;
bookmark->used = NULL;
}
return 1;
}
void
SC_clear_error(StatementClass *self)
{
QResultClass *res;
self->__error_number = 0;
if (self->__error_message)
{
free(self->__error_message);
self->__error_message = NULL;
}
if (self->pgerror)
{
ER_Destructor(self->pgerror);
self->pgerror = NULL;
}
self->diag_row_count = 0;
if (res = SC_get_Curres(self), res)
{
QR_set_message(res, NULL);
QR_set_notice(res, NULL);
res->sqlstate[0] = '\0';
}
self->stmt_time = 0;
memset(&self->localtime, 0, sizeof(self->localtime));
self->localtime.tm_sec = -1;
SC_unref_CC_error(self);
}
/*
* This function creates an error info which is the concatenation
* of the result, statement, connection, and socket messages.
*/
/* Map sql commands to statement types */
static const struct
{
int number;
const char ver3str[6];
const char ver2str[6];
} Statement_sqlstate[] =
{
{ STMT_ERROR_IN_ROW, "01S01", "01S01" },
{ STMT_OPTION_VALUE_CHANGED, "01S02", "01S02" },
{ STMT_ROW_VERSION_CHANGED, "01001", "01001" }, /* data changed */
{ STMT_POS_BEFORE_RECORDSET, "01S06", "01S06" },
{ STMT_TRUNCATED, "01004", "01004" }, /* data truncated */
{ STMT_INFO_ONLY, "00000", "00000" }, /* just an information that is returned, no error */
{ STMT_OK, "00000", "00000" }, /* OK */
{ STMT_EXEC_ERROR, "HY000", "S1000" }, /* also a general error */
{ STMT_STATUS_ERROR, "HY010", "S1010" },
{ STMT_SEQUENCE_ERROR, "HY010", "S1010" }, /* Function sequence error */
{ STMT_NO_MEMORY_ERROR, "HY001", "S1001" }, /* memory allocation failure */
{ STMT_COLNUM_ERROR, "07009", "S1002" }, /* invalid column number */
{ STMT_NO_STMTSTRING, "HY001", "S1001" }, /* having no stmtstring is also a malloc problem */
{ STMT_ERROR_TAKEN_FROM_BACKEND, "HY000", "S1000" }, /* general error */
{ STMT_INTERNAL_ERROR, "HY000", "S1000" }, /* general error */
{ STMT_STILL_EXECUTING, "HY010", "S1010" },
{ STMT_NOT_IMPLEMENTED_ERROR, "HYC00", "S1C00" }, /* == 'driver not
* capable' */
{ STMT_BAD_PARAMETER_NUMBER_ERROR, "07009", "S1093" },
{ STMT_OPTION_OUT_OF_RANGE_ERROR, "HY092", "S1092" },
{ STMT_INVALID_COLUMN_NUMBER_ERROR, "07009", "S1002" },
{ STMT_RESTRICTED_DATA_TYPE_ERROR, "07006", "07006" },
{ STMT_INVALID_CURSOR_STATE_ERROR, "07005", "24000" },
{ STMT_CREATE_TABLE_ERROR, "42S01", "S0001" }, /* table already exists */
{ STMT_NO_CURSOR_NAME, "S1015", "S1015" },
{ STMT_INVALID_CURSOR_NAME, "34000", "34000" },
{ STMT_INVALID_ARGUMENT_NO, "HY024", "S1009" }, /* invalid argument value */
{ STMT_ROW_OUT_OF_RANGE, "HY107", "S1107" },
{ STMT_OPERATION_CANCELLED, "HY008", "S1008" },
{ STMT_INVALID_CURSOR_POSITION, "HY109", "S1109" },
{ STMT_VALUE_OUT_OF_RANGE, "HY019", "22003" },
{ STMT_OPERATION_INVALID, "HY011", "S1011" },
{ STMT_PROGRAM_TYPE_OUT_OF_RANGE, "?????", "?????" },
{ STMT_BAD_ERROR, "08S01", "08S01" }, /* communication link failure */
{ STMT_INVALID_OPTION_IDENTIFIER, "HY092", "HY092" },
{ STMT_RETURN_NULL_WITHOUT_INDICATOR, "22002", "22002" },
{ STMT_INVALID_DESCRIPTOR_IDENTIFIER, "HY091", "HY091" },
{ STMT_OPTION_NOT_FOR_THE_DRIVER, "HYC00", "HYC00" },
{ STMT_FETCH_OUT_OF_RANGE, "HY106", "S1106" },
{ STMT_COUNT_FIELD_INCORRECT, "07002", "07002" },
{ STMT_INVALID_NULL_ARG, "HY009", "S1009" },
{ STMT_NO_RESPONSE, "08S01", "08S01" },
{ STMT_COMMUNICATION_ERROR, "08S01", "08S01" }
};
static PG_ErrorInfo *
SC_create_errorinfo(const StatementClass *self, PG_ErrorInfo *pgerror_fail_safe)
{
QResultClass *res = SC_get_Curres(self);
ConnectionClass *conn = SC_get_conn(self);
Int4 errornum;
size_t pos;
BOOL resmsg = FALSE, detailmsg = FALSE, msgend = FALSE;
BOOL looponce, loopend;
char msg[4096], *wmsg;
char *ermsg = NULL, *sqlstate = NULL;
PG_ErrorInfo *pgerror;
if (self->pgerror)
return self->pgerror;
errornum = self->__error_number;
if (errornum == 0)
return NULL;
looponce = (SC_get_Result(self) != res);
msg[0] = '\0';
for (loopend = FALSE; (NULL != res) && !loopend; res = res->next)
{
if (looponce)
loopend = TRUE;
if ('\0' != res->sqlstate[0])
{
if (NULL != sqlstate && strnicmp(res->sqlstate, "00", 2) == 0)
continue;
sqlstate = res->sqlstate;
if ('0' != sqlstate[0] ||
'1' < sqlstate[1])
loopend = TRUE;
}
if (NULL != res->message)
{
STRCPY_FIXED(msg, res->message);
detailmsg = resmsg = TRUE;
}
else if (NULL != res->messageref)
{
STRCPY_FIXED(msg, res->messageref);
detailmsg = resmsg = TRUE;
}
if (msg[0])
ermsg = msg;
else if (QR_get_notice(res))
{
char *notice = QR_get_notice(res);
size_t len = strlen(notice);
if (len < sizeof(msg))
{
memcpy(msg, notice, len);
msg[len] = '\0';
ermsg = msg;
}
else
{
ermsg = notice;
msgend = TRUE;
}
}
}
if (!msgend && (wmsg = SC_get_errormsg(self)) && wmsg[0])
{
pos = strlen(msg);
snprintf(&msg[pos], sizeof(msg) - pos, "%s%s",
detailmsg ? ";\n" : "",
wmsg);
ermsg = msg;
detailmsg = TRUE;
}
if (!self->ref_CC_error)
msgend = TRUE;
if (conn && !msgend)
{
if (!resmsg && (wmsg = CC_get_errormsg(conn)) && wmsg[0] != '\0')
{
pos = strlen(msg);
snprintf(&msg[pos], sizeof(msg) - pos,
";\n%s", CC_get_errormsg(conn));
}
ermsg = msg;
}
pgerror = ER_Constructor(self->__error_number, ermsg);
if (!pgerror)
{
if (pgerror_fail_safe)
{
memset(pgerror_fail_safe, 0, sizeof(*pgerror_fail_safe));
pgerror = pgerror_fail_safe;
pgerror->status = self->__error_number;
pgerror->errorsize = sizeof(pgerror->__error_message);
STRCPY_FIXED(pgerror->__error_message, ermsg);
pgerror->recsize = -1;
}
else
return NULL;
}
if (sqlstate)
STRCPY_FIXED(pgerror->sqlstate, sqlstate);
else if (conn)
{
if (!msgend && conn->sqlstate[0])
STRCPY_FIXED(pgerror->sqlstate, conn->sqlstate);
else
{
EnvironmentClass *env = (EnvironmentClass *) CC_get_env(conn);
errornum -= LOWEST_STMT_ERROR;
if (errornum < 0 ||
errornum >= sizeof(Statement_sqlstate) / sizeof(Statement_sqlstate[0]))
{
errornum = 1 - LOWEST_STMT_ERROR;
}
STRCPY_FIXED(pgerror->sqlstate, EN_is_odbc3(env) ?
Statement_sqlstate[errornum].ver3str :
Statement_sqlstate[errornum].ver2str);
}
}
return pgerror;
}
StatementClass *SC_get_ancestor(StatementClass *stmt)
{
StatementClass *child = stmt, *parent;
MYLOG(DETAIL_LOG_LEVEL, "entering stmt=%p\n", stmt);
for (child = stmt, parent = child->execute_parent; parent; child = parent, parent = child->execute_parent)
{
MYLOG(DETAIL_LOG_LEVEL, "parent=%p\n", parent);
}
return child;
}
void SC_reset_delegate(RETCODE retcode, StatementClass *stmt)
{
StatementClass *delegate = stmt->execute_delegate;
if (!delegate)
return;
PGAPI_FreeStmt(delegate, SQL_DROP);
}
void
SC_set_error(StatementClass *self, int number, const char *message, const char *func)
{
if (self->__error_message)
free(self->__error_message);
self->__error_number = number;
self->__error_message = message ? strdup(message) : NULL;
if (func && number != STMT_OK && number != STMT_INFO_ONLY)
SC_log_error(func, "", self);
}
void
SC_set_errormsg(StatementClass *self, const char *message)
{
if (self->__error_message)
free(self->__error_message);
self->__error_message = message ? strdup(message) : NULL;
}
void
SC_replace_error_with_res(StatementClass *self, int number, const char *message, const QResultClass *from_res, BOOL check)
{
QResultClass *self_res;
BOOL repstate;
MYLOG(DETAIL_LOG_LEVEL, "entering %p->%p check=%i\n", from_res ,self, check);
if (check)
{
if (0 == number) return;
if (0 > number && /* SQL_SUCCESS_WITH_INFO */
0 < self->__error_number)
return;
}
if (!from_res)
return;
self->__error_number = number;
if (!check || message)
{
if (self->__error_message)
free(self->__error_message);
self->__error_message = message ? strdup(message) : NULL;
}
if (self->pgerror)
{
ER_Destructor(self->pgerror);
self->pgerror = NULL;
}
self_res = SC_get_Curres(self);
if (!self_res) return;
if (self_res == from_res) return;
QR_add_message(self_res, QR_get_message(from_res));
QR_add_notice(self_res, QR_get_notice(from_res));
repstate = FALSE;
if (!check)
repstate = TRUE;
else if (from_res->sqlstate[0])
{
if (!self_res->sqlstate[0] || strncmp(self_res->sqlstate, "00", 2) == 0)
repstate = TRUE;
else if (strncmp(from_res->sqlstate, "01", 2) >= 0)
repstate = TRUE;
}
if (repstate)
STRCPY_FIXED(self_res->sqlstate, from_res->sqlstate);
}
void
SC_error_copy(StatementClass *self, const StatementClass *from, BOOL check)
{
QResultClass *self_res, *from_res;
BOOL repstate;
MYLOG(DETAIL_LOG_LEVEL, "entering %p->%p check=%i\n", from ,self, check);
if (!from) return; /* for safety */
if (self == from) return; /* for safety */
if (check)
{
if (0 == from->__error_number) /* SQL_SUCCESS */
return;
if (0 > from->__error_number && /* SQL_SUCCESS_WITH_INFO */
0 < self->__error_number)
return;
}
self->__error_number = from->__error_number;
if (!check || from->__error_message)
{
if (self->__error_message)
free(self->__error_message);
self->__error_message = from->__error_message ? strdup(from->__error_message) : NULL;
}
if (self->pgerror)
{
ER_Destructor(self->pgerror);
self->pgerror = NULL;
}
self_res = SC_get_Curres(self);
from_res = SC_get_Curres(from);
if (!self_res || !from_res)
return;
QR_add_message(self_res, QR_get_message(from_res));
QR_add_notice(self_res, QR_get_notice(from_res));
repstate = FALSE;
if (!check)
repstate = TRUE;
else if (from_res->sqlstate[0])
{
if (!self_res->sqlstate[0] || strncmp(self_res->sqlstate, "00", 2) == 0)
repstate = TRUE;
else if (strncmp(from_res->sqlstate, "01", 2) >= 0)
repstate = TRUE;
}
if (repstate)
STRCPY_FIXED(self_res->sqlstate, from_res->sqlstate);
}
void
SC_full_error_copy(StatementClass *self, const StatementClass *from, BOOL allres)
{
PG_ErrorInfo *pgerror;
MYLOG(DETAIL_LOG_LEVEL, "entering %p->%p\n", from ,self);
if (!from) return; /* for safety */
if (self == from) return; /* for safety */
if (self->__error_message)
{
free(self->__error_message);
self->__error_message = NULL;
}
if (from->__error_message)
self->__error_message = strdup(from->__error_message);
self->__error_number = from->__error_number;
if (from->pgerror)
{
if (self->pgerror)
ER_Destructor(self->pgerror);
self->pgerror = ER_Dup(from->pgerror);
return;
}
else if (!allres)
return;
pgerror = SC_create_errorinfo(from, NULL);
if (!pgerror || !pgerror->__error_message[0])
{
ER_Destructor(pgerror);
return;
}
if (self->pgerror)
ER_Destructor(self->pgerror);
self->pgerror = pgerror;
}
/* Returns the next SQL error information. */
RETCODE SQL_API
PGAPI_StmtError(SQLHSTMT hstmt,
SQLSMALLINT RecNumber,
SQLCHAR * szSqlState,
SQLINTEGER * pfNativeError,
SQLCHAR * szErrorMsg,
SQLSMALLINT cbErrorMsgMax,
SQLSMALLINT * pcbErrorMsg,
UWORD flag)
{
/* CC: return an error of a hdesc */
PG_ErrorInfo *pgerror, error;
StatementClass *stmt = (StatementClass *) hstmt;
int errnum = SC_get_errornumber(stmt);
if (pgerror = SC_create_errorinfo(stmt, &error), NULL == pgerror)
return SQL_NO_DATA_FOUND;
if (pgerror != &error)
stmt->pgerror = pgerror;
if (STMT_NO_MEMORY_ERROR == errnum &&
!pgerror->__error_message[0])
STRCPY_FIXED(pgerror->__error_message, "Memory Allocation Error??");
return ER_ReturnError(pgerror, RecNumber, szSqlState,
pfNativeError, szErrorMsg, cbErrorMsgMax,
pcbErrorMsg, flag);
}
time_t
SC_get_time(StatementClass *stmt)
{
if (!stmt)
return time(NULL);
if (0 == stmt->stmt_time)
stmt->stmt_time = time(NULL);
return stmt->stmt_time;
}
struct tm *
SC_get_localtime(StatementClass *stmt)
{
#ifndef HAVE_LOCALTIME_R
struct tm * tim;
#endif /* HAVE_LOCALTIME_R */
if (stmt->localtime.tm_sec < 0)
{
SC_get_time(stmt);
#ifdef HAVE_LOCALTIME_R
localtime_r(&stmt->stmt_time, &(stmt->localtime));
#else
tim = localtime(&stmt->stmt_time);
stmt->localtime = *tim;
#endif /* HAVE_LOCALTIME_R */
}
return &(stmt->localtime);
}
RETCODE
SC_fetch(StatementClass *self)
{
CSTR func = "SC_fetch";
QResultClass *res = SC_get_Curres(self);
ARDFields *opts;
GetDataInfo *gdata;
int retval;
RETCODE result;
Int2 num_cols,
lf;
OID type;
int atttypmod;
char *value;
ColumnInfoClass *coli;
BindInfoClass *bookmark;
BOOL useCursor;
KeySet *keyset = NULL;
/* TupleField *tupleField; */
MYLOG(DETAIL_LOG_LEVEL, "entering statement=%p res=%p ommitted=0\n", self, res);
self->last_fetch_count = self->last_fetch_count_include_ommitted = 0;
if (!res)
return SQL_ERROR;
coli = QR_get_fields(res); /* the column info */
MYLOG(0, "fetch_cursor=%d, %p->total_read=" FORMAT_LEN "\n", SC_is_fetchcursor(self), res, res->num_total_read);
useCursor = (SC_is_fetchcursor(self) && (NULL != QR_get_cursor(res)));
if (!useCursor)
{
if (self->currTuple >= (Int4) QR_get_num_total_tuples(res) - 1 ||
(self->options.maxRows > 0 && self->currTuple == self->options.maxRows - 1))
{
/*
* if at the end of the tuples, return "no data found" and set
* the cursor past the end of the result set
*/
self->currTuple = QR_get_num_total_tuples(res);
return SQL_NO_DATA_FOUND;
}
MYLOG(0, "**** : non-cursor_result\n");
(self->currTuple)++;
}
else
{
/* read from the cache or the physical next tuple */
retval = QR_next_tuple(res, self);
if (retval < 0)
{
MYLOG(0, "**** : end_tuples\n");
if (QR_get_cursor(res) &&
SQL_CURSOR_FORWARD_ONLY == self->options.cursor_type &&
QR_once_reached_eof(res))
QR_close(res);
return SQL_NO_DATA_FOUND;
}
else if (retval > 0)
(self->currTuple)++; /* all is well */
else
{
SC_set_errorinfo(self, res, 1);
return SQL_ERROR;
}
}
if (QR_haskeyset(res))
{
SQLLEN kres_ridx;
kres_ridx = GIdx2KResIdx(self->currTuple, self, res);
if (kres_ridx >= 0 && kres_ridx < res->num_cached_keys)
{
UWORD pstatus = res->keyset[kres_ridx].status;
MYLOG(DETAIL_LOG_LEVEL, "SC_ pstatus[" FORMAT_LEN "]=%hx fetch_count=" FORMAT_LEN "\n", kres_ridx, pstatus, self->last_fetch_count);
if (0 != (pstatus & (CURS_SELF_DELETING | CURS_SELF_DELETED)))
return SQL_SUCCESS_WITH_INFO;
if (SQL_ROW_DELETED != (pstatus & KEYSET_INFO_PUBLIC) &&
0 != (pstatus & CURS_OTHER_DELETED))
{
return SQL_SUCCESS_WITH_INFO;
}
if (0 != (CURS_NEEDS_REREAD & pstatus))
{
UWORD qcount;
result = SC_pos_reload(self, self->currTuple, &qcount, 0);
if (SQL_ERROR == result)
return result;
pstatus &= ~CURS_NEEDS_REREAD;
}
keyset = res->keyset + kres_ridx;
}
}
num_cols = QR_NumPublicResultCols(res);
result = SQL_SUCCESS;
self->last_fetch_count++;
MYLOG(DETAIL_LOG_LEVEL, "stmt=%p ommitted++\n", self);
self->last_fetch_count_include_ommitted++;
opts = SC_get_ARDF(self);
/*
* If the bookmark column was bound then return a bookmark. Since this
* is used with SQLExtendedFetch, and the rowset size may be greater
* than 1, and an application can use row or column wise binding, use
* the code in copy_and_convert_field() to handle that.
*/
if ((bookmark = opts->bookmark) && bookmark->buffer)
{
SC_set_current_col(self, -1);
SC_Create_bookmark(self, bookmark, self->bind_row, self->currTuple, keyset);
}
if (self->options.retrieve_data == SQL_RD_OFF) /* data isn't required */
return SQL_SUCCESS;
/* The following adjustment would be needed after SQLMoreResults() */
if (opts->allocated < num_cols)
extend_column_bindings(opts, num_cols);
gdata = SC_get_GDTI(self);
if (gdata->allocated != opts->allocated)
extend_getdata_info(gdata, opts->allocated, TRUE);
for (lf = 0; lf < num_cols; lf++)
{
MYLOG(0, "fetch: cols=%d, lf=%d, opts = %p, opts->bindings = %p, buffer[] = %p\n", num_cols, lf, opts, opts->bindings, opts->bindings[lf].buffer);
/* reset for SQLGetData */
GETDATA_RESET(gdata->gdata[lf]);
if (NULL == opts->bindings)
continue;
if (opts->bindings[lf].buffer != NULL)
{
/* this column has a binding */
type = CI_get_oid(coli, lf); /* speed things up */
atttypmod = CI_get_atttypmod(coli, lf); /* speed things up */
MYLOG(0, "type = %d, atttypmod = %d\n", type, atttypmod);
if (useCursor)
value = QR_get_value_backend(res, lf);
else
{
SQLLEN curt = GIdx2CacheIdx(self->currTuple, self, res);
MYLOG(DETAIL_LOG_LEVEL, "%p->base=" FORMAT_LEN " curr=" FORMAT_LEN " st=" FORMAT_LEN " valid=%d\n", res, QR_get_rowstart_in_cache(res), self->currTuple, SC_get_rowset_start(self), QR_has_valid_base(res));
MYLOG(DETAIL_LOG_LEVEL, "curt=" FORMAT_LEN "\n", curt);
value = QR_get_value_backend_row(res, curt, lf);
}
MYLOG(0, "value = '%s'\n", (value == NULL) ? "<NULL>" : value);
retval = copy_and_convert_field_bindinfo(self, type, atttypmod, value, lf);
MYLOG(0, "copy_and_convert: retval = %d\n", retval);
switch (retval)
{
case COPY_OK:
break; /* OK, do next bound column */
case COPY_UNSUPPORTED_TYPE:
SC_set_error(self, STMT_RESTRICTED_DATA_TYPE_ERROR, "Received an unsupported type from Postgres.", func);
result = SQL_ERROR;
break;
case COPY_UNSUPPORTED_CONVERSION:
SC_set_error(self, STMT_RESTRICTED_DATA_TYPE_ERROR, "Couldn't handle the necessary data type conversion.", func);
result = SQL_ERROR;
break;
case COPY_RESULT_TRUNCATED:
SC_set_error(self, STMT_TRUNCATED, "Fetched item was truncated.", func);
MYLOG(DETAIL_LOG_LEVEL, "The %dth item was truncated\n", lf + 1);
MYLOG(DETAIL_LOG_LEVEL, "The buffer size = " FORMAT_LEN, opts->bindings[lf].buflen);
MYLOG(DETAIL_LOG_LEVEL, " and the value is '%s'\n", value);
result = SQL_SUCCESS_WITH_INFO;
break;
case COPY_INVALID_STRING_CONVERSION: /* invalid string */
SC_set_error(self, STMT_STRING_CONVERSION_ERROR, "invalid string conversion occured.", func);
result = SQL_ERROR;
break;
/* error msg already filled in */
case COPY_GENERAL_ERROR:
result = SQL_ERROR;
break;
/* This would not be meaningful in SQLFetch. */
case COPY_NO_DATA_FOUND:
break;
default:
SC_set_error(self, STMT_INTERNAL_ERROR, "Unrecognized return value from copy_and_convert_field.", func);
result = SQL_ERROR;
break;
}
}
}
return result;
}
#include "dlg_specific.h"
RETCODE
SC_execute(StatementClass *self)
{
CSTR func = "SC_execute";
ConnectionClass *conn;
IPDFields *ipdopts;
char was_ok, was_nonfatal;
QResultClass *res = NULL;
Int2 oldstatus,
numcols;
QueryInfo qi;
ConnInfo *ci;
unsigned int qflag = 0;
BOOL is_in_trans, issue_begin, has_out_para;
BOOL use_extended_protocol;
int func_cs_count = 0, i;
BOOL useCursor, isSelectType;
conn = SC_get_conn(self);
ci = &(conn->connInfo);
/* Begin a transaction if one is not already in progress */
/*
* Basically we don't have to begin a transaction in autocommit mode
* because Postgres backend runs in autocomit mode. We issue "BEGIN"
* in the following cases. 1) we use declare/fetch and the statement
* is SELECT (because declare/fetch must be called in a transaction).
* 2) we are in autocommit off state and the statement isn't of type
* OTHER.
*/
#define return DONT_CALL_RETURN_FROM_HERE???
ENTER_INNER_CONN_CS(conn, func_cs_count);
oldstatus = conn->status;
if (CONN_EXECUTING == conn->status)
{
SC_set_error(self, STMT_SEQUENCE_ERROR, "Connection is already in use.", func);
MYLOG(0, "problem with connection\n");
goto cleanup;
}
is_in_trans = CC_is_in_trans(conn);
if ((useCursor = SC_is_fetchcursor(self)))
{
QResultClass *curres = SC_get_Curres(self);
if (NULL != curres &&
curres->dataFilled)
useCursor = (NULL != QR_get_cursor(curres));
}
/* issue BEGIN ? */
issue_begin = TRUE;
if (!self->external)
issue_begin = FALSE;
else if (is_in_trans)
{
issue_begin = FALSE;
if (STMT_TYPE_START == self->statement_type &&
CC_does_autocommit(conn))
{
CC_commit(conn);
is_in_trans = CC_is_in_trans(conn);
}
}
else if (CC_does_autocommit(conn) &&
(!useCursor
/* || SC_is_with_hold(self) thiw would lose the performance */
))
issue_begin = FALSE;
else if (self->statement_type == STMT_TYPE_SPECIAL)
{
/*
* Some utility commands like VACUUM cannot be run in a transaction
* block, so don't begin one even if auto-commit mode is disabled.
*
* An application should never issue an explicit BEGIN when
* auto-commit mode is disabled (probably not even when it's enabled,
* actually). We used to also suppress the implicit BEGIN when the
* statement was of STMT_TYPE_START type, ie. if the application
* issued an explicit BEGIN, but that actually seems like a bad idea.
* First of all, if you issue a BEGIN twice the backend will give a
* warning which can be helpful to spot mistakes in the application
* (because it shouldn't be doing that).
*/
issue_begin = FALSE;
}
if (issue_begin)
{
MYLOG(0, " about to begin a transaction on statement = %p\n", self);
qflag |= GO_INTO_TRANSACTION;
}
/*
* If the session query timeout setting differs from the statement one,
* change it.
*/
if (conn->stmt_timeout_in_effect != self->options.stmt_timeout)
{
char query[64];
SPRINTF_FIXED(query, "SET statement_timeout = %d",
(int) self->options.stmt_timeout * 1000);
res = CC_send_query(conn, query, NULL, 0, NULL);
if (QR_command_maybe_successful(res))
conn->stmt_timeout_in_effect = self->options.stmt_timeout;
QR_Destructor(res);
}
if (!SC_SetExecuting(self, TRUE))
{
SC_set_error(self, STMT_OPERATION_CANCELLED, "Cancel Reuest Accepted", func);
goto cleanup;
}
conn->status = CONN_EXECUTING;
/* If it's a SELECT statement, use a cursor. */
/*
* Note that the declare cursor has already been prepended to the
* statement
*/
/* in copy_statement... */
if (self->stmt_with_params)
use_extended_protocol = FALSE;
else
{
use_extended_protocol = TRUE;
}
isSelectType = (SC_may_use_cursor(self) || self->statement_type == STMT_TYPE_PROCCALL);
if (use_extended_protocol)
{
if (issue_begin)
CC_begin(conn);
res = libpq_bind_and_exec(self);
if (!res)
{
if (SC_get_errornumber(self) <= 0)
{
SC_set_error(self, STMT_NO_RESPONSE, "Could not receive the response, communication down ??", func);
}
goto cleanup;
}
}
else if (isSelectType)
{
char fetch[128];
const char *appendq = NULL;
QueryInfo *qryi = NULL;
qflag |= (SQL_CONCUR_READ_ONLY != self->options.scroll_concurrency ? CREATE_KEYSET : 0);
MYLOG(0, " Sending SELECT statement on stmt=%p, cursor_name='%s' qflag=%d,%d\n", self, SC_cursor_name(self), qflag, self->options.scroll_concurrency);
/* send the declare/select */
if (useCursor)
{
qi.result_in = NULL;
qi.cursor = SC_cursor_name(self);
qi.fetch_size = qi.row_size = ci->drivers.fetch_max;
SPRINTF_FIXED(fetch,
"fetch " FORMAT_LEN " in \"%s\"",
qi.fetch_size, SC_cursor_name(self));
qryi = &qi;
appendq = fetch;
qflag &= (~READ_ONLY_QUERY); /* must be a SAVEPOINT after DECLARE */
}
res = SC_get_Result(self);
if (self->curr_param_result && res)
SC_set_Result(self, res->next);
res = CC_send_query_append(conn, self->stmt_with_params, qryi, qflag, SC_get_ancestor(self), appendq);
if (useCursor && QR_command_maybe_successful(res))
{
/*
* If we sent a DECLARE CURSOR + FETCH, throw away the result of
* the DECLARE CURSOR statement, and only return the result of the
* FETCH to the caller. However, if we received any NOTICEs as
* part of the DECLARE CURSOR, carry those over.
*/
if (appendq)
{
QResultClass *qres, *nres;
for (qres = res; qres;)
{
if (qres->command && strnicmp(qres->command, "fetch", 5) == 0)
{
break;
}
nres = qres->next;
if (nres && QR_get_notice(qres) != NULL)
{
if (QR_command_successful(nres) &&
QR_command_nonfatal(qres))
{
QR_set_rstatus(nres, PORES_NONFATAL_ERROR);
}
QR_add_notice(nres, QR_get_notice(qres));
}
qres->next = NULL;
QR_Destructor(qres);
qres = nres;
/*
* If we received fewer rows than requested, there are no
* more rows to fetch.
*/
if (qres->num_cached_rows < qi.row_size)
QR_set_reached_eof(qres);
}
res = qres;
}
if (res && SC_is_with_hold(self))
QR_set_withhold(res);
}
MYLOG(0, " done sending the query:\n");
}
else
{
/* not a SELECT statement so don't use a cursor */
MYLOG(0, " it's NOT a select statement: stmt=%p\n", self);
res = CC_send_query(conn, self->stmt_with_params, NULL, qflag, SC_get_ancestor(self));
}
if (!isSelectType)
{
/*
* We shouldn't send COMMIT. Postgres backend does the autocommit
* if neccessary. (Zoltan, 04/26/2000)
*/
/*
* Above seems wrong. Even in case of autocommit, started
* transactions must be committed. (Hiroshi, 02/11/2001)
*/
if (CC_is_in_trans(conn))
{
if (!is_in_trans)
CC_set_in_manual_trans(conn);
if (self->external && CC_does_autocommit(conn) && !CC_is_in_manual_trans(conn))
CC_commit(conn);
}
}
if (CONN_DOWN != conn->status)
conn->status = oldstatus;
self->status = STMT_FINISHED;
LEAVE_INNER_CONN_CS(func_cs_count, conn);
/* Check the status of the result */
if (res)
{
was_ok = QR_command_successful(res);
was_nonfatal = QR_command_nonfatal(res);
if (0 < SC_get_errornumber(self))
;
else if (was_ok)
SC_set_errornumber(self, STMT_OK);
else if (was_nonfatal)
SC_set_errornumber(self, STMT_INFO_ONLY);
else
SC_set_errorinfo(self, res, 0);
/* set cursor before the first tuple in the list */
self->currTuple = -1;
SC_set_current_col(self, -1);
SC_set_rowset_start(self, -1, FALSE);
/* issue "ABORT" when query aborted */
if (QR_get_aborted(res))
{
}
else
{
QResultClass *tres;
/* see if the query did return any result columns */
for (tres = res, numcols = 0; !numcols && tres; tres = tres->next)
{
numcols = QR_NumResultCols(tres);
}
/* now allocate the array to hold the binding info */
if (numcols > 0)
{
ARDFields *opts = SC_get_ARDF(self);
extend_column_bindings(opts, numcols);
if (opts->bindings == NULL)
{
QR_Destructor(res);
SC_set_error(self, STMT_NO_MEMORY_ERROR,"Could not get enough free memory to store the binding information", func);
goto cleanup;
}
}
MYLOG(DETAIL_LOG_LEVEL, "!!%p->miscinfo=%x res=%p\n", self, self->miscinfo, res);
/*
* special handling of result for keyset driven cursors.
* Use the columns info of the 1st query and
* user the keyset info of the 2nd query.
*/
if (SQL_CURSOR_KEYSET_DRIVEN == self->options.cursor_type &&
SQL_CONCUR_READ_ONLY != self->options.scroll_concurrency &&
!useCursor)
{
if (tres = res->next, tres)
{
QR_set_fields(tres, QR_get_fields(res));
QR_set_fields(res, NULL);
tres->num_fields = res->num_fields;
res->next = NULL;
QR_Destructor(res);
SC_init_Result(self);
SC_set_Result(self, tres);
res = tres;
}
}
}
}
else
{
/* Bad Error -- The error message will be in the Connection */
if (!conn->pqconn)
SC_set_error(self, STMT_BAD_ERROR, CC_get_errormsg(conn), func);
else if (self->statement_type == STMT_TYPE_CREATE)
{
SC_set_error(self, STMT_CREATE_TABLE_ERROR, "Error creating the table", func);
/*
* This would allow the table to already exists, thus
* appending rows to it. BUT, if the table didn't have the
* same attributes, it would fail. return
* SQL_SUCCESS_WITH_INFO;
*/
}
else
{
SC_set_error(self, STMT_EXEC_ERROR, CC_get_errormsg(conn), func);
}
}
if (!SC_get_Result(self))
SC_set_Result(self, res);
else
{
QResultClass *last;
for (last = SC_get_Result(self); NULL != last->next; last = last->next)
{
if (last == res)
break;
}
if (last != res)
last->next = res;
self->curr_param_result = 1;
}
if (NULL == SC_get_Curres(self))
SC_set_Curres(self, SC_get_Result(self));
ipdopts = SC_get_IPDF(self);
has_out_para = FALSE;
if (self->statement_type == STMT_TYPE_PROCCALL &&
(SC_get_errornumber(self) == STMT_OK ||
SC_get_errornumber(self) == STMT_INFO_ONLY))
{
Int2 io, out;
has_out_para = (CountParameters(self, NULL, &io, &out) > 0);
/*
* I'm not sure if the following REFCUR_SUPPORT stuff is valuable
* or not.
*/
#ifdef REFCUR_SUPPORT
MYLOG(DETAIL_LOG_LEVEL, "!!! numfield=%d field_type=%u\n", QR_NumResultCols(res), QR_get_field_type(res, 0));
if (!has_out_para &&
0 < QR_NumResultCols(res) &&
PG_TYPE_REFCURSOR == QR_get_field_type(res, 0))
{
char fetch[128];
int stmt_type = self->statement_type;
STR_TO_NAME(self->cursor_name, QR_get_value_backend_text(res, 0, 0));
QR_Destructor(res);
SC_init_Result(self);
SC_set_fetchcursor(self);
qi.result_in = NULL;
qi.cursor = SC_cursor_name(self);
qi.cache_size = qi.row_size = ci->drivers.fetch_max;
SPRINTF_FIXED(fetch, "fetch " FORMAT_LEN " in \"%s\"", qi.fetch_size, SC_cursor_name(self));
res = CC_send_query(conn, fetch, &qi, qflag | READ_ONLY_QUERY, SC_get_ancestor(self));
if (NULL != res)
SC_set_Result(self, res);
}
#endif /* REFCUR_SUPPORT */
}
if (has_out_para)
{ /* get the return value of the procedure call */
RETCODE ret;
HSTMT hstmt = (HSTMT) self;
self->bind_row = 0;
ret = SC_fetch(hstmt);
MYLOG(DETAIL_LOG_LEVEL, "!!SC_fetch return =%d\n", ret);
if (SQL_SUCCEEDED(ret))
{
APDFields *apdopts = SC_get_APDF(self);
SQLULEN offset = apdopts->param_offset_ptr ? *apdopts->param_offset_ptr : 0;
ARDFields *ardopts = SC_get_ARDF(self);
const ParameterInfoClass *apara;
const ParameterImplClass *ipara;
int save_bind_size = ardopts->bind_size, gidx, num_p;
ardopts->bind_size = apdopts->param_bind_type;
num_p = self->num_params;
if (ipdopts->allocated < num_p)
num_p = ipdopts->allocated;
for (i = 0, gidx = 0; i < num_p; i++)
{
ipara = ipdopts->parameters + i;
if (ipara->paramType == SQL_PARAM_OUTPUT ||
ipara->paramType == SQL_PARAM_INPUT_OUTPUT)
{
apara = apdopts->parameters + i;
ret = PGAPI_GetData(hstmt, gidx + 1, apara->CType, apara->buffer + offset, apara->buflen, apara->used ? LENADDR_SHIFT(apara->used, offset) : NULL);
if (!SQL_SUCCEEDED(ret))
{
SC_set_error(self, STMT_EXEC_ERROR, "GetData to Procedure return failed.", func);
break;
}
gidx++;
}
}
ardopts->bind_size = save_bind_size; /* restore */
}
else
{
SC_set_error(self, STMT_EXEC_ERROR, "SC_fetch to get a Procedure return failed.", func);
}
}
cleanup:
#undef return
SC_SetExecuting(self, FALSE);
CLEANUP_FUNC_CONN_CS(func_cs_count, conn);
if (CONN_DOWN != conn->status)
conn->status = oldstatus;
/* self->status = STMT_FINISHED; */
if (SC_get_errornumber(self) == STMT_OK)
return SQL_SUCCESS;
else if (SC_get_errornumber(self) < STMT_OK)
return SQL_SUCCESS_WITH_INFO;
else
{
if (!SC_get_errormsg(self) || !SC_get_errormsg(self)[0])
{
if (STMT_NO_MEMORY_ERROR != SC_get_errornumber(self))
SC_set_errormsg(self, "Error while executing the query");
SC_log_error(func, NULL, self);
}
return SQL_ERROR;
}
}
#define CALLBACK_ALLOC_ONCE 4
int enqueueNeedDataCallback(StatementClass *stmt, NeedDataCallfunc func, void *data)
{
if (stmt->num_callbacks >= stmt->allocated_callbacks)
{
SC_REALLOC_return_with_error(stmt->callbacks, NeedDataCallback,
sizeof(NeedDataCallback) * (stmt->allocated_callbacks +
CALLBACK_ALLOC_ONCE), stmt, "NeedDataCallback enqueue error", 0);
stmt->allocated_callbacks += CALLBACK_ALLOC_ONCE;
}
stmt->callbacks[stmt->num_callbacks].func = func;
stmt->callbacks[stmt->num_callbacks].data = data;
stmt->num_callbacks++;
MYLOG(DETAIL_LOG_LEVEL, "stmt=%p, func=%p, count=%d\n", stmt, func, stmt->num_callbacks);
return stmt->num_callbacks;
}
RETCODE dequeueNeedDataCallback(RETCODE retcode, StatementClass *stmt)
{
RETCODE ret;
NeedDataCallfunc func;
void *data;
int i, cnt;
MYLOG(0, "entering ret=%d count=%d\n", retcode, stmt->num_callbacks);
if (SQL_NEED_DATA == retcode)
return retcode;
if (stmt->num_callbacks <= 0)
return retcode;
func = stmt->callbacks[0].func;
data = stmt->callbacks[0].data;
for (i = 1; i < stmt->num_callbacks; i++)
stmt->callbacks[i - 1] = stmt->callbacks[i];
cnt = --stmt->num_callbacks;
ret = (*func)(retcode, data);
free(data);
if (SQL_NEED_DATA != ret && cnt > 0)
ret = dequeueNeedDataCallback(ret, stmt);
return ret;
}
void cancelNeedDataState(StatementClass *stmt)
{
int cnt = stmt->num_callbacks, i;
stmt->num_callbacks = 0;
for (i = 0; i < cnt; i++)
{
if (stmt->callbacks[i].data)
free(stmt->callbacks[i].data);
stmt->callbacks[i].data = NULL;
}
SC_reset_delegate(SQL_ERROR, stmt);
}
void
SC_log_error(const char *func, const char *desc, const StatementClass *self)
{
const char *head;
#define NULLCHECK(a) (a ? a : "(NULL)")
if (self)
{
QResultClass *res = SC_get_Result(self);
const ARDFields *opts = SC_get_ARDF(self);
const APDFields *apdopts = SC_get_APDF(self);
SQLLEN rowsetSize;
const int level = 9;
rowsetSize = (STMT_TRANSITION_EXTENDED_FETCH == self->transition_status ? opts->size_of_rowset_odbc2 : opts->size_of_rowset);
if (SC_get_errornumber(self) <= 0)
head = "STATEMENT WARNING";
else
{
head = "STATEMENT ERROR";
QLOG(level, "%s: func=%s, desc='%s', errnum=%d, errmsg='%s'\n",head, func, desc, self->__error_number, NULLCHECK(self->__error_message));
}
MYLOG(0, "%s: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", head, func, desc, self->__error_number, NULLCHECK(self->__error_message));
if (SC_get_errornumber(self) > 0)
{
QLOG(level, " ------------------------------------------------------------\n");
QLOG(level, " hdbc=%p, stmt=%p, result=%p\n", self->hdbc, self, res);
QLOG(level, " prepare=%d, external=%d\n", self->prepare, self->external);
QLOG(level, " bindings=%p, bindings_allocated=%d\n", opts->bindings, opts->allocated);
QLOG(level, " parameters=%p, parameters_allocated=%d\n", apdopts->parameters, apdopts->allocated);
QLOG(level, " statement_type=%d, statement='%s'\n", self->statement_type, NULLCHECK(self->statement));
QLOG(level, " stmt_with_params='%s'\n", NULLCHECK(self->stmt_with_params));
QLOG(level, " data_at_exec=%d, current_exec_param=%d, put_data=%d\n", self->data_at_exec, self->current_exec_param, self->put_data);
QLOG(level, " currTuple=" FORMAT_LEN ", current_col=%d, lobj_fd=%d\n", self->currTuple, self->current_col, self->lobj_fd);
QLOG(level, " maxRows=" FORMAT_LEN ", rowset_size=" FORMAT_LEN ", keyset_size=" FORMAT_LEN ", cursor_type=%u, scroll_concurrency=%d\n", self->options.maxRows, rowsetSize, self->options.keyset_size, self->options.cursor_type, self->options.scroll_concurrency);
QLOG(level, " cursor_name='%s'\n", SC_cursor_name(self));
QLOG(level, " ----------------QResult Info -------------------------------\n");
if (res)
{
QLOG(level, " fields=%p, backend_tuples=%p, tupleField=%p, conn=%p\n", QR_get_fields(res), res->backend_tuples, res->tupleField, res->conn);
QLOG(level, " fetch_count=" FORMAT_LEN ", num_total_rows=" FORMAT_ULEN ", num_fields=%d, cursor='%s'\n", res->fetch_number, QR_get_num_total_tuples(res), res->num_fields, NULLCHECK(QR_get_cursor(res)));
QLOG(level, " message='%s', command='%s', notice='%s'\n", NULLCHECK(QR_get_message(res)), NULLCHECK(res->command), NULLCHECK(res->notice));
QLOG(level, " status=%d\n", QR_get_rstatus(res));
}
/* Log the connection error if there is one */
CC_log_error(func, desc, self->hdbc);
}
}
else
{
MYLOG(0, "INVALID STATEMENT HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
}
}
/*
* Extended Query
*/
static BOOL
RequestStart(StatementClass *stmt, ConnectionClass *conn, const char *func)
{
BOOL ret = TRUE;
unsigned int svpopt = 0;
#ifdef _HANDLE_ENLIST_IN_DTC_
if (conn->asdum)
CALL_IsolateDtcConn(conn, TRUE);
#endif /* _HANDLE_ENLIST_IN_DTC_ */
if (NULL == conn->pqconn)
{
SC_set_error(stmt, STMT_COMMUNICATION_ERROR, "The connection has been lost", __FUNCTION__);
return SQL_ERROR;
}
if (CC_started_rbpoint(conn))
return TRUE;
if (SC_is_readonly(stmt))
svpopt |= SVPOPT_RDONLY;
if (SQL_ERROR == SetStatementSvp(stmt, svpopt))
{
char emsg[128];
SPRINTF_FIXED(emsg, "internal savepoint error in %s", func);
SC_set_error_if_not_set(stmt, STMT_INTERNAL_ERROR, emsg, func);
return FALSE;
}
/*
* In auto-commit mode, begin a new transaction implicitly if no
* transaction is in progress yet. However, some special statements like
* VACUUM and CLUSTER cannot be run in a transaction block.
*/
if (!CC_is_in_trans(conn) && CC_loves_visible_trans(conn) &&
stmt->statement_type != STMT_TYPE_SPECIAL)
{
ret = CC_begin(conn);
}
return ret;
}
static void log_params(int nParams, const Oid *paramTypes, const UCHAR * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
{
int i, j;
BOOL isBinary;
for (i = 0; i < nParams; i++)
{
isBinary = paramFormats ? paramFormats[i] : FALSE;
if (!paramValues[i])
QLOG(TUPLE_LOG_LEVEL, "\t%c (null) OID=%u\n", isBinary ? 'b' : 't', paramTypes ? paramTypes[i] : 0);
else if (isBinary)
{
QLOG(TUPLE_LOG_LEVEL, "\tb '");
for (j = 0; j < paramLengths[i]; j++)
QPRINTF(TUPLE_LOG_LEVEL, "%02x", paramValues[i][j]);
QPRINTF(TUPLE_LOG_LEVEL, " OID=%u\n", paramTypes ? paramTypes[i] : 0);
}
else
QLOG(TUPLE_LOG_LEVEL, "\tt '%s' OID=%u\n", paramValues[i], paramTypes ? paramTypes[i] : 0);
}
}
static QResultClass *
libpq_bind_and_exec(StatementClass *stmt)
{
CSTR func = "libpq_bind_and_exec";
ConnectionClass *conn = SC_get_conn(stmt);
int nParams;
Oid *paramTypes = NULL;
char **paramValues = NULL;
int *paramLengths = NULL;
int *paramFormats = NULL;
int resultFormat;
PGresult *pgres = NULL;
int pgresstatus;
QResultClass *newres = NULL;
QResultClass *res = NULL;
char *cmdtag;
char *rowcount;
/* It will only be used under Batch Protocol. */
int nBatchCount = 1;
if (!RequestStart(stmt, conn, func))
return NULL;
#ifdef NOT_USED
if (CC_is_in_trans(conn) && !CC_started_rbpoint(conn))
{
if (SQL_ERROR == SetStatementSvp(stmt, 0))
{
SC_set_error_if_not_set(stmt, STMT_INTERNAL_ERROR, "internal savepoint error in build_libpq_bind_params", func);
return NULL;
}
}
#endif /* NOT_USED */
/* 1. Bind */
MYLOG(0, "bind stmt=%p\n", stmt);
if (SC_CanUseBatchProto(stmt))
{
if (!build_libpq_bind_params_batch(stmt,
&nParams,
&nBatchCount,
&paramTypes,
&paramValues,
&paramLengths, &paramFormats,
&resultFormat))
{
goto cleanup;
}
}
else
{
if (!build_libpq_bind_params(stmt,
&nParams,
&paramTypes,
&paramValues,
&paramLengths, &paramFormats,
&resultFormat))
{
if (SC_get_errornumber(stmt) <= 0)
SC_set_errornumber(stmt, STMT_NO_MEMORY_ERROR);
goto cleanup;
}
}
/* 2. Execute */
MYLOG(0, "execute stmt=%p\n", stmt);
if (!SC_is_fetchcursor(stmt))
{
if (stmt->prepared == NOT_YET_PREPARED ||
(stmt->prepared == PREPARED_TEMPORARILY && conn->unnamed_prepared_stmt != stmt))
{
SC_set_error(stmt, STMT_EXEC_ERROR, "about to execute a non-prepared statement", func);
goto cleanup;
}
}
/* 2.5 Prepare and Describe if needed */
if (stmt->prepared == PREPARING_TEMPORARILY ||
(stmt->prepared == PREPARED_TEMPORARILY && conn->unnamed_prepared_stmt != stmt))
{
ProcessedStmt *pstmt;
if (!stmt->processed_statements)
{
if (prepareParametersNoDesc(stmt, FALSE, EXEC_PARAM_CAST) == SQL_ERROR)
goto cleanup;
}
pstmt = stmt->processed_statements;
QLOG(0, "PQexecParams: %p '%s' nParams=%d\n", conn->pqconn, pstmt->query, nParams);
log_params(nParams, paramTypes, (const UCHAR * const *) paramValues, paramLengths, paramFormats, resultFormat);
if (SC_CanUseBatchProto(stmt))
{
pgres = PQexecParamsBatch(conn->pqconn,
pstmt->query,
nParams,
nBatchCount,
paramTypes,
(const char **) paramValues,
paramLengths,
paramFormats,
resultFormat);
}
else
{
pgres = PQexecParams(conn->pqconn,
pstmt->query,
nParams,
paramTypes,
(const char **) paramValues,
paramLengths,
paramFormats,
resultFormat);
}
}
else
{
const char *plan_name;
if (stmt->prepared == PREPARING_PERMANENTLY)
{
if (prepareParameters(stmt, FALSE) == SQL_ERROR)
goto cleanup;
}
/* prepareParameters() set plan name, so don't fetch this earlier */
plan_name = stmt->plan_name ? stmt->plan_name : NULL_STRING;
/* already prepared */
QLOG(0, "PQexecPrepared: %p plan=%s nParams=%d\n", conn->pqconn, plan_name, nParams);
log_params(nParams, paramTypes, (const UCHAR * const *) paramValues, paramLengths, paramFormats, resultFormat);
if (SC_CanUseBatchProto(stmt))
{
/* already prepared */
pgres = PQexecPreparedBatch(conn->pqconn,
plan_name, /* portal name == plan name */
nParams,
nBatchCount,
(const char **) paramValues, paramLengths, paramFormats,
resultFormat);
}
else
{
pgres = PQexecPrepared(conn->pqconn,
plan_name, /* portal name == plan name */
nParams,
(const char **) paramValues, paramLengths, paramFormats,
resultFormat);
}
}
if (stmt->curr_param_result)
{
for (res = SC_get_Result(stmt); NULL != res && NULL != res->next; res = res->next) ;
}
else
res = NULL;
if (!res)
{
newres = res = QR_Constructor();
if (!res)
{
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory while allocating result set", func);
goto cleanup;
}
}
/* 3. Receive results */
MYLOG(DETAIL_LOG_LEVEL, "get_Result=%p %p %d\n", res, SC_get_Result(stmt), stmt->curr_param_result);
pgresstatus = PQresultStatus(pgres);
switch (pgresstatus)
{
case PGRES_COMMAND_OK:
/* portal query command, no tuples returned */
/* read in the return message from the backend */
cmdtag = PQcmdStatus(pgres);
QLOG(0, "\tok: - 'C' - %s\n", cmdtag);
QR_set_command(res, cmdtag);
if (QR_command_successful(res))
QR_set_rstatus(res, PORES_COMMAND_OK);
/* get rowcount */
rowcount = PQcmdTuples(pgres);
if (rowcount && rowcount[0])
res->recent_processed_row_count = atoi(rowcount);
else
res->recent_processed_row_count = -1;
/* Update the transaction status of connection. */
{
PGTransactionStatusType transStatus = PQtransactionStatus(conn->pqconn);
switch(transStatus)
{
case PQTRANS_INTRANS:
case PQTRANS_INERROR:
{
const char *trimedQuery = stmt->statement;
CC_set_in_trans(conn);
/* Trim blank prefix. */
while(*trimedQuery != '\0' && IS_BLANK(*trimedQuery))
trimedQuery ++;
if (strnicmp(trimedQuery, "begin", strlen("begin")) == 0)
{
CC_set_in_manual_trans(conn);
}
else if (strnicmp(trimedQuery, "start", strlen("start")) == 0 &&
IS_BLANK(*(trimedQuery + strlen("start"))))
{
trimedQuery += strlen("start");
while(*trimedQuery != '\0' && IS_BLANK(*trimedQuery))
trimedQuery ++;
if (strnicmp(trimedQuery, "transaction", strlen("transaction")) == 0)
CC_set_in_manual_trans(conn);
}
}
break;
default:
CC_set_no_trans(conn);
CC_set_no_manual_trans(conn);
break;
}
}
break;
case PGRES_EMPTY_QUERY:
/* We return the empty query */
QR_set_rstatus(res, PORES_EMPTY_QUERY);
break;
case PGRES_NONFATAL_ERROR:
handle_pgres_error(conn, pgres, "libpq_bind_and_exec", res, FALSE);
break;
case PGRES_BAD_RESPONSE:
case PGRES_FATAL_ERROR:
handle_pgres_error(conn, pgres, "libpq_bind_and_exec", res, TRUE);
break;
case PGRES_TUPLES_OK:
if (!QR_from_PGresult(res, stmt, conn, NULL, &pgres))
goto cleanup;
if (res->rstatus == PORES_TUPLES_OK && res->notice)
QR_set_rstatus(res, PORES_NONFATAL_ERROR);
break;
case PGRES_COPY_OUT:
case PGRES_COPY_IN:
case PGRES_COPY_BOTH:
default:
/* skip the unexpected response if possible */
QR_set_rstatus(res, PORES_BAD_RESPONSE);
CC_set_error(conn, CONNECTION_BACKEND_CRAZY, "Unexpected protocol character from backend (send_query)", func);
CC_on_abort(conn, CONN_DEAD);
QLOG(0, "PQexecXxxx error: - (%d) - %s\n", pgresstatus, CC_get_errormsg(conn));
break;
}
if (res != newres && NULL != newres)
QR_Destructor(newres);
if (SC_CanUseBatchProto(stmt))
stmt->exec_end_row =
stmt->exec_current_row = SC_get_APDF(stmt)->paramset_size;
cleanup:
if (pgres)
PQclear(pgres);
if (paramValues)
{
int i;
for (i = 0; i < nParams * nBatchCount; i++)
{
if (paramValues[i] != NULL)
free(paramValues[i]);
}
free(paramValues);
}
if (paramTypes)
free(paramTypes);
if (paramLengths)
free(paramLengths);
if (paramFormats)
free(paramFormats);
return res;
}
/*
* Parse a query using libpq.
*
* 'res' is only passed here for error reporting purposes. If an error is
* encountered, it is set in 'res', and the function returns FALSE.
*/
static BOOL
ParseWithLibpq(StatementClass *stmt, const char *plan_name,
const char *query,
Int2 num_params, const char *comment, QResultClass *res)
{
CSTR func = "ParseWithLibpq";
ConnectionClass *conn = SC_get_conn(stmt);
Int4 sta_pidx = -1, end_pidx = -1;
const char *cstatus;
Oid *paramTypes = NULL;
BOOL retval = FALSE;
PGresult *pgres = NULL;
MYLOG(0, "entering plan_name=%s query=%s\n", plan_name, query);
if (!RequestStart(stmt, conn, func))
return FALSE;
if (stmt->discard_output_params)
num_params = 0;
else if (num_params != 0)
{
#ifdef NOT_USED
sta_pidx += stmt->proc_return;
#endif /* NOT_USED */
int pidx;
sta_pidx = stmt->current_exec_param;
if (num_params < 0)
end_pidx = stmt->num_params - 1;
else
end_pidx = sta_pidx + num_params - 1;
#ifdef NOT_USED
num_params = end_pidx - sta_pidx + 1;
#endif /* NOT_USED */
for (num_params = 0, pidx = sta_pidx - 1;;)
{
SC_param_next(stmt, &pidx, NULL, NULL);
if (pidx > end_pidx)
break;
else if (pidx < end_pidx)
{
if (0 == num_params)
sta_pidx = pidx;
num_params++;
}
else
{
num_params++;
break;
}
}
MYLOG(0, "sta_pidx=%d end_pidx=%d num_p=%d\n", sta_pidx, end_pidx, num_params);
}
/*
* We let the server deduce the right datatype for the parameters, except
* for out parameters, which are sent as VOID.
*/
if (num_params > 0)
{
int i;
int j;
IPDFields *ipdopts = SC_get_IPDF(stmt);
paramTypes = malloc(sizeof(Oid) * num_params);
if (paramTypes == NULL)
{
SC_set_errornumber(stmt, STMT_NO_MEMORY_ERROR);
goto cleanup;
}
MYLOG(0, "ipdopts->allocated: %d\n", ipdopts->allocated);
j = 0;
for (i = sta_pidx; i <= end_pidx; i++)
{
if (i < ipdopts->allocated)
{
if (SQL_PARAM_OUTPUT == ipdopts->parameters[i].paramType)
paramTypes[j++] = PG_TYPE_VOID;
else
paramTypes[j++] = sqltype_to_bind_pgtype(conn,
ipdopts->parameters[i].SQLType);
}
else
{
/* Unknown type of parameter. Let the server decide */
paramTypes[j++] = 0;
}
}
}
if (plan_name == NULL || plan_name[0] == '\0')
conn->unnamed_prepared_stmt = NULL;
/* Prepare */
QLOG(0, "PQprepare: %p '%s' plan=%s nParams=%d\n", conn->pqconn, query, plan_name, num_params);
pgres = PQprepare(conn->pqconn, plan_name, query, num_params, paramTypes);
if (PQresultStatus(pgres) != PGRES_COMMAND_OK)
{
handle_pgres_error(conn, pgres, "ParseWithlibpq", res, TRUE);
goto cleanup;
}
cstatus = PQcmdStatus(pgres);
QLOG(0, "\tok: - 'C' - %s\n", cstatus);
if (stmt->plan_name)
SC_set_prepared(stmt, PREPARED_PERMANENTLY);
else
SC_set_prepared(stmt, PREPARED_TEMPORARILY);
if (plan_name == NULL || plan_name[0] == '\0')
conn->unnamed_prepared_stmt = stmt;
retval = TRUE;
cleanup:
if (paramTypes)
free(paramTypes);
if (pgres)
PQclear(pgres);
return retval;
}
/*
* Parse and describe a query using libpq.
*
* Returns an empty result set that has the column information, or error code
* and message, filled in. If 'res' is not NULL, it is the result set
* returned, otherwise a new one is allocated.
*
* NB: The caller must set stmt->current_exec_param before calling this
* function!
*/
QResultClass *
ParseAndDescribeWithLibpq(StatementClass *stmt, const char *plan_name,
const char *query_param,
Int2 num_params, const char *comment,
QResultClass *res)
{
CSTR func = "ParseAndDescribeWithLibpq";
ConnectionClass *conn = SC_get_conn(stmt);
PGresult *pgres = NULL;
int num_p;
Int2 num_discard_params;
IPDFields *ipdopts;
int pidx;
int i;
Oid oid;
SQLSMALLINT paramType;
MYLOG(0, "entering plan_name=%s query=%s\n", plan_name, query_param);
if (!RequestStart(stmt, conn, func))
return NULL;
if (!res)
res = QR_Constructor();
if (!res)
{
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Couldn't allocate memory for query", func);
return NULL;
}
/*
* We need to do Prepare + Describe as two different round-trips to the
* server, while before we switched to use libpq, we used to send a Parse
* and Describe message followed by a single Sync.
*/
if (!ParseWithLibpq(stmt, plan_name, query_param, num_params, comment, res))
goto cleanup;
/* Describe */
QLOG(0, "\tPQdescribePrepared: %p plan_name=%s\n", conn->pqconn, plan_name);
pgres = PQdescribePrepared(conn->pqconn, plan_name);
switch (PQresultStatus(pgres))
{
case PGRES_COMMAND_OK:
QLOG(0, "\tok: - 'C' - %s\n", PQcmdStatus(pgres));
/* expected */
break;
case PGRES_NONFATAL_ERROR:
handle_pgres_error(conn, pgres, "ParseAndDescribeWithLibpq", res, FALSE);
goto cleanup;
case PGRES_FATAL_ERROR:
handle_pgres_error(conn, pgres, "ParseAndDescribeWithLibpq", res, TRUE);
goto cleanup;
default:
/* skip the unexpected response if possible */
CC_set_error(conn, CONNECTION_BACKEND_CRAZY, "Unexpected result from PQdescribePrepared", func);
CC_on_abort(conn, CONN_DEAD);
MYLOG(0, "PQdescribePrepared: error - %s\n", CC_get_errormsg(conn));
goto cleanup;
}
/* Extract parameter information from the result set */
num_p = PQnparams(pgres);
MYLOG(DETAIL_LOG_LEVEL, "num_params=%d info=%d\n", stmt->num_params, num_p);
if (get_qlog() > 0 || get_mylog() > 0)
{
int i;
QLOG(0, "\tnParams=%d", num_p);
for (i = 0; i < num_p; i++)
QPRINTF(0, " %u", PQparamtype(pgres, i));
QPRINTF(0, "\n");
}
num_discard_params = 0;
if (stmt->discard_output_params)
CountParameters(stmt, NULL, NULL, &num_discard_params);
if (num_discard_params < stmt->proc_return)
num_discard_params = stmt->proc_return;
if (num_p + num_discard_params != (int) stmt->num_params)
{
MYLOG(0, "ParamInfo unmatch num_params(=%d) != info(=%d)+discard(=%d)\n", stmt->num_params, num_p, num_discard_params);
/* stmt->num_params = (Int2) num_p + num_discard_params; it's possible in case of multi command queries */
}
ipdopts = SC_get_IPDF(stmt);
extend_iparameter_bindings(ipdopts, stmt->num_params);
pidx = stmt->current_exec_param;
if (pidx >= 0)
pidx--;
for (i = 0; i < num_p; i++)
{
SC_param_next(stmt, &pidx, NULL, NULL);
if (pidx >= stmt->num_params)
{
MYLOG(0, "%dth parameter's position(%d) is out of bound[%d]\n", i, pidx, stmt->num_params);
break;
}
oid = PQparamtype(pgres, i);
paramType = ipdopts->parameters[pidx].paramType;
if (SQL_PARAM_OUTPUT != paramType ||
PG_TYPE_VOID != oid)
PIC_set_pgtype(ipdopts->parameters[pidx], oid);
}
/* Extract Portal information */
QR_set_conn(res, conn);
if (CI_read_fields_from_pgres(QR_get_fields(res), pgres))
{
Int2 dummy1, dummy2;
int cidx;
int num_io_params;
QR_set_rstatus(res, PORES_FIELDS_OK);
res->num_fields = CI_get_num_fields(QR_get_fields(res));
if (QR_haskeyset(res))
res->num_fields -= res->num_key_fields;
num_io_params = CountParameters(stmt, NULL, &dummy1, &dummy2);
if (stmt->proc_return > 0 ||
num_io_params > 0)
{
ipdopts = SC_get_IPDF(stmt);
extend_iparameter_bindings(ipdopts, stmt->num_params);
for (i = 0, cidx = 0; i < stmt->num_params; i++)
{
if (i < stmt->proc_return)
ipdopts->parameters[i].paramType = SQL_PARAM_OUTPUT;
paramType =ipdopts->parameters[i].paramType;
if (SQL_PARAM_OUTPUT == paramType ||
SQL_PARAM_INPUT_OUTPUT == paramType)
{
MYLOG(DETAIL_LOG_LEVEL, "!![%d].PGType %u->%u\n", i, PIC_get_pgtype(ipdopts->parameters[i]), CI_get_oid(QR_get_fields(res), cidx));
PIC_set_pgtype(ipdopts->parameters[i], CI_get_oid(QR_get_fields(res), cidx));
cidx++;
}
}
}
}
else
{
if (NULL == QR_get_fields(res)->coli_array)
{
QR_set_rstatus(res, PORES_NO_MEMORY_ERROR);
QR_set_messageref(res, "Out of memory while reading field information");
}
else
{
QR_set_rstatus(res, PORES_BAD_RESPONSE);
QR_set_message(res, "Error reading field information");
}
}
cleanup:
if (pgres)
PQclear(pgres);
return res;
}
enum {
CancelRequestSet = 1L
,CancelRequestAccepted = (1L << 1)
,CancelCompleted = (1L << 2)
};
/* commonly used for short term lock */
#if defined(WIN_MULTITHREAD_SUPPORT)
extern CRITICAL_SECTION common_cs;
#elif defined(POSIX_MULTITHREAD_SUPPORT)
extern pthread_mutex_t common_cs;
#endif /* WIN_MULTITHREAD_SUPPORT */
BOOL SC_IsExecuting(const StatementClass *self)
{
BOOL ret;
ENTER_COMMON_CS; /* short time blocking */
ret = (STMT_EXECUTING == self->status);
LEAVE_COMMON_CS;
return ret;
}
BOOL SC_SetExecuting(StatementClass *self, BOOL on)
{
BOOL exeSet = FALSE;
ENTER_COMMON_CS; /* short time blocking */
if (on)
{
if (0 == (self->cancel_info & CancelRequestSet))
{
self->status = STMT_EXECUTING;
exeSet = TRUE;
}
}
else
{
self->cancel_info = 0;
self->status = STMT_FINISHED;
exeSet = TRUE;
}
LEAVE_COMMON_CS;
return exeSet;
}
#ifdef NOT_USED
BOOL SC_SetCancelRequest(StatementClass *self)
{
BOOL enteredCS = FALSE;
ENTER_COMMON_CS;
if (0 != (self->cancel_info & CancelCompleted))
;
else if (STMT_EXECUTING == self->status)
{
self->cancel_info |= CancelRequestSet;
}
else
{
/* try to acquire */
if (TRY_ENTER_STMT_CS(self))
enteredCS = TRUE;
else
self->cancel_info |= CancelRequestSet;
}
LEAVE_COMMON_CS;
return enteredCS;
}
#endif /* NOT_USED */
BOOL SC_AcceptedCancelRequest(const StatementClass *self)
{
BOOL shouldCancel = FALSE;
ENTER_COMMON_CS;
if (0 != (self->cancel_info & (CancelRequestSet | CancelRequestAccepted | CancelCompleted)))
shouldCancel = TRUE;
LEAVE_COMMON_CS;
return shouldCancel;
}
static void
SC_set_error_if_not_set(StatementClass *self, int errornumber, const char *errmsg, const char *func)
{
int errnum = SC_get_errornumber(self);
if (errnum <= 0)
{
const char *emsg = SC_get_errormsg(self);
if (emsg && 0 == errnum)
SC_set_errornumber(self, errornumber);
else
SC_set_error(self, errornumber, errmsg, func);
}
}
static void
SC_set_errorinfo(StatementClass *self, QResultClass *res, int errkind)
{
ConnectionClass *conn = SC_get_conn(self);
if (CC_not_connected(conn))
{
SC_set_error_if_not_set(self, STMT_COMMUNICATION_ERROR, "The connection has been lost", __FUNCTION__);
return;
}
switch (QR_get_rstatus(res))
{
case PORES_NO_MEMORY_ERROR:
SC_set_error_if_not_set(self, STMT_NO_MEMORY_ERROR, "memory allocation error???", __FUNCTION__);
break;
case PORES_BAD_RESPONSE:
SC_set_error_if_not_set(self, STMT_COMMUNICATION_ERROR, "communication error occured", __FUNCTION__);
break;
case PORES_INTERNAL_ERROR:
SC_set_error_if_not_set(self, STMT_INTERNAL_ERROR, "Internal error fetching next row", __FUNCTION__);
break;
default:
switch (errkind)
{
case 1:
SC_set_error_if_not_set(self, STMT_EXEC_ERROR, "Error while fetching the next result", __FUNCTION__);
break;
default:
SC_set_error_if_not_set(self, STMT_EXEC_ERROR, "Error while executing the query", __FUNCTION__);
break;
}
break;
}
}
/*
* Before 9.6, the driver offered very simple bookmark support -- it is just
* the current row number.
* Now the driver offers more verbose bookmarks which contain KeySet informations
* (CTID (+ OID)). Though they consume 12bytes(row number + CTID) or 16bytes
* (row number + CTID + OID), they are useful in declare/fetch mode.
*/
PG_BM SC_Resolve_bookmark(const ARDFields *opts, Int4 idx)
{
BindInfoClass *bookmark;
SQLLEN *used;
SQLULEN offset;
SQLUINTEGER bind_size;
size_t cpylen = sizeof(Int4);
PG_BM pg_bm;
bookmark = opts->bookmark;
offset = opts->row_offset_ptr ? *(opts->row_offset_ptr) : 0;
bind_size = opts->bind_size;
memset(&pg_bm, 0, sizeof(pg_bm));
if (used = bookmark->used, used != NULL)
{
used = LENADDR_SHIFT(used, offset);
if (bind_size > 0)
used = LENADDR_SHIFT(used, idx * bind_size);
else
used = LENADDR_SHIFT(used, idx * sizeof(SQLLEN));
if (*used >= sizeof(pg_bm))
cpylen = sizeof(pg_bm);
else if (*used >= 12)
cpylen = 12;
MYLOG(0, "used=" FORMAT_LEN " cpylen=" FORMAT_SIZE_T "\n", *used, cpylen);
}
memcpy(&pg_bm, CALC_BOOKMARK_ADDR(bookmark, offset, bind_size, idx), cpylen);
MYLOG(0, "index=%d block=%d off=%d\n", pg_bm.index, pg_bm.keys.blocknum, pg_bm.keys.offset);
pg_bm.index = SC_resolve_int4_bookmark(pg_bm.index);
return pg_bm;
}
int SC_Create_bookmark(StatementClass *self, BindInfoClass *bookmark, Int4 bind_row, Int4 currTuple, const KeySet *keyset)
{
ARDFields *opts = SC_get_ARDF(self);
SQLUINTEGER bind_size = opts->bind_size;
SQLULEN offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
size_t cvtlen = sizeof(Int4);
PG_BM pg_bm;
MYLOG(0, "entering type=%d buflen=" FORMAT_LEN " buf=%p\n", bookmark->returntype, bookmark->buflen, bookmark->buffer);
memset(&pg_bm, 0, sizeof(pg_bm));
if (SQL_C_BOOKMARK == bookmark->returntype)
;
else if (bookmark->buflen >= sizeof(pg_bm))
cvtlen = sizeof(pg_bm);
else if (bookmark->buflen >= 12)
cvtlen = 12;
pg_bm.index = SC_make_int4_bookmark(currTuple);
if (keyset)
pg_bm.keys = *keyset;
memcpy(CALC_BOOKMARK_ADDR(bookmark, offset, bind_size, bind_row), &pg_bm, cvtlen);
if (bookmark->used)
{
SQLLEN *used = LENADDR_SHIFT(bookmark->used, offset);
if (bind_size > 0)
used = LENADDR_SHIFT(used, bind_row * bind_size);
else
used = LENADDR_SHIFT(used, bind_row * sizeof(SQLLEN));
*used = cvtlen;
}
MYLOG(0, "leaving cvtlen=" FORMAT_SIZE_T " ix(bl,of)=%d(%d,%d)\n", cvtlen, pg_bm.index, pg_bm.keys.blocknum, pg_bm.keys.offset);
return COPY_OK;
}
/* For batch execution:
* - binded parameter sets count should be greater than 1.
* - binded parameter sets should not be all ignored.
* - INSERT/UPDATE/DELETE commands enabled.
*/
BOOL SC_CanUseBatchProto(const StatementClass *self)
{
Int2 st = STMT_TYPE_UNKNOWN;
APDFields *apdopts = NULL;
int usefulParamsCount = 0;
int i = 0;
/* Trigger for protocol is off. */
if (NULL == self->hdbc ||
/* Only exten protocol can trigger batch operations on. */
0 == self->hdbc->connInfo.use_server_side_prepare ||
0 == self->hdbc->connInfo.use_batch_protocol ||
1 != self->hdbc->connInfo.backend_support_batch_proto)
return FALSE;
apdopts = SC_get_APDF(self);
if (apdopts->paramset_size <= 1)
return FALSE;
usefulParamsCount = apdopts->paramset_size;
for (i = 0; i < apdopts->paramset_size; i++)
{
if (apdopts->param_operation_ptr &&
apdopts->param_operation_ptr[i] == SQL_PARAM_IGNORE)
usefulParamsCount --;
}
/* Only one(or zero?) parameter set not ignored by user? */
if (usefulParamsCount <= 1)
return FALSE;
if (self->statement &&
st == STMT_TYPE_UNKNOWN)
st = statement_type(self->statement);
if (st == STMT_TYPE_INSERT ||
st == STMT_TYPE_UPDATE ||
st == STMT_TYPE_DELETE)
return TRUE;
return FALSE;
}
C
1
https://gitee.com/wx_79982d16d3/openGauss-connector-odbc.git
git@gitee.com:wx_79982d16d3/openGauss-connector-odbc.git
wx_79982d16d3
openGauss-connector-odbc
openGauss-connector-odbc
master

搜索帮助