2
0

sqlite3conn.pp 31 KB

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