sqlite3conn.pp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. {
  2. This file is part of the Free Pascal Classes Library (FCL).
  3. Copyright (c) 2006 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. function stringsquery(const asql: string): TArrayStringArray;
  42. procedure checkerror(const aerror: integer);
  43. procedure DoInternalConnect; override;
  44. procedure DoInternalDisconnect; override;
  45. function GetHandle : pointer; override;
  46. Function AllocateCursorHandle : TSQLCursor; override;
  47. //aowner used as blob cache
  48. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); override;
  49. Function AllocateTransactionHandle : TSQLHandle; override;
  50. procedure PrepareStatement(cursor: TSQLCursor; ATransaction : TSQLTransaction;
  51. buf: string; AParams : TParams); override;
  52. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); override;
  53. function Fetch(cursor : TSQLCursor) : boolean; override;
  54. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); override;
  55. procedure UnPrepareStatement(cursor : TSQLCursor); override;
  56. procedure FreeFldBuffers(cursor : TSQLCursor); override;
  57. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
  58. //if bufsize < 0 -> buffer was to small, should be -bufsize
  59. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  60. function Commit(trans : TSQLHandle) : boolean; override;
  61. function RollBack(trans : TSQLHandle) : boolean; override;
  62. function StartdbTransaction(trans : TSQLHandle; aParams : string) : boolean; override;
  63. procedure CommitRetaining(trans : TSQLHandle); override;
  64. procedure RollBackRetaining(trans : TSQLHandle); override;
  65. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction); override;
  66. // New methods
  67. procedure execsql(const asql: string);
  68. procedure UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string); override; // Differs from SQLDB.
  69. function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
  70. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; override;
  71. function StrToStatementType(s : string) : TStatementType; override;
  72. public
  73. constructor Create(AOwner : TComponent); override;
  74. function GetInsertID: int64;
  75. procedure GetFieldNames(const TableName : string; List : TStrings); override;
  76. procedure LoadExtension(LibraryFile: string);
  77. published
  78. property Options: TSqliteOptions read FOptions write SetOptions;
  79. end;
  80. Var
  81. SQLiteLibraryName : String = sqlite3lib;
  82. implementation
  83. uses
  84. dbconst, sysutils, dateutils, FmtBCD;
  85. const
  86. JulianDateShift = 2415018.5; //distance from "julian day 0" (January 1, 4713 BC 12:00AM) to "1899-12-30 00:00AM"
  87. type
  88. TStorageType = (stNone,stInteger,stFloat,stText,stBlob,stNull);
  89. TSQLite3Cursor = class(tsqlcursor)
  90. private
  91. fhandle : psqlite3;
  92. fconnection: TSQLite3Connection;
  93. fstatement: psqlite3_stmt;
  94. ftail: pchar;
  95. fstate: integer;
  96. fparambinding: array of Integer;
  97. procedure checkerror(const aerror: integer);
  98. procedure bindparams(AParams : TParams);
  99. Procedure Prepare(Buf : String; APArams : TParams);
  100. Procedure UnPrepare;
  101. Procedure Execute;
  102. Function Fetch : Boolean;
  103. public
  104. RowsAffected : Largeint;
  105. end;
  106. procedure freebindstring(astring: pointer); cdecl;
  107. begin
  108. StrDispose(AString);
  109. end;
  110. procedure TSQLite3Cursor.checkerror(const aerror: integer);
  111. Var
  112. S : String;
  113. begin
  114. if (aerror<>sqlite_ok) then
  115. begin
  116. S:=strpas(sqlite3_errmsg(fhandle));
  117. DatabaseError(S);
  118. end;
  119. end;
  120. Procedure TSQLite3Cursor.bindparams(AParams : TParams);
  121. Function PCharStr(Const S : String) : PChar;
  122. begin
  123. Result:=StrAlloc(Length(S)+1);
  124. If (Result<>Nil) then
  125. StrPCopy(Result,S);
  126. end;
  127. Var
  128. I : Integer;
  129. P : TParam;
  130. pc : pchar;
  131. str1: string;
  132. cu1: currency;
  133. do1: double;
  134. parms : array of Integer;
  135. wstr1: widestring;
  136. begin
  137. for I:=1 to high(fparambinding)+1 do
  138. begin
  139. P:=aparams[fparambinding[I-1]];
  140. if P.isnull then
  141. checkerror(sqlite3_bind_null(fstatement,I))
  142. else
  143. case P.datatype of
  144. ftinteger,
  145. ftboolean,
  146. ftsmallint: checkerror(sqlite3_bind_int(fstatement,I,p.asinteger));
  147. ftword: checkerror(sqlite3_bind_int(fstatement,I,P.asword));
  148. ftlargeint: checkerror(sqlite3_bind_int64(fstatement,I,P.aslargeint));
  149. ftbcd,
  150. ftfloat,
  151. ftcurrency:
  152. begin
  153. do1:= P.AsFloat;
  154. checkerror(sqlite3_bind_double(fstatement,I,do1));
  155. end;
  156. ftdatetime,
  157. ftdate,
  158. fttime: begin
  159. do1:= P.AsFloat + JulianDateShift;
  160. checkerror(sqlite3_bind_double(fstatement,I,do1));
  161. end;
  162. ftFMTBcd:
  163. begin
  164. str1:=BCDToStr(P.AsFMTBCD, Fconnection.FSQLFormatSettings);
  165. checkerror(sqlite3_bind_text(fstatement, I, PChar(str1), length(str1), sqlite3_destructor_type(SQLITE_TRANSIENT)));
  166. end;
  167. ftstring,
  168. ftFixedChar,
  169. ftmemo: begin // According to SQLite documentation, CLOB's (ftMemo) have the Text affinity
  170. str1:= p.asstring;
  171. checkerror(sqlite3_bind_text(fstatement,I,pcharstr(str1), length(str1),@freebindstring));
  172. end;
  173. ftBytes,
  174. ftVarBytes,
  175. ftBlob: begin
  176. str1:= P.asstring;
  177. checkerror(sqlite3_bind_blob(fstatement,I,pcharstr(str1), length(str1),@freebindstring));
  178. end;
  179. ftWideString, ftFixedWideChar, ftWideMemo:
  180. begin
  181. wstr1:=P.AsWideString;
  182. checkerror(sqlite3_bind_text16(fstatement,I, PWideChar(wstr1), length(wstr1)*sizeof(WideChar), sqlite3_destructor_type(SQLITE_TRANSIENT)));
  183. end
  184. else
  185. DatabaseErrorFmt(SUnsupportedParameter, [Fieldtypenames[P.DataType], Self]);
  186. end; { Case }
  187. end;
  188. end;
  189. Procedure TSQLite3Cursor.Prepare(Buf : String; APArams : TParams);
  190. begin
  191. if assigned(aparams) and (aparams.count > 0) then
  192. buf := aparams.parsesql(buf,false,false,false,psinterbase,fparambinding);
  193. checkerror(sqlite3_prepare(fhandle,pchar(buf),length(buf),@fstatement,@ftail));
  194. FPrepared:=True;
  195. end;
  196. Procedure TSQLite3Cursor.UnPrepare;
  197. begin
  198. sqlite3_finalize(fstatement); // No check.
  199. FPrepared:=False;
  200. end;
  201. Procedure TSQLite3Cursor.Execute;
  202. var
  203. wo1: word;
  204. begin
  205. {$ifdef i386}
  206. wo1:= get8087cw;
  207. set8087cw(wo1 or $1f); //mask exceptions, Sqlite3 has overflow
  208. Try // Why do people always forget this ??
  209. {$endif}
  210. fstate:= sqlite3_step(fstatement);
  211. {$ifdef i386}
  212. finally
  213. set8087cw(wo1); //restore
  214. end;
  215. {$endif}
  216. if (fstate<=sqliteerrormax) then
  217. checkerror(sqlite3_reset(fstatement));
  218. RowsAffected:=sqlite3_changes(fhandle);
  219. if (fstate=sqlite_row) then
  220. fstate:= sqliteerrormax; //first row
  221. end;
  222. Function TSQLite3Cursor.Fetch : Boolean;
  223. begin
  224. if (fstate=sqliteerrormax) then
  225. fstate:=sqlite_row //first row;
  226. else if (fstate=sqlite_row) then
  227. begin
  228. fstate:=sqlite3_step(fstatement);
  229. if (fstate<=sqliteerrormax) then
  230. checkerror(sqlite3_reset(fstatement)); //right error returned??
  231. end;
  232. result:=(fstate=sqlite_row);
  233. end;
  234. { TSQLite3Connection }
  235. procedure TSQLite3Connection.LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction);
  236. var
  237. int1: integer;
  238. st: psqlite3_stmt;
  239. fnum: integer;
  240. p1: Pointer;
  241. begin
  242. st:=TSQLite3Cursor(cursor).fstatement;
  243. fnum:= FieldDef.fieldno - 1;
  244. case FieldDef.DataType of
  245. ftWideMemo:
  246. begin
  247. p1 := sqlite3_column_text16(st,fnum);
  248. int1 := sqlite3_column_bytes16(st,fnum);
  249. end;
  250. ftMemo:
  251. begin
  252. p1 := sqlite3_column_text(st,fnum);
  253. int1 := sqlite3_column_bytes(st,fnum);
  254. end;
  255. else //ftBlob
  256. begin
  257. p1 := sqlite3_column_blob(st,fnum);
  258. int1 := sqlite3_column_bytes(st,fnum);
  259. end;
  260. end;
  261. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer, int1);
  262. if int1 > 0 then
  263. move(p1^, ABlobBuf^.BlobBuffer^.Buffer^, int1);
  264. ABlobBuf^.BlobBuffer^.Size := int1;
  265. end;
  266. function TSQLite3Connection.AllocateTransactionHandle: TSQLHandle;
  267. begin
  268. result:= tsqlhandle.create;
  269. end;
  270. function TSQLite3Connection.AllocateCursorHandle: TSQLCursor;
  271. Var
  272. Res : TSQLite3Cursor;
  273. begin
  274. Res:= TSQLite3Cursor.create;
  275. Res.fconnection:=Self;
  276. Result:=Res;
  277. end;
  278. procedure TSQLite3Connection.DeAllocateCursorHandle(var cursor: TSQLCursor);
  279. begin
  280. freeandnil(cursor);
  281. end;
  282. procedure TSQLite3Connection.PrepareStatement(cursor: TSQLCursor;
  283. ATransaction: TSQLTransaction; buf: string; AParams: TParams);
  284. begin
  285. TSQLite3Cursor(cursor).fhandle:=self.fhandle;
  286. TSQLite3Cursor(cursor).Prepare(Buf,AParams);
  287. end;
  288. procedure TSQLite3Connection.UnPrepareStatement(cursor: TSQLCursor);
  289. begin
  290. TSQLite3Cursor(cursor).UnPrepare;
  291. TSQLite3Cursor(cursor).fhandle:=nil;
  292. end;
  293. Type
  294. TFieldMap = Record
  295. N : String;
  296. T : TFieldType;
  297. end;
  298. Const
  299. FieldMapCount = 26;
  300. FieldMap : Array [1..FieldMapCount] of TFieldMap = (
  301. (n:'INT'; t: ftInteger),
  302. (n:'LARGEINT'; t:ftlargeInt),
  303. (n:'BIGINT'; t:ftlargeInt),
  304. (n:'WORD'; t: ftWord),
  305. (n:'SMALLINT'; t: ftSmallint),
  306. (n:'BOOLEAN'; t: ftBoolean),
  307. (n:'REAL'; t: ftFloat),
  308. (n:'FLOAT'; t: ftFloat),
  309. (n:'DOUBLE'; t: ftFloat),
  310. (n:'TIMESTAMP'; t: ftDateTime),
  311. (n:'DATETIME'; t: ftDateTime), // MUST be before date
  312. (n:'DATE'; t: ftDate),
  313. (n:'TIME'; t: ftTime),
  314. (n:'CURRENCY'; t: ftCurrency),
  315. (n:'VARCHAR'; t: ftString),
  316. (n:'CHAR'; t: ftFixedChar),
  317. (n:'NUMERIC'; t: ftBCD),
  318. (n:'DECIMAL'; t: ftBCD),
  319. (n:'TEXT'; t: ftmemo),
  320. (n:'CLOB'; t: ftmemo),
  321. (n:'BLOB'; t: ftBlob),
  322. (n:'NCHAR'; t: ftFixedWideChar),
  323. (n:'NVARCHAR'; t: ftWideString),
  324. (n:'NCLOB'; t: ftWideMemo),
  325. (n:'VARBINARY'; t: ftVarBytes),
  326. (n:'BINARY'; t: ftBytes)
  327. { Template:
  328. (n:''; t: ft)
  329. }
  330. );
  331. procedure TSQLite3Connection.AddFieldDefs(cursor: TSQLCursor;
  332. FieldDefs: TfieldDefs);
  333. var
  334. i : integer;
  335. FN,FD : string;
  336. ft1 : tfieldtype;
  337. size1, size2 : integer;
  338. ar1 : TStringArray;
  339. fi : integer;
  340. st : psqlite3_stmt;
  341. function ExtractPrecisionAndScale(decltype: string; var precision, scale: integer): boolean;
  342. var p: integer;
  343. begin
  344. p:=pos('(', decltype);
  345. Result:=p>0;
  346. if not Result then Exit;
  347. System.Delete(decltype,1,p);
  348. p:=pos(')', decltype);
  349. Result:=p>0;
  350. if not Result then Exit;
  351. decltype:=copy(decltype,1,p-1);
  352. p:=pos(',', decltype);
  353. if p=0 then
  354. begin
  355. precision:=StrToIntDef(decltype, precision);
  356. scale:=0;
  357. end
  358. else
  359. begin
  360. precision:=StrToIntDef(copy(decltype,1,p-1), precision);
  361. scale:=StrToIntDef(copy(decltype,p+1,length(decltype)-p), scale);
  362. end;
  363. end;
  364. begin
  365. st:=TSQLite3Cursor(cursor).fstatement;
  366. for i:= 0 to sqlite3_column_count(st) - 1 do
  367. begin
  368. FN:=sqlite3_column_name(st,i);
  369. FD:=uppercase(sqlite3_column_decltype(st,i));
  370. ft1:= ftUnknown;
  371. size1:= 0;
  372. for fi := 1 to FieldMapCount do if pos(FieldMap[fi].N,FD)=1 then
  373. begin
  374. ft1:=FieldMap[fi].t;
  375. break;
  376. end;
  377. // In case of an empty fieldtype (FD='', which is allowed and used in calculated
  378. // columns (aggregates) and by pragma-statements) or an unknown fieldtype,
  379. // use the field's affinity:
  380. if ft1=ftUnknown then
  381. case TStorageType(sqlite3_column_type(st,i)) of
  382. stInteger: ft1:=ftLargeInt;
  383. stFloat: ft1:=ftFloat;
  384. stBlob: ft1:=ftBlob;
  385. else ft1:=ftString;
  386. end;
  387. // handle some specials.
  388. size1:=0;
  389. case ft1 of
  390. ftString,
  391. ftFixedChar,
  392. ftFixedWideChar,
  393. ftWideString,
  394. ftBytes,
  395. ftVarBytes:
  396. begin
  397. size1 := 255; //sql: if length is omitted then length is 1
  398. size2 := 0;
  399. ExtractPrecisionAndScale(FD, size1, size2);
  400. if size1 > dsMaxStringSize then size1 := dsMaxStringSize;
  401. end;
  402. ftBCD: begin
  403. size2 := MaxBCDPrecision; //sql: if a precision is omitted, then use implementation-defined
  404. size1 := 0; //sql: if a scale is omitted then scale is 0
  405. ExtractPrecisionAndScale(FD, size2, size1);
  406. if (size2<=18) and (size1=0) then
  407. ft1:=ftLargeInt
  408. else if (size2-size1>MaxBCDPrecision-MaxBCDScale) or (size1>MaxBCDScale) then
  409. ft1:=ftFmtBCD;
  410. end;
  411. ftUnknown : DatabaseError('Unknown record type: '+FN);
  412. end; // Case
  413. tfielddef.create(fielddefs,FieldDefs.MakeNameUnique(FN),ft1,size1,false,i+1);
  414. end;
  415. end;
  416. procedure TSQLite3Connection.Execute(cursor: TSQLCursor; atransaction: tsqltransaction; AParams: TParams);
  417. var
  418. SC : TSQLite3Cursor;
  419. begin
  420. SC:=TSQLite3Cursor(cursor);
  421. checkerror(sqlite3_reset(sc.fstatement));
  422. If (AParams<>Nil) and (AParams.count > 0) then
  423. SC.BindParams(AParams);
  424. SC.Execute;
  425. end;
  426. Function NextWord(Var S : ShortString; Sep : Char) : String;
  427. Var
  428. P : Integer;
  429. begin
  430. P:=Pos(Sep,S);
  431. If (P=0) then
  432. P:=Length(S)+1;
  433. Result:=Copy(S,1,P-1);
  434. Delete(S,1,P);
  435. end;
  436. Function ParseSQLiteDate(S : ShortString) : TDateTime;
  437. Var
  438. Year, Month, Day : Integer;
  439. begin
  440. Result:=0;
  441. If TryStrToInt(NextWord(S,'-'),Year) then
  442. if TryStrToInt(NextWord(S,'-'),Month) then
  443. if TryStrToInt(NextWord(S,' '),Day) then
  444. Result:=EncodeDate(Year,Month,Day);
  445. end;
  446. Function ParseSQLiteTime(S : ShortString; Interval: boolean) : TDateTime;
  447. Var
  448. Hour, Min, Sec, MSec : Integer;
  449. begin
  450. Result:=0;
  451. If TryStrToInt(NextWord(S,':'),Hour) then
  452. if TryStrToInt(NextWord(S,':'),Min) then
  453. if TryStrToInt(NextWord(S,'.'),Sec) then
  454. begin
  455. MSec:=StrToIntDef(S,0);
  456. if Interval then
  457. Result:=EncodeTimeInterval(Hour,Min,Sec,MSec)
  458. else
  459. Result:=EncodeTime(Hour,Min,Sec,MSec);
  460. end;
  461. end;
  462. Function ParseSQLiteDateTime(S : String) : TDateTime;
  463. var
  464. P : Integer;
  465. DS,TS : ShortString;
  466. begin
  467. DS:='';
  468. TS:='';
  469. P:=Pos(' ',S);
  470. If (P<>0) then
  471. begin
  472. DS:=Copy(S,1,P-1);
  473. TS:=S;
  474. Delete(TS,1,P);
  475. end
  476. else
  477. begin
  478. If (Pos('-',S)<>0) then
  479. DS:=S
  480. else if (Pos(':',S)<>0) then
  481. TS:=S;
  482. end;
  483. Result:=ComposeDateTime(ParseSQLiteDate(DS),ParseSQLiteTime(TS,False));
  484. end;
  485. function TSQLite3Connection.LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean;
  486. var
  487. st1: TStorageType;
  488. fnum: integer;
  489. str1: string;
  490. int1 : integer;
  491. bcd: tBCD;
  492. bcdstr: FmtBCDStringtype;
  493. st : psqlite3_stmt;
  494. begin
  495. st:=TSQLite3Cursor(cursor).fstatement;
  496. fnum:= FieldDef.fieldno - 1;
  497. st1:= TStorageType(sqlite3_column_type(st,fnum));
  498. CreateBlob:=false;
  499. result:= st1 <> stnull;
  500. if Not result then
  501. Exit;
  502. case FieldDef.datatype of
  503. ftInteger : pinteger(buffer)^ := sqlite3_column_int(st,fnum);
  504. ftSmallInt : psmallint(buffer)^ := sqlite3_column_int(st,fnum);
  505. ftWord : pword(buffer)^ := sqlite3_column_int(st,fnum);
  506. ftBoolean : pwordbool(buffer)^ := sqlite3_column_int(st,fnum)<>0;
  507. ftLargeInt : PInt64(buffer)^:= sqlite3_column_int64(st,fnum);
  508. ftBCD : PCurrency(buffer)^:= FloattoCurr(sqlite3_column_double(st,fnum));
  509. ftFloat,
  510. ftCurrency : pdouble(buffer)^:= sqlite3_column_double(st,fnum);
  511. ftDateTime,
  512. ftDate,
  513. ftTime: if st1 = sttext then
  514. begin
  515. setlength(str1,sqlite3_column_bytes(st,fnum));
  516. move(sqlite3_column_text(st,fnum)^,str1[1],length(str1));
  517. case FieldDef.datatype of
  518. ftDateTime: PDateTime(Buffer)^:=ParseSqliteDateTime(str1);
  519. ftDate : PDateTime(Buffer)^:=ParseSqliteDate(str1);
  520. ftTime : PDateTime(Buffer)^:=ParseSQLiteTime(str1,true);
  521. end; {case}
  522. end
  523. else
  524. begin
  525. PDateTime(buffer)^ := sqlite3_column_double(st,fnum);
  526. if PDateTime(buffer)^ > 1721059.5 {Julian 01/01/0000} then
  527. PDateTime(buffer)^ := PDateTime(buffer)^ - JulianDateShift; //backward compatibility hack
  528. end;
  529. ftFixedChar,
  530. ftString: begin
  531. int1:= sqlite3_column_bytes(st,fnum);
  532. if int1>FieldDef.Size then
  533. int1:=FieldDef.Size;
  534. if int1 > 0 then
  535. move(sqlite3_column_text(st,fnum)^,buffer^,int1);
  536. end;
  537. ftFmtBCD: begin
  538. int1:= sqlite3_column_bytes(st,fnum);
  539. if (int1 > 0) and (int1 <= MAXFMTBcdFractionSize) then
  540. begin
  541. SetLength(bcdstr,int1);
  542. move(sqlite3_column_text(st,fnum)^,bcdstr[1],int1);
  543. // sqlite always uses the point as decimal-point
  544. if not TryStrToBCD(bcdstr,bcd,FSQLFormatSettings) then
  545. // sqlite does the same, if the value can't be interpreted as a
  546. // number in sqlite3_column_int, return 0
  547. bcd := 0;
  548. end
  549. else
  550. bcd := 0;
  551. pBCD(buffer)^:= bcd;
  552. end;
  553. ftFixedWideChar,
  554. ftWideString:
  555. begin
  556. int1 := sqlite3_column_bytes16(st,fnum)+2; //The value returned does not include the zero terminator at the end of the string
  557. if int1>(FieldDef.Size+1)*2 then
  558. int1:=(FieldDef.Size+1)*2;
  559. if int1 > 0 then
  560. 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.
  561. end;
  562. ftVarBytes,
  563. ftBytes:
  564. begin
  565. int1 := sqlite3_column_bytes(st,fnum);
  566. if int1 > FieldDef.Size then
  567. int1 := FieldDef.Size;
  568. if FieldDef.DataType = ftVarBytes then
  569. begin
  570. PWord(buffer)^ := int1;
  571. inc(buffer, sizeof(Word));
  572. end;
  573. if int1 > 0 then
  574. move(sqlite3_column_blob(st,fnum)^, buffer^, int1);
  575. end;
  576. ftWideMemo,
  577. ftMemo,
  578. ftBlob: CreateBlob:=True;
  579. else { Case }
  580. result:= false; // unknown
  581. end; { Case }
  582. end;
  583. function TSQLite3Connection.Fetch(cursor: TSQLCursor): boolean;
  584. begin
  585. Result:=TSQLite3Cursor(cursor).Fetch;
  586. end;
  587. procedure TSQLite3Connection.FreeFldBuffers(cursor: TSQLCursor);
  588. begin
  589. //dummy
  590. end;
  591. function TSQLite3Connection.GetTransactionHandle(trans: TSQLHandle): pointer;
  592. begin
  593. result:= nil;
  594. end;
  595. function TSQLite3Connection.Commit(trans: TSQLHandle): boolean;
  596. begin
  597. execsql('COMMIT');
  598. result:= true;
  599. end;
  600. function TSQLite3Connection.RollBack(trans: TSQLHandle): boolean;
  601. begin
  602. execsql('ROLLBACK');
  603. result:= true;
  604. end;
  605. function TSQLite3Connection.StartdbTransaction(trans: TSQLHandle;
  606. aParams: string): boolean;
  607. begin
  608. execsql('BEGIN');
  609. result:= true;
  610. end;
  611. procedure TSQLite3Connection.CommitRetaining(trans: TSQLHandle);
  612. begin
  613. commit(trans);
  614. execsql('BEGIN');
  615. end;
  616. procedure TSQLite3Connection.RollBackRetaining(trans: TSQLHandle);
  617. begin
  618. rollback(trans);
  619. execsql('BEGIN');
  620. end;
  621. procedure TSQLite3Connection.DoInternalConnect;
  622. var
  623. str1: string;
  624. begin
  625. if Length(databasename)=0 then
  626. DatabaseError(SErrNoDatabaseName,self);
  627. InitializeSqlite(SQLiteLibraryName);
  628. str1:= databasename;
  629. checkerror(sqlite3_open(pchar(str1),@fhandle));
  630. if (Length(Password)>0) and assigned(sqlite3_key) then
  631. checkerror(sqlite3_key(fhandle,PChar(Password),StrLen(PChar(Password))));
  632. if Params.IndexOfName('foreign_keys') <> -1 then
  633. execsql('PRAGMA foreign_keys = '+Params.Values['foreign_keys']);
  634. end;
  635. procedure TSQLite3Connection.DoInternalDisconnect;
  636. begin
  637. if fhandle <> nil then
  638. begin
  639. checkerror(sqlite3_close(fhandle));
  640. fhandle:= nil;
  641. releasesqlite;
  642. end;
  643. end;
  644. function TSQLite3Connection.GetHandle: pointer;
  645. begin
  646. result:= fhandle;
  647. end;
  648. procedure TSQLite3Connection.checkerror(const aerror: integer);
  649. Var
  650. S : String;
  651. begin
  652. if (aerror<>sqlite_ok) then
  653. begin
  654. S:=strpas(sqlite3_errmsg(fhandle));
  655. DatabaseError(S,Self);
  656. end;
  657. end;
  658. procedure TSQLite3Connection.execsql(const asql: string);
  659. var
  660. err : pchar;
  661. str1 : string;
  662. res : integer;
  663. begin
  664. err:= nil;
  665. Res := sqlite3_exec(fhandle,pchar(asql),nil,nil,@err);
  666. if err <> nil then
  667. begin
  668. str1:= strpas(err);
  669. sqlite3_free(err);
  670. end;
  671. if (res<>sqlite_ok) then
  672. databaseerror(str1);
  673. end;
  674. function execcallback(adata: pointer; ncols: longint; //adata = PStringArray
  675. avalues: PPchar; anames: PPchar):longint; cdecl;
  676. var
  677. P : PStringArray;
  678. i : integer;
  679. begin
  680. P:=PStringArray(adata);
  681. SetLength(P^,ncols);
  682. for i:= 0 to ncols - 1 do
  683. P^[i]:= strPas(avalues[i]);
  684. result:= 0;
  685. end;
  686. function execscallback(adata: pointer; ncols: longint; //adata = PArrayStringArray
  687. avalues: PPchar; anames: PPchar):longint; cdecl;
  688. var
  689. I,N : integer;
  690. PP : PArrayStringArray;
  691. p : PStringArray;
  692. begin
  693. PP:=PArrayStringArray(adata);
  694. N:=high(PP^); // Length-1;
  695. setlength(PP^,N+2); // increase with 1;
  696. p:= @(PP^[N+1]); // newly added array, fill with data.
  697. setlength(p^,ncols);
  698. for i:= 0 to ncols - 1 do
  699. p^[i]:= strPas(avalues[i]);
  700. result:= 0;
  701. end;
  702. function TSQLite3Connection.stringsquery(const asql: string): TArrayStringArray;
  703. begin
  704. SetLength(result,0);
  705. checkerror(sqlite3_exec(fhandle,pchar(asql),@execscallback,@result,nil));
  706. end;
  707. function TSQLite3Connection.RowsAffected(cursor: TSQLCursor): TRowsCount;
  708. begin
  709. if assigned(cursor) then
  710. Result := (cursor as TSQLite3Cursor).RowsAffected
  711. else
  712. Result := -1;
  713. end;
  714. function TSQLite3Connection.GetSchemaInfoSQL(SchemaType: TSchemaType;
  715. SchemaObjectName, SchemaPattern: string): string;
  716. begin
  717. case SchemaType of
  718. stTables : result := 'select name as table_name from sqlite_master where type = ''table'' order by 1';
  719. stColumns : result := 'pragma table_info(''' + (SchemaObjectName) + ''')';
  720. else
  721. DatabaseError(SMetadataUnavailable)
  722. end; {case}
  723. end;
  724. function TSQLite3Connection.StrToStatementType(s: string): TStatementType;
  725. begin
  726. S:=Lowercase(s);
  727. if s = 'pragma' then exit(stSelect);
  728. result := inherited StrToStatementType(s);
  729. end;
  730. constructor TSQLite3Connection.Create(AOwner: TComponent);
  731. begin
  732. inherited Create(AOwner);
  733. FConnOptions := FConnOptions + [sqEscapeRepeat] + [sqEscapeSlash];
  734. FieldNameQuoteChars:=DoubleQuotes;
  735. end;
  736. procedure TSQLite3Connection.UpdateIndexDefs(IndexDefs: TIndexDefs; TableName: string);
  737. var
  738. artableinfo, arindexlist, arindexinfo: TArrayStringArray;
  739. il,ii: integer;
  740. IndexName: string;
  741. IndexOptions: TIndexOptions;
  742. PKFields, IXFields: TStrings;
  743. function CheckPKFields:boolean;
  744. var i: integer;
  745. begin
  746. Result:=false;
  747. if IXFields.Count<>PKFields.Count then Exit;
  748. for i:=0 to IXFields.Count-1 do
  749. if PKFields.IndexOf(IXFields[i])<0 then Exit;
  750. Result:=true;
  751. PKFields.Clear;
  752. end;
  753. begin
  754. PKFields:=TStringList.Create;
  755. PKFields.Delimiter:=';';
  756. IXFields:=TStringList.Create;
  757. IXFields.Delimiter:=';';
  758. //primary key fields
  759. artableinfo := stringsquery('PRAGMA table_info('+TableName+');');
  760. for ii:=low(artableinfo) to high(artableinfo) do
  761. if (high(artableinfo[ii]) >= 5) and (artableinfo[ii][5] = '1') then
  762. PKFields.Add(artableinfo[ii][1]);
  763. //list of all table indexes
  764. arindexlist:=stringsquery('PRAGMA index_list('+TableName+');');
  765. for il:=low(arindexlist) to high(arindexlist) do
  766. begin
  767. IndexName:=arindexlist[il][1];
  768. if arindexlist[il][2]='1' then
  769. IndexOptions:=[ixUnique]
  770. else
  771. IndexOptions:=[];
  772. //list of columns in given index
  773. arindexinfo:=stringsquery('PRAGMA index_info('+IndexName+');');
  774. IXFields.Clear;
  775. for ii:=low(arindexinfo) to high(arindexinfo) do
  776. IXFields.Add(arindexinfo[ii][2]);
  777. if CheckPKFields then IndexOptions:=IndexOptions+[ixPrimary];
  778. IndexDefs.Add(IndexName, IXFields.DelimitedText, IndexOptions);
  779. end;
  780. if PKFields.Count > 0 then //in special case for INTEGER PRIMARY KEY column, unique index is not created
  781. IndexDefs.Add('$PRIMARY_KEY$', PKFields.DelimitedText, [ixPrimary,ixUnique]);
  782. PKFields.Free;
  783. IXFields.Free;
  784. end;
  785. function TSQLite3Connection.getinsertid: int64;
  786. begin
  787. result:= sqlite3_last_insert_rowid(fhandle);
  788. end;
  789. procedure TSQLite3Connection.GetFieldNames(const TableName: string;
  790. List: TStrings);
  791. begin
  792. GetDBInfo(stColumns,TableName,'name',List);
  793. end;
  794. procedure Tsqlite3connection.LoadExtension(Libraryfile: String);
  795. var
  796. LoadResult: integer;
  797. begin
  798. CheckConnected; //Apparently we need a connection before we can load extensions.
  799. LoadResult:=SQLITE_ERROR; //Default to failed
  800. try
  801. LoadResult:=sqlite3_enable_load_extension(fhandle, 1); //Make sure we are allowed to load
  802. if LoadResult=SQLITE_OK then
  803. begin
  804. LoadResult:=sqlite3_load_extension(fhandle, PChar(LibraryFile), nil, nil); //Actually load extension
  805. if LoadResult=SQLITE_ERROR then
  806. begin
  807. DatabaseError('LoadExtension: failed to load SQLite extension (SQLite returned an error while loading).',Self);
  808. end;
  809. end
  810. else
  811. begin
  812. DatabaseError('LoadExtension: failed to load SQLite extension (SQLite returned an error while enabling extensions).',Self);
  813. end;
  814. except
  815. DatabaseError('LoadExtension: failed to load SQLite extension.',Self)
  816. end;
  817. end;
  818. procedure TSQLite3Connection.setoptions(const avalue: tsqliteoptions);
  819. begin
  820. if avalue <> foptions then
  821. begin
  822. checkdisconnected;
  823. foptions:= avalue;
  824. end;
  825. end;
  826. end.