1 Star 0 Fork 53

huangjiajun/openGauss-connector-odbc

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
convert.c 161.73 KB
一键复制 编辑 原始数据 按行查看 历史
Lain 提交于 2023-02-02 10:55 . update convert.c.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690
/*-------
* Module: convert.c
*
* Description: This module contains routines related to
* converting parameters and columns into requested data types.
* Parameters are converted from their SQL_C data types into
* the appropriate postgres type. Columns are converted from
* their postgres type (SQL type) into the appropriate SQL_C
* data type.
*
* Classes: n/a
*
* API functions: none
*
* Comments: See "readme.txt" for copyright and license information.
*-------
*/
/* Multibyte support Eiji Tokuya 2001-03-15 */
#include "convert.h"
#include "unicode_support.h"
#include "misc.h"
#ifdef WIN32
#include <float.h>
#define HAVE_LOCALE_H
#endif /* WIN32 */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "multibyte.h"
#include <time.h>
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#include <math.h>
#include <stdlib.h>
#include <limits.h>
#include "statement.h"
#include "qresult.h"
#include "bind.h"
#include "pgtypes.h"
#include "lobj.h"
#include "connection.h"
#include "catfunc.h"
#include "pgapifunc.h"
CSTR NAN_STRING = "NaN";
CSTR INFINITY_STRING = "Infinity";
CSTR MINFINITY_STRING = "-Infinity";
#if defined(WIN32) || defined(__CYGWIN__)
#define TIMEZONE_GLOBAL _timezone
#define TZNAME_GLOBAL _tzname
#define DAYLIGHT_GLOBAL _daylight
#elif defined(HAVE_INT_TIMEZONE)
#define TIMEZONE_GLOBAL timezone
#define TZNAME_GLOBAL tzname
#define DAYLIGHT_GLOBAL daylight
#endif
/*
* How to map ODBC scalar functions {fn func(args)} to Postgres.
* This is just a simple substitution. List augmented from:
* http://www.merant.com/datadirect/download/docs/odbc16/Odbcref/rappc.htm
* - thomas 2000-04-03
*/
static const struct
{
/*
* There's a horrible hack in odbc_name field: if it begins with a %,
* the digit after the % indicates the number of arguments. Otherwise,
* the entry matches any number of args.
*/
char *odbc_name;
char *pgsql_name;
} mapFuncs[] = {
/* { "ASCII", "ascii" }, built_in */
{"CHAR", "chr($*)" },
{"CONCAT", "textcat($*)" },
/* { "DIFFERENCE", "difference" }, how to ? */
{"INSERT", "substring($1 from 1 for $2 - 1) || $4 || substring($1 from $2 + $3)" },
{"LCASE", "lower($*)" },
{"LEFT", "ltrunc($*)" },
{"%2LOCATE", "strpos($2, $1)" }, /* 2 parameters */
{"%3LOCATE", "strpos(substring($2 from $3), $1) + $3 - 1" }, /* 3 parameters */
{"LENGTH", "char_length($*)"},
/* { "LTRIM", "ltrim" }, built_in */
{"RIGHT", "rtrunc($*)" },
{"SPACE", "repeat(' ', $1)" },
/* { "REPEAT", "repeat" }, built_in */
/* { "REPLACE", "replace" }, ??? */
/* { "RTRIM", "rtrim" }, built_in */
/* { "SOUNDEX", "soundex" }, how to ? */
{"SUBSTRING", "substr($*)" },
{"UCASE", "upper($*)" },
/* { "ABS", "abs" }, built_in */
/* { "ACOS", "acos" }, built_in */
/* { "ASIN", "asin" }, built_in */
/* { "ATAN", "atan" }, built_in */
/* { "ATAN2", "atan2" }, built_in */
{"CEILING", "ceil($*)" },
/* { "COS", "cos" }, built_in */
/* { "COT", "cot" }, built_in */
/* { "DEGREES", "degrees" }, built_in */
/* { "EXP", "exp" }, built_in */
/* { "FLOOR", "floor" }, built_in */
{"LOG", "ln($*)" },
{"LOG10", "log($*)" },
/* { "MOD", "mod" }, built_in */
/* { "PI", "pi" }, built_in */
{"POWER", "pow($*)" },
/* { "RADIANS", "radians" }, built_in */
{"%0RAND", "random()" }, /* 0 parameters */
{"%1RAND", "(setseed($1) * .0 + random())" }, /* 1 parameters */
/* { "ROUND", "round" }, built_in */
/* { "SIGN", "sign" }, built_in */
/* { "SIN", "sin" }, built_in */
/* { "SQRT", "sqrt" }, built_in */
/* { "TAN", "tan" }, built_in */
{"TRUNCATE", "trunc($*)" },
{"CURRENT_DATE", "current_date" },
{"CURRENT_TIME", "current_time" },
{"CURRENT_TIMESTAMP", "current_timestamp" },
{"LOCALTIME", "localtime" },
{"LOCALTIMESTAMP", "localtimestamp" },
{"CURRENT_USER", "cast(current_user as text)" },
{"SESSION_USER", "cast(session_user as text)" },
{"CURDATE", "current_date" },
{"CURTIME", "current_time" },
{"DAYNAME", "to_char($1, 'Day')" },
{"DAYOFMONTH", "cast(extract(day from $1) as integer)" },
{"DAYOFWEEK", "(cast(extract(dow from $1) as integer) + 1)" },
{"DAYOFYEAR", "cast(extract(doy from $1) as integer)" },
{"HOUR", "cast(extract(hour from $1) as integer)" },
{"MINUTE", "cast(extract(minute from $1) as integer)" },
{"MONTH", "cast(extract(month from $1) as integer)" },
{"MONTHNAME", " to_char($1, 'Month')" },
/* { "NOW", "now" }, built_in */
{"QUARTER", "cast(extract(quarter from $1) as integer)" },
{"SECOND", "cast(extract(second from $1) as integer)" },
{"WEEK", "cast(extract(week from $1) as integer)" },
{"YEAR", "cast(extract(year from $1) as integer)" },
/* { "DATABASE", "database" }, */
{"IFNULL", "coalesce($*)" },
{"USER", "cast(current_user as text)" },
{0, 0}
};
typedef struct
{
int infinity;
int m;
int d;
int y;
int hh;
int mm;
int ss;
int fr;
} SIMPLE_TIME;
static const char *mapFunction(const char *func, int param_count);
static BOOL convert_money(const char *s, char *sout, size_t soutmax);
static char parse_datetime(const char *buf, SIMPLE_TIME *st);
size_t convert_linefeeds(const char *s, char *dst, size_t max, BOOL convlf, BOOL *changed);
static size_t convert_from_pgbinary(const char *value, bool binary_rawout, char *rgbValue, SQLLEN cbValueMax);
static int convert_lo(StatementClass *stmt, const void *value, SQLSMALLINT fCType,
PTR rgbValue, SQLLEN cbValueMax, SQLLEN *pcbValue);
static int conv_from_octal(const char *s);
static SQLLEN pg_bin2hex(const char *src, char *dst, SQLLEN length);
#ifdef UNICODE_SUPPORT
static SQLLEN pg_bin2whex(const char *src, SQLWCHAR *dst, SQLLEN length);
#endif /* UNICODE_SUPPORT */
/*---------
* A Guide for date/time/timestamp conversions
*
* field_type fCType Output
* ---------- ------ ----------
* PG_TYPE_DATE SQL_C_DEFAULT SQL_C_DATE
* PG_TYPE_DATE SQL_C_DATE SQL_C_DATE
* PG_TYPE_DATE SQL_C_TIMESTAMP SQL_C_TIMESTAMP (time = 0 (midnight))
* PG_TYPE_TIME SQL_C_DEFAULT SQL_C_TIME
* PG_TYPE_TIME SQL_C_TIME SQL_C_TIME
* PG_TYPE_TIME SQL_C_TIMESTAMP SQL_C_TIMESTAMP (date = current date)
* PG_TYPE_ABSTIME SQL_C_DEFAULT SQL_C_TIMESTAMP
* PG_TYPE_ABSTIME SQL_C_DATE SQL_C_DATE (time is truncated)
* PG_TYPE_ABSTIME SQL_C_TIME SQL_C_TIME (date is truncated)
* PG_TYPE_ABSTIME SQL_C_TIMESTAMP SQL_C_TIMESTAMP
*---------
*/
/*
* Macros for unsigned long handling.
*/
#ifdef WIN32
#define ATOI32U(val) strtoul(val, NULL, 10)
#elif defined(HAVE_STRTOUL)
#define ATOI32U(val) strtoul(val, NULL, 10)
#else /* HAVE_STRTOUL */
#define ATOI32U atol
#endif /* WIN32 */
/*
* Macros for BIGINT handling.
*/
#ifdef ODBCINT64
#ifdef WIN32
#define ATOI64(val) _strtoi64(val, NULL, 10)
#define ATOI64U(val) _strtoui64(val, NULL, 10)
#elif (SIZEOF_LONG == 8)
#define ATOI64(val) strtol(val, NULL, 10)
#define ATOI64U(val) strtoul(val, NULL, 10)
#else
#if defined(HAVE_STRTOLL)
#define ATOI64(val) strtoll(val, NULL, 10)
#define ATOI64U(val) strtoull(val, NULL, 10)
#else
static ODBCINT64 ATOI64(const char *val)
{
ODBCINT64 ll;
sscanf(val, "%lld", &ll);
return ll;
}
static unsigned ODBCINT64 ATOI64U(const char *val)
{
unsigned ODBCINT64 ll;
sscanf(val, "%llu", &ll);
return ll;
}
#endif /* HAVE_STRTOLL */
#endif /* WIN32 */
#endif /* ODBCINT64 */
static void ResolveNumericParam(const SQL_NUMERIC_STRUCT *ns, char *chrform);
static void parse_to_numeric_struct(const char *wv, SQL_NUMERIC_STRUCT *ns, BOOL *overflow);
/*
* field_is_bytea
* 检查oid field_type在数据库中是否为类似bytea的二进制数据类型
* 返回0 表示不是二进制数据类型
* 返回1 表示是二进制数据类型,并且byteaout进行输出
* 有两种字符串输出方式 (由数据库的bytea_output选项控制,分别为hex和escape)
* a. 将数据的每个字节以十六进制输出,在最前面加上 \x (\x010203)
* b. 将数据的每个字节以八进制输出, 每个字节前加上 \ (\001\002\003),但如果是可打印字符,那么还是按字符打出
* 返回2 表示是二进制数据类型,并且rawout进行输出
* 将数据的每个字节以十六进制输出即可
*
*/
#define FIELD_BINARY_BYTEAOUT 1
#define FIELD_BINARY_RAWOUT 2
static int
filed_is_bytea(const OID field_type)
{
int res = 0;
switch (field_type) {
case PG_TYPE_BYTEA:
case PG_TYPE_MYSQL_BINARY:
res = FIELD_BINARY_BYTEAOUT;
break;
case PG_TYPE_RAW:
case PG_TYPE_LONG_RAW:
case PG_TYPE_BLOB:
res = FIELD_BINARY_RAWOUT;
break;
default:
res = 0;
}
return res;
}
/*
* TIMESTAMP <-----> SIMPLE_TIME
* precision support since 7.2.
* time zone support is unavailable(the stuff is unreliable)
*/
static BOOL
timestamp2stime(const char *str, SIMPLE_TIME *st, BOOL *bZone, int *zone)
{
char rest[64], bc[16],
*ptr;
int scnt,
i;
int y, m, d, hh, mm, ss;
#ifdef TIMEZONE_GLOBAL
long timediff;
#endif
BOOL withZone = *bZone;
*bZone = FALSE;
*zone = 0;
st->fr = 0;
st->infinity = 0;
rest[0] = '\0';
bc[0] = '\0';
if ((scnt = sscanf(str, "%4d-%2d-%2d %2d:%2d:%2d%31s %15s", &y, &m, &d, &hh, &mm, &ss, rest, bc)) < 6)
{
if (scnt == 3) /* date */
{
st->y = y;
st->m = m;
st->d = d;
st->hh = 0;
st->mm = 0;
st->ss = 0;
return TRUE;
}
if ((scnt = sscanf(str, "%2d:%2d:%2d%31s %15s", &hh, &mm, &ss, rest, bc)) < 3)
return FALSE;
else
{
st->hh = hh;
st->mm = mm;
st->ss = ss;
if (scnt == 3) /* time */
return TRUE;
}
}
else
{
st->y = y;
st->m = m;
st->d = d;
st->hh = hh;
st->mm = mm;
st->ss = ss;
if (scnt == 6)
return TRUE;
}
switch (rest[0])
{
case '+':
*bZone = TRUE;
*zone = atoi(&rest[1]);
break;
case '-':
*bZone = TRUE;
*zone = -atoi(&rest[1]);
break;
case '.':
if ((ptr = strchr(rest, '+')) != NULL)
{
*bZone = TRUE;
*zone = atoi(&ptr[1]);
*ptr = '\0';
}
else if ((ptr = strchr(rest, '-')) != NULL)
{
*bZone = TRUE;
*zone = -atoi(&ptr[1]);
*ptr = '\0';
}
for (i = 1; i < 10; i++)
{
if (!isdigit((UCHAR) rest[i]))
break;
}
for (; i < 10; i++)
rest[i] = '0';
rest[i] = '\0';
st->fr = atoi(&rest[1]);
break;
case 'B':
if (stricmp(rest, "BC") == 0)
st->y *= -1;
return TRUE;
default:
return TRUE;
}
if (stricmp(bc, "BC") == 0)
{
st->y *= -1;
}
if (!withZone || !*bZone || st->y < 1970)
return TRUE;
#ifdef TIMEZONE_GLOBAL
if (!TZNAME_GLOBAL[0] || !TZNAME_GLOBAL[0][0])
{
*bZone = FALSE;
return TRUE;
}
timediff = TIMEZONE_GLOBAL + (*zone) * 3600;
if (!DAYLIGHT_GLOBAL && timediff == 0) /* the same timezone */
return TRUE;
else
{
struct tm tm,
*tm2;
time_t time0;
*bZone = FALSE;
tm.tm_year = st->y - 1900;
tm.tm_mon = st->m - 1;
tm.tm_mday = st->d;
tm.tm_hour = st->hh;
tm.tm_min = st->mm;
tm.tm_sec = st->ss;
tm.tm_isdst = -1;
time0 = mktime(&tm);
if (time0 < 0)
return TRUE;
if (tm.tm_isdst > 0)
timediff -= 3600;
if (timediff == 0) /* the same time zone */
return TRUE;
time0 -= timediff;
#ifdef HAVE_LOCALTIME_R
if (time0 >= 0 && (tm2 = localtime_r(&time0, &tm)) != NULL)
#else
if (time0 >= 0 && (tm2 = localtime(&time0)) != NULL)
#endif /* HAVE_LOCALTIME_R */
{
st->y = tm2->tm_year + 1900;
st->m = tm2->tm_mon + 1;
st->d = tm2->tm_mday;
st->hh = tm2->tm_hour;
st->mm = tm2->tm_min;
st->ss = tm2->tm_sec;
*bZone = TRUE;
}
}
#endif /* TIMEZONE_GLOBAL */
return TRUE;
}
static int
stime2timestamp(const SIMPLE_TIME *st, char *str, size_t bufsize, BOOL bZone,
int precision)
{
char precstr[16],
zonestr[16];
int i;
precstr[0] = '\0';
if (st->infinity > 0)
{
return snprintf(str, bufsize, "%s", INFINITY_STRING);
}
else if (st->infinity < 0)
{
return snprintf(str, bufsize, "%s", MINFINITY_STRING);
}
if (precision > 0 && st->fr)
{
SPRINTF_FIXED(precstr, ".%09d", st->fr);
if (precision < 9)
precstr[precision + 1] = '\0';
else if (precision > 9)
precision = 9;
for (i = precision; i > 0; i--)
{
if (precstr[i] != '0')
break;
precstr[i] = '\0';
}
if (i == 0)
precstr[i] = '\0';
}
zonestr[0] = '\0';
#ifdef TIMEZONE_GLOBAL
if (bZone && TZNAME_GLOBAL[0] && TZNAME_GLOBAL[0][0] && st->y >= 1970)
{
long zoneint;
struct tm tm;
time_t time0;
zoneint = TIMEZONE_GLOBAL;
if (DAYLIGHT_GLOBAL && st->y >= 1900)
{
tm.tm_year = st->y - 1900;
tm.tm_mon = st->m - 1;
tm.tm_mday = st->d;
tm.tm_hour = st->hh;
tm.tm_min = st->mm;
tm.tm_sec = st->ss;
tm.tm_isdst = -1;
time0 = mktime(&tm);
if (time0 >= 0 && tm.tm_isdst > 0)
zoneint -= 3600;
}
if (zoneint > 0)
SPRINTF_FIXED(zonestr, "-%02d", (int) zoneint / 3600);
else
SPRINTF_FIXED(zonestr, "+%02d", -(int) zoneint / 3600);
}
#endif /* TIMEZONE_GLOBAL */
if (st->y < 0)
return snprintf(str, bufsize, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d%s%s BC", -st->y, st->m, st->d, st->hh, st->mm, st->ss, precstr, zonestr);
else
return snprintf(str, bufsize, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d%s%s", st->y, st->m, st->d, st->hh, st->mm, st->ss, precstr, zonestr);
}
static
SQLINTERVAL interval2itype(SQLSMALLINT ctype)
{
SQLINTERVAL sqlitv = 0;
switch (ctype)
{
case SQL_C_INTERVAL_YEAR:
sqlitv = SQL_IS_YEAR;
break;
case SQL_C_INTERVAL_MONTH:
sqlitv = SQL_IS_MONTH;
break;
case SQL_C_INTERVAL_YEAR_TO_MONTH:
sqlitv = SQL_IS_YEAR_TO_MONTH;
break;
case SQL_C_INTERVAL_DAY:
sqlitv = SQL_IS_DAY;
break;
case SQL_C_INTERVAL_HOUR:
sqlitv = SQL_IS_HOUR;
break;
case SQL_C_INTERVAL_DAY_TO_HOUR:
sqlitv = SQL_IS_DAY_TO_HOUR;
break;
case SQL_C_INTERVAL_MINUTE:
sqlitv = SQL_IS_MINUTE;
break;
case SQL_C_INTERVAL_DAY_TO_MINUTE:
sqlitv = SQL_IS_DAY_TO_MINUTE;
break;
case SQL_C_INTERVAL_HOUR_TO_MINUTE:
sqlitv = SQL_IS_HOUR_TO_MINUTE;
break;
case SQL_C_INTERVAL_SECOND:
sqlitv = SQL_IS_SECOND;
break;
case SQL_C_INTERVAL_DAY_TO_SECOND:
sqlitv = SQL_IS_DAY_TO_SECOND;
break;
case SQL_C_INTERVAL_HOUR_TO_SECOND:
sqlitv = SQL_IS_HOUR_TO_SECOND;
break;
case SQL_C_INTERVAL_MINUTE_TO_SECOND:
sqlitv = SQL_IS_MINUTE_TO_SECOND;
break;
}
return sqlitv;
}
/*
* Interval data <-----> SQL_INTERVAL_STRUCT
*/
static int getPrecisionPart(int precision, const char * precPart)
{
char fraction[] = "000000000";
int fracs = sizeof(fraction) - 1;
size_t cpys;
if (precision < 0)
precision = 6; /* default */
if (precision == 0)
return 0;
cpys = strlen(precPart);
if (cpys > fracs)
cpys = fracs;
memcpy(fraction, precPart, cpys);
fraction[precision] = '\0';
return atoi(fraction);
}
static BOOL
interval2istruct(SQLSMALLINT ctype, int precision, const char *str, SQL_INTERVAL_STRUCT *st)
{
char lit1[64], lit2[64];
int scnt, years, mons, days, hours, minutes, seconds;
BOOL sign;
SQLINTERVAL itype = interval2itype(ctype);
memset(st, 0, sizeof(SQL_INTERVAL_STRUCT));
if ((scnt = sscanf(str, "%d-%d", &years, &mons)) >=2)
{
if (SQL_IS_YEAR_TO_MONTH == itype)
{
sign = years < 0 ? SQL_TRUE : SQL_FALSE;
st->interval_type = itype;
st->interval_sign = sign;
st->intval.year_month.year = sign ? (-years) : years;
st->intval.year_month.month = mons;
return TRUE;
}
return FALSE;
}
else if (scnt = sscanf(str, "%d %02d:%02d:%02d.%09s", &days, &hours, &minutes, &seconds, lit2), 5 == scnt || 4 == scnt)
{
sign = days < 0 ? SQL_TRUE : SQL_FALSE;
st->interval_type = itype;
st->interval_sign = sign;
st->intval.day_second.day = sign ? (-days) : days;
st->intval.day_second.hour = hours;
st->intval.day_second.minute = minutes;
st->intval.day_second.second = seconds;
if (scnt > 4)
st->intval.day_second.fraction = getPrecisionPart(precision, lit2);
return TRUE;
}
else if ((scnt = sscanf(str, "%d %10s %d %10s", &years, lit1, &mons, lit2)) >=4)
{
if (strnicmp(lit1, "year", 4) == 0 &&
strnicmp(lit2, "mon", 2) == 0 &&
(SQL_IS_MONTH == itype ||
SQL_IS_YEAR_TO_MONTH == itype))
{
sign = years < 0 ? SQL_TRUE : SQL_FALSE;
st->interval_type = itype;
st->interval_sign = sign;
st->intval.year_month.year = sign ? (-years) : years;
st->intval.year_month.month = sign ? (-mons) : mons;
return TRUE;
}
return FALSE;
}
if ((scnt = sscanf(str, "%d %10s %d", &years, lit1, &days)) == 2)
{
sign = years < 0 ? SQL_TRUE : SQL_FALSE;
if (SQL_IS_YEAR == itype &&
(stricmp(lit1, "year") == 0 ||
stricmp(lit1, "years") == 0))
{
st->interval_type = itype;
st->interval_sign = sign;
st->intval.year_month.year = sign ? (-years) : years;
return TRUE;
}
if (SQL_IS_MONTH == itype &&
(stricmp(lit1, "mon") == 0 ||
stricmp(lit1, "mons") == 0))
{
st->interval_type = itype;
st->interval_sign = sign;
st->intval.year_month.month = sign ? (-years) : years;
return TRUE;
}
if (SQL_IS_DAY == itype &&
(stricmp(lit1, "day") == 0 ||
stricmp(lit1, "days") == 0))
{
st->interval_type = itype;
st->interval_sign = sign;
st->intval.day_second.day = sign ? (-years) : years;
return TRUE;
}
return FALSE;
}
if (itype == SQL_IS_YEAR || itype == SQL_IS_MONTH || itype == SQL_IS_YEAR_TO_MONTH)
{
/* these formats should've been handled above already */
return FALSE;
}
scnt = sscanf(str, "%d %10s %02d:%02d:%02d.%09s", &days, lit1, &hours, &minutes, &seconds, lit2);
if (scnt == 5 || scnt == 6)
{
if (strnicmp(lit1, "day", 3) != 0)
return FALSE;
sign = days < 0 ? SQL_TRUE : SQL_FALSE;
st->interval_type = itype;
st->interval_sign = sign;
st->intval.day_second.day = sign ? (-days) : days;
st->intval.day_second.hour = sign ? (-hours) : hours;
st->intval.day_second.minute = minutes;
st->intval.day_second.second = seconds;
if (scnt > 5)
st->intval.day_second.fraction = getPrecisionPart(precision, lit2);
return TRUE;
}
scnt = sscanf(str, "%02d:%02d:%02d.%09s", &hours, &minutes, &seconds, lit2);
if (scnt == 3 || scnt == 4)
{
sign = hours < 0 ? SQL_TRUE : SQL_FALSE;
st->interval_type = itype;
st->interval_sign = sign;
st->intval.day_second.hour = sign ? (-hours) : hours;
st->intval.day_second.minute = minutes;
st->intval.day_second.second = seconds;
if (scnt > 3)
st->intval.day_second.fraction = getPrecisionPart(precision, lit2);
return TRUE;
}
return FALSE;
}
#ifdef HAVE_LOCALE_H
/*
* Get the decimal point of the current locale.
*
* XXX: This isn't thread-safe, if another thread changes the locale with
* setlocale() concurrently. There are two problems with that:
*
* 1. The pointer returned by localeconv(), or the lc->decimal_point string,
* might be invalidated by calls in other threads. Until someone comes up
* with a thread-safe version of localeconv(), there isn't much we can do
* about that. (libc implementations that return a static buffer (like glibc)
* happen to be safe from the lconv struct being invalidated, but the
* decimal_point string might still not point to a static buffer).
*
* 2. The between the call to sprintf() and get_current_decimal_point(), the
* decimal point might change. That would cause set_server_decimal_point()
* to fail to recognize a decimal separator, and we might send a numeric
* string to the server that the server won't recognize. This would cause
* the query to fail in the server.
*
* XXX: we only take into account the first byte of the decimal separator.
*/
static char get_current_decimal_point(void)
{
struct lconv *lc = localeconv();
return lc->decimal_point[0];
}
/*
* Modify the string representation of a numeric/float value, converting the
* decimal point from '.' to the correct decimal separator of the current
* locale.
*/
static void set_server_decimal_point(char *num, SQLLEN len)
{
char current_decimal_point = get_current_decimal_point();
char *str;
SQLLEN i;
if ('.' == current_decimal_point)
return;
i = 0;
for (str = num; '\0' != *str; str++)
{
if (*str == current_decimal_point)
{
*str = '.';
break;
}
if (len != SQL_NTS && i++ >= len)
break;
}
}
/*
* Inverse of set_server_decimal_point.
*/
static void set_client_decimal_point(char *num)
{
char current_decimal_point = get_current_decimal_point();
char *str;
if ('.' == current_decimal_point)
return;
for (str = num; '\0' != *str; str++)
{
if (*str == '.')
{
*str = current_decimal_point;
break;
}
}
}
#else
static void set_server_decimal_point(char *num) {}
static void set_client_decimal_point(char *num, BOOL) {}
#endif /* HAVE_LOCALE_H */
/* This is called by SQLFetch() */
int
copy_and_convert_field_bindinfo(StatementClass *stmt, OID field_type, int atttypmod, void *value, int col)
{
ARDFields *opts = SC_get_ARDF(stmt);
BindInfoClass *bic;
SQLULEN offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
if (opts->allocated <= col)
extend_column_bindings(opts, col + 1);
bic = &(opts->bindings[col]);
SC_set_current_col(stmt, -1);
return copy_and_convert_field(stmt, field_type, atttypmod, value,
bic->returntype, bic->precision,
(PTR) (bic->buffer + offset), bic->buflen,
LENADDR_SHIFT(bic->used, offset), LENADDR_SHIFT(bic->indicator, offset));
}
/*
* Is 'str' a valid integer literal, consisting only of ASCII characters
* 0-9 ?
*
* Also, *negative is set to TRUE if the value was negative.
*
* We don't check for overflow here. This is just to decide if we need to
* quote the value.
*/
static BOOL
valid_int_literal(const char *str, SQLLEN len, BOOL *negative)
{
SQLLEN i = 0;
/* Check there is a minus sign in front */
if ((len == SQL_NTS || len > 0) && str[0] == '-')
{
i++;
*negative = TRUE;
}
else
*negative = FALSE;
/*
* Must begin with a digit. This also rejects empty strings and '-'.
*/
if (i == len || !(str[i] >= '0' && str[i] <= '9'))
return FALSE;
for (; str[i] && (len == SQL_NTS || i < len); i++)
{
if (!(str[i] >= '0' && str[i] <= '9'))
return FALSE;
}
return TRUE;
}
static double get_double_value(const char *str)
{
if (stricmp(str, NAN_STRING) == 0)
#ifdef NAN
return (double) NAN;
#else
{
double a = .0;
return .0 / a;
}
#endif /* NAN */
else if (stricmp(str, INFINITY_STRING) == 0)
#ifdef INFINITY
return (double) INFINITY;
#else
return (double) (HUGE_VAL * HUGE_VAL);
#endif /* INFINITY */
else if (stricmp(str, MINFINITY_STRING) == 0)
#ifdef INFINITY
return (double) -INFINITY;
#else
return (double) -(HUGE_VAL * HUGE_VAL);
#endif /* INFINITY */
return atof(str);
}
static int char2guid(const char *str, SQLGUID *g)
{
/*
* SQLGUID.Data1 is an "unsigned long" on some platforms, and
* "unsigned int" on others. For format "%08X", it should be an
* "unsigned int", so use a temporary variable for it.
*/
unsigned int Data1;
if (sscanf(str,
"%08X-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
&Data1,
&g->Data2, &g->Data3,
&g->Data4[0], &g->Data4[1], &g->Data4[2], &g->Data4[3],
&g->Data4[4], &g->Data4[5], &g->Data4[6], &g->Data4[7]) < 11)
return COPY_GENERAL_ERROR;
g->Data1 = Data1;
return COPY_OK;
}
static int effective_fraction(int fraction, int *width)
{
for (*width = 9; fraction % 10 == 0; (*width)--, fraction /= 10)
;
return fraction;
}
static int
get_terminator_len(SQLSMALLINT fCType)
{
switch (fCType)
{
#ifdef UNICODE_SUPPORT
case SQL_C_WCHAR:
return WCLEN;
#endif /* UNICODE_SUPPORT */
case SQL_C_BINARY:
return 0;
}
/* SQL_C_CHAR or INTERNAL_ASIS_TYPE */
return 1;
}
static SQLLEN
get_adjust_len(SQLSMALLINT fCType, SQLLEN len)
{
switch (fCType)
{
#ifdef UNICODE_SUPPORT
case SQL_C_WCHAR:
return (len / WCLEN) * WCLEN;
#endif /* UNICODE_SUPPORT */
}
return len;
}
#define BYTEA_PROCESS_ESCAPE 1
#define BYTEA_PROCESS_BINARY 2
static int
setup_getdataclass(SQLLEN * const length_return, const char ** const ptr_return,
int *needbuflen_return, GetDataClass * const pgdc, const char *neut_str,
const OID field_type, const SQLSMALLINT fCType,
const SQLLEN cbValueMax, const ConnectionClass * const conn)
{
SQLLEN len = (-2);
const char *ptr = NULL;
int needbuflen = 0;
int result = COPY_OK;
BOOL lf_conv = conn->connInfo.lf_conversion;
int bytea_process_kind = 0;
BOOL already_processed = FALSE;
BOOL changed = FALSE;
int len_for_wcs_term = 0;
bool binary_rawout = false;
#ifdef UNICODE_SUPPORT
char *allocbuf = NULL;
int unicode_count = -1;
BOOL localize_needed = FALSE;
BOOL hybrid = FALSE;
#endif /* UNICODE_SUPPORT */
// if (PG_TYPE_BYTEA == field_type)
if (FIELD_BINARY_BYTEAOUT == filed_is_bytea(field_type))
{
//BYTEA类型数据从pg内核发送过来时,会根据参数添加\x表示十六进制
//或者直接收到其二进制裸数据的八进制字符串(\001 \002 \003)
//如果以SQL_C_BINARY接收,则要将字符串转换成二进制字节流
if (SQL_C_BINARY == fCType)
bytea_process_kind = BYTEA_PROCESS_BINARY;
else if (0 == strnicmp(neut_str, "\\x", 2)) /* hex format */
neut_str += 2;
else
bytea_process_kind = BYTEA_PROCESS_ESCAPE;
}
else if (FIELD_BINARY_RAWOUT == filed_is_bytea(field_type))
{
//对于raw调用的rawout,将收到裸数据的十六进制字符串,且前面没有\x
if (SQL_C_BINARY == fCType)
{
bytea_process_kind = BYTEA_PROCESS_BINARY;
binary_rawout = true;
}
}
#ifdef UNICODE_SUPPORT
if (0 == bytea_process_kind)
{
if (get_convtype() > 0) /* coversion between the current locale is available */
{
BOOL wcs_debug = conn->connInfo.wcs_debug;
BOOL same_encoding = (conn->ccsc == pg_CS_code(conn->locale_encoding));
BOOL is_utf8 = (UTF8 == conn->ccsc);
switch (field_type)
{
case PG_TYPE_UNKNOWN:
case PG_TYPE_BPCHAR:
case PG_TYPE_VARCHAR:
case PG_TYPE_NVARCHAR2:
case PG_TYPE_TEXT:
case PG_TYPE_BPCHARARRAY:
case PG_TYPE_VARCHARARRAY:
case PG_TYPE_TEXTARRAY:
if (SQL_C_CHAR == fCType || SQL_C_BINARY == fCType)
localize_needed = (!same_encoding || wcs_debug);
if (SQL_C_WCHAR == fCType)
hybrid = (!is_utf8 || (same_encoding && wcs_debug));
}
MYLOG(0, "localize=%d hybrid=%d is_utf8=%d same_encoding=%d wcs_debug=%d\n", localize_needed, hybrid, is_utf8, same_encoding, wcs_debug);
}
}
if (fCType == SQL_C_WCHAR)
{
if (BYTEA_PROCESS_ESCAPE == bytea_process_kind)
unicode_count = convert_from_pgbinary(neut_str, binary_rawout, NULL, 0) * 2;
else if (hybrid)
{
MYLOG(0, "hybrid estimate\n");
if ((unicode_count = bindcol_hybrid_estimate(neut_str, lf_conv, &allocbuf)) < 0)
{
result = COPY_INVALID_STRING_CONVERSION;
goto cleanup;
}
}
else /* normally */
{
unicode_count = utf8_to_ucs2_lf(neut_str, SQL_NTS, lf_conv, NULL, 0, FALSE);
}
len = WCLEN * unicode_count;
already_processed = changed = TRUE;
}
else if (localize_needed)
{
if ((len = bindcol_localize_estimate(neut_str, lf_conv, &allocbuf)) < 0)
{
result = COPY_INVALID_STRING_CONVERSION;
goto cleanup;
}
already_processed = changed = TRUE;
}
#endif /* UNICODE_SUPPORT */
if (already_processed) /* skip */
;
else if (0 != bytea_process_kind)
{
len = convert_from_pgbinary(neut_str, binary_rawout, NULL, 0);
if (BYTEA_PROCESS_BINARY != bytea_process_kind)
len *= 2;
changed = TRUE;
}
else
/* convert linefeeds to carriage-return/linefeed */
len = convert_linefeeds(neut_str, NULL, 0, lf_conv, &changed);
/* just returns length info */
if (cbValueMax == 0)
{
result = COPY_RESULT_TRUNCATED;
goto cleanup;
}
if (!pgdc->ttlbuf)
pgdc->ttlbuflen = 0;
needbuflen = len + get_terminator_len(fCType);
if (SQL_C_BINARY == fCType)
{
/*
* Though Binary doesn't have NULL terminator,
* bindcol_localize_exec() needs output buffer
* for NULL terminator.
*/
len_for_wcs_term = 1;
}
if (changed || needbuflen > cbValueMax)
{
if (needbuflen > (SQLLEN) pgdc->ttlbuflen)
{
pgdc->ttlbuf = realloc(pgdc->ttlbuf, needbuflen + len_for_wcs_term);
pgdc->ttlbuflen = needbuflen;
}
already_processed = FALSE;
#ifdef UNICODE_SUPPORT
if (fCType == SQL_C_WCHAR)
{
if (BYTEA_PROCESS_ESCAPE == bytea_process_kind)
{
len = convert_from_pgbinary(neut_str, binary_rawout, pgdc->ttlbuf, pgdc->ttlbuflen);
len = pg_bin2whex(pgdc->ttlbuf, (SQLWCHAR *) pgdc->ttlbuf, len);
}
else
{
if (!hybrid) /* normally */
utf8_to_ucs2_lf(neut_str, SQL_NTS, lf_conv, (SQLWCHAR *) pgdc->ttlbuf, unicode_count, FALSE);
else /* hybrid */
{
MYLOG(0, "hybrid convert\n");
if (bindcol_hybrid_exec((SQLWCHAR *) pgdc->ttlbuf, neut_str, unicode_count + 1, lf_conv, &allocbuf) < 0)
{
result = COPY_INVALID_STRING_CONVERSION;
goto cleanup;
}
}
}
already_processed = TRUE;
}
else if (localize_needed)
{
if (bindcol_localize_exec(pgdc->ttlbuf, len + 1, lf_conv, &allocbuf) < 0)
{
result = COPY_INVALID_STRING_CONVERSION;
goto cleanup;
}
already_processed = TRUE;
}
#endif /* UNICODE_SUPPORT */
if (already_processed)
;
else if (0 != bytea_process_kind)
{
len = convert_from_pgbinary(neut_str, binary_rawout, pgdc->ttlbuf, pgdc->ttlbuflen);
if (BYTEA_PROCESS_ESCAPE == bytea_process_kind)
len = pg_bin2hex(pgdc->ttlbuf, pgdc->ttlbuf, len);
}
else
convert_linefeeds(neut_str, pgdc->ttlbuf, pgdc->ttlbuflen, lf_conv, &changed);
ptr = pgdc->ttlbuf;
pgdc->ttlbufused = len;
}
else
{
if (pgdc->ttlbuf)
{
free(pgdc->ttlbuf);
pgdc->ttlbuf = NULL;
}
ptr = neut_str;
}
cleanup:
#ifdef UNICODE_SUPPORT
if (allocbuf)
free(allocbuf);
#endif /* UNICODE_SUPPORT */
*length_return = len;
*ptr_return = ptr;
*needbuflen_return = needbuflen;
return result;
}
/*
gdata SC_get_GDTI(stmt)
current_col stmt->current_col
*/
/*
* fCType treated in the following function is
*
* SQL_C_CHAR, SQL_C_BINARY, SQL_C_WCHAR or INTERNAL_ASIS_TYPE
*/
static int
convert_text_field_to_sql_c(GetDataInfo * const gdata, const int current_col,
const char * const neut_str, const OID field_type,
const SQLSMALLINT fCType, char * const rgbValueBindRow,
const SQLLEN cbValueMax, const ConnectionClass * const conn,
SQLLEN * const length_return)
{
int result = COPY_OK;
SQLLEN len = (-2);
GetDataClass *pgdc;
int copy_len = 0, needbuflen = 0, i;
const char *ptr;
MYLOG(0, "field_type=%u type=%d\n", field_type, fCType);
switch (field_type)
{
case PG_TYPE_FLOAT4:
case PG_TYPE_FLOAT8:
case PG_TYPE_NUMERIC:
set_client_decimal_point((char *) neut_str);
break;
}
if (current_col < 0)
{
pgdc = &(gdata->fdata);
pgdc->data_left = -1;
}
else
pgdc = &gdata->gdata[current_col];
if (pgdc->data_left < 0)
{
if (COPY_OK != (result = setup_getdataclass(&len, &ptr,
&needbuflen, pgdc, neut_str, field_type,
fCType, cbValueMax, conn)))
goto cleanup;
}
else
{
ptr = pgdc->ttlbuf;
len = pgdc->ttlbufused;
}
MYLOG(0, "DEFAULT: len = " FORMAT_LEN ", ptr = '%.*s'\n", len, (int) len, ptr);
if (current_col >= 0)
{
if (pgdc->data_left > 0)
{
ptr += (len - pgdc->data_left);
len = pgdc->data_left;
needbuflen = len + (pgdc->ttlbuflen - pgdc->ttlbufused);
}
else
pgdc->data_left = len;
}
if (cbValueMax > 0)
{
BOOL already_copied = FALSE;
int terminatorlen;
terminatorlen = get_terminator_len(fCType);
if (terminatorlen >= cbValueMax)
copy_len = 0;
else if (len + terminatorlen > cbValueMax)
copy_len = get_adjust_len(fCType, cbValueMax - terminatorlen);
else
copy_len = len;
if (!already_copied)
{
/* Copy the data */
if (copy_len > 0)
memcpy(rgbValueBindRow, ptr, copy_len);
/* Add null terminator */
for (i = 0; i < terminatorlen && copy_len + i < cbValueMax; i++)
rgbValueBindRow[copy_len + i] = '\0';
}
/* Adjust data_left for next time */
if (current_col >= 0)
pgdc->data_left -= copy_len;
}
/*
* Finally, check for truncation so that proper status can
* be returned
*/
if (cbValueMax > 0 && needbuflen > cbValueMax)
result = COPY_RESULT_TRUNCATED;
else
{
if (pgdc->ttlbuf != NULL)
{
free(pgdc->ttlbuf);
pgdc->ttlbuf = NULL;
}
}
#ifdef UNICODE_SUPPORT
if (SQL_C_WCHAR == fCType)
MYLOG(0, " SQL_C_WCHAR, default: len = " FORMAT_LEN ", cbValueMax = " FORMAT_LEN ", rgbValueBindRow = '%s'\n", len, cbValueMax, rgbValueBindRow);
else
#endif /* UNICODE_SUPPORT */
if (SQL_C_BINARY == fCType)
MYLOG(0, " SQL_C_BINARY, default: len = " FORMAT_LEN ", cbValueMax = " FORMAT_LEN ", rgbValueBindRow = '%.*s'\n", len, cbValueMax, copy_len, rgbValueBindRow);
else
MYLOG(0, " SQL_C_CHAR, default: len = " FORMAT_LEN ", cbValueMax = " FORMAT_LEN ", rgbValueBindRow = '%s'\n", len, cbValueMax, rgbValueBindRow);
cleanup:
*length_return = len;
return result;
}
/* This is called by SQLGetData() */
int
copy_and_convert_field(StatementClass *stmt,
OID field_type, int atttypmod,
void *valuei,
SQLSMALLINT fCType, int precision,
PTR rgbValue, SQLLEN cbValueMax,
SQLLEN *pcbValue, SQLLEN *pIndicator)
{
CSTR func = "copy_and_convert_field";
const char *value = valuei;
ARDFields *opts = SC_get_ARDF(stmt);
GetDataInfo *gdata = SC_get_GDTI(stmt);
SQLLEN len = 0;
SIMPLE_TIME std_time;
#ifdef HAVE_LOCALTIME_R
struct tm tm;
#endif /* HAVE_LOCALTIME_R */
SQLLEN pcbValueOffset,
rgbValueOffset;
char *rgbValueBindRow = NULL;
SQLLEN *pcbValueBindRow = NULL, *pIndicatorBindRow = NULL;
SQLSETPOSIROW bind_row = stmt->bind_row;
int bind_size = opts->bind_size;
int result = COPY_OK;
const ConnectionClass *conn = SC_get_conn(stmt);
BOOL text_bin_handling;
const char *neut_str = value;
char booltemp[3];
char midtemp[64];
GetDataClass *pgdc;
if (stmt->current_col >= 0)
{
if (stmt->current_col >= opts->allocated)
{
return SQL_ERROR;
}
if (gdata->allocated != opts->allocated)
extend_getdata_info(gdata, opts->allocated, TRUE);
pgdc = &gdata->gdata[stmt->current_col];
if (pgdc->data_left == -2)
pgdc->data_left = (cbValueMax > 0) ? 0 : -1; /* This seems to be *
* needed by ADO ? */
if (pgdc->data_left == 0)
{
if (pgdc->ttlbuf != NULL)
{
free(pgdc->ttlbuf);
pgdc->ttlbuf = NULL;
pgdc->ttlbuflen = 0;
}
pgdc->data_left = -2; /* needed by ADO ? */
return COPY_NO_DATA_FOUND;
}
}
/*---------
* rgbValueOffset is *ONLY* for character and binary data.
* pcbValueOffset is for computing any pcbValue location
*---------
*/
if (bind_size > 0)
pcbValueOffset = rgbValueOffset = (bind_size * bind_row);
else
{
pcbValueOffset = bind_row * sizeof(SQLLEN);
rgbValueOffset = bind_row * cbValueMax;
}
/*
* The following is applicable in case bind_size > 0
* or the fCType is of variable length.
*/
if (rgbValue)
rgbValueBindRow = (char *) rgbValue + rgbValueOffset;
if (pcbValue)
pcbValueBindRow = LENADDR_SHIFT(pcbValue, pcbValueOffset);
if (pIndicator)
{
pIndicatorBindRow = LENADDR_SHIFT(pIndicator, pcbValueOffset);
*pIndicatorBindRow = 0;
}
memset(&std_time, 0, sizeof(SIMPLE_TIME));
MYLOG(0, "field_type = %d, fctype = %d, value = '%s', cbValueMax=" FORMAT_LEN "\n", field_type, fCType, (value == NULL) ? "<NULL>" : value, cbValueMax);
if (!value)
{
MYLOG(0, "null_cvt_date_string=%d\n", conn->connInfo.cvt_null_date_string);
/* a speicial handling for FOXPRO NULL -> NULL_STRING */
if (conn->connInfo.cvt_null_date_string > 0 &&
(PG_TYPE_DATE == field_type ||
PG_TYPE_DATETIME == field_type ||
PG_TYPE_TIMESTAMP_NO_TMZONE == field_type) &&
(SQL_C_CHAR == fCType ||
#ifdef UNICODE_SUPPORT
SQL_C_WCHAR == fCType ||
#endif /* UNICODE_SUPPORT */
SQL_C_DATE == fCType ||
SQL_C_TYPE_DATE == fCType ||
SQL_C_DEFAULT == fCType))
{
if (pcbValueBindRow)
*pcbValueBindRow = 0;
switch (fCType)
{
case SQL_C_CHAR:
if (rgbValueBindRow && cbValueMax > 0)
*rgbValueBindRow = '\0';
else
result = COPY_RESULT_TRUNCATED;
break;
case SQL_C_DATE:
case SQL_C_TYPE_DATE:
case SQL_C_DEFAULT:
if (rgbValueBindRow && cbValueMax >= sizeof(DATE_STRUCT))
{
memset(rgbValueBindRow, 0, cbValueMax);
if (pcbValueBindRow)
*pcbValueBindRow = sizeof(DATE_STRUCT);
}
else
result = COPY_RESULT_TRUNCATED;
break;
#ifdef UNICODE_SUPPORT
case SQL_C_WCHAR:
if (rgbValueBindRow && cbValueMax >= WCLEN)
memset(rgbValueBindRow, 0, WCLEN);
else
result = COPY_RESULT_TRUNCATED;
break;
#endif /* UNICODE_SUPPORT */
}
return result;
}
/*
* handle a null just by returning SQL_NULL_DATA in pcbValue, and
* doing nothing to the buffer.
*/
else if (pIndicator)
{
*pIndicatorBindRow = SQL_NULL_DATA;
return COPY_OK;
}
else
{
SC_set_error(stmt, STMT_RETURN_NULL_WITHOUT_INDICATOR, "StrLen_or_IndPtr was a null pointer and NULL data was retrieved", func);
return SQL_ERROR;
}
}
if (stmt->hdbc->DataSourceToDriver != NULL)
{
size_t length = strlen(value);
stmt->hdbc->DataSourceToDriver(stmt->hdbc->translation_option,
SQL_CHAR, valuei, (SDWORD) length,
valuei, (SDWORD) length, NULL,
NULL, 0, NULL);
}
/*
* First convert any specific postgres types into more useable data.
*
* NOTE: Conversions from PG char/varchar of a date/time/timestamp value
* to SQL_C_DATE,SQL_C_TIME, SQL_C_TIMESTAMP not supported
*/
switch (field_type)
{
/*
* $$$ need to add parsing for date/time/timestamp strings in
* PG_TYPE_CHAR,VARCHAR $$$
*/
case PG_TYPE_DATE:
sscanf(value, "%4d-%2d-%2d", &std_time.y, &std_time.m, &std_time.d);
break;
case PG_TYPE_TIME:
{
BOOL bZone = FALSE; /* time zone stuff is unreliable */
int zone;
timestamp2stime(value, &std_time, &bZone, &zone);
}
break;
case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME:
case PG_TYPE_TIMESTAMP_NO_TMZONE:
case PG_TYPE_TIMESTAMP:
case PG_TYPE_SMALLDATETIME:
case PG_TYPE_ORADATE:
std_time.fr = 0;
std_time.infinity = 0;
if (strnicmp(value, INFINITY_STRING, 8) == 0)
{
std_time.infinity = 1;
std_time.m = 12;
std_time.d = 31;
std_time.y = 9999;
std_time.hh = 23;
std_time.mm = 59;
std_time.ss = 59;
}
if (strnicmp(value, MINFINITY_STRING, 9) == 0)
{
std_time.infinity = -1;
std_time.m = 1;
std_time.d = 1;
// std_time.y = -4713;
std_time.y = -9999;
std_time.hh = 0;
std_time.mm = 0;
std_time.ss = 0;
}
if (strnicmp(value, "invalid", 7) != 0)
{
BOOL bZone = field_type != PG_TYPE_TIMESTAMP_NO_TMZONE;
int zone;
/*
* sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &std_time.y, &std_time.m,
* &std_time.d, &std_time.hh, &std_time.mm, &std_time.ss);
*/
bZone = FALSE; /* time zone stuff is unreliable */
timestamp2stime(value, &std_time, &bZone, &zone);
MYLOG(DETAIL_LOG_LEVEL, "2stime fr=%d\n", std_time.fr);
}
else
{
/*
* The timestamp is invalid so set something conspicuous,
* like the epoch
*/
struct tm *tim;
time_t t = 0;
#ifdef HAVE_LOCALTIME_R
tim = localtime_r(&t, &tm);
#else
tim = localtime(&t);
#endif /* HAVE_LOCALTIME_R */
/* CodeDEX with CID=11902 */
if (NULL == tim)
{
MYLOG(MIN_LOG_LEVEL, "%s: failed to get localtime.", func);
SC_set_error(stmt, STMT_STATUS_ERROR, "Failed to get local time", func);
return SQL_ERROR;
}
std_time.m = tim->tm_mon + 1;
std_time.d = tim->tm_mday;
std_time.y = tim->tm_year + 1900;
std_time.hh = tim->tm_hour;
std_time.mm = tim->tm_min;
std_time.ss = tim->tm_sec;
}
break;
case PG_TYPE_BOOL:
{ /* change T/F to 1/0 */
const ConnInfo *ci = &(conn->connInfo);
switch (((char *)value)[0])
{
case 'f':
case 'F':
case 'n':
case 'N':
case '0':
STRCPY_FIXED(booltemp, "0");
break;
default:
if (ci->true_is_minus1)
STRCPY_FIXED(booltemp, "-1");
else
STRCPY_FIXED(booltemp, "1");
}
neut_str = booltemp;
}
break;
/* This is for internal use by SQLStatistics() */
case PG_TYPE_INT2VECTOR:
if (SQL_C_DEFAULT == fCType)
{
int i, nval, maxc;
const char *vp;
/* this is an array of eight integers */
short *short_array = (short *) rgbValueBindRow, shortv;
maxc = 0;
if (NULL != short_array)
maxc = (int) cbValueMax / sizeof(short);
vp = value;
nval = 0;
MYLOG(0, "index=(");
for (i = 0;; i++)
{
if (sscanf(vp, "%hi", &shortv) != 1)
break;
MYPRINTF(0, " %hi", shortv);
nval++;
if (nval < maxc)
short_array[i + 1] = shortv;
/* skip the current token */
while (IS_NOT_SPACE(*vp))
vp++;
/* and skip the space to the next token */
while ((*vp != '\0') && (isspace(*vp)))
vp++;
if (*vp == '\0')
break;
}
MYPRINTF(0, ") nval = %i\n", nval);
if (maxc > 0)
short_array[0] = nval;
/* There is no corresponding fCType for this. */
len = (nval + 1) * sizeof(short);
if (pcbValue)
*pcbValueBindRow = len;
if (len <= cbValueMax)
return COPY_OK; /* dont go any further or the data will be
* trashed */
else
return COPY_RESULT_TRUNCATED;
}
break;
/*
* This is a large object OID, which is used to store
* LONGVARBINARY objects.
*/
case PG_TYPE_LO_UNDEFINED:
return convert_lo(stmt, value, fCType, rgbValueBindRow, cbValueMax, pcbValueBindRow);
case 0:
break;
default:
if (field_type == stmt->hdbc->lobj_type /* hack until permanent type available */
|| (PG_TYPE_OID == field_type && SQL_C_BINARY == fCType && conn->lo_is_domain)
)
return convert_lo(stmt, value, fCType, rgbValueBindRow, cbValueMax, pcbValueBindRow);
}
/* Change default into something useable */
if (fCType == SQL_C_DEFAULT)
{
fCType = pgtype_attr_to_ctype(conn, field_type, atttypmod);
#ifdef UNICODE_SUPPORT
if (fCType == SQL_C_WCHAR
&& CC_default_is_c(conn))
fCType = SQL_C_CHAR;
#endif
MYLOG(0, ", SQL_C_DEFAULT: fCType = %d\n", fCType);
}
text_bin_handling = FALSE;
switch (fCType)
{
case INTERNAL_ASIS_TYPE:
#ifdef UNICODE_SUPPORT
case SQL_C_WCHAR:
#endif /* UNICODE_SUPPORT */
case SQL_C_CHAR:
text_bin_handling = TRUE;
break;
case SQL_C_BINARY:
switch (field_type)
{
case PG_TYPE_UNKNOWN:
case PG_TYPE_BPCHAR:
case PG_TYPE_VARCHAR:
case PG_TYPE_NVARCHAR2:
case PG_TYPE_TEXT:
case PG_TYPE_XML:
case PG_TYPE_BPCHARARRAY:
case PG_TYPE_VARCHARARRAY:
case PG_TYPE_TEXTARRAY:
case PG_TYPE_XMLARRAY:
case PG_TYPE_BYTEA:
text_bin_handling = TRUE;
break;
default:
if (filed_is_bytea(field_type)) {
text_bin_handling = TRUE;
}
}
break;
}
if (text_bin_handling)
{
BOOL pre_convert = TRUE;
int midsize = sizeof(midtemp);
int i;
/* Special character formatting as required */
/*
* These really should return error if cbValueMax is not big
* enough.
*/
switch (field_type)
{
case PG_TYPE_DATE:
len = SPRINTF_FIXED(midtemp, "%.4d-%.2d-%.2d", std_time.y, std_time.m, std_time.d);
break;
case PG_TYPE_TIME:
len = SPRINTF_FIXED(midtemp, "%.2d:%.2d:%.2d", std_time.hh, std_time.mm, std_time.ss);
if (std_time.fr > 0)
{
int wdt;
int fr = effective_fraction(std_time.fr, &wdt);
#pragma GCC diagnostic push
len = SPRINTF_FIXED(midtemp, "%s.%0*d", midtemp, wdt, fr);
#pragma GCC diagnostic pop
}
break;
case PG_TYPE_ABSTIME:
case PG_TYPE_DATETIME:
case PG_TYPE_TIMESTAMP_NO_TMZONE:
case PG_TYPE_TIMESTAMP:
case PG_TYPE_ORADATE:
len = stime2timestamp(&std_time, midtemp, midsize, FALSE,
(int) (midsize - 19 - 2) );
break;
case PG_TYPE_SMALLDATETIME:
len = 16;
SPRINTF_FIXED(midtemp, "%.4d-%.2d-%.2d %.2d:%.2d", std_time.y, std_time.m, std_time.d,std_time.hh, std_time.mm);
break;
case PG_TYPE_UUID:
len = strlen(neut_str);
for (i = 0; i < len && i < midsize - 2; i++)
midtemp[i] = toupper((UCHAR) neut_str[i]);
midtemp[i] = '\0';
MYLOG(0, "PG_TYPE_UUID: rgbValueBindRow = '%s'\n", rgbValueBindRow);
break;
/*
* Currently, data is SILENTLY TRUNCATED for BYTEA and
* character data types if there is not enough room in
* cbValueMax because the driver can't handle multiple
* calls to SQLGetData for these, yet. Most likely, the
* buffer passed in will be big enough to handle the
* maximum limit of postgres, anyway.
*
* LongVarBinary types are handled correctly above, observing
* truncation and all that stuff since there is
* essentially no limit on the large object used to store
* those.
*/
case PG_TYPE_BYTEA:/* convert binary data to hex strings
* (i.e, 255 = "FF") */
default:
pre_convert = FALSE;
}
if (pre_convert)
neut_str = midtemp;
result = convert_text_field_to_sql_c(gdata, stmt->current_col, neut_str, field_type, fCType, rgbValueBindRow, cbValueMax, conn, &len);
}
else
{
SQLGUID g;
/*
* for SQL_C_CHAR, it's probably ok to leave currency symbols in.
* But to convert to numeric types, it is necessary to get rid of
* those.
*/
if (field_type == PG_TYPE_MONEY)
{
if (convert_money(neut_str, midtemp, sizeof(midtemp)))
neut_str = midtemp;
else
{
MYLOG(0, "couldn't convert money type to %d\n", fCType);
return COPY_UNSUPPORTED_TYPE;
}
}
switch (fCType)
{
case SQL_C_DATE:
case SQL_C_TYPE_DATE: /* 91 */
len = 6;
{
DATE_STRUCT *ds;
struct tm *tim;
if (bind_size > 0)
ds = (DATE_STRUCT *) rgbValueBindRow;
else
ds = (DATE_STRUCT *) rgbValue + bind_row;
/*
* Initialize date in case conversion destination
* expects date part from this source time data.
* A value may be partially set here, so do some
* sanity checks on the existing values before
* setting them.
*/
tim = SC_get_localtime(stmt);
if (std_time.m == 0)
std_time.m = tim->tm_mon + 1;
if (std_time.d == 0)
std_time.d = tim->tm_mday;
if (std_time.y == 0)
std_time.y = tim->tm_year + 1900;
ds->year = std_time.y;
ds->month = std_time.m;
ds->day = std_time.d;
}
break;
case SQL_C_TIME:
case SQL_C_TYPE_TIME: /* 92 */
len = 6;
{
TIME_STRUCT *ts;
if (bind_size > 0)
ts = (TIME_STRUCT *) rgbValueBindRow;
else
ts = (TIME_STRUCT *) rgbValue + bind_row;
ts->hour = std_time.hh;
ts->minute = std_time.mm;
ts->second = std_time.ss;
}
break;
case SQL_C_TIMESTAMP:
case SQL_C_TYPE_TIMESTAMP: /* 93 */
len = 16;
{
struct tm *tim;
TIMESTAMP_STRUCT *ts;
if (bind_size > 0)
ts = (TIMESTAMP_STRUCT *) rgbValueBindRow;
else
ts = (TIMESTAMP_STRUCT *) rgbValue + bind_row;
/*
* Initialize date in case conversion destination
* expects date part from this source time data.
* A value may be partially set here, so do some
* sanity checks on the existing values before
* setting them.
*/
tim = SC_get_localtime(stmt);
if (std_time.m == 0)
std_time.m = tim->tm_mon + 1;
if (std_time.d == 0)
std_time.d = tim->tm_mday;
if (std_time.y == 0)
std_time.y = tim->tm_year + 1900;
ts->year = std_time.y;
ts->month = std_time.m;
ts->day = std_time.d;
ts->hour = std_time.hh;
ts->minute = std_time.mm;
ts->second = std_time.ss;
ts->fraction = std_time.fr;
}
break;
case SQL_C_BIT:
len = 1;
if (bind_size > 0)
*((UCHAR *) rgbValueBindRow) = atoi(neut_str);
else
*((UCHAR *) rgbValue + bind_row) = atoi(neut_str);
MYLOG(99, "SQL_C_BIT: bind_row = " FORMAT_POSIROW " val = %d, cb = " FORMAT_LEN ", rgb=%d\n",
bind_row, atoi(neut_str), cbValueMax, *((UCHAR *)rgbValue));
break;
case SQL_C_STINYINT:
case SQL_C_TINYINT:
len = 1;
if (bind_size > 0)
*((SCHAR *) rgbValueBindRow) = atoi(neut_str);
else
*((SCHAR *) rgbValue + bind_row) = atoi(neut_str);
break;
case SQL_C_UTINYINT:
len = 1;
if (bind_size > 0)
*((UCHAR *) rgbValueBindRow) = atoi(neut_str);
else
*((UCHAR *) rgbValue + bind_row) = atoi(neut_str);
break;
case SQL_C_FLOAT:
set_client_decimal_point((char *) neut_str);
len = 4;
if (bind_size > 0)
*((SFLOAT *) rgbValueBindRow) = (float) get_double_value(neut_str);
else
*((SFLOAT *) rgbValue + bind_row) = (float) get_double_value(neut_str);
break;
case SQL_C_DOUBLE:
set_client_decimal_point((char *) neut_str);
len = 8;
if (bind_size > 0)
*((SDOUBLE *) rgbValueBindRow) = get_double_value(neut_str);
else
*((SDOUBLE *) rgbValue + bind_row) = get_double_value(neut_str);
break;
case SQL_C_NUMERIC:
{
SQL_NUMERIC_STRUCT *ns;
BOOL overflowed;
if (bind_size > 0)
ns = (SQL_NUMERIC_STRUCT *) rgbValueBindRow;
else
ns = (SQL_NUMERIC_STRUCT *) rgbValue + bind_row;
parse_to_numeric_struct(neut_str, ns, &overflowed);
if (overflowed)
result = COPY_RESULT_TRUNCATED;
}
break;
case SQL_C_SSHORT:
case SQL_C_SHORT:
len = 2;
if (bind_size > 0)
*((SQLSMALLINT *) rgbValueBindRow) = atoi(neut_str);
else
*((SQLSMALLINT *) rgbValue + bind_row) = atoi(neut_str);
break;
case SQL_C_USHORT:
len = 2;
if (bind_size > 0)
*((SQLUSMALLINT *) rgbValueBindRow) = atoi(neut_str);
else
*((SQLUSMALLINT *) rgbValue + bind_row) = atoi(neut_str);
break;
case SQL_C_SLONG:
case SQL_C_LONG:
len = 4;
if (bind_size > 0)
*((SQLINTEGER *) rgbValueBindRow) = atol(neut_str);
else
*((SQLINTEGER *) rgbValue + bind_row) = atol(neut_str);
break;
case SQL_C_ULONG:
len = 4;
if (bind_size > 0)
*((SQLUINTEGER *) rgbValueBindRow) = ATOI32U(neut_str);
else
*((SQLUINTEGER *) rgbValue + bind_row) = ATOI32U(neut_str);
break;
#ifdef ODBCINT64
case SQL_C_SBIGINT:
len = 8;
if (bind_size > 0)
*((SQLBIGINT *) rgbValueBindRow) = ATOI64(neut_str);
else
*((SQLBIGINT *) rgbValue + bind_row) = ATOI64(neut_str);
break;
case SQL_C_UBIGINT:
len = 8;
if (bind_size > 0)
*((SQLUBIGINT *) rgbValueBindRow) = ATOI64U(neut_str);
else
*((SQLUBIGINT *) rgbValue + bind_row) = ATOI64U(neut_str);
break;
#endif /* ODBCINT64 */
case SQL_C_BINARY:
/* The following is for SQL_C_VARBOOKMARK */
if (PG_TYPE_INT4 == field_type)
{
UInt4 ival = ATOI32U(neut_str);
MYLOG(DETAIL_LOG_LEVEL, "SQL_C_VARBOOKMARK value=%d\n", ival);
if (pcbValue)
*pcbValueBindRow = sizeof(ival);
if (cbValueMax >= sizeof(ival))
{
memcpy(rgbValueBindRow, &ival, sizeof(ival));
return COPY_OK;
}
else
return COPY_RESULT_TRUNCATED;
}
else if (PG_TYPE_UUID == field_type)
{
int rtn = char2guid(neut_str, &g);
if (COPY_OK != rtn)
return rtn;
if (pcbValue)
*pcbValueBindRow = sizeof(g);
if (cbValueMax >= sizeof(g))
{
memcpy(rgbValueBindRow, &g, sizeof(g));
return COPY_OK;
}
else
return COPY_RESULT_TRUNCATED;
}
else
{
MYLOG(0, "couldn't convert the type %d to SQL_C_BINARY\n", field_type);
return COPY_UNSUPPORTED_TYPE;
}
break;
case SQL_C_GUID:
result = char2guid(neut_str, &g);
if (COPY_OK != result)
{
MYLOG(0, "Could not convert to SQL_C_GUID\n");
return COPY_UNSUPPORTED_TYPE;
}
len = sizeof(g);
if (bind_size > 0)
*((SQLGUID *) rgbValueBindRow) = g;
else
*((SQLGUID *) rgbValue + bind_row) = g;
break;
case SQL_C_INTERVAL_YEAR:
case SQL_C_INTERVAL_MONTH:
case SQL_C_INTERVAL_YEAR_TO_MONTH:
case SQL_C_INTERVAL_DAY:
case SQL_C_INTERVAL_HOUR:
case SQL_C_INTERVAL_DAY_TO_HOUR:
case SQL_C_INTERVAL_MINUTE:
case SQL_C_INTERVAL_HOUR_TO_MINUTE:
case SQL_C_INTERVAL_SECOND:
case SQL_C_INTERVAL_DAY_TO_SECOND:
case SQL_C_INTERVAL_HOUR_TO_SECOND:
case SQL_C_INTERVAL_MINUTE_TO_SECOND:
interval2istruct(fCType, precision, neut_str, bind_size > 0 ? (SQL_INTERVAL_STRUCT *) rgbValueBindRow : (SQL_INTERVAL_STRUCT *) rgbValue + bind_row);
break;
default:
MYLOG(0, "conversion to the type %d isn't supported\n", fCType);
return COPY_UNSUPPORTED_TYPE;
}
}
/* store the length of what was copied, if there's a place for it */
if (pcbValue)
*pcbValueBindRow = len;
if (result == COPY_OK && stmt->current_col >= 0)
gdata->gdata[stmt->current_col].data_left = 0;
return result;
}
/*--------------------------------------------------------------------
* Functions/Macros to get rid of query size limit.
*
* I always used the follwoing macros to convert from
* old_statement to new_statement. Please improve it
* if you have a better way. Hiroshi 2001/05/22
*--------------------------------------------------------------------
*/
#define FLGP_USING_CURSOR (1L << 1)
#define FLGP_SELECT_INTO (1L << 2)
#define FLGP_SELECT_FOR_UPDATE_OR_SHARE (1L << 3)
#define FLGP_MULTIPLE_STATEMENT (1L << 5)
#define FLGP_SELECT_FOR_READONLY (1L << 6)
typedef struct _QueryParse {
const char *statement;
int statement_type;
size_t opos;
ssize_t from_pos;
ssize_t where_pos;
ssize_t stmt_len;
int in_status;
char escape_in_literal, prev_token_end;
const char *dollar_tag;
ssize_t taglen;
char token_curr[64];
int token_len;
size_t declare_pos;
UInt4 flags, comment_level;
encoded_str encstr;
} QueryParse;
static void
QP_initialize(QueryParse *q, const StatementClass *stmt)
{
q->statement = stmt->statement;
q->statement_type = stmt->statement_type;
q->opos = 0;
q->from_pos = -1;
q->where_pos = -1;
q->stmt_len = (q->statement) ? strlen(q->statement) : -1;
q->in_status = 0;
q->escape_in_literal = '\0';
q->dollar_tag = NULL;
q->taglen = -1;
q->token_curr[0] = '\0';
q->token_len = 0;
q->prev_token_end = TRUE;
q->declare_pos = 0;
q->flags = 0;
q->comment_level = 0;
make_encoded_str(&q->encstr, SC_get_conn(stmt), q->statement);
}
enum {
QP_IN_IDENT_KEYWORD = 1L /* identifier or keyword */
, QP_IN_DQUOTE_IDENTIFIER = (1L << 1) /* "" */
, QP_IN_LITERAL = (1L << 2) /* '' */
, QP_IN_ESCAPE = (1L << 3) /* \ in literal */
, QP_IN_DOLLAR_QUOTE = (1L << 4) /* $...$ $...$ */
, QP_IN_COMMENT_BLOCK = (1L << 5) /* slash asterisk */
, QP_IN_LINE_COMMENT = (1L << 6) /* -- */
};
#define QP_in_idle_status(qp) ((qp)->in_status == 0)
#define QP_is_in(qp, status) (((qp)->in_status & status) != 0)
#define QP_enter(qp, status) ((qp)->in_status |= status)
#define QP_exit(qp, status) ((qp)->in_status &= (~status))
/*
* ResolveOneParam can be work in these four modes:
*
* RPM_REPLACE_PARAMS
* Replace parameter markers with their values.
*
* RPM_FAKE_PARAMS
* The query is going to be sent to the server only to be able to
* Describe the result set. Parameter markers are replaced with NULL
* literals if we don't know their real values yet.
*
* RPM_BUILDING_PREPARE_STATEMENT
* Building a query suitable for PREPARE statement, or server-side Parse.
* Parameter markers are replaced with $n-style parameter markers.
*
* RPM_BUILDING_BIND_REQUEST
* Building the actual parameter values to send to the server in a Bind
* request. Return an unescaped value that will be accepted by the
* server's input function.
*/
typedef enum
{
RPM_REPLACE_PARAMS,
RPM_FAKE_PARAMS,
RPM_BUILDING_PREPARE_STATEMENT,
RPM_BUILDING_BIND_REQUEST
} ResolveParamMode;
#define FLGB_INACCURATE_RESULT (1L << 4)
#define FLGB_CREATE_KEYSET (1L << 5)
#define FLGB_KEYSET_DRIVEN (1L << 6)
#define FLGB_CONVERT_LF (1L << 7)
#define FLGB_DISCARD_OUTPUT (1L << 8)
#define FLGB_BINARY_AS_POSSIBLE (1L << 9)
#define FLGB_LITERAL_EXTENSION (1L << 10)
#define FLGB_HEX_BIN_FORMAT (1L << 11)
#define FLGB_PARAM_CAST (1L << 12)
typedef struct _QueryBuild {
char *query_statement;
size_t str_alsize;
size_t npos;
SQLLEN current_row;
Int2 param_number;
Int2 dollar_number;
Int2 num_io_params;
Int2 num_output_params;
Int2 num_discard_params;
Int2 proc_return;
Int2 brace_level;
char parenthesize_the_first;
APDFields *apdopts;
IPDFields *ipdopts;
PutDataInfo *pdata;
size_t load_stmt_len;
size_t load_from_pos;
ResolveParamMode param_mode;
UInt4 flags;
int ccsc;
int errornumber;
const char *errormsg;
ConnectionClass *conn; /* mainly needed for LO handling */
StatementClass *stmt; /* needed to set error info in ENLARGE_.. */
} QueryBuild;
#define INIT_MIN_ALLOC 4096
static ssize_t
QB_initialize(QueryBuild *qb, size_t size, StatementClass *stmt, ResolveParamMode param_mode)
{
size_t newsize = 0;
qb->param_mode = param_mode;
qb->flags = 0;
qb->load_stmt_len = 0;
qb->load_from_pos = 0;
qb->stmt = stmt;
qb->apdopts = NULL;
qb->ipdopts = NULL;
qb->pdata = NULL;
qb->proc_return = 0;
qb->num_io_params = 0;
qb->num_output_params = 0;
qb->num_discard_params = 0;
qb->brace_level = 0;
qb->parenthesize_the_first = FALSE;
/* Copy options from statement */
qb->apdopts = SC_get_APDF(stmt);
qb->ipdopts = SC_get_IPDF(stmt);
qb->pdata = SC_get_PDTI(stmt);
qb->conn = SC_get_conn(stmt);
if (stmt->discard_output_params)
qb->flags |= FLGB_DISCARD_OUTPUT;
qb->num_io_params = CountParameters(stmt, NULL, NULL, &qb->num_output_params);
qb->proc_return = stmt->proc_return;
if (0 != (qb->flags & FLGB_DISCARD_OUTPUT))
qb->num_discard_params = qb->num_output_params;
if (qb->num_discard_params < qb->proc_return)
qb->num_discard_params = qb->proc_return;
/* Copy options from connection */
if (qb->conn->connInfo.lf_conversion)
qb->flags |= FLGB_CONVERT_LF;
qb->ccsc = qb->conn->ccsc;
if (CC_get_escape(qb->conn) &&
PG_VERSION_GE(qb->conn, 8.1))
qb->flags |= FLGB_LITERAL_EXTENSION;
if (PG_VERSION_GE(qb->conn, 9.0))
qb->flags |= FLGB_HEX_BIN_FORMAT;
newsize = INIT_MIN_ALLOC;
while (newsize <= size)
newsize *= 2;
if ((qb->query_statement = malloc(newsize)) == NULL)
{
qb->str_alsize = 0;
return -1;
}
qb->query_statement[0] = '\0';
qb->str_alsize = newsize;
qb->npos = 0;
qb->current_row = stmt->exec_current_row < 0 ? 0 : stmt->exec_current_row;
qb->param_number = -1;
qb->dollar_number = 0;
qb->errornumber = 0;
qb->errormsg = NULL;
return newsize;
}
static int
QB_initialize_copy(QueryBuild *qb_to, const QueryBuild *qb_from, UInt4 size)
{
memcpy(qb_to, qb_from, sizeof(QueryBuild));
if ((qb_to->query_statement = malloc(size)) == NULL)
{
qb_to->str_alsize = 0;
return -1;
}
qb_to->query_statement[0] = '\0';
qb_to->str_alsize = size;
qb_to->npos = 0;
return size;
}
static void
QB_replace_SC_error(StatementClass *stmt, const QueryBuild *qb, const char *func)
{
int number;
if (0 == qb->errornumber) return;
if ((number = SC_get_errornumber(stmt)) > 0) return;
if (number < 0 && qb->errornumber < 0) return;
SC_set_error(stmt, qb->errornumber, qb->errormsg, func);
}
static void
QB_Destructor(QueryBuild *qb)
{
if (qb->query_statement)
{
free(qb->query_statement);
qb->query_statement = NULL;
qb->str_alsize = 0;
}
}
/*
* New macros (Aceto)
*--------------------
*/
#define F_OldChar(qp) ((qp)->statement[(qp)->opos])
#define F_OldPtr(qp) ((qp)->statement + (qp)->opos)
#define F_OldNext(qp) (++(qp)->opos)
#define F_OldPrior(qp) (--(qp)->opos)
#define F_OldPos(qp) (qp)->opos
#define F_ExtractOldTo(qp, buf, ch, maxsize) \
do { \
size_t c = 0; \
while ((qp)->statement[qp->opos] != '\0' && (qp)->statement[qp->opos] != ch) \
{ \
if (c >= maxsize) \
break; \
buf[c++] = (qp)->statement[qp->opos++]; \
} \
if ((qp)->statement[qp->opos] == '\0') \
{retval = SQL_ERROR; goto cleanup;} \
buf[c] = '\0'; \
} while (0)
#define F_NewChar(qb) (qb->query_statement[(qb)->npos])
#define F_NewPtr(qb) ((qb)->query_statement + (qb)->npos)
#define F_NewNext(qb) (++(qb)->npos)
#define F_NewPos(qb) ((qb)->npos)
static int
convert_escape(QueryParse *qp, QueryBuild *qb);
static int
inner_process_tokens(QueryParse *qp, QueryBuild *qb);
static int
ResolveOneParam(QueryBuild *qb, QueryParse *qp, BOOL *isnull, BOOL *usebinary,
Oid *pgType);
static int
processParameters(QueryParse *qp, QueryBuild *qb,
size_t *output_count, SQLLEN param_pos[][2]);
static size_t
convert_to_pgbinary(const char *in, char *out, size_t len, QueryBuild *qb);
static BOOL convert_special_chars(QueryBuild *qb, const char *si, size_t used);
/*
* Enlarge qb->query_statement buffer so that it is at least newsize + 1
* in size.
*/
static ssize_t
enlarge_query_statement(QueryBuild *qb, size_t newsize)
{
size_t newalsize = INIT_MIN_ALLOC;
CSTR func = "enlarge_statement";
while (newalsize <= newsize)
newalsize *= 2;
if (!(qb->query_statement = realloc(qb->query_statement, newalsize)))
{
qb->str_alsize = 0;
if (qb->stmt)
{
SC_set_error(qb->stmt, STMT_EXEC_ERROR, "Query buffer allocate error in copy_statement_with_parameters", func);
}
else
{
qb->errormsg = "Query buffer allocate error in copy_statement_with_parameters";
qb->errornumber = STMT_EXEC_ERROR;
}
return 0;
}
qb->str_alsize = newalsize;
return newalsize;
}
/*----------
* Enlarge stmt_with_params if necessary.
*----------
*/
#define ENLARGE_NEWSTATEMENT(qb, newpos) \
do { \
if ((newpos) >= (qb)->str_alsize) \
{ \
if (enlarge_query_statement(qb, newpos) <= 0) \
{ \
retval = SQL_ERROR; \
goto cleanup; \
} \
} \
} while(0)
/*----------
* Terminate the stmt_with_params string with NULL.
*----------
*/
#define CVT_TERMINATE(qb) \
do { \
if (NULL == (qb)->query_statement) \
{ \
retval = SQL_ERROR; \
goto cleanup; \
} \
(qb)->query_statement[(qb)->npos] = '\0'; \
} while (0)
/*----------
* Append a data.
*----------
*/
#define CVT_APPEND_DATA(qb, s, len) \
do { \
size_t newpos = (qb)->npos + len; \
ENLARGE_NEWSTATEMENT((qb), newpos); \
memcpy(&(qb)->query_statement[(qb)->npos], s, len); \
(qb)->npos = newpos; \
(qb)->query_statement[newpos] = '\0'; \
} while (0)
/*----------
* Append a string.
*----------
*/
#define CVT_APPEND_STR(qb, s) \
do { \
size_t len = strlen(s); \
CVT_APPEND_DATA(qb, s, len); \
} while (0)
/*----------
* Append a char.
*----------
*/
#define CVT_APPEND_CHAR(qb, c) \
do { \
ENLARGE_NEWSTATEMENT(qb, (qb)->npos + 1); \
(qb)->query_statement[(qb)->npos++] = c; \
} while (0)
/*----------
* Append a binary data.
* Newly required size may be overestimated currently.
*----------
*/
#define CVT_APPEND_BINARY(qb, buf, used) \
do { \
size_t newlimit; \
\
newlimit = (qb)->npos; \
if (qb->flags & FLGB_HEX_BIN_FORMAT) \
newlimit += 2 * used + 4; \
else \
newlimit += 5 * used; \
ENLARGE_NEWSTATEMENT(qb, newlimit); \
(qb)->npos += convert_to_pgbinary(buf, \
&qb->query_statement[(qb)->npos], \
used, qb); \
} while (0)
/*----------
*
*----------
*/
#define CVT_SPECIAL_CHARS(qb, buf, used) \
do { \
if (!convert_special_chars(qb, buf, used)) \
{ \
retval = SQL_ERROR; \
goto cleanup; \
} \
} while (0)
static RETCODE
QB_start_brace(QueryBuild *qb)
{
BOOL replace_by_parenthesis = TRUE;
RETCODE retval = SQL_ERROR;
if (0 == qb->brace_level)
{
if (0 == F_NewPos(qb))
{
qb->parenthesize_the_first = FALSE;
replace_by_parenthesis = FALSE;
}
else
qb->parenthesize_the_first = TRUE;
}
if (replace_by_parenthesis)
CVT_APPEND_CHAR(qb, '(');
qb->brace_level++;
retval = SQL_SUCCESS;
cleanup:
return retval;
}
static RETCODE
QB_end_brace(QueryBuild *qb)
{
BOOL replace_by_parenthesis = TRUE;
RETCODE retval = SQL_ERROR;
if (qb->brace_level <= 1 &&
!qb->parenthesize_the_first)
replace_by_parenthesis = FALSE;
if (replace_by_parenthesis)
CVT_APPEND_CHAR(qb, ')');
qb->brace_level--;
retval = SQL_SUCCESS;
cleanup:
return retval;
}
static RETCODE QB_append_space_to_separate_identifiers(QueryBuild *qb, const QueryParse *qp)
{
unsigned char tchar = F_OldChar(qp);
encoded_str encstr;
BOOL add_space = FALSE;
RETCODE retval = SQL_ERROR;
if (ODBC_ESCAPE_END != tchar)
return SQL_SUCCESS;
encoded_str_constr(&encstr, qb->ccsc, F_OldPtr(qp) + 1);
tchar = encoded_nextchar(&encstr);
if (MBCS_NON_ASCII(encstr))
add_space = TRUE;
else
{
if (isalnum(tchar))
add_space = TRUE;
else
{
switch (tchar)
{
case '_':
case '$':
add_space = TRUE;
}
}
}
if (add_space)
CVT_APPEND_CHAR(qb, ' ');
retval = SQL_SUCCESS;
cleanup:
return retval;
}
/*----------
* Check if the statement is
* SELECT ... INTO table FROM .....
* This isn't really a strict check but ...
*----------
*/
static BOOL
into_table_from(const char *stmt)
{
if (strnicmp(stmt, "into", 4))
return FALSE;
stmt += 4;
while (isspace((UCHAR) *stmt)) stmt++;
switch (*stmt)
{
case '\0':
case ',':
case LITERAL_QUOTE:
case DOLLAR_QUOTE:
return FALSE;
case '-':
case '/':
return TRUE;
case IDENTIFIER_QUOTE: /* double quoted table name ? */
do
{
do {
++stmt;
} while (*stmt != IDENTIFIER_QUOTE && *stmt);
if (*stmt)
stmt++;
}
while (*stmt == IDENTIFIER_QUOTE);
break;
default:
while (IS_NOT_SPACE(*stmt)) stmt++;
break;
}
if (!*stmt)
return FALSE;
while (isspace((UCHAR) *stmt)) stmt++;
if ('/' == *stmt ||
'-' == *stmt)
return TRUE;
if (strnicmp(stmt, "from", 4))
return FALSE;
return TRUE;
}
/*----------
* Check if the statement is
* SELECT ... FOR UPDATE .....
* This isn't really a strict check but ...
*----------
*/
static UInt4
table_for_update_or_share(const char *stmt, size_t *endpos)
{
const char *wstmt = stmt;
int advance;
UInt4 flag = 0, zeroflag = 0;
while (isspace((UCHAR) *wstmt)) wstmt++;
if (!*wstmt)
return 0;
if (0 == strnicmp(wstmt, "update", advance = 6))
flag |= FLGP_SELECT_FOR_UPDATE_OR_SHARE;
else if (0 == strnicmp(wstmt, "share", advance = 5))
flag |= FLGP_SELECT_FOR_UPDATE_OR_SHARE;
else if (0 == strnicmp(wstmt, "read", advance = 4))
flag |= FLGP_SELECT_FOR_READONLY;
else
{
flag |= FLGP_SELECT_FOR_UPDATE_OR_SHARE; /* maybe */
return flag;
}
zeroflag = flag; /* returns flag anyway */
wstmt += advance;
if (IS_NOT_SPACE(wstmt[0]))
return zeroflag;
else if (0 != (flag & FLGP_SELECT_FOR_READONLY))
{
if (IS_NOT_SPACE(wstmt[0]))
return zeroflag;
while (isspace((UCHAR) *wstmt)) wstmt++;
if (!*wstmt)
return zeroflag;
if (0 != strnicmp(wstmt, "only", advance = 4))
return zeroflag;
wstmt += advance;
}
if (IS_NOT_SPACE(wstmt[0]))
return zeroflag;
*endpos = wstmt - stmt;
return flag;
}
/*----------
* Check if the statement has OUTER JOIN
* This isn't really a strict check but ...
*----------
*/
static BOOL
check_join(StatementClass *stmt, const char *curptr, size_t curpos)
{
const char *wstmt;
ssize_t stapos, endpos, tokenwd;
const int backstep = 4;
BOOL outerj = TRUE;
for (endpos = curpos, wstmt = curptr; endpos >= 0 && isspace((UCHAR) *wstmt); endpos--, wstmt--)
;
if (endpos < 0)
return FALSE;
for (endpos -= backstep, wstmt -= backstep; endpos >= 0 && isspace((UCHAR) *wstmt); endpos--, wstmt--)
;
if (endpos < 0)
return FALSE;
for (stapos = endpos; stapos >= 0 && IS_NOT_SPACE(*wstmt); stapos--, wstmt--)
;
if (stapos < 0 || 0 == *wstmt)
return FALSE;
wstmt++;
switch (tokenwd = endpos - stapos)
{
case 4:
if (strnicmp(wstmt, "FULL", tokenwd) == 0 ||
strnicmp(wstmt, "LEFT", tokenwd) == 0)
break;
return FALSE;
case 5:
if (strnicmp(wstmt, "OUTER", tokenwd) == 0 ||
strnicmp(wstmt, "RIGHT", tokenwd) == 0)
break;
if (strnicmp(wstmt, "INNER", tokenwd) == 0 ||
strnicmp(wstmt, "CROSS", tokenwd) == 0)
{
outerj = FALSE;
break;
}
return FALSE;
default:
return FALSE;
}
if (stmt)
{
if (outerj)
SC_set_outer_join(stmt);
else
SC_set_inner_join(stmt);
}
return TRUE;
}
/*----------
* Check if the statement is
* INSERT INTO ... () VALUES ()
* This isn't really a strict check but ...
*----------
*/
static BOOL
insert_without_target(const char *stmt, size_t *endpos)
{
const char *wstmt = stmt;
while (isspace((UCHAR) *wstmt)) wstmt++;
if (!*wstmt)
return FALSE;
if (strnicmp(wstmt, "VALUES", 6))
return FALSE;
wstmt += 6;
if (!wstmt[0] || IS_NOT_SPACE(wstmt[0]))
return FALSE;
while (isspace((UCHAR) *wstmt)) wstmt++;
if (*wstmt != '(' || *(++wstmt) != ')')
return FALSE;
wstmt++;
*endpos = wstmt - stmt;
return !wstmt[0] || isspace((UCHAR) wstmt[0])
|| ';' == wstmt[0];
}
static ProcessedStmt *
buildProcessedStmt(const char *srvquery, ssize_t endp, int num_params)
{
ProcessedStmt *pstmt;
size_t qlen;
qlen = (endp == SQL_NTS) ? strlen(srvquery) : endp;
pstmt = malloc(sizeof(ProcessedStmt));
if (!pstmt)
return NULL;
pstmt->next = NULL;
pstmt->query = malloc(qlen + 1);
if (!pstmt->query)
{
free(pstmt);
return NULL;
}
memcpy(pstmt->query, srvquery, qlen);
pstmt->query[qlen] = '\0';
pstmt->num_params = num_params;
return pstmt;
}
/*
* Process the original SQL query for execution using server-side prepared
* statements.
*
* Split a possible multi-statement query into parts, and replace ?-style
* parameter markers with $n. The resulting queries are stored in a linked
* list in stmt->processed_statements.
*
* If 'fake_params' is true, we will replace ?-style parameter markers with
* fake parameter values instead. This is used when a query's result columns
* have to be described (SQLPrepare+SQLDescribeCol) before executing the
* query, in UseServerSidePrepare=0 mode.
*/
RETCODE
prepareParametersNoDesc(StatementClass *stmt, BOOL fake_params, BOOL param_cast)
{
CSTR func = "process_statements";
RETCODE retval;
ConnectionClass *conn = SC_get_conn(stmt);
char plan_name[32];
po_ind_t multi;
const char *orgquery = NULL, *srvquery = NULL;
ssize_t endp1, endp2;
SQLSMALLINT num_pa = 0, num_p1, num_p2;
ProcessedStmt *pstmt;
ProcessedStmt *last_pstmt;
QueryParse query_org, *qp;
QueryBuild query_crt, *qb;
MYLOG(DETAIL_LOG_LEVEL, "entering\n");
qp = &query_org;
QP_initialize(qp, stmt);
qb = &query_crt;
if (QB_initialize(qb, qp->stmt_len, stmt,
fake_params ? RPM_FAKE_PARAMS : RPM_BUILDING_PREPARE_STATEMENT) < 0)
{
SC_set_errornumber(stmt, STMT_NO_MEMORY_ERROR);
return SQL_ERROR;
}
if (param_cast)
qb->flags |= FLGB_PARAM_CAST;
for (qp->opos = 0; qp->opos < qp->stmt_len; qp->opos++)
{
retval = inner_process_tokens(qp, qb);
if (SQL_ERROR == retval)
{
QB_replace_SC_error(stmt, qb, func);
QB_Destructor(qb);
return retval;
}
}
CVT_TERMINATE(qb);
retval = SQL_ERROR;
#define return DONT_CALL_RETURN_FROM_HERE???
if (NAMED_PARSE_REQUEST == SC_get_prepare_method(stmt))
SPRINTF_FIXED(plan_name, "_PLAN%p", stmt);
else
plan_name[0] = '\0';
stmt->current_exec_param = 0;
multi = stmt->multi_statement;
orgquery = stmt->statement;
srvquery = qb->query_statement;
SC_scanQueryAndCountParams(orgquery, conn, &endp1, &num_p1, &multi, NULL);
SC_scanQueryAndCountParams(srvquery, conn, &endp2, NULL, NULL, NULL);
MYLOG(0, "parsed for the first command length=" FORMAT_SSIZE_T "(" FORMAT_SSIZE_T ") num_p=%d\n", endp2, endp1, num_p1);
pstmt = buildProcessedStmt(srvquery,
endp2 < 0 ? SQL_NTS : endp2,
fake_params ? 0 : num_p1);
if (!pstmt)
{
SC_set_errornumber(stmt, STMT_NO_MEMORY_ERROR);
goto cleanup;
}
stmt->processed_statements = last_pstmt = pstmt;
while (multi > 0)
{
orgquery += (endp1 + 1);
srvquery += (endp2 + 1);
num_pa += num_p1;
SC_scanQueryAndCountParams(orgquery, conn, &endp1, &num_p1, &multi, NULL);
SC_scanQueryAndCountParams(srvquery, conn, &endp2, &num_p2, NULL, NULL);
MYLOG(0, "parsed for the subsequent command length=" FORMAT_SSIZE_T "(" FORMAT_SSIZE_T ") num_p=%d\n", endp2, endp1, num_p1);
pstmt = buildProcessedStmt(srvquery,
endp2 < 0 ? SQL_NTS : endp2,
fake_params ? 0 : num_p1);
if (!pstmt)
{
SC_set_errornumber(stmt, STMT_NO_MEMORY_ERROR);
goto cleanup;
}
last_pstmt->next = pstmt;
last_pstmt = pstmt;
}
SC_set_planname(stmt, plan_name);
SC_set_prepared(stmt, plan_name[0] ? PREPARING_PERMANENTLY : PREPARING_TEMPORARILY);
retval = SQL_SUCCESS;
cleanup:
#undef return
stmt->current_exec_param = -1;
QB_Destructor(qb);
return retval;
}
/*
* Describe the parameters and portal for given query.
*/
static RETCODE
desc_params_and_sync(StatementClass *stmt)
{
CSTR func = "desc_params_and_sync";
RETCODE retval;
ConnectionClass *conn = SC_get_conn(stmt);
QResultClass *res;
char *plan_name;
int func_cs_count = 0;
SQLSMALLINT num_pa = 0;
ProcessedStmt *pstmt;
MYLOG(DETAIL_LOG_LEVEL, "entering\n");
retval = SQL_ERROR;
#define return DONT_CALL_RETURN_FROM_HERE???
ENTER_INNER_CONN_CS(conn, func_cs_count);
plan_name = stmt->plan_name ? stmt->plan_name : "";
pstmt = stmt->processed_statements;
stmt->current_exec_param = 0;
res = ParseAndDescribeWithLibpq(stmt, plan_name, pstmt->query, pstmt->num_params, "prepare_and_describe", NULL);
if (res == NULL)
goto cleanup;
SC_set_Result(stmt, res);
if (!QR_command_maybe_successful(res))
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Error while preparing parameters", func);
goto cleanup;
}
num_pa = pstmt->num_params;
for (pstmt = pstmt->next; pstmt; pstmt = pstmt->next)
{
if (pstmt->num_params > 0)
{
stmt->current_exec_param = num_pa;
res = ParseAndDescribeWithLibpq(stmt, plan_name, pstmt->query, pstmt->num_params, "prepare_and_describe", NULL);
if (res == NULL)
goto cleanup;
QR_Destructor(res);
num_pa += pstmt->num_params;
}
}
retval = SQL_SUCCESS;
cleanup:
#undef return
CLEANUP_FUNC_CONN_CS(func_cs_count, conn);
stmt->current_exec_param = -1;
return retval;
}
/*
* Process the original SQL query, and and ask the server describe the
* parameters.
*/
RETCODE prepareParameters(StatementClass *stmt, BOOL fake_params)
{
ConnectionClass *conn = SC_get_conn(stmt);
switch (stmt->prepared)
{
case PREPARED_TEMPORARILY:
if (conn->unnamed_prepared_stmt == stmt)
return SQL_SUCCESS;
else
break;
case NOT_YET_PREPARED:
case PREPARING_PERMANENTLY:
case PREPARING_TEMPORARILY:
break;
default:
return SQL_SUCCESS;
}
MYLOG(DETAIL_LOG_LEVEL, "calling prepareParameters\n");
if (prepareParametersNoDesc(stmt, fake_params, PARSE_PARAM_CAST) == SQL_ERROR)
return SQL_ERROR;
return desc_params_and_sync(stmt);
}
/*
* This function inserts parameters into an SQL statements.
* It will also modify a SELECT statement for use with declare/fetch cursors.
* This function does a dynamic memory allocation to get rid of query size limit!
*/
int
copy_statement_with_parameters(StatementClass *stmt, BOOL buildPrepareStatement)
{
CSTR func = "copy_statement_with_parameters";
RETCODE retval;
QueryParse query_org, *qp;
QueryBuild query_crt, *qb;
char *new_statement;
ConnectionClass *conn = SC_get_conn(stmt);
ConnInfo *ci = &(conn->connInfo);
const char *bestitem = NULL;
MYLOG(DETAIL_LOG_LEVEL, "entering prepared=%d\n", stmt->prepared);
if (!stmt->statement)
{
SC_set_error(stmt, STMT_INTERNAL_ERROR, "No statement string", func);
return SQL_ERROR;
}
qp = &query_org;
QP_initialize(qp, stmt);
if (stmt->statement_type != STMT_TYPE_SELECT)
{
stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY;
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
}
else if (stmt->options.cursor_type == SQL_CURSOR_FORWARD_ONLY)
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
else if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
{
if (SQL_CURSOR_DYNAMIC == stmt->options.cursor_type &&
0 == (ci->updatable_cursors & ALLOW_DYNAMIC_CURSORS))
stmt->options.cursor_type = SQL_CURSOR_KEYSET_DRIVEN;
if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type &&
0 == (ci->updatable_cursors & ALLOW_KEYSET_DRIVEN_CURSORS))
stmt->options.cursor_type = SQL_CURSOR_STATIC;
switch (stmt->options.cursor_type)
{
case SQL_CURSOR_DYNAMIC:
case SQL_CURSOR_KEYSET_DRIVEN:
if (SC_update_not_ready(stmt))
parse_statement(stmt, TRUE);
if (SC_is_updatable(stmt) && stmt->ntab > 0)
{
if (bestitem = GET_NAME(stmt->ti[0]->bestitem), NULL == bestitem)
stmt->options.cursor_type = SQL_CURSOR_STATIC;
}
break;
}
if (SQL_CURSOR_STATIC == stmt->options.cursor_type)
{
if (0 == (ci->updatable_cursors & ALLOW_STATIC_CURSORS))
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
else if (SC_update_not_ready(stmt))
parse_statement(stmt, TRUE);
}
if (SC_parsed_status(stmt) == STMT_PARSE_FATAL)
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
stmt->options.cursor_type = SQL_CURSOR_STATIC;
}
else if (!stmt->updatable)
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
stmt->options.cursor_type = SQL_CURSOR_STATIC;
}
else
{
qp->from_pos = stmt->from_pos;
qp->where_pos = stmt->where_pos;
}
MYLOG(DETAIL_LOG_LEVEL, "type=%d concur=%d\n", stmt->options.cursor_type, stmt->options.scroll_concurrency);
}
SC_miscinfo_clear(stmt);
/* If the application hasn't set a cursor name, then generate one */
if (!SC_cursor_is_valid(stmt))
{
char curname[32];
SPRINTF_FIXED(curname, "SQL_CUR%p", stmt);
STRX_TO_NAME(stmt->cursor_name, curname);
}
if (stmt->stmt_with_params)
{
free(stmt->stmt_with_params);
stmt->stmt_with_params = NULL;
}
SC_no_fetchcursor(stmt);
qb = &query_crt;
qb->query_statement = NULL;
if (PREPARED_PERMANENTLY == stmt->prepared)
{
/* already prepared */
retval = SQL_SUCCESS;
goto cleanup;
}
/*
* If it's a simple read-only cursor, we use extended query protocol to
* Parse it.
*/
if (buildPrepareStatement &&
SQL_CONCUR_READ_ONLY == stmt->options.scroll_concurrency)
{
/* Nothing to do here. It will be prepared before execution. */
char plan_name[32];
if (NAMED_PARSE_REQUEST == SC_get_prepare_method(stmt))
SPRINTF_FIXED(plan_name, "_PLAN%p", stmt);
else
plan_name[0] = '\0';
SC_set_planname(stmt, plan_name);
SC_set_prepared(stmt, plan_name[0] ? PREPARING_PERMANENTLY : PREPARING_TEMPORARILY);
retval = SQL_SUCCESS;
goto cleanup;
}
/* Otherwise... */
if (QB_initialize(qb, qp->stmt_len, stmt, RPM_REPLACE_PARAMS) < 0)
{
retval = SQL_ERROR;
goto cleanup;
}
if (SIMPLE_PARAM_CAST)
qb->flags |= FLGB_PARAM_CAST;
new_statement = qb->query_statement;
/* For selects, prepend a declare cursor to the statement */
if (SC_may_use_cursor(stmt) && stmt->external)
{
const char *opt_scroll = NULL_STRING, *opt_hold = NULL_STRING;
if (ci->drivers.use_declarefetch
/** && SQL_CONCUR_READ_ONLY == stmt->options.scroll_concurrency **/
)
{
SC_set_fetchcursor(stmt);
if (SC_is_with_hold(stmt))
opt_hold = " with hold";
if (SQL_CURSOR_FORWARD_ONLY != stmt->options.cursor_type)
opt_scroll = " scroll";
}
if (SC_is_fetchcursor(stmt))
{
snprintfcat(new_statement, qb->str_alsize,
"declare \"%s\"%s cursor%s for ",
SC_cursor_name(stmt), opt_scroll, opt_hold);
qb->npos = strlen(new_statement);
qp->flags |= FLGP_USING_CURSOR;
qp->declare_pos = qb->npos;
}
if (SQL_CONCUR_READ_ONLY != stmt->options.scroll_concurrency)
{
qb->flags |= FLGB_CREATE_KEYSET;
if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type)
qb->flags |= FLGB_KEYSET_DRIVEN;
}
}
for (qp->opos = 0; qp->opos < qp->stmt_len; qp->opos++)
{
retval = inner_process_tokens(qp, qb);
if (SQL_ERROR == retval)
{
QB_replace_SC_error(stmt, qb, func);
QB_Destructor(qb);
return retval;
}
}
/* make sure new_statement is always null-terminated */
CVT_TERMINATE(qb);
new_statement = qb->query_statement;
stmt->statement_type = qp->statement_type;
if (0 == (qp->flags & FLGP_USING_CURSOR))
SC_no_fetchcursor(stmt);
#ifdef NOT_USED /* this seems problematic */
else if (0 == (qp->flags & (FLGP_SELECT_FOR_UPDATE_OR_SHARE | FLGP_SELECT_FOR_READONLY)) &&
0 == stmt->multi_statement &&
PG_VERSION_GE(conn, 8.3))
{
BOOL semi_colon_found = FALSE;
const UCHAR *ptr = NULL, semi_colon = ';';
int npos;
if (npos = F_NewPos(qb) - 1, npos >= 0)
ptr = F_NewPtr(qb) - 1;
for (; npos >= 0 && isspace(*ptr); npos--, ptr--) ;
if (npos >= 0 && semi_colon == *ptr)
{
qb->npos = npos;
semi_colon_found = TRUE;
}
CVT_APPEND_STR(qb, " for read only");
if (semi_colon_found)
CVT_APPEND_CHAR(qb, semi_colon);
CVT_TERMINATE(qb);
}
#endif /* NOT_USED */
if (0 != (qp->flags & FLGP_SELECT_INTO) ||
0 != (qp->flags & FLGP_MULTIPLE_STATEMENT))
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
}
if (0 != (qp->flags & FLGP_SELECT_FOR_UPDATE_OR_SHARE))
{
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
}
if (conn->DriverToDataSource != NULL)
{
size_t length = strlen(new_statement);
conn->DriverToDataSource(conn->translation_option,
SQL_CHAR, new_statement, (SDWORD) length,
new_statement, (SDWORD) length, NULL,
NULL, 0, NULL);
}
if (!stmt->load_statement && qp->from_pos >= 0)
{
size_t npos = qb->load_stmt_len;
if (0 == npos)
{
npos = qb->npos;
for (; npos > 0; npos--)
{
if (isspace((unsigned char) new_statement[npos - 1]))
continue;
if (';' != new_statement[npos - 1])
break;
}
if (0 != (qb->flags & FLGB_KEYSET_DRIVEN))
{
qb->npos = npos;
/* ----------
* 1st query is for field information
* 2nd query is keyset gathering
*/
CVT_APPEND_STR(qb, " where ctid = '(0,0)';select \"ctid");
if (bestitem)
{
CVT_APPEND_STR(qb, "\", \"");
CVT_APPEND_STR(qb, bestitem);
}
CVT_APPEND_STR(qb, "\" from ");
CVT_APPEND_DATA(qb, qp->statement + qp->from_pos + 5, npos - qp->from_pos - 5);
}
}
npos -= qp->declare_pos;
stmt->load_statement = malloc(npos + 1);
if (!stmt->load_statement)
{
retval = SQL_ERROR;
goto cleanup;
}
memcpy(stmt->load_statement, qb->query_statement + qp->declare_pos, npos);
stmt->load_from_pos = qb->load_from_pos - qp->declare_pos;
stmt->load_statement[npos] = '\0';
}
stmt->stmt_with_params = qb->query_statement;
retval = SQL_SUCCESS;
cleanup:
return retval;
}
static void
remove_declare_cursor(QueryBuild *qb, QueryParse *qp)
{
qp->flags &= ~FLGP_USING_CURSOR;
if (qp->declare_pos <= 0) return;
memmove(qb->query_statement, qb->query_statement + qp->declare_pos, qb->npos - qp->declare_pos);
qb->npos -= qp->declare_pos;
qp->declare_pos = 0;
}
/*
* When 'tag' starts with dollar-quoted tag, e.g. "$foo$...", return
* the length of the tag (e.g. 5, with the previous example). If there
* is no end-dollar in the string, returns 0. The caller should've checked
* that the string begins with a dollar.
*/
size_t
findTag(const char *tag, int ccsc)
{
size_t taglen = 0;
encoded_str encstr;
UCHAR tchar;
encoded_str_constr(&encstr, ccsc, tag + 1);
for (tchar = encoded_nextchar(&encstr); tchar; tchar = encoded_nextchar(&encstr))
{
if (MBCS_NON_ASCII(encstr))
continue;
if (DOLLAR_QUOTE == tchar)
{
taglen = encstr.pos + 2;
break;
}
if (!isalnum(tchar))
break;
}
return taglen;
}
int
findIdentifier(const UCHAR *str, int ccsc, const UCHAR **next_token)
{
int slen = -1;
encoded_str encstr;
UCHAR tchar;
BOOL dquote = FALSE;
*next_token = NULL;
encoded_str_constr(&encstr, ccsc, (const char *) str);
for (tchar = encoded_nextchar(&encstr); tchar; tchar = encoded_nextchar(&encstr))
{
if (MBCS_NON_ASCII(encstr))
continue;
if (encstr.pos == 0) /* the first character */
{
if (dquote = (IDENTIFIER_QUOTE == tchar), dquote)
continue;
if (!isalpha(tchar))
{
slen = 0;
if (IS_NOT_SPACE(tchar))
*next_token = ENCODE_PTR(encstr);
break;
}
}
if (dquote)
{
if (IDENTIFIER_QUOTE == tchar)
{
tchar = encoded_nextchar(&encstr);
if (IDENTIFIER_QUOTE == tchar)
continue;
slen = encstr.pos;
break;
}
}
else
{
if (isalnum(tchar))
continue;
switch (tchar)
{
case '_':
case DOLLAR_QUOTE:
continue;
}
slen = encstr.pos;
if (IS_NOT_SPACE(tchar))
*next_token = ENCODE_PTR(encstr);
break;
}
}
if (slen < 0 && !dquote)
slen = encstr.pos;
if (NULL == *next_token)
{
for (; tchar; tchar = encoded_nextchar(&encstr))
{
if (IS_NOT_SPACE((UCHAR) tchar))
{
*next_token = ENCODE_PTR(encstr);
break;
}
}
}
return slen;
}
static pgNAME lower_or_remove_dquote(pgNAME nm, const UCHAR *src, int srclen, int ccsc)
{
int i, outlen;
char *tc;
UCHAR tchar;
BOOL idQuote;
encoded_str encstr;
if (nm.name)
tc = realloc(nm.name, srclen + 1);
else
tc = malloc(srclen + 1);
if (!tc)
{
NULL_THE_NAME(nm);
return nm;
}
nm.name = tc;
idQuote = (src[0] == IDENTIFIER_QUOTE);
encoded_str_constr(&encstr, ccsc, (const char *) src);
for (i = 0, tchar = encoded_nextchar(&encstr), outlen = 0; i < srclen; i++, tchar = encoded_nextchar(&encstr))
{
if (MBCS_NON_ASCII(encstr))
{
tc[outlen++] = tchar;
continue;
}
if (idQuote)
{
if (IDENTIFIER_QUOTE == tchar)
{
if (0 == i)
continue;
if (i == srclen - 1)
continue;
i++;
tchar = encoded_nextchar(&encstr);
}
tc[outlen++] = tchar;
}
else
{
tc[outlen++] = tolower(tchar);
}
}
tc[outlen] = '\0';
return nm;
}
int
eatTableIdentifiers(const UCHAR *str, int ccsc, pgNAME *table, pgNAME *schema)
{
int len;
const UCHAR *next_token;
const UCHAR *tstr = str;
while (isspace(*tstr)) tstr++;
if ((len = findIdentifier(tstr, ccsc, &next_token)) <= 0)
return len; /* table name doesn't exist */
if (table)
{
if (IDENTIFIER_QUOTE == *tstr)
*table = lower_or_remove_dquote(*table, tstr, len, ccsc);
else
STRN_TO_NAME(*table, tstr, len);
}
if (!next_token || '.' != *next_token || (int) (next_token - tstr) != len)
return (int) (next_token - str); /* table only */
tstr = next_token + 1;
if ((len = findIdentifier(tstr, ccsc, &next_token)) <= 0)
return -1;
if (table)
{
if (schema)
MOVE_NAME(*schema, *table);
*table = lower_or_remove_dquote(*table, tstr, len, ccsc);
}
if (!next_token || '.' != *next_token || (int) (next_token - tstr) != len)
return (int) (next_token - str); /* schema.table */
tstr = next_token + 1;
if ((len = findIdentifier(tstr, ccsc, &next_token)) <= 0)
return -1;
if (table)
{
if (schema)
MOVE_NAME(*schema, *table);
*table = lower_or_remove_dquote(*table, tstr, len, ccsc);
}
return (int) (next_token - str); /* catalog.schema.table */
}
static void token_start(QueryParse *qp, char oldchar)
{
qp->prev_token_end = FALSE;
qp->token_curr[0] = oldchar;
qp->token_len = 1;
}
static int token_finish(QueryParse *qp, char oldchar, char *finished_token)
{
int ret = -1;
if (!qp->prev_token_end)
{
if (oldchar && qp->token_len + 1 < sizeof(qp->token_curr))
qp->token_curr[qp->token_len++] = oldchar;
qp->prev_token_end = TRUE;
qp->token_curr[qp->token_len] = '\0';
strncpy_null(finished_token, qp->token_curr, sizeof(qp->token_curr));
MYLOG(DETAIL_LOG_LEVEL, "finished token=%s\n", finished_token);
ret = qp->token_len;
}
return ret;
}
static int token_restart(QueryParse *qp, char oldchar, char *finished_token)
{
int ret = token_finish(qp, 0, finished_token);
if (IS_NOT_SPACE(oldchar))
token_start(qp, oldchar);
return ret;
}
static int token_continue(QueryParse *qp, char oldchar)
{
if (qp->prev_token_end)
token_start(qp, oldchar);
else if (qp->token_len + 1 < sizeof(qp->token_curr))
qp->token_curr[qp->token_len++] = oldchar;
return qp->token_len;
}
/*
* ParseToken functions
*/
typedef struct {
QueryParse *qp;
int token_len;
BOOL curchar_processed;
unsigned int in_status;
char finished_token[sizeof(((QueryParse *) NULL)->token_curr)];
} ParseToken;
static void PT_initialize(ParseToken *pt, QueryParse *qp)
{
pt->qp = qp;
pt->token_len = -1;
pt->curchar_processed = FALSE;
pt->in_status = 0;
pt->finished_token[0] = '\0';
}
static int PT_token_finish(ParseToken *pt, char oldchar)
{
int token_len_tmp;
if (pt->curchar_processed)
return pt->token_len;
if ((token_len_tmp = token_finish(pt->qp, oldchar, pt->finished_token)) > 0)
{
pt->token_len = token_len_tmp;
pt->in_status = pt->qp->in_status;
}
if (oldchar)
pt->curchar_processed = TRUE;
return pt->token_len;
}
static int PT_token_restart(ParseToken *pt, char oldchar)
{
int token_len_tmp;
unsigned int in_status_save;
if (pt->curchar_processed)
return pt->token_len;
in_status_save = pt->qp->in_status;
if ((token_len_tmp = token_restart(pt->qp, oldchar, pt->finished_token)) > 0)
{
pt->token_len = token_len_tmp;
pt->in_status = in_status_save;
}
pt->curchar_processed = TRUE;
return pt->token_len;
}
static int PT_token_continue(ParseToken *pt, char oldchar)
{
if (pt->curchar_processed)
return pt->token_len;
token_continue(pt->qp, oldchar);
pt->curchar_processed = TRUE;
return pt->token_len;
}
#define PT_TOKEN_IGNORE(pt) ((pt)->curchar_processed = TRUE)
static int
inner_process_tokens(QueryParse *qp, QueryBuild *qb)
{
CSTR func = "inner_process_tokens";
BOOL lf_conv = ((qb->flags & FLGB_CONVERT_LF) != 0);
const char *bestitem = NULL;
RETCODE retval;
Int4 opos;
char oldchar;
StatementClass *stmt = qb->stmt;
BOOL isnull;
BOOL isbinary;
Oid dummy;
ParseToken pts, *pt = &pts;
int ret = SQL_SUCCESS;
PT_initialize(pt, qp);
if (stmt->ntab > 0)
bestitem = GET_NAME(stmt->ti[0]->bestitem);
opos = (Int4) qp->opos;
if (opos < 0)
{
qb->errornumber = STMT_SEQUENCE_ERROR;
qb->errormsg = "Function call for inner_process_tokens sequence error";
return SQL_ERROR;
}
if (qp->from_pos == opos)
{
if (0 == (qb->flags & FLGB_CREATE_KEYSET))
{
qb->errornumber = STMT_SEQUENCE_ERROR;
qb->errormsg = "Should come here only when handling updatable cursors";
return SQL_ERROR;
}
CVT_APPEND_STR(qb, ", \"ctid");
if (bestitem)
{
CVT_APPEND_STR(qb, "\", \"");
CVT_APPEND_STR(qb, bestitem);
}
CVT_APPEND_STR(qb, "\" ");
qb->load_from_pos = qb->npos;
}
else if (qp->where_pos == opos)
{
qb->load_stmt_len = qb->npos;
if (0 != (qb->flags & FLGB_KEYSET_DRIVEN))
{
CVT_APPEND_STR(qb, "where ctid = '(0,0)';select \"ctid");
if (bestitem)
{
CVT_APPEND_STR(qb, "\", \"");
CVT_APPEND_STR(qb, bestitem);
}
CVT_APPEND_STR(qb, "\" from ");
CVT_APPEND_DATA(qb, qp->statement + qp->from_pos + 5, qp->where_pos - qp->from_pos - 5);
}
}
oldchar = encoded_byte_check(&qp->encstr, qp->opos);
if (MBCS_NON_ASCII(qp->encstr))
{
if (QP_in_idle_status(qp))
{
PT_token_restart(pt, oldchar); /* placed before QP_enter() */
QP_enter(qp, QP_IN_IDENT_KEYWORD); /* identifier */
}
else if (qp->token_len > 0)
PT_token_continue(pt, oldchar);
CVT_APPEND_CHAR(qb, oldchar);
return SQL_SUCCESS;
}
/*
* From here we are guaranteed to handle a 1-byte character.
*/
if (QP_is_in(qp, QP_IN_IDENT_KEYWORD)) /* identifier or keyword */
{
if (isalnum((UCHAR)oldchar) ||
DOLLAR_QUOTE == oldchar ||
'_' == oldchar)
{
CVT_APPEND_CHAR(qb, oldchar);
PT_token_continue(pt, oldchar);
return SQL_SUCCESS;
}
PT_token_finish(pt, 0); /* placed before QP_exit() */
QP_exit(qp, QP_IN_IDENT_KEYWORD);
}
if (QP_is_in(qp, QP_IN_ESCAPE)) /* escape in literal check */
{
QP_exit(qp, QP_IN_ESCAPE);
CVT_APPEND_CHAR(qb, oldchar);
return SQL_SUCCESS;
}
else if (QP_is_in(qp, QP_IN_DOLLAR_QUOTE)) /* dollar quote check */
{
if (oldchar == DOLLAR_QUOTE)
{
if (strncmp(F_OldPtr(qp), qp->dollar_tag, qp->taglen) == 0)
{
CVT_APPEND_DATA(qb, F_OldPtr(qp), qp->taglen);
qp->opos += (qp->taglen - 1);
QP_exit(qp, QP_IN_DOLLAR_QUOTE);
qp->dollar_tag = NULL;
qp->taglen = -1;
return SQL_SUCCESS;
}
}
CVT_APPEND_CHAR(qb, oldchar);
return SQL_SUCCESS;
}
else if (QP_is_in(qp, QP_IN_LITERAL)) /* quote check */
{
if (oldchar == LITERAL_QUOTE)
{
PT_token_finish(pt, oldchar); /* placed before QP_exit() */
QP_exit(qp, QP_IN_LITERAL);
}
else
{
PT_token_continue(pt, oldchar);
if (oldchar == qp->escape_in_literal)
QP_enter(qp, QP_IN_ESCAPE); /* escape in literal */
}
CVT_APPEND_CHAR(qb, oldchar);
return SQL_SUCCESS;
}
else if (QP_is_in(qp, QP_IN_DQUOTE_IDENTIFIER)) /* double quote check */
{
if (oldchar == IDENTIFIER_QUOTE)
{
PT_token_finish(pt, oldchar); /* placed before QP_exit() */
QP_exit(qp, QP_IN_DQUOTE_IDENTIFIER);
}
else
PT_token_continue(pt, oldchar);
CVT_APPEND_CHAR(qb, oldchar);
return SQL_SUCCESS;
}
else if (QP_is_in(qp, QP_IN_COMMENT_BLOCK)) /* comment_level check */
{
if ('/' == oldchar &&
'*' == F_OldPtr(qp)[1])
{
qp->comment_level++;
QP_enter(qp, QP_IN_COMMENT_BLOCK);
CVT_APPEND_CHAR(qb, oldchar);
F_OldNext(qp);
oldchar = F_OldChar(qp);
}
else if ('*' == oldchar &&
'/' == F_OldPtr(qp)[1])
{
if (--qp->comment_level <= 0)
QP_exit(qp, QP_IN_COMMENT_BLOCK);
CVT_APPEND_CHAR(qb, oldchar);
F_OldNext(qp);
oldchar = F_OldChar(qp);
}
CVT_APPEND_CHAR(qb, oldchar);
return SQL_SUCCESS;
}
else if (QP_is_in(qp, QP_IN_LINE_COMMENT)) /* line comment check */
{
if (PG_LINEFEED == oldchar)
QP_exit(qp, QP_IN_LINE_COMMENT);
CVT_APPEND_CHAR(qb, oldchar);
return SQL_SUCCESS;
}
if (!QP_in_idle_status(qp))
{
qb->errornumber = STMT_EXEC_ERROR;
qb->errormsg = "logic error? not in QP_in_idle_status";
return SQL_ERROR;
}
/*
* From here we are guaranteed to be in neither a literal_escape,
* a LITREAL_QUOTE nor an IDENTIFIER_QUOTE.
*/
/* Squeeze carriage-return/linefeed pairs to linefeed only */
if (lf_conv &&
PG_CARRIAGE_RETURN == oldchar &&
qp->opos + 1 < qp->stmt_len &&
PG_LINEFEED == qp->statement[qp->opos + 1])
return SQL_SUCCESS;
/*
* Handle literals (date, time, timestamp) and ODBC scalar
* functions
*/
if (oldchar == ODBC_ESCAPE_START)
{
PT_token_finish(pt, 0);
if (SQL_ERROR == convert_escape(qp, qb))
{
if (0 == qb->errornumber)
{
qb->errornumber = STMT_EXEC_ERROR;
qb->errormsg = "ODBC escape convert error";
}
MYLOG(0, "convert_escape error\n");
return SQL_ERROR;
}
PT_TOKEN_IGNORE(pt);
return SQL_SUCCESS;
}
/* End of an escape sequence */
else if (oldchar == ODBC_ESCAPE_END)
{
PT_token_finish(pt, 0);
PT_TOKEN_IGNORE(pt);
return QB_end_brace(qb);
}
else if (oldchar == '@' &&
strnicmp(F_OldPtr(qp), "@@identity", 10) == 0)
{
ConnectionClass *conn = SC_get_conn(stmt);
BOOL converted = FALSE;
COL_INFO *coli;
#ifdef NOT_USED /* lastval() isn't always appropriate */
if (PG_VERSION_GE(conn, 8.1))
{
CVT_APPEND_STR(qb, "lastval()");
converted = TRUE;
}
else
#endif /* NOT_USED */
if (NAME_IS_VALID(conn->tableIns))
{
TABLE_INFO ti, *pti = &ti;
BOOL found = FALSE;
memset(&ti, 0, sizeof(ti));
NAME_TO_NAME(ti.schema_name, conn->schemaIns);
NAME_TO_NAME(ti.table_name, conn->tableIns);
found = getCOLIfromTI(func, conn, qb->stmt, 0, &pti);
if(!found && conn->status== CONN_DOWN)
ret = SQL_ERROR;
coli = ti.col_info;
NULL_THE_NAME(ti.schema_name);
NULL_THE_NAME(ti.table_name);
if (NULL != coli)
{
int i, num_fields = QR_NumResultCols(coli->result);
const char *auto_increment;
for (i = 0; i < num_fields; i++)
{
auto_increment = (const char *) QR_get_value_backend_text(coli->result, i, COLUMNS_AUTO_INCREMENT);
if (auto_increment && auto_increment[0] == '1')
{
converted = TRUE;
break;
}
}
if (converted)
{
const char *column_def = (const char *) QR_get_value_backend_text(coli->result, i, COLUMNS_COLUMN_DEF);
if (NULL != column_def &&
strncmp(column_def, "nextval", 7) == 0)
{
CVT_APPEND_STR(qb, "curr");
CVT_APPEND_STR(qb, column_def + 4);
}
else
{
char relcnv[128];
const char *column_name = (const char *) QR_get_value_backend_text(coli->result, i, COLUMNS_COLUMN_NAME);
CVT_APPEND_STR(qb, "currval(pg_get_serial_sequence('");
if (NAME_IS_VALID(conn->schemaIns))
{
CVT_APPEND_STR(qb, identifierEscape((const SQLCHAR *) SAFE_NAME(conn->schemaIns), SQL_NTS, conn, relcnv, sizeof(relcnv), TRUE));
CVT_APPEND_STR(qb, ".");
}
CVT_APPEND_STR(qb, identifierEscape((const SQLCHAR *) SAFE_NAME(conn->tableIns), SQL_NTS, conn, relcnv, sizeof(relcnv), TRUE));
CVT_APPEND_STR(qb, "','");
if (NULL != column_name)
CVT_APPEND_STR(qb, identifierEscape((const SQLCHAR *) column_name, SQL_NTS, conn, relcnv, sizeof(relcnv), FALSE));
CVT_APPEND_STR(qb, "')::regclass)");
}
}
}
}
if (!converted)
CVT_APPEND_STR(qb, "NULL");
qp->opos += 10;
return ret;
}
/*
* Can you have parameter markers inside of quotes? I dont think
* so. All the queries I've seen expect the driver to put quotes
* if needed.
*/
else if (oldchar != '?')
{
if (oldchar == DOLLAR_QUOTE)
{
PT_token_finish(pt, 0);
qp->taglen = findTag(F_OldPtr(qp), qp->encstr.ccsc);
if (qp->taglen > 0)
{
QP_enter(qp, QP_IN_DOLLAR_QUOTE);
qp->dollar_tag = F_OldPtr(qp);
CVT_APPEND_DATA(qb, F_OldPtr(qp), qp->taglen);
qp->opos += (qp->taglen - 1);
return SQL_SUCCESS;
}
}
else if (oldchar == LITERAL_QUOTE)
{
PT_token_restart(pt, oldchar); /* placed before QP_enter() */
QP_enter(qp, QP_IN_LITERAL);
qp->escape_in_literal = CC_get_escape(qb->conn);
if (!qp->escape_in_literal)
{
if (LITERAL_EXT == F_OldPtr(qp)[-1])
qp->escape_in_literal = ESCAPE_IN_LITERAL;
}
}
else if (oldchar == IDENTIFIER_QUOTE)
{
PT_token_restart(pt, oldchar); /* placed before QP_enter() */
QP_enter(qp, QP_IN_DQUOTE_IDENTIFIER);
}
else if ('/' == oldchar &&
'*' == F_OldPtr(qp)[1])
{
qp->comment_level++;
PT_token_finish(pt, 0); /* comments are excluded */
QP_enter(qp, QP_IN_COMMENT_BLOCK);
PT_TOKEN_IGNORE(pt);
}
else if ('-' == oldchar &&
'-' == F_OldPtr(qp)[1])
{
PT_token_finish(pt, 0); /* comments are excluded */
QP_enter(qp, QP_IN_LINE_COMMENT);
PT_TOKEN_IGNORE(pt);
}
else if (oldchar == ';')
{
PT_token_restart(pt, 0);
/*
* can't parse multiple statement using protocol V3.
* reset the dollar number here in case it is divided
* to parse.
*/
qb->dollar_number = 0;
if (0 != (qp->flags & FLGP_USING_CURSOR))
{
const char *vp = &(qp->statement[qp->opos + 1]);
while (*vp && isspace((unsigned char) *vp))
vp++;
if (*vp) /* multiple statement */
{
qp->flags |= FLGP_MULTIPLE_STATEMENT;
qb->flags &= ~FLGB_KEYSET_DRIVEN;
remove_declare_cursor(qb, qp);
}
}
}
else if (isalnum(oldchar))
{
PT_token_restart(pt, oldchar); /* placed before QP_enter() */
QP_enter(qp, QP_IN_IDENT_KEYWORD); /* identifier or keyword */
}
else
{
/*
* a hack to handle boolean items in VBA
* with MS Access.
* VBA seems to transform the where condition
* a_boolean_item=True
* into
* ("a_boolean_item" = 1)
* which causes an ERROR:Operator does not exist boolean = integer .
* So transforms it into
* ("a_boolean_item"='1')
* here.
*/
if (')' == oldchar &&
qb->conn->ms_jet &&
1 == qp->token_len &&
'1' == qp->token_curr[0] &&
8 <= F_OldPos(qp))
{
const char *oldptr = F_OldPtr(qp);
int oldpos = F_OldPos(qp);
if (strncmp(oldptr - 5, "\" =", 3) == 0)
{
int i;
for (i = 6; i < oldpos - 1; i++)
{
if (oldptr[-i] == '"')
{
if (oldptr[-(i+1)] == '(')
{
F_NewPtr(qb)[-4] = '=';
F_NewPtr(qb)[-3] = '\'';
F_NewPtr(qb)[-2] = '1';
F_NewPtr(qb)[-1] = '\'';
}
break;
}
}
}
}
if (!isalnum((UCHAR) oldchar))
{
PT_token_restart(pt, oldchar);
}
else
PT_token_continue(pt, oldchar);
}
if (pt->token_len > 0)
MYLOG(0, "token_len=%d status=%x token=%s\n", pt->token_len, pt->in_status, pt->finished_token);
if (!pt->curchar_processed)
{
MYLOG(0, "Forgot to process ParseToken char=%c status=%u\n", oldchar, qp->in_status);
#ifdef NOT_USED /* strict check for debugging */
qb->errornumber = STMT_EXEC_ERROR;
qb->errormsg = "Forget to process ParseToken";
return SQL_ERROR;
#endif /* NOT_USED */
}
switch (pt->token_len)
{
case 4:
if (0 != (qp->flags & FLGP_USING_CURSOR) &&
into_table_from(&qp->statement[qp->opos - pt->token_len]))
{
qp->flags |= FLGP_SELECT_INTO;
qb->flags &= ~FLGB_KEYSET_DRIVEN;
qp->statement_type = STMT_TYPE_CREATE;
remove_declare_cursor(qb, qp);
}
else if (stricmp(pt->finished_token, "join") == 0)
check_join(stmt, F_OldPtr(qp), F_OldPos(qp));
break;
case 3:
if (0 != (qp->flags & FLGP_USING_CURSOR) &&
strnicmp(pt->finished_token, "for", 3) == 0)
{
UInt4 flg;
size_t endpos;
flg = table_for_update_or_share(F_OldPtr(qp), &endpos);
if (0 != (FLGP_SELECT_FOR_UPDATE_OR_SHARE & flg))
{
qp->flags |= flg;
remove_declare_cursor(qb, qp);
}
else
qp->flags |= flg;
}
break;
case 2:
{
size_t endpos;
if (STMT_TYPE_INSERT == qp->statement_type &&
strnicmp(pt->finished_token, "()", 2) == 0 &&
insert_without_target(F_OldPtr(qp), &endpos))
{
qb->npos -= 2;
CVT_APPEND_STR(qb, "DEFAULT VALUES");
qp->opos += endpos;
return SQL_SUCCESS;
}
break;
}
case 1:
{
size_t endpos;
if (STMT_TYPE_INSERT == qp->statement_type &&
pt->finished_token[0] == '(' &&
oldchar == ')' &&
insert_without_target(F_OldPtr(qp)+1, &endpos))
{
qb->npos --;
CVT_APPEND_STR(qb, " DEFAULT VALUES");
qp->opos += endpos;
return SQL_SUCCESS;
}
break;
}
}
CVT_APPEND_CHAR(qb, oldchar);
return SQL_SUCCESS;
}
else
PT_token_restart(pt, oldchar);
/*
* It's a '?' parameter alright
*/
retval = ResolveOneParam(qb, qp, &isnull, &isbinary, &dummy);
if (retval < 0)
return retval;
if (SQL_SUCCESS_WITH_INFO == retval) /* means discarding output parameter */
{
}
retval = SQL_SUCCESS;
cleanup:
return retval;
}
#define MIN_ALC_SIZE 128
/*
* Build an array of parameters to pass to libpq's PQexecPrepared
* function.
*/
BOOL
build_libpq_bind_params(StatementClass *stmt,
int *nParams,
OID **paramTypes,
char ***paramValues,
int **paramLengths,
int **paramFormats,
int *resultFormat)
{
CSTR func = "build_libpq_bind_params";
QueryBuild qb;
SQLSMALLINT num_p;
int i, num_params;
ConnectionClass *conn = SC_get_conn(stmt);
BOOL ret = FALSE, discard_output;
RETCODE retval;
const IPDFields *ipdopts = SC_get_IPDF(stmt);
*paramTypes = NULL;
*paramValues = NULL;
*paramLengths = NULL;
*paramFormats = NULL;
num_params = stmt->num_params;
if (num_params < 0)
{
PGAPI_NumParams(stmt, &num_p);
num_params = num_p;
}
if (ipdopts->allocated < num_params)
{
char tmp[100];
if (0 == ipdopts->allocated)
STRCPY_FIXED(tmp, "Parameters exist but IPD isn't set. Please call SQLDescribeParam()");
else
SPRINTF_FIXED(tmp, "The # of IPD parameters %d < %d the # of parameter markers", ipdopts->allocated, num_params);
MYLOG(0, "%s\n", tmp);
SC_set_error(stmt, STMT_COUNT_FIELD_INCORRECT, tmp, func);
return FALSE;
}
if (QB_initialize(&qb, MIN_ALC_SIZE, stmt, RPM_BUILDING_BIND_REQUEST) < 0)
return FALSE;
if (num_params > 0)
{
*paramTypes = malloc(sizeof(OID) * num_params);
if (*paramTypes == NULL)
goto cleanup;
*paramValues = malloc(sizeof(char *) * num_params);
if (*paramValues == NULL)
goto cleanup;
memset(*paramValues, 0, sizeof(char *) * num_params);
*paramLengths = malloc(sizeof(int) * num_params);
if (*paramLengths == NULL)
goto cleanup;
*paramFormats = malloc(sizeof(int) * num_params);
if (*paramFormats == NULL)
goto cleanup;
}
qb.flags |= FLGB_BINARY_AS_POSSIBLE;
MYLOG(DETAIL_LOG_LEVEL, "num_params=%d proc_return=%d\n", num_params, stmt->proc_return);
num_p = num_params - qb.num_discard_params;
MYLOG(DETAIL_LOG_LEVEL, "num_p=%d\n", num_p);
discard_output = (0 != (qb.flags & FLGB_DISCARD_OUTPUT));
*nParams = 0;
if (num_p > 0)
{
ParameterImplClass *parameters = ipdopts->parameters;
int pno;
BOOL isnull;
BOOL isbinary;
char *val_copy;
OID pgType;
/*
* Now build the parameter values.
*/
for (i = 0, pno = 0; i < stmt->num_params; i++)
{
qb.npos = 0;
retval = ResolveOneParam(&qb, NULL, &isnull, &isbinary, &pgType);
if (SQL_ERROR == retval)
{
QB_replace_SC_error(stmt, &qb, func);
ret = FALSE;
goto cleanup;
}
MYLOG(DETAIL_LOG_LEVEL, "%dth parameter type oid is %u\n", i, PIC_dsp_pgtype(conn, parameters[i]));
if (i < qb.proc_return)
continue;
if (SQL_PARAM_OUTPUT == parameters[i].paramType)
{
if (discard_output)
continue;
(*paramTypes)[pno] = PG_TYPE_VOID;
(*paramValues)[pno] = NULL;
(*paramLengths)[pno] = 0;
(*paramFormats)[pno] = 0;
pno++;
continue;
}
if (!isnull)
{
val_copy = malloc(qb.npos + 1);
if (!val_copy)
goto cleanup;
memcpy(val_copy, qb.query_statement, qb.npos);
val_copy[qb.npos] = '\0';
(*paramTypes)[pno] = pgType;
(*paramValues)[pno] = val_copy;
if (qb.npos > INT_MAX)
goto cleanup;
(*paramLengths)[pno] = (int) qb.npos;
}
else
{
(*paramTypes)[pno] = pgType;
(*paramValues)[pno] = NULL;
(*paramLengths)[pno] = 0;
}
if (isbinary)
MYLOG(0, "%dth parameter is of binary format\n", pno);
(*paramFormats)[pno] = isbinary ? 1 : 0;
pno++;
}
*nParams = pno;
}
/* result format is text */
*resultFormat = 0;
ret = TRUE;
cleanup:
QB_Destructor(&qb);
return ret;
}
/*
* Build an array of parameters to pass to libpq's PQexecPreparedBatch
* function.
*/
BOOL
build_libpq_bind_params_batch(StatementClass *stmt,
int *nParams,
int *nBatchCount, /* [output] It's only for batch protocol. */
OID **paramTypes,
char ***paramValues,
int **paramLengths,
int **paramFormats,
int *resultFormat)
{
CSTR func = "build_libpq_bind_params";
QueryBuild qb;
SQLSMALLINT num_p;
int i, num_params;
int total_rows;
ConnectionClass *conn = SC_get_conn(stmt);
BOOL ret = FALSE, discard_output;
RETCODE retval;
const IPDFields *ipdopts = SC_get_IPDF(stmt);
const APDFields *apdopts = SC_get_APDF(stmt);
*paramTypes = NULL;
*paramValues = NULL;
*paramLengths = NULL;
*paramFormats = NULL;
*nBatchCount = 0;
total_rows = apdopts->paramset_size;
num_params = stmt->num_params;
if (num_params < 0)
{
PGAPI_NumParams(stmt, &num_p);
num_params = num_p;
}
if (ipdopts->allocated < num_params)
{
SC_set_error(stmt, STMT_COUNT_FIELD_INCORRECT, "The # of binded parameters < the # of parameter markers", func);
return FALSE;
}
if (QB_initialize(&qb, MIN_ALC_SIZE, stmt, RPM_BUILDING_BIND_REQUEST) < 0)
return FALSE;
*paramTypes = malloc(sizeof(OID) * num_params);
if (*paramTypes == NULL)
goto cleanup;
*paramValues = malloc(sizeof(char *) * num_params * total_rows);
if (*paramValues == NULL)
goto cleanup;
memset(*paramValues, 0, sizeof(char *) * num_params * total_rows);
*paramLengths = malloc(sizeof(int) * num_params * total_rows);
if (*paramLengths == NULL)
goto cleanup;
*paramFormats = malloc(sizeof(int) * num_params * total_rows);
if (*paramFormats == NULL)
goto cleanup;
MYLOG(DETAIL_LOG_LEVEL, "num_params=%d proc_return=%d\n", num_params, stmt->proc_return);
num_p = num_params - qb.num_discard_params;
MYLOG(DETAIL_LOG_LEVEL, "num_p=%d\n", num_p);
stmt->exec_start_row = 0;
stmt->exec_end_row = total_rows;
if (num_p > 0)
{
ParameterImplClass *parameters = ipdopts->parameters;
int row = 0;
stmt->exec_current_row = 0;
do
{
BOOL paramproceed = FALSE;
/*
* Now build the parameter values.
*/
*nParams = 0;
stmt->exec_current_row = row;
QB_Destructor(&qb);
if (QB_initialize(&qb, MIN_ALC_SIZE, stmt, RPM_BUILDING_BIND_REQUEST) < 0)
return FALSE;
discard_output = (0 != (qb.flags & FLGB_DISCARD_OUTPUT));
qb.flags |= FLGB_BINARY_AS_POSSIBLE;
for (i = 0; i < stmt->num_params; i++)
{
BOOL isnull = FALSE;
BOOL isbinary = FALSE;
char *val_copy = NULL;
OID pgType = 0;
MYLOG(DETAIL_LOG_LEVEL, "%dth parameter type oid is %u\n", i, PIC_dsp_pgtype(conn, parameters[i]));
if (discard_output && SQL_PARAM_OUTPUT == parameters[i].paramType)
continue;
qb.npos = 0;
retval = ResolveOneParam(&qb, NULL, &isnull, &isbinary, &pgType);
if (SQL_ERROR == retval)
{
QB_replace_SC_error(stmt, &qb, func);
ret = FALSE;
goto cleanup;
}
/* Batch execution, ignore the parameters user set to ignored.
* It's different from normal execution, in which case, the ignored
* parameters are treated by the caller.
*/
if (apdopts->param_operation_ptr &&
apdopts->param_operation_ptr[row] == SQL_PARAM_IGNORE)
{
(*nParams)++;
continue;
}
else
{
paramproceed = TRUE;
}
if (!isnull)
{
val_copy = malloc(qb.npos + 1);
if (!val_copy)
goto cleanup;
memcpy(val_copy, qb.query_statement, qb.npos);
val_copy[qb.npos] = '\0';
(*paramTypes)[i] = pgType;
(*paramValues)[(*nBatchCount) * stmt->num_params + i] = val_copy;
if (qb.npos > INT_MAX)
goto cleanup;
(*paramLengths)[(*nBatchCount) * stmt->num_params + i] = (int) qb.npos;
}
else
{
(*paramTypes)[i] = pgType;
(*paramValues)[(*nBatchCount) * stmt->num_params + i] = NULL;
(*paramLengths)[(*nBatchCount) * stmt->num_params + i] = 0;
}
if (isbinary)
mylog("%dth parameter is of binary format\n", *nParams);
(*paramFormats)[i] = isbinary ? 1 : 0;
(*nParams)++;
}
if (paramproceed)
{
*nBatchCount += 1;
}
row ++;
}while(row < total_rows);
}
else
*nParams = 0;
/* result format is text */
*resultFormat = 0;
ret = TRUE;
cleanup:
QB_Destructor(&qb);
return ret;
}
/*
* With SQL_MAX_NUMERIC_LEN = 16, the highest representable number is
* 2^128 - 1, which fits in 39 digits.
*/
#define MAX_NUMERIC_DIGITS 39
/*
* Convert a SQL_NUMERIC_STRUCT into string representation.
*/
static void
ResolveNumericParam(const SQL_NUMERIC_STRUCT *ns, char *chrform)
{
Int4 i, vlen, len, newlen;
const UCHAR *val = (const UCHAR *) ns->val;
UCHAR vals[SQL_MAX_NUMERIC_LEN];
int lastnonzero;
UCHAR calv[MAX_NUMERIC_DIGITS];
int precision;
MYLOG(DETAIL_LOG_LEVEL, "C_NUMERIC [prec=%d scale=%d]", ns->precision, ns->scale);
if (0 == ns->precision)
{
if (chrform)
strncpy_null(chrform, "0", 2);
return;
}
precision = ns->precision;
if (precision > MAX_NUMERIC_DIGITS)
precision = MAX_NUMERIC_DIGITS;
/*
* The representation in SQL_NUMERIC_STRUCT is 16 bytes with most
* significant byte first. Make a working copy.
*/
memcpy(vals, val, SQL_MAX_NUMERIC_LEN);
vlen = SQL_MAX_NUMERIC_LEN;
len = 0;
do
{
UInt2 d, r;
/*
* Divide the number by 10, and output the reminder as the next digit.
*
* Begin from the most-significant byte (last in the array), and at
* each step, carry the remainder to the prev byte.
*/
r = 0;
lastnonzero = -1;
for (i = vlen - 1; i >= 0; i--)
{
UInt2 v;
v = ((UInt2) vals[i]) + (r << 8);
d = v / 10; r = v % 10;
vals[i] = (UCHAR) d;
if (d != 0 && lastnonzero == -1)
lastnonzero = i;
}
/* output the remainder */
calv[len++] = (UCHAR) r;
vlen = lastnonzero + 1;
} while(lastnonzero >= 0 && len < precision);
/*
* calv now contains the digits in reverse order, i.e. least significant
* digit is at calv[0]
*/
MYPRINTF(DETAIL_LOG_LEVEL, " len2=%d", len);
/* build the final output string. */
newlen = 0;
if (0 == ns->sign)
chrform[newlen++] = '-';
i = len - 1;
if (i < ns->scale)
i = ns->scale;
for (; i >= ns->scale; i--)
{
if (i >= len)
chrform[newlen++] = '0';
else
chrform[newlen++] = calv[i] + '0';
}
if (ns->scale > 0)
{
chrform[newlen++] = '.';
for (; i >= 0; i--)
{
if (i >= len)
chrform[newlen++] = '0';
else
chrform[newlen++] = calv[i] + '0';
}
}
if (0 == len)
chrform[newlen++] = '0';
chrform[newlen] = '\0';
MYLOG(DETAIL_LOG_LEVEL, " convval(2) len=%d %s\n", newlen, chrform);
}
/*
* Convert a string representation of a numeric into SQL_NUMERIC_STRUCT.
*/
static void
parse_to_numeric_struct(const char *wv, SQL_NUMERIC_STRUCT *ns, BOOL *overflow)
{
int i, nlen, dig;
char calv[SQL_MAX_NUMERIC_LEN * 3];
BOOL dot_exist;
*overflow = FALSE;
/* skip leading space */
while (*wv && isspace((unsigned char) *wv))
wv++;
/* sign */
ns->sign = 1;
if (*wv == '-')
{
ns->sign = 0;
wv++;
}
else if (*wv == '+')
wv++;
/* skip leading zeros */
while (*wv == '0')
wv++;
/* read the digits into calv */
ns->precision = 0;
ns->scale = 0;
for (nlen = 0, dot_exist = FALSE;; wv++)
{
if (*wv == '.')
{
if (dot_exist)
break;
dot_exist = TRUE;
}
else if (*wv == '\0' || !isdigit((unsigned char) *wv))
break;
else
{
if (nlen >= sizeof(calv))
{
if (dot_exist)
break;
else
{
ns->scale--;
*overflow = TRUE;
continue;
}
}
if (dot_exist)
ns->scale++;
calv[nlen++] = *wv;
}
}
ns->precision = nlen;
/* Convert the decimal digits to binary */
memset(ns->val, 0, sizeof(ns->val));
for (dig = 0; dig < nlen; dig++)
{
UInt4 carry;
/* multiply the current value by 10, and add the next digit */
carry = calv[dig] - '0';
for (i = 0; i < sizeof(ns->val); i++)
{
UInt4 t;
t = ((UInt4) ns->val[i]) * 10 + carry;
ns->val[i] = (unsigned char) (t & 0xFF);
carry = (t >> 8);
}
if (carry != 0)
*overflow = TRUE;
}
}
static BOOL
parameter_is_with_cast(const QueryParse *qp)
{
const char *str = F_OldPtr(qp);
if ('?' != *str) return FALSE;
while (isspace(*(++str))) ;
if (strncmp(str, "::", 2) == 0)
return TRUE;
if (strnicmp(str, "as", 2) != 0)
return FALSE;
if (isspace(str[2]))
return TRUE;
return FALSE;
}
#ifdef UNICODE_SUPPORT
enum {
ErrorOutConversionErrors /* error out conversion errors */
, ReturnZeroLengthString /* simply returns zero length strings */
};
static int convert_err_flag =
#ifdef WIN32
ReturnZeroLengthString;
#else
ErrorOutConversionErrors;
#endif /* WIN32 */
static BOOL
handle_lu_onvert_error(QueryBuild *qb, int flag, char *buffer, SQLLEN paralen)
{
int blen = paralen;
if (!buffer)
return FALSE;
if (get_mylog() > 0 || ReturnZeroLengthString != flag)
{
const UCHAR *buf = (UCHAR *) buffer;
int i;
PQExpBufferData pbuf = {0};
if (SQL_NTS == blen)
blen = strlen(buffer);
initPQExpBuffer(&pbuf);
appendPQExpBuffer(&pbuf, "Could not convert the current data '");
for (i = 0; i < blen; i++)
{
if (buf[i] >= 0x80)
appendPQExpBuffer(&pbuf, "\\%03o", buf[i]);
else if ('\\' == buf[i])
appendPQExpBuffer(&pbuf, "\\\\");
else
appendPQExpBuffer(&pbuf, "%c", buf[i]);
}
appendPQExpBuffer(&pbuf, "' to wide chars");
MYLOG(0, "%s\n", pbuf.data);
if (ReturnZeroLengthString != flag)
{
if (qb->stmt)
SC_set_error(qb->stmt, STMT_EXEC_ERROR, pbuf.data, __FUNCTION__);
else
qb->errormsg = "could not convert the current data to wide chars";
}
termPQExpBuffer(&pbuf);
}
switch (flag)
{
case ReturnZeroLengthString:
if (qb->stmt)
SC_set_error(qb->stmt, STMT_ERROR_IN_ROW, "conversion error to wide chars occured", __FUNCTION__);
return TRUE;
default:
qb->errornumber = STMT_EXEC_ERROR;
return FALSE;
}
}
#endif /* UNICODE_SUPPORT */
/*
* Resolve one parameter.
*
* *isnull is set to TRUE if it was NULL.
* *isbinary is set to TRUE, if the binary output format was used. (binary
* output is only produced if the FLGB_BINARY_AS_POSSIBLE flag is set)
* *pgType is set to the PostgreSQL type OID that should be used when binding
* (or 0, to let the server decide)
*/
static int
ResolveOneParam(QueryBuild *qb, QueryParse *qp, BOOL *isnull, BOOL *isbinary,
OID *pgType)
{
ConnectionClass *conn = qb->conn;
const APDFields *apdopts = qb->apdopts;
const IPDFields *ipdopts = qb->ipdopts;
PutDataInfo *pdata = qb->pdata;
int param_number;
char param_string[150],
tmp[256];
char cbuf[PG_NUMERIC_MAX_PRECISION * 2]; /* seems big enough to handle the data in this function */
OID param_pgtype;
SQLSMALLINT param_ctype, param_sqltype;
SIMPLE_TIME st;
struct tm *tim;
SQLLEN used;
const char *send_buf;
char *buffer, *allocbuf = NULL, *lastadd = NULL;
OID lobj_oid;
int lobj_fd;
SQLULEN offset = apdopts->param_offset_ptr ? *apdopts->param_offset_ptr : 0;
size_t current_row = qb->current_row;
BOOL handling_large_object = FALSE, req_bind;
BOOL need_quotes = TRUE;
BOOL add_parens = FALSE;
BOOL negative;
ParameterInfoClass *apara;
ParameterImplClass *ipara;
BOOL outputDiscard,
valueOutput;
SDOUBLE dbv;
SFLOAT flv;
SQL_INTERVAL_STRUCT *ivstruct;
const char *ivsign;
BOOL final_binary_convert = FALSE;
RETCODE retval = SQL_ERROR;
*isnull = FALSE;
*isbinary = FALSE;
*pgType = 0;
outputDiscard = (0 != (qb->flags & FLGB_DISCARD_OUTPUT));
valueOutput = (qb->param_mode != RPM_FAKE_PARAMS &&
qb->param_mode != RPM_BUILDING_PREPARE_STATEMENT);
req_bind = (qb->param_mode == RPM_BUILDING_BIND_REQUEST);
if (qb->proc_return < 0 && qb->stmt)
qb->proc_return = qb->stmt->proc_return;
/*
* It's a '?' parameter alright
*/
param_number = ++qb->param_number;
MYLOG(DETAIL_LOG_LEVEL, "para:%d(%d,%d)\n", param_number, ipdopts->allocated, apdopts->allocated);
apara = NULL;
ipara = NULL;
if (param_number < apdopts->allocated)
apara = apdopts->parameters + param_number;
if (param_number < ipdopts->allocated)
ipara = ipdopts->parameters + param_number;
if ((!apara || !ipara) && valueOutput)
{
MYLOG(0, "The # of (A|I)PD parameters (%d, %d) < %d the # of parameter markers\n", apdopts->allocated, ipdopts->allocated, param_number);
qb->errormsg = "The # of binded parameters < the # of parameter markers";
qb->errornumber = STMT_COUNT_FIELD_INCORRECT;
CVT_TERMINATE(qb); /* just in case */
return SQL_ERROR;
}
MYLOG(DETAIL_LOG_LEVEL, "ipara=%p paramType=%d %d proc_return=%d\n", ipara, ipara ? ipara->paramType : -1, PG_VERSION_LT(conn, 8.1), qb->proc_return);
if (param_number < qb->proc_return)
{
if (ipara && SQL_PARAM_OUTPUT != ipara->paramType)
{
qb->errormsg = "The function return value isn't marked as output parameter";
qb->errornumber = STMT_EXEC_ERROR;
CVT_TERMINATE(qb); /* just in case */
return SQL_ERROR;
}
return SQL_SUCCESS;
}
if (ipara && SQL_PARAM_OUTPUT == ipara->paramType)
{
if (PG_VERSION_LT(conn, 8.1))
{
qb->errormsg = "Output parameter isn't available before 8.1 version";
qb->errornumber = STMT_INTERNAL_ERROR;
CVT_TERMINATE(qb); /* just in case */
return SQL_ERROR;
}
if (outputDiscard)
{
ssize_t npos = 0;
for (npos = qb->npos - 1; npos >= 0 && isspace((unsigned char) qb->query_statement[npos]) ; npos--) ;
if (npos >= 0)
{
switch (qb->query_statement[npos])
{
case ',':
qb->npos = npos;
qb->query_statement[npos] = '\0';
break;
case '(':
if (!qp)
break;
for (npos = qp->opos + 1; isspace((unsigned char) qp->statement[npos]); npos++) ;
if (qp->statement[npos] == ',')
qp->opos = npos;
break;
}
}
return SQL_SUCCESS_WITH_INFO;
}
}
if ((!apara || !ipara) && qb->param_mode == RPM_FAKE_PARAMS)
{
CVT_APPEND_STR(qb, "NULL");
qb->flags |= FLGB_INACCURATE_RESULT;
return SQL_SUCCESS;
}
if (qb->param_mode == RPM_BUILDING_PREPARE_STATEMENT)
{
char pnum[16];
qb->dollar_number++;
if (ipara &&
SQL_PARAM_OUTPUT != ipara->paramType &&
(qb->flags & FLGB_PARAM_CAST) != 0 &&
!parameter_is_with_cast(qp))
SPRINTF_FIXED(pnum, "$%d%s", qb->dollar_number, sqltype_to_pgcast(conn, ipara->SQLType));
else
SPRINTF_FIXED(pnum, "$%d", qb->dollar_number);
CVT_APPEND_STR(qb, pnum);
return SQL_SUCCESS;
}
/*
* After this point, we can assume apara and ipara to be set. The only
* cases where we allow them to be NULL is when param_mode is
* RPM_FAKE_PARAMS or RPM_BUILDING_PREPARE_STATEMENT, and we've now handled
* those cases.
*/
/* Assign correct buffers based on data at exec param or not */
if (apara->data_at_exec)
{
if (pdata->allocated != apdopts->allocated)
extend_putdata_info(pdata, apdopts->allocated, TRUE);
used = pdata->pdata[param_number].EXEC_used ? *pdata->pdata[param_number].EXEC_used : SQL_NTS;
buffer = pdata->pdata[param_number].EXEC_buffer;
if (pdata->pdata[param_number].lobj_oid)
handling_large_object = TRUE;
}
else
{
UInt4 bind_size = apdopts->param_bind_type;
UInt4 ctypelen;
BOOL bSetUsed = FALSE;
buffer = apara->buffer + offset;
if (current_row > 0)
{
if (bind_size > 0)
buffer += (bind_size * current_row);
else if (ctypelen = ctype_length(apara->CType), ctypelen > 0)
buffer += current_row * ctypelen;
else
buffer += current_row * apara->buflen;
}
if (apara->used || apara->indicator)
{
SQLULEN p_offset;
if (bind_size > 0)
p_offset = offset + bind_size * current_row;
else
p_offset = offset + sizeof(SQLLEN) * current_row;
if (apara->indicator)
{
used = *LENADDR_SHIFT(apara->indicator, p_offset);
if (SQL_NULL_DATA == used)
bSetUsed = TRUE;
}
if (!bSetUsed && apara->used)
{
used = *LENADDR_SHIFT(apara->used, p_offset);
bSetUsed = TRUE;
}
}
if (!bSetUsed)
used = SQL_NTS;
}
param_ctype = apara->CType;
param_sqltype = ipara->SQLType;
param_pgtype = PIC_dsp_pgtype(qb->conn, *ipara);
/* XXX: should we use param_pgtype here instead? */
*pgType = sqltype_to_bind_pgtype(conn, param_sqltype);
if (0 == param_sqltype) /* calling SQLSetStmtAttr(.., SQL_ATTR_APP_PARAM_DES, an ARD of another statement) may cause this */
{
if (0 != param_pgtype)
{
param_sqltype = pgtype_attr_to_concise_type(conn, param_pgtype, PG_ATP_UNSET, PG_ADT_UNSET, PG_UNKNOWNS_UNSET);
MYLOG(0, "convert from pgtype(%u) to sqltype(%d)\n", param_pgtype, param_sqltype);
}
}
MYLOG(0, "from(fcType)=%d, to(fSqlType)=%d(%u), *pgType=%u\n",
param_ctype, param_sqltype, param_pgtype, *pgType);
/* Handle NULL parameter data */
if (SQL_PARAM_OUTPUT == ipara->paramType
|| used == SQL_NULL_DATA
|| used == SQL_DEFAULT_PARAM)
{
*isnull = TRUE;
if (!req_bind)
CVT_APPEND_STR(qb, "NULL");
return SQL_SUCCESS;
}
/*
* If no buffer, and it's not null, then what the hell is it? Just
* leave it alone then.
*/
if (!buffer)
{
if (qb->param_mode == RPM_FAKE_PARAMS)
{
CVT_APPEND_STR(qb, "NULL");
qb->flags |= FLGB_INACCURATE_RESULT;
return SQL_SUCCESS;
}
else if (!handling_large_object)
{
/* shouldn't happen */
qb->errormsg = "unexpected NULL parameter value";
qb->errornumber = STMT_EXEC_ERROR;
return SQL_ERROR;
}
}
/* replace DEFAULT with something we can use */
if (param_ctype == SQL_C_DEFAULT)
{
param_ctype = sqltype_to_default_ctype(conn, param_sqltype);
#ifdef UNICODE_SUPPORT
if (param_ctype == SQL_C_WCHAR
&& CC_default_is_c(conn))
param_ctype =SQL_C_CHAR;
#endif
}
allocbuf = NULL;
send_buf = NULL;
param_string[0] = '\0';
cbuf[0] = '\0';
memset(&st, 0, sizeof(st));
ivstruct = (SQL_INTERVAL_STRUCT *) buffer;
/* Convert input C type to a neutral format */
#ifdef UNICODE_SUPPORT
if (get_convtype() > 0) /* coversion between the current locale is available */
{
BOOL wcs_debug = conn->connInfo.wcs_debug;
BOOL is_utf8 = (UTF8 == conn->ccsc);
BOOL same_encoding = (conn->ccsc == pg_CS_code(conn->locale_encoding));
switch (param_ctype)
{
case SQL_C_CHAR:
if (!same_encoding || wcs_debug)
{
SQLLEN paralen = used;
MYLOG(0, "locale param convert\n");
if ((used = bindpara_msg_to_utf8(buffer, &allocbuf, used)) < 0)
{
if (!handle_lu_onvert_error(qb, convert_err_flag, buffer, paralen))
goto cleanup;
send_buf = NULL_STRING;
used = 0;
}
else
send_buf = allocbuf;
}
break;
case SQL_C_WCHAR:
if (!is_utf8 || (same_encoding && wcs_debug))
{
MYLOG(0, "hybrid param convert\n");
if ((used = bindpara_wchar_to_msg((SQLWCHAR *) buffer, &allocbuf, used)) < 0)
{
qb->errormsg = "Could not convert from wide characters to the current locale";
qb->errornumber = STMT_EXEC_ERROR;
goto cleanup;
}
send_buf = allocbuf;
}
break;
}
}
#endif /* UNICODE_SUPPORT */
switch (param_ctype)
{
case SQL_C_BINARY:
send_buf = buffer;
break;
case SQL_C_CHAR:
if (NULL == send_buf)
send_buf = buffer;
break;
#ifdef UNICODE_SUPPORT
case SQL_C_WCHAR:
MYLOG(0, " C_WCHAR=%d contents=%s(" FORMAT_LEN ")\n", param_ctype, buffer, used);
if (NULL == send_buf)
{
allocbuf = ucs2_to_utf8((SQLWCHAR *) buffer, used > 0 ? used / WCLEN : used, &used, FALSE);
send_buf = allocbuf;
}
break;
#endif /* UNICODE_SUPPORT */
case SQL_C_DOUBLE:
dbv = *((SDOUBLE *) buffer);
#ifdef WIN32
if (_finite(dbv))
#endif /* WIN32 */
{
SPRINTF_FIXED(param_string, "%.*g", PG_DOUBLE_DIGITS, dbv);
set_server_decimal_point(param_string, SQL_NTS);
}
#ifdef WIN32
else if (_isnan(dbv))
STRCPY_FIXED(param_string, NAN_STRING);
else if (dbv < .0)
STRCPY_FIXED(param_string, MINFINITY_STRING);
else
STRCPY_FIXED(param_string, INFINITY_STRING);
#endif /* WIN32 */
break;
case SQL_C_FLOAT:
flv = *((SFLOAT *) buffer);
#ifdef WIN32
if (_finite(flv))
#endif /* WIN32 */
{
SPRINTF_FIXED(param_string, "%.*g", PG_REAL_DIGITS, flv);
set_server_decimal_point(param_string, SQL_NTS);
}
#ifdef WIN32
else if (_isnan(flv))
STRCPY_FIXED(param_string, NAN_STRING);
else if (flv < .0)
STRCPY_FIXED(param_string, MINFINITY_STRING);
else
STRCPY_FIXED(param_string, INFINITY_STRING);
#endif /* WIN32 */
break;
case SQL_C_SLONG:
case SQL_C_LONG:
SPRINTF_FIXED(param_string, FORMAT_INTEGER,
*((SQLINTEGER *) buffer));
break;
#ifdef ODBCINT64
case SQL_C_SBIGINT:
case SQL_BIGINT: /* Is this needed ? */
SPRINTF_FIXED(param_string, FORMATI64,
*((SQLBIGINT *) buffer));
break;
case SQL_C_UBIGINT:
SPRINTF_FIXED(param_string, FORMATI64U,
*((SQLUBIGINT *) buffer));
break;
#endif /* ODBCINT64 */
case SQL_C_SSHORT:
case SQL_C_SHORT:
ITOA_FIXED(param_string, *((SQLSMALLINT *) buffer));
break;
case SQL_C_STINYINT:
case SQL_C_TINYINT:
ITOA_FIXED(param_string, *((SCHAR *) buffer));
break;
case SQL_C_ULONG:
SPRINTF_FIXED(param_string, FORMAT_UINTEGER,
*((SQLUINTEGER *) buffer));
break;
case SQL_C_USHORT:
SPRINTF_FIXED(param_string, "%u",
*((SQLUSMALLINT *) buffer));
break;
case SQL_C_UTINYINT:
SPRINTF_FIXED(param_string, "%u",
*((UCHAR *) buffer));
break;
case SQL_C_BIT:
{
int i = *((UCHAR *) buffer);
ITOA_FIXED(param_string, i ? 1 : 0);
break;
}
case SQL_C_DATE:
case SQL_C_TYPE_DATE: /* 91 */
{
DATE_STRUCT *ds = (DATE_STRUCT *) buffer;
st.m = ds->month;
st.d = ds->day;
st.y = ds->year;
break;
}
case SQL_C_TIME:
case SQL_C_TYPE_TIME: /* 92 */
{
TIME_STRUCT *ts = (TIME_STRUCT *) buffer;
st.hh = ts->hour;
st.mm = ts->minute;
st.ss = ts->second;
/*
* Initialize date in case conversion destination
* expects date part from this source time data.
*/
tim = SC_get_localtime(qb->stmt);
st.m = tim->tm_mon + 1;
st.d = tim->tm_mday;
st.y = tim->tm_year + 1900;
break;
}
case SQL_C_TIMESTAMP:
case SQL_C_TYPE_TIMESTAMP: /* 93 */
{
TIMESTAMP_STRUCT *tss = (TIMESTAMP_STRUCT *) buffer;
st.m = tss->month;
st.d = tss->day;
st.y = tss->year;
st.hh = tss->hour;
st.mm = tss->minute;
st.ss = tss->second;
st.fr = tss->fraction;
MYLOG(0, "m=%d,d=%d,y=%d,hh=%d,mm=%d,ss=%d\n", st.m, st.d, st.y, st.hh, st.mm, st.ss);
break;
}
case SQL_C_NUMERIC:
{
ResolveNumericParam((SQL_NUMERIC_STRUCT *) buffer, param_string);
break;
}
case SQL_C_INTERVAL_YEAR:
ivsign = ivstruct->interval_sign ? "-" : "";
SPRINTF_FIXED(param_string, "%s%u years", ivsign, (unsigned int) ivstruct->intval.year_month.year);
break;
case SQL_C_INTERVAL_MONTH:
case SQL_C_INTERVAL_YEAR_TO_MONTH:
ivsign = ivstruct->interval_sign ? "-" : "";
SPRINTF_FIXED(param_string, "%s%u years %s%u mons", ivsign, (unsigned int) ivstruct->intval.year_month.year, ivsign, (unsigned int) ivstruct->intval.year_month.month);
break;
case SQL_C_INTERVAL_DAY:
ivsign = ivstruct->interval_sign ? "-" : "";
SPRINTF_FIXED(param_string, "%s%u days", ivsign, (unsigned int) ivstruct->intval.day_second.day);
break;
case SQL_C_INTERVAL_HOUR:
case SQL_C_INTERVAL_DAY_TO_HOUR:
ivsign = ivstruct->interval_sign ? "-" : "";
SPRINTF_FIXED(param_string, "%s%u days %s%02u:00:00", ivsign, (unsigned int) ivstruct->intval.day_second.day, ivsign, (unsigned int) ivstruct->intval.day_second.hour);
break;
case SQL_C_INTERVAL_MINUTE:
case SQL_C_INTERVAL_HOUR_TO_MINUTE:
case SQL_C_INTERVAL_DAY_TO_MINUTE:
ivsign = ivstruct->interval_sign ? "-" : "";
SPRINTF_FIXED(param_string, "%s%u days %s%02u:%02u:00", ivsign, (unsigned int) ivstruct->intval.day_second.day, ivsign, (unsigned int) ivstruct->intval.day_second.hour, (unsigned int) ivstruct->intval.day_second.minute);
break;
case SQL_C_INTERVAL_SECOND:
case SQL_C_INTERVAL_DAY_TO_SECOND:
case SQL_C_INTERVAL_HOUR_TO_SECOND:
case SQL_C_INTERVAL_MINUTE_TO_SECOND:
ivsign = ivstruct->interval_sign ? "-" : "";
SPRINTF_FIXED(param_string, "%s%u days %s%02u:%02u:%02u",
ivsign, (unsigned int) ivstruct->intval.day_second.day,
ivsign, (unsigned int) ivstruct->intval.day_second.hour,
(unsigned int) ivstruct->intval.day_second.minute,
(unsigned int) ivstruct->intval.day_second.second);
if (ivstruct->intval.day_second.fraction > 0)
{
int fraction = ivstruct->intval.day_second.fraction, prec = apara->precision;
while (fraction % 10 == 0)
{
fraction /= 10;
prec--;
}
SPRINTFCAT_FIXED(param_string, ".%0*d", prec, fraction);
}
break;
case SQL_C_GUID:
{
/*
* SQLGUID.Data1 is an "unsigned long" on some platforms, and
* "unsigned int" on others.
*/
SQLGUID *g = (SQLGUID *) buffer;
SPRINTF_FIXED (param_string,
"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
(unsigned int) g->Data1,
g->Data2, g->Data3,
g->Data4[0], g->Data4[1], g->Data4[2], g->Data4[3],
g->Data4[4], g->Data4[5], g->Data4[6], g->Data4[7]);
}
break;
default:
/* error */
qb->errormsg = "Unrecognized C_parameter type in copy_statement_with_parameters";
qb->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
CVT_TERMINATE(qb); /* just in case */
goto cleanup;
}
/*
* Now that the input data is in a neutral format, convert it to
* the desired output format (sqltype)
*/
/* Special handling NULL string For FOXPRO */
MYLOG(0, "cvt_null_date_string=%d pgtype=%d send_buf=%p\n", conn->connInfo.cvt_null_date_string, param_pgtype, send_buf);
if (conn->connInfo.cvt_null_date_string > 0 &&
(PG_TYPE_DATE == param_pgtype ||
PG_TYPE_DATETIME == param_pgtype ||
PG_TYPE_TIMESTAMP_NO_TMZONE == param_pgtype) &&
NULL != send_buf &&
(
(SQL_C_CHAR == param_ctype && '\0' == send_buf[0])
#ifdef UNICODE_SUPPORT
|| (SQL_C_WCHAR ==param_ctype && '\0' == send_buf[0] && '\0' == send_buf[1])
#endif /* UNICODE_SUPPORT */
))
{
*isnull = TRUE;
if (!req_bind)
CVT_APPEND_STR(qb, "NULL");
retval = SQL_SUCCESS;
goto cleanup;
}
/*
* We now have the value we want to print in one of these three canonical
* formats:
*
* 1. As a string in 'send_buf', with length indicated by 'used' (can be
* SQL_NTS).
* 2. As a null-terminated string in 'param_string'.
* 3. Time-related fields in 'st'.
*/
/*
* For simplicity, fold the param_string representation into 'send_buf'.
*/
if (!send_buf && param_string[0])
{
send_buf = param_string;
used = SQL_NTS;
}
/*
* Do some further processing to create the final string we want to output.
* This will use the fields in 'st' to create a string if it's a time/date
* value, and do some other conversions.
*/
switch (param_sqltype)
{
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
#ifdef UNICODE_SUPPORT
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
#endif /* UNICODE_SUPPORT */
case SQL_BIT:
/* Special handling for some column types */
switch (param_pgtype)
{
case PG_TYPE_BOOL:
/*
* consider True is -1 case.
*
* FIXME: This actually matches anything that begins
* with -1, like "-1234" or "-1foobar". Is that
* intentional?
*/
if (NULL != send_buf && '-' == send_buf[0] && '1' == send_buf[1])
{
send_buf = "1";
used = 1;
}
break;
case PG_TYPE_FLOAT4:
case PG_TYPE_FLOAT8:
case PG_TYPE_NUMERIC:
if (NULL != send_buf)
set_server_decimal_point((char *) send_buf, used);
break;
}
if (!send_buf)
{
/* it was date,time,timestamp -- use m,d,y,hh,mm,ss */
SPRINTF_FIXED(tmp, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
st.y, st.m, st.d, st.hh, st.mm, st.ss);
send_buf = tmp;
used = SQL_NTS;
}
break;
case SQL_DATE:
case SQL_TYPE_DATE: /* 91 */
if (send_buf)
{ /* copy char data to time */
my_strcpy(cbuf, sizeof(cbuf), send_buf, used);
parse_datetime(cbuf, &st);
}
if (st.y < 0)
SPRINTF_FIXED(tmp, "%.4d-%.2d-%.2d BC", -st.y, st.m, st.d);
else
SPRINTF_FIXED(tmp, "%.4d-%.2d-%.2d", st.y, st.m, st.d);
lastadd = "::date";
send_buf = tmp;
used = SQL_NTS;
break;
case SQL_TIME:
case SQL_TYPE_TIME: /* 92 */
if (send_buf)
{ /* copy char data to time */
my_strcpy(cbuf, sizeof(cbuf), send_buf, used);
parse_datetime(cbuf, &st);
}
if (st.fr > 0)
{
int wdt;
int fr = effective_fraction(st.fr, &wdt);
SPRINTF_FIXED(tmp, "%.2d:%.2d:%.2d.%0*d", st.hh, st.mm, st.ss, wdt, fr);
}
else
SPRINTF_FIXED(tmp, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss);
lastadd = "::time";
send_buf = tmp;
used = SQL_NTS;
break;
case SQL_TIMESTAMP:
case SQL_TYPE_TIMESTAMP: /* 93 */
if (send_buf)
{
my_strcpy(cbuf, sizeof(cbuf), send_buf, used);
parse_datetime(cbuf, &st);
}
/*
* SPRINTF_FIXED(tmp, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d", st.y,
* st.m, st.d, st.hh, st.mm, st.ss);
*/
/* Time zone stuff is unreliable */
stime2timestamp(&st, tmp, sizeof(tmp), USE_ZONE, 6);
lastadd = "::timestamp";
send_buf = tmp;
used = SQL_NTS;
break;
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
switch (param_ctype)
{
case SQL_C_BINARY:
break;
case SQL_C_CHAR:
#ifdef UNICODE_SUPPORT
case SQL_C_WCHAR:
#endif /* UNICODE_SUPPORT */
switch (used)
{
case SQL_NTS:
used = strlen(send_buf);
break;
}
allocbuf = malloc(used / 2 + 1);
if (allocbuf)
{
pg_hex2bin(send_buf, allocbuf, used);
send_buf = allocbuf;
used /= 2;
}
break;
default:
qb->errormsg = "Could not convert the ctype to binary type";
qb->errornumber = STMT_EXEC_ERROR;
goto cleanup;
}
if (filed_is_bytea(param_pgtype))
{
if (0 != (qb->flags & FLGB_BINARY_AS_POSSIBLE))
{
MYLOG(0, "sending binary data leng=" FORMAT_LEN "\n", used);
*isbinary = TRUE;
}
else
{
/* non-ascii characters should be
* converted to octal
*/
MYLOG(0, "SQL_VARBINARY: about to call convert_to_pgbinary, used = " FORMAT_LEN "\n", used);
final_binary_convert = TRUE;
}
break;
}
if (PG_TYPE_OID == param_pgtype && conn->lo_is_domain)
;
else if (param_pgtype != conn->lobj_type)
{
qb->errormsg = "Could not convert binary other than LO type";
qb->errornumber = STMT_EXEC_ERROR;
goto cleanup;
}
if (apara->data_at_exec)
lobj_oid = pdata->pdata[param_number].lobj_oid;
else
{
BOOL is_in_trans_at_entry = CC_is_in_trans(conn);
int write_result;
/* begin transaction if needed */
if (!is_in_trans_at_entry)
{
if (!CC_begin(conn))
{
qb->errormsg = "Could not begin (in-line) a transaction";
qb->errornumber = STMT_EXEC_ERROR;
goto cleanup;
}
}
/* store the oid */
lobj_oid = odbc_lo_creat(conn, INV_READ | INV_WRITE);
if (lobj_oid == 0)
{
qb->errornumber = STMT_EXEC_ERROR;
qb->errormsg = "Couldn't create (in-line) large object.";
goto cleanup;
}
/* store the fd */
lobj_fd = odbc_lo_open(conn, lobj_oid, INV_WRITE);
if (lobj_fd < 0)
{
qb->errornumber = STMT_EXEC_ERROR;
qb->errormsg = "Couldn't open (in-line) large object for writing.";
goto cleanup;
}
write_result = odbc_lo_write(conn, lobj_fd, buffer, (Int4) used);
if (write_result < 0)
{
qb->errornumber = STMT_EXEC_ERROR;
qb->errormsg = "Couldn't write to (in-line) large object.";
goto cleanup;
}
odbc_lo_close(conn, lobj_fd);
/* commit transaction if needed */
if (!is_in_trans_at_entry)
{
if (!CC_commit(conn))
{
qb->errormsg = "Could not commit (in-line) a transaction";
qb->errornumber = STMT_EXEC_ERROR;
goto cleanup;
}
}
}
/*
* the oid of the large object -- just put that in for the
* parameter marker -- the data has already been sent to
* the large object
*/
SPRINTF_FIXED(param_string, "%u", lobj_oid);
lastadd = "::lo";
send_buf = param_string;
used = SQL_NTS;
break;
/*
* because of no conversion operator for bool and int4,
* SQL_BIT
*/
/* must be quoted (0 or 1 is ok to use inside the quotes) */
case SQL_REAL:
set_server_decimal_point((char *) send_buf, used);
lastadd = "::float4";
break;
case SQL_FLOAT:
case SQL_DOUBLE:
set_server_decimal_point((char *) send_buf, used);
lastadd = "::float8";
break;
case SQL_NUMERIC:
break;
/*
* If it looks like a valid integer, we can pass it without quotes
* and let the server interpret it. Arguably, it would always be
* better to explicitly pass it as 'xxx'::integer or 'xxx'::smallint,
* but historically we haven't done that, so let's avoid changing the
* behaviour.
*
* If it's a negative number, we have to wrap it in parens. Otherwise
* a query like "SELECT 0-?" would turn into "SELECT 0--123".
*/
case SQL_INTEGER:
if (valid_int_literal(send_buf, used, &negative))
{
need_quotes = FALSE;
add_parens = negative;
}
else
{
/*
* Doesn't look like a valid integer. The server will most
* likely throw an error, unless it's in some format we don't
* recognize but the server does.
*/
lastadd = "::int4";
}
break;
case SQL_SMALLINT:
if (valid_int_literal(send_buf, used, &negative))
{
need_quotes = FALSE;
add_parens = negative;
}
else
lastadd = "::smallint";
break;
default: /* a numeric type or SQL_BIT */
break;
}
if (!send_buf)
{
qb->errormsg = "Could not convert parameter ctype to sqltype";
qb->errornumber = STMT_EXEC_ERROR;
goto cleanup;
}
if (used == SQL_NTS)
used = strlen(send_buf);
/*
* Ok, we now have the final string representation in 'send_buf', length 'used'.
* We're ready to output the final string, with quotes and other
* embellishments if necessary.
*
* In bind-mode, we don't need to do any quoting.
*/
if (req_bind)
CVT_APPEND_DATA(qb, send_buf, used);
else
{
if (add_parens)
CVT_APPEND_CHAR(qb, '(');
if (need_quotes)
{
if ((qb->flags & FLGB_LITERAL_EXTENSION) != 0)
CVT_APPEND_CHAR(qb, LITERAL_EXT);
CVT_APPEND_CHAR(qb, LITERAL_QUOTE);
if (final_binary_convert)
CVT_APPEND_BINARY(qb, send_buf, used);
else
CVT_SPECIAL_CHARS(qb, send_buf, used);
CVT_APPEND_CHAR(qb, LITERAL_QUOTE);
}
else
CVT_APPEND_DATA(qb, send_buf, used);
if (add_parens)
CVT_APPEND_CHAR(qb, ')');
if (lastadd && (FLGB_PARAM_CAST & qb->flags) != 0)
CVT_APPEND_STR(qb, lastadd);
}
retval = SQL_SUCCESS;
cleanup:
if (allocbuf)
free(allocbuf);
return retval;
}
static const char *
mapFunction(const char *func, int param_count)
{
int i;
for (i = 0; mapFuncs[i].odbc_name; i++)
{
if (mapFuncs[i].odbc_name[0] == '%')
{
if (mapFuncs[i].odbc_name[1] - '0' == param_count &&
!stricmp(&mapFuncs[i].odbc_name[2], func))
return mapFuncs[i].pgsql_name;
}
else if (!stricmp(mapFuncs[i].odbc_name, func))
return mapFuncs[i].pgsql_name;
}
return NULL;
}
/*
* processParameters()
* Process function parameters and work with embedded escapes sequences.
*/
static int
processParameters(QueryParse *qp, QueryBuild *qb,
size_t *output_count, SQLLEN param_pos[][2])
{
int retval, innerParenthesis, param_count;
BOOL stop;
/* begin with outer '(' */
innerParenthesis = 0;
param_count = 0;
if (NULL != output_count)
*output_count = 0;
stop = FALSE;
for (; F_OldPos(qp) < qp->stmt_len; F_OldNext(qp))
{
retval = inner_process_tokens(qp, qb);
if (retval == SQL_ERROR)
return retval;
if (MBCS_NON_ASCII(qp->encstr))
continue;
if (!QP_in_idle_status(qp))
continue;
switch (F_OldChar(qp))
{
case ',':
if (1 == innerParenthesis)
{
param_pos[param_count][1] = F_NewPos(qb) - 2;
param_count++;
param_pos[param_count][0] = F_NewPos(qb);
param_pos[param_count][1] = -1;
}
break;
case '(':
if (0 == innerParenthesis)
{
param_pos[param_count][0] = F_NewPos(qb);
param_pos[param_count][1] = -1;
}
innerParenthesis++;
break;
case ')':
innerParenthesis--;
if (0 == innerParenthesis)
{
param_pos[param_count][1] = F_NewPos(qb) - 2;
param_count++;
param_pos[param_count][0] =
param_pos[param_count][1] = -1;
}
if (output_count)
*output_count = F_NewPos(qb);
break;
case ODBC_ESCAPE_END:
stop = (0 == innerParenthesis);
break;
}
if (stop) /* returns with the last } position */
break;
}
if (param_pos[param_count][0] >= 0)
{
MYLOG(0, "closing ) not found %d\n", innerParenthesis);
qb->errornumber = STMT_EXEC_ERROR;
qb->errormsg = "processParameters closing ) not found";
return SQL_ERROR;
}
else if (1 == param_count) /* the 1 parameter is really valid ? */
{
BOOL param_exist = FALSE;
SQLLEN i;
for (i = param_pos[0][0]; i <= param_pos[0][1]; i++)
{
if (IS_NOT_SPACE(qb->query_statement[i]))
{
param_exist = TRUE;
break;
}
}
if (!param_exist)
{
param_pos[0][0] = param_pos[0][1] = -1;
}
}
return SQL_SUCCESS;
}
/*
* convert_escape()
* This function doesn't return a pointer to static memory any longer !
*/
static int
convert_escape(QueryParse *qp, QueryBuild *qb)
{
RETCODE retval = SQL_SUCCESS;
char buf[1024], buf_small[128], key[65];
UCHAR ucv;
UInt4 prtlen;
QueryBuild nqb;
BOOL nqb_is_valid = FALSE;
if (F_OldChar(qp) == ODBC_ESCAPE_START) /* skip the first { */
F_OldNext(qp);
/* Separate off the key, skipping leading and trailing whitespace */
while ((ucv = F_OldChar(qp)) != '\0' && isspace(ucv))
F_OldNext(qp);
/*
* procedure calls
*/
/* '?=' to accept return values exists ? */
if (F_OldChar(qp) == '?')
{
qb->param_number++;
qb->proc_return = 1;
if (qb->stmt)
qb->stmt->proc_return = 1;
while (isspace((UCHAR) qp->statement[++qp->opos]));
if (F_OldChar(qp) != '=')
{
F_OldPrior(qp);
return SQL_SUCCESS;
}
while (isspace((UCHAR) qp->statement[++qp->opos]));
}
sscanf(F_OldPtr(qp), "%32s", key);
while ((ucv = F_OldChar(qp)) != '\0' && (IS_NOT_SPACE(ucv)))
F_OldNext(qp);
while ((ucv = F_OldChar(qp)) != '\0' && isspace(ucv))
F_OldNext(qp);
/* Avoid the concatenation of the function name with the previous word. Aceto */
if (stricmp(key, "call") == 0)
{
size_t funclen;
const UCHAR *next_token;
if (SQL_ERROR == QB_start_brace(qb))
{
retval = SQL_ERROR;
goto cleanup;
}
if (qb->num_io_params > 1 ||
(0 == qb->proc_return))
CVT_APPEND_STR(qb, "SELECT * FROM ");
else
CVT_APPEND_STR(qb, "SELECT ");
funclen = findIdentifier((const UCHAR *) F_OldPtr(qp), qb->ccsc, &next_token);
if (next_token && ODBC_ESCAPE_END == *next_token)
{
CVT_APPEND_DATA(qb, F_OldPtr(qp), funclen);
CVT_APPEND_STR(qb, "()");
if (SQL_ERROR == QB_end_brace(qb))
{
retval = SQL_ERROR;
goto cleanup;
}
/* positioned at } */
qp->opos += ((const char *) next_token - F_OldPtr(qp));
}
else
{
/* Continue at inner_process_tokens loop */
F_OldPrior(qp);
return SQL_SUCCESS;
}
}
else if (stricmp(key, "d") == 0)
{
/* Literal; return the escape part adding type cast */
F_ExtractOldTo(qp, buf_small, ODBC_ESCAPE_END, sizeof(buf_small));
prtlen = SPRINTF_FIXED(buf, "%s::date", buf_small);
CVT_APPEND_DATA(qb, buf, prtlen);
retval = QB_append_space_to_separate_identifiers(qb, qp);
}
else if (stricmp(key, "t") == 0)
{
/* Literal; return the escape part adding type cast */
F_ExtractOldTo(qp, buf_small, ODBC_ESCAPE_END, sizeof(buf_small));
prtlen = SPRINTF_FIXED(buf, "%s::time", buf_small);
CVT_APPEND_DATA(qb, buf, prtlen);
retval = QB_append_space_to_separate_identifiers(qb, qp);
}
else if (stricmp(key, "ts") == 0)
{
/* Literal; return the escape part adding type cast */
F_ExtractOldTo(qp, buf_small, ODBC_ESCAPE_END, sizeof(buf_small));
prtlen = SPRINTF_FIXED(buf, "%s::timestamp", buf_small);
CVT_APPEND_DATA(qb, buf, prtlen);
retval = QB_append_space_to_separate_identifiers(qb, qp);
}
else if (stricmp(key, "oj") == 0) /* {oj syntax support for 7.1 * servers */
{
if (qb->stmt)
SC_set_outer_join(qb->stmt);
retval = QB_start_brace(qb);
/* Continue at inner_process_tokens loop */
F_OldPrior(qp);
goto cleanup;
}
else if (stricmp(key, "escape") == 0) /* like escape syntax support for 7.1+ servers */
{
/* Literal; return the escape part adding type cast */
F_ExtractOldTo(qp, buf_small, ODBC_ESCAPE_END, sizeof(buf_small));
prtlen = SPRINTF_FIXED(buf, "%s %s", key, buf_small);
CVT_APPEND_DATA(qb, buf, prtlen);
retval = QB_append_space_to_separate_identifiers(qb, qp);
}
else if (stricmp(key, "fn") == 0)
{
const char *mapExpr;
int i, param_count;
SQLLEN from, to;
size_t param_consumed;
SQLLEN param_pos[16][2];
BOOL cvt_func = FALSE;
/* Separate off the func name, skipping leading and trailing whitespace */
i = 0;
while ((ucv = F_OldChar(qp)) != '\0' && ucv != '(' &&
(IS_NOT_SPACE(ucv)))
{
if (i < sizeof(key) - 1)
key[i++] = ucv;
F_OldNext(qp);
}
key[i] = '\0';
while ((ucv = F_OldChar(qp)) != '\0' && isspace(ucv))
F_OldNext(qp);
/*
* We expect left parenthesis here, else return fn body as-is
* since it is one of those "function constants".
*/
if (F_OldChar(qp) != '(')
{
CVT_APPEND_STR(qb, key);
goto cleanup;
}
/*
* Process parameter list and inner escape
* sequences
* Aceto 2002-01-29
*/
QB_initialize_copy(&nqb, qb, 1024);
nqb_is_valid = TRUE;
if (retval = processParameters(qp, &nqb, &param_consumed, param_pos), retval == SQL_ERROR)
{
qb->errornumber = nqb.errornumber;
qb->errormsg = nqb.errormsg;
goto cleanup;
}
for (param_count = 0;; param_count++)
{
if (param_pos[param_count][0] < 0)
break;
}
if (param_count == 1 &&
param_pos[0][1] < param_pos[0][0])
param_count = 0;
mapExpr = NULL;
if (stricmp(key, "convert") == 0)
cvt_func = TRUE;
else
mapExpr = mapFunction(key, param_count);
if (cvt_func)
{
if (2 == param_count)
{
BOOL add_cast = FALSE, add_quote = FALSE;
const char *pptr;
from = param_pos[0][0];
to = param_pos[0][1];
for (pptr = nqb.query_statement + from; *pptr && isspace((unsigned char) *pptr); pptr++)
;
if (LITERAL_QUOTE == *pptr)
;
else if ('-' == *pptr)
add_quote = TRUE;
else if (isdigit((unsigned char) *pptr))
add_quote = TRUE;
else
add_cast = TRUE;
if (add_quote)
CVT_APPEND_CHAR(qb, LITERAL_QUOTE);
else if (add_cast)
CVT_APPEND_CHAR(qb, '(');
CVT_APPEND_DATA(qb, nqb.query_statement + from, to - from + 1);
if (add_quote)
CVT_APPEND_CHAR(qb, LITERAL_QUOTE);
else if (add_cast)
{
const char *cast_form = NULL;
CVT_APPEND_CHAR(qb, ')');
from = param_pos[1][0];
to = param_pos[1][1];
if (to < from + 9)
{
char num[10];
memcpy(num, nqb.query_statement + from, to - from + 1);
num[to - from + 1] = '\0';
MYLOG(0, FORMAT_LEN "-" FORMAT_LEN " num=%s SQL_BIT=%d\n", to, from, num, SQL_BIT);
switch (atoi(num))
{
case SQL_BIT:
cast_form = "boolean";
break;
case SQL_INTEGER:
cast_form = "int4";
break;
}
}
if (NULL != cast_form)
{
CVT_APPEND_STR(qb, "::");
CVT_APPEND_STR(qb, cast_form);
}
}
}
else
{
qb->errornumber = STMT_EXEC_ERROR;
qb->errormsg = "convert param count must be 2";
retval = SQL_ERROR;
}
}
else if (mapExpr == NULL)
{
CVT_APPEND_STR(qb, key);
CVT_APPEND_DATA(qb, nqb.query_statement, nqb.npos);
}
else
{
const char *mapptr;
SQLLEN paramlen;
int pidx;
for (mapptr = mapExpr; *mapptr; mapptr++)
{
if (*mapptr != '$')
{
CVT_APPEND_CHAR(qb, *mapptr);
continue;
}
mapptr++;
if (*mapptr == '*')
{
from = 1;
to = param_consumed - 2;
}
else if (isdigit((unsigned char) *mapptr))
{
pidx = *mapptr - '0' - 1;
if (pidx < 0 ||
param_pos[pidx][0] < 0)
{
qb->errornumber = STMT_EXEC_ERROR;
qb->errormsg = "param not found";
MYLOG(0, "%dth param not found for the expression %s\n", pidx + 1, mapExpr);
retval = SQL_ERROR;
break;
}
from = param_pos[pidx][0];
to = param_pos[pidx][1];
}
else
{
qb->errornumber = STMT_EXEC_ERROR;
qb->errormsg = "internal expression error";
MYLOG(0, "internal expression error %s\n", mapExpr);
retval = SQL_ERROR;
break;
}
paramlen = to - from + 1;
if (paramlen > 0)
CVT_APPEND_DATA(qb, nqb.query_statement + from, paramlen);
}
}
if (0 == qb->errornumber)
{
qb->errornumber = nqb.errornumber;
qb->errormsg = nqb.errormsg;
}
if (SQL_ERROR != retval)
{
qb->param_number = nqb.param_number;
qb->flags = nqb.flags;
}
}
else
{
/* Bogus key, leave untranslated */
retval = SQL_ERROR;
}
cleanup:
if (nqb_is_valid)
QB_Destructor(&nqb);
return retval;
}
static BOOL
convert_money(const char *s, char *sout, size_t soutmax)
{
char in, decp = 0;
size_t i = 0,
out = 0;
int num_in = -1, period_in = -1, comma_in = -1;
for (i = 0; s[i]; i++)
{
switch (in = s[i])
{
case '.':
if (period_in < 0)
period_in = i;
break;
case ',':
if (comma_in < 0)
comma_in = i;
break;
default:
if ('0' <= in && '9' >= in)
num_in = i;
break;
}
}
if (period_in > comma_in)
{
if ( period_in >= num_in - 2)
decp = '.';
}
else if (comma_in >= 0 &&
comma_in >= num_in - 2)
decp = ',';
for (i = 0; s[i] && out + 1 < soutmax; i++)
{
switch (in = s[i])
{
case '(':
case '-':
sout[out++] = '-';
break;
default:
if (in >= '0' && in <= '9')
sout[out++] = in;
else if (in == decp)
sout[out++] = '.';
}
}
sout[out] = '\0';
return TRUE;
}
/*
* This function parses a character string for date/time info and fills in SIMPLE_TIME
* It does not zero out SIMPLE_TIME in case it is desired to initialize it with a value
*/
static char
parse_datetime(const char *buf, SIMPLE_TIME *st)
{
int y,
m,
d,
hh,
mm,
ss;
int nf;
BOOL bZone; int zone;
y = m = d = hh = mm = ss = 0;
st->fr = 0;
st->infinity = 0;
/*
* Handle ODBC time/date/timestamp literals, e.g.
* { d '2011-04-22' }
* { t '12:34:56' }
* { ts '2011-04-22 12:34:56' }
*/
if (buf[0] == ODBC_ESCAPE_START)
{
while (*(++buf) && *buf != LITERAL_QUOTE);
if (!(*buf))
return FALSE;
buf++;
}
bZone = FALSE;
if (timestamp2stime(buf, st, &bZone, &zone))
return TRUE;
if (buf[4] == '-') /* year first */
nf = sscanf(buf, "%4d-%2d-%2d %2d:%2d:%2d", &y, &m, &d, &hh, &mm, &ss);
else
nf = sscanf(buf, "%2d-%2d-%4d %2d:%2d:%2d", &m, &d, &y, &hh, &mm, &ss);
if (nf == 5 || nf == 6)
{
st->y = y;
st->m = m;
st->d = d;
st->hh = hh;
st->mm = mm;
st->ss = ss;
return TRUE;
}
if (buf[4] == '-') /* year first */
nf = sscanf(buf, "%4d-%2d-%2d", &y, &m, &d);
else
nf = sscanf(buf, "%2d-%2d-%4d", &m, &d, &y);
if (nf == 3)
{
st->y = y;
st->m = m;
st->d = d;
return TRUE;
}
nf = sscanf(buf, "%2d:%2d:%2d", &hh, &mm, &ss);
if (nf == 2 || nf == 3)
{
st->hh = hh;
st->mm = mm;
st->ss = ss;
return TRUE;
}
return FALSE;
}
/* Change linefeed to carriage-return/linefeed */
size_t
convert_linefeeds(const char *si, char *dst, size_t max, BOOL convlf, BOOL *changed)
{
size_t i = 0,
out = 0;
if (max == 0)
max = 0xffffffff;
*changed = FALSE;
for (i = 0; si[i] && out < max - 1; i++)
{
if (convlf && si[i] == '\n')
{
/* Only add the carriage-return if needed */
if (i > 0 && PG_CARRIAGE_RETURN == si[i - 1])
{
if (dst)
dst[out++] = si[i];
else
out++;
continue;
}
*changed = TRUE;
if (dst)
{
dst[out++] = PG_CARRIAGE_RETURN;
dst[out++] = '\n';
}
else
out += 2;
}
else
{
if (dst)
dst[out++] = si[i];
else
out++;
}
}
if (dst)
dst[out] = '\0';
return out;
}
/*
* Change carriage-return/linefeed to just linefeed
* Plus, escape any special characters.
*/
static BOOL
convert_special_chars(QueryBuild *qb, const char *si, size_t used)
{
size_t i = 0,
max;
char tchar;
encoded_str encstr;
BOOL convlf = (0 != (qb->flags & FLGB_CONVERT_LF));
BOOL double_special = (qb->param_mode != RPM_BUILDING_BIND_REQUEST);
int ccsc = qb->ccsc;
char escape_in_literal = CC_get_escape(qb->conn);
if (used == SQL_NTS)
max = strlen(si);
else
max = used;
/*
* Make sure there's room for the null-terminator, if the input is an
* empty string. XXX: I don't think the null-termination is actually
* even required, but better safe than sorry.
*/
if (!enlarge_query_statement(qb, qb->npos + 1))
return FALSE;
encoded_str_constr(&encstr, ccsc, si);
for (i = 0; i < max && si[i]; i++)
{
tchar = encoded_nextchar(&encstr);
/*
* Make sure there is room for three more bytes in the buffer. We
* expand quotes to two bytes, plus null-terminate the end.
*/
if (qb->npos + 3 >= qb->str_alsize)
{
if (!enlarge_query_statement(qb, qb->npos + 3))
return FALSE;
}
if (MBCS_NON_ASCII(encstr))
{
qb->query_statement[qb->npos++] = tchar;
continue;
}
if (convlf && /* CR/LF -> LF */
PG_CARRIAGE_RETURN == tchar &&
PG_LINEFEED == si[i + 1])
continue;
else if (double_special && /* double special chars ? */
(tchar == LITERAL_QUOTE ||
tchar == escape_in_literal))
{
qb->query_statement[qb->npos++] = tchar;
}
qb->query_statement[qb->npos++] = tchar;
}
qb->query_statement[qb->npos] = '\0';
return TRUE;
}
static int
conv_from_octal(const char *s)
{
ssize_t i;
int y = 0;
for (i = 1; i <= 3; i++)
y += (s[i] - '0') << (3 * (3 - i));
return y;
}
/* convert octal escapes to bytes */
static size_t
convert_from_pgbinary(const char *value, bool binary_rawout, char *rgbValue, SQLLEN cbValueMax)
{
size_t i,
ilen = strlen(value);
size_t o = 0;
for (i = 0; i < ilen;)
{
if (value[i] == BYTEA_ESCAPE_CHAR)
{
if (value[i + 1] == BYTEA_ESCAPE_CHAR)
{
if (rgbValue)
rgbValue[o] = value[i];
o++;
i += 2;
}
else if (value[i + 1] == 'x')
{
i += 2;
if (i < ilen)
{
ilen -= i;
if (rgbValue)
pg_hex2bin(value + i, rgbValue + o, ilen);
o += ilen / 2;
}
break;
}
else
{
if (rgbValue)
rgbValue[o] = conv_from_octal(&value[i]);
o++;
i += 4;
}
}
else if (binary_rawout)
{
//rawout, 十六进制,但最前面没有\x
if (i < ilen)
{
ilen -= i;
if (rgbValue)
pg_hex2bin(value + i, rgbValue + o, ilen);
o += ilen / 2;
}
break;
}
else
{
if (rgbValue)
rgbValue[o] = value[i];
o++;
i++;
}
/** if (rgbValue)
MYLOG(0, "i=%d, rgbValue[%d] = %d, %c\n", i, o, rgbValue[o], rgbValue[o]); ***/
}
if (rgbValue)
rgbValue[o] = '\0'; /* extra protection */
MYLOG(0, "in=" FORMAT_SIZE_T ", out = " FORMAT_SIZE_T "\n", ilen, o);
return o;
}
static UInt2
conv_to_octal(UCHAR val, char *octal, char escape_ch)
{
int i, pos = 0, len;
if (escape_ch)
octal[pos++] = escape_ch;
octal[pos] = BYTEA_ESCAPE_CHAR;
len = 4 + pos;
octal[len] = '\0';
for (i = len - 1; i > pos; i--)
{
octal[i] = (val & 7) + '0';
val >>= 3;
}
return (UInt2) len;
}
static char *
conv_to_octal2(UCHAR val, char *octal)
{
int i;
octal[0] = BYTEA_ESCAPE_CHAR;
octal[4] = '\0';
for (i = 3; i > 0; i--)
{
octal[i] = (val & 7) + '0';
val >>= 3;
}
return octal;
}
/* convert non-ascii bytes to octal escape sequences */
static size_t
convert_to_pgbinary(const char *in, char *out, size_t len, QueryBuild *qb)
{
UCHAR inc;
size_t i, o = 0;
char escape_in_literal = CC_get_escape(qb->conn);
BOOL esc_double = (qb->param_mode != RPM_BUILDING_BIND_REQUEST &&
0 != escape_in_literal);
/* use hex format for 9.0 or later servers */
if (0 != (qb->flags & FLGB_HEX_BIN_FORMAT))
{
if (esc_double)
out[o++] = escape_in_literal;
out[o++] = '\\';
out[o++] = 'x';
o += pg_bin2hex(in, out + o, len);
return o;
}
for (i = 0; i < len; i++)
{
inc = in[i];
MYLOG(DETAIL_LOG_LEVEL, "in[" FORMAT_SIZE_T "] = %d, %c\n", i, inc, inc);
if (inc < 128 && (isalnum(inc) || inc == ' '))
out[o++] = inc;
else
{
if (esc_double)
{
o += conv_to_octal(inc, &out[o], escape_in_literal);
}
else
{
conv_to_octal2(inc, &out[o]);
o += 4;
}
}
}
MYLOG(0, "leaving " FORMAT_SIZE_T ", out='%.*s'\n", o, (int) o, out);
return o;
}
static const char *hextbl = "0123456789ABCDEF";
#define def_bin2hex(type) \
(const char *src, type *dst, SQLLEN length) \
{ \
const char *src_wk; \
UCHAR chr; \
type *dst_wk; \
BOOL backwards; \
int i; \
\
backwards = FALSE; \
if ((char *) dst < src) \
{ \
if ((char *) (dst + 2 * (length - 1)) > src + length - 1) \
return -1; \
} \
else if ((char *) dst < src + length) \
backwards = TRUE; \
if (backwards) \
{ \
for (i = 0, src_wk = src + length - 1, dst_wk = dst + 2 * length - 1; i < length; i++, src_wk--) \
{ \
chr = *src_wk; \
*dst_wk-- = hextbl[chr % 16]; \
*dst_wk-- = hextbl[chr >> 4]; \
} \
} \
else \
{ \
for (i = 0, src_wk = src, dst_wk = dst; i < length; i++, src_wk++) \
{ \
chr = *src_wk; \
*dst_wk++ = hextbl[chr >> 4]; \
*dst_wk++ = hextbl[chr % 16]; \
} \
} \
dst[2 * length] = '\0'; \
return 2 * length * sizeof(type); \
}
#ifdef UNICODE_SUPPORT
static SQLLEN
pg_bin2whex def_bin2hex(SQLWCHAR)
#endif /* UNICODE_SUPPORT */
static SQLLEN
pg_bin2hex def_bin2hex(char)
SQLLEN
pg_hex2bin(const char *src, char *dst, SQLLEN length)
{
UCHAR chr;
const char *src_wk;
char *dst_wk;
SQLLEN i;
int val;
BOOL HByte = TRUE;
for (i = 0, src_wk = src, dst_wk = dst; i < length; i++, src_wk++)
{
chr = *src_wk;
if (!chr)
break;
if (chr >= 'a' && chr <= 'f')
val = chr - 'a' + 10;
else if (chr >= 'A' && chr <= 'F')
val = chr - 'A' + 10;
else
val = chr - '0';
if (HByte)
*dst_wk = (val << 4);
else
{
*dst_wk += val;
dst_wk++;
}
HByte = !HByte;
}
*dst_wk = '\0';
return length;
}
/*-------
* 1. get oid (from 'value')
* 2. open the large object
* 3. read from the large object (handle multiple GetData)
* 4. close when read less than requested? -OR-
* lseek/read each time
* handle case where application receives truncated and
* decides not to continue reading.
*
* CURRENTLY, ONLY LONGVARBINARY is handled, since that is the only
* data type currently mapped to a PG_TYPE_LO. But, if any other types
* are desired to map to a large object (PG_TYPE_LO), then that would
* need to be handled here. For example, LONGVARCHAR could possibly be
* mapped to PG_TYPE_LO someday, instead of PG_TYPE_TEXT as it is now.
*-------
*/
static int
convert_lo(StatementClass *stmt, const void *value, SQLSMALLINT fCType, PTR rgbValue,
SQLLEN cbValueMax, SQLLEN *pcbValue)
{
CSTR func = "convert_lo";
OID oid;
int result;
Int8 retval;
Int8 left64 = -1;
struct GetBlobDataClass *gdata_blob = NULL;
ConnectionClass *conn = SC_get_conn(stmt);
ConnInfo *ci = &(conn->connInfo);
GetDataInfo *gdata_info = SC_get_GDTI(stmt);
int factor;
oid = ATOI32U(value);
if (0 == oid)
{
if (pcbValue)
*pcbValue = SQL_NULL_DATA;
return COPY_OK;
}
switch (fCType)
{
case SQL_C_CHAR:
factor = 2;
break;
case SQL_C_BINARY:
factor = 1;
break;
default:
SC_set_error(stmt, STMT_EXEC_ERROR, "Could not convert lo to the c-type", func);
return COPY_GENERAL_ERROR;
}
/* If using SQLGetData, then current_col will be set */
if (stmt->current_col >= 0)
{
gdata_blob = &(gdata_info->gdata[stmt->current_col].blob);
left64 = gdata_blob->data_left64;
}
/*
* if this is the first call for this column, open the large object
* for reading
*/
if (!gdata_blob || gdata_blob->data_left64 == -1)
{
/* begin transaction if needed */
if (!CC_is_in_trans(conn))
{
if (!CC_begin(conn))
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Could not begin (in-line) a transaction", func);
return COPY_GENERAL_ERROR;
}
}
stmt->lobj_fd = odbc_lo_open(conn, oid, INV_READ);
if (stmt->lobj_fd < 0)
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Couldnt open large object for reading.", func);
return COPY_GENERAL_ERROR;
}
/* Get the size */
retval = odbc_lo_lseek64(conn, stmt->lobj_fd, 0L, SEEK_END);
if (retval >= 0)
{
left64 = odbc_lo_tell64(conn, stmt->lobj_fd);
if (gdata_blob)
gdata_blob->data_left64 = left64;
/* return to beginning */
odbc_lo_lseek64(conn, stmt->lobj_fd, 0L, SEEK_SET);
}
}
else if (left64 == 0)
return COPY_NO_DATA_FOUND;
MYLOG(0, "lo data left = " FORMATI64 "\n", left64);
if (stmt->lobj_fd < 0)
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Large object FD undefined for multiple read.", func);
return COPY_GENERAL_ERROR;
}
if (0 >= cbValueMax)
retval = 0;
else
retval = (Int8) odbc_lo_read(conn, stmt->lobj_fd, (char *) rgbValue, (Int4) (factor > 1 ? (cbValueMax - 1) / factor : cbValueMax));
if (retval < 0)
{
odbc_lo_close(conn, stmt->lobj_fd);
/* commit transaction if needed */
if (!ci->drivers.use_declarefetch && CC_does_autocommit(conn))
{
if (!CC_commit(conn))
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction", func);
return COPY_GENERAL_ERROR;
}
}
stmt->lobj_fd = -1;
SC_set_error(stmt, STMT_EXEC_ERROR, "Error reading from large object.", func);
return COPY_GENERAL_ERROR;
}
if (factor > 1)
pg_bin2hex((char *) rgbValue, (char *) rgbValue, retval);
if (retval < left64)
result = COPY_RESULT_TRUNCATED;
else
result = COPY_OK;
if (pcbValue)
{
Int8 leftbytes = left64 * factor;
*pcbValue = left64 < 0 ? SQL_NO_TOTAL : (leftbytes == (SQLLEN) leftbytes ? leftbytes : /* exceeds SQLLEN limit */ SQL_NO_TOTAL);
}
if (gdata_blob && gdata_blob->data_left64 > 0)
gdata_blob->data_left64 -= retval;
if (!gdata_blob || gdata_blob->data_left64 == 0)
{
odbc_lo_close(conn, stmt->lobj_fd);
/* commit transaction if needed */
if (!ci->drivers.use_declarefetch && CC_does_autocommit(conn))
{
if (!CC_commit(conn))
{
SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction", func);
return COPY_GENERAL_ERROR;
}
}
stmt->lobj_fd = -1; /* prevent further reading */
}
return result;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/b_127/openGauss-connector-odbc.git
git@gitee.com:b_127/openGauss-connector-odbc.git
b_127
openGauss-connector-odbc
openGauss-connector-odbc
master

搜索帮助