sqlite3db.pas 15 KB

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