sqlite3conn.pp 29 KB

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