sqlite3conn.pp 25 KB

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