2
0

sqlite3db.pas 15 KB

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