TdsParser.cs 417 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279
  1. //------------------------------------------------------------------------------
  2. // <copyright file="TdsParser.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data.SqlClient {
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Data.Common;
  12. using System.Data.ProviderBase;
  13. using System.Data.Sql;
  14. using System.Data.SqlTypes;
  15. using System.Diagnostics;
  16. using System.Globalization;
  17. using System.IO;
  18. using System.Runtime.CompilerServices;
  19. using System.Runtime.InteropServices;
  20. using System.Text;
  21. using System.Threading;
  22. using System.Threading.Tasks;
  23. using System.Xml;
  24. using MSS = Microsoft.SqlServer.Server;
  25. // The TdsParser Object controls reading/writing to the netlib, parsing the tds,
  26. // and surfacing objects to the user.
  27. sealed internal class TdsParser {
  28. private static int _objectTypeCount; // Bid counter
  29. internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
  30. static Task completedTask;
  31. static Task CompletedTask {
  32. get {
  33. if (completedTask == null) {
  34. completedTask = Task.FromResult<object>(null);
  35. }
  36. return completedTask;
  37. }
  38. }
  39. internal int ObjectID {
  40. get {
  41. return _objectID;
  42. }
  43. }
  44. // ReliabilitySection Usage:
  45. //
  46. // #if DEBUG
  47. // TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  48. //
  49. // RuntimeHelpers.PrepareConstrainedRegions();
  50. // try {
  51. // tdsReliabilitySection.Start();
  52. // #else
  53. // {
  54. // #endif //DEBUG
  55. //
  56. // // code that requires reliability
  57. //
  58. // }
  59. // #if DEBUG
  60. // finally {
  61. // tdsReliabilitySection.Stop();
  62. // }
  63. // #endif //DEBUG
  64. internal struct ReliabilitySection {
  65. #if DEBUG
  66. // do not allocate TLS data in RETAIL bits
  67. [ThreadStatic]
  68. private static int s_reliabilityCount; // initialized to 0 by CLR
  69. private bool m_started; // initialized to false (not started) by CLR
  70. #endif //DEBUG
  71. [Conditional("DEBUG")]
  72. internal void Start() {
  73. #if DEBUG
  74. Debug.Assert(!m_started);
  75. RuntimeHelpers.PrepareConstrainedRegions();
  76. try {
  77. }
  78. finally {
  79. ++s_reliabilityCount;
  80. m_started = true;
  81. }
  82. #endif //DEBUG
  83. }
  84. [Conditional("DEBUG")]
  85. internal void Stop() {
  86. #if DEBUG
  87. // cannot assert m_started - ThreadAbortException can be raised before Start is called
  88. if (m_started) {
  89. Debug.Assert(s_reliabilityCount > 0);
  90. RuntimeHelpers.PrepareConstrainedRegions();
  91. try {
  92. }
  93. finally {
  94. --s_reliabilityCount;
  95. m_started = false;
  96. }
  97. }
  98. #endif //DEBUG
  99. }
  100. // you need to setup for a thread abort somewhere before you call this method
  101. [Conditional("DEBUG")]
  102. internal static void Assert(string message) {
  103. #if DEBUG
  104. Debug.Assert(s_reliabilityCount > 0, message);
  105. #endif //DEBUG
  106. }
  107. }
  108. // Default state object for parser
  109. internal TdsParserStateObject _physicalStateObj = null; // Default stateObj and connection for Dbnetlib and non-MARS SNI.
  110. // Also, default logical stateObj and connection for MARS over SNI.
  111. internal TdsParserStateObject _pMarsPhysicalConObj = null; // With MARS enabled, cached physical stateObj and connection.
  112. // Must keep this around - especially for callbacks on pre-MARS
  113. // ReadAsync which will return if physical connection broken!
  114. //
  115. // Per Instance TDS Parser variables
  116. //
  117. // Constants
  118. const int constBinBufferSize = 4096; // Size of the buffer used to read input parameter of type Stream
  119. const int constTextBufferSize = 4096; // Size of the buffer (in chars) user to read input parameter of type TextReader
  120. // State variables
  121. internal TdsParserState _state = TdsParserState.Closed; // status flag for connection
  122. private string _server = ""; // name of server that the parser connects to
  123. internal volatile bool _fResetConnection = false; // flag to denote whether we are needing to call sp_reset
  124. internal volatile bool _fPreserveTransaction = false; // flag to denote whether we need to preserve the transaction when reseting
  125. private SqlCollation _defaultCollation; // default collation from the server
  126. private int _defaultCodePage;
  127. private int _defaultLCID;
  128. internal Encoding _defaultEncoding = null; // for sql character data
  129. private static EncryptionOptions _sniSupportedEncryptionOption = SNILoadHandle.SingletonInstance.Options;
  130. private EncryptionOptions _encryptionOption = _sniSupportedEncryptionOption;
  131. private SqlInternalTransaction _currentTransaction;
  132. private SqlInternalTransaction _pendingTransaction; // pending transaction for Yukon and beyond.
  133. // SQLHOT 483
  134. // need to hold on to the transaction id if distributed transaction merely rolls back without defecting.
  135. private long _retainedTransactionId = SqlInternalTransaction.NullTransactionId;
  136. // This counter is used for the entire connection to track the open result count for all
  137. // operations not under a transaction.
  138. private int _nonTransactedOpenResultCount = 0;
  139. // Connection reference
  140. private SqlInternalConnectionTds _connHandler;
  141. // Async/Mars variables
  142. private bool _fMARS = false;
  143. internal bool _loginWithFailover = false; // set to true while connect in failover mode so parser state object can adjust its logic
  144. internal AutoResetEvent _resetConnectionEvent = null; // Used to serialize executes and call reset on first execute only.
  145. internal TdsParserSessionPool _sessionPool = null; // initialized only when we're a MARS parser.
  146. // Version variables
  147. private bool _isShiloh = false; // set to true if we connect to a 8.0 server (SQL 2000) or later
  148. private bool _isShilohSP1 = false; // set to true if speaking to Shiloh SP1 or later
  149. private bool _isYukon = false; // set to true if speaking to Yukon or later
  150. private bool _isKatmai = false;
  151. private bool _isDenali = false;
  152. private byte[] _sniSpnBuffer = null;
  153. //
  154. // SqlStatistics
  155. private SqlStatistics _statistics = null;
  156. private bool _statisticsIsInTransaction = false;
  157. //
  158. // STATIC TDS Parser variables
  159. //
  160. // NIC address caching
  161. private static byte[] s_nicAddress; // cache the NIC address from the registry
  162. // SSPI variables
  163. private static bool s_fSSPILoaded = false; // bool to indicate whether library has been loaded
  164. private volatile static UInt32 s_maxSSPILength = 0; // variable to hold max SSPI data size, keep for token from server
  165. // textptr sequence
  166. private static readonly byte[] s_longDataHeader = { 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  167. private static object s_tdsParserLock = new object();
  168. // Various other statics
  169. private const int ATTENTION_TIMEOUT = 5000; // internal attention timeout, in ticks
  170. // XML metadata substitue sequence
  171. private static readonly byte[] s_xmlMetadataSubstituteSequence = { 0xe7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 };
  172. // size of Guid (e.g. _clientConnectionId, ActivityId.Id)
  173. private const int GUID_SIZE = 16;
  174. // NOTE: You must take the internal connection's _parserLock before modifying this
  175. internal bool _asyncWrite = false;
  176. internal TdsParser(bool MARS, bool fAsynchronous) {
  177. _fMARS = MARS; // may change during Connect to pre Yukon servers
  178. _physicalStateObj = new TdsParserStateObject(this);
  179. }
  180. internal SqlInternalConnectionTds Connection {
  181. get {
  182. return _connHandler;
  183. }
  184. }
  185. internal SqlInternalTransaction CurrentTransaction {
  186. get {
  187. return _currentTransaction;
  188. }
  189. set {
  190. Debug.Assert(value == _currentTransaction
  191. || null == _currentTransaction
  192. || null == value
  193. || (null != _currentTransaction && !_currentTransaction.IsLocal), "attempting to change current transaction?");
  194. // If there is currently a transaction active, we don't want to
  195. // change it; this can occur when there is a delegated transaction
  196. // and the user attempts to do an API begin transaction; in these
  197. // cases, it's safe to ignore the set.
  198. if ((null == _currentTransaction && null != value)
  199. ||(null != _currentTransaction && null == value)) {
  200. _currentTransaction = value;
  201. }
  202. }
  203. }
  204. internal int DefaultLCID {
  205. get {
  206. return _defaultLCID;
  207. }
  208. }
  209. internal EncryptionOptions EncryptionOptions {
  210. get {
  211. return _encryptionOption;
  212. }
  213. set {
  214. _encryptionOption = value;
  215. }
  216. }
  217. internal bool IsYukonOrNewer {
  218. get {
  219. return _isYukon;
  220. }
  221. }
  222. internal bool IsKatmaiOrNewer {
  223. get {
  224. return _isKatmai;
  225. }
  226. }
  227. internal bool MARSOn {
  228. get {
  229. return _fMARS;
  230. }
  231. }
  232. internal SqlInternalTransaction PendingTransaction {
  233. get {
  234. return _pendingTransaction;
  235. }
  236. set {
  237. Debug.Assert (null != value, "setting a non-null PendingTransaction?");
  238. _pendingTransaction = value;
  239. }
  240. }
  241. internal string Server {
  242. get {
  243. return _server;
  244. }
  245. }
  246. internal TdsParserState State {
  247. get {
  248. return _state;
  249. }
  250. set {
  251. _state = value;
  252. }
  253. }
  254. internal SqlStatistics Statistics {
  255. get {
  256. return _statistics;
  257. }
  258. set {
  259. _statistics = value;
  260. }
  261. }
  262. private bool IncludeTraceHeader {
  263. get {
  264. return (_isDenali && Bid.TraceOn && Bid.IsOn(ActivityCorrelator.CorrelationTracePoints));
  265. }
  266. }
  267. internal int IncrementNonTransactedOpenResultCount() {
  268. // IMPORTANT - this increments the connection wide open result count for all
  269. // operations not under a transaction! Do not call if you intend to modify the
  270. // count for a transaction!
  271. Debug.Assert(_nonTransactedOpenResultCount >= 0, "Unexpected result count state");
  272. int result = Interlocked.Increment(ref _nonTransactedOpenResultCount);
  273. return result;
  274. }
  275. internal void DecrementNonTransactedOpenResultCount() {
  276. // IMPORTANT - this decrements the connection wide open result count for all
  277. // operations not under a transaction! Do not call if you intend to modify the
  278. // count for a transaction!
  279. Interlocked.Decrement(ref _nonTransactedOpenResultCount);
  280. Debug.Assert(_nonTransactedOpenResultCount >= 0, "Unexpected result count state");
  281. }
  282. internal void ProcessPendingAck(TdsParserStateObject stateObj) {
  283. if (stateObj._attentionSent) {
  284. ProcessAttention(stateObj);
  285. }
  286. }
  287. internal void Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, bool ignoreSniOpenTimeout, long timerExpire, bool encrypt, bool trustServerCert, bool integratedSecurity, bool withFailover) {
  288. if (_state != TdsParserState.Closed) {
  289. Debug.Assert(false, "TdsParser.Connect called on non-closed connection!");
  290. return;
  291. }
  292. _connHandler = connHandler;
  293. _loginWithFailover = withFailover;
  294. UInt32 sniStatus = SNILoadHandle.SingletonInstance.SNIStatus;
  295. if (sniStatus != TdsEnums.SNI_SUCCESS) {
  296. _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
  297. _physicalStateObj.Dispose();
  298. ThrowExceptionAndWarning(_physicalStateObj);
  299. Debug.Assert(false, "SNI returned status != success, but no error thrown?");
  300. }
  301. //Create LocalDB instance if necessary
  302. if (connHandler.ConnectionOptions.LocalDBInstance != null)
  303. LocalDBAPI.CreateLocalDBInstance(connHandler.ConnectionOptions.LocalDBInstance);
  304. if (integratedSecurity) {
  305. LoadSSPILibrary();
  306. // now allocate proper length of buffer
  307. _sniSpnBuffer = new byte[SNINativeMethodWrapper.SniMaxComposedSpnLength];
  308. Bid.Trace("<sc.TdsParser.Connect|SEC> SSPI authentication\n");
  309. }
  310. else {
  311. _sniSpnBuffer = null;
  312. Bid.Trace("<sc.TdsParser.Connect|SEC> SQL authentication\n");
  313. }
  314. byte[] instanceName = null;
  315. Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point.");
  316. _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.PreLoginBegin);
  317. _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.InitializeConnection);
  318. bool fParallel = _connHandler.ConnectionOptions.MultiSubnetFailover;
  319. _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire,
  320. out instanceName, _sniSpnBuffer, false, true, fParallel);
  321. if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) {
  322. _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
  323. // Since connect failed, free the unmanaged connection memory.
  324. // HOWEVER - only free this after the netlib error was processed - if you
  325. // don't, the memory for the connection object might not be accurate and thus
  326. // a bad error could be returned (as it was when it was freed to early for me).
  327. _physicalStateObj.Dispose();
  328. Bid.Trace("<sc.TdsParser.Connect|ERR|SEC> Login failure\n");
  329. ThrowExceptionAndWarning(_physicalStateObj);
  330. Debug.Assert(false, "SNI returned status != success, but no error thrown?");
  331. }
  332. _server = serverInfo.ResolvedServerName;
  333. if (null != connHandler.PoolGroupProviderInfo) {
  334. // If we are pooling, check to see if we were processing an
  335. // alias which has changed, which means we need to clean out
  336. // the pool. See Webdata 104293.
  337. // This should not apply to routing, as it is not an alias change, routed connection
  338. // should still use VNN of AlwaysOn cluster as server for pooling purposes.
  339. connHandler.PoolGroupProviderInfo.AliasCheck(serverInfo.PreRoutingServerName==null ?
  340. serverInfo.ResolvedServerName: serverInfo.PreRoutingServerName);
  341. }
  342. _state = TdsParserState.OpenNotLoggedIn;
  343. _physicalStateObj.SniContext = SniContext.Snix_PreLoginBeforeSuccessfullWrite; // SQL BU DT 376766
  344. _physicalStateObj.TimeoutTime = timerExpire;
  345. bool marsCapable = false;
  346. _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.InitializeConnection);
  347. _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake);
  348. UInt32 result = SNINativeMethodWrapper.SniGetConnectionId(_physicalStateObj.Handle, ref _connHandler._clientConnectionId);
  349. Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId");
  350. //
  351. SendPreLoginHandshake(instanceName, encrypt);
  352. _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake);
  353. _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake);
  354. _physicalStateObj.SniContext = SniContext.Snix_PreLogin;
  355. PreLoginHandshakeStatus status = ConsumePreLoginHandshake(encrypt, trustServerCert, integratedSecurity, out marsCapable);
  356. if (status == PreLoginHandshakeStatus.InstanceFailure) {
  357. _physicalStateObj.Dispose(); // Close previous connection
  358. // On Instance failure re-connect and flush SNI named instance cache.
  359. _physicalStateObj.SniContext=SniContext.Snix_Connect;
  360. _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, _sniSpnBuffer, true, true, fParallel);
  361. if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) {
  362. _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
  363. Bid.Trace("<sc.TdsParser.Connect|ERR|SEC> Login failure\n");
  364. ThrowExceptionAndWarning(_physicalStateObj);
  365. }
  366. UInt32 retCode = SNINativeMethodWrapper.SniGetConnectionId(_physicalStateObj.Handle, ref _connHandler._clientConnectionId);
  367. Debug.Assert(retCode == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId");
  368. SendPreLoginHandshake(instanceName, encrypt);
  369. status = ConsumePreLoginHandshake(encrypt, trustServerCert, integratedSecurity, out marsCapable);
  370. // Don't need to check for Sphinx failure, since we've already consumed
  371. // one pre-login packet and know we are connecting to Shiloh.
  372. if (status == PreLoginHandshakeStatus.InstanceFailure) {
  373. Bid.Trace("<sc.TdsParser.Connect|ERR|SEC> Login failure\n");
  374. throw SQL.InstanceFailure();
  375. }
  376. }
  377. if (_fMARS && marsCapable) {
  378. // if user explictly disables mars or mars not supported, don't create the session pool
  379. _sessionPool = new TdsParserSessionPool(this);
  380. }
  381. else {
  382. _fMARS = false;
  383. }
  384. return;
  385. }
  386. internal void RemoveEncryption() {
  387. Debug.Assert(_encryptionOption == EncryptionOptions.LOGIN, "Invalid encryption option state");
  388. UInt32 error = 0;
  389. // Remove SSL (Encryption) SNI provider since we only wanted to encrypt login.
  390. error = SNINativeMethodWrapper.SNIRemoveProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV);
  391. if (error != TdsEnums.SNI_SUCCESS) {
  392. _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
  393. ThrowExceptionAndWarning(_physicalStateObj);
  394. }
  395. // create a new packet encryption changes the internal packet size Bug# 228403
  396. try {} // EmptyTry/Finally to avoid FXCop violation
  397. finally {
  398. _physicalStateObj.ClearAllWritePackets();
  399. }
  400. }
  401. internal void EnableMars() {
  402. if (_fMARS) {
  403. // Cache physical stateObj and connection.
  404. _pMarsPhysicalConObj = _physicalStateObj;
  405. UInt32 error = 0;
  406. UInt32 info = 0;
  407. // Add SMUX (MARS) SNI provider.
  408. error = SNINativeMethodWrapper.SNIAddProvider(_pMarsPhysicalConObj.Handle, SNINativeMethodWrapper.ProviderEnum.SMUX_PROV, ref info);
  409. if (error != TdsEnums.SNI_SUCCESS) {
  410. _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
  411. ThrowExceptionAndWarning(_physicalStateObj);
  412. }
  413. // HACK HACK HACK - for Async only
  414. // Have to post read to intialize MARS - will get callback on this when connection goes
  415. // down or is closed.
  416. IntPtr temp = IntPtr.Zero;
  417. RuntimeHelpers.PrepareConstrainedRegions();
  418. try {} finally {
  419. _pMarsPhysicalConObj.IncrementPendingCallbacks();
  420. error = SNINativeMethodWrapper.SNIReadAsync(_pMarsPhysicalConObj.Handle, ref temp);
  421. if (temp != IntPtr.Zero) {
  422. // Be sure to release packet, otherwise it will be leaked by native.
  423. SNINativeMethodWrapper.SNIPacketRelease(temp);
  424. }
  425. }
  426. Debug.Assert(IntPtr.Zero == temp, "unexpected syncReadPacket without corresponding SNIPacketRelease");
  427. if (TdsEnums.SNI_SUCCESS_IO_PENDING != error) {
  428. Debug.Assert(TdsEnums.SNI_SUCCESS != error, "Unexpected successfull read async on physical connection before enabling MARS!");
  429. _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
  430. ThrowExceptionAndWarning(_physicalStateObj);
  431. }
  432. _physicalStateObj = CreateSession(); // Create and open default MARS stateObj and connection.
  433. }
  434. }
  435. internal TdsParserStateObject CreateSession() {
  436. TdsParserStateObject session = new TdsParserStateObject(this, (SNIHandle)_pMarsPhysicalConObj.Handle, true);
  437. if (Bid.AdvancedOn) {
  438. Bid.Trace("<sc.TdsParser.CreateSession|ADV> %d# created session %d\n", ObjectID, session.ObjectID);
  439. }
  440. return session;
  441. }
  442. internal TdsParserStateObject GetSession(object owner) {
  443. TdsParserStateObject session = null;
  444. //
  445. if (MARSOn) {
  446. session = _sessionPool.GetSession(owner);
  447. Debug.Assert(!session._pendingData, "pending data on a pooled MARS session");
  448. if (Bid.AdvancedOn) {
  449. Bid.Trace("<sc.TdsParser.GetSession|ADV> %d# getting session %d from pool\n", ObjectID, session.ObjectID);
  450. }
  451. }
  452. else {
  453. session = _physicalStateObj;
  454. if (Bid.AdvancedOn) {
  455. Bid.Trace("<sc.TdsParser.GetSession|ADV> %d# getting physical session %d\n", ObjectID, session.ObjectID);
  456. }
  457. }
  458. Debug.Assert(session._outputPacketNumber==1, "The packet number is expected to be 1");
  459. return session;
  460. }
  461. internal void PutSession(TdsParserStateObject session) {
  462. session.AssertStateIsClean();
  463. if (MARSOn) {
  464. // This will take care of disposing if the parser is closed
  465. _sessionPool.PutSession(session);
  466. }
  467. else if ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken)) {
  468. // Parser is closed\broken - dispose the stateObj
  469. Debug.Assert(session == _physicalStateObj, "MARS is off, but session to close is not the _physicalStateObj");
  470. _physicalStateObj.SniContext = SniContext.Snix_Close;
  471. #if DEBUG
  472. _physicalStateObj.InvalidateDebugOnlyCopyOfSniContext();
  473. #endif
  474. _physicalStateObj.Dispose();
  475. }
  476. else {
  477. // Non-MARS, and session is ok - remove its owner
  478. _physicalStateObj.Owner = null;
  479. }
  480. }
  481. // This is called from a ThreadAbort - ensure that it can be run from a CER Catch
  482. internal void BestEffortCleanup() {
  483. _state = TdsParserState.Broken;
  484. var stateObj = _physicalStateObj;
  485. if (stateObj != null) {
  486. var stateObjHandle = stateObj.Handle;
  487. if (stateObjHandle != null) {
  488. stateObjHandle.Dispose();
  489. }
  490. }
  491. if (_fMARS) {
  492. var sessionPool = _sessionPool;
  493. if (sessionPool != null) {
  494. sessionPool.BestEffortCleanup();
  495. }
  496. var marsStateObj = _pMarsPhysicalConObj;
  497. if (marsStateObj != null) {
  498. var marsStateObjHandle = marsStateObj.Handle;
  499. if (marsStateObjHandle != null) {
  500. marsStateObjHandle.Dispose();
  501. }
  502. }
  503. }
  504. }
  505. private void SendPreLoginHandshake(byte[] instanceName, bool encrypt) {
  506. // PreLoginHandshake buffer consists of:
  507. // 1) Standard header, with type = MT_PRELOGIN
  508. // 2) Consecutive 5 bytes for each option, (1 byte length, 2 byte offset, 2 byte payload length)
  509. // 3) Consecutive data blocks for each option
  510. // NOTE: packet data needs to be big endian - not the standard little endian used by
  511. // the rest of the parser.
  512. _physicalStateObj._outputMessageType = TdsEnums.MT_PRELOGIN;
  513. // Initialize option offset into payload buffer
  514. // 5 bytes for each option (1 byte length, 2 byte offset, 2 byte payload length)
  515. int offset = (int)PreLoginOptions.NUMOPT * 5 + 1;
  516. byte[] payload = new byte[(int)PreLoginOptions.NUMOPT * 5 + TdsEnums.MAX_PRELOGIN_PAYLOAD_LENGTH];
  517. int payloadLength = 0;
  518. //
  519. for (int option = (int)PreLoginOptions.VERSION; option < (int)PreLoginOptions.NUMOPT; option++) {
  520. int optionDataSize = 0;
  521. // Fill in the option
  522. _physicalStateObj.WriteByte((byte)option);
  523. // Fill in the offset of the option data
  524. _physicalStateObj.WriteByte((byte)((offset & 0xff00) >> 8)); // send upper order byte
  525. _physicalStateObj.WriteByte((byte)(offset & 0x00ff)); // send lower order byte
  526. switch (option) {
  527. case (int)PreLoginOptions.VERSION:
  528. Version systemDataVersion = ADP.GetAssemblyVersion();
  529. // Major and minor
  530. payload[payloadLength++] = (byte)(systemDataVersion.Major & 0xff);
  531. payload[payloadLength++] = (byte)(systemDataVersion.Minor & 0xff);
  532. // Build (Big Endian)
  533. payload[payloadLength++] = (byte)((systemDataVersion.Build & 0xff00) >> 8);
  534. payload[payloadLength++] = (byte)(systemDataVersion.Build & 0xff);
  535. // Sub-build (Little Endian)
  536. payload[payloadLength++] = (byte)(systemDataVersion.Revision & 0xff);
  537. payload[payloadLength++] = (byte)((systemDataVersion.Revision & 0xff00) >> 8);
  538. offset += 6;
  539. optionDataSize = 6;
  540. break;
  541. case (int)PreLoginOptions.ENCRYPT:
  542. if (_encryptionOption == EncryptionOptions.NOT_SUP) {
  543. // If OS doesn't support encryption, inform server not supported.
  544. payload[payloadLength] = (byte)EncryptionOptions.NOT_SUP;
  545. }
  546. else {
  547. // Else, inform server of user request.
  548. if (encrypt) {
  549. payload[payloadLength] = (byte)EncryptionOptions.ON;
  550. _encryptionOption = EncryptionOptions.ON;
  551. }
  552. else {
  553. payload[payloadLength] = (byte)EncryptionOptions.OFF;
  554. _encryptionOption = EncryptionOptions.OFF;
  555. }
  556. }
  557. payloadLength += 1;
  558. offset += 1;
  559. optionDataSize = 1;
  560. break;
  561. case (int)PreLoginOptions.INSTANCE:
  562. int i = 0;
  563. while (instanceName[i] != 0) {
  564. payload[payloadLength] = instanceName[i];
  565. payloadLength++;
  566. i++;
  567. }
  568. payload[payloadLength] = 0; // null terminate
  569. payloadLength++;
  570. i++;
  571. offset += i;
  572. optionDataSize = i;
  573. break;
  574. case (int)PreLoginOptions.THREADID:
  575. Int32 threadID = TdsParserStaticMethods.GetCurrentThreadIdForTdsLoginOnly();
  576. payload[payloadLength++] = (byte)((0xff000000 & threadID) >> 24);
  577. payload[payloadLength++] = (byte)((0x00ff0000 & threadID) >> 16);
  578. payload[payloadLength++] = (byte)((0x0000ff00 & threadID) >> 8);
  579. payload[payloadLength++] = (byte)(0x000000ff & threadID);
  580. offset += 4;
  581. optionDataSize = 4;
  582. break;
  583. case (int)PreLoginOptions.MARS:
  584. payload[payloadLength++] = (byte)(_fMARS ? 1 : 0);
  585. offset += 1;
  586. optionDataSize += 1;
  587. break;
  588. case (int)PreLoginOptions.TRACEID:
  589. byte[] connectionIdBytes = _connHandler._clientConnectionId.ToByteArray();
  590. Debug.Assert(GUID_SIZE == connectionIdBytes.Length);
  591. Buffer.BlockCopy(connectionIdBytes, 0, payload, payloadLength, GUID_SIZE);
  592. payloadLength += GUID_SIZE;
  593. offset += GUID_SIZE;
  594. optionDataSize = GUID_SIZE;
  595. ActivityCorrelator.ActivityId actId = ActivityCorrelator.Next();
  596. connectionIdBytes = actId.Id.ToByteArray();
  597. Buffer.BlockCopy(connectionIdBytes, 0, payload, payloadLength, GUID_SIZE);
  598. payloadLength += GUID_SIZE;
  599. payload[payloadLength++] = (byte)(0x000000ff & actId.Sequence);
  600. payload[payloadLength++] = (byte)((0x0000ff00 & actId.Sequence) >> 8);
  601. payload[payloadLength++] = (byte)((0x00ff0000 & actId.Sequence) >> 16);
  602. payload[payloadLength++] = (byte)((0xff000000 & actId.Sequence) >> 24);
  603. int actIdSize = GUID_SIZE + sizeof(UInt32);
  604. offset += actIdSize;
  605. optionDataSize += actIdSize;
  606. Bid.Trace("<sc.TdsParser.SendPreLoginHandshake|INFO> ClientConnectionID %ls, ActivityID %ls\n", _connHandler._clientConnectionId.ToString(), actId.ToString());
  607. break;
  608. default:
  609. Debug.Assert(false, "UNKNOWN option in SendPreLoginHandshake");
  610. break;
  611. }
  612. // Write data length
  613. _physicalStateObj.WriteByte((byte)((optionDataSize & 0xff00) >> 8));
  614. _physicalStateObj.WriteByte((byte)(optionDataSize & 0x00ff));
  615. }
  616. // Write out last option - to let server know the second part of packet completed
  617. _physicalStateObj.WriteByte((byte)PreLoginOptions.LASTOPT);
  618. // Write out payload
  619. _physicalStateObj.WriteByteArray(payload, payloadLength, 0);
  620. // Flush packet
  621. _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH);
  622. }
  623. private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trustServerCert, bool integratedSecurity, out bool marsCapable) {
  624. marsCapable = _fMARS; // Assign default value
  625. bool isYukonOrLater = false;
  626. //
  627. Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
  628. bool result = _physicalStateObj.TryReadNetworkPacket();
  629. if (!result) { throw SQL.SynchronousCallMayNotPend(); }
  630. if (_physicalStateObj._inBytesRead == 0) {
  631. // If the server did not respond then something has gone wrong and we need to close the connection
  632. _physicalStateObj.AddError(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.PreloginError(), "", 0));
  633. _physicalStateObj.Dispose();
  634. ThrowExceptionAndWarning(_physicalStateObj);
  635. }
  636. // SEC
  637. byte[] payload = new byte[_physicalStateObj._inBytesRead - _physicalStateObj._inBytesUsed - _physicalStateObj._inputHeaderLen];
  638. Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
  639. result = _physicalStateObj.TryReadByteArray(payload, 0, payload.Length);
  640. if (!result) { throw SQL.SynchronousCallMayNotPend(); }
  641. if (payload[0] == 0xaa) {
  642. // If the first byte is 0xAA, we are connecting to a 6.5 or earlier server, which
  643. // is not supported. SQL BU DT 296425
  644. throw SQL.InvalidSQLServerVersionUnknown();
  645. }
  646. int offset = 0;
  647. int payloadOffset = 0;
  648. int payloadLength = 0;
  649. int option = payload[offset++];
  650. while (option != (byte)PreLoginOptions.LASTOPT) {
  651. switch (option) {
  652. case (int)PreLoginOptions.VERSION:
  653. payloadOffset = payload[offset++] << 8 | payload[offset++];
  654. payloadLength = payload[offset++] << 8 | payload[offset++];
  655. byte majorVersion = payload[payloadOffset];
  656. byte minorVersion = payload[payloadOffset + 1];
  657. int level = (payload[payloadOffset + 2] << 8) |
  658. payload[payloadOffset + 3];
  659. isYukonOrLater = majorVersion >= 9;
  660. if (!isYukonOrLater) {
  661. marsCapable = false; // If pre-Yukon, MARS not supported.
  662. }
  663. break;
  664. case (int)PreLoginOptions.ENCRYPT:
  665. payloadOffset = payload[offset++] << 8 | payload[offset++];
  666. payloadLength = payload[offset++] << 8 | payload[offset++];
  667. EncryptionOptions serverOption = (EncryptionOptions)payload[payloadOffset];
  668. /* internal enum EncryptionOptions {
  669. OFF,
  670. ON,
  671. NOT_SUP,
  672. REQ,
  673. LOGIN
  674. } */
  675. switch (_encryptionOption) {
  676. case (EncryptionOptions.ON):
  677. if (serverOption == EncryptionOptions.NOT_SUP) {
  678. _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
  679. _physicalStateObj.Dispose();
  680. ThrowExceptionAndWarning(_physicalStateObj);
  681. }
  682. break;
  683. case (EncryptionOptions.OFF):
  684. if (serverOption == EncryptionOptions.OFF) {
  685. // Only encrypt login.
  686. _encryptionOption = EncryptionOptions.LOGIN;
  687. }
  688. else if (serverOption == EncryptionOptions.REQ) {
  689. // Encrypt all.
  690. _encryptionOption = EncryptionOptions.ON;
  691. }
  692. break;
  693. case (EncryptionOptions.NOT_SUP):
  694. if (serverOption == EncryptionOptions.REQ) {
  695. _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0));
  696. _physicalStateObj.Dispose();
  697. ThrowExceptionAndWarning(_physicalStateObj);
  698. }
  699. break;
  700. default:
  701. Debug.Assert(false, "Invalid client encryption option detected");
  702. break;
  703. }
  704. if (_encryptionOption == EncryptionOptions.ON ||
  705. _encryptionOption == EncryptionOptions.LOGIN) {
  706. UInt32 error = 0;
  707. UInt32 info = ((encrypt && !trustServerCert) ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0)
  708. | (isYukonOrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0);
  709. if (encrypt && !integratedSecurity) {
  710. // optimization: in case of SQL Authentication and encryption, set SNI_SSL_IGNORE_CHANNEL_BINDINGS to let SNI
  711. // know that it does not need to allocate/retrieve the Channel Bindings from the SSL context.
  712. info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS;
  713. }
  714. // Add SSL (Encryption) SNI provider.
  715. error = SNINativeMethodWrapper.SNIAddProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV, ref info);
  716. if (error != TdsEnums.SNI_SUCCESS) {
  717. _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
  718. ThrowExceptionAndWarning(_physicalStateObj);
  719. }
  720. // in the case where an async connection is made, encryption is used and Windows Authentication is used,
  721. // wait for SSL handshake to complete, so that the SSL context is fully negotiated before we try to use its
  722. // Channel Bindings as part of the Windows Authentication context build (SSL handshake must complete
  723. // before calling SNISecGenClientContext).
  724. error = SNINativeMethodWrapper.SNIWaitForSSLHandshakeToComplete(_physicalStateObj.Handle, _physicalStateObj.GetTimeoutRemaining());
  725. if (error != TdsEnums.SNI_SUCCESS)
  726. {
  727. _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
  728. ThrowExceptionAndWarning(_physicalStateObj);
  729. }
  730. // create a new packet encryption changes the internal packet size Bug# 228403
  731. try {} // EmptyTry/Finally to avoid FXCop violation
  732. finally {
  733. _physicalStateObj.ClearAllWritePackets();
  734. }
  735. }
  736. break;
  737. case (int)PreLoginOptions.INSTANCE:
  738. payloadOffset = payload[offset++] << 8 | payload[offset++];
  739. payloadLength = payload[offset++] << 8 | payload[offset++];
  740. byte ERROR_INST = 0x1;
  741. byte instanceResult = payload[payloadOffset];
  742. if (instanceResult == ERROR_INST) {
  743. // Check if server says ERROR_INST. That either means the cached info
  744. // we used to connect is not valid or we connected to a named instance
  745. // listening on default params.
  746. return PreLoginHandshakeStatus.InstanceFailure;
  747. }
  748. break;
  749. case (int)PreLoginOptions.THREADID:
  750. // DO NOTHING FOR THREADID
  751. offset += 4;
  752. break;
  753. case (int)PreLoginOptions.MARS:
  754. payloadOffset = payload[offset++] << 8 | payload[offset++];
  755. payloadLength = payload[offset++] << 8 | payload[offset++];
  756. marsCapable = (payload[payloadOffset] == 0 ? false : true);
  757. Debug.Assert(payload[payloadOffset] == 0 || payload[payloadOffset] == 1, "Value for Mars PreLoginHandshake option not equal to 1 or 0!");
  758. break;
  759. case (int)PreLoginOptions.TRACEID:
  760. // DO NOTHING FOR TRACEID
  761. offset += 4;
  762. break;
  763. default:
  764. Debug.Assert(false, "UNKNOWN option in ConsumePreLoginHandshake, option:" + option);
  765. // DO NOTHING FOR THESE UNKNOWN OPTIONS
  766. offset += 4;
  767. break;
  768. }
  769. if (offset < payload.Length) {
  770. option = payload[offset++];
  771. }
  772. else {
  773. break;
  774. }
  775. }
  776. return PreLoginHandshakeStatus.Successful;
  777. }
  778. internal void Deactivate(bool connectionIsDoomed) {
  779. // Called when the connection that owns us is deactivated.
  780. if (Bid.AdvancedOn) {
  781. Bid.Trace("<sc.TdsParser.Deactivate|ADV> %d# deactivating\n", ObjectID);
  782. }
  783. if (Bid.IsOn(Bid.ApiGroup.StateDump)) {
  784. Bid.Trace("<sc.TdsParser.Deactivate|STATE> %d#, %ls\n", ObjectID, TraceString());
  785. }
  786. if (MARSOn) {
  787. _sessionPool.Deactivate();
  788. }
  789. Debug.Assert(connectionIsDoomed || null == _pendingTransaction, "pending transaction at disconnect?");
  790. if (!connectionIsDoomed && null != _physicalStateObj) {
  791. if (_physicalStateObj._pendingData) {
  792. DrainData(_physicalStateObj);
  793. }
  794. if (_physicalStateObj.HasOpenResult) { // SQL BU DT 383773 - need to decrement openResultCount for all pending operations.
  795. _physicalStateObj.DecrementOpenResultCount();
  796. }
  797. }
  798. // Any active, non-distributed transaction must be rolled back. We
  799. // need to wait for distributed transactions to be completed by the
  800. // transaction manager -- we don't want to automatically roll them
  801. // back.
  802. //
  803. // Note that when there is a transaction delegated to this connection,
  804. // we will defer the deactivation of this connection until the
  805. // transaction manager completes the transaction.
  806. SqlInternalTransaction currentTransaction = CurrentTransaction;
  807. if (null != currentTransaction && currentTransaction.HasParentTransaction) {
  808. currentTransaction.CloseFromConnection();
  809. Debug.Assert(null == CurrentTransaction, "rollback didn't clear current transaction?");
  810. }
  811. Statistics = null; // must come after CleanWire or we won't count the stuff that happens there...
  812. }
  813. // Used to close the connection and then free the memory allocated for the netlib connection.
  814. internal void Disconnect() {
  815. if (null != _sessionPool) {
  816. // MARSOn may be true, but _sessionPool not yet created
  817. _sessionPool.Dispose();
  818. }
  819. // Can close the connection if its open or broken
  820. if (_state != TdsParserState.Closed) {
  821. //benign assert - the user could close the connection before consuming all the data
  822. //Debug.Assert(_physicalStateObj._inBytesUsed == _physicalStateObj._inBytesRead && _physicalStateObj._outBytesUsed == _physicalStateObj._inputHeaderLen, "TDSParser closed with data not fully sent or consumed.");
  823. _state = TdsParserState.Closed;
  824. try {
  825. // If the _physicalStateObj has an owner, we will delay the disposal until the owner is finished with it
  826. if (!_physicalStateObj.HasOwner) {
  827. _physicalStateObj.SniContext = SniContext.Snix_Close;
  828. #if DEBUG
  829. _physicalStateObj.InvalidateDebugOnlyCopyOfSniContext();
  830. #endif
  831. _physicalStateObj.Dispose();
  832. }
  833. else {
  834. // Remove the "initial" callback (this will allow the stateObj to be GC collected if need be)
  835. _physicalStateObj.DecrementPendingCallbacks(false);
  836. }
  837. // Not allocated until MARS is actually enabled in SNI.
  838. if (null != _pMarsPhysicalConObj) {
  839. _pMarsPhysicalConObj.Dispose();
  840. }
  841. }
  842. finally {
  843. _pMarsPhysicalConObj = null;
  844. }
  845. }
  846. }
  847. // Fires a single InfoMessageEvent
  848. private void FireInfoMessageEvent(SqlConnection connection, TdsParserStateObject stateObj, SqlError error) {
  849. string serverVersion = null;
  850. Debug.Assert(connection != null && _connHandler.Connection == connection);
  851. if (_state == TdsParserState.OpenLoggedIn) {
  852. serverVersion = _connHandler.ServerVersion;
  853. }
  854. SqlErrorCollection sqlErs = new SqlErrorCollection();
  855. sqlErs.Add(error);
  856. SqlException exc = SqlException.CreateException(sqlErs, serverVersion, _connHandler);
  857. bool notified;
  858. connection.OnInfoMessage(new SqlInfoMessageEventArgs(exc), out notified);
  859. if (notified) {
  860. // observable side-effects, no retry
  861. stateObj._syncOverAsync = true;
  862. }
  863. return;
  864. }
  865. internal void DisconnectTransaction(SqlInternalTransaction internalTransaction) {
  866. Debug.Assert(_currentTransaction != null && _currentTransaction == internalTransaction, "disconnecting different transaction");
  867. if (_currentTransaction != null && _currentTransaction == internalTransaction) {
  868. _currentTransaction = null;
  869. }
  870. }
  871. internal void RollbackOrphanedAPITransactions() {
  872. // Any active, non-distributed transaction must be rolled back.
  873. SqlInternalTransaction currentTransaction = CurrentTransaction;
  874. if (null != currentTransaction && currentTransaction.HasParentTransaction && currentTransaction.IsOrphaned) {
  875. currentTransaction.CloseFromConnection();
  876. Debug.Assert(null == CurrentTransaction, "rollback didn't clear current transaction?");
  877. }
  878. }
  879. internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj, bool callerHasConnectionLock = false, bool asyncClose = false) {
  880. Debug.Assert(!callerHasConnectionLock || _connHandler._parserLock.ThreadMayHaveLock(), "Caller claims to have lock, but connection lock is not taken");
  881. SqlException exception = null;
  882. bool breakConnection;
  883. // This function should only be called when there was an error or warning. If there aren't any
  884. // errors, the handler will be called for the warning(s). If there was an error, the warning(s) will
  885. // be copied to the end of the error collection so that the user may see all the errors and also the
  886. // warnings that occurred.
  887. // can be deleted)
  888. SqlErrorCollection temp = stateObj.GetFullErrorAndWarningCollection(out breakConnection);
  889. Debug.Assert(temp.Count > 0, "TdsParser::ThrowExceptionAndWarning called with no exceptions or warnings!");
  890. if (temp.Count == 0)
  891. {
  892. Bid.Trace("<sc.TdsParser.ThrowExceptionAndWarning|ERR> Potential multi-threaded misuse of connection, unexpectedly empty warnings/errors under lock %d#\n", ObjectID);
  893. }
  894. Debug.Assert(_connHandler != null, "TdsParser::ThrowExceptionAndWarning called with null connectionHandler!");
  895. // Don't break the connection if it is already closed
  896. breakConnection &= (TdsParserState.Closed != _state);
  897. if (breakConnection) {
  898. if ((_state == TdsParserState.OpenNotLoggedIn) && (_connHandler.ConnectionOptions.MultiSubnetFailover || _loginWithFailover) && (temp.Count == 1) && ((temp[0].Number == TdsEnums.TIMEOUT_EXPIRED) || (temp[0].Number == TdsEnums.SNI_WAIT_TIMEOUT))) {
  899. // DevDiv2 Bug 459546: With "MultiSubnetFailover=yes" in the Connection String, SQLClient incorrectly throws a Timeout using shorter time slice (3-4 seconds), not honoring the actual 'Connect Timeout'
  900. // http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/459546
  901. // For Multisubnet Failover we slice the timeout to make reconnecting faster (with the assumption that the server will not failover instantaneously)
  902. // However, when timeout occurs we need to not doom the internal connection and also to mark the TdsParser as closed such that the login will be will retried
  903. breakConnection = false;
  904. Disconnect();
  905. }
  906. else {
  907. _state = TdsParserState.Broken;
  908. }
  909. }
  910. Debug.Assert(temp != null, "TdsParser::ThrowExceptionAndWarning: 0 errors in collection");
  911. if (temp != null && temp.Count > 0) {
  912. // Construct the exception now that we've collected all the errors
  913. string serverVersion = null;
  914. if (_state == TdsParserState.OpenLoggedIn) {
  915. serverVersion = _connHandler.ServerVersion;
  916. }
  917. exception = SqlException.CreateException(temp, serverVersion, _connHandler);
  918. }
  919. // call OnError outside of _ErrorCollectionLock to avoid deadlock
  920. if (exception != null) {
  921. if (breakConnection) {
  922. // report exception to pending async operation
  923. // before OnConnectionClosed overrides the exception
  924. // due to connection close notification through references
  925. var taskSource = stateObj._networkPacketTaskSource;
  926. if (taskSource != null) {
  927. taskSource.TrySetException(ADP.ExceptionWithStackTrace(exception));
  928. }
  929. }
  930. if (asyncClose) {
  931. // Wait until we have the parser lock, then try to close
  932. var connHandler = _connHandler;
  933. Action<Action> wrapCloseAction = closeAction => {
  934. Task.Factory.StartNew(() => {
  935. connHandler._parserLock.Wait(canReleaseFromAnyThread: false);
  936. connHandler.ThreadHasParserLockForClose = true;
  937. try {
  938. closeAction();
  939. }
  940. finally {
  941. connHandler.ThreadHasParserLockForClose = false;
  942. connHandler._parserLock.Release();
  943. }
  944. });
  945. };
  946. _connHandler.OnError(exception, breakConnection, wrapCloseAction);
  947. }
  948. else {
  949. // Let close know that we already have the _parserLock
  950. bool threadAlreadyHadParserLockForClose = _connHandler.ThreadHasParserLockForClose;
  951. if (callerHasConnectionLock) {
  952. _connHandler.ThreadHasParserLockForClose = true;
  953. }
  954. try {
  955. // the following handler will throw an exception or generate a warning event
  956. _connHandler.OnError(exception, breakConnection);
  957. }
  958. finally {
  959. if (callerHasConnectionLock) {
  960. _connHandler.ThreadHasParserLockForClose = threadAlreadyHadParserLockForClose;
  961. }
  962. }
  963. }
  964. }
  965. }
  966. internal SqlError ProcessSNIError(TdsParserStateObject stateObj) {
  967. #if DEBUG
  968. // There is an exception here for MARS as its possible that another thread has closed the connection just as we see an error
  969. Debug.Assert(SniContext.Undefined!=stateObj.DebugOnlyCopyOfSniContext || ((_fMARS) && ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken))), "SniContext must not be None");
  970. #endif
  971. SNINativeMethodWrapper.SNI_Error sniError = new SNINativeMethodWrapper.SNI_Error();
  972. SNINativeMethodWrapper.SNIGetLastError(sniError);
  973. if (sniError.sniError != 0) {
  974. // handle special SNI error codes that are converted into exception which is not a SqlException.
  975. switch (sniError.sniError) {
  976. case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithMoreThan64IPs:
  977. // Connecting with the MultiSubnetFailover connection option to a SQL Server instance configured with more than 64 IP addresses is not supported.
  978. throw SQL.MultiSubnetFailoverWithMoreThan64IPs();
  979. case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithInstanceSpecified:
  980. // Connecting to a named SQL Server instance using the MultiSubnetFailover connection option is not supported.
  981. throw SQL.MultiSubnetFailoverWithInstanceSpecified();
  982. case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithNonTcpProtocol:
  983. // Connecting to a SQL Server instance using the MultiSubnetFailover connection option is only supported when using the TCP protocol.
  984. throw SQL.MultiSubnetFailoverWithNonTcpProtocol();
  985. // continue building SqlError instance
  986. }
  987. }
  988. // error.errorMessage is null terminated with garbage beyond that, since fixed length
  989. string errorMessage;
  990. int MessageLength = Array.IndexOf(sniError.errorMessage, '\0');
  991. if (MessageLength == -1) {
  992. errorMessage = String.Empty; // If we don't see the expected null return nothing.
  993. } else {
  994. errorMessage = new String(sniError.errorMessage, 0, MessageLength);
  995. }
  996. // Format SNI errors and add Context Information
  997. //
  998. // General syntax is:
  999. // <sqlclient message>
  1000. // (provider:<SNIx provider>, error: <SNIx error code> - <SNIx error message>)
  1001. //
  1002. // errorMessage | sniError |
  1003. // -------------------------------------------
  1004. // ==null | x | must never happen
  1005. // !=null | != 0 | retrieve corresponding errorMessage from resources
  1006. // !=null | == 0 | replace text left of errorMessage
  1007. //
  1008. Debug.Assert(!ADP.IsEmpty(errorMessage),"Empty error message received from SNI");
  1009. string sqlContextInfo = Res.GetString(Enum.GetName(typeof(SniContext), stateObj.SniContext));
  1010. string providerRid = String.Format((IFormatProvider)null,"SNI_PN{0}", (int)sniError.provider);
  1011. string providerName = Res.GetString(providerRid);
  1012. Debug.Assert(!ADP.IsEmpty(providerName), String.Format((IFormatProvider)null,"invalid providerResourceId '{0}'", providerRid));
  1013. uint win32ErrorCode = sniError.nativeError;
  1014. if (sniError.sniError == 0) {
  1015. // Provider error. The message from provider is preceeded with non-localizable info from SNI
  1016. // strip provider info from SNI
  1017. //
  1018. int iColon = errorMessage.IndexOf(':');
  1019. Debug.Assert(0<=iColon, "':' character missing in sni errorMessage");
  1020. Debug.Assert(errorMessage.Length>iColon+1 && errorMessage[iColon+1]==' ', "Expecting a space after the ':' character");
  1021. // extract the message excluding the colon and trailing cr/lf chars
  1022. if (0<=iColon) {
  1023. int len = errorMessage.Length;
  1024. len -=2; // exclude "\r\n" sequence
  1025. iColon+=2; // skip over ": " sequence
  1026. len-=iColon;
  1027. /*
  1028. The error message should come back in the following format: "TCP Provider: MESSAGE TEXT"
  1029. Fix Bug 370686, if the message is recieved on a Win9x OS, the error message will not contain MESSAGE TEXT
  1030. per Bug: 269574. If we get a errormessage with no message text, just return the entire message otherwise
  1031. return just the message text.
  1032. */
  1033. if (len > 0) {
  1034. errorMessage = errorMessage.Substring(iColon, len);
  1035. }
  1036. }
  1037. }
  1038. else {
  1039. // SNI error. Replace the entire message
  1040. //
  1041. errorMessage = SQL.GetSNIErrorMessage((int)sniError.sniError);
  1042. // If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
  1043. if (sniError.sniError == (int)SNINativeMethodWrapper.SniSpecialErrors.LocalDBErrorCode) {
  1044. errorMessage += LocalDBAPI.GetLocalDBMessage((int)sniError.nativeError);
  1045. win32ErrorCode = 0;
  1046. }
  1047. }
  1048. errorMessage = String.Format((IFormatProvider)null, "{0} (provider: {1}, error: {2} - {3})",
  1049. sqlContextInfo, providerName, (int)sniError.sniError, errorMessage);
  1050. return new SqlError((int)sniError.nativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS,
  1051. _server, errorMessage, sniError.function, (int)sniError.lineNumber, win32ErrorCode);
  1052. }
  1053. internal void CheckResetConnection(TdsParserStateObject stateObj) {
  1054. if (_fResetConnection && !stateObj._fResetConnectionSent) {
  1055. Debug.Assert(stateObj._outputPacketNumber == 1 || stateObj._outputPacketNumber == 2, "In ResetConnection logic unexpectedly!");
  1056. try {
  1057. if (_fMARS && !stateObj._fResetEventOwned) {
  1058. // If using Async & MARS and we do not own ResetEvent - grab it. We need to not grab lock here
  1059. // for case where multiple packets are sent to server from one execute.
  1060. stateObj._fResetEventOwned = _resetConnectionEvent.WaitOne(stateObj.GetTimeoutRemaining(), false);
  1061. if (stateObj._fResetEventOwned) {
  1062. if (stateObj.TimeoutHasExpired) {
  1063. // We didn't timeout on the WaitOne, but we timed out by the time we decremented stateObj._timeRemaining.
  1064. stateObj._fResetEventOwned = !_resetConnectionEvent.Set();
  1065. stateObj.TimeoutTime = 0;
  1066. }
  1067. }
  1068. if (!stateObj._fResetEventOwned) {
  1069. // We timed out waiting for ResetEvent. Throw timeout exception and reset
  1070. // the buffer. Nothing else to do since we did not actually send anything
  1071. // to the server.
  1072. stateObj.ResetBuffer();
  1073. Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point.");
  1074. stateObj.AddError(new SqlError(TdsEnums.TIMEOUT_EXPIRED, (byte)0x00, TdsEnums.MIN_ERROR_CLASS, _server, _connHandler.TimeoutErrorInternal.GetErrorMessage(), "", 0, TdsEnums.SNI_WAIT_TIMEOUT));
  1075. Debug.Assert(_connHandler._parserLock.ThreadMayHaveLock(), "Thread is writing without taking the connection lock");
  1076. ThrowExceptionAndWarning(stateObj, callerHasConnectionLock: true);
  1077. }
  1078. }
  1079. if (_fResetConnection) {
  1080. // Check again to see if we need to send reset.
  1081. Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state for sending reset connection");
  1082. Debug.Assert(_isShiloh, "TdsParser.cs: Error! _fResetConnection true when not going against Shiloh or later!");
  1083. if (_fPreserveTransaction) {
  1084. // if we are reseting, set bit in header by or'ing with other value
  1085. stateObj._outBuff[1] = (Byte)(stateObj._outBuff[1] | TdsEnums.ST_RESET_CONNECTION_PRESERVE_TRANSACTION);
  1086. }
  1087. else {
  1088. // if we are reseting, set bit in header by or'ing with other value
  1089. stateObj._outBuff[1] = (Byte)(stateObj._outBuff[1] | TdsEnums.ST_RESET_CONNECTION);
  1090. }
  1091. if (!_fMARS) {
  1092. _fResetConnection = false; // If not MARS, can turn off flag now.
  1093. _fPreserveTransaction = false;
  1094. }
  1095. else {
  1096. stateObj._fResetConnectionSent = true; // Otherwise set flag so we don't resend on multiple packet execute.
  1097. }
  1098. }
  1099. else if (_fMARS && stateObj._fResetEventOwned) {
  1100. Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state on WritePacket ResetConnection");
  1101. // Otherwise if Yukon and we grabbed the event, free it. Another execute grabbed the event and
  1102. // took care of sending the reset.
  1103. stateObj._fResetEventOwned = !_resetConnectionEvent.Set();
  1104. Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!");
  1105. }
  1106. }
  1107. catch (Exception) {
  1108. if (_fMARS && stateObj._fResetEventOwned) {
  1109. // If exception thrown, and we are on Yukon and own the event, release it!
  1110. stateObj._fResetConnectionSent = false;
  1111. stateObj._fResetEventOwned = !_resetConnectionEvent.Set();
  1112. Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!");
  1113. }
  1114. throw;
  1115. }
  1116. }
  1117. #if DEBUG
  1118. else {
  1119. Debug.Assert(!_fResetConnection ||
  1120. (_fResetConnection && stateObj._fResetConnectionSent && stateObj._fResetEventOwned),
  1121. "Unexpected state on else ResetConnection block in WritePacket");
  1122. }
  1123. #endif
  1124. }
  1125. //
  1126. // Takes a 16 bit short and writes it.
  1127. //
  1128. internal void WriteShort(int v, TdsParserStateObject stateObj) {
  1129. ReliabilitySection.Assert("unreliable call to WriteShort"); // you need to setup for a thread abort somewhere before you call this method
  1130. if ((stateObj._outBytesUsed + 2) > stateObj._outBuff.Length) {
  1131. // if all of the short doesn't fit into the buffer
  1132. stateObj.WriteByte((byte)(v & 0xff));
  1133. stateObj.WriteByte((byte)((v >> 8) & 0xff));
  1134. }
  1135. else {
  1136. // all of the short fits into the buffer
  1137. stateObj._outBuff[stateObj._outBytesUsed] = (byte)(v & 0xff);
  1138. stateObj._outBuff[stateObj._outBytesUsed + 1] = (byte)((v >> 8) & 0xff);
  1139. stateObj._outBytesUsed += 2;
  1140. }
  1141. }
  1142. internal void WriteUnsignedShort(ushort us, TdsParserStateObject stateObj) {
  1143. WriteShort((short)us, stateObj);
  1144. }
  1145. //
  1146. // Takes a long and writes out an unsigned int
  1147. //
  1148. internal void WriteUnsignedInt(uint i, TdsParserStateObject stateObj) {
  1149. WriteInt((int)i, stateObj);
  1150. }
  1151. //
  1152. // Takes an int and writes it as an int.
  1153. //
  1154. internal void WriteInt(int v, TdsParserStateObject stateObj) {
  1155. ReliabilitySection.Assert("unreliable call to WriteInt"); // you need to setup for a thread abort somewhere before you call this method
  1156. if ((stateObj._outBytesUsed + 4) > stateObj._outBuff.Length) {
  1157. // if all of the int doesn't fit into the buffer
  1158. for (int shiftValue = 0; shiftValue < sizeof(int) * 8; shiftValue += 8) {
  1159. stateObj.WriteByte((byte)((v >> shiftValue) & 0xff));
  1160. }
  1161. }
  1162. else {
  1163. // all of the int fits into the buffer
  1164. // NOTE: We don't use a loop here for performance
  1165. stateObj._outBuff[stateObj._outBytesUsed] = (byte)(v & 0xff);
  1166. stateObj._outBuff[stateObj._outBytesUsed + 1] = (byte)((v >> 8) & 0xff);
  1167. stateObj._outBuff[stateObj._outBytesUsed + 2] = (byte)((v >> 16) & 0xff);
  1168. stateObj._outBuff[stateObj._outBytesUsed + 3] = (byte)((v >> 24) & 0xff);
  1169. stateObj._outBytesUsed += 4;
  1170. }
  1171. }
  1172. //
  1173. // Takes a float and writes it as a 32 bit float.
  1174. //
  1175. internal void WriteFloat(float v, TdsParserStateObject stateObj) {
  1176. byte[] bytes = BitConverter.GetBytes(v);
  1177. stateObj.WriteByteArray(bytes, bytes.Length, 0);
  1178. }
  1179. //
  1180. // Takes a long and writes it as a long.
  1181. //
  1182. internal void WriteLong(long v, TdsParserStateObject stateObj) {
  1183. ReliabilitySection.Assert("unreliable call to WriteLong"); // you need to setup for a thread abort somewhere before you call this method
  1184. if ((stateObj._outBytesUsed + 8) > stateObj._outBuff.Length) {
  1185. // if all of the long doesn't fit into the buffer
  1186. for (int shiftValue = 0; shiftValue < sizeof(long) * 8; shiftValue += 8) {
  1187. stateObj.WriteByte((byte)((v >> shiftValue) & 0xff));
  1188. }
  1189. }
  1190. else {
  1191. // all of the long fits into the buffer
  1192. // NOTE: We don't use a loop here for performance
  1193. stateObj._outBuff[stateObj._outBytesUsed] = (byte)(v & 0xff);
  1194. stateObj._outBuff[stateObj._outBytesUsed + 1] = (byte)((v >> 8) & 0xff);
  1195. stateObj._outBuff[stateObj._outBytesUsed + 2] = (byte)((v >> 16) & 0xff);
  1196. stateObj._outBuff[stateObj._outBytesUsed + 3] = (byte)((v >> 24) & 0xff);
  1197. stateObj._outBuff[stateObj._outBytesUsed + 4] = (byte)((v >> 32) & 0xff);
  1198. stateObj._outBuff[stateObj._outBytesUsed + 5] = (byte)((v >> 40) & 0xff);
  1199. stateObj._outBuff[stateObj._outBytesUsed + 6] = (byte)((v >> 48) & 0xff);
  1200. stateObj._outBuff[stateObj._outBytesUsed + 7] = (byte)((v >> 56) & 0xff);
  1201. stateObj._outBytesUsed += 8;
  1202. }
  1203. }
  1204. //
  1205. // Takes a long and writes part of it
  1206. //
  1207. internal void WritePartialLong(long v, int length, TdsParserStateObject stateObj) {
  1208. ReliabilitySection.Assert("unreliable call to WritePartialLong"); // you need to setup for a thread abort somewhere before you call this method
  1209. Debug.Assert(length <= 8, "Length specified is longer than the size of a long");
  1210. Debug.Assert(length >= 0, "Length should not be negative");
  1211. if ((stateObj._outBytesUsed + length) > stateObj._outBuff.Length) {
  1212. // if all of the long doesn't fit into the buffer
  1213. for (int shiftValue = 0; shiftValue < length * 8; shiftValue += 8) {
  1214. stateObj.WriteByte((byte)((v >> shiftValue) & 0xff));
  1215. }
  1216. }
  1217. else {
  1218. // all of the long fits into the buffer
  1219. for (int index = 0; index < length; index++) {
  1220. stateObj._outBuff[stateObj._outBytesUsed + index] = (byte)((v >> (index * 8)) & 0xff);
  1221. }
  1222. stateObj._outBytesUsed += length;
  1223. }
  1224. }
  1225. //
  1226. // Takes a ulong and writes it as a ulong.
  1227. //
  1228. internal void WriteUnsignedLong(ulong uv, TdsParserStateObject stateObj) {
  1229. WriteLong((long)uv, stateObj);
  1230. }
  1231. //
  1232. // Takes a double and writes it as a 64 bit double.
  1233. //
  1234. internal void WriteDouble(double v, TdsParserStateObject stateObj) {
  1235. byte[] bytes = BitConverter.GetBytes(v);
  1236. stateObj.WriteByteArray(bytes, bytes.Length, 0);
  1237. }
  1238. internal void PrepareResetConnection(bool preserveTransaction) {
  1239. // Set flag to reset connection upon next use - only for use on shiloh!
  1240. _fResetConnection = true;
  1241. _fPreserveTransaction = preserveTransaction;
  1242. }
  1243. internal bool RunReliably(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) {
  1244. RuntimeHelpers.PrepareConstrainedRegions();
  1245. try {
  1246. #if DEBUG
  1247. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  1248. RuntimeHelpers.PrepareConstrainedRegions();
  1249. try {
  1250. tdsReliabilitySection.Start();
  1251. #endif //DEBUG
  1252. return Run(runBehavior, cmdHandler, dataStream, bulkCopyHandler, stateObj);
  1253. #if DEBUG
  1254. }
  1255. finally {
  1256. tdsReliabilitySection.Stop();
  1257. }
  1258. #endif //DEBUG
  1259. }
  1260. catch (OutOfMemoryException) {
  1261. _connHandler.DoomThisConnection();
  1262. throw;
  1263. }
  1264. catch (StackOverflowException) {
  1265. _connHandler.DoomThisConnection();
  1266. throw;
  1267. }
  1268. catch (ThreadAbortException) {
  1269. _connHandler.DoomThisConnection();
  1270. throw;
  1271. }
  1272. }
  1273. internal bool Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) {
  1274. bool syncOverAsync = stateObj._syncOverAsync;
  1275. try
  1276. {
  1277. stateObj._syncOverAsync = true;
  1278. bool dataReady;
  1279. bool result = TryRun(runBehavior, cmdHandler, dataStream, bulkCopyHandler, stateObj, out dataReady);
  1280. Debug.Assert(result == true, "Should never return false when _syncOverAsync is set");
  1281. return dataReady;
  1282. }
  1283. finally
  1284. {
  1285. stateObj._syncOverAsync = syncOverAsync;
  1286. }
  1287. }
  1288. /// <summary>
  1289. /// Checks if the given token is a valid TDS token
  1290. /// </summary>
  1291. /// <param name="token">Token to check</param>
  1292. /// <returns>True if the token is a valid TDS token, otherwise false</returns>
  1293. internal static bool IsValidTdsToken(byte token) {
  1294. return (
  1295. token == TdsEnums.SQLERROR ||
  1296. token == TdsEnums.SQLINFO ||
  1297. token == TdsEnums.SQLLOGINACK ||
  1298. token == TdsEnums.SQLENVCHANGE ||
  1299. token == TdsEnums.SQLRETURNVALUE ||
  1300. token == TdsEnums.SQLRETURNSTATUS ||
  1301. token == TdsEnums.SQLCOLNAME ||
  1302. token == TdsEnums.SQLCOLFMT ||
  1303. token == TdsEnums.SQLCOLMETADATA ||
  1304. token == TdsEnums.SQLALTMETADATA ||
  1305. token == TdsEnums.SQLTABNAME ||
  1306. token == TdsEnums.SQLCOLINFO ||
  1307. token == TdsEnums.SQLORDER ||
  1308. token == TdsEnums.SQLALTROW ||
  1309. token == TdsEnums.SQLROW ||
  1310. token == TdsEnums.SQLNBCROW ||
  1311. token == TdsEnums.SQLDONE ||
  1312. token == TdsEnums.SQLDONEPROC ||
  1313. token == TdsEnums.SQLDONEINPROC ||
  1314. token == TdsEnums.SQLROWCRC ||
  1315. token == TdsEnums.SQLSECLEVEL ||
  1316. token == TdsEnums.SQLPROCID ||
  1317. token == TdsEnums.SQLOFFSET ||
  1318. token == TdsEnums.SQLSSPI ||
  1319. token == TdsEnums.SQLFEATUREEXTACK ||
  1320. token == TdsEnums.SQLSESSIONSTATE);
  1321. }
  1322. // Main parse loop for the top-level tds tokens, calls back into the I*Handler interfaces
  1323. internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, out bool dataReady) {
  1324. ReliabilitySection.Assert("unreliable call to Run"); // you need to setup for a thread abort somewhere before you call this method
  1325. Debug.Assert((SniContext.Undefined != stateObj.SniContext) && // SniContext must not be Undefined
  1326. ((stateObj._attentionSent) || ((SniContext.Snix_Execute != stateObj.SniContext) && (SniContext.Snix_SendRows != stateObj.SniContext))), // SniContext should not be Execute or SendRows unless attention was sent (and, therefore, we are looking for an ACK)
  1327. String.Format("Unexpected SniContext on call to TryRun; SniContext={0}", stateObj.SniContext));
  1328. if (TdsParserState.Broken == State || TdsParserState.Closed == State ) {
  1329. dataReady = true;
  1330. return true; // Just in case this is called in a loop, expecting data to be returned.
  1331. }
  1332. dataReady = false;
  1333. do {
  1334. // If there is data ready, but we didn't exit the loop, then something is wrong
  1335. Debug.Assert(!dataReady, "dataReady not expected - did we forget to skip the row?");
  1336. if (stateObj._internalTimeout) {
  1337. runBehavior = RunBehavior.Attention;
  1338. }
  1339. if (TdsParserState.Broken == State || TdsParserState.Closed == State)
  1340. break; // jump out of the loop if the state is already broken or closed.
  1341. if (!stateObj._accumulateInfoEvents && (stateObj._pendingInfoEvents != null)) {
  1342. if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior)) {
  1343. SqlConnection connection = null;
  1344. if (_connHandler != null)
  1345. connection = _connHandler.Connection; // SqlInternalConnection holds the user connection object as a weak ref
  1346. // We are omitting checks for error.Class in the code below (see processing of INFO) since we know (and assert) that error class
  1347. // error.Class < TdsEnums.MIN_ERROR_CLASS for info message.
  1348. // Also we know that TdsEnums.MIN_ERROR_CLASS<TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS
  1349. if ((connection != null) && connection.FireInfoMessageEventOnUserErrors)
  1350. {
  1351. foreach (SqlError error in stateObj._pendingInfoEvents)
  1352. FireInfoMessageEvent(connection, stateObj, error);
  1353. }
  1354. else
  1355. foreach (SqlError error in stateObj._pendingInfoEvents)
  1356. stateObj.AddWarning(error);
  1357. }
  1358. stateObj._pendingInfoEvents=null;
  1359. }
  1360. byte token;
  1361. if (!stateObj.TryReadByte(out token)) {
  1362. return false;
  1363. }
  1364. if (!IsValidTdsToken(token)) {
  1365. Debug.Assert(false, String.Format((IFormatProvider)null, "unexpected token; token = {0,-2:X2}", token));
  1366. _state = TdsParserState.Broken;
  1367. _connHandler.BreakConnection();
  1368. Bid.Trace("<sc.TdsParser.Run|ERR> Potential multi-threaded misuse of connection, unexpected TDS token found %d#\n", ObjectID);
  1369. throw SQL.ParsingError(); // MDAC 82443
  1370. }
  1371. int tokenLength;
  1372. if (!TryGetTokenLength(token, stateObj, out tokenLength)) {
  1373. return false;
  1374. }
  1375. switch (token) {
  1376. case TdsEnums.SQLERROR:
  1377. case TdsEnums.SQLINFO:
  1378. {
  1379. if (token == TdsEnums.SQLERROR) {
  1380. stateObj._errorTokenReceived = true; // Keep track of the fact error token was received - for Done processing.
  1381. }
  1382. SqlError error;
  1383. if (!TryProcessError(token, stateObj, out error)) {
  1384. return false;
  1385. }
  1386. if (token == TdsEnums.SQLINFO && stateObj._accumulateInfoEvents)
  1387. {
  1388. Debug.Assert(error.Class < TdsEnums.MIN_ERROR_CLASS, "INFO with class > TdsEnums.MIN_ERROR_CLASS");
  1389. if (stateObj._pendingInfoEvents == null)
  1390. stateObj._pendingInfoEvents = new List<SqlError>();
  1391. stateObj._pendingInfoEvents.Add(error);
  1392. stateObj._syncOverAsync = true;
  1393. break;
  1394. }
  1395. if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior)) {
  1396. // If FireInfoMessageEventOnUserErrors is true, we have to fire event without waiting.
  1397. // Otherwise we can go ahead and add it to errors/warnings collection.
  1398. SqlConnection connection = null;
  1399. if (_connHandler != null)
  1400. connection = _connHandler.Connection; // SqlInternalConnection holds the user connection object as a weak ref
  1401. if ((connection != null) &&
  1402. (connection.FireInfoMessageEventOnUserErrors == true) &&
  1403. (error.Class <= TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS)) {
  1404. // Fire SqlInfoMessage here
  1405. FireInfoMessageEvent(connection, stateObj, error);
  1406. }
  1407. else {
  1408. // insert error/info into the appropriate exception - warning if info, exception if error
  1409. if (error.Class < TdsEnums.MIN_ERROR_CLASS) {
  1410. stateObj.AddWarning(error);
  1411. }
  1412. else if (error.Class < TdsEnums.FATAL_ERROR_CLASS) {
  1413. // VSTFDEVDIV 479643: continue results processing for all non-fatal errors (<20)
  1414. stateObj.AddError(error);
  1415. // Add it to collection - but do NOT change run behavior UNLESS
  1416. // we are in an ExecuteReader call - at which time we will be throwing
  1417. // anyways so we need to consume all errors. This is not the case
  1418. // if we have already given out a reader. If we have already given out
  1419. // a reader we need to throw the error but not halt further processing. We used to
  1420. // halt processing and that was a bug preventing the user from
  1421. // processing subsequent results.
  1422. if (null != dataStream) { // Webdata 104560
  1423. if (!dataStream.IsInitialized) {
  1424. runBehavior = RunBehavior.UntilDone;
  1425. }
  1426. }
  1427. }
  1428. else {
  1429. stateObj.AddError(error);
  1430. // Else we have a fatal error and we need to change the behavior
  1431. // since we want the complete error information in the exception.
  1432. // Besides - no further results will be received.
  1433. runBehavior = RunBehavior.UntilDone;
  1434. }
  1435. }
  1436. }
  1437. else if (error.Class >= TdsEnums.FATAL_ERROR_CLASS) {
  1438. stateObj.AddError(error);
  1439. }
  1440. break;
  1441. }
  1442. case TdsEnums.SQLCOLINFO:
  1443. {
  1444. if (null != dataStream) {
  1445. _SqlMetaDataSet metaDataSet;
  1446. if (!TryProcessColInfo(dataStream.MetaData, dataStream, stateObj, out metaDataSet)) {
  1447. return false;
  1448. }
  1449. if (!dataStream.TrySetMetaData(metaDataSet, false)) {
  1450. return false;
  1451. }
  1452. dataStream.BrowseModeInfoConsumed = true;
  1453. }
  1454. else { // no dataStream
  1455. if (!stateObj.TrySkipBytes(tokenLength)) {
  1456. return false;
  1457. }
  1458. }
  1459. break;
  1460. }
  1461. case TdsEnums.SQLDONE:
  1462. case TdsEnums.SQLDONEPROC:
  1463. case TdsEnums.SQLDONEINPROC:
  1464. {
  1465. // RunBehavior can be modified - see SQL BU DT 269516 & 290090
  1466. if (!TryProcessDone(cmdHandler, dataStream, ref runBehavior, stateObj)) {
  1467. return false;
  1468. }
  1469. if ((token == TdsEnums.SQLDONEPROC) && (cmdHandler != null)) {
  1470. cmdHandler.OnDoneProc();
  1471. }
  1472. break;
  1473. }
  1474. case TdsEnums.SQLORDER:
  1475. {
  1476. // don't do anything with the order token so read off the pipe
  1477. if (!stateObj.TrySkipBytes(tokenLength)) {
  1478. return false;
  1479. }
  1480. break;
  1481. }
  1482. case TdsEnums.SQLALTMETADATA:
  1483. {
  1484. stateObj.CloneCleanupAltMetaDataSetArray();
  1485. if (stateObj._cleanupAltMetaDataSetArray == null) {
  1486. // create object on demand (lazy creation)
  1487. stateObj._cleanupAltMetaDataSetArray = new _SqlMetaDataSetCollection();
  1488. }
  1489. _SqlMetaDataSet cleanupAltMetaDataSet;
  1490. if (!TryProcessAltMetaData(tokenLength, stateObj, out cleanupAltMetaDataSet)) {
  1491. return false;
  1492. }
  1493. stateObj._cleanupAltMetaDataSetArray.SetAltMetaData(cleanupAltMetaDataSet);
  1494. if (null != dataStream) {
  1495. byte metadataConsumedByte;
  1496. if (!stateObj.TryPeekByte(out metadataConsumedByte)) {
  1497. return false;
  1498. }
  1499. if (!dataStream.TrySetAltMetaDataSet(cleanupAltMetaDataSet, (TdsEnums.SQLALTMETADATA != metadataConsumedByte))) {
  1500. return false;
  1501. }
  1502. }
  1503. break;
  1504. }
  1505. case TdsEnums.SQLALTROW:
  1506. {
  1507. if (!stateObj.TryStartNewRow(isNullCompressed:false)) { // altrows are not currently null compressed
  1508. return false;
  1509. }
  1510. // read will call run until dataReady. Must not read any data if returnimmetiately set
  1511. if (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior)) {
  1512. ushort altRowId;
  1513. if (!stateObj.TryReadUInt16(out altRowId)) { // get altRowId
  1514. return false;
  1515. }
  1516. if (!TrySkipRow(stateObj._cleanupAltMetaDataSetArray.GetAltMetaData(altRowId), stateObj)) { // skip altRow
  1517. return false;
  1518. }
  1519. }
  1520. else {
  1521. dataReady = true;
  1522. }
  1523. break;
  1524. }
  1525. case TdsEnums.SQLENVCHANGE:
  1526. {
  1527. // ENVCHANGE must be processed synchronously (since it can modify the state of many objects)
  1528. stateObj._syncOverAsync = true;
  1529. SqlEnvChange[] env;
  1530. if (!TryProcessEnvChange(tokenLength, stateObj, out env)) {
  1531. return false;
  1532. }
  1533. for (int ii = 0; ii < env.Length; ii++) {
  1534. if (env[ii] != null && !this.Connection.IgnoreEnvChange) {
  1535. switch (env[ii].type) {
  1536. case TdsEnums.ENV_BEGINTRAN:
  1537. case TdsEnums.ENV_ENLISTDTC:
  1538. // When we get notification from the server of a new
  1539. // transaction, we move any pending transaction over to
  1540. // the current transaction, then we store the token in it.
  1541. // if there isn't a pending transaction, then it's either
  1542. // a TSQL transaction or a distributed transaction.
  1543. Debug.Assert(null == _currentTransaction, "non-null current transaction with an ENV Change");
  1544. _currentTransaction = _pendingTransaction;
  1545. _pendingTransaction = null;
  1546. if (null != _currentTransaction) {
  1547. _currentTransaction.TransactionId = env[ii].newLongValue; // this is defined as a ULongLong in the server and in the TDS Spec.
  1548. }
  1549. else {
  1550. TransactionType transactionType = (TdsEnums.ENV_BEGINTRAN == env[ii].type) ? TransactionType.LocalFromTSQL : TransactionType.Distributed;
  1551. _currentTransaction = new SqlInternalTransaction(_connHandler, transactionType, null, env[ii].newLongValue);
  1552. }
  1553. if (null != _statistics && !_statisticsIsInTransaction) {
  1554. _statistics.SafeIncrement(ref _statistics._transactions);
  1555. }
  1556. _statisticsIsInTransaction = true;
  1557. _retainedTransactionId = SqlInternalTransaction.NullTransactionId;
  1558. break;
  1559. case TdsEnums.ENV_DEFECTDTC:
  1560. case TdsEnums.ENV_TRANSACTIONENDED:
  1561. case TdsEnums.ENV_COMMITTRAN:
  1562. // SQLHOT 483
  1563. // Must clear the retain id if the server-side transaction ends by anything other
  1564. // than rollback.
  1565. _retainedTransactionId = SqlInternalTransaction.NullTransactionId;
  1566. goto case TdsEnums.ENV_ROLLBACKTRAN;
  1567. case TdsEnums.ENV_ROLLBACKTRAN:
  1568. // When we get notification of a completed transaction
  1569. // we null out the current transaction.
  1570. if (null != _currentTransaction) {
  1571. #if DEBUG
  1572. // Check null for case where Begin and Rollback obtained in the same message.
  1573. if (SqlInternalTransaction.NullTransactionId != _currentTransaction.TransactionId) {
  1574. Debug.Assert(_currentTransaction.TransactionId != env[ii].newLongValue, "transaction id's are not equal!");
  1575. }
  1576. #endif
  1577. if (TdsEnums.ENV_COMMITTRAN == env[ii].type) {
  1578. _currentTransaction.Completed(TransactionState.Committed);
  1579. }
  1580. else if (TdsEnums.ENV_ROLLBACKTRAN == env[ii].type) {
  1581. // Hold onto transaction id if distributed tran is rolled back. This must
  1582. // be sent to the server on subsequent executions even though the transaction
  1583. // is considered to be rolled back.
  1584. if (_currentTransaction.IsDistributed && _currentTransaction.IsActive) {
  1585. _retainedTransactionId = env[ii].oldLongValue;
  1586. }
  1587. _currentTransaction.Completed(TransactionState.Aborted);
  1588. }
  1589. else {
  1590. //
  1591. _currentTransaction.Completed(TransactionState.Unknown);
  1592. }
  1593. _currentTransaction = null;
  1594. }
  1595. _statisticsIsInTransaction = false;
  1596. break;
  1597. default:
  1598. _connHandler.OnEnvChange(env[ii]);
  1599. break;
  1600. }
  1601. }
  1602. }
  1603. break;
  1604. }
  1605. case TdsEnums.SQLLOGINACK:
  1606. {
  1607. SqlLoginAck ack;
  1608. if (!TryProcessLoginAck(stateObj, out ack)) {
  1609. return false;
  1610. }
  1611. _connHandler.OnLoginAck(ack);
  1612. break;
  1613. }
  1614. case TdsEnums.SQLFEATUREEXTACK:
  1615. {
  1616. if (!TryProcessFeatureExtAck(stateObj)) {
  1617. return false;
  1618. }
  1619. break;
  1620. }
  1621. case TdsEnums.SQLSESSIONSTATE:
  1622. {
  1623. if (!TryProcessSessionState(stateObj, tokenLength, _connHandler._currentSessionData)) {
  1624. return false;
  1625. }
  1626. break;
  1627. }
  1628. case TdsEnums.SQLCOLMETADATA:
  1629. {
  1630. if (tokenLength != TdsEnums.VARNULL) {
  1631. _SqlMetaDataSet metadata;
  1632. if (!TryProcessMetaData(tokenLength, stateObj, out metadata)) {
  1633. return false;
  1634. }
  1635. stateObj._cleanupMetaData = metadata;
  1636. }
  1637. else {
  1638. if (cmdHandler != null) {
  1639. stateObj._cleanupMetaData = cmdHandler.MetaData;
  1640. }
  1641. }
  1642. if (null != dataStream) {
  1643. byte peekedToken;
  1644. if (!stateObj.TryPeekByte(out peekedToken)) { // temporarily cache next byte
  1645. return false;
  1646. }
  1647. if (!dataStream.TrySetMetaData(stateObj._cleanupMetaData, (TdsEnums.SQLTABNAME == peekedToken || TdsEnums.SQLCOLINFO == peekedToken))) {
  1648. return false;
  1649. }
  1650. }
  1651. else if (null != bulkCopyHandler) {
  1652. bulkCopyHandler.SetMetaData(stateObj._cleanupMetaData);
  1653. }
  1654. break;
  1655. }
  1656. case TdsEnums.SQLROW:
  1657. case TdsEnums.SQLNBCROW:
  1658. {
  1659. Debug.Assert(stateObj._cleanupMetaData != null, "Reading a row, but the metadata is null");
  1660. if (token == TdsEnums.SQLNBCROW) {
  1661. if (!stateObj.TryStartNewRow(isNullCompressed: true, nullBitmapColumnsCount: stateObj._cleanupMetaData.Length)) {
  1662. return false;
  1663. }
  1664. }
  1665. else {
  1666. if (!stateObj.TryStartNewRow(isNullCompressed:false)) {
  1667. return false;
  1668. }
  1669. }
  1670. if (null != bulkCopyHandler) {
  1671. //
  1672. if (!TryProcessRow(stateObj._cleanupMetaData, bulkCopyHandler.CreateRowBuffer(), bulkCopyHandler.CreateIndexMap(), stateObj)) {
  1673. return false;
  1674. }
  1675. }
  1676. else if (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior)) {
  1677. if (!TrySkipRow(stateObj._cleanupMetaData, stateObj)) { // skip rows
  1678. return false;
  1679. }
  1680. }
  1681. else {
  1682. dataReady = true;
  1683. }
  1684. if (_statistics != null) {
  1685. _statistics.WaitForDoneAfterRow = true;
  1686. }
  1687. break;
  1688. }
  1689. case TdsEnums.SQLRETURNSTATUS:
  1690. int status;
  1691. if (!stateObj.TryReadInt32(out status)) {
  1692. return false;
  1693. }
  1694. if (cmdHandler != null) {
  1695. cmdHandler.OnReturnStatus(status);
  1696. }
  1697. break;
  1698. case TdsEnums.SQLRETURNVALUE:
  1699. {
  1700. SqlReturnValue returnValue;
  1701. if (!TryProcessReturnValue(tokenLength, stateObj, out returnValue)) {
  1702. return false;
  1703. }
  1704. if (cmdHandler != null) {
  1705. cmdHandler.OnReturnValue(returnValue);
  1706. }
  1707. break;
  1708. }
  1709. case TdsEnums.SQLSSPI:
  1710. {
  1711. // token length is length of SSPI data - call ProcessSSPI with it
  1712. Debug.Assert(stateObj._syncOverAsync, "ProcessSSPI does not support retry, do not attempt asynchronously");
  1713. stateObj._syncOverAsync = true;
  1714. ProcessSSPI(tokenLength);
  1715. break;
  1716. }
  1717. case TdsEnums.SQLTABNAME:
  1718. {
  1719. if (null != dataStream) {
  1720. MultiPartTableName[] tableNames;
  1721. if (!TryProcessTableName(tokenLength, stateObj, out tableNames)) {
  1722. return false;
  1723. }
  1724. dataStream.TableNames = tableNames;
  1725. }
  1726. else {
  1727. if (!stateObj.TrySkipBytes(tokenLength)) {
  1728. return false;
  1729. }
  1730. }
  1731. break;
  1732. }
  1733. default:
  1734. Debug.Assert(false, "Unhandled token: " + token.ToString(CultureInfo.InvariantCulture));
  1735. break;
  1736. }
  1737. Debug.Assert(stateObj._pendingData || !dataReady, "dataReady is set, but there is no pending data");
  1738. }
  1739. // Loop while data pending & runbehavior not return immediately, OR
  1740. // if in attention case, loop while no more pending data & attention has not yet been
  1741. // received.
  1742. while ((stateObj._pendingData &&
  1743. (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior))) ||
  1744. (!stateObj._pendingData && stateObj._attentionSent && !stateObj._attentionReceived));
  1745. #if DEBUG
  1746. if ((stateObj._pendingData) && (!dataReady)) {
  1747. byte token;
  1748. if (!stateObj.TryPeekByte(out token)) {
  1749. return false;
  1750. }
  1751. Debug.Assert(IsValidTdsToken(token), string.Format("DataReady is false, but next token is not valid: {0,-2:X2}", token));
  1752. }
  1753. #endif
  1754. if (!stateObj._pendingData) {
  1755. if (null != CurrentTransaction) {
  1756. CurrentTransaction.Activate();
  1757. }
  1758. }
  1759. // if we recieved an attention (but this thread didn't send it) then
  1760. // we throw an Operation Cancelled error
  1761. if (stateObj._attentionReceived) {
  1762. // Dev11 #344723: SqlClient stress hang System_Data!Tcp::ReadSync via a call to SqlDataReader::Close
  1763. // Spin until SendAttention has cleared _attentionSending, this prevents a race condition between receiving the attention ACK and setting _attentionSent
  1764. SpinWait.SpinUntil(() => !stateObj._attentionSending);
  1765. Debug.Assert(stateObj._attentionSent, "Attention ACK has been received without attention sent");
  1766. if (stateObj._attentionSent) {
  1767. // Reset attention state.
  1768. stateObj._attentionSent = false;
  1769. stateObj._attentionReceived = false;
  1770. if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior) && !stateObj._internalTimeout) {
  1771. // Add attention error to collection - if not RunBehavior.Clean!
  1772. stateObj.AddError(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.OperationCancelled(), "", 0));
  1773. }
  1774. }
  1775. }
  1776. if (stateObj.HasErrorOrWarning) {
  1777. ThrowExceptionAndWarning(stateObj);
  1778. }
  1779. return true;
  1780. }
  1781. private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, out SqlEnvChange[] sqlEnvChange) {
  1782. // There could be multiple environment change messages following this token.
  1783. byte byteLength;
  1784. int processedLength = 0;
  1785. int nvalues = 0;
  1786. SqlEnvChange[] envarray = new SqlEnvChange[3]; // Why is this hardcoded to 3?
  1787. sqlEnvChange = null;
  1788. while (tokenLength > processedLength) {
  1789. if (nvalues >= envarray.Length) {
  1790. // This is a rare path. Most of the time we will have 1 or 2 envchange data streams.
  1791. SqlEnvChange[] newenvarray = new SqlEnvChange[envarray.Length + 3];
  1792. for (int ii = 0; ii < envarray.Length; ii++)
  1793. newenvarray[ii] = envarray[ii];
  1794. envarray = newenvarray;
  1795. }
  1796. SqlEnvChange env = new SqlEnvChange();
  1797. if (!stateObj.TryReadByte(out env.type)) {
  1798. return false;
  1799. }
  1800. envarray[nvalues] = env;
  1801. nvalues++;
  1802. switch (env.type) {
  1803. case TdsEnums.ENV_DATABASE:
  1804. case TdsEnums.ENV_LANG:
  1805. if (!TryReadTwoStringFields(env, stateObj)) {
  1806. return false;
  1807. }
  1808. break;
  1809. case TdsEnums.ENV_CHARSET:
  1810. // we copied this behavior directly from luxor - see charset envchange
  1811. // section from sqlctokn.c
  1812. Debug.Assert(!_isShiloh, "Received ENV_CHARSET on non 7.0 server!");
  1813. if (!TryReadTwoStringFields(env, stateObj)) {
  1814. return false;
  1815. }
  1816. if (env.newValue == TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_STRING) {
  1817. _defaultCodePage = TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_VALUE;
  1818. _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage);
  1819. }
  1820. else {
  1821. Debug.Assert(env.newValue.Length > TdsEnums.CHARSET_CODE_PAGE_OFFSET, "TdsParser.ProcessEnvChange(): charset value received with length <=10");
  1822. string stringCodePage = env.newValue.Substring(TdsEnums.CHARSET_CODE_PAGE_OFFSET);
  1823. _defaultCodePage = Int32.Parse(stringCodePage, NumberStyles.Integer, CultureInfo.InvariantCulture);
  1824. _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage);
  1825. }
  1826. break;
  1827. case TdsEnums.ENV_PACKETSIZE:
  1828. // take care of packet size right here
  1829. Debug.Assert(stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
  1830. if (!TryReadTwoStringFields(env, stateObj)) {
  1831. // Changing packet size does not support retry, should not pend"
  1832. throw SQL.SynchronousCallMayNotPend();
  1833. }
  1834. // Only set on physical state object - this should only occur on LoginAck prior
  1835. // to MARS initialization!
  1836. Int32 packetSize = Int32.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
  1837. if (_physicalStateObj.SetPacketSize(packetSize)) {
  1838. // If packet size changed, we need to release our SNIPackets since
  1839. // those are tied to packet size of connection.
  1840. _physicalStateObj.ClearAllWritePackets();
  1841. // Update SNI ConsumerInfo value to be resulting packet size
  1842. UInt32 unsignedPacketSize = (UInt32) packetSize;
  1843. UInt32 result = SNINativeMethodWrapper.SNISetInfo(_physicalStateObj.Handle, SNINativeMethodWrapper.QTypes.SNI_QUERY_CONN_BUFSIZE, ref unsignedPacketSize);
  1844. Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SNISetInfo");
  1845. }
  1846. break;
  1847. case TdsEnums.ENV_LOCALEID:
  1848. //
  1849. if (!TryReadTwoStringFields(env, stateObj)) {
  1850. return false;
  1851. }
  1852. _defaultLCID = Int32.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
  1853. break;
  1854. case TdsEnums.ENV_COMPFLAGS:
  1855. if (!TryReadTwoStringFields(env, stateObj)) {
  1856. return false;
  1857. }
  1858. break;
  1859. case TdsEnums.ENV_COLLATION:
  1860. Debug.Assert(env.newLength == 5 || env.newLength == 0, "Improper length in new collation!");
  1861. if (!stateObj.TryReadByte(out byteLength)) {
  1862. return false;
  1863. }
  1864. env.newLength = byteLength;
  1865. if (env.newLength == 5) {
  1866. if (!TryProcessCollation(stateObj, out env.newCollation)) {
  1867. return false;
  1868. }
  1869. // give the parser the new collation values in case parameters don't specify one
  1870. _defaultCollation = env.newCollation;
  1871. int newCodePage = GetCodePage(env.newCollation, stateObj);
  1872. if (newCodePage != _defaultCodePage) {
  1873. _defaultCodePage = newCodePage;
  1874. _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage);
  1875. }
  1876. _defaultLCID = env.newCollation.LCID;
  1877. }
  1878. if (!stateObj.TryReadByte(out byteLength)) {
  1879. return false;
  1880. }
  1881. env.oldLength = byteLength;
  1882. Debug.Assert(env.oldLength == 5 || env.oldLength == 0, "Improper length in old collation!");
  1883. if (env.oldLength == 5) {
  1884. if (!TryProcessCollation(stateObj, out env.oldCollation)) {
  1885. return false;
  1886. }
  1887. }
  1888. env.length = 3 + env.newLength + env.oldLength;
  1889. break;
  1890. case TdsEnums.ENV_BEGINTRAN:
  1891. case TdsEnums.ENV_COMMITTRAN:
  1892. case TdsEnums.ENV_ROLLBACKTRAN:
  1893. case TdsEnums.ENV_ENLISTDTC:
  1894. case TdsEnums.ENV_DEFECTDTC:
  1895. case TdsEnums.ENV_TRANSACTIONENDED:
  1896. Debug.Assert(_isYukon, "Received new ENVCHANGE transaction/DTC token on pre 9.0 server!");
  1897. if (!stateObj.TryReadByte(out byteLength)) {
  1898. return false;
  1899. }
  1900. env.newLength = byteLength;
  1901. Debug.Assert(env.newLength == 0 || env.newLength == 8, "Improper length for new transaction id!");
  1902. if (env.newLength > 0) {
  1903. if (!stateObj.TryReadInt64(out env.newLongValue)) {
  1904. return false;
  1905. }
  1906. Debug.Assert(env.newLongValue != SqlInternalTransaction.NullTransactionId, "New transaction id is null?"); // the server guarantees that zero is an invalid transaction id.
  1907. }
  1908. else {
  1909. env.newLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id.
  1910. }
  1911. if (!stateObj.TryReadByte(out byteLength)) {
  1912. return false;
  1913. }
  1914. env.oldLength = byteLength;
  1915. Debug.Assert(env.oldLength == 0 || env.oldLength == 8, "Improper length for old transaction id!");
  1916. if (env.oldLength > 0) {
  1917. if (!stateObj.TryReadInt64(out env.oldLongValue)) {
  1918. return false;
  1919. }
  1920. Debug.Assert(env.oldLongValue != SqlInternalTransaction.NullTransactionId, "Old transaction id is null?"); // the server guarantees that zero is an invalid transaction id.
  1921. }
  1922. else {
  1923. env.oldLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id.
  1924. }
  1925. // env.length includes 1 byte type token
  1926. env.length = 3 + env.newLength + env.oldLength;
  1927. break;
  1928. case TdsEnums.ENV_LOGSHIPNODE:
  1929. // env.newBinValue is secondary node, env.oldBinValue is witness node
  1930. // comes before LoginAck so we can't assert this
  1931. if (!TryReadTwoStringFields(env, stateObj)) {
  1932. return false;
  1933. }
  1934. break;
  1935. case TdsEnums.ENV_PROMOTETRANSACTION:
  1936. Debug.Assert(_isYukon, "Received new ENVCHANGE tokens on pre 9.0 server!");
  1937. if (!stateObj.TryReadInt32(out env.newLength)) { // new value has 4 byte length
  1938. return false;
  1939. }
  1940. env.newBinValue = new byte[env.newLength];
  1941. if (!stateObj.TryReadByteArray(env.newBinValue, 0, env.newLength)) { // read new value with 4 byte length
  1942. return false;
  1943. }
  1944. if (!stateObj.TryReadByte(out byteLength)) {
  1945. return false;
  1946. }
  1947. env.oldLength = byteLength;
  1948. Debug.Assert(0 == env.oldLength, "old length should be zero");
  1949. // env.length includes 1 byte for type token
  1950. env.length = 5 + env.newLength;
  1951. break;
  1952. case TdsEnums.ENV_TRANSACTIONMANAGERADDRESS:
  1953. case TdsEnums.ENV_SPRESETCONNECTIONACK:
  1954. //
  1955. Debug.Assert(_isYukon, "Received new ENVCHANGE tokens on pre 9.0 server!");
  1956. if (!TryReadTwoBinaryFields(env, stateObj)) {
  1957. return false;
  1958. }
  1959. break;
  1960. case TdsEnums.ENV_USERINSTANCE:
  1961. Debug.Assert(!_isYukon, "Received ENV_USERINSTANCE on non 9.0 server!");
  1962. if (!TryReadTwoStringFields(env, stateObj)) {
  1963. return false;
  1964. }
  1965. break;
  1966. case TdsEnums.ENV_ROUTING:
  1967. ushort newLength;
  1968. if (!stateObj.TryReadUInt16(out newLength)) {
  1969. return false;
  1970. }
  1971. env.newLength = newLength;
  1972. byte protocol;
  1973. if (!stateObj.TryReadByte(out protocol)) {
  1974. return false;
  1975. }
  1976. ushort port;
  1977. if (!stateObj.TryReadUInt16(out port)) {
  1978. return false;
  1979. }
  1980. UInt16 serverLen;
  1981. if (!stateObj.TryReadUInt16(out serverLen)) {
  1982. return false;
  1983. }
  1984. string serverName;
  1985. if (!stateObj.TryReadString(serverLen, out serverName)) {
  1986. return false;
  1987. }
  1988. env.newRoutingInfo = new RoutingInfo(protocol, port, serverName);
  1989. UInt16 oldLength;
  1990. if (!stateObj.TryReadUInt16(out oldLength)) {
  1991. return false;
  1992. }
  1993. if (!stateObj.TrySkipBytes(oldLength)) {
  1994. return false;
  1995. }
  1996. env.length = env.newLength + oldLength + 5; // 5=2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength]
  1997. break;
  1998. default:
  1999. Debug.Assert(false, "Unknown environment change token: " + env.type);
  2000. break;
  2001. }
  2002. processedLength += env.length;
  2003. }
  2004. sqlEnvChange = envarray;
  2005. return true;
  2006. }
  2007. private bool TryReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject stateObj) {
  2008. // Used by ProcessEnvChangeToken
  2009. byte byteLength;
  2010. if (!stateObj.TryReadByte(out byteLength)) {
  2011. return false;
  2012. }
  2013. env.newLength = byteLength;
  2014. env.newBinValue = new byte[env.newLength];
  2015. if (!stateObj.TryReadByteArray(env.newBinValue, 0, env.newLength)) {
  2016. return false;
  2017. }
  2018. if (!stateObj.TryReadByte(out byteLength)) {
  2019. return false;
  2020. }
  2021. env.oldLength = byteLength;
  2022. env.oldBinValue = new byte[env.oldLength];
  2023. if (!stateObj.TryReadByteArray(env.oldBinValue, 0, env.oldLength)) {
  2024. return false;
  2025. }
  2026. // env.length includes 1 byte type token
  2027. env.length = 3 + env.newLength + env.oldLength;
  2028. return true;
  2029. }
  2030. private bool TryReadTwoStringFields(SqlEnvChange env, TdsParserStateObject stateObj) {
  2031. // Used by ProcessEnvChangeToken
  2032. byte newLength, oldLength;
  2033. string newValue, oldValue;
  2034. if (!stateObj.TryReadByte(out newLength)) {
  2035. return false;
  2036. }
  2037. if (!stateObj.TryReadString(newLength, out newValue)) {
  2038. return false;
  2039. }
  2040. if (!stateObj.TryReadByte(out oldLength)) {
  2041. return false;
  2042. }
  2043. if (!stateObj.TryReadString(oldLength, out oldValue)) {
  2044. return false;
  2045. }
  2046. env.newLength = newLength;
  2047. env.newValue = newValue;
  2048. env.oldLength = oldLength;
  2049. env.oldValue = oldValue;
  2050. // env.length includes 1 byte type token
  2051. env.length = 3 + env.newLength * 2 + env.oldLength * 2;
  2052. return true;
  2053. }
  2054. private bool TryProcessDone(SqlCommand cmd, SqlDataReader reader, ref RunBehavior run, TdsParserStateObject stateObj) {
  2055. ushort curCmd;
  2056. ushort status;
  2057. int count;
  2058. // Can't retry TryProcessDone
  2059. stateObj._syncOverAsync = true;
  2060. // status
  2061. // command
  2062. // rowcount (valid only if DONE_COUNT bit is set)
  2063. if (!stateObj.TryReadUInt16(out status)) {
  2064. return false;
  2065. }
  2066. if (!stateObj.TryReadUInt16(out curCmd)) {
  2067. return false;
  2068. }
  2069. if (_isYukon) {
  2070. long longCount;
  2071. if (!stateObj.TryReadInt64(out longCount)) {
  2072. return false;
  2073. }
  2074. count = (int) longCount;
  2075. }
  2076. else {
  2077. if (!stateObj.TryReadInt32(out count)) {
  2078. return false;
  2079. }
  2080. // If we haven't yet completed processing login token stream yet, we may be talking to a Yukon server
  2081. // In that case we still have to read another 4 bytes
  2082. // But don't try to read beyond the TDS stream in this case, because it generates errors if login failed.
  2083. if ( _state == TdsParserState.OpenNotLoggedIn) {
  2084. // Login incomplete, if we are reading from Yukon we need to read another int
  2085. if (stateObj._inBytesRead > stateObj._inBytesUsed) {
  2086. byte b;
  2087. if (!stateObj.TryPeekByte(out b)) {
  2088. return false;
  2089. }
  2090. if (b == 0) {
  2091. // This is an invalid token value
  2092. if (!stateObj.TryReadInt32(out count)) {
  2093. return false;
  2094. }
  2095. }
  2096. }
  2097. }
  2098. }
  2099. // We get a done token with the attention bit set
  2100. if (TdsEnums.DONE_ATTN == (status & TdsEnums.DONE_ATTN)) {
  2101. Debug.Assert(TdsEnums.DONE_MORE != (status & TdsEnums.DONE_MORE),"Not expecting DONE_MORE when receiving DONE_ATTN");
  2102. Debug.Assert(stateObj._attentionSent, "Received attention done without sending one!");
  2103. stateObj._attentionReceived = true;
  2104. Debug.Assert(stateObj._inBytesUsed == stateObj._inBytesRead && stateObj._inBytesPacket == 0, "DONE_ATTN received with more data left on wire");
  2105. }
  2106. if ((null != cmd) && (TdsEnums.DONE_COUNT == (status & TdsEnums.DONE_COUNT))) {
  2107. if (curCmd != TdsEnums.SELECT) {
  2108. cmd.InternalRecordsAffected = count;
  2109. }
  2110. // Skip the bogus DONE counts sent by the server
  2111. if (stateObj._receivedColMetaData || (curCmd != TdsEnums.SELECT)) {
  2112. cmd.OnStatementCompleted(count);
  2113. }
  2114. }
  2115. stateObj._receivedColMetaData = false;
  2116. // Surface exception for DONE_ERROR in the case we did not receive an error token
  2117. // in the stream, but an error occurred. In these cases, we throw a general server error. The
  2118. // situations where this can occur are: an invalid buffer received from client, login error
  2119. // and the server refused our connection, and the case where we are trying to log in but
  2120. // the server has reached its max connection limit. Bottom line, we need to throw general
  2121. // error in the cases where we did not receive a error token along with the DONE_ERROR.
  2122. if ((TdsEnums.DONE_ERROR == (TdsEnums.DONE_ERROR & status)) && stateObj.ErrorCount == 0 &&
  2123. stateObj._errorTokenReceived == false && (RunBehavior.Clean != (RunBehavior.Clean & run))) {
  2124. stateObj.AddError(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.SevereError(), "", 0));
  2125. if (null != reader) { // SQL BU DT 269516
  2126. if (!reader.IsInitialized) {
  2127. run = RunBehavior.UntilDone;
  2128. }
  2129. }
  2130. }
  2131. // Similar to above, only with a more severe error. In this case, if we received
  2132. // the done_srverror, this exception will be added to the collection regardless.
  2133. // MDAC #93896. Also, per Ashwin, the server will always break the connection in this case.
  2134. if ((TdsEnums.DONE_SRVERROR == (TdsEnums.DONE_SRVERROR & status)) && (RunBehavior.Clean != (RunBehavior.Clean & run))) {
  2135. stateObj.AddError(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.SevereError(), "", 0));
  2136. if (null != reader) { // SQL BU DT 269516
  2137. if (!reader.IsInitialized) {
  2138. run = RunBehavior.UntilDone;
  2139. }
  2140. }
  2141. }
  2142. ProcessSqlStatistics(curCmd, status, count);
  2143. // stop if the DONE_MORE bit isn't set (see above for attention handling)
  2144. if (TdsEnums.DONE_MORE != (status & TdsEnums.DONE_MORE)) {
  2145. stateObj._errorTokenReceived = false;
  2146. if (stateObj._inBytesUsed >= stateObj._inBytesRead) {
  2147. stateObj._pendingData = false;
  2148. }
  2149. }
  2150. // _pendingData set by e.g. 'TdsExecuteSQLBatch'
  2151. // _hasOpenResult always set to true by 'WriteMarsHeader'
  2152. //
  2153. if (!stateObj._pendingData && stateObj._hasOpenResult) {
  2154. /*
  2155. Debug.Assert(!((sqlTransaction != null && _distributedTransaction != null) ||
  2156. (_userStartedLocalTransaction != null && _distributedTransaction != null))
  2157. , "ProcessDone - have both distributed and local transactions not null!");
  2158. */ // WebData 112722
  2159. stateObj.DecrementOpenResultCount();
  2160. }
  2161. return true;
  2162. }
  2163. private void ProcessSqlStatistics(ushort curCmd, ushort status, int count) {
  2164. // SqlStatistics bookkeeping stuff
  2165. //
  2166. if (null != _statistics) {
  2167. // any done after row(s) counts as a resultset
  2168. if (_statistics.WaitForDoneAfterRow) {
  2169. _statistics.SafeIncrement(ref _statistics._sumResultSets);
  2170. _statistics.WaitForDoneAfterRow = false;
  2171. }
  2172. // clear row count DONE_COUNT flag is not set
  2173. if (!(TdsEnums.DONE_COUNT == (status & TdsEnums.DONE_COUNT))) {
  2174. count = 0;
  2175. }
  2176. switch (curCmd) {
  2177. case TdsEnums.INSERT:
  2178. case TdsEnums.DELETE:
  2179. case TdsEnums.UPDATE:
  2180. case TdsEnums.MERGE:
  2181. _statistics.SafeIncrement(ref _statistics._iduCount);
  2182. _statistics.SafeAdd(ref _statistics._iduRows, count);
  2183. if (!_statisticsIsInTransaction) {
  2184. _statistics.SafeIncrement(ref _statistics._transactions);
  2185. }
  2186. break;
  2187. case TdsEnums.SELECT:
  2188. _statistics.SafeIncrement(ref _statistics._selectCount);
  2189. _statistics.SafeAdd(ref _statistics._selectRows, count);
  2190. break;
  2191. case TdsEnums.BEGINXACT:
  2192. if (!_statisticsIsInTransaction) {
  2193. _statistics.SafeIncrement(ref _statistics._transactions);
  2194. }
  2195. _statisticsIsInTransaction = true;
  2196. break;
  2197. case TdsEnums.OPENCURSOR:
  2198. _statistics.SafeIncrement(ref _statistics._cursorOpens);
  2199. break;
  2200. case TdsEnums.ABORT:
  2201. _statisticsIsInTransaction = false;
  2202. break;
  2203. case TdsEnums.ENDXACT:
  2204. _statisticsIsInTransaction = false;
  2205. break;
  2206. } // switch
  2207. }
  2208. else {
  2209. switch (curCmd) {
  2210. case TdsEnums.BEGINXACT:
  2211. _statisticsIsInTransaction = true;
  2212. break;
  2213. case TdsEnums.ABORT:
  2214. case TdsEnums.ENDXACT:
  2215. _statisticsIsInTransaction = false;
  2216. break;
  2217. }
  2218. }
  2219. }
  2220. private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) {
  2221. // read feature ID
  2222. byte featureId;
  2223. do {
  2224. if (!stateObj.TryReadByte(out featureId)) {
  2225. return false;
  2226. }
  2227. if (featureId != TdsEnums.FEATUREEXT_TERMINATOR) {
  2228. UInt32 dataLen;
  2229. if (!stateObj.TryReadUInt32(out dataLen)) {
  2230. return false;
  2231. }
  2232. byte[] data = new byte[dataLen];
  2233. if (dataLen > 0) {
  2234. if (!stateObj.TryReadByteArray(data, 0, checked ((int)dataLen))) {
  2235. return false;
  2236. }
  2237. }
  2238. _connHandler.OnFeatureExtAck(featureId, data);
  2239. }
  2240. } while (featureId != TdsEnums.FEATUREEXT_TERMINATOR);
  2241. return true;
  2242. }
  2243. private bool TryProcessSessionState(TdsParserStateObject stateObj, int length, SessionData sdata) {
  2244. if (length < 5) {
  2245. throw SQL.ParsingError();
  2246. }
  2247. UInt32 seqNum;
  2248. if (!stateObj.TryReadUInt32(out seqNum)) {
  2249. return false;
  2250. }
  2251. if (seqNum == UInt32.MaxValue) {
  2252. _connHandler.DoNotPoolThisConnection();
  2253. }
  2254. byte status;
  2255. if (!stateObj.TryReadByte(out status)) {
  2256. return false;
  2257. }
  2258. if (status > 1) {
  2259. throw SQL.ParsingError();
  2260. }
  2261. bool recoverable = status != 0;
  2262. length -= 5;
  2263. while (length > 0) {
  2264. byte stateId;
  2265. if (!stateObj.TryReadByte(out stateId)) {
  2266. return false;
  2267. }
  2268. int stateLen;
  2269. byte stateLenByte;
  2270. if (!stateObj.TryReadByte(out stateLenByte)) {
  2271. return false;
  2272. }
  2273. if (stateLenByte < 0xFF) {
  2274. stateLen = stateLenByte;
  2275. }
  2276. else {
  2277. if (!stateObj.TryReadInt32(out stateLen)) {
  2278. return false;
  2279. }
  2280. }
  2281. byte[] buffer = null;
  2282. lock (sdata._delta) {
  2283. if (sdata._delta[stateId] == null) {
  2284. buffer = new byte[stateLen];
  2285. sdata._delta[stateId] = new SessionStateRecord { _version = seqNum, _dataLength = stateLen, _data = buffer, _recoverable = recoverable };
  2286. sdata._deltaDirty = true;
  2287. if (!recoverable) {
  2288. checked { sdata._unrecoverableStatesCount++; }
  2289. }
  2290. }
  2291. else {
  2292. if (sdata._delta[stateId]._version <= seqNum) {
  2293. SessionStateRecord sv = sdata._delta[stateId];
  2294. sv._version = seqNum;
  2295. sv._dataLength = stateLen;
  2296. if (sv._recoverable != recoverable) {
  2297. if (recoverable) {
  2298. Debug.Assert(sdata._unrecoverableStatesCount > 0, "Unrecoverable states count >0");
  2299. sdata._unrecoverableStatesCount--;
  2300. }
  2301. else {
  2302. checked { sdata._unrecoverableStatesCount++; }
  2303. }
  2304. sv._recoverable = recoverable;
  2305. }
  2306. buffer = sv._data;
  2307. if (buffer.Length < stateLen) {
  2308. buffer = new byte[stateLen];
  2309. sv._data = buffer;
  2310. }
  2311. }
  2312. }
  2313. }
  2314. if (buffer != null) {
  2315. if (!stateObj.TryReadByteArray(buffer, 0, stateLen)) {
  2316. return false;
  2317. }
  2318. }
  2319. else {
  2320. if (!stateObj.TrySkipBytes(stateLen))
  2321. return false;
  2322. }
  2323. if (stateLenByte < 0xFF) {
  2324. length -= 2 + stateLen;
  2325. }
  2326. else {
  2327. length -= 6 + stateLen;
  2328. }
  2329. }
  2330. sdata.AssertUnrecoverableStateCountIsCorrect();
  2331. return true;
  2332. }
  2333. private bool TryProcessLoginAck(TdsParserStateObject stateObj, out SqlLoginAck sqlLoginAck) {
  2334. SqlLoginAck a = new SqlLoginAck();
  2335. sqlLoginAck = null;
  2336. // read past interface type and version
  2337. if (!stateObj.TrySkipBytes(1)) {
  2338. return false;
  2339. }
  2340. byte[] b = new byte[TdsEnums.VERSION_SIZE];
  2341. if (!stateObj.TryReadByteArray(b, 0, b.Length)) {
  2342. return false;
  2343. }
  2344. a.tdsVersion = (UInt32)((((((b[0]<<8)|b[1])<<8)|b[2])<<8)|b[3]); // bytes are in motorola order (high byte first)
  2345. UInt32 majorMinor = a.tdsVersion & 0xff00ffff;
  2346. UInt32 increment = (a.tdsVersion >> 16) & 0xff;
  2347. // Server responds:
  2348. // 0x07000000 -> Sphinx // Notice server response format is different for bwd compat
  2349. // 0x07010000 -> Shiloh RTM // Notice server response format is different for bwd compat
  2350. // 0x71000001 -> Shiloh SP1
  2351. // 0x72xx0002 -> Yukon RTM
  2352. // information provided by S. Ashwin
  2353. switch (majorMinor) {
  2354. case TdsEnums.SPHINXORSHILOH_MAJOR<<24|TdsEnums.DEFAULT_MINOR: // Sphinx & Shiloh RTM
  2355. // note that sphinx and shiloh_rtm can only be distinguished by the increment
  2356. switch (increment) {
  2357. case TdsEnums.SHILOH_INCREMENT:
  2358. _isShiloh = true;
  2359. break;
  2360. case TdsEnums.SPHINX_INCREMENT:
  2361. // no flag will be set
  2362. break;
  2363. default:
  2364. throw SQL.InvalidTDSVersion();
  2365. }
  2366. break;
  2367. case TdsEnums.SHILOHSP1_MAJOR<<24|TdsEnums.SHILOHSP1_MINOR: // Shiloh SP1
  2368. if (increment != TdsEnums.SHILOHSP1_INCREMENT) { throw SQL.InvalidTDSVersion(); }
  2369. _isShilohSP1 = true;
  2370. break;
  2371. case TdsEnums.YUKON_MAJOR<<24|TdsEnums.YUKON_RTM_MINOR: // Yukon
  2372. if (increment != TdsEnums.YUKON_INCREMENT) { throw SQL.InvalidTDSVersion(); }
  2373. _isYukon = true;
  2374. break;
  2375. case TdsEnums.KATMAI_MAJOR<<24|TdsEnums.KATMAI_MINOR:
  2376. if (increment != TdsEnums.KATMAI_INCREMENT) { throw SQL.InvalidTDSVersion(); }
  2377. _isKatmai = true;
  2378. break;
  2379. case TdsEnums.DENALI_MAJOR << 24|TdsEnums.DENALI_MINOR:
  2380. if (increment != TdsEnums.DENALI_INCREMENT) { throw SQL.InvalidTDSVersion(); }
  2381. _isDenali = true;
  2382. break;
  2383. default:
  2384. throw SQL.InvalidTDSVersion();
  2385. }
  2386. _isKatmai |= _isDenali;
  2387. _isYukon |= _isKatmai;
  2388. _isShilohSP1 |= _isYukon; // includes all lower versions
  2389. _isShiloh |= _isShilohSP1; //
  2390. a.isVersion8 = _isShiloh;
  2391. stateObj._outBytesUsed = stateObj._outputHeaderLen;
  2392. byte len;
  2393. if (!stateObj.TryReadByte(out len)) {
  2394. return false;
  2395. }
  2396. if (!stateObj.TryReadString(len, out a.programName)) {
  2397. return false;
  2398. }
  2399. if (!stateObj.TryReadByte(out a.majorVersion)) {
  2400. return false;
  2401. }
  2402. if (!stateObj.TryReadByte(out a.minorVersion)) {
  2403. return false;
  2404. }
  2405. byte buildNumHi, buildNumLo;
  2406. if (!stateObj.TryReadByte(out buildNumHi)) {
  2407. return false;
  2408. }
  2409. if (!stateObj.TryReadByte(out buildNumLo)) {
  2410. return false;
  2411. }
  2412. a.buildNum = (short)((buildNumHi << 8) + buildNumLo);
  2413. Debug.Assert(_state == TdsParserState.OpenNotLoggedIn, "ProcessLoginAck called with state not TdsParserState.OpenNotLoggedIn");
  2414. _state = TdsParserState.OpenLoggedIn;
  2415. if (_isYukon) {
  2416. if (_fMARS) {
  2417. _resetConnectionEvent = new AutoResetEvent(true);
  2418. }
  2419. }
  2420. // Fail if SSE UserInstance and we have not received this info.
  2421. if ( _connHandler.ConnectionOptions.UserInstance &&
  2422. ADP.IsEmpty(_connHandler.InstanceName)) {
  2423. stateObj.AddError(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, Server, SQLMessage.UserInstanceFailure(), "", 0));
  2424. ThrowExceptionAndWarning(stateObj);
  2425. }
  2426. sqlLoginAck = a;
  2427. return true;
  2428. }
  2429. internal bool TryProcessError(byte token, TdsParserStateObject stateObj, out SqlError error) {
  2430. ushort shortLen;
  2431. byte byteLen;
  2432. int number;
  2433. byte state;
  2434. byte errorClass;
  2435. error = null;
  2436. if (!stateObj.TryReadInt32(out number)) {
  2437. return false;
  2438. }
  2439. if (!stateObj.TryReadByte(out state)) {
  2440. return false;
  2441. }
  2442. if (!stateObj.TryReadByte(out errorClass)) {
  2443. return false;
  2444. }
  2445. Debug.Assert(((errorClass >= TdsEnums.MIN_ERROR_CLASS) && token == TdsEnums.SQLERROR) ||
  2446. ((errorClass < TdsEnums.MIN_ERROR_CLASS) && token == TdsEnums.SQLINFO), "class and token don't match!");
  2447. if (!stateObj.TryReadUInt16(out shortLen)) {
  2448. return false;
  2449. }
  2450. string message;
  2451. if (!stateObj.TryReadString(shortLen, out message)) {
  2452. return false;
  2453. }
  2454. if (!stateObj.TryReadByte(out byteLen)) {
  2455. return false;
  2456. }
  2457. string server;
  2458. // MDAC bug #49307 - server sometimes does not send over server field! In those cases
  2459. // we will use our locally cached value.
  2460. if (byteLen == 0) {
  2461. server = _server;
  2462. }
  2463. else {
  2464. if (!stateObj.TryReadString(byteLen, out server)) {
  2465. return false;
  2466. }
  2467. }
  2468. if (!stateObj.TryReadByte(out byteLen)) {
  2469. return false;
  2470. }
  2471. string procedure;
  2472. if (!stateObj.TryReadString(byteLen, out procedure)) {
  2473. return false;
  2474. }
  2475. int line;
  2476. if (_isYukon) {
  2477. if (!stateObj.TryReadInt32(out line)) {
  2478. return false;
  2479. }
  2480. }
  2481. else {
  2482. ushort shortLine;
  2483. if (!stateObj.TryReadUInt16(out shortLine)) {
  2484. return false;
  2485. }
  2486. line = shortLine;
  2487. // If we haven't yet completed processing login token stream yet, we may be talking to a Yukon server
  2488. // In that case we still have to read another 2 bytes
  2489. if ( _state == TdsParserState.OpenNotLoggedIn) {
  2490. // Login incomplete
  2491. byte b;
  2492. if (!stateObj.TryPeekByte(out b)) {
  2493. return false;
  2494. }
  2495. if (b == 0) {
  2496. // This is an invalid token value
  2497. ushort value;
  2498. if (!stateObj.TryReadUInt16(out value)) {
  2499. return false;
  2500. }
  2501. line = (line << 16) + value;
  2502. }
  2503. }
  2504. }
  2505. error = new SqlError(number, state, errorClass, _server, message, procedure, line);
  2506. return true;
  2507. }
  2508. internal bool TryProcessReturnValue(int length, TdsParserStateObject stateObj, out SqlReturnValue returnValue) {
  2509. returnValue = null;
  2510. SqlReturnValue rec = new SqlReturnValue();
  2511. rec.length = length; // In Yukon this length is -1
  2512. if (_isYukon) {
  2513. if (!stateObj.TryReadUInt16(out rec.parmIndex)) {
  2514. return false;
  2515. }
  2516. }
  2517. byte len;
  2518. if (!stateObj.TryReadByte(out len)) { // Length of parameter name
  2519. return false;
  2520. }
  2521. if (len > 0) {
  2522. if (!stateObj.TryReadString(len, out rec.parameter)) {
  2523. return false;
  2524. }
  2525. }
  2526. // read status and ignore
  2527. byte ignored;
  2528. if (!stateObj.TryReadByte(out ignored)) {
  2529. return false;
  2530. }
  2531. UInt32 userType;
  2532. // read user type - 4 bytes Yukon, 2 backwards
  2533. if (IsYukonOrNewer) {
  2534. if (!stateObj.TryReadUInt32(out userType)) {
  2535. return false;
  2536. }
  2537. }
  2538. else {
  2539. ushort userTypeShort;
  2540. if (!stateObj.TryReadUInt16(out userTypeShort)) {
  2541. return false;
  2542. }
  2543. userType = userTypeShort;
  2544. }
  2545. // read off the flags
  2546. ushort ignoredFlags;
  2547. if (!stateObj.TryReadUInt16(out ignoredFlags)) {
  2548. return false;
  2549. }
  2550. // read the type
  2551. byte tdsType;
  2552. if (!stateObj.TryReadByte(out tdsType)) {
  2553. return false;
  2554. }
  2555. // read the MaxLen
  2556. // For xml datatpyes, there is no tokenLength
  2557. int tdsLen;
  2558. if (tdsType == TdsEnums.SQLXMLTYPE) {
  2559. tdsLen = TdsEnums.SQL_USHORTVARMAXLEN;
  2560. }
  2561. else if (IsVarTimeTds(tdsType))
  2562. tdsLen = 0; // placeholder until we read the scale, just make sure it's not SQL_USHORTVARMAXLEN
  2563. else if (tdsType == TdsEnums.SQLDATE) {
  2564. tdsLen = 3;
  2565. }
  2566. else {
  2567. if (!TryGetTokenLength(tdsType, stateObj, out tdsLen)) {
  2568. return false;
  2569. }
  2570. }
  2571. rec.metaType = MetaType.GetSqlDataType(tdsType, userType, tdsLen);
  2572. rec.type = rec.metaType.SqlDbType;
  2573. // always use the nullable type for parameters if Shiloh or later
  2574. // Sphinx sometimes sends fixed length return values
  2575. if (_isShiloh) {
  2576. rec.tdsType = rec.metaType.NullableType;
  2577. rec.isNullable = true;
  2578. if (tdsLen == TdsEnums.SQL_USHORTVARMAXLEN) {
  2579. Debug.Assert(_isYukon, "plp data from pre-Yukon server");
  2580. rec.metaType = MetaType.GetMaxMetaTypeFromMetaType(rec.metaType);
  2581. }
  2582. }
  2583. else { // For sphinx, keep the fixed type if that is what is returned
  2584. if (rec.metaType.NullableType == tdsType)
  2585. rec.isNullable = true;
  2586. rec.tdsType = (byte)tdsType;
  2587. }
  2588. if (rec.type == SqlDbType.Decimal) {
  2589. if (!stateObj.TryReadByte(out rec.precision)) {
  2590. return false;
  2591. }
  2592. if (!stateObj.TryReadByte(out rec.scale)) {
  2593. return false;
  2594. }
  2595. }
  2596. if (rec.metaType.IsVarTime) {
  2597. if (!stateObj.TryReadByte(out rec.scale)) {
  2598. return false;
  2599. }
  2600. }
  2601. if (tdsType == TdsEnums.SQLUDT) {
  2602. if (!TryProcessUDTMetaData((SqlMetaDataPriv) rec, stateObj)) {
  2603. return false;
  2604. }
  2605. }
  2606. if (rec.type == SqlDbType.Xml) {
  2607. // Read schema info
  2608. byte schemapresent;
  2609. if (!stateObj.TryReadByte(out schemapresent)) {
  2610. return false;
  2611. }
  2612. if ((schemapresent & 1) != 0) {
  2613. if (!stateObj.TryReadByte(out len)) {
  2614. return false;
  2615. }
  2616. if (len != 0) {
  2617. if (!stateObj.TryReadString(len, out rec.xmlSchemaCollectionDatabase)) {
  2618. return false;
  2619. }
  2620. }
  2621. if (!stateObj.TryReadByte(out len)) {
  2622. return false;
  2623. }
  2624. if (len != 0) {
  2625. if (!stateObj.TryReadString(len, out rec.xmlSchemaCollectionOwningSchema)) {
  2626. return false;
  2627. }
  2628. }
  2629. short slen;
  2630. if (!stateObj.TryReadInt16(out slen)) {
  2631. return false;
  2632. }
  2633. if (slen != 0) {
  2634. if (!stateObj.TryReadString(slen, out rec.xmlSchemaCollectionName)) {
  2635. return false;
  2636. }
  2637. }
  2638. }
  2639. }
  2640. else if (_isShiloh && rec.metaType.IsCharType) {
  2641. // read the collation for 8.x servers
  2642. if (!TryProcessCollation(stateObj, out rec.collation)) {
  2643. return false;
  2644. }
  2645. int codePage = GetCodePage(rec.collation, stateObj);
  2646. // if the column lcid is the same as the default, use the default encoder
  2647. if (codePage == _defaultCodePage) {
  2648. rec.codePage = _defaultCodePage;
  2649. rec.encoding = _defaultEncoding;
  2650. }
  2651. else {
  2652. rec.codePage = codePage;
  2653. rec.encoding = System.Text.Encoding.GetEncoding(rec.codePage);
  2654. }
  2655. }
  2656. // for now we coerce return values into a SQLVariant, not good...
  2657. bool isNull = false;
  2658. ulong valLen;
  2659. if (!TryProcessColumnHeaderNoNBC(rec, stateObj, out isNull, out valLen)) {
  2660. return false;
  2661. }
  2662. // always read as sql types
  2663. Debug.Assert(valLen < (ulong)(Int32.MaxValue), "ProcessReturnValue received data size > 2Gb");
  2664. int intlen = valLen > (ulong)(Int32.MaxValue) ? Int32.MaxValue : (int)valLen;
  2665. if (rec.metaType.IsPlp) {
  2666. intlen = Int32.MaxValue; // If plp data, read it all
  2667. }
  2668. if (isNull) {
  2669. GetNullSqlValue(rec.value, rec);
  2670. }
  2671. else {
  2672. if (!TryReadSqlValue(rec.value, rec, intlen, stateObj)) {
  2673. return false;
  2674. }
  2675. }
  2676. returnValue = rec;
  2677. return true;
  2678. }
  2679. internal bool TryProcessCollation(TdsParserStateObject stateObj, out SqlCollation collation) {
  2680. SqlCollation newCollation = new SqlCollation();
  2681. if (!stateObj.TryReadUInt32(out newCollation.info)) {
  2682. collation = null;
  2683. return false;
  2684. }
  2685. if (!stateObj.TryReadByte(out newCollation.sortId)) {
  2686. collation = null;
  2687. return false;
  2688. }
  2689. collation = newCollation;
  2690. return true;
  2691. }
  2692. private void WriteCollation(SqlCollation collation, TdsParserStateObject stateObj) {
  2693. if (collation == null) {
  2694. _physicalStateObj.WriteByte(0);
  2695. }
  2696. else {
  2697. _physicalStateObj.WriteByte(sizeof(UInt32)+sizeof(byte));
  2698. WriteUnsignedInt(collation.info, _physicalStateObj);
  2699. _physicalStateObj.WriteByte(collation.sortId);
  2700. }
  2701. }
  2702. internal int GetCodePage(SqlCollation collation, TdsParserStateObject stateObj) {
  2703. int codePage = 0;
  2704. if (0 != collation.sortId) {
  2705. codePage = TdsEnums.CODE_PAGE_FROM_SORT_ID[collation.sortId];
  2706. Debug.Assert(0 != codePage, "GetCodePage accessed codepage array and produced 0!, sortID =" + ((Byte)(collation.sortId)).ToString((IFormatProvider)null));
  2707. }
  2708. else {
  2709. int cultureId = collation.LCID;
  2710. bool success = false;
  2711. try {
  2712. codePage = CultureInfo.GetCultureInfo(cultureId).TextInfo.ANSICodePage;
  2713. // SqlHot 50001398: CodePage can be zero, but we should defer such errors until
  2714. // we actually MUST use the code page (i.e. don't error if no ANSI data is sent).
  2715. success = true;
  2716. }
  2717. catch (ArgumentException e) {
  2718. ADP.TraceExceptionWithoutRethrow(e);
  2719. }
  2720. // If we failed, it is quite possible this is because certain culture id's
  2721. // were removed in Win2k and beyond, however Sql Server still supports them.
  2722. // There is a workaround for the culture id's listed below, which is to mask
  2723. // off the sort id (the leading 1). If that fails, or we have a culture id
  2724. // other than the special cases below, we throw an error and throw away the
  2725. // rest of the results. For additional info, see MDAC 65963.
  2726. // SqlHot 50001398: Sometimes GetCultureInfo will return CodePage 0 instead of throwing.
  2727. // treat this as an error also, and switch into the special-case logic.
  2728. if (!success || codePage == 0) {
  2729. CultureInfo ci = null;
  2730. switch (cultureId) {
  2731. case 0x10404: // zh-TW
  2732. case 0x10804: // zh-CN
  2733. case 0x10c04: // zh-HK
  2734. case 0x11004: // zh-SG
  2735. case 0x11404: // zh-MO
  2736. case 0x10411: // ja-JP
  2737. case 0x10412: // ko-KR
  2738. // If one of the following special cases, mask out sortId and
  2739. // retry.
  2740. cultureId = cultureId & 0x03fff;
  2741. try {
  2742. ci = new CultureInfo(cultureId);
  2743. success = true;
  2744. }
  2745. catch (ArgumentException e) {
  2746. ADP.TraceExceptionWithoutRethrow(e);
  2747. }
  2748. break;
  2749. case 0x827: // Non-supported Lithuanian code page, map it to supported Lithuanian.
  2750. try {
  2751. ci = new CultureInfo(0x427);
  2752. success = true;
  2753. }
  2754. catch (ArgumentException e) {
  2755. ADP.TraceExceptionWithoutRethrow(e);
  2756. }
  2757. break;
  2758. default:
  2759. break;
  2760. }
  2761. // I don't believe we should still be in failure case, but just in case.
  2762. if (!success) {
  2763. ThrowUnsupportedCollationEncountered(stateObj);
  2764. }
  2765. if (null != ci) {
  2766. codePage = ci.TextInfo.ANSICodePage;
  2767. }
  2768. }
  2769. }
  2770. return codePage;
  2771. }
  2772. internal void DrainData(TdsParserStateObject stateObj) {
  2773. RuntimeHelpers.PrepareConstrainedRegions();
  2774. try {
  2775. #if DEBUG
  2776. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  2777. RuntimeHelpers.PrepareConstrainedRegions();
  2778. try {
  2779. tdsReliabilitySection.Start();
  2780. #else
  2781. {
  2782. #endif //DEBUG
  2783. try {
  2784. SqlDataReader.SharedState sharedState = stateObj._readerState;
  2785. if (sharedState != null && sharedState._dataReady) {
  2786. var metadata = stateObj._cleanupMetaData;
  2787. if (stateObj._partialHeaderBytesRead > 0) {
  2788. if (!stateObj.TryProcessHeader()) {
  2789. throw SQL.SynchronousCallMayNotPend();
  2790. }
  2791. }
  2792. if (0 == sharedState._nextColumnHeaderToRead) {
  2793. // i. user called read but didn't fetch anything
  2794. if (!stateObj.Parser.TrySkipRow(stateObj._cleanupMetaData, stateObj)) {
  2795. throw SQL.SynchronousCallMayNotPend();
  2796. }
  2797. }
  2798. else {
  2799. // iia. if we still have bytes left from a partially read column, skip
  2800. if (sharedState._nextColumnDataToRead < sharedState._nextColumnHeaderToRead) {
  2801. if ((sharedState._nextColumnHeaderToRead > 0) && (metadata[sharedState._nextColumnHeaderToRead - 1].metaType.IsPlp)) {
  2802. if (stateObj._longlen != 0) {
  2803. ulong ignored;
  2804. if (!TrySkipPlpValue(UInt64.MaxValue, stateObj, out ignored)) {
  2805. throw SQL.SynchronousCallMayNotPend();
  2806. }
  2807. }
  2808. }
  2809. else if (0 < sharedState._columnDataBytesRemaining) {
  2810. if (!stateObj.TrySkipLongBytes(sharedState._columnDataBytesRemaining)) {
  2811. throw SQL.SynchronousCallMayNotPend();
  2812. }
  2813. }
  2814. }
  2815. // iib.
  2816. // now read the remaining values off the wire for this row
  2817. if (!stateObj.Parser.TrySkipRow(metadata, sharedState._nextColumnHeaderToRead, stateObj)) {
  2818. throw SQL.SynchronousCallMayNotPend();
  2819. }
  2820. }
  2821. }
  2822. Run(RunBehavior.Clean, null, null, null, stateObj);
  2823. }
  2824. catch {
  2825. _connHandler.DoomThisConnection();
  2826. throw;
  2827. }
  2828. }
  2829. #if DEBUG
  2830. finally {
  2831. tdsReliabilitySection.Stop();
  2832. }
  2833. #endif //DEBUG
  2834. }
  2835. catch (System.OutOfMemoryException) {
  2836. _connHandler.DoomThisConnection();
  2837. throw;
  2838. }
  2839. catch (System.StackOverflowException) {
  2840. _connHandler.DoomThisConnection();
  2841. throw;
  2842. }
  2843. catch (System.Threading.ThreadAbortException) {
  2844. _connHandler.DoomThisConnection();
  2845. throw;
  2846. }
  2847. }
  2848. internal void ThrowUnsupportedCollationEncountered(TdsParserStateObject stateObj) {
  2849. stateObj.AddError(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.CultureIdError(), "", 0));
  2850. if (null != stateObj) {
  2851. DrainData(stateObj);
  2852. stateObj._pendingData = false;
  2853. }
  2854. ThrowExceptionAndWarning(stateObj);
  2855. }
  2856. internal bool TryProcessAltMetaData(int cColumns, TdsParserStateObject stateObj, out _SqlMetaDataSet metaData) {
  2857. Debug.Assert(cColumns > 0, "should have at least 1 column in altMetaData!");
  2858. metaData = null;
  2859. _SqlMetaDataSet altMetaDataSet = new _SqlMetaDataSet(cColumns);
  2860. int[] indexMap = new int[cColumns];
  2861. if (!stateObj.TryReadUInt16(out altMetaDataSet.id)) {
  2862. return false;
  2863. }
  2864. byte byCols;
  2865. if (!stateObj.TryReadByte(out byCols)) {
  2866. return false;
  2867. }
  2868. while (byCols > 0) {
  2869. if (!stateObj.TrySkipBytes(2)) { // ignore ColNum ...
  2870. return false;
  2871. }
  2872. byCols--;
  2873. }
  2874. // pass 1, read the meta data off the wire
  2875. for (int i = 0; i < cColumns; i++) {
  2876. // internal meta data class
  2877. _SqlMetaData col = altMetaDataSet[i];
  2878. if (!stateObj.TryReadByte(out col.op)) {
  2879. return false;
  2880. }
  2881. if (!stateObj.TryReadUInt16(out col.operand)) {
  2882. return false;
  2883. }
  2884. if (!TryCommonProcessMetaData(stateObj, col)) {
  2885. return false;
  2886. }
  2887. if (ADP.IsEmpty(col.column)) {
  2888. // create column name from op
  2889. switch (col.op) {
  2890. case TdsEnums.AOPAVG:
  2891. col.column = "avg";
  2892. break;
  2893. case TdsEnums.AOPCNT:
  2894. col.column = "cnt";
  2895. break;
  2896. case TdsEnums.AOPCNTB:
  2897. col.column = "cntb";
  2898. break;
  2899. case TdsEnums.AOPMAX:
  2900. col.column = "max";
  2901. break;
  2902. case TdsEnums.AOPMIN:
  2903. col.column = "min";
  2904. break;
  2905. case TdsEnums.AOPSUM:
  2906. col.column = "sum";
  2907. break;
  2908. case TdsEnums.AOPANY:
  2909. col.column = "any";
  2910. break;
  2911. case TdsEnums.AOPNOOP:
  2912. col.column = "noop";
  2913. break;
  2914. case TdsEnums.AOPSTDEV:
  2915. col.column = "stdev";
  2916. break;
  2917. case TdsEnums.AOPSTDEVP:
  2918. col.column = "stdevp";
  2919. break;
  2920. case TdsEnums.AOPVAR:
  2921. col.column = "var";
  2922. break;
  2923. case TdsEnums.AOPVARP:
  2924. col.column = "varp";
  2925. break;
  2926. }
  2927. }
  2928. indexMap[i] = i;
  2929. }
  2930. altMetaDataSet.indexMap = indexMap;
  2931. altMetaDataSet.visibleColumns = cColumns;
  2932. metaData = altMetaDataSet;
  2933. return true;
  2934. }
  2935. internal bool TryProcessMetaData(int cColumns, TdsParserStateObject stateObj, out _SqlMetaDataSet metaData) {
  2936. Debug.Assert(cColumns > 0, "should have at least 1 column in metadata!");
  2937. _SqlMetaDataSet newMetaData = new _SqlMetaDataSet(cColumns);
  2938. for (int i = 0; i < cColumns; i++) {
  2939. if (!TryCommonProcessMetaData(stateObj, newMetaData[i])) {
  2940. metaData = null;
  2941. return false;
  2942. }
  2943. }
  2944. metaData = newMetaData;
  2945. return true;
  2946. }
  2947. private bool IsVarTimeTds(byte tdsType) {
  2948. return tdsType == TdsEnums.SQLTIME || tdsType == TdsEnums.SQLDATETIME2 || tdsType == TdsEnums.SQLDATETIMEOFFSET;
  2949. }
  2950. private bool TryCommonProcessMetaData(TdsParserStateObject stateObj, _SqlMetaData col) {
  2951. byte byteLen;
  2952. UInt32 userType;
  2953. // read user type - 4 bytes Yukon, 2 backwards
  2954. if (IsYukonOrNewer) {
  2955. if (!stateObj.TryReadUInt32(out userType)) {
  2956. return false;
  2957. }
  2958. }
  2959. else {
  2960. ushort userTypeShort;
  2961. if (!stateObj.TryReadUInt16(out userTypeShort)) {
  2962. return false;
  2963. }
  2964. userType = userTypeShort;
  2965. }
  2966. // read flags and set appropriate flags in structure
  2967. byte flags;
  2968. if (!stateObj.TryReadByte(out flags)) {
  2969. return false;
  2970. }
  2971. col.updatability = (byte)((flags & TdsEnums.Updatability) >> 2);
  2972. col.isNullable = (TdsEnums.Nullable == (flags & TdsEnums.Nullable));
  2973. col.isIdentity = (TdsEnums.Identity == (flags & TdsEnums.Identity));
  2974. // read second byte of column metadata flags
  2975. if (!stateObj.TryReadByte(out flags)) {
  2976. return false;
  2977. }
  2978. col.isColumnSet = (TdsEnums.IsColumnSet == (flags & TdsEnums.IsColumnSet));
  2979. byte tdsType;
  2980. if (!stateObj.TryReadByte(out tdsType)) {
  2981. return false;
  2982. }
  2983. if (tdsType == TdsEnums.SQLXMLTYPE)
  2984. col.length = TdsEnums.SQL_USHORTVARMAXLEN; //Use the same length as other plp datatypes
  2985. else if (IsVarTimeTds(tdsType))
  2986. col.length = 0; // placeholder until we read the scale, just make sure it's not SQL_USHORTVARMAXLEN
  2987. else if (tdsType == TdsEnums.SQLDATE) {
  2988. col.length = 3;
  2989. }
  2990. else {
  2991. if (!TryGetTokenLength(tdsType, stateObj, out col.length)) {
  2992. return false;
  2993. }
  2994. }
  2995. col.metaType = MetaType.GetSqlDataType(tdsType, userType, col.length);
  2996. col.type = col.metaType.SqlDbType;
  2997. // If sphinx, do not change to nullable type
  2998. if (_isShiloh)
  2999. col.tdsType = (col.isNullable ? col.metaType.NullableType : col.metaType.TDSType);
  3000. else
  3001. col.tdsType = tdsType;
  3002. if (_isYukon) {
  3003. if (TdsEnums.SQLUDT == tdsType) {
  3004. if (!TryProcessUDTMetaData((SqlMetaDataPriv) col, stateObj)) {
  3005. return false;
  3006. }
  3007. }
  3008. if (col.length == TdsEnums.SQL_USHORTVARMAXLEN) {
  3009. Debug.Assert(tdsType == TdsEnums.SQLXMLTYPE ||
  3010. tdsType == TdsEnums.SQLBIGVARCHAR ||
  3011. tdsType == TdsEnums.SQLBIGVARBINARY ||
  3012. tdsType == TdsEnums.SQLNVARCHAR ||
  3013. tdsType == TdsEnums.SQLUDT,
  3014. "Invalid streaming datatype");
  3015. col.metaType = MetaType.GetMaxMetaTypeFromMetaType(col.metaType);
  3016. Debug.Assert(col.metaType.IsLong, "Max datatype not IsLong");
  3017. col.length = Int32.MaxValue;
  3018. if (tdsType == TdsEnums.SQLXMLTYPE) {
  3019. byte schemapresent;
  3020. if (!stateObj.TryReadByte(out schemapresent)) {
  3021. return false;
  3022. }
  3023. if ((schemapresent & 1) != 0) {
  3024. if (!stateObj.TryReadByte(out byteLen)) {
  3025. return false;
  3026. }
  3027. if (byteLen != 0) {
  3028. if (!stateObj.TryReadString(byteLen, out col.xmlSchemaCollectionDatabase)) {
  3029. return false;
  3030. }
  3031. }
  3032. if (!stateObj.TryReadByte(out byteLen)) {
  3033. return false;
  3034. }
  3035. if (byteLen != 0) {
  3036. if (!stateObj.TryReadString(byteLen, out col.xmlSchemaCollectionOwningSchema)) {
  3037. return false;
  3038. }
  3039. }
  3040. short shortLen;
  3041. if (!stateObj.TryReadInt16(out shortLen)) {
  3042. return false;
  3043. }
  3044. if (byteLen != 0) {
  3045. if (!stateObj.TryReadString(shortLen, out col.xmlSchemaCollectionName)) {
  3046. return false;
  3047. }
  3048. }
  3049. }
  3050. }
  3051. }
  3052. }
  3053. if (col.type == SqlDbType.Decimal) {
  3054. if (!stateObj.TryReadByte(out col.precision)) {
  3055. return false;
  3056. }
  3057. if (!stateObj.TryReadByte(out col.scale)) {
  3058. return false;
  3059. }
  3060. }
  3061. if (col.metaType.IsVarTime) {
  3062. if (!stateObj.TryReadByte(out col.scale)) {
  3063. return false;
  3064. }
  3065. Debug.Assert(0 <= col.scale && col.scale <= 7);
  3066. // calculate actual column length here
  3067. //
  3068. switch (col.metaType.SqlDbType)
  3069. {
  3070. case SqlDbType.Time:
  3071. col.length = MetaType.GetTimeSizeFromScale(col.scale);
  3072. break;
  3073. case SqlDbType.DateTime2:
  3074. // Date in number of days (3 bytes) + time
  3075. col.length = 3 + MetaType.GetTimeSizeFromScale(col.scale);
  3076. break;
  3077. case SqlDbType.DateTimeOffset:
  3078. // Date in days (3 bytes) + offset in minutes (2 bytes) + time
  3079. col.length = 5 + MetaType.GetTimeSizeFromScale(col.scale);
  3080. break;
  3081. default:
  3082. Debug.Assert(false, "Unknown VariableTime type!");
  3083. break;
  3084. }
  3085. }
  3086. // read the collation for 7.x servers
  3087. if (_isShiloh && col.metaType.IsCharType && (tdsType != TdsEnums.SQLXMLTYPE)) {
  3088. if (!TryProcessCollation(stateObj, out col.collation)) {
  3089. return false;
  3090. }
  3091. int codePage = GetCodePage(col.collation, stateObj);
  3092. if (codePage == _defaultCodePage) {
  3093. col.codePage = _defaultCodePage;
  3094. col.encoding = _defaultEncoding;
  3095. }
  3096. else {
  3097. col.codePage = codePage;
  3098. col.encoding = System.Text.Encoding.GetEncoding(col.codePage);
  3099. }
  3100. }
  3101. if (col.metaType.IsLong && !col.metaType.IsPlp) {
  3102. if (_isYukon) {
  3103. int unusedLen = 0xFFFF; //We ignore this value
  3104. if (!TryProcessOneTable(stateObj, ref unusedLen, out col.multiPartTableName)) {
  3105. return false;
  3106. }
  3107. } else {
  3108. ushort shortLen;
  3109. if (!stateObj.TryReadUInt16(out shortLen)) {
  3110. return false;
  3111. }
  3112. string tableName;
  3113. if (!stateObj.TryReadString(shortLen, out tableName)) {
  3114. return false;
  3115. }
  3116. // with Sql2000 this is returned as an unquoted mix of catalog.owner.table
  3117. // all of which may contain "." and unable to parse correctly from the string alone
  3118. // example "select * from pubs..[A.B.C.D.E]" AND only when * will contain a image/text/ntext column
  3119. // by delay parsing from execute to SqlDataReader.GetSchemaTable to enable more scenarios
  3120. col.multiPartTableName = new MultiPartTableName(tableName);
  3121. }
  3122. }
  3123. if (!stateObj.TryReadByte(out byteLen)) {
  3124. return false;
  3125. }
  3126. if (!stateObj.TryReadString(byteLen, out col.column)) {
  3127. return false;
  3128. }
  3129. // We get too many DONE COUNTs from the server, causing too meany StatementCompleted event firings.
  3130. // We only need to fire this event when we actually have a meta data stream with 0 or more rows.
  3131. stateObj._receivedColMetaData = true;
  3132. return true;
  3133. }
  3134. private bool TryProcessUDTMetaData(SqlMetaDataPriv metaData, TdsParserStateObject stateObj) {
  3135. ushort shortLength;
  3136. byte byteLength;
  3137. if (!stateObj.TryReadUInt16(out shortLength)) { // max byte size
  3138. return false;
  3139. }
  3140. metaData.length = shortLength;
  3141. // database name
  3142. if (!stateObj.TryReadByte(out byteLength)) {
  3143. return false;
  3144. }
  3145. if (byteLength != 0) {
  3146. if (!stateObj.TryReadString(byteLength, out metaData.udtDatabaseName)) {
  3147. return false;
  3148. }
  3149. }
  3150. // schema name
  3151. if (!stateObj.TryReadByte(out byteLength)) {
  3152. return false;
  3153. }
  3154. if (byteLength != 0) {
  3155. if (!stateObj.TryReadString(byteLength, out metaData.udtSchemaName)) {
  3156. return false;
  3157. }
  3158. }
  3159. // type name
  3160. if (!stateObj.TryReadByte(out byteLength)) {
  3161. return false;
  3162. }
  3163. if (byteLength != 0) {
  3164. if (!stateObj.TryReadString(byteLength, out metaData.udtTypeName)) {
  3165. return false;
  3166. }
  3167. }
  3168. if (!stateObj.TryReadUInt16(out shortLength)) {
  3169. return false;
  3170. }
  3171. if (shortLength != 0) {
  3172. if (!stateObj.TryReadString(shortLength, out metaData.udtAssemblyQualifiedName)) {
  3173. return false;
  3174. }
  3175. }
  3176. return true;
  3177. }
  3178. private void WriteUDTMetaData(object value, string database, string schema, string type,
  3179. TdsParserStateObject stateObj) {
  3180. // database
  3181. if (ADP.IsEmpty(database)) {
  3182. stateObj.WriteByte(0);
  3183. }
  3184. else {
  3185. stateObj.WriteByte((byte)database.Length);
  3186. WriteString(database, stateObj);
  3187. }
  3188. // schema
  3189. if (ADP.IsEmpty(schema)) {
  3190. stateObj.WriteByte(0);
  3191. }
  3192. else {
  3193. stateObj.WriteByte((byte)schema.Length);
  3194. WriteString(schema, stateObj);
  3195. }
  3196. // type
  3197. if (ADP.IsEmpty(type)) {
  3198. stateObj.WriteByte(0);
  3199. }
  3200. else {
  3201. stateObj.WriteByte((byte)type.Length);
  3202. WriteString(type, stateObj);
  3203. }
  3204. }
  3205. internal bool TryProcessTableName(int length, TdsParserStateObject stateObj, out MultiPartTableName[] multiPartTableNames) {
  3206. int tablesAdded = 0;
  3207. MultiPartTableName[] tables = new MultiPartTableName[1];
  3208. MultiPartTableName mpt;
  3209. while (length > 0) {
  3210. //
  3211. if (!TryProcessOneTable(stateObj, ref length, out mpt)) {
  3212. multiPartTableNames = null;
  3213. return false;
  3214. }
  3215. if (tablesAdded == 0) {
  3216. tables[tablesAdded] = mpt;
  3217. }
  3218. else {
  3219. MultiPartTableName[] newTables = new MultiPartTableName[tables.Length + 1];
  3220. Array.Copy(tables, 0, newTables, 0, tables.Length);
  3221. newTables[tables.Length] = mpt;
  3222. tables = newTables;
  3223. }
  3224. tablesAdded++;
  3225. }
  3226. multiPartTableNames = tables;
  3227. return true;
  3228. }
  3229. private bool TryProcessOneTable(TdsParserStateObject stateObj, ref int length, out MultiPartTableName multiPartTableName) {
  3230. ushort tableLen;
  3231. MultiPartTableName mpt;
  3232. string value;
  3233. multiPartTableName = default(MultiPartTableName);
  3234. if (_isShilohSP1) {
  3235. mpt = new MultiPartTableName();
  3236. byte nParts;
  3237. // Find out how many parts in the TDS stream
  3238. if (!stateObj.TryReadByte(out nParts)) {
  3239. return false;
  3240. }
  3241. length--;
  3242. if (nParts == 4) {
  3243. if (!stateObj.TryReadUInt16(out tableLen)) {
  3244. return false;
  3245. }
  3246. length -= 2;
  3247. if (!stateObj.TryReadString(tableLen, out value)) {
  3248. return false;
  3249. }
  3250. mpt.ServerName = value;
  3251. nParts--;
  3252. length -= (tableLen * 2); // wide bytes
  3253. }
  3254. if (nParts == 3) {
  3255. if (!stateObj.TryReadUInt16(out tableLen)) {
  3256. return false;
  3257. }
  3258. length -= 2;
  3259. if (!stateObj.TryReadString(tableLen, out value)) {
  3260. return false;
  3261. }
  3262. mpt.CatalogName = value;
  3263. length -= (tableLen * 2); // wide bytes
  3264. nParts--;
  3265. }
  3266. if (nParts == 2) {
  3267. if (!stateObj.TryReadUInt16(out tableLen)) {
  3268. return false;
  3269. }
  3270. length -= 2;
  3271. if (!stateObj.TryReadString(tableLen, out value)) {
  3272. return false;
  3273. }
  3274. mpt.SchemaName = value;
  3275. length -= (tableLen * 2); // wide bytes
  3276. nParts--;
  3277. }
  3278. if (nParts == 1) {
  3279. if (!stateObj.TryReadUInt16(out tableLen)) {
  3280. return false;
  3281. }
  3282. length -= 2;
  3283. if (!stateObj.TryReadString(tableLen, out value)) {
  3284. return false;
  3285. }
  3286. mpt.TableName = value;
  3287. length -= (tableLen * 2); // wide bytes
  3288. nParts--;
  3289. }
  3290. Debug.Assert(nParts == 0 , "ProcessTableName:Unidentified parts in the table name token stream!");
  3291. }
  3292. else {
  3293. if (!stateObj.TryReadUInt16(out tableLen)) {
  3294. return false;
  3295. }
  3296. length -= 2;
  3297. if (!stateObj.TryReadString(tableLen, out value)) {
  3298. return false;
  3299. }
  3300. string tableName = value;
  3301. length -= (tableLen * 2); // wide bytes
  3302. mpt = new MultiPartTableName(MultipartIdentifier.ParseMultipartIdentifier(tableName, "[\"", "]\"", Res.SQL_TDSParserTableName, false));
  3303. }
  3304. multiPartTableName = mpt;
  3305. return true;
  3306. }
  3307. // augments current metadata with table and key information
  3308. private bool TryProcessColInfo(_SqlMetaDataSet columns, SqlDataReader reader, TdsParserStateObject stateObj, out _SqlMetaDataSet metaData) {
  3309. Debug.Assert(columns != null && columns.Length > 0, "no metadata available!");
  3310. metaData = null;
  3311. for (int i = 0; i < columns.Length; i++) {
  3312. _SqlMetaData col = columns[i];
  3313. byte ignored;
  3314. if (!stateObj.TryReadByte(out ignored)) { // colnum, ignore
  3315. return false;
  3316. }
  3317. if (!stateObj.TryReadByte(out col.tableNum)) {
  3318. return false;
  3319. }
  3320. // interpret status
  3321. byte status;
  3322. if (!stateObj.TryReadByte(out status)) {
  3323. return false;
  3324. }
  3325. col.isDifferentName = (TdsEnums.SQLDifferentName == (status & TdsEnums.SQLDifferentName));
  3326. col.isExpression = (TdsEnums.SQLExpression == (status & TdsEnums.SQLExpression));
  3327. col.isKey = (TdsEnums.SQLKey == (status & TdsEnums.SQLKey));
  3328. col.isHidden = (TdsEnums.SQLHidden == (status & TdsEnums.SQLHidden));
  3329. // read off the base table name if it is different than the select list column name
  3330. if (col.isDifferentName) {
  3331. byte len;
  3332. if (!stateObj.TryReadByte(out len)) {
  3333. return false;
  3334. }
  3335. if (!stateObj.TryReadString(len, out col.baseColumn)) {
  3336. return false;
  3337. }
  3338. }
  3339. // Fixup column name - only if result of a table - that is if it was not the result of
  3340. // an expression.
  3341. if ((reader.TableNames != null) && (col.tableNum > 0)) {
  3342. Debug.Assert(reader.TableNames.Length >= col.tableNum, "invalid tableNames array!");
  3343. col.multiPartTableName = reader.TableNames[col.tableNum - 1];
  3344. }
  3345. // MDAC 60109: expressions are readonly
  3346. if (col.isExpression) {
  3347. col.updatability = 0;
  3348. }
  3349. }
  3350. // set the metadata so that the stream knows some metadata info has changed
  3351. metaData = columns;
  3352. return true;
  3353. }
  3354. // takes care of any per data header information:
  3355. // for long columns, reads off textptrs, reads length, check nullability
  3356. // for other columns, reads length, checks nullability
  3357. // returns length and nullability
  3358. internal bool TryProcessColumnHeader(SqlMetaDataPriv col, TdsParserStateObject stateObj, int columnOrdinal, out bool isNull, out ulong length) {
  3359. // query NBC row information first
  3360. if (stateObj.IsNullCompressionBitSet(columnOrdinal)) {
  3361. isNull = true;
  3362. // column information is not present in TDS if null compression bit is set, return now
  3363. length = 0;
  3364. return true;
  3365. }
  3366. return TryProcessColumnHeaderNoNBC(col, stateObj, out isNull, out length);
  3367. }
  3368. private bool TryProcessColumnHeaderNoNBC(SqlMetaDataPriv col, TdsParserStateObject stateObj, out bool isNull, out ulong length) {
  3369. if (col.metaType.IsLong && !col.metaType.IsPlp) {
  3370. //
  3371. // we don't care about TextPtrs, simply go after the data after it
  3372. //
  3373. byte textPtrLen;
  3374. if (!stateObj.TryReadByte(out textPtrLen)) {
  3375. isNull = false;
  3376. length = 0;
  3377. return false;
  3378. }
  3379. if (0 != textPtrLen) {
  3380. // read past text pointer
  3381. if (!stateObj.TrySkipBytes(textPtrLen)) {
  3382. isNull = false;
  3383. length = 0;
  3384. return false;
  3385. }
  3386. // read past timestamp
  3387. if (!stateObj.TrySkipBytes(TdsEnums.TEXT_TIME_STAMP_LEN)) {
  3388. isNull = false;
  3389. length = 0;
  3390. return false;
  3391. }
  3392. isNull = false;
  3393. return TryGetDataLength(col, stateObj, out length);
  3394. }
  3395. else {
  3396. isNull = true;
  3397. length = 0;
  3398. return true;
  3399. }
  3400. }
  3401. else {
  3402. // non-blob columns
  3403. ulong longlen;
  3404. if (!TryGetDataLength(col, stateObj, out longlen)) {
  3405. isNull = false;
  3406. length = 0;
  3407. return false;
  3408. }
  3409. isNull = IsNull(col.metaType, longlen);
  3410. length = (isNull ? 0 : longlen);
  3411. return true;
  3412. }
  3413. }
  3414. // assumes that the current position is at the start of an altrow!
  3415. internal bool TryGetAltRowId(TdsParserStateObject stateObj, out int id) {
  3416. byte token;
  3417. if (!stateObj.TryReadByte(out token)) { // skip over ALTROW token
  3418. id = 0;
  3419. return false;
  3420. }
  3421. Debug.Assert((token == TdsEnums.SQLALTROW), "");
  3422. // Start a fresh row - disable NBC since Alt Rows are never compressed
  3423. if (!stateObj.TryStartNewRow(isNullCompressed: false)) {
  3424. id = 0;
  3425. return false;
  3426. }
  3427. ushort shortId;
  3428. if (!stateObj.TryReadUInt16(out shortId)) {
  3429. id = 0;
  3430. return false;
  3431. }
  3432. id = shortId;
  3433. return true;
  3434. }
  3435. // Used internally by BulkCopy only
  3436. private bool TryProcessRow(_SqlMetaDataSet columns, object[] buffer, int[] map, TdsParserStateObject stateObj) {
  3437. SqlBuffer data = new SqlBuffer();
  3438. for (int i = 0; i < columns.Length; i++) {
  3439. _SqlMetaData md = columns[i];
  3440. Debug.Assert(md != null, "_SqlMetaData should not be null for column " + i.ToString(CultureInfo.InvariantCulture));
  3441. bool isNull;
  3442. ulong len;
  3443. if (!TryProcessColumnHeader(md, stateObj, i, out isNull, out len)) {
  3444. return false;
  3445. }
  3446. if (isNull) {
  3447. GetNullSqlValue(data, md);
  3448. buffer[map[i]] = data.SqlValue;
  3449. }
  3450. else {
  3451. // We only read up to 2Gb. Throw if data is larger. Very large data
  3452. // should be read in chunks in sequential read mode
  3453. // For Plp columns, we may have gotten only the length of the first chunk
  3454. if (!TryReadSqlValue(data, md, md.metaType.IsPlp ? (Int32.MaxValue) : (int)len, stateObj)) {
  3455. return false;
  3456. }
  3457. buffer[map[i]] = data.SqlValue;
  3458. if (stateObj._longlen != 0) {
  3459. throw new SqlTruncateException(Res.GetString(Res.SqlMisc_TruncationMaxDataMessage));
  3460. }
  3461. }
  3462. data.Clear();
  3463. }
  3464. return true;
  3465. }
  3466. internal object GetNullSqlValue(SqlBuffer nullVal, SqlMetaDataPriv md) {
  3467. switch (md.type) {
  3468. case SqlDbType.Real:
  3469. nullVal.SetToNullOfType(SqlBuffer.StorageType.Single);
  3470. break;
  3471. case SqlDbType.Float:
  3472. nullVal.SetToNullOfType(SqlBuffer.StorageType.Double);
  3473. break;
  3474. case SqlDbType.Udt:
  3475. case SqlDbType.Binary:
  3476. case SqlDbType.VarBinary:
  3477. case SqlDbType.Image:
  3478. nullVal.SqlBinary = SqlBinary.Null;
  3479. break;
  3480. case SqlDbType.UniqueIdentifier:
  3481. nullVal.SqlGuid = SqlGuid.Null;
  3482. break;
  3483. case SqlDbType.Bit:
  3484. nullVal.SetToNullOfType(SqlBuffer.StorageType.Boolean);
  3485. break;
  3486. case SqlDbType.TinyInt:
  3487. nullVal.SetToNullOfType(SqlBuffer.StorageType.Byte);
  3488. break;
  3489. case SqlDbType.SmallInt:
  3490. nullVal.SetToNullOfType(SqlBuffer.StorageType.Int16);
  3491. break;
  3492. case SqlDbType.Int:
  3493. nullVal.SetToNullOfType(SqlBuffer.StorageType.Int32);
  3494. break;
  3495. case SqlDbType.BigInt:
  3496. nullVal.SetToNullOfType(SqlBuffer.StorageType.Int64);
  3497. break;
  3498. case SqlDbType.Char:
  3499. case SqlDbType.VarChar:
  3500. case SqlDbType.NChar:
  3501. case SqlDbType.NVarChar:
  3502. case SqlDbType.Text:
  3503. case SqlDbType.NText:
  3504. nullVal.SetToNullOfType(SqlBuffer.StorageType.String);
  3505. break;
  3506. case SqlDbType.Decimal:
  3507. nullVal.SetToNullOfType(SqlBuffer.StorageType.Decimal);
  3508. break;
  3509. case SqlDbType.DateTime:
  3510. case SqlDbType.SmallDateTime:
  3511. nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTime);
  3512. break;
  3513. case SqlDbType.Money:
  3514. case SqlDbType.SmallMoney:
  3515. nullVal.SetToNullOfType(SqlBuffer.StorageType.Money);
  3516. break;
  3517. case SqlDbType.Variant:
  3518. // DBNull.Value will have to work here
  3519. nullVal.SetToNullOfType(SqlBuffer.StorageType.Empty);
  3520. break;
  3521. case SqlDbType.Xml:
  3522. nullVal.SqlCachedBuffer = SqlCachedBuffer.Null;
  3523. break;
  3524. case SqlDbType.Date:
  3525. nullVal.SetToNullOfType(SqlBuffer.StorageType.Date);
  3526. break;
  3527. case SqlDbType.Time:
  3528. nullVal.SetToNullOfType(SqlBuffer.StorageType.Time);
  3529. break;
  3530. case SqlDbType.DateTime2:
  3531. nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTime2);
  3532. break;
  3533. case SqlDbType.DateTimeOffset:
  3534. nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTimeOffset);
  3535. break;
  3536. case SqlDbType.Timestamp:
  3537. // Dev10 Bug #479607 - this should have been the same as SqlDbType.Binary, but it's a rejected breaking change
  3538. // Dev10 Bug #752790 - don't assert when it does happen
  3539. break;
  3540. default:
  3541. Debug.Assert(false, "unknown null sqlType!" + md.type.ToString());
  3542. break;
  3543. }
  3544. return nullVal;
  3545. }
  3546. internal bool TrySkipRow(_SqlMetaDataSet columns, TdsParserStateObject stateObj) {
  3547. return TrySkipRow(columns, 0, stateObj);
  3548. }
  3549. internal bool TrySkipRow(_SqlMetaDataSet columns, int startCol, TdsParserStateObject stateObj) {
  3550. for (int i = startCol; i < columns.Length; i++) {
  3551. _SqlMetaData md = columns[i];
  3552. if (!TrySkipValue(md, i, stateObj)) {
  3553. return false;
  3554. }
  3555. }
  3556. return true;
  3557. }
  3558. /// <summary>
  3559. /// This method skips bytes of a single column value from the media. It supports NBCROW and handles all types of values, including PLP and long
  3560. /// </summary>
  3561. internal bool TrySkipValue(SqlMetaDataPriv md, int columnOrdinal, TdsParserStateObject stateObj) {
  3562. if (stateObj.IsNullCompressionBitSet(columnOrdinal)) {
  3563. return true;
  3564. }
  3565. if (md.metaType.IsPlp) {
  3566. ulong ignored;
  3567. if (!TrySkipPlpValue(UInt64.MaxValue, stateObj, out ignored)) {
  3568. return false;
  3569. }
  3570. }
  3571. else if (md.metaType.IsLong) {
  3572. Debug.Assert(!md.metaType.IsPlp, "Plp types must be handled using SkipPlpValue");
  3573. byte textPtrLen;
  3574. if (!stateObj.TryReadByte(out textPtrLen)) {
  3575. return false;
  3576. }
  3577. if (0 != textPtrLen) {
  3578. if (!stateObj.TrySkipBytes(textPtrLen + TdsEnums.TEXT_TIME_STAMP_LEN)) {
  3579. return false;
  3580. }
  3581. int length;
  3582. if (!TryGetTokenLength(md.tdsType, stateObj, out length)) {
  3583. return false;
  3584. }
  3585. if (!stateObj.TrySkipBytes(length)) {
  3586. return false;
  3587. }
  3588. }
  3589. }
  3590. else {
  3591. int length;
  3592. if (!TryGetTokenLength(md.tdsType, stateObj, out length)) {
  3593. return false;
  3594. }
  3595. // if false, no value to skip - it's null
  3596. if (!IsNull(md.metaType, (ulong)length)) {
  3597. if (!stateObj.TrySkipBytes(length)) {
  3598. return false;
  3599. }
  3600. }
  3601. }
  3602. return true;
  3603. }
  3604. private bool IsNull(MetaType mt, ulong length) {
  3605. // null bin and char types have a length of -1 to represent null
  3606. if (mt.IsPlp) {
  3607. return (TdsEnums.SQL_PLP_NULL == length);
  3608. }
  3609. // HOTFIX #50000415: for image/text, 0xFFFF is the length, not representing null
  3610. if ((TdsEnums.VARNULL == length) && !mt.IsLong) {
  3611. return true;
  3612. }
  3613. // other types have a length of 0 to represent null
  3614. // long and non-PLP types will always return false because these types are either char or binary
  3615. // this is expected since for long and non-plp types isnull is checked based on textptr field and not the length
  3616. return ((TdsEnums.FIXEDNULL == length) && !mt.IsCharType && !mt.IsBinType);
  3617. }
  3618. private bool TryReadSqlStringValue(SqlBuffer value, byte type, int length, Encoding encoding, bool isPlp, TdsParserStateObject stateObj) {
  3619. switch (type) {
  3620. case TdsEnums.SQLCHAR:
  3621. case TdsEnums.SQLBIGCHAR:
  3622. case TdsEnums.SQLVARCHAR:
  3623. case TdsEnums.SQLBIGVARCHAR:
  3624. case TdsEnums.SQLTEXT:
  3625. // If bigvarchar(max), we only read the first chunk here,
  3626. // expecting the caller to read the rest
  3627. if (encoding == null) {
  3628. // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
  3629. // 7.0 has no support for multiple code pages in data - single code page support only
  3630. encoding = _defaultEncoding;
  3631. }
  3632. string stringValue;
  3633. if (!stateObj.TryReadStringWithEncoding(length, encoding, isPlp, out stringValue)) {
  3634. return false;
  3635. }
  3636. value.SetToString(stringValue);
  3637. break;
  3638. case TdsEnums.SQLNCHAR:
  3639. case TdsEnums.SQLNVARCHAR:
  3640. case TdsEnums.SQLNTEXT:
  3641. {
  3642. String s = null;
  3643. if (isPlp) {
  3644. char[] cc = null;
  3645. if (!TryReadPlpUnicodeChars(ref cc, 0, length >> 1, stateObj, out length)) {
  3646. return false;
  3647. }
  3648. if (length > 0) {
  3649. s = new String(cc, 0, length);
  3650. }
  3651. else {
  3652. s = ADP.StrEmpty;
  3653. }
  3654. }
  3655. else {
  3656. if (!stateObj.TryReadString(length >> 1, out s)) {
  3657. return false;
  3658. }
  3659. }
  3660. value.SetToString(s);
  3661. break;
  3662. }
  3663. default:
  3664. Debug.Assert(false, "Unknown tds type for SqlString!" + type.ToString(CultureInfo.InvariantCulture));
  3665. break;
  3666. }
  3667. return true;
  3668. }
  3669. internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, TdsParserStateObject stateObj) {
  3670. bool isPlp = md.metaType.IsPlp;
  3671. byte tdsType = md.tdsType;
  3672. Debug.Assert(isPlp || !IsNull(md.metaType, (ulong)length), "null value should not get here!");
  3673. if (isPlp) {
  3674. // We must read the column value completely, no matter what length is passed in
  3675. length = Int32.MaxValue;
  3676. }
  3677. switch (tdsType) {
  3678. case TdsEnums.SQLDECIMALN:
  3679. case TdsEnums.SQLNUMERICN:
  3680. if (!TryReadSqlDecimal(value, length, md.precision, md.scale, stateObj)) {
  3681. return false;
  3682. }
  3683. break;
  3684. case TdsEnums.SQLUDT:
  3685. case TdsEnums.SQLBINARY:
  3686. case TdsEnums.SQLBIGBINARY:
  3687. case TdsEnums.SQLBIGVARBINARY:
  3688. case TdsEnums.SQLVARBINARY:
  3689. case TdsEnums.SQLIMAGE:
  3690. byte[] b = null;
  3691. // If varbinary(max), we only read the first chunk here, expecting the caller to read the rest
  3692. if (isPlp) {
  3693. // If we are given -1 for length, then we read the entire value,
  3694. // otherwise only the requested amount, usually first chunk.
  3695. int ignored;
  3696. if (!stateObj.TryReadPlpBytes(ref b, 0, length, out ignored)) {
  3697. return false;
  3698. }
  3699. }
  3700. else {
  3701. //Debug.Assert(length > 0 && length < (long)(Int32.MaxValue), "Bad length for column");
  3702. b = new byte[length];
  3703. if (!stateObj.TryReadByteArray(b, 0, length)) {
  3704. return false;
  3705. }
  3706. }
  3707. value.SqlBinary = new SqlBinary(b, true); // doesn't copy the byte array
  3708. break;
  3709. case TdsEnums.SQLCHAR:
  3710. case TdsEnums.SQLBIGCHAR:
  3711. case TdsEnums.SQLVARCHAR:
  3712. case TdsEnums.SQLBIGVARCHAR:
  3713. case TdsEnums.SQLTEXT:
  3714. case TdsEnums.SQLNCHAR:
  3715. case TdsEnums.SQLNVARCHAR:
  3716. case TdsEnums.SQLNTEXT:
  3717. if (!TryReadSqlStringValue(value, tdsType, length, md.encoding, isPlp, stateObj)) {
  3718. return false;
  3719. }
  3720. break;
  3721. case TdsEnums.SQLXMLTYPE:
  3722. // We store SqlCachedBuffer here, so that we can return either SqlBinary, SqlString or SqlXmlReader.
  3723. SqlCachedBuffer sqlBuf;
  3724. if (!SqlCachedBuffer.TryCreate(md, this, stateObj, out sqlBuf)) {
  3725. return false;
  3726. }
  3727. value.SqlCachedBuffer = sqlBuf;
  3728. break;
  3729. case TdsEnums.SQLDATE:
  3730. case TdsEnums.SQLTIME:
  3731. case TdsEnums.SQLDATETIME2:
  3732. case TdsEnums.SQLDATETIMEOFFSET:
  3733. if (!TryReadSqlDateTime(value, tdsType, length, md.scale, stateObj)) {
  3734. return false;
  3735. }
  3736. break;
  3737. default:
  3738. Debug.Assert(!isPlp, "ReadSqlValue calling ReadSqlValueInternal with plp data");
  3739. if (!TryReadSqlValueInternal(value, tdsType, length, stateObj)) {
  3740. return false;
  3741. }
  3742. break;
  3743. }
  3744. Debug.Assert((stateObj._longlen == 0) && (stateObj._longlenleft == 0), "ReadSqlValue did not read plp field completely, longlen =" + stateObj._longlen.ToString((IFormatProvider)null) + ",longlenleft=" + stateObj._longlenleft.ToString((IFormatProvider)null));
  3745. return true;
  3746. }
  3747. private bool TryReadSqlDateTime(SqlBuffer value, byte tdsType, int length, byte scale, TdsParserStateObject stateObj) {
  3748. byte[] datetimeBuffer = new byte[length];
  3749. if (!stateObj.TryReadByteArray(datetimeBuffer, 0, length)) {
  3750. return false;
  3751. }
  3752. switch (tdsType) {
  3753. case TdsEnums.SQLDATE:
  3754. Debug.Assert(length == 3, "invalid length for date type!");
  3755. value.SetToDate(datetimeBuffer);
  3756. break;
  3757. case TdsEnums.SQLTIME:
  3758. Debug.Assert(3 <= length && length <= 5, "invalid length for time type!");
  3759. value.SetToTime(datetimeBuffer, length, scale);
  3760. break;
  3761. case TdsEnums.SQLDATETIME2:
  3762. Debug.Assert(6 <= length && length <= 8, "invalid length for datetime2 type!");
  3763. value.SetToDateTime2(datetimeBuffer, length, scale);
  3764. break;
  3765. case TdsEnums.SQLDATETIMEOFFSET:
  3766. Debug.Assert(8 <= length && length <= 10, "invalid length for datetimeoffset type!");
  3767. value.SetToDateTimeOffset(datetimeBuffer, length, scale);
  3768. break;
  3769. default:
  3770. Debug.Assert(false, "ReadSqlDateTime is called with the wrong tdsType");
  3771. break;
  3772. }
  3773. return true;
  3774. }
  3775. internal bool TryReadSqlValueInternal(SqlBuffer value, byte tdsType, int length, TdsParserStateObject stateObj) {
  3776. switch (tdsType) {
  3777. case TdsEnums.SQLBIT:
  3778. case TdsEnums.SQLBITN:
  3779. Debug.Assert(length == 1, "invalid length for SqlBoolean type!");
  3780. byte byteValue;
  3781. if (!stateObj.TryReadByte(out byteValue)) {
  3782. return false;
  3783. }
  3784. value.Boolean = (byteValue != 0);
  3785. break;
  3786. case TdsEnums.SQLINTN:
  3787. if (length == 1) {
  3788. goto case TdsEnums.SQLINT1;
  3789. }
  3790. else if (length == 2) {
  3791. goto case TdsEnums.SQLINT2;
  3792. }
  3793. else if (length == 4) {
  3794. goto case TdsEnums.SQLINT4;
  3795. }
  3796. else {
  3797. goto case TdsEnums.SQLINT8;
  3798. }
  3799. case TdsEnums.SQLINT1:
  3800. Debug.Assert(length == 1, "invalid length for SqlByte type!");
  3801. if (!stateObj.TryReadByte(out byteValue)) {
  3802. return false;
  3803. }
  3804. value.Byte = byteValue;
  3805. break;
  3806. case TdsEnums.SQLINT2:
  3807. Debug.Assert(length == 2, "invalid length for SqlInt16 type!");
  3808. short shortValue;
  3809. if (!stateObj.TryReadInt16(out shortValue)) {
  3810. return false;
  3811. }
  3812. value.Int16 = shortValue;
  3813. break;
  3814. case TdsEnums.SQLINT4:
  3815. Debug.Assert(length == 4, "invalid length for SqlInt32 type!");
  3816. int intValue;
  3817. if (!stateObj.TryReadInt32(out intValue)) {
  3818. return false;
  3819. }
  3820. value.Int32 = intValue;
  3821. break;
  3822. case TdsEnums.SQLINT8:
  3823. Debug.Assert(length == 8, "invalid length for SqlInt64 type!");
  3824. long longValue;
  3825. if (!stateObj.TryReadInt64(out longValue)) {
  3826. return false;
  3827. }
  3828. value.Int64 = longValue;
  3829. break;
  3830. case TdsEnums.SQLFLTN:
  3831. if (length == 4) {
  3832. goto case TdsEnums.SQLFLT4;
  3833. }
  3834. else {
  3835. goto case TdsEnums.SQLFLT8;
  3836. }
  3837. case TdsEnums.SQLFLT4:
  3838. Debug.Assert(length == 4, "invalid length for SqlSingle type!");
  3839. float singleValue;
  3840. if (!stateObj.TryReadSingle(out singleValue)) {
  3841. return false;
  3842. }
  3843. value.Single = singleValue;
  3844. break;
  3845. case TdsEnums.SQLFLT8:
  3846. Debug.Assert(length == 8, "invalid length for SqlDouble type!");
  3847. double doubleValue;
  3848. if (!stateObj.TryReadDouble(out doubleValue)) {
  3849. return false;
  3850. }
  3851. value.Double = doubleValue;
  3852. break;
  3853. case TdsEnums.SQLMONEYN:
  3854. if (length == 4) {
  3855. goto case TdsEnums.SQLMONEY4;
  3856. }
  3857. else {
  3858. goto case TdsEnums.SQLMONEY;
  3859. }
  3860. case TdsEnums.SQLMONEY:
  3861. {
  3862. int mid;
  3863. uint lo;
  3864. if (!stateObj.TryReadInt32(out mid)) {
  3865. return false;
  3866. }
  3867. if (!stateObj.TryReadUInt32(out lo)) {
  3868. return false;
  3869. }
  3870. long l = (((long)mid) << 0x20) + ((long)lo);
  3871. value.SetToMoney(l);
  3872. break;
  3873. }
  3874. case TdsEnums.SQLMONEY4:
  3875. if (!stateObj.TryReadInt32(out intValue)) {
  3876. return false;
  3877. }
  3878. value.SetToMoney(intValue);
  3879. break;
  3880. case TdsEnums.SQLDATETIMN:
  3881. if (length == 4) {
  3882. goto case TdsEnums.SQLDATETIM4;
  3883. }
  3884. else {
  3885. goto case TdsEnums.SQLDATETIME;
  3886. }
  3887. case TdsEnums.SQLDATETIM4:
  3888. ushort daypartShort, timepartShort;
  3889. if (!stateObj.TryReadUInt16(out daypartShort)) {
  3890. return false;
  3891. }
  3892. if (!stateObj.TryReadUInt16(out timepartShort)) {
  3893. return false;
  3894. }
  3895. value.SetToDateTime(daypartShort, timepartShort * SqlDateTime.SQLTicksPerMinute);
  3896. break;
  3897. case TdsEnums.SQLDATETIME:
  3898. int daypart;
  3899. uint timepart;
  3900. if (!stateObj.TryReadInt32(out daypart)) {
  3901. return false;
  3902. }
  3903. if (!stateObj.TryReadUInt32(out timepart)) {
  3904. return false;
  3905. }
  3906. value.SetToDateTime(daypart, (int)timepart);
  3907. break;
  3908. case TdsEnums.SQLUNIQUEID:
  3909. {
  3910. Debug.Assert(length == 16, "invalid length for SqlGuid type!");
  3911. byte[] b = new byte[length];
  3912. if (!stateObj.TryReadByteArray(b, 0, length)) {
  3913. return false;
  3914. }
  3915. value.SqlGuid = new SqlGuid(b, true); // doesn't copy the byte array
  3916. break;
  3917. }
  3918. case TdsEnums.SQLBINARY:
  3919. case TdsEnums.SQLBIGBINARY:
  3920. case TdsEnums.SQLBIGVARBINARY:
  3921. case TdsEnums.SQLVARBINARY:
  3922. case TdsEnums.SQLIMAGE:
  3923. {
  3924. // Note: Better not come here with plp data!!
  3925. Debug.Assert(length <= TdsEnums.MAXSIZE);
  3926. byte[] b = new byte[length];
  3927. if (!stateObj.TryReadByteArray(b, 0, length)) {
  3928. return false;
  3929. }
  3930. value.SqlBinary = new SqlBinary(b, true); // doesn't copy the byte array
  3931. break;
  3932. }
  3933. case TdsEnums.SQLVARIANT:
  3934. if (!TryReadSqlVariant(value, length, stateObj)) {
  3935. return false;
  3936. }
  3937. break;
  3938. default:
  3939. Debug.Assert(false, "Unknown SqlType!" + tdsType.ToString(CultureInfo.InvariantCulture));
  3940. break;
  3941. } // switch
  3942. return true;
  3943. }
  3944. //
  3945. // Read in a SQLVariant
  3946. //
  3947. // SQLVariant looks like:
  3948. // struct
  3949. // {
  3950. // BYTE TypeTag
  3951. // BYTE cbPropBytes
  3952. // BYTE[] Properties
  3953. // BYTE[] DataVal
  3954. // }
  3955. internal bool TryReadSqlVariant(SqlBuffer value, int lenTotal, TdsParserStateObject stateObj) {
  3956. Debug.Assert(_isShiloh == true, "Shouldn't be dealing with sql_variaint in pre-SQL2000 server!");
  3957. // get the SQLVariant type
  3958. byte type;
  3959. if (!stateObj.TryReadByte(out type)) {
  3960. return false;
  3961. }
  3962. ushort lenMax = 0; // maximum lenData of value inside variant
  3963. // read cbPropBytes
  3964. byte cbPropsActual;
  3965. if (!stateObj.TryReadByte(out cbPropsActual)) {
  3966. return false;
  3967. }
  3968. MetaType mt = MetaType.GetSqlDataType(type, 0 /*no user datatype*/, 0 /* no lenData, non-nullable type */);
  3969. byte cbPropsExpected = mt.PropBytes;
  3970. int lenConsumed = TdsEnums.SQLVARIANT_SIZE + cbPropsActual; // type, count of propBytes, and actual propBytes
  3971. int lenData = lenTotal - lenConsumed; // length of actual data
  3972. // read known properties and skip unknown properties
  3973. Debug.Assert(cbPropsActual >= cbPropsExpected, "cbPropsActual is less that cbPropsExpected!");
  3974. //
  3975. // now read the value
  3976. //
  3977. switch (type) {
  3978. case TdsEnums.SQLBIT:
  3979. case TdsEnums.SQLINT1:
  3980. case TdsEnums.SQLINT2:
  3981. case TdsEnums.SQLINT4:
  3982. case TdsEnums.SQLINT8:
  3983. case TdsEnums.SQLFLT4:
  3984. case TdsEnums.SQLFLT8:
  3985. case TdsEnums.SQLMONEY:
  3986. case TdsEnums.SQLMONEY4:
  3987. case TdsEnums.SQLDATETIME:
  3988. case TdsEnums.SQLDATETIM4:
  3989. case TdsEnums.SQLUNIQUEID:
  3990. if (!TryReadSqlValueInternal(value, type, lenData, stateObj)) {
  3991. return false;
  3992. }
  3993. break;
  3994. case TdsEnums.SQLDECIMALN:
  3995. case TdsEnums.SQLNUMERICN:
  3996. {
  3997. Debug.Assert(cbPropsExpected == 2, "SqlVariant: invalid PropBytes for decimal/numeric type!");
  3998. byte precision;
  3999. if (!stateObj.TryReadByte(out precision)) {
  4000. return false;
  4001. }
  4002. byte scale;
  4003. if (!stateObj.TryReadByte(out scale)) {
  4004. return false;
  4005. }
  4006. // skip over unknown properties
  4007. if (cbPropsActual > cbPropsExpected) {
  4008. if (!stateObj.TrySkipBytes(cbPropsActual - cbPropsExpected)) {
  4009. return false;
  4010. }
  4011. }
  4012. if (!TryReadSqlDecimal(value, TdsEnums.MAX_NUMERIC_LEN, precision, scale, stateObj)) {
  4013. return false;
  4014. }
  4015. break;
  4016. }
  4017. case TdsEnums.SQLBIGBINARY:
  4018. case TdsEnums.SQLBIGVARBINARY:
  4019. //Debug.Assert(TdsEnums.VARNULL == lenData, "SqlVariant: data length for Binary indicates null?");
  4020. Debug.Assert(cbPropsExpected == 2, "SqlVariant: invalid PropBytes for binary type!");
  4021. if (!stateObj.TryReadUInt16(out lenMax)) {
  4022. return false;
  4023. }
  4024. Debug.Assert(lenMax != TdsEnums.SQL_USHORTVARMAXLEN, "bigvarbinary(max) in a sqlvariant");
  4025. // skip over unknown properties
  4026. if (cbPropsActual > cbPropsExpected) {
  4027. if (!stateObj.TrySkipBytes(cbPropsActual - cbPropsExpected)) {
  4028. return false;
  4029. }
  4030. }
  4031. goto case TdsEnums.SQLBIT;
  4032. case TdsEnums.SQLBIGCHAR:
  4033. case TdsEnums.SQLBIGVARCHAR:
  4034. case TdsEnums.SQLNCHAR:
  4035. case TdsEnums.SQLNVARCHAR:
  4036. {
  4037. Debug.Assert(cbPropsExpected == 7, "SqlVariant: invalid PropBytes for character type!");
  4038. SqlCollation collation;
  4039. if (!TryProcessCollation(stateObj, out collation)) {
  4040. return false;
  4041. }
  4042. if (!stateObj.TryReadUInt16(out lenMax)) {
  4043. return false;
  4044. }
  4045. Debug.Assert(lenMax != TdsEnums.SQL_USHORTVARMAXLEN, "bigvarchar(max) or nvarchar(max) in a sqlvariant");
  4046. // skip over unknown properties
  4047. if (cbPropsActual > cbPropsExpected) {
  4048. if (!stateObj.TrySkipBytes(cbPropsActual - cbPropsExpected)) {
  4049. return false;
  4050. }
  4051. }
  4052. Encoding encoding = Encoding.GetEncoding(GetCodePage(collation, stateObj));
  4053. if (!TryReadSqlStringValue(value, type, lenData, encoding, false, stateObj)) {
  4054. return false;
  4055. }
  4056. break;
  4057. }
  4058. case TdsEnums.SQLDATE:
  4059. if (!TryReadSqlDateTime(value, type, lenData, 0, stateObj)) {
  4060. return false;
  4061. }
  4062. break;
  4063. case TdsEnums.SQLTIME:
  4064. case TdsEnums.SQLDATETIME2:
  4065. case TdsEnums.SQLDATETIMEOFFSET:
  4066. {
  4067. Debug.Assert(cbPropsExpected == 1, "SqlVariant: invalid PropBytes for time/datetime2/datetimeoffset type!");
  4068. byte scale;
  4069. if (!stateObj.TryReadByte(out scale)) {
  4070. return false;
  4071. }
  4072. // skip over unknown properties
  4073. if (cbPropsActual > cbPropsExpected) {
  4074. if (!stateObj.TrySkipBytes(cbPropsActual - cbPropsExpected)) {
  4075. return false;
  4076. }
  4077. }
  4078. if (!TryReadSqlDateTime(value, type, lenData, scale, stateObj)) {
  4079. return false;
  4080. }
  4081. break;
  4082. }
  4083. default:
  4084. Debug.Assert(false, "Unknown tds type in SqlVariant!" + type.ToString(CultureInfo.InvariantCulture));
  4085. break;
  4086. } // switch
  4087. return true;
  4088. }
  4089. //
  4090. // Translates a com+ object -> SqlVariant
  4091. // when the type is ambiguous, we always convert to the bigger type
  4092. // note that we also write out the maxlen and actuallen members (4 bytes each)
  4093. // in addition to the SQLVariant structure
  4094. //
  4095. internal Task WriteSqlVariantValue(object value, int length, int offset, TdsParserStateObject stateObj, bool canAccumulate = true) {
  4096. Debug.Assert(_isShiloh == true, "Shouldn't be dealing with sql_variant in pre-SQL2000 server!");
  4097. // handle null values
  4098. if (ADP.IsNull(value)) {
  4099. WriteInt(TdsEnums.FIXEDNULL, stateObj); //maxlen
  4100. WriteInt(TdsEnums.FIXEDNULL, stateObj); //actuallen
  4101. return null;
  4102. }
  4103. MetaType mt = MetaType.GetMetaTypeFromValue(value);
  4104. // Special case data type correction for SqlMoney inside a SqlVariant.
  4105. if ((TdsEnums.SQLNUMERICN == mt.TDSType) && (8 == length)) {
  4106. // The caller will coerce all SqlTypes to native CLR types, which means SqlMoney will
  4107. // coerce to decimal/SQLNUMERICN (via SqlMoney.Value call). In the case where the original
  4108. // value was SqlMoney the caller will also pass in the metadata length for the SqlMoney type
  4109. // which is 8 bytes. To honor the intent of the caller here we coerce this special case
  4110. // input back to SqlMoney from decimal/SQLNUMERICN.
  4111. mt = MetaType.GetMetaTypeFromValue(new SqlMoney((decimal)value));
  4112. }
  4113. if (mt.IsAnsiType) {
  4114. length = GetEncodingCharLength((string)value, length, 0, _defaultEncoding);
  4115. }
  4116. // max and actual len are equal to
  4117. // SQLVARIANTSIZE {type (1 byte) + cbPropBytes (1 byte)} + cbPropBytes + length (actual length of data in bytes)
  4118. WriteInt(TdsEnums.SQLVARIANT_SIZE + mt.PropBytes + length, stateObj); // maxLen
  4119. WriteInt(TdsEnums.SQLVARIANT_SIZE + mt.PropBytes + length, stateObj); // actualLen
  4120. // write the SQLVariant header (type and cbPropBytes)
  4121. stateObj.WriteByte(mt.TDSType);
  4122. stateObj.WriteByte(mt.PropBytes);
  4123. // now write the actual PropBytes and data
  4124. switch (mt.TDSType) {
  4125. case TdsEnums.SQLFLT4:
  4126. WriteFloat((Single)value, stateObj);
  4127. break;
  4128. case TdsEnums.SQLFLT8:
  4129. WriteDouble((Double)value, stateObj);
  4130. break;
  4131. case TdsEnums.SQLINT8:
  4132. WriteLong((Int64)value, stateObj);
  4133. break;
  4134. case TdsEnums.SQLINT4:
  4135. WriteInt((Int32)value, stateObj);
  4136. break;
  4137. case TdsEnums.SQLINT2:
  4138. WriteShort((Int16)value, stateObj);
  4139. break;
  4140. case TdsEnums.SQLINT1:
  4141. stateObj.WriteByte((byte)value);
  4142. break;
  4143. case TdsEnums.SQLBIT:
  4144. if ((bool)value == true)
  4145. stateObj.WriteByte(1);
  4146. else
  4147. stateObj.WriteByte(0);
  4148. break;
  4149. case TdsEnums.SQLBIGVARBINARY:
  4150. {
  4151. byte[] b = (byte[])value;
  4152. WriteShort(length, stateObj); // propbytes: varlen
  4153. return stateObj.WriteByteArray(b, length, offset, canAccumulate);
  4154. }
  4155. case TdsEnums.SQLBIGVARCHAR:
  4156. {
  4157. string s = (string)value;
  4158. WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
  4159. stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId
  4160. WriteShort(length, stateObj); // propbyte: varlen
  4161. return WriteEncodingChar(s, _defaultEncoding, stateObj, canAccumulate);
  4162. }
  4163. case TdsEnums.SQLUNIQUEID:
  4164. {
  4165. System.Guid guid = (System.Guid)value;
  4166. byte[] b = guid.ToByteArray();
  4167. Debug.Assert((length == b.Length) && (length == 16), "Invalid length for guid type in com+ object");
  4168. stateObj.WriteByteArray(b, length, 0);
  4169. break;
  4170. }
  4171. case TdsEnums.SQLNVARCHAR:
  4172. {
  4173. string s = (string)value;
  4174. WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
  4175. stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId
  4176. WriteShort(length, stateObj); // propbyte: varlen
  4177. // string takes cchar, not cbyte so convert
  4178. length >>= 1;
  4179. return WriteString(s, length, offset, stateObj, canAccumulate);
  4180. }
  4181. case TdsEnums.SQLDATETIME:
  4182. {
  4183. TdsDateTime dt = MetaType.FromDateTime((DateTime)value, 8);
  4184. WriteInt(dt.days, stateObj);
  4185. WriteInt(dt.time, stateObj);
  4186. break;
  4187. }
  4188. case TdsEnums.SQLMONEY:
  4189. {
  4190. WriteCurrency((Decimal)value, 8, stateObj);
  4191. break;
  4192. }
  4193. case TdsEnums.SQLNUMERICN:
  4194. {
  4195. stateObj.WriteByte(mt.Precision); //propbytes: precision
  4196. stateObj.WriteByte((byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale
  4197. WriteDecimal((Decimal)value, stateObj);
  4198. break;
  4199. }
  4200. case TdsEnums.SQLTIME:
  4201. stateObj.WriteByte(mt.Scale); //propbytes: scale
  4202. WriteTime((TimeSpan)value, mt.Scale, length, stateObj);
  4203. break;
  4204. case TdsEnums.SQLDATETIMEOFFSET:
  4205. stateObj.WriteByte(mt.Scale); //propbytes: scale
  4206. WriteDateTimeOffset((DateTimeOffset)value, mt.Scale, length, stateObj);
  4207. break;
  4208. default:
  4209. Debug.Assert(false, "unknown tds type for sqlvariant!");
  4210. break;
  4211. } // switch
  4212. // return point for accumulated writes, note: non-accumulated writes returned from their case statements
  4213. return null;
  4214. }
  4215. // todo: since we now know the difference between SqlWriteVariantValue and SqlWriteRowDataVariant we should consider
  4216. // combining these tow methods.
  4217. //
  4218. // Translates a com+ object -> SqlVariant
  4219. // when the type is ambiguous, we always convert to the bigger type
  4220. // note that we also write out the maxlen and actuallen members (4 bytes each)
  4221. // in addition to the SQLVariant structure
  4222. //
  4223. // Devnote: DataRows are preceeded by Metadata. The Metadata includes the MaxLen value.
  4224. // Therefore the sql_variant value must not include the MaxLength. This is the major difference
  4225. // between this method and WriteSqlVariantValue above.
  4226. //
  4227. internal Task WriteSqlVariantDataRowValue(object value, TdsParserStateObject stateObj, bool canAccumulate = true) {
  4228. Debug.Assert(_isShiloh == true, "Shouldn't be dealing with sql_variant in pre-SQL2000 server!");
  4229. // handle null values
  4230. if ((null == value) || (DBNull.Value == value)) {
  4231. WriteInt(TdsEnums.FIXEDNULL, stateObj);
  4232. return null;
  4233. }
  4234. MetaType metatype = MetaType.GetMetaTypeFromValue(value);
  4235. int length = 0;
  4236. if (metatype.IsAnsiType) {
  4237. length = GetEncodingCharLength((string)value, length, 0, _defaultEncoding);
  4238. }
  4239. switch (metatype.TDSType) {
  4240. case TdsEnums.SQLFLT4:
  4241. WriteSqlVariantHeader(6, metatype.TDSType, metatype.PropBytes, stateObj);
  4242. WriteFloat((Single)value, stateObj);
  4243. break;
  4244. case TdsEnums.SQLFLT8:
  4245. WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj);
  4246. WriteDouble((Double)value, stateObj);
  4247. break;
  4248. case TdsEnums.SQLINT8:
  4249. WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj);
  4250. WriteLong((Int64)value, stateObj);
  4251. break;
  4252. case TdsEnums.SQLINT4:
  4253. WriteSqlVariantHeader(6, metatype.TDSType, metatype.PropBytes, stateObj);
  4254. WriteInt((Int32)value, stateObj);
  4255. break;
  4256. case TdsEnums.SQLINT2:
  4257. WriteSqlVariantHeader(4, metatype.TDSType, metatype.PropBytes, stateObj);
  4258. WriteShort((Int16)value, stateObj);
  4259. break;
  4260. case TdsEnums.SQLINT1:
  4261. WriteSqlVariantHeader(3, metatype.TDSType, metatype.PropBytes, stateObj);
  4262. stateObj.WriteByte((byte)value);
  4263. break;
  4264. case TdsEnums.SQLBIT:
  4265. WriteSqlVariantHeader(3, metatype.TDSType, metatype.PropBytes, stateObj);
  4266. if ((bool)value == true)
  4267. stateObj.WriteByte(1);
  4268. else
  4269. stateObj.WriteByte(0);
  4270. break;
  4271. case TdsEnums.SQLBIGVARBINARY:
  4272. {
  4273. byte[] b = (byte[])value;
  4274. length = b.Length;
  4275. WriteSqlVariantHeader(4 + length, metatype.TDSType, metatype.PropBytes, stateObj);
  4276. WriteShort(length, stateObj); // propbytes: varlen
  4277. return stateObj.WriteByteArray(b, length, 0, canAccumulate);
  4278. }
  4279. case TdsEnums.SQLBIGVARCHAR:
  4280. {
  4281. string s = (string)value;
  4282. length = s.Length;
  4283. WriteSqlVariantHeader(9 + length, metatype.TDSType, metatype.PropBytes, stateObj);
  4284. WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
  4285. stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId
  4286. WriteShort(length, stateObj);
  4287. return WriteEncodingChar(s, _defaultEncoding, stateObj, canAccumulate);
  4288. }
  4289. case TdsEnums.SQLUNIQUEID:
  4290. {
  4291. System.Guid guid = (System.Guid)value;
  4292. byte[] b = guid.ToByteArray();
  4293. length = b.Length;
  4294. Debug.Assert(length == 16, "Invalid length for guid type in com+ object");
  4295. WriteSqlVariantHeader(18, metatype.TDSType, metatype.PropBytes, stateObj);
  4296. stateObj.WriteByteArray(b, length, 0);
  4297. break;
  4298. }
  4299. case TdsEnums.SQLNVARCHAR:
  4300. {
  4301. string s = (string)value;
  4302. length = s.Length * 2;
  4303. WriteSqlVariantHeader(9 + length, metatype.TDSType, metatype.PropBytes, stateObj);
  4304. WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
  4305. stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId
  4306. WriteShort(length, stateObj); // propbyte: varlen
  4307. // string takes cchar, not cbyte so convert
  4308. length >>= 1;
  4309. return WriteString(s, length, 0, stateObj, canAccumulate);
  4310. }
  4311. case TdsEnums.SQLDATETIME:
  4312. {
  4313. TdsDateTime dt = MetaType.FromDateTime((DateTime)value, 8);
  4314. WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj);
  4315. WriteInt(dt.days, stateObj);
  4316. WriteInt(dt.time, stateObj);
  4317. break;
  4318. }
  4319. case TdsEnums.SQLMONEY:
  4320. {
  4321. WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj);
  4322. WriteCurrency((Decimal)value, 8, stateObj);
  4323. break;
  4324. }
  4325. case TdsEnums.SQLNUMERICN:
  4326. {
  4327. WriteSqlVariantHeader(21, metatype.TDSType, metatype.PropBytes, stateObj);
  4328. stateObj.WriteByte(metatype.Precision); //propbytes: precision
  4329. stateObj.WriteByte((byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale
  4330. WriteDecimal((Decimal)value, stateObj);
  4331. break;
  4332. }
  4333. case TdsEnums.SQLTIME:
  4334. WriteSqlVariantHeader(8, metatype.TDSType, metatype.PropBytes, stateObj);
  4335. stateObj.WriteByte(metatype.Scale); //propbytes: scale
  4336. WriteTime((TimeSpan)value, metatype.Scale, 5, stateObj);
  4337. break;
  4338. case TdsEnums.SQLDATETIMEOFFSET:
  4339. WriteSqlVariantHeader(13, metatype.TDSType, metatype.PropBytes, stateObj);
  4340. stateObj.WriteByte(metatype.Scale); //propbytes: scale
  4341. WriteDateTimeOffset((DateTimeOffset)value, metatype.Scale, 10, stateObj);
  4342. break;
  4343. default:
  4344. Debug.Assert(false, "unknown tds type for sqlvariant!");
  4345. break;
  4346. } // switch
  4347. // return point for accumualated writes, note: non-accumulated writes returned from their case statements
  4348. return null;
  4349. }
  4350. internal void WriteSqlVariantHeader(int length, byte tdstype, byte propbytes, TdsParserStateObject stateObj) {
  4351. WriteInt(length, stateObj);
  4352. stateObj.WriteByte(tdstype);
  4353. stateObj.WriteByte(propbytes);
  4354. }
  4355. internal void WriteSqlVariantDateTime2(DateTime value, TdsParserStateObject stateObj)
  4356. {
  4357. MSS.SmiMetaData dateTime2MetaData = MSS.SmiMetaData.DefaultDateTime2;
  4358. // NOTE: 3 bytes added here to support additional header information for variant - internal type, scale prop, scale
  4359. WriteSqlVariantHeader((int)(dateTime2MetaData.MaxLength + 3), TdsEnums.SQLDATETIME2, 1 /* one scale prop */, stateObj);
  4360. stateObj.WriteByte(dateTime2MetaData.Scale); //scale property
  4361. WriteDateTime2(value, dateTime2MetaData.Scale, (int)(dateTime2MetaData.MaxLength), stateObj);
  4362. }
  4363. internal void WriteSqlVariantDate(DateTime value, TdsParserStateObject stateObj)
  4364. {
  4365. MSS.SmiMetaData dateMetaData = MSS.SmiMetaData.DefaultDate;
  4366. // NOTE: 2 bytes added here to support additional header information for variant - internal type, scale prop (ignoring scale here)
  4367. WriteSqlVariantHeader((int)(dateMetaData.MaxLength + 2), TdsEnums.SQLDATE, 0 /* one scale prop */, stateObj);
  4368. WriteDate(value, stateObj);
  4369. }
  4370. private void WriteSqlMoney(SqlMoney value, int length, TdsParserStateObject stateObj) {
  4371. //
  4372. int[] bits = Decimal.GetBits(value.Value);
  4373. // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
  4374. bool isNeg = (0 != (bits[3] & unchecked((int)0x80000000)));
  4375. long l = ((long)(uint)bits[1]) << 0x20 | (uint)bits[0];
  4376. if (isNeg)
  4377. l = -l;
  4378. if (length == 4) {
  4379. Decimal decimalValue = value.Value;
  4380. // validate the value can be represented as a small money
  4381. if (decimalValue < TdsEnums.SQL_SMALL_MONEY_MIN || decimalValue > TdsEnums.SQL_SMALL_MONEY_MAX) {
  4382. throw SQL.MoneyOverflow(decimalValue.ToString(CultureInfo.InvariantCulture));
  4383. }
  4384. WriteInt((int)l, stateObj);
  4385. }
  4386. else {
  4387. WriteInt((int)(l >> 0x20), stateObj);
  4388. WriteInt((int)l, stateObj);
  4389. }
  4390. }
  4391. private void WriteCurrency(Decimal value, int length, TdsParserStateObject stateObj) {
  4392. SqlMoney m = new SqlMoney(value);
  4393. int[] bits = Decimal.GetBits(m.Value);
  4394. // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
  4395. bool isNeg = (0 != (bits[3] & unchecked((int)0x80000000)));
  4396. long l = ((long)(uint)bits[1]) << 0x20 | (uint)bits[0];
  4397. if (isNeg)
  4398. l = -l;
  4399. if (length == 4) {
  4400. // validate the value can be represented as a small money
  4401. if (value < TdsEnums.SQL_SMALL_MONEY_MIN || value > TdsEnums.SQL_SMALL_MONEY_MAX) {
  4402. throw SQL.MoneyOverflow(value.ToString(CultureInfo.InvariantCulture));
  4403. }
  4404. WriteInt((int)l, stateObj);
  4405. }
  4406. else {
  4407. WriteInt((int)(l >> 0x20), stateObj);
  4408. WriteInt((int)l, stateObj);
  4409. }
  4410. }
  4411. private void WriteDate(DateTime value, TdsParserStateObject stateObj) {
  4412. long days = value.Subtract(DateTime.MinValue).Days;
  4413. WritePartialLong(days, 3, stateObj);
  4414. }
  4415. private void WriteTime(TimeSpan value, byte scale, int length, TdsParserStateObject stateObj) {
  4416. if (0 > value.Ticks || value.Ticks >= TimeSpan.TicksPerDay) {
  4417. throw SQL.TimeOverflow(value.ToString());
  4418. }
  4419. long time = value.Ticks / TdsEnums.TICKS_FROM_SCALE[scale];
  4420. WritePartialLong(time, length, stateObj);
  4421. }
  4422. private void WriteDateTime2(DateTime value, byte scale, int length, TdsParserStateObject stateObj) {
  4423. long time = value.TimeOfDay.Ticks / TdsEnums.TICKS_FROM_SCALE[scale]; // DateTime.TimeOfDay always returns a valid TimeSpan for Time
  4424. WritePartialLong(time, length - 3, stateObj);
  4425. WriteDate(value, stateObj);
  4426. }
  4427. private void WriteDateTimeOffset(DateTimeOffset value, byte scale, int length, TdsParserStateObject stateObj) {
  4428. WriteDateTime2(value.UtcDateTime, scale, length - 2, stateObj);
  4429. Int16 offset = (Int16)value.Offset.TotalMinutes;
  4430. stateObj.WriteByte((byte)(offset & 0xff));
  4431. stateObj.WriteByte((byte)((offset >> 8) & 0xff));
  4432. }
  4433. private bool TryReadSqlDecimal(SqlBuffer value, int length, byte precision, byte scale, TdsParserStateObject stateObj) {
  4434. byte byteValue;
  4435. if (!stateObj.TryReadByte(out byteValue)) {
  4436. return false;
  4437. }
  4438. bool fPositive = (1 == byteValue);
  4439. length = checked((int)length-1);
  4440. int[] bits;
  4441. if (!TryReadDecimalBits(length, stateObj, out bits)) {
  4442. return false;
  4443. }
  4444. value.SetToDecimal(precision, scale, fPositive, bits);
  4445. return true;
  4446. }
  4447. // @devnote: length should be size of decimal without the sign
  4448. // @devnote: sign should have already been read off the wire
  4449. private bool TryReadDecimalBits(int length, TdsParserStateObject stateObj, out int[] bits) {
  4450. bits = stateObj._decimalBits; // used alloc'd array if we have one already
  4451. int i;
  4452. if (null == bits)
  4453. bits = new int[4];
  4454. else {
  4455. for (i = 0; i < bits.Length; i++)
  4456. bits[i] = 0;
  4457. }
  4458. Debug.Assert((length > 0) &&
  4459. (length <= TdsEnums.MAX_NUMERIC_LEN - 1) &&
  4460. (length % 4 == 0), "decimal should have 4, 8, 12, or 16 bytes of data");
  4461. int decLength = length >> 2;
  4462. for (i = 0; i < decLength; i++) {
  4463. // up to 16 bytes of data following the sign byte
  4464. if (!stateObj.TryReadInt32(out bits[i])) {
  4465. return false;
  4466. }
  4467. }
  4468. return true;
  4469. }
  4470. static internal SqlDecimal AdjustSqlDecimalScale(SqlDecimal d, int newScale) {
  4471. if (d.Scale != newScale) {
  4472. return SqlDecimal.AdjustScale(d, newScale - d.Scale, false /* Don't round, truncate. MDAC 69229 */);
  4473. }
  4474. return d;
  4475. }
  4476. static internal decimal AdjustDecimalScale(decimal value, int newScale) {
  4477. int oldScale = (Decimal.GetBits(value)[3] & 0x00ff0000) >> 0x10;
  4478. if (newScale != oldScale) {
  4479. SqlDecimal num = new SqlDecimal(value);
  4480. num = SqlDecimal.AdjustScale(num, newScale - oldScale, false /* Don't round, truncate. MDAC 69229 */);
  4481. return num.Value;
  4482. }
  4483. return value;
  4484. }
  4485. internal void WriteSqlDecimal(SqlDecimal d, TdsParserStateObject stateObj) {
  4486. // sign
  4487. if (d.IsPositive)
  4488. stateObj.WriteByte(1);
  4489. else
  4490. stateObj.WriteByte(0);
  4491. WriteUnsignedInt(d.m_data1, stateObj);
  4492. WriteUnsignedInt(d.m_data2, stateObj);
  4493. WriteUnsignedInt(d.m_data3, stateObj);
  4494. WriteUnsignedInt(d.m_data4, stateObj);
  4495. }
  4496. private void WriteDecimal(decimal value, TdsParserStateObject stateObj) {
  4497. stateObj._decimalBits = Decimal.GetBits(value);
  4498. Debug.Assert(null != stateObj._decimalBits, "decimalBits should be filled in at TdsExecuteRPC time");
  4499. /*
  4500. Returns a binary representation of a Decimal. The return value is an integer
  4501. array with four elements. Elements 0, 1, and 2 contain the low, middle, and
  4502. high 32 bits of the 96-bit integer part of the Decimal. Element 3 contains
  4503. the scale factor and sign of the Decimal: bits 0-15 (the lower word) are
  4504. unused; bits 16-23 contain a value between 0 and 28, indicating the power of
  4505. 10 to divide the 96-bit integer part by to produce the Decimal value; bits 24-
  4506. 30 are unused; and finally bit 31 indicates the sign of the Decimal value, 0
  4507. meaning positive and 1 meaning negative.
  4508. SQLDECIMAL/SQLNUMERIC has a byte stream of:
  4509. struct {
  4510. BYTE sign; // 1 if positive, 0 if negative
  4511. BYTE data[];
  4512. }
  4513. For TDS 7.0 and above, there are always 17 bytes of data
  4514. */
  4515. // write the sign (note that COM and SQL are opposite)
  4516. if (0x80000000 == (stateObj._decimalBits[3] & 0x80000000))
  4517. stateObj.WriteByte(0);
  4518. else
  4519. stateObj.WriteByte(1);
  4520. WriteInt(stateObj._decimalBits[0], stateObj);
  4521. WriteInt(stateObj._decimalBits[1], stateObj);
  4522. WriteInt(stateObj._decimalBits[2], stateObj);
  4523. WriteInt(0, stateObj);
  4524. }
  4525. private void WriteIdentifier(string s, TdsParserStateObject stateObj) {
  4526. if (null != s) {
  4527. stateObj.WriteByte(checked((byte)s.Length));
  4528. WriteString(s, stateObj);
  4529. }
  4530. else {
  4531. stateObj.WriteByte((byte)0);
  4532. }
  4533. }
  4534. private void WriteIdentifierWithShortLength(string s, TdsParserStateObject stateObj) {
  4535. if (null != s) {
  4536. WriteShort(checked((short)s.Length), stateObj);
  4537. WriteString(s, stateObj);
  4538. }
  4539. else {
  4540. WriteShort(0, stateObj);
  4541. }
  4542. }
  4543. private Task WriteString(string s, TdsParserStateObject stateObj, bool canAccumulate = true) {
  4544. return WriteString(s, s.Length, 0, stateObj, canAccumulate);
  4545. }
  4546. internal Task WriteCharArray(char[] carr, int length, int offset, TdsParserStateObject stateObj, bool canAccumulate = true) {
  4547. int cBytes = ADP.CharSize * length;
  4548. // Perf shortcut: If it fits, write directly to the outBuff
  4549. if(cBytes < (stateObj._outBuff.Length - stateObj._outBytesUsed)) {
  4550. CopyCharsToBytes(carr, offset, stateObj._outBuff, stateObj._outBytesUsed, length);
  4551. stateObj._outBytesUsed += cBytes;
  4552. return null;
  4553. }
  4554. else {
  4555. if (stateObj._bTmp == null || stateObj._bTmp.Length < cBytes) {
  4556. stateObj._bTmp = new byte[cBytes];
  4557. }
  4558. CopyCharsToBytes(carr, offset, stateObj._bTmp, 0, length);
  4559. return stateObj.WriteByteArray(stateObj._bTmp, cBytes, 0, canAccumulate);
  4560. }
  4561. }
  4562. internal Task WriteString(string s, int length, int offset, TdsParserStateObject stateObj, bool canAccumulate = true) {
  4563. int cBytes = ADP.CharSize * length;
  4564. // Perf shortcut: If it fits, write directly to the outBuff
  4565. if(cBytes < (stateObj._outBuff.Length - stateObj._outBytesUsed)) {
  4566. CopyStringToBytes(s, offset, stateObj._outBuff, stateObj._outBytesUsed, length);
  4567. stateObj._outBytesUsed += cBytes;
  4568. return null;
  4569. }
  4570. else {
  4571. if (stateObj._bTmp == null || stateObj._bTmp.Length < cBytes) {
  4572. stateObj._bTmp = new byte[cBytes];
  4573. }
  4574. CopyStringToBytes(s, offset, stateObj._bTmp, 0, length);
  4575. return stateObj.WriteByteArray(stateObj._bTmp, cBytes, 0, canAccumulate);
  4576. }
  4577. }
  4578. private unsafe static void CopyCharsToBytes(char[] source, int sourceOffset, byte[] dest, int destOffset, int charLength) {
  4579. // DEVNOTE: BE EXTREMELY CAREFULL in this method. Since it pins the buffers and copies the memory
  4580. // directly, it bypasses all of the CLR's usual array-bounds checking. For maintainability, the checks
  4581. // here should NOT be removed just because some other code will check them ahead of time.
  4582. if (charLength < 0) {
  4583. throw ADP.InvalidDataLength(charLength);
  4584. }
  4585. if (checked(sourceOffset + charLength) > source.Length || sourceOffset < 0) {
  4586. throw ADP.IndexOutOfRange(sourceOffset);
  4587. }
  4588. // charLength >= 0 & checked conversion implies byteLength >= 0
  4589. int byteLength = checked(charLength * ADP.CharSize);
  4590. if (checked(destOffset + byteLength) > dest.Length || destOffset < 0) {
  4591. throw ADP.IndexOutOfRange(destOffset);
  4592. }
  4593. fixed (char* sourcePtr = source) {
  4594. char* srcPtr = sourcePtr; // Can't increment the target of a Fixed statement
  4595. srcPtr += sourceOffset; // char* increments by sizeof(char)
  4596. fixed (byte* destinationPtr = dest) {
  4597. byte* destPtr = destinationPtr;
  4598. destPtr += destOffset;
  4599. NativeOledbWrapper.MemoryCopy((IntPtr)destPtr, (IntPtr)srcPtr, byteLength);
  4600. }
  4601. }
  4602. }
  4603. private unsafe static void CopyStringToBytes(string source, int sourceOffset, byte[] dest, int destOffset, int charLength) {
  4604. // DEVNOTE: BE EXTREMELY CAREFULL in this method. Since it pins the buffers and copies the memory
  4605. // directly, it bypasses all of the CLR's usual array-bounds checking. For maintainability, the checks
  4606. // here should NOT be removed just because some other code will check them ahead of time.
  4607. if (charLength < 0) {
  4608. throw ADP.InvalidDataLength(charLength);
  4609. }
  4610. if (checked(sourceOffset + charLength) > source.Length || sourceOffset < 0) {
  4611. throw ADP.IndexOutOfRange(sourceOffset);
  4612. }
  4613. // charLength >= 0 & checked conversion implies byteLength >= 0
  4614. int byteLength = checked(charLength * ADP.CharSize);
  4615. if (checked(destOffset + byteLength) > dest.Length || destOffset < 0) {
  4616. throw ADP.IndexOutOfRange(destOffset);
  4617. }
  4618. fixed (char* sourcePtr = source) {
  4619. char* srcPtr = sourcePtr; // Can't increment the target of a Fixed statement
  4620. srcPtr += sourceOffset; // char* increments by sizeof(char)
  4621. fixed (byte* destinationPtr = dest) {
  4622. byte* destPtr = destinationPtr;
  4623. destPtr += destOffset;
  4624. NativeOledbWrapper.MemoryCopy((IntPtr)destPtr, (IntPtr)srcPtr, byteLength);
  4625. }
  4626. }
  4627. }
  4628. private Task WriteEncodingChar(string s, Encoding encoding, TdsParserStateObject stateObj, bool canAccumulate = true) {
  4629. return WriteEncodingChar(s, s.Length, 0, encoding, stateObj, canAccumulate);
  4630. }
  4631. private Task WriteEncodingChar(string s, int numChars, int offset, Encoding encoding, TdsParserStateObject stateObj, bool canAccumulate = true) {
  4632. char[] charData;
  4633. byte[] byteData;
  4634. // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
  4635. // 7.0 has no support for multiple code pages in data - single code page support only
  4636. if (encoding == null)
  4637. encoding = _defaultEncoding;
  4638. charData = s.ToCharArray(offset, numChars);
  4639. // Optimization: if the entire string fits in the current buffer, then copy it directly
  4640. int bytesLeft = stateObj._outBuff.Length - stateObj._outBytesUsed;
  4641. if ((numChars <= bytesLeft) && (encoding.GetMaxByteCount(charData.Length) <= bytesLeft)) {
  4642. int bytesWritten = encoding.GetBytes(charData, 0, charData.Length, stateObj._outBuff, stateObj._outBytesUsed);
  4643. stateObj._outBytesUsed += bytesWritten;
  4644. return null;
  4645. }
  4646. else {
  4647. byteData = encoding.GetBytes(charData, 0, numChars);
  4648. Debug.Assert(byteData != null, "no data from encoding");
  4649. return stateObj.WriteByteArray(byteData, byteData.Length, 0, canAccumulate);
  4650. }
  4651. }
  4652. internal int GetEncodingCharLength(string value, int numChars, int charOffset, Encoding encoding) {
  4653. //
  4654. if (value == null || value == ADP.StrEmpty) {
  4655. return 0;
  4656. }
  4657. // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
  4658. // 7.0 has no support for multiple code pages in data - single code page support only
  4659. if (encoding == null) {
  4660. if (null == _defaultEncoding) {
  4661. ThrowUnsupportedCollationEncountered(null);
  4662. }
  4663. encoding = _defaultEncoding;
  4664. }
  4665. char[] charData = value.ToCharArray(charOffset, numChars);
  4666. return encoding.GetByteCount(charData, 0, numChars);
  4667. }
  4668. //
  4669. // Returns the data stream length of the data identified by tds type or SqlMetaData returns
  4670. // Returns either the total size or the size of the first chunk for partially length prefixed types.
  4671. //
  4672. internal bool TryGetDataLength(SqlMetaDataPriv colmeta, TdsParserStateObject stateObj, out ulong length) {
  4673. // Handle Yukon specific tokens
  4674. if (_isYukon && colmeta.metaType.IsPlp) {
  4675. Debug.Assert(colmeta.tdsType == TdsEnums.SQLXMLTYPE ||
  4676. colmeta.tdsType == TdsEnums.SQLBIGVARCHAR ||
  4677. colmeta.tdsType == TdsEnums.SQLBIGVARBINARY ||
  4678. colmeta.tdsType == TdsEnums.SQLNVARCHAR ||
  4679. // Large UDTs is WinFS-only
  4680. colmeta.tdsType == TdsEnums.SQLUDT,
  4681. "GetDataLength:Invalid streaming datatype");
  4682. return stateObj.TryReadPlpLength(true, out length);
  4683. }
  4684. else {
  4685. int intLength;
  4686. if (!TryGetTokenLength(colmeta.tdsType, stateObj, out intLength)) {
  4687. length = 0;
  4688. return false;
  4689. }
  4690. length = (ulong)intLength;
  4691. return true;
  4692. }
  4693. }
  4694. //
  4695. // returns the token length of the token or tds type
  4696. // Returns -1 for partially length prefixed (plp) types for metadata info.
  4697. // DOES NOT handle plp data streams correctly!!!
  4698. // Plp data streams length information should be obtained from GetDataLength
  4699. //
  4700. internal bool TryGetTokenLength(byte token, TdsParserStateObject stateObj, out int tokenLength) {
  4701. Debug.Assert(token != 0, "0 length token!");
  4702. switch (token) { // rules about SQLLenMask no longer apply to new tokens (as of 7.4)
  4703. case TdsEnums.SQLFEATUREEXTACK:
  4704. tokenLength = -1;
  4705. return true;
  4706. case TdsEnums.SQLSESSIONSTATE:
  4707. return stateObj.TryReadInt32(out tokenLength);
  4708. }
  4709. if (_isYukon) { // Handle Yukon specific exceptions
  4710. if (token == TdsEnums.SQLUDT) { // special case for UDTs
  4711. tokenLength = -1; // Should we return -1 or not call GetTokenLength for UDTs?
  4712. return true;
  4713. }
  4714. else if (token == TdsEnums.SQLRETURNVALUE) {
  4715. tokenLength = -1; // In Yukon, the RETURNVALUE token stream no longer has length
  4716. return true;
  4717. }
  4718. else if (token == TdsEnums.SQLXMLTYPE) {
  4719. ushort value;
  4720. if (!stateObj.TryReadUInt16(out value)) {
  4721. tokenLength = 0;
  4722. return false;
  4723. }
  4724. tokenLength = (int)value;
  4725. Debug.Assert(tokenLength == TdsEnums.SQL_USHORTVARMAXLEN, "Invalid token stream for xml datatype");
  4726. return true;
  4727. }
  4728. }
  4729. switch (token & TdsEnums.SQLLenMask) {
  4730. case TdsEnums.SQLFixedLen:
  4731. tokenLength = ((0x01 << ((token & 0x0c) >> 2))) & 0xff;
  4732. return true;
  4733. case TdsEnums.SQLZeroLen:
  4734. tokenLength = 0;
  4735. return true;
  4736. case TdsEnums.SQLVarLen:
  4737. case TdsEnums.SQLVarCnt:
  4738. if (0 != (token & 0x80)) {
  4739. ushort value;
  4740. if (!stateObj.TryReadUInt16(out value)) {
  4741. tokenLength = 0;
  4742. return false;
  4743. }
  4744. tokenLength = value;
  4745. return true;
  4746. }
  4747. else if (0 == (token & 0x0c)) {
  4748. //
  4749. if (!stateObj.TryReadInt32(out tokenLength)) {
  4750. return false;
  4751. }
  4752. return true;
  4753. }
  4754. else {
  4755. byte value;
  4756. if (!stateObj.TryReadByte(out value)) {
  4757. tokenLength = 0;
  4758. return false;
  4759. }
  4760. tokenLength = value;
  4761. return true;
  4762. }
  4763. default:
  4764. Debug.Assert(false, "Unknown token length!");
  4765. tokenLength = 0;
  4766. return true;
  4767. }
  4768. }
  4769. private void ProcessAttention(TdsParserStateObject stateObj) {
  4770. if (_state == TdsParserState.Closed || _state == TdsParserState.Broken){
  4771. return;
  4772. }
  4773. Debug.Assert(stateObj._attentionSent, "invalid attempt to ProcessAttention, attentionSent == false!");
  4774. // Attention processing scenarios:
  4775. // 1) EOM packet with header ST_AACK bit plus DONE with status DONE_ATTN
  4776. // 2) Packet without ST_AACK header bit but has DONE with status DONE_ATTN
  4777. // 3) Secondary timeout occurs while reading, break connection
  4778. // Since errors can occur and we need to cancel prior to throwing those errors, we
  4779. // cache away error state and then process TDS for the attention. We restore those
  4780. // errors after processing.
  4781. stateObj.StoreErrorAndWarningForAttention();
  4782. try {
  4783. // Call run loop to process looking for attention ack.
  4784. Run(RunBehavior.Attention, null, null, null, stateObj);
  4785. }
  4786. catch (Exception e) {
  4787. //
  4788. if (!ADP.IsCatchableExceptionType(e)) {
  4789. throw;
  4790. }
  4791. // If an exception occurs - break the connection.
  4792. // Attention error will not be thrown in this case by Run(), but other failures may.
  4793. ADP.TraceExceptionWithoutRethrow(e);
  4794. _state = TdsParserState.Broken;
  4795. _connHandler.BreakConnection();
  4796. throw;
  4797. }
  4798. stateObj.RestoreErrorAndWarningAfterAttention();
  4799. Debug.Assert(!stateObj._attentionSent, "Invalid attentionSent state at end of ProcessAttention");
  4800. }
  4801. static private int StateValueLength(int dataLen) {
  4802. return dataLen < 0xFF ? (dataLen + 1) : (dataLen + 5);
  4803. }
  4804. internal int WriteSessionRecoveryFeatureRequest(SessionData reconnectData, bool write /* if false just calculates the length */) {
  4805. int len = 1;
  4806. if (write) {
  4807. _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_SRECOVERY);
  4808. }
  4809. if (reconnectData == null) {
  4810. if (write) {
  4811. WriteInt(0, _physicalStateObj);
  4812. }
  4813. len += 4;
  4814. }
  4815. else {
  4816. Debug.Assert(reconnectData._unrecoverableStatesCount == 0, "Unrecoverable state count should be 0");
  4817. int initialLength = 0; // sizeof(DWORD) - length itself
  4818. initialLength += 1 + 2 * TdsParserStaticMethods.NullAwareStringLength(reconnectData._initialDatabase);
  4819. initialLength += 1 + 2 * TdsParserStaticMethods.NullAwareStringLength(reconnectData._initialLanguage);
  4820. initialLength += (reconnectData._initialCollation == null) ? 1 : 6;
  4821. for (int i = 0; i < SessionData._maxNumberOfSessionStates; i++) {
  4822. if (reconnectData._initialState[i] != null) {
  4823. initialLength += 1 /* StateId*/ + StateValueLength(reconnectData._initialState[i].Length);
  4824. }
  4825. }
  4826. int currentLength = 0; // sizeof(DWORD) - length itself
  4827. currentLength += 1 + 2 * (reconnectData._initialDatabase == reconnectData._database ? 0 : TdsParserStaticMethods.NullAwareStringLength(reconnectData._database));
  4828. currentLength += 1 + 2 * (reconnectData._initialLanguage == reconnectData._language ? 0 : TdsParserStaticMethods.NullAwareStringLength(reconnectData._language));
  4829. currentLength += (reconnectData._collation != null && !SqlCollation.AreSame(reconnectData._collation, reconnectData._initialCollation)) ? 6 : 1;
  4830. bool[] writeState = new bool[SessionData._maxNumberOfSessionStates];
  4831. for (int i = 0; i < SessionData._maxNumberOfSessionStates; i++) {
  4832. if (reconnectData._delta[i] != null) {
  4833. Debug.Assert(reconnectData._delta[i]._recoverable, "State should be recoverable");
  4834. writeState[i] = true;
  4835. if (reconnectData._initialState[i] != null && reconnectData._initialState[i].Length == reconnectData._delta[i]._dataLength) {
  4836. writeState[i] = false;
  4837. for (int j = 0; j < reconnectData._delta[i]._dataLength; j++) {
  4838. if (reconnectData._initialState[i][j] != reconnectData._delta[i]._data[j]) {
  4839. writeState[i] = true;
  4840. break;
  4841. }
  4842. }
  4843. }
  4844. if (writeState[i]) {
  4845. currentLength += 1 /* StateId*/ + StateValueLength(reconnectData._delta[i]._dataLength);
  4846. }
  4847. }
  4848. }
  4849. if (write) {
  4850. WriteInt(8 + initialLength + currentLength, _physicalStateObj); // length of data w/o total length (initil+current+2*sizeof(DWORD))
  4851. WriteInt(initialLength, _physicalStateObj);
  4852. WriteIdentifier(reconnectData._initialDatabase, _physicalStateObj);
  4853. WriteCollation(reconnectData._initialCollation, _physicalStateObj);
  4854. WriteIdentifier(reconnectData._initialLanguage, _physicalStateObj);
  4855. for (int i = 0; i < SessionData._maxNumberOfSessionStates; i++) {
  4856. if (reconnectData._initialState[i] != null) {
  4857. _physicalStateObj.WriteByte((byte)i);
  4858. if (reconnectData._initialState[i].Length < 0xFF) {
  4859. _physicalStateObj.WriteByte((byte)reconnectData._initialState[i].Length);
  4860. }
  4861. else {
  4862. _physicalStateObj.WriteByte(0xFF);
  4863. WriteInt(reconnectData._initialState[i].Length, _physicalStateObj);
  4864. }
  4865. _physicalStateObj.WriteByteArray(reconnectData._initialState[i], reconnectData._initialState[i].Length, 0);
  4866. }
  4867. }
  4868. WriteInt(currentLength, _physicalStateObj);
  4869. WriteIdentifier(reconnectData._database != reconnectData._initialDatabase ? reconnectData._database : null, _physicalStateObj);
  4870. WriteCollation(SqlCollation.AreSame(reconnectData._initialCollation, reconnectData._collation) ? null : reconnectData._collation, _physicalStateObj);
  4871. WriteIdentifier(reconnectData._language != reconnectData._initialLanguage ? reconnectData._language : null, _physicalStateObj);
  4872. for (int i = 0; i < SessionData._maxNumberOfSessionStates; i++) {
  4873. if (writeState[i]) {
  4874. _physicalStateObj.WriteByte((byte)i);
  4875. if (reconnectData._delta[i]._dataLength < 0xFF) {
  4876. _physicalStateObj.WriteByte((byte)reconnectData._delta[i]._dataLength);
  4877. }
  4878. else {
  4879. _physicalStateObj.WriteByte(0xFF);
  4880. WriteInt(reconnectData._delta[i]._dataLength, _physicalStateObj);
  4881. }
  4882. _physicalStateObj.WriteByteArray(reconnectData._delta[i]._data, reconnectData._delta[i]._dataLength, 0);
  4883. }
  4884. }
  4885. }
  4886. len += initialLength + currentLength + 12 /* length fields (initial, current, total) */;
  4887. }
  4888. return len;
  4889. }
  4890. internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures, SessionData recoverySessionData) {
  4891. _physicalStateObj.SetTimeoutSeconds(rec.timeout);
  4892. Debug.Assert(recoverySessionData == null || (requestedFeatures | TdsEnums.FeatureExtension.SessionRecovery) != 0, "Recovery session data without session recovery feature request");
  4893. Debug.Assert(TdsEnums.MAXLEN_HOSTNAME>=rec.hostName.Length, "_workstationId.Length exceeds the max length for this value");
  4894. Debug.Assert(rec.userName == null || (rec.userName != null && TdsEnums.MAXLEN_USERNAME >= rec.userName.Length), "_userID.Length exceeds the max length for this value");
  4895. Debug.Assert(rec.credential == null || (rec.credential != null && TdsEnums.MAXLEN_USERNAME >= rec.credential.UserId.Length), "_credential.UserId.Length exceeds the max length for this value");
  4896. Debug.Assert(rec.password == null || (rec.password != null && TdsEnums.MAXLEN_PASSWORD>=rec.password.Length), "_password.Length exceeds the max length for this value");
  4897. Debug.Assert(rec.credential == null || (rec.credential != null && TdsEnums.MAXLEN_PASSWORD >= rec.credential.Password.Length), "_credential.Password.Length exceeds the max length for this value");
  4898. Debug.Assert(rec.credential != null || rec.userName != null || rec.password != null, "cannot mix the new secure password system and the connection string based password");
  4899. Debug.Assert(rec.newSecurePassword != null || rec.newPassword != null, "cannot have both new secure change password and string based change password");
  4900. Debug.Assert(TdsEnums.MAXLEN_APPNAME>=rec.applicationName.Length, "_applicationName.Length exceeds the max length for this value");
  4901. Debug.Assert(TdsEnums.MAXLEN_SERVERNAME>=rec.serverName.Length, "_dataSource.Length exceeds the max length for this value");
  4902. Debug.Assert(TdsEnums.MAXLEN_LANGUAGE>=rec.language.Length, "_currentLanguage .Length exceeds the max length for this value");
  4903. Debug.Assert(TdsEnums.MAXLEN_DATABASE>=rec.database.Length, "_initialCatalog.Length exceeds the max length for this value");
  4904. Debug.Assert(TdsEnums.MAXLEN_ATTACHDBFILE>=rec.attachDBFilename.Length, "_attachDBFileName.Length exceeds the max length for this value");
  4905. Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point.");
  4906. _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.LoginBegin);
  4907. _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth);
  4908. // get the password up front to use in sspi logic below
  4909. byte[] encryptedPassword = null;
  4910. byte[] encryptedChangePassword = null;
  4911. int encryptedPasswordLengthInBytes;
  4912. int encryptedChangePasswordLengthInBytes;
  4913. bool useFeatureExt = (requestedFeatures != TdsEnums.FeatureExtension.None);
  4914. string userName;
  4915. if (rec.credential != null) {
  4916. userName = rec.credential.UserId;
  4917. encryptedPasswordLengthInBytes = rec.credential.Password.Length * 2;
  4918. }
  4919. else {
  4920. userName = rec.userName;
  4921. encryptedPassword = TdsParserStaticMethods.EncryptPassword(rec.password);
  4922. encryptedPasswordLengthInBytes = encryptedPassword.Length; // password in clear text is already encrypted and its length is in byte
  4923. }
  4924. if (rec.newSecurePassword != null) {
  4925. encryptedChangePasswordLengthInBytes = rec.newSecurePassword.Length * 2;
  4926. }
  4927. else {
  4928. encryptedChangePassword = TdsParserStaticMethods.EncryptPassword(rec.newPassword);
  4929. encryptedChangePasswordLengthInBytes = encryptedChangePassword.Length;
  4930. }
  4931. // set the message type
  4932. _physicalStateObj._outputMessageType = TdsEnums.MT_LOGIN7;
  4933. // length in bytes
  4934. int length = TdsEnums.YUKON_LOG_REC_FIXED_LEN;
  4935. string clientInterfaceName = TdsEnums.SQL_PROVIDER_NAME;
  4936. Debug.Assert(TdsEnums.MAXLEN_CLIENTINTERFACE >= clientInterfaceName.Length, "cchCltIntName can specify at most 128 unicode characters. See Tds spec");
  4937. // add up variable-len portions (multiply by 2 for byte len of char strings)
  4938. //
  4939. checked {
  4940. length += (rec.hostName.Length + rec.applicationName.Length +
  4941. rec.serverName.Length + clientInterfaceName.Length +
  4942. rec.language.Length + rec.database.Length +
  4943. rec.attachDBFilename.Length) * 2;
  4944. if (useFeatureExt) {
  4945. length += 4;
  4946. }
  4947. }
  4948. // allocate memory for SSPI variables
  4949. byte[] outSSPIBuff = null;
  4950. UInt32 outSSPILength = 0;
  4951. // only add lengths of password and username if not using SSPI
  4952. if (!rec.useSSPI) {
  4953. checked {
  4954. length += (userName.Length * 2) + encryptedPasswordLengthInBytes
  4955. + encryptedChangePasswordLengthInBytes;
  4956. }
  4957. }
  4958. else {
  4959. if (rec.useSSPI) {
  4960. // now allocate proper length of buffer, and set length
  4961. outSSPIBuff = new byte[s_maxSSPILength];
  4962. outSSPILength = s_maxSSPILength;
  4963. // Call helper function for SSPI data and actual length.
  4964. // Since we don't have SSPI data from the server, send null for the
  4965. // byte[] buffer and 0 for the int length.
  4966. Debug.Assert(SniContext.Snix_Login==_physicalStateObj.SniContext, String.Format((IFormatProvider)null, "Unexpected SniContext. Expecting Snix_Login, actual value is '{0}'", _physicalStateObj.SniContext));
  4967. _physicalStateObj.SniContext = SniContext.Snix_LoginSspi;
  4968. SSPIData(null, 0, outSSPIBuff, ref outSSPILength);
  4969. if (outSSPILength > Int32.MaxValue) {
  4970. throw SQL.InvalidSSPIPacketSize(); // SqlBu 332503
  4971. }
  4972. _physicalStateObj.SniContext=SniContext.Snix_Login;
  4973. checked {
  4974. length += (Int32)outSSPILength;
  4975. }
  4976. }
  4977. }
  4978. int feOffset = length;
  4979. if (useFeatureExt) {
  4980. if ((requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) !=0) {
  4981. length += WriteSessionRecoveryFeatureRequest(recoverySessionData, false);
  4982. };
  4983. length++; // for terminator
  4984. }
  4985. try {
  4986. WriteInt(length, _physicalStateObj);
  4987. if (recoverySessionData == null) {
  4988. WriteInt((TdsEnums.DENALI_MAJOR << 24) | (TdsEnums.DENALI_INCREMENT << 16) | TdsEnums.DENALI_MINOR, _physicalStateObj);
  4989. }
  4990. else {
  4991. WriteUnsignedInt(recoverySessionData._tdsVersion, _physicalStateObj);
  4992. }
  4993. WriteInt(rec.packetSize, _physicalStateObj);
  4994. WriteInt(TdsEnums.CLIENT_PROG_VER, _physicalStateObj);
  4995. WriteInt(TdsParserStaticMethods.GetCurrentProcessIdForTdsLoginOnly(), _physicalStateObj); //MDAC 84718
  4996. WriteInt(0, _physicalStateObj); // connectionID is unused
  4997. // Log7Flags (DWORD)
  4998. int log7Flags = 0;
  4999. /*
  5000. Current snapshot from TDS spec with the offsets added:
  5001. 0) fByteOrder:1, // byte order of numeric data types on client
  5002. 1) fCharSet:1, // character set on client
  5003. 2) fFloat:2, // Type of floating point on client
  5004. 4) fDumpLoad:1, // Dump/Load and BCP enable
  5005. 5) fUseDb:1, // USE notification
  5006. 6) fDatabase:1, // Initial database fatal flag
  5007. 7) fSetLang:1, // SET LANGUAGE notification
  5008. 8) fLanguage:1, // Initial language fatal flag
  5009. 9) fODBC:1, // Set if client is ODBC driver
  5010. 10) fTranBoundary:1, // Transaction boundary notification
  5011. 11) fDelegatedSec:1, // Security with delegation is available
  5012. 12) fUserType:3, // Type of user
  5013. 15) fIntegratedSecurity:1, // Set if client is using integrated security
  5014. 16) fSQLType:4, // Type of SQL sent from client
  5015. 20) fOLEDB:1, // Set if client is OLEDB driver
  5016. 21) fSpare1:3, // first bit used for read-only intent, rest unused
  5017. 24) fResetPassword:1, // set if client wants to reset password
  5018. 25) fNoNBCAndSparse:1, // set if client does not support NBC and Sparse column
  5019. 26) fUserInstance:1, // This connection wants to connect to a SQL "user instance"
  5020. 27) fUnknownCollationHandling:1, // This connection can handle unknown collation correctly.
  5021. 28) fExtension:1 // Extensions are used
  5022. 32 - total
  5023. */
  5024. // first byte
  5025. log7Flags |= TdsEnums.USE_DB_ON << 5;
  5026. log7Flags |= TdsEnums.INIT_DB_FATAL << 6;
  5027. log7Flags |= TdsEnums.SET_LANG_ON << 7;
  5028. // second byte
  5029. log7Flags |= TdsEnums.INIT_LANG_FATAL << 8;
  5030. log7Flags |= TdsEnums.ODBC_ON << 9;
  5031. if (rec.useReplication) {
  5032. log7Flags |= TdsEnums.REPL_ON << 12;
  5033. }
  5034. if (rec.useSSPI) {
  5035. log7Flags |= TdsEnums.SSPI_ON << 15;
  5036. }
  5037. // third byte
  5038. if (rec.readOnlyIntent) {
  5039. log7Flags |= TdsEnums.READONLY_INTENT_ON << 21; // read-only intent flag is a first bit of fSpare1
  5040. }
  5041. // 4th one
  5042. if (!ADP.IsEmpty(rec.newPassword) || (rec.newSecurePassword != null && rec.newSecurePassword.Length != 0)) {
  5043. log7Flags |= 1 << 24;
  5044. }
  5045. if (rec.userInstance) {
  5046. log7Flags |= 1 << 26;
  5047. }
  5048. if (useFeatureExt) {
  5049. log7Flags |= 1 << 28;
  5050. }
  5051. WriteInt(log7Flags, _physicalStateObj);
  5052. if (Bid.AdvancedOn) {
  5053. Bid.Trace("<sc.TdsParser.TdsLogin|ADV> %d#, TDS Login7 flags = %d:\n", ObjectID, log7Flags);
  5054. }
  5055. WriteInt(0, _physicalStateObj); // ClientTimeZone is not used
  5056. WriteInt(0, _physicalStateObj); // LCID is unused by server
  5057. // Start writing offset and length of variable length portions
  5058. int offset = TdsEnums.YUKON_LOG_REC_FIXED_LEN;
  5059. // write offset/length pairs
  5060. // note that you must always set ibHostName since it indicaters the beginning of the variable length section of the login record
  5061. WriteShort(offset, _physicalStateObj); // host name offset
  5062. WriteShort(rec.hostName.Length, _physicalStateObj);
  5063. offset += rec.hostName.Length * 2;
  5064. // Only send user/password over if not fSSPI... If both user/password and SSPI are in login
  5065. // rec, only SSPI is used. Confirmed same bahavior as in luxor.
  5066. if (rec.useSSPI == false) {
  5067. WriteShort(offset, _physicalStateObj); // userName offset
  5068. WriteShort(userName.Length, _physicalStateObj);
  5069. offset += userName.Length * 2;
  5070. // the encrypted password is a byte array - so length computations different than strings
  5071. WriteShort(offset, _physicalStateObj); // password offset
  5072. WriteShort(encryptedPasswordLengthInBytes / 2, _physicalStateObj);
  5073. offset += encryptedPasswordLengthInBytes;
  5074. }
  5075. else {
  5076. // case where user/password data is not used, send over zeros
  5077. WriteShort(0, _physicalStateObj); // userName offset
  5078. WriteShort(0, _physicalStateObj);
  5079. WriteShort(0, _physicalStateObj); // password offset
  5080. WriteShort(0, _physicalStateObj);
  5081. }
  5082. WriteShort(offset, _physicalStateObj); // app name offset
  5083. WriteShort(rec.applicationName.Length, _physicalStateObj);
  5084. offset += rec.applicationName.Length * 2;
  5085. WriteShort(offset, _physicalStateObj); // server name offset
  5086. WriteShort(rec.serverName.Length, _physicalStateObj);
  5087. offset += rec.serverName.Length * 2;
  5088. WriteShort(offset, _physicalStateObj);
  5089. if (useFeatureExt) {
  5090. WriteShort(4, _physicalStateObj); // length of ibFeatgureExtLong (which is a DWORD)
  5091. offset += 4;
  5092. }
  5093. else {
  5094. WriteShort(0, _physicalStateObj); // ununsed (was remote password ?)
  5095. }
  5096. WriteShort(offset, _physicalStateObj); // client interface name offset
  5097. WriteShort(clientInterfaceName.Length, _physicalStateObj);
  5098. offset += clientInterfaceName.Length * 2;
  5099. WriteShort(offset, _physicalStateObj); // language name offset
  5100. WriteShort(rec.language.Length, _physicalStateObj);
  5101. offset += rec.language.Length * 2;
  5102. WriteShort(offset, _physicalStateObj); // database name offset
  5103. WriteShort(rec.database.Length, _physicalStateObj);
  5104. offset += rec.database.Length * 2;
  5105. //
  5106. if (null == s_nicAddress)
  5107. s_nicAddress = TdsParserStaticMethods.GetNetworkPhysicalAddressForTdsLoginOnly();
  5108. _physicalStateObj.WriteByteArray(s_nicAddress, s_nicAddress.Length, 0);
  5109. WriteShort(offset, _physicalStateObj); // ibSSPI offset
  5110. if (rec.useSSPI) {
  5111. WriteShort((int)outSSPILength, _physicalStateObj);
  5112. offset += (int)outSSPILength;
  5113. }
  5114. else {
  5115. WriteShort(0, _physicalStateObj);
  5116. }
  5117. WriteShort(offset, _physicalStateObj); // DB filename offset
  5118. WriteShort(rec.attachDBFilename.Length, _physicalStateObj);
  5119. offset += rec.attachDBFilename.Length * 2;
  5120. WriteShort(offset, _physicalStateObj); // reset password offset
  5121. WriteShort(encryptedChangePasswordLengthInBytes / 2, _physicalStateObj);
  5122. WriteInt(0, _physicalStateObj); // reserved for chSSPI
  5123. // write variable length portion
  5124. WriteString(rec.hostName, _physicalStateObj);
  5125. // if we are using SSPI, do not send over username/password, since we will use SSPI instead
  5126. // same behavior as Luxor
  5127. if (!rec.useSSPI) {
  5128. WriteString(userName, _physicalStateObj);
  5129. // Cache offset in packet for tracing.
  5130. _physicalStateObj._tracePasswordOffset = _physicalStateObj._outBytesUsed;
  5131. _physicalStateObj._tracePasswordLength = encryptedPasswordLengthInBytes;
  5132. if (rec.credential != null) {
  5133. _physicalStateObj.WriteSecureString(rec.credential.Password);
  5134. }
  5135. else {
  5136. _physicalStateObj.WriteByteArray(encryptedPassword, encryptedPasswordLengthInBytes, 0);
  5137. }
  5138. }
  5139. WriteString(rec.applicationName, _physicalStateObj);
  5140. WriteString(rec.serverName, _physicalStateObj);
  5141. // write ibFeatureExtLong
  5142. if (useFeatureExt) {
  5143. WriteInt(feOffset, _physicalStateObj);
  5144. }
  5145. WriteString(clientInterfaceName, _physicalStateObj);
  5146. WriteString(rec.language, _physicalStateObj);
  5147. WriteString(rec.database, _physicalStateObj);
  5148. // send over SSPI data if we are using SSPI
  5149. if (rec.useSSPI)
  5150. _physicalStateObj.WriteByteArray(outSSPIBuff, (int)outSSPILength, 0);
  5151. WriteString(rec.attachDBFilename, _physicalStateObj);
  5152. if (!rec.useSSPI) {
  5153. // Cache offset in packet for tracing.
  5154. _physicalStateObj._traceChangePasswordOffset = _physicalStateObj._outBytesUsed;
  5155. _physicalStateObj._traceChangePasswordLength = encryptedChangePasswordLengthInBytes;
  5156. if (rec.newSecurePassword != null) {
  5157. _physicalStateObj.WriteSecureString(rec.newSecurePassword);
  5158. }
  5159. else {
  5160. _physicalStateObj.WriteByteArray(encryptedChangePassword, encryptedChangePasswordLengthInBytes, 0);
  5161. }
  5162. }
  5163. if (useFeatureExt) {
  5164. if ((requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) != 0) {
  5165. length += WriteSessionRecoveryFeatureRequest(recoverySessionData, true);
  5166. };
  5167. _physicalStateObj.WriteByte(0xFF); // terminator
  5168. }
  5169. } // try
  5170. catch (Exception e) {
  5171. //
  5172. if (ADP.IsCatchableExceptionType(e)) {
  5173. // be sure to wipe out our buffer if we started sending stuff
  5174. _physicalStateObj._outputPacketNumber = 1; // end of message - reset to 1 - per ramas
  5175. _physicalStateObj.ResetBuffer();
  5176. }
  5177. throw;
  5178. }
  5179. _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH);
  5180. _physicalStateObj.ResetSecurePasswordsInfomation(); // Password information is needed only from Login process; done with writing login packet and should clear information
  5181. _physicalStateObj._pendingData = true;
  5182. _physicalStateObj._messageStatus = 0;
  5183. }// tdsLogin
  5184. private void SSPIData(byte[] receivedBuff, UInt32 receivedLength, byte[] sendBuff, ref UInt32 sendLength) {
  5185. SNISSPIData(receivedBuff, receivedLength, sendBuff, ref sendLength);
  5186. }
  5187. private void SNISSPIData(byte[] receivedBuff, UInt32 receivedLength, byte[] sendBuff, ref UInt32 sendLength)
  5188. {
  5189. if (receivedBuff == null)
  5190. {
  5191. // we do not have SSPI data coming from server, so send over 0's for pointer and length
  5192. receivedLength = 0;
  5193. }
  5194. // we need to respond to the server's message with SSPI data
  5195. if(0 != SNINativeMethodWrapper.SNISecGenClientContext(_physicalStateObj.Handle, receivedBuff, receivedLength, sendBuff, ref sendLength, _sniSpnBuffer))
  5196. {
  5197. SSPIError(SQLMessage.SSPIGenerateError(), TdsEnums.GEN_CLIENT_CONTEXT);
  5198. }
  5199. }
  5200. private void ProcessSSPI(int receivedLength) {
  5201. SniContext outerContext=_physicalStateObj.SniContext;
  5202. _physicalStateObj.SniContext = SniContext.Snix_ProcessSspi;
  5203. // allocate received buffer based on length from SSPI message
  5204. byte[] receivedBuff = new byte[receivedLength];
  5205. // read SSPI data received from server
  5206. Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
  5207. bool result = _physicalStateObj.TryReadByteArray(receivedBuff, 0, receivedLength);
  5208. if (!result) { throw SQL.SynchronousCallMayNotPend(); }
  5209. // allocate send buffer and initialize length
  5210. byte[] sendBuff = new byte[s_maxSSPILength];
  5211. UInt32 sendLength = s_maxSSPILength;
  5212. // make call for SSPI data
  5213. SSPIData(receivedBuff, (UInt32)receivedLength, sendBuff, ref sendLength);
  5214. // DO NOT SEND LENGTH - TDS DOC INCORRECT! JUST SEND SSPI DATA!
  5215. _physicalStateObj.WriteByteArray(sendBuff, (int)sendLength, 0);
  5216. // set message type so server knows its a SSPI response
  5217. _physicalStateObj._outputMessageType = TdsEnums.MT_SSPI;
  5218. // send to server
  5219. _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH);
  5220. _physicalStateObj.SniContext=outerContext;
  5221. }
  5222. private void SSPIError(string error, string procedure) {
  5223. Debug.Assert(!ADP.IsEmpty(procedure), "TdsParser.SSPIError called with an empty or null procedure string");
  5224. Debug.Assert(!ADP.IsEmpty(error), "TdsParser.SSPIError called with an empty or null error string");
  5225. _physicalStateObj.AddError(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, _server, error, procedure, 0));
  5226. ThrowExceptionAndWarning(_physicalStateObj);
  5227. }
  5228. private void LoadSSPILibrary() {
  5229. // Outer check so we don't acquire lock once once it's loaded.
  5230. if (!s_fSSPILoaded) {
  5231. lock (s_tdsParserLock) {
  5232. // re-check inside lock
  5233. if (!s_fSSPILoaded) {
  5234. // use local for ref param to defer setting s_maxSSPILength until we know the call succeeded.
  5235. UInt32 maxLength = 0;
  5236. if (0 != SNINativeMethodWrapper.SNISecInitPackage(ref maxLength))
  5237. SSPIError(SQLMessage.SSPIInitializeError(), TdsEnums.INIT_SSPI_PACKAGE);
  5238. s_maxSSPILength = maxLength;
  5239. s_fSSPILoaded = true;
  5240. }
  5241. }
  5242. }
  5243. if (s_maxSSPILength > Int32.MaxValue) {
  5244. throw SQL.InvalidSSPIPacketSize(); // SqlBu 332503
  5245. }
  5246. }
  5247. internal byte[] GetDTCAddress(int timeout, TdsParserStateObject stateObj) {
  5248. // If this fails, the server will return a server error - Sameet Agarwal confirmed.
  5249. // Success: DTCAddress returned. Failure: SqlError returned.
  5250. byte[] dtcAddr = null;
  5251. using (SqlDataReader dtcReader = TdsExecuteTransactionManagerRequest(
  5252. null,
  5253. TdsEnums.TransactionManagerRequestType.GetDTCAddress,
  5254. null,
  5255. TdsEnums.TransactionManagerIsolationLevel.Unspecified,
  5256. timeout, null, stateObj, true)) {
  5257. Debug.Assert(SniContext.Snix_Read==stateObj.SniContext, String.Format((IFormatProvider)null, "The SniContext should be Snix_Read but it actually is {0}", stateObj.SniContext));
  5258. if (null != dtcReader && dtcReader.Read()) {
  5259. Debug.Assert(dtcReader.GetName(0) == "TM Address", "TdsParser: GetDTCAddress did not return 'TM Address'");
  5260. // DTCAddress is of variable size, and does not have a maximum. So we call GetBytes
  5261. // to get the length of the dtcAddress, then allocate a byte array of that length,
  5262. // then call GetBytes again on that byte[] with the length
  5263. long dtcLength = dtcReader.GetBytes(0, 0, null, 0, 0);
  5264. //
  5265. if (dtcLength <= Int32.MaxValue) {
  5266. int cb = (int)dtcLength;
  5267. dtcAddr = new byte[cb];
  5268. dtcReader.GetBytes(0, 0, dtcAddr, 0, cb);
  5269. }
  5270. #if DEBUG
  5271. else {
  5272. Debug.Assert(false, "unexpected length (> Int32.MaxValue) returned from dtcReader.GetBytes");
  5273. // if we hit this case we'll just return a null address so that the user
  5274. // will get a transcaction enlistment error in the upper layers
  5275. }
  5276. #endif
  5277. }
  5278. }
  5279. return dtcAddr;
  5280. }
  5281. // Propagate the dtc cookie to the server, enlisting the connection.
  5282. internal void PropagateDistributedTransaction(byte[] buffer, int timeout, TdsParserStateObject stateObj) {
  5283. // if this fails, the server will return a server error - Sameet Agarwal confirmed
  5284. // Success: server will return done token. Failure: SqlError returned.
  5285. TdsExecuteTransactionManagerRequest(buffer,
  5286. TdsEnums.TransactionManagerRequestType.Propagate, null,
  5287. TdsEnums.TransactionManagerIsolationLevel.Unspecified, timeout, null, stateObj, true);
  5288. }
  5289. internal SqlDataReader TdsExecuteTransactionManagerRequest(
  5290. byte[] buffer,
  5291. TdsEnums.TransactionManagerRequestType request,
  5292. string transactionName,
  5293. TdsEnums.TransactionManagerIsolationLevel isoLevel,
  5294. int timeout,
  5295. SqlInternalTransaction transaction,
  5296. TdsParserStateObject stateObj,
  5297. bool isDelegateControlRequest) {
  5298. Debug.Assert(this == stateObj.Parser, "different parsers");
  5299. if (TdsParserState.Broken == State || TdsParserState.Closed == State) {
  5300. return null;
  5301. }
  5302. // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
  5303. // delegated transactions often happen while there is an open result
  5304. // set, so we need to handle them by using a different MARS session,
  5305. // otherwise we'll write on the physical state objects while someone
  5306. // else is using it. When we don't have MARS enabled, we need to
  5307. // lock the physical state object to syncronize it's use at least
  5308. // until we increment the open results count. Once it's been
  5309. // incremented the delegated transaction requests will fail, so they
  5310. // won't stomp on anything.
  5311. Debug.Assert(!_connHandler.ThreadHasParserLockForClose || _connHandler._parserLock.ThreadMayHaveLock(), "Thread claims to have parser lock, but lock is not taken");
  5312. bool callerHasConnectionLock = _connHandler.ThreadHasParserLockForClose; // If the thread already claims to have the parser lock, then we will let the caller handle releasing it
  5313. if (!callerHasConnectionLock) {
  5314. _connHandler._parserLock.Wait(canReleaseFromAnyThread:false);
  5315. _connHandler.ThreadHasParserLockForClose = true;
  5316. }
  5317. // Capture _asyncWrite (after taking lock) to restore it afterwards
  5318. bool hadAsyncWrites = _asyncWrite;
  5319. try {
  5320. // Temprarily disable async writes
  5321. _asyncWrite = false;
  5322. // This validation step MUST be done after locking the connection to guarantee we don't
  5323. // accidentally execute after the transaction has completed on a different thread.
  5324. if (!isDelegateControlRequest) {
  5325. _connHandler.CheckEnlistedTransactionBinding();
  5326. }
  5327. stateObj._outputMessageType = TdsEnums.MT_TRANS; // set message type
  5328. stateObj.SetTimeoutSeconds(timeout);
  5329. stateObj.SniContext = SniContext.Snix_Execute;
  5330. if (_isYukon) {
  5331. const int marsHeaderSize = 18; // 4 + 2 + 8 + 4
  5332. const int totalHeaderLength = 22; // 4 + 4 + 2 + 8 + 4
  5333. Debug.Assert(stateObj._outBytesUsed == stateObj._outputHeaderLen, "Output bytes written before total header length");
  5334. // Write total header length
  5335. WriteInt(totalHeaderLength, stateObj);
  5336. // Write mars header length
  5337. WriteInt(marsHeaderSize, stateObj);
  5338. WriteMarsHeaderData(stateObj, _currentTransaction);
  5339. }
  5340. WriteShort((short)request, stateObj); // write TransactionManager Request type
  5341. bool returnReader = false;
  5342. switch (request) {
  5343. case TdsEnums.TransactionManagerRequestType.GetDTCAddress:
  5344. WriteShort(0, stateObj);
  5345. returnReader = true;
  5346. break;
  5347. case TdsEnums.TransactionManagerRequestType.Propagate:
  5348. if (null != buffer) {
  5349. WriteShort(buffer.Length, stateObj);
  5350. stateObj.WriteByteArray(buffer, buffer.Length, 0);
  5351. }
  5352. else {
  5353. WriteShort(0, stateObj);
  5354. }
  5355. break;
  5356. case TdsEnums.TransactionManagerRequestType.Begin:
  5357. Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for BeginTransaction!");
  5358. Debug.Assert(null != transaction, "Should have specified an internalTransaction when doing a BeginTransaction request!");
  5359. // Only assign the passed in transaction if it is not equal to the current transaction.
  5360. // And, if it is not equal, the current actually should be null. Anything else
  5361. // is a unexpected state. The concern here is mainly for the mixed use of
  5362. // T-SQL and API transactions. See SQL BU DT 345300 for full details and repro.
  5363. // Expected states:
  5364. // 1) _pendingTransaction = null, _currentTransaction = null, non null transaction
  5365. // passed in on BeginTransaction API call.
  5366. // 2) _currentTransaction != null, _pendingTransaction = null, non null transaction
  5367. // passed in but equivalent to _currentTransaction.
  5368. // #1 will occur on standard BeginTransactionAPI call. #2 should only occur if
  5369. // t-sql transaction started followed by a call to SqlConnection.BeginTransaction.
  5370. // Any other state is unknown.
  5371. if (_currentTransaction != transaction) {
  5372. Debug.Assert(_currentTransaction == null || true == _fResetConnection, "We should not have a current Tx at this point");
  5373. PendingTransaction = transaction;
  5374. }
  5375. stateObj.WriteByte((byte)isoLevel);
  5376. stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string).
  5377. WriteString(transactionName, stateObj);
  5378. break;
  5379. case TdsEnums.TransactionManagerRequestType.Promote:
  5380. Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for PromoteTransaction!");
  5381. // No payload - except current transaction in header
  5382. // Promote returns a DTC cookie. However, the transaction cookie we use for the
  5383. // connection does not change after a promote.
  5384. break;
  5385. case TdsEnums.TransactionManagerRequestType.Commit:
  5386. Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for CommitTransaction!");
  5387. Debug.Assert(transactionName.Length == 0, "Should not have a transaction name on Commit");
  5388. stateObj.WriteByte((byte)0); // No xact name
  5389. stateObj.WriteByte(0); // No flags
  5390. Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!");
  5391. // WriteByte((byte) 0, stateObj); // IsolationLevel
  5392. // WriteByte((byte) 0, stateObj); // No begin xact name
  5393. break;
  5394. case TdsEnums.TransactionManagerRequestType.Rollback:
  5395. Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for RollbackTransaction!");
  5396. stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string).
  5397. WriteString(transactionName, stateObj);
  5398. stateObj.WriteByte(0); // No flags
  5399. Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!");
  5400. // WriteByte((byte) 0, stateObj); // IsolationLevel
  5401. // WriteByte((byte) 0, stateObj); // No begin xact name
  5402. break;
  5403. case TdsEnums.TransactionManagerRequestType.Save:
  5404. Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for SaveTransaction!");
  5405. stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string).
  5406. WriteString(transactionName, stateObj);
  5407. break;
  5408. default:
  5409. Debug.Assert(false, "Unexpected TransactionManagerRequest");
  5410. break;
  5411. }
  5412. Task writeTask = stateObj.WritePacket(TdsEnums.HARDFLUSH);
  5413. Debug.Assert(writeTask == null, "Writes should not pend when writing sync");
  5414. stateObj._pendingData = true;
  5415. stateObj._messageStatus = 0;
  5416. SqlDataReader dtcReader = null;
  5417. stateObj.SniContext = SniContext.Snix_Read;
  5418. if (returnReader) {
  5419. dtcReader = new SqlDataReader(null, CommandBehavior.Default);
  5420. Debug.Assert(this == stateObj.Parser, "different parser");
  5421. #if DEBUG
  5422. // Remove the current owner of stateObj - otherwise we will hit asserts
  5423. stateObj.Owner = null;
  5424. #endif
  5425. dtcReader.Bind(stateObj);
  5426. // force consumption of metadata
  5427. _SqlMetaDataSet metaData = dtcReader.MetaData;
  5428. }
  5429. else {
  5430. Run(RunBehavior.UntilDone, null, null, null, stateObj);
  5431. }
  5432. // If the retained ID is no longer valid (because we are enlisting in null or a new transaction) then it should be cleared
  5433. if (((request == TdsEnums.TransactionManagerRequestType.Begin) || (request == TdsEnums.TransactionManagerRequestType.Propagate)) && ((transaction == null) || (transaction.TransactionId != _retainedTransactionId))) {
  5434. _retainedTransactionId = SqlInternalTransaction.NullTransactionId;
  5435. }
  5436. return dtcReader;
  5437. }
  5438. catch (Exception e) {
  5439. //
  5440. if (!ADP.IsCatchableExceptionType(e)) {
  5441. throw;
  5442. }
  5443. FailureCleanup(stateObj, e);
  5444. throw;
  5445. }
  5446. finally {
  5447. // SQLHotfix 50000518
  5448. // make sure we don't leave temporary fields set when leaving this function
  5449. _pendingTransaction = null;
  5450. _asyncWrite = hadAsyncWrites;
  5451. if (!callerHasConnectionLock) {
  5452. _connHandler.ThreadHasParserLockForClose = false;
  5453. _connHandler._parserLock.Release();
  5454. }
  5455. }
  5456. }
  5457. internal void FailureCleanup(TdsParserStateObject stateObj, Exception e) {
  5458. int old_outputPacketNumber = stateObj._outputPacketNumber;
  5459. if (Bid.TraceOn) {
  5460. Bid.Trace("<sc.TdsParser.FailureCleanup|ERR> Exception caught on ExecuteXXX: '%ls' \n", e.ToString());
  5461. }
  5462. if (stateObj.HasOpenResult) { // SQL BU DT 383773 - need to decrement openResultCount if operation failed.
  5463. stateObj.DecrementOpenResultCount();
  5464. }
  5465. // be sure to wipe out our buffer if we started sending stuff
  5466. stateObj.ResetBuffer();
  5467. stateObj._outputPacketNumber = 1; // end of message - reset to 1 - per ramas
  5468. if (old_outputPacketNumber != 1 && _state == TdsParserState.OpenLoggedIn) {
  5469. Debug.Assert(_connHandler._parserLock.ThreadMayHaveLock(), "Should not be calling into FailureCleanup without first taking the parser lock");
  5470. bool originalThreadHasParserLock = _connHandler.ThreadHasParserLockForClose;
  5471. try {
  5472. // Dev11 Bug 385286 : ExecuteNonQueryAsync hangs when trying to write a parameter which generates ArgumentException and while handling that exception the server disconnects the connection
  5473. // Need to set this to true such that if we have an error sending\processing the attention, we won't deadlock ourselves
  5474. _connHandler.ThreadHasParserLockForClose = true;
  5475. // If _outputPacketNumber prior to ResetBuffer was not equal to 1, a packet was already
  5476. // sent to the server and so we need to send an attention and process the attention ack.
  5477. stateObj.SendAttention();
  5478. ProcessAttention(stateObj);
  5479. }
  5480. finally {
  5481. // Reset the ThreadHasParserLock value incase our caller expects it to be set\not set
  5482. _connHandler.ThreadHasParserLockForClose = originalThreadHasParserLock;
  5483. }
  5484. }
  5485. Bid.Trace("<sc.TdsParser.FailureCleanup|ERR> Exception rethrown. \n");
  5486. }
  5487. internal Task TdsExecuteSQLBatch(string text, int timeout, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, bool sync, bool callerHasConnectionLock = false) {
  5488. if (TdsParserState.Broken == State || TdsParserState.Closed == State) {
  5489. return null;
  5490. }
  5491. if (stateObj.BcpLock) {
  5492. throw SQL.ConnectionLockedForBcpEvent();
  5493. }
  5494. // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
  5495. // delegated transactions often happen while there is an open result
  5496. // set, so we need to handle them by using a different MARS session,
  5497. // otherwise we'll write on the physical state objects while someone
  5498. // else is using it. When we don't have MARS enabled, we need to
  5499. // lock the physical state object to syncronize it's use at least
  5500. // until we increment the open results count. Once it's been
  5501. // incremented the delegated transaction requests will fail, so they
  5502. // won't stomp on anything.
  5503. // Only need to take the lock if neither the thread nor the caller claims to already have it
  5504. bool needToTakeParserLock = (!callerHasConnectionLock) && (!_connHandler.ThreadHasParserLockForClose);
  5505. Debug.Assert(!_connHandler.ThreadHasParserLockForClose || sync, "Thread shouldn't claim to have the parser lock if we are doing async writes"); // Since we have the possibility of pending with async writes, make sure the thread doesn't claim to already have the lock
  5506. Debug.Assert(needToTakeParserLock || _connHandler._parserLock.ThreadMayHaveLock(), "Thread or caller claims to have connection lock, but lock is not taken");
  5507. bool releaseConnectionLock = false;
  5508. if (needToTakeParserLock) {
  5509. _connHandler._parserLock.Wait(canReleaseFromAnyThread: !sync);
  5510. releaseConnectionLock = true;
  5511. }
  5512. // Switch the writing mode
  5513. // NOTE: We are not turning off async writes when we complete since SqlBulkCopy uses this method and expects _asyncWrite to not change
  5514. _asyncWrite = !sync;
  5515. try {
  5516. // Check that the connection is still alive
  5517. if ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken)) {
  5518. throw ADP.ClosedConnectionError();
  5519. }
  5520. // This validation step MUST be done after locking the connection to guarantee we don't
  5521. // accidentally execute after the transaction has completed on a different thread.
  5522. _connHandler.CheckEnlistedTransactionBinding();
  5523. stateObj.SetTimeoutSeconds(timeout);
  5524. if ((!_fMARS) && (_physicalStateObj.HasOpenResult))
  5525. {
  5526. Bid.Trace("<sc.TdsParser.TdsExecuteSQLBatch|ERR> Potential multi-threaded misuse of connection, non-MARs connection with an open result %d#\n", ObjectID);
  5527. }
  5528. stateObj.SniContext = SniContext.Snix_Execute;
  5529. if (_isYukon) {
  5530. WriteRPCBatchHeaders(stateObj, notificationRequest);
  5531. }
  5532. stateObj._outputMessageType = TdsEnums.MT_SQL;
  5533. WriteString(text, text.Length, 0, stateObj);
  5534. Task executeTask = stateObj.ExecuteFlush();
  5535. if (executeTask == null) {
  5536. stateObj.SniContext = SniContext.Snix_Read;
  5537. }
  5538. else {
  5539. Debug.Assert(!sync, "Should not have gotten a Task when writing in sync mode");
  5540. // Need to wait for flush - continuation will unlock the connection
  5541. bool taskReleaseConnectionLock = releaseConnectionLock;
  5542. releaseConnectionLock = false;
  5543. return executeTask.ContinueWith(t => {
  5544. Debug.Assert(!t.IsCanceled, "Task should not be canceled");
  5545. try {
  5546. if (t.IsFaulted) {
  5547. FailureCleanup(stateObj, t.Exception.InnerException);
  5548. throw t.Exception.InnerException;
  5549. }
  5550. else {
  5551. stateObj.SniContext = SniContext.Snix_Read;
  5552. }
  5553. }
  5554. finally {
  5555. if (taskReleaseConnectionLock) {
  5556. _connHandler._parserLock.Release();
  5557. }
  5558. }
  5559. }, TaskScheduler.Default);
  5560. }
  5561. // Finished [....]
  5562. return null;
  5563. }
  5564. catch (Exception e) {
  5565. //Debug.Assert(_state == TdsParserState.Broken, "Caught exception in TdsExecuteSQLBatch but connection was not broken!");
  5566. //
  5567. if (!ADP.IsCatchableExceptionType(e)) {
  5568. throw;
  5569. }
  5570. FailureCleanup(stateObj, e);
  5571. throw;
  5572. }
  5573. finally {
  5574. if (releaseConnectionLock) {
  5575. _connHandler._parserLock.Release();
  5576. }
  5577. }
  5578. }
  5579. internal Task TdsExecuteRPC(_SqlRPC[] rpcArray, int timeout, bool inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, bool isCommandProc, bool sync = true,
  5580. TaskCompletionSource<object> completion = null, int startRpc = 0, int startParam = 0) {
  5581. bool firstCall = (completion == null);
  5582. bool releaseConnectionLock = false;
  5583. Debug.Assert(!firstCall || startRpc == 0, "startRpc is not 0 on first call");
  5584. Debug.Assert(!firstCall || startParam == 0, "startParam is not 0 on first call");
  5585. Debug.Assert(!firstCall || !_connHandler.ThreadHasParserLockForClose, "Thread should not already have connection lock");
  5586. Debug.Assert(firstCall || _connHandler._parserLock.ThreadMayHaveLock(), "Connection lock not taken after the first call");
  5587. try {
  5588. _SqlRPC rpcext = null;
  5589. int tempLen;
  5590. // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
  5591. // delegated transactions often happen while there is an open result
  5592. // set, so we need to handle them by using a different MARS session,
  5593. // otherwise we'll write on the physical state objects while someone
  5594. // else is using it. When we don't have MARS enabled, we need to
  5595. // lock the physical state object to syncronize it's use at least
  5596. // until we increment the open results count. Once it's been
  5597. // incremented the delegated transaction requests will fail, so they
  5598. // won't stomp on anything.
  5599. if (firstCall) {
  5600. _connHandler._parserLock.Wait(canReleaseFromAnyThread:!sync);
  5601. releaseConnectionLock = true;
  5602. }
  5603. try {
  5604. // Ensure that connection is alive
  5605. if ((TdsParserState.Broken == State) || (TdsParserState.Closed == State)) {
  5606. throw ADP.ClosedConnectionError();
  5607. }
  5608. // This validation step MUST be done after locking the connection to guarantee we don't
  5609. // accidentally execute after the transaction has completed on a different thread.
  5610. if (firstCall) {
  5611. _asyncWrite = !sync;
  5612. _connHandler.CheckEnlistedTransactionBinding();
  5613. stateObj.SetTimeoutSeconds(timeout);
  5614. if ((!_fMARS) && (_physicalStateObj.HasOpenResult))
  5615. {
  5616. Bid.Trace("<sc.TdsParser.TdsExecuteRPC|ERR> Potential multi-threaded misuse of connection, non-MARs connection with an open result %d#\n", ObjectID);
  5617. }
  5618. stateObj.SniContext = SniContext.Snix_Execute;
  5619. if (_isYukon) {
  5620. WriteRPCBatchHeaders(stateObj, notificationRequest);
  5621. }
  5622. stateObj._outputMessageType = TdsEnums.MT_RPC;
  5623. }
  5624. for (int ii = startRpc; ii < rpcArray.Length; ii++) {
  5625. rpcext = rpcArray[ii];
  5626. if (startParam == 0 || ii > startRpc) {
  5627. if (rpcext.ProcID != 0 && _isShiloh) {
  5628. // Perf optimization for Shiloh and later,
  5629. Debug.Assert(rpcext.ProcID < 255, "rpcExec:ProcID can't be larger than 255");
  5630. WriteShort(0xffff, stateObj);
  5631. WriteShort((short)(rpcext.ProcID), stateObj);
  5632. }
  5633. else {
  5634. Debug.Assert(!ADP.IsEmpty(rpcext.rpcName), "must have an RPC name");
  5635. tempLen = rpcext.rpcName.Length;
  5636. WriteShort(tempLen, stateObj);
  5637. WriteString(rpcext.rpcName, tempLen, 0, stateObj);
  5638. }
  5639. // Options
  5640. WriteShort((short)rpcext.options, stateObj);
  5641. }
  5642. // Stream out parameters
  5643. SqlParameter[] parameters = rpcext.parameters;
  5644. for (int i = (ii == startRpc) ? startParam : 0; i < parameters.Length; i++) {
  5645. // Debug.WriteLine("i: " + i.ToString(CultureInfo.InvariantCulture));
  5646. // parameters can be unnamed
  5647. SqlParameter param = parameters[i];
  5648. // Since we are reusing the parameters array, we cannot rely on length to indicate no of parameters.
  5649. if (param == null)
  5650. break; // End of parameters for this execute
  5651. // Validate parameters are not variable length without size and with null value. MDAC 66522
  5652. param.Validate(i, isCommandProc);
  5653. // type (parameter record stores the MetaType class which is a helper that encapsulates all the type information we need here)
  5654. MetaType mt = param.InternalMetaType;
  5655. if (mt.IsNewKatmaiType) {
  5656. WriteSmiParameter(param, i, 0 != (rpcext.paramoptions[i] & TdsEnums.RPC_PARAM_DEFAULT), stateObj);
  5657. continue;
  5658. }
  5659. if ((!_isShiloh && !mt.Is70Supported) ||
  5660. (!_isYukon && !mt.Is80Supported) ||
  5661. (!_isKatmai && !mt.Is90Supported)) {
  5662. throw ADP.VersionDoesNotSupportDataType(mt.TypeName);
  5663. }
  5664. object value = null;
  5665. bool isNull = true;
  5666. bool isSqlVal = false;
  5667. bool isDataFeed = false;
  5668. // if we have an output param, set the value to null so we do not send it across to the server
  5669. if (param.Direction == ParameterDirection.Output) {
  5670. isSqlVal = param.ParamaterIsSqlType; // We have to forward the TYPE info, we need to know what type we are returning. Once we null the paramater we will no longer be able to distinguish what type were seeing.
  5671. param.Value = null;
  5672. param.ParamaterIsSqlType = isSqlVal;
  5673. }
  5674. else {
  5675. value = param.GetCoercedValue();
  5676. isNull = param.IsNull;
  5677. if (!isNull) {
  5678. isSqlVal = param.CoercedValueIsSqlType;
  5679. isDataFeed = param.CoercedValueIsDataFeed;
  5680. }
  5681. }
  5682. WriteParameterName(param.ParameterNameFixed, stateObj);
  5683. // Write parameter status
  5684. stateObj.WriteByte(rpcext.paramoptions[i]);
  5685. //
  5686. // fixup the types by using the NullableType property of the MetaType class
  5687. //
  5688. // following rules should be followed based on feedback from the M-SQL team
  5689. // 1) always use the BIG* types (ex: instead of SQLCHAR use SQLBIGCHAR)
  5690. // 2) always use nullable types (ex: instead of SQLINT use SQLINTN)
  5691. // 3) DECIMALN should always be sent as NUMERICN
  5692. //
  5693. stateObj.WriteByte(mt.NullableType);
  5694. // handle variants here: the SQLVariant writing routine will write the maxlen and actual len columns
  5695. if (mt.TDSType == TdsEnums.SQLVARIANT) {
  5696. // devnote: Do we ever hit this codepath? Yes, when a null value is being writen out via a sql variant
  5697. // param.GetActualSize is not used
  5698. WriteSqlVariantValue(isSqlVal ? MetaType.GetComValueFromSqlVariant(value) : value, param.GetActualSize(), param.Offset, stateObj);
  5699. continue;
  5700. }
  5701. // MaxLen field is only written out for non-fixed length data types
  5702. // use the greater of the two sizes for maxLen
  5703. int actualSize;
  5704. int size = mt.IsSizeInCharacters ? param.GetParameterSize() * 2 : param.GetParameterSize();
  5705. //for UDTs, we calculate the length later when we get the bytes. This is a really expensive operation
  5706. if (mt.TDSType != TdsEnums.SQLUDT)
  5707. // getting the actualSize is expensive, cache here and use below
  5708. actualSize = param.GetActualSize();
  5709. else
  5710. actualSize = 0; //get this later
  5711. int codePageByteSize = 0;
  5712. int maxsize = 0;
  5713. if (mt.IsAnsiType) {
  5714. // Avoid the following code block if ANSI but unfilled LazyMat blob
  5715. if ((!isNull) && (!isDataFeed)) {
  5716. string s;
  5717. if (isSqlVal) {
  5718. if (value is SqlString) {
  5719. s = ((SqlString)value).Value;
  5720. }
  5721. else {
  5722. Debug.Assert(value is SqlChars, "Unknown value for Ansi datatype");
  5723. s = new String(((SqlChars)value).Value);
  5724. }
  5725. }
  5726. else {
  5727. s = (string)value;
  5728. }
  5729. codePageByteSize = GetEncodingCharLength(s, actualSize, param.Offset, _defaultEncoding);
  5730. }
  5731. if (mt.IsPlp) {
  5732. WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
  5733. }
  5734. else {
  5735. maxsize = (size > codePageByteSize) ? size : codePageByteSize;
  5736. if (maxsize == 0) {
  5737. // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
  5738. if (mt.IsNCharType)
  5739. maxsize = 2;
  5740. else
  5741. maxsize = 1;
  5742. }
  5743. WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj);
  5744. }
  5745. }
  5746. else {
  5747. // If type timestamp - treat as fixed type and always send over timestamp length, which is 8.
  5748. // For fixed types, we either send null or fixed length for type length. We want to match that
  5749. // behavior for timestamps. However, in the case of null, we still must send 8 because if we
  5750. // send null we will not receive a output val. You can send null for fixed types and still
  5751. // receive a output value, but not for variable types. So, always send 8 for timestamp because
  5752. // while the user sees it as a fixed type, we are actually representing it as a bigbinary which
  5753. // is variable.
  5754. if (mt.SqlDbType == SqlDbType.Timestamp) {
  5755. WriteParameterVarLen(mt, TdsEnums.TEXT_TIME_STAMP_LEN, false, stateObj);
  5756. }
  5757. else if (mt.SqlDbType == SqlDbType.Udt) {
  5758. byte[] udtVal = null;
  5759. Microsoft.SqlServer.Server.Format format = Microsoft.SqlServer.Server.Format.Native;
  5760. Debug.Assert(_isYukon, "Invalid DataType UDT for non-Yukon or later server!");
  5761. if (!isNull) {
  5762. udtVal = _connHandler.Connection.GetBytes(value, out format, out maxsize);
  5763. Debug.Assert(null != udtVal, "GetBytes returned null instance. Make sure that it always returns non-null value");
  5764. size = udtVal.Length;
  5765. //it may be legitimate, but we dont support it yet
  5766. if (size < 0 || (size >= UInt16.MaxValue && maxsize != -1))
  5767. throw new IndexOutOfRangeException();
  5768. }
  5769. //if this is NULL value, write special null value
  5770. byte[] lenBytes = BitConverter.GetBytes((Int64)size);
  5771. if (ADP.IsEmpty(param.UdtTypeName))
  5772. throw SQL.MustSetUdtTypeNameForUdtParams();
  5773. // Split the input name. TypeName is returned as single 3 part name during DeriveParameters.
  5774. // NOTE: ParseUdtTypeName throws if format is incorrect
  5775. String[] names = SqlParameter.ParseTypeName(param.UdtTypeName, true /* is UdtTypeName */);
  5776. if (!ADP.IsEmpty(names[0]) && TdsEnums.MAX_SERVERNAME < names[0].Length) {
  5777. throw ADP.ArgumentOutOfRange("names");
  5778. }
  5779. if (!ADP.IsEmpty(names[1]) && TdsEnums.MAX_SERVERNAME < names[names.Length - 2].Length) {
  5780. throw ADP.ArgumentOutOfRange("names");
  5781. }
  5782. if (TdsEnums.MAX_SERVERNAME < names[2].Length) {
  5783. throw ADP.ArgumentOutOfRange("names");
  5784. }
  5785. WriteUDTMetaData(value, names[0], names[1], names[2], stateObj);
  5786. //
  5787. if (!isNull) {
  5788. WriteUnsignedLong((ulong)udtVal.Length, stateObj); // PLP length
  5789. if (udtVal.Length > 0) { // Only write chunk length if its value is greater than 0
  5790. WriteInt(udtVal.Length, stateObj); // Chunk length
  5791. stateObj.WriteByteArray(udtVal, udtVal.Length, 0); // Value
  5792. }
  5793. WriteInt(0, stateObj); // Terminator
  5794. }
  5795. else {
  5796. WriteUnsignedLong(TdsEnums.SQL_PLP_NULL, stateObj); // PLP Null.
  5797. }
  5798. continue; // End of UDT - continue to next parameter.
  5799. //
  5800. }
  5801. else if (mt.IsPlp) {
  5802. if (mt.SqlDbType != SqlDbType.Xml)
  5803. WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
  5804. }
  5805. else if ((!mt.IsVarTime) && (mt.SqlDbType != SqlDbType.Date)) { // Time, Date, DateTime2, DateTimeoffset do not have the size written out
  5806. maxsize = (size > actualSize) ? size : actualSize;
  5807. if (maxsize == 0 && IsYukonOrNewer) {
  5808. // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
  5809. if (mt.IsNCharType)
  5810. maxsize = 2;
  5811. else
  5812. maxsize = 1;
  5813. }
  5814. WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj);
  5815. }
  5816. }
  5817. // scale and precision are only relevant for numeric and decimal types
  5818. if (mt.SqlDbType == SqlDbType.Decimal) {
  5819. byte precision = param.GetActualPrecision();
  5820. byte scale = param.GetActualScale();
  5821. if (precision > TdsEnums.MAX_NUMERIC_PRECISION) {
  5822. throw SQL.PrecisionValueOutOfRange(precision);
  5823. }
  5824. // bug 49512, make sure the value matches the scale the user enters
  5825. if (!isNull) {
  5826. if (isSqlVal) {
  5827. value = AdjustSqlDecimalScale((SqlDecimal)value, scale);
  5828. // If Precision is specified, verify value precision vs param precision
  5829. if (precision != 0) {
  5830. if (precision < ((SqlDecimal)value).Precision) {
  5831. throw ADP.ParameterValueOutOfRange((SqlDecimal)value);
  5832. }
  5833. }
  5834. }
  5835. else {
  5836. value = AdjustDecimalScale((Decimal)value, scale);
  5837. SqlDecimal sqlValue = new SqlDecimal((Decimal)value);
  5838. // If Precision is specified, verify value precision vs param precision
  5839. if (precision != 0) {
  5840. if (precision < sqlValue.Precision) {
  5841. throw ADP.ParameterValueOutOfRange((Decimal)value);
  5842. }
  5843. }
  5844. }
  5845. }
  5846. if (0 == precision) {
  5847. if (_isShiloh)
  5848. stateObj.WriteByte(TdsEnums.DEFAULT_NUMERIC_PRECISION);
  5849. else
  5850. stateObj.WriteByte(TdsEnums.SPHINX_DEFAULT_NUMERIC_PRECISION);
  5851. }
  5852. else
  5853. stateObj.WriteByte(precision);
  5854. stateObj.WriteByte(scale);
  5855. }
  5856. else if (mt.IsVarTime) {
  5857. stateObj.WriteByte(param.GetActualScale());
  5858. }
  5859. // write out collation or xml metadata
  5860. if (_isYukon && (mt.SqlDbType == SqlDbType.Xml)) {
  5861. if (((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) ||
  5862. ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) ||
  5863. ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty))) {
  5864. stateObj.WriteByte(1); //Schema present flag
  5865. if ((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) {
  5866. tempLen = (param.XmlSchemaCollectionDatabase).Length;
  5867. stateObj.WriteByte((byte)(tempLen));
  5868. WriteString(param.XmlSchemaCollectionDatabase, tempLen, 0, stateObj);
  5869. }
  5870. else {
  5871. stateObj.WriteByte(0); // No dbname
  5872. }
  5873. if ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) {
  5874. tempLen = (param.XmlSchemaCollectionOwningSchema).Length;
  5875. stateObj.WriteByte((byte)(tempLen));
  5876. WriteString(param.XmlSchemaCollectionOwningSchema, tempLen, 0, stateObj);
  5877. }
  5878. else {
  5879. stateObj.WriteByte(0); // no xml schema name
  5880. }
  5881. if ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty)) {
  5882. tempLen = (param.XmlSchemaCollectionName).Length;
  5883. WriteShort((short)(tempLen), stateObj);
  5884. WriteString(param.XmlSchemaCollectionName, tempLen, 0, stateObj);
  5885. }
  5886. else {
  5887. WriteShort(0, stateObj); // No xml schema collection name
  5888. }
  5889. }
  5890. else {
  5891. stateObj.WriteByte(0); // No schema
  5892. }
  5893. }
  5894. else if (_isShiloh && mt.IsCharType) {
  5895. // if it is not supplied, simply write out our default collation, otherwise, write out the one attached to the parameter
  5896. SqlCollation outCollation = (param.Collation != null) ? param.Collation : _defaultCollation;
  5897. Debug.Assert(_defaultCollation != null, "_defaultCollation is null!");
  5898. WriteUnsignedInt(outCollation.info, stateObj);
  5899. stateObj.WriteByte(outCollation.sortId);
  5900. }
  5901. if (0 == codePageByteSize)
  5902. WriteParameterVarLen(mt, actualSize, isNull, stateObj, isDataFeed);
  5903. else
  5904. WriteParameterVarLen(mt, codePageByteSize, isNull, stateObj, isDataFeed);
  5905. Task writeParamTask = null;
  5906. // write the value now
  5907. if (!isNull) {
  5908. if (isSqlVal) {
  5909. writeParamTask = WriteSqlValue(value, mt, actualSize, codePageByteSize, param.Offset, stateObj);
  5910. }
  5911. else {
  5912. // for codePageEncoded types, WriteValue simply expects the number of characters
  5913. // For plp types, we also need the encoded byte size
  5914. writeParamTask = WriteValue(value, mt, param.GetActualScale(), actualSize, codePageByteSize, param.Offset, stateObj, param.Size, isDataFeed);
  5915. }
  5916. }
  5917. if (!sync) {
  5918. if (writeParamTask == null) {
  5919. writeParamTask = stateObj.WaitForAccumulatedWrites();
  5920. }
  5921. if (writeParamTask != null) {
  5922. Task task = null;
  5923. if (completion == null) {
  5924. completion = new TaskCompletionSource<object>();
  5925. task = completion.Task;
  5926. }
  5927. AsyncHelper.ContinueTask(writeParamTask, completion,
  5928. () => TdsExecuteRPC(rpcArray, timeout, inSchema, notificationRequest, stateObj, isCommandProc, sync, completion,
  5929. startRpc: ii, startParam: i + 1),
  5930. connectionToDoom: _connHandler,
  5931. onFailure: exc => TdsExecuteRPC_OnFailure(exc, stateObj));
  5932. // Take care of releasing the locks
  5933. if (releaseConnectionLock) {
  5934. task.ContinueWith(_ => {
  5935. _connHandler._parserLock.Release();
  5936. }, TaskScheduler.Default);
  5937. releaseConnectionLock = false;
  5938. }
  5939. return task;
  5940. }
  5941. }
  5942. #if DEBUG
  5943. else {
  5944. Debug.Assert(writeParamTask == null, "Should not have a task when executing sync");
  5945. }
  5946. #endif
  5947. } // parameter for loop
  5948. // If this is not the last RPC we are sending, add the batch flag
  5949. if (ii < (rpcArray.Length - 1)) {
  5950. if (_isYukon) {
  5951. stateObj.WriteByte(TdsEnums.YUKON_RPCBATCHFLAG);
  5952. }
  5953. else {
  5954. stateObj.WriteByte(TdsEnums.SHILOH_RPCBATCHFLAG);
  5955. }
  5956. }
  5957. } // rpc for loop
  5958. Task execFlushTask = stateObj.ExecuteFlush();
  5959. Debug.Assert(!sync || execFlushTask == null, "Should not get a task when executing sync");
  5960. if (execFlushTask != null) {
  5961. Task task = null;
  5962. if (completion == null) {
  5963. completion = new TaskCompletionSource<object>();
  5964. task = completion.Task;
  5965. }
  5966. bool taskReleaseConnectionLock = releaseConnectionLock;
  5967. execFlushTask.ContinueWith(tsk => ExecuteFlushTaskCallback(tsk, stateObj, completion, taskReleaseConnectionLock), TaskScheduler.Default);
  5968. // ExecuteFlushTaskCallback will take care of the locks for us
  5969. releaseConnectionLock = false;
  5970. return task;
  5971. }
  5972. }
  5973. catch (Exception e) {
  5974. //
  5975. if (!ADP.IsCatchableExceptionType(e)) {
  5976. throw;
  5977. }
  5978. FailureCleanup(stateObj, e);
  5979. throw;
  5980. }
  5981. FinalizeExecuteRPC(stateObj);
  5982. if (completion != null) {
  5983. completion.SetResult(null);
  5984. }
  5985. return null;
  5986. }
  5987. catch (Exception e) {
  5988. FinalizeExecuteRPC(stateObj);
  5989. if (completion != null) {
  5990. completion.SetException(e);
  5991. return null;
  5992. }
  5993. else {
  5994. throw e;
  5995. }
  5996. }
  5997. finally {
  5998. Debug.Assert(firstCall || !releaseConnectionLock, "Shouldn't be releasing locks synchronously after the first call");
  5999. if (releaseConnectionLock) {
  6000. _connHandler._parserLock.Release();
  6001. }
  6002. }
  6003. }
  6004. private void FinalizeExecuteRPC(TdsParserStateObject stateObj) {
  6005. stateObj.SniContext = SniContext.Snix_Read;
  6006. _asyncWrite = false;
  6007. }
  6008. private void TdsExecuteRPC_OnFailure(Exception exc, TdsParserStateObject stateObj) {
  6009. RuntimeHelpers.PrepareConstrainedRegions();
  6010. try {
  6011. #if DEBUG
  6012. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  6013. RuntimeHelpers.PrepareConstrainedRegions();
  6014. try {
  6015. tdsReliabilitySection.Start();
  6016. #else
  6017. {
  6018. #endif //DEBUG
  6019. FailureCleanup(stateObj, exc);
  6020. }
  6021. #if DEBUG
  6022. finally {
  6023. tdsReliabilitySection.Stop();
  6024. }
  6025. #endif //DEBUG
  6026. }
  6027. catch (System.OutOfMemoryException) {
  6028. _connHandler.DoomThisConnection();
  6029. throw;
  6030. }
  6031. catch (System.StackOverflowException) {
  6032. _connHandler.DoomThisConnection();
  6033. throw;
  6034. }
  6035. catch (System.Threading.ThreadAbortException) {
  6036. _connHandler.DoomThisConnection();
  6037. throw;
  6038. }
  6039. }
  6040. private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, TaskCompletionSource<object> completion, bool releaseConnectionLock) {
  6041. try {
  6042. FinalizeExecuteRPC(stateObj);
  6043. if (tsk.Exception != null) {
  6044. Exception exc = tsk.Exception.InnerException;
  6045. RuntimeHelpers.PrepareConstrainedRegions();
  6046. try {
  6047. #if DEBUG
  6048. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  6049. RuntimeHelpers.PrepareConstrainedRegions();
  6050. try {
  6051. tdsReliabilitySection.Start();
  6052. #else
  6053. {
  6054. #endif //DEBUG
  6055. FailureCleanup(stateObj, tsk.Exception);
  6056. }
  6057. #if DEBUG
  6058. finally {
  6059. tdsReliabilitySection.Stop();
  6060. }
  6061. #endif //DEBUG
  6062. }
  6063. catch (System.OutOfMemoryException e) {
  6064. _connHandler.DoomThisConnection();
  6065. completion.SetException(e);
  6066. throw;
  6067. }
  6068. catch (System.StackOverflowException e) {
  6069. _connHandler.DoomThisConnection();
  6070. completion.SetException(e);
  6071. throw;
  6072. }
  6073. catch (System.Threading.ThreadAbortException e) {
  6074. _connHandler.DoomThisConnection();
  6075. completion.SetException(e);
  6076. throw;
  6077. }
  6078. catch (Exception e) {
  6079. exc = e;
  6080. }
  6081. completion.SetException(exc);
  6082. }
  6083. else {
  6084. completion.SetResult(null);
  6085. }
  6086. }
  6087. finally {
  6088. if (releaseConnectionLock) {
  6089. _connHandler._parserLock.Release();
  6090. }
  6091. }
  6092. }
  6093. private void WriteParameterName(string parameterName, TdsParserStateObject stateObj) {
  6094. // paramLen
  6095. // paramName
  6096. if (!ADP.IsEmpty(parameterName)) {
  6097. Debug.Assert(parameterName.Length <= 0xff, "parameter name can only be 255 bytes, shouldn't get to TdsParser!");
  6098. int tempLen = parameterName.Length & 0xff;
  6099. stateObj.WriteByte((byte)tempLen);
  6100. WriteString(parameterName, tempLen, 0, stateObj);
  6101. }
  6102. else {
  6103. stateObj.WriteByte(0);
  6104. }
  6105. }
  6106. private static readonly IEnumerable<MSS.SqlDataRecord> __tvpEmptyValue = new List<MSS.SqlDataRecord>().AsReadOnly();
  6107. private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefault, TdsParserStateObject stateObj) {
  6108. //
  6109. // Determine Metadata
  6110. //
  6111. ParameterPeekAheadValue peekAhead;
  6112. MSS.SmiParameterMetaData metaData = param.MetaDataForSmi(out peekAhead);
  6113. if (!_isKatmai) {
  6114. MetaType mt = MetaType.GetMetaTypeFromSqlDbType(metaData.SqlDbType, metaData.IsMultiValued);
  6115. throw ADP.VersionDoesNotSupportDataType(mt.TypeName);
  6116. }
  6117. //
  6118. // Determine value to send
  6119. //
  6120. object value;
  6121. MSS.ExtendedClrTypeCode typeCode;
  6122. // if we have an output or default param, set the value to null so we do not send it across to the server
  6123. if (sendDefault) {
  6124. // Value for TVP default is empty list, not NULL
  6125. if (SqlDbType.Structured == metaData.SqlDbType && metaData.IsMultiValued) {
  6126. value = __tvpEmptyValue;
  6127. typeCode = MSS.ExtendedClrTypeCode.IEnumerableOfSqlDataRecord;
  6128. }
  6129. else {
  6130. // Need to send null value for default
  6131. value = null;
  6132. typeCode = MSS.ExtendedClrTypeCode.DBNull;
  6133. }
  6134. }
  6135. else if (param.Direction == ParameterDirection.Output) {
  6136. bool isCLRType = param.ParamaterIsSqlType; // We have to forward the TYPE info, we need to know what type we are returning. Once we null the paramater we will no longer be able to distinguish what type were seeing.
  6137. param.Value = null;
  6138. value = null;
  6139. typeCode = MSS.ExtendedClrTypeCode.DBNull;
  6140. param.ParamaterIsSqlType = isCLRType;
  6141. }
  6142. else {
  6143. value = param.GetCoercedValue();
  6144. typeCode = MSS.MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType(
  6145. metaData.SqlDbType, metaData.IsMultiValued, value, null, MSS.SmiContextFactory.KatmaiVersion);
  6146. }
  6147. if (Bid.AdvancedOn) {
  6148. Bid.Trace("<sc.TdsParser.WriteSmiParameter|ADV> %d#, Sending parameter '%ls', default flag=%d, metadata:\n", ObjectID, param.ParameterName, sendDefault?1:0);
  6149. Bid.PutStr(metaData.TraceString(3));
  6150. Bid.Trace("\n");
  6151. }
  6152. //
  6153. // Write parameter metadata
  6154. //
  6155. WriteSmiParameterMetaData(metaData, sendDefault, stateObj);
  6156. //
  6157. // Now write the value
  6158. //
  6159. TdsParameterSetter paramSetter = new TdsParameterSetter(stateObj, metaData);
  6160. MSS.ValueUtilsSmi.SetCompatibleValueV200(
  6161. new MSS.SmiEventSink_Default(), // TDS Errors/events dealt with at lower level for now, just need an object for processing
  6162. paramSetter,
  6163. 0, // ordinal. TdsParameterSetter only handles one parameter at a time
  6164. metaData,
  6165. value,
  6166. typeCode,
  6167. param.Offset,
  6168. 0 < param.Size ? param.Size : -1,
  6169. peekAhead);
  6170. }
  6171. // Writes metadata portion of parameter stream from an SmiParameterMetaData object.
  6172. private void WriteSmiParameterMetaData(MSS.SmiParameterMetaData metaData, bool sendDefault, TdsParserStateObject stateObj) {
  6173. // Determine status
  6174. byte status = 0;
  6175. if (ParameterDirection.Output == metaData.Direction || ParameterDirection.InputOutput == metaData.Direction) {
  6176. status |= TdsEnums.RPC_PARAM_BYREF;
  6177. }
  6178. if (sendDefault) {
  6179. status |= TdsEnums.RPC_PARAM_DEFAULT;
  6180. }
  6181. // Write everything out
  6182. WriteParameterName(metaData.Name, stateObj);
  6183. stateObj.WriteByte(status);
  6184. WriteSmiTypeInfo(metaData, stateObj);
  6185. }
  6186. // Write a TypeInfo stream
  6187. // Devnote: we remap the legacy types (text, ntext, and image) to SQLBIGVARCHAR, SQLNVARCHAR, and SQLBIGVARBINARY
  6188. private void WriteSmiTypeInfo(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) {
  6189. switch(metaData.SqlDbType) {
  6190. case SqlDbType.BigInt:
  6191. stateObj.WriteByte(TdsEnums.SQLINTN);
  6192. stateObj.WriteByte(checked((byte)metaData.MaxLength));
  6193. break;
  6194. case SqlDbType.Binary:
  6195. stateObj.WriteByte(TdsEnums.SQLBIGBINARY);
  6196. WriteUnsignedShort(checked((ushort)metaData.MaxLength), stateObj);
  6197. break;
  6198. case SqlDbType.Bit:
  6199. stateObj.WriteByte(TdsEnums.SQLBITN);
  6200. stateObj.WriteByte(checked((byte)metaData.MaxLength));
  6201. break;
  6202. case SqlDbType.Char:
  6203. stateObj.WriteByte(TdsEnums.SQLBIGCHAR);
  6204. WriteUnsignedShort(checked((ushort)(metaData.MaxLength)), stateObj);
  6205. WriteUnsignedInt(_defaultCollation.info, stateObj); // TODO: Use metadata's collation??
  6206. stateObj.WriteByte(_defaultCollation.sortId);
  6207. break;
  6208. case SqlDbType.DateTime:
  6209. stateObj.WriteByte(TdsEnums.SQLDATETIMN);
  6210. stateObj.WriteByte(checked((byte)metaData.MaxLength));
  6211. break;
  6212. case SqlDbType.Decimal:
  6213. stateObj.WriteByte(TdsEnums.SQLNUMERICN);
  6214. stateObj.WriteByte(checked((byte)MetaType.MetaDecimal.FixedLength)); // SmiMetaData's length and actual wire format's length are different
  6215. stateObj.WriteByte(0 == metaData.Precision ? (byte)1 : metaData.Precision);
  6216. stateObj.WriteByte(metaData.Scale);
  6217. break;
  6218. case SqlDbType.Float:
  6219. stateObj.WriteByte(TdsEnums.SQLFLTN);
  6220. stateObj.WriteByte(checked((byte)metaData.MaxLength));
  6221. break;
  6222. case SqlDbType.Image:
  6223. stateObj.WriteByte(TdsEnums.SQLBIGVARBINARY);
  6224. WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj);
  6225. break;
  6226. case SqlDbType.Int:
  6227. stateObj.WriteByte(TdsEnums.SQLINTN);
  6228. stateObj.WriteByte(checked((byte)metaData.MaxLength));
  6229. break;
  6230. case SqlDbType.Money:
  6231. stateObj.WriteByte(TdsEnums.SQLMONEYN);
  6232. stateObj.WriteByte(checked((byte)metaData.MaxLength));
  6233. break;
  6234. case SqlDbType.NChar:
  6235. stateObj.WriteByte(TdsEnums.SQLNCHAR);
  6236. WriteUnsignedShort(checked((ushort)(metaData.MaxLength*2)), stateObj);
  6237. WriteUnsignedInt(_defaultCollation.info, stateObj); // TODO: Use metadata's collation??
  6238. stateObj.WriteByte(_defaultCollation.sortId);
  6239. break;
  6240. case SqlDbType.NText:
  6241. stateObj.WriteByte(TdsEnums.SQLNVARCHAR);
  6242. WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj);
  6243. WriteUnsignedInt(_defaultCollation.info, stateObj); // TODO: Use metadata's collation??
  6244. stateObj.WriteByte(_defaultCollation.sortId);
  6245. break;
  6246. case SqlDbType.NVarChar:
  6247. stateObj.WriteByte(TdsEnums.SQLNVARCHAR);
  6248. if (MSS.SmiMetaData.UnlimitedMaxLengthIndicator == metaData.MaxLength) {
  6249. WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj);
  6250. }
  6251. else {
  6252. WriteUnsignedShort(checked((ushort)(metaData.MaxLength*2)), stateObj);
  6253. }
  6254. WriteUnsignedInt(_defaultCollation.info, stateObj); // TODO: Use metadata's collation??
  6255. stateObj.WriteByte(_defaultCollation.sortId);
  6256. break;
  6257. case SqlDbType.Real:
  6258. stateObj.WriteByte(TdsEnums.SQLFLTN);
  6259. stateObj.WriteByte(checked((byte)metaData.MaxLength));
  6260. break;
  6261. case SqlDbType.UniqueIdentifier:
  6262. stateObj.WriteByte(TdsEnums.SQLUNIQUEID);
  6263. stateObj.WriteByte(checked((byte)metaData.MaxLength));
  6264. break;
  6265. case SqlDbType.SmallDateTime:
  6266. stateObj.WriteByte(TdsEnums.SQLDATETIMN);
  6267. stateObj.WriteByte(checked((byte)metaData.MaxLength));
  6268. break;
  6269. case SqlDbType.SmallInt:
  6270. stateObj.WriteByte(TdsEnums.SQLINTN);
  6271. stateObj.WriteByte(checked((byte)metaData.MaxLength));
  6272. break;
  6273. case SqlDbType.SmallMoney:
  6274. stateObj.WriteByte(TdsEnums.SQLMONEYN);
  6275. stateObj.WriteByte(checked((byte)metaData.MaxLength));
  6276. break;
  6277. case SqlDbType.Text:
  6278. stateObj.WriteByte(TdsEnums.SQLBIGVARCHAR);
  6279. WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj);
  6280. WriteUnsignedInt(_defaultCollation.info, stateObj); // TODO: Use metadata's collation??
  6281. stateObj.WriteByte(_defaultCollation.sortId);
  6282. break;
  6283. case SqlDbType.Timestamp:
  6284. stateObj.WriteByte(TdsEnums.SQLBIGBINARY);
  6285. WriteShort(checked((int)metaData.MaxLength), stateObj);
  6286. break;
  6287. case SqlDbType.TinyInt:
  6288. stateObj.WriteByte(TdsEnums.SQLINTN);
  6289. stateObj.WriteByte(checked((byte)metaData.MaxLength));
  6290. break;
  6291. case SqlDbType.VarBinary:
  6292. stateObj.WriteByte(TdsEnums.SQLBIGVARBINARY);
  6293. WriteUnsignedShort(unchecked((ushort)metaData.MaxLength), stateObj);
  6294. break;
  6295. case SqlDbType.VarChar:
  6296. stateObj.WriteByte(TdsEnums.SQLBIGVARCHAR);
  6297. WriteUnsignedShort(unchecked((ushort)metaData.MaxLength), stateObj);
  6298. WriteUnsignedInt(_defaultCollation.info, stateObj); // TODO: Use metadata's collation??
  6299. stateObj.WriteByte(_defaultCollation.sortId);
  6300. break;
  6301. case SqlDbType.Variant:
  6302. stateObj.WriteByte(TdsEnums.SQLVARIANT);
  6303. WriteInt(checked((int)metaData.MaxLength), stateObj);
  6304. break;
  6305. case SqlDbType.Xml:
  6306. stateObj.WriteByte(TdsEnums.SQLXMLTYPE);
  6307. // Is there a schema
  6308. if (ADP.IsEmpty(metaData.TypeSpecificNamePart1) && ADP.IsEmpty(metaData.TypeSpecificNamePart2) &&
  6309. ADP.IsEmpty(metaData.TypeSpecificNamePart3)) {
  6310. stateObj.WriteByte(0); // schema not present
  6311. }
  6312. else {
  6313. stateObj.WriteByte(1); // schema present
  6314. WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj);
  6315. WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj);
  6316. WriteIdentifierWithShortLength(metaData.TypeSpecificNamePart3, stateObj);
  6317. }
  6318. break;
  6319. case SqlDbType.Udt:
  6320. stateObj.WriteByte(TdsEnums.SQLUDT);
  6321. WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj);
  6322. WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj);
  6323. WriteIdentifier(metaData.TypeSpecificNamePart3, stateObj);
  6324. break;
  6325. case SqlDbType.Structured:
  6326. if (metaData.IsMultiValued) {
  6327. WriteTvpTypeInfo(metaData, stateObj);
  6328. }
  6329. else {
  6330. Debug.Assert(false, "SUDTs not yet supported.");
  6331. }
  6332. break;
  6333. case SqlDbType.Date:
  6334. stateObj.WriteByte(TdsEnums.SQLDATE);
  6335. break;
  6336. case SqlDbType.Time:
  6337. stateObj.WriteByte(TdsEnums.SQLTIME);
  6338. stateObj.WriteByte(metaData.Scale);
  6339. break;
  6340. case SqlDbType.DateTime2:
  6341. stateObj.WriteByte(TdsEnums.SQLDATETIME2);
  6342. stateObj.WriteByte(metaData.Scale);
  6343. break;
  6344. case SqlDbType.DateTimeOffset:
  6345. stateObj.WriteByte(TdsEnums.SQLDATETIMEOFFSET);
  6346. stateObj.WriteByte(metaData.Scale);
  6347. break;
  6348. default:
  6349. Debug.Assert(false, "Unknown SqlDbType should have been caught earlier!");
  6350. break;
  6351. }
  6352. }
  6353. private void WriteTvpTypeInfo(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) {
  6354. Debug.Assert(SqlDbType.Structured == metaData.SqlDbType && metaData.IsMultiValued,
  6355. "Invalid metadata for TVPs. Type=" + metaData.SqlDbType);
  6356. // Type token
  6357. stateObj.WriteByte((byte)TdsEnums.SQLTABLE);
  6358. // 3-part name (DB, Schema, TypeName)
  6359. WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj);
  6360. WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj);
  6361. WriteIdentifier(metaData.TypeSpecificNamePart3, stateObj);
  6362. // TVP_COLMETADATA
  6363. if (0 == metaData.FieldMetaData.Count) {
  6364. WriteUnsignedShort((ushort)TdsEnums.TVP_NOMETADATA_TOKEN, stateObj);
  6365. }
  6366. else {
  6367. // COUNT of columns
  6368. WriteUnsignedShort(checked((ushort) metaData.FieldMetaData.Count), stateObj);
  6369. // TvpColumnMetaData for each column (look for defaults in this loop
  6370. MSS.SmiDefaultFieldsProperty defaults = (MSS.SmiDefaultFieldsProperty) metaData.ExtendedProperties[MSS.SmiPropertySelector.DefaultFields];
  6371. for(int i=0; i<metaData.FieldMetaData.Count; i++) {
  6372. WriteTvpColumnMetaData(metaData.FieldMetaData[i], defaults[i], stateObj);
  6373. }
  6374. // optional OrderUnique metadata
  6375. WriteTvpOrderUnique(metaData, stateObj);
  6376. }
  6377. // END of optional metadata
  6378. stateObj.WriteByte(TdsEnums.TVP_END_TOKEN);
  6379. }
  6380. // Write a single TvpColumnMetaData stream to the server
  6381. private void WriteTvpColumnMetaData(MSS.SmiExtendedMetaData md, bool isDefault, TdsParserStateObject stateObj) {
  6382. // User Type
  6383. if (SqlDbType.Timestamp == md.SqlDbType) {
  6384. WriteUnsignedInt(TdsEnums.SQLTIMESTAMP, stateObj);
  6385. } else {
  6386. WriteUnsignedInt(0, stateObj);
  6387. }
  6388. // Flags
  6389. ushort status = TdsEnums.Nullable;
  6390. if (isDefault) {
  6391. status |= TdsEnums.TVP_DEFAULT_COLUMN;
  6392. }
  6393. WriteUnsignedShort(status, stateObj);
  6394. // Type info
  6395. WriteSmiTypeInfo(md, stateObj);
  6396. // Column name
  6397. // per spec, "ColName is never sent to server or client for TVP, it is required within a TVP to be zero length."
  6398. WriteIdentifier(null, stateObj);
  6399. }
  6400. // temporary-results structure used only by WriteTvpOrderUnique
  6401. // use class to avoid List<T>'s per-struct-instantiated memory costs.
  6402. private class TdsOrderUnique {
  6403. internal short ColumnOrdinal;
  6404. internal byte Flags;
  6405. internal TdsOrderUnique(short ordinal, byte flags) {
  6406. ColumnOrdinal = ordinal;
  6407. Flags = flags;
  6408. }
  6409. }
  6410. private void WriteTvpOrderUnique(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) {
  6411. // TVP_ORDER_UNIQUE token (uniqueness and sort order)
  6412. // Merge order and unique keys into a single token stream
  6413. MSS.SmiOrderProperty orderProperty = (MSS.SmiOrderProperty) metaData.ExtendedProperties[MSS.SmiPropertySelector.SortOrder];
  6414. MSS.SmiUniqueKeyProperty uniqueKeyProperty = (MSS.SmiUniqueKeyProperty) metaData.ExtendedProperties[MSS.SmiPropertySelector.UniqueKey];
  6415. // Build list from
  6416. List<TdsOrderUnique> columnList = new List<TdsOrderUnique>(metaData.FieldMetaData.Count);
  6417. for(int i=0; i<metaData.FieldMetaData.Count; i++) {
  6418. // Add appropriate SortOrder flag
  6419. byte flags = 0;
  6420. MSS.SmiOrderProperty.SmiColumnOrder columnOrder = orderProperty[i];
  6421. if (SortOrder.Ascending == columnOrder.Order) {
  6422. flags = TdsEnums.TVP_ORDERASC_FLAG;
  6423. }
  6424. else if (SortOrder.Descending == columnOrder.Order) {
  6425. flags = TdsEnums.TVP_ORDERDESC_FLAG;
  6426. }
  6427. // Add unique key flage if appropriate
  6428. if (uniqueKeyProperty[i]) {
  6429. flags |= TdsEnums.TVP_UNIQUE_FLAG;
  6430. }
  6431. // Remember this column if any flags were set
  6432. if (0 != flags) {
  6433. columnList.Add(new TdsOrderUnique(checked((short)(i+1)), flags));
  6434. }
  6435. }
  6436. // Write flagged columns to wire...
  6437. if (0 < columnList.Count) {
  6438. stateObj.WriteByte(TdsEnums.TVP_ORDER_UNIQUE_TOKEN);
  6439. WriteShort(columnList.Count, stateObj);
  6440. foreach(TdsOrderUnique column in columnList) {
  6441. WriteShort(column.ColumnOrdinal, stateObj);
  6442. stateObj.WriteByte(column.Flags);
  6443. }
  6444. }
  6445. }
  6446. internal Task WriteBulkCopyDone(TdsParserStateObject stateObj) {
  6447. // Write DONE packet
  6448. //
  6449. if (!(State == TdsParserState.OpenNotLoggedIn || State == TdsParserState.OpenLoggedIn)) {
  6450. throw ADP.ClosedConnectionError();
  6451. }
  6452. stateObj.WriteByte(TdsEnums.SQLDONE);
  6453. WriteShort(0, stateObj);
  6454. WriteShort(0, stateObj);
  6455. WriteInt(0, stateObj);
  6456. stateObj._pendingData = true;
  6457. stateObj._messageStatus = 0;
  6458. return stateObj.WritePacket(TdsEnums.HARDFLUSH);
  6459. }
  6460. internal void WriteBulkCopyMetaData(_SqlMetaDataSet metadataCollection, int count, TdsParserStateObject stateObj) {
  6461. if (!(State == TdsParserState.OpenNotLoggedIn || State == TdsParserState.OpenLoggedIn)) {
  6462. throw ADP.ClosedConnectionError();
  6463. }
  6464. stateObj.WriteByte(TdsEnums.SQLCOLMETADATA);
  6465. WriteShort(count, stateObj);
  6466. for (int i = 0; i < metadataCollection.Length; i++) {
  6467. if (metadataCollection[i] != null) {
  6468. _SqlMetaData md = metadataCollection[i];
  6469. // read user type - 4 bytes Yukon, 2 backwards
  6470. if (IsYukonOrNewer) {
  6471. WriteInt(0x0, stateObj);
  6472. }
  6473. else {
  6474. WriteShort(0x0000, stateObj);
  6475. }
  6476. UInt16 flags;
  6477. flags = (UInt16)(md.updatability << 2);
  6478. flags |= (UInt16)(md.isNullable ? (UInt16)TdsEnums.Nullable : (UInt16)0);
  6479. flags |= (UInt16)(md.isIdentity ? (UInt16)TdsEnums.Identity : (UInt16)0);
  6480. WriteShort(flags, stateObj); // write the flags
  6481. // todo:
  6482. // for xml WriteTokenLength results in a no-op
  6483. // discuss this with blaine ...
  6484. // ([....]) xml datatype does not have token length in its metadata. So it should be a noop.
  6485. switch (md.type) {
  6486. case SqlDbType.Decimal:
  6487. stateObj.WriteByte(md.tdsType);
  6488. WriteTokenLength(md.tdsType, md.length, stateObj);
  6489. stateObj.WriteByte(md.precision);
  6490. stateObj.WriteByte(md.scale);
  6491. break;
  6492. case SqlDbType.Xml:
  6493. //
  6494. stateObj.WriteByteArray(s_xmlMetadataSubstituteSequence, s_xmlMetadataSubstituteSequence.Length, 0);
  6495. break;
  6496. case SqlDbType.Udt:
  6497. stateObj.WriteByte(TdsEnums.SQLBIGVARBINARY);
  6498. WriteTokenLength(TdsEnums.SQLBIGVARBINARY, md.length, stateObj);
  6499. break;
  6500. case SqlDbType.Date:
  6501. stateObj.WriteByte(md.tdsType);
  6502. break;
  6503. case SqlDbType.Time:
  6504. case SqlDbType.DateTime2:
  6505. case SqlDbType.DateTimeOffset:
  6506. stateObj.WriteByte(md.tdsType);
  6507. stateObj.WriteByte(md.scale);
  6508. break;
  6509. default:
  6510. stateObj.WriteByte(md.tdsType);
  6511. WriteTokenLength(md.tdsType, md.length, stateObj);
  6512. if (md.metaType.IsCharType && _isShiloh) {
  6513. WriteUnsignedInt(md.collation.info, stateObj);
  6514. stateObj.WriteByte(md.collation.sortId);
  6515. }
  6516. break;
  6517. }
  6518. if (md.metaType.IsLong && !md.metaType.IsPlp) {
  6519. WriteShort(md.tableName.Length, stateObj);
  6520. WriteString(md.tableName, stateObj);
  6521. }
  6522. stateObj.WriteByte((byte)md.column.Length);
  6523. WriteString(md.column, stateObj);
  6524. }
  6525. } // end for loop
  6526. }
  6527. internal Task WriteBulkCopyValue(object value, SqlMetaDataPriv metadata, TdsParserStateObject stateObj, bool isSqlType, bool isDataFeed, bool isNull) {
  6528. Debug.Assert(!isSqlType || value is INullable, "isSqlType is true, but value can not be type cast to an INullable");
  6529. Debug.Assert(!isDataFeed ^ value is DataFeed, "Incorrect value for isDataFeed");
  6530. Encoding saveEncoding = _defaultEncoding;
  6531. SqlCollation saveCollation = _defaultCollation;
  6532. int saveCodePage = _defaultCodePage;
  6533. int saveLCID = _defaultLCID;
  6534. Task resultTask = null;
  6535. Task internalWriteTask = null;
  6536. if (!(State == TdsParserState.OpenNotLoggedIn || State == TdsParserState.OpenLoggedIn)) {
  6537. throw ADP.ClosedConnectionError();
  6538. }
  6539. try {
  6540. if (metadata.encoding != null) {
  6541. _defaultEncoding = metadata.encoding;
  6542. }
  6543. if (metadata.collation != null) {
  6544. _defaultCollation = metadata.collation;
  6545. _defaultLCID = _defaultCollation.LCID;
  6546. }
  6547. _defaultCodePage = metadata.codePage;
  6548. MetaType metatype = metadata.metaType;
  6549. int ccb = 0;
  6550. int ccbStringBytes = 0;
  6551. if (isNull) {
  6552. // For UDT, remember we treat as binary even though it is a PLP
  6553. if (metatype.IsPlp && (metatype.NullableType != TdsEnums.SQLUDT || metatype.IsLong)) {
  6554. WriteLong(unchecked((long)TdsEnums.SQL_PLP_NULL), stateObj);
  6555. }
  6556. else if (!metatype.IsFixed && !metatype.IsLong && !metatype.IsVarTime) {
  6557. WriteShort(TdsEnums.VARNULL, stateObj);
  6558. }
  6559. else {
  6560. stateObj.WriteByte(TdsEnums.FIXEDNULL);
  6561. }
  6562. return resultTask;
  6563. }
  6564. if (!isDataFeed) {
  6565. switch (metatype.NullableType) {
  6566. case TdsEnums.SQLBIGBINARY:
  6567. case TdsEnums.SQLBIGVARBINARY:
  6568. case TdsEnums.SQLIMAGE:
  6569. case TdsEnums.SQLUDT:
  6570. ccb = (isSqlType) ? ((SqlBinary)value).Length : ((byte[])value).Length;
  6571. break;
  6572. case TdsEnums.SQLUNIQUEID:
  6573. ccb = GUID_SIZE; // that's a constant for guid
  6574. break;
  6575. case TdsEnums.SQLBIGCHAR:
  6576. case TdsEnums.SQLBIGVARCHAR:
  6577. case TdsEnums.SQLTEXT:
  6578. if (null == _defaultEncoding) {
  6579. ThrowUnsupportedCollationEncountered(null); // stateObject only when reading
  6580. }
  6581. string stringValue = null;
  6582. if (isSqlType) {
  6583. stringValue = ((SqlString)value).Value;
  6584. }
  6585. else {
  6586. stringValue = (string)value;
  6587. }
  6588. ccb = stringValue.Length;
  6589. ccbStringBytes = _defaultEncoding.GetByteCount(stringValue);
  6590. break;
  6591. case TdsEnums.SQLNCHAR:
  6592. case TdsEnums.SQLNVARCHAR:
  6593. case TdsEnums.SQLNTEXT:
  6594. ccb = ((isSqlType) ? ((SqlString)value).Value.Length : ((string)value).Length) * 2;
  6595. break;
  6596. case TdsEnums.SQLXMLTYPE:
  6597. // Value here could be string or XmlReader
  6598. if (value is XmlReader) {
  6599. value = MetaType.GetStringFromXml((XmlReader)value);
  6600. }
  6601. ccb = ((isSqlType) ? ((SqlString)value).Value.Length : ((string)value).Length) * 2;
  6602. break;
  6603. default:
  6604. ccb = metadata.length;
  6605. break;
  6606. }
  6607. }
  6608. else {
  6609. Debug.Assert(metatype.IsLong &&
  6610. ((metatype.SqlDbType == SqlDbType.VarBinary && value is StreamDataFeed) ||
  6611. ((metatype.SqlDbType == SqlDbType.VarChar || metatype.SqlDbType == SqlDbType.NVarChar) && value is TextDataFeed) ||
  6612. (metatype.SqlDbType == SqlDbType.Xml && value is XmlDataFeed)),
  6613. "Stream data feed should only be assigned to VarBinary(max), Text data feed should only be assigned to [N]VarChar(max), Xml data feed should only be assigned to XML(max)");
  6614. }
  6615. // Expected the text length in data stream for bulk copy of text, ntext, or image data.
  6616. //
  6617. if (metatype.IsLong) {
  6618. switch (metatype.SqlDbType) {
  6619. case SqlDbType.Text:
  6620. case SqlDbType.NText:
  6621. case SqlDbType.Image:
  6622. stateObj.WriteByteArray(s_longDataHeader, s_longDataHeader.Length, 0);
  6623. WriteTokenLength(metadata.tdsType, ccbStringBytes == 0 ? ccb : ccbStringBytes, stateObj);
  6624. break;
  6625. case SqlDbType.VarChar:
  6626. case SqlDbType.NVarChar:
  6627. case SqlDbType.VarBinary:
  6628. case SqlDbType.Xml:
  6629. case SqlDbType.Udt:
  6630. // plp data
  6631. WriteUnsignedLong(TdsEnums.SQL_PLP_UNKNOWNLEN, stateObj);
  6632. break;
  6633. }
  6634. }
  6635. else {
  6636. WriteTokenLength(metadata.tdsType, ccbStringBytes == 0 ? ccb : ccbStringBytes, stateObj);
  6637. }
  6638. if (isSqlType) {
  6639. internalWriteTask = WriteSqlValue(value, metatype, ccb, ccbStringBytes, 0, stateObj);
  6640. }
  6641. else if (metatype.SqlDbType != SqlDbType.Udt || metatype.IsLong) {
  6642. internalWriteTask = WriteValue(value, metatype, metadata.scale, ccb, ccbStringBytes, 0, stateObj, metadata.length, isDataFeed);
  6643. if ((internalWriteTask == null) && (_asyncWrite)) {
  6644. internalWriteTask = stateObj.WaitForAccumulatedWrites();
  6645. }
  6646. Debug.Assert(_asyncWrite || stateObj.WaitForAccumulatedWrites() == null, "Should not have accumulated writes when writing sync");
  6647. }
  6648. else {
  6649. WriteShort(ccb, stateObj);
  6650. internalWriteTask = stateObj.WriteByteArray((byte[])value, ccb, 0);
  6651. }
  6652. #if DEBUG
  6653. //In DEBUG mode, when SetAlwaysTaskOnWrite is true, we create a task. Allows us to verify async execution paths.
  6654. if (_asyncWrite && internalWriteTask == null && SqlBulkCopy.SetAlwaysTaskOnWrite == true) {
  6655. internalWriteTask = Task.FromResult<object>(null);
  6656. }
  6657. #endif
  6658. if (internalWriteTask != null) { //i.e. the write was async.
  6659. resultTask = WriteBulkCopyValueSetupContinuation(internalWriteTask, saveEncoding, saveCollation, saveCodePage, saveLCID);
  6660. }
  6661. }
  6662. finally {
  6663. if (internalWriteTask == null) {
  6664. _defaultEncoding = saveEncoding;
  6665. _defaultCollation = saveCollation;
  6666. _defaultCodePage = saveCodePage;
  6667. _defaultLCID = saveLCID;
  6668. }
  6669. }
  6670. return resultTask;
  6671. }
  6672. // This is in its own method to avoid always allocating the lambda in WriteBulkCopyValue
  6673. private Task WriteBulkCopyValueSetupContinuation(Task internalWriteTask, Encoding saveEncoding, SqlCollation saveCollation, int saveCodePage, int saveLCID) {
  6674. return internalWriteTask.ContinueWith<Task>(t => {
  6675. _defaultEncoding = saveEncoding;
  6676. _defaultCollation = saveCollation;
  6677. _defaultCodePage = saveCodePage;
  6678. _defaultLCID = saveLCID;
  6679. return t;
  6680. }, TaskScheduler.Default).Unwrap();
  6681. }
  6682. // Write mars header data, not including the mars header length
  6683. private void WriteMarsHeaderData(TdsParserStateObject stateObj, SqlInternalTransaction transaction) {
  6684. // Function to send over additional payload header data for Yukon and beyond only.
  6685. Debug.Assert(_isYukon, "WriteMarsHeaderData called on a non-Yukon server");
  6686. // These are not necessary - can have local started in distributed.
  6687. // Debug.Assert(!(null != sqlTransaction && null != distributedTransaction), "Error to have local (api started) and distributed transaction at the same time!");
  6688. // Debug.Assert(!(null != _userStartedLocalTransaction && null != distributedTransaction), "Error to have local (started outside of the api) and distributed transaction at the same time!");
  6689. // We may need to update the mars header length if mars header is changed in the future
  6690. WriteShort(TdsEnums.HEADERTYPE_MARS, stateObj);
  6691. if (null != transaction && SqlInternalTransaction.NullTransactionId != transaction.TransactionId) {
  6692. WriteLong(transaction.TransactionId, stateObj);
  6693. WriteInt(stateObj.IncrementAndObtainOpenResultCount(transaction), stateObj);
  6694. }
  6695. else {
  6696. // If no transaction, send over retained transaction descriptor (empty if none retained)
  6697. // and always 1 for result count.
  6698. WriteLong(_retainedTransactionId, stateObj);
  6699. WriteInt(stateObj.IncrementAndObtainOpenResultCount(null), stateObj);
  6700. }
  6701. }
  6702. private int GetNotificationHeaderSize(SqlNotificationRequest notificationRequest) {
  6703. if (null != notificationRequest) {
  6704. string callbackId = notificationRequest.UserData;
  6705. string service = notificationRequest.Options;
  6706. int timeout = notificationRequest.Timeout;
  6707. if (null == callbackId) {
  6708. throw ADP.ArgumentNull("CallbackId");
  6709. }
  6710. else if (UInt16.MaxValue < callbackId.Length) {
  6711. throw ADP.ArgumentOutOfRange("CallbackId");
  6712. }
  6713. if (null == service) {
  6714. throw ADP.ArgumentNull("Service");
  6715. }
  6716. else if (UInt16.MaxValue < service.Length) {
  6717. throw ADP.ArgumentOutOfRange("Service");
  6718. }
  6719. if (-1 > timeout) {
  6720. throw ADP.ArgumentOutOfRange("Timeout");
  6721. }
  6722. // Header Length (uint) (included in size) (already written to output buffer)
  6723. // Header Type (ushort)
  6724. // NotifyID Length (ushort)
  6725. // NotifyID UnicodeStream (unicode text)
  6726. // SSBDeployment Length (ushort)
  6727. // SSBDeployment UnicodeStream (unicode text)
  6728. // Timeout (uint) -- optional
  6729. // WEBDATA 102263: Don't send timeout value if it is 0
  6730. int headerLength = 4 + 2 + 2 + (callbackId.Length * 2) + 2 + (service.Length * 2);
  6731. if (timeout > 0)
  6732. headerLength += 4;
  6733. return headerLength;
  6734. }
  6735. else {
  6736. return 0;
  6737. }
  6738. }
  6739. // Write query notificaiton header data, not including the notificaiton header length
  6740. private void WriteQueryNotificationHeaderData(SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj) {
  6741. Debug.Assert(_isYukon, "WriteQueryNotificationHeaderData called on a non-Yukon server");
  6742. // We may need to update the notification header length if the header is changed in the future
  6743. Debug.Assert (null != notificationRequest, "notificaitonRequest is null");
  6744. string callbackId = notificationRequest.UserData;
  6745. string service = notificationRequest.Options;
  6746. int timeout = notificationRequest.Timeout;
  6747. // we did verification in GetNotificationHeaderSize, so just assert here.
  6748. Debug.Assert(null != callbackId, "CallbackId is null");
  6749. Debug.Assert(UInt16.MaxValue >= callbackId.Length, "CallbackId length is out of range");
  6750. Debug.Assert(null != service, "Service is null");
  6751. Debug.Assert(UInt16.MaxValue >= service.Length, "Service length is out of range");
  6752. Debug.Assert(-1 <= timeout, "Timeout");
  6753. Bid.NotificationsTrace("<sc.TdsParser.WriteQueryNotificationHeader|DEP> NotificationRequest: userData: '%ls', options: '%ls', timeout: '%d'\n", notificationRequest.UserData, notificationRequest.Options, notificationRequest.Timeout);
  6754. WriteShort(TdsEnums.HEADERTYPE_QNOTIFICATION, stateObj); // Query notifications Type
  6755. WriteShort(callbackId.Length * 2, stateObj); // Length in bytes
  6756. WriteString(callbackId, stateObj);
  6757. WriteShort(service.Length * 2, stateObj); // Length in bytes
  6758. WriteString(service, stateObj);
  6759. if (timeout > 0)
  6760. WriteInt(timeout, stateObj);
  6761. }
  6762. // Write the trace header data, not including the trace header length
  6763. private void WriteTraceHeaderData(TdsParserStateObject stateObj) {
  6764. Debug.Assert(this.IncludeTraceHeader, "WriteTraceHeaderData can only be called on a Denali or higher version server and bid trace with the control bit are on");
  6765. // We may need to update the trace header length if trace header is changed in the future
  6766. ActivityCorrelator.ActivityId actId = ActivityCorrelator.Current;
  6767. WriteShort(TdsEnums.HEADERTYPE_TRACE, stateObj); // Trace Header Type
  6768. stateObj.WriteByteArray(actId.Id.ToByteArray(), GUID_SIZE, 0); // Id (Guid)
  6769. WriteUnsignedInt(actId.Sequence, stateObj); // sequence number
  6770. Bid.Trace("<sc.TdsParser.WriteTraceHeaderData|INFO> ActivityID %ls\n", actId.ToString());
  6771. }
  6772. private void WriteRPCBatchHeaders(TdsParserStateObject stateObj, SqlNotificationRequest notificationRequest) {
  6773. Debug.Assert(_isYukon, "WriteRPCBatchHeaders can only be called on Yukon or higher version servers");
  6774. /* Header:
  6775. TotalLength - DWORD - including all headers and lengths, including itself
  6776. Each Data Session:
  6777. {
  6778. HeaderLength - DWORD - including all header length fields, including itself
  6779. HeaderType - USHORT
  6780. HeaderData
  6781. }
  6782. */
  6783. int notificationHeaderSize = GetNotificationHeaderSize(notificationRequest);
  6784. const int marsHeaderSize = 18; // 4 + 2 + 8 + 4
  6785. // Header Length (DWORD)
  6786. // Header Type (ushort)
  6787. // Trace Data Guid
  6788. // Trace Data Sequence Number (uint)
  6789. const int traceHeaderSize = 26; // 4 + 2 + GUID_SIZE + sizeof(UInt32);
  6790. // TotalLength - DWORD - including all headers and lengths, including itself
  6791. int totalHeaderLength = this.IncludeTraceHeader ? (4 + marsHeaderSize + notificationHeaderSize + traceHeaderSize) : (4 + marsHeaderSize + notificationHeaderSize);
  6792. Debug.Assert(stateObj._outBytesUsed == stateObj._outputHeaderLen, "Output bytes written before total header length");
  6793. // Write total header length
  6794. WriteInt(totalHeaderLength, stateObj);
  6795. // Write Mars header length
  6796. WriteInt(marsHeaderSize, stateObj);
  6797. // Write Mars header data
  6798. WriteMarsHeaderData(stateObj, CurrentTransaction);
  6799. if (0 != notificationHeaderSize) {
  6800. // Write Notification header length
  6801. WriteInt(notificationHeaderSize, stateObj);
  6802. // Write notificaiton header data
  6803. WriteQueryNotificationHeaderData(notificationRequest, stateObj);
  6804. }
  6805. if (IncludeTraceHeader) {
  6806. // Write trace header length
  6807. WriteInt(traceHeaderSize, stateObj);
  6808. // Write trace header data
  6809. WriteTraceHeaderData(stateObj);
  6810. }
  6811. }
  6812. //
  6813. // Reverse function of GetTokenLength
  6814. //
  6815. private void WriteTokenLength(byte token, int length, TdsParserStateObject stateObj) {
  6816. int tokenLength = 0;
  6817. Debug.Assert(token != 0, "0 length token!");
  6818. // For Plp fields, this should only be used when writing to metadata header.
  6819. // For actual data length, WriteDataLength should be used.
  6820. // For Xml fields, there is no token length field. For MAX fields it is 0xffff.
  6821. if (_isYukon) { // Handle Yukon specific exceptions
  6822. if (TdsEnums.SQLUDT == token) {
  6823. tokenLength = 8;
  6824. }
  6825. else if (token == TdsEnums.SQLXMLTYPE) {
  6826. tokenLength = 8;
  6827. }
  6828. }
  6829. if (tokenLength == 0) {
  6830. switch (token & TdsEnums.SQLLenMask) {
  6831. case TdsEnums.SQLFixedLen:
  6832. Debug.Assert(length == 0x01 << ((token & 0x0c) >> 2), "length does not match encoded length in token");
  6833. tokenLength = 0;
  6834. break;
  6835. case TdsEnums.SQLZeroLen:
  6836. tokenLength = 0;
  6837. break;
  6838. case TdsEnums.SQLVarLen:
  6839. case TdsEnums.SQLVarCnt:
  6840. if (0 != (token & 0x80))
  6841. tokenLength = 2;
  6842. else if (0 == (token & 0x0c))
  6843. //
  6844. tokenLength = 4;
  6845. else
  6846. tokenLength = 1;
  6847. break;
  6848. default:
  6849. Debug.Assert(false, "Unknown token length!");
  6850. break;
  6851. }
  6852. switch (tokenLength) {
  6853. case 1:
  6854. stateObj.WriteByte((byte)length);
  6855. break;
  6856. case 2:
  6857. WriteShort(length, stateObj);
  6858. break;
  6859. case 4:
  6860. WriteInt(length, stateObj);
  6861. break;
  6862. case 8:
  6863. // In the metadata case we write 0xffff for partial length prefixed types.
  6864. // For actual data length preceding data, WriteDataLength should be used.
  6865. WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
  6866. break;
  6867. } // end switch
  6868. }
  6869. }
  6870. // Returns true if BOM byte mark is needed for an XML value
  6871. private bool IsBOMNeeded(MetaType type, object value) {
  6872. if (type.NullableType == TdsEnums.SQLXMLTYPE) {
  6873. Type currentType = value.GetType();
  6874. if(currentType == typeof(SqlString)) {
  6875. if (!((SqlString)value).IsNull && ((((SqlString)value).Value).Length > 0)) {
  6876. if ((((SqlString)value).Value[0] & 0xff) != 0xff)
  6877. return true;
  6878. }
  6879. }
  6880. else if ((currentType == typeof(String)) && (((String)value).Length > 0)) {
  6881. if ((value != null) && (((String)value)[0] & 0xff) != 0xff)
  6882. return true;
  6883. }
  6884. else if (currentType == typeof(SqlXml)) {
  6885. if (!((SqlXml)value).IsNull)
  6886. return true;
  6887. }
  6888. else if (currentType == typeof(XmlDataFeed)) {
  6889. return true; // Values will eventually converted to unicode string here
  6890. }
  6891. }
  6892. return false;
  6893. }
  6894. private Task GetTerminationTask(Task unterminatedWriteTask, object value, MetaType type, int actualLength, TdsParserStateObject stateObj, bool isDataFeed) {
  6895. if (type.IsPlp && ((actualLength > 0) || isDataFeed)) {
  6896. if (unterminatedWriteTask == null) {
  6897. WriteInt(0, stateObj);
  6898. return null;
  6899. }
  6900. else {
  6901. return AsyncHelper.CreateContinuationTask<int, TdsParserStateObject>(unterminatedWriteTask,
  6902. WriteInt, 0, stateObj,
  6903. connectionToDoom: _connHandler);
  6904. }
  6905. }
  6906. else {
  6907. return unterminatedWriteTask;
  6908. }
  6909. }
  6910. private Task WriteSqlValue(object value, MetaType type, int actualLength, int codePageByteSize, int offset, TdsParserStateObject stateObj) {
  6911. return GetTerminationTask(
  6912. WriteUnterminatedSqlValue(value, type, actualLength, codePageByteSize, offset, stateObj),
  6913. value, type, actualLength, stateObj, false);
  6914. }
  6915. // For MAX types, this method can only write everything in one big chunk. If multiple
  6916. // chunk writes needed, please use WritePlpBytes/WritePlpChars
  6917. private Task WriteUnterminatedSqlValue(object value, MetaType type, int actualLength, int codePageByteSize, int offset, TdsParserStateObject stateObj) {
  6918. Debug.Assert(((type.NullableType == TdsEnums.SQLXMLTYPE) ||
  6919. (value is INullable && !((INullable)value).IsNull)),
  6920. "unexpected null SqlType!");
  6921. // parameters are always sent over as BIG or N types
  6922. switch (type.NullableType) {
  6923. case TdsEnums.SQLFLTN:
  6924. if (type.FixedLength == 4)
  6925. WriteFloat(((SqlSingle)value).Value, stateObj);
  6926. else {
  6927. Debug.Assert(type.FixedLength == 8, "Invalid length for SqlDouble type!");
  6928. WriteDouble(((SqlDouble)value).Value, stateObj);
  6929. }
  6930. break;
  6931. case TdsEnums.SQLBIGBINARY:
  6932. case TdsEnums.SQLBIGVARBINARY:
  6933. case TdsEnums.SQLIMAGE:
  6934. {
  6935. if (type.IsPlp) {
  6936. WriteInt(actualLength, stateObj); // chunk length
  6937. }
  6938. if (value is SqlBinary) {
  6939. return stateObj.WriteByteArray(((SqlBinary)value).Value, actualLength, offset, canAccumulate:false);
  6940. }
  6941. else {
  6942. Debug.Assert(value is SqlBytes);
  6943. return stateObj.WriteByteArray(((SqlBytes)value).Value, actualLength, offset, canAccumulate:false);
  6944. }
  6945. }
  6946. case TdsEnums.SQLUNIQUEID:
  6947. {
  6948. byte[] b = ((SqlGuid)value).ToByteArray();
  6949. Debug.Assert((actualLength == b.Length) && (actualLength == 16), "Invalid length for guid type in com+ object");
  6950. stateObj.WriteByteArray(b, actualLength, 0);
  6951. break;
  6952. }
  6953. case TdsEnums.SQLBITN:
  6954. {
  6955. Debug.Assert(type.FixedLength == 1, "Invalid length for SqlBoolean type");
  6956. if (((SqlBoolean)value).Value == true)
  6957. stateObj.WriteByte(1);
  6958. else
  6959. stateObj.WriteByte(0);
  6960. break;
  6961. }
  6962. case TdsEnums.SQLINTN:
  6963. if (type.FixedLength == 1)
  6964. stateObj.WriteByte(((SqlByte)value).Value);
  6965. else
  6966. if (type.FixedLength == 2)
  6967. WriteShort(((SqlInt16)value).Value, stateObj);
  6968. else
  6969. if (type.FixedLength == 4)
  6970. WriteInt(((SqlInt32)value).Value, stateObj);
  6971. else {
  6972. Debug.Assert(type.FixedLength == 8, "invalid length for SqlIntN type: " + type.FixedLength.ToString(CultureInfo.InvariantCulture));
  6973. WriteLong(((SqlInt64)value).Value, stateObj);
  6974. }
  6975. break;
  6976. case TdsEnums.SQLBIGCHAR:
  6977. case TdsEnums.SQLBIGVARCHAR:
  6978. case TdsEnums.SQLTEXT:
  6979. if (type.IsPlp) {
  6980. WriteInt(codePageByteSize, stateObj); // chunk length
  6981. }
  6982. if (value is SqlChars) {
  6983. String sch = new String(((SqlChars)value).Value);
  6984. return WriteEncodingChar(sch, actualLength, offset, _defaultEncoding, stateObj, canAccumulate:false);
  6985. }
  6986. else {
  6987. Debug.Assert(value is SqlString);
  6988. return WriteEncodingChar(((SqlString)value).Value, actualLength, offset, _defaultEncoding, stateObj, canAccumulate:false);
  6989. }
  6990. case TdsEnums.SQLNCHAR:
  6991. case TdsEnums.SQLNVARCHAR:
  6992. case TdsEnums.SQLNTEXT:
  6993. case TdsEnums.SQLXMLTYPE:
  6994. if (type.IsPlp) {
  6995. if(IsBOMNeeded(type, value)) {
  6996. WriteInt(actualLength+2, stateObj); // chunk length
  6997. WriteShort(TdsEnums.XMLUNICODEBOM , stateObj);
  6998. } else {
  6999. WriteInt(actualLength, stateObj); // chunk length
  7000. }
  7001. }
  7002. // convert to cchars instead of cbytes
  7003. // Xml type is already converted to string through GetCoercedValue
  7004. if (actualLength != 0)
  7005. actualLength >>= 1;
  7006. if (value is SqlChars) {
  7007. return WriteCharArray(((SqlChars)value).Value, actualLength, offset, stateObj, canAccumulate:false);
  7008. }
  7009. else {
  7010. Debug.Assert(value is SqlString);
  7011. return WriteString(((SqlString)value).Value, actualLength, offset, stateObj, canAccumulate:false);
  7012. }
  7013. case TdsEnums.SQLNUMERICN:
  7014. Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes");
  7015. WriteSqlDecimal((SqlDecimal)value, stateObj);
  7016. break;
  7017. case TdsEnums.SQLDATETIMN:
  7018. SqlDateTime dt = (SqlDateTime)value;
  7019. if (type.FixedLength == 4) {
  7020. if (0 > dt.DayTicks || dt.DayTicks > UInt16.MaxValue)
  7021. throw SQL.SmallDateTimeOverflow(dt.ToString());
  7022. WriteShort(dt.DayTicks, stateObj);
  7023. WriteShort(dt.TimeTicks / SqlDateTime.SQLTicksPerMinute, stateObj);
  7024. }
  7025. else {
  7026. WriteInt(dt.DayTicks, stateObj);
  7027. WriteInt(dt.TimeTicks, stateObj);
  7028. }
  7029. break;
  7030. case TdsEnums.SQLMONEYN:
  7031. {
  7032. WriteSqlMoney((SqlMoney)value, type.FixedLength, stateObj);
  7033. break;
  7034. }
  7035. case TdsEnums.SQLUDT:
  7036. Debug.Assert(false, "Called WriteSqlValue on UDT param.Should have already been handled");
  7037. throw SQL.UDTUnexpectedResult(value.GetType().AssemblyQualifiedName);
  7038. default:
  7039. Debug.Assert(false, "Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null));
  7040. break;
  7041. } // switch
  7042. // return point for accumualated writes, note: non-accumulated writes returned from their case statements
  7043. return null;
  7044. }
  7045. private class TdsOutputStream : Stream {
  7046. TdsParser _parser;
  7047. TdsParserStateObject _stateObj;
  7048. byte[] _preambleToStrip;
  7049. public TdsOutputStream(TdsParser parser, TdsParserStateObject stateObj, byte[] preambleToStrip) {
  7050. _parser = parser;
  7051. _stateObj = stateObj;
  7052. _preambleToStrip = preambleToStrip;
  7053. }
  7054. public override bool CanRead {
  7055. get { return false; }
  7056. }
  7057. public override bool CanSeek {
  7058. get { return false; }
  7059. }
  7060. public override bool CanWrite {
  7061. get { return true; }
  7062. }
  7063. public override void Flush() {
  7064. // NOOP
  7065. }
  7066. public override long Length {
  7067. get { throw new NotSupportedException(); }
  7068. }
  7069. public override long Position {
  7070. get {
  7071. throw new NotSupportedException();
  7072. }
  7073. set {
  7074. throw new NotSupportedException();
  7075. }
  7076. }
  7077. public override int Read(byte[] buffer, int offset, int count) {
  7078. throw new NotSupportedException();
  7079. }
  7080. public override long Seek(long offset, SeekOrigin origin) {
  7081. throw new NotSupportedException();
  7082. }
  7083. public override void SetLength(long value) {
  7084. throw new NotSupportedException();
  7085. }
  7086. private void StripPreamble(byte[] buffer, ref int offset, ref int count) {
  7087. if (_preambleToStrip != null && count >= _preambleToStrip.Length) {
  7088. for (int idx = 0; idx < _preambleToStrip.Length; idx++) {
  7089. if (_preambleToStrip[idx] != buffer[idx]) {
  7090. _preambleToStrip = null;
  7091. return;
  7092. }
  7093. }
  7094. offset += _preambleToStrip.Length;
  7095. count -= _preambleToStrip.Length;
  7096. }
  7097. _preambleToStrip = null;
  7098. }
  7099. public override void Write(byte[] buffer, int offset, int count) {
  7100. Debug.Assert(!_parser._asyncWrite);
  7101. ValidateWriteParameters(buffer, offset, count);
  7102. StripPreamble(buffer, ref offset, ref count);
  7103. if (count > 0) {
  7104. _parser.WriteInt(count, _stateObj); // write length of chunk
  7105. _stateObj.WriteByteArray(buffer, count, offset);
  7106. }
  7107. }
  7108. public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) {
  7109. Debug.Assert(_parser._asyncWrite);
  7110. ValidateWriteParameters(buffer, offset, count);
  7111. StripPreamble(buffer, ref offset, ref count);
  7112. RuntimeHelpers.PrepareConstrainedRegions();
  7113. try {
  7114. #if DEBUG
  7115. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  7116. RuntimeHelpers.PrepareConstrainedRegions();
  7117. try {
  7118. tdsReliabilitySection.Start();
  7119. #else
  7120. {
  7121. #endif //DEBUG
  7122. Task task = null;
  7123. if (count > 0) {
  7124. _parser.WriteInt(count, _stateObj); // write length of chunk
  7125. task = _stateObj.WriteByteArray(buffer, count, offset, canAccumulate: false);
  7126. }
  7127. if (task == null) {
  7128. return CompletedTask;
  7129. }
  7130. else {
  7131. return task;
  7132. }
  7133. }
  7134. #if DEBUG
  7135. finally {
  7136. tdsReliabilitySection.Stop();
  7137. }
  7138. #endif //DEBUG
  7139. }
  7140. catch (System.OutOfMemoryException) {
  7141. _parser._connHandler.DoomThisConnection();
  7142. throw;
  7143. }
  7144. catch (System.StackOverflowException) {
  7145. _parser._connHandler.DoomThisConnection();
  7146. throw;
  7147. }
  7148. catch (System.Threading.ThreadAbortException) {
  7149. _parser._connHandler.DoomThisConnection();
  7150. throw;
  7151. }
  7152. }
  7153. internal static void ValidateWriteParameters(byte[] buffer, int offset, int count) {
  7154. if (buffer == null) {
  7155. throw ADP.ArgumentNull(ADP.ParameterBuffer);
  7156. }
  7157. if (offset < 0) {
  7158. throw ADP.ArgumentOutOfRange(ADP.ParameterOffset);
  7159. }
  7160. if (count < 0) {
  7161. throw ADP.ArgumentOutOfRange(ADP.ParameterCount);
  7162. }
  7163. try {
  7164. if (checked(offset + count) > buffer.Length) {
  7165. throw ExceptionBuilder.InvalidOffsetLength();
  7166. }
  7167. }
  7168. catch (OverflowException) {
  7169. // If we've overflowed when adding offset and count, then they never would have fit into buffer anyway
  7170. throw ExceptionBuilder.InvalidOffsetLength();
  7171. }
  7172. }
  7173. }
  7174. private class ConstrainedTextWriter : TextWriter {
  7175. TextWriter _next;
  7176. int _size;
  7177. int _written;
  7178. public ConstrainedTextWriter(TextWriter next, int size) {
  7179. _next = next;
  7180. _size = size;
  7181. _written = 0;
  7182. if (_size < 1) {
  7183. _size = int.MaxValue;
  7184. }
  7185. }
  7186. public bool IsComplete {
  7187. get {
  7188. return _size > 0 && _written >= _size;
  7189. }
  7190. }
  7191. public override Encoding Encoding {
  7192. get { return _next.Encoding; }
  7193. }
  7194. public override void Flush() {
  7195. _next.Flush();
  7196. }
  7197. public override Task FlushAsync() {
  7198. return _next.FlushAsync();
  7199. }
  7200. public override void Write(char value) {
  7201. if (_written < _size) {
  7202. _next.Write(value);
  7203. _written++;
  7204. }
  7205. Debug.Assert(_size < 0 || _written <= _size, string.Format("Length of data written exceeds specified length. Written: {0}, specified: {1}", _written, _size));
  7206. }
  7207. public override void Write(char[] buffer, int index, int count) {
  7208. ValidateWriteParameters(buffer, index, count);
  7209. Debug.Assert(_size >= _written);
  7210. count = Math.Min(_size - _written, count);
  7211. if (count > 0) {
  7212. _next.Write(buffer, index, count);
  7213. }
  7214. _written += count;
  7215. }
  7216. public override Task WriteAsync(char value) {
  7217. if (_written < _size) {
  7218. _written++;
  7219. return _next.WriteAsync(value);
  7220. }
  7221. return CompletedTask;
  7222. }
  7223. public override Task WriteAsync(char[] buffer, int index, int count) {
  7224. ValidateWriteParameters(buffer, index, count);
  7225. Debug.Assert(_size >= _written);
  7226. count = Math.Min(_size - _written, count);
  7227. if (count > 0) {
  7228. _written += count;
  7229. return _next.WriteAsync(buffer, index, count);
  7230. }
  7231. return CompletedTask;
  7232. }
  7233. public override Task WriteAsync(string value) {
  7234. return WriteAsync(value.ToCharArray());
  7235. }
  7236. internal static void ValidateWriteParameters(char[] buffer, int offset, int count) {
  7237. if (buffer == null) {
  7238. throw ADP.ArgumentNull(ADP.ParameterBuffer);
  7239. }
  7240. if (offset < 0) {
  7241. throw ADP.ArgumentOutOfRange(ADP.ParameterOffset);
  7242. }
  7243. if (count < 0) {
  7244. throw ADP.ArgumentOutOfRange(ADP.ParameterCount);
  7245. }
  7246. try {
  7247. if (checked(offset + count) > buffer.Length) {
  7248. throw ExceptionBuilder.InvalidOffsetLength();
  7249. }
  7250. }
  7251. catch (OverflowException) {
  7252. // If we've overflowed when adding offset and count, then they never would have fit into buffer anyway
  7253. throw ExceptionBuilder.InvalidOffsetLength();
  7254. }
  7255. }
  7256. }
  7257. private async Task WriteXmlFeed(XmlDataFeed feed, TdsParserStateObject stateObj, bool needBom, Encoding encoding, int size) {
  7258. byte[] preambleToSkip = null;
  7259. if (!needBom) {
  7260. preambleToSkip = encoding.GetPreamble();
  7261. }
  7262. ConstrainedTextWriter writer = new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, preambleToSkip), encoding), size);
  7263. XmlWriterSettings writerSettings = new XmlWriterSettings();
  7264. writerSettings.CloseOutput = false; // don't close the memory stream
  7265. writerSettings.ConformanceLevel = ConformanceLevel.Fragment;
  7266. if (_asyncWrite) {
  7267. writerSettings.Async = true;
  7268. }
  7269. XmlWriter ww = XmlWriter.Create(writer, writerSettings);
  7270. if (feed._source.ReadState == ReadState.Initial) {
  7271. feed._source.Read();
  7272. }
  7273. while (!feed._source.EOF && !writer.IsComplete) {
  7274. // We are copying nodes from a reader to a writer. This will cause the
  7275. // XmlDeclaration to be emitted despite ConformanceLevel.Fragment above.
  7276. // Therefore, we filter out the XmlDeclaration while copying.
  7277. if (feed._source.NodeType == XmlNodeType.XmlDeclaration) {
  7278. feed._source.Read();
  7279. continue;
  7280. }
  7281. if (_asyncWrite) {
  7282. await ww.WriteNodeAsync(feed._source, true).ConfigureAwait(false);
  7283. }
  7284. else {
  7285. ww.WriteNode(feed._source, true);
  7286. }
  7287. }
  7288. if (_asyncWrite) {
  7289. await ww.FlushAsync().ConfigureAwait(false);
  7290. }
  7291. else {
  7292. ww.Flush();
  7293. }
  7294. }
  7295. private async Task WriteTextFeed(TextDataFeed feed, Encoding encoding, bool needBom, TdsParserStateObject stateObj, int size) {
  7296. Debug.Assert(encoding == null || !needBom);
  7297. char[] inBuff = new char[constTextBufferSize];
  7298. encoding = encoding ?? new UnicodeEncoding(false, false);
  7299. ConstrainedTextWriter writer = new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, null), encoding), size);
  7300. if (needBom) {
  7301. if (_asyncWrite) {
  7302. await writer.WriteAsync((char)TdsEnums.XMLUNICODEBOM).ConfigureAwait(false);
  7303. }
  7304. else {
  7305. writer.Write((char)TdsEnums.XMLUNICODEBOM);
  7306. }
  7307. }
  7308. int nWritten = 0;
  7309. do {
  7310. int nRead = 0;
  7311. if (_asyncWrite) {
  7312. nRead = await feed._source.ReadBlockAsync(inBuff, 0, constTextBufferSize).ConfigureAwait(false);
  7313. }
  7314. else {
  7315. nRead = feed._source.ReadBlock(inBuff, 0, constTextBufferSize);
  7316. }
  7317. if (nRead == 0) {
  7318. break;
  7319. }
  7320. if (_asyncWrite) {
  7321. await writer.WriteAsync(inBuff, 0, nRead).ConfigureAwait(false);
  7322. }
  7323. else {
  7324. writer.Write(inBuff, 0, nRead);
  7325. }
  7326. nWritten += nRead;
  7327. } while (!writer.IsComplete);
  7328. if (_asyncWrite) {
  7329. await writer.FlushAsync().ConfigureAwait(false);
  7330. }
  7331. else {
  7332. writer.Flush();
  7333. }
  7334. }
  7335. private async Task WriteStreamFeed(StreamDataFeed feed, TdsParserStateObject stateObj, int len) {
  7336. TdsOutputStream output = new TdsOutputStream(this, stateObj, null);
  7337. byte[] buff = new byte[constBinBufferSize];
  7338. int nWritten = 0;
  7339. do {
  7340. int nRead = 0;
  7341. int readSize = constBinBufferSize;
  7342. if (len > 0 && nWritten + readSize > len) {
  7343. readSize = len - nWritten;
  7344. }
  7345. Debug.Assert(readSize >= 0);
  7346. if (_asyncWrite) {
  7347. nRead = await feed._source.ReadAsync(buff, 0, readSize).ConfigureAwait(false);
  7348. }
  7349. else {
  7350. nRead = feed._source.Read(buff, 0, readSize);
  7351. }
  7352. if (nRead == 0) {
  7353. return;
  7354. }
  7355. if (_asyncWrite) {
  7356. await output.WriteAsync(buff, 0, nRead).ConfigureAwait(false);
  7357. }
  7358. else {
  7359. output.Write(buff, 0, nRead);
  7360. }
  7361. nWritten += nRead;
  7362. } while (len <= 0 || nWritten < len);
  7363. }
  7364. private Task NullIfCompletedWriteTask(Task task) {
  7365. if (task == null) {
  7366. return null;
  7367. }
  7368. switch (task.Status) {
  7369. case TaskStatus.RanToCompletion:
  7370. return null;
  7371. case TaskStatus.Faulted:
  7372. throw task.Exception.InnerException;
  7373. case TaskStatus.Canceled:
  7374. throw SQL.OperationCancelled();
  7375. default:
  7376. return task;
  7377. }
  7378. }
  7379. private Task WriteValue(object value, MetaType type, byte scale, int actualLength, int encodingByteSize, int offset, TdsParserStateObject stateObj, int paramSize, bool isDataFeed) {
  7380. return GetTerminationTask(WriteUnterminatedValue(value, type, scale, actualLength, encodingByteSize, offset, stateObj, paramSize, isDataFeed),
  7381. value, type, actualLength, stateObj, isDataFeed);
  7382. }
  7383. // For MAX types, this method can only write everything in one big chunk. If multiple
  7384. // chunk writes needed, please use WritePlpBytes/WritePlpChars
  7385. private Task WriteUnterminatedValue(object value, MetaType type, byte scale, int actualLength, int encodingByteSize, int offset, TdsParserStateObject stateObj, int paramSize, bool isDataFeed) {
  7386. Debug.Assert((null != value) && (DBNull.Value != value), "unexpected missing or empty object");
  7387. // parameters are always sent over as BIG or N types
  7388. switch (type.NullableType) {
  7389. case TdsEnums.SQLFLTN:
  7390. if (type.FixedLength == 4)
  7391. WriteFloat((Single)value, stateObj);
  7392. else {
  7393. Debug.Assert(type.FixedLength == 8, "Invalid length for SqlDouble type!");
  7394. WriteDouble((Double)value, stateObj);
  7395. }
  7396. break;
  7397. case TdsEnums.SQLBIGBINARY:
  7398. case TdsEnums.SQLBIGVARBINARY:
  7399. case TdsEnums.SQLIMAGE:
  7400. case TdsEnums.SQLUDT: {
  7401. // An array should be in the object
  7402. Debug.Assert(isDataFeed || value is byte[], "Value should be an array of bytes");
  7403. Debug.Assert(!isDataFeed || value is StreamDataFeed, "Value should be a stream");
  7404. if (isDataFeed) {
  7405. Debug.Assert(type.IsPlp,"Stream assigned to non-PLP was not converted!");
  7406. return NullIfCompletedWriteTask(WriteStreamFeed((StreamDataFeed)value, stateObj, paramSize));
  7407. }
  7408. else {
  7409. if (type.IsPlp) {
  7410. WriteInt(actualLength, stateObj); // chunk length
  7411. }
  7412. return stateObj.WriteByteArray((byte[])value, actualLength, offset, canAccumulate: false);
  7413. }
  7414. }
  7415. case TdsEnums.SQLUNIQUEID: {
  7416. System.Guid guid = (System.Guid)value;
  7417. byte[] b = guid.ToByteArray();
  7418. Debug.Assert((actualLength == b.Length) && (actualLength == 16), "Invalid length for guid type in com+ object");
  7419. stateObj.WriteByteArray(b, actualLength, 0);
  7420. break;
  7421. }
  7422. case TdsEnums.SQLBITN: {
  7423. Debug.Assert(type.FixedLength == 1, "Invalid length for SqlBoolean type");
  7424. if ((bool)value == true)
  7425. stateObj.WriteByte(1);
  7426. else
  7427. stateObj.WriteByte(0);
  7428. break;
  7429. }
  7430. case TdsEnums.SQLINTN:
  7431. if (type.FixedLength == 1)
  7432. stateObj.WriteByte((byte)value);
  7433. else if (type.FixedLength == 2)
  7434. WriteShort((Int16)value, stateObj);
  7435. else if (type.FixedLength == 4)
  7436. WriteInt((Int32)value, stateObj);
  7437. else {
  7438. Debug.Assert(type.FixedLength == 8, "invalid length for SqlIntN type: " + type.FixedLength.ToString(CultureInfo.InvariantCulture));
  7439. WriteLong((Int64)value, stateObj);
  7440. }
  7441. break;
  7442. case TdsEnums.SQLBIGCHAR:
  7443. case TdsEnums.SQLBIGVARCHAR:
  7444. case TdsEnums.SQLTEXT: {
  7445. Debug.Assert(!isDataFeed || (value is TextDataFeed || value is XmlDataFeed), "Value must be a TextReader or XmlReader");
  7446. Debug.Assert(isDataFeed || (value is string || value is byte[]), "Value is a byte array or string");
  7447. if (isDataFeed) {
  7448. Debug.Assert(type.IsPlp, "Stream assigned to non-PLP was not converted!");
  7449. TextDataFeed tdf = value as TextDataFeed;
  7450. if (tdf == null) {
  7451. return NullIfCompletedWriteTask(WriteXmlFeed((XmlDataFeed)value, stateObj, needBom:true, encoding:_defaultEncoding, size:paramSize));
  7452. }
  7453. else {
  7454. return NullIfCompletedWriteTask(WriteTextFeed(tdf, _defaultEncoding, false, stateObj, paramSize));
  7455. }
  7456. }
  7457. else {
  7458. if (type.IsPlp) {
  7459. WriteInt(encodingByteSize, stateObj); // chunk length
  7460. }
  7461. if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
  7462. return stateObj.WriteByteArray((byte[])value, actualLength, 0, canAccumulate: false);
  7463. }
  7464. else {
  7465. return WriteEncodingChar((string)value, actualLength, offset, _defaultEncoding, stateObj, canAccumulate: false);
  7466. }
  7467. }
  7468. }
  7469. case TdsEnums.SQLNCHAR:
  7470. case TdsEnums.SQLNVARCHAR:
  7471. case TdsEnums.SQLNTEXT:
  7472. case TdsEnums.SQLXMLTYPE: {
  7473. Debug.Assert(!isDataFeed || (value is TextDataFeed || value is XmlDataFeed), "Value must be a TextReader or XmlReader");
  7474. Debug.Assert(isDataFeed || (value is string || value is byte[]), "Value is a byte array or string");
  7475. if (isDataFeed) {
  7476. Debug.Assert(type.IsPlp, "Stream assigned to non-PLP was not converted!");
  7477. TextDataFeed tdf = value as TextDataFeed;
  7478. if (tdf == null) {
  7479. return NullIfCompletedWriteTask(WriteXmlFeed((XmlDataFeed)value, stateObj, IsBOMNeeded(type, value), Encoding.Unicode, paramSize));
  7480. }
  7481. else {
  7482. return NullIfCompletedWriteTask(WriteTextFeed(tdf, null, IsBOMNeeded(type, value), stateObj, paramSize));
  7483. }
  7484. }
  7485. else {
  7486. if (type.IsPlp) {
  7487. if (IsBOMNeeded(type, value)) {
  7488. WriteInt(actualLength + 2, stateObj); // chunk length
  7489. WriteShort(TdsEnums.XMLUNICODEBOM, stateObj);
  7490. }
  7491. else {
  7492. WriteInt(actualLength, stateObj); // chunk length
  7493. }
  7494. }
  7495. if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
  7496. return stateObj.WriteByteArray((byte[])value, actualLength, 0, canAccumulate: false);
  7497. }
  7498. else {
  7499. // convert to cchars instead of cbytes
  7500. actualLength >>= 1;
  7501. return WriteString((string)value, actualLength, offset, stateObj, canAccumulate: false);
  7502. }
  7503. }
  7504. }
  7505. case TdsEnums.SQLNUMERICN:
  7506. Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes");
  7507. WriteDecimal((Decimal)value, stateObj);
  7508. break;
  7509. case TdsEnums.SQLDATETIMN:
  7510. Debug.Assert(type.FixedLength <= 0xff, "Invalid Fixed Length");
  7511. TdsDateTime dt = MetaType.FromDateTime((DateTime)value, (byte)type.FixedLength);
  7512. if (type.FixedLength == 4) {
  7513. if (0 > dt.days || dt.days > UInt16.MaxValue)
  7514. throw SQL.SmallDateTimeOverflow(MetaType.ToDateTime(dt.days, dt.time, 4).ToString(CultureInfo.InvariantCulture));
  7515. WriteShort(dt.days, stateObj);
  7516. WriteShort(dt.time, stateObj);
  7517. }
  7518. else {
  7519. WriteInt(dt.days, stateObj);
  7520. WriteInt(dt.time, stateObj);
  7521. }
  7522. break;
  7523. case TdsEnums.SQLMONEYN: {
  7524. WriteCurrency((Decimal)value, type.FixedLength, stateObj);
  7525. break;
  7526. }
  7527. case TdsEnums.SQLDATE: {
  7528. WriteDate((DateTime)value, stateObj);
  7529. break;
  7530. }
  7531. case TdsEnums.SQLTIME:
  7532. if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) {
  7533. throw SQL.TimeScaleValueOutOfRange(scale);
  7534. }
  7535. WriteTime((TimeSpan)value, scale, actualLength, stateObj);
  7536. break;
  7537. case TdsEnums.SQLDATETIME2:
  7538. if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) {
  7539. throw SQL.TimeScaleValueOutOfRange(scale);
  7540. }
  7541. WriteDateTime2((DateTime)value, scale, actualLength, stateObj);
  7542. break;
  7543. case TdsEnums.SQLDATETIMEOFFSET:
  7544. WriteDateTimeOffset((DateTimeOffset)value, scale, actualLength, stateObj);
  7545. break;
  7546. default:
  7547. Debug.Assert(false, "Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null));
  7548. break;
  7549. } // switch
  7550. // return point for accumualated writes, note: non-accumulated writes returned from their case statements
  7551. return null;
  7552. // Debug.WriteLine("value: " + value.ToString(CultureInfo.InvariantCulture));
  7553. }
  7554. //
  7555. // we always send over nullable types for parameters so we always write the varlen fields
  7556. //
  7557. internal void WriteParameterVarLen(MetaType type, int size, bool isNull, TdsParserStateObject stateObj, bool unknownLength=false) {
  7558. if (type.IsLong) { // text/image/SQLVariant have a 4 byte length, plp datatypes have 8 byte lengths
  7559. if (isNull) {
  7560. if (type.IsPlp) {
  7561. WriteLong(unchecked((long)TdsEnums.SQL_PLP_NULL), stateObj);
  7562. }
  7563. else {
  7564. WriteInt(unchecked((int)TdsEnums.VARLONGNULL), stateObj);
  7565. }
  7566. }
  7567. else if (type.NullableType == TdsEnums.SQLXMLTYPE || unknownLength) {
  7568. WriteUnsignedLong(TdsEnums.SQL_PLP_UNKNOWNLEN, stateObj);
  7569. }
  7570. else if (type.IsPlp) {
  7571. // Non-xml plp types
  7572. WriteLong((long)size, stateObj);
  7573. }
  7574. else {
  7575. WriteInt(size, stateObj);
  7576. }
  7577. }
  7578. else if (type.IsVarTime) {
  7579. if (isNull) {
  7580. stateObj.WriteByte(TdsEnums.FIXEDNULL);
  7581. }
  7582. else {
  7583. stateObj.WriteByte((byte)size);
  7584. }
  7585. }
  7586. else if (false == type.IsFixed) { // non-long but variable length column, must be a BIG* type: 2 byte length
  7587. if (isNull) {
  7588. WriteShort(TdsEnums.VARNULL, stateObj);
  7589. }
  7590. else {
  7591. WriteShort(size, stateObj);
  7592. }
  7593. }
  7594. else {
  7595. if (isNull) {
  7596. stateObj.WriteByte(TdsEnums.FIXEDNULL);
  7597. }
  7598. else {
  7599. Debug.Assert(type.FixedLength <= 0xff, "WriteParameterVarLen: invalid one byte length!");
  7600. stateObj.WriteByte((byte)(type.FixedLength & 0xff)); // 1 byte for everything else
  7601. }
  7602. }
  7603. }
  7604. // Reads the next chunk in a nvarchar(max) data stream.
  7605. // This call must be preceeded by a call to ReadPlpLength or ReadDataLength.
  7606. // Will not start reading into the next chunk if bytes requested is larger than
  7607. // the current chunk length. Do another ReadPlpLength, ReadPlpUnicodeChars in that case.
  7608. // Returns the actual chars read
  7609. private bool TryReadPlpUnicodeCharsChunk(char[] buff, int offst, int len, TdsParserStateObject stateObj, out int charsRead) {
  7610. Debug.Assert((buff == null && len == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!");
  7611. Debug.Assert((stateObj._longlen != 0) && (stateObj._longlen != TdsEnums.SQL_PLP_NULL),
  7612. "Out of sync plp read request");
  7613. if (stateObj._longlenleft == 0) {
  7614. Debug.Assert(false, "Out of sync read request");
  7615. charsRead = 0;
  7616. return true;
  7617. }
  7618. charsRead = len;
  7619. // stateObj._longlenleft is in bytes
  7620. if ((stateObj._longlenleft >> 1) < (ulong)len)
  7621. charsRead = (int)(stateObj._longlenleft >> 1);
  7622. for (int ii = 0; ii < charsRead; ii++) {
  7623. if (!stateObj.TryReadChar(out buff[offst + ii])) {
  7624. return false;
  7625. }
  7626. }
  7627. stateObj._longlenleft -= ((ulong)charsRead << 1);
  7628. return true;
  7629. }
  7630. internal int ReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj) {
  7631. int charsRead;
  7632. Debug.Assert(stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
  7633. bool result = TryReadPlpUnicodeChars(ref buff, offst, len, stateObj, out charsRead);
  7634. if (!result) { throw SQL.SynchronousCallMayNotPend(); }
  7635. return charsRead;
  7636. }
  7637. // Reads the requested number of chars from a plp data stream, or the entire data if
  7638. // requested length is -1 or larger than the actual length of data. First call to this method
  7639. // should be preceeded by a call to ReadPlpLength or ReadDataLength.
  7640. // Returns the actual chars read.
  7641. internal bool TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj, out int totalCharsRead) {
  7642. int charsRead = 0;
  7643. int charsLeft = 0;
  7644. char[] newbuf;
  7645. if (stateObj._longlen == 0) {
  7646. Debug.Assert(stateObj._longlenleft == 0);
  7647. totalCharsRead = 0;
  7648. return true; // No data
  7649. }
  7650. Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL),
  7651. "Out of sync plp read request");
  7652. Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!");
  7653. charsLeft = len;
  7654. // If total length is known up front, allocate the whole buffer in one shot instead of realloc'ing and copying over each time
  7655. if (buff == null && stateObj._longlen != TdsEnums.SQL_PLP_UNKNOWNLEN) {
  7656. buff = new char[(int)Math.Min((int)stateObj._longlen, len)];
  7657. }
  7658. if (stateObj._longlenleft == 0) {
  7659. ulong ignored;
  7660. if (!stateObj.TryReadPlpLength(false, out ignored)) {
  7661. totalCharsRead = 0;
  7662. return false;
  7663. }
  7664. if (stateObj._longlenleft == 0) { // Data read complete
  7665. totalCharsRead = 0;
  7666. return true;
  7667. }
  7668. }
  7669. totalCharsRead = 0;
  7670. while (charsLeft > 0) {
  7671. charsRead = (int)Math.Min((stateObj._longlenleft + 1) >> 1, (ulong)charsLeft);
  7672. if ((buff == null) || (buff.Length < (offst + charsRead))) {
  7673. // Grow the array
  7674. newbuf = new char[offst + charsRead];
  7675. if (buff != null) {
  7676. Buffer.BlockCopy(buff, 0, newbuf, 0, offst*2);
  7677. }
  7678. buff = newbuf;
  7679. }
  7680. if (charsRead > 0) {
  7681. if (!TryReadPlpUnicodeCharsChunk(buff, offst, charsRead, stateObj, out charsRead)) {
  7682. return false;
  7683. }
  7684. charsLeft -= charsRead;
  7685. offst += charsRead;
  7686. totalCharsRead += charsRead;
  7687. }
  7688. // Special case single byte left
  7689. if (stateObj._longlenleft == 1 && (charsLeft > 0)) {
  7690. byte b1;
  7691. if (!stateObj.TryReadByte(out b1)) {
  7692. return false;
  7693. }
  7694. stateObj._longlenleft--;
  7695. ulong ignored;
  7696. if (!stateObj.TryReadPlpLength(false, out ignored)) {
  7697. return false;
  7698. }
  7699. Debug.Assert((stateObj._longlenleft != 0), "ReadPlpUnicodeChars: Odd byte left at the end!");
  7700. byte b2;
  7701. if (!stateObj.TryReadByte(out b2)) {
  7702. return false;
  7703. }
  7704. stateObj._longlenleft--;
  7705. // Put it at the end of the array. At this point we know we have an extra byte.
  7706. buff[offst] = (char)(((b2 & 0xff) << 8) + (b1 & 0xff));
  7707. offst = checked((int)offst + 1);
  7708. charsRead++;
  7709. charsLeft--;
  7710. totalCharsRead++;
  7711. }
  7712. if (stateObj._longlenleft == 0) { // Read the next chunk or cleanup state if hit the end
  7713. ulong ignored;
  7714. if (!stateObj.TryReadPlpLength(false, out ignored)) {
  7715. return false;
  7716. }
  7717. }
  7718. if (stateObj._longlenleft == 0) // Data read complete
  7719. break;
  7720. }
  7721. return true;
  7722. }
  7723. internal int ReadPlpAnsiChars(ref char[] buff, int offst, int len, SqlMetaDataPriv metadata, TdsParserStateObject stateObj) {
  7724. int charsRead = 0;
  7725. int charsLeft = 0;
  7726. int bytesRead = 0;
  7727. int totalcharsRead = 0;
  7728. if (stateObj._longlen == 0) {
  7729. Debug.Assert(stateObj._longlenleft == 0);
  7730. return 0; // No data
  7731. }
  7732. Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL),
  7733. "Out of sync plp read request");
  7734. Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpAnsiChars()!");
  7735. charsLeft = len;
  7736. if (stateObj._longlenleft == 0) {
  7737. stateObj.ReadPlpLength(false);
  7738. if (stateObj._longlenleft == 0) {// Data read complete
  7739. stateObj._plpdecoder = null;
  7740. return 0;
  7741. }
  7742. }
  7743. if (stateObj._plpdecoder == null) {
  7744. Encoding enc = metadata.encoding;
  7745. if (enc == null)
  7746. {
  7747. if (null == _defaultEncoding) {
  7748. ThrowUnsupportedCollationEncountered(stateObj);
  7749. }
  7750. enc = _defaultEncoding;
  7751. }
  7752. stateObj._plpdecoder = enc.GetDecoder();
  7753. }
  7754. while (charsLeft > 0) {
  7755. bytesRead = (int)Math.Min(stateObj._longlenleft, (ulong)charsLeft);
  7756. if ((stateObj._bTmp == null) || (stateObj._bTmp.Length < bytesRead)) {
  7757. // Grow the array
  7758. stateObj._bTmp = new byte[bytesRead];
  7759. }
  7760. bytesRead = stateObj.ReadPlpBytesChunk(stateObj._bTmp, 0, bytesRead);
  7761. charsRead = stateObj._plpdecoder.GetChars(stateObj._bTmp, 0, bytesRead, buff, offst);
  7762. charsLeft -= charsRead;
  7763. offst += charsRead;
  7764. totalcharsRead += charsRead;
  7765. if (stateObj._longlenleft == 0) // Read the next chunk or cleanup state if hit the end
  7766. stateObj.ReadPlpLength(false);
  7767. if (stateObj._longlenleft == 0) { // Data read complete
  7768. stateObj._plpdecoder = null;
  7769. break;
  7770. }
  7771. }
  7772. return (totalcharsRead);
  7773. }
  7774. // ensure value is not null and does not have an NBC bit set for it before using this method
  7775. internal ulong SkipPlpValue(ulong cb, TdsParserStateObject stateObj) {
  7776. ulong skipped;
  7777. Debug.Assert(stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
  7778. bool result = TrySkipPlpValue(cb, stateObj, out skipped);
  7779. if (!result) { throw SQL.SynchronousCallMayNotPend(); }
  7780. return skipped;
  7781. }
  7782. internal bool TrySkipPlpValue(ulong cb, TdsParserStateObject stateObj, out ulong totalBytesSkipped) {
  7783. // Read and skip cb bytes or until ReadPlpLength returns 0.
  7784. int bytesSkipped;
  7785. totalBytesSkipped = 0;
  7786. if (stateObj._longlenleft == 0) {
  7787. ulong ignored;
  7788. if (!stateObj.TryReadPlpLength(false, out ignored)) {
  7789. return false;
  7790. }
  7791. }
  7792. while ((totalBytesSkipped < cb) &&
  7793. (stateObj._longlenleft > 0)) {
  7794. if (stateObj._longlenleft > Int32.MaxValue)
  7795. bytesSkipped = Int32.MaxValue;
  7796. else
  7797. bytesSkipped = (int)stateObj._longlenleft;
  7798. bytesSkipped = ((cb - totalBytesSkipped) < (ulong) bytesSkipped) ? (int)(cb - totalBytesSkipped) : bytesSkipped;
  7799. if (!stateObj.TrySkipBytes(bytesSkipped)) {
  7800. return false;
  7801. }
  7802. stateObj._longlenleft -= (ulong)bytesSkipped;
  7803. totalBytesSkipped += (ulong)bytesSkipped;
  7804. if (stateObj._longlenleft == 0) {
  7805. ulong ignored;
  7806. if (!stateObj.TryReadPlpLength(false, out ignored)) {
  7807. return false;
  7808. }
  7809. }
  7810. }
  7811. return true;
  7812. }
  7813. internal ulong PlpBytesLeft(TdsParserStateObject stateObj) {
  7814. if ((stateObj._longlen != 0) && (stateObj._longlenleft == 0))
  7815. stateObj.ReadPlpLength(false);
  7816. return stateObj._longlenleft;
  7817. }
  7818. internal bool TryPlpBytesLeft(TdsParserStateObject stateObj, out ulong left) {
  7819. if ((stateObj._longlen != 0) && (stateObj._longlenleft == 0)) {
  7820. if (!stateObj.TryReadPlpLength(false, out left)) {
  7821. return false;
  7822. }
  7823. }
  7824. left = stateObj._longlenleft;
  7825. return true;
  7826. }
  7827. private const ulong _indeterminateSize = 0xffffffffffffffff; // Represents unknown size
  7828. internal ulong PlpBytesTotalLength(TdsParserStateObject stateObj) {
  7829. if (stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN)
  7830. return _indeterminateSize;
  7831. else if (stateObj._longlen == TdsEnums.SQL_PLP_NULL)
  7832. return 0;
  7833. return stateObj._longlen;
  7834. }
  7835. const string StateTraceFormatString = "\n\t"
  7836. + " _physicalStateObj = {0}\n\t"
  7837. + " _pMarsPhysicalConObj = {1}\n\t"
  7838. + " _state = {2}\n\t"
  7839. + " _server = {3}\n\t"
  7840. + " _fResetConnection = {4}\n\t"
  7841. + " _defaultCollation = {5}\n\t"
  7842. + " _defaultCodePage = {6}\n\t"
  7843. + " _defaultLCID = {7}\n\t"
  7844. + " _defaultEncoding = {8}\n\t"
  7845. + " _encryptionOption = {10}\n\t"
  7846. + " _currentTransaction = {11}\n\t"
  7847. + " _pendingTransaction = {12}\n\t"
  7848. + " _retainedTransactionId = {13}\n\t"
  7849. + " _nonTransactedOpenResultCount = {14}\n\t"
  7850. + " _connHandler = {15}\n\t"
  7851. + " _fMARS = {16}\n\t"
  7852. + " _sessionPool = {17}\n\t"
  7853. + " _isShiloh = {18}\n\t"
  7854. + " _isShilohSP1 = {19}\n\t"
  7855. + " _isYukon = {20}\n\t"
  7856. + " _sniSpnBuffer = {21}\n\t"
  7857. + " _errors = {22}\n\t"
  7858. + " _warnings = {23}\n\t"
  7859. + " _attentionErrors = {24}\n\t"
  7860. + " _attentionWarnings = {25}\n\t"
  7861. + " _statistics = {26}\n\t"
  7862. + " _statisticsIsInTransaction = {27}\n\t"
  7863. + " _fPreserveTransaction = {28}"
  7864. + " _fParallel = {29}"
  7865. ;
  7866. internal string TraceString() {
  7867. return String.Format(/*IFormatProvider*/ null,
  7868. StateTraceFormatString,
  7869. null == _physicalStateObj,
  7870. null == _pMarsPhysicalConObj,
  7871. _state,
  7872. _server,
  7873. _fResetConnection,
  7874. null == _defaultCollation ? "(null)" : _defaultCollation.TraceString(),
  7875. _defaultCodePage,
  7876. _defaultLCID,
  7877. TraceObjectClass(_defaultEncoding),
  7878. "",
  7879. _encryptionOption,
  7880. null == _currentTransaction ? "(null)" : _currentTransaction.TraceString(),
  7881. null == _pendingTransaction ? "(null)" : _pendingTransaction.TraceString(),
  7882. _retainedTransactionId,
  7883. _nonTransactedOpenResultCount,
  7884. null == _connHandler ? "(null)" : _connHandler.ObjectID.ToString((IFormatProvider)null),
  7885. _fMARS,
  7886. null == _sessionPool ? "(null)" : _sessionPool.TraceString(),
  7887. _isShiloh,
  7888. _isShilohSP1,
  7889. _isYukon,
  7890. null == _sniSpnBuffer ? "(null)" : _sniSpnBuffer.Length.ToString((IFormatProvider)null),
  7891. _physicalStateObj != null ? "(null)" : _physicalStateObj.ErrorCount.ToString((IFormatProvider)null),
  7892. _physicalStateObj != null ? "(null)" : _physicalStateObj.WarningCount.ToString((IFormatProvider)null),
  7893. _physicalStateObj != null ? "(null)" : _physicalStateObj.PreAttentionErrorCount.ToString((IFormatProvider)null),
  7894. _physicalStateObj != null ? "(null)" : _physicalStateObj.PreAttentionWarningCount.ToString((IFormatProvider)null),
  7895. null == _statistics,
  7896. _statisticsIsInTransaction,
  7897. _fPreserveTransaction,
  7898. null == _connHandler ? "(null)" : _connHandler.ConnectionOptions.MultiSubnetFailover.ToString((IFormatProvider)null));
  7899. }
  7900. private string TraceObjectClass(object instance) {
  7901. if (null == instance) {
  7902. return "(null)";
  7903. }
  7904. else {
  7905. return instance.GetType().ToString();
  7906. }
  7907. }
  7908. } // tdsparser
  7909. }//namespace