| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871 |
- /*
- * PROGRAM: JRD Remote Interface
- * MODULE: interface.cpp
- * DESCRIPTION: User visible entrypoints remote interface
- *
- * The contents of this file are subject to the Interbase Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy
- * of the License at http://www.Inprise.com/IPL.html
- *
- * Software distributed under the License is distributed on an
- * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
- * or implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code was created by Inprise Corporation
- * and its predecessors. Portions created by Inprise Corporation are
- * Copyright (C) Inprise Corporation.
- *
- * All Rights Reserved.
- * Contributor(s): ______________________________________.
- *
- * 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "Ultrix" port
- *
- * 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "MPEXL" port
- * 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "DecOSF" port
- *
- * 2002.10.29 Sean Leyne - Removed support for obsolete IPX/SPX Protocol
- * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
- *
- */
- #include "firebird.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "../remote/remote.h"
- #include "../common/gdsassert.h"
- #include "../common/isc_proto.h"
- #include <stdarg.h>
- #ifndef NO_NFS
- #include <sys/param.h>
- #endif
- #include "ibase.h"
- #include "../common/ThreadStart.h"
- #include "../jrd/license.h"
- #include "../remote/inet_proto.h"
- #include "../remote/merge_proto.h"
- #include "../remote/parse_proto.h"
- #include "../remote/remot_proto.h"
- #include "../remote/proto_proto.h"
- #include "../common/cvt.h"
- #include "../yvalve/gds_proto.h"
- #include "../common/isc_f_proto.h"
- #include "../common/classes/ClumpletWriter.h"
- #include "../common/classes/BatchCompletionState.h"
- #include "../common/config/config.h"
- #include "../common/utils_proto.h"
- #include "../common/classes/DbImplementation.h"
- #include "../common/Auth.h"
- #include "../common/classes/GetPlugins.h"
- #include "firebird/Interface.h"
- #include "../common/StatementMetadata.h"
- #include "../common/IntlParametersBlock.h"
- #include "../common/status.h"
- #include "../common/db_alias.h"
- #include "../common/classes/auto.h"
- #include "../auth/SecurityDatabase/LegacyClient.h"
- #include "../auth/SecureRemotePassword/client/SrpClient.h"
- #include "../auth/trusted/AuthSspi.h"
- #include "../plugins/crypt/arc4/Arc4.h"
- #include "BlrFromMessage.h"
- #include "../dsql/DsqlBatch.h"
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #ifdef WIN_NT
- #include <process.h>
- #endif
- #if defined(WIN_NT)
- #include "../common/isc_proto.h"
- #include "../remote/os/win32/xnet_proto.h"
- #endif
- const char* const PROTOCOL_INET = "inet";
- const char* const PROTOCOL_INET4 = "inet4";
- const char* const PROTOCOL_INET6 = "inet6";
- #ifdef WIN_NT
- const char* const PROTOCOL_XNET = "xnet";
- #endif
- const char* const INET_SEPARATOR = "/";
- const char* const INET_LOCALHOST = "localhost";
- using namespace Firebird;
- namespace {
- void handle_error(ISC_STATUS code)
- {
- Arg::Gds(code).raise();
- }
- template <typename T>
- inline void CHECK_HANDLE(T* blk, ISC_STATUS error)
- {
- if (!blk || !blk->checkHandle())
- {
- handle_error(error);
- }
- }
- inline void CHECK_LENGTH(rem_port* port, size_t length)
- {
- if (length > MAX_USHORT && port->port_protocol < PROTOCOL_VERSION13)
- status_exception::raise(Arg::Gds(isc_imp_exc) << Arg::Gds(isc_blktoobig));
- }
- class SaveString
- {
- public:
- SaveString(cstring& toSave, ULONG newLength, UCHAR* newBuffer)
- : ptr(&toSave),
- oldValue(*ptr)
- {
- ptr->cstr_address = newBuffer;
- ptr->cstr_allocated = newLength;
- }
- ~SaveString()
- {
- *ptr = oldValue;
- }
- private:
- cstring* ptr;
- cstring oldValue;
- };
- class ClientPortsCleanup : public PortsCleanup
- {
- public:
- ClientPortsCleanup() :
- PortsCleanup()
- {}
- explicit ClientPortsCleanup(MemoryPool& p) :
- PortsCleanup(p)
- {}
- void closePort(rem_port* port) override;
- void delay() override
- {
- Thread::sleep(50);
- }
- };
- GlobalPtr<ClientPortsCleanup> outPorts;
- }
- namespace Remote {
- // Provider stuff
- class Attachment;
- class Statement;
- class Blob final : public RefCntIface<IBlobImpl<Blob, CheckStatusWrapper> >
- {
- public:
- // IBlob implementation
- int release() override;
- void getInfo(CheckStatusWrapper* status,
- unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer) override;
- int getSegment(CheckStatusWrapper* status, unsigned int bufferLength,
- void* buffer, unsigned int* segmentLength) override;
- void putSegment(CheckStatusWrapper* status, unsigned int length, const void* buffer) override;
- void cancel(CheckStatusWrapper* status) override;
- void close(CheckStatusWrapper* status) override;
- int seek(CheckStatusWrapper* status, int mode, int offset) override; // returns position
- void deprecatedCancel(Firebird::CheckStatusWrapper* status) override;
- void deprecatedClose(Firebird::CheckStatusWrapper* status) override;
- public:
- explicit Blob(Rbl* handle)
- : blob(handle)
- {
- blob->rbl_self = &blob;
- }
- private:
- void freeClientData(CheckStatusWrapper* status, bool force = false);
- void internalCancel(Firebird::CheckStatusWrapper* status);
- void internalClose(Firebird::CheckStatusWrapper* status);
- Rbl* blob;
- };
- int Blob::release()
- {
- if (--refCounter != 0)
- {
- return 1;
- }
- if (blob)
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- freeClientData(&status, true);
- }
- delete this;
- return 0;
- }
- class Transaction final : public RefCntIface<ITransactionImpl<Transaction, CheckStatusWrapper> >
- {
- public:
- // ITransaction implementation
- int release() override;
- void getInfo(CheckStatusWrapper* status,
- unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer) override;
- void prepare(CheckStatusWrapper* status,
- unsigned int msg_length = 0, const unsigned char* message = 0) override;
- void commit(CheckStatusWrapper* status) override;
- void commitRetaining(CheckStatusWrapper* status) override;
- void rollback(CheckStatusWrapper* status) override;
- void rollbackRetaining(CheckStatusWrapper* status) override;
- void disconnect(CheckStatusWrapper* status) override;
- ITransaction* join(CheckStatusWrapper* status, ITransaction* tra) override;
- Transaction* validate(CheckStatusWrapper* status, IAttachment* attachment) override;
- Transaction* enterDtc(CheckStatusWrapper* status) override;
- void deprecatedCommit(Firebird::CheckStatusWrapper* status) override;
- void deprecatedRollback(Firebird::CheckStatusWrapper* status) override;
- void deprecatedDisconnect(Firebird::CheckStatusWrapper* status) override;
- public:
- Transaction(Rtr* handle, Attachment* a)
- : remAtt(a),
- transaction(handle)
- {
- transaction->rtr_self = &transaction;
- }
- Rtr* getTransaction()
- {
- return transaction;
- }
- void clear()
- {
- transaction = NULL;
- }
- private:
- Transaction(Transaction* from)
- : remAtt(from->remAtt),
- transaction(from->transaction)
- { }
- void freeClientData(CheckStatusWrapper* status, bool force = false);
- void internalCommit(Firebird::CheckStatusWrapper* status);
- void internalRollback(Firebird::CheckStatusWrapper* status);
- void internalDisconnect(Firebird::CheckStatusWrapper* status);
- Attachment* remAtt;
- Rtr* transaction;
- };
- int Transaction::release()
- {
- if (--refCounter != 0)
- return 1;
- if (transaction)
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- freeClientData(&status, true); // ASF: Rollback - is this correct for reconnected transactions?
- }
- delete this;
- return 0;
- }
- class ResultSet final : public RefCntIface<IResultSetImpl<ResultSet, CheckStatusWrapper> >
- {
- public:
- // IResultSet implementation
- int release() override;
- int fetchNext(CheckStatusWrapper* status, void* message) override;
- int fetchPrior(CheckStatusWrapper* status, void* message) override;
- int fetchFirst(CheckStatusWrapper* status, void* message) override;
- int fetchLast(CheckStatusWrapper* status, void* message) override;
- int fetchAbsolute(CheckStatusWrapper* status, int position, void* message) override;
- int fetchRelative(CheckStatusWrapper* status, int offset, void* message) override;
- FB_BOOLEAN isEof(CheckStatusWrapper* status) override;
- FB_BOOLEAN isBof(CheckStatusWrapper* status) override;
- IMessageMetadata* getMetadata(CheckStatusWrapper* status) override;
- void close(CheckStatusWrapper* status) override;
- void deprecatedClose(CheckStatusWrapper* status) override;
- void setDelayedOutputFormat(CheckStatusWrapper* status, IMessageMetadata* format) override;
- void getInfo(CheckStatusWrapper* status,
- unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer) override;
- ResultSet(Statement* s, IMessageMetadata* outFmt, unsigned f)
- : stmt(s), flags(f), tmpStatement(false), delayedFormat(outFmt == DELAYED_OUT_FORMAT)
- {
- if (!delayedFormat)
- outputFormat = outFmt;
- }
- private:
- bool fetch(CheckStatusWrapper* status, void* message, P_FETCH operation, int position = 0);
- void releaseStatement();
- void freeClientData(CheckStatusWrapper* status, bool force = false);
- void internalClose(CheckStatusWrapper* status);
- Statement* stmt;
- const unsigned flags;
- RefPtr<IMessageMetadata> outputFormat;
- public:
- bool tmpStatement, delayedFormat;
- };
- int ResultSet::release()
- {
- if (--refCounter != 0)
- return 1;
- if (stmt)
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- freeClientData(&status, true);
- }
- delete this;
- return 0;
- }
- class Batch final : public RefCntIface<IBatchImpl<Batch, CheckStatusWrapper> >
- {
- public:
- static const ULONG DEFER_BATCH_LIMIT = 64;
- Batch(Statement* s, IMessageMetadata* inFmt, unsigned parLength, const unsigned char* par);
- // IBatch implementation
- int release() override;
- void add(Firebird::CheckStatusWrapper* status, unsigned count, const void* inBuffer) override;
- void addBlob(Firebird::CheckStatusWrapper* status, unsigned length, const void* inBuffer, ISC_QUAD* blobId,
- unsigned parLength, const unsigned char* par) override;
- void appendBlobData(Firebird::CheckStatusWrapper* status, unsigned length, const void* inBuffer) override;
- void addBlobStream(Firebird::CheckStatusWrapper* status, unsigned length, const void* inBuffer) override;
- void registerBlob(Firebird::CheckStatusWrapper* status, const ISC_QUAD* existingBlob, ISC_QUAD* blobId) override;
- Firebird::IBatchCompletionState* execute(Firebird::CheckStatusWrapper* status, Firebird::ITransaction* transaction) override;
- void cancel(Firebird::CheckStatusWrapper* status) override;
- unsigned getBlobAlignment(Firebird::CheckStatusWrapper* status) override;
- void setDefaultBpb(Firebird::CheckStatusWrapper* status, unsigned parLength, const unsigned char* par) override;
- Firebird::IMessageMetadata* getMetadata(Firebird::CheckStatusWrapper* status) override;
- void close(Firebird::CheckStatusWrapper* status) override;
- void deprecatedClose(Firebird::CheckStatusWrapper* status) override;
- void getInfo(CheckStatusWrapper* status,
- unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer) override;
- private:
- void freeClientData(CheckStatusWrapper* status, bool force = false);
- void internalClose(Firebird::CheckStatusWrapper* status);
- void releaseStatement();
- void setServerInfo();
- void cleanup()
- {
- if (blobPolicy != BLOB_NONE)
- blobStream = blobStreamBuffer;
- sizePointer = nullptr;
- messageStream = 0;
- }
- void genBlobId(ISC_QUAD* blobId)
- {
- if (++genId.gds_quad_low == 0)
- ++genId.gds_quad_high;
- memcpy(blobId, &genId, sizeof(genId));
- }
- bool batchHasData()
- {
- return batchActive;
- }
- // working with message stream buffer
- void putMessageData(ULONG count, const void* p)
- {
- fb_assert(messageStreamBuffer);
- const UCHAR* ptr = reinterpret_cast<const UCHAR*>(p);
- while(count)
- {
- ULONG remainSpace = messageBufferSize - messageStream;
- ULONG step = MIN(count, remainSpace);
- if (step == messageBufferSize)
- {
- // direct packet sent
- sendMessagePacket(step, ptr, false);
- }
- else
- {
- // use buffer
- memcpy(&messageStreamBuffer[messageStream * alignedSize], ptr, step * alignedSize);
- messageStream += step;
- if (messageStream == messageBufferSize)
- {
- sendMessagePacket(messageBufferSize, messageStreamBuffer, false);
- messageStream = 0;
- }
- }
- count -= step;
- ptr += step * alignedSize;
- }
- }
- // working with blob stream buffer
- void newBlob()
- {
- setServerInfo();
- alignBlobBuffer(blobAlign);
- fb_assert(blobStream - blobStreamBuffer <= blobBufferSize);
- ULONG space = blobBufferSize - (blobStream - blobStreamBuffer);
- if (space < Rsr::BatchStream::SIZEOF_BLOB_HEAD)
- {
- sendBlobPacket(blobStream - blobStreamBuffer, blobStreamBuffer, false);
- blobStream = blobStreamBuffer;
- }
- }
- void alignBlobBuffer(unsigned alignment, ULONG* bs = NULL)
- {
- fb_assert(alignment);
- ULONG align = FB_ALIGN(blobStream, alignment) - blobStream;
- if (bs)
- *bs += align;
- FB_UINT64 zeroFill = 0;
- putBlobData(align, &zeroFill);
- }
- void putBlobData(ULONG size, const void* p)
- {
- fb_assert(blobStreamBuffer);
- const UCHAR* ptr = reinterpret_cast<const UCHAR*>(p);
- while(size)
- {
- ULONG space = blobBufferSize - (blobStream - blobStreamBuffer);
- ULONG step = MIN(size, space);
- if (step == blobBufferSize)
- {
- // direct packet sent
- sendBlobPacket(blobBufferSize, ptr, false);
- }
- else
- {
- // use buffer
- memcpy(blobStream, ptr, step);
- blobStream += step;
- if (blobStream - blobStreamBuffer == blobBufferSize)
- {
- sendBlobPacket(blobBufferSize, blobStreamBuffer, false);
- blobStream = blobStreamBuffer;
- sizePointer = nullptr;
- }
- }
- size -= step;
- ptr += step;
- }
- }
- void setSizePointer()
- {
- fb_assert(FB_ALIGN(blobStream, sizeof(*sizePointer)) == blobStream);
- sizePointer = reinterpret_cast<ULONG*>(blobStream);
- }
- void putSegment(ULONG size, const void* ptr)
- {
- if (!sizePointer)
- {
- newBlob();
- ISC_QUAD quadZero = {0, 0};
- putBlobData(sizeof quadZero, &quadZero);
- setSizePointer();
- ULONG longZero = 0;
- putBlobData(sizeof longZero, &longZero);
- putBlobData(sizeof longZero, &longZero);
- }
- *sizePointer += size;
- if (segmented)
- {
- if (size > MAX_USHORT)
- {
- (Arg::Gds(isc_imp_exc) << Arg::Gds(isc_blobtoobig)
- << Arg::Gds(isc_big_segment) << Arg::Num(size)).raise();
- }
- *sizePointer += sizeof(USHORT);
- alignBlobBuffer(BLOB_SEGHDR_ALIGN, sizePointer);
- USHORT segSize = size;
- putBlobData(sizeof segSize, &segSize);
- }
- putBlobData(size, ptr);
- }
- void flashBatch()
- {
- if (blobPolicy != BLOB_NONE)
- {
- setServerInfo();
- alignBlobBuffer(blobAlign);
- ULONG size = blobStream - blobStreamBuffer;
- if (size)
- {
- sendBlobPacket(size, blobStreamBuffer, messageStream == 0);
- blobStream = blobStreamBuffer;
- }
- }
- if (messageStream)
- {
- sendMessagePacket(messageStream, messageStreamBuffer, true);
- messageStream = 0;
- }
- batchActive = false;
- blobCount = messageCount = 0;
- }
- void sendBlobPacket(unsigned size, const UCHAR* ptr, bool flash);
- void sendMessagePacket(unsigned size, const UCHAR* ptr, bool flash);
- void sendDeferredPacket(IStatus* status, rem_port* port, PACKET* packet, bool flash);
- Firebird::AutoPtr<UCHAR, Firebird::ArrayDelete> messageStreamBuffer, blobStreamBuffer;
- ULONG messageStream;
- UCHAR* blobStream;
- ULONG* sizePointer;
- ULONG messageSize, alignedSize, blobBufferSize, messageBufferSize, flags;
- Statement* stmt;
- RefPtr<IMessageMetadata> format;
- ISC_QUAD genId;
- int blobAlign;
- UCHAR blobPolicy;
- bool segmented, defSegmented, batchActive;
- ULONG messageCount, blobCount, serverSize, blobHeadSize;
- public:
- bool tmpStatement;
- };
- int Batch::release()
- {
- if (--refCounter != 0)
- return 1;
- if (stmt)
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- freeClientData(&status, true);
- }
- delete this;
- return 0;
- }
- class Replicator final : public RefCntIface<IReplicatorImpl<Replicator, CheckStatusWrapper> >
- {
- public:
- // IReplicator implementation
- int release() override;
- void process(CheckStatusWrapper* status, unsigned length, const unsigned char* data) override;
- void close(CheckStatusWrapper* status) override;
- void deprecatedClose(CheckStatusWrapper* status) override;
- explicit Replicator(Attachment* att) : attachment(att)
- {}
- private:
- void freeClientData(CheckStatusWrapper* status, bool force = false);
- void internalClose(CheckStatusWrapper* status);
- Attachment* attachment;
- };
- int Replicator::release()
- {
- if (--refCounter != 0)
- return 1;
- if (attachment)
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- freeClientData(&status, true);
- }
- delete this;
- return 0;
- }
- class Statement final : public RefCntIface<IStatementImpl<Statement, CheckStatusWrapper> >
- {
- public:
- // IStatement implementation
- int release() override;
- void getInfo(CheckStatusWrapper* status,
- unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer) override;
- unsigned getType(CheckStatusWrapper* status) override;
- const char* getPlan(CheckStatusWrapper* status, FB_BOOLEAN detailed) override;
- Firebird::IMessageMetadata* getInputMetadata(CheckStatusWrapper* status) override;
- Firebird::IMessageMetadata* getOutputMetadata(CheckStatusWrapper* status) override;
- ISC_UINT64 getAffectedRecords(CheckStatusWrapper* status) override;
- ITransaction* execute(CheckStatusWrapper* status, ITransaction* tra,
- IMessageMetadata* inMetadata, void* inBuffer,
- IMessageMetadata* outMetadata, void* outBuffer) override;
- ResultSet* openCursor(CheckStatusWrapper* status, ITransaction* tra,
- IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata* outFormat,
- unsigned int flags) override;
- void setCursorName(CheckStatusWrapper* status, const char* name) override;
- void free(CheckStatusWrapper* status) override;
- void deprecatedFree(CheckStatusWrapper* status) override;
- unsigned getFlags(CheckStatusWrapper* status) override;
- unsigned int getTimeout(CheckStatusWrapper* status) override
- {
- if (statement->rsr_rdb->rdb_port->port_protocol < PROTOCOL_STMT_TOUT)
- {
- status->setErrors(Arg::Gds(isc_wish_list).value());
- return 0;
- }
- return statement->rsr_timeout;
- }
- void setTimeout(CheckStatusWrapper* status, unsigned int timeOut) override
- {
- if (timeOut && statement->rsr_rdb->rdb_port->port_protocol < PROTOCOL_STMT_TOUT)
- {
- status->setErrors(Arg::Gds(isc_wish_list).value());
- return;
- }
- statement->rsr_timeout = timeOut;
- }
- Batch* createBatch(CheckStatusWrapper* status, IMessageMetadata* inMetadata,
- unsigned parLength, const unsigned char* par) override;
- public:
- Statement(Rsr* handle, Attachment* a, unsigned aDialect)
- : metadata(getPool(), this, NULL),
- remAtt(a),
- statement(handle),
- dialect(aDialect)
- {
- statement->rsr_self = &statement;
- }
- Rsr* getStatement()
- {
- return statement;
- }
- Attachment* getAttachment()
- {
- return remAtt;
- }
- void parseMetadata(const Array<UCHAR>& buffer)
- {
- metadata.clear();
- metadata.parse((ULONG) buffer.getCount(), buffer.begin());
- }
- unsigned getDialect() const
- {
- return dialect;
- }
- private:
- void freeClientData(CheckStatusWrapper* status, bool force = false);
- void internalFree(CheckStatusWrapper* status);
- StatementMetadata metadata;
- Attachment* remAtt;
- Rsr* statement;
- unsigned dialect;
- };
- int Statement::release()
- {
- if (--refCounter != 0)
- return 1;
- if (statement)
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- freeClientData(&status, true);
- }
- delete this;
- return 0;
- }
- class Request final : public RefCntIface<IRequestImpl<Request, CheckStatusWrapper> >
- {
- public:
- // IRequest implementation
- int release() override;
- void receive(CheckStatusWrapper* status, int level, unsigned int msg_type,
- unsigned int length, void* message) override;
- void send(CheckStatusWrapper* status, int level, unsigned int msg_type,
- unsigned int length, const void* message) override;
- void getInfo(CheckStatusWrapper* status, int level,
- unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer) override;
- void start(CheckStatusWrapper* status, Firebird::ITransaction* tra, int level) override;
- void startAndSend(CheckStatusWrapper* status, Firebird::ITransaction* tra, int level, unsigned int msg_type,
- unsigned int length, const void* message) override;
- void unwind(CheckStatusWrapper* status, int level) override;
- void free(CheckStatusWrapper* status) override;
- void deprecatedFree(CheckStatusWrapper* status) override;
- public:
- Request(Rrq* handle, Attachment* a)
- : remAtt(a), rq(handle)
- {
- rq->rrq_self = &rq;
- }
- private:
- void freeClientData(CheckStatusWrapper* status, bool force = false);
- void internalFree(CheckStatusWrapper* status);
- Attachment* remAtt;
- Rrq* rq;
- };
- int Request::release()
- {
- if (--refCounter != 0)
- return 1;
- if (rq)
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- freeClientData(&status, true);
- }
- delete this;
- return 0;
- }
- class Events final : public RefCntIface<IEventsImpl<Events, CheckStatusWrapper> >
- {
- public:
- // IEvents implementation
- int release() override;
- void cancel(CheckStatusWrapper* status) override;
- void deprecatedCancel(CheckStatusWrapper* status) override;
- public:
- Events(Rvnt* handle)
- : rvnt(handle), rdb(rvnt->rvnt_rdb)
- {
- rvnt->rvnt_self = &rvnt;
- }
- private:
- void freeClientData(CheckStatusWrapper* status, bool force = false);
- void internalCancel(CheckStatusWrapper* status);
- Rvnt* rvnt;
- Rdb* rdb;
- };
- int Events::release()
- {
- int rc = --refCounter;
- if (rc != 0)
- {
- fb_assert(rc > 0);
- return 1;
- }
- if (rvnt)
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- freeClientData(&status, true);
- }
- delete this;
- return 0;
- }
- class Attachment final : public RefCntIface<IAttachmentImpl<Attachment, CheckStatusWrapper> >
- {
- public:
- // IAttachment implementation
- int release() override;
- void getInfo(CheckStatusWrapper* status,
- unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer) override;
- Firebird::ITransaction* startTransaction(CheckStatusWrapper* status,
- unsigned int tpbLength, const unsigned char* tpb) override;
- Firebird::ITransaction* reconnectTransaction(CheckStatusWrapper* status, unsigned int length, const unsigned char* id) override;
- Firebird::IRequest* compileRequest(CheckStatusWrapper* status, unsigned int blr_length, const unsigned char* blr) override;
- void transactRequest(CheckStatusWrapper* status, ITransaction* transaction,
- unsigned int blr_length, const unsigned char* blr,
- unsigned int in_msg_length, const unsigned char* in_msg,
- unsigned int out_msg_length, unsigned char* out_msg) override;
- Firebird::IBlob* createBlob(CheckStatusWrapper* status, ITransaction* transaction,
- ISC_QUAD* id, unsigned int bpbLength = 0, const unsigned char* bpb = 0) override;
- Firebird::IBlob* openBlob(CheckStatusWrapper* status, ITransaction* transaction,
- ISC_QUAD* id, unsigned int bpbLength = 0, const unsigned char* bpb = 0) override;
- int getSlice(CheckStatusWrapper* status, ITransaction* transaction, ISC_QUAD* id,
- unsigned int sdl_length, const unsigned char* sdl,
- unsigned int param_length, const unsigned char* param,
- int sliceLength, unsigned char* slice) override;
- void putSlice(CheckStatusWrapper* status, ITransaction* transaction, ISC_QUAD* id,
- unsigned int sdl_length, const unsigned char* sdl,
- unsigned int param_length, const unsigned char* param,
- int sliceLength, unsigned char* slice) override;
- void executeDyn(CheckStatusWrapper* status, ITransaction* transaction, unsigned int length,
- const unsigned char* dyn) override;
- Statement* prepare(CheckStatusWrapper* status, ITransaction* transaction,
- unsigned int stmtLength, const char* sqlStmt, unsigned dialect, unsigned int flags) override;
- Firebird::ITransaction* execute(CheckStatusWrapper* status, ITransaction* transaction,
- unsigned int stmtLength, const char* sqlStmt, unsigned dialect,
- IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata* outMetadata, void* outBuffer) override;
- Firebird::IResultSet* openCursor(CheckStatusWrapper* status, ITransaction* transaction,
- unsigned int stmtLength, const char* sqlStmt, unsigned dialect,
- IMessageMetadata* inMetadata, void* inBuffer, Firebird::IMessageMetadata* outMetadata,
- const char* cursorName, unsigned int cursorFlags) override;
- Firebird::IEvents* queEvents(CheckStatusWrapper* status, Firebird::IEventCallback* callback,
- unsigned int length, const unsigned char* events) override;
- void cancelOperation(CheckStatusWrapper* status, int option) override;
- void ping(CheckStatusWrapper* status) override;
- void detach(CheckStatusWrapper* status) override;
- void dropDatabase(CheckStatusWrapper* status) override;
- void deprecatedDetach(Firebird::CheckStatusWrapper* status) override;
- void deprecatedDropDatabase(Firebird::CheckStatusWrapper* status) override;
- unsigned int getIdleTimeout(CheckStatusWrapper* status) override;
- void setIdleTimeout(CheckStatusWrapper* status, unsigned int timeOut) override;
- unsigned int getStatementTimeout(CheckStatusWrapper* status) override;
- void setStatementTimeout(CheckStatusWrapper* status, unsigned int timeOut) override;
- Batch* createBatch(Firebird::CheckStatusWrapper* status, ITransaction* transaction,
- unsigned stmtLength, const char* sqlStmt, unsigned dialect,
- IMessageMetadata* inMetadata, unsigned parLength, const unsigned char* par) override;
- Replicator* createReplicator(Firebird::CheckStatusWrapper* status) override;
- public:
- Attachment(Rdb* handle, const PathName& path)
- : replicator(nullptr), rdb(handle), dbPath(getPool(), path)
- { }
- Rdb* getRdb()
- {
- return rdb;
- }
- const PathName& getDbPath()
- {
- return dbPath;
- }
- Rtr* remoteTransaction(ITransaction* apiTra);
- Transaction* remoteTransactionInterface(ITransaction* apiTra);
- Statement* createStatement(CheckStatusWrapper* status, unsigned dialect);
- Replicator* replicator;
- private:
- void execWithCheck(CheckStatusWrapper* status, const string& stmt);
- void freeClientData(CheckStatusWrapper* status, bool force = false);
- void internalDetach(Firebird::CheckStatusWrapper* status);
- void internalDropDatabase(Firebird::CheckStatusWrapper* status);
- SLONG getSingleInfo(CheckStatusWrapper* status, UCHAR infoItem);
- Rdb* rdb;
- const PathName dbPath;
- };
- int Attachment::release()
- {
- if (--refCounter != 0)
- return 1;
- if (rdb)
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- freeClientData(&status, true);
- }
- delete this;
- return 0;
- }
- class Service final : public RefCntIface<IServiceImpl<Service, CheckStatusWrapper> >
- {
- public:
- // IService implementation
- int release() override;
- void detach(CheckStatusWrapper* status) override;
- void deprecatedDetach(CheckStatusWrapper* status) override;
- void query(CheckStatusWrapper* status,
- unsigned int sendLength, const unsigned char* sendItems,
- unsigned int receiveLength, const unsigned char* receiveItems,
- unsigned int bufferLength, unsigned char* buffer) override;
- void start(CheckStatusWrapper* status, unsigned int spbLength, const unsigned char* spb) override;
- void cancel(CheckStatusWrapper* status) override;
- public:
- Service(Rdb* handle) : rdb(handle) { }
- private:
- void freeClientData(CheckStatusWrapper* status, bool force = false);
- void internalDetach(CheckStatusWrapper* status);
- Rdb* rdb;
- };
- int Service::release()
- {
- if (--refCounter != 0)
- return 1;
- if (rdb)
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- freeClientData(&status, true);
- }
- delete this;
- return 0;
- }
- class RProvider : public StdPlugin<IProviderImpl<RProvider, CheckStatusWrapper> >
- {
- public:
- explicit RProvider(IPluginConfig*)
- : cryptCallback(NULL)
- { }
- RProvider()
- : cryptCallback(NULL)
- { }
- // IProvider implementation
- IAttachment* attachDatabase(CheckStatusWrapper* status, const char* fileName,
- unsigned int dpbLength, const unsigned char* dpb);
- IAttachment* createDatabase(CheckStatusWrapper* status, const char* fileName,
- unsigned int dpbLength, const unsigned char* dpb);
- IService* attachServiceManager(CheckStatusWrapper* status, const char* service,
- unsigned int spbLength, const unsigned char* spb);
- void shutdown(CheckStatusWrapper* status, unsigned int timeout, const int reason);
- void setDbCryptCallback(CheckStatusWrapper* status, ICryptKeyCallback* cryptCallback);
- protected:
- IAttachment* attach(CheckStatusWrapper* status, const char* filename, unsigned int dpb_length,
- const unsigned char* dpb, bool loopback);
- IAttachment* create(CheckStatusWrapper* status, const char* filename, unsigned int dpb_length,
- const unsigned char* dpb, bool loopback);
- IService* attachSvc(CheckStatusWrapper* status, const char* service, unsigned int spbLength,
- const unsigned char* spb, bool loopback);
- private:
- Firebird::ICryptKeyCallback* cryptCallback;
- };
- void RProvider::shutdown(CheckStatusWrapper* status, unsigned int /*timeout*/, const int /*reason*/)
- {
- status->init();
- try
- {
- outPorts->closePorts();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void RProvider::setDbCryptCallback(CheckStatusWrapper* status, ICryptKeyCallback* callback)
- {
- status->init();
- cryptCallback = callback;
- }
- class Loopback : public IProviderBaseImpl<Loopback, CheckStatusWrapper, RProvider>
- {
- public:
- explicit Loopback(IPluginConfig*)
- { }
- // IProvider implementation
- IAttachment* attachDatabase(CheckStatusWrapper* status, const char* fileName,
- unsigned int dpbLength, const unsigned char* dpb);
- IAttachment* createDatabase(CheckStatusWrapper* status, const char* fileName,
- unsigned int dpbLength, const unsigned char* dpb);
- IService* attachServiceManager(CheckStatusWrapper* status, const char* service,
- unsigned int spbLength, const unsigned char* spb);
- };
- namespace {
- SimpleFactory<RProvider> remoteFactory;
- SimpleFactory<Loopback> loopbackFactory;
- }
- void registerRedirector(Firebird::IPluginManager* iPlugin)
- {
- iPlugin->registerPluginFactory(IPluginManager::TYPE_PROVIDER, "Remote", &remoteFactory);
- iPlugin->registerPluginFactory(IPluginManager::TYPE_PROVIDER, "Loopback", &loopbackFactory);
- Auth::registerLegacyClient(iPlugin);
- Auth::registerSrpClient(iPlugin);
- #ifdef TRUSTED_AUTH
- Auth::registerTrustedClient(iPlugin);
- #endif
- Crypt::registerArc4(iPlugin);
- }
- } // namespace Remote
- /*
- extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster* master)
- {
- IPluginManager* pi = master->getPluginManager();
- registerRedirector(pi);
- pi->release();
- }
- */
- namespace Remote {
- static Rvnt* add_event(rem_port*);
- static void add_other_params(rem_port*, ClumpletWriter&, const ParametersSet&);
- static void add_working_directory(ClumpletWriter&, const PathName&);
- static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned flags,
- ClumpletWriter& pb, const ParametersSet& parSet, PathName& node_name, PathName* ref_db_name,
- Firebird::ICryptKeyCallback* cryptCb);
- static void batch_gds_receive(rem_port*, struct rmtque *, USHORT);
- static void batch_dsql_fetch(rem_port*, struct rmtque *, USHORT);
- static void clear_queue(rem_port*);
- static void clear_stmt_que(rem_port*, Rsr*);
- static void finalize(rem_port* port);
- static void disconnect(rem_port*, bool rmRef = true);
- static void enqueue_receive(rem_port*, t_rmtque_fn, Rdb*, void*, Rrq::rrq_repeat*);
- static void dequeue_receive(rem_port*);
- static THREAD_ENTRY_DECLARE event_thread(THREAD_ENTRY_PARAM);
- static Rvnt* find_event(rem_port*, SLONG);
- static bool get_new_dpb(ClumpletWriter&, const ParametersSet&, bool);
- static void info(CheckStatusWrapper*, Rdb*, P_OP, USHORT, USHORT, USHORT,
- const UCHAR*, USHORT, const UCHAR*, ULONG, UCHAR*);
- static bool init(CheckStatusWrapper*, ClntAuthBlock&, rem_port*, P_OP, PathName&,
- ClumpletWriter&, IntlParametersBlock&, ICryptKeyCallback* cryptCallback);
- static Rtr* make_transaction(Rdb*, USHORT);
- static void mov_dsql_message(const UCHAR*, const rem_fmt*, UCHAR*, const rem_fmt*);
- static void move_error(const Arg::StatusVector& v);
- static void receive_after_start(Rrq*, USHORT);
- static void receive_packet(rem_port*, PACKET *);
- static void receive_packet_noqueue(rem_port*, PACKET *);
- static void receive_queued_packet(rem_port*, USHORT);
- static void receive_response(IStatus*, Rdb*, PACKET *);
- static void release_blob(Rbl*);
- static void release_event(Rvnt*);
- static void release_object(IStatus*, Rdb*, P_OP, USHORT);
- static void release_request(Rrq*);
- static void release_statement(Rsr**);
- static void release_sql_request(Rsr*);
- static void release_transaction(Rtr*);
- static void send_and_receive(IStatus*, Rdb*, PACKET *);
- static void send_blob(CheckStatusWrapper*, Rbl*, USHORT, const UCHAR*);
- static void send_packet(rem_port*, PACKET *);
- static void send_partial_packet(rem_port*, PACKET *);
- static void server_death(rem_port*);
- static void svcstart(CheckStatusWrapper*, Rdb*, P_OP, USHORT, USHORT, USHORT, const UCHAR*);
- static void unsupported();
- static void zap_packet(PACKET *);
- static void cleanDpb(Firebird::ClumpletWriter&, const ParametersSet*);
- static void authFillParametersBlock(ClntAuthBlock& authItr, ClumpletWriter& dpb,
- const ParametersSet* tags, rem_port* port);
- static void authReceiveResponse(bool havePacket, ClntAuthBlock& authItr, rem_port* port,
- Rdb* rdb, IStatus* status, PACKET* packet, bool checkKeys);
- static AtomicCounter remote_event_id;
- static const unsigned ANALYZE_USER_VFY = 0x01;
- static const unsigned ANALYZE_LOOPBACK = 0x02;
- static const unsigned ANALYZE_MOUNTS = 0x04;
- static const unsigned ANALYZE_EMP_NAME = 0x08;
- inline static void reset(IStatus* status) throw()
- {
- status->init();
- }
- #define SET_OBJECT(rdb, object, id) rdb->rdb_port->setHandle(object, id)
- inline static void defer_packet(rem_port* port, PACKET* packet, bool sent = false)
- {
- fb_assert(port->port_flags & PORT_lazy);
- fb_assert(port->port_deferred_packets);
- // hvlad: passed packet often is rdb->rdb_packet and therefore can be
- // changed inside clear_queue. To not confuse caller we must preserve
- // packet content
- rem_que_packet p;
- p.packet = *packet;
- p.sent = sent;
- clear_queue(port);
- *packet = p.packet;
- // don't use string references in P_RESP structure copied from another packet
- memset(&p.packet.p_resp, 0, sizeof(p.packet.p_resp));
- port->port_deferred_packets->add(p);
- }
- IAttachment* RProvider::attach(CheckStatusWrapper* status, const char* filename, unsigned int dpb_length,
- const unsigned char* dpb, bool loopback)
- {
- /**************************************
- *
- * g d s _ a t t a c h _ d a t a b a s e
- *
- **************************************
- *
- * Functional description
- * Connect to an old, grungy database, corrupted by user data.
- *
- **************************************/
- try
- {
- reset(status);
- ClumpletWriter newDpb(ClumpletReader::dpbList, MAX_DPB_SIZE, dpb, dpb_length);
- unsigned flags = ANALYZE_MOUNTS;
- if (get_new_dpb(newDpb, dpbParam, loopback))
- flags |= ANALYZE_USER_VFY;
- if (loopback)
- flags |= ANALYZE_LOOPBACK;
- PathName expanded_name(filename);
- resolveAlias(filename, expanded_name, nullptr);
- ClntAuthBlock cBlock(&expanded_name, &newDpb, &dpbParam);
- PathName node_name;
- rem_port* port = analyze(cBlock, expanded_name, flags, newDpb, dpbParam, node_name, NULL, cryptCallback);
- if (!port)
- {
- Arg::Gds(isc_unavailable).copyTo(status);
- return NULL;
- }
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // The client may have set a parameter for dummy_packet_interval. Add that to the
- // the DPB so the server can pay attention to it.
- add_other_params(port, newDpb, dpbParam);
- add_working_directory(newDpb, node_name);
- IntlDpb intl;
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: call init for DB='%s'\n", expanded_name.c_str()));
- if (!init(status, cBlock, port, op_attach, expanded_name, newDpb, intl, cryptCallback))
- return NULL;
- Attachment* a = FB_NEW Attachment(port->port_context, filename);
- a->addRef();
- return a;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- return NULL;
- }
- }
- IAttachment* RProvider::attachDatabase(CheckStatusWrapper* status, const char* filename,
- unsigned int dpb_length, const unsigned char* dpb)
- {
- /**************************************
- *
- * g d s _ a t t a c h _ d a t a b a s e
- *
- **************************************
- *
- * Functional description
- * Connect to an old, grungy database, corrupted by user data.
- *
- **************************************/
- return attach(status, filename, dpb_length, dpb, false);
- }
- IAttachment* Loopback::attachDatabase(CheckStatusWrapper* status, const char* filename,
- unsigned int dpb_length, const unsigned char* dpb)
- {
- /**************************************
- *
- * g d s _ a t t a c h _ d a t a b a s e
- *
- **************************************
- *
- * Functional description
- * Connect to an old, grungy database, corrupted by user data.
- *
- **************************************/
- return attach(status, filename, dpb_length, dpb, true);
- }
- void Blob::getInfo(CheckStatusWrapper* status,
- unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer)
- {
- /**************************************
- *
- * g d s _ b l o b _ i n f o
- *
- **************************************
- *
- * Functional description
- * Provide information on blob object.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(blob, isc_bad_segstr_handle);
- Rdb* rdb = blob->rbl_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- info(status, rdb, op_info_blob, blob->rbl_id, 0,
- itemsLength, items, 0, 0, bufferLength, buffer);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Blob::freeClientData(CheckStatusWrapper* status, bool force)
- {
- /**************************************
- *
- * g d s _ c a n c e l _ b l o b
- *
- **************************************
- *
- * Functional description
- * Abort a partially completed blob.
- *
- **************************************/
- try
- {
- if (!blob)
- {
- return;
- }
- CHECK_HANDLE(blob, isc_bad_segstr_handle);
- Rdb* rdb = blob->rbl_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- try
- {
- release_object(status, rdb, op_cancel_blob, blob->rbl_id);
- }
- catch (const Exception&)
- {
- if (!force)
- throw;
- }
- release_blob(blob);
- blob = NULL;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Blob::internalCancel(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * g d s _ c a n c e l _ b l o b
- *
- **************************************
- *
- * Functional description
- * Abort a partially completed blob.
- *
- **************************************/
- reset(status);
- freeClientData(status);
- }
- void Blob::cancel(CheckStatusWrapper* status)
- {
- internalCancel(status);
- if (status->isEmpty())
- release();
- }
- void Blob::deprecatedCancel(CheckStatusWrapper* status)
- {
- internalCancel(status);
- }
- void Blob::internalClose(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * g d s _ c l o s e _ b l o b
- *
- **************************************
- *
- * Functional description
- * Close a completed blob.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(blob, isc_bad_segstr_handle);
- Rdb* rdb = blob->rbl_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- if ((blob->rbl_flags & Rbl::CREATE) && blob->rbl_ptr != blob->rbl_buffer)
- {
- send_blob(status, blob, 0, NULL);
- }
- release_object(status, rdb, op_close_blob, blob->rbl_id);
- release_blob(blob);
- blob = NULL;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Blob::close(CheckStatusWrapper* status)
- {
- internalClose(status);
- if (status->isEmpty())
- release();
- }
- void Blob::deprecatedClose(CheckStatusWrapper* status)
- {
- internalClose(status);
- }
- void Events::freeClientData(CheckStatusWrapper* status, bool force)
- {
- /**************************************
- *
- * g d s _ $ c a n c e l _ e v e n t s
- *
- **************************************
- *
- * Functional description
- * Cancel an outstanding event.
- *
- **************************************/
- RefPtr<IEventCallback> callback;
- try
- {
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- if (!rvnt)
- {
- return;
- }
- CHECK_HANDLE(rvnt, isc_bad_events_handle);
- try
- {
- // Tell the remote server to cancel it and delete it from the list
- PACKET* packet = &rdb->rdb_packet;
- // Set the various parameters for the packet:
- // remote operation to perform, which database,
- // and which event.
- packet->p_operation = op_cancel_events;
- packet->p_event.p_event_database = rdb->rdb_id;
- const SLONG save_id = packet->p_event.p_event_rid = rvnt->rvnt_id;
- // Send the packet, and if that worked, get a response
- try
- {
- LocalStatus ls;
- CheckStatusWrapper dummy(&ls);
- send_packet(rdb->rdb_port, packet);
- receive_response(&dummy, rdb, packet);
- }
- catch (const Exception&) { }
- // Get ready to fire the event.
- if (rvnt->rvnt_id == save_id)
- {
- callback = rvnt->rvnt_callback;
- rvnt->rvnt_id = 0;
- }
- }
- catch (const Exception&)
- {
- if (!force)
- throw;
- }
- rvnt = NULL;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- // If the event has never been fired, fire it off with a length of 0.
- // Note: it is job of person being notified to check that counts
- // actually changed and that they were not woken up because of
- // server death.
- if (callback)
- callback->eventCallbackFunction(0, NULL);
- }
- void Events::internalCancel(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * g d s _ $ c a n c e l _ e v e n t s
- *
- **************************************
- *
- * Functional description
- * Cancel an outstanding event.
- *
- **************************************/
- reset(status);
- freeClientData(status);
- }
- void Events::cancel(CheckStatusWrapper* status)
- {
- internalCancel(status);
- if (status->isEmpty())
- release();
- }
- void Events::deprecatedCancel(CheckStatusWrapper* status)
- {
- internalCancel(status);
- }
- void Transaction::internalCommit(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * g d s _ c o m m i t
- *
- **************************************
- *
- * Functional description
- * Commit a transaction.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- Rdb* rdb = transaction->rtr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- release_object(status, rdb, op_commit, transaction->rtr_id);
- REMOTE_cleanup_transaction(transaction);
- release_transaction(transaction);
- transaction = NULL;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Transaction::commit(CheckStatusWrapper* status)
- {
- internalCommit(status);
- if (status->isEmpty())
- release();
- }
- void Transaction::deprecatedCommit(CheckStatusWrapper* status)
- {
- internalCommit(status);
- }
- void Transaction::commitRetaining(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * g d s _ c o m m i t _ r e t a i n i n g
- *
- **************************************
- *
- * Functional description
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- Rdb* rdb = transaction->rtr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- release_object(status, rdb, op_commit_retaining, transaction->rtr_id);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- ITransaction* Transaction::join(CheckStatusWrapper* status, ITransaction* tra)
- {
- /**************************************
- *
- * I T r a n s a c t i o n :: j o i n
- *
- **************************************
- *
- * Functional description
- * Join this and passed transactions
- * into single distributed transaction
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- return DtcInterfacePtr()->join(status, this, tra);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- Transaction* Transaction::validate(CheckStatusWrapper* /*status*/, IAttachment* testAtt)
- {
- return (transaction && remAtt == testAtt) ? this : NULL;
- }
- Transaction* Transaction::enterDtc(CheckStatusWrapper* status)
- {
- try
- {
- Transaction* copy = FB_NEW Transaction(this);
- copy->addRef();
- transaction = NULL;
- return copy;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- Firebird::IRequest* Attachment::compileRequest(CheckStatusWrapper* status,
- unsigned int blr_length, const unsigned char* blr)
- {
- /**************************************
- *
- * g d s _ c o m p i l e
- *
- **************************************
- *
- * Functional description
- *
- **************************************/
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Validate data length
- CHECK_LENGTH(port, blr_length);
- // Parse the request in case blr_d_float must be converted to blr_double
- const UCHAR* new_blr = blr;
- // Make up a packet for the remote guy
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_compile;
- P_CMPL* compile = &packet->p_cmpl;
- compile->p_cmpl_database = rdb->rdb_id;
- compile->p_cmpl_blr.cstr_length = blr_length;
- compile->p_cmpl_blr.cstr_address = new_blr;
- send_and_receive(status, rdb, packet);
- // Parse the request to find the messages
- RMessage* next;
- RMessage* message = PARSE_messages(blr, blr_length);
- USHORT max_msg = 0;
- for (next = message; next; next = next->msg_next)
- max_msg = MAX(max_msg, next->msg_number);
- // Allocate request block
- Rrq* request = FB_NEW Rrq(max_msg + 1);
- request->rrq_rdb = rdb;
- request->rrq_id = packet->p_resp.p_resp_object;
- request->rrq_max_msg = max_msg;
- SET_OBJECT(rdb, request, request->rrq_id);
- request->rrq_next = rdb->rdb_requests;
- rdb->rdb_requests = request;
- // when the messages are parsed, they are linked together; we need
- // to place the messages in the tail of the request block and create
- // a queue of length 1 for each message number
- for (; message; message = next)
- {
- next = message->msg_next;
- message->msg_next = message;
- Rrq::rrq_repeat * tail = &request->rrq_rpt[message->msg_number];
- tail->rrq_message = message;
- tail->rrq_xdr = message;
- tail->rrq_format = (rem_fmt*) message->msg_address;
- message->msg_address = NULL;
- }
- Firebird::IRequest* r = FB_NEW Request(request, this);
- r->addRef();
- return r;
- }
- catch (const Exception& ex)
- {
- // deallocate new_blr here???
- ex.stuffException(status);
- }
- return NULL;
- }
- IBlob* Attachment::createBlob(CheckStatusWrapper* status, ITransaction* apiTra, ISC_QUAD* blob_id,
- unsigned int bpb_length, const unsigned char* bpb)
- {
- /**************************************
- *
- * g d s _ c r e a t e _ b l o b 2
- *
- **************************************
- *
- * Functional description
- * Open an existing blob.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rtr* transaction = remoteTransaction(apiTra);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- // Validate data length
- CHECK_LENGTH(port, bpb_length);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_create_blob2;
- P_BLOB* p_blob = &packet->p_blob;
- p_blob->p_blob_transaction = transaction->rtr_id;
- p_blob->p_blob_bpb.cstr_length = bpb_length;
- fb_assert(!p_blob->p_blob_bpb.cstr_allocated ||
- p_blob->p_blob_bpb.cstr_allocated < p_blob->p_blob_bpb.cstr_length);
- // CVC: Should we ensure here that cstr_allocated < bpb_length???
- // Otherwise, xdr_cstring() calling alloc_string() to decode would
- // cause memory problems on the client side for SS, as the client
- // would try to write to the application's provided R/O buffer.
- p_blob->p_blob_bpb.cstr_address = bpb;
- try
- {
- send_and_receive(status, rdb, packet);
- }
- catch (const Exception&)
- {
- p_blob->p_blob_bpb.cstr_length = 0;
- p_blob->p_blob_bpb.cstr_address = NULL;
- throw;
- }
- p_blob->p_blob_bpb.cstr_length = 0;
- p_blob->p_blob_bpb.cstr_address = NULL;
- Rbl* blob = FB_NEW Rbl();
- *blob_id = packet->p_resp.p_resp_blob_id;
- blob->rbl_rdb = rdb;
- blob->rbl_rtr = transaction;
- blob->rbl_id = packet->p_resp.p_resp_object;
- blob->rbl_flags |= Rbl::CREATE;
- SET_OBJECT(rdb, blob, blob->rbl_id);
- blob->rbl_next = transaction->rtr_blobs;
- transaction->rtr_blobs = blob;
- Firebird::IBlob* b = FB_NEW Blob(blob);
- b->addRef();
- return b;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- Firebird::IAttachment* RProvider::create(CheckStatusWrapper* status, const char* filename,
- unsigned int dpb_length, const unsigned char* dpb, bool loopback)
- {
- /**************************************
- *
- * g d s _ c r e a t e _ d a t a b a s e
- *
- **************************************
- *
- * Functional description
- * Create a nice, squeeky clean database, uncorrupted by user data.
- *
- **************************************/
- try
- {
- reset(status);
- ClumpletWriter newDpb(ClumpletReader::dpbList, MAX_DPB_SIZE,
- reinterpret_cast<const UCHAR*>(dpb), dpb_length);
- unsigned flags = ANALYZE_MOUNTS;
- if (get_new_dpb(newDpb, dpbParam, loopback))
- flags |= ANALYZE_USER_VFY;
- if (loopback)
- flags |= ANALYZE_LOOPBACK;
- PathName expanded_name(filename);
- resolveAlias(filename, expanded_name, nullptr);
- ClntAuthBlock cBlock(&expanded_name, &newDpb, &dpbParam);
- PathName node_name;
- rem_port* port = analyze(cBlock, expanded_name, flags, newDpb, dpbParam, node_name, NULL, cryptCallback);
- if (!port)
- {
- Arg::Gds(isc_unavailable).copyTo(status);
- return NULL;
- }
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rdb* rdb = port->port_context;
- // The client may have set a parameter for dummy_packet_interval. Add that to the
- // the DPB so the server can pay attention to it. Note: allocation code must
- // ensure sufficient space has been added.
- add_other_params(port, newDpb, dpbParam);
- add_working_directory(newDpb, node_name);
- IntlDpb intl;
- if (!init(status, cBlock, port, op_create, expanded_name, newDpb, intl, cryptCallback))
- return NULL;
- Firebird::IAttachment* a = FB_NEW Attachment(rdb, filename);
- a->addRef();
- return a;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- IAttachment* RProvider::createDatabase(CheckStatusWrapper* status, const char* fileName,
- unsigned int dpbLength, const unsigned char* dpb)
- {
- /**************************************
- *
- * g d s _ c r e a t e _ d a t a b a s e
- *
- **************************************
- *
- * Functional description
- * Create a nice, squeeky clean database, uncorrupted by user data.
- *
- **************************************/
- return create(status, fileName, dpbLength, dpb, false);
- }
- IAttachment* Loopback::createDatabase(CheckStatusWrapper* status, const char* fileName,
- unsigned int dpbLength, const unsigned char* dpb)
- {
- /**************************************
- *
- * g d s _ c r e a t e _ d a t a b a s e
- *
- **************************************
- *
- * Functional description
- * Create a nice, squeeky clean database, uncorrupted by user data.
- *
- **************************************/
- return create(status, fileName, dpbLength, dpb, true);
- }
- void Attachment::getInfo(CheckStatusWrapper* status,
- unsigned int item_length, const unsigned char* items,
- unsigned int buffer_length, unsigned char* buffer)
- {
- /**************************************
- *
- * g d s _ d a t a b a s e _ i n f o
- *
- **************************************
- *
- * Functional description
- * Provide information on database object.
- *
- **************************************/
- try
- {
- reset(status);
- HalfStaticArray<UCHAR, 1024> temp;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- USHORT protocol = memchr(items, fb_info_protocol_version, item_length) ? port->port_protocol : 0;
- protocol &= FB_PROTOCOL_MASK;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- UCHAR* temp_buffer = temp.getBuffer(buffer_length);
- info(status, rdb, op_info_database, rdb->rdb_id, 0,
- item_length, items, 0, 0, buffer_length, temp_buffer);
- string version;
- port->versionInfo(version);
- MERGE_database_info(temp_buffer, buffer, buffer_length,
- DbImplementation::current.backwardCompatibleImplementation(), 3, 1,
- reinterpret_cast<const UCHAR*>(version.c_str()),
- reinterpret_cast<const UCHAR*>(port->port_host->str_data),
- protocol);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Attachment::executeDyn(CheckStatusWrapper* status, ITransaction* apiTra, unsigned int length,
- const unsigned char* dyn)
- {
- /**************************************
- *
- * g d s _ d d l
- *
- **************************************
- *
- * Functional description
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rtr* transaction = remoteTransaction(apiTra);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- // Validate data length
- CHECK_LENGTH(port, length);
- // Make up a packet for the remote guy
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_ddl;
- P_DDL* ddl = &packet->p_ddl;
- ddl->p_ddl_database = rdb->rdb_id;
- ddl->p_ddl_transaction = transaction->rtr_id;
- ddl->p_ddl_blr.cstr_length = length;
- ddl->p_ddl_blr.cstr_address = dyn;
- send_and_receive(status, rdb, packet);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Attachment::freeClientData(CheckStatusWrapper* status, bool force)
- {
- /**************************************
- *
- * g d s _ d e t a c h
- *
- **************************************
- *
- * Functional description
- * Close down a database.
- *
- **************************************/
- try
- {
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RemotePortGuard portGuard(port, FB_FUNCTION);
- try
- {
- if (!(port->port_flags & (PORT_rdb_shutdown | PORT_detached)))
- {
- release_object(status, rdb, op_detach, rdb->rdb_id);
- }
- }
- catch (const status_exception& ex)
- {
- // If something other than a network error occurred, just return. Otherwise
- // we need to free up the associated structures, close the socket and
- // scream. By the way, we should probably create an entry in the log
- // telling the user that an unrecoverable network error occurred and that
- // if there was any uncommitted work, its gone...... Oh well....
- ex.stuffException(status);
- if (!fb_utils::isNetworkError(status->getErrors()[1]) && (!force))
- {
- return;
- }
- }
- while (rdb->rdb_events)
- release_event(rdb->rdb_events);
- while (rdb->rdb_requests)
- release_request(rdb->rdb_requests);
- while (rdb->rdb_sql_requests)
- release_sql_request(rdb->rdb_sql_requests);
- while (rdb->rdb_transactions)
- release_transaction(rdb->rdb_transactions);
- if (port->port_statement)
- release_statement(&port->port_statement);
- // If there is a network error, don't try to send another packet, just
- // free the packet and disconnect the port. Put something into firebird.log
- // informing the user of the following.
- if (status->getState() & Firebird::IStatus::STATE_ERRORS)
- {
- iscLogStatus("REMOTE INTERFACE/gds__detach: Unsuccessful detach from "
- "database.\n\tUncommitted work may have been lost.", status);
- reset(status);
- }
- disconnect(port);
- rdb = NULL;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Attachment::internalDetach(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * g d s _ d e t a c h
- *
- **************************************
- *
- * Functional description
- * Close down a database.
- *
- **************************************/
- reset(status);
- freeClientData(status);
- }
- void Attachment::detach(CheckStatusWrapper* status)
- {
- internalDetach(status);
- if (status->isEmpty())
- release();
- }
- void Attachment::deprecatedDetach(CheckStatusWrapper* status)
- {
- internalDetach(status);
- }
- void Attachment::internalDropDatabase(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * i s c _ d r o p _ d a t a b a s e
- *
- **************************************
- *
- * Functional description
- * Close down and purge a database.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RemotePortGuard portGuard(port, FB_FUNCTION);
- try
- {
- release_object(status, rdb, op_drop_database, rdb->rdb_id);
- }
- catch (const status_exception& ex)
- {
- ex.stuffException(status);
- if (ex.value()[1] != isc_drdb_completed_with_errs)
- {
- return;
- }
- }
- while (rdb->rdb_events)
- release_event(rdb->rdb_events);
- while (rdb->rdb_requests)
- release_request(rdb->rdb_requests);
- while (rdb->rdb_sql_requests)
- release_sql_request(rdb->rdb_sql_requests);
- while (rdb->rdb_transactions)
- release_transaction(rdb->rdb_transactions);
- if (port->port_statement)
- release_statement(&port->port_statement);
- disconnect(port);
- rdb = NULL;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Attachment::dropDatabase(CheckStatusWrapper* status)
- {
- internalDropDatabase(status);
- if (status->isEmpty())
- release();
- }
- void Attachment::deprecatedDropDatabase(CheckStatusWrapper* status)
- {
- internalDropDatabase(status);
- }
- SLONG Attachment::getSingleInfo(CheckStatusWrapper* status, UCHAR infoItem)
- {
- UCHAR buff[16];
- getInfo(status, 1, &infoItem, sizeof(buff), buff);
- if (status->getState() & IStatus::STATE_ERRORS)
- return 0;
- const UCHAR* p = buff;
- const UCHAR* const end = buff + sizeof(buff);
- UCHAR item;
- while ((item = *p++) != isc_info_end && p < end - 1)
- {
- const SLONG length = gds__vax_integer(p, 2);
- p += 2;
- if (item == infoItem)
- return gds__vax_integer(p, (SSHORT)length);
- fb_assert(false);
- p += length;
- }
- return 0;
- }
- void Attachment::execWithCheck(CheckStatusWrapper* status, const string& stmt)
- {
- /**************************************
- *
- * Used to execute "SET xxx TIMEOUT" statements. Checks for protocol version
- * and convert expected SQL error into isc_wish_list error. The only possible
- * case is when modern network server works with legacy engine.
- *
- **************************************/
- if (rdb->rdb_port->port_protocol >= PROTOCOL_STMT_TOUT)
- {
- execute(status, NULL, stmt.length(), stmt.c_str(), SQL_DIALECT_CURRENT, NULL, NULL, NULL, NULL);
- if (!(status->getState() & IStatus::STATE_ERRORS))
- return;
- // handle isc_dsql_token_unk_err
- const ISC_STATUS* errs = status->getErrors();
- if (!fb_utils::containsErrorCode(errs, isc_sqlerr) ||
- !fb_utils::containsErrorCode(errs, isc_dsql_token_unk_err))
- {
- return;
- }
- status->init();
- }
- status->setErrors(Arg::Gds(isc_wish_list).value());
- }
- unsigned int Attachment::getIdleTimeout(CheckStatusWrapper* status)
- {
- if (rdb->rdb_port->port_protocol >= PROTOCOL_STMT_TOUT)
- return getSingleInfo(status, fb_info_ses_idle_timeout_att);
- status->setErrors(Arg::Gds(isc_wish_list).value());
- return 0;
- }
- void Attachment::setIdleTimeout(CheckStatusWrapper* status, unsigned int timeOut)
- {
- string stmt;
- stmt.printf("SET SESSION IDLE TIMEOUT %lu", timeOut);
- execWithCheck(status, stmt);
- }
- unsigned int Attachment::getStatementTimeout(CheckStatusWrapper* status)
- {
- if (rdb->rdb_port->port_protocol >= PROTOCOL_STMT_TOUT)
- return getSingleInfo(status, fb_info_statement_timeout_att);
- status->setErrors(Arg::Gds(isc_wish_list).value());
- return 0;
- }
- void Attachment::setStatementTimeout(CheckStatusWrapper* status, unsigned int timeOut)
- {
- string stmt;
- stmt.printf("SET STATEMENT TIMEOUT %lu", timeOut);
- execWithCheck(status, stmt);
- }
- Batch* Attachment::createBatch(CheckStatusWrapper* status, ITransaction* transaction,
- unsigned stmtLength, const char* sqlStmt, unsigned dialect,
- IMessageMetadata* inMetadata, unsigned parLength, const unsigned char* par)
- {
- /**************************************
- *
- * c r e a t e B a t c h
- *
- **************************************
- *
- * Functional description
- * Create jdbc-style batch for SQL statement.
- *
- **************************************/
- Statement* stmt = prepare(status, transaction, stmtLength, sqlStmt, dialect, 0);
- if (status->getState() & Firebird::IStatus::STATE_ERRORS)
- {
- return NULL;
- }
- Batch* rc = stmt->createBatch(status, inMetadata, parLength, par);
- if (status->getState() & Firebird::IStatus::STATE_ERRORS)
- {
- stmt->release();
- return NULL;
- }
- rc->tmpStatement = true;
- return rc;
- }
- Batch* Statement::createBatch(CheckStatusWrapper* status, IMessageMetadata* inMetadata,
- unsigned parLength, const unsigned char* par)
- {
- /**************************************
- *
- * c r e a t e B a t c h
- *
- **************************************
- *
- * Functional description
- * Create jdbc-style batch for prepared statement.
- *
- **************************************/
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- if (port->port_protocol < PROTOCOL_VERSION16)
- unsupported();
- // Build input BLR
- RefPtr<IMessageMetadata> meta;
- if (!inMetadata)
- {
- meta.assignRefNoIncr(getInputMetadata(status));
- check(status);
- inMetadata = meta;
- }
- BlrFromMessage inBlr(inMetadata, dialect, port->port_protocol);
- const unsigned int in_blr_length = inBlr.getLength();
- const UCHAR* const in_blr = inBlr.getBytes();
- // Validate data length
- CHECK_LENGTH(port, in_blr_length);
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- delete statement->rsr_bind_format;
- statement->rsr_bind_format = NULL;
- if (port->port_statement)
- {
- delete port->port_statement->rsr_select_format;
- port->port_statement->rsr_select_format = NULL;
- }
- // Parse the blr describing the message, if there is any.
- if (in_blr_length)
- statement->rsr_bind_format = PARSE_msg_format(in_blr, in_blr_length);
- RMessage* message = NULL;
- if (!statement->rsr_buffer)
- {
- statement->rsr_buffer = message = FB_NEW RMessage(0);
- statement->rsr_message = message;
- message->msg_next = message;
- statement->rsr_fmt_length = 0;
- }
- else
- message = statement->rsr_message = statement->rsr_buffer;
- statement->rsr_flags.clear(Rsr::FETCHED);
- statement->rsr_format = statement->rsr_bind_format;
- statement->rsr_batch_stream.blobRemaining = 0;
- statement->clearException();
- // set up the packet for the other guy...
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_batch_create;
- P_BATCH_CREATE* batch = &packet->p_batch_create;
- batch->p_batch_statement = statement->rsr_id;
- batch->p_batch_blr.cstr_length = in_blr_length;
- batch->p_batch_blr.cstr_address = in_blr;
- batch->p_batch_msglen = inMetadata->getMessageLength(status);
- check(status);
- batch->p_batch_pb.cstr_length = parLength;
- batch->p_batch_pb.cstr_address = par;
- if (port->port_flags & PORT_lazy)
- {
- send_partial_packet(port, packet);
- defer_packet(port, packet, true);
- }
- else {
- send_and_receive(status, rdb, packet);
- }
- message->msg_address = NULL;
- Batch* b = FB_NEW Batch(this, inMetadata, parLength, par);
- b->addRef();
- return b;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- Batch::Batch(Statement* s, IMessageMetadata* inFmt, unsigned parLength, const unsigned char* par)
- : messageStream(0), blobStream(nullptr), sizePointer(nullptr),
- messageSize(0), alignedSize(0), blobBufferSize(0), messageBufferSize(0), flags(0),
- stmt(s), format(inFmt), blobAlign(0), blobPolicy(BLOB_NONE),
- segmented(false), defSegmented(false), batchActive(false),
- messageCount(0), blobCount(0), serverSize(0), blobHeadSize(0),
- tmpStatement(false)
- {
- LocalStatus ls;
- CheckStatusWrapper st(&ls);
- messageSize = format->getMessageLength(&st);
- check(&st);
- alignedSize = format->getAlignedLength(&st);
- check(&st);
- memset(&genId, 0, sizeof(genId));
- ClumpletReader rdr(ClumpletReader::WideTagged, par, parLength);
- for (rdr.rewind(); !rdr.isEof(); rdr.moveNext())
- {
- UCHAR t = rdr.getClumpTag();
- switch (t)
- {
- case TAG_MULTIERROR:
- case TAG_RECORD_COUNTS:
- if (rdr.getInt())
- flags |= (1 << t);
- else
- flags &= ~(1 << t);
- break;
- case TAG_BLOB_POLICY:
- blobPolicy = rdr.getInt();
- switch (blobPolicy)
- {
- case BLOB_ID_ENGINE:
- case BLOB_ID_USER:
- case BLOB_STREAM:
- break;
- default:
- blobPolicy = BLOB_NONE;
- break;
- }
- break;
- }
- }
- s->getStatement()->rsr_batch_flags = flags;
- // allocate buffers
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- blobBufferSize = port->getPortConfig()->getClientBatchBuffer();
- messageBufferSize = blobBufferSize / alignedSize;
- if (!messageBufferSize)
- messageBufferSize = 1;
- messageStreamBuffer.reset(FB_NEW UCHAR[messageBufferSize * alignedSize]);
- if (blobPolicy != BLOB_NONE)
- {
- blobStreamBuffer.reset(FB_NEW UCHAR[blobBufferSize]);
- blobStream = blobStreamBuffer;
- }
- }
- void Batch::add(CheckStatusWrapper* status, unsigned count, const void* inBuffer)
- {
- try
- {
- // Check and validate handles, etc.
- if (!stmt)
- {
- Arg::Gds(isc_bad_req_handle).raise();
- }
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- if (count == 0)
- return;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- putMessageData(count, inBuffer);
- batchActive = true;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Batch::sendMessagePacket(unsigned count, const UCHAR* ptr, bool flash)
- {
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_batch_msg;
- P_BATCH_MSG* batch = &packet->p_batch_msg;
- batch->p_batch_statement = statement->rsr_id;
- batch->p_batch_messages = count;
- batch->p_batch_data.cstr_address = const_cast<UCHAR*>(ptr);
- statement->rsr_batch_size = alignedSize;
- sendDeferredPacket(nullptr, port, packet, flash);
- messageCount += count;
- }
- void Batch::addBlob(CheckStatusWrapper* status, unsigned length, const void* inBuffer, ISC_QUAD* blobId,
- unsigned parLength, const unsigned char* par)
- {
- try
- {
- // Check and validate handles, etc.
- if (!stmt)
- {
- Arg::Gds(isc_bad_req_handle).raise();
- }
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Policy check
- switch (blobPolicy)
- {
- case IBatch::BLOB_ID_ENGINE:
- genBlobId(blobId);
- break;
- case IBatch::BLOB_ID_USER:
- break;
- default:
- (Arg::Gds(isc_batch_policy) << "addBlob").raise();
- }
- // Build blob HDR in stream
- newBlob();
- putBlobData(sizeof *blobId, blobId);
- setSizePointer();
- putBlobData(sizeof parLength, &parLength);
- putBlobData(sizeof parLength, &parLength);
- putBlobData(parLength, par);
- segmented = parLength ? fb_utils::isBpbSegmented(parLength, par) : defSegmented;
- // Store blob data
- putSegment(length, inBuffer);
- batchActive = true;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Batch::appendBlobData(CheckStatusWrapper* status, unsigned length, const void* inBuffer)
- {
- try
- {
- // Check and validate handles, etc.
- if (!stmt)
- {
- Arg::Gds(isc_bad_req_handle).raise();
- }
- // Policy check
- switch (blobPolicy)
- {
- case IBatch::BLOB_ID_USER:
- case IBatch::BLOB_ID_ENGINE:
- break;
- default:
- (Arg::Gds(isc_batch_policy) << "appendBlobData").raise();
- }
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Store blob data
- putSegment(length, inBuffer);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Batch::addBlobStream(CheckStatusWrapper* status, unsigned length, const void* inBuffer)
- {
- try
- {
- // Check and validate handles, etc.
- if (!stmt)
- {
- Arg::Gds(isc_bad_req_handle).raise();
- }
- // Policy check
- if (blobPolicy != IBatch::BLOB_STREAM)
- {
- (Arg::Gds(isc_batch_policy) << "addBlobStream").raise();
- }
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Store stream data
- putBlobData(length, inBuffer);
- batchActive = true;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Batch::sendBlobPacket(unsigned size, const UCHAR* ptr, bool flash)
- {
- Rsr* statement = stmt->getStatement();
- Rdb* rdb = statement->rsr_rdb;
- rem_port* port = rdb->rdb_port;
- setServerInfo();
- fb_assert(!(size % blobAlign));
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_batch_blob_stream;
- P_BATCH_BLOB* batch = &packet->p_batch_blob;
- batch->p_batch_statement = statement->rsr_id;
- batch->p_batch_blob_data.cstr_address = const_cast<UCHAR*>(ptr);
- batch->p_batch_blob_data.cstr_length = size;
- sendDeferredPacket(nullptr, port, packet, flash);
- blobCount += size;
- }
- void Batch::sendDeferredPacket(IStatus* status, rem_port* port, PACKET* packet, bool flash)
- {
- if (port->port_flags & PORT_lazy)
- {
- send_partial_packet(port, packet);
- defer_packet(port, packet, true);
- if ((port->port_protocol >= PROTOCOL_VERSION17) &&
- ((port->port_deferred_packets->getCount() >= DEFER_BATCH_LIMIT) || flash))
- {
- packet->p_operation = op_batch_sync;
- send_packet(port, packet);
- receive_packet(port, packet);
- LocalStatus warning;
- port->checkResponse(&warning, packet, false);
- Rsr* statement = stmt->getStatement();
- if (statement->haveException())
- {
- cleanup();
- statement->raiseException();
- }
- }
- }
- else if (status)
- {
- send_and_receive(status, port->port_context, packet);
- }
- else
- {
- LocalStatus local;
- send_and_receive(&local, port->port_context, packet);
- }
- }
- void Batch::setDefaultBpb(CheckStatusWrapper* status, unsigned parLength, const unsigned char* par)
- {
- try
- {
- // Check and validate handles, etc.
- if (!stmt)
- Arg::Gds(isc_bad_req_handle).raise();
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Check for presence of any data in batch buffers
- if (batchHasData())
- Arg::Gds(isc_batch_defbpb).raise();
- // Set default segmentation flag
- defSegmented = fb_utils::isBpbSegmented(parLength, par);
- // Prepare and send the packet
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_batch_set_bpb;
- P_BATCH_SETBPB* batch = &packet->p_batch_setbpb;
- batch->p_batch_statement = statement->rsr_id;
- batch->p_batch_blob_bpb.cstr_address = par;
- batch->p_batch_blob_bpb.cstr_length = parLength;
- sendDeferredPacket(status, port, packet, true);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- unsigned Batch::getBlobAlignment(CheckStatusWrapper* status)
- {
- try
- {
- setServerInfo();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return blobAlign;
- }
- void Batch::setServerInfo()
- {
- if (blobAlign)
- return;
- // Check and validate handles, etc.
- if (!stmt)
- {
- Arg::Gds(isc_bad_req_handle).raise();
- }
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- LocalStatus ls;
- CheckStatusWrapper s(&ls);
- if (port->port_protocol < PROTOCOL_VERSION17)
- {
- UCHAR item = isc_info_sql_stmt_blob_align;
- UCHAR buffer[16];
- info(&s, rdb, op_info_sql, statement->rsr_id, 0,
- 1, &item, 0, 0, sizeof(buffer), buffer);
- check(&s);
- // Extract from buffer
- if (buffer[0] != item)
- Arg::Gds(isc_batch_align).raise();
- int len = gds__vax_integer(&buffer[1], 2);
- statement->rsr_batch_stream.alignment = blobAlign = gds__vax_integer(&buffer[3], len);
- if (!blobAlign)
- Arg::Gds(isc_batch_align).raise();
- return;
- }
- // Perform info call to server
- UCHAR items[] = {IBatch::INF_BLOB_ALIGNMENT, IBatch::INF_BUFFER_BYTES_SIZE, IBatch::INF_BLOB_HEADER};
- UCHAR buffer[64];
- info(&s, rdb, op_info_batch, statement->rsr_id, 0,
- sizeof(items), items, 0, 0, sizeof(buffer), buffer);
- check(&s);
- // Extract from buffer
- ClumpletReader out(ClumpletReader::InfoResponse, buffer, sizeof(buffer));
- for (out.rewind(); !out.isEof(); out.moveNext())
- {
- UCHAR item = out.getClumpTag();
- if (item == isc_info_end)
- break;
- switch(item)
- {
- case IBatch::INF_BLOB_ALIGNMENT:
- statement->rsr_batch_stream.alignment = blobAlign = out.getInt();
- break;
- case IBatch::INF_BUFFER_BYTES_SIZE:
- serverSize = out.getInt();
- break;
- case IBatch::INF_BLOB_HEADER:
- blobHeadSize = out.getInt();
- break;
- case isc_info_error:
- (Arg::Gds(isc_batch_align) << Arg::Gds(out.getInt())).raise();
- case isc_info_truncated:
- (Arg::Gds(isc_batch_align) << Arg::Gds(isc_random) << "truncated").raise();
- default:
- {
- string msg;
- msg.printf("Wrong info item %u", item);
- (Arg::Gds(isc_batch_align) << Arg::Gds(isc_random) << msg).raise();
- }
- }
- }
- if (! (blobAlign && serverSize && blobHeadSize))
- Arg::Gds(isc_batch_align).raise();
- }
- IMessageMetadata* Batch::getMetadata(CheckStatusWrapper* status)
- {
- reset(status);
- format->addRef();
- return format;
- }
- void Batch::registerBlob(CheckStatusWrapper* status, const ISC_QUAD* existingBlob, ISC_QUAD* blobId)
- {
- try
- {
- // Check and validate handles, etc.
- if (!stmt)
- {
- Arg::Gds(isc_bad_req_handle).raise();
- }
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- if (blobPolicy == IBatch::BLOB_ID_ENGINE)
- genBlobId(blobId);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_batch_regblob;
- P_BATCH_REGBLOB* batch = &packet->p_batch_regblob;
- batch->p_batch_statement = statement->rsr_id;
- batch->p_batch_exist_id = *existingBlob;
- batch->p_batch_blob_id = *blobId;
- sendDeferredPacket(status, port, packet, true);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- IBatchCompletionState* Batch::execute(CheckStatusWrapper* status, ITransaction* apiTra)
- {
- try
- {
- // Check and validate handles, etc.
- if (!stmt)
- {
- Arg::Gds(isc_bad_req_handle).raise();
- }
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rtr* transaction = NULL;
- Transaction* rt = stmt->getAttachment()->remoteTransactionInterface(apiTra);
- if (rt)
- {
- transaction = rt->getTransaction();
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- }
- // Sanity checks complete - flash data in buffers
- flashBatch();
- // Prepare and send execute packet
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_batch_exec;
- P_BATCH_EXEC* batch = &packet->p_batch_exec;
- batch->p_batch_statement = statement->rsr_id;
- batch->p_batch_transaction = transaction->rtr_id;
- send_packet(port, packet);
- statement->rsr_batch_size = alignedSize;
- AutoPtr<BatchCompletionState, SimpleDispose>
- cs(FB_NEW BatchCompletionState(flags & (1 << IBatch::TAG_RECORD_COUNTS), 256));
- statement->rsr_batch_cs = cs;
- receive_packet(port, packet);
- statement->rsr_batch_cs = nullptr;
- if (packet->p_operation == op_batch_cs)
- {
- // when working with 4.0.0 server we could not raise it in advance...
- statement->clearException();
- return cs.release();
- }
- REMOTE_check_response(status, rdb, packet);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return nullptr;
- }
- void Batch::cancel(CheckStatusWrapper* status)
- {
- try
- {
- // Check and validate handles, etc.
- if (!stmt)
- {
- Arg::Gds(isc_dsql_cursor_err).raise();
- }
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Cleanup local data
- cleanup();
- batchActive = false;
- // Prepare packet
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_batch_cancel;
- P_BATCH_FREE_CANCEL* batch = &packet->p_batch_free_cancel;
- batch->p_batch_statement = statement->rsr_id;
- send_and_receive(status, rdb, packet);
- batchActive = false;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Batch::freeClientData(CheckStatusWrapper* status, bool force)
- {
- try
- {
- // Check and validate handles, etc.
- if (!stmt)
- {
- Arg::Gds(isc_dsql_cursor_err).raise();
- }
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_batch_rls;
- P_BATCH_FREE_CANCEL* batch = &packet->p_batch_free_cancel;
- batch->p_batch_statement = statement->rsr_id;
- if (rdb->rdb_port->port_flags & PORT_lazy)
- {
- defer_packet(rdb->rdb_port, packet);
- packet->p_resp.p_resp_object = statement->rsr_id;
- }
- else
- {
- try
- {
- send_and_receive(status, rdb, packet);
- }
- catch (const Exception&)
- {
- if (!force)
- throw;
- }
- }
- releaseStatement();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Batch::internalClose(CheckStatusWrapper* status)
- {
- reset(status);
- freeClientData(status);
- }
- void Batch::close(CheckStatusWrapper* status)
- {
- internalClose(status);
- if (status->isEmpty())
- release();
- }
- void Batch::deprecatedClose(CheckStatusWrapper* status)
- {
- internalClose(status);
- }
- void Batch::getInfo(CheckStatusWrapper* status, unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer)
- {
- try
- {
- ClumpletReader it(ClumpletReader::InfoItems, items, itemsLength);
- ClumpletWriter out(ClumpletReader::InfoResponse, bufferLength - 1); // place for isc_info_end / isc_info_truncated
- for (it.rewind(); !it.isEof(); it.moveNext())
- {
- UCHAR item = it.getClumpTag();
- if (item == isc_info_end)
- break;
- try
- {
- switch(item)
- {
- case IBatch::INF_BUFFER_BYTES_SIZE:
- setServerInfo();
- if (serverSize)
- out.insertInt(item, serverSize);
- break;
- case IBatch::INF_DATA_BYTES_SIZE:
- out.insertInt(item, (messageCount + messageStream) * alignedSize);
- break;
- case IBatch::INF_BLOBS_BYTES_SIZE:
- if (blobStream)
- out.insertInt(item, blobCount + (blobStream - blobStreamBuffer));
- break;
- case IBatch::INF_BLOB_ALIGNMENT:
- setServerInfo();
- out.insertInt(item, blobAlign);
- break;
- case IBatch::INF_BLOB_HEADER:
- setServerInfo();
- out.insertInt(item, blobHeadSize);
- break;
- default:
- out.insertInt(isc_info_error, isc_infunk);
- break;
- }
- }
- catch(const fatal_exception&)
- {
- // here it's sooner of all caused by writer overflow but anyway check that
- if (out.hasOverflow())
- {
- memcpy(buffer, out.getBuffer(), out.getBufferLength());
- buffer += out.getBufferLength();
- *buffer++ = isc_info_truncated;
- if (out.getBufferLength() <= bufferLength - 2)
- *buffer++ = isc_info_end;
- return;
- }
- else
- throw;
- }
- }
- memcpy(buffer, out.getBuffer(), out.getBufferLength());
- buffer += out.getBufferLength();
- *buffer++ = isc_info_end;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Batch::releaseStatement()
- {
- if (tmpStatement)
- {
- stmt->release();
- }
- stmt = NULL;
- }
- Replicator* Attachment::createReplicator(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * c r e a t e R e p l i c a t o r
- *
- **************************************
- *
- * Functional description
- * Create data replication interface.
- *
- **************************************/
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- if (port->port_protocol < PROTOCOL_VERSION16)
- unsupported();
- if (!replicator)
- replicator = FB_NEW Replicator(this);
- replicator->addRef();
- return replicator;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- void Replicator::process(CheckStatusWrapper* status, unsigned length, const unsigned char* data)
- {
- try
- {
- reset(status);
- Rdb* rdb = attachment->getRdb();
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- if (port->port_protocol < PROTOCOL_VERSION16)
- unsupported();
- // Validate data length
- CHECK_LENGTH(port, length);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_repl_data;
- P_REPLICATE* repl = &packet->p_replicate;
- repl->p_repl_database = rdb->rdb_id;
- repl->p_repl_data.cstr_length = length;
- repl->p_repl_data.cstr_address = data;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- send_and_receive(status, rdb, packet);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Replicator::internalClose(CheckStatusWrapper* status)
- {
- reset(status);
- freeClientData(status);
- }
- void Replicator::close(CheckStatusWrapper* status)
- {
- internalClose(status);
- if (status->isEmpty())
- release();
- }
- void Replicator::deprecatedClose(CheckStatusWrapper* status)
- {
- internalClose(status);
- }
- void Replicator::freeClientData(CheckStatusWrapper* status, bool force)
- {
- try
- {
- reset(status);
- if (attachment && attachment->replicator)
- {
- Rdb* rdb = attachment->getRdb();
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- if (port->port_protocol < PROTOCOL_VERSION16)
- unsupported();
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_repl_data;
- P_REPLICATE* repl = &packet->p_replicate;
- repl->p_repl_database = rdb->rdb_id;
- repl->p_repl_data.cstr_length = 0;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- try
- {
- send_and_receive(status, rdb, packet);
- }
- catch (const Exception&)
- {
- if (!force)
- throw;
- }
- attachment->replicator = NULL;
- }
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- ITransaction* Statement::execute(CheckStatusWrapper* status, ITransaction* apiTra,
- IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata* outMetadata, void* outBuffer)
- {
- /**************************************
- *
- * d s q l _ e x e c u t e 2
- *
- **************************************
- *
- * Functional description
- * Execute a non-SELECT dynamic SQL statement.
- *
- **************************************/
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- BlrFromMessage inBlr(inMetadata, dialect, port->port_protocol);
- const unsigned int in_blr_length = inBlr.getLength();
- const UCHAR* const in_blr = inBlr.getBytes();
- const unsigned int in_msg_length = inBlr.getMsgLength();
- UCHAR* const in_msg = static_cast<UCHAR*>(inBuffer);
- BlrFromMessage outBlr(outMetadata, dialect, port->port_protocol);
- const unsigned int out_blr_length = outBlr.getLength();
- const UCHAR* const out_blr = outBlr.getBytes();
- const unsigned int out_msg_length = outBlr.getMsgLength();
- UCHAR* const out_msg = static_cast<UCHAR*>(outBuffer);
- // Validate data length
- CHECK_LENGTH(port, in_blr_length);
- CHECK_LENGTH(port, in_msg_length);
- CHECK_LENGTH(port, out_blr_length);
- CHECK_LENGTH(port, out_msg_length);
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rtr* transaction = NULL;
- Transaction* rt = remAtt->remoteTransactionInterface(apiTra);
- if (rt)
- {
- transaction = rt->getTransaction();
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- }
- // 24-Mar-2004 Nickolay Samofatov
- // Unconditionally deallocate existing formats that are left from
- // previous executions (possibly with different statement if
- // isc_dsql_prepare is called multiple times).
- // This should cure SF#919246
- delete statement->rsr_bind_format;
- statement->rsr_bind_format = NULL;
- if (port->port_statement)
- {
- delete port->port_statement->rsr_select_format;
- port->port_statement->rsr_select_format = NULL;
- }
- // Parse the blr describing the message, if there is any.
- if (in_blr_length)
- statement->rsr_bind_format = PARSE_msg_format(in_blr, in_blr_length);
- // Parse the blr describing the output message. This is not the fetch
- // message! That comes later.
- if (out_blr_length)
- {
- if (!port->port_statement)
- port->port_statement = FB_NEW Rsr;
- port->port_statement->rsr_select_format = PARSE_msg_format(out_blr, out_blr_length);
- if (!port->port_statement->rsr_buffer)
- {
- RMessage* message2 = FB_NEW RMessage(0);
- port->port_statement->rsr_buffer = message2;
- port->port_statement->rsr_message = message2;
- message2->msg_next = message2;
- port->port_statement->rsr_fmt_length = 0;
- }
- }
- RMessage* message = NULL;
- if (!statement->rsr_buffer)
- {
- statement->rsr_buffer = message = FB_NEW RMessage(0);
- statement->rsr_message = message;
- message->msg_next = message;
- statement->rsr_fmt_length = 0;
- }
- else {
- message = statement->rsr_message = statement->rsr_buffer;
- }
- message->msg_address = const_cast<UCHAR*>(in_msg);
- statement->rsr_flags.clear(Rsr::FETCHED);
- statement->rsr_format = statement->rsr_bind_format;
- statement->clearException();
- // set up the packet for the other guy...
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = out_msg_length ? op_execute2 : op_execute;
- P_SQLDATA* sqldata = &packet->p_sqldata;
- sqldata->p_sqldata_statement = statement->rsr_id;
- sqldata->p_sqldata_transaction = transaction ? transaction->rtr_id : 0;
- sqldata->p_sqldata_blr.cstr_length = in_blr_length;
- sqldata->p_sqldata_blr.cstr_address = const_cast<UCHAR*>(in_blr); // safe, see protocol.cpp and server.cpp
- sqldata->p_sqldata_message_number = 0;
- sqldata->p_sqldata_messages = (statement->rsr_bind_format) ? 1 : 0;
- sqldata->p_sqldata_out_blr.cstr_length = out_blr_length;
- sqldata->p_sqldata_out_blr.cstr_address = const_cast<UCHAR*>(out_blr);
- sqldata->p_sqldata_out_message_number = 0; // out_msg_type
- sqldata->p_sqldata_timeout = statement->rsr_timeout;
- sqldata->p_sqldata_cursor_flags = 0;
- send_packet(port, packet);
- // Set up the response packet. We may receive an SQL response followed
- // by a normal response packet or simply a response packet.
- message->msg_address = NULL;
- if (out_msg_length)
- port->port_statement->rsr_message->msg_address = out_msg;
- receive_packet(port, packet);
- if (packet->p_operation != op_sql_response)
- REMOTE_check_response(status, rdb, packet);
- else
- {
- port->port_statement->rsr_message->msg_address = NULL;
- receive_response(status, rdb, packet);
- }
- if (transaction && !packet->p_resp.p_resp_object)
- {
- REMOTE_cleanup_transaction(transaction);
- release_transaction(transaction);
- transaction = NULL;
- rt->clear();
- statement->rsr_rtr = NULL;
- return NULL;
- }
- else if (!transaction && packet->p_resp.p_resp_object)
- {
- transaction = make_transaction(rdb, packet->p_resp.p_resp_object);
- statement->rsr_rtr = transaction;
- Transaction* newTrans = FB_NEW Transaction(transaction, remAtt);
- newTrans->addRef();
- return newTrans;
- }
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return apiTra;
- }
- ResultSet* Statement::openCursor(CheckStatusWrapper* status, Firebird::ITransaction* apiTra,
- IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata* outFormat, unsigned int flags)
- {
- /**************************************
- *
- * d s q l _ e x e c u t e 2
- *
- **************************************
- *
- * Functional description
- * Execute a non-SELECT dynamic SQL statement.
- *
- **************************************/
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- BlrFromMessage inBlr(inMetadata, dialect, port->port_protocol);
- const unsigned int in_blr_length = inBlr.getLength();
- const UCHAR* const in_blr = inBlr.getBytes();
- const unsigned int in_msg_length = inBlr.getMsgLength();
- UCHAR* const in_msg = static_cast<UCHAR*>(inBuffer);
- RefPtr<IMessageMetadata> defaultOutputFormat;
- if (!outFormat)
- {
- defaultOutputFormat.assignRefNoIncr(this->getOutputMetadata(status));
- if (status->getState() & Firebird::IStatus::STATE_ERRORS)
- {
- return NULL;
- }
- if (defaultOutputFormat)
- {
- outFormat = defaultOutputFormat;
- }
- }
- BlrFromMessage outBlr((outFormat == DELAYED_OUT_FORMAT ? NULL : outFormat), dialect, port->port_protocol);
- const unsigned int out_blr_length = outBlr.getLength();
- const UCHAR* const out_blr = outBlr.getBytes();
- // Validate data length
- CHECK_LENGTH(port, in_blr_length);
- CHECK_LENGTH(port, in_msg_length);
- CHECK_LENGTH(port, out_blr_length);
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rtr* transaction = NULL;
- Transaction* rt = remAtt->remoteTransactionInterface(apiTra);
- if (rt)
- {
- transaction = rt->getTransaction();
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- }
- // 24-Mar-2004 Nickolay Samofatov
- // Unconditionally deallocate existing formats that are left from
- // previous executions (possibly with different statement if
- // isc_dsql_prepare is called multiple times).
- // This should cure SF#919246
- delete statement->rsr_bind_format;
- statement->rsr_bind_format = NULL;
- if (port->port_statement)
- {
- delete port->port_statement->rsr_select_format;
- port->port_statement->rsr_select_format = NULL;
- }
- // Parse the blr describing the message, if there is any.
- if (in_blr_length)
- statement->rsr_bind_format = PARSE_msg_format(in_blr, in_blr_length);
- RMessage* message = NULL;
- if (!statement->rsr_buffer)
- {
- statement->rsr_buffer = message = FB_NEW RMessage(0);
- statement->rsr_message = message;
- message->msg_next = message;
- statement->rsr_fmt_length = 0;
- }
- else {
- message = statement->rsr_message = statement->rsr_buffer;
- }
- message->msg_address = const_cast<UCHAR*>(in_msg);
- statement->rsr_flags.clear(Rsr::FETCHED);
- statement->rsr_format = statement->rsr_bind_format;
- statement->clearException();
- // set up the packet for the other guy...
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_execute;
- P_SQLDATA* sqldata = &packet->p_sqldata;
- sqldata->p_sqldata_statement = statement->rsr_id;
- sqldata->p_sqldata_transaction = transaction ? transaction->rtr_id : 0;
- sqldata->p_sqldata_blr.cstr_length = in_blr_length;
- sqldata->p_sqldata_blr.cstr_address = const_cast<UCHAR*>(in_blr); // safe, see protocol.cpp and server.cpp
- sqldata->p_sqldata_message_number = 0;
- sqldata->p_sqldata_messages = (statement->rsr_bind_format) ? 1 : 0;
- sqldata->p_sqldata_out_blr.cstr_length = out_blr_length;
- sqldata->p_sqldata_out_blr.cstr_address = const_cast<UCHAR*>(out_blr);
- sqldata->p_sqldata_out_message_number = 0; // out_msg_type
- sqldata->p_sqldata_timeout = statement->rsr_timeout;
- sqldata->p_sqldata_cursor_flags = flags;
- {
- Firebird::Cleanup msgClean([&message] {
- message->msg_address = NULL;
- });
- if (statement->rsr_flags.test(Rsr::DEFER_EXECUTE))
- {
- send_partial_packet(port, packet);
- defer_packet(port, packet, true);
- }
- else
- {
- send_and_receive(status, rdb, packet);
- }
- }
- ResultSet* rs = FB_NEW ResultSet(this, outFormat, flags);
- rs->addRef();
- return rs;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- IResultSet* Attachment::openCursor(CheckStatusWrapper* status, ITransaction* transaction,
- unsigned int stmtLength, const char* sqlStmt, unsigned dialect,
- IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata* outMetadata,
- const char* cursorName, unsigned int cursorFlags)
- {
- Statement* stmt = prepare(status, transaction, stmtLength, sqlStmt, dialect,
- (outMetadata ? 0 : IStatement::PREPARE_PREFETCH_OUTPUT_PARAMETERS));
- if (status->getState() & Firebird::IStatus::STATE_ERRORS)
- {
- return NULL;
- }
- ResultSet* rc = stmt->openCursor(status, transaction, inMetadata, inBuffer, outMetadata, cursorFlags);
- if (status->getState() & Firebird::IStatus::STATE_ERRORS)
- {
- stmt->release();
- return NULL;
- }
- if (cursorName)
- {
- stmt->setCursorName(status, cursorName);
- if (status->getState() & Firebird::IStatus::STATE_ERRORS)
- {
- rc->release();
- stmt->release();
- return NULL;
- }
- }
- rc->tmpStatement = true;
- return rc;
- }
- ITransaction* Attachment::execute(CheckStatusWrapper* status, ITransaction* apiTra,
- unsigned int stmtLength, const char* sqlStmt, unsigned int dialect,
- IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata* outMetadata, void* outBuffer)
- {
- /**************************************
- *
- * d s q l _ e x e c u t e _ i m m e d i a t e 2
- *
- **************************************
- *
- * Functional description
- * Prepare and execute a statement.
- *
- **************************************/
- try
- {
- // Check and validate handles, etc.
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- BlrFromMessage inBlr(inMetadata, dialect, port->port_protocol);
- const unsigned int in_blr_length = inBlr.getLength();
- const UCHAR* const in_blr = inBlr.getBytes();
- const unsigned int in_msg_length = inBlr.getMsgLength();
- UCHAR* const in_msg = static_cast<UCHAR*>(inBuffer);
- BlrFromMessage outBlr(outMetadata, dialect, port->port_protocol);
- const unsigned int out_blr_length = outBlr.getLength();
- const UCHAR* const out_blr = outBlr.getBytes();
- const unsigned int out_msg_length = outBlr.getMsgLength();
- UCHAR* const out_msg = static_cast<UCHAR*>(outBuffer);
- // Validate data length
- CHECK_LENGTH(port, in_blr_length);
- CHECK_LENGTH(port, in_msg_length);
- CHECK_LENGTH(port, out_blr_length);
- CHECK_LENGTH(port, out_msg_length);
- if (sqlStmt && !stmtLength)
- stmtLength = static_cast<ULONG>(strlen(sqlStmt));
- // Validate string length
- CHECK_LENGTH(port, stmtLength);
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rtr* transaction = NULL;
- Transaction* rt = remoteTransactionInterface(apiTra);
- if (rt)
- {
- transaction = rt->getTransaction();
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- }
- if (dialect > 10)
- {
- // dimitr: adjust dialect received after
- // a multi-hop transmission to be
- // redirected in its original value.
- dialect /= 10;
- }
- reset(status);
- Rsr* statement = port->port_statement;
- if (!statement) {
- statement = port->port_statement = FB_NEW Rsr;
- }
- // reset statement buffers
- clear_queue(rdb->rdb_port);
- REMOTE_reset_statement(statement);
- delete statement->rsr_bind_format;
- statement->rsr_bind_format = NULL;
- delete statement->rsr_select_format;
- statement->rsr_select_format = NULL;
- if (in_msg_length || out_msg_length)
- {
- if (in_blr_length)
- statement->rsr_bind_format = PARSE_msg_format(in_blr, in_blr_length);
- if (out_blr_length)
- statement->rsr_select_format = PARSE_msg_format(out_blr, out_blr_length);
- }
- RMessage* message = 0;
- if (!statement->rsr_buffer)
- {
- statement->rsr_buffer = message = FB_NEW RMessage(0);
- statement->rsr_message = message;
- message->msg_next = message;
- statement->rsr_fmt_length = 0;
- }
- else {
- message = statement->rsr_message = statement->rsr_buffer;
- }
- message->msg_address = const_cast<UCHAR*>(in_msg);
- statement->clearException();
- // set up the packet for the other guy...
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = (in_msg_length || out_msg_length) ?
- op_exec_immediate2 : op_exec_immediate;
- P_SQLST* ex_now = &packet->p_sqlst;
- ex_now->p_sqlst_transaction = transaction ? transaction->rtr_id : 0;
- ex_now->p_sqlst_SQL_dialect = dialect;
- ex_now->p_sqlst_SQL_str.cstr_length = stmtLength;
- ex_now->p_sqlst_SQL_str.cstr_address = reinterpret_cast<const UCHAR*>(sqlStmt);
- ex_now->p_sqlst_items.cstr_length = 0;
- ex_now->p_sqlst_buffer_length = 0;
- ex_now->p_sqlst_blr.cstr_length = in_blr_length;
- ex_now->p_sqlst_blr.cstr_address = const_cast<UCHAR*>(in_blr);
- ex_now->p_sqlst_message_number = 0;
- ex_now->p_sqlst_messages = (in_msg_length && statement->rsr_bind_format) ? 1 : 0;
- ex_now->p_sqlst_out_blr.cstr_length = out_blr_length;
- ex_now->p_sqlst_out_blr.cstr_address = const_cast<unsigned char*>(out_blr);
- ex_now->p_sqlst_out_message_number = 0; // out_msg_type
- send_packet(port, packet);
- // SEND could have changed the message
- message = statement->rsr_message;
- // Set up the response packet. We may receive an SQL response followed
- // by a normal response packet or simply a response packet.
- if (in_msg_length || out_msg_length)
- port->port_statement->rsr_message->msg_address = out_msg;
- receive_packet(rdb->rdb_port, packet);
- if (packet->p_operation != op_sql_response)
- REMOTE_check_response(status, rdb, packet);
- else
- {
- message->msg_address = NULL;
- receive_response(status, rdb, packet);
- }
- if (transaction && !packet->p_resp.p_resp_object)
- {
- REMOTE_cleanup_transaction(transaction);
- release_transaction(transaction);
- transaction = NULL;
- rt->clear();
- return NULL;
- }
- else if (!transaction && packet->p_resp.p_resp_object)
- {
- transaction = make_transaction(rdb, packet->p_resp.p_resp_object);
- Firebird::ITransaction* newTrans = FB_NEW Transaction(transaction, this);
- newTrans->addRef();
- return newTrans;
- }
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return apiTra;
- }
- void Statement::freeClientData(CheckStatusWrapper* status, bool force)
- {
- /**************************************
- *
- * d s q l _ f r e e _ s t a t e m e n t
- *
- **************************************
- *
- * Functional description
- * Release request for a Dynamic SQL statement
- *
- **************************************/
- try
- {
- // Check and validate handles, etc.
- if (!statement)
- {
- return;
- }
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- fb_assert(statement->haveException() == 0);
- statement->clearException();
- if (statement->rsr_flags.test(Rsr::LAZY))
- {
- release_sql_request(statement);
- statement = NULL;
- return;
- }
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_free_statement;
- P_SQLFREE* free_stmt = &packet->p_sqlfree;
- free_stmt->p_sqlfree_statement = statement->rsr_id;
- free_stmt->p_sqlfree_option = DSQL_drop;
- if (rdb->rdb_port->port_flags & PORT_lazy)
- {
- send_packet(rdb->rdb_port, packet);
- defer_packet(rdb->rdb_port, packet, true);
- packet->p_resp.p_resp_object = statement->rsr_id;
- }
- else
- {
- try
- {
- send_and_receive(status, rdb, packet);
- }
- catch (const Exception&)
- {
- if (!force)
- throw;
- }
- }
- if (packet->p_resp.p_resp_object == INVALID_OBJECT)
- {
- release_sql_request(statement);
- }
- else
- {
- statement->rsr_flags.clear(Rsr::FETCHED);
- statement->rsr_rtr = NULL;
- clear_queue(rdb->rdb_port);
- REMOTE_reset_statement(statement);
- }
- statement = NULL;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Statement::internalFree(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * d s q l _ f r e e _ s t a t e m e n t
- *
- **************************************
- *
- * Functional description
- * Release request for a Dynamic SQL statement
- *
- **************************************/
- reset(status);
- freeClientData(status);
- }
- void Statement::free(CheckStatusWrapper* status)
- {
- internalFree(status);
- if (status->isEmpty())
- release();
- }
- void Statement::deprecatedFree(CheckStatusWrapper* status)
- {
- internalFree(status);
- }
- Statement* Attachment::createStatement(CheckStatusWrapper* status, unsigned dialect)
- {
- reset(status);
- Rsr* statement = NULL;
- if (rdb->rdb_port->port_flags & PORT_lazy)
- {
- statement = FB_NEW Rsr;
- statement->rsr_rdb = rdb;
- statement->rsr_id = INVALID_OBJECT;
- statement->rsr_flags.set(Rsr::LAZY);
- }
- else
- {
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_allocate_statement;
- packet->p_rlse.p_rlse_object = rdb->rdb_id;
- send_and_receive(status, rdb, packet);
- // Allocate SQL request block
- statement = FB_NEW Rsr;
- statement->rsr_rdb = rdb;
- statement->rsr_id = packet->p_resp.p_resp_object;
- // register the object
- SET_OBJECT(rdb, statement, statement->rsr_id);
- }
- statement->rsr_next = rdb->rdb_sql_requests;
- rdb->rdb_sql_requests = statement;
- Statement* s = FB_NEW Statement(statement, this, dialect);
- s->addRef();
- return s;
- }
- Statement* Attachment::prepare(CheckStatusWrapper* status, ITransaction* apiTra,
- unsigned int stmtLength, const char* sqlStmt, unsigned int dialect, unsigned int flags)
- {
- /**************************************
- *
- * d s q l _ p r e p a r e
- *
- **************************************
- *
- * Functional description
- * Prepare a dynamic SQL statement for execution.
- *
- **************************************/
- Statement* stmt = NULL;
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rtr* transaction = NULL;
- if (apiTra)
- {
- transaction = remoteTransaction(apiTra);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- }
- if (sqlStmt && !stmtLength)
- stmtLength = static_cast<ULONG>(strlen(sqlStmt));
- // Validate string length
- CHECK_LENGTH(port, stmtLength);
- if (dialect > 10)
- {
- // dimitr: adjust dialect received after
- // a multi-hop transmission to be
- // redirected in its original value.
- dialect /= 10;
- }
- // create new statement
- stmt = createStatement(status, dialect);
- Rsr* statement = stmt->getStatement();
- // reset current statement
- clear_queue(rdb->rdb_port);
- REMOTE_reset_statement(statement);
- // set up the packet for the other guy...
- PACKET* packet = &rdb->rdb_packet;
- if (statement->rsr_flags.test(Rsr::LAZY))
- {
- packet->p_operation = op_allocate_statement;
- packet->p_rlse.p_rlse_object = rdb->rdb_id;
- send_partial_packet(rdb->rdb_port, packet);
- }
- Array<UCHAR> items, buffer;
- buffer.resize(StatementMetadata::buildInfoItems(items, flags));
- // Validate data length
- CHECK_LENGTH(port, items.getCount());
- CHECK_LENGTH(port, buffer.getCount());
- packet->p_operation = op_prepare_statement;
- P_SQLST* prepare = &packet->p_sqlst;
- prepare->p_sqlst_transaction = transaction ? transaction->rtr_id : 0;
- prepare->p_sqlst_statement = statement->rsr_id;
- prepare->p_sqlst_SQL_dialect = dialect;
- prepare->p_sqlst_SQL_str.cstr_length = stmtLength;
- prepare->p_sqlst_SQL_str.cstr_address = reinterpret_cast<const UCHAR*>(sqlStmt);
- prepare->p_sqlst_items.cstr_length = (ULONG) items.getCount();
- prepare->p_sqlst_items.cstr_address = items.begin();
- prepare->p_sqlst_buffer_length = (ULONG) buffer.getCount();
- send_packet(rdb->rdb_port, packet);
- statement->rsr_flags.clear(Rsr::DEFER_EXECUTE);
- // Set up for the response packet.
- if (statement->rsr_flags.test(Rsr::LAZY))
- {
- receive_response(status, rdb, packet);
- statement->rsr_id = packet->p_resp.p_resp_object;
- SET_OBJECT(rdb, statement, statement->rsr_id);
- statement->rsr_flags.clear(Rsr::LAZY);
- }
- P_RESP* response = &packet->p_resp;
- SaveString temp(response->p_resp_data, buffer.getCount(), buffer.begin());
- try
- {
- receive_response(status, rdb, packet);
- stmt->parseMetadata(buffer);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- if (rdb->rdb_port->port_flags & PORT_lazy)
- {
- if (response->p_resp_object & STMT_DEFER_EXECUTE) {
- statement->rsr_flags.set(Rsr::DEFER_EXECUTE);
- }
- }
- else
- {
- fb_assert(!response->p_resp_object);
- response->p_resp_object = 0;
- }
- if (!(status->getState() & Firebird::IStatus::STATE_ERRORS))
- {
- return stmt;
- }
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- // free statement in case of error
- if (stmt)
- {
- stmt->release();
- }
- return NULL;
- }
- void Statement::getInfo(CheckStatusWrapper* status,
- unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer)
- {
- /**************************************
- *
- * d s q l _ s q l _ i n f o
- *
- **************************************
- *
- * Functional description
- * Provide information on sql object.
- *
- **************************************/
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- statement->raiseException();
- if (!metadata.fillFromCache(itemsLength, items, bufferLength, buffer))
- {
- info(status, rdb, op_info_sql, statement->rsr_id, 0,
- itemsLength, items, 0, 0, bufferLength, buffer);
- metadata.parse(bufferLength, buffer);
- }
- statement->raiseException();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- unsigned Statement::getType(CheckStatusWrapper* status)
- {
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- statement->raiseException();
- return metadata.getType();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return 0;
- }
- unsigned Statement::getFlags(CheckStatusWrapper* status)
- {
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- statement->raiseException();
- if (port->port_protocol >= PROTOCOL_VERSION13)
- {
- // we are in luck - use flags from server
- return metadata.getFlags();
- }
- // Need to guess flags based on statement type
- unsigned value = IStatement::FLAG_REPEAT_EXECUTE;
- switch (metadata.getType())
- {
- case isc_info_sql_stmt_ddl:
- value &= ~IStatement::FLAG_REPEAT_EXECUTE;
- break;
- case isc_info_sql_stmt_select:
- case isc_info_sql_stmt_select_for_upd:
- value |= IStatement::FLAG_HAS_CURSOR;
- break;
- }
- return value;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return 0;
- }
- const char* Statement::getPlan(CheckStatusWrapper* status, FB_BOOLEAN detailed)
- {
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- statement->raiseException();
- return metadata.getPlan(detailed);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- IMessageMetadata* Statement::getInputMetadata(CheckStatusWrapper* status)
- {
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- statement->raiseException();
- return metadata.getInputMetadata();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- IMessageMetadata* Statement::getOutputMetadata(CheckStatusWrapper* status)
- {
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- statement->raiseException();
- return metadata.getOutputMetadata();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- ISC_UINT64 Statement::getAffectedRecords(CheckStatusWrapper* status)
- {
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- statement->raiseException();
- return metadata.getAffectedRecords();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return 0;
- }
- void Statement::setCursorName(CheckStatusWrapper* status, const char* cursor)
- {
- /*****************************************
- *
- * d s q l _ s e t _ c u r s o r
- *
- *****************************************
- *
- * Functional Description
- * Declare a cursor for a dynamic request.
- *
- * Note: prior to version 6.0, this function terminated the
- * cursor name at the first blank. With delimited cursor
- * name support that is no longer sufficient. We now pass
- * the entire NULL-Terminated cursor name to the server, and let
- * the server deal with blank termination or not.
- * NOTE: THIS NOW MEANS THAT IF CURSOR is NOT null terminated
- * we will have inconsistant results with version 5.x. The only
- * "normal" way this happens is if this API is called from a
- * non-C host language. If that results in a later problem we
- * must provide a new API that takes a "cursor_name_length"
- * parameter.
- *
- *****************************************/
- try
- {
- reset(status);
- // Check and validate handles, etc.
- Rsr* statement = getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- statement->raiseException();
- // set up the packet for the other guy...
- PACKET* packet = &rdb->rdb_packet;
- if (statement->rsr_flags.test(Rsr::LAZY))
- {
- packet->p_operation = op_allocate_statement;
- packet->p_rlse.p_rlse_object = rdb->rdb_id;
- send_partial_packet(rdb->rdb_port, packet);
- }
- packet->p_operation = op_set_cursor;
- P_SQLCUR* sqlcur = &packet->p_sqlcur;
- sqlcur->p_sqlcur_statement = statement->rsr_id;
- const ULONG name_l = static_cast<ULONG>(strlen(cursor));
- sqlcur->p_sqlcur_cursor_name.cstr_length = name_l + 1;
- sqlcur->p_sqlcur_cursor_name.cstr_address = reinterpret_cast<const UCHAR*>(cursor);
- sqlcur->p_sqlcur_type = 0; // type
- send_packet(port, packet);
- if (statement->rsr_flags.test(Rsr::LAZY))
- {
- receive_response(status, rdb, packet);
- statement->rsr_id = packet->p_resp.p_resp_object;
- SET_OBJECT(rdb, statement, statement->rsr_id);
- statement->rsr_flags.clear(Rsr::LAZY);
- }
- receive_response(status, rdb, packet);
- statement->raiseException();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void ResultSet::setDelayedOutputFormat(CheckStatusWrapper* status, IMessageMetadata* format)
- {
- try
- {
- reset(status);
- // Check and validate handles, etc.
- if (!delayedFormat)
- {
- (Arg::Gds(isc_dsql_cursor_err) << Arg::Gds(isc_bad_req_handle)).raise();
- }
- outputFormat = format;
- delayedFormat = false;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- bool ResultSet::fetch(CheckStatusWrapper* status, void* buffer, P_FETCH operation, int position)
- {
- /**************************************
- *
- * d s q l _ f e t c h
- *
- **************************************
- *
- * Functional description
- * Fetch next record from a dynamic SQL cursor.
- *
- **************************************/
- reset(status);
- // Check and validate handles, etc.
- if (delayedFormat || !stmt)
- {
- (Arg::Gds(isc_dsql_cursor_err) << Arg::Gds(isc_bad_req_handle)).raise();
- }
- Rsr* const statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* const rdb = statement->rsr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* const port = rdb->rdb_port;
- // Scrolling is not available in older protocols
- if (operation != fetch_next && port->port_protocol < PROTOCOL_FETCH_SCROLL)
- unsupported();
- // Whether we're fetching relatively to the current position
- const bool relative =
- (operation == fetch_next || operation == fetch_prior || operation == fetch_relative);
- BlrFromMessage outBlr(outputFormat, stmt->getDialect(), port->port_protocol);
- unsigned int blr_length = outBlr.getLength();
- const UCHAR* blr = outBlr.getBytes();
- const unsigned int msg_length = outBlr.getMsgLength();
- UCHAR* msg = static_cast<UCHAR*>(buffer);
- // Validate data length
- CHECK_LENGTH(port, blr_length);
- CHECK_LENGTH(port, msg_length);
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- if (!statement->rsr_flags.test(Rsr::FETCHED))
- {
- // On first fetch, clear the end-of-stream flag & reset the message buffers
- statement->raiseException();
- statement->rsr_flags.clear(Rsr::STREAM_END | Rsr::PAST_END | Rsr::STREAM_ERR);
- statement->rsr_rows_pending = 0;
- statement->rsr_fetch_operation = operation;
- statement->rsr_fetch_position = position;
- statement->clearException();
- RMessage* message = statement->rsr_message;
- if (message)
- {
- statement->rsr_buffer = message;
- while (true)
- {
- message->msg_address = NULL;
- message = message->msg_next;
- if (message == statement->rsr_message)
- break;
- }
- }
- }
- else if (!relative)
- {
- // Clear the end-of-stream flag if the fetch is positioned absolutely
- statement->rsr_flags.clear(Rsr::STREAM_END | Rsr::PAST_END);
- }
- else if (statement->rsr_flags.test(Rsr::PAST_END))
- {
- // If we're already at BOF/EOF and the requested fetch operation
- // cannot change our position, just do nothing
- if (operation == fetch_relative && position == 0)
- return false;
- if ((operation == fetch_next || (operation == fetch_relative && position > 0)) &&
- statement->rsr_flags.test(Rsr::PAST_EOF))
- {
- return false;
- }
- if ((operation == fetch_prior || (operation == fetch_relative && position < 0)) &&
- statement->rsr_flags.test(Rsr::PAST_BOF))
- {
- return false;
- }
- }
- // Parse the blr describing the message, if there is any.
- if (blr_length)
- {
- if (statement->rsr_user_select_format &&
- statement->rsr_user_select_format != statement->rsr_select_format)
- {
- delete statement->rsr_user_select_format;
- }
- statement->rsr_user_select_format = PARSE_msg_format(blr, blr_length);
- if (statement->rsr_flags.test(Rsr::FETCHED))
- blr_length = 0;
- else
- {
- delete statement->rsr_select_format;
- statement->rsr_select_format = statement->rsr_user_select_format;
- }
- }
- if (!statement->rsr_buffer)
- {
- statement->rsr_buffer = FB_NEW RMessage(0);
- statement->rsr_message = statement->rsr_buffer;
- statement->rsr_message->msg_next = statement->rsr_message;
- statement->rsr_fmt_length = 0;
- }
- RMessage* message = statement->rsr_message;
- #ifdef DEBUG
- fprintf(stdout, "Rows Pending in REM_fetch=%lu\n", statement->rsr_rows_pending);
- #endif
- // If the fetch direction was changed, we don't need the batched rows anymore.
- // Swallow them and reset the stream for subsequent fetches.
- if (operation != statement->rsr_fetch_operation ||
- position != statement->rsr_fetch_position)
- {
- while (statement->rsr_rows_pending)
- receive_queued_packet(port, statement->rsr_id);
- if (statement->rsr_flags.test(Rsr::STREAM_ERR))
- {
- statement->rsr_flags.clear(Rsr::STREAM_ERR);
- // hvlad: prevent subsequent fetches
- statement->rsr_flags.set(Rsr::STREAM_END);
- statement->raiseException();
- }
- const SLONG adjustment = statement->getCursorAdjustment();
- statement->rsr_flags.clear(Rsr::STREAM_END | Rsr::PAST_END);
- // We have some messages in the queue. Reset them for reuse.
- if (statement->rsr_msgs_waiting)
- {
- fb_assert(statement->rsr_fetch_operation == fetch_next ||
- statement->rsr_fetch_operation == fetch_prior);
- RMessage* message = statement->rsr_message;
- if (message)
- {
- statement->rsr_buffer = message;
- while (true)
- {
- message->msg_address = NULL;
- message = message->msg_next;
- if (message == statement->rsr_message)
- break;
- }
- }
- statement->rsr_msgs_waiting = 0;
- }
- // If we had some rows batched and the requested scrolling is relative,
- // then move the server cursor to the actual client's position before proceeding.
- // We don't know the absolute client's position, but it's not really necessary.
- // rsr_msgs_waiting shows how much we're ahead the server, so we may re-position
- // the cursor relatively.
- if (relative && adjustment)
- {
- const bool isAhead = (statement->rsr_fetch_operation == fetch_next);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_fetch_scroll;
- P_SQLDATA* sqldata = &packet->p_sqldata;
- sqldata->p_sqldata_statement = statement->rsr_id;
- sqldata->p_sqldata_blr.cstr_length = 0;
- sqldata->p_sqldata_blr.cstr_address = nullptr;
- sqldata->p_sqldata_message_number = 0; // msg_type
- sqldata->p_sqldata_messages = statement->rsr_select_format ? 1 : 0;
- sqldata->p_sqldata_fetch_op = fetch_relative;
- sqldata->p_sqldata_fetch_pos = adjustment;
- send_packet(port, packet);
- // Receive response packets. If everything is OK, there should be two of them:
- // first with packet->p_sqldata.p_sqldata_messages == 1 and second with
- // packet->p_sqldata.p_sqldata_messages == 0 (end-of-batch).
- do
- {
- receive_packet(rdb->rdb_port, packet);
- // If we get an error, handle it
- if (packet->p_operation != op_fetch_response)
- {
- statement->rsr_flags.set(Rsr::STREAM_ERR);
- REMOTE_check_response(status, rdb, packet);
- break;
- }
- // If we get end-of-stream, something went seriously wrong, thus punt
- if (packet->p_sqldata.p_sqldata_status == 100)
- Arg::Gds(isc_req_sync).raise();
- // We should get either the requested row or the end-of-batch marker
- fb_assert(packet->p_sqldata.p_sqldata_messages == 0 ||
- packet->p_sqldata.p_sqldata_messages == 1);
- // Release the received message, we don't need it
- const auto message = statement->rsr_message;
- if (message && message->msg_address)
- {
- statement->rsr_message = message->msg_next;
- message->msg_address = NULL;
- }
- }
- while (packet->p_sqldata.p_sqldata_messages);
- }
- // These are the necessary conditions to continue fetching (see below)
- fb_assert(!statement->rsr_flags.test(Rsr::STREAM_END | Rsr::STREAM_ERR));
- fb_assert(!statement->rsr_message->msg_address);
- fb_assert(!statement->rsr_rows_pending);
- }
- // Check to see if data is waiting. If not, solicite data.
- if ((!statement->rsr_flags.test(Rsr::STREAM_END | Rsr::STREAM_ERR) &&
- !statement->rsr_message->msg_address && !statement->rsr_rows_pending) ||
- ( // Low in inventory
- (statement->rsr_rows_pending <= statement->rsr_reorder_level) &&
- (statement->rsr_msgs_waiting <= statement->rsr_reorder_level) &&
- // Pipelining causes both server & client to
- // write at the same time. In XNET, writes
- // block for the other end to read - and so when both
- // attempt to write simultaneously, they end up
- // waiting indefinitely for the other end to read.
- (port->port_type != rem_port::XNET) &&
- // We're fetching either forward or backward
- (operation == fetch_next || operation == fetch_prior) &&
- // We've reached end-of-stream or there was an error
- !statement->rsr_flags.test(Rsr::STREAM_END | Rsr::STREAM_ERR) &&
- // No error pending
- !statement->haveException() ))
- {
- // set up the packet for the other guy...
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = (operation == fetch_next) ? op_fetch : op_fetch_scroll;
- P_SQLDATA* sqldata = &packet->p_sqldata;
- sqldata->p_sqldata_statement = statement->rsr_id;
- sqldata->p_sqldata_blr.cstr_length = blr_length;
- sqldata->p_sqldata_blr.cstr_address = const_cast<unsigned char*>(blr);
- sqldata->p_sqldata_message_number = 0; // msg_type
- sqldata->p_sqldata_messages = statement->rsr_select_format ? 1 : 0;
- sqldata->p_sqldata_fetch_op = operation;
- sqldata->p_sqldata_fetch_pos = position;
- if (statement->rsr_select_format)
- {
- if (operation == fetch_next || operation == fetch_prior)
- {
- sqldata->p_sqldata_messages = REMOTE_compute_batch_size(
- port, 0, op_fetch_response, statement->rsr_select_format);
- }
- // Reorder data when the local buffer is half empty
- statement->rsr_reorder_level = sqldata->p_sqldata_messages / 2;
- #ifdef DEBUG
- fprintf(stdout, "Recalculating Rows Pending in REM_fetch=%lu\n",
- statement->rsr_rows_pending);
- #endif
- }
- statement->rsr_rows_pending += sqldata->p_sqldata_messages;
- // We've either got data, or some is on the way, or we have an error, or we have EOF
- if (!(statement->rsr_msgs_waiting ||
- statement->rsr_rows_pending ||
- statement->haveException() ||
- statement->rsr_flags.test(Rsr::STREAM_END)))
- {
- // We were asked to fetch from the statement, not ready for it.
- // Give up before sending something to the server.
- Arg::Gds(isc_req_sync).raise();
- }
- // Make the batch request - and force the packet over the wire
- send_packet(port, packet);
- statement->rsr_batch_count++;
- statement->rsr_fetch_operation = operation;
- statement->rsr_fetch_position = position;
- // Queue up receipt of the pending data
- enqueue_receive(port, batch_dsql_fetch, rdb, statement, NULL);
- fb_assert(statement->rsr_rows_pending || !statement->rsr_select_format);
- }
- // Receive queued responses until we have some data for this cursor
- // or an error status has been received.
- // We've either got data, or some is on the way, or we have an error, or we have EOF
- fb_assert(statement->rsr_msgs_waiting || statement->rsr_rows_pending ||
- statement->haveException() || statement->rsr_flags.test(Rsr::STREAM_END));
- while (!statement->haveException() && // received a database error
- !statement->rsr_flags.test(Rsr::STREAM_END) && // reached end of stream
- statement->rsr_msgs_waiting < 2 && // Have looked ahead for end of batch
- statement->rsr_rows_pending)
- {
- // Hit end of batch
- receive_queued_packet(port, statement->rsr_id);
- }
- if (!statement->rsr_msgs_waiting)
- {
- if (statement->rsr_flags.test(Rsr::STREAM_END))
- {
- // hvlad: we may have queued fetch packet but received end-of-stream before start
- // handling of this packet. Handle it now.
- clear_stmt_que(port, statement);
- // hvlad: as we processed all queued packets at code above we can leave Rsr::EOF_SET flag.
- // It allows us to return EOF for all subsequent isc_dsql_fetch calls until statement
- // will be re-executed (and without roundtrip to remote server).
- //statement->rsr_flags.clear(Rsr::STREAM_END);
- if (statement->rsr_flags.test(Rsr::BOF_SET))
- statement->rsr_flags.set(Rsr::PAST_BOF);
- if (statement->rsr_flags.test(Rsr::EOF_SET))
- statement->rsr_flags.set(Rsr::PAST_EOF);
- return false;
- }
- if (statement->rsr_flags.test(Rsr::STREAM_ERR))
- {
- // The previous batch of receives ended with an error status.
- // We're all done returning data in the local queue.
- // Return that error status vector to the user.
- // Stuff in the error result to the user's vector
- statement->rsr_flags.clear(Rsr::STREAM_ERR);
- // hvlad: prevent subsequent fetches
- statement->rsr_flags.set(Rsr::STREAM_END);
- statement->raiseException();
- }
- }
- statement->rsr_msgs_waiting--;
- message = statement->rsr_message;
- statement->rsr_message = message->msg_next;
- if (statement->rsr_user_select_format->fmt_length != msg_length)
- {
- status_exception::raise(Arg::Gds(isc_port_len) <<
- Arg::Num(msg_length) << Arg::Num(statement->rsr_user_select_format->fmt_length));
- }
- if (statement->rsr_user_select_format == statement->rsr_select_format)
- {
- if (!msg || !message->msg_address)
- {
- move_error(Arg::Gds(isc_dsql_sqlda_err));
- // Msg 263 SQLDA missing or wrong number of variables
- }
- memcpy(msg, message->msg_address, msg_length);
- }
- else
- {
- mov_dsql_message(message->msg_address, statement->rsr_select_format, msg,
- statement->rsr_user_select_format);
- }
- message->msg_address = NULL;
- return true;
- }
- int ResultSet::fetchNext(CheckStatusWrapper* user_status, void* buffer)
- {
- try
- {
- return fetch(user_status, buffer, fetch_next) ?
- IStatus::RESULT_OK : IStatus::RESULT_NO_DATA;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(user_status);
- }
- return IStatus::RESULT_ERROR;
- }
- int ResultSet::fetchPrior(CheckStatusWrapper* user_status, void* buffer)
- {
- try
- {
- if (!(flags & IStatement::CURSOR_TYPE_SCROLLABLE))
- (Arg::Gds(isc_invalid_fetch_option) << Arg::Str("PRIOR")).raise();
- return fetch(user_status, buffer, fetch_prior) ?
- IStatus::RESULT_OK : IStatus::RESULT_NO_DATA;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(user_status);
- }
- return IStatus::RESULT_ERROR;
- }
- int ResultSet::fetchFirst(CheckStatusWrapper* user_status, void* buffer)
- {
- try
- {
- if (!(flags & IStatement::CURSOR_TYPE_SCROLLABLE))
- (Arg::Gds(isc_invalid_fetch_option) << Arg::Str("FIRST")).raise();
- return fetch(user_status, buffer, fetch_first) ?
- IStatus::RESULT_OK : IStatus::RESULT_NO_DATA;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(user_status);
- }
- return IStatus::RESULT_ERROR;
- }
- int ResultSet::fetchLast(CheckStatusWrapper* user_status, void* buffer)
- {
- try
- {
- if (!(flags & IStatement::CURSOR_TYPE_SCROLLABLE))
- (Arg::Gds(isc_invalid_fetch_option) << Arg::Str("LAST")).raise();
- return fetch(user_status, buffer, fetch_last) ?
- IStatus::RESULT_OK : IStatus::RESULT_NO_DATA;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(user_status);
- }
- return IStatus::RESULT_ERROR;
- }
- int ResultSet::fetchAbsolute(CheckStatusWrapper* user_status, int position, void* buffer)
- {
- try
- {
- if (!(flags & IStatement::CURSOR_TYPE_SCROLLABLE))
- (Arg::Gds(isc_invalid_fetch_option) << Arg::Str("ABSOLUTE")).raise();
- return fetch(user_status, buffer, fetch_absolute, position) ?
- IStatus::RESULT_OK : IStatus::RESULT_NO_DATA;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(user_status);
- }
- return IStatus::RESULT_ERROR;
- }
- int ResultSet::fetchRelative(CheckStatusWrapper* user_status, int offset, void* buffer)
- {
- try
- {
- if (!(flags & IStatement::CURSOR_TYPE_SCROLLABLE))
- (Arg::Gds(isc_invalid_fetch_option) << Arg::Str("RELATIVE")).raise();
- return fetch(user_status, buffer, fetch_relative, offset) ?
- IStatus::RESULT_OK : IStatus::RESULT_NO_DATA;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(user_status);
- }
- return IStatus::RESULT_ERROR;
- }
- FB_BOOLEAN ResultSet::isEof(CheckStatusWrapper* status)
- {
- try
- {
- reset(status);
- // Check and validate handles, etc.
- if (!stmt)
- {
- Arg::Gds(isc_dsql_cursor_err).raise();
- }
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- if (!statement->rsr_flags.test(Rsr::FETCHED))
- return FB_FALSE;
- return statement->rsr_flags.test(Rsr::PAST_EOF) ? FB_TRUE : FB_FALSE;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return FB_FALSE;
- }
- FB_BOOLEAN ResultSet::isBof(CheckStatusWrapper* status)
- {
- try
- {
- reset(status);
- // Check and validate handles, etc.
- if (!stmt)
- {
- Arg::Gds(isc_dsql_cursor_err).raise();
- }
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- if (!statement->rsr_flags.test(Rsr::FETCHED))
- return FB_TRUE;
- return statement->rsr_flags.test(Rsr::PAST_BOF) ? FB_TRUE : FB_FALSE;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return FB_FALSE;
- }
- IMessageMetadata* ResultSet::getMetadata(CheckStatusWrapper* status)
- {
- if (!outputFormat)
- {
- status->setErrors(Arg::Gds(isc_no_output_format).value());
- return NULL;
- }
- reset(status);
- outputFormat->addRef();
- return outputFormat;
- }
- void ResultSet::getInfo(CheckStatusWrapper* status,
- unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer)
- {
- try
- {
- reset(status);
- // Check and validate handles, etc.
- if (!stmt)
- Arg::Gds(isc_dsql_cursor_err).raise();
- const auto statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- const auto rdb = statement->rsr_rdb;
- const auto port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- if (port->port_protocol < PROTOCOL_FETCH_SCROLL)
- unsupported();
- info(status, rdb, op_info_cursor, statement->rsr_id, 0,
- itemsLength, items, 0, 0, bufferLength, buffer);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void ResultSet::freeClientData(CheckStatusWrapper* status, bool force)
- {
- /**************************************
- *
- * d s q l _ f r e e _ s t a t e m e n t
- *
- **************************************
- *
- * Functional description
- * Close SQL cursor
- *
- **************************************/
- try
- {
- // Check and validate handles, etc.
- if (!stmt)
- {
- Arg::Gds(isc_dsql_cursor_err).raise();
- }
- Rsr* statement = stmt->getStatement();
- CHECK_HANDLE(statement, isc_bad_req_handle);
- Rdb* rdb = statement->rsr_rdb;
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- statement->clearException();
- if (statement->rsr_flags.test(Rsr::LAZY))
- {
- statement->rsr_flags.clear(Rsr::FETCHED);
- statement->rsr_rtr = NULL;
- clear_queue(rdb->rdb_port);
- REMOTE_reset_statement(statement);
- releaseStatement();
- return;
- }
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_free_statement;
- P_SQLFREE* free_stmt = &packet->p_sqlfree;
- free_stmt->p_sqlfree_statement = statement->rsr_id;
- free_stmt->p_sqlfree_option = DSQL_close;
- if (rdb->rdb_port->port_flags & PORT_lazy)
- {
- defer_packet(rdb->rdb_port, packet);
- packet->p_resp.p_resp_object = statement->rsr_id;
- statement->clearException();
- }
- else
- {
- try
- {
- send_and_receive(status, rdb, packet);
- }
- catch (const Exception&)
- {
- if (!force)
- throw;
- }
- }
- statement->rsr_flags.clear(Rsr::FETCHED);
- statement->rsr_rtr = NULL;
- clear_queue(rdb->rdb_port);
- REMOTE_reset_statement(statement);
- releaseStatement();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void ResultSet::internalClose(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * d s q l _ f r e e _ s t a t e m e n t
- *
- **************************************
- *
- * Functional description
- * Close SQL cursor
- *
- **************************************/
- reset(status);
- freeClientData(status);
- }
- void ResultSet::close(CheckStatusWrapper* status)
- {
- internalClose(status);
- if (status->isEmpty())
- release();
- }
- void ResultSet::deprecatedClose(CheckStatusWrapper* status)
- {
- internalClose(status);
- }
- void ResultSet::releaseStatement()
- {
- if (tmpStatement)
- {
- stmt->release();
- }
- stmt = NULL;
- }
- int Blob::getSegment(CheckStatusWrapper* status, unsigned int bufferLength, void* buffer,
- unsigned int* segmentLength)
- {
- /**************************************
- *
- * g d s _ g e t _ s e g m e n t
- *
- **************************************
- *
- * Functional description
- * Buffer segments of a blob and pass
- * them one by one to the caller.
- *
- **************************************/
- try
- {
- reset(status);
- UCHAR* bufferPtr = static_cast<UCHAR*>(buffer);
- // Sniff out handles, etc, and find the various blocks.
- CHECK_HANDLE(blob, isc_bad_segstr_handle);
- Rdb* rdb = blob->rbl_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Build the primary packet to get the operation started.
- PACKET* packet = &rdb->rdb_packet;
- P_SGMT* segment = &packet->p_sgmt;
- P_RESP* response = &packet->p_resp;
- SaveString temp(response->p_resp_data, bufferLength, bufferPtr);
- // Handle a blob that has been created rather than opened (this should yield an error)
- if (blob->rbl_flags & Rbl::CREATE)
- {
- packet->p_operation = op_get_segment;
- segment->p_sgmt_length = bufferLength;
- segment->p_sgmt_blob = blob->rbl_id;
- segment->p_sgmt_segment.cstr_length = 0;
- send_packet(port, packet);
- receive_response(status, rdb, packet);
- if (segmentLength)
- *segmentLength = response->p_resp_data.cstr_length;
- return IStatus::RESULT_OK;
- }
- // New protocol -- ask for a 1K chunk of blob and
- // fill segment requests from it until its time to
- // get the next section. In other words, get a bunch,
- // pass it out piece by piece, then when there isn't
- // enough left, ask for more.
- unsigned int length = 0;
- // if we're already done, stop now
- if (blob->rbl_flags & Rbl::EOF_SET)
- {
- if (segmentLength)
- *segmentLength = length;
- return IStatus::RESULT_NO_DATA;
- }
- // Here's the loop, passing out data from our basket & refilling it.
- // Our buffer (described by the structure blob) is counted strings
- // <count word> <string> <count word> <string>...
- int code = IStatus::RESULT_OK;
- while (true)
- {
- // If there's data to be given away, give some away (p points to the local data)
- if (blob->rbl_length)
- {
- UCHAR* p = blob->rbl_ptr;
- // If there was a fragment left over last time use it
- USHORT l = blob->rbl_fragment_length;
- if (l) {
- blob->rbl_fragment_length = 0;
- }
- else
- {
- // otherwise pick up the count word as the length, & decrement the local length
- l = *p++;
- l += *p++ << 8;
- blob->rbl_length -= 2;
- }
- // Now check that what we've got fits.
- // If not, set up the fragment pointer and set the status vector
- if (l > bufferLength)
- {
- blob->rbl_fragment_length = l - bufferLength;
- l = bufferLength;
- code = IStatus::RESULT_SEGMENT;
- }
- // and, just for yucks, see if we're exactly using up the fragment
- // part of a previous incomplete read - if so mark this as an
- // incomplete read
- if (l == bufferLength && l == blob->rbl_length && (blob->rbl_flags & Rbl::SEGMENT))
- {
- code = IStatus::RESULT_SEGMENT;
- }
- // finally set up the return length, decrement the current length,
- // copy the data, and indicate where to start next time.
- length += l;
- blob->rbl_length -= l;
- blob->rbl_offset += l;
- bufferLength -= l;
- if (l) {
- memcpy(bufferPtr, p, l);
- }
- bufferPtr += l;
- p += l;
- blob->rbl_ptr = p;
- // return if we've filled up the caller's buffer, or completed a segment
- if (!bufferLength || blob->rbl_length || !(blob->rbl_flags & Rbl::SEGMENT))
- {
- break;
- }
- }
- // We're done with buffer. If this was the last, we're done
- if (blob->rbl_flags & Rbl::EOF_PENDING)
- {
- blob->rbl_flags |= Rbl::EOF_SET;
- code = IStatus::RESULT_NO_DATA;
- break;
- }
- // Preparatory to asking for more data, use input buffer length
- // to cue more efficient blob buffering.
- // Allocate 2 extra bytes to handle the special case where the
- // segment size of blob in the database is equal to the buffer
- // size that the user has passed.
- // Do not go into this loop if we already have a buffer
- // of size 65535 or 65534.
- if (bufferLength > blob->rbl_buffer_length - sizeof(USHORT) &&
- blob->rbl_buffer_length <= MAX_USHORT - sizeof(USHORT))
- {
- ULONG new_size = bufferLength + sizeof(USHORT);
- if (new_size > MAX_USHORT) // Check if we've overflown
- new_size = bufferLength;
- blob->rbl_ptr = blob->rbl_buffer = blob->rbl_data.getBuffer(new_size);
- blob->rbl_buffer_length = (USHORT) new_size;
- }
- // We need more data. Ask for it politely
- packet->p_operation = op_get_segment;
- segment->p_sgmt_length = blob->rbl_buffer_length;
- segment->p_sgmt_blob = blob->rbl_id;
- segment->p_sgmt_segment.cstr_length = 0;
- send_packet(rdb->rdb_port, packet);
- response->p_resp_data.cstr_allocated = blob->rbl_buffer_length;
- response->p_resp_data.cstr_address = blob->rbl_buffer;
- receive_response(status, rdb, packet);
- blob->rbl_length = (USHORT) response->p_resp_data.cstr_length;
- blob->rbl_ptr = blob->rbl_buffer;
- blob->rbl_flags &= ~Rbl::SEGMENT;
- if (response->p_resp_object == 1)
- blob->rbl_flags |= Rbl::SEGMENT;
- else if (response->p_resp_object == 2)
- blob->rbl_flags |= Rbl::EOF_PENDING;
- }
- if (segmentLength)
- *segmentLength = length;
- return code;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return IStatus::RESULT_ERROR;
- }
- int Attachment::getSlice(CheckStatusWrapper* status, ITransaction* apiTra, ISC_QUAD* array_id,
- unsigned int sdl_length, const unsigned char* sdl,
- unsigned int param_length, const unsigned char* param,
- int slice_length, unsigned char* slice)
- {
- /**************************************
- *
- * g d s _ g e t _ s l i c e
- *
- **************************************
- *
- * Functional description
- * Snatch a slice of an array.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rtr* transaction = remoteTransaction(apiTra);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- // Validate data length
- CHECK_LENGTH(port, sdl_length);
- CHECK_LENGTH(port, param_length);
- // Parse the sdl in case blr_d_float must be converted to blr_double
- const UCHAR* new_sdl = sdl;
- // CVC: Modified this horrible idea: don't touch input parameters!
- // The modified (perhaps) sdl is send to the remote connection. The
- // original sdl is used to process the slice data when it is received.
- // (This is why both 'new_sdl' and 'sdl' are saved in the packet.)
- HalfStaticArray<UCHAR, 128> sdl_buffer;
- UCHAR* old_sdl = sdl_buffer.getBuffer(sdl_length);
- memcpy(old_sdl, sdl, sdl_length);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_get_slice;
- P_SLC* data = &packet->p_slc;
- data->p_slc_transaction = transaction->rtr_id;
- data->p_slc_id = *array_id;
- data->p_slc_length = slice_length;
- data->p_slc_sdl.cstr_length = sdl_length;
- data->p_slc_sdl.cstr_address = const_cast<UCHAR*>(new_sdl);
- data->p_slc_parameters.cstr_length = param_length;
- data->p_slc_parameters.cstr_address = const_cast<UCHAR*>(param);
- data->p_slc_slice.lstr_length = 0;
- data->p_slc_slice.lstr_address = slice;
- P_SLR* response = &packet->p_slr;
- response->p_slr_sdl = old_sdl; //const_cast<UCHAR*>(sdl);
- response->p_slr_sdl_length = sdl_length;
- response->p_slr_slice.lstr_address = slice;
- response->p_slr_slice.lstr_length = slice_length;
- send_packet(rdb->rdb_port, packet);
- receive_packet(rdb->rdb_port, packet);
- if (packet->p_operation != op_slice)
- {
- REMOTE_check_response(status, rdb, packet);
- }
- return response->p_slr_length;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return 0;
- }
- IBlob* Attachment::openBlob(CheckStatusWrapper* status, ITransaction* apiTra, ISC_QUAD* id,
- unsigned int bpb_length, const unsigned char* bpb)
- {
- /**************************************
- *
- * g d s _ o p e n _ b l o b 2
- *
- **************************************
- *
- * Functional description
- * Open an existing blob.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rtr* transaction = remoteTransaction(apiTra);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- // Validate data length
- CHECK_LENGTH(port, bpb_length);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_open_blob2;
- P_BLOB* p_blob = &packet->p_blob;
- p_blob->p_blob_transaction = transaction->rtr_id;
- p_blob->p_blob_id = *id;
- p_blob->p_blob_bpb.cstr_length = bpb_length;
- fb_assert(!p_blob->p_blob_bpb.cstr_allocated ||
- p_blob->p_blob_bpb.cstr_allocated < p_blob->p_blob_bpb.cstr_length);
- // CVC: Should we ensure here that cstr_allocated < bpb_length???
- // Otherwise, xdr_cstring() calling alloc_string() to decode would
- // cause memory problems on the client side for SS, as the client
- // would try to write to the application's provided R/O buffer.
- p_blob->p_blob_bpb.cstr_address = bpb;
- send_and_receive(status, rdb, packet);
- // CVC: It's not evident to me why these two lines that I've copied
- // here as comments are only found in create_blob calls.
- // I think they should be enabled to avoid whatever buffer corruption.
- //p_blob->p_blob_bpb.cstr_length = 0;
- //p_blob->p_blob_bpb.cstr_address = NULL;
- Rbl* blob = FB_NEW Rbl;
- blob->rbl_rdb = rdb;
- blob->rbl_rtr = transaction;
- blob->rbl_id = packet->p_resp.p_resp_object;
- SET_OBJECT(rdb, blob, blob->rbl_id);
- blob->rbl_next = transaction->rtr_blobs;
- transaction->rtr_blobs = blob;
- Firebird::IBlob* b = FB_NEW Blob(blob);
- b->addRef();
- return b;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- void Transaction::prepare(CheckStatusWrapper* status, unsigned int msg_length, const unsigned char* msg)
- {
- /**************************************
- *
- * g d s _ p r e p a r e
- *
- **************************************
- *
- * Functional description
- * Prepare a transaction for commit. First phase of a two
- * phase commit.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- Rdb* rdb = transaction->rtr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Validate data length
- CHECK_LENGTH(port, msg_length);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_prepare2;
- packet->p_prep.p_prep_transaction = transaction->rtr_id;
- packet->p_prep.p_prep_data.cstr_length = msg_length;
- packet->p_prep.p_prep_data.cstr_address = msg;
- send_packet(rdb->rdb_port, packet);
- receive_response(status, rdb, packet);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Blob::putSegment(CheckStatusWrapper* status, unsigned int segment_length, const void* segment)
- {
- /**************************************
- *
- * g d s _ p u t _ s e g m e n t
- *
- **************************************
- *
- * Functional description
- * Emit a blob segment. If the protocol allows,
- * the segment is buffered locally for a later
- * batch put.
- *
- **************************************/
- try
- {
- reset(status);
- const UCHAR* segmentPtr = static_cast<const UCHAR*>(segment);
- // Sniff out handles, etc, and find the various blocks.
- CHECK_HANDLE(blob, isc_bad_segstr_handle);
- Rdb* rdb = blob->rbl_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Handle a blob that has been opened rather than created (this should yield an error)
- if (!(blob->rbl_flags & Rbl::CREATE))
- {
- send_blob(status, blob, segment_length, segmentPtr);
- fb_assert(false);
- }
- // If the buffer can't hold the complete incoming segment, flush out the
- // buffer. If the incoming segment is too large to fit into the blob
- // buffer, just send it as a single segment.
- UCHAR* p = blob->rbl_ptr;
- const unsigned int l = blob->rbl_buffer_length - (p - blob->rbl_buffer);
- if (segment_length + 2 > l)
- {
- if (blob->rbl_ptr > blob->rbl_buffer)
- {
- send_blob(status, blob, 0, NULL);
- }
- if ((ULONG) segment_length + 2 > blob->rbl_buffer_length)
- {
- send_blob(status, blob, segment_length, segmentPtr);
- return;
- }
- p = blob->rbl_buffer;
- }
- // Move segment length and data into blob buffer
- *p++ = (UCHAR) segment_length;
- *p++ = segment_length >> 8;
- if (segment_length) {
- memcpy(p, segmentPtr, segment_length);
- }
- blob->rbl_ptr = p + segment_length;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Attachment::putSlice(CheckStatusWrapper* status, ITransaction* apiTra, ISC_QUAD* id,
- unsigned int sdl_length, const unsigned char* sdl,
- unsigned int param_length, const unsigned char* param,
- int sliceLength, unsigned char* slice)
- {
- /**************************************
- *
- * g d s _ p u t _ s l i c e
- *
- **************************************
- *
- * Functional description
- * Store a slice of an array.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rtr* transaction = remoteTransaction(apiTra);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- // Validate data length
- CHECK_LENGTH(port, sdl_length);
- CHECK_LENGTH(port, param_length);
- // Parse the sdl in case blr_d_float must be converted to blr_double
- const UCHAR* new_sdl = sdl;
- // CVC: Modified this horrible idea: don't touch input parameters!
- // The modified (perhaps) sdl is sent to the remote connection. The
- // original sdl is used to process the slice data before it is sent.
- // (This is why both 'new_sdl' and 'sdl' are saved in the packet.)
- HalfStaticArray<UCHAR, 128> sdl_buffer;
- UCHAR* old_sdl = sdl_buffer.getBuffer(sdl_length);
- memcpy(old_sdl, sdl, sdl_length);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_put_slice;
- P_SLC* data = &packet->p_slc;
- data->p_slc_transaction = transaction->rtr_id;
- data->p_slc_id = *id;
- data->p_slc_length = sliceLength;
- data->p_slc_sdl.cstr_length = sdl_length;
- data->p_slc_sdl.cstr_address = const_cast<UCHAR*>(new_sdl);
- data->p_slc_parameters.cstr_length = param_length;
- data->p_slc_parameters.cstr_address = const_cast<UCHAR*>(param);
- data->p_slc_slice.lstr_length = sliceLength;
- data->p_slc_slice.lstr_address = slice;
- P_SLR* response = &packet->p_slr;
- response->p_slr_sdl = old_sdl; //const_cast<UCHAR*>(sdl);
- response->p_slr_sdl_length = sdl_length;
- response->p_slr_slice.lstr_address = slice;
- response->p_slr_slice.lstr_length = sliceLength;
- send_and_receive(status, rdb, packet);
- *id = packet->p_resp.p_resp_blob_id;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- Firebird::IEvents* Attachment::queEvents(CheckStatusWrapper* status, Firebird::IEventCallback* callback,
- unsigned int length, const unsigned char* events)
- {
- /**************************************
- *
- * g d s _ $ q u e _ e v e n t s
- *
- **************************************
- *
- * Functional description
- * Queue a request for event notification.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Validate data length
- CHECK_LENGTH(port, length);
- PACKET* packet = &rdb->rdb_packet;
- // If there isn't a auxiliary asynchronous port, make one now
- if (!port->port_async)
- {
- packet->p_operation = op_connect_request;
- P_REQ* request = &packet->p_req;
- request->p_req_object = rdb->rdb_id;
- request->p_req_type = P_REQ_async;
- send_packet(port, packet);
- receive_response(status, rdb, packet);
- port->connect(packet);
- rem_port* port_async = port->port_async;
- port_async->port_events_threadId =
- Thread::start(event_thread, port_async, THREAD_high, &port_async->port_events_thread);
- port_async->port_context = rdb;
- }
- // Add event block to port's list of active remote events
- Rvnt* rem_event = add_event(port);
- rem_event->rvnt_callback = callback;
- rem_event->rvnt_port = port->port_async;
- rem_event->rvnt_length = length;
- rem_event->rvnt_rdb = rdb;
- // Build the primary packet to get the operation started.
- packet = &rdb->rdb_packet;
- packet->p_operation = op_que_events;
- P_EVENT* event = &packet->p_event;
- event->p_event_database = rdb->rdb_id;
- event->p_event_items.cstr_length = length;
- event->p_event_items.cstr_address = events;
- event->p_event_ast = 0;
- event->p_event_arg = 0;
- event->p_event_rid = rem_event->rvnt_id;
- send_packet(port, packet);
- receive_response(status, rdb, packet);
- Firebird::IEvents* rc = FB_NEW Events(rem_event);
- rc->addRef();
- return rc;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- void Request::receive(CheckStatusWrapper* status, int level, unsigned int msg_type,
- unsigned int msg_length, void* msg)
- {
- /**************************************
- *
- * g d s _ r e c e i v e
- *
- **************************************
- *
- * Functional description
- * Give a client program a record. Ask the
- * Remote server to send it to us if necessary.
- *
- **************************************/
- try
- {
- reset(status);
- // Check handles and environment, then set up error handling
- CHECK_HANDLE(rq, isc_bad_req_handle);
- Rrq* request = REMOTE_find_request(rq, level);
- Rdb* rdb = request->rrq_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rrq::rrq_repeat* tail = &request->rrq_rpt[msg_type];
- RMessage* message = tail->rrq_message;
- #ifdef DEBUG
- fprintf(stdout, "Rows Pending in REM_receive=%d\n", tail->rrq_rows_pending);
- #endif
- // Check to see if data is waiting. If not, solicit data.
- // Solicit data either when we've run out, or there's a low
- // inventory of messages in local buffers & no shipments on the
- // ether being sent to us.
- if (request->rrqStatus.isSuccess() && // No error pending
- ((!message->msg_address && tail->rrq_rows_pending == 0) || // No message waiting
- (tail->rrq_rows_pending <= tail->rrq_reorder_level && // Low in inventory
- tail->rrq_msgs_waiting <= tail->rrq_reorder_level &&
- // Pipelining causes both server & client to
- // write at the same time. In XNET, writes
- // block for the other end to read - and so when both
- // attempt to write simultaenously, they end up
- // waiting indefinetly for the other end to read.
- (port->port_type != rem_port::XNET) &&
- request->rrq_max_msg <= 1)))
- {
- // there's only one message type
- #ifdef DEBUG
- fprintf(stderr, "Rows Pending %d\n", tail->rrq_rows_pending);
- if (!message->msg_address)
- fprintf(stderr, "Out of data - reordering\n");
- else
- fprintf(stderr, "Low on inventory - reordering\n");
- #endif
- // Format a request for data
- PACKET *packet = &rdb->rdb_packet;
- packet->p_operation = op_receive;
- P_DATA* data = &packet->p_data;
- data->p_data_request = request->rrq_id;
- data->p_data_message_number = msg_type;
- data->p_data_incarnation = level;
- // Compute how many to send in a batch. While this calculation
- // is the same for each batch (June 1996), perhaps in the future it
- // could dynamically adjust batching sizes based on fetch patterns
- data->p_data_messages = REMOTE_compute_batch_size(port, 0, op_send, tail->rrq_format);
- tail->rrq_reorder_level = data->p_data_messages / 2;
- tail->rrq_rows_pending += data->p_data_messages;
- #ifdef DEBUG
- fprintf(stdout, "Recalculating Rows Pending in REM_receive=%d\n",
- tail->rrq_rows_pending);
- #endif
- #ifdef DEBUG
- fprintf(stderr, "port_flags %d max_msg %d\n", port->port_flags, request->rrq_max_msg);
- fprintf(stderr, "Fetch: Req One batch of %d messages\n", data->p_data_messages);
- #endif
- send_packet(port, packet);
- tail->rrq_batch_count++;
- #ifdef DEBUG
- fprintf(stderr, "Rows Pending %d\n", tail->rrq_rows_pending);
- #endif
- // Queue up receipt of the pending data
- enqueue_receive(port, batch_gds_receive, rdb, request, tail);
- }
- // Receive queued responses until we have some data for this cursor
- // or an error status has been received.
- // We've either got data, or some is on the way, or we have an error
- fb_assert(message->msg_address || tail->rrq_rows_pending > 0 || (!request->rrqStatus.isSuccess()));
- while (!message->msg_address && request->rrqStatus.isSuccess())
- {
- receive_queued_packet(port, request->rrq_id);
- }
- if (!message->msg_address && !request->rrqStatus.isSuccess())
- {
- // The previous batch of receives ended with an error status.
- // We're all done returning data in the local queue.
- // Return that error status vector to the user.
- // Stuff in the error result to the user's vector
- request->rrqStatus.raise();
- }
- // Copy data from the message buffer to the client buffer
- if (tail->rrq_format->fmt_length != msg_length)
- {
- status_exception::raise(Arg::Gds(isc_port_len) <<
- Arg::Num(msg_length) << Arg::Num(tail->rrq_format->fmt_length));
- }
- message = tail->rrq_message;
- memcpy(msg, message->msg_address, msg_length);
- // Move the head-of-full-buffer-queue pointer forward
- tail->rrq_message = message->msg_next;
- // Mark the buffer the message came from as available for reuse
- message->msg_address = NULL;
- tail->rrq_msgs_waiting--;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- Firebird::ITransaction* Attachment::reconnectTransaction(CheckStatusWrapper* status,
- unsigned int length, const unsigned char* id)
- {
- /**************************************
- *
- * g d s _ r e c o n n e c t
- *
- **************************************
- *
- * Functional description
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Validate data length
- CHECK_LENGTH(port, length);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_reconnect;
- P_STTR* trans = &packet->p_sttr;
- trans->p_sttr_database = rdb->rdb_id;
- trans->p_sttr_tpb.cstr_length = length;
- trans->p_sttr_tpb.cstr_address = id;
- send_and_receive(status, rdb, packet);
- Firebird::ITransaction* t = FB_NEW Transaction(make_transaction(rdb, packet->p_resp.p_resp_object), this);
- t->addRef();
- return t;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- void Request::freeClientData(CheckStatusWrapper* status, bool force)
- {
- /**************************************
- *
- * g d s _ r e l e a s e _ r e q u e s t
- *
- **************************************
- *
- * Functional description
- * Release a request.
- *
- **************************************/
- try
- {
- CHECK_HANDLE(rq, isc_bad_req_handle);
- Rdb* rdb = rq->rrq_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- try
- {
- release_object(status, rdb, op_release, rq->rrq_id);
- }
- catch (const Exception&)
- {
- if (!force)
- throw;
- }
- release_request(rq);
- rq = NULL;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Request::internalFree(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * g d s _ r e l e a s e _ r e q u e s t
- *
- **************************************
- *
- * Functional description
- * Release a request.
- *
- **************************************/
- reset(status);
- freeClientData(status);
- }
- void Request::free(CheckStatusWrapper* status)
- {
- internalFree(status);
- if (status->isEmpty())
- release();
- }
- void Request::deprecatedFree(CheckStatusWrapper* status)
- {
- internalFree(status);
- }
- void Request::getInfo(CheckStatusWrapper* status, int level,
- unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer)
- {
- /**************************************
- *
- * g d s _ r e q u e s t _ i n f o
- *
- **************************************
- *
- * Functional description
- * Provide information on request object.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rq, isc_bad_req_handle);
- Rrq* request = REMOTE_find_request(rq, level);
- CHECK_HANDLE(request, isc_bad_req_handle);
- Rdb* rdb = request->rrq_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Check for buffered message. If there is, report on it locally.
- const Rrq::rrq_repeat* tail= request->rrq_rpt.begin();
- for (const Rrq::rrq_repeat* const end = tail + request->rrq_max_msg; tail <= end; tail++)
- {
- RMessage* msg = tail->rrq_message;
- if (!msg || !msg->msg_address) {
- continue;
- }
- // We've got a pending message, respond locally
- const rem_fmt* format = tail->rrq_format;
- UCHAR* out = buffer;
- const UCHAR* infoItems = items;
- const UCHAR* const endItems = infoItems + itemsLength;
- while (infoItems < endItems)
- {
- USHORT data = 0;
- const UCHAR item = *infoItems++;
- switch (item)
- {
- case isc_info_end:
- break;
- case isc_info_state:
- data = isc_info_req_send;
- break;
- case isc_info_message_number:
- data = msg->msg_number;
- break;
- case isc_info_message_size:
- data = format->fmt_length;
- break;
- default:
- goto punt;
- }
- *out++ = item;
- if (item == isc_info_end)
- break;
- *out++ = 2;
- *out++ = 2 >> 8;
- *out++ = (UCHAR) data;
- *out++ = data >> 8;
- }
- }
- // No message pending, request status from other end
- punt:
- info(status, rdb, op_info_request, request->rrq_id, level,
- itemsLength, items, 0, 0, bufferLength, buffer);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Transaction::rollbackRetaining(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * i s c _ r o l l b a c k _ r e t a i n i n g
- *
- **************************************
- *
- * Functional description
- * Abort a transaction but keep its environment valid
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- Rdb* rdb = transaction->rtr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- release_object(status, rdb, op_rollback_retaining, transaction->rtr_id);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Transaction::freeClientData(CheckStatusWrapper* status, bool force)
- {
- /**************************************
- *
- * g d s _ r o l l b a c k
- *
- **************************************
- *
- * Functional description
- * Abort a transaction.
- *
- **************************************/
- try
- {
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- Rdb* rdb = transaction->rtr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- try
- {
- release_object(status, rdb, op_rollback, transaction->rtr_id);
- }
- catch (const Exception&)
- {
- if (!force)
- throw;
- }
- REMOTE_cleanup_transaction(transaction);
- release_transaction(transaction);
- transaction = NULL;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Transaction::internalRollback(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * g d s _ r o l l b a c k
- *
- **************************************
- *
- * Functional description
- * Abort a transaction.
- *
- **************************************/
- reset(status);
- freeClientData(status);
- }
- void Transaction::rollback(CheckStatusWrapper* status)
- {
- internalRollback(status);
- if (status->isEmpty())
- release();
- }
- void Transaction::deprecatedRollback(CheckStatusWrapper* status)
- {
- internalRollback(status);
- }
- void Transaction::internalDisconnect(CheckStatusWrapper* status)
- {
- try
- {
- reset(status);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- Rdb* rdb = transaction->rtr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- // ASF: Looks wrong that this method is ignored in the engine and remote providers.
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Transaction::disconnect(CheckStatusWrapper* status)
- {
- internalDisconnect(status);
- if (status->isEmpty())
- release();
- }
- void Transaction::deprecatedDisconnect(CheckStatusWrapper* status)
- {
- internalDisconnect(status);
- }
- int Blob::seek(CheckStatusWrapper* status, int mode, int offset)
- {
- /**************************************
- *
- * g d s _ s e e k _ b l o b
- *
- **************************************
- *
- * Functional description
- * Seek into a blob.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(blob, isc_bad_segstr_handle);
- Rdb* rdb = blob->rbl_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_seek_blob;
- P_SEEK* seek = &packet->p_seek;
- seek->p_seek_blob = blob->rbl_id;
- seek->p_seek_mode = mode;
- seek->p_seek_offset = offset;
- if (mode == 1)
- {
- seek->p_seek_mode = 0;
- seek->p_seek_offset = blob->rbl_offset + offset;
- }
- send_and_receive(status, rdb, packet);
- blob->rbl_offset = packet->p_resp.p_resp_blob_id.gds_quad_low;
- blob->rbl_length = 0;
- blob->rbl_fragment_length = 0;
- blob->rbl_flags &= ~(Rbl::EOF_SET | Rbl::EOF_PENDING | Rbl::SEGMENT);
- return blob->rbl_offset;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return 0;
- }
- void Request::send(CheckStatusWrapper* status, int level, unsigned int msg_type,
- unsigned int /*length*/, const void* msg)
- {
- /**************************************
- *
- * g d s _ s e n d
- *
- **************************************
- *
- * Functional description
- * Send a message to the server.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rq, isc_bad_req_handle);
- Rrq* request = REMOTE_find_request(rq, level);
- Rdb* rdb = request->rrq_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- if (msg_type > request->rrq_max_msg)
- {
- handle_error(isc_badmsgnum);
- }
- RMessage* message = request->rrq_rpt[msg_type].rrq_message;
- // We are lying here, but the interface shows for years this param as const
- message->msg_address = const_cast<unsigned char*>(static_cast<const unsigned char*>(msg));
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_send;
- P_DATA* data = &packet->p_data;
- data->p_data_request = request->rrq_id;
- data->p_data_message_number = msg_type;
- data->p_data_incarnation = level;
- send_packet(port, packet);
- // Bump up the message pointer to resync with rrq_xdr (rrq_xdr
- // was incremented by xdr_request in the SEND call).
- message->msg_address = NULL;
- request->rrq_rpt[msg_type].rrq_message = message->msg_next;
- receive_response(status, rdb, packet);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- Firebird::IService* RProvider::attachSvc(CheckStatusWrapper* status, const char* service,
- unsigned int spbLength, const unsigned char* spb, bool loopback)
- {
- /**************************************
- *
- * g d s _ s e r v i c e _ a t t a c h
- *
- **************************************
- *
- * Functional description
- * Connect to a Firebird service.
- *
- **************************************/
- try
- {
- reset(status);
- PathName node_name, expanded_name(service);
- ClumpletWriter newSpb(ClumpletReader::spbList, MAX_DPB_SIZE, spb, spbLength);
- const bool user_verification = get_new_dpb(newSpb, spbParam, loopback);
- ClntAuthBlock cBlock(NULL, &newSpb, &spbParam);
- unsigned flags = 0;
- if (user_verification)
- flags |= ANALYZE_USER_VFY;
- if (loopback)
- flags |= ANALYZE_LOOPBACK;
- flags |= ANALYZE_EMP_NAME;
- PathName refDbName;
- if (newSpb.find(isc_spb_expected_db))
- newSpb.getPath(refDbName);
- rem_port* port = analyze(cBlock, expanded_name, flags, newSpb, spbParam, node_name, &refDbName, cryptCallback);
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rdb* rdb = port->port_context;
- // The client may have set a parameter for dummy_packet_interval. Add that to the
- // the SPB so the server can pay attention to it. Note: allocation code must
- // ensure sufficient space has been added.
- add_other_params(port, newSpb, spbParam);
- IntlSpb intl;
- if (!init(status, cBlock, port, op_service_attach, expanded_name, newSpb, intl, cryptCallback))
- return NULL;
- Firebird::IService* s = FB_NEW Service(rdb);
- s->addRef();
- return s;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- Firebird::IService* RProvider::attachServiceManager(CheckStatusWrapper* status, const char* service,
- unsigned int spbLength, const unsigned char* spb)
- {
- /**************************************
- *
- * g d s _ s e r v i c e _ a t t a c h
- *
- **************************************
- *
- * Functional description
- * Connect to a Firebird service.
- *
- **************************************/
- return attachSvc(status, service, spbLength, spb, false);
- }
- Firebird::IService* Loopback::attachServiceManager(CheckStatusWrapper* status, const char* service,
- unsigned int spbLength, const unsigned char* spb)
- {
- /**************************************
- *
- * g d s _ s e r v i c e _ a t t a c h
- *
- **************************************
- *
- * Functional description
- * Connect to a Firebird service.
- *
- **************************************/
- return attachSvc(status, service, spbLength, spb, true);
- }
- void Service::freeClientData(CheckStatusWrapper* status, bool force)
- {
- /**************************************
- *
- * g d s _ s e r v i c e _ d e t a c h
- *
- **************************************
- *
- * Functional description
- * Close down a connection to a Firebird service.
- *
- **************************************/
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(rdb, isc_bad_svc_handle);
- rem_port* port = rdb->rdb_port;
- RemotePortGuard portGuard(port, FB_FUNCTION);
- if (!(port->port_flags & PORT_detached))
- {
- try
- {
- release_object(status, rdb, op_service_detach, rdb->rdb_id);
- }
- catch (const Exception&)
- {
- if (!force)
- throw;
- }
- }
- disconnect(port);
- rdb = NULL;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Service::internalDetach(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * g d s _ s e r v i c e _ d e t a c h
- *
- **************************************
- *
- * Functional description
- * Close down a connection to a Firebird service.
- *
- **************************************/
- reset(status);
- freeClientData(status);
- }
- void Service::detach(CheckStatusWrapper* status)
- {
- internalDetach(status);
- if (status->isEmpty())
- release();
- }
- void Service::deprecatedDetach(CheckStatusWrapper* status)
- {
- internalDetach(status);
- }
- void Service::query(CheckStatusWrapper* status,
- unsigned int sendLength, const unsigned char* sendItems,
- unsigned int receiveLength, const unsigned char* receiveItems,
- unsigned int bufferLength, unsigned char* buffer)
- {
- /**************************************
- *
- * g d s _ s e r v i c e _ q u e r y
- *
- **************************************
- *
- * Functional description
- * Provide information on service object.
- *
- **************************************/
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(rdb, isc_bad_svc_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- info(status, rdb, op_service_info, rdb->rdb_id, 0,
- sendLength, sendItems, receiveLength, receiveItems,
- bufferLength, buffer);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Service::cancel(CheckStatusWrapper* status)
- {
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(rdb, isc_bad_svc_handle);
- /*
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- */
- Arg::Gds(isc_wish_list).raise();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Service::start(CheckStatusWrapper* status,
- unsigned int spbLength, const unsigned char* spb)
- {
- /**************************************
- *
- * g d s _ s e r v i c e _ s t a r t
- *
- **************************************
- *
- * Functional description
- * Start a Firebird service
- *
- **************************************/
- try
- {
- reset(status);
- // Check and validate handles, etc.
- CHECK_HANDLE(rdb, isc_bad_svc_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- svcstart(status, rdb, op_service_start, rdb->rdb_id, 0, spbLength, spb);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Request::startAndSend(CheckStatusWrapper* status, Firebird::ITransaction* apiTra, int level,
- unsigned int msg_type, unsigned int /*length*/, const void* msg)
- {
- /**************************************
- *
- * g d s _ s t a r t _ a n d _ s e n d
- *
- **************************************
- *
- * Functional description
- * Get a record from the host program.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rq, isc_bad_req_handle);
- Rrq* request = REMOTE_find_request(rq, level);
- Rtr* transaction = remAtt->remoteTransaction(apiTra);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- Rdb* rdb = request->rrq_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- if (msg_type > request->rrq_max_msg)
- {
- handle_error(isc_badmsgnum);
- }
- if (transaction->rtr_rdb != rdb)
- {
- Arg::Gds(isc_trareqmis).raise();
- }
- clear_queue(rdb->rdb_port);
- REMOTE_reset_request(request, 0);
- RMessage* message = request->rrq_rpt[msg_type].rrq_message;
- message->msg_address = const_cast<unsigned char*>(static_cast<const unsigned char*>(msg));
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_start_send_and_receive;
- P_DATA* data = &packet->p_data;
- data->p_data_request = request->rrq_id;
- data->p_data_transaction = transaction->rtr_id;
- data->p_data_message_number = msg_type;
- data->p_data_incarnation = level;
- send_packet(port, packet);
- // Bump up the message pointer to resync with rrq_xdr (rrq_xdr
- // was incremented by xdr_request in the SEND call).
- message->msg_address = NULL;
- request->rrq_rpt[msg_type].rrq_message = message->msg_next;
- receive_response(status, rdb, packet);
- // Save the request's transaction.
- request->rrq_rtr = transaction;
- if (packet->p_operation == op_response_piggyback)
- {
- receive_after_start(request, packet->p_resp.p_resp_object);
- }
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Request::start(CheckStatusWrapper* status, Firebird::ITransaction* apiTra, int level)
- {
- /**************************************
- *
- * g d s _ s t a r t
- *
- **************************************
- *
- * Functional description
- * Get a record from the host program.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rq, isc_bad_req_handle);
- Rrq* request = REMOTE_find_request(rq, level);
- Rtr* transaction = remAtt->remoteTransaction(apiTra);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- Rdb* rdb = request->rrq_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- if (transaction->rtr_rdb != rdb)
- {
- Arg::Gds(isc_trareqmis).raise();
- }
- clear_queue(rdb->rdb_port);
- REMOTE_reset_request(request, 0);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_start_and_receive;
- P_DATA* data = &packet->p_data;
- data->p_data_request = request->rrq_id;
- data->p_data_transaction = transaction->rtr_id;
- data->p_data_message_number = 0;
- data->p_data_incarnation = level;
- send_and_receive(status, rdb, packet);
- // Save the request's transaction.
- request->rrq_rtr = transaction;
- if (packet->p_operation == op_response_piggyback)
- {
- receive_after_start(request, packet->p_resp.p_resp_object);
- }
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- Firebird::ITransaction* Attachment::startTransaction(CheckStatusWrapper* status, unsigned int tpbLength,
- const unsigned char* tpb)
- {
- /**************************************
- *
- * g d s _ t r a n s a c t i o n
- *
- **************************************
- *
- * Functional description
- * Start a transaction.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- if (/***tpbLength < 0 ||***/ (tpbLength > 0 && !tpb))
- {
- status_exception::raise(Arg::Gds(isc_bad_tpb_form));
- }
- // Validate data length
- CHECK_LENGTH(port, tpbLength);
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_transaction;
- P_STTR* trans = &packet->p_sttr;
- trans->p_sttr_database = rdb->rdb_id;
- trans->p_sttr_tpb.cstr_length = tpbLength;
- trans->p_sttr_tpb.cstr_address = tpb;
- send_and_receive(status, rdb, packet);
- Firebird::ITransaction* t = FB_NEW Transaction(make_transaction(rdb, packet->p_resp.p_resp_object), this);
- t->addRef();
- return t;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- void Attachment::transactRequest(CheckStatusWrapper* status, ITransaction* apiTra,
- unsigned int blr_length, const unsigned char* blr,
- unsigned int in_msg_length, const unsigned char* in_msg,
- unsigned int out_msg_length, unsigned char* out_msg)
- {
- /**************************************
- *
- * i s c _ t r a n s a c t _ r e q u e s t
- *
- **************************************
- *
- * Functional description
- * Execute a procedure on remote host.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- Rtr* transaction = remoteTransaction(apiTra);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- // Validate data length
- CHECK_LENGTH(port, blr_length);
- CHECK_LENGTH(port, in_msg_length);
- CHECK_LENGTH(port, out_msg_length);
- Rpr* procedure = port->port_rpr;
- if (!procedure) {
- procedure = port->port_rpr = FB_NEW Rpr;
- }
- // Parse the blr describing the messages
- delete procedure->rpr_in_msg;
- procedure->rpr_in_msg = NULL;
- delete procedure->rpr_in_format;
- procedure->rpr_in_format = NULL;
- delete procedure->rpr_out_msg;
- procedure->rpr_out_msg = NULL;
- delete procedure->rpr_out_format;
- procedure->rpr_out_format = NULL;
- RMessage* message = PARSE_messages(blr, blr_length);
- while (message)
- {
- switch (message->msg_number)
- {
- case 0:
- procedure->rpr_in_msg = message;
- procedure->rpr_in_format = (rem_fmt*) message->msg_address;
- message->msg_address = const_cast<unsigned char*>(in_msg);
- message = message->msg_next;
- procedure->rpr_in_msg->msg_next = NULL;
- break;
- case 1:
- procedure->rpr_out_msg = message;
- procedure->rpr_out_format = (rem_fmt*) message->msg_address;
- message->msg_address = out_msg;
- message = message->msg_next;
- procedure->rpr_out_msg->msg_next = NULL;
- break;
- default:
- RMessage* temp = message;
- message = message->msg_next;
- delete temp;
- break;
- }
- }
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_transact;
- P_TRRQ* trrq = &packet->p_trrq;
- trrq->p_trrq_database = rdb->rdb_id;
- trrq->p_trrq_transaction = transaction->rtr_id;
- trrq->p_trrq_blr.cstr_length = blr_length;
- trrq->p_trrq_blr.cstr_address = const_cast<unsigned char*>(blr);
- trrq->p_trrq_messages = in_msg_length ? 1 : 0;
- send_packet(port, packet);
- // Two types of responses are possible, op_transact_response or
- // op_response. When there is an error op_response packet is returned
- // and it modifies the status vector to indicate the error which occurred.
- // But when success occurs a packet with op_transact_response comes back
- // which does not change the status vector.
- receive_packet(port, packet);
- if (packet->p_operation != op_transact_response)
- {
- REMOTE_check_response(status, rdb, packet);
- }
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Transaction::getInfo(CheckStatusWrapper* status,
- unsigned int itemsLength, const unsigned char* items,
- unsigned int bufferLength, unsigned char* buffer)
- {
- /**************************************
- *
- * g d s _ t r a n s a c t i o n _ i n f o
- *
- **************************************
- *
- * Functional description
- *
- **************************************/
- Array<unsigned char> newItemsBuffer;
- try
- {
- reset(status);
- CHECK_HANDLE(transaction, isc_bad_trans_handle);
- Rdb* rdb = transaction->rtr_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- fb_utils::getDbPathInfo(itemsLength, items, bufferLength, buffer,
- newItemsBuffer, remAtt->getDbPath());
- info(status, rdb, op_info_transaction, transaction->rtr_id, 0,
- itemsLength, items, 0, 0, bufferLength, buffer);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Request::unwind(CheckStatusWrapper* status, int level)
- {
- /**************************************
- *
- * g d s _ u n w i n d
- *
- **************************************
- *
- * Functional description
- * Unwind a running request.
- *
- **************************************/
- try
- {
- reset(status);
- Rrq* request = REMOTE_find_request(rq, level);
- CHECK_HANDLE(request, isc_bad_req_handle);
- Rdb* rdb = request->rrq_rdb;
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- void Attachment::ping(CheckStatusWrapper* status)
- {
- /**************************************
- *
- * p i n g
- *
- **************************************
- *
- * Functional description
- * Check the attachment handle for persistent errors.
- *
- **************************************/
- try
- {
- reset(status);
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- rem_port* port = rdb->rdb_port;
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- // Make sure protocol support action
- if (rdb->rdb_port->port_protocol < PROTOCOL_VERSION13)
- unsupported();
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_ping;
- send_and_receive(status, rdb, packet);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- static Rvnt* add_event( rem_port* port)
- {
- /*************************************
- *
- * a d d _ e v e n t
- *
- **************************************
- *
- * Functional description
- * Add remote event block to active chain.
- *
- **************************************/
- Rdb* rdb = port->port_context;
- // Find unused event block or, if necessary, a new one
- Rvnt* event;
- for (event = rdb->rdb_events; event; event = event->rvnt_next)
- {
- if (!event->rvnt_id)
- break;
- }
- if (!event)
- {
- event = FB_NEW Rvnt;
- event->rvnt_next = rdb->rdb_events;
- rdb->rdb_events = event;
- }
- event->rvnt_id = ++remote_event_id;
- return event;
- }
- static void add_other_params(rem_port* port, ClumpletWriter& dpb, const ParametersSet& par)
- {
- /**************************************
- *
- * a d d _ o t h e r _ p a r a m s
- *
- **************************************
- *
- * Functional description
- * Add parameters to a dpb to describe client-side
- * settings that the server should know about.
- *
- **************************************/
- if (port->port_flags & PORT_dummy_pckt_set)
- {
- dpb.deleteWithTag(par.dummy_packet_interval);
- dpb.insertInt(par.dummy_packet_interval, port->port_dummy_packet_interval);
- }
- // Older version of engine not understand new tags and may process whole
- // DPB incorrectly. Check for protocol version is an poor attempt to make
- // guess about remote engine's version
- if (port->port_protocol >= PROTOCOL_VERSION11)
- {
- dpb.deleteWithTag(par.process_id);
- dpb.insertInt(par.process_id, getpid());
- if (!dpb.find(par.process_name))
- {
- PathName path(fb_utils::get_process_name());
- ISC_systemToUtf8(path);
- ISC_escape(path);
- if (!dpb.find(isc_dpb_utf8_filename))
- ISC_utf8ToSystem(path);
- dpb.insertString(par.process_name, path);
- }
- }
- if (port->port_protocol >= PROTOCOL_VERSION13)
- {
- dpb.deleteWithTag(par.client_version);
- dpb.insertString(par.client_version, FB_VERSION);
- }
- }
- static void add_working_directory(ClumpletWriter& dpb, const PathName& node_name)
- {
- /************************************************
- *
- * a d d _ w o r k i n g _ d i r e c t o r y
- *
- ************************************************
- *
- * Functional description
- * Add parameters to a dpb or spb to describe client-side
- * settings that the server should know about.
- *
- ************************************************/
- if (dpb.find(isc_dpb_working_directory))
- {
- return;
- }
- PathName cwd;
- // for WNet local node_name should be compared with "\\\\." ?
- if (node_name == "localhost")
- {
- fb_utils::getCwd(cwd);
- ISC_systemToUtf8(cwd);
- ISC_escape(cwd);
- if (!dpb.find(isc_dpb_utf8_filename))
- ISC_utf8ToSystem(cwd);
- }
- dpb.insertString(isc_dpb_working_directory, cwd);
- }
- static void authenticateStep0(ClntAuthBlock& cBlock)
- {
- LocalStatus ls;
- CheckStatusWrapper s(&ls);
- for (; cBlock.plugins.hasData(); cBlock.plugins.next())
- {
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authenticateStep0(%s)\n", cBlock.plugins.name()));
- switch(cBlock.plugins.plugin()->authenticate(&s, &cBlock))
- {
- case IAuth::AUTH_SUCCESS:
- case IAuth::AUTH_MORE_DATA:
- return;
- case IAuth::AUTH_FAILED:
- if (s.getState() & Firebird::IStatus::STATE_ERRORS)
- {
- iscLogStatus("Authentication, client plugin:", &s);
- }
- (Arg::Gds(isc_login_error)
- #ifdef DEV_BUILD
- << Arg::StatusVector(&s)
- #endif
- ).raise();
- break; // compiler silencer
- }
- }
- }
- static void secureAuthentication(ClntAuthBlock& cBlock, rem_port* port)
- {
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: secureAuthentication\n"));
- if (!port)
- return;
- Rdb* rdb = port->port_context;
- fb_assert(rdb);
- PACKET* packet = &rdb->rdb_packet;
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: secureAuthentication: port OK, op=%d\n", packet->p_operation));
- if (packet->p_operation == op_cond_accept)
- {
- LocalStatus ls;
- CheckStatusWrapper st(&ls);
- authReceiveResponse(true, cBlock, port, rdb, &st, packet, true);
- if (st.getState() & Firebird::IStatus::STATE_ERRORS)
- status_exception::raise(&st);
- }
- else
- {
- // try to start crypt
- cBlock.tryNewKeys(port);
- }
- }
- static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned flags,
- ClumpletWriter& pb, const ParametersSet& parSet, PathName& node_name, PathName* ref_db_name,
- Firebird::ICryptKeyCallback* cryptCb)
- {
- /**************************************
- *
- * a n a l y z e
- *
- **************************************
- *
- * Functional description
- * Analyze an attach specification and determine whether
- * a remote server is required, and if so, what protocol
- * to use. If the target can be accessed via the
- * remote subsystem, return address of a port block
- * with which to communicate with the server.
- * Otherwise, return NULL.
- *
- * NOTE: The file name must have been expanded prior to this call.
- *
- **************************************/
- rem_port* port = NULL;
- int inet_af = AF_UNSPEC;
- cBlock.loadClnt(pb, &parSet);
- pb.deleteWithTag(parSet.auth_block);
- authenticateStep0(cBlock);
- bool needFile = !(flags & ANALYZE_EMP_NAME);
- #ifdef WIN_NT
- if (ISC_analyze_protocol(PROTOCOL_XNET, attach_name, node_name, NULL, needFile))
- port = XNET_analyze(&cBlock, attach_name, flags & ANALYZE_USER_VFY, cBlock.getConfig(), ref_db_name);
- else
- #endif
- if (ISC_analyze_protocol(PROTOCOL_INET4, attach_name, node_name, INET_SEPARATOR, needFile))
- inet_af = AF_INET;
- else if (ISC_analyze_protocol(PROTOCOL_INET6, attach_name, node_name, INET_SEPARATOR, needFile))
- inet_af = AF_INET6;
- if (inet_af != AF_UNSPEC ||
- ISC_analyze_protocol(PROTOCOL_INET, attach_name, node_name, INET_SEPARATOR, needFile) ||
- ISC_analyze_tcp(attach_name, node_name, needFile))
- {
- if (node_name.isEmpty())
- node_name = INET_LOCALHOST;
- else
- {
- ISC_unescape(node_name);
- ISC_utf8ToSystem(node_name);
- }
- port = INET_analyze(&cBlock, attach_name, node_name.c_str(), flags & ANALYZE_USER_VFY, pb,
- cBlock.getConfig(), ref_db_name, cryptCb, inet_af);
- }
- // We have a local connection string. If it's a file on a network share,
- // try to connect to the corresponding host remotely.
- if (flags & ANALYZE_MOUNTS)
- {
- #ifdef WIN_NT
- if (!port)
- {
- PathName expanded_name = attach_name;
- if (ISC_analyze_pclan(expanded_name, node_name))
- {
- ISC_unescape(node_name);
- ISC_utf8ToSystem(node_name);
- port = INET_analyze(&cBlock, expanded_name, node_name.c_str(), flags & ANALYZE_USER_VFY, pb,
- cBlock.getConfig(), ref_db_name, cryptCb);
- }
- }
- #endif
- #ifndef NO_NFS
- if (!port)
- {
- PathName expanded_name = attach_name;
- if (ISC_analyze_nfs(expanded_name, node_name))
- {
- ISC_unescape(node_name);
- ISC_utf8ToSystem(node_name);
- port = INET_analyze(&cBlock, expanded_name, node_name.c_str(), flags & ANALYZE_USER_VFY, pb,
- cBlock.getConfig(), ref_db_name, cryptCb);
- }
- }
- #endif
- }
- if ((flags & ANALYZE_LOOPBACK) && !port)
- {
- // We have a local connection string.
- // If we are in loopback mode attempt connect to a localhost.
- if (node_name.isEmpty())
- {
- #ifdef WIN_NT
- if (!port)
- {
- port = XNET_analyze(&cBlock, attach_name, flags & ANALYZE_USER_VFY,
- cBlock.getConfig(), ref_db_name);
- }
- #endif
- if (!port)
- {
- port = INET_analyze(&cBlock, attach_name, INET_LOCALHOST, flags & ANALYZE_USER_VFY, pb,
- cBlock.getConfig(), ref_db_name, cryptCb);
- }
- }
- }
- if (!port)
- Arg::Gds(isc_unavailable).raise();
- try
- {
- secureAuthentication(cBlock, port);
- }
- catch (const Exception&)
- {
- disconnect(port, false);
- throw;
- }
- outPorts->registerPort(port);
- return port;
- }
- static void clear_stmt_que(rem_port* port, Rsr* statement)
- {
- /**************************************
- *
- * c l e a r _ s t m t _ q u e
- *
- **************************************
- *
- * Functional description
- *
- * Receive and handle all queued packets for a completely fetched statement.
- * There must be no more than one such packet.
- *
- **************************************/
- fb_assert(statement->rsr_batch_count <= 1);
- while (statement->rsr_batch_count)
- receive_queued_packet(port, statement->rsr_id);
- // hvlad: clear isc_req_sync error as it is received because of our batch
- // fetching code, not because of wrong client application.
- // dimitr: modern engine versions do not pass isc_req_sync to the client,
- // but it's possible if we're connected to the older one.
- if (statement->haveException() == isc_req_sync)
- statement->clearException();
- }
- static void batch_dsql_fetch(rem_port* port,
- rmtque* que_inst,
- USHORT id)
- {
- /**************************************
- *
- * b a t c h _ d s q l _ f e t c h
- *
- **************************************
- *
- * Functional description
- * Receive a batch of messages that were queued
- * on the wire.
- *
- * This function will be invoked whenever we need to wait
- * for something to come over on the wire, and there are
- * items in the queue for receipt.
- *
- * Note on error handing: Actual networking errors
- * need to be reported to status - which is bubbled
- * upwards to the API call which initiated this receive.
- * A status vector being returned as part of the cursor
- * fetch needs to be stored away for later return to the
- * client in the proper place in the stream.
- *
- **************************************/
- fb_assert(port);
- fb_assert(que_inst);
- fb_assert(que_inst->rmtque_function == batch_dsql_fetch);
- Rdb* rdb = que_inst->rmtque_rdb;
- Rsr* statement = static_cast<Rsr*>(que_inst->rmtque_parm);
- PACKET* packet = &rdb->rdb_packet;
- fb_assert(port == rdb->rdb_port);
- // Setup the packet structures so it knows what statement we
- // are trying to receive at this point in time
- packet->p_sqldata.p_sqldata_statement = statement->rsr_id;
- // We'll either receive the whole batch, until end-of-batch is seen,
- // or we'll just fetch one. We'll fetch one when we've run out of
- // local data to return to the client, so we grab one "hot off the wire"
- // to handoff to them. We'll grab the whole batch when we need to
- // receive a response for a DIFFERENT network request on the wire,
- // so we have to clear the wire before the response can be received
- // In addition to the above we grab all the records in case of XNET as
- // we need to clear the queue.
- const bool clear_queue = (id != statement->rsr_id || port->port_type == rem_port::XNET);
- statement->rsr_flags.set(Rsr::FETCHED);
- while (true)
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- // Swallow up data. If a buffer isn't available, allocate another.
- RMessage* message = statement->rsr_buffer;
- if (message->msg_address)
- {
- RMessage* new_msg = FB_NEW RMessage(statement->rsr_fmt_length);
- statement->rsr_buffer = new_msg;
- new_msg->msg_next = message;
- while (message->msg_next != new_msg->msg_next)
- message = message->msg_next;
- message->msg_next = new_msg;
- }
- try {
- receive_packet_noqueue(port, packet);
- }
- catch (const Exception&)
- {
- // Must be a network error
- statement->rsr_rows_pending = 0;
- --statement->rsr_batch_count;
- dequeue_receive(port);
- throw;
- }
- if (packet->p_operation != op_fetch_response)
- {
- statement->rsr_flags.set(Rsr::STREAM_ERR);
- try
- {
- REMOTE_check_response(&status, rdb, packet);
- statement->saveException(&status, false);
- }
- catch (const Exception& ex)
- {
- // Queue errors within the batched request
- statement->saveException(ex, false);
- }
- statement->rsr_rows_pending = 0;
- --statement->rsr_batch_count;
- dequeue_receive(port);
- break;
- }
- // See if we're at end of the batch
- if (packet->p_sqldata.p_sqldata_status || !packet->p_sqldata.p_sqldata_messages)
- {
- if (packet->p_sqldata.p_sqldata_status == 100)
- {
- const auto operation = statement->rsr_fetch_operation;
- const auto position = statement->rsr_fetch_position;
- const bool forward =
- (operation == fetch_next || operation == fetch_last ||
- ((operation == fetch_absolute || operation == fetch_relative) && position > 0));
- if (forward)
- statement->rsr_flags.set(Rsr::EOF_SET);
- else
- statement->rsr_flags.set(Rsr::BOF_SET);
- statement->rsr_rows_pending = 0;
- #ifdef DEBUG
- fprintf(stdout, "Resetting Rows Pending in batch_dsql_fetch=%lu\n",
- statement->rsr_rows_pending);
- #endif
- }
- if (--statement->rsr_batch_count == 0)
- statement->rsr_rows_pending = 0;
- dequeue_receive(port);
- // clear next queued batch(es) if present
- if (packet->p_sqldata.p_sqldata_status == 100)
- {
- try
- {
- clear_stmt_que(port, statement);
- }
- catch (const Exception&) { }
- }
- break;
- }
- statement->rsr_msgs_waiting++;
- statement->rsr_rows_pending--;
- #ifdef DEBUG
- fprintf(stdout, "Decrementing Rows Pending in batch_dsql_fetch=%lu\n",
- statement->rsr_rows_pending);
- #endif
- if (!clear_queue)
- break;
- }
- }
- static void batch_gds_receive(rem_port* port,
- rmtque* que_inst,
- USHORT id)
- {
- /**************************************
- *
- * b a t c h _ g d s _ r e c e i v e
- *
- **************************************
- *
- * Functional description
- * Receive a batch of messages that were queued
- * on the wire.
- *
- * This function will be invoked whenever we need to wait
- * for something to come over on the wire, and there are
- * items in the queue for receipt.
- *
- * Note on error handing: Actual networking errors
- * need to be reported to status - which is bubbled
- * upwards to the API call which initiated this receive.
- * A status vector being returned as part of the cursor
- * fetch needs to be stored away for later return to the
- * client in the proper place in the stream.
- *
- **************************************/
- fb_assert(port);
- fb_assert(que_inst);
- fb_assert(que_inst->rmtque_function == batch_gds_receive);
- Rdb* rdb = que_inst->rmtque_rdb;
- Rrq* request = static_cast<Rrq*>(que_inst->rmtque_parm);
- Rrq::rrq_repeat* tail = que_inst->rmtque_message;
- PACKET *packet = &rdb->rdb_packet;
- fb_assert(port == rdb->rdb_port);
- bool clear_queue = false;
- // indicates whether queue is just being emptied, not retrieved
- // always clear the complete queue for XNET, as we might
- // have incomplete packets
- if (id != request->rrq_id || port->port_type == rem_port::XNET)
- {
- clear_queue = true;
- }
- // Receive the whole batch of records, until end-of-batch is seen
- while (true)
- {
- RMessage* message = tail->rrq_xdr; // First free buffer
- // If the buffer queue is full, allocate a new message and
- // place it in the queue--if we are clearing the queue, don't
- // read records into messages linked list so that we don't
- // mess up the record cache for scrolling purposes.
- if (message->msg_address)
- {
- const rem_fmt* format = tail->rrq_format;
- RMessage* new_msg = FB_NEW RMessage(format->fmt_length);
- tail->rrq_xdr = new_msg;
- new_msg->msg_next = message;
- new_msg->msg_number = message->msg_number;
- // Walk the que until we find the predecessor of message
- while (message->msg_next != new_msg->msg_next)
- {
- message = message->msg_next;
- }
- message->msg_next = new_msg;
- }
- // Note: not receive_packet
- try
- {
- receive_packet_noqueue(rdb->rdb_port, packet);
- }
- catch (const Exception&)
- {
- // Must be a network error
- tail->rrq_rows_pending = 0;
- --tail->rrq_batch_count;
- dequeue_receive(port);
- throw;
- }
- if (packet->p_operation != op_send)
- {
- tail->rrq_rows_pending = 0;
- --tail->rrq_batch_count;
- try
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- REMOTE_check_response(&status, rdb, packet);
- #ifdef DEBUG
- fprintf(stderr, "End of batch. rows pending = %d\n", tail->rrq_rows_pending);
- #endif
- request->saveStatus(&status);
- }
- catch (const Exception& ex)
- {
- #ifdef DEBUG
- fprintf(stderr, "Got batch error %ld Max message = %d\n",
- ex->value()[1], request->rrq_max_msg);
- #endif
- // Queue errors within the batched request
- request->saveStatus(ex);
- }
- dequeue_receive(port);
- break;
- }
- tail->rrq_msgs_waiting++;
- tail->rrq_rows_pending--;
- #ifdef DEBUG
- fprintf(stdout, "Decrementing Rows Pending in batch_gds_receive=%d\n",
- tail->rrq_rows_pending);
- #endif
- // See if we're at end of the batch
- if (!packet->p_data.p_data_messages)
- {
- if (!(--tail->rrq_batch_count))
- tail->rrq_rows_pending = 0;
- #ifdef DEBUG
- fprintf(stderr, "End of batch waiting %d\n", tail->rrq_rows_pending);
- #endif
- dequeue_receive(port);
- break;
- }
- // one packet is enough unless we are trying to clear the queue
- if (!clear_queue)
- break;
- }
- }
- static void clear_queue(rem_port* port)
- {
- /**************************************
- *
- * c l e a r _ q u e u e
- *
- **************************************
- *
- * Functional description
- * Clear the queue of batched packets - in preparation
- * for waiting for a specific response, or when we are
- * about to reuse an internal request.
- * Return codes:
- * true - no errors.
- * false - Network error occurred, error code in status
- **************************************/
- while (port->port_receive_rmtque)
- {
- receive_queued_packet(port, (USHORT) -1);
- }
- }
- static void finalize(rem_port* port)
- {
- /**************************************
- *
- * f i n a l i z e
- *
- **************************************
- *
- * Functional description
- * Disconnect remote port.
- *
- **************************************/
- // no need to do something if port already detached
- if (port->port_flags & PORT_detached)
- return;
- // Avoid async send during finalize
- RefMutexGuard guard(*port->port_write_sync, FB_FUNCTION);
- // recheck with mutex taken
- if (port->port_flags & PORT_detached)
- return;
- // Send a disconnect to the server so that it
- // gracefully terminates.
- Rdb* rdb = port->port_context;
- if (rdb)
- {
- PACKET* packet = &rdb->rdb_packet;
- // Deliver the pending deferred packets
- if (port->port_deferred_packets)
- {
- for (rem_que_packet* p = port->port_deferred_packets->begin();
- p < port->port_deferred_packets->end();
- p++)
- {
- if (!p->sent)
- port->send(&p->packet);
- }
- }
- packet->p_operation = op_disconnect;
- port->send(packet);
- REMOTE_free_packet(port, packet);
- }
- // Cleanup the queue
- delete port->port_deferred_packets;
- port->port_deferred_packets = nullptr;
- port->port_flags &= ~PORT_lazy;
- port->port_flags |= PORT_detached;
- }
- static void disconnect(rem_port* port, bool rmRef)
- {
- /**************************************
- *
- * d i s c o n n e c t
- *
- **************************************
- *
- * Functional description
- * Disconnect a port and free its memory.
- *
- **************************************/
- finalize(port);
- Rdb* rdb = port->port_context;
- port->port_context = nullptr;
- // Clear context reference for the associated event handler
- // to avoid SEGV during shutdown
- if (port->port_async)
- {
- port->port_async->port_context = NULL;
- port->port_async->port_flags |= PORT_disconnect;
- }
- // Perform physical network disconnect and release
- // memory for remote database context.
- port->port_flags |= PORT_disconnect;
- port->disconnect();
- delete rdb;
- // Remove from active ports
- if (rmRef)
- outPorts->unRegisterPort(port);
- }
- static THREAD_ENTRY_DECLARE event_thread(THREAD_ENTRY_PARAM arg)
- {
- /**************************************
- *
- * e v e n t _ t h r e a d
- *
- **************************************
- *
- * Functional description
- * Wait on auxilary mailbox for event notification.
- *
- **************************************/
- rem_port* port = (rem_port*)arg;
- // Reference portRef(*port);
- PACKET packet;
- while (!(port->port_flags & PORT_disconnect))
- {
- // zero packet
- zap_packet(&packet);
- // read what should be an event message
- rem_port* stuff = NULL;
- P_OP operation = op_void;
- { // scope
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- try
- {
- stuff = port->receive(&packet);
- }
- catch(status_exception&)
- {
- // ignore
- }
- operation = packet.p_operation;
- if (!stuff || operation == op_exit || operation == op_disconnect)
- {
- // Actually, the remote server doing the watching died.
- // Clean up and leave.
- REMOTE_free_packet(port, &packet);
- server_death(port);
- break;
- }
- } // end scope
- // If the packet was an event, we handle it
- if (operation == op_event)
- {
- P_EVENT* pevent = &packet.p_event;
- Rvnt* event = NULL;
- { // scope
- RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
- event = find_event(port, pevent->p_event_rid);
- }
- if (event)
- {
- // Call the asynchronous event routine associated
- // with this event
- const ULONG length = pevent->p_event_items.cstr_length;
- if (length <= event->rvnt_length)
- {
- event->rvnt_callback->eventCallbackFunction(length, pevent->p_event_items.cstr_address);
- }
- //else {....
- //This is error condition, but we have absolutely no ways to report it.
- //Therefore simply ignore such bad packet.
- // Finished processing this event
- // Callback above should release event and another thread could reuse it meanwhile.
- // Make sure we don't release such reused event.
- if (event->rvnt_id == pevent->p_event_rid)
- event->rvnt_id = 0;
- }
- } // end of event handling for op_event
- REMOTE_free_packet(port, &packet);
- } // end of infinite for loop
- // to make compilers happy
- return 0;
- }
- static Rvnt* find_event( rem_port* port, SLONG id)
- {
- /*************************************
- *
- * f i n d _ e v e n t
- *
- **************************************
- *
- * Functional description
- * Find event with specified event_id.
- *
- **************************************/
- Rdb* rdb = port->port_context;
- if (rdb && !(port->port_flags & PORT_disconnect))
- {
- for (Rvnt* event = rdb->rdb_events; event; event = event->rvnt_next)
- {
- if (event->rvnt_id == id)
- return event;
- }
- }
- return NULL;
- }
- static bool get_new_dpb(ClumpletWriter& dpb, const ParametersSet& par, bool loopback)
- {
- /**************************************
- *
- * g e t _ n e w _ d p b
- *
- **************************************
- *
- * Functional description
- * Fetch user_string out of dpb.
- * Analyze and prepare dpb for attachment to remote server.
- *
- **************************************/
- bool redirection = Config::getRedirection();
- if (((loopback || !redirection) && dpb.find(par.address_path)) || dpb.find(par.map_attach))
- {
- status_exception::raise(Arg::Gds(isc_unavailable));
- }
- return dpb.find(par.user_name);
- }
- static void info(CheckStatusWrapper* status,
- Rdb* rdb,
- P_OP operation,
- USHORT object,
- USHORT incarnation,
- USHORT item_length,
- const UCHAR* items,
- USHORT recv_item_length,
- const UCHAR* recv_items,
- ULONG buffer_length,
- UCHAR* buffer)
- {
- /**************************************
- *
- * i n f o
- *
- **************************************
- *
- * Functional description
- * Solicit and receive information.
- *
- **************************************/
- // Build the primary packet to get the operation started.
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = operation;
- P_INFO* information = &packet->p_info;
- information->p_info_object = object;
- information->p_info_incarnation = incarnation;
- information->p_info_items.cstr_length = item_length;
- information->p_info_items.cstr_address = items;
- if (operation == op_service_info)
- {
- information->p_info_recv_items.cstr_length = recv_item_length;
- information->p_info_recv_items.cstr_address = recv_items;
- }
- information->p_info_buffer_length = buffer_length;
- send_packet(rdb->rdb_port, packet);
- // Set up for the response packet.
- P_RESP* response = &packet->p_resp;
- SaveString temp(response->p_resp_data, buffer_length, buffer);
- receive_response(status, rdb, packet);
- }
- static bool useLegacyAuth(const char* nm, int protocol, ClumpletWriter& dpb)
- {
- LegacyPlugin legacyAuth = REMOTE_legacy_auth(nm, protocol);
- if (!legacyAuth)
- return false;
- int requestedAuth = dpb.find(isc_dpb_user_name) ? PLUGIN_LEGACY : PLUGIN_TRUSTED;
- return legacyAuth == requestedAuth;
- }
- // Let plugins try to add data to DPB in order to avoid extra network roundtrip
- static void authFillParametersBlock(ClntAuthBlock& cBlock, ClumpletWriter& dpb,
- const ParametersSet* tags, rem_port* port)
- {
- if (cBlock.authComplete)
- return; // Already authenticated
- LocalStatus ls;
- CheckStatusWrapper s(&ls);
- cBlock.resetDataFromPlugin();
- for (; cBlock.plugins.hasData(); cBlock.plugins.next())
- {
- if (port->port_protocol >= PROTOCOL_VERSION13 ||
- useLegacyAuth(cBlock.plugins.name(), port->port_protocol, dpb))
- {
- // OK to use plugin
- cBlock.resetDataFromPlugin();
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authFillParametersBlock(%s)\n", cBlock.plugins.name()));
- int authRc = cBlock.plugins.plugin()->authenticate(&s, &cBlock);
- switch (authRc)
- {
- case IAuth::AUTH_SUCCESS:
- case IAuth::AUTH_MORE_DATA:
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authFillParametersBlock: plugin %s is OK\n",
- cBlock.plugins.name()));
- cleanDpb(dpb, tags);
- cBlock.extractDataFromPluginTo(dpb, tags, port->port_protocol);
- return;
- case IAuth::AUTH_CONTINUE:
- continue;
- case IAuth::AUTH_FAILED:
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authFillParametersBlock: plugin %s FAILED\n",
- cBlock.plugins.name()));
- (Arg::Gds(isc_login) << Arg::StatusVector(&s)).raise();
- break; // compiler silencer
- }
- }
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authFillParametersBlock: try next plugin, %s skipped\n",
- cBlock.plugins.name()));
- }
- }
- #ifdef NOT_USED_OR_REPLACED
- static CSTRING* REMOTE_dup_string(const CSTRING* from)
- {
- if (from && from->cstr_length)
- {
- CSTRING* rc = FB_NEW_POOL(*getDefaultMemoryPool()) CSTRING;
- memset(rc, 0, sizeof(CSTRING));
- rc->cstr_length = from->cstr_length;
- rc->cstr_allocated = rc->cstr_length;
- rc->cstr_address = FB_NEW_POOL(*getDefaultMemoryPool()) UCHAR[rc->cstr_length];
- memcpy(rc->cstr_address, from->cstr_address, rc->cstr_length);
- return rc;
- }
- return NULL;
- }
- static void REMOTE_free_string(CSTRING* tmp)
- {
- if (tmp)
- {
- if (tmp->cstr_address)
- {
- fb_assert(tmp->cstr_allocated >= tmp->cstr_length);
- delete[] tmp->cstr_address;
- }
- delete tmp;
- }
- }
- #endif // NOT_USED_OR_REPLACED
- static void authReceiveResponse(bool havePacket, ClntAuthBlock& cBlock, rem_port* port,
- Rdb* rdb, IStatus* status, PACKET* packet, bool checkKeys)
- {
- LocalStatus ls;
- CheckStatusWrapper s(&ls);
- for (;;)
- {
- // Get response
- if (!havePacket)
- receive_packet(port, packet);
- else
- fb_assert(packet->p_operation == op_cond_accept);
- havePacket = false; // havePacket means first packet is already received
- // Check response
- cstring* n = NULL;
- cstring* d = NULL;
- switch(packet->p_operation)
- {
- case op_trusted_auth:
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authReceiveResponse: trusted_auth\n"));
- d = &packet->p_trau.p_trau_data;
- break;
- case op_cont_auth:
- d = &packet->p_auth_cont.p_data;
- n = &packet->p_auth_cont.p_name;
- port->addServerKeys(&packet->p_auth_cont.p_keys);
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authReceiveResponse: cont_auth d=%d n=%d '%.*s' 0x%x\n",
- d->cstr_length, n->cstr_length,
- n->cstr_length, n->cstr_address, n->cstr_address ? n->cstr_address[0] : 0));
- break;
- case op_cond_accept:
- d = &packet->p_acpd.p_acpt_data;
- n = &packet->p_acpd.p_acpt_plugin;
- port->addServerKeys(&packet->p_acpd.p_acpt_keys);
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authReceiveResponse: cond_accept d=%d n=%d '%.*s' 0x%x\n",
- d->cstr_length, n->cstr_length,
- n->cstr_length, n->cstr_address, n->cstr_address ? n->cstr_address[0] : 0));
- if (packet->p_acpd.p_acpt_type & pflag_compress)
- {
- port->initCompression();
- port->port_flags |= PORT_compressed;
- }
- packet->p_acpd.p_acpt_type &= ptype_MASK;
- break;
- default:
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authReceiveResponse: Default answer\n"));
- REMOTE_check_response(status, rdb, packet, checkKeys);
- // successfully attached
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authReceiveResponse: OK!\n"));
- cBlock.authComplete = true;
- rdb->rdb_id = packet->p_resp.p_resp_object;
- // try to start crypt
- cBlock.tryNewKeys(port);
- return;
- }
- if (n && n->cstr_length && cBlock.plugins.hasData())
- {
- // if names match, do not change instance
- if (strlen(cBlock.plugins.name()) == n->cstr_length &&
- memcmp(cBlock.plugins.name(), n->cstr_address, n->cstr_length) == 0)
- {
- n = NULL;
- }
- }
- if (n && n->cstr_length)
- {
- // switch to other plugin
- PathName tmp(n->cstr_address, n->cstr_length);
- if (!cBlock.checkPluginName(tmp))
- {
- break;
- }
- cBlock.plugins.set(tmp.c_str());
- }
- if (!cBlock.plugins.hasData())
- {
- break;
- }
- cBlock.resetDataFromPlugin();
- cBlock.storeDataForPlugin(d->cstr_length, d->cstr_address);
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: receiveResponse: authenticate(%s)\n", cBlock.plugins.name()));
- if (cBlock.plugins.plugin()->authenticate(&s, &cBlock) == IAuth::AUTH_FAILED)
- {
- break;
- }
- // send answer (may be empty) to server
- if (port->port_protocol >= PROTOCOL_VERSION13)
- {
- packet->p_operation = op_cont_auth;
- cBlock.extractDataFromPluginTo(&packet->p_auth_cont);
- }
- else
- {
- packet->p_operation = op_trusted_auth;
- cBlock.extractDataFromPluginTo(&packet->p_trau.p_trau_data);
- }
- send_packet(port, packet);
- REMOTE_free_packet(port, packet, true);
- memset(&packet->p_auth_cont, 0, sizeof packet->p_auth_cont);
- }
- // If we have exited from the cycle, this mean auth failed
- (Arg::Gds(isc_login) << Arg::StatusVector(&s)).raise();
- }
- static bool init(CheckStatusWrapper* status, ClntAuthBlock& cBlock, rem_port* port, P_OP op, PathName& file_name,
- ClumpletWriter& dpb, IntlParametersBlock& intlParametersBlock, ICryptKeyCallback* cryptCallback)
- {
- /**************************************
- *
- * i n i t
- *
- **************************************
- *
- * Functional description
- * Initialize for database access. First call from both CREATE and
- * OPEN.
- *
- **************************************/
- try
- {
- Rdb* rdb = port->port_context;
- PACKET* packet = &rdb->rdb_packet;
- MemoryPool& pool = *getDefaultMemoryPool();
- port->port_deferred_packets = FB_NEW_POOL(pool) PacketQueue(pool);
- if (port->port_protocol < PROTOCOL_VERSION12)
- {
- // This is FB < 2.5. Lets remove that not recognized DPB/SPB and convert the UTF8
- // strings to the OS codepage.
- intlParametersBlock.fromUtf8(dpb);
- ISC_unescape(file_name);
- ISC_utf8ToSystem(file_name);
- }
- const ParametersSet* const ps = (op == op_service_attach ? &spbParam : &dpbParam);
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: init calls authFillParametersBlock\n"));
- authFillParametersBlock(cBlock, dpb, ps, port);
- port->port_client_crypt_callback = cryptCallback;
- cBlock.createCryptCallback(&port->port_client_crypt_callback);
- // Make attach packet
- P_ATCH* attach = &packet->p_atch;
- packet->p_operation = op;
- attach->p_atch_file.cstr_length = (ULONG) file_name.length();
- attach->p_atch_file.cstr_address = reinterpret_cast<const UCHAR*>(file_name.c_str());
- attach->p_atch_dpb.cstr_length = (ULONG) dpb.getBufferLength();
- attach->p_atch_dpb.cstr_address = dpb.getBuffer();
- send_packet(port, packet);
- authReceiveResponse(false, cBlock, port, rdb, status, packet, true);
- return true;
- }
- catch (const Exception& ex)
- {
- // report primary init error
- ex.stuffException(status);
- }
- try
- {
- disconnect(port);
- }
- catch (const Exception&)
- {
- // ignore secondary error
- }
- return false;
- }
- static Rtr* make_transaction( Rdb* rdb, USHORT id)
- {
- /**************************************
- *
- * m a k e _ t r a n s a c t i o n
- *
- **************************************
- *
- * Functional description
- * Create a local transaction handle.
- *
- **************************************/
- Rtr* transaction = FB_NEW Rtr;
- transaction->rtr_rdb = rdb;
- transaction->rtr_id = id;
- transaction->rtr_next = rdb->rdb_transactions;
- rdb->rdb_transactions = transaction;
- SET_OBJECT(rdb, transaction, id);
- return transaction;
- }
- static void mov_dsql_message(const UCHAR* from_msg,
- const rem_fmt* from_fmt,
- UCHAR* to_msg,
- const rem_fmt* to_fmt)
- {
- /**************************************
- *
- * m o v _ d s q l _ m e s s a g e
- *
- **************************************
- *
- * Functional description
- * Move data using formats.
- *
- **************************************/
- if (!from_msg || !from_fmt || !to_msg || !to_fmt ||
- from_fmt->fmt_desc.getCount() != to_fmt->fmt_desc.getCount())
- {
- move_error(Arg::Gds(isc_dsql_sqlda_err));
- // Msg 263 SQLDA missing or wrong number of variables
- }
- const dsc* from_desc = from_fmt->fmt_desc.begin();
- const dsc* to_desc = to_fmt->fmt_desc.begin();
- for (const dsc* const end_desc = to_fmt->fmt_desc.end();
- to_desc < end_desc; from_desc++, to_desc++)
- {
- dsc from = *from_desc;
- dsc to = *to_desc;
- // Safe const cast, we are going to move from it to anywhere.
- from.dsc_address = const_cast<UCHAR*>(from_msg) + (IPTR) from.dsc_address;
- to.dsc_address = to_msg + (IPTR) to.dsc_address;
- CVT_move(&from, &to, DecimalStatus(FB_DEC_Errors), move_error);
- }
- }
- static void move_error(const Arg::StatusVector& v)
- {
- /**************************************
- *
- * m o v e _ e r r o r
- *
- **************************************
- *
- * Functional description
- * A conversion error occurred. Complain.
- *
- **************************************/
- Arg::Gds status_vector(isc_random);
- status_vector << "Dynamic SQL Error" << Arg::Gds(isc_sqlerr) << Arg::Num(-303);
- // append any other arguments which may have been handed to us, then post the error
- status_vector.append(v);
- status_exception::raise(status_vector);
- }
- static void receive_after_start(Rrq* request, USHORT msg_type)
- {
- /*****************************************
- *
- * r e c e i v e _ a f t e r _ s t a r t
- *
- *****************************************
- *
- * Functional Description
- * Some opcodes, such as "start_and_send" automatically start the
- * cursor being started, under protcol 8 we then receive the first
- * batch of records without having to ask for them.
- *
- * Note: if a network error occurs during this receive, we do not
- * recognize it in the "gds_start" API call that initiated this
- * action. It will be stored with the queue of records for the
- * cursor that is being fetched. This is not ideal - but compabile
- * with how the code worked prior to pipelining work done
- * 1996-Jul-15 David Schnepper
- *
- *****************************************/
- // Check to see if any data is waiting to happen
- Rdb* rdb = request->rrq_rdb;
- PACKET* packet = &rdb->rdb_packet;
- Rrq::rrq_repeat* tail = &request->rrq_rpt[msg_type];
- // CVC: I commented this line because it's overwritten immediately in the loop.
- // RMessage* message = tail->rrq_message;
- const rem_fmt* format = tail->rrq_format;
- // Swallow up data. If a buffer isn't available, allocate another
- while (true)
- {
- RMessage* message = tail->rrq_xdr;
- if (message->msg_address)
- {
- RMessage* new_msg = FB_NEW RMessage(format->fmt_length);
- tail->rrq_xdr = new_msg;
- new_msg->msg_next = message;
- new_msg->msg_number = message->msg_number;
- while (message->msg_next != new_msg->msg_next)
- message = message->msg_next;
- message->msg_next = new_msg;
- }
- // Note: not receive_packet
- try
- {
- receive_packet_noqueue(rdb->rdb_port, packet);
- }
- catch (const Exception& ex)
- {
- request->saveStatus(ex);
- return;
- }
- // Did an error response come back ?
- if (packet->p_operation != op_send)
- {
- try
- {
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- REMOTE_check_response(&status, rdb, packet);
- request->saveStatus(&status);
- }
- catch (const Exception& ex)
- {
- request->saveStatus(ex);
- }
- return;
- }
- tail->rrq_msgs_waiting++;
- // Reached end of batch
- if (!packet->p_data.p_data_messages)
- break;
- }
- }
- static void receive_packet(rem_port* port, PACKET* packet)
- {
- /**************************************
- *
- * r e c e i v e _ p a c k e t
- *
- **************************************
- *
- * Functional description
- * Clear the queue of any pending receives, then receive the
- * response to a sent request, blocking if necessary until
- * the response is present.
- *
- * Return codes:
- * true - no errors.
- * false - Network error occurred, error code in status
- *
- **************************************/
- // Must clear the wire of any queued receives before fetching
- // the desired packet
- clear_queue(port);
- receive_packet_noqueue(port, packet);
- }
- static void receive_packet_with_callback(rem_port* port, PACKET* packet)
- {
- /**************************************
- *
- * r e c e i v e _ p a c k e t _ w i t h _ c a l l b a c k
- *
- **************************************
- *
- * Functional description
- * If received packet is request from callback info from user,
- * send requested info (or no data if callback is not set) and
- * wait for next packet.
- *
- **************************************/
- UCharBuffer buf;
- for (;;)
- {
- if (!port->receive(packet))
- {
- Arg::Gds(isc_net_read_err).raise();
- }
- switch (packet->p_operation)
- {
- case op_crypt_key_callback:
- {
- P_CRYPT_CALLBACK* cc = &packet->p_cc;
- Cleanup ccData([&cc]() {
- cc->p_cc_data.cstr_length = 0;
- cc->p_cc_data.cstr_address = nullptr;
- });
- if (port->port_client_crypt_callback)
- {
- if (cc->p_cc_reply <= 0)
- {
- cc->p_cc_reply = 1;
- }
- UCHAR* reply = buf.getBuffer(cc->p_cc_reply);
- unsigned l = port->port_client_crypt_callback->callback(cc->p_cc_data.cstr_length,
- cc->p_cc_data.cstr_address, cc->p_cc_reply, reply);
- REMOTE_free_packet(port, packet, true);
- cc->p_cc_data.cstr_length = l;
- cc->p_cc_data.cstr_address = reply;
- }
- else
- {
- REMOTE_free_packet(port, packet, true);
- cc->p_cc_data.cstr_length = 0;
- }
- packet->p_operation = op_crypt_key_callback;
- cc->p_cc_reply = 0;
- port->send(packet);
- }
- break;
- default:
- return;
- }
- }
- }
- static void receive_packet_noqueue(rem_port* port, PACKET* packet)
- {
- /**************************************
- *
- * r e c e i v e _ p a c k e t _ n o q u e u e
- *
- **************************************
- *
- * Functional description
- * Receive a packet and check for a network
- * error on the receive.
- * Note: SOME of the network lower level protocols
- * will set up a status vector when errors
- * occur, but other ones won't.
- * So this routine sets up an error result
- * for the vector prior to going into the
- * network layer. Note that we can't
- * RESET the status vector as one thing
- * that can be received is a new status vector
- *
- * See also cousin routine: send_packet, send_partial_packet
- *
- **************************************/
- // Receive responses for all deferred packets that were already sent
- if (port->port_deferred_packets)
- {
- while (port->port_deferred_packets->getCount())
- {
- rem_que_packet* const p = port->port_deferred_packets->begin();
- if (!p->sent)
- break;
- OBJCT stmt_id = 0;
- bool bCheckResponse = false, bFreeStmt = false, bAssign = false;
- switch (p->packet.p_operation)
- {
- case op_execute:
- stmt_id = p->packet.p_sqldata.p_sqldata_statement;
- bCheckResponse = true;
- bAssign = true;
- break;
- case op_batch_msg:
- stmt_id = p->packet.p_batch_msg.p_batch_statement;
- bCheckResponse = true;
- break;
- case op_batch_create:
- stmt_id = p->packet.p_batch_create.p_batch_statement;
- bCheckResponse = true;
- break;
- case op_free_statement:
- stmt_id = p->packet.p_sqlfree.p_sqlfree_statement;
- bFreeStmt = (p->packet.p_sqlfree.p_sqlfree_option == DSQL_drop);
- break;
- }
- receive_packet_with_callback(port, &p->packet);
- Rsr* statement = NULL;
- if (bCheckResponse || bFreeStmt)
- statement = port->port_objects[stmt_id];
- if (bCheckResponse)
- {
- try
- {
- Rdb* rdb = port->port_context;
- LocalStatus ls;
- CheckStatusWrapper status(&ls);
- REMOTE_check_response(&status, rdb, &p->packet);
- statement->saveException(&status, false);
- }
- catch (const Exception& ex)
- {
- // save error within the corresponding statement
- statement->saveException(ex, false);
- bAssign = false;
- }
- if (bAssign)
- {
- // assign statement to transaction
- const OBJCT tran_id = p->packet.p_sqldata.p_sqldata_transaction;
- Rtr* transaction = port->port_objects[tran_id];
- statement->rsr_rtr = transaction;
- }
- }
- if (bFreeStmt && p->packet.p_resp.p_resp_object == INVALID_OBJECT)
- release_sql_request(statement);
- // free only part of packet we worked with
- REMOTE_free_packet(port, &p->packet, true);
- port->port_deferred_packets->remove(p);
- }
- }
- receive_packet_with_callback(port, packet);
- }
- static void receive_queued_packet(rem_port* port, USHORT id)
- {
- /**************************************
- *
- * r e c e i v e _ q u e u e d_ p a c k e t
- *
- **************************************
- *
- * Functional description
- * We're marked as having pending receives on the
- * wire. Grab the first pending receive and return.
- *
- **************************************/
- // Trivial case, nothing pending on the wire
- if (!port->port_receive_rmtque)
- {
- return;
- }
- // Grab first queue entry
- rmtque* que_inst = port->port_receive_rmtque;
- // Receive the data
- (que_inst->rmtque_function) (port, que_inst, id);
- }
- static void enqueue_receive(rem_port* port,
- t_rmtque_fn fn,
- Rdb* rdb,
- void* parm,
- Rrq::rrq_repeat* parm1)
- {
- /**************************************
- *
- * e n q u e u e _ r e c e i v e
- *
- **************************************
- *
- * Functional description
- *
- **************************************/
- rmtque* const que_inst = FB_NEW rmtque;
- // Prepare a queue entry
- que_inst->rmtque_next = NULL;
- que_inst->rmtque_function = fn;
- que_inst->rmtque_parm = parm;
- que_inst->rmtque_message = parm1;
- que_inst->rmtque_rdb = rdb;
- // Walk to the end of the current queue
- rmtque** queptr = &port->port_receive_rmtque;
- while (*queptr)
- queptr = &(*queptr)->rmtque_next;
- // Add the new entry to the end of the queue
- *queptr = que_inst;
- }
- static void dequeue_receive( rem_port* port)
- {
- /**************************************
- *
- * d e q u e u e _ r e c e i v e
- *
- **************************************
- *
- * Functional description
- *
- **************************************/
- // Grab first queue entry & de-queue it
- rmtque* que_inst = port->port_receive_rmtque;
- port->port_receive_rmtque = que_inst->rmtque_next;
- que_inst->rmtque_next = NULL;
- // Add queue entry onto free queue
- delete que_inst;
- }
- static void receive_response(IStatus* status, Rdb* rdb, PACKET* packet)
- {
- /**************************************
- *
- * r e c e i v e _ r e s p o n s e
- *
- **************************************
- *
- * Functional description
- * Check response to a remote call.
- *
- **************************************/
- receive_packet(rdb->rdb_port, packet);
- REMOTE_check_response(status, rdb, packet);
- }
- static void release_blob( Rbl* blob)
- {
- /**************************************
- *
- * r e l e a s e _ b l o b
- *
- **************************************
- *
- * Functional description
- * Release a blob block and friends.
- *
- **************************************/
- Rtr* transaction = blob->rbl_rtr;
- Rdb* rdb = blob->rbl_rdb;
- rdb->rdb_port->releaseObject(blob->rbl_id);
- for (Rbl** p = &transaction->rtr_blobs; *p; p = &(*p)->rbl_next)
- {
- if (*p == blob)
- {
- *p = blob->rbl_next;
- break;
- }
- }
- delete blob;
- }
- static void release_event( Rvnt* event)
- {
- /**************************************
- *
- * r e l e a s e _ e v e n t
- *
- **************************************
- *
- * Functional description
- * Release an event block.
- *
- **************************************/
- Rdb* rdb = event->rvnt_rdb;
- for (Rvnt** p = &rdb->rdb_events; *p; p = &(*p)->rvnt_next)
- {
- if (*p == event)
- {
- *p = event->rvnt_next;
- break;
- }
- }
- delete event;
- }
- static void release_object(IStatus* status, Rdb* rdb, P_OP op, USHORT id)
- {
- /**************************************
- *
- * r e l e a s e _ o b j e c t
- *
- **************************************
- *
- * Functional description
- * Tell the server to zap an object. This doesn't necessary
- * release the object, but usually does.
- *
- **************************************/
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op;
- packet->p_rlse.p_rlse_object = id;
- if (rdb->rdb_port->port_flags & PORT_lazy)
- {
- switch (op)
- {
- case op_close_blob:
- case op_cancel_blob:
- case op_release:
- defer_packet(rdb->rdb_port, packet);
- return;
- default:
- break;
- }
- }
- send_packet(rdb->rdb_port, packet);
- receive_response(status, rdb, packet);
- }
- static void release_request( Rrq* request)
- {
- /**************************************
- *
- * r e l e a s e _ r e q u e s t
- *
- **************************************
- *
- * Functional description
- * Release a request block and friends.
- *
- **************************************/
- Rdb* rdb = request->rrq_rdb;
- rdb->rdb_port->releaseObject(request->rrq_id);
- REMOTE_release_request(request);
- }
- static void release_statement( Rsr** statement)
- {
- /**************************************
- *
- * r e l e a s e _ s t a t e m e n t
- *
- **************************************
- *
- * Functional description
- * Release a GDML or SQL statement block ?
- *
- **************************************/
- delete (*statement)->rsr_bind_format;
- if ((*statement)->rsr_user_select_format &&
- (*statement)->rsr_user_select_format != (*statement)->rsr_select_format)
- {
- delete (*statement)->rsr_user_select_format;
- }
- delete (*statement)->rsr_select_format;
- (*statement)->releaseException();
- REMOTE_release_messages((*statement)->rsr_message);
- delete *statement;
- *statement = NULL;
- }
- static void release_sql_request( Rsr* statement)
- {
- /**************************************
- *
- * r e l e a s e _ s q l _ r e q u e s t
- *
- **************************************
- *
- * Functional description
- * Release an SQL request block.
- *
- **************************************/
- Rdb* rdb = statement->rsr_rdb;
- rdb->rdb_port->releaseObject(statement->rsr_id);
- for (Rsr** p = &rdb->rdb_sql_requests; *p; p = &(*p)->rsr_next)
- {
- if (*p == statement)
- {
- *p = statement->rsr_next;
- break;
- }
- }
- release_statement(&statement);
- }
- static void release_transaction( Rtr* transaction)
- {
- /**************************************
- *
- * r e l e a s e _ t r a n s a c t i o n
- *
- **************************************
- *
- * Functional description
- * Release a transaction block and friends.
- *
- **************************************/
- Rdb* rdb = transaction->rtr_rdb;
- rdb->rdb_port->releaseObject(transaction->rtr_id);
- while (transaction->rtr_blobs)
- release_blob(transaction->rtr_blobs);
- for (Rtr** p = &rdb->rdb_transactions; *p; p = &(*p)->rtr_next)
- {
- if (*p == transaction)
- {
- *p = transaction->rtr_next;
- break;
- }
- }
- delete transaction;
- }
- static void send_and_receive(IStatus* status, Rdb* rdb, PACKET* packet)
- {
- /**************************************
- *
- * s e n d _ a n d _ r e c e i v e
- *
- **************************************
- *
- * Functional description
- * Send a packet, check status, receive a packet, and check status.
- *
- **************************************/
- send_packet(rdb->rdb_port, packet);
- receive_response(status, rdb, packet);
- }
- static void send_blob(CheckStatusWrapper* status,
- Rbl* blob,
- USHORT buffer_length,
- const UCHAR* buffer)
- {
- /**************************************
- *
- * s e n d _ b l o b
- *
- **************************************
- *
- * Functional description
- * Actually send blob data (which might be buffered)
- *
- **************************************/
- Rdb* rdb = blob->rbl_rdb;
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = op_put_segment;
- // If we aren't passed a buffer address, this is a batch send. Pick up the
- // address and length from the blob buffer and blast away
- if (!buffer)
- {
- buffer = blob->rbl_buffer;
- buffer_length = blob->rbl_ptr - buffer;
- blob->rbl_ptr = blob->rbl_buffer;
- packet->p_operation = op_batch_segments;
- }
- P_SGMT* segment = &packet->p_sgmt;
- CSTRING_CONST temp = segment->p_sgmt_segment;
- segment->p_sgmt_blob = blob->rbl_id;
- segment->p_sgmt_segment.cstr_length = buffer_length;
- segment->p_sgmt_segment.cstr_address = buffer;
- segment->p_sgmt_length = buffer_length;
- send_packet(rdb->rdb_port, packet);
- // restore the string; "buffer" is not referenced anymore, hence no
- // possibility to overwrite it accidentally.
- segment->p_sgmt_segment = temp;
- // Set up for the response packet.
- receive_response(status, rdb, packet);
- }
- static void send_packet(rem_port* port, PACKET* packet)
- {
- /**************************************
- *
- * s e n d _ p a c k e t
- *
- **************************************
- *
- * Functional description
- * Send a packet and check for a network error
- * on the send.
- * Make up a status vector for any error.
- * Note: SOME of the network lower level protocols
- * will set up a status vector when errors
- * occur, but other ones won't.
- * So this routine sets up an error result
- * for the vector and resets it to true
- * if the packet send occurred.
- *
- * See also cousin routine: receive_packet
- *
- **************************************/
- RefMutexGuard guard(*port->port_write_sync, FB_FUNCTION);
- if (port->port_flags & PORT_detached || port->port_state == rem_port::BROKEN)
- {
- (Arg::Gds(isc_net_write_err)
- #ifdef DEV_BUILD
- << Arg::Gds(isc_random) << "port detached"
- #endif
- ).raise();
- }
- // Send packets that were deferred
- if (port->port_deferred_packets)
- {
- for (rem_que_packet* p = port->port_deferred_packets->begin();
- p < port->port_deferred_packets->end();
- ++p)
- {
- if (!p->sent)
- {
- if (!port->send_partial(&p->packet))
- (Arg::Gds(isc_net_write_err) <<
- Arg::Gds(isc_random) << "send_packet/send_partial").raise();
- p->sent = true;
- }
- }
- }
- if (!port->send(packet))
- {
- (Arg::Gds(isc_net_write_err)<< Arg::Gds(isc_random) << "send_packet/send").raise();
- }
- }
- static void send_partial_packet(rem_port* port, PACKET* packet)
- {
- /**************************************
- *
- * s e n d _ p a r t i a l _ p a c k e t
- *
- **************************************
- *
- * Functional description
- * Send a packet and check for a network error
- * on the send.
- * Make up a status vector for any error.
- * Note: SOME of the network lower level protocols
- * will set up a status vector when errors
- * occur, but other ones won't.
- * So this routine sets up an error result
- * for the vector and resets it to true
- * if the packet send occurred.
- *
- * See also cousin routine: receive_packet, send_packet
- *
- **************************************/
- RefMutexGuard guard(*port->port_write_sync, FB_FUNCTION);
- if (port->port_flags & PORT_detached || port->port_state == rem_port::BROKEN)
- {
- (Arg::Gds(isc_net_write_err)
- #ifdef DEV_BUILD
- << Arg::Gds(isc_random) << "port detached"
- #endif
- ).raise();
- }
- // Send packets that were deferred
- if (port->port_deferred_packets)
- {
- for (rem_que_packet* p = port->port_deferred_packets->begin();
- p < port->port_deferred_packets->end(); p++)
- {
- if (!p->sent)
- {
- if (!port->send_partial(&p->packet))
- {
- (Arg::Gds(isc_net_write_err) <<
- Arg::Gds(isc_random) << "send_partial_packet/send_partial").raise();
- }
- p->sent = true;
- }
- }
- }
- if (!port->send_partial(packet))
- {
- (Arg::Gds(isc_net_write_err) <<
- Arg::Gds(isc_random) << "send_partial_packet/send").raise();
- }
- }
- static void server_death(rem_port* port)
- {
- /**************************************
- *
- * s e r v e r _ d e a t h
- *
- **************************************
- *
- * Functional description
- * Received "EOF" from remote server
- * Cleanup events.
- *
- **************************************/
- Rdb* rdb = port->port_context;
- if (rdb && !(port->port_flags & PORT_disconnect))
- {
- for (Rvnt* event = rdb->rdb_events; event; event = event->rvnt_next)
- {
- if (event->rvnt_id)
- {
- event->rvnt_id = 0;
- event->rvnt_callback->eventCallbackFunction(0, NULL);
- }
- }
- }
- }
- static void svcstart(CheckStatusWrapper* status,
- Rdb* rdb,
- P_OP operation,
- USHORT object,
- USHORT incarnation,
- USHORT item_length,
- const UCHAR* items)
- {
- /**************************************
- *
- * s v c s t a r t
- *
- **************************************
- *
- * Functional description
- * Instruct the server to start a service
- *
- **************************************/
- ClumpletWriter send(ClumpletReader::SpbStart, MAX_DPB_SIZE, items, item_length);
- if (rdb->rdb_port->port_protocol < PROTOCOL_VERSION13)
- {
- // This is FB < 3.0. Lets convert the UTF8 strings to the OS codepage.
- IntlSpbStart().fromUtf8(send);
- }
- // Build the primary packet to get the operation started.
- PACKET* packet = &rdb->rdb_packet;
- packet->p_operation = operation;
- P_INFO* information = &packet->p_info;
- information->p_info_object = object;
- information->p_info_incarnation = incarnation;
- information->p_info_items.cstr_length = (ULONG) send.getBufferLength();
- information->p_info_items.cstr_address = send.getBuffer();
- information->p_info_buffer_length = (ULONG) send.getBufferLength();
- send_packet(rdb->rdb_port, packet);
- // Set up for the response packet.
- P_RESP* response = &packet->p_resp;
- SaveString temp(response->p_resp_data, 0, NULL);
- response->p_resp_data.cstr_length = 0;
- receive_response(status, rdb, packet);
- }
- static void unsupported()
- {
- /**************************************
- *
- * u n s u p p o r t e d
- *
- **************************************
- *
- * Functional description
- * No_entrypoint is called if there is not entrypoint for a given routine.
- *
- **************************************/
- Arg::Gds(isc_wish_list).raise();
- }
- static void zap_packet(PACKET* packet)
- {
- /**************************************
- *
- * z a p _ p a c k e t
- *
- **************************************
- *
- * Functional description
- * Zero out a packet block.
- *
- **************************************/
- memset(packet, 0, sizeof(struct packet));
- }
- void Attachment::cancelOperation(CheckStatusWrapper* status, int kind)
- {
- /*************************************
- *
- * G D S _ C A N C E L _ O P E R A T I O N
- *
- **************************************
- *
- * Functional description
- * Asynchronously cancel requests, running with db_handle on remote server.
- *
- **************************************/
- try {
- reset(status);
- CHECK_HANDLE(rdb, isc_bad_db_handle);
- RemPortPtr port(rdb->rdb_port);
- if (kind == fb_cancel_abort)
- {
- port->force_close();
- return;
- }
- if (port->port_protocol < PROTOCOL_VERSION12 || port->port_type != rem_port::INET)
- {
- unsupported();
- }
- Cleanup unlockAsyncLock([this] { --(rdb->rdb_async_lock); });
- if (++(rdb->rdb_async_lock) != 1)
- {
- // Something async already runs
- Arg::Gds(isc_async_active).raise();
- }
- PACKET packet;
- packet.p_operation = op_cancel;
- P_CANCEL_OP* cancel = &packet.p_cancel_op;
- cancel->p_co_kind = kind;
- send_packet(rdb->rdb_port, &packet);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- Rtr* Attachment::remoteTransaction(ITransaction* apiTra)
- {
- Transaction* rt = remoteTransactionInterface(apiTra);
- return rt ? rt->getTransaction() : NULL;
- }
- Transaction* Attachment::remoteTransactionInterface(ITransaction* apiTra)
- {
- if (!apiTra)
- return NULL;
- LocalStatus ls;
- CheckStatusWrapper dummy(&ls);
- ITransaction* valid = apiTra->validate(&dummy, this);
- if (!valid)
- return NULL;
- // If validation is successfull, this means that this attachment and valid transaction
- // use same provider. I.e. the following cast is safe.
- return static_cast<Transaction*>(valid);
- }
- static void cleanDpb(Firebird::ClumpletWriter& dpb, const ParametersSet* tags)
- {
- dpb.deleteWithTag(tags->password);
- dpb.deleteWithTag(tags->password_enc);
- dpb.deleteWithTag(tags->trusted_auth);
- }
- } //namespace Remote
- void ClientPortsCleanup::closePort(rem_port* port)
- {
- RefMutexEnsureUnlock guard(*port->port_sync, FB_FUNCTION);
- if (port->port_flags & PORT_disconnect)
- return;
- if (guard.tryEnter())
- Remote::finalize(port);
- else
- PortsCleanup::closePort(port);
- }
- RmtAuthBlock::RmtAuthBlock(const Firebird::AuthReader::AuthBlock& aBlock)
- : buffer(*getDefaultMemoryPool(), aBlock),
- rdr(*getDefaultMemoryPool(), buffer),
- info(*getDefaultMemoryPool())
- {
- FbLocalStatus st;
- first(&st);
- check(&st);
- }
- const char* RmtAuthBlock::getType()
- {
- return info.type.nullStr();
- }
- const char* RmtAuthBlock::getName()
- {
- return info.name.nullStr();
- }
- const char* RmtAuthBlock::getPlugin()
- {
- return info.plugin.nullStr();
- }
- const char* RmtAuthBlock::getSecurityDb()
- {
- return info.secDb.nullStr();
- }
- const char* RmtAuthBlock::getOriginalPlugin()
- {
- return info.origPlug.nullStr();
- }
- FB_BOOLEAN RmtAuthBlock::next(Firebird::CheckStatusWrapper* status)
- {
- try
- {
- rdr.moveNext();
- return loadInfo();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return FB_FALSE;
- }
- FB_BOOLEAN RmtAuthBlock::first(Firebird::CheckStatusWrapper* status)
- {
- try
- {
- rdr.rewind();
- return loadInfo();
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return FB_FALSE;
- }
- FB_BOOLEAN RmtAuthBlock::loadInfo()
- {
- if (rdr.isEof())
- return FB_FALSE;
- rdr.getInfo(info);
- return FB_TRUE;
- }
- ClntAuthBlock::ClntAuthBlock(const Firebird::PathName* fileName, Firebird::ClumpletReader* dpb,
- const ParametersSet* tags)
- : pluginList(getPool()), serverPluginList(getPool()),
- cliUserName(getPool()), cliPassword(getPool()), cliOrigUserName(getPool()),
- dataForPlugin(getPool()), dataFromPlugin(getPool()),
- cryptKeys(getPool()), dpbConfig(getPool()), dpbPlugins(getPool()),
- createdInterface(nullptr),
- plugins(IPluginManager::TYPE_AUTH_CLIENT), authComplete(false), firstTime(true)
- {
- if (dpb && tags)
- {
- if (dpb->find(tags->config_text))
- dpb->getString(dpbConfig);
- if (dpb->find(tags->plugin_list))
- dpb->getPath(dpbPlugins);
- if (dpb->find(tags->auth_block))
- {
- AuthReader::AuthBlock plain;
- plain.add(dpb->getBytes(), dpb->getClumpLength());
- remAuthBlock.reset(FB_NEW RmtAuthBlock(plain));
- }
- }
- clntConfig = REMOTE_get_config(fileName, &dpbConfig);
- resetClnt();
- }
- void ClntAuthBlock::resetDataFromPlugin()
- {
- dataFromPlugin.clear();
- }
- void ClntAuthBlock::extractDataFromPluginTo(Firebird::ClumpletWriter& dpb,
- const ParametersSet* tags,
- int protocol)
- {
- if (!dataFromPlugin.hasData())
- {
- return;
- }
- PathName pluginName = getPluginName();
- if (protocol >= PROTOCOL_VERSION13)
- {
- if (firstTime)
- {
- fb_assert(tags->plugin_name && tags->plugin_list);
- if (pluginName.hasData())
- dpb.insertString(tags->plugin_name, pluginName);
- dpb.deleteWithTag(tags->plugin_list);
- dpb.insertString(tags->plugin_list, pluginList);
- firstTime = false;
- HANDSHAKE_DEBUG(fprintf(stderr,
- "Cli: extractDataFromPluginTo: first time - added plugName & pluginList\n"));
- }
- fb_assert(tags->specific_data);
- dpb.insertBytes(tags->specific_data, dataFromPlugin.begin(), dataFromPlugin.getCount());
- HANDSHAKE_DEBUG(fprintf(stderr,
- "Cli: extractDataFromPluginTo: Added %u bytes of spec data with tag %d\n",
- static_cast<unsigned>(dataFromPlugin.getCount()), tags->specific_data));
- return;
- }
- if (REMOTE_legacy_auth(pluginName.c_str(), PROTOCOL_VERSION10)) // dataFromPlugin is encrypted password
- {
- fb_assert(tags->password_enc);
- dpb.insertBytes(tags->password_enc, dataFromPlugin.begin(), dataFromPlugin.getCount());
- return;
- }
- fb_assert(REMOTE_legacy_auth(pluginName.c_str(), protocol)); // dataFromPlugin must be trustedAuth
- fb_assert(tags->trusted_auth);
- dpb.insertBytes(tags->trusted_auth, dataFromPlugin.begin(), dataFromPlugin.getCount());
- }
- static inline void makeUtfString(bool uft8Convert, Firebird::string& s)
- {
- if (uft8Convert)
- {
- ISC_systemToUtf8(s);
- }
- ISC_unescape(s);
- }
- void ClntAuthBlock::loadClnt(Firebird::ClumpletWriter& dpb, const ParametersSet* tags)
- {
- bool uft8Convert = !dpb.find(tags->utf8_filename);
- for (dpb.rewind(); !dpb.isEof(); dpb.moveNext())
- {
- const UCHAR t = dpb.getClumpTag();
- if (t == tags->user_name)
- {
- dpb.getString(cliUserName);
- makeUtfString(uft8Convert, cliUserName);
- cliOrigUserName = cliUserName;
- fb_utils::dpbItemUpper(cliUserName);
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: loadClnt: Loaded from PB user = %s(was %s)\n",
- cliUserName.c_str(), cliOrigUserName.c_str()));
- }
- else if (t == tags->password)
- {
- dpb.getString(cliPassword);
- makeUtfString(uft8Convert, cliPassword);
- HANDSHAKE_DEBUG(fprintf(stderr,
- "Cli: loadClnt: Loaded from PB cliPassword = %s\n", cliPassword.c_str()));
- }
- else if (t == tags->encrypt_key)
- {
- HANDSHAKE_DEBUG(fprintf(stderr,
- "Cli: loadClnt: PB contains crypt key\n"));
- }
- }
- dpb.deleteWithTag(tags->password);
- }
- void ClntAuthBlock::extractDataFromPluginTo(CSTRING* to)
- {
- to->cstr_length = (ULONG) dataFromPlugin.getCount();
- to->cstr_address = dataFromPlugin.begin();
- to->cstr_allocated = 0;
- }
- void ClntAuthBlock::extractDataFromPluginTo(P_AUTH_CONT* to)
- {
- extractDataFromPluginTo(&to->p_data);
- PathName pluginName = getPluginName();
- to->p_name.cstr_length = (ULONG) pluginName.length();
- to->p_name.cstr_address = FB_NEW_POOL(*getDefaultMemoryPool()) UCHAR[to->p_name.cstr_length];
- to->p_name.cstr_allocated = to->p_name.cstr_length;
- memcpy(to->p_name.cstr_address, pluginName.c_str(), to->p_name.cstr_length);
- HANDSHAKE_DEBUG(fprintf(stderr, "Cli: extractDataFromPluginTo: added plugin name (%d) and data (%d)\n",
- to->p_name.cstr_length, to->p_data.cstr_length));
- if (firstTime)
- {
- to->p_list.cstr_length = (ULONG) pluginList.length();
- to->p_list.cstr_address = (UCHAR*) pluginList.c_str();
- to->p_list.cstr_allocated = 0;
- HANDSHAKE_DEBUG(fprintf(stderr,
- "Cli: extractDataFromPluginTo: added plugin list (%d len) to packet\n",
- to->p_list.cstr_length));
- firstTime = false;
- }
- else
- {
- to->p_list.cstr_length = 0;
- }
- }
- const char* ClntAuthBlock::getLogin()
- {
- return cliUserName.nullStr();
- }
- const char* ClntAuthBlock::getPassword()
- {
- return cliPassword.nullStr();
- }
- IAuthBlock* ClntAuthBlock::getAuthBlock(CheckStatusWrapper* status)
- {
- return remAuthBlock;
- }
- const unsigned char* ClntAuthBlock::getData(unsigned int* length)
- {
- *length = (ULONG) dataForPlugin.getCount();
- return *length ? dataForPlugin.begin() : NULL;
- }
- void ClntAuthBlock::putData(CheckStatusWrapper* status, unsigned int length, const void* data)
- {
- try
- {
- void* to = dataFromPlugin.getBuffer(length);
- memcpy(to, data, length);
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- }
- bool ClntAuthBlock::checkPluginName(Firebird::PathName& nameToCheck)
- {
- Firebird::ParsedList parsed(pluginList);
- for (unsigned i = 0; i < parsed.getCount(); ++i)
- {
- if (parsed[i] == nameToCheck)
- {
- return true;
- }
- }
- return false;
- }
- Firebird::ICryptKey* ClntAuthBlock::newKey(CheckStatusWrapper* status)
- {
- status->init();
- try
- {
- InternalCryptKey* k = FB_NEW InternalCryptKey;
- fb_assert(plugins.hasData());
- k->keyName = plugins.name();
- WIRECRYPT_DEBUG(fprintf(stderr, "Cli: newkey %s\n", k->keyName.c_str());)
- cryptKeys.add(k);
- return k;
- }
- catch (const Exception& ex)
- {
- ex.stuffException(status);
- }
- return NULL;
- }
- void ClntAuthBlock::tryNewKeys(rem_port* port)
- {
- for (unsigned k = cryptKeys.getCount(); k--; )
- {
- if (port->tryNewKey(cryptKeys[k]))
- {
- releaseKeys(k);
- cryptKeys.clear();
- return;
- }
- }
- cryptKeys.clear();
- }
- void ClntAuthBlock::releaseKeys(unsigned from)
- {
- while (from < cryptKeys.getCount())
- {
- delete cryptKeys[from++];
- }
- }
- void ClntAuthBlock::createCryptCallback(Firebird::ICryptKeyCallback** callback)
- {
- if (*callback)
- return;
- *callback = clientCrypt.create(clntConfig);
- if (*callback)
- createdInterface = callback;
- }
- Firebird::ICryptKeyCallback* ClntAuthBlock::ClientCrypt::create(const Config* conf)
- {
- pluginItr.set(conf);
- return pluginItr.hasData() ? this : nullptr;
- }
- unsigned ClntAuthBlock::ClientCrypt::callback(unsigned dlen, const void* data, unsigned blen, void* buffer)
- {
- HANDSHAKE_DEBUG(fprintf(stderr, "dlen=%d blen=%d\n", dlen, blen));
- int loop = 0;
- while (loop < 2)
- {
- for (; pluginItr.hasData(); pluginItr.next())
- {
- if (!currentIface)
- {
- LocalStatus ls;
- CheckStatusWrapper st(&ls);
- HANDSHAKE_DEBUG(fprintf(stderr, "Try plugin %s\n", pluginItr.name()));
- currentIface = pluginItr.plugin()->chainHandle(&st);
- // if plugin does not support chaining - silently ignore it
- check(&st, isc_wish_list);
- HANDSHAKE_DEBUG(fprintf(stderr, "Use plugin %s, ptr=%p\n", pluginItr.name(), currentIface));
- }
- // if we have an iface - try it
- if (currentIface)
- {
- unsigned retlen = currentIface->callback(dlen, data, blen, buffer);
- HANDSHAKE_DEBUG(fprintf(stderr, "Iface %p returned %d\n", currentIface, retlen));
- if (retlen)
- return retlen;
- }
- // no success with iface - clear it
- // appropriate data structures to be released by plugin cleanup code
- currentIface = nullptr;
- }
- ++loop;
- // prepare iterator for next use
- pluginItr.rewind();
- }
- // no luck with suggested data
- return 0;
- }
|