sqlite3conn.pp 36 KB

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