sqlite3conn.pp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  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 stringquery(const asql: string): TStringArray;
  39. function stringsquery(const asql: string): TArrayStringArray;
  40. procedure checkerror(const aerror: integer);
  41. procedure DoInternalConnect; override;
  42. procedure DoInternalDisconnect; override;
  43. function GetHandle : pointer; override;
  44. Function AllocateCursorHandle : TSQLCursor; override;
  45. //aowner used as blob cache
  46. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); override;
  47. Function AllocateTransactionHandle : TSQLHandle; override;
  48. procedure PrepareStatement(cursor: TSQLCursor; ATransaction : TSQLTransaction;
  49. buf: string; AParams : TParams); override;
  50. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); override;
  51. function Fetch(cursor : TSQLCursor) : boolean; override;
  52. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); override;
  53. procedure UnPrepareStatement(cursor : TSQLCursor); override;
  54. procedure FreeFldBuffers(cursor : TSQLCursor); override;
  55. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
  56. //if bufsize < 0 -> buffer was to small, should be -bufsize
  57. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  58. function Commit(trans : TSQLHandle) : boolean; override;
  59. function RollBack(trans : TSQLHandle) : boolean; override;
  60. function StartdbTransaction(trans : TSQLHandle; aParams : string) : boolean; override;
  61. procedure CommitRetaining(trans : TSQLHandle); override;
  62. procedure RollBackRetaining(trans : TSQLHandle); override;
  63. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction); override;
  64. // New methods
  65. procedure execsql(const asql: string);
  66. procedure UpdateIndexDefs(var IndexDefs : TIndexDefs; const TableName : string); // Differs from SQLDB.
  67. function getprimarykeyfield(const atablename: string; const acursor: tsqlcursor): string;
  68. function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
  69. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; override;
  70. function StrToStatementType(s : string) : TStatementType; override;
  71. public
  72. function GetInsertID: int64;
  73. procedure GetFieldNames(const TableName : string; List : TStrings); override;
  74. published
  75. property Options: TSqliteOptions read FOptions write SetOptions;
  76. end;
  77. implementation
  78. uses
  79. dbconst, sysutils, typinfo, dateutils;
  80. type
  81. TStorageType = (stNone,stInteger,stFloat,stText,stBlob,stNull);
  82. TSQLite3Cursor = class(tsqlcursor)
  83. private
  84. fhandle : psqlite3;
  85. fstatement: psqlite3_stmt;
  86. ftail: pchar;
  87. fstate: integer;
  88. fparambinding: array of Integer;
  89. procedure checkerror(const aerror: integer);
  90. procedure bindparams(AParams : TParams);
  91. Procedure Prepare(Buf : String; APArams : TParams);
  92. Procedure UnPrepare;
  93. Procedure Execute;
  94. Function Fetch : Boolean;
  95. public
  96. RowsAffected : Largeint;
  97. end;
  98. procedure freebindstring(astring: pointer); cdecl;
  99. begin
  100. StrDispose(AString);
  101. end;
  102. procedure TSQLite3Cursor.checkerror(const aerror: integer);
  103. Var
  104. S : String;
  105. begin
  106. if (aerror<>sqlite_ok) then
  107. begin
  108. S:=strpas(sqlite3_errmsg(fhandle));
  109. DatabaseError(S);
  110. end;
  111. end;
  112. Procedure TSQLite3Cursor.bindparams(AParams : TParams);
  113. Function PCharStr(Const S : String) : PChar;
  114. begin
  115. Result:=StrAlloc(Length(S)+1);
  116. If (Result<>Nil) then
  117. StrPCopy(Result,S);
  118. end;
  119. Var
  120. I : Integer;
  121. P : TParam;
  122. pc : pchar;
  123. str1: string;
  124. cu1: currency;
  125. do1: double;
  126. parms : array of Integer;
  127. begin
  128. for I:=1 to high(fparambinding)+1 do
  129. begin
  130. P:=aparams[fparambinding[I-1]];
  131. if P.isnull then
  132. checkerror(sqlite3_bind_null(fstatement,I))
  133. else
  134. case P.datatype of
  135. ftinteger,
  136. ftboolean,
  137. ftsmallint: checkerror(sqlite3_bind_int(fstatement,I,p.asinteger));
  138. ftword: checkerror(sqlite3_bind_int(fstatement,I,P.asword));
  139. ftlargeint: checkerror(sqlite3_bind_int64(fstatement,I,P.aslargeint));
  140. ftbcd: begin
  141. cu1:= P.ascurrency;
  142. checkerror(sqlite3_bind_int64(fstatement,I,pint64(@cu1)^));
  143. end;
  144. ftfloat,
  145. ftcurrency,
  146. ftdatetime,
  147. ftdate,
  148. fttime: begin
  149. do1:= P.asfloat;
  150. checkerror(sqlite3_bind_double(fstatement,I,do1));
  151. end;
  152. ftstring: begin
  153. str1:= p.asstring;
  154. checkerror(sqlite3_bind_text(fstatement,I,pcharstr(str1), length(str1),@freebindstring));
  155. end;
  156. ftblob: begin
  157. str1:= P.asstring;
  158. checkerror(sqlite3_bind_blob(fstatement,I,pcharstr(str1), length(str1),@freebindstring));
  159. end;
  160. else
  161. databaseerror('Parameter type '+getenumname(typeinfo(tfieldtype),ord(P.datatype))+' not supported.');
  162. end; { Case }
  163. end;
  164. end;
  165. Procedure TSQLite3Cursor.Prepare(Buf : String; APArams : TParams);
  166. begin
  167. if assigned(aparams) and (aparams.count > 0) then
  168. buf := aparams.parsesql(buf,false,false,false,psinterbase,fparambinding);
  169. checkerror(sqlite3_prepare(fhandle,pchar(buf),length(buf),@fstatement,@ftail));
  170. end;
  171. Procedure TSQLite3Cursor.UnPrepare;
  172. begin
  173. sqlite3_finalize(fstatement); // No check.
  174. end;
  175. Procedure TSQLite3Cursor.Execute;
  176. var
  177. wo1: word;
  178. begin
  179. {$ifdef i386}
  180. wo1:= get8087cw;
  181. set8087cw(wo1 or $1f); //mask exceptions, Sqlite3 has overflow
  182. Try // Why do people always forget this ??
  183. {$endif}
  184. fstate:= sqlite3_step(fstatement);
  185. {$ifdef i386}
  186. finally
  187. set8087cw(wo1); //restore
  188. end;
  189. {$endif}
  190. if (fstate<=sqliteerrormax) then
  191. checkerror(sqlite3_reset(fstatement));
  192. RowsAffected:=sqlite3_changes(fhandle);
  193. if (fstate=sqlite_row) then
  194. fstate:= sqliteerrormax; //first row
  195. end;
  196. Function TSQLite3Cursor.Fetch : Boolean;
  197. begin
  198. if (fstate=sqliteerrormax) then
  199. fstate:=sqlite_row //first row;
  200. else if (fstate=sqlite_row) then
  201. begin
  202. fstate:=sqlite3_step(fstatement);
  203. if (fstate<=sqliteerrormax) then
  204. checkerror(sqlite3_reset(fstatement)); //right error returned??
  205. end;
  206. result:=(fstate=sqlite_row);
  207. end;
  208. { TSQLite3Connection }
  209. procedure TSQLite3Connection.LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction);
  210. var
  211. int1: integer;
  212. st: psqlite3_stmt;
  213. fnum: integer;
  214. begin
  215. st:=TSQLite3Cursor(cursor).fstatement;
  216. fnum:= FieldDef.fieldno - 1;
  217. int1:= sqlite3_column_bytes(st,fnum);
  218. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer,int1);
  219. if int1 > 0 then
  220. move(sqlite3_column_text(st,fnum)^,ABlobBuf^.BlobBuffer^.Buffer^,int1);
  221. ABlobBuf^.BlobBuffer^.Size := int1;
  222. end;
  223. function TSQLite3Connection.AllocateTransactionHandle: TSQLHandle;
  224. begin
  225. result:= tsqlhandle.create;
  226. end;
  227. function TSQLite3Connection.AllocateCursorHandle: TSQLCursor;
  228. Var
  229. Res : TSQLite3Cursor;
  230. begin
  231. Res:= TSQLite3Cursor.create;
  232. Res.fhandle:=self.fhandle;
  233. Result:=Res;
  234. end;
  235. procedure TSQLite3Connection.DeAllocateCursorHandle(var cursor: TSQLCursor);
  236. begin
  237. freeandnil(cursor);
  238. end;
  239. procedure TSQLite3Connection.PrepareStatement(cursor: TSQLCursor;
  240. ATransaction: TSQLTransaction; buf: string; AParams: TParams);
  241. begin
  242. TSQLite3Cursor(cursor).Prepare(Buf,AParams);
  243. end;
  244. procedure TSQLite3Connection.UnPrepareStatement(cursor: TSQLCursor);
  245. begin
  246. TSQLite3Cursor(cursor).UnPrepare;
  247. end;
  248. Type
  249. TFieldMap = Record
  250. N : String;
  251. T : TFieldType;
  252. end;
  253. Const
  254. FieldMapCount = 19;
  255. FieldMap : Array [1..FieldMapCount] of TFieldMap = (
  256. (n:'INT'; t: ftInteger),
  257. (n:'LARGEINT'; t:ftlargeInt),
  258. (n:'WORD'; t: ftWord),
  259. (n:'SMALLINT'; t: ftSmallint),
  260. (n:'BOOLEAN'; t: ftBoolean),
  261. (n:'REAL'; t: ftFloat),
  262. (n:'FLOAT'; t: ftFloat),
  263. (n:'DOUBLE'; t: ftFloat),
  264. (n:'TIMESTAMP'; t: ftDateTime),
  265. (n:'DATETIME'; t: ftDateTime), // MUST be before date
  266. (n:'DATE'; t: ftDate),
  267. (n:'TIME'; t: ftTime),
  268. (n:'CURRENCY'; t: ftCurrency),
  269. (n:'VARCHAR'; t: ftString),
  270. (n:'CHAR'; t: ftString),
  271. (n:'NUMERIC'; t: ftBCD),
  272. (n:'DECIMAL'; t: ftBCD),
  273. (n:'TEXT'; t: ftmemo),
  274. (n:'BLOB'; t: ftBlob)
  275. { Template:
  276. (n:''; t: ft)
  277. }
  278. );
  279. procedure TSQLite3Connection.AddFieldDefs(cursor: TSQLCursor;
  280. FieldDefs: TfieldDefs);
  281. var
  282. i : integer;
  283. FN,FD : string;
  284. ft1 : tfieldtype;
  285. size1 : word;
  286. ar1 : TStringArray;
  287. fi : integer;
  288. st : psqlite3_stmt;
  289. begin
  290. st:=TSQLite3Cursor(cursor).fstatement;
  291. for i:= 0 to sqlite3_column_count(st) - 1 do
  292. begin
  293. FN:=sqlite3_column_name(st,i);
  294. FD:=uppercase(sqlite3_column_decltype(st,i));
  295. ft1:= ftUnknown;
  296. size1:= 0;
  297. for fi := 1 to FieldMapCount do if pos(FieldMap[fi].N,FD)=1 then
  298. begin
  299. ft1:=FieldMap[fi].t;
  300. break;
  301. end;
  302. // Empty field types are allowed and used in calculated columns (aggregates)
  303. // and by pragma-statements
  304. if FD='' then ft1 := ftString;
  305. // handle some specials.
  306. size1:=0;
  307. case ft1 of
  308. ftString: begin
  309. fi:=pos('(',FD);
  310. if (fi>0) then
  311. begin
  312. System.Delete(FD,1,fi);
  313. fi:=pos(')',FD);
  314. size1:=StrToIntDef(trim(copy(FD,1,fi-1)),255);
  315. if size1 > dsMaxStringSize then size1 := dsMaxStringSize;
  316. end
  317. else size1 := 255;
  318. end;
  319. ftBCD: begin
  320. fi:=pos(',',FD);
  321. if (fi>0) then
  322. begin
  323. System.Delete(FD,1,fi);
  324. fi:=pos(')',FD);
  325. size1:=StrToIntDef(trim(copy(FD,1,fi-1)),255);
  326. end
  327. else size1 := 4;
  328. end;
  329. ftUnknown : DatabaseError('Unknown record type: '+FN);
  330. end; // Case
  331. tfielddef.create(fielddefs,FN,ft1,size1,false,i+1);
  332. end;
  333. end;
  334. procedure TSQLite3Connection.Execute(cursor: TSQLCursor; atransaction: tsqltransaction; AParams: TParams);
  335. var
  336. SC : TSQLite3Cursor;
  337. begin
  338. SC:=TSQLite3Cursor(cursor);
  339. If (AParams<>Nil) and (AParams.count > 0) then
  340. SC.BindParams(AParams);
  341. SC.Execute;
  342. end;
  343. Function NextWord(Var S : ShortString; Sep : Char) : String;
  344. Var
  345. P : Integer;
  346. begin
  347. P:=Pos(Sep,S);
  348. If (P=0) then
  349. P:=Length(S)+1;
  350. Result:=Copy(S,1,P-1);
  351. Delete(S,1,P);
  352. end;
  353. Function ParseSQLiteDate(S : ShortString) : TDateTime;
  354. Var
  355. Year, Month, Day : Integer;
  356. begin
  357. Result:=0;
  358. If TryStrToInt(NextWord(S,'-'),Year) then
  359. if TryStrToInt(NextWord(S,'-'),Month) then
  360. if TryStrToInt(NextWord(S,'-'),Day) then
  361. Result:=EncodeDate(Year,Month,Day);
  362. end;
  363. Function ParseSQLiteTime(S : ShortString) : TDateTime;
  364. Var
  365. Hour, Min, Sec : Integer;
  366. begin
  367. Result:=0;
  368. If TryStrToInt(NextWord(S,':'),Hour) then
  369. if TryStrToInt(NextWord(S,':'),Min) then
  370. if TryStrToInt(NextWord(S,':'),Sec) then
  371. Result:=EncodeTime(Hour,Min,Sec,0);
  372. end;
  373. Function ParseSQLiteDateTime(S : String) : TDateTime;
  374. var
  375. P : Integer;
  376. DS,TS : ShortString;
  377. begin
  378. DS:='';
  379. TS:='';
  380. P:=Pos(' ',S);
  381. If (P<>0) then
  382. begin
  383. DS:=Copy(S,1,P-1);
  384. TS:=S;
  385. Delete(TS,1,P);
  386. end
  387. else
  388. begin
  389. If (Pos('-',S)<>0) then
  390. DS:=S
  391. else if (Pos(':',S)<>0) then
  392. TS:=S;
  393. end;
  394. Result:=ComposeDateTime(ParseSQLiteDate(DS),ParseSQLiteTime(TS));
  395. end;
  396. function TSQLite3Connection.LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean;
  397. var
  398. st1: TStorageType;
  399. fnum: integer;
  400. i: integer;
  401. i64: int64;
  402. int1,int2: integer;
  403. str1: string;
  404. ar1,ar2: TStringArray;
  405. st : psqlite3_stmt;
  406. begin
  407. st:=TSQLite3Cursor(cursor).fstatement;
  408. fnum:= FieldDef.fieldno - 1;
  409. st1:= TStorageType(sqlite3_column_type(st,fnum));
  410. CreateBlob:=false;
  411. result:= st1 <> stnull;
  412. if Not result then
  413. Exit;
  414. case FieldDef.datatype of
  415. ftInteger : pinteger(buffer)^ := sqlite3_column_int(st,fnum);
  416. ftSmallInt : psmallint(buffer)^ := sqlite3_column_int(st,fnum);
  417. ftWord : pword(buffer)^ := sqlite3_column_int(st,fnum);
  418. ftBoolean : pwordbool(buffer)^ := sqlite3_column_int(st,fnum)<>0;
  419. ftLargeInt,
  420. ftBCD : PInt64(buffer)^:= sqlite3_column_int64(st,fnum);
  421. ftFloat,
  422. ftCurrency : pdouble(buffer)^:= sqlite3_column_double(st,fnum);
  423. ftDateTime,
  424. ftDate,
  425. ftTime: if st1 = sttext then
  426. begin
  427. setlength(str1,sqlite3_column_bytes(st,fnum));
  428. move(sqlite3_column_text(st,fnum)^,str1[1],length(str1));
  429. PDateTime(Buffer)^:=ParseSqliteDateTime(str1)
  430. end
  431. else
  432. Pdatetime(buffer)^:= sqlite3_column_double(st,fnum);
  433. ftString: begin
  434. int1:= sqlite3_column_bytes(st,fnum);
  435. if int1>FieldDef.Size then
  436. int1:=FieldDef.Size;
  437. if int1 > 0 then
  438. move(sqlite3_column_text(st,fnum)^,buffer^,int1);
  439. end;
  440. ftMemo,
  441. ftBlob: CreateBlob:=True;
  442. else { Case }
  443. result:= false; // unknown
  444. end; { Case }
  445. end;
  446. function TSQLite3Connection.Fetch(cursor: TSQLCursor): boolean;
  447. begin
  448. Result:=TSQLite3Cursor(cursor).Fetch;
  449. end;
  450. procedure TSQLite3Connection.FreeFldBuffers(cursor: TSQLCursor);
  451. begin
  452. //dummy
  453. end;
  454. function TSQLite3Connection.GetTransactionHandle(trans: TSQLHandle): pointer;
  455. begin
  456. result:= nil;
  457. end;
  458. function TSQLite3Connection.Commit(trans: TSQLHandle): boolean;
  459. begin
  460. execsql('COMMIT');
  461. result:= true;
  462. end;
  463. function TSQLite3Connection.RollBack(trans: TSQLHandle): boolean;
  464. begin
  465. execsql('ROLLBACK');
  466. result:= true;
  467. end;
  468. function TSQLite3Connection.StartdbTransaction(trans: TSQLHandle;
  469. aParams: string): boolean;
  470. begin
  471. execsql('BEGIN');
  472. result:= true;
  473. end;
  474. procedure TSQLite3Connection.CommitRetaining(trans: TSQLHandle);
  475. begin
  476. commit(trans);
  477. execsql('BEGIN');
  478. end;
  479. procedure TSQLite3Connection.RollBackRetaining(trans: TSQLHandle);
  480. begin
  481. rollback(trans);
  482. execsql('BEGIN');
  483. end;
  484. procedure TSQLite3Connection.DoInternalConnect;
  485. var
  486. str1: string;
  487. begin
  488. if Length(databasename)=0 then
  489. DatabaseError(SErrNoDatabaseName,self);
  490. initialisesqlite;
  491. str1:= databasename;
  492. checkerror(sqlite3_open(pchar(str1),@fhandle));
  493. end;
  494. procedure TSQLite3Connection.DoInternalDisconnect;
  495. begin
  496. if fhandle <> nil then
  497. begin
  498. checkerror(sqlite3_close(fhandle));
  499. fhandle:= nil;
  500. releasesqlite;
  501. end;
  502. end;
  503. function TSQLite3Connection.GetHandle: pointer;
  504. begin
  505. result:= fhandle;
  506. end;
  507. procedure TSQLite3Connection.checkerror(const aerror: integer);
  508. Var
  509. S : String;
  510. begin
  511. if (aerror<>sqlite_ok) then
  512. begin
  513. S:=strpas(sqlite3_errmsg(fhandle));
  514. DatabaseError(S,Self);
  515. end;
  516. end;
  517. procedure TSQLite3Connection.execsql(const asql: string);
  518. var
  519. err : pchar;
  520. str1 : string;
  521. res : integer;
  522. begin
  523. err:= nil;
  524. Res := sqlite3_exec(fhandle,pchar(asql),nil,nil,@err);
  525. if err <> nil then
  526. begin
  527. str1:= strpas(err);
  528. sqlite3_free(err);
  529. end;
  530. if (res<>sqlite_ok) then
  531. databaseerror(str1);
  532. end;
  533. function execcallback(adata: pointer; ncols: longint; //adata = PStringArray
  534. avalues: PPchar; anames: PPchar):longint; cdecl;
  535. var
  536. P : PStringArray;
  537. i : integer;
  538. begin
  539. P:=PStringArray(adata);
  540. SetLength(P^,ncols);
  541. for i:= 0 to ncols - 1 do
  542. P^[i]:= strPas(avalues[i]);
  543. result:= 0;
  544. end;
  545. function TSQLite3Connection.stringquery(const asql: string): TStringArray;
  546. begin
  547. SetLength(result,0);
  548. CheckError(sqlite3_exec(fhandle,pchar(asql),@execcallback,@result,nil));
  549. end;
  550. function execscallback(adata: pointer; ncols: longint; //adata = PArrayStringArray
  551. avalues: PPchar; anames: PPchar):longint; cdecl;
  552. var
  553. I,N : integer;
  554. PP : PArrayStringArray;
  555. p : PStringArray;
  556. begin
  557. PP:=PArrayStringArray(adata);
  558. N:=high(PP^); // Length-1;
  559. setlength(PP^,N+2); // increase with 1;
  560. p:= @(PP^[N]); // newly added array, fill with data.
  561. setlength(p^,ncols);
  562. for i:= 0 to ncols - 1 do
  563. p^[i]:= strPas(avalues[i]);
  564. result:= 0;
  565. end;
  566. function TSQLite3Connection.stringsquery(const asql: string): TArrayStringArray;
  567. begin
  568. SetLength(result,0);
  569. checkerror(sqlite3_exec(fhandle,pchar(asql),@execscallback,@result,nil));
  570. end;
  571. function TSQLite3Connection.getprimarykeyfield(const atablename: string;
  572. const acursor: tsqlcursor): string;
  573. var
  574. int1,int2: integer;
  575. ar1: TArrayStringArray;
  576. str1: string;
  577. begin
  578. result:= '';
  579. if atablename <> '' then
  580. begin
  581. ar1:= stringsquery('PRAGMA table_info('+atablename+');');
  582. for int1:= 0 to high(ar1) do
  583. begin
  584. if (high(ar1[int1]) >= 5) and (ar1[int1][5] <> '0') then
  585. begin
  586. result:= ar1[int1][1];
  587. break;
  588. end;
  589. end;
  590. end;
  591. end;
  592. function TSQLite3Connection.RowsAffected(cursor: TSQLCursor): TRowsCount;
  593. begin
  594. if assigned(cursor) then
  595. Result := (cursor as TSQLite3Cursor).RowsAffected
  596. else
  597. Result := -1;
  598. end;
  599. function TSQLite3Connection.GetSchemaInfoSQL(SchemaType: TSchemaType;
  600. SchemaObjectName, SchemaPattern: string): string;
  601. begin
  602. case SchemaType of
  603. stTables : result := 'select name as table_name from sqlite_master where type = ''table''';
  604. stColumns : result := 'pragma table_info(''' + (SchemaObjectName) + ''')';
  605. else
  606. DatabaseError(SMetadataUnavailable)
  607. end; {case}
  608. end;
  609. function TSQLite3Connection.StrToStatementType(s: string): TStatementType;
  610. begin
  611. S:=Lowercase(s);
  612. if s = 'pragma' then exit(stSelect);
  613. result := inherited StrToStatementType(s);
  614. end;
  615. procedure TSQLite3Connection.UpdateIndexDefs(var IndexDefs: TIndexDefs;
  616. const TableName: string);
  617. var
  618. str1: string;
  619. begin
  620. str1:= getprimarykeyfield(tablename,nil);
  621. if str1 <> '' then
  622. begin
  623. indexdefs.add('$PRIMARY_KEY$',str1,[ixPrimary,ixUnique]);
  624. end;
  625. end;
  626. {
  627. procedure TSQLite3Connection.UpdateIndexDefs(var IndexDefs: TIndexDefs;
  628. const TableName: string);
  629. var
  630. int1,int2: integer;
  631. ar1: TArrayStringArray;
  632. str1: string;
  633. begin
  634. ar1:= stringsquery('PRAGMA table_info('+tablename+');');
  635. for int1:= 0 to high(ar1) do begin
  636. if (high(ar1[int1]) >= 5) and (ar1[int1][5] <> '0') then begin
  637. indexdefs.add('$PRIMARY_KEY$',ar1[int1][1],[ixPrimary,ixUnique]);
  638. break;
  639. end;
  640. end;
  641. end;
  642. }
  643. function TSQLite3Connection.getinsertid: int64;
  644. begin
  645. result:= sqlite3_last_insert_rowid(fhandle);
  646. end;
  647. procedure TSQLite3Connection.GetFieldNames(const TableName: string;
  648. List: TStrings);
  649. begin
  650. GetDBInfo(stColumns,TableName,'name',List);
  651. end;
  652. procedure TSQLite3Connection.setoptions(const avalue: tsqliteoptions);
  653. begin
  654. if avalue <> foptions then
  655. begin
  656. checkdisconnected;
  657. foptions:= avalue;
  658. end;
  659. end;
  660. end.