sqlite3conn.pp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038
  1. {
  2. This file is part of the Free Pascal Classes Library (FCL).
  3. Copyright (c) 2006 by the Free Pascal development team
  4. SQLite3 connection for SQLDB
  5. See the File COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {
  12. Based on an implementation by Martin Schreiber, part of MSEIDE.
  13. Reworked all code so it conforms to FCL coding standards.
  14. TSQLite3Connection properties
  15. Params - "foreign_keys=ON" - enable foreign key support for this connection:
  16. http://www.sqlite.org/foreignkeys.html#fk_enable
  17. }
  18. unit sqlite3conn;
  19. {$mode objfpc}
  20. {$h+}
  21. interface
  22. uses
  23. classes, db, bufdataset, sqldb, sqlite3dyn, types;
  24. const
  25. sqliteerrormax = 99;
  26. type
  27. PDateTime = ^TDateTime;
  28. TSqliteOption = (sloTransactions,sloDesignTransactions);
  29. TSqliteOptions = set of TSqliteOption;
  30. TStringArray = Array of string;
  31. PStringArray = ^TStringArray;
  32. TArrayStringArray = Array of TStringArray;
  33. PArrayStringArray = ^TArrayStringArray;
  34. { TSQLite3Connection }
  35. TSQLite3Connection = class(TSQLConnection)
  36. private
  37. fhandle: psqlite3;
  38. foptions: TSQLiteOptions;
  39. procedure setoptions(const avalue: tsqliteoptions);
  40. protected
  41. function stringsquery(const asql: string): TArrayStringArray;
  42. procedure checkerror(const aerror: integer);
  43. procedure DoInternalConnect; override;
  44. procedure DoInternalDisconnect; override;
  45. function GetHandle : pointer; override;
  46. Function AllocateCursorHandle : TSQLCursor; override;
  47. //aowner used as blob cache
  48. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); override;
  49. Function AllocateTransactionHandle : TSQLHandle; override;
  50. procedure PrepareStatement(cursor: TSQLCursor; ATransaction : TSQLTransaction;
  51. buf: string; AParams : TParams); override;
  52. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); override;
  53. function Fetch(cursor : TSQLCursor) : boolean; override;
  54. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); override;
  55. procedure UnPrepareStatement(cursor : TSQLCursor); override;
  56. procedure FreeFldBuffers(cursor : TSQLCursor); override;
  57. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
  58. //if bufsize < 0 -> buffer was too small, should be -bufsize
  59. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  60. function Commit(trans : TSQLHandle) : boolean; override;
  61. function RollBack(trans : TSQLHandle) : boolean; override;
  62. function StartdbTransaction(trans : TSQLHandle; aParams : string) : boolean; override;
  63. procedure CommitRetaining(trans : TSQLHandle); override;
  64. procedure RollBackRetaining(trans : TSQLHandle); override;
  65. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction); override;
  66. // New methods
  67. procedure execsql(const asql: string);
  68. procedure UpdateIndexDefs(IndexDefs : TIndexDefs; TableName : string); override;
  69. function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
  70. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; override;
  71. function StrToStatementType(s : string) : TStatementType; override;
  72. public
  73. constructor Create(AOwner : TComponent); override;
  74. procedure GetFieldNames(const TableName : string; List : TStrings); override;
  75. function GetConnectionInfo(InfoType:TConnInfoType): string; override;
  76. function GetInsertID: int64;
  77. // See http://www.sqlite.org/c3ref/create_collation.html for detailed information
  78. // If eTextRep=0 a default UTF-8 compare function is used (UTF8CompareCallback)
  79. // Warning: UTF8CompareCallback needs a wide string manager on linux such as cwstring
  80. // Warning: CollationName has to be a UTF-8 string
  81. procedure CreateCollation(const CollationName: string; eTextRep: integer; Arg: Pointer=nil; Compare: xCompare=nil);
  82. procedure LoadExtension(LibraryFile: string);
  83. published
  84. property Options: TSqliteOptions read FOptions write SetOptions;
  85. end;
  86. { TSQLite3ConnectionDef }
  87. TSQLite3ConnectionDef = class(TConnectionDef)
  88. class function TypeName: string; override;
  89. class function ConnectionClass: TSQLConnectionClass; override;
  90. class function Description: string; override;
  91. class function LoadedLibraryName: string; override;
  92. end;
  93. Var
  94. SQLiteLibraryName : String absolute sqlite3dyn.SQLiteDefaultLibrary deprecated 'use sqlite3dyn.SQLiteDefaultLibrary instead';
  95. implementation
  96. uses
  97. dbconst, sysutils, dateutils, FmtBCD;
  98. {$IF NOT DECLARED(JulianEpoch)} // sysutils/datih.inc
  99. const
  100. JulianEpoch = TDateTime(-2415018.5); // "julian day 0" is January 1, 4713 BC 12:00AM
  101. {$ENDIF}
  102. type
  103. TStorageType = (stNone,stInteger,stFloat,stText,stBlob,stNull);
  104. TSQLite3Cursor = class(tsqlcursor)
  105. private
  106. fhandle : psqlite3;
  107. fconnection: TSQLite3Connection;
  108. fstatement: psqlite3_stmt;
  109. ftail: pchar;
  110. fstate: integer;
  111. fparambinding: array of Integer;
  112. procedure checkerror(const aerror: integer);
  113. procedure bindparams(AParams : TParams);
  114. Procedure Prepare(Buf : String; AParams : TParams);
  115. Procedure UnPrepare;
  116. Procedure Execute;
  117. Function Fetch : Boolean;
  118. public
  119. RowsAffected : Largeint;
  120. end;
  121. procedure freebindstring(astring: pointer); cdecl;
  122. begin
  123. StrDispose(AString);
  124. end;
  125. procedure TSQLite3Cursor.checkerror(const aerror: integer);
  126. Var
  127. S : String;
  128. begin
  129. if (aerror<>sqlite_ok) then
  130. begin
  131. S:=strpas(sqlite3_errmsg(fhandle));
  132. DatabaseError(S);
  133. end;
  134. end;
  135. Procedure TSQLite3Cursor.bindparams(AParams : TParams);
  136. Function PCharStr(Const S : String) : PChar;
  137. begin
  138. Result:=StrAlloc(Length(S)+1);
  139. If (Result<>Nil) then
  140. StrPCopy(Result,S);
  141. end;
  142. Var
  143. I : Integer;
  144. P : TParam;
  145. str1: string;
  146. do1: double;
  147. wstr1: widestring;
  148. begin
  149. for I:=1 to high(fparambinding)+1 do
  150. begin
  151. P:=AParams[fparambinding[I-1]];
  152. if P.IsNull then
  153. checkerror(sqlite3_bind_null(fstatement,I))
  154. else
  155. case P.datatype of
  156. ftInteger,
  157. ftBoolean,
  158. ftSmallint: checkerror(sqlite3_bind_int(fstatement,I,P.AsInteger));
  159. ftWord: checkerror(sqlite3_bind_int(fstatement,I,P.AsWord));
  160. ftLargeint: checkerror(sqlite3_bind_int64(fstatement,I,P.AsLargeint));
  161. ftBcd,
  162. ftFloat,
  163. ftCurrency:
  164. begin
  165. do1:= P.AsFloat;
  166. checkerror(sqlite3_bind_double(fstatement,I,do1));
  167. end;
  168. ftDateTime,
  169. ftDate,
  170. ftTime: begin
  171. do1:= P.AsFloat - JulianEpoch;
  172. checkerror(sqlite3_bind_double(fstatement,I,do1));
  173. end;
  174. ftFMTBcd:
  175. begin
  176. str1:=BCDToStr(P.AsFMTBCD, Fconnection.FSQLFormatSettings);
  177. checkerror(sqlite3_bind_text(fstatement, I, PChar(str1), length(str1), sqlite3_destructor_type(SQLITE_TRANSIENT)));
  178. end;
  179. ftString,
  180. ftFixedChar,
  181. ftMemo: begin // According to SQLite documentation, CLOB's (ftMemo) have the Text affinity
  182. str1:= p.asstring;
  183. checkerror(sqlite3_bind_text(fstatement,I,pcharstr(str1), length(str1),@freebindstring));
  184. end;
  185. ftBytes,
  186. ftVarBytes,
  187. ftBlob: begin
  188. str1:= P.asstring;
  189. checkerror(sqlite3_bind_blob(fstatement,I,pcharstr(str1), length(str1),@freebindstring));
  190. end;
  191. ftWideString, ftFixedWideChar, ftWideMemo:
  192. begin
  193. wstr1:=P.AsWideString;
  194. checkerror(sqlite3_bind_text16(fstatement,I, PWideChar(wstr1), length(wstr1)*sizeof(WideChar), sqlite3_destructor_type(SQLITE_TRANSIENT)));
  195. end
  196. else
  197. DatabaseErrorFmt(SUnsupportedParameter, [Fieldtypenames[P.DataType], Self]);
  198. end; { Case }
  199. end;
  200. end;
  201. Procedure TSQLite3Cursor.Prepare(Buf : String; AParams : TParams);
  202. begin
  203. if assigned(AParams) and (AParams.Count > 0) then
  204. Buf := AParams.ParseSQL(Buf,false,false,false,psInterbase,fparambinding);
  205. checkerror(sqlite3_prepare(fhandle,pchar(Buf),length(Buf),@fstatement,@ftail));
  206. FPrepared:=True;
  207. end;
  208. Procedure TSQLite3Cursor.UnPrepare;
  209. begin
  210. sqlite3_finalize(fstatement); // No check.
  211. FPrepared:=False;
  212. end;
  213. Procedure TSQLite3Cursor.Execute;
  214. var
  215. wo1: word;
  216. begin
  217. {$ifdef i386}
  218. wo1:= get8087cw;
  219. set8087cw(wo1 or $1f); //mask exceptions, Sqlite3 has overflow
  220. Try // Why do people always forget this ??
  221. {$endif}
  222. fstate:= sqlite3_step(fstatement);
  223. {$ifdef i386}
  224. finally
  225. set8087cw(wo1); //restore
  226. end;
  227. {$endif}
  228. if (fstate<=sqliteerrormax) then
  229. checkerror(sqlite3_reset(fstatement));
  230. FSelectable :=sqlite3_column_count(fstatement)>0;
  231. RowsAffected:=sqlite3_changes(fhandle);
  232. if (fstate=sqlite_row) then
  233. fstate:= sqliteerrormax; //first row
  234. end;
  235. Function TSQLite3Cursor.Fetch : Boolean;
  236. begin
  237. if (fstate=sqliteerrormax) then
  238. fstate:=sqlite_row //first row;
  239. else if (fstate=sqlite_row) then
  240. begin
  241. fstate:=sqlite3_step(fstatement);
  242. if (fstate<=sqliteerrormax) then
  243. checkerror(sqlite3_reset(fstatement)); //right error returned??
  244. end;
  245. result:=(fstate=sqlite_row);
  246. end;
  247. { TSQLite3Connection }
  248. procedure TSQLite3Connection.LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction);
  249. var
  250. int1: integer;
  251. st: psqlite3_stmt;
  252. fnum: integer;
  253. p1: Pointer;
  254. begin
  255. st:=TSQLite3Cursor(cursor).fstatement;
  256. fnum:= FieldDef.fieldno - 1;
  257. case FieldDef.DataType of
  258. ftWideMemo:
  259. begin
  260. p1 := sqlite3_column_text16(st,fnum);
  261. int1 := sqlite3_column_bytes16(st,fnum);
  262. end;
  263. ftMemo:
  264. begin
  265. p1 := sqlite3_column_text(st,fnum);
  266. int1 := sqlite3_column_bytes(st,fnum);
  267. end;
  268. else //ftBlob
  269. begin
  270. p1 := sqlite3_column_blob(st,fnum);
  271. int1 := sqlite3_column_bytes(st,fnum);
  272. end;
  273. end;
  274. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer, int1);
  275. if int1 > 0 then
  276. move(p1^, ABlobBuf^.BlobBuffer^.Buffer^, int1);
  277. ABlobBuf^.BlobBuffer^.Size := int1;
  278. end;
  279. function TSQLite3Connection.AllocateTransactionHandle: TSQLHandle;
  280. begin
  281. result:= tsqlhandle.create;
  282. end;
  283. function TSQLite3Connection.AllocateCursorHandle: TSQLCursor;
  284. Var
  285. Res : TSQLite3Cursor;
  286. begin
  287. Res:= TSQLite3Cursor.create;
  288. Res.fconnection:=Self;
  289. Result:=Res;
  290. end;
  291. procedure TSQLite3Connection.DeAllocateCursorHandle(var cursor: TSQLCursor);
  292. begin
  293. freeandnil(cursor);
  294. end;
  295. procedure TSQLite3Connection.PrepareStatement(cursor: TSQLCursor;
  296. ATransaction: TSQLTransaction; buf: string; AParams: TParams);
  297. begin
  298. TSQLite3Cursor(cursor).fhandle:=self.fhandle;
  299. TSQLite3Cursor(cursor).Prepare(Buf,AParams);
  300. end;
  301. procedure TSQLite3Connection.UnPrepareStatement(cursor: TSQLCursor);
  302. begin
  303. TSQLite3Cursor(cursor).UnPrepare;
  304. TSQLite3Cursor(cursor).fhandle:=nil;
  305. end;
  306. Type
  307. TFieldMap = Record
  308. N : String;
  309. T : TFieldType;
  310. end;
  311. Const
  312. FieldMapCount = 27;
  313. FieldMap : Array [1..FieldMapCount] of TFieldMap = (
  314. (n:'INT'; t: ftInteger),
  315. (n:'LARGEINT'; t:ftlargeInt),
  316. (n:'BIGINT'; t:ftlargeInt),
  317. (n:'WORD'; t: ftWord),
  318. (n:'SMALLINT'; t: ftSmallint),
  319. (n:'BOOLEAN'; t: ftBoolean),
  320. (n:'REAL'; t: ftFloat),
  321. (n:'FLOAT'; t: ftFloat),
  322. (n:'DOUBLE'; t: ftFloat),
  323. (n:'TIMESTAMP'; t: ftDateTime),
  324. (n:'DATETIME'; t: ftDateTime), // MUST be before date
  325. (n:'DATE'; t: ftDate),
  326. (n:'TIME'; t: ftTime),
  327. (n:'CURRENCY'; t: ftCurrency),
  328. (n:'MONEY'; t: ftCurrency),
  329. (n:'VARCHAR'; t: ftString),
  330. (n:'CHAR'; t: ftFixedChar),
  331. (n:'NUMERIC'; t: ftBCD),
  332. (n:'DECIMAL'; t: ftBCD),
  333. (n:'TEXT'; t: ftmemo),
  334. (n:'CLOB'; t: ftmemo),
  335. (n:'BLOB'; t: ftBlob),
  336. (n:'NCHAR'; t: ftFixedWideChar),
  337. (n:'NVARCHAR'; t: ftWideString),
  338. (n:'NCLOB'; t: ftWideMemo),
  339. (n:'VARBINARY'; t: ftVarBytes),
  340. (n:'BINARY'; t: ftBytes)
  341. { Template:
  342. (n:''; t: ft)
  343. }
  344. );
  345. procedure TSQLite3Connection.AddFieldDefs(cursor: TSQLCursor;
  346. FieldDefs: TfieldDefs);
  347. var
  348. i : integer;
  349. FN,FD : string;
  350. ft1 : tfieldtype;
  351. size1, size2 : integer;
  352. fi : integer;
  353. st : psqlite3_stmt;
  354. function ExtractPrecisionAndScale(decltype: string; var precision, scale: integer): boolean;
  355. var p: integer;
  356. begin
  357. p:=pos('(', decltype);
  358. Result:=p>0;
  359. if not Result then Exit;
  360. System.Delete(decltype,1,p);
  361. p:=pos(')', decltype);
  362. Result:=p>0;
  363. if not Result then Exit;
  364. decltype:=copy(decltype,1,p-1);
  365. p:=pos(',', decltype);
  366. if p=0 then
  367. begin
  368. precision:=StrToIntDef(decltype, precision);
  369. scale:=0;
  370. end
  371. else
  372. begin
  373. precision:=StrToIntDef(copy(decltype,1,p-1), precision);
  374. scale:=StrToIntDef(copy(decltype,p+1,length(decltype)-p), scale);
  375. end;
  376. end;
  377. begin
  378. st:=TSQLite3Cursor(cursor).fstatement;
  379. for i:= 0 to sqlite3_column_count(st) - 1 do
  380. begin
  381. FN:=sqlite3_column_name(st,i);
  382. FD:=uppercase(sqlite3_column_decltype(st,i));
  383. ft1:= ftUnknown;
  384. size1:= 0;
  385. for fi := 1 to FieldMapCount do if pos(FieldMap[fi].N,FD)=1 then
  386. begin
  387. ft1:=FieldMap[fi].t;
  388. break;
  389. end;
  390. // In case of an empty fieldtype (FD='', which is allowed and used in calculated
  391. // columns (aggregates) and by pragma-statements) or an unknown fieldtype,
  392. // use the field's affinity:
  393. if ft1=ftUnknown then
  394. case TStorageType(sqlite3_column_type(st,i)) of
  395. stInteger: ft1:=ftLargeInt;
  396. stFloat: ft1:=ftFloat;
  397. stBlob: ft1:=ftBlob;
  398. else ft1:=ftString;
  399. end;
  400. // handle some specials.
  401. size1:=0;
  402. case ft1 of
  403. ftString,
  404. ftFixedChar,
  405. ftFixedWideChar,
  406. ftWideString,
  407. ftBytes,
  408. ftVarBytes:
  409. begin
  410. size1 := 255; //sql: if length is omitted then length is 1
  411. size2 := 0;
  412. ExtractPrecisionAndScale(FD, size1, size2);
  413. if size1 > MaxSmallint then size1 := MaxSmallint;
  414. end;
  415. ftBCD: begin
  416. size2 := MaxBCDPrecision; //sql: if a precision is omitted, then use implementation-defined
  417. size1 := 0; //sql: if a scale is omitted then scale is 0
  418. ExtractPrecisionAndScale(FD, size2, size1);
  419. if (size2<=18) and (size1=0) then
  420. ft1:=ftLargeInt
  421. else if (size2-size1>MaxBCDPrecision-MaxBCDScale) or (size1>MaxBCDScale) then
  422. ft1:=ftFmtBCD;
  423. end;
  424. ftUnknown : DatabaseError('Unknown record type: '+FN);
  425. end; // Case
  426. tfielddef.create(fielddefs,FieldDefs.MakeNameUnique(FN),ft1,size1,false,i+1);
  427. end;
  428. end;
  429. procedure TSQLite3Connection.Execute(cursor: TSQLCursor; atransaction: tsqltransaction; AParams: TParams);
  430. var
  431. SC : TSQLite3Cursor;
  432. begin
  433. SC:=TSQLite3Cursor(cursor);
  434. checkerror(sqlite3_reset(sc.fstatement));
  435. If (AParams<>Nil) and (AParams.count > 0) then
  436. SC.BindParams(AParams);
  437. SC.Execute;
  438. end;
  439. Function NextWord(Var S : ShortString; Sep : Char) : String;
  440. Var
  441. P : Integer;
  442. begin
  443. P:=Pos(Sep,S);
  444. If (P=0) then
  445. P:=Length(S)+1;
  446. Result:=Copy(S,1,P-1);
  447. Delete(S,1,P);
  448. end;
  449. Function ParseSQLiteDate(S : ShortString) : TDateTime;
  450. Var
  451. Year, Month, Day : Integer;
  452. begin
  453. Result:=0;
  454. If TryStrToInt(NextWord(S,'-'),Year) then
  455. if TryStrToInt(NextWord(S,'-'),Month) then
  456. if TryStrToInt(NextWord(S,' '),Day) then
  457. Result:=EncodeDate(Year,Month,Day);
  458. end;
  459. Function ParseSQLiteTime(S : ShortString; Interval: boolean) : TDateTime;
  460. Var
  461. Hour, Min, Sec, MSec : Integer;
  462. begin
  463. Result:=0;
  464. If TryStrToInt(NextWord(S,':'),Hour) then
  465. if TryStrToInt(NextWord(S,':'),Min) then
  466. if TryStrToInt(NextWord(S,'.'),Sec) then
  467. begin
  468. MSec:=StrToIntDef(S,0);
  469. if Interval then
  470. Result:=EncodeTimeInterval(Hour,Min,Sec,MSec)
  471. else
  472. Result:=EncodeTime(Hour,Min,Sec,MSec);
  473. end;
  474. end;
  475. Function ParseSQLiteDateTime(S : String) : TDateTime;
  476. var
  477. P : Integer;
  478. DS,TS : ShortString;
  479. begin
  480. DS:='';
  481. TS:='';
  482. P:=Pos(' ',S);
  483. If (P<>0) then
  484. begin
  485. DS:=Copy(S,1,P-1);
  486. TS:=S;
  487. Delete(TS,1,P);
  488. end
  489. else
  490. begin
  491. If (Pos('-',S)<>0) then
  492. DS:=S
  493. else if (Pos(':',S)<>0) then
  494. TS:=S;
  495. end;
  496. Result:=ComposeDateTime(ParseSQLiteDate(DS),ParseSQLiteTime(TS,False));
  497. end;
  498. function TSQLite3Connection.LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean;
  499. var
  500. st1: TStorageType;
  501. fnum: integer;
  502. str1: string;
  503. int1 : integer;
  504. bcd: tBCD;
  505. bcdstr: FmtBCDStringtype;
  506. st : psqlite3_stmt;
  507. begin
  508. st:=TSQLite3Cursor(cursor).fstatement;
  509. fnum:= FieldDef.fieldno - 1;
  510. st1:= TStorageType(sqlite3_column_type(st,fnum));
  511. CreateBlob:=false;
  512. result:= st1 <> stnull;
  513. if Not result then
  514. Exit;
  515. case FieldDef.datatype of
  516. ftInteger : pinteger(buffer)^ := sqlite3_column_int(st,fnum);
  517. ftSmallInt : psmallint(buffer)^ := sqlite3_column_int(st,fnum);
  518. ftWord : pword(buffer)^ := sqlite3_column_int(st,fnum);
  519. ftBoolean : pwordbool(buffer)^ := sqlite3_column_int(st,fnum)<>0;
  520. ftLargeInt : PInt64(buffer)^:= sqlite3_column_int64(st,fnum);
  521. ftBCD : PCurrency(buffer)^:= FloattoCurr(sqlite3_column_double(st,fnum));
  522. ftFloat,
  523. ftCurrency : pdouble(buffer)^:= sqlite3_column_double(st,fnum);
  524. ftDateTime,
  525. ftDate,
  526. ftTime: if st1 = sttext then
  527. begin
  528. setlength(str1,sqlite3_column_bytes(st,fnum));
  529. move(sqlite3_column_text(st,fnum)^,str1[1],length(str1));
  530. case FieldDef.datatype of
  531. ftDateTime: PDateTime(Buffer)^:=ParseSqliteDateTime(str1);
  532. ftDate : PDateTime(Buffer)^:=ParseSqliteDate(str1);
  533. ftTime : PDateTime(Buffer)^:=ParseSQLiteTime(str1,true);
  534. end; {case}
  535. end
  536. else
  537. begin
  538. PDateTime(buffer)^ := sqlite3_column_double(st,fnum);
  539. if PDateTime(buffer)^ > 1721059.5 {Julian 01/01/0000} then
  540. PDateTime(buffer)^ := PDateTime(buffer)^ + JulianEpoch; //backward compatibility hack
  541. end;
  542. ftFixedChar,
  543. ftString: begin
  544. int1:= sqlite3_column_bytes(st,fnum);
  545. if int1>FieldDef.Size then
  546. int1:=FieldDef.Size;
  547. if int1 > 0 then
  548. move(sqlite3_column_text(st,fnum)^,buffer^,int1);
  549. PAnsiChar(buffer + int1)^ := #0;
  550. end;
  551. ftFmtBCD: begin
  552. int1:= sqlite3_column_bytes(st,fnum);
  553. if (int1 > 0) and (int1 <= MAXFMTBcdFractionSize) then
  554. begin
  555. SetLength(bcdstr,int1);
  556. move(sqlite3_column_text(st,fnum)^,bcdstr[1],int1);
  557. // sqlite always uses the point as decimal-point
  558. if not TryStrToBCD(bcdstr,bcd,FSQLFormatSettings) then
  559. // sqlite does the same, if the value can't be interpreted as a
  560. // number in sqlite3_column_int, return 0
  561. bcd := 0;
  562. end
  563. else
  564. bcd := 0;
  565. pBCD(buffer)^:= bcd;
  566. end;
  567. ftFixedWideChar,
  568. ftWideString:
  569. begin
  570. int1 := sqlite3_column_bytes16(st,fnum); //The value returned does not include the zero terminator at the end of the string
  571. if int1>FieldDef.Size*2 then
  572. int1:=FieldDef.Size*2;
  573. if int1 > 0 then
  574. move(sqlite3_column_text16(st,fnum)^, buffer^, int1); //Strings returned by sqlite3_column_text() and sqlite3_column_text16(), even empty strings, are always zero terminated.
  575. PWideChar(buffer + int1)^ := #0;
  576. end;
  577. ftVarBytes,
  578. ftBytes:
  579. begin
  580. int1 := sqlite3_column_bytes(st,fnum);
  581. if int1 > FieldDef.Size then
  582. int1 := FieldDef.Size;
  583. if FieldDef.DataType = ftVarBytes then
  584. begin
  585. PWord(buffer)^ := int1;
  586. inc(buffer, sizeof(Word));
  587. end;
  588. if int1 > 0 then
  589. move(sqlite3_column_blob(st,fnum)^, buffer^, int1);
  590. end;
  591. ftWideMemo,
  592. ftMemo,
  593. ftBlob: CreateBlob:=True;
  594. else { Case }
  595. result:= false; // unknown
  596. end; { Case }
  597. end;
  598. function TSQLite3Connection.Fetch(cursor: TSQLCursor): boolean;
  599. begin
  600. Result:=TSQLite3Cursor(cursor).Fetch;
  601. end;
  602. procedure TSQLite3Connection.FreeFldBuffers(cursor: TSQLCursor);
  603. begin
  604. //dummy
  605. end;
  606. function TSQLite3Connection.GetTransactionHandle(trans: TSQLHandle): pointer;
  607. begin
  608. result:= nil;
  609. end;
  610. function TSQLite3Connection.Commit(trans: TSQLHandle): boolean;
  611. begin
  612. execsql('COMMIT');
  613. result:= true;
  614. end;
  615. function TSQLite3Connection.RollBack(trans: TSQLHandle): boolean;
  616. begin
  617. execsql('ROLLBACK');
  618. result:= true;
  619. end;
  620. function TSQLite3Connection.StartdbTransaction(trans: TSQLHandle;
  621. aParams: string): boolean;
  622. begin
  623. execsql('BEGIN');
  624. result:= true;
  625. end;
  626. procedure TSQLite3Connection.CommitRetaining(trans: TSQLHandle);
  627. begin
  628. commit(trans);
  629. execsql('BEGIN');
  630. end;
  631. procedure TSQLite3Connection.RollBackRetaining(trans: TSQLHandle);
  632. begin
  633. rollback(trans);
  634. execsql('BEGIN');
  635. end;
  636. procedure TSQLite3Connection.DoInternalConnect;
  637. var
  638. str1: string;
  639. begin
  640. if Length(databasename)=0 then
  641. DatabaseError(SErrNoDatabaseName,self);
  642. InitializeSqlite(SQLiteDefaultLibrary);
  643. str1:= databasename;
  644. checkerror(sqlite3_open(pchar(str1),@fhandle));
  645. if (Length(Password)>0) and assigned(sqlite3_key) then
  646. checkerror(sqlite3_key(fhandle,PChar(Password),StrLen(PChar(Password))));
  647. if Params.IndexOfName('foreign_keys') <> -1 then
  648. execsql('PRAGMA foreign_keys = '+Params.Values['foreign_keys']);
  649. end;
  650. procedure TSQLite3Connection.DoInternalDisconnect;
  651. begin
  652. if fhandle <> nil then
  653. begin
  654. checkerror(sqlite3_close(fhandle));
  655. fhandle:= nil;
  656. releasesqlite;
  657. end;
  658. end;
  659. function TSQLite3Connection.GetHandle: pointer;
  660. begin
  661. result:= fhandle;
  662. end;
  663. procedure TSQLite3Connection.checkerror(const aerror: integer);
  664. Var
  665. S : String;
  666. begin
  667. if (aerror<>sqlite_ok) then
  668. begin
  669. S:=strpas(sqlite3_errmsg(fhandle));
  670. DatabaseError(S,Self);
  671. end;
  672. end;
  673. procedure TSQLite3Connection.execsql(const asql: string);
  674. var
  675. err : pchar;
  676. str1 : string;
  677. res : integer;
  678. begin
  679. err:= nil;
  680. Res := sqlite3_exec(fhandle,pchar(asql),nil,nil,@err);
  681. if err <> nil then
  682. begin
  683. str1:= strpas(err);
  684. sqlite3_free(err);
  685. end;
  686. if (res<>sqlite_ok) then
  687. databaseerror(str1);
  688. end;
  689. function execcallback(adata: pointer; ncols: longint; //adata = PStringArray
  690. avalues: PPchar; anames: PPchar):longint; cdecl;
  691. var
  692. P : PStringArray;
  693. i : integer;
  694. begin
  695. P:=PStringArray(adata);
  696. SetLength(P^,ncols);
  697. for i:= 0 to ncols - 1 do
  698. P^[i]:= strPas(avalues[i]);
  699. result:= 0;
  700. end;
  701. function execscallback(adata: pointer; ncols: longint; //adata = PArrayStringArray
  702. avalues: PPchar; anames: PPchar):longint; cdecl;
  703. var
  704. I,N : integer;
  705. PP : PArrayStringArray;
  706. p : PStringArray;
  707. begin
  708. PP:=PArrayStringArray(adata);
  709. N:=high(PP^); // Length-1;
  710. setlength(PP^,N+2); // increase with 1;
  711. p:= @(PP^[N+1]); // newly added array, fill with data.
  712. setlength(p^,ncols);
  713. for i:= 0 to ncols - 1 do
  714. p^[i]:= strPas(avalues[i]);
  715. result:= 0;
  716. end;
  717. function TSQLite3Connection.stringsquery(const asql: string): TArrayStringArray;
  718. begin
  719. SetLength(result,0);
  720. checkerror(sqlite3_exec(fhandle,pchar(asql),@execscallback,@result,nil));
  721. end;
  722. function TSQLite3Connection.RowsAffected(cursor: TSQLCursor): TRowsCount;
  723. begin
  724. if assigned(cursor) then
  725. Result := (cursor as TSQLite3Cursor).RowsAffected
  726. else
  727. Result := -1;
  728. end;
  729. function TSQLite3Connection.GetSchemaInfoSQL(SchemaType: TSchemaType;
  730. SchemaObjectName, SchemaPattern: string): string;
  731. begin
  732. case SchemaType of
  733. stTables : result := 'select name as table_name from sqlite_master where type = ''table'' order by 1';
  734. stSysTables : result := 'select ''sqlite_master'' as table_name';
  735. stColumns : result := 'pragma table_info(''' + (SchemaObjectName) + ''')';
  736. else
  737. DatabaseError(SMetadataUnavailable)
  738. end; {case}
  739. end;
  740. function TSQLite3Connection.StrToStatementType(s: string): TStatementType;
  741. begin
  742. S:=Lowercase(s);
  743. if s = 'pragma' then exit(stSelect);
  744. result := inherited StrToStatementType(s);
  745. end;
  746. constructor TSQLite3Connection.Create(AOwner: TComponent);
  747. begin
  748. inherited Create(AOwner);
  749. FConnOptions := FConnOptions + [sqEscapeRepeat] + [sqEscapeSlash];
  750. FieldNameQuoteChars:=DoubleQuotes;
  751. end;
  752. procedure TSQLite3Connection.UpdateIndexDefs(IndexDefs: TIndexDefs; TableName: string);
  753. var
  754. artableinfo, arindexlist, arindexinfo: TArrayStringArray;
  755. il,ii: integer;
  756. IndexName: string;
  757. IndexOptions: TIndexOptions;
  758. PKFields, IXFields: TStrings;
  759. function CheckPKFields:boolean;
  760. var i: integer;
  761. begin
  762. Result:=false;
  763. if IXFields.Count<>PKFields.Count then Exit;
  764. for i:=0 to IXFields.Count-1 do
  765. if PKFields.IndexOf(IXFields[i])<0 then Exit;
  766. Result:=true;
  767. PKFields.Clear;
  768. end;
  769. begin
  770. PKFields:=TStringList.Create;
  771. PKFields.Delimiter:=';';
  772. IXFields:=TStringList.Create;
  773. IXFields.Delimiter:=';';
  774. //primary key fields; 5th column "pk" is zero for columns that are not part of PK
  775. artableinfo := stringsquery('PRAGMA table_info('+TableName+');');
  776. for ii:=low(artableinfo) to high(artableinfo) do
  777. if (high(artableinfo[ii]) >= 5) and (artableinfo[ii][5] >= '1') then
  778. PKFields.Add(artableinfo[ii][1]);
  779. //list of all table indexes
  780. arindexlist:=stringsquery('PRAGMA index_list('+TableName+');');
  781. for il:=low(arindexlist) to high(arindexlist) do
  782. begin
  783. IndexName:=arindexlist[il][1];
  784. if arindexlist[il][2]='1' then
  785. IndexOptions:=[ixUnique]
  786. else
  787. IndexOptions:=[];
  788. //list of columns in given index
  789. arindexinfo:=stringsquery('PRAGMA index_info('+IndexName+');');
  790. IXFields.Clear;
  791. for ii:=low(arindexinfo) to high(arindexinfo) do
  792. IXFields.Add(arindexinfo[ii][2]);
  793. if CheckPKFields then IndexOptions:=IndexOptions+[ixPrimary];
  794. IndexDefs.Add(IndexName, IXFields.DelimitedText, IndexOptions);
  795. end;
  796. if PKFields.Count > 0 then //in special case for INTEGER PRIMARY KEY column, unique index is not created
  797. IndexDefs.Add('$PRIMARY_KEY$', PKFields.DelimitedText, [ixPrimary,ixUnique]);
  798. PKFields.Free;
  799. IXFields.Free;
  800. end;
  801. function TSQLite3Connection.getinsertid: int64;
  802. begin
  803. result:= sqlite3_last_insert_rowid(fhandle);
  804. end;
  805. procedure TSQLite3Connection.GetFieldNames(const TableName: string;
  806. List: TStrings);
  807. begin
  808. GetDBInfo(stColumns,TableName,'name',List);
  809. end;
  810. function TSQLite3Connection.GetConnectionInfo(InfoType: TConnInfoType): string;
  811. begin
  812. Result:='';
  813. try
  814. InitializeSqlite(SQLiteDefaultLibrary);
  815. case InfoType of
  816. citServerType:
  817. Result:=TSQLite3ConnectionDef.TypeName;
  818. citServerVersion,
  819. citClientVersion:
  820. Result:=inttostr(sqlite3_libversion_number());
  821. citServerVersionString:
  822. Result:=sqlite3_libversion();
  823. citClientName:
  824. Result:=TSQLite3ConnectionDef.LoadedLibraryName;
  825. else
  826. Result:=inherited GetConnectionInfo(InfoType);
  827. end;
  828. finally
  829. ReleaseSqlite;
  830. end;
  831. end;
  832. function UTF8CompareCallback(user: pointer; len1: longint; data1: pointer; len2: longint; data2: pointer): longint; cdecl;
  833. var S1, S2: AnsiString;
  834. begin
  835. SetString(S1, data1, len1);
  836. SetString(S2, data2, len2);
  837. Result := UnicodeCompareStr(UTF8Decode(S1), UTF8Decode(S2));
  838. end;
  839. procedure TSQLite3Connection.CreateCollation(const CollationName: string;
  840. eTextRep: integer; Arg: Pointer; Compare: xCompare);
  841. begin
  842. if eTextRep = 0 then
  843. begin
  844. eTextRep := SQLITE_UTF8;
  845. Compare := @UTF8CompareCallback;
  846. end;
  847. CheckConnected;
  848. CheckError(sqlite3_create_collation(fhandle, PChar(CollationName), eTextRep, Arg, Compare));
  849. end;
  850. procedure TSQLite3Connection.LoadExtension(LibraryFile: String);
  851. var
  852. LoadResult: integer;
  853. begin
  854. CheckConnected; //Apparently we need a connection before we can load extensions.
  855. LoadResult:=SQLITE_ERROR; //Default to failed
  856. try
  857. LoadResult:=sqlite3_enable_load_extension(fhandle, 1); //Make sure we are allowed to load
  858. if LoadResult=SQLITE_OK then
  859. begin
  860. LoadResult:=sqlite3_load_extension(fhandle, PChar(LibraryFile), nil, nil); //Actually load extension
  861. if LoadResult=SQLITE_ERROR then
  862. begin
  863. DatabaseError('LoadExtension: failed to load SQLite extension (SQLite returned an error while loading).',Self);
  864. end;
  865. end
  866. else
  867. begin
  868. DatabaseError('LoadExtension: failed to load SQLite extension (SQLite returned an error while enabling extensions).',Self);
  869. end;
  870. except
  871. DatabaseError('LoadExtension: failed to load SQLite extension.',Self)
  872. end;
  873. end;
  874. procedure TSQLite3Connection.setoptions(const avalue: tsqliteoptions);
  875. begin
  876. if avalue <> foptions then
  877. begin
  878. checkdisconnected;
  879. foptions:= avalue;
  880. end;
  881. end;
  882. { TSQLite3ConnectionDef }
  883. class function TSQLite3ConnectionDef.TypeName: string;
  884. begin
  885. Result := 'SQLite3';
  886. end;
  887. class function TSQLite3ConnectionDef.ConnectionClass: TSQLConnectionClass;
  888. begin
  889. Result := TSQLite3Connection;
  890. end;
  891. class function TSQLite3ConnectionDef.Description: string;
  892. begin
  893. Result := 'Connect to a SQLite3 database directly via the client library';
  894. end;
  895. class function TSQLite3ConnectionDef.LoadedLibraryName: string;
  896. begin
  897. Result := SQLiteLoadedLibrary;
  898. end;
  899. initialization
  900. RegisterConnection(TSQLite3ConnectionDef);
  901. finalization
  902. UnRegisterConnection(TSQLite3ConnectionDef);
  903. end.