sqlite3conn.pp 32 KB

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