19 Star 39 Fork 47

openGauss / openGauss-connector-odbc

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
results.c 133.00 KB
一键复制 编辑 原始数据 按行查看 历史
zhangxubo 提交于 2022-03-11 17:10 . add 3.0.0 patch file
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136
/*
* Module: results.c
*
* Description: This module contains functions related to
* retrieving result information through the ODBC API.
*
* Classes: n/a
*
* API functions: SQLRowCount, SQLNumResultCols, SQLDescribeCol,
* SQLColAttributes, SQLGetData, SQLFetch, SQLExtendedFetch,
* SQLMoreResults, SQLSetPos, SQLSetScrollOptions(NI),
* SQLSetCursorName, SQLGetCursorName
*
* Comments: See "readme.txt" for copyright and license information.
*-------
*/
#include "psqlodbc.h"
#include <string.h>
#include "misc.h"
#include "dlg_specific.h"
#include "environ.h"
#include "connection.h"
#include "statement.h"
#include "bind.h"
#include "qresult.h"
#include "convert.h"
#include "pgtypes.h"
#include <stdio.h>
#include <limits.h>
#include "pgapifunc.h"
/* Helper macro */
#define getEffectiveOid(conn, fi) pg_true_type((conn), (fi)->columntype, FI_type(fi))
#define NULL_IF_NULL(a) ((a) ? ((const char *)(a)) : "(null)")
RETCODE SQL_API
PGAPI_RowCount(HSTMT hstmt,
SQLLEN * pcrow)
{
CSTR func = "PGAPI_RowCount";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
MYLOG(0, "entering...\n");
if (!stmt)
{
SC_log_error(func, NULL_STRING, NULL);
return SQL_INVALID_HANDLE;
}
if (stmt->proc_return > 0)
{
*pcrow = 0;
MYLOG(DETAIL_LOG_LEVEL, "returning RowCount=" FORMAT_LEN "\n", *pcrow);
return SQL_SUCCESS;
}
res = SC_get_Curres(stmt);
if (res)
{
if (stmt->status != STMT_FINISHED)
{
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't get row count while statement is still executing.", func);
return SQL_ERROR;
}
if (res->recent_processed_row_count >= 0)
{
*pcrow = res->recent_processed_row_count;
MYLOG(0, "**** THE ROWS: *pcrow = " FORMAT_LEN "\n", *pcrow);
return SQL_SUCCESS;
}
else if (QR_NumResultCols(res) > 0)
{
*pcrow = QR_get_cursor(res) ? -1 : QR_get_num_total_tuples(res) - res->dl_count;
MYLOG(0, "RowCount=" FORMAT_LEN "\n", *pcrow);
return SQL_SUCCESS;
}
}
return SQL_SUCCESS;
}
static BOOL
SC_describe_ok(StatementClass *stmt, BOOL build_fi, int col_idx, const char *func)
{
Int2 num_fields;
QResultClass *result;
BOOL exec_ok = TRUE;
num_fields = SC_describe(stmt);
result = SC_get_Curres(stmt);
MYLOG(0, "entering result = %p, status = %d, numcols = %d\n", result, stmt->status, result != NULL ? QR_NumResultCols(result) : -1);
/****if ((!result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE))) ****/
if (!QR_command_maybe_successful(result) || num_fields < 0)
{
/* no query has been executed on this statement */
SC_set_error(stmt, STMT_EXEC_ERROR, "No query has been executed with that handle", func);
exec_ok = FALSE;
}
else if (col_idx >= 0 && col_idx < num_fields)
{
OID reloid = QR_get_relid(result, col_idx);
IRDFields *irdflds = SC_get_IRDF(stmt);
FIELD_INFO *fi;
TABLE_INFO *ti = NULL;
BOOL found = 0;
MYLOG(DETAIL_LOG_LEVEL, "build_fi=%d reloid=%u\n", build_fi, reloid);
if (build_fi && 0 != QR_get_attid(result, col_idx))
{
found = getCOLIfromTI(func, NULL, stmt, reloid, &ti);
if(!found && stmt->hdbc && stmt->hdbc->status == CONN_DOWN)
exec_ok = FALSE;
}
MYLOG(DETAIL_LOG_LEVEL, "nfields=%d\n", irdflds->nfields);
if (irdflds->fi && col_idx < (int) irdflds->nfields)
{
fi = irdflds->fi[col_idx];
if (fi)
{
if (ti)
{
if (NULL == fi->ti)
fi->ti = ti;
if (!FI_is_applicable(fi)
&& 0 != (ti->flags & TI_COLATTRIBUTE))
fi->flag |= FIELD_COL_ATTRIBUTE;
}
fi->basetype = QR_get_field_type(result, col_idx);
if (0 == fi->columntype)
fi->columntype = fi->basetype;
}
}
}
return exec_ok;
}
/*
* This returns the number of columns associated with the database
* attached to "hstmt".
*/
RETCODE SQL_API
PGAPI_NumResultCols(HSTMT hstmt,
SQLSMALLINT * pccol)
{
CSTR func = "PGAPI_NumResultCols";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *result;
char parse_ok;
RETCODE ret = SQL_SUCCESS;
MYLOG(0, "entering...\n");
if (!stmt)
{
SC_log_error(func, NULL_STRING, NULL);
return SQL_INVALID_HANDLE;
}
if (pccol == NULL) {
return SQL_ERROR;
}
SC_clear_error(stmt);
#define return DONT_CALL_RETURN_FROM_HERE???
if (stmt->proc_return > 0)
{
*pccol = 0;
goto cleanup;
}
parse_ok = FALSE;
if (!stmt->catalog_result && SC_is_parse_forced(stmt) && SC_can_parse_statement(stmt))
{
if (SC_parsed_status(stmt) == STMT_PARSE_NONE)
{
MYLOG(0, "calling parse_statement on stmt=%p\n", stmt);
parse_statement(stmt, FALSE);
}
if (SC_parsed_status(stmt) != STMT_PARSE_FATAL)
{
parse_ok = TRUE;
*pccol = SC_get_IRDF(stmt)->nfields;
MYLOG(0, "PARSE: *pccol = %d\n", *pccol);
}
}
if (!parse_ok)
{
if (!SC_describe_ok(stmt, FALSE, -1, func))
{
ret = SQL_ERROR;
goto cleanup;
}
result = SC_get_Curres(stmt);
*pccol = QR_NumPublicResultCols(result);
}
cleanup:
#undef return
return ret;
}
#define USE_FI(fi, unknown) (fi && UNKNOWNS_AS_LONGEST != unknown)
/*
* Return information about the database column the user wants
* information about.
*/
RETCODE SQL_API
PGAPI_DescribeCol(HSTMT hstmt,
SQLUSMALLINT icol,
SQLCHAR * szColName,
SQLSMALLINT cbColNameMax,
SQLSMALLINT * pcbColName,
SQLSMALLINT * pfSqlType,
SQLULEN * pcbColDef,
SQLSMALLINT * pibScale,
SQLSMALLINT * pfNullable)
{
CSTR func = "PGAPI_DescribeCol";
/* gets all the information about a specific column */
StatementClass *stmt = (StatementClass *) hstmt;
ConnectionClass *conn;
IRDFields *irdflds;
QResultClass *res = NULL;
char *col_name = NULL;
OID fieldtype = 0;
SQLLEN column_size = 0;
int unknown_sizes;
SQLINTEGER decimal_digits = 0;
ConnInfo *ci;
FIELD_INFO *fi;
char buf[255];
int len = 0;
RETCODE result = SQL_SUCCESS;
MYLOG(0, "entering.%d..\n", icol);
if (!stmt)
{
SC_log_error(func, NULL_STRING, NULL);
return SQL_INVALID_HANDLE;
}
conn = SC_get_conn(stmt);
ci = &(conn->connInfo);
unknown_sizes = ci->drivers.unknown_sizes;
SC_clear_error(stmt);
#define return DONT_CALL_RETURN_FROM_HERE???
irdflds = SC_get_IRDF(stmt);
if (0 == icol) /* bookmark column */
{
SQLSMALLINT fType = stmt->options.use_bookmarks == SQL_UB_VARIABLE ? SQL_BINARY : SQL_INTEGER;
MYLOG(DETAIL_LOG_LEVEL, "answering bookmark info\n");
if (szColName && cbColNameMax > 0)
*szColName = '\0';
if (pcbColName)
*pcbColName = 0;
if (pfSqlType)
*pfSqlType = fType;
if (pcbColDef)
*pcbColDef = 10;
if (pibScale)
*pibScale = 0;
if (pfNullable)
*pfNullable = SQL_NO_NULLS;
result = SQL_SUCCESS;
goto cleanup;
}
/*
* Dont check for bookmark column. This is the responsibility of the
* driver manager.
*/
icol--; /* use zero based column numbers */
fi = NULL;
if (icol < irdflds->nfields && irdflds->fi)
fi = irdflds->fi[icol];
if (!FI_is_applicable(fi) && !stmt->catalog_result && SC_is_parse_forced(stmt) && SC_can_parse_statement(stmt))
{
if (SC_parsed_status(stmt) == STMT_PARSE_NONE)
{
MYLOG(0, "calling parse_statement on stmt=%p\n", stmt);
parse_statement(stmt, FALSE);
}
MYLOG(0, "PARSE: icol=%d, stmt=%p, stmt->nfld=%d, stmt->fi=%p\n", icol, stmt, irdflds->nfields, irdflds->fi);
if (SC_parsed_status(stmt) != STMT_PARSE_FATAL && irdflds->fi)
{
if (icol < irdflds->nfields)
fi = irdflds->fi[icol];
else
{
SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in DescribeCol.", func);
result = SQL_ERROR;
goto cleanup;
}
MYLOG(0, "getting info for icol=%d\n", icol);
}
}
if (!FI_is_applicable(fi))
{
/*
* If couldn't parse it OR the field being described was not parsed
* (i.e., because it was a function or expression, etc, then do it the
* old fashioned way.
*/
BOOL build_fi = (NULL != pfNullable || NULL != pfSqlType);
fi = NULL;
if (!SC_describe_ok(stmt, build_fi, icol, func))
{
result = SQL_ERROR;
goto cleanup;
}
res = SC_get_Curres(stmt);
if (icol >= QR_NumPublicResultCols(res))
{
SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in DescribeCol.", func);
SPRINTF_FIXED(buf, "Col#=%d, #Cols=%d,%d keys=%d", icol, QR_NumResultCols(res), QR_NumPublicResultCols(res), res->num_key_fields);
SC_log_error(func, buf, stmt);
result = SQL_ERROR;
goto cleanup;
}
if (icol < irdflds->nfields && irdflds->fi)
fi = irdflds->fi[icol];
}
res = SC_get_Curres(stmt);
#ifdef SUPPRESS_LONGEST_ON_CURSORS
if (UNKNOWNS_AS_LONGEST == unknown_sizes)
{
if (QR_once_reached_eof(res))
unknown_sizes = UNKNOWNS_AS_LONGEST;
else
unknown_sizes = UNKNOWNS_AS_MAX;
}
#endif /* SUPPRESS_LONGEST_ON_CURSORS */
/* handle constants */
if (res &&
-2 == QR_get_fieldsize(res, icol))
unknown_sizes = UNKNOWNS_AS_LONGEST;
if (FI_is_applicable(fi))
{
fieldtype = getEffectiveOid(conn, fi);
if (NAME_IS_VALID(fi->column_alias))
col_name = GET_NAME(fi->column_alias);
else
col_name = GET_NAME(fi->column_name);
if (USE_FI(fi, unknown_sizes))
{
column_size = fi->column_size;
decimal_digits = fi->decimal_digits;
}
else
{
column_size = pgtype_column_size(stmt, fieldtype, icol, unknown_sizes);
decimal_digits = pgtype_decimal_digits(stmt, fieldtype, icol);
}
MYLOG(0, "PARSE: fieldtype=%u, col_name='%s', column_size=" FORMAT_LEN "\n", fieldtype, NULL_IF_NULL(col_name), column_size);
}
else
{
col_name = QR_get_fieldname(res, icol);
fieldtype = QR_get_field_type(res, icol);
column_size = pgtype_column_size(stmt, fieldtype, icol, unknown_sizes);
decimal_digits = pgtype_decimal_digits(stmt, fieldtype, icol);
}
MYLOG(0, "col %d fieldname = '%s'\n", icol, NULL_IF_NULL(col_name));
MYLOG(0, "col %d fieldtype = %d\n", icol, fieldtype);
MYLOG(0, "col %d column_size = " FORMAT_LEN "\n", icol, column_size);
result = SQL_SUCCESS;
/*
* COLUMN NAME
*/
len = col_name ? (int) strlen(col_name) : 0;
if (pcbColName)
*pcbColName = len;
if (szColName && cbColNameMax > 0)
{
if (NULL != col_name)
strncpy_null((char *) szColName, col_name, cbColNameMax);
else
szColName[0] = '\0';
if (len >= cbColNameMax)
{
result = SQL_SUCCESS_WITH_INFO;
SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the colName.", func);
}
}
/*
* CONCISE(SQL) TYPE
*/
if (pfSqlType)
{
*pfSqlType = pgtype_to_concise_type(stmt, fieldtype, icol, unknown_sizes);
MYLOG(0, "col %d *pfSqlType = %d\n", icol, *pfSqlType);
}
/*
* COLUMN SIZE(PRECISION in 2.x)
*/
if (pcbColDef)
{
if (column_size < 0)
column_size = 0; /* "I dont know" */
*pcbColDef = column_size;
MYLOG(0, "Col: col %d *pcbColDef = " FORMAT_ULEN "\n", icol, *pcbColDef);
}
/*
* DECIMAL DIGITS(SCALE in 2.x)
*/
if (pibScale)
{
if (decimal_digits < 0)
decimal_digits = 0;
*pibScale = (SQLSMALLINT) decimal_digits;
MYLOG(0, "col %d *pibScale = %d\n", icol, *pibScale);
}
/*
* NULLABILITY
*/
if (pfNullable)
{
if (SC_has_outer_join(stmt))
*pfNullable = TRUE;
else
*pfNullable = fi ? fi->nullable : pgtype_nullable(conn, fieldtype);
MYLOG(0, "col %d *pfNullable = %d\n", icol, *pfNullable);
}
cleanup:
#undef return
return result;
}
/* Returns result column descriptor information for a result set. */
RETCODE SQL_API
PGAPI_ColAttributes(HSTMT hstmt,
SQLUSMALLINT icol,
SQLUSMALLINT fDescType,
PTR rgbDesc,
SQLSMALLINT cbDescMax,
SQLSMALLINT * pcbDesc,
SQLLEN * pfDesc)
{
CSTR func = "PGAPI_ColAttributes";
StatementClass *stmt = (StatementClass *) hstmt;
IRDFields *irdflds;
OID field_type = 0;
Int2 col_idx;
ConnectionClass *conn;
ConnInfo *ci;
int column_size, unknown_sizes;
int cols = 0;
RETCODE result;
const char *p = NULL;
SQLLEN value = 0;
const FIELD_INFO *fi = NULL;
const TABLE_INFO *ti = NULL;
QResultClass *res;
BOOL stmt_updatable;
MYLOG(0, "entering..col=%d %d len=%d.\n", icol, fDescType,
cbDescMax);
if (!stmt)
{
SC_log_error(func, NULL_STRING, NULL);
return SQL_INVALID_HANDLE;
}
stmt_updatable = SC_is_updatable(stmt)
/* The following doesn't seem appropriate for client side cursors
&& stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY
*/
;
if (pcbDesc)
*pcbDesc = 0;
irdflds = SC_get_IRDF(stmt);
conn = SC_get_conn(stmt);
ci = &(conn->connInfo);
/*
* Dont check for bookmark column. This is the responsibility of the
* driver manager. For certain types of arguments, the column number
* is ignored anyway, so it may be 0.
*/
res = SC_get_Curres(stmt);
if (0 == icol && SQL_DESC_COUNT != fDescType) /* bookmark column */
{
MYLOG(DETAIL_LOG_LEVEL, "answering bookmark info\n");
switch (fDescType)
{
case SQL_DESC_OCTET_LENGTH:
if (pfDesc)
*pfDesc = 4;
break;
case SQL_DESC_TYPE:
if (pfDesc)
*pfDesc = stmt->options.use_bookmarks == SQL_UB_VARIABLE ? SQL_BINARY : SQL_INTEGER;
break;
}
return SQL_SUCCESS;
}
col_idx = icol - 1;
unknown_sizes = ci->drivers.unknown_sizes;
/* not appropriate for SQLColAttributes() */
if (stmt->catalog_result)
unknown_sizes = UNKNOWNS_AS_LONGEST;
else if (unknown_sizes == UNKNOWNS_AS_DONTKNOW)
unknown_sizes = UNKNOWNS_AS_MAX;
if (!stmt->catalog_result && SC_is_parse_forced(stmt) && SC_can_parse_statement(stmt))
{
if (SC_parsed_status(stmt) == STMT_PARSE_NONE)
{
MYLOG(0, "calling parse_statement\n");
parse_statement(stmt, FALSE);
}
cols = irdflds->nfields;
/*
* Column Count is a special case. The Column number is ignored
* in this case.
*/
if (fDescType == SQL_DESC_COUNT)
{
if (pfDesc)
*pfDesc = cols;
return SQL_SUCCESS;
}
if (SC_parsed_status(stmt) != STMT_PARSE_FATAL && irdflds->fi)
{
if (col_idx >= cols)
{
SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in ColAttributes.", func);
return SQL_ERROR;
}
}
}
if (col_idx < irdflds->nfields && irdflds->fi)
fi = irdflds->fi[col_idx];
if (FI_is_applicable(fi))
field_type = getEffectiveOid(conn, fi);
else
{
BOOL build_fi = FALSE;
fi = NULL;
switch (fDescType)
{
case SQL_COLUMN_OWNER_NAME:
case SQL_COLUMN_TABLE_NAME:
case SQL_COLUMN_TYPE:
case SQL_COLUMN_TYPE_NAME:
case SQL_COLUMN_AUTO_INCREMENT:
case SQL_DESC_NULLABLE:
case SQL_DESC_BASE_TABLE_NAME:
case SQL_DESC_BASE_COLUMN_NAME:
case SQL_COLUMN_UPDATABLE:
case 1212: /* SQL_CA_SS_COLUMN_KEY ? */
build_fi = TRUE;
break;
}
if (!SC_describe_ok(stmt, build_fi, col_idx, func))
return SQL_ERROR;
res = SC_get_Curres(stmt);
cols = QR_NumPublicResultCols(res);
/*
* Column Count is a special case. The Column number is ignored
* in this case.
*/
if (fDescType == SQL_DESC_COUNT)
{
if (pfDesc)
*pfDesc = cols;
return SQL_SUCCESS;
}
if (col_idx >= cols)
{
SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in ColAttributes.", func);
return SQL_ERROR;
}
field_type = QR_get_field_type(res, col_idx);
if (col_idx < irdflds->nfields && irdflds->fi)
fi = irdflds->fi[col_idx];
}
if (FI_is_applicable(fi))
{
ti = fi->ti;
field_type = getEffectiveOid(conn, fi);
}
MYLOG(0, "col %d field_type=%d fi,ti=%p,%p\n", col_idx, field_type, fi, ti);
#ifdef SUPPRESS_LONGEST_ON_CURSORS
if (UNKNOWNS_AS_LONGEST == unknown_sizes)
{
if (QR_once_reached_eof(res))
unknown_sizes = UNKNOWNS_AS_LONGEST;
else
unknown_sizes = UNKNOWNS_AS_MAX;
}
#endif /* SUPPRESS_LONGEST_ON_CURSORS */
/* handle constants */
if (res &&
-2 == QR_get_fieldsize(res, col_idx))
unknown_sizes = UNKNOWNS_AS_LONGEST;
column_size = (USE_FI(fi, unknown_sizes) && fi->column_size > 0) ? fi->column_size : pgtype_column_size(stmt, field_type, col_idx, unknown_sizes);
switch (fDescType)
{
case SQL_COLUMN_AUTO_INCREMENT: /* == SQL_DESC_AUTO_UNIQUE_VALUE */
if (fi && fi->auto_increment)
value = TRUE;
else
value = pgtype_auto_increment(conn, field_type);
if (value == -1) /* non-numeric becomes FALSE (ODBC Doc) */
value = FALSE;
MYLOG(0, "AUTO_INCREMENT=" FORMAT_LEN "\n", value);
break;
case SQL_COLUMN_CASE_SENSITIVE: /* == SQL_DESC_CASE_SENSITIVE */
value = pgtype_case_sensitive(conn, field_type);
break;
/*
* This special case is handled above.
*
* case SQL_COLUMN_COUNT:
*/
case SQL_COLUMN_DISPLAY_SIZE: /* == SQL_DESC_DISPLAY_SIZE */
value = (USE_FI(fi, unknown_sizes) && 0 != fi->display_size) ? fi->display_size : pgtype_display_size(stmt, field_type, col_idx, unknown_sizes);
MYLOG(0, "col %d, display_size= " FORMAT_LEN "\n", col_idx, value);
break;
case SQL_COLUMN_LABEL: /* == SQL_DESC_LABEL */
if (fi && (NAME_IS_VALID(fi->column_alias)))
{
p = GET_NAME(fi->column_alias);
MYLOG(0, "COLUMN_LABEL = '%s'\n", p);
break;
}
/* otherwise same as column name -- FALL THROUGH!!! */
case SQL_DESC_NAME:
MYLOG(DETAIL_LOG_LEVEL, "fi=%p (alias, name)=", fi);
if (fi)
MYPRINTF(DETAIL_LOG_LEVEL, "(%s,%s)\n", PRINT_NAME(fi->column_alias), PRINT_NAME(fi->column_name));
else
MYPRINTF(DETAIL_LOG_LEVEL, "NULL\n");
p = fi ? (NAME_IS_NULL(fi->column_alias) ? SAFE_NAME(fi->column_name) : GET_NAME(fi->column_alias)) : QR_get_fieldname(res, col_idx);
MYLOG(0, "COLUMN_NAME = '%s'\n", p);
break;
case SQL_COLUMN_LENGTH:
value = (USE_FI(fi, unknown_sizes) && fi->length > 0) ? fi->length : pgtype_buffer_length(stmt, field_type, col_idx, unknown_sizes);
if (0 > value)
/* if (-1 == value) I'm not sure which is right */
value = 0;
MYLOG(0, "col %d, column_length = " FORMAT_LEN "\n", col_idx, value);
break;
case SQL_COLUMN_MONEY: /* == SQL_DESC_FIXED_PREC_SCALE */
value = pgtype_money(conn, field_type);
MYLOG(DETAIL_LOG_LEVEL, "COLUMN_MONEY=" FORMAT_LEN "\n", value);
break;
case SQL_DESC_NULLABLE:
if (SC_has_outer_join(stmt))
value = TRUE;
else
value = fi ? fi->nullable : pgtype_nullable(conn, field_type);
MYLOG(DETAIL_LOG_LEVEL, "COLUMN_NULLABLE=" FORMAT_LEN "\n", value);
break;
case SQL_COLUMN_OWNER_NAME: /* == SQL_DESC_SCHEMA_NAME */
p = ti ? SAFE_NAME(ti->schema_name) : NULL_STRING;
MYLOG(0, "SCHEMA_NAME = '%s'\n", p);
break;
case SQL_COLUMN_PRECISION: /* in 2.x */
value = column_size;
if (value < 0)
value = 0;
MYLOG(0, "col %d, column_size = " FORMAT_LEN "\n", col_idx, value);
break;
case SQL_COLUMN_QUALIFIER_NAME: /* == SQL_DESC_CATALOG_NAME */
p = ti ? CurrCatString(conn) : NULL_STRING; /* empty string means *not supported* */
break;
case SQL_COLUMN_SCALE: /* in 2.x */
value = pgtype_decimal_digits(stmt, field_type, col_idx);
MYLOG(DETAIL_LOG_LEVEL, "COLUMN_SCALE=" FORMAT_LEN "\n", value);
if (value < 0)
value = 0;
break;
case SQL_COLUMN_SEARCHABLE: /* == SQL_DESC_SEARCHABLE */
value = pgtype_searchable(conn, field_type);
break;
case SQL_COLUMN_TABLE_NAME: /* == SQL_DESC_TABLE_NAME */
p = ti ? SAFE_NAME(ti->table_name) : NULL_STRING;
MYLOG(0, "TABLE_NAME = '%s'\n", p);
break;
case SQL_COLUMN_TYPE: /* == SQL_DESC_CONCISE_TYPE */
value = pgtype_to_concise_type(stmt, field_type, col_idx, unknown_sizes);
MYLOG(0, "COLUMN_TYPE=" FORMAT_LEN "\n", value);
break;
case SQL_COLUMN_TYPE_NAME: /* == SQL_DESC_TYPE_NAME */
p = pgtype_to_name(stmt, field_type, col_idx, fi && fi->auto_increment);
break;
case SQL_COLUMN_UNSIGNED: /* == SQL_DESC_UNSINGED */
value = pgtype_unsigned(conn, field_type);
if (value == -1) /* non-numeric becomes TRUE (ODBC Doc) */
value = SQL_TRUE;
break;
case SQL_COLUMN_UPDATABLE: /* == SQL_DESC_UPDATABLE */
/*
* Neither Access or Borland care about this.
*
* if (field_type == PG_TYPE_OID) pfDesc = SQL_ATTR_READONLY;
* else
*/
if (!stmt_updatable)
value = SQL_ATTR_READONLY;
else
value = fi ? (fi->updatable ? SQL_ATTR_WRITE : SQL_ATTR_READONLY) : (QR_get_attid(res, col_idx) > 0 ? SQL_ATTR_WRITE : SQL_ATTR_READONLY);
if (SQL_ATTR_READONLY != value)
{
const char *name = fi ? SAFE_NAME(fi->column_name) : QR_get_fieldname(res, col_idx);
if (stricmp(name, OID_NAME) == 0 ||
stricmp(name, "ctid") == 0 ||
stricmp(name, XMIN_NAME) == 0)
value = SQL_ATTR_READONLY;
else if (conn->ms_jet && fi && fi->auto_increment)
value = SQL_ATTR_READONLY;
}
MYLOG(0, "%s: UPDATEABLE = " FORMAT_LEN "\n", func, value);
break;
case SQL_DESC_BASE_COLUMN_NAME:
p = fi ? SAFE_NAME(fi->column_name) : QR_get_fieldname(res, col_idx);
MYLOG(0, "BASE_COLUMN_NAME = '%s'\n", p);
break;
case SQL_DESC_BASE_TABLE_NAME: /* the same as TABLE_NAME ok ? */
p = ti ? SAFE_NAME(ti->table_name) : NULL_STRING;
MYLOG(0, "BASE_TABLE_NAME = '%s'\n", p);
break;
case SQL_DESC_LENGTH: /* different from SQL_COLUMN_LENGTH */
value = (fi && column_size > 0) ? column_size : pgtype_desclength(stmt, field_type, col_idx, unknown_sizes);
if (-1 == value)
value = 0;
MYLOG(0, "col %d, desc_length = " FORMAT_LEN "\n", col_idx, value);
break;
case SQL_DESC_OCTET_LENGTH:
value = (USE_FI(fi, unknown_sizes) && fi->length > 0) ? fi->length : pgtype_attr_transfer_octet_length(conn, field_type, column_size, unknown_sizes);
if (-1 == value)
value = 0;
MYLOG(0, "col %d, octet_length = " FORMAT_LEN "\n", col_idx, value);
break;
case SQL_DESC_PRECISION: /* different from SQL_COLUMN_PRECISION */
if (value = FI_precision(fi), value <= 0)
value = pgtype_precision(stmt, field_type, col_idx, unknown_sizes);
if (value < 0)
value = 0;
MYLOG(0, "col %d, desc_precision = " FORMAT_LEN "\n", col_idx, value);
break;
case SQL_DESC_SCALE: /* different from SQL_COLUMN_SCALE */
value = pgtype_scale(stmt, field_type, col_idx);
if (value < 0)
value = 0;
break;
case SQL_DESC_LOCAL_TYPE_NAME:
p = pgtype_to_name(stmt, field_type, col_idx, fi && fi->auto_increment);
break;
case SQL_DESC_TYPE:
value = pgtype_to_sqldesctype(stmt, field_type, col_idx, unknown_sizes);
break;
case SQL_DESC_NUM_PREC_RADIX:
value = pgtype_radix(conn, field_type);
break;
case SQL_DESC_LITERAL_PREFIX:
p = pgtype_literal_prefix(conn, field_type);
break;
case SQL_DESC_LITERAL_SUFFIX:
p = pgtype_literal_suffix(conn, field_type);
break;
case SQL_DESC_UNNAMED:
value = (fi && NAME_IS_NULL(fi->column_name) && NAME_IS_NULL(fi->column_alias)) ? SQL_UNNAMED : SQL_NAMED;
break;
case 1211: /* SQL_CA_SS_COLUMN_HIDDEN ? */
value = 0;
break;
case 1212: /* SQL_CA_SS_COLUMN_KEY ? */
if (fi)
{
if (fi->columnkey < 0)
{
SC_set_SS_columnkey(stmt);
}
value = fi->columnkey;
MYLOG(0, "SS_COLUMN_KEY=" FORMAT_LEN "\n", value);
break;
}
SC_set_error(stmt, STMT_OPTION_NOT_FOR_THE_DRIVER, "this request may be for MS SQL Server", func);
return SQL_ERROR;
default:
SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "ColAttribute for this type not implemented yet", func);
return SQL_ERROR;
}
result = SQL_SUCCESS;
if (p)
{ /* char/binary data */
size_t len = strlen(p);
if (rgbDesc)
{
strncpy_null((char *) rgbDesc, p, (size_t) cbDescMax);
if (len >= cbDescMax)
{
result = SQL_SUCCESS_WITH_INFO;
SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the rgbDesc.", func);
}
}
if (pcbDesc)
*pcbDesc = (SQLSMALLINT) len;
}
else
{
/* numeric data */
if (pfDesc)
*pfDesc = value;
}
return result;
}
/* Returns result data for a single column in the current row. */
RETCODE SQL_API
PGAPI_GetData(HSTMT hstmt,
SQLUSMALLINT icol,
SQLSMALLINT fCType,
PTR rgbValue,
SQLLEN cbValueMax,
SQLLEN * pcbValue)
{
CSTR func = "PGAPI_GetData";
QResultClass *res;
StatementClass *stmt = (StatementClass *) hstmt;
UInt2 num_cols;
SQLLEN num_rows;
OID field_type;
int atttypmod;
void *value = NULL;
RETCODE result = SQL_SUCCESS;
char get_bookmark = FALSE;
SQLSMALLINT target_type;
int precision = -1;
#ifdef WITH_UNIXODBC
SQLCHAR dum_rgb[2] = "\0\0";
#endif /* WITH_UNIXODBC */
MYLOG(0, "entering stmt=%p icol=%d\n", stmt, icol);
if (!stmt)
{
SC_log_error(func, NULL_STRING, NULL);
return SQL_INVALID_HANDLE;
}
res = SC_get_Curres(stmt);
if (STMT_EXECUTING == stmt->status)
{
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't get data while statement is still executing.", func);
return SQL_ERROR;
}
if (stmt->status != STMT_FINISHED)
{
SC_set_error(stmt, STMT_STATUS_ERROR, "GetData can only be called after the successful execution on a SQL statement", func);
return SQL_ERROR;
}
#ifdef WITH_UNIXODBC
if (NULL == rgbValue) /* unixODBC allows rgbValue is NULL? */
{
cbValueMax = 0;
rgbValue = dum_rgb; /* to avoid a crash */
}
#endif /* WITH_UNIXODBC */
if (SQL_ARD_TYPE == fCType)
{
ARDFields *opts;
BindInfoClass *binfo = NULL;
opts = SC_get_ARDF(stmt);
if (0 == icol)
binfo = opts->bookmark;
else if (icol <= opts->allocated && opts->bindings)
binfo = &opts->bindings[icol - 1];
if (binfo)
{
target_type = binfo->returntype;
MYLOG(0, "SQL_ARD_TYPE=%d\n", target_type);
precision = binfo->precision;
}
else
{
SC_set_error(stmt, STMT_STATUS_ERROR, "GetData can't determine the type via ARD", func);
return SQL_ERROR;
}
}
else
target_type = fCType;
if (icol == 0)
{
if (stmt->options.use_bookmarks == SQL_UB_OFF)
{
SC_set_error(stmt, STMT_COLNUM_ERROR, "Attempt to retrieve bookmark with bookmark usage disabled", func);
return SQL_ERROR;
}
/* Make sure it is the bookmark data type */
switch (target_type)
{
case SQL_C_BOOKMARK:
case SQL_C_VARBOOKMARK:
break;
default:
MYLOG(DETAIL_LOG_LEVEL, "GetData Column 0 is type %d not of type SQL_C_BOOKMARK\n", target_type);
SC_set_error(stmt, STMT_PROGRAM_TYPE_OUT_OF_RANGE, "Column 0 is not of type SQL_C_BOOKMARK", func);
return SQL_ERROR;
}
get_bookmark = TRUE;
}
else
{
/* use zero-based column numbers */
icol--;
/* make sure the column number is valid */
num_cols = QR_NumPublicResultCols(res);
if (icol >= num_cols)
{
SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number.", func);
return SQL_ERROR;
}
}
#define return DONT_CALL_RETURN_FROM_HERE???
if (!SC_is_fetchcursor(stmt))
{
/* make sure we're positioned on a valid row */
num_rows = QR_get_num_total_tuples(res);
if ((stmt->currTuple < 0) ||
(stmt->currTuple >= num_rows))
{
SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Not positioned on a valid row for GetData.", func);
result = SQL_ERROR;
goto cleanup;
}
MYLOG(0, " num_rows = " FORMAT_LEN "\n", num_rows);
if (!get_bookmark)
{
SQLLEN curt = GIdx2CacheIdx(stmt->currTuple, stmt, res);
value = QR_get_value_backend_row(res, curt, icol);
MYLOG(DETAIL_LOG_LEVEL, "currT=" FORMAT_LEN " base=" FORMAT_LEN " rowset=" FORMAT_LEN "\n", stmt->currTuple, QR_get_rowstart_in_cache(res), SC_get_rowset_start(stmt));
MYLOG(0, " value = '%s'\n", NULL_IF_NULL(value));
}
}
else
{
/* it's a SOCKET result (backend data) */
if (stmt->currTuple == -1 || !res || !res->tupleField)
{
SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Not positioned on a valid row for GetData.", func);
result = SQL_ERROR;
goto cleanup;
}
if (!get_bookmark)
{
/** value = QR_get_value_backend(res, icol); maybe thiw doesn't work */
SQLLEN curt = GIdx2CacheIdx(stmt->currTuple, stmt, res);
value = QR_get_value_backend_row(res, curt, icol);
}
MYLOG(0, " socket: value = '%s'\n", NULL_IF_NULL(value));
}
if (get_bookmark)
{
BOOL contents_get = FALSE;
if (rgbValue)
{
if (SQL_C_BOOKMARK == target_type || sizeof(UInt4) <= cbValueMax)
{
Int4 bookmark = SC_make_int4_bookmark(stmt->currTuple);
contents_get = TRUE;
memcpy(rgbValue, &bookmark, sizeof(bookmark));
}
}
if (pcbValue)
*pcbValue = sizeof(Int4);
if (contents_get)
result = SQL_SUCCESS;
else
{
SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the GetData.", func);
result = SQL_SUCCESS_WITH_INFO;
}
goto cleanup;
}
field_type = QR_get_field_type(res, icol);
atttypmod = QR_get_atttypmod(res, icol);
MYLOG(0, "**** icol = %d, target_type = %d, field_type = %d, value = '%s'\n", icol, target_type, field_type, NULL_IF_NULL(value));
SC_set_current_col(stmt, icol);
result = copy_and_convert_field(stmt, field_type, atttypmod, value,
target_type, precision, rgbValue, cbValueMax, pcbValue, pcbValue);
switch (result)
{
case COPY_OK:
result = SQL_SUCCESS;
break;
case COPY_UNSUPPORTED_TYPE:
SC_set_error(stmt, STMT_RESTRICTED_DATA_TYPE_ERROR, "Received an unsupported type from Postgres.", func);
result = SQL_ERROR;
break;
case COPY_UNSUPPORTED_CONVERSION:
SC_set_error(stmt, 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(stmt, STMT_TRUNCATED, "The buffer was too small for the GetData.", func);
result = SQL_SUCCESS_WITH_INFO;
break;
case COPY_INVALID_STRING_CONVERSION: /* invalid string */
SC_set_error(stmt, STMT_STRING_CONVERSION_ERROR, "invalid string conversion occured.", func);
result = SQL_ERROR;
break;
case COPY_GENERAL_ERROR: /* error msg already filled in */
result = SQL_ERROR;
break;
case COPY_NO_DATA_FOUND:
result = SQL_NO_DATA_FOUND;
break;
default:
SC_set_error(stmt, STMT_INTERNAL_ERROR, "Unrecognized return value from copy_and_convert_field.", func);
result = SQL_ERROR;
break;
}
cleanup:
#undef return
MYLOG(DETAIL_LOG_LEVEL, "leaving %d\n", result);
return result;
}
/*
* Returns data for bound columns in the current row ("hstmt->iCursor"),
* advances the cursor.
*/
RETCODE SQL_API
PGAPI_Fetch(HSTMT hstmt)
{
CSTR func = "PGAPI_Fetch";
StatementClass *stmt = (StatementClass *) hstmt;
ARDFields *opts;
QResultClass *res;
BindInfoClass *bookmark;
RETCODE retval = SQL_SUCCESS;
MYLOG(0, "entering stmt = %p, stmt->result= %p\n", stmt, stmt ? SC_get_Curres(stmt) : NULL);
if (!stmt)
{
SC_log_error(func, NULL_STRING, NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if (!(res = SC_get_Curres(stmt)))
{
SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in PGAPI_Fetch.", func);
return SQL_ERROR;
}
/* Not allowed to bind a bookmark column when using SQLFetch. */
opts = SC_get_ARDF(stmt);
if ((bookmark = opts->bookmark) && bookmark->buffer)
{
SC_set_error(stmt, STMT_COLNUM_ERROR, "Not allowed to bind a bookmark column when using PGAPI_Fetch", func);
return SQL_ERROR;
}
if (stmt->status == STMT_EXECUTING)
{
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't fetch while statement is still executing.", func);
return SQL_ERROR;
}
if (stmt->status != STMT_FINISHED)
{
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Fetch can only be called after the successful execution on a SQL statement", func);
return SQL_ERROR;
}
if (opts->bindings == NULL)
{
if (!SC_may_fetch_rows(stmt))
return SQL_NO_DATA_FOUND;
/* just to avoid a crash if the user insists on calling this */
/* function even if SQL_ExecDirect has reported an Error */
SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Bindings were not allocated properly.", func);
return SQL_ERROR;
}
#define return DONT_CALL_RETURN_FROM_HERE???
if (stmt->rowset_start < 0)
SC_set_rowset_start(stmt, 0, TRUE);
QR_set_reqsize(res, 1);
SC_inc_rowset_start(stmt, stmt->last_fetch_count_include_ommitted);
retval = SC_fetch(stmt);
#undef return
return retval;
}
static RETCODE SQL_API
SC_pos_reload_needed(StatementClass *stmt, SQLULEN req_size, UDWORD flag);
SQLLEN
getNthValid(const QResultClass *res, SQLLEN sta, UWORD orientation,
SQLULEN nth, SQLLEN *nearest)
{
SQLLEN i, num_tuples = QR_get_num_total_tuples(res), nearp;
SQLULEN count;
KeySet *keyset;
if (!QR_once_reached_eof(res))
num_tuples = INT_MAX;
/* Note that the parameter nth is 1-based */
MYLOG(DETAIL_LOG_LEVEL, "get " FORMAT_ULEN "th Valid data from " FORMAT_LEN " to %s [dlt=%d]", nth, sta, orientation == SQL_FETCH_PRIOR ? "backward" : "forward", res->dl_count);
if (0 == res->dl_count)
{
MYPRINTF(DETAIL_LOG_LEVEL, "\n");
if (SQL_FETCH_PRIOR == orientation)
{
if (sta + 1 >= (SQLLEN) nth)
{
*nearest = sta + 1 - nth;
return nth;
}
*nearest = -1;
return -(SQLLEN)(sta + 1);
}
else
{
nearp = sta - 1 + nth;
if (nearp < num_tuples)
{
*nearest = nearp;
return nth;
}
*nearest = num_tuples;
return -(SQLLEN)(num_tuples - sta);
}
}
count = 0;
if (QR_get_cursor(res))
{
SQLLEN *deleted = res->deleted;
SQLLEN delsta;
if (SQL_FETCH_PRIOR == orientation)
{
*nearest = sta + 1 - nth;
delsta = (-1);
MYPRINTF(DETAIL_LOG_LEVEL, "deleted ");
for (i = res->dl_count - 1; i >=0 && *nearest <= deleted[i]; i--)
{
MYPRINTF(DETAIL_LOG_LEVEL, "[" FORMAT_LEN "]=" FORMAT_LEN " ", i, deleted[i]);
if (sta >= deleted[i])
{
(*nearest)--;
if (i > delsta)
delsta = i;
}
}
MYPRINTF(DETAIL_LOG_LEVEL, "nearest=" FORMAT_LEN "\n", *nearest);
if (*nearest < 0)
{
*nearest = -1;
count = sta - delsta;
}
else
return nth;
}
else
{
MYPRINTF(DETAIL_LOG_LEVEL, "\n");
*nearest = sta - 1 + nth;
delsta = res->dl_count;
if (!QR_once_reached_eof(res))
num_tuples = INT_MAX;
for (i = 0; i < res->dl_count && *nearest >= deleted[i]; i++)
{
if (sta <= deleted[i])
{
(*nearest)++;
if (i < delsta)
delsta = i;
}
}
if (*nearest >= num_tuples)
{
*nearest = num_tuples;
count = *nearest - sta + delsta - res->dl_count;
}
else
return nth;
}
}
else if (SQL_FETCH_PRIOR == orientation)
{
for (i = sta, keyset = res->keyset + sta;
i >= 0; i--, keyset--)
{
if (0 == (keyset->status & (CURS_SELF_DELETING | CURS_SELF_DELETED | CURS_OTHER_DELETED)))
{
*nearest = i;
MYPRINTF(DETAIL_LOG_LEVEL, " nearest=" FORMAT_LEN "\n", *nearest);
if (++count == nth)
return count;
}
}
*nearest = -1;
}
else
{
for (i = sta, keyset = res->keyset + sta;
i < num_tuples; i++, keyset++)
{
if (0 == (keyset->status & (CURS_SELF_DELETING | CURS_SELF_DELETED | CURS_OTHER_DELETED)))
{
*nearest = i;
MYPRINTF(DETAIL_LOG_LEVEL, " nearest=" FORMAT_LEN "\n", *nearest);
if (++count == nth)
return count;
}
}
*nearest = num_tuples;
}
MYPRINTF(DETAIL_LOG_LEVEL, " nearest not found\n");
return -(SQLLEN)count;
}
static void
move_cursor_position_if_needed(StatementClass *self, QResultClass *res)
{
SQLLEN move_offset;
/*
* The move direction must be initialized to is_not_moving or
* is_moving_from_the_last in advance.
*/
if (!QR_get_cursor(res))
{
QR_stop_movement(res); /* for safety */
res->move_offset = 0;
return;
}
MYLOG(DETAIL_LOG_LEVEL, "BASE=" FORMAT_LEN " numb=" FORMAT_LEN " curr=" FORMAT_LEN " cursT=" FORMAT_LEN "\n", QR_get_rowstart_in_cache(res), res->num_cached_rows, self->currTuple, res->cursTuple);
/* normal case */
res->move_offset = 0;
move_offset = self->currTuple - res->cursTuple;
if (QR_get_rowstart_in_cache(res) >= 0 &&
QR_get_rowstart_in_cache(res) <= res->num_cached_rows)
{
QR_set_next_in_cache(res, (QR_get_rowstart_in_cache(res) < 0) ? 0 : QR_get_rowstart_in_cache(res));
return;
}
if (0 == move_offset)
return;
if (move_offset > 0)
{
QR_set_move_forward(res);
res->move_offset = move_offset;
}
else
{
QR_set_move_backward(res);
res->move_offset = -move_offset;
}
}
/*
* return NO_DATA_FOUND macros
* save_rowset_start or num_tuples must be defined
*/
#define EXTFETCH_RETURN_BOF(stmt, res) \
{ \
MYLOG(DETAIL_LOG_LEVEL, "RETURN_BOF\n"); \
SC_set_rowset_start(stmt, -1, TRUE); \
stmt->currTuple = -1; \
return SQL_NO_DATA_FOUND; \
}
#define EXTFETCH_RETURN_EOF(stmt, res) \
{ \
MYLOG(DETAIL_LOG_LEVEL, "RETURN_EOF\n"); \
SC_set_rowset_start(stmt, num_tuples, TRUE); \
stmt->currTuple = -1; \
return SQL_NO_DATA_FOUND; \
}
/* This fetchs a block of data (rowset). */
RETCODE SQL_API
PGAPI_ExtendedFetch(HSTMT hstmt,
SQLUSMALLINT fFetchType,
SQLLEN irow,
SQLULEN * pcrow,
SQLUSMALLINT * rgfRowStatus,
SQLLEN bookmark_offset,
SQLLEN rowsetSize)
{
CSTR func = "PGAPI_ExtendedFetch";
StatementClass *stmt = (StatementClass *) hstmt;
ARDFields *opts;
QResultClass *res;
BindInfoClass *bookmark;
SQLLEN num_tuples, i, fc_io;
SQLLEN save_rowset_size, progress_size;
SQLLEN rowset_start, rowset_end = (-1);
RETCODE result = SQL_SUCCESS;
char truncated, error, should_set_rowset_start = FALSE;
SQLLEN currp;
UWORD pstatus;
BOOL currp_is_valid, reached_eof, useCursor;
SQLLEN reqsize = rowsetSize;
MYLOG(0, "entering stmt=%p rowsetSize=" FORMAT_LEN "\n", stmt, rowsetSize);
if (!stmt)
{
SC_log_error(func, NULL_STRING, NULL);
return SQL_INVALID_HANDLE;
}
/* if (SC_is_fetchcursor(stmt) && !stmt->manual_result) */
if (SQL_CURSOR_FORWARD_ONLY == stmt->options.cursor_type)
{
if (fFetchType != SQL_FETCH_NEXT)
{
SC_set_error(stmt, STMT_FETCH_OUT_OF_RANGE, "The fetch type for PGAPI_ExtendedFetch isn't allowed with ForwardOnly cursor.", func);
return SQL_ERROR;
}
}
SC_clear_error(stmt);
if (!(res = SC_get_Curres(stmt)))
{
SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in PGAPI_ExtendedFetch.", func);
return SQL_ERROR;
}
opts = SC_get_ARDF(stmt);
/*
* If a bookmark colunmn is bound but bookmark usage is off, then
* error
*/
if ((bookmark = opts->bookmark) && bookmark->buffer && stmt->options.use_bookmarks == SQL_UB_OFF)
{
SC_set_error(stmt, STMT_COLNUM_ERROR, "Attempt to retrieve bookmark with bookmark usage disabled", func);
return SQL_ERROR;
}
if (stmt->status == STMT_EXECUTING)
{
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't fetch while statement is still executing.", func);
return SQL_ERROR;
}
if (stmt->status != STMT_FINISHED)
{
SC_set_error(stmt, STMT_STATUS_ERROR, "ExtendedFetch can only be called after the successful execution on a SQL statement", func);
return SQL_ERROR;
}
if (opts->bindings == NULL)
{
if (!SC_may_fetch_rows(stmt))
return SQL_NO_DATA_FOUND;
/* just to avoid a crash if the user insists on calling this */
/* function even if SQL_ExecDirect has reported an Error */
SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Bindings were not allocated properly.", func);
return SQL_ERROR;
}
/* Initialize to no rows fetched */
if (rgfRowStatus)
for (i = 0; i < rowsetSize; i++)
*(rgfRowStatus + i) = SQL_ROW_NOROW;
if (pcrow)
*pcrow = 0;
useCursor = (SC_is_fetchcursor(stmt) && NULL != QR_get_cursor(res));
num_tuples = QR_get_num_total_tuples(res);
reached_eof = QR_once_reached_eof(res) && QR_get_cursor(res);
if (useCursor && !reached_eof)
num_tuples = INT_MAX;
MYLOG(DETAIL_LOG_LEVEL, "num_tuples=" FORMAT_LEN "\n", num_tuples);
/* Save and discard the saved rowset size */
save_rowset_size = stmt->save_rowset_size;
stmt->save_rowset_size = -1;
rowset_start = SC_get_rowset_start(stmt);
QR_stop_movement(res);
res->move_offset = 0;
switch (fFetchType)
{
int min_nth;
SQLLEN bidx;
case SQL_FETCH_NEXT:
/*
* From the odbc spec... If positioned before the start of the
* RESULT SET, then this should be equivalent to
* SQL_FETCH_FIRST.
*/
progress_size = (save_rowset_size > 0 ? save_rowset_size : rowsetSize);
if (rowset_start < 0)
SC_set_rowset_start(stmt, 0, TRUE);
else if (res->keyset)
{
if (stmt->last_fetch_count <= progress_size)
{
SC_inc_rowset_start(stmt, stmt->last_fetch_count_include_ommitted);
progress_size -= stmt->last_fetch_count;
}
if (progress_size > 0)
{
if (getNthValid(res, SC_get_rowset_start(stmt),
SQL_FETCH_NEXT, progress_size + 1,
&rowset_start) <= 0)
{
EXTFETCH_RETURN_EOF(stmt, res)
}
else
should_set_rowset_start =TRUE;
}
}
else
SC_inc_rowset_start(stmt, progress_size);
MYLOG(0, "SQL_FETCH_NEXT: num_tuples=" FORMAT_LEN ", currtuple=" FORMAT_LEN ", rowst=" FORMAT_LEN "\n", num_tuples, stmt->currTuple, rowset_start);
break;
case SQL_FETCH_PRIOR:
MYLOG(0, "SQL_FETCH_PRIOR: num_tuples=" FORMAT_LEN ", currtuple=" FORMAT_LEN "\n", num_tuples, stmt->currTuple);
/*
* From the odbc spec... If positioned after the end of the
* RESULT SET, then this should be equivalent to
* SQL_FETCH_LAST.
*/
if (rowset_start <= 0)
{
EXTFETCH_RETURN_BOF(stmt, res)
}
if (rowset_start >= num_tuples &&
rowsetSize > num_tuples)
{
SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET, "fetch prior from eof and before the beginning", func);
SC_set_rowset_start(stmt, 0, TRUE);
}
else if (QR_haskeyset(res))
{
rowset_end = rowset_start - 1;
if (min_nth = getNthValid(res, rowset_end, SQL_FETCH_PRIOR, rowsetSize, &rowset_start), min_nth <= 0)
{
if (min_nth == 0)
{
EXTFETCH_RETURN_BOF(stmt, res)
}
else
{
SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET, "fetch prior and before the beginning", func);
rowset_end = (-1);
SC_set_rowset_start(stmt, 0, TRUE);
}
}
else
{
should_set_rowset_start = TRUE;
reqsize = rowset_end - rowset_start + 1;
}
}
else if (rowset_start < rowsetSize)
{
SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET, "fetch prior from eof and before the beginning", func);
SC_set_rowset_start(stmt, 0, TRUE);
}
else
SC_inc_rowset_start(stmt, -rowsetSize);
break;
case SQL_FETCH_FIRST:
MYLOG(0, "SQL_FETCH_FIRST: num_tuples=" FORMAT_LEN ", currtuple=" FORMAT_LEN "\n", num_tuples, stmt->currTuple);
SC_set_rowset_start(stmt, 0, TRUE);
break;
case SQL_FETCH_LAST:
MYLOG(0, "SQL_FETCH_LAST: num_tuples=" FORMAT_LEN ", currtuple=" FORMAT_LEN "\n", num_tuples, stmt->currTuple);
if (!reached_eof)
{
QR_move_cursor_to_last(res, stmt);
num_tuples = QR_get_num_total_tuples(res);
}
rowset_end = num_tuples - 1;
if (min_nth = getNthValid(res, rowset_end, SQL_FETCH_PRIOR, rowsetSize, &rowset_start), min_nth <= 0)
{
if (min_nth == 0)
{
EXTFETCH_RETURN_BOF(stmt, res)
}
else
{
SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET, "fetch last and before the beginning", func);
rowset_end = (-1);
SC_set_rowset_start(stmt, 0, TRUE);
}
}
else
{
should_set_rowset_start = TRUE;
reqsize = rowset_end - rowset_start + 1;
}
break;
case SQL_FETCH_ABSOLUTE:
MYLOG(0, "SQL_FETCH_ABSOLUTE: num_tuples=" FORMAT_LEN ", currtuple=" FORMAT_LEN ", irow=" FORMAT_LEN "\n", num_tuples, stmt->currTuple, irow);
/* Position before result set, but dont fetch anything */
if (irow == 0)
{
EXTFETCH_RETURN_BOF(stmt, res)
}
/* Position before the desired row */
else if (irow > 0)
{
if (getNthValid(res, 0, SQL_FETCH_NEXT, irow, &rowset_start) <= 0)
{
EXTFETCH_RETURN_EOF(stmt, res)
}
else
should_set_rowset_start = TRUE;
}
/* Position with respect to the end of the result set */
else
{
if (!reached_eof)
{
QR_move_cursor_to_last(res, stmt);
num_tuples = QR_get_num_total_tuples(res);
}
if (min_nth = getNthValid(res, num_tuples - 1, SQL_FETCH_PRIOR, -irow, &rowset_start), min_nth <= 0)
{
if (min_nth == 0)
{
EXTFETCH_RETURN_BOF(stmt, res)
}
else
{
SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET, "fetch absolute and before the beginning", func);
rowset_end = (-1);
SC_set_rowset_start(stmt, 0, TRUE);
}
}
else
should_set_rowset_start = TRUE;
}
break;
case SQL_FETCH_RELATIVE:
/*
* Refresh the current rowset -- not currently implemented,
* but lie anyway
*/
if (irow == 0)
break;
if (irow > 0)
{
if (getNthValid(res, SC_get_rowset_start(stmt) + 1, SQL_FETCH_NEXT, irow, &rowset_start) <= 0)
{
EXTFETCH_RETURN_EOF(stmt, res)
}
else
should_set_rowset_start = TRUE;
}
else
{
if (min_nth = getNthValid(res, SC_get_rowset_start(stmt) - 1, SQL_FETCH_PRIOR, -irow, &rowset_start), min_nth <= 0)
{
if (min_nth == 0)
EXTFETCH_RETURN_BOF(stmt, res)
else
{
SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET, "fetch relative and before the beginning", func);
rowset_end = (-1);
SC_set_rowset_start(stmt, 0, TRUE);
}
}
else
should_set_rowset_start = TRUE;
}
break;
case SQL_FETCH_BOOKMARK:
bidx = SC_resolve_int4_bookmark(irow);
if (bidx < 0)
{
if (!reached_eof)
{
QR_move_cursor_to_last(res, stmt);
num_tuples = QR_get_num_total_tuples(res);
}
bidx = num_tuples - 1 - res->ad_count - bidx;
}
rowset_start = bidx;
if (bookmark_offset >= 0)
{
if (getNthValid(res, bidx, SQL_FETCH_NEXT, bookmark_offset + 1, &rowset_start) <= 0)
{
EXTFETCH_RETURN_EOF(stmt, res)
}
else
should_set_rowset_start = TRUE;
}
else if (getNthValid(res, bidx, SQL_FETCH_PRIOR, 1 - bookmark_offset, &rowset_start) <= 0)
{
stmt->currTuple = -1;
EXTFETCH_RETURN_BOF(stmt, res)
}
else
should_set_rowset_start = TRUE;
break;
default:
SC_set_error(stmt, STMT_FETCH_OUT_OF_RANGE, "Unsupported PGAPI_ExtendedFetch Direction", func);
return SQL_ERROR;
}
/*
* CHECK FOR PROPER CURSOR STATE
*/
/*
* Handle Declare Fetch style specially because the end is not really
* the end...
*/
if (!should_set_rowset_start)
rowset_start = SC_get_rowset_start(stmt);
if (useCursor)
{
if (reached_eof &&
rowset_start >= num_tuples)
{
EXTFETCH_RETURN_EOF(stmt, res)
}
}
else
{
/* If *new* rowset is after the result_set, return no data found */
if (rowset_start >= num_tuples)
{
EXTFETCH_RETURN_EOF(stmt, res)
}
}
/* If *new* rowset is prior to result_set, return no data found */
if (rowset_start < 0)
{
if (rowset_start + rowsetSize <= 0)
{
EXTFETCH_RETURN_BOF(stmt, res)
}
else
{ /* overlap with beginning of result set,
* so get first rowset */
SC_set_rowset_start(stmt, 0, TRUE);
}
should_set_rowset_start = FALSE;
}
#define return DONT_CALL_RETURN_FROM_HERE???
/* set the rowset_start if needed */
if (should_set_rowset_start)
SC_set_rowset_start(stmt, rowset_start, TRUE);
if (rowset_end < 0 && QR_haskeyset(res))
{
getNthValid(res, rowset_start,
SQL_FETCH_NEXT, rowsetSize, &rowset_end);
reqsize = rowset_end - rowset_start + 1;
}
QR_set_reqsize(res, (Int4) reqsize);
/* currTuple is always 1 row prior to the rowset start */
stmt->currTuple = RowIdx2GIdx(-1, stmt);
if (SC_is_fetchcursor(stmt) ||
SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type)
{
move_cursor_position_if_needed(stmt, res);
}
else
QR_set_rowstart_in_cache(res, SC_get_rowset_start(stmt));
if (res->keyset && !QR_get_cursor(res))
{
UDWORD flag = 0;
if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type)
{
if (fFetchType != SQL_FETCH_NEXT ||
QR_get_rowstart_in_cache(res) + reqsize > QR_get_num_cached_tuples(res))
{
flag = 1;
}
}
if (SQL_RD_ON == stmt->options.retrieve_data ||
flag != 0)
{
SC_pos_reload_needed(stmt, reqsize, flag);
}
}
/* Physical Row advancement occurs for each row fetched below */
MYLOG(0, "new currTuple = " FORMAT_LEN "\n", stmt->currTuple);
truncated = error = FALSE;
currp = -1;
stmt->bind_row = 0; /* set the binding location */
result = SC_fetch(stmt);
if (SQL_ERROR == result)
goto cleanup;
if (SQL_NO_DATA_FOUND != result && res->keyset)
{
currp = GIdx2KResIdx(SC_get_rowset_start(stmt), stmt, res);
MYLOG(DETAIL_LOG_LEVEL, "currp=" FORMAT_LEN "\n", currp);
if (currp < 0)
{
result = SQL_ERROR;
MYLOG(0, "rowset_start=" FORMAT_LEN " but currp=" FORMAT_LEN "\n", SC_get_rowset_start(stmt), currp);
SC_set_error(stmt, STMT_INTERNAL_ERROR, "rowset_start not in the keyset", func);
goto cleanup;
}
}
for (i = 0, fc_io = 0; SQL_NO_DATA_FOUND != result && SQL_ERROR != result; currp++)
{
fc_io++;
currp_is_valid = FALSE;
if (res->keyset)
{
if (currp < res->num_cached_keys)
{
currp_is_valid = TRUE;
res->keyset[currp].status &= ~CURS_IN_ROWSET; /* Off the flag first */
}
else
{
MYLOG(0, "Umm current row is out of keyset\n");
break;
}
}
MYLOG(DETAIL_LOG_LEVEL, "ExtFetch result=%d\n", result);
if (currp_is_valid && SQL_SUCCESS_WITH_INFO == result && 0 == stmt->last_fetch_count)
{
MYLOG(DETAIL_LOG_LEVEL, "just skipping deleted row " FORMAT_LEN "\n", currp);
if (rowsetSize - i + fc_io > reqsize)
QR_set_reqsize(res, (Int4) (rowsetSize - i + fc_io));
result = SC_fetch(stmt);
if (SQL_ERROR == result)
break;
continue;
}
/* Determine Function status */
if (result == SQL_SUCCESS_WITH_INFO)
truncated = TRUE;
else if (result == SQL_ERROR)
error = TRUE;
/* Determine Row Status */
if (rgfRowStatus)
{
if (result == SQL_ERROR)
*(rgfRowStatus + i) = SQL_ROW_ERROR;
else if (currp_is_valid)
{
pstatus = (res->keyset[currp].status & KEYSET_INFO_PUBLIC);
if (pstatus != 0 && pstatus != SQL_ROW_ADDED)
{
rgfRowStatus[i] = pstatus;
}
else
rgfRowStatus[i] = SQL_ROW_SUCCESS;
/* refresh the status */
/* if (SQL_ROW_DELETED != pstatus) */
res->keyset[currp].status &= (~KEYSET_INFO_PUBLIC);
}
else
*(rgfRowStatus + i) = SQL_ROW_SUCCESS;
}
if (SQL_ERROR != result && currp_is_valid)
res->keyset[currp].status |= CURS_IN_ROWSET; /* This is the unique place where the CURS_IN_ROWSET bit is turned on */
i++;
if (i >= rowsetSize)
break;
stmt->bind_row = (SQLSETPOSIROW) i; /* set the binding location */
result = SC_fetch(stmt);
}
if (SQL_ERROR == result)
goto cleanup;
/* Save the fetch count for SQLSetPos */
stmt->last_fetch_count = i;
stmt->save_rowset_size = rowsetSize;
stmt->last_fetch_count_include_ommitted = fc_io;
/* Reset next binding row */
stmt->bind_row = 0;
/* Move the cursor position to the first row in the result set. */
stmt->currTuple = RowIdx2GIdx(0, stmt);
/* For declare/fetch, need to reset cursor to beginning of rowset */
if (useCursor)
QR_set_position(res, 0);
/* Set the number of rows retrieved */
if (pcrow)
*pcrow = i;
MYLOG(DETAIL_LOG_LEVEL, "pcrow=" FORMAT_LEN "\n", i);
if (i == 0)
/* Only DeclareFetch should wind up here */
result = SQL_NO_DATA_FOUND;
else if (error)
result = SQL_ERROR;
else if (truncated)
result = SQL_SUCCESS_WITH_INFO;
else if (SC_get_errornumber(stmt) == STMT_POS_BEFORE_RECORDSET)
result = SQL_SUCCESS_WITH_INFO;
else
result = SQL_SUCCESS;
cleanup:
#undef return
return result;
}
/*
* This determines whether there are more results sets available for
* the "hstmt".
*/
/* CC: return SQL_NO_DATA_FOUND since we do not support multiple result sets */
RETCODE SQL_API
PGAPI_MoreResults(HSTMT hstmt)
{
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
RETCODE ret = SQL_SUCCESS;
MYLOG(0, "entering...\n");
res = SC_get_Curres(stmt);
if (res)
{
res = res->next;
SC_set_Curres(stmt, res);
}
if (res)
{
SQLSMALLINT num_p;
if (stmt->multi_statement < 0)
PGAPI_NumParams(stmt, &num_p);
if (stmt->multi_statement > 0)
{
const char *cmdstr;
SC_initialize_cols_info(stmt, FALSE, TRUE);
stmt->statement_type = STMT_TYPE_UNKNOWN;
if (cmdstr = QR_get_command(res), NULL != cmdstr)
stmt->statement_type = statement_type(cmdstr);
stmt->join_info = 0;
SC_clear_parse_method(stmt);
}
stmt->diag_row_count = res->recent_processed_row_count;
SC_set_rowset_start(stmt, -1, FALSE);
stmt->currTuple = -1;
}
else
{
PGAPI_FreeStmt(hstmt, SQL_CLOSE);
ret = SQL_NO_DATA_FOUND;
}
MYLOG(0, "leaving %d\n", ret);
return ret;
}
/*
* Stuff for updatable cursors.
*/
static Int2 getNumResultCols(const QResultClass *res)
{
Int2 res_cols = QR_NumPublicResultCols(res);
return res_cols;
}
static OID getOid(const QResultClass *res, SQLLEN index)
{
return res->keyset[index].oid;
}
static void getTid(const QResultClass *res, SQLLEN index, UInt4 *blocknum, UInt2 *offset)
{
*blocknum = res->keyset[index].blocknum;
*offset = res->keyset[index].offset;
}
static void KeySetSet(const TupleField *tuple, int num_fields, int num_key_fields, KeySet *keyset, BOOL statusInit)
{
if (statusInit)
keyset->status = 0;
sscanf(tuple[num_fields - num_key_fields].value, "(%u,%hu)",
&keyset->blocknum, &keyset->offset);
if (num_key_fields > 1)
{
const char *oval = tuple[num_fields - 1].value;
if ('-' == oval[0])
sscanf(oval, "%d", &keyset->oid);
else
sscanf(oval, "%u", &keyset->oid);
}
else
keyset->oid = 0;
}
static void AddRollback(StatementClass *stmt, QResultClass *res, SQLLEN index, const KeySet *keyset, Int4 dmlcode)
{
ConnectionClass *conn = SC_get_conn(stmt);
Rollback *rollback;
if (!CC_is_in_trans(conn))
return;
MYLOG(DETAIL_LOG_LEVEL, "entering " FORMAT_LEN "(%u,%u) %s\n", index, keyset->blocknum, keyset->offset, dmlcode == SQL_ADD ? "ADD" : (dmlcode == SQL_UPDATE ? "UPDATE" : (dmlcode == SQL_DELETE ? "DELETE" : "REFRESH")));
if (!res->rollback)
{
res->rb_count = 0;
res->rb_alloc = 10;
rollback = res->rollback = malloc(sizeof(Rollback) * res->rb_alloc);
if (!rollback)
{
res->rb_alloc = res->rb_count = 0;
MYLOG(DETAIL_LOG_LEVEL, "out of memory\n");
return;
}
}
else
{
if (res->rb_count >= res->rb_alloc)
{
res->rb_alloc *= 2;
if (rollback = realloc(res->rollback, sizeof(Rollback) * res->rb_alloc), !rollback)
{
res->rb_alloc = res->rb_count = 0;
return;
}
res->rollback = rollback;
}
rollback = res->rollback + res->rb_count;
}
rollback->index = index;
rollback->option = dmlcode;
rollback->offset = 0;
rollback->blocknum = 0;
rollback->oid = 0;
if (keyset)
{
rollback->blocknum = keyset->blocknum;
rollback->offset = keyset->offset;
rollback->oid = keyset->oid;
}
conn->result_uncommitted = 1;
res->rb_count++;
}
SQLLEN ClearCachedRows(TupleField *tuple, int num_fields, SQLLEN num_rows)
{
SQLLEN i;
for (i = 0; i < num_fields * num_rows; i++, tuple++)
{
if (tuple->value)
{
MYLOG(DETAIL_LOG_LEVEL, "freeing tuple[" FORMAT_LEN "][" FORMAT_LEN "].value=%p\n", i / num_fields, i % num_fields, tuple->value);
free(tuple->value);
tuple->value = NULL;
}
tuple->len = -1;
}
return i;
}
SQLLEN ReplaceCachedRows(TupleField *otuple, const TupleField *ituple, int num_fields, SQLLEN num_rows)
{
SQLLEN i;
MYLOG(DETAIL_LOG_LEVEL, "entering %p num_fields=%d num_rows=" FORMAT_LEN "\n", otuple, num_fields, num_rows);
for (i = 0; i < num_fields * num_rows; i++, ituple++, otuple++)
{
if (otuple->value)
{
free(otuple->value);
otuple->value = NULL;
}
if (ituple->value)
{
otuple->value = strdup(ituple->value);
MYLOG(DETAIL_LOG_LEVEL, "[" FORMAT_LEN "," FORMAT_LEN "] %s copied\n", i / num_fields, i % num_fields, (const char *) otuple->value);
}
if (otuple->value)
otuple->len = ituple->len;
else
otuple->len = -1;
}
return i;
}
static
int MoveCachedRows(TupleField *otuple, TupleField *ituple, Int2 num_fields, SQLLEN num_rows)
{
int i;
MYLOG(DETAIL_LOG_LEVEL, "entering %p num_fields=%d num_rows=" FORMAT_LEN "\n", otuple, num_fields, num_rows);
for (i = 0; i < num_fields * num_rows; i++, ituple++, otuple++)
{
if (otuple->value)
{
free(otuple->value);
otuple->value = NULL;
}
if (ituple->value)
{
otuple->value = ituple->value;
ituple->value = NULL;
MYLOG(DETAIL_LOG_LEVEL, "[%d,%d] %s copied\n", i / num_fields, i % num_fields, (const char *) otuple->value);
}
otuple->len = ituple->len;
ituple->len = -1;
}
return i;
}
static const char *
ti_quote(StatementClass *stmt, OID tableoid, char *buf, int buf_size)
{
TABLE_INFO *ti = stmt->ti[0];
pgNAME rNAME;
if (0 == tableoid || !TI_has_subclass(ti))
return quote_table(ti->schema_name, ti->table_name, buf, buf_size);
else if (NAME_IS_VALID(rNAME = TI_From_IH(ti, tableoid)))
return SAFE_NAME(rNAME);
else
{
char query[200];
QResultClass *res;
char *ret = "";
SPRINTF_FIXED(query, "select relname, nspname from pg_class c, pg_namespace n where c.oid=%u and c.relnamespace=n.oid", tableoid);
res = CC_send_query(SC_get_conn(stmt), query, NULL, READ_ONLY_QUERY, stmt);
if (QR_command_maybe_successful(res) &&
QR_get_num_cached_tuples(res) == 1)
{
pgNAME schema_name, table_name;
SET_NAME_DIRECTLY(schema_name, QR_get_value_backend_text(res, 0, 1));
SET_NAME_DIRECTLY(table_name, QR_get_value_backend_text(res, 0, 0));
ret = quote_table(schema_name, table_name, buf, buf_size);
TI_Ins_IH(ti, tableoid, ret);
}
QR_Destructor(res);
return ret;
}
}
static BOOL tupleExists(StatementClass *stmt, const KeySet *keyset)
{
PQExpBufferData selstr = {0};
const TABLE_INFO *ti = stmt->ti[0];
QResultClass *res;
RETCODE ret = FALSE;
const char *bestqual = GET_NAME(ti->bestqual);
char table_fqn[256];
initPQExpBuffer(&selstr);
printfPQExpBuffer(&selstr,
"select 1 from %s where ctid = '(%u,%u)'",
ti_quote(stmt, keyset->oid, table_fqn, sizeof(table_fqn)),
keyset->blocknum, keyset->offset);
if (NULL != bestqual && 0 != keyset->oid && !TI_has_subclass(ti))
{
appendPQExpBuffer(&selstr, " and ");
appendPQExpBuffer(&selstr, bestqual, keyset->oid);
}
if (PQExpBufferDataBroken(selstr))
{
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory in tupleExists()", __FUNCTION__);
goto cleanup;
}
res = CC_send_query(SC_get_conn(stmt), selstr.data, NULL, READ_ONLY_QUERY, NULL);
if (QR_command_maybe_successful(res) && 1 == res->num_cached_rows)
ret = TRUE;
QR_Destructor(res);
cleanup:
if (!PQExpBufferDataBroken(selstr))
termPQExpBuffer(&selstr);
return ret;
}
static BOOL enlargeAdded(QResultClass *res, UInt4 number, const StatementClass *stmt)
{
UInt4 alloc;
int num_fields = res->num_fields;
alloc = res->ad_alloc;
if (0 == alloc)
alloc = number > 10 ? number : 10;
else
while (alloc < number)
{
alloc *= 2;
}
if (alloc <= res->ad_alloc)
return TRUE;
QR_REALLOC_return_with_error(res->added_keyset, KeySet, sizeof(KeySet) * alloc, res, "enlargeAdded failed", FALSE);
if (SQL_CURSOR_KEYSET_DRIVEN != stmt->options.cursor_type)
QR_REALLOC_return_with_error(res->added_tuples, TupleField, sizeof(TupleField) * num_fields * alloc, res, "enlargeAdded failed 2", FALSE);
res->ad_alloc = alloc;
return TRUE;
}
static void AddAdded(StatementClass *stmt, QResultClass *res, SQLLEN index, const TupleField *tuple_added)
{
KeySet *added_keyset, *keyset, keys;
TupleField *added_tuples = NULL, *tuple;
UInt4 ad_count;
Int2 num_fields;
if (!res) return;
num_fields = res->num_fields;
MYLOG(DETAIL_LOG_LEVEL, "entering index=" FORMAT_LEN ", tuple=%p, num_fields=%d\n", index, tuple_added, num_fields);
ad_count = res->ad_count;
res->ad_count++;
if (QR_get_cursor(res))
index = -(SQLLEN)res->ad_count;
if (!tuple_added)
return;
KeySetSet(tuple_added, num_fields + res->num_key_fields, res->num_key_fields, &keys, TRUE);
keys.status = SQL_ROW_ADDED;
if (CC_is_in_trans(SC_get_conn(stmt)))
keys.status |= CURS_SELF_ADDING;
else
keys.status |= CURS_SELF_ADDED;
AddRollback(stmt, res, index, &keys, SQL_ADD);
if (!QR_get_cursor(res))
return;
if (ad_count > 0 && 0 == res->ad_alloc)
return;
if (!enlargeAdded(res, ad_count + 1, stmt))
return;
added_keyset = res->added_keyset;
added_tuples = res->added_tuples;
keyset = added_keyset + ad_count;
*keyset = keys;
if (added_tuples)
{
tuple = added_tuples + num_fields * ad_count;
memset(tuple, 0, sizeof(TupleField) * num_fields);
ReplaceCachedRows(tuple, tuple_added, num_fields, 1);
}
}
static void RemoveAdded(QResultClass *, SQLLEN);
static void RemoveUpdated(QResultClass *, SQLLEN);
static void RemoveUpdatedAfterTheKey(QResultClass *, SQLLEN, const KeySet*);
static void RemoveDeleted(QResultClass *, SQLLEN);
static void RemoveAdded(QResultClass *res, SQLLEN index)
{
SQLLEN rmidx, mv_count;
Int2 num_fields = res->num_fields;
KeySet *added_keyset;
TupleField *added_tuples;
MYLOG(0, "entering index=" FORMAT_LEN "\n", index);
if (index < 0)
rmidx = -index - 1;
else
rmidx = index - res->num_total_read;
if (rmidx >= res->ad_count)
return;
added_keyset = res->added_keyset + rmidx;
added_tuples = res->added_tuples + num_fields * rmidx;
ClearCachedRows(added_tuples, num_fields, 1);
mv_count = res->ad_count - rmidx - 1;
if (mv_count > 0)
{
memmove(added_keyset, added_keyset + 1, mv_count * sizeof(KeySet));
memmove(added_tuples, added_tuples + num_fields, mv_count * num_fields * sizeof(TupleField));
}
RemoveDeleted(res, index);
RemoveUpdated(res, index);
res->ad_count--;
MYLOG(0, "removed=1 count=%d\n", res->ad_count);
}
static void
CommitAdded(QResultClass *res)
{
KeySet *added_keyset;
int i;
UWORD status;
MYLOG(0, "entering res=%p\n", res);
if (!res || !res->added_keyset) return;
added_keyset = res->added_keyset;
for (i = res->ad_count - 1; i >= 0; i--)
{
status = added_keyset[i].status;
if (0 != (status & CURS_SELF_ADDING))
{
status |= CURS_SELF_ADDED;
status &= ~CURS_SELF_ADDING;
}
if (0 != (status & CURS_SELF_UPDATING))
{
status |= CURS_SELF_UPDATED;
status &= ~CURS_SELF_UPDATING;
}
if (0 != (status & CURS_SELF_DELETING))
{
status |= CURS_SELF_DELETED;
status &= ~CURS_SELF_DELETING;
}
if (status != added_keyset[i].status)
{
MYLOG(DETAIL_LOG_LEVEL, "!!Commit Added=" FORMAT_ULEN "(%d)\n", QR_get_num_total_read(res) + i, i);
added_keyset[i].status = status;
}
}
}
static int
AddDeleted(QResultClass *res, SQLULEN index, const KeySet *keyset)
{
int i;
Int2 dl_count, new_alloc;
SQLLEN *deleted;
KeySet *deleted_keyset;
UWORD status;
MYLOG(DETAIL_LOG_LEVEL, "entering " FORMAT_ULEN "\n", index);
dl_count = res->dl_count;
res->dl_count++;
if (!QR_get_cursor(res))
return TRUE;
if (!res->deleted)
{
dl_count = 0;
new_alloc = 10;
QR_MALLOC_return_with_error(res->deleted, SQLLEN, sizeof(SQLLEN) * new_alloc, res, "Deleted index malloc error", FALSE);
QR_MALLOC_return_with_error(res->deleted_keyset, KeySet, sizeof(KeySet) * new_alloc, res, "Deleted keyset malloc error", FALSE);
deleted = res->deleted;
deleted_keyset = res->deleted_keyset;
res->dl_alloc = new_alloc;
}
else
{
if (dl_count >= res->dl_alloc)
{
new_alloc = res->dl_alloc * 2;
res->dl_alloc = 0;
QR_REALLOC_return_with_error(res->deleted, SQLLEN, sizeof(SQLLEN) * new_alloc, res, "Deleted index realloc error", FALSE);
deleted = res->deleted;
QR_REALLOC_return_with_error(res->deleted_keyset, KeySet, sizeof(KeySet) * new_alloc, res, "Deleted KeySet realloc error", FALSE);
deleted_keyset = res->deleted_keyset;
res->dl_alloc = new_alloc;
}
/* sort deleted indexes in ascending order */
for (i = 0, deleted = res->deleted, deleted_keyset = res->deleted_keyset; i < dl_count; i++, deleted++, deleted_keyset++)
{
if (index < *deleted)
break;
}
memmove(deleted + 1, deleted, sizeof(SQLLEN) * (dl_count - i));
memmove(deleted_keyset + 1, deleted_keyset, sizeof(KeySet) * (dl_count - i));
}
*deleted = index;
*deleted_keyset = *keyset;
status = keyset->status;
status &= (~KEYSET_INFO_PUBLIC);
status |= SQL_ROW_DELETED;
if (CC_is_in_trans(QR_get_conn(res)))
{
status |= CURS_SELF_DELETING;
QR_get_conn(res)->result_uncommitted = 1;
}
else
{
status &= ~(CURS_SELF_ADDING | CURS_SELF_UPDATING | CURS_SELF_DELETING);
status |= CURS_SELF_DELETED;
}
deleted_keyset->status = status;
res->dl_count = dl_count + 1;
return TRUE;
}
static void
RemoveDeleted(QResultClass *res, SQLLEN index)
{
int i, mv_count, rm_count = 0;
SQLLEN pidx, midx;
SQLLEN *deleted, num_read = QR_get_num_total_read(res);
KeySet *deleted_keyset;
MYLOG(0, "entering index=" FORMAT_LEN "\n", index);
if (index < 0)
{
midx = index;
pidx = num_read - index - 1;
}
else
{
pidx = index;
if (index >= num_read)
midx = num_read - index - 1;
else
midx = index;
}
for (i = 0; i < res->dl_count; i++)
{
if (pidx == res->deleted[i] ||
midx == res->deleted[i])
{
mv_count = res->dl_count - i - 1;
if (mv_count > 0)
{
deleted = res->deleted + i;
deleted_keyset = res->deleted_keyset + i;
memmove(deleted, deleted + 1, mv_count * sizeof(SQLLEN));
memmove(deleted_keyset, deleted_keyset + 1, mv_count * sizeof(KeySet));
}
res->dl_count--;
rm_count++;
}
}
MYLOG(0, "removed count=%d,%d\n", rm_count, res->dl_count);
}
static void
CommitDeleted(QResultClass *res)
{
int i;
SQLLEN *deleted;
KeySet *deleted_keyset;
UWORD status;
if (!res->deleted)
return;
for (i = 0, deleted = res->deleted, deleted_keyset = res->deleted_keyset; i < res->dl_count; i++, deleted++, deleted_keyset++)
{
status = deleted_keyset->status;
if (0 != (status & CURS_SELF_ADDING))
{
status |= CURS_SELF_ADDED;
status &= ~CURS_SELF_ADDING;
}
if (0 != (status & CURS_SELF_UPDATING))
{
status |= CURS_SELF_UPDATED;
status &= ~CURS_SELF_UPDATING;
}
if (0 != (status & CURS_SELF_DELETING))
{
status |= CURS_SELF_DELETED;
status &= ~CURS_SELF_DELETING;
}
if (status != deleted_keyset->status)
{
MYLOG(DETAIL_LOG_LEVEL, "Deleted=" FORMAT_LEN "(%d)\n", *deleted, i);
deleted_keyset->status = status;
}
}
}
static BOOL
enlargeUpdated(QResultClass *res, Int4 number, const StatementClass *stmt)
{
Int2 alloc;
alloc = res->up_alloc;
if (0 == alloc)
alloc = number > 10 ? number : 10;
else
while (alloc < number)
{
alloc *= 2;
}
if (alloc <= res->up_alloc)
return TRUE;
QR_REALLOC_return_with_error(res->updated, SQLLEN, sizeof(SQLLEN) * alloc, res, "enlargeUpdated failed", FALSE);
QR_REALLOC_return_with_error(res->updated_keyset, KeySet, sizeof(KeySet) * alloc, res, "enlargeUpdated failed 2", FALSE);
if (SQL_CURSOR_KEYSET_DRIVEN != stmt->options.cursor_type)
QR_REALLOC_return_with_error(res->updated_tuples, TupleField, sizeof(TupleField) * res->num_fields * alloc, res, "enlargeUpdated failed 3", FALSE);
res->up_alloc = alloc;
return TRUE;
}
static void
AddUpdated(StatementClass *stmt, SQLLEN index, const KeySet *keyset, const TupleField *tuple_updated)
{
QResultClass *res;
SQLLEN *updated;
KeySet *updated_keyset;
TupleField *updated_tuples = NULL, *tuple;
/* SQLLEN res_ridx; */
UInt2 up_count;
BOOL is_in_trans;
SQLLEN upd_idx, upd_add_idx;
Int2 num_fields;
int i;
UWORD status;
MYLOG(DETAIL_LOG_LEVEL, "entering index=" FORMAT_LEN "\n", index);
if (!stmt) return;
if (res = SC_get_Curres(stmt), !res) return;
if (!keyset) return;
if (!QR_get_cursor(res)) return;
up_count = res->up_count;
if (up_count > 0 && 0 == res->up_alloc) return;
num_fields = res->num_fields;
if (!tuple_updated)
return;
upd_idx = -1;
upd_add_idx = -1;
updated = res->updated;
is_in_trans = CC_is_in_trans(SC_get_conn(stmt));
updated_keyset = res->updated_keyset;
status = keyset->status;
status &= (~KEYSET_INFO_PUBLIC);
status |= SQL_ROW_UPDATED;
if (is_in_trans)
status |= CURS_SELF_UPDATING;
else
{
for (i = up_count - 1; i >= 0; i--)
{
if (updated[i] == index)
break;
}
if (i >= 0)
upd_idx = i;
else
{
SQLLEN num_totals = QR_get_num_total_tuples(res);
if (index >= num_totals)
upd_add_idx = num_totals - index;
}
status |= CURS_SELF_UPDATED;
status &= ~(CURS_SELF_ADDING | CURS_SELF_UPDATING | CURS_SELF_DELETING);
}
tuple = NULL;
/* update the corresponding add(updat)ed info */
if (upd_add_idx >= 0)
{
res->added_keyset[upd_add_idx].status = status;
if (res->added_tuples)
{
tuple = res->added_tuples + num_fields * upd_add_idx;
ClearCachedRows(tuple, num_fields, 1);
}
}
else if (upd_idx >= 0)
{
res->updated_keyset[upd_idx].status = status;
if (res->updated_tuples)
{
tuple = res->added_tuples + num_fields * upd_add_idx;
ClearCachedRows(tuple, num_fields, 1);
}
}
else
{
if (!enlargeUpdated(res, res->up_count + 1, stmt))
return;
updated = res->updated;
updated_keyset = res->updated_keyset;
updated_tuples = res->updated_tuples;
upd_idx = up_count;
updated[up_count] = index;
updated_keyset[up_count] = *keyset;
updated_keyset[up_count].status = status;
if (updated_tuples)
{
tuple = updated_tuples + num_fields * up_count;
memset(tuple, 0, sizeof(TupleField) * num_fields);
}
res->up_count++;
}
if (tuple)
ReplaceCachedRows(tuple, tuple_updated, num_fields, 1);
if (is_in_trans)
SC_get_conn(stmt)->result_uncommitted = 1;
MYLOG(0, "up_count=%d\n", res->up_count);
}
static void
RemoveUpdated(QResultClass *res, SQLLEN index)
{
MYLOG(0, "entering index=" FORMAT_LEN "\n", index);
RemoveUpdatedAfterTheKey(res, index, NULL);
}
static void
RemoveUpdatedAfterTheKey(QResultClass *res, SQLLEN index, const KeySet *keyset)
{
SQLLEN *updated, num_read = QR_get_num_total_read(res);
KeySet *updated_keyset;
TupleField *updated_tuples = NULL;
SQLLEN pidx, midx, mv_count;
int i, num_fields = res->num_fields, rm_count = 0;
MYLOG(0, "entering " FORMAT_LEN ",(%u,%u)\n", index, keyset ? keyset->blocknum : 0, keyset ? keyset->offset : 0);
if (index < 0)
{
midx = index;
pidx = num_read - index - 1;
}
else
{
pidx = index;
if (index >= num_read)
midx = num_read - index - 1;
else
midx = index;
}
for (i = 0; i < res->up_count; i++)
{
updated = res->updated + i;
if (pidx == *updated ||
midx == *updated)
{
updated_keyset = res->updated_keyset + i;
if (keyset &&
updated_keyset->blocknum == keyset->blocknum &&
updated_keyset->offset == keyset->offset)
break;
updated_tuples = NULL;
if (res->updated_tuples)
{
updated_tuples = res->updated_tuples + i * num_fields;
ClearCachedRows(updated_tuples, num_fields, 1);
}
mv_count = res->up_count - i -1;
if (mv_count > 0)
{
memmove(updated, updated + 1, sizeof(SQLLEN) * mv_count);
memmove(updated_keyset, updated_keyset + 1, sizeof(KeySet) * mv_count);
if (updated_tuples)
memmove(updated_tuples, updated_tuples + num_fields, sizeof(TupleField) * num_fields * mv_count);
}
res->up_count--;
rm_count++;
}
}
MYLOG(0, "removed count=%d,%d\n", rm_count, res->up_count);
}
static void
CommitUpdated(QResultClass *res)
{
KeySet *updated_keyset;
int i;
UWORD status;
MYLOG(0, "entering res=%p\n", res);
if (!res) return;
if (!QR_get_cursor(res))
return;
if (res->up_count <= 0)
return;
if (updated_keyset = res->updated_keyset, !updated_keyset)
return;
for (i = res->up_count - 1; i >= 0; i--)
{
status = updated_keyset[i].status;
if (0 != (status & CURS_SELF_UPDATING))
{
status &= ~CURS_SELF_UPDATING;
status |= CURS_SELF_UPDATED;
}
if (0 != (status & CURS_SELF_ADDING))
{
status &= ~CURS_SELF_ADDING;
status |= CURS_SELF_ADDED;
}
if (0 != (status & CURS_SELF_DELETING))
{
status &= ~CURS_SELF_DELETING;
status |= CURS_SELF_DELETED;
}
if (status != updated_keyset[i].status)
{
MYLOG(DETAIL_LOG_LEVEL, "!!Commit Updated=" FORMAT_LEN "(%d)\n", res->updated[i], i);
updated_keyset[i].status = status;
}
}
}
static void
DiscardRollback(StatementClass *stmt, QResultClass *res)
{
int i;
SQLLEN index, kres_ridx;
UWORD status;
Rollback *rollback;
KeySet *keyset;
BOOL kres_is_valid;
MYLOG(DETAIL_LOG_LEVEL, "entering\n");
if (QR_get_cursor(res))
{
CommitAdded(res);
CommitUpdated(res);
CommitDeleted(res);
return;
}
if (0 == res->rb_count || NULL == res->rollback)
return;
rollback = res->rollback;
keyset = res->keyset;
for (i = 0; i < res->rb_count; i++)
{
index = rollback[i].index;
status = 0;
kres_is_valid = FALSE;
if (index >= 0)
{
kres_ridx = GIdx2KResIdx(index, stmt, res);
if (kres_ridx >= 0 && kres_ridx < res->num_cached_keys)
{
kres_is_valid = TRUE;
status = keyset[kres_ridx].status;
}
}
if (kres_is_valid)
{
keyset[kres_ridx].status &= ~(CURS_SELF_DELETING | CURS_SELF_UPDATING | CURS_SELF_ADDING);
keyset[kres_ridx].status |= ((status & (CURS_SELF_DELETING | CURS_SELF_UPDATING | CURS_SELF_ADDING)) << 3);
}
}
free(rollback);
res->rollback = NULL;
res->rb_count = res->rb_alloc = 0;
}
static QResultClass *positioned_load(StatementClass *stmt, UInt4 flag, const UInt4 *oidint, const char *tid);
static void
UndoRollback(StatementClass *stmt, QResultClass *res, BOOL partial)
{
Int4 i, rollbp;
SQLLEN index, ridx, kres_ridx;
UWORD status;
Rollback *rollback;
KeySet *keyset, keys, *wkey = NULL;
BOOL curs = (NULL != QR_get_cursor(res)), texist, kres_is_valid;
if (0 == res->rb_count || NULL == res->rollback)
return;
rollback = res->rollback;
keyset = res->keyset;
rollbp = 0;
if (partial)
{
SQLLEN pidx, midx;
int rollbps;
int doubtp;
int j;
MYLOG(DETAIL_LOG_LEVEL, " ");
for (i = 0, doubtp = 0; i < res->rb_count; i++)
{
keys.status = 0;
keys.blocknum = rollback[i].blocknum;
keys.offset = rollback[i].offset;
keys.oid = rollback[i].oid;
texist = tupleExists(stmt, &keys);
MYPRINTF(DETAIL_LOG_LEVEL, "texist[%d]=%d", i, texist);
if (SQL_ADD == rollback[i].option)
{
if (texist)
doubtp = i + 1;
}
else if (SQL_REFRESH == rollback[i].option)
{
if (texist || doubtp == i)
doubtp = i + 1;
}
else
{
if (texist)
break;
if (doubtp == i)
doubtp = i + 1;
}
MYPRINTF(DETAIL_LOG_LEVEL, " (doubtp=%d)", doubtp);
}
rollbp = i;
MYPRINTF(DETAIL_LOG_LEVEL, " doubtp=%d,rollbp=%d\n", doubtp, rollbp);
do
{
rollbps = rollbp;
for (i = doubtp; i < rollbp; i++)
{
index = rollback[i].index;
if (SQL_ADD == rollback[i].option)
{
MYLOG(DETAIL_LOG_LEVEL, "index[%d]=" FORMAT_LEN "\n", i, index);
if (index < 0)
{
midx = index;
pidx = res->num_total_read - index - 1;
}
else
{
pidx = index;
midx = res->num_total_read - index - 1;
}
MYLOG(DETAIL_LOG_LEVEL, "pidx=" FORMAT_LEN ",midx=" FORMAT_LEN "\n", pidx, midx);
for (j = rollbp - 1; j > i; j--)
{
if (rollback[j].index == midx ||
rollback[j].index == pidx)
{
if (SQL_DELETE == rollback[j].option)
{
MYLOG(DETAIL_LOG_LEVEL, "delete[%d].index=" FORMAT_LEN "\n", j, rollback[j].index);
break;
}
}
}
if (j <= i)
{
rollbp = i;
break;
}
}
}
} while (rollbp < rollbps);
}
MYLOG(DETAIL_LOG_LEVEL, "rollbp=%d\n", rollbp);
for (i = res->rb_count - 1; i >= rollbp; i--)
{
MYLOG(DETAIL_LOG_LEVEL, "do %d(%d)\n", i, rollback[i].option);
index = rollback[i].index;
if (curs)
{
if (SQL_ADD == rollback[i].option)
RemoveAdded(res, index);
RemoveDeleted(res, index);
keys.status = 0;
keys.blocknum = rollback[i].blocknum;
keys.offset = rollback[i].offset;
keys.oid = rollback[i].oid;
/* RemoveUpdatedAfterTheKey(res, index, &keys); is no longer needed? */
RemoveUpdated(res, index);
}
status = 0;
kres_is_valid = FALSE;
if (index >= 0)
{
kres_ridx = GIdx2KResIdx(index, stmt, res);
if (kres_ridx >= 0 && kres_ridx < res->num_cached_keys)
{
kres_is_valid = TRUE;
wkey = keyset + kres_ridx;
status = wkey->status;
}
}
MYLOG(DETAIL_LOG_LEVEL, " index=" FORMAT_LEN " status=%hx", index, status);
if (kres_is_valid)
{
QResultClass *qres;
Int2 num_fields = res->num_fields;
ridx = GIdx2CacheIdx(index, stmt, res);
if (SQL_ADD == rollback[i].option)
{
if (ridx >=0 && ridx < res->num_cached_rows)
{
TupleField *tuple = res->backend_tuples + res->num_fields * ridx;
ClearCachedRows(tuple, res->num_fields, 1);
res->num_cached_rows--;
}
res->num_cached_keys--;
if (!curs)
res->ad_count--;
}
else if (SQL_REFRESH == rollback[i].option)
continue;
else
{
MYPRINTF(DETAIL_LOG_LEVEL, " (%u, %u)", wkey->blocknum, wkey->offset);
wkey->blocknum = rollback[i].blocknum;
wkey->offset = rollback[i].offset;
wkey->oid = rollback[i].oid;
MYPRINTF(DETAIL_LOG_LEVEL, "->(%u, %u)", wkey->blocknum, wkey->offset);
wkey->status &= ~KEYSET_INFO_PUBLIC;
if (SQL_DELETE == rollback[i].option)
wkey->status &= ~CURS_SELF_DELETING;
else if (SQL_UPDATE == rollback[i].option)
wkey->status &= ~CURS_SELF_UPDATING;
wkey->status |= CURS_NEEDS_REREAD;
if (ridx >=0 && ridx < res->num_cached_rows)
{
char tidval[32];
Oid oid = wkey->oid;
SPRINTF_FIXED(tidval,
"(%u,%u)", wkey->blocknum, wkey->offset);
qres = positioned_load(stmt, 0, &oid, tidval);
if (QR_command_maybe_successful(qres) &&
QR_get_num_cached_tuples(qres) == 1)
{
MoveCachedRows(res->backend_tuples + num_fields * ridx, qres->backend_tuples, num_fields, 1);
wkey->status &= ~CURS_NEEDS_REREAD;
}
QR_Destructor(qres);
}
}
}
}
MYPRINTF(DETAIL_LOG_LEVEL, "\n");
res->rb_count = rollbp;
if (0 == rollbp)
{
free(rollback);
res->rollback = NULL;
res->rb_alloc = 0;
}
}
void
ProcessRollback(ConnectionClass *conn, BOOL undo, BOOL partial)
{
int i;
StatementClass *stmt;
QResultClass *res;
for (i = 0; i < conn->num_stmts; i++)
{
if (stmt = conn->stmts[i], !stmt)
continue;
for (res = SC_get_Result(stmt); res; res = res->next)
{
if (undo)
UndoRollback(stmt, res, partial);
else
DiscardRollback(stmt, res);
}
}
}
#define LATEST_TUPLE_LOAD 1L
#define USE_INSERTED_TID (1L << 1)
static QResultClass *
positioned_load(StatementClass *stmt, UInt4 flag, const UInt4 *oidint, const char *tidval)
{
CSTR func = "positioned_load";
QResultClass *qres = NULL;
PQExpBufferData selstr = {0};
BOOL latest = ((flag & LATEST_TUPLE_LOAD) != 0);
TABLE_INFO *ti = stmt->ti[0];
const char *bestqual = GET_NAME(ti->bestqual);
const ssize_t from_pos = stmt->load_from_pos;
const char *load_stmt = stmt->load_statement;
MYLOG(DETAIL_LOG_LEVEL, "entering bestitem=%s bestqual=%s\n", SAFE_NAME(ti->bestitem), SAFE_NAME(ti->bestqual));
initPQExpBuffer(&selstr);
#define return DONT_CALL_RETURN_FROM_HERE???
if (TI_has_subclass(ti))
{
OID tableoid = *oidint;
char table_fqn[256];
const char *quoted_table;
quoted_table = ti_quote(stmt, tableoid, table_fqn, sizeof(table_fqn));
if (tidval)
{
if (latest)
{
printfPQExpBuffer(&selstr,
"%.*sfrom %s where ctid = (select currtid2('%s', '%s'))",
(int) from_pos, load_stmt,
quoted_table,
quoted_table,
tidval);
}
else
printfPQExpBuffer(&selstr, "%.*sfrom %s where ctid = '%s'", (int) from_pos, load_stmt, quoted_table, tidval);
}
else if ((flag & USE_INSERTED_TID) != 0)
printfPQExpBuffer(&selstr, "%.*sfrom %s where ctid = (select currtid(0, '(0,0)'))", (int) from_pos, load_stmt, quoted_table);
/*
else if (bestitem && oidint)
{
}
*/
else
{
SC_set_error(stmt,STMT_INTERNAL_ERROR, "can't find added and updating row because of the lack of oid", func);
goto cleanup;
}
}
else
{
CSTR andqual = " and ";
BOOL andExist = TRUE;
if (tidval)
{
if (latest)
{
char table_fqn[256];
printfPQExpBuffer(&selstr,
"%s where ctid = (select currtid2('%s', '%s'))",
load_stmt,
ti_quote(stmt, 0, table_fqn, sizeof(table_fqn)),
tidval);
}
else
printfPQExpBuffer(&selstr, "%s where ctid = '%s'", load_stmt, tidval);
}
else if ((flag & USE_INSERTED_TID) != 0)
printfPQExpBuffer(&selstr, "%s where ctid = (select currtid(0, '(0,0)'))", load_stmt);
else if (bestqual)
{
andExist = FALSE;
printfPQExpBuffer(&selstr, "%s where ", load_stmt);
}
else
{
SC_set_error(stmt,STMT_INTERNAL_ERROR, "can't find added and updating row because of the lack of oid", func);
goto cleanup;
}
if (bestqual && oidint)
{
if (andExist)
appendPQExpBufferStr(&selstr, andqual);
appendPQExpBuffer(&selstr, bestqual, *oidint);
}
}
if (PQExpBufferDataBroken(selstr))
{
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Could not allocate memory positioned_load()", func);
goto cleanup;
}
MYLOG(0, "selstr=%s\n", selstr.data);
qres = CC_send_query(SC_get_conn(stmt), selstr.data, NULL, READ_ONLY_QUERY, stmt);
cleanup:
#undef return
if (!PQExpBufferDataBroken(selstr))
termPQExpBuffer(&selstr);
return qres;
}
BOOL QR_get_last_bookmark(const QResultClass *res, Int4 index, KeySet *keyset)
{
int i;
if (res->dl_count > 0 && res->deleted)
{
for (i = 0; i < res->dl_count; i++)
{
if (res->deleted[i] == index)
{
*keyset = res->deleted_keyset[i];
return TRUE;
}
if (res->deleted[i] > index)
break;
}
}
if (res->up_count > 0 && res->updated)
{
for (i = res->up_count - 1; i >= 0; i--)
{
if (res->updated[i] == index)
{
*keyset = res->updated_keyset[i];
return TRUE;
}
}
}
return FALSE;
}
static RETCODE
SC_pos_reload_with_key(StatementClass *stmt, SQLULEN global_ridx, UInt2 *count, Int4 logKind, const KeySet *keyset)
{
CSTR func = "SC_pos_reload_with_key";
int res_cols;
UInt2 rcnt;
SQLLEN kres_ridx;
OID oidint;
QResultClass *res, *qres;
IRDFields *irdflds = SC_get_IRDF(stmt);
RETCODE ret = SQL_ERROR;
char tidval[32];
BOOL use_ctid = TRUE;
BOOL idx_exist = TRUE;
MYLOG(0, "entering fi=%p ti=%p\n", irdflds->fi, stmt->ti);
rcnt = 0;
if (count)
*count = 0;
if (!(res = SC_get_Curres(stmt)))
{
SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_reload.", func);
return SQL_ERROR;
}
kres_ridx = GIdx2KResIdx(global_ridx, stmt, res);
if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
{
if (NULL == keyset || keyset->offset == 0)
{
SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "the target keys are out of the rowset", func);
return SQL_ERROR;
}
idx_exist = FALSE;
}
else if (0 != (res->keyset[kres_ridx].status & CURS_SELF_ADDING))
{
if (NULL == keyset || keyset->offset == 0)
{
use_ctid = FALSE;
MYLOG(0, "The tuple is currently being added and can't use ctid\n");
}
}
if (SC_update_not_ready(stmt))
parse_statement(stmt, TRUE); /* not preferable */
if (!SC_is_updatable(stmt))
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only", func);
return SQL_ERROR;
}
/* old oid & tid */
if (idx_exist)
{
UInt4 blocknum;
UInt2 offset;
if (!(oidint = getOid(res, kres_ridx)))
{
if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
{
SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?", func);
return SQL_SUCCESS_WITH_INFO;
}
}
getTid(res, kres_ridx, &blocknum, &offset);
SPRINTF_FIXED(tidval, "(%u, %u)", blocknum, offset);
}
res_cols = getNumResultCols(res);
if (keyset) /* after or update */
{
char tid[32];
oidint = keyset->oid;
SPRINTF_FIXED(tid, "(%u,%hu)", keyset->blocknum, keyset->offset);
qres = positioned_load(stmt, 0, &oidint, tid);
}
else
{
if (use_ctid)
qres = positioned_load(stmt, LATEST_TUPLE_LOAD, &oidint, tidval);
else
qres = positioned_load(stmt, 0, &oidint, NULL);
keyset = res->keyset + kres_ridx;
}
if (!QR_command_maybe_successful(qres))
{
ret = SQL_ERROR;
if (qres)
SC_replace_error_with_res(stmt, STMT_ERROR_TAKEN_FROM_BACKEND, "positioned_load failed", qres, TRUE);
}
else if (rcnt = (UInt2) QR_get_num_cached_tuples(qres), rcnt == 1)
{
SQLLEN res_ridx;
switch (logKind)
{
case 0:
case SQL_FETCH_BY_BOOKMARK:
break;
case SQL_UPDATE:
AddUpdated(stmt, global_ridx, keyset, qres->tupleField);
break;
default:
AddRollback(stmt, res, global_ridx, keyset, logKind);
}
res_ridx = GIdx2CacheIdx(global_ridx, stmt, res);
if (res_ridx >= 0 && res_ridx < QR_get_num_cached_tuples(res))
{
TupleField *tuple_old, *tuple_new;
int effective_fields = res_cols;
tuple_old = res->backend_tuples + res->num_fields * res_ridx;
QR_set_position(qres, 0);
tuple_new = qres->tupleField;
if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type &&
strcmp(tuple_new[qres->num_fields - res->num_key_fields].value, tidval))
res->keyset[kres_ridx].status |= SQL_ROW_UPDATED;
KeySetSet(tuple_new, qres->num_fields, res->num_key_fields, res->keyset + kres_ridx, FALSE);
MoveCachedRows(tuple_old, tuple_new, effective_fields, 1);
}
ret = SQL_SUCCESS;
}
else
{
SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the content was deleted after last fetch", func);
ret = SQL_SUCCESS_WITH_INFO;
AddRollback(stmt, res, global_ridx, keyset, logKind);
if (idx_exist)
{
if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
{
res->keyset[kres_ridx].status |= SQL_ROW_DELETED;
}
}
}
QR_Destructor(qres);
if (count)
*count = rcnt;
return ret;
}
RETCODE
SC_pos_reload(StatementClass *stmt, SQLULEN global_ridx, UInt2 *count, Int4 logKind)
{
return SC_pos_reload_with_key(stmt, global_ridx, count, logKind, NULL);
}
static const int pre_fetch_count = 32;
static SQLLEN LoadFromKeyset(StatementClass *stmt, QResultClass * res, int rows_per_fetch, SQLLEN limitrow)
{
CSTR func = "LoadFromKeyset";
ConnectionClass *conn = SC_get_conn(stmt);
SQLLEN i;
int j, rowc, rcnt = 0;
OID oid;
UInt4 blocknum;
SQLLEN kres_ridx;
UInt2 offset;
PQExpBufferData qval = {0};
int keys_per_fetch = 10;
#define return DONT_CALL_RETURN_FROM_HERE???
for (i = SC_get_rowset_start(stmt), kres_ridx = GIdx2KResIdx(i, stmt, res), rowc = 0;; i++, kres_ridx++)
{
if (i >= limitrow)
{
if (!rowc)
break;
if (res->reload_count > 0)
{
for (j = rowc; j < keys_per_fetch; j++)
{
appendPQExpBufferStr(&qval, j ? ",NULL" : "NULL");
}
}
rowc = -1; /* end of loop */
}
if (rowc < 0 || rowc >= keys_per_fetch)
{
QResultClass *qres;
appendPQExpBufferStr(&qval, ")");
if (PQExpBufferDataBroken(qval))
{
rcnt = -1;
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory in LoadFromKeyset()", func);
goto cleanup;
}
qres = CC_send_query(conn, qval.data, NULL, CREATE_KEYSET | READ_ONLY_QUERY, stmt);
if (QR_command_maybe_successful(qres))
{
SQLLEN j, k, l;
Int2 m;
TupleField *tuple, *tuplew;
UInt4 bln;
UInt2 off;
for (j = 0; j < QR_get_num_total_read(qres); j++)
{
oid = getOid(qres, j);
getTid(qres, j, &blocknum, &offset);
for (k = SC_get_rowset_start(stmt); k < limitrow; k++)
{
getTid(res, k, &bln, &off);
if (oid == getOid(res, k) &&
bln == blocknum &&
off == offset)
{
l = GIdx2CacheIdx(k, stmt, res);
tuple = res->backend_tuples + res->num_fields * l;
tuplew = qres->backend_tuples + qres->num_fields * j;
for (m = 0; m < res->num_fields; m++, tuple++, tuplew++)
{
if (tuple->len > 0 && tuple->value)
free(tuple->value);
tuple->value = tuplew->value;
tuple->len = tuplew->len;
tuplew->value = NULL;
tuplew->len = -1;
}
res->keyset[k].status &= ~CURS_NEEDS_REREAD;
break;
}
}
}
}
else
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Data Load Error", func);
rcnt = -1;
QR_Destructor(qres);
break;
}
QR_Destructor(qres);
if (rowc < 0)
break;
rowc = 0;
}
if (!rowc)
{
if (PQExpBufferDataBroken(qval))
{
initPQExpBuffer(&qval);
if (res->reload_count > 0)
keys_per_fetch = res->reload_count;
else
{
char planname[32];
int j;
QResultClass *qres;
if (rows_per_fetch >= pre_fetch_count * 2)
keys_per_fetch = pre_fetch_count;
else
keys_per_fetch = rows_per_fetch;
if (!keys_per_fetch)
keys_per_fetch = 2;
SPRINTF_FIXED(planname, "_KEYSET_%p", res);
printfPQExpBuffer(&qval, "PREPARE \"%s\"", planname);
for (j = 0; j < keys_per_fetch; j++)
{
appendPQExpBufferStr(&qval, j ? ",tid" : "(tid");
}
appendPQExpBuffer(&qval, ") as %s where ctid in ", stmt->load_statement);
for (j = 0; j < keys_per_fetch; j++)
{
if (j == 0)
appendPQExpBufferStr(&qval, "($1");
else
appendPQExpBuffer(&qval, ",$%d", j + 1);
}
appendPQExpBufferStr(&qval, ")");
if (PQExpBufferDataBroken(qval))
{
rcnt = -1;
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory in LoadFromKeyset()", func);
goto cleanup;
}
qres = CC_send_query(conn, qval.data, NULL, READ_ONLY_QUERY, stmt);
if (QR_command_maybe_successful(qres))
{
res->reload_count = keys_per_fetch;
}
else
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Prepare for Data Load Error", func);
rcnt = -1;
QR_Destructor(qres);
break;
}
QR_Destructor(qres);
}
}
if (res->reload_count > 0)
{
printfPQExpBuffer(&qval, "EXECUTE \"_KEYSET_%p\"(", res);
}
else
{
printfPQExpBuffer(&qval, "%s where ctid in (", stmt->load_statement);
}
}
if (0 != (res->keyset[kres_ridx].status & CURS_NEEDS_REREAD))
{
getTid(res, kres_ridx, &blocknum, &offset);
if (rowc)
appendPQExpBuffer(&qval, ",'(%u,%u)'", blocknum, offset);
else
appendPQExpBuffer(&qval, "'(%u,%u)'", blocknum, offset);
rowc++;
rcnt++;
}
}
cleanup:
#undef return
if (!PQExpBufferDataBroken(qval))
termPQExpBuffer(&qval);
return rcnt;
}
static SQLLEN LoadFromKeyset_inh(StatementClass *stmt, QResultClass * res, int rows_per_fetch, SQLLEN limitrow)
{
ConnectionClass *conn = SC_get_conn(stmt);
SQLLEN i;
int j, rowc, rcnt = 0;
OID oid, new_oid;
UInt4 blocknum;
SQLLEN kres_ridx;
UInt2 offset;
PQExpBufferData qval = {0};
int keys_per_fetch = 10;
const char *load_stmt = stmt->load_statement;
const ssize_t from_pos = stmt->load_from_pos;
MYLOG(0, "entering in rows_per_fetch=%d limitrow=" FORMAT_LEN "\n", rows_per_fetch, limitrow);
new_oid = 0;
#define return DONT_CALL_RETURN_FROM_HERE???
for (i = SC_get_rowset_start(stmt), kres_ridx = GIdx2KResIdx(i, stmt, res), rowc = 0, oid = 0;; i++, kres_ridx++)
{
if (i >= limitrow)
{
if (!rowc)
break;
rowc = -1; /* end of loop */
}
else if (0 == (res->keyset[kres_ridx].status & CURS_NEEDS_REREAD))
continue;
else
new_oid = getOid(res, kres_ridx);
if (rowc < 0 ||
rowc >= keys_per_fetch ||
(oid != 0 &&
new_oid != oid))
{
QResultClass *qres;
appendPQExpBufferStr(&qval, ")");
if (PQExpBufferDataBroken(qval))
{
rcnt = -1;
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory in LoadFromKeyset_inh()", __FUNCTION__);
goto cleanup;
}
qres = CC_send_query(conn, qval.data, NULL, CREATE_KEYSET | READ_ONLY_QUERY, stmt);
if (QR_command_maybe_successful(qres))
{
SQLLEN k, l;
Int2 m;
TupleField *tuple, *tuplew;
UInt4 bln;
UInt2 off;
OID tbloid;
for (j = 0; j < QR_get_num_total_read(qres); j++)
{
tbloid = getOid(qres, j);
getTid(qres, j, &blocknum, &offset);
for (k = SC_get_rowset_start(stmt); k < limitrow; k++)
{
getTid(res, k, &bln, &off);
if (tbloid == getOid(res, k) &&
bln == blocknum &&
off == offset)
{
l = GIdx2CacheIdx(k, stmt, res);
tuple = res->backend_tuples + res->num_fields * l;
tuplew = qres->backend_tuples + qres->num_fields * j;
for (m = 0; m < res->num_fields; m++, tuple++, tuplew++)
{
if (tuple->len > 0 && tuple->value)
free(tuple->value);
tuple->value = tuplew->value;
tuple->len = tuplew->len;
tuplew->value = NULL;
tuplew->len = -1;
}
res->keyset[k].status &= ~CURS_NEEDS_REREAD;
break;
}
}
}
}
else
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Data Load Error", __FUNCTION__);
rcnt = -1;
QR_Destructor(qres);
break;
}
QR_Destructor(qres);
if (rowc < 0)
break;
rowc = 0;
}
if (!rowc)
{
char table_fqn[256];
if (PQExpBufferDataBroken(qval))
{
if (rows_per_fetch >= pre_fetch_count * 2)
keys_per_fetch = pre_fetch_count;
else
keys_per_fetch = rows_per_fetch;
if (!keys_per_fetch)
keys_per_fetch = 2;
initPQExpBuffer(&qval);
}
printfPQExpBuffer(&qval, "%.*sfrom %s where ctid in (", (int) from_pos, load_stmt, ti_quote(stmt, new_oid, table_fqn, sizeof(table_fqn)));
}
if (new_oid != oid)
oid = new_oid;
getTid(res, kres_ridx, &blocknum, &offset);
if (rowc)
appendPQExpBuffer(&qval, ",'(%u,%u)'", blocknum, offset);
else
appendPQExpBuffer(&qval, "'(%u,%u)'", blocknum, offset);
rowc++;
rcnt++;
}
cleanup:
#undef return
if (!PQExpBufferDataBroken(qval))
termPQExpBuffer(&qval);
return rcnt;
}
static RETCODE SQL_API
SC_pos_reload_needed(StatementClass *stmt, SQLULEN req_size, UDWORD flag)
{
CSTR func = "SC_pos_reload_needed";
Int4 req_rows_size;
SQLLEN i, limitrow;
UInt2 qcount;
QResultClass *res;
RETCODE ret = SQL_ERROR;
SQLLEN kres_ridx, rowc;
Int4 rows_per_fetch;
BOOL create_from_scratch = (0 != flag);
MYLOG(0, "entering\n");
if (!(res = SC_get_Curres(stmt)))
{
SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_reload_needed.", func);
return SQL_ERROR;
}
if (SC_update_not_ready(stmt))
parse_statement(stmt, TRUE); /* not preferable */
if (!SC_is_updatable(stmt))
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only", func);
return SQL_ERROR;
}
rows_per_fetch = 0;
req_rows_size = QR_get_reqsize(res);
if (req_size > req_rows_size)
req_rows_size = (UInt4) req_size;
if (create_from_scratch)
{
rows_per_fetch = ((pre_fetch_count - 1) / req_rows_size + 1) * req_rows_size;
limitrow = RowIdx2GIdx(rows_per_fetch, stmt);
}
else
{
limitrow = RowIdx2GIdx(req_rows_size, stmt);
}
if (limitrow > res->num_cached_keys)
limitrow = res->num_cached_keys;
if (create_from_scratch ||
!res->dataFilled)
{
ClearCachedRows(res->backend_tuples, res->num_fields, res->num_cached_rows);
res->dataFilled = FALSE;
}
if (!res->dataFilled)
{
SQLLEN brows = GIdx2RowIdx(limitrow, stmt);
if (brows > res->count_backend_allocated)
{
QR_REALLOC_return_with_error(res->backend_tuples, TupleField, sizeof(TupleField) * res->num_fields * brows, res, "pos_reload_needed failed", SQL_ERROR);
res->count_backend_allocated = brows;
}
if (brows > 0)
memset(res->backend_tuples, 0, sizeof(TupleField) * res->num_fields * brows);
QR_set_num_cached_rows(res, brows);
QR_set_rowstart_in_cache(res, 0);
if (SQL_RD_ON != stmt->options.retrieve_data)
return SQL_SUCCESS;
for (i = SC_get_rowset_start(stmt), kres_ridx = GIdx2KResIdx(i, stmt,res); i < limitrow; i++, kres_ridx++)
{
if (0 == (res->keyset[kres_ridx].status & (CURS_SELF_DELETING | CURS_SELF_DELETED | CURS_OTHER_DELETED)))
res->keyset[kres_ridx].status |= CURS_NEEDS_REREAD;
}
}
if (TI_has_subclass(stmt->ti[0]))
{
if (rowc = LoadFromKeyset_inh(stmt, res, rows_per_fetch, limitrow), rowc < 0)
{
return SQL_ERROR;
}
}
else if (rowc = LoadFromKeyset(stmt, res, rows_per_fetch, limitrow), rowc < 0)
{
return SQL_ERROR;
}
for (i = SC_get_rowset_start(stmt), kres_ridx = GIdx2KResIdx(i, stmt, res); i < limitrow; i++)
{
if (0 != (res->keyset[kres_ridx].status & CURS_NEEDS_REREAD))
{
ret = SC_pos_reload(stmt, i, &qcount, 0);
if (SQL_ERROR == ret)
{
break;
}
if (SQL_ROW_DELETED == (res->keyset[kres_ridx].status & KEYSET_INFO_PUBLIC))
{
res->keyset[kres_ridx].status |= CURS_OTHER_DELETED;
}
res->keyset[kres_ridx].status &= ~CURS_NEEDS_REREAD;
}
}
res->dataFilled = TRUE;
return ret;
}
static RETCODE SQL_API
SC_pos_newload(StatementClass *stmt, const UInt4 *oidint, BOOL tidRef,
const char *tidval)
{
CSTR func = "SC_pos_newload";
int i;
QResultClass *res, *qres;
RETCODE ret = SQL_ERROR;
MYLOG(0, "entering ti=%p\n", stmt->ti);
if (!(res = SC_get_Curres(stmt)))
{
SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_newload.", func);
return SQL_ERROR;
}
if (SC_update_not_ready(stmt))
parse_statement(stmt, TRUE); /* not preferable */
if (!SC_is_updatable(stmt))
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only", func);
return SQL_ERROR;
}
qres = positioned_load(stmt, (tidRef && NULL == tidval) ? USE_INSERTED_TID : 0, oidint, tidRef ? tidval : NULL);
if (!qres || !QR_command_maybe_successful(qres))
{
SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND, "positioned_load in pos_newload failed", func);
}
else
{
SQLLEN count = QR_get_num_cached_tuples(qres);
QR_set_position(qres, 0);
if (count == 1)
{
int effective_fields = res->num_fields;
ssize_t tuple_size;
SQLLEN num_total_rows, num_cached_rows, kres_ridx;
BOOL appendKey = FALSE, appendData = FALSE;
TupleField *tuple_old, *tuple_new;
tuple_new = qres->tupleField;
num_total_rows = QR_get_num_total_tuples(res);
AddAdded(stmt, res, num_total_rows, tuple_new);
num_cached_rows = QR_get_num_cached_tuples(res);
kres_ridx = GIdx2KResIdx(num_total_rows, stmt, res);
if (QR_haskeyset(res))
{ if (!QR_get_cursor(res))
{
appendKey = TRUE;
if (num_total_rows == CacheIdx2GIdx(num_cached_rows, stmt, res))
appendData = TRUE;
else
{
MYLOG(DETAIL_LOG_LEVEL, "total " FORMAT_LEN " <> backend " FORMAT_LEN " - base " FORMAT_LEN " + start " FORMAT_LEN " cursor_type=%d\n",
num_total_rows, num_cached_rows,
QR_get_rowstart_in_cache(res), SC_get_rowset_start(stmt), stmt->options.cursor_type);
}
}
else if (kres_ridx >= 0 && kres_ridx < res->cache_size)
{
appendKey = TRUE;
appendData = TRUE;
}
}
if (appendKey)
{
if (res->num_cached_keys >= res->count_keyset_allocated)
{
if (!res->count_keyset_allocated)
tuple_size = TUPLE_MALLOC_INC;
else
tuple_size = res->count_keyset_allocated * 2;
QR_REALLOC_return_with_error(res->keyset, KeySet, sizeof(KeySet) * tuple_size, res, "pos_newload failed", SQL_ERROR);
res->count_keyset_allocated = tuple_size;
}
KeySetSet(tuple_new, qres->num_fields, res->num_key_fields, res->keyset + kres_ridx, TRUE);
res->num_cached_keys++;
}
if (appendData)
{
MYLOG(DETAIL_LOG_LEVEL, "total " FORMAT_LEN " == backend " FORMAT_LEN " - base " FORMAT_LEN " + start " FORMAT_LEN " cursor_type=%d\n",
num_total_rows, num_cached_rows,
QR_get_rowstart_in_cache(res), SC_get_rowset_start(stmt), stmt->options.cursor_type);
if (num_cached_rows >= res->count_backend_allocated)
{
if (!res->count_backend_allocated)
tuple_size = TUPLE_MALLOC_INC;
else
tuple_size = res->count_backend_allocated * 2;
QR_REALLOC_return_with_error(res->backend_tuples, TupleField, res->num_fields * sizeof(TupleField) * tuple_size, res, "SC_pos_newload failed", SQL_ERROR);
res->count_backend_allocated = tuple_size;
}
tuple_old = res->backend_tuples + res->num_fields * num_cached_rows;
for (i = 0; i < effective_fields; i++)
{
tuple_old[i].len = tuple_new[i].len;
tuple_new[i].len = -1;
tuple_old[i].value = tuple_new[i].value;
tuple_new[i].value = NULL;
}
res->num_cached_rows++;
}
ret = SQL_SUCCESS;
}
else if (0 == count)
ret = SQL_NO_DATA_FOUND;
else
{
SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the driver cound't identify inserted rows", func);
ret = SQL_ERROR;
}
/* stmt->currTuple = SC_get_rowset_start(stmt) + ridx; */
}
QR_Destructor(qres);
return ret;
}
static RETCODE SQL_API
irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt, SQLULEN global_ridx, const KeySet *old_keyset)
{
CSTR func = "irow_update";
if (ret != SQL_ERROR)
{
int updcnt;
QResultClass *tres = SC_get_Curres(ustmt);
const char *cmdstr = QR_get_command(tres);
if (cmdstr &&
sscanf(cmdstr, "UPDATE %d", &updcnt) == 1)
{
if (updcnt == 1)
{
KeySet keys;
if (NULL != tres->backend_tuples &&
1 == QR_get_num_cached_tuples(tres))
{
KeySetSet(tres->backend_tuples, QR_NumResultCols(tres), QR_NumResultCols(tres), &keys, TRUE);
if (SQL_SUCCEEDED(ret = SC_pos_reload_with_key(stmt, global_ridx, (UInt2 *) 0, SQL_UPDATE, &keys)))
AddRollback(stmt, SC_get_Curres(stmt), global_ridx, old_keyset, SQL_UPDATE);
}
else
ret = SQL_ERROR;
}
else if (updcnt == 0)
{
SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the content was changed before updation", func);
ret = SQL_ERROR;
if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
SC_pos_reload(stmt, global_ridx, (UInt2 *) 0, 0);
}
else
ret = SQL_ERROR;
}
else
ret = SQL_ERROR;
if (ret == SQL_ERROR && SC_get_errornumber(stmt) == 0)
{
SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND, "SetPos update return error", func);
}
}
return ret;
}
/* SQL_NEED_DATA callback for SC_pos_update */
typedef struct
{
BOOL updyes;
QResultClass *res;
StatementClass *stmt, *qstmt;
IRDFields *irdflds;
SQLSETPOSIROW irow;
SQLULEN global_ridx;
KeySet old_keyset;
} pup_cdata;
static RETCODE
pos_update_callback(RETCODE retcode, void *para)
{
RETCODE ret = retcode;
pup_cdata *s = (pup_cdata *) para;
SQLLEN kres_ridx;
BOOL idx_exist = TRUE;
if (s->updyes)
{
MYLOG(0, "entering\n");
ret = irow_update(ret, s->stmt, s->qstmt, s->global_ridx, &s->old_keyset);
MYLOG(DETAIL_LOG_LEVEL, "irow_update ret=%d,%d\n", ret, SC_get_errornumber(s->qstmt));
if (ret != SQL_SUCCESS)
SC_error_copy(s->stmt, s->qstmt, TRUE);
PGAPI_FreeStmt(s->qstmt, SQL_DROP);
s->qstmt = NULL;
}
s->updyes = FALSE;
kres_ridx = GIdx2KResIdx(s->global_ridx, s->stmt, s->res);
if (kres_ridx < 0 || kres_ridx >= s->res->num_cached_keys)
{
idx_exist = FALSE;
}
if (SQL_SUCCESS == ret && s->res->keyset && idx_exist)
{
ConnectionClass *conn = SC_get_conn(s->stmt);
if (CC_is_in_trans(conn))
{
s->res->keyset[kres_ridx].status |= (SQL_ROW_UPDATED | CURS_SELF_UPDATING);
}
else
s->res->keyset[kres_ridx].status |= (SQL_ROW_UPDATED | CURS_SELF_UPDATED);
}
if (s->irdflds->rowStatusArray)
{
switch (ret)
{
case SQL_SUCCESS:
s->irdflds->rowStatusArray[s->irow] = SQL_ROW_UPDATED;
break;
default:
s->irdflds->rowStatusArray[s->irow] = ret;
}
}
return ret;
}
RETCODE
SC_pos_update(StatementClass *stmt,
SQLSETPOSIROW irow, SQLULEN global_ridx, const KeySet *keyset)
{
CSTR func = "SC_pos_update";
int i,
num_cols,
upd_cols;
pup_cdata s;
ConnectionClass *conn;
ARDFields *opts = SC_get_ARDF(stmt);
BindInfoClass *bindings = opts->bindings;
TABLE_INFO *ti;
FIELD_INFO **fi;
PQExpBufferData updstr = {0};
RETCODE ret = SQL_ERROR;
OID oid;
UInt4 blocknum;
UInt2 pgoffset;
SQLLEN offset;
SQLLEN *used, kres_ridx;
Int4 bind_size = opts->bind_size;
BOOL idx_exist = TRUE;
char table_fqn[256];
s.stmt = stmt;
s.irow = irow;
s.global_ridx = global_ridx;
s.irdflds = SC_get_IRDF(s.stmt);
fi = s.irdflds->fi;
if (!(s.res = SC_get_Curres(s.stmt)))
{
SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_update.", func);
return SQL_ERROR;
}
MYLOG(0, "entering " FORMAT_POSIROW "+" FORMAT_LEN " fi=%p ti=%p\n", s.irow, QR_get_rowstart_in_cache(s.res), fi, s.stmt->ti);
if (SC_update_not_ready(stmt))
parse_statement(s.stmt, TRUE); /* not preferable */
if (!SC_is_updatable(s.stmt))
{
s.stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
SC_set_error(s.stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only", func);
return SQL_ERROR;
}
kres_ridx = GIdx2KResIdx(s.global_ridx, s.stmt, s.res);
if (kres_ridx < 0 || kres_ridx >= s.res->num_cached_keys)
{
if (NULL == keyset || keyset->offset == 0)
{
SC_set_error(s.stmt, STMT_ROW_OUT_OF_RANGE, "the target keys are out of the rowset", func);
return SQL_ERROR;
}
idx_exist = FALSE;
}
if (idx_exist)
{
if (!(oid = getOid(s.res, kres_ridx)))
{
if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
{
SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?", func);
return SQL_ERROR;
}
}
getTid(s.res, kres_ridx, &blocknum, &pgoffset);
s.old_keyset = s.res->keyset[kres_ridx];
}
else
{
oid = keyset->oid;
blocknum = keyset->blocknum;
pgoffset = keyset->offset;
s.old_keyset = *keyset;
}
ti = s.stmt->ti[0];
initPQExpBuffer(&updstr);
#define return DONT_CALL_RETURN_FROM_HERE???
printfPQExpBuffer(&updstr,
"update %s set", ti_quote(stmt, oid, table_fqn, sizeof(table_fqn)));
num_cols = s.irdflds->nfields;
offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
for (i = upd_cols = 0; i < num_cols; i++)
{
if (used = bindings[i].used, used != NULL)
{
used = LENADDR_SHIFT(used, offset);
if (bind_size > 0)
used = LENADDR_SHIFT(used, bind_size * s.irow);
else
used = LENADDR_SHIFT(used, s.irow * sizeof(SQLLEN));
MYLOG(0, "%d used=" FORMAT_LEN ",%p\n", i, *used, used);
if (*used != SQL_IGNORE && fi[i]->updatable)
{
if (upd_cols)
appendPQExpBuffer(&updstr,
", \"%s\" = ?", GET_NAME(fi[i]->column_name));
else
appendPQExpBuffer(&updstr,
" \"%s\" = ?", GET_NAME(fi[i]->column_name));
upd_cols++;
}
}
else
MYLOG(0, "%d null bind\n", i);
}
conn = SC_get_conn(s.stmt);
s.updyes = FALSE;
if (upd_cols > 0)
{
HSTMT hstmt;
int j;
ConnInfo *ci = &(conn->connInfo);
APDFields *apdopts;
IPDFields *ipdopts;
OID fieldtype = 0;
const char *bestitem = GET_NAME(ti->bestitem);
const char *bestqual = GET_NAME(ti->bestqual);
int unknown_sizes = ci->drivers.unknown_sizes;
appendPQExpBuffer(&updstr,
" where ctid = '(%u, %u)'",
blocknum, pgoffset);
if (bestqual)
{
appendPQExpBuffer(&updstr, " and ");
appendPQExpBuffer(&updstr, bestqual, oid);
}
if (PG_VERSION_GE(conn, 8.2))
{
appendPQExpBuffer(&updstr, " returning ctid");
if (bestitem)
{
appendPQExpBuffer(&updstr, ", ");
appendPQExpBuffer(&updstr, "\"%s\"", bestitem);
}
}
MYLOG(0, "updstr=%s\n", updstr.data);
if (PGAPI_AllocStmt(conn, &hstmt, 0) != SQL_SUCCESS)
{
SC_set_error(s.stmt, STMT_NO_MEMORY_ERROR, "internal AllocStmt error", func);
goto cleanup;
}
s.qstmt = (StatementClass *) hstmt;
apdopts = SC_get_APDF(s.qstmt);
apdopts->param_bind_type = opts->bind_size;
apdopts->param_offset_ptr = opts->row_offset_ptr;
ipdopts = SC_get_IPDF(s.qstmt);
SC_set_delegate(s.stmt, s.qstmt);
extend_iparameter_bindings(ipdopts, num_cols);
for (i = j = 0; i < num_cols; i++)
{
if (used = bindings[i].used, used != NULL)
{
used = LENADDR_SHIFT(used, offset);
if (bind_size > 0)
used = LENADDR_SHIFT(used, bind_size * s.irow);
else
used = LENADDR_SHIFT(used, s.irow * sizeof(SQLLEN));
MYLOG(0, "%d used=" FORMAT_LEN "\n", i, *used);
if (*used != SQL_IGNORE && fi[i]->updatable)
{
fieldtype = getEffectiveOid(conn, fi[i]);
PIC_set_pgtype(ipdopts->parameters[j], fieldtype);
PGAPI_BindParameter(hstmt,
(SQLUSMALLINT) ++j,
SQL_PARAM_INPUT,
bindings[i].returntype,
pgtype_to_concise_type(s.stmt, fieldtype, i, unknown_sizes),
fi[i]->column_size > 0 ? fi[i]->column_size : pgtype_column_size(s.stmt, fieldtype, i, unknown_sizes),
(SQLSMALLINT) fi[i]->decimal_digits,
bindings[i].buffer,
bindings[i].buflen,
bindings[i].used);
}
}
}
s.qstmt->exec_start_row = s.qstmt->exec_end_row = s.irow;
s.updyes = TRUE;
if (PQExpBufferDataBroken(updstr))
{
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory in SC_pos_updatet()", func);
goto cleanup;
}
ret = PGAPI_ExecDirect(hstmt, (SQLCHAR *) updstr.data, SQL_NTS, 0);
if (ret == SQL_NEED_DATA)
{
pup_cdata *cbdata = (pup_cdata *) malloc(sizeof(pup_cdata));
if (!cbdata)
{
SC_set_error(s.stmt, STMT_NO_MEMORY_ERROR, "Could not allocate memory for cbdata", func);
ret = SQL_ERROR;
goto cleanup;
}
memcpy(cbdata, &s, sizeof(pup_cdata));
if (0 == enqueueNeedDataCallback(s.stmt, pos_update_callback, cbdata))
ret = SQL_ERROR;
goto cleanup;
}
}
else
{
ret = SQL_SUCCESS_WITH_INFO;
SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR, "update list null", func);
}
ret = pos_update_callback(ret, &s);
cleanup:
#undef return
if (!PQExpBufferDataBroken(updstr))
termPQExpBuffer(&updstr);
return ret;
}
RETCODE
SC_pos_delete(StatementClass *stmt,
SQLSETPOSIROW irow, SQLULEN global_ridx, const KeySet *keyset)
{
CSTR func = "SC_pos_update";
UWORD offset;
QResultClass *res, *qres;
ConnectionClass *conn = SC_get_conn(stmt);
IRDFields *irdflds = SC_get_IRDF(stmt);
PQExpBufferData dltstr = {0};
RETCODE ret;
SQLLEN kres_ridx;
OID oid;
UInt4 blocknum, qflag;
TABLE_INFO *ti;
const char *bestitem;
const char *bestqual;
BOOL idx_exist = TRUE;
char table_fqn[256];
MYLOG(0, "entering ti=%p\n", stmt->ti);
if (!(res = SC_get_Curres(stmt)))
{
SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_delete.", func);
return SQL_ERROR;
}
if (SC_update_not_ready(stmt))
parse_statement(stmt, TRUE); /* not preferable */
if (!SC_is_updatable(stmt))
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only", func);
return SQL_ERROR;
}
kres_ridx = GIdx2KResIdx(global_ridx, stmt, res);
if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
{
if (NULL == keyset || keyset->offset == 0)
{
SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "the target keys are out of the rowset", func);
return SQL_ERROR;
}
idx_exist = FALSE;
}
ti = stmt->ti[0];
bestitem = GET_NAME(ti->bestitem);
bestqual = GET_NAME(ti->bestqual);
if (idx_exist)
{
if (!(oid = getOid(res, kres_ridx)))
{
if (bestitem && !strcmp(bestitem, OID_NAME))
{
SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?", func);
return SQL_ERROR;
}
}
getTid(res, kres_ridx, &blocknum, &offset);
keyset = res->keyset + kres_ridx;
}
else
{
oid = keyset->oid;
blocknum = keyset->blocknum;
offset = keyset->offset;
}
initPQExpBuffer(&dltstr);
#define return DONT_CALL_RETURN_FROM_HERE???
printfPQExpBuffer(&dltstr,
"delete from %s where ctid = '(%u, %u)'",
ti_quote(stmt, oid, table_fqn, sizeof(table_fqn)), blocknum, offset);
if (bestqual && !TI_has_subclass(ti))
{
appendPQExpBuffer(&dltstr, " and ");
appendPQExpBuffer(&dltstr, bestqual, oid);
}
if (PQExpBufferDataBroken(dltstr))
{
ret = SQL_ERROR;
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory in SC_pos_delete()", func);
goto cleanup;
}
MYLOG(0, "dltstr=%s\n", dltstr.data);
qflag = 0;
if (stmt->external && !CC_is_in_trans(conn) &&
(!CC_does_autocommit(conn)))
qflag |= GO_INTO_TRANSACTION;
qres = CC_send_query(conn, dltstr.data, NULL, qflag, stmt);
ret = SQL_SUCCESS;
if (QR_command_maybe_successful(qres))
{
int dltcnt;
const char *cmdstr = QR_get_command(qres);
if (cmdstr &&
sscanf(cmdstr, "DELETE %d", &dltcnt) == 1)
{
if (dltcnt == 1)
{
RETCODE tret = SC_pos_reload_with_key(stmt, global_ridx, (UInt2 *) 0, SQL_DELETE, keyset);
if (!SQL_SUCCEEDED(tret))
ret = tret;
}
else if (dltcnt == 0)
{
SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the content was changed before deletion", func);
ret = SQL_ERROR;
if (idx_exist && stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
SC_pos_reload(stmt, global_ridx, (UInt2 *) 0, 0);
}
else
ret = SQL_ERROR;
}
else
ret = SQL_ERROR;
}
else
{
ret = SQL_ERROR;
if (qres)
{
STRCPY_FIXED(res->sqlstate, qres->sqlstate);
res->message = qres->message;
qres->message = NULL;
}
}
if (ret == SQL_ERROR && SC_get_errornumber(stmt) == 0)
{
SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND, "SetPos delete return error", func);
}
if (qres)
QR_Destructor(qres);
if (SQL_SUCCESS == ret && keyset)
AddDeleted(res, global_ridx, keyset);
if (SQL_SUCCESS == ret && keyset && idx_exist)
{
res->keyset[kres_ridx].status &= (~KEYSET_INFO_PUBLIC);
if (CC_is_in_trans(conn))
{
res->keyset[kres_ridx].status |= (SQL_ROW_DELETED | CURS_SELF_DELETING);
}
else
res->keyset[kres_ridx].status |= (SQL_ROW_DELETED | CURS_SELF_DELETED);
MYLOG(DETAIL_LOG_LEVEL, ".status[" FORMAT_ULEN "]=%x\n", global_ridx, res->keyset[kres_ridx].status);
}
if (irdflds->rowStatusArray)
{
switch (ret)
{
case SQL_SUCCESS:
irdflds->rowStatusArray[irow] = SQL_ROW_DELETED;
break;
default:
irdflds->rowStatusArray[irow] = ret;
}
}
cleanup:
#undef return
if (!PQExpBufferDataBroken(dltstr))
termPQExpBuffer(&dltstr);
return ret;
}
static RETCODE SQL_API
irow_insert(RETCODE ret, StatementClass *stmt, StatementClass *istmt,
SQLLEN addpos)
{
CSTR func = "irow_insert";
if (ret != SQL_ERROR)
{
int addcnt;
OID oid, *poid = NULL;
ARDFields *opts = SC_get_ARDF(stmt);
QResultClass *ires = SC_get_Curres(istmt), *tres;
const char *cmdstr;
BindInfoClass *bookmark;
tres = (ires->next ? ires->next : ires);
cmdstr = QR_get_command(tres);
if (cmdstr &&
sscanf(cmdstr, "INSERT %u %d", &oid, &addcnt) == 2 &&
addcnt == 1)
{
RETCODE qret;
const char * tidval = NULL;
char tidv[32];
KeySet keys;
if (NULL != tres->backend_tuples &&
1 == QR_get_num_cached_tuples(tres))
{
KeySetSet(tres->backend_tuples, QR_NumResultCols(tres), QR_NumResultCols(tres), &keys, TRUE);
oid = keys.oid;
SPRINTF_FIXED(tidv, "(%u,%hu)", keys.blocknum, keys.offset);
tidval = tidv;
}
if (0 != oid)
poid = &oid;
qret = SC_pos_newload(stmt, poid, TRUE, tidval);
if (SQL_ERROR == qret)
return qret;
if (SQL_NO_DATA_FOUND == qret)
{
qret = SC_pos_newload(stmt, poid, FALSE, NULL);
if (SQL_ERROR == qret)
return qret;
}
bookmark = opts->bookmark;
if (bookmark && bookmark->buffer)
{
SC_set_current_col(stmt, -1);
SC_Create_bookmark(stmt, bookmark, stmt->bind_row, addpos, &keys);
}
}
else
{
SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND, "SetPos insert return error", func);
}
}
return ret;
}
/* SQL_NEED_DATA callback for SC_pos_add */
typedef struct
{
BOOL updyes;
QResultClass *res;
StatementClass *stmt, *qstmt;
IRDFields *irdflds;
SQLSETPOSIROW irow;
} padd_cdata;
static RETCODE
pos_add_callback(RETCODE retcode, void *para)
{
RETCODE ret = retcode;
padd_cdata *s = (padd_cdata *) para;
SQLLEN addpos;
if (s->updyes)
{
SQLSETPOSIROW brow_save;
MYLOG(0, "entering ret=%d\n", ret);
brow_save = s->stmt->bind_row;
s->stmt->bind_row = s->irow;
if (QR_get_cursor(s->res))
addpos = -(SQLLEN)(s->res->ad_count + 1);
else
addpos = QR_get_num_total_tuples(s->res);
ret = irow_insert(ret, s->stmt, s->qstmt, addpos);
s->stmt->bind_row = brow_save;
}
s->updyes = FALSE;
SC_setInsertedTable(s->qstmt, ret);
if (ret != SQL_SUCCESS)
SC_error_copy(s->stmt, s->qstmt, TRUE);
PGAPI_FreeStmt((HSTMT) s->qstmt, SQL_DROP);
s->qstmt = NULL;
if (SQL_SUCCESS == ret && s->res->keyset)
{
SQLLEN global_ridx = QR_get_num_total_tuples(s->res) - 1;
ConnectionClass *conn = SC_get_conn(s->stmt);
SQLLEN kres_ridx;
UWORD status = SQL_ROW_ADDED;
if (CC_is_in_trans(conn))
status |= CURS_SELF_ADDING;
else
status |= CURS_SELF_ADDED;
kres_ridx = GIdx2KResIdx(global_ridx, s->stmt, s->res);
if (kres_ridx >= 0 && kres_ridx < s->res->num_cached_keys)
{
s->res->keyset[kres_ridx].status = status;
}
}
if (s->irdflds->rowStatusArray)
{
switch (ret)
{
case SQL_SUCCESS:
s->irdflds->rowStatusArray[s->irow] = SQL_ROW_ADDED;
break;
default:
s->irdflds->rowStatusArray[s->irow] = ret;
}
}
return ret;
}
RETCODE
SC_pos_add(StatementClass *stmt,
SQLSETPOSIROW irow)
{
CSTR func = "SC_pos_add";
int num_cols,
add_cols,
i;
HSTMT hstmt;
padd_cdata s;
ConnectionClass *conn;
ConnInfo *ci;
ARDFields *opts = SC_get_ARDF(stmt);
APDFields *apdopts;
IPDFields *ipdopts;
BindInfoClass *bindings = opts->bindings;
FIELD_INFO **fi = SC_get_IRDF(stmt)->fi;
PQExpBufferData addstr = {0};
RETCODE ret;
SQLULEN offset;
SQLLEN *used;
Int4 bind_size = opts->bind_size;
OID fieldtype;
int unknown_sizes;
int func_cs_count = 0;
char table_fqn[256];
MYLOG(0, "entering fi=%p ti=%p\n", fi, stmt->ti);
s.stmt = stmt;
s.irow = irow;
if (!(s.res = SC_get_Curres(s.stmt)))
{
SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_add.", func);
return SQL_ERROR;
}
if (SC_update_not_ready(stmt))
parse_statement(s.stmt, TRUE); /* not preferable */
if (!SC_is_updatable(s.stmt))
{
s.stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
SC_set_error(s.stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only", func);
return SQL_ERROR;
}
s.irdflds = SC_get_IRDF(s.stmt);
num_cols = s.irdflds->nfields;
conn = SC_get_conn(s.stmt);
if (PGAPI_AllocStmt(conn, &hstmt, 0) != SQL_SUCCESS)
{
SC_set_error(s.stmt, STMT_NO_MEMORY_ERROR, "internal AllocStmt error", func);
return SQL_ERROR;
}
initPQExpBuffer(&addstr);
#define return DONT_CALL_RETURN_FROM_HERE???
printfPQExpBuffer(&addstr,
"insert into %s (",
ti_quote(s.stmt, 0, table_fqn, sizeof(table_fqn)));
if (opts->row_offset_ptr)
offset = *opts->row_offset_ptr;
else
offset = 0;
s.qstmt = (StatementClass *) hstmt;
apdopts = SC_get_APDF(s.qstmt);
apdopts->param_bind_type = opts->bind_size;
apdopts->param_offset_ptr = opts->row_offset_ptr;
ipdopts = SC_get_IPDF(s.qstmt);
SC_set_delegate(s.stmt, s.qstmt);
ci = &(conn->connInfo);
unknown_sizes = ci->drivers.unknown_sizes;
extend_iparameter_bindings(ipdopts, num_cols);
for (i = add_cols = 0; i < num_cols; i++)
{
if (used = bindings[i].used, used != NULL)
{
used = LENADDR_SHIFT(used, offset);
if (bind_size > 0)
used = LENADDR_SHIFT(used, bind_size * s.irow);
else
used = LENADDR_SHIFT(used, s.irow * sizeof(SQLLEN));
MYLOG(0, "%d used=" FORMAT_LEN "\n", i, *used);
if (*used != SQL_IGNORE && fi[i]->updatable)
{
fieldtype = getEffectiveOid(conn, fi[i]);
if (add_cols)
appendPQExpBuffer(&addstr,
", \"%s\"", GET_NAME(fi[i]->column_name));
else
appendPQExpBuffer(&addstr,
"\"%s\"", GET_NAME(fi[i]->column_name));
PIC_set_pgtype(ipdopts->parameters[add_cols], fieldtype);
PGAPI_BindParameter(hstmt,
(SQLUSMALLINT) ++add_cols,
SQL_PARAM_INPUT,
bindings[i].returntype,
pgtype_to_concise_type(s.stmt, fieldtype, i, unknown_sizes),
fi[i]->column_size > 0 ? fi[i]->column_size : pgtype_column_size(s.stmt, fieldtype, i, unknown_sizes),
(SQLSMALLINT) fi[i]->decimal_digits,
bindings[i].buffer,
bindings[i].buflen,
bindings[i].used);
}
}
else
MYLOG(0, "%d null bind\n", i);
}
s.updyes = FALSE;
ENTER_INNER_CONN_CS(conn, func_cs_count);
if (add_cols > 0)
{
appendPQExpBuffer(&addstr, ") values (");
for (i = 0; i < add_cols; i++)
{
if (i)
appendPQExpBuffer(&addstr, ", ?");
else
appendPQExpBuffer(&addstr, "?");
}
appendPQExpBuffer(&addstr, ")");
if (PG_VERSION_GE(conn, 8.2))
{
TABLE_INFO *ti = stmt->ti[0];
const char *bestitem = GET_NAME(ti->bestitem);
appendPQExpBuffer(&addstr, " returning ctid");
if (bestitem)
{
appendPQExpBuffer(&addstr, ", ");
appendPQExpBuffer(&addstr, "\"%s\"", bestitem);
}
}
if (PQExpBufferDataBroken(addstr))
{
ret = SQL_ERROR;
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory in SC_pos_add()", func);
goto cleanup;
}
MYLOG(0, "addstr=%s\n", addstr.data);
mylog("addstr=%s\n", addstr.data);
s.qstmt->exec_start_row = s.qstmt->exec_end_row = s.irow;
s.updyes = TRUE;
ret = PGAPI_ExecDirect(hstmt, (SQLCHAR *) addstr.data, SQL_NTS, 0);
if (ret == SQL_NEED_DATA)
{
padd_cdata *cbdata = (padd_cdata *) malloc(sizeof(padd_cdata));
if (!cbdata)
{
SC_set_error(s.stmt, STMT_NO_MEMORY_ERROR, "Could not allocate memory for cbdata", func);
ret = SQL_ERROR;
goto cleanup;
}
memcpy(cbdata, &s, sizeof(padd_cdata));
if (0 == enqueueNeedDataCallback(s.stmt, pos_add_callback, cbdata))
ret = SQL_ERROR;
goto cleanup;
}
}
else
{
ret = SQL_SUCCESS_WITH_INFO;
SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR, "insert list null", func);
}
ret = pos_add_callback(ret, &s);
cleanup:
#undef return
CLEANUP_FUNC_CONN_CS(func_cs_count, conn);
if (!PQExpBufferDataBroken(addstr))
termPQExpBuffer(&addstr);
return ret;
}
/*
* Stuff for updatable cursors end.
*/
RETCODE
SC_pos_refresh(StatementClass *stmt, SQLSETPOSIROW irow , SQLULEN global_ridx)
{
RETCODE ret;
IRDFields *irdflds = SC_get_IRDF(stmt);
/* save the last_fetch_count */
SQLLEN last_fetch = stmt->last_fetch_count;
SQLLEN last_fetch2 = stmt->last_fetch_count_include_ommitted;
SQLSETPOSIROW bind_save = stmt->bind_row;
BOOL tuple_reload = FALSE;
if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
tuple_reload = TRUE;
else
{
QResultClass *res = SC_get_Curres(stmt);
if (res && res->keyset)
{
SQLLEN kres_ridx = GIdx2KResIdx(global_ridx, stmt, res);
if (kres_ridx >= 0 && kres_ridx < QR_get_num_cached_tuples(res))
{
if (0 != (CURS_NEEDS_REREAD & res->keyset[kres_ridx].status))
tuple_reload = TRUE;
}
}
}
if (tuple_reload)
{
if (!SQL_SUCCEEDED(ret = SC_pos_reload(stmt, global_ridx, (UInt2 *) 0, 0)))
return ret;
}
stmt->bind_row = irow;
ret = SC_fetch(stmt);
/* restore the last_fetch_count */
stmt->last_fetch_count = last_fetch;
stmt->last_fetch_count_include_ommitted = last_fetch2;
stmt->bind_row = bind_save;
if (irdflds->rowStatusArray)
{
switch (ret)
{
case SQL_ERROR:
irdflds->rowStatusArray[irow] = SQL_ROW_ERROR;
break;
case SQL_SUCCESS:
irdflds->rowStatusArray[irow] = SQL_ROW_SUCCESS;
break;
case SQL_SUCCESS_WITH_INFO:
default:
irdflds->rowStatusArray[irow] = ret;
break;
}
}
return SQL_SUCCESS;
}
/* SQL_NEED_DATA callback for PGAPI_SetPos */
typedef struct
{
BOOL need_data_callback, auto_commit_needed;
QResultClass *res;
StatementClass *stmt;
ARDFields *opts;
GetDataInfo *gdata;
SQLLEN idx, start_row, end_row, ridx;
UWORD fOption;
SQLSETPOSIROW irow, nrow, processed;
} spos_cdata;
static
RETCODE spos_callback(RETCODE retcode, void *para)
{
CSTR func = "spos_callback";
RETCODE ret;
spos_cdata *s = (spos_cdata *) para;
QResultClass *res;
ARDFields *opts;
ConnectionClass *conn;
SQLULEN global_ridx;
SQLLEN kres_ridx, pos_ridx = 0;
ret = retcode;
MYLOG(0, "entering %d in\n", s->need_data_callback);
if (s->need_data_callback)
{
s->processed++;
if (SQL_ERROR != retcode)
{
s->nrow++;
s->idx++;
}
}
else
{
s->ridx = -1;
s->idx = s->nrow = s->processed = 0;
}
res = s->res;
opts = s->opts;
if (!res || !opts)
{
SC_set_error(s->stmt, STMT_SEQUENCE_ERROR, "Passed res or opts for spos_callback is NULL", func);
return SQL_ERROR;
}
s->need_data_callback = FALSE;
for (; SQL_ERROR != ret && s->nrow <= s->end_row; s->idx++)
{
global_ridx = RowIdx2GIdx(s->idx, s->stmt);
if (SQL_ADD != s->fOption)
{
if ((int) global_ridx >= QR_get_num_total_tuples(res))
break;
if (res->keyset)
{
kres_ridx = GIdx2KResIdx(global_ridx, s->stmt, res);
if (kres_ridx >= res->num_cached_keys)
break;
if (kres_ridx >= 0) /* the row may be deleted and not in the rowset */
{
if (0 == (res->keyset[kres_ridx].status & CURS_IN_ROWSET))
continue;
}
}
}
if (s->nrow < s->start_row)
{
s->nrow++;
continue;
}
s->ridx = s->nrow;
pos_ridx = s->idx;
if (0 != s->irow || !opts->row_operation_ptr || opts->row_operation_ptr[s->nrow] == SQL_ROW_PROCEED)
{
switch (s->fOption)
{
case SQL_UPDATE:
ret = SC_pos_update(s->stmt, s->nrow, global_ridx, NULL);
break;
case SQL_DELETE:
ret = SC_pos_delete(s->stmt, s->nrow, global_ridx, NULL);
break;
case SQL_ADD:
ret = SC_pos_add(s->stmt, s->nrow);
break;
case SQL_REFRESH:
ret = SC_pos_refresh(s->stmt, s->nrow, global_ridx);
break;
}
if (SQL_NEED_DATA == ret)
{
spos_cdata *cbdata = (spos_cdata *) malloc(sizeof(spos_cdata));
if (!cbdata)
{
SC_set_error(s->stmt, STMT_NO_MEMORY_ERROR, "Could not allocate memory for cbdata", func);
return SQL_ERROR;
}
memcpy(cbdata, s, sizeof(spos_cdata));
cbdata->need_data_callback = TRUE;
if (0 == enqueueNeedDataCallback(s->stmt, spos_callback, cbdata))
ret = SQL_ERROR;
return ret;
}
s->processed++;
}
if (SQL_ERROR != ret)
s->nrow++;
}
conn = SC_get_conn(s->stmt);
if (s->auto_commit_needed)
CC_set_autocommit(conn, TRUE);
if (s->irow > 0)
{
if (SQL_ADD != s->fOption && s->ridx >= 0) /* for SQLGetData */
{
s->stmt->currTuple = RowIdx2GIdx(pos_ridx, s->stmt);
QR_set_position(res, pos_ridx);
}
}
else if (SC_get_IRDF(s->stmt)->rowsFetched)
*(SC_get_IRDF(s->stmt)->rowsFetched) = s->processed;
res->recent_processed_row_count = s->stmt->diag_row_count = s->processed;
if (opts) /* logging */
{
MYLOG(DETAIL_LOG_LEVEL, "processed=" FORMAT_POSIROW " ret=%d rowset=" FORMAT_LEN, s->processed, ret, opts->size_of_rowset_odbc2);
MYPRINTF(DETAIL_LOG_LEVEL, "," FORMAT_LEN "\n", opts->size_of_rowset);
}
return ret;
}
/*
* This positions the cursor within a rowset, that was positioned using SQLExtendedFetch.
* This will be useful (so far) only when using SQLGetData after SQLExtendedFetch.
*/
RETCODE SQL_API
PGAPI_SetPos(HSTMT hstmt,
SQLSETPOSIROW irow,
SQLUSMALLINT fOption,
SQLUSMALLINT fLock)
{
CSTR func = "PGAPI_SetPos";
RETCODE ret;
ConnectionClass *conn;
SQLLEN rowsetSize;
int i;
UInt2 gdata_allocated;
GetDataInfo *gdata_info;
GetDataClass *gdata = NULL;
spos_cdata s;
s.stmt = (StatementClass *) hstmt;
if (!s.stmt)
{
SC_log_error(func, NULL_STRING, NULL);
return SQL_INVALID_HANDLE;
}
s.irow = irow;
s.fOption = fOption;
s.auto_commit_needed = FALSE;
s.opts = SC_get_ARDF(s.stmt);
gdata_info = SC_get_GDTI(s.stmt);
gdata = gdata_info->gdata;
MYLOG(0, "entering fOption=%d irow=" FORMAT_POSIROW " lock=%hu currt=" FORMAT_LEN "\n", s.fOption, s.irow, fLock, s.stmt->currTuple);
if (s.stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
;
else if (s.fOption != SQL_POSITION && s.fOption != SQL_REFRESH)
{
SC_set_error(s.stmt, STMT_NOT_IMPLEMENTED_ERROR, "Only SQL_POSITION/REFRESH is supported for PGAPI_SetPos", func);
return SQL_ERROR;
}
if (!(s.res = SC_get_Curres(s.stmt)))
{
SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in PGAPI_SetPos.", func);
return SQL_ERROR;
}
rowsetSize = (s.stmt->transition_status == STMT_TRANSITION_EXTENDED_FETCH ? s.opts->size_of_rowset_odbc2 : s.opts->size_of_rowset);
if (s.irow == 0) /* bulk operation */
{
if (SQL_POSITION == s.fOption)
{
SC_set_error(s.stmt, STMT_INVALID_CURSOR_POSITION, "Bulk Position operations not allowed.", func);
return SQL_ERROR;
}
s.start_row = 0;
s.end_row = rowsetSize - 1;
}
else
{
if (SQL_ADD != s.fOption && s.irow > s.stmt->last_fetch_count)
{
SC_set_error(s.stmt, STMT_ROW_OUT_OF_RANGE, "Row value out of range", func);
return SQL_ERROR;
}
s.start_row = s.end_row = s.irow - 1;
}
gdata_allocated = gdata_info->allocated;
MYLOG(0, "num_cols=%d gdatainfo=%d\n", QR_NumPublicResultCols(s.res), gdata_allocated);
/* Reset for SQLGetData */
if (gdata)
{
for (i = 0; i < gdata_allocated; i++)
GETDATA_RESET(gdata[i]);
}
conn = SC_get_conn(s.stmt);
switch (s.fOption)
{
case SQL_UPDATE:
case SQL_DELETE:
case SQL_ADD:
if (s.auto_commit_needed = CC_does_autocommit(conn), s.auto_commit_needed)
CC_set_autocommit(conn, FALSE);
break;
case SQL_POSITION:
break;
}
s.need_data_callback = FALSE;
#define return DONT_CALL_RETURN_FROM_HERE???
ret = spos_callback(SQL_SUCCESS, &s);
#undef return
if (SQL_SUCCEEDED(ret) && 0 == s.processed)
{
SC_set_error(s.stmt, STMT_ROW_OUT_OF_RANGE, "the row was deleted?", func);
ret = SQL_ERROR;
}
MYLOG(0, "leaving %d\n", ret);
return ret;
}
/* Sets options that control the behavior of cursors. */
RETCODE SQL_API
PGAPI_SetScrollOptions(HSTMT hstmt,
SQLUSMALLINT fConcurrency,
SQLLEN crowKeyset,
SQLUSMALLINT crowRowset)
{
CSTR func = "PGAPI_SetScrollOptions";
StatementClass *stmt = (StatementClass *) hstmt;
MYLOG(0, "entering fConcurrency=%d crowKeyset=" FORMAT_LEN " crowRowset=%d\n",
fConcurrency, crowKeyset, crowRowset);
SC_set_error(stmt, STMT_NOT_IMPLEMENTED_ERROR, "SetScroll option not implemeted", func);
return SQL_ERROR;
}
/* Set the cursor name on a statement handle */
RETCODE SQL_API
PGAPI_SetCursorName(HSTMT hstmt,
const SQLCHAR * szCursor,
SQLSMALLINT cbCursor)
{
CSTR func = "PGAPI_SetCursorName";
StatementClass *stmt = (StatementClass *) hstmt;
MYLOG(0, "entering hstmt=%p, szCursor=%p, cbCursorMax=%d\n", hstmt, szCursor, cbCursor);
if (!stmt)
{
SC_log_error(func, NULL_STRING, NULL);
return SQL_INVALID_HANDLE;
}
SET_NAME_DIRECTLY(stmt->cursor_name, make_string(szCursor, cbCursor, NULL, 0));
return SQL_SUCCESS;
}
/* Return the cursor name for a statement handle */
RETCODE SQL_API
PGAPI_GetCursorName(HSTMT hstmt,
SQLCHAR * szCursor,
SQLSMALLINT cbCursorMax,
SQLSMALLINT * pcbCursor)
{
CSTR func = "PGAPI_GetCursorName";
StatementClass *stmt = (StatementClass *) hstmt;
size_t len = 0;
RETCODE result;
MYLOG(0, "entering hstmt=%p, szCursor=%p, cbCursorMax=%d, pcbCursor=%p\n", hstmt, szCursor, cbCursorMax, pcbCursor);
if (!stmt)
{
SC_log_error(func, NULL_STRING, NULL);
return SQL_INVALID_HANDLE;
}
result = SQL_SUCCESS;
len = strlen(SC_cursor_name(stmt));
if (szCursor)
{
strncpy_null((char *) szCursor, SC_cursor_name(stmt), cbCursorMax);
if (len >= cbCursorMax)
{
result = SQL_SUCCESS_WITH_INFO;
SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the GetCursorName.", func);
}
}
if (pcbCursor)
*pcbCursor = (SQLSMALLINT) len;
/*
* Because this function causes no db-access, there's
* no need to call DiscardStatementSvp()
*/
return result;
}
RETCODE
SC_fetch_by_bookmark(StatementClass *stmt)
{
UInt2 offset;
SQLLEN kres_ridx;
OID oidint;
UInt4 blocknum;
QResultClass *res, *qres;
RETCODE ret = SQL_ERROR;
HSTMT hstmt = NULL;
StatementClass *fstmt;
SQLLEN size_of_rowset, cached_rows;
SQLULEN cRow;
UInt2 num_fields;
ARDFields *opts = SC_get_ARDF(stmt);
SQLHDESC hdesc;
APDFields *apdf;
const int tidbuflen = 20;
char *tidbuf = NULL, *query = NULL;
int i, lodlen;
BindInfoClass *bookmark_orig = opts->bookmark;
TupleField *otuple, *ituple;
SQLUSMALLINT *rowStatusArray;
MYLOG(0, "entering\n");
if (!(res = SC_get_Curres(stmt)))
{
SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_fetch_by_bookmark.", __FUNCTION__);
return SQL_ERROR;
}
if (SC_update_not_ready(stmt))
parse_statement(stmt, TRUE); /* not preferable */
if (!SC_is_updatable(stmt))
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only", __FUNCTION__);
return SQL_ERROR;
}
if (ret = PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt, 0), !SQL_SUCCEEDED(ret))
{
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "internal AllocStmt error", __FUNCTION__);
return ret;
}
size_of_rowset = opts->size_of_rowset;
SC_MALLOC_gexit_with_error(tidbuf, char, size_of_rowset * tidbuflen, stmt, "Couldn't allocate memory for tidbuf bind.", (ret = SQL_ERROR));
for (i = 0; i < size_of_rowset; i++)
{
PG_BM pg_bm;
SQLLEN bidx;
pg_bm = SC_Resolve_bookmark(opts, i);
bidx = pg_bm.index;
MYLOG(0, "i=%d bidx=" FORMAT_LEN " cached=" FORMAT_ULEN "\n", i, bidx, res->num_cached_keys);
kres_ridx = GIdx2KResIdx(bidx, stmt, res);
if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
{
if (pg_bm.keys.offset > 0)
{
QR_get_last_bookmark(res, bidx, &pg_bm.keys);
blocknum = pg_bm.keys.blocknum;
offset = pg_bm.keys.offset;
oidint = pg_bm.keys.oid;
}
else
{
SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "the target rows is out of the rowset", __FUNCTION__);
goto cleanup;
}
}
else
{
if (!(oidint = getOid(res, kres_ridx)))
{
if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
{
SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?", __FUNCTION__);
}
}
getTid(res, kres_ridx, &blocknum, &offset);
}
snprintf(tidbuf + i * tidbuflen, tidbuflen, "(%u,%u)", blocknum, offset);
MYLOG(0, "!!!! tidbuf=%s\n", tidbuf + i * tidbuflen);
}
if (!SQL_SUCCEEDED(PGAPI_BindParameter(hstmt, 1, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_CHAR, tidbuflen, 0,
tidbuf, tidbuflen, NULL)))
goto cleanup;
apdf = SC_get_APDF((StatementClass *) hstmt);
apdf->paramset_size = size_of_rowset;
if (!SQL_SUCCEEDED(PGAPI_GetStmtAttr(stmt, SQL_ATTR_APP_ROW_DESC, (SQLPOINTER) &hdesc, SQL_IS_POINTER, NULL)))
goto cleanup;
if (!SQL_SUCCEEDED(PGAPI_SetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, (SQLPOINTER) hdesc, SQL_IS_POINTER)))
goto cleanup;
lodlen = strlen(stmt->load_statement) + 15;
SC_MALLOC_gexit_with_error(query, char, lodlen, stmt, "Couldn't allocate memory for query buf.", (ret = SQL_ERROR));
snprintf(query, lodlen, "%s where ctid=?", stmt->load_statement);
if (!SQL_SUCCEEDED(ret = PGAPI_ExecDirect(hstmt, (SQLCHAR *) query, SQL_NTS, PODBC_RDONLY)))
goto cleanup;
/*
* Combine multiple results into one
*/
fstmt = (StatementClass *) hstmt;
res = SC_get_Result(fstmt);
num_fields = QR_NumResultCols(res);
cached_rows = QR_get_num_cached_tuples(res);
if (size_of_rowset > res->count_backend_allocated)
{
SC_REALLOC_gexit_with_error(res->backend_tuples, TupleField, size_of_rowset * sizeof(TupleField) * num_fields, hstmt, "Couldn't realloc memory for backend.", (ret = SQL_ERROR));
res->count_backend_allocated = size_of_rowset;
}
memset(res->backend_tuples + num_fields * cached_rows, 0, (size_of_rowset - cached_rows) * num_fields * sizeof(TupleField));
QR_set_num_cached_rows(res, size_of_rowset);
res->num_total_read = size_of_rowset;
rowStatusArray = (SC_get_IRDF(stmt))->rowStatusArray;
for (i = 0, qres = res; i < size_of_rowset && NULL != qres; i++, qres = qres->next)
{
if (1 == QR_get_num_cached_tuples(qres))
{
otuple = res->backend_tuples + i * num_fields;
ituple = qres->backend_tuples;
if (otuple != ituple)
MoveCachedRows(otuple, ituple, num_fields, 1);
if (NULL != rowStatusArray)
rowStatusArray[i] = SQL_ROW_SUCCESS;
}
else if (NULL != rowStatusArray)
rowStatusArray[i] = SQL_ROW_DELETED;
}
/* Fetch and fill bind info */
cRow = 0;
opts->bookmark = NULL;
ret = PGAPI_ExtendedFetch(fstmt, SQL_FETCH_NEXT, 0,
&cRow, NULL, 0, size_of_rowset);
MYLOG(0, "cRow=" FORMAT_ULEN "\n", cRow);
cleanup:
if (NULL != hstmt)
{
PGAPI_SetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, (SQLPOINTER) NULL, SQL_IS_POINTER);
PGAPI_FreeStmt(hstmt, SQL_DROP);
}
opts->bookmark = bookmark_orig;
if (NULL != tidbuf)
free(tidbuf);
if (NULL != query)
free(query);
return ret;
}
C
1
https://gitee.com/opengauss/openGauss-connector-odbc.git
git@gitee.com:opengauss/openGauss-connector-odbc.git
opengauss
openGauss-connector-odbc
openGauss-connector-odbc
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891