sqlite3db.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. {$mode objfpc}
  2. {$h+}
  3. {*************************************************************
  4. SQLite3 Object Oriented handle
  5. O. Rinaudo - 2005 - [email protected]
  6. G. Marcou - 2007 - [email protected]
  7. *************************************************************}
  8. {$IFNDEF FPC_DOTTEDUNITS}
  9. unit SQLite3DB;
  10. {$ENDIF FPC_DOTTEDUNITS}
  11. interface
  12. {$IFDEF FPC_DOTTEDUNITS}
  13. uses System.Classes,System.Strings,Api.Sqlite3;
  14. {$ELSE FPC_DOTTEDUNITS}
  15. uses Classes,strings,sqlite3;
  16. {$ENDIF FPC_DOTTEDUNITS}
  17. {*************************************************************}
  18. {*************************************************************}
  19. type
  20. TSQLiteExecCallback = function(Sender: pointer; Columns: Integer; ColumnValues: PPAnsiChar; ColumnNames: PPAnsiChar): integer of object; cdecl;
  21. TSQLiteBusyCallback = function(Sender: TObject; BusyCount: integer): longint of object; cdecl;
  22. TOnData = Procedure(Sender: TObject; Columns: Integer; ColumnNames, ColumnValues: AnsiString) of object;
  23. TOnBusy = Procedure(Sender: TObject; BusyCount: integer; var Cancel: Boolean) of object;
  24. TOnQueryComplete = Procedure(Sender: TObject) of object;
  25. TSQLite = class(TObject)
  26. {*************************************************************}
  27. {*************************************************************}
  28. private
  29. type
  30. TFieldList = class(TList)
  31. protected
  32. procedure Notify(Ptr: Pointer; Action: TListNotification); override;
  33. end;
  34. var
  35. fSQLite:Psqlite3;
  36. fMsg: AnsiString;
  37. fIsOpen: Boolean;
  38. fBusy: Boolean;
  39. fError: longint;
  40. fVersion: AnsiString;
  41. fEncoding: AnsiString;
  42. fTable: TStrings;
  43. fLstName: TStringList;
  44. fLstVal: TStringList;
  45. fOnData: TOnData;
  46. fOnBusy: TOnBusy;
  47. fOnQueryComplete: TOnQueryComplete;
  48. fBusyTimeout: longint;
  49. fPMsg: PAnsiChar;
  50. fChangeCount: longint;
  51. fNb_Champ : Integer;
  52. fList_FieldName : TStringList;
  53. fList_Field : TList;
  54. procedure SetBusyTimeout(Timeout: integer);
  55. {*************************************************************}
  56. {*************************************************************}
  57. public
  58. constructor Create(const DBFileName: AnsiString);
  59. destructor Destroy; override;
  60. function Query(const Sql: AnsiString; Table: TStrings ): boolean;
  61. function ErrorMessage(ErrNo: Integer): AnsiString;
  62. function IsComplete(Sql: AnsiString): boolean;
  63. function LastInsertRow: integer;
  64. function Cancel: boolean;
  65. function DatabaseDetails(Table: TStrings): boolean;
  66. property LastErrorMessage: AnsiString read fMsg;
  67. property LastError: longint read fError;
  68. property Version: AnsiString read fVersion;
  69. property Encoding: AnsiString read fEncoding;
  70. property OnData: TOnData read fOnData write fOnData;
  71. property OnBusy: TOnBusy read fOnBusy write fOnBusy;
  72. property OnQueryComplete: TOnQueryComplete read fOnQueryComplete write fOnQueryComplete;
  73. property BusyTimeout: longint read fBusyTimeout write SetBusyTimeout;
  74. property ChangeCount: longint read fChangeCount;
  75. property List_FieldName: TStringList read fList_FieldName;
  76. property List_Field: TList read fList_Field;
  77. property Nb_Champ: integer read fNb_Champ write fNb_Champ;
  78. procedure SQLOnData(Sender: TObject; Columns: Integer; ColumnNames, ColumnValues: AnsiString);
  79. end;
  80. function Pas2SQLStr(const PasString: AnsiString): AnsiString;
  81. function SQL2PasStr(const SQLString: AnsiString): AnsiString;
  82. function QuoteStr(const s: AnsiString; QuoteChar: AnsiChar ): AnsiString;
  83. function UnQuoteStr(const s: AnsiString; QuoteChar: AnsiChar ): AnsiString;
  84. procedure ValueList(const ColumnNames, ColumnValues: AnsiString; NameValuePairs: TStrings);
  85. {*************************************************************}
  86. {*************************************************************}
  87. implementation
  88. Const
  89. DblQuote: AnsiChar = '"';
  90. SngQuote: AnsiChar = #39;
  91. DblSngQuote: AnsiString = #39#39;
  92. Crlf: AnsiString = #13#10;
  93. Tab: AnsiChar = #9;
  94. var
  95. MsgNoError : AnsiString;
  96. {*************************************************************}
  97. {*************************************************************}
  98. function QuoteStr(const s: AnsiString; QuoteChar: AnsiChar ): AnsiString;
  99. {*************************************************************
  100. SQlite3 enclosing AnsiString with quotes
  101. G. Marcou
  102. *************************************************************}
  103. begin
  104. Result := Concat(QuoteChar, s, QuoteChar);
  105. end;
  106. {*************************************************************}
  107. function UnQuoteStr(const s: AnsiString; QuoteChar: AnsiChar ): AnsiString;
  108. {*************************************************************
  109. SQlite3 Remove enclosing quotes from string
  110. G. Marcou
  111. *************************************************************}
  112. begin
  113. Result := s;
  114. if length(Result) > 1 then
  115. begin
  116. if Result[1] = QuoteChar then
  117. Delete(Result, 1, 1);
  118. if Result[Length(Result)] = QuoteChar then
  119. Delete(Result, Length(Result), 1);
  120. end;
  121. end;
  122. {*************************************************************}
  123. function Pas2SQLStr(const PasString: AnsiString): AnsiString;
  124. {*************************************************************
  125. SQlite3 SQL string are use double quotes, Pascal string use
  126. single quote.
  127. G. Marcou
  128. *************************************************************}
  129. var
  130. n : integer;
  131. begin
  132. Result := SQL2PasStr(PasString);
  133. n := Length(Result);
  134. while n > 0 do
  135. begin
  136. if Result[n] = SngQuote then
  137. Insert(SngQuote, Result, n);
  138. dec(n);
  139. end;
  140. Result := QuoteStr(Result,SngQuote);
  141. end;
  142. {*************************************************************}
  143. function SQL2PasStr(const SQLString: AnsiString): AnsiString;
  144. {*************************************************************
  145. SQlite3 SQL string are use double quotes, Pascal string use
  146. single quote.
  147. G. Marcou
  148. *************************************************************}
  149. var
  150. p : integer;
  151. begin
  152. Result := SQLString;
  153. p := pos(DblSngQuote, Result);
  154. while p > 0 do
  155. begin
  156. Delete(Result, p, 1);
  157. p := pos(DblSngQuote, Result);
  158. end;
  159. Result := UnQuoteStr(Result,SngQuote);
  160. end;
  161. {*************************************************************}
  162. procedure ValueList(const ColumnNames, ColumnValues : AnsiString;
  163. NameValuePairs : TStrings);
  164. {*************************************************************
  165. SQlite3 build (name=value) pair list
  166. G. Marcou
  167. *************************************************************}
  168. var
  169. n : integer;
  170. lstName, lstValue : TStringList;
  171. begin
  172. if NameValuePairs <> nil then
  173. begin
  174. lstName := TStringList.Create;
  175. lstValue := TStringList.Create;
  176. lstName.CommaText := ColumnNames;
  177. lstValue.CommaText := ColumnValues;
  178. NameValuePairs.Clear;
  179. if lstName.Count = LstValue.Count then
  180. if lstName.Count > 0 then
  181. for n := 0 to lstName.Count - 1 do
  182. NameValuePairs.Append(Concat(lstName.Strings[n], '=', lstValue.Strings[n]));
  183. lstValue.Free;
  184. lstName.Free;
  185. end;
  186. end;
  187. {*************************************************************}
  188. {function SystemErrorMsg(ErrNo: Integer ): AnsiString;
  189. var
  190. buf: PAnsiChar;
  191. size: Integer;
  192. MsgLen: Integer;
  193. begin}
  194. { size := 256;
  195. GetMem(buf, size);
  196. If ErrNo = - 1 then
  197. ErrNo := GetLastError;
  198. MsgLen := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, ErrNo, 0, buf, size, nil);
  199. if MsgLen = 0 then
  200. Result := 'ERROR'
  201. else
  202. Result := buf;}
  203. {end;}
  204. {*************************************************************}
  205. function BusyCallback(Sender : pointer;
  206. BusyCount : integer): longint; cdecl;
  207. {*************************************************************
  208. SQlite3 busy callback
  209. G. Marcou
  210. *************************************************************}
  211. var
  212. bCancel: Boolean;
  213. begin
  214. Result := -1;
  215. with TObject(Sender) as TSQLite do
  216. begin
  217. if Assigned(fOnBusy) then
  218. begin
  219. bCancel := False;
  220. fOnBusy(Tobject(Sender), BusyCount, bCancel);
  221. if bCancel then
  222. Result := 0;
  223. end;
  224. end;
  225. end;
  226. {*************************************************************}
  227. function ExecCallback(Sender : Pointer;
  228. Columns : Integer;
  229. ColumnValues : PPAnsiChar;
  230. ColumnNames : PPAnsiChar): integer; cdecl;
  231. {*************************************************************
  232. SQlite3 Build table and data from callback
  233. G. Marcou
  234. *************************************************************}
  235. var
  236. PVal, PName : ^PAnsiChar;
  237. n : integer;
  238. sVal, sName : AnsiString;
  239. begin
  240. Result := 0;
  241. with TObject(Sender) as TSQLite do
  242. begin
  243. if (Assigned(fOnData) or Assigned(fTable)) then
  244. begin
  245. fLstName.Clear;
  246. fLstVal.Clear;
  247. if Columns > 0 then
  248. begin
  249. PName := ColumnNames;
  250. PVal := ColumnValues;
  251. for n := 0 to Columns - 1 do
  252. begin
  253. fLstName.Append(PName^);
  254. fLstVal.Append(PVal^);
  255. inc(PName);
  256. inc(PVal);
  257. end;
  258. end;
  259. sVal := fLstVal.CommaText;
  260. sName := fLstName.CommaText;
  261. if Assigned(fOnData) then
  262. fOnData(TObject(Sender), Columns, sName, sVal);
  263. if Assigned(fTable) then
  264. begin
  265. if fTable.Count = 0 then
  266. fTable.Append(sName);
  267. fTable.Append(sVal);
  268. end;
  269. end;
  270. end;
  271. end;
  272. {*************************************************************}
  273. procedure TSQLite.SQLOnData(Sender : TObject;
  274. Columns : Integer;
  275. ColumnNames, ColumnValues : AnsiString);
  276. {*************************************************************
  277. SQlite3 Fill up field list names and field list values
  278. G. Marcou
  279. *************************************************************}
  280. Var
  281. InterS,val : AnsiString;
  282. Field : TStringList;
  283. {************************************************}
  284. function Pos1(a: AnsiString ; s : AnsiChar) : integer;
  285. var i,j : Integer;
  286. begin
  287. j:=-1;
  288. for i:=1 to length(a) Do
  289. begin
  290. if a[i] = s then
  291. begin
  292. j:=i;
  293. break;
  294. end;
  295. end;
  296. result:=j;
  297. end; { Pos1 }
  298. {*************************************************}
  299. begin
  300. If Nb_Champ = -1 Then
  301. Begin {Put the fields name in List_FieldName}
  302. Nb_Champ:=Columns;
  303. InterS:=ColumnNames;
  304. While (Pos1(InterS,',') > 0) do
  305. begin
  306. val:=copy(InterS,1,Pos1(InterS,',')-1);
  307. InterS:=copy(InterS,Pos1(InterS,',')+1,length(InterS));
  308. List_FieldName.add(val);
  309. end;
  310. if length(InterS) > 0 then List_FieldName.add(InterS);
  311. end;
  312. {Put the list of TStringList of value}
  313. Field :=TStringList.Create;
  314. InterS:=ColumnValues;
  315. While (Pos1(InterS,',') > 0) do
  316. begin
  317. val:=copy(InterS,1,Pos1(InterS,',')-1);
  318. InterS:=copy(InterS,Pos1(InterS,',')+1,length(InterS));
  319. Field.add(val);
  320. end;
  321. if length(InterS) > 0 then Field.add(InterS);
  322. List_Field.add(Field);
  323. end;
  324. {*************************************************************}
  325. procedure TSQLite.TFieldList.Notify(Ptr: Pointer; Action: TListNotification);
  326. {*************************************************************}
  327. begin
  328. if Action=lnDeleted then
  329. TObject(Ptr).Free;
  330. inherited;
  331. end;
  332. {*************************************************************}
  333. constructor TSQLite.Create(const DBFileName: AnsiString);
  334. {*************************************************************
  335. SQlite3 constructor
  336. G. Marcou
  337. *************************************************************}
  338. begin
  339. inherited Create;
  340. fList_FieldName := TStringList.Create;
  341. fList_Field := TFieldList.Create;
  342. fError := SQLITE_ERROR;
  343. fIsOpen := False;
  344. fLstName := TStringList.Create;
  345. fLstVal := TStringList.Create;
  346. fOnData := nil;
  347. fOnBusy := nil;
  348. fOnQueryComplete := nil;
  349. fChangeCount := 0;
  350. OnData:=@SQLOnData;
  351. sqlite3_open(PAnsiChar(DBFileName), @fSQLite);
  352. sqlite3_free(fPMsg);
  353. if fSQLite <> nil then
  354. begin
  355. //fVersion := String(SQLite_Version);
  356. //fEncoding := SQLite_Encoding;
  357. fIsOpen := True;
  358. fError := SQLITE_OK;
  359. end;
  360. fMsg := sqlite3_errmsg(fSQLite);
  361. end;
  362. {*************************************************************}
  363. destructor TSQLite.Destroy;
  364. {*************************************************************
  365. SQLite3 destructor
  366. G. Marcou
  367. *************************************************************}
  368. begin
  369. if fIsOpen then
  370. fError:=sqlite3_close(fSQLite);
  371. fIsOpen := False;
  372. fLstName.Free;
  373. fLstVal.Free;
  374. fSQLite := nil;
  375. fOnData := nil;
  376. fOnBusy := nil;
  377. fOnQueryComplete := nil;
  378. fLstName := nil;
  379. fLstVal := nil;
  380. fList_FieldName.destroy;
  381. fList_Field.destroy;
  382. inherited Destroy;
  383. end;
  384. {*************************************************************}
  385. function TSQLite.Query(const Sql: AnsiString; Table: TStrings ): boolean;
  386. {*************************************************************
  387. SQLite3 query the database
  388. G. Marcou
  389. *************************************************************}
  390. //var
  391. // fPMsg: PAnsiChar;
  392. //var Psql : PAnsiChar;
  393. begin
  394. fError := SQLITE_ERROR;
  395. if fIsOpen then
  396. begin
  397. fPMsg := nil;
  398. fBusy := True;
  399. fTable := Table;
  400. if fTable <> nil then
  401. fTable.Clear;
  402. List_FieldName.clear;
  403. List_Field.clear;
  404. Nb_Champ:=-1;
  405. fError := sqlite3_exec(fSQLite, PAnsiChar(sql), @ExecCallback, Self, @fPMsg);
  406. sqlite3_free(fPMsg);
  407. fChangeCount := sqlite3_changes(fSQLite);
  408. fTable := nil;
  409. fBusy := False;
  410. if Assigned(fOnQueryComplete) then
  411. fOnQueryComplete(Self);
  412. end;
  413. fMsg := ErrorMessage(fError);
  414. Result := (fError = SQLITE_OK);
  415. end;
  416. {*************************************************************}
  417. function TSQLite.Cancel: boolean;
  418. {*************************************************************
  419. SQLite3 interrupt database
  420. G. Marcou
  421. *************************************************************}
  422. begin
  423. Result := False;
  424. if fBusy and fIsOpen then
  425. begin
  426. sqlite3_interrupt(fSQLite);
  427. fBusy := false;
  428. Result := True;
  429. end;
  430. end;
  431. {*************************************************************}
  432. procedure TSQLite.SetBusyTimeout(Timeout: Integer);
  433. {*************************************************************
  434. SQLite3 busy timeout
  435. G. Marcou
  436. *************************************************************}
  437. begin
  438. fBusyTimeout := Timeout;
  439. if fIsOpen then
  440. begin
  441. fError:=sqlite3_busy_timeout(fSQLite, fBusyTimeout);
  442. if fBusyTimeout > 0 then
  443. sqlite3_busy_handler(fSQLite, @BusyCallback, Self)
  444. else
  445. sqlite3_busy_handler(fSQLite, nil, nil);
  446. end;
  447. end;
  448. {*************************************************************}
  449. function TSQLite.LastInsertRow: longint;
  450. {*************************************************************
  451. SQLite3 Get ID of the last inserted row
  452. G. Marcou
  453. *************************************************************}
  454. begin
  455. if fIsOpen then
  456. Result := sqlite3_last_insert_rowid(fSQLite)
  457. else
  458. Result := -1;
  459. end;
  460. {*************************************************************}
  461. function TSQLite.ErrorMessage(ErrNo: Integer): AnsiString;
  462. {*************************************************************
  463. SQLite3 Return comprehensive error message
  464. G. Marcou
  465. *************************************************************}
  466. begin
  467. if ErrNo = 0 then
  468. Result := MsgNoError
  469. else
  470. Result := sqlite3_errmsg(fSQLite);
  471. end;
  472. {*************************************************************}
  473. function TSQLite.IsComplete(Sql: AnsiString): boolean;
  474. {*************************************************************
  475. SQLite3 Return true when complete
  476. G. Marcou
  477. *************************************************************}
  478. var Psql : PAnsiChar;
  479. begin
  480. Psql:=StrAlloc (length(Sql)+1);
  481. strpcopy(Psql,Sql);
  482. // Writeln('Testing: ',psql);
  483. Result := sqlite3_complete(Psql)<>0;
  484. strdispose(Psql);
  485. end;
  486. {*************************************************************}
  487. function TSQLite.DatabaseDetails(Table: TStrings): boolean;
  488. {*************************************************************
  489. SQLite3 Query the database
  490. G. Marcou
  491. *************************************************************}
  492. begin
  493. Result := Query('SELECT * FROM SQLITE_MASTER;', Table);
  494. end;
  495. {*************************************************************}
  496. {*************************************************************}
  497. initialization
  498. finalization
  499. end.