sqlite3conn.pp 36 KB

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