sqlite3conn.pp 23 KB

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