sqlite3conn.pp 29 KB

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