SQLite.cs 164 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034
  1. //
  2. // Copyright (c) 2009-2024 Krueger Systems, Inc.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #if WINDOWS_PHONE && !USE_WP8_NATIVE_SQLITE
  23. #define USE_CSHARP_SQLITE
  24. #endif
  25. using System;
  26. using System.Collections;
  27. using System.Diagnostics;
  28. #if !USE_SQLITEPCL_RAW
  29. using System.Runtime.InteropServices;
  30. #endif
  31. using System.Collections.Generic;
  32. using System.Reflection;
  33. using System.Linq;
  34. using System.Linq.Expressions;
  35. using System.Text;
  36. using System.Threading;
  37. #if USE_CSHARP_SQLITE
  38. using Sqlite3 = Community.CsharpSqlite.Sqlite3;
  39. using Sqlite3DatabaseHandle = Community.CsharpSqlite.Sqlite3.sqlite3;
  40. using Sqlite3Statement = Community.CsharpSqlite.Sqlite3.Vdbe;
  41. #elif USE_WP8_NATIVE_SQLITE
  42. using Sqlite3 = Sqlite.Sqlite3;
  43. using Sqlite3DatabaseHandle = Sqlite.Database;
  44. using Sqlite3Statement = Sqlite.Statement;
  45. #elif USE_SQLITEPCL_RAW
  46. using Sqlite3DatabaseHandle = SQLitePCL.sqlite3;
  47. using Sqlite3BackupHandle = SQLitePCL.sqlite3_backup;
  48. using Sqlite3Statement = SQLitePCL.sqlite3_stmt;
  49. using Sqlite3 = SQLitePCL.raw;
  50. #else
  51. using Sqlite3DatabaseHandle = System.IntPtr;
  52. using Sqlite3BackupHandle = System.IntPtr;
  53. using Sqlite3Statement = System.IntPtr;
  54. #endif
  55. #pragma warning disable 1591 // XML Doc Comments
  56. namespace SQLite
  57. {
  58. public class SQLiteException : Exception
  59. {
  60. public SQLite3.Result Result { get; private set; }
  61. protected SQLiteException (SQLite3.Result r, string message) : base (message)
  62. {
  63. Result = r;
  64. }
  65. public static SQLiteException New (SQLite3.Result r, string message)
  66. {
  67. return new SQLiteException (r, message);
  68. }
  69. }
  70. public class NotNullConstraintViolationException : SQLiteException
  71. {
  72. public IEnumerable<TableMapping.Column> Columns { get; protected set; }
  73. protected NotNullConstraintViolationException (SQLite3.Result r, string message)
  74. : this (r, message, null, null)
  75. {
  76. }
  77. protected NotNullConstraintViolationException (SQLite3.Result r, string message, TableMapping mapping, object obj)
  78. : base (r, message)
  79. {
  80. if (mapping != null && obj != null) {
  81. this.Columns = from c in mapping.Columns
  82. where c.IsNullable == false && c.GetValue (obj) == null
  83. select c;
  84. }
  85. }
  86. public static new NotNullConstraintViolationException New (SQLite3.Result r, string message)
  87. {
  88. return new NotNullConstraintViolationException (r, message);
  89. }
  90. public static NotNullConstraintViolationException New (SQLite3.Result r, string message, TableMapping mapping, object obj)
  91. {
  92. return new NotNullConstraintViolationException (r, message, mapping, obj);
  93. }
  94. public static NotNullConstraintViolationException New (SQLiteException exception, TableMapping mapping, object obj)
  95. {
  96. return new NotNullConstraintViolationException (exception.Result, exception.Message, mapping, obj);
  97. }
  98. }
  99. [Flags]
  100. public enum SQLiteOpenFlags
  101. {
  102. ReadOnly = 1, ReadWrite = 2, Create = 4,
  103. Uri = 0x40, Memory = 0x80,
  104. NoMutex = 0x8000, FullMutex = 0x10000,
  105. SharedCache = 0x20000, PrivateCache = 0x40000,
  106. ProtectionComplete = 0x00100000,
  107. ProtectionCompleteUnlessOpen = 0x00200000,
  108. ProtectionCompleteUntilFirstUserAuthentication = 0x00300000,
  109. ProtectionNone = 0x00400000
  110. }
  111. [Flags]
  112. public enum CreateFlags
  113. {
  114. /// <summary>
  115. /// Use the default creation options
  116. /// </summary>
  117. None = 0x000,
  118. /// <summary>
  119. /// Create a primary key index for a property called 'Id' (case-insensitive).
  120. /// This avoids the need for the [PrimaryKey] attribute.
  121. /// </summary>
  122. ImplicitPK = 0x001,
  123. /// <summary>
  124. /// Create indices for properties ending in 'Id' (case-insensitive).
  125. /// </summary>
  126. ImplicitIndex = 0x002,
  127. /// <summary>
  128. /// Create a primary key for a property called 'Id' and
  129. /// create an indices for properties ending in 'Id' (case-insensitive).
  130. /// </summary>
  131. AllImplicit = 0x003,
  132. /// <summary>
  133. /// Force the primary key property to be auto incrementing.
  134. /// This avoids the need for the [AutoIncrement] attribute.
  135. /// The primary key property on the class should have type int or long.
  136. /// </summary>
  137. AutoIncPK = 0x004,
  138. /// <summary>
  139. /// Create virtual table using FTS3
  140. /// </summary>
  141. FullTextSearch3 = 0x100,
  142. /// <summary>
  143. /// Create virtual table using FTS4
  144. /// </summary>
  145. FullTextSearch4 = 0x200
  146. }
  147. public interface ISQLiteConnection : IDisposable
  148. {
  149. Sqlite3DatabaseHandle Handle { get; }
  150. string DatabasePath { get; }
  151. int LibVersionNumber { get; }
  152. bool TimeExecution { get; set; }
  153. bool Trace { get; set; }
  154. Action<string> Tracer { get; set; }
  155. bool StoreDateTimeAsTicks { get; }
  156. bool StoreTimeSpanAsTicks { get; }
  157. string DateTimeStringFormat { get; }
  158. TimeSpan BusyTimeout { get; set; }
  159. IEnumerable<TableMapping> TableMappings { get; }
  160. bool IsInTransaction { get; }
  161. event EventHandler<NotifyTableChangedEventArgs> TableChanged;
  162. void Backup (string destinationDatabasePath, string databaseName = "main");
  163. void BeginTransaction ();
  164. void Close ();
  165. void Commit ();
  166. SQLiteCommand CreateCommand (string cmdText, params object[] ps);
  167. SQLiteCommand CreateCommand (string cmdText, Dictionary<string, object> args);
  168. int CreateIndex (string indexName, string tableName, string[] columnNames, bool unique = false);
  169. int CreateIndex (string indexName, string tableName, string columnName, bool unique = false);
  170. int CreateIndex (string tableName, string columnName, bool unique = false);
  171. int CreateIndex (string tableName, string[] columnNames, bool unique = false);
  172. int CreateIndex<T> (Expression<Func<T, object>> property, bool unique = false);
  173. CreateTableResult CreateTable<T> (CreateFlags createFlags = CreateFlags.None,string tableName = "");
  174. CreateTableResult CreateTable (Type ty, CreateFlags createFlags = CreateFlags.None,string tableName = "");
  175. CreateTablesResult CreateTables<T, T2> (CreateFlags createFlags = CreateFlags.None)
  176. where T : new()
  177. where T2 : new();
  178. CreateTablesResult CreateTables<T, T2, T3> (CreateFlags createFlags = CreateFlags.None)
  179. where T : new()
  180. where T2 : new()
  181. where T3 : new();
  182. CreateTablesResult CreateTables<T, T2, T3, T4> (CreateFlags createFlags = CreateFlags.None)
  183. where T : new()
  184. where T2 : new()
  185. where T3 : new()
  186. where T4 : new();
  187. CreateTablesResult CreateTables<T, T2, T3, T4, T5> (CreateFlags createFlags = CreateFlags.None)
  188. where T : new()
  189. where T2 : new()
  190. where T3 : new()
  191. where T4 : new()
  192. where T5 : new();
  193. CreateTablesResult CreateTables (CreateFlags createFlags = CreateFlags.None, params Type[] types);
  194. IEnumerable<T> DeferredQuery<T> (string query, params object[] args) where T : new();
  195. IEnumerable<object> DeferredQuery (TableMapping map, string query, params object[] args);
  196. int Delete (object objectToDelete,string tableName = "");
  197. int Delete<T> (object primaryKey,string tableName = "");
  198. int Delete (object primaryKey, TableMapping map);
  199. int DeleteAll<T> (string tableName = "");
  200. int DeleteAll (TableMapping map);
  201. int DropTable<T> (string tableName = "");
  202. int DropTable (TableMapping map);
  203. void EnableLoadExtension (bool enabled);
  204. void EnableWriteAheadLogging ();
  205. int Execute (string query, params object[] args);
  206. T ExecuteScalar<T> (string query, params object[] args);
  207. T Find<T> (object pk,string tableName = "") where T : new();
  208. object Find (object pk, TableMapping map);
  209. T Find<T> (Expression<Func<T, bool>> predicate,string tableName = "") where T : new();
  210. T FindWithQuery<T> (string query, params object[] args) where T : new();
  211. object FindWithQuery (TableMapping map, string query, params object[] args);
  212. T Get<T> (object pk,string tableName = "") where T : new();
  213. object Get (object pk, TableMapping map);
  214. T Get<T> (Expression<Func<T, bool>> predicate,string tableName = "") where T : new();
  215. TableMapping GetMapping (Type type, CreateFlags createFlags = CreateFlags.None,string tableName = "");
  216. TableMapping GetMapping<T> (CreateFlags createFlags = CreateFlags.None,string tableName = "");
  217. List<SQLiteConnection.ColumnInfo> GetTableInfo (string tableName);
  218. int Insert (object obj,string tableName = "");
  219. int Insert (object obj, Type objType,string tableName = "");
  220. int Insert (object obj, string extra,string tableName = "");
  221. int Insert (object obj, string extra, Type objType,string tableName = "");
  222. int InsertAll (IEnumerable objects, bool runInTransaction = true,string tableName = "");
  223. int InsertAll (IEnumerable objects, string extra, bool runInTransaction = true,string tableName = "");
  224. int InsertAll (IEnumerable objects, Type objType, bool runInTransaction = true,string tableName = "");
  225. int InsertOrReplace (object obj,string tableName = "");
  226. int InsertOrReplace (object obj, Type objType,string tableName = "");
  227. List<T> Query<T> (string query, params object[] args) where T : new();
  228. List<object> Query (TableMapping map, string query, params object[] args);
  229. List<T> QueryScalars<T> (string query, params object[] args);
  230. void ReKey (string key);
  231. void ReKey (byte[] key);
  232. void Release (string savepoint);
  233. void Rollback ();
  234. void RollbackTo (string savepoint);
  235. void RunInTransaction (Action action);
  236. string SaveTransactionPoint ();
  237. TableQuery<T> Table<T> (string tableName = "") where T : new();
  238. int Update (object obj,string tableName = "");
  239. int Update (object obj, Type objType,string tableName = "");
  240. int UpdateAll (IEnumerable objects, bool runInTransaction = true,string tableName = "");
  241. }
  242. /// <summary>
  243. /// An open connection to a SQLite database.
  244. /// </summary>
  245. [Preserve (AllMembers = true)]
  246. public partial class SQLiteConnection : ISQLiteConnection
  247. {
  248. private bool _open;
  249. private TimeSpan _busyTimeout;
  250. readonly static Dictionary<string, TableMapping> _mappings = new Dictionary<string, TableMapping> ();
  251. private System.Diagnostics.Stopwatch _sw;
  252. private long _elapsedMilliseconds = 0;
  253. private int _transactionDepth = 0;
  254. private Random _rand = new Random ();
  255. public Sqlite3DatabaseHandle Handle { get; private set; }
  256. static readonly Sqlite3DatabaseHandle NullHandle = default (Sqlite3DatabaseHandle);
  257. static readonly Sqlite3BackupHandle NullBackupHandle = default (Sqlite3BackupHandle);
  258. /// <summary>
  259. /// Gets the database path used by this connection.
  260. /// </summary>
  261. public string DatabasePath { get; private set; }
  262. /// <summary>
  263. /// Gets the SQLite library version number. 3007014 would be v3.7.14
  264. /// </summary>
  265. public int LibVersionNumber { get; private set; }
  266. /// <summary>
  267. /// Whether Trace lines should be written that show the execution time of queries.
  268. /// </summary>
  269. public bool TimeExecution { get; set; }
  270. /// <summary>
  271. /// Whether to write queries to <see cref="Tracer"/> during execution.
  272. /// </summary>
  273. public bool Trace { get; set; }
  274. /// <summary>
  275. /// The delegate responsible for writing trace lines.
  276. /// </summary>
  277. /// <value>The tracer.</value>
  278. public Action<string> Tracer { get; set; }
  279. /// <summary>
  280. /// Whether to store DateTime properties as ticks (true) or strings (false).
  281. /// </summary>
  282. public bool StoreDateTimeAsTicks { get; private set; }
  283. /// <summary>
  284. /// Whether to store TimeSpan properties as ticks (true) or strings (false).
  285. /// </summary>
  286. public bool StoreTimeSpanAsTicks { get; private set; }
  287. /// <summary>
  288. /// The format to use when storing DateTime properties as strings. Ignored if StoreDateTimeAsTicks is true.
  289. /// </summary>
  290. /// <value>The date time string format.</value>
  291. public string DateTimeStringFormat { get; private set; }
  292. /// <summary>
  293. /// The DateTimeStyles value to use when parsing a DateTime property string.
  294. /// </summary>
  295. /// <value>The date time style.</value>
  296. internal System.Globalization.DateTimeStyles DateTimeStyle { get; private set; }
  297. #if USE_SQLITEPCL_RAW && !NO_SQLITEPCL_RAW_BATTERIES
  298. static SQLiteConnection ()
  299. {
  300. SQLitePCL.Batteries_V2.Init ();
  301. }
  302. #endif
  303. /// <summary>
  304. /// Constructs a new SQLiteConnection and opens a SQLite database specified by databasePath.
  305. /// </summary>
  306. /// <param name="databasePath">
  307. /// Specifies the path to the database file.
  308. /// </param>
  309. /// <param name="storeDateTimeAsTicks">
  310. /// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
  311. /// absolutely do want to store them as Ticks in all new projects. The value of false is
  312. /// only here for backwards compatibility. There is a *significant* speed advantage, with no
  313. /// down sides, when setting storeDateTimeAsTicks = true.
  314. /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
  315. /// the storeDateTimeAsTicks parameter.
  316. /// </param>
  317. public SQLiteConnection (string databasePath, bool storeDateTimeAsTicks = true)
  318. : this (new SQLiteConnectionString (databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks))
  319. {
  320. }
  321. /// <summary>
  322. /// Constructs a new SQLiteConnection and opens a SQLite database specified by databasePath.
  323. /// </summary>
  324. /// <param name="databasePath">
  325. /// Specifies the path to the database file.
  326. /// </param>
  327. /// <param name="openFlags">
  328. /// Flags controlling how the connection should be opened.
  329. /// </param>
  330. /// <param name="storeDateTimeAsTicks">
  331. /// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
  332. /// absolutely do want to store them as Ticks in all new projects. The value of false is
  333. /// only here for backwards compatibility. There is a *significant* speed advantage, with no
  334. /// down sides, when setting storeDateTimeAsTicks = true.
  335. /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
  336. /// the storeDateTimeAsTicks parameter.
  337. /// </param>
  338. public SQLiteConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true)
  339. : this (new SQLiteConnectionString (databasePath, openFlags, storeDateTimeAsTicks))
  340. {
  341. }
  342. /// <summary>
  343. /// Constructs a new SQLiteConnection and opens a SQLite database specified by databasePath.
  344. /// </summary>
  345. /// <param name="connectionString">
  346. /// Details on how to find and open the database.
  347. /// </param>
  348. public SQLiteConnection (SQLiteConnectionString connectionString)
  349. {
  350. if (connectionString == null)
  351. throw new ArgumentNullException (nameof (connectionString));
  352. if (connectionString.DatabasePath == null)
  353. throw new InvalidOperationException ("DatabasePath must be specified");
  354. DatabasePath = connectionString.DatabasePath;
  355. LibVersionNumber = SQLite3.LibVersionNumber ();
  356. #if NETFX_CORE
  357. SQLite3.SetDirectory(/*temp directory type*/2, Windows.Storage.ApplicationData.Current.TemporaryFolder.Path);
  358. #endif
  359. Sqlite3DatabaseHandle handle;
  360. #if SILVERLIGHT || USE_CSHARP_SQLITE || USE_SQLITEPCL_RAW
  361. var r = SQLite3.Open (connectionString.DatabasePath, out handle, (int)connectionString.OpenFlags, connectionString.VfsName);
  362. #else
  363. // open using the byte[]
  364. // in the case where the path may include Unicode
  365. // force open to using UTF-8 using sqlite3_open_v2
  366. var databasePathAsBytes = GetNullTerminatedUtf8 (connectionString.DatabasePath);
  367. var r = SQLite3.Open (databasePathAsBytes, out handle, (int)connectionString.OpenFlags, connectionString.VfsName);
  368. #endif
  369. Handle = handle;
  370. if (r != SQLite3.Result.OK) {
  371. throw SQLiteException.New (r, String.Format ("Could not open database file: {0} ({1})", DatabasePath, r));
  372. }
  373. _open = true;
  374. StoreDateTimeAsTicks = connectionString.StoreDateTimeAsTicks;
  375. StoreTimeSpanAsTicks = connectionString.StoreTimeSpanAsTicks;
  376. DateTimeStringFormat = connectionString.DateTimeStringFormat;
  377. DateTimeStyle = connectionString.DateTimeStyle;
  378. BusyTimeout = TimeSpan.FromSeconds (1.0);
  379. Tracer = line => Debug.WriteLine (line);
  380. connectionString.PreKeyAction?.Invoke (this);
  381. if (connectionString.Key is string stringKey) {
  382. SetKey (stringKey);
  383. }
  384. else if (connectionString.Key is byte[] bytesKey) {
  385. SetKey (bytesKey);
  386. }
  387. else if (connectionString.Key != null) {
  388. throw new InvalidOperationException ("Encryption keys must be strings or byte arrays");
  389. }
  390. connectionString.PostKeyAction?.Invoke (this);
  391. }
  392. /// <summary>
  393. /// Enables the write ahead logging. WAL is significantly faster in most scenarios
  394. /// by providing better concurrency and better disk IO performance than the normal
  395. /// journal mode. You only need to call this function once in the lifetime of the database.
  396. /// </summary>
  397. public void EnableWriteAheadLogging ()
  398. {
  399. ExecuteScalar<string> ("PRAGMA journal_mode=WAL");
  400. }
  401. /// <summary>
  402. /// Convert an input string to a quoted SQL string that can be safely used in queries.
  403. /// </summary>
  404. /// <returns>The quoted string.</returns>
  405. /// <param name="unsafeString">The unsafe string to quote.</param>
  406. static string Quote (string unsafeString)
  407. {
  408. // TODO: Doesn't call sqlite3_mprintf("%Q", u) because we're waiting on https://github.com/ericsink/SQLitePCL.raw/issues/153
  409. if (unsafeString == null)
  410. return "NULL";
  411. var safe = unsafeString.Replace ("'", "''");
  412. return "'" + safe + "'";
  413. }
  414. /// <summary>
  415. /// Sets the key used to encrypt/decrypt the database with "pragma key = ...".
  416. /// This must be the first thing you call before doing anything else with this connection
  417. /// if your database is encrypted.
  418. /// This only has an effect if you are using the SQLCipher nuget package.
  419. /// </summary>
  420. /// <param name="key">Encryption key plain text that is converted to the real encryption key using PBKDF2 key derivation</param>
  421. void SetKey (string key)
  422. {
  423. if (key == null)
  424. throw new ArgumentNullException (nameof (key));
  425. var q = Quote (key);
  426. ExecuteScalar<string> ("pragma key = " + q);
  427. }
  428. /// <summary>
  429. /// Sets the key used to encrypt/decrypt the database.
  430. /// This must be the first thing you call before doing anything else with this connection
  431. /// if your database is encrypted.
  432. /// This only has an effect if you are using the SQLCipher nuget package.
  433. /// </summary>
  434. /// <param name="key">256-bit (32 byte) encryption key data</param>
  435. void SetKey (byte[] key)
  436. {
  437. if (key == null)
  438. throw new ArgumentNullException (nameof (key));
  439. if (key.Length != 32 && key.Length != 48)
  440. throw new ArgumentException ("Key must be 32 bytes (256-bit) or 48 bytes (384-bit)", nameof (key));
  441. var s = String.Join ("", key.Select (x => x.ToString ("X2")));
  442. ExecuteScalar<string> ("pragma key = \"x'" + s + "'\"");
  443. }
  444. /// <summary>
  445. /// Change the encryption key for a SQLCipher database with "pragma rekey = ...".
  446. /// </summary>
  447. /// <param name="key">Encryption key plain text that is converted to the real encryption key using PBKDF2 key derivation</param>
  448. public void ReKey (string key)
  449. {
  450. if (key == null)
  451. throw new ArgumentNullException(nameof(key));
  452. var q = Quote(key);
  453. ExecuteScalar<string>("pragma rekey = " + q);
  454. }
  455. /// <summary>
  456. /// Change the encryption key for a SQLCipher database.
  457. /// </summary>
  458. /// <param name="key">256-bit (32 byte) or 384-bit (48 bytes) encryption key data</param>
  459. public void ReKey (byte[] key)
  460. {
  461. if (key == null)
  462. throw new ArgumentNullException(nameof(key));
  463. if (key.Length != 32 && key.Length != 48)
  464. throw new ArgumentException ("Key must be 32 bytes (256-bit) or 48 bytes (384-bit)", nameof (key));
  465. var s = String.Join("", key.Select(x => x.ToString("X2")));
  466. ExecuteScalar<string>("pragma rekey = \"x'" + s + "'\"");
  467. }
  468. /// <summary>
  469. /// Enable or disable extension loading.
  470. /// </summary>
  471. public void EnableLoadExtension (bool enabled)
  472. {
  473. SQLite3.Result r = SQLite3.EnableLoadExtension (Handle, enabled ? 1 : 0);
  474. if (r != SQLite3.Result.OK) {
  475. string msg = SQLite3.GetErrmsg (Handle);
  476. throw SQLiteException.New (r, msg);
  477. }
  478. }
  479. #if !USE_SQLITEPCL_RAW
  480. static byte[] GetNullTerminatedUtf8 (string s)
  481. {
  482. var utf8Length = System.Text.Encoding.UTF8.GetByteCount (s);
  483. var bytes = new byte [utf8Length + 1];
  484. utf8Length = System.Text.Encoding.UTF8.GetBytes(s, 0, s.Length, bytes, 0);
  485. return bytes;
  486. }
  487. #endif
  488. /// <summary>
  489. /// Sets a busy handler to sleep the specified amount of time when a table is locked.
  490. /// The handler will sleep multiple times until a total time of <see cref="BusyTimeout"/> has accumulated.
  491. /// </summary>
  492. public TimeSpan BusyTimeout {
  493. get { return _busyTimeout; }
  494. set {
  495. _busyTimeout = value;
  496. if (Handle != NullHandle) {
  497. SQLite3.BusyTimeout (Handle, (int)_busyTimeout.TotalMilliseconds);
  498. }
  499. }
  500. }
  501. /// <summary>
  502. /// Returns the mappings from types to tables that the connection
  503. /// currently understands.
  504. /// </summary>
  505. public IEnumerable<TableMapping> TableMappings {
  506. get {
  507. lock (_mappings) {
  508. return new List<TableMapping> (_mappings.Values);
  509. }
  510. }
  511. }
  512. /// <summary>
  513. /// Retrieves the mapping that is automatically generated for the given type.
  514. /// </summary>
  515. /// <param name="type">
  516. /// The type whose mapping to the database is returned.
  517. /// </param>
  518. /// <param name="createFlags">
  519. /// Optional flags allowing implicit PK and indexes based on naming conventions
  520. /// </param>
  521. /// <returns>
  522. /// The mapping represents the schema of the columns of the database and contains
  523. /// methods to set and get properties of objects.
  524. /// </returns>
  525. public TableMapping GetMapping (Type type, CreateFlags createFlags = CreateFlags.None,string tableName = "")
  526. {
  527. TableMapping map;
  528. var key = type.FullName;
  529. if (!string.IsNullOrEmpty(tableName)) key = tableName;
  530. lock (_mappings) {
  531. if (_mappings.TryGetValue (key, out map)) {
  532. if (createFlags != CreateFlags.None && createFlags != map.CreateFlags) {
  533. map = new TableMapping (type, createFlags,tableName);
  534. _mappings[key] = map;
  535. }
  536. }
  537. else {
  538. map = new TableMapping (type, createFlags,tableName);
  539. _mappings.Add (key, map);
  540. }
  541. }
  542. return map;
  543. }
  544. /// <summary>
  545. /// Retrieves the mapping that is automatically generated for the given type.
  546. /// </summary>
  547. /// <param name="createFlags">
  548. /// Optional flags allowing implicit PK and indexes based on naming conventions
  549. /// </param>
  550. /// <returns>
  551. /// The mapping represents the schema of the columns of the database and contains
  552. /// methods to set and get properties of objects.
  553. /// </returns>
  554. public TableMapping GetMapping<T> (CreateFlags createFlags = CreateFlags.None,string tableName = "")
  555. {
  556. return GetMapping (typeof (T), createFlags,tableName);
  557. }
  558. private struct IndexedColumn
  559. {
  560. public int Order;
  561. public string ColumnName;
  562. }
  563. private struct IndexInfo
  564. {
  565. public string IndexName;
  566. public string TableName;
  567. public bool Unique;
  568. public List<IndexedColumn> Columns;
  569. }
  570. /// <summary>
  571. /// Executes a "drop table" on the database. This is non-recoverable.
  572. /// </summary>
  573. public int DropTable<T> (string tableName = "")
  574. {
  575. return DropTable (GetMapping (typeof (T),tableName:tableName));
  576. }
  577. /// <summary>
  578. /// Executes a "drop table" on the database. This is non-recoverable.
  579. /// </summary>
  580. /// <param name="map">
  581. /// The TableMapping used to identify the table.
  582. /// </param>
  583. public int DropTable (TableMapping map)
  584. {
  585. var query = string.Format ("drop table if exists \"{0}\"", map.TableName);
  586. return Execute (query);
  587. }
  588. /// <summary>
  589. /// Executes a "create table if not exists" on the database. It also
  590. /// creates any specified indexes on the columns of the table. It uses
  591. /// a schema automatically generated from the specified type. You can
  592. /// later access this schema by calling GetMapping.
  593. /// </summary>
  594. /// <returns>
  595. /// Whether the table was created or migrated.
  596. /// </returns>
  597. public CreateTableResult CreateTable<T> (CreateFlags createFlags = CreateFlags.None,string tableName = "")
  598. {
  599. return CreateTable (typeof (T), createFlags,tableName);
  600. }
  601. /// <summary>
  602. /// Executes a "create table if not exists" on the database. It also
  603. /// creates any specified indexes on the columns of the table. It uses
  604. /// a schema automatically generated from the specified type. You can
  605. /// later access this schema by calling GetMapping.
  606. /// </summary>
  607. /// <param name="ty">Type to reflect to a database table.</param>
  608. /// <param name="createFlags">Optional flags allowing implicit PK and indexes based on naming conventions.</param>
  609. /// <returns>
  610. /// Whether the table was created or migrated.
  611. /// </returns>
  612. public CreateTableResult CreateTable (Type ty, CreateFlags createFlags = CreateFlags.None,string tableName="")
  613. {
  614. var map = GetMapping (ty, createFlags,tableName);
  615. // Present a nice error if no columns specified
  616. if (map.Columns.Length == 0) {
  617. throw new Exception (string.Format ("Cannot create a table without columns (does '{0}' have public properties?)", ty.FullName));
  618. }
  619. // Check if the table exists
  620. var result = CreateTableResult.Created;
  621. var existingCols = GetTableInfo (map.TableName);
  622. // Create or migrate it
  623. if (existingCols.Count == 0) {
  624. // Facilitate virtual tables a.k.a. full-text search.
  625. bool fts3 = (createFlags & CreateFlags.FullTextSearch3) != 0;
  626. bool fts4 = (createFlags & CreateFlags.FullTextSearch4) != 0;
  627. bool fts = fts3 || fts4;
  628. var @virtual = fts ? "virtual " : string.Empty;
  629. var @using = fts3 ? "using fts3 " : fts4 ? "using fts4 " : string.Empty;
  630. // Build query.
  631. var query = "create " + @virtual + "table if not exists \"" + map.TableName + "\" " + @using + "(\n";
  632. var decls = map.Columns.Select (p => Orm.SqlDecl (p, StoreDateTimeAsTicks, StoreTimeSpanAsTicks));
  633. var decl = string.Join (",\n", decls.ToArray ());
  634. query += decl;
  635. query += ")";
  636. if (map.WithoutRowId) {
  637. query += " without rowid";
  638. }
  639. Execute (query);
  640. }
  641. else {
  642. result = CreateTableResult.Migrated;
  643. MigrateTable (map, existingCols);
  644. }
  645. var indexes = new Dictionary<string, IndexInfo> ();
  646. foreach (var c in map.Columns) {
  647. foreach (var i in c.Indices) {
  648. var iname = i.Name ?? map.TableName + "_" + c.Name;
  649. IndexInfo iinfo;
  650. if (!indexes.TryGetValue (iname, out iinfo)) {
  651. iinfo = new IndexInfo {
  652. IndexName = iname,
  653. TableName = map.TableName,
  654. Unique = i.Unique,
  655. Columns = new List<IndexedColumn> ()
  656. };
  657. indexes.Add (iname, iinfo);
  658. }
  659. if (i.Unique != iinfo.Unique)
  660. throw new Exception ("All the columns in an index must have the same value for their Unique property");
  661. iinfo.Columns.Add (new IndexedColumn {
  662. Order = i.Order,
  663. ColumnName = c.Name
  664. });
  665. }
  666. }
  667. foreach (var indexName in indexes.Keys) {
  668. var index = indexes[indexName];
  669. var columns = index.Columns.OrderBy (i => i.Order).Select (i => i.ColumnName).ToArray ();
  670. CreateIndex (indexName, index.TableName, columns, index.Unique);
  671. }
  672. return result;
  673. }
  674. /// <summary>
  675. /// Executes a "create table if not exists" on the database for each type. It also
  676. /// creates any specified indexes on the columns of the table. It uses
  677. /// a schema automatically generated from the specified type. You can
  678. /// later access this schema by calling GetMapping.
  679. /// </summary>
  680. /// <returns>
  681. /// Whether the table was created or migrated for each type.
  682. /// </returns>
  683. public CreateTablesResult CreateTables<T, T2> (CreateFlags createFlags = CreateFlags.None)
  684. where T : new()
  685. where T2 : new()
  686. {
  687. return CreateTables (createFlags, typeof (T), typeof (T2));
  688. }
  689. /// <summary>
  690. /// Executes a "create table if not exists" on the database for each type. It also
  691. /// creates any specified indexes on the columns of the table. It uses
  692. /// a schema automatically generated from the specified type. You can
  693. /// later access this schema by calling GetMapping.
  694. /// </summary>
  695. /// <returns>
  696. /// Whether the table was created or migrated for each type.
  697. /// </returns>
  698. public CreateTablesResult CreateTables<T, T2, T3> (CreateFlags createFlags = CreateFlags.None)
  699. where T : new()
  700. where T2 : new()
  701. where T3 : new()
  702. {
  703. return CreateTables (createFlags, typeof (T), typeof (T2), typeof (T3));
  704. }
  705. /// <summary>
  706. /// Executes a "create table if not exists" on the database for each type. It also
  707. /// creates any specified indexes on the columns of the table. It uses
  708. /// a schema automatically generated from the specified type. You can
  709. /// later access this schema by calling GetMapping.
  710. /// </summary>
  711. /// <returns>
  712. /// Whether the table was created or migrated for each type.
  713. /// </returns>
  714. public CreateTablesResult CreateTables<T, T2, T3, T4> (CreateFlags createFlags = CreateFlags.None)
  715. where T : new()
  716. where T2 : new()
  717. where T3 : new()
  718. where T4 : new()
  719. {
  720. return CreateTables (createFlags, typeof (T), typeof (T2), typeof (T3), typeof (T4));
  721. }
  722. /// <summary>
  723. /// Executes a "create table if not exists" on the database for each type. It also
  724. /// creates any specified indexes on the columns of the table. It uses
  725. /// a schema automatically generated from the specified type. You can
  726. /// later access this schema by calling GetMapping.
  727. /// </summary>
  728. /// <returns>
  729. /// Whether the table was created or migrated for each type.
  730. /// </returns>
  731. public CreateTablesResult CreateTables<T, T2, T3, T4, T5> (CreateFlags createFlags = CreateFlags.None)
  732. where T : new()
  733. where T2 : new()
  734. where T3 : new()
  735. where T4 : new()
  736. where T5 : new()
  737. {
  738. return CreateTables (createFlags, typeof (T), typeof (T2), typeof (T3), typeof (T4), typeof (T5));
  739. }
  740. /// <summary>
  741. /// Executes a "create table if not exists" on the database for each type. It also
  742. /// creates any specified indexes on the columns of the table. It uses
  743. /// a schema automatically generated from the specified type. You can
  744. /// later access this schema by calling GetMapping.
  745. /// </summary>
  746. /// <returns>
  747. /// Whether the table was created or migrated for each type.
  748. /// </returns>
  749. public CreateTablesResult CreateTables (CreateFlags createFlags = CreateFlags.None, params Type[] types)
  750. {
  751. var result = new CreateTablesResult ();
  752. foreach (Type type in types) {
  753. var aResult = CreateTable (type, createFlags);
  754. result.Results[type] = aResult;
  755. }
  756. return result;
  757. }
  758. /// <summary>
  759. /// Creates an index for the specified table and columns.
  760. /// </summary>
  761. /// <param name="indexName">Name of the index to create</param>
  762. /// <param name="tableName">Name of the database table</param>
  763. /// <param name="columnNames">An array of column names to index</param>
  764. /// <param name="unique">Whether the index should be unique</param>
  765. /// <returns>Zero on success.</returns>
  766. public int CreateIndex (string indexName, string tableName, string[] columnNames, bool unique = false)
  767. {
  768. const string sqlFormat = "create {2} index if not exists \"{3}\" on \"{0}\"(\"{1}\")";
  769. var sql = String.Format (sqlFormat, tableName, string.Join ("\", \"", columnNames), unique ? "unique" : "", indexName);
  770. return Execute (sql);
  771. }
  772. /// <summary>
  773. /// Creates an index for the specified table and column.
  774. /// </summary>
  775. /// <param name="indexName">Name of the index to create</param>
  776. /// <param name="tableName">Name of the database table</param>
  777. /// <param name="columnName">Name of the column to index</param>
  778. /// <param name="unique">Whether the index should be unique</param>
  779. /// <returns>Zero on success.</returns>
  780. public int CreateIndex (string indexName, string tableName, string columnName, bool unique = false)
  781. {
  782. return CreateIndex (indexName, tableName, new string[] { columnName }, unique);
  783. }
  784. /// <summary>
  785. /// Creates an index for the specified table and column.
  786. /// </summary>
  787. /// <param name="tableName">Name of the database table</param>
  788. /// <param name="columnName">Name of the column to index</param>
  789. /// <param name="unique">Whether the index should be unique</param>
  790. /// <returns>Zero on success.</returns>
  791. public int CreateIndex (string tableName, string columnName, bool unique = false)
  792. {
  793. return CreateIndex (tableName + "_" + columnName, tableName, columnName, unique);
  794. }
  795. /// <summary>
  796. /// Creates an index for the specified table and columns.
  797. /// </summary>
  798. /// <param name="tableName">Name of the database table</param>
  799. /// <param name="columnNames">An array of column names to index</param>
  800. /// <param name="unique">Whether the index should be unique</param>
  801. /// <returns>Zero on success.</returns>
  802. public int CreateIndex (string tableName, string[] columnNames, bool unique = false)
  803. {
  804. return CreateIndex (tableName + "_" + string.Join ("_", columnNames), tableName, columnNames, unique);
  805. }
  806. /// <summary>
  807. /// Creates an index for the specified object property.
  808. /// e.g. CreateIndex&lt;Client&gt;(c => c.Name);
  809. /// </summary>
  810. /// <typeparam name="T">Type to reflect to a database table.</typeparam>
  811. /// <param name="property">Property to index</param>
  812. /// <param name="unique">Whether the index should be unique</param>
  813. /// <returns>Zero on success.</returns>
  814. public int CreateIndex<T> (Expression<Func<T, object>> property, bool unique = false)
  815. {
  816. MemberExpression mx;
  817. if (property.Body.NodeType == ExpressionType.Convert) {
  818. mx = ((UnaryExpression)property.Body).Operand as MemberExpression;
  819. }
  820. else {
  821. mx = (property.Body as MemberExpression);
  822. }
  823. var propertyInfo = mx.Member as PropertyInfo;
  824. if (propertyInfo == null) {
  825. throw new ArgumentException ("The lambda expression 'property' should point to a valid Property");
  826. }
  827. var propName = propertyInfo.Name;
  828. var map = GetMapping<T> ();
  829. var colName = map.FindColumnWithPropertyName (propName).Name;
  830. return CreateIndex (map.TableName, colName, unique);
  831. }
  832. [Preserve (AllMembers = true)]
  833. public class ColumnInfo
  834. {
  835. // public int cid { get; set; }
  836. [Column ("name")]
  837. public string Name { get; set; }
  838. // [Column ("type")]
  839. // public string ColumnType { get; set; }
  840. public int notnull { get; set; }
  841. // public string dflt_value { get; set; }
  842. // public int pk { get; set; }
  843. public override string ToString ()
  844. {
  845. return Name;
  846. }
  847. }
  848. /// <summary>
  849. /// Query the built-in sqlite table_info table for a specific tables columns.
  850. /// </summary>
  851. /// <returns>The columns contains in the table.</returns>
  852. /// <param name="tableName">Table name.</param>
  853. public List<ColumnInfo> GetTableInfo (string tableName)
  854. {
  855. var query = "pragma table_info(\"" + tableName + "\")";
  856. return Query<ColumnInfo> (query);
  857. }
  858. void MigrateTable (TableMapping map, List<ColumnInfo> existingCols)
  859. {
  860. var toBeAdded = new List<TableMapping.Column> ();
  861. foreach (var p in map.Columns) {
  862. var found = false;
  863. foreach (var c in existingCols) {
  864. found = (string.Compare (p.Name, c.Name, StringComparison.OrdinalIgnoreCase) == 0);
  865. if (found)
  866. break;
  867. }
  868. if (!found) {
  869. toBeAdded.Add (p);
  870. }
  871. }
  872. foreach (var p in toBeAdded) {
  873. var addCol = "alter table \"" + map.TableName + "\" add column " + Orm.SqlDecl (p, StoreDateTimeAsTicks, StoreTimeSpanAsTicks);
  874. Execute (addCol);
  875. }
  876. }
  877. /// <summary>
  878. /// Creates a new SQLiteCommand. Can be overridden to provide a sub-class.
  879. /// </summary>
  880. /// <seealso cref="SQLiteCommand.OnInstanceCreated"/>
  881. protected virtual SQLiteCommand NewCommand ()
  882. {
  883. return new SQLiteCommand (this);
  884. }
  885. /// <summary>
  886. /// Creates a new SQLiteCommand given the command text with arguments. Place a '?'
  887. /// in the command text for each of the arguments.
  888. /// </summary>
  889. /// <param name="cmdText">
  890. /// The fully escaped SQL.
  891. /// </param>
  892. /// <param name="ps">
  893. /// Arguments to substitute for the occurences of '?' in the command text.
  894. /// </param>
  895. /// <returns>
  896. /// A <see cref="SQLiteCommand"/>
  897. /// </returns>
  898. public SQLiteCommand CreateCommand (string cmdText, params object[] ps)
  899. {
  900. if (!_open)
  901. throw SQLiteException.New (SQLite3.Result.Error, "Cannot create commands from unopened database");
  902. var cmd = NewCommand ();
  903. cmd.CommandText = cmdText;
  904. foreach (var o in ps) {
  905. cmd.Bind (o);
  906. }
  907. return cmd;
  908. }
  909. /// <summary>
  910. /// Creates a new SQLiteCommand given the command text with named arguments. Place a "[@:$]VVV"
  911. /// in the command text for each of the arguments. VVV represents an alphanumeric identifier.
  912. /// For example, @name :name and $name can all be used in the query.
  913. /// </summary>
  914. /// <param name="cmdText">
  915. /// The fully escaped SQL.
  916. /// </param>
  917. /// <param name="args">
  918. /// Arguments to substitute for the occurences of "[@:$]VVV" in the command text.
  919. /// </param>
  920. /// <returns>
  921. /// A <see cref="SQLiteCommand" />
  922. /// </returns>
  923. public SQLiteCommand CreateCommand (string cmdText, Dictionary<string, object> args)
  924. {
  925. if (!_open)
  926. throw SQLiteException.New (SQLite3.Result.Error, "Cannot create commands from unopened database");
  927. SQLiteCommand cmd = NewCommand ();
  928. cmd.CommandText = cmdText;
  929. foreach (var kv in args) {
  930. cmd.Bind (kv.Key, kv.Value);
  931. }
  932. return cmd;
  933. }
  934. /// <summary>
  935. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  936. /// in the command text for each of the arguments and then executes that command.
  937. /// Use this method instead of Query when you don't expect rows back. Such cases include
  938. /// INSERTs, UPDATEs, and DELETEs.
  939. /// You can set the Trace or TimeExecution properties of the connection
  940. /// to profile execution.
  941. /// </summary>
  942. /// <param name="query">
  943. /// The fully escaped SQL.
  944. /// </param>
  945. /// <param name="args">
  946. /// Arguments to substitute for the occurences of '?' in the query.
  947. /// </param>
  948. /// <returns>
  949. /// The number of rows modified in the database as a result of this execution.
  950. /// </returns>
  951. public int Execute (string query, params object[] args)
  952. {
  953. var cmd = CreateCommand (query, args);
  954. if (TimeExecution) {
  955. if (_sw == null) {
  956. _sw = new Stopwatch ();
  957. }
  958. _sw.Reset ();
  959. _sw.Start ();
  960. }
  961. var r = cmd.ExecuteNonQuery ();
  962. if (TimeExecution) {
  963. _sw.Stop ();
  964. _elapsedMilliseconds += _sw.ElapsedMilliseconds;
  965. Tracer?.Invoke (string.Format ("Finished in {0} ms ({1:0.0} s total)", _sw.ElapsedMilliseconds, _elapsedMilliseconds / 1000.0));
  966. }
  967. return r;
  968. }
  969. /// <summary>
  970. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  971. /// in the command text for each of the arguments and then executes that command.
  972. /// Use this method when return primitive values.
  973. /// You can set the Trace or TimeExecution properties of the connection
  974. /// to profile execution.
  975. /// </summary>
  976. /// <param name="query">
  977. /// The fully escaped SQL.
  978. /// </param>
  979. /// <param name="args">
  980. /// Arguments to substitute for the occurences of '?' in the query.
  981. /// </param>
  982. /// <returns>
  983. /// The number of rows modified in the database as a result of this execution.
  984. /// </returns>
  985. public T ExecuteScalar<T> (string query, params object[] args)
  986. {
  987. var cmd = CreateCommand (query, args);
  988. if (TimeExecution) {
  989. if (_sw == null) {
  990. _sw = new Stopwatch ();
  991. }
  992. _sw.Reset ();
  993. _sw.Start ();
  994. }
  995. var r = cmd.ExecuteScalar<T> ();
  996. if (TimeExecution) {
  997. _sw.Stop ();
  998. _elapsedMilliseconds += _sw.ElapsedMilliseconds;
  999. Tracer?.Invoke (string.Format ("Finished in {0} ms ({1:0.0} s total)", _sw.ElapsedMilliseconds, _elapsedMilliseconds / 1000.0));
  1000. }
  1001. return r;
  1002. }
  1003. /// <summary>
  1004. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  1005. /// in the command text for each of the arguments and then executes that command.
  1006. /// It returns each row of the result using the mapping automatically generated for
  1007. /// the given type.
  1008. /// </summary>
  1009. /// <param name="query">
  1010. /// The fully escaped SQL.
  1011. /// </param>
  1012. /// <param name="args">
  1013. /// Arguments to substitute for the occurences of '?' in the query.
  1014. /// </param>
  1015. /// <returns>
  1016. /// An enumerable with one result for each row returned by the query.
  1017. /// </returns>
  1018. public List<T> Query<T> (string query, params object[] args) where T : new()
  1019. {
  1020. var cmd = CreateCommand (query, args);
  1021. return cmd.ExecuteQuery<T> ();
  1022. }
  1023. /// <summary>
  1024. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  1025. /// in the command text for each of the arguments and then executes that command.
  1026. /// It returns the first column of each row of the result.
  1027. /// </summary>
  1028. /// <param name="query">
  1029. /// The fully escaped SQL.
  1030. /// </param>
  1031. /// <param name="args">
  1032. /// Arguments to substitute for the occurences of '?' in the query.
  1033. /// </param>
  1034. /// <returns>
  1035. /// An enumerable with one result for the first column of each row returned by the query.
  1036. /// </returns>
  1037. public List<T> QueryScalars<T> (string query, params object[] args)
  1038. {
  1039. var cmd = CreateCommand (query, args);
  1040. return cmd.ExecuteQueryScalars<T> ().ToList ();
  1041. }
  1042. /// <summary>
  1043. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  1044. /// in the command text for each of the arguments and then executes that command.
  1045. /// It returns each row of the result using the mapping automatically generated for
  1046. /// the given type.
  1047. /// </summary>
  1048. /// <param name="query">
  1049. /// The fully escaped SQL.
  1050. /// </param>
  1051. /// <param name="args">
  1052. /// Arguments to substitute for the occurences of '?' in the query.
  1053. /// </param>
  1054. /// <returns>
  1055. /// An enumerable with one result for each row returned by the query.
  1056. /// The enumerator (retrieved by calling GetEnumerator() on the result of this method)
  1057. /// will call sqlite3_step on each call to MoveNext, so the database
  1058. /// connection must remain open for the lifetime of the enumerator.
  1059. /// </returns>
  1060. public IEnumerable<T> DeferredQuery<T> (string query, params object[] args) where T : new()
  1061. {
  1062. var cmd = CreateCommand (query, args);
  1063. return cmd.ExecuteDeferredQuery<T> ();
  1064. }
  1065. /// <summary>
  1066. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  1067. /// in the command text for each of the arguments and then executes that command.
  1068. /// It returns each row of the result using the specified mapping. This function is
  1069. /// only used by libraries in order to query the database via introspection. It is
  1070. /// normally not used.
  1071. /// </summary>
  1072. /// <param name="map">
  1073. /// A <see cref="TableMapping"/> to use to convert the resulting rows
  1074. /// into objects.
  1075. /// </param>
  1076. /// <param name="query">
  1077. /// The fully escaped SQL.
  1078. /// </param>
  1079. /// <param name="args">
  1080. /// Arguments to substitute for the occurences of '?' in the query.
  1081. /// </param>
  1082. /// <returns>
  1083. /// An enumerable with one result for each row returned by the query.
  1084. /// </returns>
  1085. public List<object> Query (TableMapping map, string query, params object[] args)
  1086. {
  1087. var cmd = CreateCommand (query, args);
  1088. return cmd.ExecuteQuery<object> (map);
  1089. }
  1090. /// <summary>
  1091. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  1092. /// in the command text for each of the arguments and then executes that command.
  1093. /// It returns each row of the result using the specified mapping. This function is
  1094. /// only used by libraries in order to query the database via introspection. It is
  1095. /// normally not used.
  1096. /// </summary>
  1097. /// <param name="map">
  1098. /// A <see cref="TableMapping"/> to use to convert the resulting rows
  1099. /// into objects.
  1100. /// </param>
  1101. /// <param name="query">
  1102. /// The fully escaped SQL.
  1103. /// </param>
  1104. /// <param name="args">
  1105. /// Arguments to substitute for the occurences of '?' in the query.
  1106. /// </param>
  1107. /// <returns>
  1108. /// An enumerable with one result for each row returned by the query.
  1109. /// The enumerator (retrieved by calling GetEnumerator() on the result of this method)
  1110. /// will call sqlite3_step on each call to MoveNext, so the database
  1111. /// connection must remain open for the lifetime of the enumerator.
  1112. /// </returns>
  1113. public IEnumerable<object> DeferredQuery (TableMapping map, string query, params object[] args)
  1114. {
  1115. var cmd = CreateCommand (query, args);
  1116. return cmd.ExecuteDeferredQuery<object> (map);
  1117. }
  1118. /// <summary>
  1119. /// Returns a queryable interface to the table represented by the given type.
  1120. /// </summary>
  1121. /// <returns>
  1122. /// A queryable object that is able to translate Where, OrderBy, and Take
  1123. /// queries into native SQL.
  1124. /// </returns>
  1125. public TableQuery<T> Table<T> (string tableName = "") where T : new()
  1126. {
  1127. return new TableQuery<T> (this,tableName);
  1128. }
  1129. /// <summary>
  1130. /// Attempts to retrieve an object with the given primary key from the table
  1131. /// associated with the specified type. Use of this method requires that
  1132. /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
  1133. /// </summary>
  1134. /// <param name="pk">
  1135. /// The primary key.
  1136. /// </param>
  1137. /// <returns>
  1138. /// The object with the given primary key. Throws a not found exception
  1139. /// if the object is not found.
  1140. /// </returns>
  1141. public T Get<T> (object pk,string tableName = "") where T : new()
  1142. {
  1143. var map = GetMapping (typeof (T),tableName:tableName);
  1144. return Query<T> (map.GetByPrimaryKeySql, pk).First ();
  1145. }
  1146. /// <summary>
  1147. /// Attempts to retrieve an object with the given primary key from the table
  1148. /// associated with the specified type. Use of this method requires that
  1149. /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
  1150. /// </summary>
  1151. /// <param name="pk">
  1152. /// The primary key.
  1153. /// </param>
  1154. /// <param name="map">
  1155. /// The TableMapping used to identify the table.
  1156. /// </param>
  1157. /// <returns>
  1158. /// The object with the given primary key. Throws a not found exception
  1159. /// if the object is not found.
  1160. /// </returns>
  1161. public object Get (object pk, TableMapping map)
  1162. {
  1163. return Query (map, map.GetByPrimaryKeySql, pk).First ();
  1164. }
  1165. /// <summary>
  1166. /// Attempts to retrieve the first object that matches the predicate from the table
  1167. /// associated with the specified type.
  1168. /// </summary>
  1169. /// <param name="predicate">
  1170. /// A predicate for which object to find.
  1171. /// </param>
  1172. /// <returns>
  1173. /// The object that matches the given predicate. Throws a not found exception
  1174. /// if the object is not found.
  1175. /// </returns>
  1176. public T Get<T> (Expression<Func<T, bool>> predicate,string tableName = "") where T : new()
  1177. {
  1178. return Table<T> (tableName).Where (predicate).First ();
  1179. }
  1180. /// <summary>
  1181. /// Attempts to retrieve an object with the given primary key from the table
  1182. /// associated with the specified type. Use of this method requires that
  1183. /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
  1184. /// </summary>
  1185. /// <param name="pk">
  1186. /// The primary key.
  1187. /// </param>
  1188. /// <returns>
  1189. /// The object with the given primary key or null
  1190. /// if the object is not found.
  1191. /// </returns>
  1192. public T Find<T> (object pk,string tableName = "") where T : new()
  1193. {
  1194. var map = GetMapping (typeof (T),tableName:tableName);
  1195. return Query<T> (map.GetByPrimaryKeySql, pk).FirstOrDefault ();
  1196. }
  1197. /// <summary>
  1198. /// Attempts to retrieve an object with the given primary key from the table
  1199. /// associated with the specified type. Use of this method requires that
  1200. /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
  1201. /// </summary>
  1202. /// <param name="pk">
  1203. /// The primary key.
  1204. /// </param>
  1205. /// <param name="map">
  1206. /// The TableMapping used to identify the table.
  1207. /// </param>
  1208. /// <returns>
  1209. /// The object with the given primary key or null
  1210. /// if the object is not found.
  1211. /// </returns>
  1212. public object Find (object pk, TableMapping map)
  1213. {
  1214. return Query (map, map.GetByPrimaryKeySql, pk).FirstOrDefault ();
  1215. }
  1216. /// <summary>
  1217. /// Attempts to retrieve the first object that matches the predicate from the table
  1218. /// associated with the specified type.
  1219. /// </summary>
  1220. /// <param name="predicate">
  1221. /// A predicate for which object to find.
  1222. /// </param>
  1223. /// <returns>
  1224. /// The object that matches the given predicate or null
  1225. /// if the object is not found.
  1226. /// </returns>
  1227. public T Find<T> (Expression<Func<T, bool>> predicate,string tableName = "") where T : new()
  1228. {
  1229. return Table<T> (tableName).Where (predicate).FirstOrDefault ();
  1230. }
  1231. /// <summary>
  1232. /// Attempts to retrieve the first object that matches the query from the table
  1233. /// associated with the specified type.
  1234. /// </summary>
  1235. /// <param name="query">
  1236. /// The fully escaped SQL.
  1237. /// </param>
  1238. /// <param name="args">
  1239. /// Arguments to substitute for the occurences of '?' in the query.
  1240. /// </param>
  1241. /// <returns>
  1242. /// The object that matches the given predicate or null
  1243. /// if the object is not found.
  1244. /// </returns>
  1245. public T FindWithQuery<T> (string query, params object[] args) where T : new()
  1246. {
  1247. return Query<T> (query, args).FirstOrDefault ();
  1248. }
  1249. /// <summary>
  1250. /// Attempts to retrieve the first object that matches the query from the table
  1251. /// associated with the specified type.
  1252. /// </summary>
  1253. /// <param name="map">
  1254. /// The TableMapping used to identify the table.
  1255. /// </param>
  1256. /// <param name="query">
  1257. /// The fully escaped SQL.
  1258. /// </param>
  1259. /// <param name="args">
  1260. /// Arguments to substitute for the occurences of '?' in the query.
  1261. /// </param>
  1262. /// <returns>
  1263. /// The object that matches the given predicate or null
  1264. /// if the object is not found.
  1265. /// </returns>
  1266. public object FindWithQuery (TableMapping map, string query, params object[] args)
  1267. {
  1268. return Query (map, query, args).FirstOrDefault ();
  1269. }
  1270. /// <summary>
  1271. /// Whether <see cref="BeginTransaction"/> has been called and the database is waiting for a <see cref="Commit"/>.
  1272. /// </summary>
  1273. public bool IsInTransaction {
  1274. get { return _transactionDepth > 0; }
  1275. }
  1276. /// <summary>
  1277. /// Begins a new transaction. Call <see cref="Commit"/> to end the transaction.
  1278. /// </summary>
  1279. /// <example cref="System.InvalidOperationException">Throws if a transaction has already begun.</example>
  1280. public void BeginTransaction ()
  1281. {
  1282. // The BEGIN command only works if the transaction stack is empty,
  1283. // or in other words if there are no pending transactions.
  1284. // If the transaction stack is not empty when the BEGIN command is invoked,
  1285. // then the command fails with an error.
  1286. // Rather than crash with an error, we will just ignore calls to BeginTransaction
  1287. // that would result in an error.
  1288. if (Interlocked.CompareExchange (ref _transactionDepth, 1, 0) == 0) {
  1289. try {
  1290. Execute ("begin transaction");
  1291. }
  1292. catch (Exception ex) {
  1293. var sqlExp = ex as SQLiteException;
  1294. if (sqlExp != null) {
  1295. // It is recommended that applications respond to the errors listed below
  1296. // by explicitly issuing a ROLLBACK command.
  1297. // TODO: This rollback failsafe should be localized to all throw sites.
  1298. switch (sqlExp.Result) {
  1299. case SQLite3.Result.IOError:
  1300. case SQLite3.Result.Full:
  1301. case SQLite3.Result.Busy:
  1302. case SQLite3.Result.NoMem:
  1303. case SQLite3.Result.Interrupt:
  1304. RollbackTo (null, true);
  1305. break;
  1306. }
  1307. }
  1308. else {
  1309. // Call decrement and not VolatileWrite in case we've already
  1310. // created a transaction point in SaveTransactionPoint since the catch.
  1311. Interlocked.Decrement (ref _transactionDepth);
  1312. }
  1313. throw;
  1314. }
  1315. }
  1316. else {
  1317. // Calling BeginTransaction on an already open transaction is invalid
  1318. throw new InvalidOperationException ("Cannot begin a transaction while already in a transaction.");
  1319. }
  1320. }
  1321. /// <summary>
  1322. /// Creates a savepoint in the database at the current point in the transaction timeline.
  1323. /// Begins a new transaction if one is not in progress.
  1324. ///
  1325. /// Call <see cref="RollbackTo(string)"/> to undo transactions since the returned savepoint.
  1326. /// Call <see cref="Release"/> to commit transactions after the savepoint returned here.
  1327. /// Call <see cref="Commit"/> to end the transaction, committing all changes.
  1328. /// </summary>
  1329. /// <returns>A string naming the savepoint.</returns>
  1330. public string SaveTransactionPoint ()
  1331. {
  1332. int depth = Interlocked.Increment (ref _transactionDepth) - 1;
  1333. string retVal = "S" + _rand.Next (short.MaxValue) + "D" + depth;
  1334. try {
  1335. Execute ("savepoint " + retVal);
  1336. }
  1337. catch (Exception ex) {
  1338. var sqlExp = ex as SQLiteException;
  1339. if (sqlExp != null) {
  1340. // It is recommended that applications respond to the errors listed below
  1341. // by explicitly issuing a ROLLBACK command.
  1342. // TODO: This rollback failsafe should be localized to all throw sites.
  1343. switch (sqlExp.Result) {
  1344. case SQLite3.Result.IOError:
  1345. case SQLite3.Result.Full:
  1346. case SQLite3.Result.Busy:
  1347. case SQLite3.Result.NoMem:
  1348. case SQLite3.Result.Interrupt:
  1349. RollbackTo (null, true);
  1350. break;
  1351. }
  1352. }
  1353. else {
  1354. Interlocked.Decrement (ref _transactionDepth);
  1355. }
  1356. throw;
  1357. }
  1358. return retVal;
  1359. }
  1360. /// <summary>
  1361. /// Rolls back the transaction that was begun by <see cref="BeginTransaction"/> or <see cref="SaveTransactionPoint"/>.
  1362. /// </summary>
  1363. public void Rollback ()
  1364. {
  1365. RollbackTo (null, false);
  1366. }
  1367. /// <summary>
  1368. /// Rolls back the savepoint created by <see cref="BeginTransaction"/> or SaveTransactionPoint.
  1369. /// </summary>
  1370. /// <param name="savepoint">The name of the savepoint to roll back to, as returned by <see cref="SaveTransactionPoint"/>. If savepoint is null or empty, this method is equivalent to a call to <see cref="Rollback"/></param>
  1371. public void RollbackTo (string savepoint)
  1372. {
  1373. RollbackTo (savepoint, false);
  1374. }
  1375. /// <summary>
  1376. /// Rolls back the transaction that was begun by <see cref="BeginTransaction"/>.
  1377. /// </summary>
  1378. /// <param name="savepoint">The name of the savepoint to roll back to, as returned by <see cref="SaveTransactionPoint"/>. If savepoint is null or empty, this method is equivalent to a call to <see cref="Rollback"/></param>
  1379. /// <param name="noThrow">true to avoid throwing exceptions, false otherwise</param>
  1380. void RollbackTo (string savepoint, bool noThrow)
  1381. {
  1382. // Rolling back without a TO clause rolls backs all transactions
  1383. // and leaves the transaction stack empty.
  1384. try {
  1385. if (String.IsNullOrEmpty (savepoint)) {
  1386. if (Interlocked.Exchange (ref _transactionDepth, 0) > 0) {
  1387. Execute ("rollback");
  1388. }
  1389. }
  1390. else {
  1391. DoSavePointExecute (savepoint, "rollback to ");
  1392. }
  1393. }
  1394. catch (SQLiteException) {
  1395. if (!noThrow)
  1396. throw;
  1397. }
  1398. // No need to rollback if there are no transactions open.
  1399. }
  1400. /// <summary>
  1401. /// Releases a savepoint returned from <see cref="SaveTransactionPoint"/>. Releasing a savepoint
  1402. /// makes changes since that savepoint permanent if the savepoint began the transaction,
  1403. /// or otherwise the changes are permanent pending a call to <see cref="Commit"/>.
  1404. ///
  1405. /// The RELEASE command is like a COMMIT for a SAVEPOINT.
  1406. /// </summary>
  1407. /// <param name="savepoint">The name of the savepoint to release. The string should be the result of a call to <see cref="SaveTransactionPoint"/></param>
  1408. public void Release (string savepoint)
  1409. {
  1410. try {
  1411. DoSavePointExecute (savepoint, "release ");
  1412. }
  1413. catch (SQLiteException ex) {
  1414. if (ex.Result == SQLite3.Result.Busy) {
  1415. // Force a rollback since most people don't know this function can fail
  1416. // Don't call Rollback() since the _transactionDepth is 0 and it won't try
  1417. // Calling rollback makes our _transactionDepth variable correct.
  1418. // Writes to the database only happen at depth=0, so this failure will only happen then.
  1419. try {
  1420. Execute ("rollback");
  1421. }
  1422. catch {
  1423. // rollback can fail in all sorts of wonderful version-dependent ways. Let's just hope for the best
  1424. }
  1425. }
  1426. throw;
  1427. }
  1428. }
  1429. void DoSavePointExecute (string savepoint, string cmd)
  1430. {
  1431. // Validate the savepoint
  1432. int firstLen = savepoint.IndexOf ('D');
  1433. if (firstLen >= 2 && savepoint.Length > firstLen + 1) {
  1434. int depth;
  1435. if (Int32.TryParse (savepoint.Substring (firstLen + 1), out depth)) {
  1436. // TODO: Mild race here, but inescapable without locking almost everywhere.
  1437. if (0 <= depth && depth < _transactionDepth) {
  1438. #if NETFX_CORE || USE_SQLITEPCL_RAW || NETCORE
  1439. Volatile.Write (ref _transactionDepth, depth);
  1440. #elif SILVERLIGHT
  1441. _transactionDepth = depth;
  1442. #else
  1443. Thread.VolatileWrite (ref _transactionDepth, depth);
  1444. #endif
  1445. Execute (cmd + savepoint);
  1446. return;
  1447. }
  1448. }
  1449. }
  1450. throw new ArgumentException ("savePoint is not valid, and should be the result of a call to SaveTransactionPoint.", "savePoint");
  1451. }
  1452. /// <summary>
  1453. /// Commits the transaction that was begun by <see cref="BeginTransaction"/>.
  1454. /// </summary>
  1455. public void Commit ()
  1456. {
  1457. if (Interlocked.Exchange (ref _transactionDepth, 0) != 0) {
  1458. try {
  1459. Execute ("commit");
  1460. }
  1461. catch {
  1462. // Force a rollback since most people don't know this function can fail
  1463. // Don't call Rollback() since the _transactionDepth is 0 and it won't try
  1464. // Calling rollback makes our _transactionDepth variable correct.
  1465. try {
  1466. Execute ("rollback");
  1467. }
  1468. catch {
  1469. // rollback can fail in all sorts of wonderful version-dependent ways. Let's just hope for the best
  1470. }
  1471. throw;
  1472. }
  1473. }
  1474. // Do nothing on a commit with no open transaction
  1475. }
  1476. /// <summary>
  1477. /// Executes <paramref name="action"/> within a (possibly nested) transaction by wrapping it in a SAVEPOINT. If an
  1478. /// exception occurs the whole transaction is rolled back, not just the current savepoint. The exception
  1479. /// is rethrown.
  1480. /// </summary>
  1481. /// <param name="action">
  1482. /// The <see cref="Action"/> to perform within a transaction. <paramref name="action"/> can contain any number
  1483. /// of operations on the connection but should never call <see cref="BeginTransaction"/> or
  1484. /// <see cref="Commit"/>.
  1485. /// </param>
  1486. public void RunInTransaction (Action action)
  1487. {
  1488. try {
  1489. var savePoint = SaveTransactionPoint ();
  1490. action ();
  1491. Release (savePoint);
  1492. }
  1493. catch (Exception) {
  1494. Rollback ();
  1495. throw;
  1496. }
  1497. }
  1498. /// <summary>
  1499. /// Inserts all specified objects.
  1500. /// </summary>
  1501. /// <param name="objects">
  1502. /// An <see cref="IEnumerable"/> of the objects to insert.
  1503. /// <param name="runInTransaction"/>
  1504. /// A boolean indicating if the inserts should be wrapped in a transaction.
  1505. /// </param>
  1506. /// <returns>
  1507. /// The number of rows added to the table.
  1508. /// </returns>
  1509. public int InsertAll (System.Collections.IEnumerable objects, bool runInTransaction = true,string tableName = "")
  1510. {
  1511. var c = 0;
  1512. if (runInTransaction) {
  1513. RunInTransaction (() => {
  1514. foreach (var r in objects) {
  1515. c += Insert (r,tableName:tableName);
  1516. }
  1517. });
  1518. }
  1519. else {
  1520. foreach (var r in objects) {
  1521. c += Insert (r,tableName:tableName);
  1522. }
  1523. }
  1524. return c;
  1525. }
  1526. /// <summary>
  1527. /// Inserts all specified objects.
  1528. /// </summary>
  1529. /// <param name="objects">
  1530. /// An <see cref="IEnumerable"/> of the objects to insert.
  1531. /// </param>
  1532. /// <param name="extra">
  1533. /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
  1534. /// </param>
  1535. /// <param name="runInTransaction">
  1536. /// A boolean indicating if the inserts should be wrapped in a transaction.
  1537. /// </param>
  1538. /// <returns>
  1539. /// The number of rows added to the table.
  1540. /// </returns>
  1541. public int InsertAll (System.Collections.IEnumerable objects, string extra, bool runInTransaction = true,string tableName = "")
  1542. {
  1543. var c = 0;
  1544. if (runInTransaction) {
  1545. RunInTransaction (() => {
  1546. foreach (var r in objects) {
  1547. c += Insert (r, extra,tableName:tableName);
  1548. }
  1549. });
  1550. }
  1551. else {
  1552. foreach (var r in objects) {
  1553. c += Insert (r, extra,tableName:tableName);
  1554. }
  1555. }
  1556. return c;
  1557. }
  1558. /// <summary>
  1559. /// Inserts all specified objects.
  1560. /// </summary>
  1561. /// <param name="objects">
  1562. /// An <see cref="IEnumerable"/> of the objects to insert.
  1563. /// </param>
  1564. /// <param name="objType">
  1565. /// The type of object to insert.
  1566. /// </param>
  1567. /// <param name="runInTransaction">
  1568. /// A boolean indicating if the inserts should be wrapped in a transaction.
  1569. /// </param>
  1570. /// <returns>
  1571. /// The number of rows added to the table.
  1572. /// </returns>
  1573. public int InsertAll (System.Collections.IEnumerable objects, Type objType, bool runInTransaction = true,string tableName = "")
  1574. {
  1575. var c = 0;
  1576. if (runInTransaction) {
  1577. RunInTransaction (() => {
  1578. foreach (var r in objects) {
  1579. c += Insert (r, objType,tableName:tableName);
  1580. }
  1581. });
  1582. }
  1583. else {
  1584. foreach (var r in objects) {
  1585. c += Insert (r, objType,tableName:tableName);
  1586. }
  1587. }
  1588. return c;
  1589. }
  1590. /// <summary>
  1591. /// Inserts the given object (and updates its
  1592. /// auto incremented primary key if it has one).
  1593. /// The return value is the number of rows added to the table.
  1594. /// </summary>
  1595. /// <param name="obj">
  1596. /// The object to insert.
  1597. /// </param>
  1598. /// <returns>
  1599. /// The number of rows added to the table.
  1600. /// </returns>
  1601. public int Insert (object obj,string tableName = "")
  1602. {
  1603. if (obj == null) {
  1604. return 0;
  1605. }
  1606. return Insert (obj, "", Orm.GetType (obj),tableName:tableName);
  1607. }
  1608. /// <summary>
  1609. /// Inserts the given object (and updates its
  1610. /// auto incremented primary key if it has one).
  1611. /// The return value is the number of rows added to the table.
  1612. /// If a UNIQUE constraint violation occurs with
  1613. /// some pre-existing object, this function deletes
  1614. /// the old object.
  1615. /// </summary>
  1616. /// <param name="obj">
  1617. /// The object to insert.
  1618. /// </param>
  1619. /// <returns>
  1620. /// The number of rows modified.
  1621. /// </returns>
  1622. public int InsertOrReplace (object obj,string tableName = "")
  1623. {
  1624. if (obj == null) {
  1625. return 0;
  1626. }
  1627. return Insert (obj, "OR REPLACE", Orm.GetType (obj),tableName:tableName);
  1628. }
  1629. /// <summary>
  1630. /// Inserts the given object (and updates its
  1631. /// auto incremented primary key if it has one).
  1632. /// The return value is the number of rows added to the table.
  1633. /// </summary>
  1634. /// <param name="obj">
  1635. /// The object to insert.
  1636. /// </param>
  1637. /// <param name="objType">
  1638. /// The type of object to insert.
  1639. /// </param>
  1640. /// <returns>
  1641. /// The number of rows added to the table.
  1642. /// </returns>
  1643. public int Insert (object obj, Type objType,string tableName = "")
  1644. {
  1645. return Insert (obj, "", objType,tableName:tableName);
  1646. }
  1647. /// <summary>
  1648. /// Inserts the given object (and updates its
  1649. /// auto incremented primary key if it has one).
  1650. /// The return value is the number of rows added to the table.
  1651. /// If a UNIQUE constraint violation occurs with
  1652. /// some pre-existing object, this function deletes
  1653. /// the old object.
  1654. /// </summary>
  1655. /// <param name="obj">
  1656. /// The object to insert.
  1657. /// </param>
  1658. /// <param name="objType">
  1659. /// The type of object to insert.
  1660. /// </param>
  1661. /// <returns>
  1662. /// The number of rows modified.
  1663. /// </returns>
  1664. public int InsertOrReplace (object obj, Type objType,string tableName = "")
  1665. {
  1666. return Insert (obj, "OR REPLACE", objType,tableName:tableName);
  1667. }
  1668. /// <summary>
  1669. /// Inserts the given object (and updates its
  1670. /// auto incremented primary key if it has one).
  1671. /// The return value is the number of rows added to the table.
  1672. /// </summary>
  1673. /// <param name="obj">
  1674. /// The object to insert.
  1675. /// </param>
  1676. /// <param name="extra">
  1677. /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
  1678. /// </param>
  1679. /// <returns>
  1680. /// The number of rows added to the table.
  1681. /// </returns>
  1682. public int Insert (object obj, string extra,string tableName = "")
  1683. {
  1684. if (obj == null) {
  1685. return 0;
  1686. }
  1687. return Insert (obj, extra, Orm.GetType (obj),tableName:tableName);
  1688. }
  1689. /// <summary>
  1690. /// Inserts the given object (and updates its
  1691. /// auto incremented primary key if it has one).
  1692. /// The return value is the number of rows added to the table.
  1693. /// </summary>
  1694. /// <param name="obj">
  1695. /// The object to insert.
  1696. /// </param>
  1697. /// <param name="extra">
  1698. /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
  1699. /// </param>
  1700. /// <param name="objType">
  1701. /// The type of object to insert.
  1702. /// </param>
  1703. /// <returns>
  1704. /// The number of rows added to the table.
  1705. /// </returns>
  1706. public int Insert (object obj, string extra, Type objType,string tableName = "")
  1707. {
  1708. if (obj == null || objType == null) {
  1709. return 0;
  1710. }
  1711. var map = GetMapping (objType,tableName:tableName);
  1712. if (map.PK != null && map.PK.IsAutoGuid) {
  1713. if (map.PK.GetValue (obj).Equals (Guid.Empty)) {
  1714. map.PK.SetValue (obj, Guid.NewGuid ());
  1715. }
  1716. }
  1717. var replacing = string.Compare (extra, "OR REPLACE", StringComparison.OrdinalIgnoreCase) == 0;
  1718. var cols = replacing ? map.InsertOrReplaceColumns : map.InsertColumns;
  1719. var vals = new object[cols.Length];
  1720. for (var i = 0; i < vals.Length; i++) {
  1721. vals[i] = cols[i].GetValue (obj);
  1722. }
  1723. var insertCmd = GetInsertCommand (map, extra);
  1724. int count;
  1725. lock (insertCmd) {
  1726. // We lock here to protect the prepared statement returned via GetInsertCommand.
  1727. // A SQLite prepared statement can be bound for only one operation at a time.
  1728. try {
  1729. count = insertCmd.ExecuteNonQuery (vals);
  1730. }
  1731. catch (SQLiteException ex) {
  1732. if (SQLite3.ExtendedErrCode (this.Handle) == SQLite3.ExtendedResult.ConstraintNotNull) {
  1733. throw NotNullConstraintViolationException.New (ex.Result, ex.Message, map, obj);
  1734. }
  1735. throw;
  1736. }
  1737. if (map.HasAutoIncPK) {
  1738. var id = SQLite3.LastInsertRowid (Handle);
  1739. map.SetAutoIncPK (obj, id);
  1740. }
  1741. }
  1742. if (count > 0)
  1743. OnTableChanged (map, NotifyTableChangedAction.Insert);
  1744. return count;
  1745. }
  1746. readonly Dictionary<Tuple<string, string>, PreparedSqlLiteInsertCommand> _insertCommandMap = new Dictionary<Tuple<string, string>, PreparedSqlLiteInsertCommand> ();
  1747. PreparedSqlLiteInsertCommand GetInsertCommand (TableMapping map, string extra)
  1748. {
  1749. PreparedSqlLiteInsertCommand prepCmd;
  1750. var key = Tuple.Create (map.TableName, extra);
  1751. lock (_insertCommandMap) {
  1752. if (_insertCommandMap.TryGetValue (key, out prepCmd)) {
  1753. return prepCmd;
  1754. }
  1755. }
  1756. prepCmd = CreateInsertCommand (map, extra);
  1757. lock (_insertCommandMap) {
  1758. if (_insertCommandMap.TryGetValue (key, out var existing)) {
  1759. prepCmd.Dispose ();
  1760. return existing;
  1761. }
  1762. _insertCommandMap.Add (key, prepCmd);
  1763. }
  1764. return prepCmd;
  1765. }
  1766. PreparedSqlLiteInsertCommand CreateInsertCommand (TableMapping map, string extra)
  1767. {
  1768. var cols = map.InsertColumns;
  1769. string insertSql;
  1770. if (cols.Length == 0 && map.Columns.Length == 1 && map.Columns[0].IsAutoInc) {
  1771. insertSql = string.Format ("insert {1} into \"{0}\" default values", map.TableName, extra);
  1772. }
  1773. else {
  1774. var replacing = string.Compare (extra, "OR REPLACE", StringComparison.OrdinalIgnoreCase) == 0;
  1775. if (replacing) {
  1776. cols = map.InsertOrReplaceColumns;
  1777. }
  1778. insertSql = string.Format ("insert {3} into \"{0}\"({1}) values ({2})", map.TableName,
  1779. string.Join (",", (from c in cols
  1780. select "\"" + c.Name + "\"").ToArray ()),
  1781. string.Join (",", (from c in cols
  1782. select "?").ToArray ()), extra);
  1783. }
  1784. var insertCommand = new PreparedSqlLiteInsertCommand (this, insertSql);
  1785. return insertCommand;
  1786. }
  1787. /// <summary>
  1788. /// Updates all of the columns of a table using the specified object
  1789. /// except for its primary key.
  1790. /// The object is required to have a primary key.
  1791. /// </summary>
  1792. /// <param name="obj">
  1793. /// The object to update. It must have a primary key designated using the PrimaryKeyAttribute.
  1794. /// </param>
  1795. /// <returns>
  1796. /// The number of rows updated.
  1797. /// </returns>
  1798. public int Update (object obj,string tableName = "")
  1799. {
  1800. if (obj == null) {
  1801. return 0;
  1802. }
  1803. return Update (obj, Orm.GetType (obj),tableName:tableName);
  1804. }
  1805. /// <summary>
  1806. /// Updates all of the columns of a table using the specified object
  1807. /// except for its primary key.
  1808. /// The object is required to have a primary key.
  1809. /// </summary>
  1810. /// <param name="obj">
  1811. /// The object to update. It must have a primary key designated using the PrimaryKeyAttribute.
  1812. /// </param>
  1813. /// <param name="objType">
  1814. /// The type of object to insert.
  1815. /// </param>
  1816. /// <returns>
  1817. /// The number of rows updated.
  1818. /// </returns>
  1819. public int Update (object obj, Type objType,string tableName = "")
  1820. {
  1821. int rowsAffected = 0;
  1822. if (obj == null || objType == null) {
  1823. return 0;
  1824. }
  1825. var map = GetMapping (objType,tableName:tableName);
  1826. var pk = map.PK;
  1827. if (pk == null) {
  1828. throw new NotSupportedException ("Cannot update " + map.TableName + ": it has no PK");
  1829. }
  1830. var cols = from p in map.Columns
  1831. where p != pk
  1832. select p;
  1833. var vals = from c in cols
  1834. select c.GetValue (obj);
  1835. var ps = new List<object> (vals);
  1836. if (ps.Count == 0) {
  1837. // There is a PK but no accompanying data,
  1838. // so reset the PK to make the UPDATE work.
  1839. cols = map.Columns;
  1840. vals = from c in cols
  1841. select c.GetValue (obj);
  1842. ps = new List<object> (vals);
  1843. }
  1844. ps.Add (pk.GetValue (obj));
  1845. var q = string.Format ("update \"{0}\" set {1} where \"{2}\" = ? ", map.TableName, string.Join (",", (from c in cols
  1846. select "\"" + c.Name + "\" = ? ").ToArray ()), pk.Name);
  1847. try {
  1848. rowsAffected = Execute (q, ps.ToArray ());
  1849. }
  1850. catch (SQLiteException ex) {
  1851. if (ex.Result == SQLite3.Result.Constraint && SQLite3.ExtendedErrCode (this.Handle) == SQLite3.ExtendedResult.ConstraintNotNull) {
  1852. throw NotNullConstraintViolationException.New (ex, map, obj);
  1853. }
  1854. throw;
  1855. }
  1856. if (rowsAffected > 0)
  1857. OnTableChanged (map, NotifyTableChangedAction.Update);
  1858. return rowsAffected;
  1859. }
  1860. /// <summary>
  1861. /// Updates all specified objects.
  1862. /// </summary>
  1863. /// <param name="objects">
  1864. /// An <see cref="IEnumerable"/> of the objects to insert.
  1865. /// </param>
  1866. /// <param name="runInTransaction">
  1867. /// A boolean indicating if the inserts should be wrapped in a transaction
  1868. /// </param>
  1869. /// <returns>
  1870. /// The number of rows modified.
  1871. /// </returns>
  1872. public int UpdateAll (System.Collections.IEnumerable objects, bool runInTransaction = true,string tableName = "")
  1873. {
  1874. var c = 0;
  1875. if (runInTransaction) {
  1876. RunInTransaction (() => {
  1877. foreach (var r in objects) {
  1878. c += Update (r,tableName:tableName);
  1879. }
  1880. });
  1881. }
  1882. else {
  1883. foreach (var r in objects) {
  1884. c += Update (r,tableName:tableName);
  1885. }
  1886. }
  1887. return c;
  1888. }
  1889. /// <summary>
  1890. /// Deletes the given object from the database using its primary key.
  1891. /// </summary>
  1892. /// <param name="objectToDelete">
  1893. /// The object to delete. It must have a primary key designated using the PrimaryKeyAttribute.
  1894. /// </param>
  1895. /// <returns>
  1896. /// The number of rows deleted.
  1897. /// </returns>
  1898. public int Delete (object objectToDelete,string tableName = "")
  1899. {
  1900. var map = GetMapping (Orm.GetType (objectToDelete),tableName:tableName);
  1901. var pk = map.PK;
  1902. if (pk == null) {
  1903. throw new NotSupportedException ("Cannot delete " + map.TableName + ": it has no PK");
  1904. }
  1905. var q = string.Format ("delete from \"{0}\" where \"{1}\" = ?", map.TableName, pk.Name);
  1906. var count = Execute (q, pk.GetValue (objectToDelete));
  1907. if (count > 0)
  1908. OnTableChanged (map, NotifyTableChangedAction.Delete);
  1909. return count;
  1910. }
  1911. /// <summary>
  1912. /// Deletes the object with the specified primary key.
  1913. /// </summary>
  1914. /// <param name="primaryKey">
  1915. /// The primary key of the object to delete.
  1916. /// </param>
  1917. /// <returns>
  1918. /// The number of objects deleted.
  1919. /// </returns>
  1920. /// <typeparam name='T'>
  1921. /// The type of object.
  1922. /// </typeparam>
  1923. public int Delete<T> (object primaryKey,string tableName = "")
  1924. {
  1925. return Delete (primaryKey, GetMapping (typeof (T),tableName:tableName));
  1926. }
  1927. /// <summary>
  1928. /// Deletes the object with the specified primary key.
  1929. /// </summary>
  1930. /// <param name="primaryKey">
  1931. /// The primary key of the object to delete.
  1932. /// </param>
  1933. /// <param name="map">
  1934. /// The TableMapping used to identify the table.
  1935. /// </param>
  1936. /// <returns>
  1937. /// The number of objects deleted.
  1938. /// </returns>
  1939. public int Delete (object primaryKey, TableMapping map)
  1940. {
  1941. var pk = map.PK;
  1942. if (pk == null) {
  1943. throw new NotSupportedException ("Cannot delete " + map.TableName + ": it has no PK");
  1944. }
  1945. var q = string.Format ("delete from \"{0}\" where \"{1}\" = ?", map.TableName, pk.Name);
  1946. var count = Execute (q, primaryKey);
  1947. if (count > 0)
  1948. OnTableChanged (map, NotifyTableChangedAction.Delete);
  1949. return count;
  1950. }
  1951. /// <summary>
  1952. /// Deletes all the objects from the specified table.
  1953. /// WARNING WARNING: Let me repeat. It deletes ALL the objects from the
  1954. /// specified table. Do you really want to do that?
  1955. /// </summary>
  1956. /// <returns>
  1957. /// The number of objects deleted.
  1958. /// </returns>
  1959. /// <typeparam name='T'>
  1960. /// The type of objects to delete.
  1961. /// </typeparam>
  1962. public int DeleteAll<T> (string tableName = "")
  1963. {
  1964. var map = GetMapping(typeof(T), tableName: tableName);
  1965. return DeleteAll (map);
  1966. }
  1967. /// <summary>
  1968. /// Deletes all the objects from the specified table.
  1969. /// WARNING WARNING: Let me repeat. It deletes ALL the objects from the
  1970. /// specified table. Do you really want to do that?
  1971. /// </summary>
  1972. /// <param name="map">
  1973. /// The TableMapping used to identify the table.
  1974. /// </param>
  1975. /// <returns>
  1976. /// The number of objects deleted.
  1977. /// </returns>
  1978. public int DeleteAll (TableMapping map)
  1979. {
  1980. var query = string.Format ("delete from \"{0}\"", map.TableName);
  1981. var count = Execute (query);
  1982. if (count > 0)
  1983. OnTableChanged (map, NotifyTableChangedAction.Delete);
  1984. return count;
  1985. }
  1986. /// <summary>
  1987. /// Backup the entire database to the specified path.
  1988. /// </summary>
  1989. /// <param name="destinationDatabasePath">Path to backup file.</param>
  1990. /// <param name="databaseName">The name of the database to backup (usually "main").</param>
  1991. public void Backup (string destinationDatabasePath, string databaseName = "main")
  1992. {
  1993. // Open the destination
  1994. var r = SQLite3.Open (destinationDatabasePath, out var destHandle);
  1995. if (r != SQLite3.Result.OK) {
  1996. throw SQLiteException.New (r, "Failed to open destination database");
  1997. }
  1998. // Init the backup
  1999. var backup = SQLite3.BackupInit (destHandle, databaseName, Handle, databaseName);
  2000. if (backup == NullBackupHandle) {
  2001. SQLite3.Close (destHandle);
  2002. throw new Exception ("Failed to create backup");
  2003. }
  2004. // Perform it
  2005. SQLite3.BackupStep (backup, -1);
  2006. SQLite3.BackupFinish (backup);
  2007. // Check for errors
  2008. r = SQLite3.GetResult (destHandle);
  2009. string msg = "";
  2010. if (r != SQLite3.Result.OK) {
  2011. msg = SQLite3.GetErrmsg (destHandle);
  2012. }
  2013. // Close everything and report errors
  2014. SQLite3.Close (destHandle);
  2015. if (r != SQLite3.Result.OK) {
  2016. throw SQLiteException.New (r, msg);
  2017. }
  2018. }
  2019. ~SQLiteConnection ()
  2020. {
  2021. Dispose (false);
  2022. }
  2023. public void Dispose ()
  2024. {
  2025. Dispose (true);
  2026. GC.SuppressFinalize (this);
  2027. }
  2028. public void Close ()
  2029. {
  2030. Dispose (true);
  2031. }
  2032. protected virtual void Dispose (bool disposing)
  2033. {
  2034. var useClose2 = LibVersionNumber >= 3007014;
  2035. if (_open && Handle != NullHandle) {
  2036. try {
  2037. if (disposing) {
  2038. lock (_insertCommandMap) {
  2039. foreach (var sqlInsertCommand in _insertCommandMap.Values) {
  2040. sqlInsertCommand.Dispose ();
  2041. }
  2042. _insertCommandMap.Clear ();
  2043. }
  2044. var r = useClose2 ? SQLite3.Close2 (Handle) : SQLite3.Close (Handle);
  2045. if (r != SQLite3.Result.OK) {
  2046. string msg = SQLite3.GetErrmsg (Handle);
  2047. throw SQLiteException.New (r, msg);
  2048. }
  2049. }
  2050. else {
  2051. var r = useClose2 ? SQLite3.Close2 (Handle) : SQLite3.Close (Handle);
  2052. }
  2053. }
  2054. finally {
  2055. Handle = NullHandle;
  2056. _open = false;
  2057. }
  2058. }
  2059. }
  2060. void OnTableChanged (TableMapping table, NotifyTableChangedAction action)
  2061. {
  2062. var ev = TableChanged;
  2063. if (ev != null)
  2064. ev (this, new NotifyTableChangedEventArgs (table, action));
  2065. }
  2066. public event EventHandler<NotifyTableChangedEventArgs> TableChanged;
  2067. }
  2068. public class NotifyTableChangedEventArgs : EventArgs
  2069. {
  2070. public TableMapping Table { get; private set; }
  2071. public NotifyTableChangedAction Action { get; private set; }
  2072. public NotifyTableChangedEventArgs (TableMapping table, NotifyTableChangedAction action)
  2073. {
  2074. Table = table;
  2075. Action = action;
  2076. }
  2077. }
  2078. public enum NotifyTableChangedAction
  2079. {
  2080. Insert,
  2081. Update,
  2082. Delete,
  2083. }
  2084. /// <summary>
  2085. /// Represents a parsed connection string.
  2086. /// </summary>
  2087. public class SQLiteConnectionString
  2088. {
  2089. const string DateTimeSqliteDefaultFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff";
  2090. public string UniqueKey { get; }
  2091. public string DatabasePath { get; }
  2092. public bool StoreDateTimeAsTicks { get; }
  2093. public bool StoreTimeSpanAsTicks { get; }
  2094. public string DateTimeStringFormat { get; }
  2095. public System.Globalization.DateTimeStyles DateTimeStyle { get; }
  2096. public object Key { get; }
  2097. public SQLiteOpenFlags OpenFlags { get; }
  2098. public Action<SQLiteConnection> PreKeyAction { get; }
  2099. public Action<SQLiteConnection> PostKeyAction { get; }
  2100. public string VfsName { get; }
  2101. #if NETFX_CORE
  2102. static readonly string MetroStyleDataPath = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
  2103. public static readonly string[] InMemoryDbPaths = new[]
  2104. {
  2105. ":memory:",
  2106. "file::memory:"
  2107. };
  2108. public static bool IsInMemoryPath(string databasePath)
  2109. {
  2110. return InMemoryDbPaths.Any(i => i.Equals(databasePath, StringComparison.OrdinalIgnoreCase));
  2111. }
  2112. #endif
  2113. /// <summary>
  2114. /// Constructs a new SQLiteConnectionString with all the data needed to open an SQLiteConnection.
  2115. /// </summary>
  2116. /// <param name="databasePath">
  2117. /// Specifies the path to the database file.
  2118. /// </param>
  2119. /// <param name="storeDateTimeAsTicks">
  2120. /// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
  2121. /// absolutely do want to store them as Ticks in all new projects. The value of false is
  2122. /// only here for backwards compatibility. There is a *significant* speed advantage, with no
  2123. /// down sides, when setting storeDateTimeAsTicks = true.
  2124. /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
  2125. /// the storeDateTimeAsTicks parameter.
  2126. /// </param>
  2127. public SQLiteConnectionString (string databasePath, bool storeDateTimeAsTicks = true)
  2128. : this (databasePath, SQLiteOpenFlags.Create | SQLiteOpenFlags.ReadWrite, storeDateTimeAsTicks)
  2129. {
  2130. }
  2131. /// <summary>
  2132. /// Constructs a new SQLiteConnectionString with all the data needed to open an SQLiteConnection.
  2133. /// </summary>
  2134. /// <param name="databasePath">
  2135. /// Specifies the path to the database file.
  2136. /// </param>
  2137. /// <param name="storeDateTimeAsTicks">
  2138. /// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
  2139. /// absolutely do want to store them as Ticks in all new projects. The value of false is
  2140. /// only here for backwards compatibility. There is a *significant* speed advantage, with no
  2141. /// down sides, when setting storeDateTimeAsTicks = true.
  2142. /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
  2143. /// the storeDateTimeAsTicks parameter.
  2144. /// </param>
  2145. /// <param name="key">
  2146. /// Specifies the encryption key to use on the database. Should be a string or a byte[].
  2147. /// </param>
  2148. /// <param name="preKeyAction">
  2149. /// Executes prior to setting key for SQLCipher databases
  2150. /// </param>
  2151. /// <param name="postKeyAction">
  2152. /// Executes after setting key for SQLCipher databases
  2153. /// </param>
  2154. /// <param name="vfsName">
  2155. /// Specifies the Virtual File System to use on the database.
  2156. /// </param>
  2157. public SQLiteConnectionString (string databasePath, bool storeDateTimeAsTicks, object key = null, Action<SQLiteConnection> preKeyAction = null, Action<SQLiteConnection> postKeyAction = null, string vfsName = null)
  2158. : this (databasePath, SQLiteOpenFlags.Create | SQLiteOpenFlags.ReadWrite, storeDateTimeAsTicks, key, preKeyAction, postKeyAction, vfsName)
  2159. {
  2160. }
  2161. /// <summary>
  2162. /// Constructs a new SQLiteConnectionString with all the data needed to open an SQLiteConnection.
  2163. /// </summary>
  2164. /// <param name="databasePath">
  2165. /// Specifies the path to the database file.
  2166. /// </param>
  2167. /// <param name="openFlags">
  2168. /// Flags controlling how the connection should be opened.
  2169. /// </param>
  2170. /// <param name="storeDateTimeAsTicks">
  2171. /// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
  2172. /// absolutely do want to store them as Ticks in all new projects. The value of false is
  2173. /// only here for backwards compatibility. There is a *significant* speed advantage, with no
  2174. /// down sides, when setting storeDateTimeAsTicks = true.
  2175. /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
  2176. /// the storeDateTimeAsTicks parameter.
  2177. /// </param>
  2178. /// <param name="key">
  2179. /// Specifies the encryption key to use on the database. Should be a string or a byte[].
  2180. /// </param>
  2181. /// <param name="preKeyAction">
  2182. /// Executes prior to setting key for SQLCipher databases
  2183. /// </param>
  2184. /// <param name="postKeyAction">
  2185. /// Executes after setting key for SQLCipher databases
  2186. /// </param>
  2187. /// <param name="vfsName">
  2188. /// Specifies the Virtual File System to use on the database.
  2189. /// </param>
  2190. /// <param name="dateTimeStringFormat">
  2191. /// Specifies the format to use when storing DateTime properties as strings.
  2192. /// </param>
  2193. /// <param name="storeTimeSpanAsTicks">
  2194. /// Specifies whether to store TimeSpan properties as ticks (true) or strings (false). You
  2195. /// absolutely do want to store them as Ticks in all new projects. The value of false is
  2196. /// only here for backwards compatibility. There is a *significant* speed advantage, with no
  2197. /// down sides, when setting storeTimeSpanAsTicks = true.
  2198. /// </param>
  2199. public SQLiteConnectionString (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks, object key = null, Action<SQLiteConnection> preKeyAction = null, Action<SQLiteConnection> postKeyAction = null, string vfsName = null, string dateTimeStringFormat = DateTimeSqliteDefaultFormat, bool storeTimeSpanAsTicks = true)
  2200. {
  2201. if (key != null && !((key is byte[]) || (key is string)))
  2202. throw new ArgumentException ("Encryption keys must be strings or byte arrays", nameof (key));
  2203. UniqueKey = string.Format ("{0}_{1:X8}", databasePath, (uint)openFlags);
  2204. StoreDateTimeAsTicks = storeDateTimeAsTicks;
  2205. StoreTimeSpanAsTicks = storeTimeSpanAsTicks;
  2206. DateTimeStringFormat = dateTimeStringFormat;
  2207. DateTimeStyle = "o".Equals (DateTimeStringFormat, StringComparison.OrdinalIgnoreCase) || "r".Equals (DateTimeStringFormat, StringComparison.OrdinalIgnoreCase) ? System.Globalization.DateTimeStyles.RoundtripKind : System.Globalization.DateTimeStyles.None;
  2208. Key = key;
  2209. PreKeyAction = preKeyAction;
  2210. PostKeyAction = postKeyAction;
  2211. OpenFlags = openFlags;
  2212. VfsName = vfsName;
  2213. #if NETFX_CORE
  2214. DatabasePath = IsInMemoryPath(databasePath)
  2215. ? databasePath
  2216. : System.IO.Path.Combine(MetroStyleDataPath, databasePath);
  2217. #else
  2218. DatabasePath = databasePath;
  2219. #endif
  2220. }
  2221. }
  2222. [AttributeUsage (AttributeTargets.Class)]
  2223. public class TableAttribute : Attribute
  2224. {
  2225. public string Name { get; set; }
  2226. /// <summary>
  2227. /// Flag whether to create the table without rowid (see https://sqlite.org/withoutrowid.html)
  2228. ///
  2229. /// The default is <c>false</c> so that sqlite adds an implicit <c>rowid</c> to every table created.
  2230. /// </summary>
  2231. public bool WithoutRowId { get; set; }
  2232. public TableAttribute (string name)
  2233. {
  2234. Name = name;
  2235. }
  2236. }
  2237. [AttributeUsage (AttributeTargets.Property)]
  2238. public class ColumnAttribute : Attribute
  2239. {
  2240. public string Name { get; set; }
  2241. public ColumnAttribute (string name)
  2242. {
  2243. Name = name;
  2244. }
  2245. }
  2246. [AttributeUsage (AttributeTargets.Property)]
  2247. public class PrimaryKeyAttribute : Attribute
  2248. {
  2249. }
  2250. [AttributeUsage (AttributeTargets.Property)]
  2251. public class AutoIncrementAttribute : Attribute
  2252. {
  2253. }
  2254. [AttributeUsage (AttributeTargets.Property, AllowMultiple = true)]
  2255. public class IndexedAttribute : Attribute
  2256. {
  2257. public string Name { get; set; }
  2258. public int Order { get; set; }
  2259. public virtual bool Unique { get; set; }
  2260. public IndexedAttribute ()
  2261. {
  2262. }
  2263. public IndexedAttribute (string name, int order)
  2264. {
  2265. Name = name;
  2266. Order = order;
  2267. }
  2268. }
  2269. [AttributeUsage (AttributeTargets.Property)]
  2270. public class IgnoreAttribute : Attribute
  2271. {
  2272. }
  2273. [AttributeUsage (AttributeTargets.Property)]
  2274. public class UniqueAttribute : IndexedAttribute
  2275. {
  2276. public override bool Unique {
  2277. get { return true; }
  2278. set { /* throw? */ }
  2279. }
  2280. }
  2281. [AttributeUsage (AttributeTargets.Property)]
  2282. public class MaxLengthAttribute : Attribute
  2283. {
  2284. public int Value { get; private set; }
  2285. public MaxLengthAttribute (int length)
  2286. {
  2287. Value = length;
  2288. }
  2289. }
  2290. public sealed class PreserveAttribute : System.Attribute
  2291. {
  2292. public bool AllMembers;
  2293. public bool Conditional;
  2294. }
  2295. /// <summary>
  2296. /// Select the collating sequence to use on a column.
  2297. /// "BINARY", "NOCASE", and "RTRIM" are supported.
  2298. /// "BINARY" is the default.
  2299. /// </summary>
  2300. [AttributeUsage (AttributeTargets.Property)]
  2301. public class CollationAttribute : Attribute
  2302. {
  2303. public string Value { get; private set; }
  2304. public CollationAttribute (string collation)
  2305. {
  2306. Value = collation;
  2307. }
  2308. }
  2309. [AttributeUsage (AttributeTargets.Property)]
  2310. public class NotNullAttribute : Attribute
  2311. {
  2312. }
  2313. [AttributeUsage (AttributeTargets.Enum)]
  2314. public class StoreAsTextAttribute : Attribute
  2315. {
  2316. }
  2317. public class TableMapping
  2318. {
  2319. public Type MappedType { get; private set; }
  2320. public string TableName { get; private set; }
  2321. public bool WithoutRowId { get; private set; }
  2322. public Column[] Columns { get; private set; }
  2323. public Column PK { get; private set; }
  2324. public string GetByPrimaryKeySql { get; private set; }
  2325. public CreateFlags CreateFlags { get; private set; }
  2326. internal MapMethod Method { get; private set; } = MapMethod.ByName;
  2327. readonly Column _autoPk;
  2328. readonly Column[] _insertColumns;
  2329. readonly Column[] _insertOrReplaceColumns;
  2330. public TableMapping (Type type, CreateFlags createFlags = CreateFlags.None,string tableName = "")
  2331. {
  2332. MappedType = type;
  2333. CreateFlags = createFlags;
  2334. var typeInfo = type.GetTypeInfo ();
  2335. #if ENABLE_IL2CPP
  2336. var tableAttr = typeInfo.GetCustomAttribute<TableAttribute> ();
  2337. #else
  2338. var tableAttr =
  2339. typeInfo.CustomAttributes
  2340. .Where (x => x.AttributeType == typeof (TableAttribute))
  2341. .Select (x => (TableAttribute)Orm.InflateAttribute (x))
  2342. .FirstOrDefault ();
  2343. #endif
  2344. TableName = string.IsNullOrEmpty(tableName)? ((tableAttr != null && !string.IsNullOrEmpty (tableAttr.Name)) ? tableAttr.Name : MappedType.Name):tableName;
  2345. WithoutRowId = tableAttr != null ? tableAttr.WithoutRowId : false;
  2346. var members = GetPublicMembers(type);
  2347. var cols = new List<Column>(members.Count);
  2348. foreach(var m in members)
  2349. {
  2350. var ignore = m.IsDefined(typeof(IgnoreAttribute), true);
  2351. if(!ignore)
  2352. cols.Add(new Column(m, createFlags));
  2353. }
  2354. Columns = cols.ToArray ();
  2355. foreach (var c in Columns) {
  2356. if (c.IsAutoInc && c.IsPK) {
  2357. _autoPk = c;
  2358. }
  2359. if (c.IsPK) {
  2360. PK = c;
  2361. }
  2362. }
  2363. HasAutoIncPK = _autoPk != null;
  2364. if (PK != null) {
  2365. GetByPrimaryKeySql = string.Format ("select * from \"{0}\" where \"{1}\" = ?", TableName, PK.Name);
  2366. }
  2367. else {
  2368. // People should not be calling Get/Find without a PK
  2369. GetByPrimaryKeySql = string.Format ("select * from \"{0}\" limit 1", TableName);
  2370. }
  2371. _insertColumns = Columns.Where (c => !c.IsAutoInc).ToArray ();
  2372. _insertOrReplaceColumns = Columns.ToArray ();
  2373. }
  2374. private readonly List<Type> SupportTypes = new List<Type>()
  2375. {
  2376. typeof(bool),
  2377. typeof(byte),
  2378. typeof(UInt16),
  2379. typeof(UInt32),
  2380. typeof(UInt64),
  2381. typeof(sbyte),
  2382. typeof(Int16),
  2383. typeof(Int32),
  2384. typeof(Int64),
  2385. typeof(float),
  2386. typeof(double),
  2387. typeof(decimal),
  2388. typeof(string),
  2389. typeof(StringBuilder),
  2390. typeof(Uri),
  2391. typeof(UriBuilder),
  2392. typeof(DateTime),
  2393. typeof(DateTimeOffset),
  2394. typeof(TimeSpan),
  2395. typeof(Guid),
  2396. typeof(byte[]),
  2397. };
  2398. private IReadOnlyCollection<MemberInfo> GetPublicMembers(Type type)
  2399. {
  2400. if(type.Name.StartsWith("ValueTuple`"))
  2401. return GetFieldsFromValueTuple(type);
  2402. var members = new List<MemberInfo>();
  2403. var memberNames = new HashSet<string>();
  2404. var newMembers = new List<MemberInfo>();
  2405. do
  2406. {
  2407. var ti = type.GetTypeInfo();
  2408. newMembers.Clear();
  2409. newMembers.AddRange(
  2410. from p in ti.DeclaredProperties
  2411. where !memberNames.Contains(p.Name) &&
  2412. p.CanRead && p.CanWrite &&
  2413. p.GetMethod != null && p.SetMethod != null &&
  2414. p.GetMethod.IsPublic && p.SetMethod.IsPublic &&
  2415. !p.GetMethod.IsStatic && !p.SetMethod.IsStatic
  2416. &&(p.PropertyType.IsEnum || (!p.PropertyType.IsEnum && SupportTypes.Contains(p.PropertyType)))
  2417. select p);
  2418. newMembers.AddRange(
  2419. from p in ti.DeclaredFields
  2420. where !memberNames.Contains(p.Name) &&
  2421. !p.IsInitOnly && p.IsPublic && !p.IsStatic
  2422. &&(p.FieldType.IsEnum || (!p.FieldType.IsEnum && SupportTypes.Contains(p.FieldType)))
  2423. select p);
  2424. members.AddRange(newMembers);
  2425. foreach(var m in newMembers)
  2426. memberNames.Add(m.Name);
  2427. type = ti.BaseType;
  2428. }
  2429. while(type != typeof(object));
  2430. return members;
  2431. }
  2432. private IReadOnlyCollection<MemberInfo> GetFieldsFromValueTuple(Type type)
  2433. {
  2434. Method = MapMethod.ByPosition;
  2435. var fields = type.GetFields();
  2436. // https://docs.microsoft.com/en-us/dotnet/api/system.valuetuple-8.rest
  2437. if(fields.Length >= 8)
  2438. throw new NotSupportedException("ValueTuple with more than 7 members not supported due to nesting; see https://docs.microsoft.com/en-us/dotnet/api/system.valuetuple-8.rest");
  2439. return fields;
  2440. }
  2441. public bool HasAutoIncPK { get; private set; }
  2442. public void SetAutoIncPK (object obj, long id)
  2443. {
  2444. if (_autoPk != null) {
  2445. _autoPk.SetValue (obj, Convert.ChangeType (id, _autoPk.ColumnType, null));
  2446. }
  2447. }
  2448. public Column[] InsertColumns {
  2449. get {
  2450. return _insertColumns;
  2451. }
  2452. }
  2453. public Column[] InsertOrReplaceColumns {
  2454. get {
  2455. return _insertOrReplaceColumns;
  2456. }
  2457. }
  2458. public Column FindColumnWithPropertyName (string propertyName)
  2459. {
  2460. var exact = Columns.FirstOrDefault (c => c.PropertyName == propertyName);
  2461. return exact;
  2462. }
  2463. public Column FindColumn (string columnName)
  2464. {
  2465. if(Method != MapMethod.ByName)
  2466. throw new InvalidOperationException($"This {nameof(TableMapping)} is not mapped by name, but {Method}.");
  2467. var exact = Columns.FirstOrDefault (c => c.Name.ToLower () == columnName.ToLower ());
  2468. return exact;
  2469. }
  2470. public class Column
  2471. {
  2472. MemberInfo _member;
  2473. public string Name { get; private set; }
  2474. public PropertyInfo PropertyInfo => _member.MemberType == MemberTypes.Property ? _member as PropertyInfo :null;
  2475. public FieldInfo FieldInfo => _member.MemberType == MemberTypes.Field ? _member as FieldInfo:null;
  2476. public bool IsProperty => _member.MemberType == MemberTypes.Property;
  2477. public string PropertyName { get { return _member.Name; } }
  2478. public Type ColumnType { get; private set; }
  2479. public string Collation { get; private set; }
  2480. public bool IsAutoInc { get; private set; }
  2481. public bool IsAutoGuid { get; private set; }
  2482. public bool IsPK { get; private set; }
  2483. public IEnumerable<IndexedAttribute> Indices { get; set; }
  2484. public bool IsNullable { get; private set; }
  2485. public int? MaxStringLength { get; private set; }
  2486. public bool StoreAsText { get; private set; }
  2487. public Column (MemberInfo member, CreateFlags createFlags = CreateFlags.None)
  2488. {
  2489. _member = member;
  2490. var memberType = GetMemberType(member);
  2491. var colAttr = member.CustomAttributes.FirstOrDefault (x => x.AttributeType == typeof (ColumnAttribute));
  2492. #if ENABLE_IL2CPP
  2493. var ca = member.GetCustomAttribute(typeof(ColumnAttribute)) as ColumnAttribute;
  2494. Name = ca == null ? member.Name : ca.Name;
  2495. #else
  2496. Name = (colAttr != null && colAttr.ConstructorArguments.Count > 0) ?
  2497. colAttr.ConstructorArguments[0].Value?.ToString () :
  2498. member.Name;
  2499. #endif
  2500. //If this type is Nullable<T> then Nullable.GetUnderlyingType returns the T, otherwise it returns null, so get the actual type instead
  2501. ColumnType = Nullable.GetUnderlyingType (memberType) ?? memberType;
  2502. Collation = Orm.Collation (member);
  2503. IsPK = Orm.IsPK (member) ||
  2504. (((createFlags & CreateFlags.ImplicitPK) == CreateFlags.ImplicitPK) &&
  2505. string.Compare (member.Name, Orm.ImplicitPkName, StringComparison.OrdinalIgnoreCase) == 0);
  2506. var isAuto = Orm.IsAutoInc (member) || (IsPK && ((createFlags & CreateFlags.AutoIncPK) == CreateFlags.AutoIncPK));
  2507. IsAutoGuid = isAuto && ColumnType == typeof (Guid);
  2508. IsAutoInc = isAuto && !IsAutoGuid;
  2509. Indices = Orm.GetIndices (member);
  2510. if (!Indices.Any ()
  2511. && !IsPK
  2512. && ((createFlags & CreateFlags.ImplicitIndex) == CreateFlags.ImplicitIndex)
  2513. && Name.EndsWith (Orm.ImplicitIndexSuffix, StringComparison.OrdinalIgnoreCase)
  2514. ) {
  2515. Indices = new IndexedAttribute[] { new IndexedAttribute () };
  2516. }
  2517. IsNullable = !(IsPK || Orm.IsMarkedNotNull (member));
  2518. MaxStringLength = Orm.MaxStringLength (member);
  2519. StoreAsText = memberType.GetTypeInfo ().CustomAttributes.Any (x => x.AttributeType == typeof (StoreAsTextAttribute));
  2520. }
  2521. public Column (PropertyInfo member, CreateFlags createFlags = CreateFlags.None)
  2522. : this((MemberInfo)member, createFlags)
  2523. { }
  2524. public void SetValue (object obj, object val)
  2525. {
  2526. if(_member is PropertyInfo propy)
  2527. {
  2528. if (val != null && ColumnType.GetTypeInfo ().IsEnum)
  2529. propy.SetValue (obj, Enum.ToObject (ColumnType, val));
  2530. else
  2531. propy.SetValue (obj, val);
  2532. }
  2533. else if(_member is FieldInfo field)
  2534. {
  2535. if (val != null && ColumnType.GetTypeInfo ().IsEnum)
  2536. field.SetValue (obj, Enum.ToObject (ColumnType, val));
  2537. else
  2538. field.SetValue (obj, val);
  2539. }
  2540. else
  2541. throw new InvalidProgramException("unreachable condition");
  2542. }
  2543. public object GetValue (object obj)
  2544. {
  2545. if(_member is PropertyInfo propy)
  2546. return propy.GetValue(obj);
  2547. else if(_member is FieldInfo field)
  2548. return field.GetValue(obj);
  2549. else
  2550. throw new InvalidProgramException("unreachable condition");
  2551. }
  2552. private static Type GetMemberType(MemberInfo m)
  2553. {
  2554. switch(m.MemberType)
  2555. {
  2556. case MemberTypes.Property: return ((PropertyInfo)m).PropertyType;
  2557. case MemberTypes.Field: return ((FieldInfo)m).FieldType;
  2558. default: throw new InvalidProgramException($"{nameof(TableMapping)} supports properties or fields only.");
  2559. }
  2560. }
  2561. }
  2562. internal enum MapMethod
  2563. {
  2564. ByName,
  2565. ByPosition
  2566. }
  2567. }
  2568. class EnumCacheInfo
  2569. {
  2570. public EnumCacheInfo (Type type)
  2571. {
  2572. var typeInfo = type.GetTypeInfo ();
  2573. IsEnum = typeInfo.IsEnum;
  2574. if (IsEnum) {
  2575. StoreAsText = typeInfo.CustomAttributes.Any (x => x.AttributeType == typeof (StoreAsTextAttribute));
  2576. if (StoreAsText) {
  2577. EnumValues = new Dictionary<int, string> ();
  2578. foreach (object e in Enum.GetValues (type)) {
  2579. EnumValues[Convert.ToInt32 (e)] = e.ToString ();
  2580. }
  2581. }
  2582. }
  2583. }
  2584. public bool IsEnum { get; private set; }
  2585. public bool StoreAsText { get; private set; }
  2586. public Dictionary<int, string> EnumValues { get; private set; }
  2587. }
  2588. static class EnumCache
  2589. {
  2590. static readonly Dictionary<Type, EnumCacheInfo> Cache = new Dictionary<Type, EnumCacheInfo> ();
  2591. public static EnumCacheInfo GetInfo<T> ()
  2592. {
  2593. return GetInfo (typeof (T));
  2594. }
  2595. public static EnumCacheInfo GetInfo (Type type)
  2596. {
  2597. lock (Cache) {
  2598. EnumCacheInfo info = null;
  2599. if (!Cache.TryGetValue (type, out info)) {
  2600. info = new EnumCacheInfo (type);
  2601. Cache[type] = info;
  2602. }
  2603. return info;
  2604. }
  2605. }
  2606. }
  2607. public static class Orm
  2608. {
  2609. public const int DefaultMaxStringLength = 140;
  2610. public const string ImplicitPkName = "Id";
  2611. public const string ImplicitIndexSuffix = "Id";
  2612. public static Type GetType (object obj)
  2613. {
  2614. if (obj == null)
  2615. return typeof (object);
  2616. var rt = obj as IReflectableType;
  2617. if (rt != null)
  2618. return rt.GetTypeInfo ().AsType ();
  2619. return obj.GetType ();
  2620. }
  2621. public static string SqlDecl (TableMapping.Column p, bool storeDateTimeAsTicks, bool storeTimeSpanAsTicks)
  2622. {
  2623. string decl = "\"" + p.Name + "\" " + SqlType (p, storeDateTimeAsTicks, storeTimeSpanAsTicks) + " ";
  2624. if (p.IsPK) {
  2625. decl += "primary key ";
  2626. }
  2627. if (p.IsAutoInc) {
  2628. decl += "autoincrement ";
  2629. }
  2630. if (!p.IsNullable) {
  2631. decl += "not null ";
  2632. }
  2633. if (!string.IsNullOrEmpty (p.Collation)) {
  2634. decl += "collate " + p.Collation + " ";
  2635. }
  2636. return decl;
  2637. }
  2638. public static string SqlType (TableMapping.Column p, bool storeDateTimeAsTicks, bool storeTimeSpanAsTicks)
  2639. {
  2640. var clrType = p.ColumnType;
  2641. if (clrType == typeof (Boolean) || clrType == typeof (Byte) || clrType == typeof (UInt16) || clrType == typeof (SByte) || clrType == typeof (Int16) || clrType == typeof (Int32) || clrType == typeof (UInt32) || clrType == typeof (Int64) || clrType == typeof (UInt64)) {
  2642. return "integer";
  2643. }
  2644. else if (clrType == typeof (Single) || clrType == typeof (Double) || clrType == typeof (Decimal)) {
  2645. return "float";
  2646. }
  2647. else if (clrType == typeof (String) || clrType == typeof (StringBuilder) || clrType == typeof (Uri) || clrType == typeof (UriBuilder)) {
  2648. int? len = p.MaxStringLength;
  2649. if (len.HasValue)
  2650. return "varchar(" + len.Value + ")";
  2651. return "varchar";
  2652. }
  2653. else if (clrType == typeof (TimeSpan)) {
  2654. return storeTimeSpanAsTicks ? "bigint" : "time";
  2655. }
  2656. else if (clrType == typeof (DateTime)) {
  2657. return storeDateTimeAsTicks ? "bigint" : "datetime";
  2658. }
  2659. else if (clrType == typeof (DateTimeOffset)) {
  2660. return "bigint";
  2661. }
  2662. else if (clrType.GetTypeInfo ().IsEnum) {
  2663. if (p.StoreAsText)
  2664. return "varchar";
  2665. else
  2666. return "integer";
  2667. }
  2668. else if (clrType == typeof (byte[])) {
  2669. return "blob";
  2670. }
  2671. else if (clrType == typeof (Guid)) {
  2672. return "varchar(36)";
  2673. }
  2674. else {
  2675. throw new NotSupportedException ("Don't know about " + clrType);
  2676. }
  2677. }
  2678. public static bool IsPK (MemberInfo p)
  2679. {
  2680. return p.CustomAttributes.Any (x => x.AttributeType == typeof (PrimaryKeyAttribute));
  2681. }
  2682. public static string Collation (MemberInfo p)
  2683. {
  2684. #if ENABLE_IL2CPP
  2685. return (p.GetCustomAttribute<CollationAttribute> ()?.Value) ?? "";
  2686. #else
  2687. return
  2688. (p.CustomAttributes
  2689. .Where (x => typeof (CollationAttribute) == x.AttributeType)
  2690. .Select (x => {
  2691. var args = x.ConstructorArguments;
  2692. return args.Count > 0 ? ((args[0].Value as string) ?? "") : "";
  2693. })
  2694. .FirstOrDefault ()) ?? "";
  2695. #endif
  2696. }
  2697. public static bool IsAutoInc (MemberInfo p)
  2698. {
  2699. return p.CustomAttributes.Any (x => x.AttributeType == typeof (AutoIncrementAttribute));
  2700. }
  2701. public static FieldInfo GetField (TypeInfo t, string name)
  2702. {
  2703. var f = t.GetDeclaredField (name);
  2704. if (f != null)
  2705. return f;
  2706. return GetField (t.BaseType.GetTypeInfo (), name);
  2707. }
  2708. public static PropertyInfo GetProperty (TypeInfo t, string name)
  2709. {
  2710. var f = t.GetDeclaredProperty (name);
  2711. if (f != null)
  2712. return f;
  2713. return GetProperty (t.BaseType.GetTypeInfo (), name);
  2714. }
  2715. public static object InflateAttribute (CustomAttributeData x)
  2716. {
  2717. var atype = x.AttributeType;
  2718. var typeInfo = atype.GetTypeInfo ();
  2719. #if ENABLE_IL2CPP
  2720. var r = Activator.CreateInstance (x.AttributeType);
  2721. #else
  2722. var args = x.ConstructorArguments.Select (a => a.Value).ToArray ();
  2723. var r = Activator.CreateInstance (x.AttributeType, args);
  2724. foreach (var arg in x.NamedArguments) {
  2725. if (arg.IsField) {
  2726. GetField (typeInfo, arg.MemberName).SetValue (r, arg.TypedValue.Value);
  2727. }
  2728. else {
  2729. GetProperty (typeInfo, arg.MemberName).SetValue (r, arg.TypedValue.Value);
  2730. }
  2731. }
  2732. #endif
  2733. return r;
  2734. }
  2735. public static IEnumerable<IndexedAttribute> GetIndices (MemberInfo p)
  2736. {
  2737. #if ENABLE_IL2CPP
  2738. return p.GetCustomAttributes<IndexedAttribute> ();
  2739. #else
  2740. var indexedInfo = typeof (IndexedAttribute).GetTypeInfo ();
  2741. return
  2742. p.CustomAttributes
  2743. .Where (x => indexedInfo.IsAssignableFrom (x.AttributeType.GetTypeInfo ()))
  2744. .Select (x => (IndexedAttribute)InflateAttribute (x));
  2745. #endif
  2746. }
  2747. public static int? MaxStringLength (MemberInfo p)
  2748. {
  2749. #if ENABLE_IL2CPP
  2750. return p.GetCustomAttribute<MaxLengthAttribute> ()?.Value;
  2751. #else
  2752. var attr = p.CustomAttributes.FirstOrDefault (x => x.AttributeType == typeof (MaxLengthAttribute));
  2753. if (attr != null) {
  2754. var attrv = (MaxLengthAttribute)InflateAttribute (attr);
  2755. return attrv.Value;
  2756. }
  2757. return null;
  2758. #endif
  2759. }
  2760. public static int? MaxStringLength (PropertyInfo p) => MaxStringLength((MemberInfo)p);
  2761. public static bool IsMarkedNotNull (MemberInfo p)
  2762. {
  2763. return p.CustomAttributes.Any (x => x.AttributeType == typeof (NotNullAttribute));
  2764. }
  2765. }
  2766. public partial class SQLiteCommand
  2767. {
  2768. SQLiteConnection _conn;
  2769. private List<Binding> _bindings;
  2770. public string CommandText { get; set; }
  2771. public SQLiteCommand (SQLiteConnection conn)
  2772. {
  2773. _conn = conn;
  2774. _bindings = new List<Binding> ();
  2775. CommandText = "";
  2776. }
  2777. public int ExecuteNonQuery ()
  2778. {
  2779. if (_conn.Trace) {
  2780. _conn.Tracer?.Invoke ("Executing: " + this);
  2781. }
  2782. var r = SQLite3.Result.OK;
  2783. var stmt = Prepare ();
  2784. r = SQLite3.Step (stmt);
  2785. Finalize (stmt);
  2786. if (r == SQLite3.Result.Done) {
  2787. int rowsAffected = SQLite3.Changes (_conn.Handle);
  2788. return rowsAffected;
  2789. }
  2790. else if (r == SQLite3.Result.Error) {
  2791. string msg = SQLite3.GetErrmsg (_conn.Handle);
  2792. throw SQLiteException.New (r, msg);
  2793. }
  2794. else if (r == SQLite3.Result.Constraint) {
  2795. if (SQLite3.ExtendedErrCode (_conn.Handle) == SQLite3.ExtendedResult.ConstraintNotNull) {
  2796. throw NotNullConstraintViolationException.New (r, SQLite3.GetErrmsg (_conn.Handle));
  2797. }
  2798. }
  2799. throw SQLiteException.New (r, SQLite3.GetErrmsg (_conn.Handle));
  2800. }
  2801. public IEnumerable<T> ExecuteDeferredQuery<T> (string tableName = "")
  2802. {
  2803. return ExecuteDeferredQuery<T> (_conn.GetMapping (typeof (T),tableName:tableName));
  2804. }
  2805. public List<T> ExecuteQuery<T> (string tableName = "")
  2806. {
  2807. return ExecuteDeferredQuery<T> (_conn.GetMapping (typeof (T),tableName:tableName)).ToList ();
  2808. }
  2809. public List<T> ExecuteQuery<T> (TableMapping map)
  2810. {
  2811. return ExecuteDeferredQuery<T> (map).ToList ();
  2812. }
  2813. /// <summary>
  2814. /// Invoked every time an instance is loaded from the database.
  2815. /// </summary>
  2816. /// <param name='obj'>
  2817. /// The newly created object.
  2818. /// </param>
  2819. /// <remarks>
  2820. /// This can be overridden in combination with the <see cref="SQLiteConnection.NewCommand"/>
  2821. /// method to hook into the life-cycle of objects.
  2822. /// </remarks>
  2823. protected virtual void OnInstanceCreated (object obj)
  2824. {
  2825. // Can be overridden.
  2826. }
  2827. public IEnumerable<T> ExecuteDeferredQuery<T> (TableMapping map)
  2828. {
  2829. if (_conn.Trace) {
  2830. _conn.Tracer?.Invoke ("Executing Query: " + this);
  2831. }
  2832. var stmt = Prepare ();
  2833. try {
  2834. var cols = new TableMapping.Column[SQLite3.ColumnCount (stmt)];
  2835. var fastColumnSetters = new Action<object, Sqlite3Statement, int>[SQLite3.ColumnCount (stmt)];
  2836. if (map.Method == TableMapping.MapMethod.ByPosition)
  2837. {
  2838. Array.Copy(map.Columns, cols, Math.Min(cols.Length, map.Columns.Length));
  2839. }
  2840. else if (map.Method == TableMapping.MapMethod.ByName) {
  2841. MethodInfo getSetter = null;
  2842. if (typeof(T) != map.MappedType) {
  2843. getSetter = typeof(FastColumnSetter)
  2844. .GetMethod (nameof(FastColumnSetter.GetFastSetter),
  2845. BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod (map.MappedType);
  2846. }
  2847. for (int i = 0; i < cols.Length; i++) {
  2848. var name = SQLite3.ColumnName16 (stmt, i);
  2849. cols[i] = map.FindColumn (name);
  2850. if (cols[i] != null)
  2851. if (getSetter != null) {
  2852. fastColumnSetters[i] = (Action<object, Sqlite3Statement, int>)getSetter.Invoke(null, new object[]{ _conn, cols[i]});
  2853. }
  2854. else {
  2855. fastColumnSetters[i] = FastColumnSetter.GetFastSetter<T>(_conn, cols[i]);
  2856. }
  2857. }
  2858. }
  2859. while (SQLite3.Step (stmt) == SQLite3.Result.Row) {
  2860. var obj = Activator.CreateInstance (map.MappedType);
  2861. for (int i = 0; i < cols.Length; i++) {
  2862. if (cols[i] == null)
  2863. continue;
  2864. if (fastColumnSetters[i] != null) {
  2865. fastColumnSetters[i].Invoke (obj, stmt, i);
  2866. }
  2867. else {
  2868. var colType = SQLite3.ColumnType (stmt, i);
  2869. var val = ReadCol (stmt, i, colType, cols[i].ColumnType);
  2870. cols[i].SetValue (obj, val);
  2871. }
  2872. }
  2873. OnInstanceCreated (obj);
  2874. yield return (T)obj;
  2875. }
  2876. }
  2877. finally {
  2878. SQLite3.Finalize (stmt);
  2879. }
  2880. }
  2881. public T ExecuteScalar<T> ()
  2882. {
  2883. if (_conn.Trace) {
  2884. _conn.Tracer?.Invoke ("Executing Query: " + this);
  2885. }
  2886. T val = default (T);
  2887. var stmt = Prepare ();
  2888. try {
  2889. var r = SQLite3.Step (stmt);
  2890. if (r == SQLite3.Result.Row) {
  2891. var colType = SQLite3.ColumnType (stmt, 0);
  2892. var colval = ReadCol (stmt, 0, colType, typeof (T));
  2893. if (colval != null) {
  2894. val = (T)colval;
  2895. }
  2896. }
  2897. else if (r == SQLite3.Result.Done) {
  2898. }
  2899. else {
  2900. throw SQLiteException.New (r, SQLite3.GetErrmsg (_conn.Handle));
  2901. }
  2902. }
  2903. finally {
  2904. Finalize (stmt);
  2905. }
  2906. return val;
  2907. }
  2908. public IEnumerable<T> ExecuteQueryScalars<T> ()
  2909. {
  2910. if (_conn.Trace) {
  2911. _conn.Tracer?.Invoke ("Executing Query: " + this);
  2912. }
  2913. var stmt = Prepare ();
  2914. try {
  2915. if (SQLite3.ColumnCount (stmt) < 1) {
  2916. throw new InvalidOperationException ("QueryScalars should return at least one column");
  2917. }
  2918. while (SQLite3.Step (stmt) == SQLite3.Result.Row) {
  2919. var colType = SQLite3.ColumnType (stmt, 0);
  2920. var val = ReadCol (stmt, 0, colType, typeof (T));
  2921. if (val == null) {
  2922. yield return default (T);
  2923. }
  2924. else {
  2925. yield return (T)val;
  2926. }
  2927. }
  2928. }
  2929. finally {
  2930. Finalize (stmt);
  2931. }
  2932. }
  2933. public void Bind (string name, object val)
  2934. {
  2935. _bindings.Add (new Binding {
  2936. Name = name,
  2937. Value = val
  2938. });
  2939. }
  2940. public void Bind (object val)
  2941. {
  2942. Bind (null, val);
  2943. }
  2944. public override string ToString ()
  2945. {
  2946. var parts = new string[1 + _bindings.Count];
  2947. parts[0] = CommandText;
  2948. var i = 1;
  2949. foreach (var b in _bindings) {
  2950. parts[i] = string.Format (" {0}: {1}", i - 1, b.Value);
  2951. i++;
  2952. }
  2953. return string.Join (Environment.NewLine, parts);
  2954. }
  2955. Sqlite3Statement Prepare ()
  2956. {
  2957. var stmt = SQLite3.Prepare2 (_conn.Handle, CommandText);
  2958. BindAll (stmt);
  2959. return stmt;
  2960. }
  2961. void Finalize (Sqlite3Statement stmt)
  2962. {
  2963. SQLite3.Finalize (stmt);
  2964. }
  2965. void BindAll (Sqlite3Statement stmt)
  2966. {
  2967. int nextIdx = 1;
  2968. foreach (var b in _bindings) {
  2969. if (b.Name != null) {
  2970. b.Index = SQLite3.BindParameterIndex (stmt, b.Name);
  2971. }
  2972. else {
  2973. b.Index = nextIdx++;
  2974. }
  2975. BindParameter (stmt, b.Index, b.Value, _conn.StoreDateTimeAsTicks, _conn.DateTimeStringFormat, _conn.StoreTimeSpanAsTicks);
  2976. }
  2977. }
  2978. static IntPtr NegativePointer = new IntPtr (-1);
  2979. internal static void BindParameter (Sqlite3Statement stmt, int index, object value, bool storeDateTimeAsTicks, string dateTimeStringFormat, bool storeTimeSpanAsTicks)
  2980. {
  2981. if (value == null) {
  2982. SQLite3.BindNull (stmt, index);
  2983. }
  2984. else {
  2985. if (value is Int32) {
  2986. SQLite3.BindInt (stmt, index, (int)value);
  2987. }
  2988. else if (value is String) {
  2989. SQLite3.BindText (stmt, index, (string)value, -1, NegativePointer);
  2990. }
  2991. else if (value is Byte || value is UInt16 || value is SByte || value is Int16) {
  2992. SQLite3.BindInt (stmt, index, Convert.ToInt32 (value));
  2993. }
  2994. else if (value is Boolean) {
  2995. SQLite3.BindInt (stmt, index, (bool)value ? 1 : 0);
  2996. }
  2997. else if (value is UInt32 || value is Int64 || value is UInt64) {
  2998. SQLite3.BindInt64 (stmt, index, Convert.ToInt64 (value));
  2999. }
  3000. else if (value is Single || value is Double || value is Decimal) {
  3001. SQLite3.BindDouble (stmt, index, Convert.ToDouble (value));
  3002. }
  3003. else if (value is TimeSpan) {
  3004. if (storeTimeSpanAsTicks) {
  3005. SQLite3.BindInt64 (stmt, index, ((TimeSpan)value).Ticks);
  3006. }
  3007. else {
  3008. SQLite3.BindText (stmt, index, ((TimeSpan)value).ToString (), -1, NegativePointer);
  3009. }
  3010. }
  3011. else if (value is DateTime) {
  3012. if (storeDateTimeAsTicks) {
  3013. SQLite3.BindInt64 (stmt, index, ((DateTime)value).Ticks);
  3014. }
  3015. else {
  3016. SQLite3.BindText (stmt, index, ((DateTime)value).ToString (dateTimeStringFormat, System.Globalization.CultureInfo.InvariantCulture), -1, NegativePointer);
  3017. }
  3018. }
  3019. else if (value is DateTimeOffset) {
  3020. SQLite3.BindInt64 (stmt, index, ((DateTimeOffset)value).UtcTicks);
  3021. }
  3022. else if (value is byte[]) {
  3023. SQLite3.BindBlob (stmt, index, (byte[])value, ((byte[])value).Length, NegativePointer);
  3024. }
  3025. else if (value is Guid) {
  3026. SQLite3.BindText (stmt, index, ((Guid)value).ToString (), 72, NegativePointer);
  3027. }
  3028. else if (value is Uri) {
  3029. SQLite3.BindText (stmt, index, ((Uri)value).ToString (), -1, NegativePointer);
  3030. }
  3031. else if (value is StringBuilder) {
  3032. SQLite3.BindText (stmt, index, ((StringBuilder)value).ToString (), -1, NegativePointer);
  3033. }
  3034. else if (value is UriBuilder) {
  3035. SQLite3.BindText (stmt, index, ((UriBuilder)value).ToString (), -1, NegativePointer);
  3036. }
  3037. else {
  3038. // Now we could possibly get an enum, retrieve cached info
  3039. var valueType = value.GetType ();
  3040. var enumInfo = EnumCache.GetInfo (valueType);
  3041. if (enumInfo.IsEnum) {
  3042. var enumIntValue = Convert.ToInt32 (value);
  3043. if (enumInfo.StoreAsText)
  3044. SQLite3.BindText (stmt, index, enumInfo.EnumValues[enumIntValue], -1, NegativePointer);
  3045. else
  3046. SQLite3.BindInt (stmt, index, enumIntValue);
  3047. }
  3048. else {
  3049. throw new NotSupportedException ("Cannot store type: " + Orm.GetType (value));
  3050. }
  3051. }
  3052. }
  3053. }
  3054. class Binding
  3055. {
  3056. public string Name { get; set; }
  3057. public object Value { get; set; }
  3058. public int Index { get; set; }
  3059. }
  3060. object ReadCol (Sqlite3Statement stmt, int index, SQLite3.ColType type, Type clrType)
  3061. {
  3062. if (type == SQLite3.ColType.Null) {
  3063. return null;
  3064. }
  3065. else {
  3066. var clrTypeInfo = clrType.GetTypeInfo ();
  3067. if (clrTypeInfo.IsGenericType && clrTypeInfo.GetGenericTypeDefinition () == typeof (Nullable<>)) {
  3068. clrType = clrTypeInfo.GenericTypeArguments[0];
  3069. clrTypeInfo = clrType.GetTypeInfo ();
  3070. }
  3071. if (clrType == typeof (String)) {
  3072. return SQLite3.ColumnString (stmt, index);
  3073. }
  3074. else if (clrType == typeof (Int32)) {
  3075. return (int)SQLite3.ColumnInt (stmt, index);
  3076. }
  3077. else if (clrType == typeof (Boolean)) {
  3078. return SQLite3.ColumnInt (stmt, index) == 1;
  3079. }
  3080. else if (clrType == typeof (double)) {
  3081. return SQLite3.ColumnDouble (stmt, index);
  3082. }
  3083. else if (clrType == typeof (float)) {
  3084. return (float)SQLite3.ColumnDouble (stmt, index);
  3085. }
  3086. else if (clrType == typeof (TimeSpan)) {
  3087. if (_conn.StoreTimeSpanAsTicks) {
  3088. return new TimeSpan (SQLite3.ColumnInt64 (stmt, index));
  3089. }
  3090. else {
  3091. var text = SQLite3.ColumnString (stmt, index);
  3092. TimeSpan resultTime;
  3093. if (!TimeSpan.TryParseExact (text, "c", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.TimeSpanStyles.None, out resultTime)) {
  3094. resultTime = TimeSpan.Parse (text);
  3095. }
  3096. return resultTime;
  3097. }
  3098. }
  3099. else if (clrType == typeof (DateTime)) {
  3100. if (_conn.StoreDateTimeAsTicks) {
  3101. return new DateTime (SQLite3.ColumnInt64 (stmt, index));
  3102. }
  3103. else {
  3104. var text = SQLite3.ColumnString (stmt, index);
  3105. DateTime resultDate;
  3106. if (!DateTime.TryParseExact (text, _conn.DateTimeStringFormat, System.Globalization.CultureInfo.InvariantCulture, _conn.DateTimeStyle, out resultDate)) {
  3107. resultDate = DateTime.Parse (text);
  3108. }
  3109. return resultDate;
  3110. }
  3111. }
  3112. else if (clrType == typeof (DateTimeOffset)) {
  3113. return new DateTimeOffset (SQLite3.ColumnInt64 (stmt, index), TimeSpan.Zero);
  3114. }
  3115. else if (clrTypeInfo.IsEnum) {
  3116. if (type == SQLite3.ColType.Text) {
  3117. var value = SQLite3.ColumnString (stmt, index);
  3118. return Enum.Parse (clrType, value.ToString (), true);
  3119. }
  3120. else
  3121. return SQLite3.ColumnInt (stmt, index);
  3122. }
  3123. else if (clrType == typeof (Int64)) {
  3124. return SQLite3.ColumnInt64 (stmt, index);
  3125. }
  3126. else if (clrType == typeof (UInt64)) {
  3127. return (ulong)SQLite3.ColumnInt64 (stmt, index);
  3128. }
  3129. else if (clrType == typeof (UInt32)) {
  3130. return (uint)SQLite3.ColumnInt64 (stmt, index);
  3131. }
  3132. else if (clrType == typeof (decimal)) {
  3133. return (decimal)SQLite3.ColumnDouble (stmt, index);
  3134. }
  3135. else if (clrType == typeof (Byte)) {
  3136. return (byte)SQLite3.ColumnInt (stmt, index);
  3137. }
  3138. else if (clrType == typeof (UInt16)) {
  3139. return (ushort)SQLite3.ColumnInt (stmt, index);
  3140. }
  3141. else if (clrType == typeof (Int16)) {
  3142. return (short)SQLite3.ColumnInt (stmt, index);
  3143. }
  3144. else if (clrType == typeof (sbyte)) {
  3145. return (sbyte)SQLite3.ColumnInt (stmt, index);
  3146. }
  3147. else if (clrType == typeof (byte[])) {
  3148. return SQLite3.ColumnByteArray (stmt, index);
  3149. }
  3150. else if (clrType == typeof (Guid)) {
  3151. var text = SQLite3.ColumnString (stmt, index);
  3152. return new Guid (text);
  3153. }
  3154. else if (clrType == typeof (Uri)) {
  3155. var text = SQLite3.ColumnString (stmt, index);
  3156. return new Uri (text);
  3157. }
  3158. else if (clrType == typeof (StringBuilder)) {
  3159. var text = SQLite3.ColumnString (stmt, index);
  3160. return new StringBuilder (text);
  3161. }
  3162. else if (clrType == typeof (UriBuilder)) {
  3163. var text = SQLite3.ColumnString (stmt, index);
  3164. return new UriBuilder (text);
  3165. }
  3166. else {
  3167. throw new NotSupportedException ("Don't know how to read " + clrType);
  3168. }
  3169. }
  3170. }
  3171. }
  3172. internal class FastColumnSetter
  3173. {
  3174. /// <summary>
  3175. /// Creates a delegate that can be used to quickly set object members from query columns.
  3176. ///
  3177. /// Note that this frontloads the slow reflection-based type checking for columns to only happen once at the beginning of a query,
  3178. /// and then afterwards each row of the query can invoke the delegate returned by this function to get much better performance (up to 10x speed boost, depending on query size and platform).
  3179. /// </summary>
  3180. /// <typeparam name="T">The type of the destination object that the query will read into</typeparam>
  3181. /// <param name="conn">The active connection. Note that this is primarily needed in order to read preferences regarding how certain data types (such as TimeSpan / DateTime) should be encoded in the database.</param>
  3182. /// <param name="column">The table mapping used to map the statement column to a member of the destination object type</param>
  3183. /// <returns>
  3184. /// A delegate for fast-setting of object members from statement columns.
  3185. ///
  3186. /// If no fast setter is available for the requested column (enums in particular cause headache), then this function returns null.
  3187. /// </returns>
  3188. internal static Action<object, Sqlite3Statement, int> GetFastSetter<T> (SQLiteConnection conn, TableMapping.Column column)
  3189. {
  3190. Action<object, Sqlite3Statement, int> fastSetter = null;
  3191. Type clrType = column.IsProperty ? column.PropertyInfo.PropertyType: column.FieldInfo.FieldType;
  3192. var clrTypeInfo = clrType.GetTypeInfo ();
  3193. if (clrTypeInfo.IsGenericType && clrTypeInfo.GetGenericTypeDefinition () == typeof (Nullable<>)) {
  3194. clrType = clrTypeInfo.GenericTypeArguments[0];
  3195. clrTypeInfo = clrType.GetTypeInfo ();
  3196. }
  3197. if (clrType == typeof (String)) {
  3198. fastSetter = CreateTypedSetterDelegate<T, string> (column, (stmt, index) => {
  3199. return SQLite3.ColumnString (stmt, index);
  3200. });
  3201. }
  3202. else if (clrType == typeof (Int32)) {
  3203. fastSetter = CreateNullableTypedSetterDelegate<T, int> (column, (stmt, index)=>{
  3204. return SQLite3.ColumnInt (stmt, index);
  3205. });
  3206. }
  3207. else if (clrType == typeof (Boolean)) {
  3208. fastSetter = CreateNullableTypedSetterDelegate<T, bool> (column, (stmt, index) => {
  3209. return SQLite3.ColumnInt (stmt, index) == 1;
  3210. });
  3211. }
  3212. else if (clrType == typeof (double)) {
  3213. fastSetter = CreateNullableTypedSetterDelegate<T, double> (column, (stmt, index) => {
  3214. return SQLite3.ColumnDouble (stmt, index);
  3215. });
  3216. }
  3217. else if (clrType == typeof (float)) {
  3218. fastSetter = CreateNullableTypedSetterDelegate<T, float> (column, (stmt, index) => {
  3219. return (float) SQLite3.ColumnDouble (stmt, index);
  3220. });
  3221. }
  3222. else if (clrType == typeof (TimeSpan)) {
  3223. if (conn.StoreTimeSpanAsTicks) {
  3224. fastSetter = CreateNullableTypedSetterDelegate<T, TimeSpan> (column, (stmt, index) => {
  3225. return new TimeSpan (SQLite3.ColumnInt64 (stmt, index));
  3226. });
  3227. }
  3228. else {
  3229. fastSetter = CreateNullableTypedSetterDelegate<T, TimeSpan> (column, (stmt, index) => {
  3230. var text = SQLite3.ColumnString (stmt, index);
  3231. TimeSpan resultTime;
  3232. if (!TimeSpan.TryParseExact (text, "c", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.TimeSpanStyles.None, out resultTime)) {
  3233. resultTime = TimeSpan.Parse (text);
  3234. }
  3235. return resultTime;
  3236. });
  3237. }
  3238. }
  3239. else if (clrType == typeof (DateTime)) {
  3240. if (conn.StoreDateTimeAsTicks) {
  3241. fastSetter = CreateNullableTypedSetterDelegate<T, DateTime> (column, (stmt, index) => {
  3242. return new DateTime (SQLite3.ColumnInt64 (stmt, index));
  3243. });
  3244. }
  3245. else {
  3246. fastSetter = CreateNullableTypedSetterDelegate<T, DateTime> (column, (stmt, index) => {
  3247. var text = SQLite3.ColumnString (stmt, index);
  3248. DateTime resultDate;
  3249. if (!DateTime.TryParseExact (text, conn.DateTimeStringFormat, System.Globalization.CultureInfo.InvariantCulture, conn.DateTimeStyle, out resultDate)) {
  3250. resultDate = DateTime.Parse (text);
  3251. }
  3252. return resultDate;
  3253. });
  3254. }
  3255. }
  3256. else if (clrType == typeof (DateTimeOffset)) {
  3257. fastSetter = CreateNullableTypedSetterDelegate<T, DateTimeOffset> (column, (stmt, index) => {
  3258. return new DateTimeOffset (SQLite3.ColumnInt64 (stmt, index), TimeSpan.Zero);
  3259. });
  3260. }
  3261. else if (clrTypeInfo.IsEnum) {
  3262. // NOTE: Not sure of a good way (if any?) to do a strongly-typed fast setter like this for enumerated types -- for now, return null and column sets will revert back to the safe (but slow) Reflection-based method of column prop.Set()
  3263. }
  3264. else if (clrType == typeof (Int64)) {
  3265. fastSetter = CreateNullableTypedSetterDelegate<T, Int64> (column, (stmt, index) => {
  3266. return SQLite3.ColumnInt64 (stmt, index);
  3267. });
  3268. }
  3269. else if (clrType == typeof(UInt64))
  3270. {
  3271. fastSetter = CreateNullableTypedSetterDelegate<T, UInt64>(column, (stmt, index) => {
  3272. return (ulong)SQLite3.ColumnInt64(stmt, index);
  3273. });
  3274. }
  3275. else if (clrType == typeof (UInt32)) {
  3276. fastSetter = CreateNullableTypedSetterDelegate<T, UInt32> (column, (stmt, index) => {
  3277. return (uint)SQLite3.ColumnInt64 (stmt, index);
  3278. });
  3279. }
  3280. else if (clrType == typeof (decimal)) {
  3281. fastSetter = CreateNullableTypedSetterDelegate<T, decimal> (column, (stmt, index) => {
  3282. return (decimal)SQLite3.ColumnDouble (stmt, index);
  3283. });
  3284. }
  3285. else if (clrType == typeof (Byte)) {
  3286. fastSetter = CreateNullableTypedSetterDelegate<T, Byte> (column, (stmt, index) => {
  3287. return (byte)SQLite3.ColumnInt (stmt, index);
  3288. });
  3289. }
  3290. else if (clrType == typeof (UInt16)) {
  3291. fastSetter = CreateNullableTypedSetterDelegate<T, UInt16> (column, (stmt, index) => {
  3292. return (ushort)SQLite3.ColumnInt (stmt, index);
  3293. });
  3294. }
  3295. else if (clrType == typeof (Int16)) {
  3296. fastSetter = CreateNullableTypedSetterDelegate<T, Int16> (column, (stmt, index) => {
  3297. return (short)SQLite3.ColumnInt (stmt, index);
  3298. });
  3299. }
  3300. else if (clrType == typeof (sbyte)) {
  3301. fastSetter = CreateNullableTypedSetterDelegate<T, sbyte> (column, (stmt, index) => {
  3302. return (sbyte)SQLite3.ColumnInt (stmt, index);
  3303. });
  3304. }
  3305. else if (clrType == typeof (byte[])) {
  3306. fastSetter = CreateTypedSetterDelegate<T, byte[]> (column, (stmt, index) => {
  3307. return SQLite3.ColumnByteArray (stmt, index);
  3308. });
  3309. }
  3310. else if (clrType == typeof (Guid)) {
  3311. fastSetter = CreateNullableTypedSetterDelegate<T, Guid> (column, (stmt, index) => {
  3312. var text = SQLite3.ColumnString (stmt, index);
  3313. return new Guid (text);
  3314. });
  3315. }
  3316. else if (clrType == typeof (Uri)) {
  3317. fastSetter = CreateTypedSetterDelegate<T, Uri> (column, (stmt, index) => {
  3318. var text = SQLite3.ColumnString (stmt, index);
  3319. return new Uri (text);
  3320. });
  3321. }
  3322. else if (clrType == typeof (StringBuilder)) {
  3323. fastSetter = CreateTypedSetterDelegate<T, StringBuilder> (column, (stmt, index) => {
  3324. var text = SQLite3.ColumnString (stmt, index);
  3325. return new StringBuilder (text);
  3326. });
  3327. }
  3328. else if (clrType == typeof (UriBuilder)) {
  3329. fastSetter = CreateTypedSetterDelegate<T, UriBuilder> (column, (stmt, index) => {
  3330. var text = SQLite3.ColumnString (stmt, index);
  3331. return new UriBuilder (text);
  3332. });
  3333. }
  3334. else {
  3335. // NOTE: Will fall back to the slow setter method in the event that we are unable to create a fast setter delegate for a particular column type
  3336. }
  3337. return fastSetter;
  3338. }
  3339. /// <summary>
  3340. /// This creates a strongly typed delegate that will permit fast setting of column values given a Sqlite3Statement and a column index.
  3341. ///
  3342. /// Note that this is identical to CreateTypedSetterDelegate(), but has an extra check to see if it should create a nullable version of the delegate.
  3343. /// </summary>
  3344. /// <typeparam name="ObjectType">The type of the object whose member column is being set</typeparam>
  3345. /// <typeparam name="ColumnMemberType">The CLR type of the member in the object which corresponds to the given SQLite columnn</typeparam>
  3346. /// <param name="column">The column mapping that identifies the target member of the destination object</param>
  3347. /// <param name="getColumnValue">A lambda that can be used to retrieve the column value at query-time</param>
  3348. /// <returns>A strongly-typed delegate</returns>
  3349. private static Action<object, Sqlite3Statement, int> CreateNullableTypedSetterDelegate<ObjectType, ColumnMemberType> (TableMapping.Column column, Func<Sqlite3Statement, int, ColumnMemberType> getColumnValue) where ColumnMemberType : struct
  3350. {
  3351. var clrTypeInfo =column.IsProperty ? column.PropertyInfo.PropertyType.GetTypeInfo():column.FieldInfo.FieldType.GetTypeInfo();
  3352. bool isNullable = false;
  3353. if (clrTypeInfo.IsGenericType && clrTypeInfo.GetGenericTypeDefinition () == typeof (Nullable<>)) {
  3354. isNullable = true;
  3355. }
  3356. if (isNullable) {
  3357. var setProperty = (Action<ObjectType, ColumnMemberType?>)Delegate.CreateDelegate (
  3358. typeof (Action<ObjectType, ColumnMemberType?>), null,
  3359. column.PropertyInfo.GetSetMethod ());
  3360. return (o, stmt, i) => {
  3361. var colType = SQLite3.ColumnType (stmt, i);
  3362. if (colType != SQLite3.ColType.Null)
  3363. setProperty.Invoke ((ObjectType)o, getColumnValue.Invoke (stmt, i));
  3364. };
  3365. }
  3366. return CreateTypedSetterDelegate<ObjectType, ColumnMemberType> (column, getColumnValue);
  3367. }
  3368. /// <summary>
  3369. /// This creates a strongly typed delegate that will permit fast setting of column values given a Sqlite3Statement and a column index.
  3370. /// </summary>
  3371. /// <typeparam name="ObjectType">The type of the object whose member column is being set</typeparam>
  3372. /// <typeparam name="ColumnMemberType">The CLR type of the member in the object which corresponds to the given SQLite columnn</typeparam>
  3373. /// <param name="column">The column mapping that identifies the target member of the destination object</param>
  3374. /// <param name="getColumnValue">A lambda that can be used to retrieve the column value at query-time</param>
  3375. /// <returns>A strongly-typed delegate</returns>
  3376. private static Action<object, Sqlite3Statement, int> CreateTypedSetterDelegate<ObjectType, ColumnMemberType> (TableMapping.Column column, Func<Sqlite3Statement, int, ColumnMemberType> getColumnValue)
  3377. {
  3378. if (column.IsProperty)
  3379. {
  3380. var setProperty = (Action<ObjectType, ColumnMemberType>)Delegate.CreateDelegate(
  3381. typeof(Action<ObjectType, ColumnMemberType>), null,
  3382. column.PropertyInfo.GetSetMethod());
  3383. return (o, stmt, i) =>
  3384. {
  3385. var colType = SQLite3.ColumnType(stmt, i);
  3386. if (colType != SQLite3.ColType.Null)
  3387. setProperty.Invoke((ObjectType)o, getColumnValue.Invoke(stmt, i));
  3388. };
  3389. }
  3390. else
  3391. {
  3392. return null;
  3393. }
  3394. }
  3395. }
  3396. /// <summary>
  3397. /// Since the insert never changed, we only need to prepare once.
  3398. /// </summary>
  3399. class PreparedSqlLiteInsertCommand : IDisposable
  3400. {
  3401. bool Initialized;
  3402. SQLiteConnection Connection;
  3403. string CommandText;
  3404. Sqlite3Statement Statement;
  3405. static readonly Sqlite3Statement NullStatement = default (Sqlite3Statement);
  3406. public PreparedSqlLiteInsertCommand (SQLiteConnection conn, string commandText)
  3407. {
  3408. Connection = conn;
  3409. CommandText = commandText;
  3410. }
  3411. public int ExecuteNonQuery (object[] source)
  3412. {
  3413. if (Initialized && Statement == NullStatement) {
  3414. throw new ObjectDisposedException (nameof (PreparedSqlLiteInsertCommand));
  3415. }
  3416. if (Connection.Trace) {
  3417. Connection.Tracer?.Invoke ("Executing: " + CommandText);
  3418. }
  3419. var r = SQLite3.Result.OK;
  3420. if (!Initialized) {
  3421. Statement = SQLite3.Prepare2 (Connection.Handle, CommandText);
  3422. Initialized = true;
  3423. }
  3424. //bind the values.
  3425. if (source != null) {
  3426. for (int i = 0; i < source.Length; i++) {
  3427. SQLiteCommand.BindParameter (Statement, i + 1, source[i], Connection.StoreDateTimeAsTicks, Connection.DateTimeStringFormat, Connection.StoreTimeSpanAsTicks);
  3428. }
  3429. }
  3430. r = SQLite3.Step (Statement);
  3431. if (r == SQLite3.Result.Done) {
  3432. int rowsAffected = SQLite3.Changes (Connection.Handle);
  3433. SQLite3.Reset (Statement);
  3434. return rowsAffected;
  3435. }
  3436. else if (r == SQLite3.Result.Error) {
  3437. string msg = SQLite3.GetErrmsg (Connection.Handle);
  3438. SQLite3.Reset (Statement);
  3439. throw SQLiteException.New (r, msg);
  3440. }
  3441. else if (r == SQLite3.Result.Constraint && SQLite3.ExtendedErrCode (Connection.Handle) == SQLite3.ExtendedResult.ConstraintNotNull) {
  3442. SQLite3.Reset (Statement);
  3443. throw NotNullConstraintViolationException.New (r, SQLite3.GetErrmsg (Connection.Handle));
  3444. }
  3445. else {
  3446. SQLite3.Reset (Statement);
  3447. throw SQLiteException.New (r, SQLite3.GetErrmsg (Connection.Handle));
  3448. }
  3449. }
  3450. public void Dispose ()
  3451. {
  3452. Dispose (true);
  3453. GC.SuppressFinalize (this);
  3454. }
  3455. void Dispose (bool disposing)
  3456. {
  3457. var s = Statement;
  3458. Statement = NullStatement;
  3459. Connection = null;
  3460. if (s != NullStatement) {
  3461. SQLite3.Finalize (s);
  3462. }
  3463. }
  3464. ~PreparedSqlLiteInsertCommand ()
  3465. {
  3466. Dispose (false);
  3467. }
  3468. }
  3469. public enum CreateTableResult
  3470. {
  3471. Created,
  3472. Migrated,
  3473. }
  3474. public class CreateTablesResult
  3475. {
  3476. public Dictionary<Type, CreateTableResult> Results { get; private set; }
  3477. public CreateTablesResult ()
  3478. {
  3479. Results = new Dictionary<Type, CreateTableResult> ();
  3480. }
  3481. }
  3482. public abstract class BaseTableQuery
  3483. {
  3484. protected class Ordering
  3485. {
  3486. public string ColumnName { get; set; }
  3487. public bool Ascending { get; set; }
  3488. }
  3489. }
  3490. public class TableQuery<T> : BaseTableQuery, IEnumerable<T>
  3491. {
  3492. public SQLiteConnection Connection { get; private set; }
  3493. public TableMapping Table { get; private set; }
  3494. Expression _where;
  3495. List<Ordering> _orderBys;
  3496. int? _limit;
  3497. int? _offset;
  3498. BaseTableQuery _joinInner;
  3499. Expression _joinInnerKeySelector;
  3500. BaseTableQuery _joinOuter;
  3501. Expression _joinOuterKeySelector;
  3502. Expression _joinSelector;
  3503. Expression _selector;
  3504. TableQuery (SQLiteConnection conn, TableMapping table)
  3505. {
  3506. Connection = conn;
  3507. Table = table;
  3508. }
  3509. public TableQuery (SQLiteConnection conn,string tableName = "")
  3510. {
  3511. Connection = conn;
  3512. Table = Connection.GetMapping (typeof (T),tableName:tableName);
  3513. }
  3514. public TableQuery<U> Clone<U> ()
  3515. {
  3516. var q = new TableQuery<U> (Connection, Table);
  3517. q._where = _where;
  3518. q._deferred = _deferred;
  3519. if (_orderBys != null) {
  3520. q._orderBys = new List<Ordering> (_orderBys);
  3521. }
  3522. q._limit = _limit;
  3523. q._offset = _offset;
  3524. q._joinInner = _joinInner;
  3525. q._joinInnerKeySelector = _joinInnerKeySelector;
  3526. q._joinOuter = _joinOuter;
  3527. q._joinOuterKeySelector = _joinOuterKeySelector;
  3528. q._joinSelector = _joinSelector;
  3529. q._selector = _selector;
  3530. return q;
  3531. }
  3532. /// <summary>
  3533. /// Filters the query based on a predicate.
  3534. /// </summary>
  3535. public TableQuery<T> Where (Expression<Func<T, bool>> predExpr)
  3536. {
  3537. if (predExpr.NodeType == ExpressionType.Lambda) {
  3538. var lambda = (LambdaExpression)predExpr;
  3539. var pred = lambda.Body;
  3540. var q = Clone<T> ();
  3541. q.AddWhere (pred);
  3542. return q;
  3543. }
  3544. else {
  3545. throw new NotSupportedException ("Must be a predicate");
  3546. }
  3547. }
  3548. /// <summary>
  3549. /// Delete all the rows that match this query.
  3550. /// </summary>
  3551. public int Delete ()
  3552. {
  3553. return Delete (null);
  3554. }
  3555. /// <summary>
  3556. /// Delete all the rows that match this query and the given predicate.
  3557. /// </summary>
  3558. public int Delete (Expression<Func<T, bool>> predExpr)
  3559. {
  3560. if (_limit.HasValue || _offset.HasValue)
  3561. throw new InvalidOperationException ("Cannot delete with limits or offsets");
  3562. if (_where == null && predExpr == null)
  3563. throw new InvalidOperationException ("No condition specified");
  3564. var pred = _where;
  3565. if (predExpr != null && predExpr.NodeType == ExpressionType.Lambda) {
  3566. var lambda = (LambdaExpression)predExpr;
  3567. pred = pred != null ? Expression.AndAlso (pred, lambda.Body) : lambda.Body;
  3568. }
  3569. var args = new List<object> ();
  3570. var cmdText = "delete from \"" + Table.TableName + "\"";
  3571. var w = CompileExpr (pred, args);
  3572. cmdText += " where " + w.CommandText;
  3573. var command = Connection.CreateCommand (cmdText, args.ToArray ());
  3574. int result = command.ExecuteNonQuery ();
  3575. return result;
  3576. }
  3577. /// <summary>
  3578. /// Yields a given number of elements from the query and then skips the remainder.
  3579. /// </summary>
  3580. public TableQuery<T> Take (int n)
  3581. {
  3582. var q = Clone<T> ();
  3583. q._limit = n;
  3584. return q;
  3585. }
  3586. /// <summary>
  3587. /// Skips a given number of elements from the query and then yields the remainder.
  3588. /// </summary>
  3589. public TableQuery<T> Skip (int n)
  3590. {
  3591. var q = Clone<T> ();
  3592. q._offset = n;
  3593. return q;
  3594. }
  3595. /// <summary>
  3596. /// Returns the element at a given index
  3597. /// </summary>
  3598. public T ElementAt (int index)
  3599. {
  3600. return Skip (index).Take (1).First ();
  3601. }
  3602. bool _deferred;
  3603. public TableQuery<T> Deferred ()
  3604. {
  3605. var q = Clone<T> ();
  3606. q._deferred = true;
  3607. return q;
  3608. }
  3609. /// <summary>
  3610. /// Order the query results according to a key.
  3611. /// </summary>
  3612. public TableQuery<T> OrderBy<U> (Expression<Func<T, U>> orderExpr)
  3613. {
  3614. return AddOrderBy<U> (orderExpr, true);
  3615. }
  3616. /// <summary>
  3617. /// Order the query results according to a key.
  3618. /// </summary>
  3619. public TableQuery<T> OrderByDescending<U> (Expression<Func<T, U>> orderExpr)
  3620. {
  3621. return AddOrderBy<U> (orderExpr, false);
  3622. }
  3623. /// <summary>
  3624. /// Order the query results according to a key.
  3625. /// </summary>
  3626. public TableQuery<T> ThenBy<U> (Expression<Func<T, U>> orderExpr)
  3627. {
  3628. return AddOrderBy<U> (orderExpr, true);
  3629. }
  3630. /// <summary>
  3631. /// Order the query results according to a key.
  3632. /// </summary>
  3633. public TableQuery<T> ThenByDescending<U> (Expression<Func<T, U>> orderExpr)
  3634. {
  3635. return AddOrderBy<U> (orderExpr, false);
  3636. }
  3637. TableQuery<T> AddOrderBy<U> (Expression<Func<T, U>> orderExpr, bool asc)
  3638. {
  3639. if (orderExpr.NodeType == ExpressionType.Lambda) {
  3640. var lambda = (LambdaExpression)orderExpr;
  3641. MemberExpression mem = null;
  3642. var unary = lambda.Body as UnaryExpression;
  3643. if (unary != null && unary.NodeType == ExpressionType.Convert) {
  3644. mem = unary.Operand as MemberExpression;
  3645. }
  3646. else {
  3647. mem = lambda.Body as MemberExpression;
  3648. }
  3649. if (mem != null && (mem.Expression.NodeType == ExpressionType.Parameter)) {
  3650. var q = Clone<T> ();
  3651. if (q._orderBys == null) {
  3652. q._orderBys = new List<Ordering> ();
  3653. }
  3654. q._orderBys.Add (new Ordering {
  3655. ColumnName = Table.FindColumnWithPropertyName (mem.Member.Name).Name,
  3656. Ascending = asc
  3657. });
  3658. return q;
  3659. }
  3660. else {
  3661. throw new NotSupportedException ("Order By does not support: " + orderExpr);
  3662. }
  3663. }
  3664. else {
  3665. throw new NotSupportedException ("Must be a predicate");
  3666. }
  3667. }
  3668. private void AddWhere (Expression pred)
  3669. {
  3670. if (_where == null) {
  3671. _where = pred;
  3672. }
  3673. else {
  3674. _where = Expression.AndAlso (_where, pred);
  3675. }
  3676. }
  3677. ///// <summary>
  3678. ///// Performs an inner join of two queries based on matching keys extracted from the elements.
  3679. ///// </summary>
  3680. //public TableQuery<TResult> Join<TInner, TKey, TResult> (
  3681. // TableQuery<TInner> inner,
  3682. // Expression<Func<T, TKey>> outerKeySelector,
  3683. // Expression<Func<TInner, TKey>> innerKeySelector,
  3684. // Expression<Func<T, TInner, TResult>> resultSelector)
  3685. //{
  3686. // var q = new TableQuery<TResult> (Connection, Connection.GetMapping (typeof (TResult))) {
  3687. // _joinOuter = this,
  3688. // _joinOuterKeySelector = outerKeySelector,
  3689. // _joinInner = inner,
  3690. // _joinInnerKeySelector = innerKeySelector,
  3691. // _joinSelector = resultSelector,
  3692. // };
  3693. // return q;
  3694. //}
  3695. // Not needed until Joins are supported
  3696. // Keeping this commented out forces the default Linq to objects processor to run
  3697. //public TableQuery<TResult> Select<TResult> (Expression<Func<T, TResult>> selector)
  3698. //{
  3699. // var q = Clone<TResult> ();
  3700. // q._selector = selector;
  3701. // return q;
  3702. //}
  3703. private SQLiteCommand GenerateCommand (string selectionList)
  3704. {
  3705. if (_joinInner != null && _joinOuter != null) {
  3706. throw new NotSupportedException ("Joins are not supported.");
  3707. }
  3708. else {
  3709. var cmdText = "select " + selectionList + " from \"" + Table.TableName + "\"";
  3710. var args = new List<object> ();
  3711. if (_where != null) {
  3712. var w = CompileExpr (_where, args);
  3713. cmdText += " where " + w.CommandText;
  3714. }
  3715. if ((_orderBys != null) && (_orderBys.Count > 0)) {
  3716. var t = string.Join (", ", _orderBys.Select (o => "\"" + o.ColumnName + "\"" + (o.Ascending ? "" : " desc")).ToArray ());
  3717. cmdText += " order by " + t;
  3718. }
  3719. if (_limit.HasValue) {
  3720. cmdText += " limit " + _limit.Value;
  3721. }
  3722. if (_offset.HasValue) {
  3723. if (!_limit.HasValue) {
  3724. cmdText += " limit -1 ";
  3725. }
  3726. cmdText += " offset " + _offset.Value;
  3727. }
  3728. return Connection.CreateCommand (cmdText, args.ToArray ());
  3729. }
  3730. }
  3731. class CompileResult
  3732. {
  3733. public string CommandText { get; set; }
  3734. public object Value { get; set; }
  3735. }
  3736. private CompileResult CompileExpr (Expression expr, List<object> queryArgs)
  3737. {
  3738. if (expr == null) {
  3739. throw new NotSupportedException ("Expression is NULL");
  3740. }
  3741. else if (expr is BinaryExpression) {
  3742. var bin = (BinaryExpression)expr;
  3743. // VB turns 'x=="foo"' into 'CompareString(x,"foo",true/false)==0', so we need to unwrap it
  3744. // http://blogs.msdn.com/b/vbteam/archive/2007/09/18/vb-expression-trees-string-comparisons.aspx
  3745. if (bin.Left.NodeType == ExpressionType.Call) {
  3746. var call = (MethodCallExpression)bin.Left;
  3747. if (call.Method.DeclaringType.FullName == "Microsoft.VisualBasic.CompilerServices.Operators"
  3748. && call.Method.Name == "CompareString")
  3749. bin = Expression.MakeBinary (bin.NodeType, call.Arguments[0], call.Arguments[1]);
  3750. }
  3751. var leftr = CompileExpr (bin.Left, queryArgs);
  3752. var rightr = CompileExpr (bin.Right, queryArgs);
  3753. //If either side is a parameter and is null, then handle the other side specially (for "is null"/"is not null")
  3754. string text;
  3755. if (leftr.CommandText == "?" && leftr.Value == null)
  3756. text = CompileNullBinaryExpression (bin, rightr);
  3757. else if (rightr.CommandText == "?" && rightr.Value == null)
  3758. text = CompileNullBinaryExpression (bin, leftr);
  3759. else
  3760. text = "(" + leftr.CommandText + " " + GetSqlName (bin) + " " + rightr.CommandText + ")";
  3761. return new CompileResult { CommandText = text };
  3762. }
  3763. else if (expr.NodeType == ExpressionType.Not) {
  3764. var operandExpr = ((UnaryExpression)expr).Operand;
  3765. var opr = CompileExpr (operandExpr, queryArgs);
  3766. object val = opr.Value;
  3767. if (val is bool)
  3768. val = !((bool)val);
  3769. return new CompileResult {
  3770. CommandText = "NOT(" + opr.CommandText + ")",
  3771. Value = val
  3772. };
  3773. }
  3774. else if (expr.NodeType == ExpressionType.Call) {
  3775. var call = (MethodCallExpression)expr;
  3776. var args = new CompileResult[call.Arguments.Count];
  3777. var obj = call.Object != null ? CompileExpr (call.Object, queryArgs) : null;
  3778. for (var i = 0; i < args.Length; i++) {
  3779. args[i] = CompileExpr (call.Arguments[i], queryArgs);
  3780. }
  3781. var sqlCall = "";
  3782. if (call.Method.Name == "Like" && args.Length == 2) {
  3783. sqlCall = "(" + args[0].CommandText + " like " + args[1].CommandText + ")";
  3784. }
  3785. else if (call.Method.Name == "Contains" && args.Length == 2) {
  3786. sqlCall = "(" + args[1].CommandText + " in " + args[0].CommandText + ")";
  3787. }
  3788. else if (call.Method.Name == "Contains" && args.Length == 1) {
  3789. if (call.Object != null && call.Object.Type == typeof (string)) {
  3790. sqlCall = "( instr(" + obj.CommandText + "," + args[0].CommandText + ") >0 )";
  3791. }
  3792. else {
  3793. sqlCall = "(" + args[0].CommandText + " in " + obj.CommandText + ")";
  3794. }
  3795. }
  3796. else if (call.Method.Name == "StartsWith" && args.Length >= 1) {
  3797. var startsWithCmpOp = StringComparison.CurrentCulture;
  3798. if (args.Length == 2) {
  3799. startsWithCmpOp = (StringComparison)args[1].Value;
  3800. }
  3801. switch (startsWithCmpOp) {
  3802. case StringComparison.Ordinal:
  3803. case StringComparison.CurrentCulture:
  3804. sqlCall = "( substr(" + obj.CommandText + ", 1, " + args[0].Value.ToString ().Length + ") = " + args[0].CommandText + ")";
  3805. break;
  3806. case StringComparison.OrdinalIgnoreCase:
  3807. case StringComparison.CurrentCultureIgnoreCase:
  3808. sqlCall = "(" + obj.CommandText + " like (" + args[0].CommandText + " || '%'))";
  3809. break;
  3810. }
  3811. }
  3812. else if (call.Method.Name == "EndsWith" && args.Length >= 1) {
  3813. var endsWithCmpOp = StringComparison.CurrentCulture;
  3814. if (args.Length == 2) {
  3815. endsWithCmpOp = (StringComparison)args[1].Value;
  3816. }
  3817. switch (endsWithCmpOp) {
  3818. case StringComparison.Ordinal:
  3819. case StringComparison.CurrentCulture:
  3820. sqlCall = "( substr(" + obj.CommandText + ", length(" + obj.CommandText + ") - " + args[0].Value.ToString ().Length + "+1, " + args[0].Value.ToString ().Length + ") = " + args[0].CommandText + ")";
  3821. break;
  3822. case StringComparison.OrdinalIgnoreCase:
  3823. case StringComparison.CurrentCultureIgnoreCase:
  3824. sqlCall = "(" + obj.CommandText + " like ('%' || " + args[0].CommandText + "))";
  3825. break;
  3826. }
  3827. }
  3828. else if (call.Method.Name == "Equals" && args.Length == 1) {
  3829. sqlCall = "(" + obj.CommandText + " = (" + args[0].CommandText + "))";
  3830. }
  3831. else if (call.Method.Name == "ToLower") {
  3832. sqlCall = "(lower(" + obj.CommandText + "))";
  3833. }
  3834. else if (call.Method.Name == "ToUpper") {
  3835. sqlCall = "(upper(" + obj.CommandText + "))";
  3836. }
  3837. else if (call.Method.Name == "Replace" && args.Length == 2) {
  3838. sqlCall = "(replace(" + obj.CommandText + "," + args[0].CommandText + "," + args[1].CommandText + "))";
  3839. }
  3840. else if (call.Method.Name == "IsNullOrEmpty" && args.Length == 1) {
  3841. sqlCall = "(" + args[0].CommandText + " is null or" + args[0].CommandText + " ='' )";
  3842. }
  3843. else {
  3844. sqlCall = call.Method.Name.ToLower () + "(" + string.Join (",", args.Select (a => a.CommandText).ToArray ()) + ")";
  3845. }
  3846. return new CompileResult { CommandText = sqlCall };
  3847. }
  3848. else if (expr.NodeType == ExpressionType.Constant) {
  3849. var c = (ConstantExpression)expr;
  3850. queryArgs.Add (c.Value);
  3851. return new CompileResult {
  3852. CommandText = "?",
  3853. Value = c.Value
  3854. };
  3855. }
  3856. else if (expr.NodeType == ExpressionType.Convert) {
  3857. var u = (UnaryExpression)expr;
  3858. var ty = u.Type;
  3859. var valr = CompileExpr (u.Operand, queryArgs);
  3860. return new CompileResult {
  3861. CommandText = valr.CommandText,
  3862. Value = valr.Value != null ? ConvertTo (valr.Value, ty) : null
  3863. };
  3864. }
  3865. else if (expr.NodeType == ExpressionType.MemberAccess) {
  3866. var mem = (MemberExpression)expr;
  3867. var paramExpr = mem.Expression as ParameterExpression;
  3868. if (paramExpr == null) {
  3869. var convert = mem.Expression as UnaryExpression;
  3870. if (convert != null && convert.NodeType == ExpressionType.Convert) {
  3871. paramExpr = convert.Operand as ParameterExpression;
  3872. }
  3873. }
  3874. if (paramExpr != null) {
  3875. //
  3876. // This is a column of our table, output just the column name
  3877. // Need to translate it if that column name is mapped
  3878. //
  3879. var columnName = Table.FindColumnWithPropertyName (mem.Member.Name).Name;
  3880. return new CompileResult { CommandText = "\"" + columnName + "\"" };
  3881. }
  3882. else {
  3883. object obj = null;
  3884. if (mem.Expression != null) {
  3885. var r = CompileExpr (mem.Expression, queryArgs);
  3886. if (r.Value == null) {
  3887. throw new NotSupportedException ("Member access failed to compile expression");
  3888. }
  3889. if (r.CommandText == "?") {
  3890. queryArgs.RemoveAt (queryArgs.Count - 1);
  3891. }
  3892. obj = r.Value;
  3893. }
  3894. //
  3895. // Get the member value
  3896. //
  3897. object val = null;
  3898. if (mem.Member is PropertyInfo) {
  3899. var m = (PropertyInfo)mem.Member;
  3900. val = m.GetValue (obj, null);
  3901. }
  3902. else if (mem.Member is FieldInfo) {
  3903. var m = (FieldInfo)mem.Member;
  3904. val = m.GetValue (obj);
  3905. }
  3906. else {
  3907. throw new NotSupportedException ("MemberExpr: " + mem.Member.GetType ());
  3908. }
  3909. //
  3910. // Work special magic for enumerables
  3911. //
  3912. if (val != null && val is System.Collections.IEnumerable && !(val is string) && !(val is System.Collections.Generic.IEnumerable<byte>)) {
  3913. var sb = new System.Text.StringBuilder ();
  3914. sb.Append ("(");
  3915. var head = "";
  3916. foreach (var a in (System.Collections.IEnumerable)val) {
  3917. queryArgs.Add (a);
  3918. sb.Append (head);
  3919. sb.Append ("?");
  3920. head = ",";
  3921. }
  3922. sb.Append (")");
  3923. return new CompileResult {
  3924. CommandText = sb.ToString (),
  3925. Value = val
  3926. };
  3927. }
  3928. else {
  3929. queryArgs.Add (val);
  3930. return new CompileResult {
  3931. CommandText = "?",
  3932. Value = val
  3933. };
  3934. }
  3935. }
  3936. }
  3937. throw new NotSupportedException ("Cannot compile: " + expr.NodeType.ToString ());
  3938. }
  3939. static object ConvertTo (object obj, Type t)
  3940. {
  3941. Type nut = Nullable.GetUnderlyingType (t);
  3942. if (nut != null) {
  3943. if (obj == null)
  3944. return null;
  3945. return Convert.ChangeType (obj, nut);
  3946. }
  3947. else {
  3948. return Convert.ChangeType (obj, t);
  3949. }
  3950. }
  3951. /// <summary>
  3952. /// Compiles a BinaryExpression where one of the parameters is null.
  3953. /// </summary>
  3954. /// <param name="expression">The expression to compile</param>
  3955. /// <param name="parameter">The non-null parameter</param>
  3956. private string CompileNullBinaryExpression (BinaryExpression expression, CompileResult parameter)
  3957. {
  3958. if (expression.NodeType == ExpressionType.Equal)
  3959. return "(" + parameter.CommandText + " is ?)";
  3960. else if (expression.NodeType == ExpressionType.NotEqual)
  3961. return "(" + parameter.CommandText + " is not ?)";
  3962. else if (expression.NodeType == ExpressionType.GreaterThan
  3963. || expression.NodeType == ExpressionType.GreaterThanOrEqual
  3964. || expression.NodeType == ExpressionType.LessThan
  3965. || expression.NodeType == ExpressionType.LessThanOrEqual)
  3966. return "(" + parameter.CommandText + " < ?)"; // always false
  3967. else
  3968. throw new NotSupportedException ("Cannot compile Null-BinaryExpression with type " + expression.NodeType.ToString ());
  3969. }
  3970. string GetSqlName (Expression expr)
  3971. {
  3972. var n = expr.NodeType;
  3973. if (n == ExpressionType.GreaterThan)
  3974. return ">";
  3975. else if (n == ExpressionType.GreaterThanOrEqual) {
  3976. return ">=";
  3977. }
  3978. else if (n == ExpressionType.LessThan) {
  3979. return "<";
  3980. }
  3981. else if (n == ExpressionType.LessThanOrEqual) {
  3982. return "<=";
  3983. }
  3984. else if (n == ExpressionType.And) {
  3985. return "&";
  3986. }
  3987. else if (n == ExpressionType.AndAlso) {
  3988. return "and";
  3989. }
  3990. else if (n == ExpressionType.Or) {
  3991. return "|";
  3992. }
  3993. else if (n == ExpressionType.OrElse) {
  3994. return "or";
  3995. }
  3996. else if (n == ExpressionType.Equal) {
  3997. return "=";
  3998. }
  3999. else if (n == ExpressionType.NotEqual) {
  4000. return "!=";
  4001. }
  4002. else {
  4003. throw new NotSupportedException ("Cannot get SQL for: " + n);
  4004. }
  4005. }
  4006. /// <summary>
  4007. /// Execute SELECT COUNT(*) on the query
  4008. /// </summary>
  4009. public int Count ()
  4010. {
  4011. return GenerateCommand ("count(*)").ExecuteScalar<int> ();
  4012. }
  4013. /// <summary>
  4014. /// Execute SELECT COUNT(*) on the query with an additional WHERE clause.
  4015. /// </summary>
  4016. public int Count (Expression<Func<T, bool>> predExpr)
  4017. {
  4018. return Where (predExpr).Count ();
  4019. }
  4020. public IEnumerator<T> GetEnumerator ()
  4021. {
  4022. if (!_deferred)
  4023. return GenerateCommand ("*").ExecuteQuery<T> ().GetEnumerator ();
  4024. return GenerateCommand ("*").ExecuteDeferredQuery<T> ().GetEnumerator ();
  4025. }
  4026. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
  4027. {
  4028. return GetEnumerator ();
  4029. }
  4030. /// <summary>
  4031. /// Queries the database and returns the results as a List.
  4032. /// </summary>
  4033. public List<T> ToList ()
  4034. {
  4035. return GenerateCommand ("*").ExecuteQuery<T> ();
  4036. }
  4037. /// <summary>
  4038. /// Queries the database and returns the results as an array.
  4039. /// </summary>
  4040. public T[] ToArray ()
  4041. {
  4042. return GenerateCommand ("*").ExecuteQuery<T> ().ToArray ();
  4043. }
  4044. /// <summary>
  4045. /// Returns the first element of this query.
  4046. /// </summary>
  4047. public T First ()
  4048. {
  4049. var query = Take (1);
  4050. return query.ToList ().First ();
  4051. }
  4052. /// <summary>
  4053. /// Returns the first element of this query, or null if no element is found.
  4054. /// </summary>
  4055. public T FirstOrDefault ()
  4056. {
  4057. var query = Take (1);
  4058. return query.ToList ().FirstOrDefault ();
  4059. }
  4060. /// <summary>
  4061. /// Returns the first element of this query that matches the predicate.
  4062. /// </summary>
  4063. public T First (Expression<Func<T, bool>> predExpr)
  4064. {
  4065. return Where (predExpr).First ();
  4066. }
  4067. /// <summary>
  4068. /// Returns the first element of this query that matches the predicate, or null
  4069. /// if no element is found.
  4070. /// </summary>
  4071. public T FirstOrDefault (Expression<Func<T, bool>> predExpr)
  4072. {
  4073. return Where (predExpr).FirstOrDefault ();
  4074. }
  4075. }
  4076. public static class SQLite3
  4077. {
  4078. public enum Result : int
  4079. {
  4080. OK = 0,
  4081. Error = 1,
  4082. Internal = 2,
  4083. Perm = 3,
  4084. Abort = 4,
  4085. Busy = 5,
  4086. Locked = 6,
  4087. NoMem = 7,
  4088. ReadOnly = 8,
  4089. Interrupt = 9,
  4090. IOError = 10,
  4091. Corrupt = 11,
  4092. NotFound = 12,
  4093. Full = 13,
  4094. CannotOpen = 14,
  4095. LockErr = 15,
  4096. Empty = 16,
  4097. SchemaChngd = 17,
  4098. TooBig = 18,
  4099. Constraint = 19,
  4100. Mismatch = 20,
  4101. Misuse = 21,
  4102. NotImplementedLFS = 22,
  4103. AccessDenied = 23,
  4104. Format = 24,
  4105. Range = 25,
  4106. NonDBFile = 26,
  4107. Notice = 27,
  4108. Warning = 28,
  4109. Row = 100,
  4110. Done = 101
  4111. }
  4112. public enum ExtendedResult : int
  4113. {
  4114. IOErrorRead = (Result.IOError | (1 << 8)),
  4115. IOErrorShortRead = (Result.IOError | (2 << 8)),
  4116. IOErrorWrite = (Result.IOError | (3 << 8)),
  4117. IOErrorFsync = (Result.IOError | (4 << 8)),
  4118. IOErrorDirFSync = (Result.IOError | (5 << 8)),
  4119. IOErrorTruncate = (Result.IOError | (6 << 8)),
  4120. IOErrorFStat = (Result.IOError | (7 << 8)),
  4121. IOErrorUnlock = (Result.IOError | (8 << 8)),
  4122. IOErrorRdlock = (Result.IOError | (9 << 8)),
  4123. IOErrorDelete = (Result.IOError | (10 << 8)),
  4124. IOErrorBlocked = (Result.IOError | (11 << 8)),
  4125. IOErrorNoMem = (Result.IOError | (12 << 8)),
  4126. IOErrorAccess = (Result.IOError | (13 << 8)),
  4127. IOErrorCheckReservedLock = (Result.IOError | (14 << 8)),
  4128. IOErrorLock = (Result.IOError | (15 << 8)),
  4129. IOErrorClose = (Result.IOError | (16 << 8)),
  4130. IOErrorDirClose = (Result.IOError | (17 << 8)),
  4131. IOErrorSHMOpen = (Result.IOError | (18 << 8)),
  4132. IOErrorSHMSize = (Result.IOError | (19 << 8)),
  4133. IOErrorSHMLock = (Result.IOError | (20 << 8)),
  4134. IOErrorSHMMap = (Result.IOError | (21 << 8)),
  4135. IOErrorSeek = (Result.IOError | (22 << 8)),
  4136. IOErrorDeleteNoEnt = (Result.IOError | (23 << 8)),
  4137. IOErrorMMap = (Result.IOError | (24 << 8)),
  4138. LockedSharedcache = (Result.Locked | (1 << 8)),
  4139. BusyRecovery = (Result.Busy | (1 << 8)),
  4140. CannottOpenNoTempDir = (Result.CannotOpen | (1 << 8)),
  4141. CannotOpenIsDir = (Result.CannotOpen | (2 << 8)),
  4142. CannotOpenFullPath = (Result.CannotOpen | (3 << 8)),
  4143. CorruptVTab = (Result.Corrupt | (1 << 8)),
  4144. ReadonlyRecovery = (Result.ReadOnly | (1 << 8)),
  4145. ReadonlyCannotLock = (Result.ReadOnly | (2 << 8)),
  4146. ReadonlyRollback = (Result.ReadOnly | (3 << 8)),
  4147. AbortRollback = (Result.Abort | (2 << 8)),
  4148. ConstraintCheck = (Result.Constraint | (1 << 8)),
  4149. ConstraintCommitHook = (Result.Constraint | (2 << 8)),
  4150. ConstraintForeignKey = (Result.Constraint | (3 << 8)),
  4151. ConstraintFunction = (Result.Constraint | (4 << 8)),
  4152. ConstraintNotNull = (Result.Constraint | (5 << 8)),
  4153. ConstraintPrimaryKey = (Result.Constraint | (6 << 8)),
  4154. ConstraintTrigger = (Result.Constraint | (7 << 8)),
  4155. ConstraintUnique = (Result.Constraint | (8 << 8)),
  4156. ConstraintVTab = (Result.Constraint | (9 << 8)),
  4157. NoticeRecoverWAL = (Result.Notice | (1 << 8)),
  4158. NoticeRecoverRollback = (Result.Notice | (2 << 8))
  4159. }
  4160. public enum ConfigOption : int
  4161. {
  4162. SingleThread = 1,
  4163. MultiThread = 2,
  4164. Serialized = 3
  4165. }
  4166. const string LibraryPath = "libsqlite3.so.0";
  4167. //const string LibraryPath = "e_sqlite3.dll";
  4168. #if !USE_CSHARP_SQLITE && !USE_WP8_NATIVE_SQLITE && !USE_SQLITEPCL_RAW
  4169. [DllImport(LibraryPath, EntryPoint = "sqlite3_threadsafe", CallingConvention=CallingConvention.Cdecl)]
  4170. public static extern int Threadsafe ();
  4171. [DllImport(LibraryPath, EntryPoint = "sqlite3_open", CallingConvention=CallingConvention.Cdecl)]
  4172. public static extern Result Open ([MarshalAs(UnmanagedType.LPStr)] string filename, out IntPtr db);
  4173. [DllImport(LibraryPath, EntryPoint = "sqlite3_open_v2", CallingConvention=CallingConvention.Cdecl)]
  4174. public static extern Result Open ([MarshalAs(UnmanagedType.LPStr)] string filename, out IntPtr db, int flags, [MarshalAs (UnmanagedType.LPStr)] string zvfs);
  4175. [DllImport(LibraryPath, EntryPoint = "sqlite3_open_v2", CallingConvention = CallingConvention.Cdecl)]
  4176. public static extern Result Open(byte[] filename, out IntPtr db, int flags, [MarshalAs (UnmanagedType.LPStr)] string zvfs);
  4177. [DllImport(LibraryPath, EntryPoint = "sqlite3_open16", CallingConvention = CallingConvention.Cdecl)]
  4178. public static extern Result Open16([MarshalAs(UnmanagedType.LPWStr)] string filename, out IntPtr db);
  4179. [DllImport(LibraryPath, EntryPoint = "sqlite3_enable_load_extension", CallingConvention=CallingConvention.Cdecl)]
  4180. public static extern Result EnableLoadExtension (IntPtr db, int onoff);
  4181. [DllImport(LibraryPath, EntryPoint = "sqlite3_close", CallingConvention=CallingConvention.Cdecl)]
  4182. public static extern Result Close (IntPtr db);
  4183. [DllImport(LibraryPath, EntryPoint = "sqlite3_close_v2", CallingConvention = CallingConvention.Cdecl)]
  4184. public static extern Result Close2(IntPtr db);
  4185. [DllImport(LibraryPath, EntryPoint = "sqlite3_initialize", CallingConvention=CallingConvention.Cdecl)]
  4186. public static extern Result Initialize();
  4187. [DllImport(LibraryPath, EntryPoint = "sqlite3_shutdown", CallingConvention=CallingConvention.Cdecl)]
  4188. public static extern Result Shutdown();
  4189. [DllImport(LibraryPath, EntryPoint = "sqlite3_config", CallingConvention=CallingConvention.Cdecl)]
  4190. public static extern Result Config (ConfigOption option);
  4191. [DllImport(LibraryPath, EntryPoint = "sqlite3_win32_set_directory", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Unicode)]
  4192. public static extern int SetDirectory (uint directoryType, string directoryPath);
  4193. [DllImport(LibraryPath, EntryPoint = "sqlite3_busy_timeout", CallingConvention=CallingConvention.Cdecl)]
  4194. public static extern Result BusyTimeout (IntPtr db, int milliseconds);
  4195. [DllImport(LibraryPath, EntryPoint = "sqlite3_changes", CallingConvention=CallingConvention.Cdecl)]
  4196. public static extern int Changes (IntPtr db);
  4197. [DllImport(LibraryPath, EntryPoint = "sqlite3_prepare_v2", CallingConvention=CallingConvention.Cdecl)]
  4198. public static extern Result Prepare2 (IntPtr db, [MarshalAs(UnmanagedType.LPStr)] string sql, int numBytes, out IntPtr stmt, IntPtr pzTail);
  4199. #if NETFX_CORE
  4200. [DllImport (LibraryPath, EntryPoint = "sqlite3_prepare_v2", CallingConvention = CallingConvention.Cdecl)]
  4201. public static extern Result Prepare2 (IntPtr db, byte[] queryBytes, int numBytes, out IntPtr stmt, IntPtr pzTail);
  4202. #endif
  4203. public static IntPtr Prepare2 (IntPtr db, string query)
  4204. {
  4205. IntPtr stmt;
  4206. #if NETFX_CORE
  4207. byte[] queryBytes = System.Text.UTF8Encoding.UTF8.GetBytes (query);
  4208. var r = Prepare2 (db, queryBytes, queryBytes.Length, out stmt, IntPtr.Zero);
  4209. #else
  4210. var r = Prepare2 (db, query, System.Text.UTF8Encoding.UTF8.GetByteCount (query), out stmt, IntPtr.Zero);
  4211. #endif
  4212. if (r != Result.OK) {
  4213. throw SQLiteException.New (r, GetErrmsg (db));
  4214. }
  4215. return stmt;
  4216. }
  4217. [DllImport(LibraryPath, EntryPoint = "sqlite3_step", CallingConvention=CallingConvention.Cdecl)]
  4218. public static extern Result Step (IntPtr stmt);
  4219. [DllImport(LibraryPath, EntryPoint = "sqlite3_reset", CallingConvention=CallingConvention.Cdecl)]
  4220. public static extern Result Reset (IntPtr stmt);
  4221. [DllImport(LibraryPath, EntryPoint = "sqlite3_finalize", CallingConvention=CallingConvention.Cdecl)]
  4222. public static extern Result Finalize (IntPtr stmt);
  4223. [DllImport(LibraryPath, EntryPoint = "sqlite3_last_insert_rowid", CallingConvention=CallingConvention.Cdecl)]
  4224. public static extern long LastInsertRowid (IntPtr db);
  4225. [DllImport(LibraryPath, EntryPoint = "sqlite3_errmsg16", CallingConvention=CallingConvention.Cdecl)]
  4226. public static extern IntPtr Errmsg (IntPtr db);
  4227. public static string GetErrmsg (IntPtr db)
  4228. {
  4229. return Marshal.PtrToStringUni (Errmsg (db));
  4230. }
  4231. [DllImport(LibraryPath, EntryPoint = "sqlite3_bind_parameter_index", CallingConvention=CallingConvention.Cdecl)]
  4232. public static extern int BindParameterIndex (IntPtr stmt, [MarshalAs(UnmanagedType.LPStr)] string name);
  4233. [DllImport(LibraryPath, EntryPoint = "sqlite3_bind_null", CallingConvention=CallingConvention.Cdecl)]
  4234. public static extern int BindNull (IntPtr stmt, int index);
  4235. [DllImport(LibraryPath, EntryPoint = "sqlite3_bind_int", CallingConvention=CallingConvention.Cdecl)]
  4236. public static extern int BindInt (IntPtr stmt, int index, int val);
  4237. [DllImport(LibraryPath, EntryPoint = "sqlite3_bind_int64", CallingConvention=CallingConvention.Cdecl)]
  4238. public static extern int BindInt64 (IntPtr stmt, int index, long val);
  4239. [DllImport(LibraryPath, EntryPoint = "sqlite3_bind_double", CallingConvention=CallingConvention.Cdecl)]
  4240. public static extern int BindDouble (IntPtr stmt, int index, double val);
  4241. [DllImport(LibraryPath, EntryPoint = "sqlite3_bind_text16", CallingConvention=CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
  4242. public static extern int BindText (IntPtr stmt, int index, [MarshalAs(UnmanagedType.LPWStr)] string val, int n, IntPtr free);
  4243. [DllImport(LibraryPath, EntryPoint = "sqlite3_bind_blob", CallingConvention=CallingConvention.Cdecl)]
  4244. public static extern int BindBlob (IntPtr stmt, int index, byte[] val, int n, IntPtr free);
  4245. [DllImport(LibraryPath, EntryPoint = "sqlite3_column_count", CallingConvention=CallingConvention.Cdecl)]
  4246. public static extern int ColumnCount (IntPtr stmt);
  4247. [DllImport(LibraryPath, EntryPoint = "sqlite3_column_name", CallingConvention=CallingConvention.Cdecl)]
  4248. public static extern IntPtr ColumnName (IntPtr stmt, int index);
  4249. [DllImport(LibraryPath, EntryPoint = "sqlite3_column_name16", CallingConvention=CallingConvention.Cdecl)]
  4250. static extern IntPtr ColumnName16Internal (IntPtr stmt, int index);
  4251. public static string ColumnName16(IntPtr stmt, int index)
  4252. {
  4253. return Marshal.PtrToStringUni(ColumnName16Internal(stmt, index));
  4254. }
  4255. [DllImport(LibraryPath, EntryPoint = "sqlite3_column_type", CallingConvention=CallingConvention.Cdecl)]
  4256. public static extern ColType ColumnType (IntPtr stmt, int index);
  4257. [DllImport(LibraryPath, EntryPoint = "sqlite3_column_int", CallingConvention=CallingConvention.Cdecl)]
  4258. public static extern int ColumnInt (IntPtr stmt, int index);
  4259. [DllImport(LibraryPath, EntryPoint = "sqlite3_column_int64", CallingConvention=CallingConvention.Cdecl)]
  4260. public static extern long ColumnInt64 (IntPtr stmt, int index);
  4261. [DllImport(LibraryPath, EntryPoint = "sqlite3_column_double", CallingConvention=CallingConvention.Cdecl)]
  4262. public static extern double ColumnDouble (IntPtr stmt, int index);
  4263. [DllImport(LibraryPath, EntryPoint = "sqlite3_column_text", CallingConvention=CallingConvention.Cdecl)]
  4264. public static extern IntPtr ColumnText (IntPtr stmt, int index);
  4265. [DllImport(LibraryPath, EntryPoint = "sqlite3_column_text16", CallingConvention=CallingConvention.Cdecl)]
  4266. public static extern IntPtr ColumnText16 (IntPtr stmt, int index);
  4267. [DllImport(LibraryPath, EntryPoint = "sqlite3_column_blob", CallingConvention=CallingConvention.Cdecl)]
  4268. public static extern IntPtr ColumnBlob (IntPtr stmt, int index);
  4269. [DllImport(LibraryPath, EntryPoint = "sqlite3_column_bytes", CallingConvention=CallingConvention.Cdecl)]
  4270. public static extern int ColumnBytes (IntPtr stmt, int index);
  4271. public static string ColumnString (IntPtr stmt, int index)
  4272. {
  4273. return Marshal.PtrToStringUni (SQLite3.ColumnText16 (stmt, index));
  4274. }
  4275. public static byte[] ColumnByteArray (IntPtr stmt, int index)
  4276. {
  4277. int length = ColumnBytes (stmt, index);
  4278. var result = new byte[length];
  4279. if (length > 0)
  4280. Marshal.Copy (ColumnBlob (stmt, index), result, 0, length);
  4281. return result;
  4282. }
  4283. [DllImport (LibraryPath, EntryPoint = "sqlite3_errcode", CallingConvention = CallingConvention.Cdecl)]
  4284. public static extern Result GetResult (Sqlite3DatabaseHandle db);
  4285. [DllImport (LibraryPath, EntryPoint = "sqlite3_extended_errcode", CallingConvention = CallingConvention.Cdecl)]
  4286. public static extern ExtendedResult ExtendedErrCode (IntPtr db);
  4287. [DllImport (LibraryPath, EntryPoint = "sqlite3_libversion_number", CallingConvention = CallingConvention.Cdecl)]
  4288. public static extern int LibVersionNumber ();
  4289. [DllImport (LibraryPath, EntryPoint = "sqlite3_backup_init", CallingConvention = CallingConvention.Cdecl)]
  4290. public static extern Sqlite3BackupHandle BackupInit (Sqlite3DatabaseHandle destDb, [MarshalAs (UnmanagedType.LPStr)] string destName, Sqlite3DatabaseHandle sourceDb, [MarshalAs (UnmanagedType.LPStr)] string sourceName);
  4291. [DllImport (LibraryPath, EntryPoint = "sqlite3_backup_step", CallingConvention = CallingConvention.Cdecl)]
  4292. public static extern Result BackupStep (Sqlite3BackupHandle backup, int numPages);
  4293. [DllImport (LibraryPath, EntryPoint = "sqlite3_backup_finish", CallingConvention = CallingConvention.Cdecl)]
  4294. public static extern Result BackupFinish (Sqlite3BackupHandle backup);
  4295. #else
  4296. public static Result Open (string filename, out Sqlite3DatabaseHandle db)
  4297. {
  4298. return (Result)Sqlite3.sqlite3_open (filename, out db);
  4299. }
  4300. public static Result Open (string filename, out Sqlite3DatabaseHandle db, int flags, string vfsName)
  4301. {
  4302. #if USE_WP8_NATIVE_SQLITE
  4303. return (Result)Sqlite3.sqlite3_open_v2(filename, out db, flags, vfsName ?? "");
  4304. #else
  4305. return (Result)Sqlite3.sqlite3_open_v2 (filename, out db, flags, vfsName);
  4306. #endif
  4307. }
  4308. public static Result Close (Sqlite3DatabaseHandle db)
  4309. {
  4310. return (Result)Sqlite3.sqlite3_close (db);
  4311. }
  4312. public static Result Close2 (Sqlite3DatabaseHandle db)
  4313. {
  4314. return (Result)Sqlite3.sqlite3_close_v2 (db);
  4315. }
  4316. public static Result BusyTimeout (Sqlite3DatabaseHandle db, int milliseconds)
  4317. {
  4318. return (Result)Sqlite3.sqlite3_busy_timeout (db, milliseconds);
  4319. }
  4320. public static int Changes (Sqlite3DatabaseHandle db)
  4321. {
  4322. return Sqlite3.sqlite3_changes (db);
  4323. }
  4324. public static Sqlite3Statement Prepare2 (Sqlite3DatabaseHandle db, string query)
  4325. {
  4326. Sqlite3Statement stmt = default (Sqlite3Statement);
  4327. #if USE_WP8_NATIVE_SQLITE || USE_SQLITEPCL_RAW
  4328. var r = Sqlite3.sqlite3_prepare_v2 (db, query, out stmt);
  4329. #else
  4330. stmt = new Sqlite3Statement();
  4331. var r = Sqlite3.sqlite3_prepare_v2(db, query, -1, ref stmt, 0);
  4332. #endif
  4333. if (r != 0) {
  4334. throw SQLiteException.New ((Result)r, GetErrmsg (db));
  4335. }
  4336. return stmt;
  4337. }
  4338. public static Result Step (Sqlite3Statement stmt)
  4339. {
  4340. return (Result)Sqlite3.sqlite3_step (stmt);
  4341. }
  4342. public static Result Reset (Sqlite3Statement stmt)
  4343. {
  4344. return (Result)Sqlite3.sqlite3_reset (stmt);
  4345. }
  4346. public static Result Finalize (Sqlite3Statement stmt)
  4347. {
  4348. return (Result)Sqlite3.sqlite3_finalize (stmt);
  4349. }
  4350. public static long LastInsertRowid (Sqlite3DatabaseHandle db)
  4351. {
  4352. return Sqlite3.sqlite3_last_insert_rowid (db);
  4353. }
  4354. public static string GetErrmsg (Sqlite3DatabaseHandle db)
  4355. {
  4356. return Sqlite3.sqlite3_errmsg (db).utf8_to_string ();
  4357. }
  4358. public static int BindParameterIndex (Sqlite3Statement stmt, string name)
  4359. {
  4360. return Sqlite3.sqlite3_bind_parameter_index (stmt, name);
  4361. }
  4362. public static int BindNull (Sqlite3Statement stmt, int index)
  4363. {
  4364. return Sqlite3.sqlite3_bind_null (stmt, index);
  4365. }
  4366. public static int BindInt (Sqlite3Statement stmt, int index, int val)
  4367. {
  4368. return Sqlite3.sqlite3_bind_int (stmt, index, val);
  4369. }
  4370. public static int BindInt64 (Sqlite3Statement stmt, int index, long val)
  4371. {
  4372. return Sqlite3.sqlite3_bind_int64 (stmt, index, val);
  4373. }
  4374. public static int BindDouble (Sqlite3Statement stmt, int index, double val)
  4375. {
  4376. return Sqlite3.sqlite3_bind_double (stmt, index, val);
  4377. }
  4378. public static int BindText (Sqlite3Statement stmt, int index, string val, int n, IntPtr free)
  4379. {
  4380. #if USE_WP8_NATIVE_SQLITE
  4381. return Sqlite3.sqlite3_bind_text(stmt, index, val, n);
  4382. #elif USE_SQLITEPCL_RAW
  4383. return Sqlite3.sqlite3_bind_text (stmt, index, val);
  4384. #else
  4385. return Sqlite3.sqlite3_bind_text(stmt, index, val, n, null);
  4386. #endif
  4387. }
  4388. public static int BindBlob (Sqlite3Statement stmt, int index, byte[] val, int n, IntPtr free)
  4389. {
  4390. #if USE_WP8_NATIVE_SQLITE
  4391. return Sqlite3.sqlite3_bind_blob(stmt, index, val, n);
  4392. #elif USE_SQLITEPCL_RAW
  4393. return Sqlite3.sqlite3_bind_blob (stmt, index, val);
  4394. #else
  4395. return Sqlite3.sqlite3_bind_blob(stmt, index, val, n, null);
  4396. #endif
  4397. }
  4398. public static int ColumnCount (Sqlite3Statement stmt)
  4399. {
  4400. return Sqlite3.sqlite3_column_count (stmt);
  4401. }
  4402. public static string ColumnName (Sqlite3Statement stmt, int index)
  4403. {
  4404. return Sqlite3.sqlite3_column_name (stmt, index).utf8_to_string ();
  4405. }
  4406. public static string ColumnName16 (Sqlite3Statement stmt, int index)
  4407. {
  4408. return Sqlite3.sqlite3_column_name (stmt, index).utf8_to_string ();
  4409. }
  4410. public static ColType ColumnType (Sqlite3Statement stmt, int index)
  4411. {
  4412. return (ColType)Sqlite3.sqlite3_column_type (stmt, index);
  4413. }
  4414. public static int ColumnInt (Sqlite3Statement stmt, int index)
  4415. {
  4416. return Sqlite3.sqlite3_column_int (stmt, index);
  4417. }
  4418. public static long ColumnInt64 (Sqlite3Statement stmt, int index)
  4419. {
  4420. return Sqlite3.sqlite3_column_int64 (stmt, index);
  4421. }
  4422. public static double ColumnDouble (Sqlite3Statement stmt, int index)
  4423. {
  4424. return Sqlite3.sqlite3_column_double (stmt, index);
  4425. }
  4426. public static string ColumnText (Sqlite3Statement stmt, int index)
  4427. {
  4428. return Sqlite3.sqlite3_column_text (stmt, index).utf8_to_string ();
  4429. }
  4430. public static string ColumnText16 (Sqlite3Statement stmt, int index)
  4431. {
  4432. return Sqlite3.sqlite3_column_text (stmt, index).utf8_to_string ();
  4433. }
  4434. public static byte[] ColumnBlob (Sqlite3Statement stmt, int index)
  4435. {
  4436. return Sqlite3.sqlite3_column_blob (stmt, index).ToArray ();
  4437. }
  4438. public static int ColumnBytes (Sqlite3Statement stmt, int index)
  4439. {
  4440. return Sqlite3.sqlite3_column_bytes (stmt, index);
  4441. }
  4442. public static string ColumnString (Sqlite3Statement stmt, int index)
  4443. {
  4444. return Sqlite3.sqlite3_column_text (stmt, index).utf8_to_string ();
  4445. }
  4446. public static byte[] ColumnByteArray (Sqlite3Statement stmt, int index)
  4447. {
  4448. int length = ColumnBytes (stmt, index);
  4449. if (length > 0) {
  4450. return ColumnBlob (stmt, index);
  4451. }
  4452. return new byte[0];
  4453. }
  4454. public static Result EnableLoadExtension (Sqlite3DatabaseHandle db, int onoff)
  4455. {
  4456. return (Result)Sqlite3.sqlite3_enable_load_extension (db, onoff);
  4457. }
  4458. public static int LibVersionNumber ()
  4459. {
  4460. return Sqlite3.sqlite3_libversion_number ();
  4461. }
  4462. public static Result GetResult (Sqlite3DatabaseHandle db)
  4463. {
  4464. return (Result)Sqlite3.sqlite3_errcode (db);
  4465. }
  4466. public static ExtendedResult ExtendedErrCode (Sqlite3DatabaseHandle db)
  4467. {
  4468. return (ExtendedResult)Sqlite3.sqlite3_extended_errcode (db);
  4469. }
  4470. public static Sqlite3BackupHandle BackupInit (Sqlite3DatabaseHandle destDb, string destName, Sqlite3DatabaseHandle sourceDb, string sourceName)
  4471. {
  4472. return Sqlite3.sqlite3_backup_init (destDb, destName, sourceDb, sourceName);
  4473. }
  4474. public static Result BackupStep (Sqlite3BackupHandle backup, int numPages)
  4475. {
  4476. return (Result)Sqlite3.sqlite3_backup_step (backup, numPages);
  4477. }
  4478. public static Result BackupFinish (Sqlite3BackupHandle backup)
  4479. {
  4480. return (Result)Sqlite3.sqlite3_backup_finish (backup);
  4481. }
  4482. #endif
  4483. public enum ColType : int
  4484. {
  4485. Integer = 1,
  4486. Float = 2,
  4487. Text = 3,
  4488. Blob = 4,
  4489. Null = 5
  4490. }
  4491. }
  4492. }