sqlite3conn.pp 32 KB

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