sqlitedb.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. {$mode objfpc}
  2. {$h+}
  3. unit SQLitedb;
  4. interface
  5. uses Classes,strings,sqlite;
  6. type
  7. TSQLiteExecCallback = function(Sender: pointer; Columns: Integer; ColumnValues: ppchar; ColumnNames: ppchar): integer of object; cdecl;
  8. TSQLiteBusyCallback = function(Sender: TObject; ObjectName: PChar; BusyCount: integer): integer of object; cdecl;
  9. TOnData = Procedure(Sender: TObject; Columns: Integer; ColumnNames, ColumnValues: String) of object;
  10. TOnBusy = Procedure(Sender: TObject; ObjectName: String; BusyCount: integer; var Cancel: Boolean) of object;
  11. TOnQueryComplete = Procedure(Sender: TObject) of object;
  12. TSQLite = class(TObject)
  13. private
  14. fSQLite: Pointer;
  15. fMsg: String;
  16. fIsOpen: Boolean;
  17. fBusy: Boolean;
  18. fError: Integer;
  19. fVersion: String;
  20. fEncoding: String;
  21. fTable: TStrings;
  22. fLstName: TStringList;
  23. fLstVal: TStringList;
  24. fOnData: TOnData;
  25. fOnBusy: TOnBusy;
  26. fOnQueryComplete: TOnQueryComplete;
  27. fBusyTimeout: integer;
  28. fPMsg: PChar;
  29. fChangeCount: integer;
  30. fNb_Champ : Integer;
  31. fList_FieldName : TStringList;
  32. fList_Field : TList;
  33. procedure SetBusyTimeout(Timeout: integer);
  34. public
  35. constructor Create(DBFileName: String);
  36. destructor Destroy; override;
  37. function Query(Sql: String; Table: TStrings ): boolean;
  38. function ErrorMessage(ErrNo: Integer): string;
  39. function IsComplete(Sql: String): boolean;
  40. function LastInsertRow: integer;
  41. function Cancel: boolean;
  42. function DatabaseDetails(Table: TStrings): boolean;
  43. property LastErrorMessage: string read fMsg;
  44. property LastError: Integer read fError;
  45. property Version: String read fVersion;
  46. property Encoding: String read fEncoding;
  47. property OnData: TOnData read fOnData write fOnData;
  48. property OnBusy: TOnBusy read fOnBusy write fOnBusy;
  49. property OnQueryComplete: TOnQueryComplete read fOnQueryComplete write fOnQueryComplete;
  50. property BusyTimeout: Integer read fBusyTimeout write SetBusyTimeout;
  51. property ChangeCount: Integer read fChangeCount;
  52. property List_FieldName: TStringList read fList_FieldName write fList_FieldName;
  53. property List_Field: TList read fList_Field write fList_Field;
  54. property Nb_Champ: integer read fNb_Champ write fNb_Champ;
  55. procedure SQLOnData(Sender: TObject; Columns: Integer; ColumnNames, ColumnValues: String);
  56. end;
  57. function Pas2SQLStr(const PasString: string): string;
  58. function SQL2PasStr(const SQLString: string): string;
  59. function QuoteStr(const s: string; QuoteChar: Char ): string;
  60. function UnQuoteStr(const s: string; QuoteChar: Char ): string;
  61. procedure ValueList(const ColumnNames, ColumnValues: String; NameValuePairs: TStrings);
  62. implementation
  63. Const
  64. DblQuote: Char = '"';
  65. SngQuote: Char = #39;
  66. Crlf: String = #13#10;
  67. Tab: Char = #9;
  68. var
  69. MsgNoError: String;
  70. function QuoteStr(const s: string; QuoteChar: Char ): string;
  71. begin
  72. Result := Concat(QuoteChar, s, QuoteChar);
  73. end;
  74. function UnQuoteStr(const s: string; QuoteChar: Char ): string;
  75. begin
  76. Result := s;
  77. if length(Result) > 1 then
  78. begin
  79. if Result[1] = QuoteChar then
  80. Delete(Result, 1, 1);
  81. if Result[Length(Result)] = QuoteChar then
  82. Delete(Result, Length(Result), 1);
  83. end;
  84. end;
  85. function Pas2SQLStr(const PasString: string): string;
  86. var
  87. n: integer;
  88. begin
  89. Result := SQL2PasStr(PasString);
  90. n := Length(Result);
  91. while n > 0 do
  92. begin
  93. if Result[n] = SngQuote then
  94. Insert(SngQuote, Result, n);
  95. dec(n);
  96. end;
  97. Result := QuoteStr(Result,#39);
  98. end;
  99. function SQL2PasStr(const SQLString: string): string;
  100. const
  101. DblSngQuote: String = #39#39;
  102. var
  103. p: integer;
  104. begin
  105. Result := SQLString;
  106. p := pos(DblSngQuote, Result);
  107. while p > 0 do
  108. begin
  109. Delete(Result, p, 1);
  110. p := pos(DblSngQuote, Result);
  111. end;
  112. Result := UnQuoteStr(Result,#39);
  113. end;
  114. procedure ValueList(const ColumnNames, ColumnValues: String; NameValuePairs: TStrings);
  115. var
  116. n: integer;
  117. lstName, lstValue: TStringList;
  118. begin
  119. if NameValuePairs <> nil then
  120. begin
  121. lstName := TStringList.Create;
  122. lstValue := TStringList.Create;
  123. lstName.CommaText := ColumnNames;
  124. lstValue.CommaText := ColumnValues;
  125. NameValuePairs.Clear;
  126. if lstName.Count = LstValue.Count then
  127. if lstName.Count > 0 then
  128. for n := 0 to lstName.Count - 1 do
  129. NameValuePairs.Append(Concat(lstName.Strings[n], '=', lstValue.Strings[n]));
  130. lstValue.Free;
  131. lstName.Free;
  132. end;
  133. end;
  134. function SystemErrorMsg(ErrNo: Integer ): String;
  135. var
  136. buf: PChar;
  137. size: Integer;
  138. MsgLen: Integer;
  139. begin
  140. { size := 256;
  141. GetMem(buf, size);
  142. If ErrNo = - 1 then
  143. ErrNo := GetLastError;
  144. MsgLen := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, ErrNo, 0, buf, size, nil);
  145. if MsgLen = 0 then
  146. Result := 'ERROR'
  147. else
  148. Result := buf;}
  149. end;
  150. function BusyCallback(Sender: pointer; ObjectName: PChar; BusyCount: integer): integer; cdecl;
  151. var
  152. sObjName: String;
  153. bCancel: Boolean;
  154. begin
  155. Result := -1;
  156. with TObject(Sender) as TSQLite do
  157. begin
  158. if Assigned(fOnBusy) then
  159. begin
  160. bCancel := False;
  161. sObjName := ObjectName;
  162. fOnBusy(Tobject(Sender), sObjName, BusyCount, bCancel);
  163. if bCancel then
  164. Result := 0;
  165. end;
  166. end;
  167. end;
  168. function ExecCallback(Sender: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPchar): integer; cdecl;
  169. var
  170. PVal, PName: ^PChar;
  171. n: integer;
  172. sVal, sName: String;
  173. begin
  174. Result := 0;
  175. with TObject(Sender) as TSQLite do
  176. begin
  177. if (Assigned(fOnData) or Assigned(fTable)) then
  178. begin
  179. fLstName.Clear;
  180. fLstVal.Clear;
  181. if Columns > 0 then
  182. begin
  183. PName := ColumnNames;
  184. PVal := ColumnValues;
  185. for n := 0 to Columns - 1 do
  186. begin
  187. fLstName.Append(PName^);
  188. fLstVal.Append(PVal^);
  189. inc(PName);
  190. inc(PVal);
  191. end;
  192. end;
  193. sVal := fLstVal.CommaText;
  194. sName := fLstName.CommaText;
  195. if Assigned(fOnData) then
  196. fOnData(TObject(Sender), Columns, sName, sVal);
  197. if Assigned(fTable) then
  198. begin
  199. if fTable.Count = 0 then
  200. fTable.Append(sName);
  201. fTable.Append(sVal);
  202. end;
  203. end;
  204. end;
  205. end;
  206. procedure TSQLite.SQLOnData(Sender: TObject; Columns: Integer; ColumnNames, ColumnValues: String);
  207. Var i : Integer;
  208. InterS,val : String;
  209. Field : TStringList;
  210. function Pos1(a: String ; s : char) : integer;
  211. var i,j : Integer;
  212. begin
  213. j:=-1;
  214. for i:=1 to length(a) Do
  215. begin
  216. if a[i] = s then
  217. begin
  218. j:=i;
  219. break;
  220. end;
  221. end;
  222. result:=j;
  223. end;
  224. begin
  225. If Nb_Champ = -1 Then
  226. Begin // Put the fields name in List_FieldName
  227. Nb_Champ:=Columns;
  228. InterS:=ColumnNames;
  229. While (Pos1(InterS,',') > 0) do
  230. begin
  231. val:=copy(InterS,1,Pos1(InterS,',')-1);
  232. InterS:=copy(InterS,Pos1(InterS,',')+1,length(InterS));
  233. List_FieldName.add(val);
  234. end;
  235. if length(InterS) > 0 then List_FieldName.add(InterS);
  236. end;
  237. // Put the list of TStringList of value
  238. Field :=TStringList.Create;
  239. InterS:=ColumnValues;
  240. While (Pos1(InterS,',') > 0) do
  241. begin
  242. val:=copy(InterS,1,Pos1(InterS,',')-1);
  243. InterS:=copy(InterS,Pos1(InterS,',')+1,length(InterS));
  244. Field.add(val);
  245. end;
  246. if length(InterS) > 0 then Field.add(InterS);
  247. List_Field.add(Field);
  248. end;
  249. constructor TSQLite.Create(DBFileName: String);
  250. var
  251. fPMsg1: PChar;
  252. name : pchar;
  253. begin
  254. inherited Create;
  255. List_FieldName := TStringList.Create;
  256. List_Field := TList.Create;
  257. fError := SQLITE_ERROR;
  258. fIsOpen := False;
  259. fLstName := TStringList.Create;
  260. fLstVal := TStringList.Create;
  261. fOnData := nil;
  262. fOnBusy := nil;
  263. fOnQueryComplete := nil;
  264. fChangeCount := 0;
  265. name:=StrAlloc (length(DBFileName)+1);
  266. strpcopy(name,DBFileName);
  267. OnData:=@SQLOnData;
  268. fSQLite := SQLite_Open(name, 1, @fPMsg);
  269. SQLite_FreeMem(fPMsg);
  270. if fSQLite <> nil then
  271. begin
  272. //fVersion := String(SQLite_Version);
  273. //fEncoding := SQLite_Encoding;
  274. fIsOpen := True;
  275. fError := SQLITE_OK;
  276. end;
  277. fMsg := ErrorMessage(fError);
  278. end;
  279. destructor TSQLite.Destroy;
  280. begin
  281. if fIsOpen then
  282. SQLite_Close(fSQLite);
  283. fIsOpen := False;
  284. fLstName.Free;
  285. fLstVal.Free;
  286. fSQLite := nil;
  287. fOnData := nil;
  288. fOnBusy := nil;
  289. fOnQueryComplete := nil;
  290. fLstName := nil;
  291. fLstVal := nil;
  292. List_FieldName.destroy;
  293. List_Field.destroy;
  294. inherited Destroy;
  295. end;
  296. function TSQLite.Query(Sql: String; Table: TStrings ): boolean;
  297. //var
  298. // fPMsg: PChar;
  299. //var Psql : pchar;
  300. begin
  301. fError := SQLITE_ERROR;
  302. if fIsOpen then
  303. begin
  304. fPMsg := nil;
  305. fBusy := True;
  306. fTable := Table;
  307. if fTable <> nil then
  308. fTable.Clear;
  309. List_FieldName.clear;
  310. List_Field.clear;
  311. Nb_Champ:=-1;
  312. fError := SQLite_Exec(fSQLite, PChar(sql), @ExecCallback, Self, @fPMsg);
  313. SQLite_FreeMem(fPMsg);
  314. fChangeCount := SQLite_Changes(fSQLite);
  315. fTable := nil;
  316. fBusy := False;
  317. if Assigned(fOnQueryComplete) then
  318. fOnQueryComplete(Self);
  319. end;
  320. fMsg := ErrorMessage(fError);
  321. Result := (fError = SQLITE_OK);
  322. end;
  323. function TSQLite.Cancel: boolean;
  324. begin
  325. Result := False;
  326. if fBusy and fIsOpen then
  327. begin
  328. do_SQLite_interrupt(fSQLite);
  329. fBusy := false;
  330. Result := True;
  331. end;
  332. end;
  333. procedure TSQLite.SetBusyTimeout(Timeout: Integer);
  334. begin
  335. fBusyTimeout := Timeout;
  336. if fIsOpen then
  337. begin
  338. SQLite_Busy_Timeout(fSQLite, fBusyTimeout);
  339. if fBusyTimeout > 0 then
  340. SQLite_Busy_Handler(fSQLite, @BusyCallback, Self)
  341. else
  342. SQLite_Busy_Handler(fSQLite, nil, nil);
  343. end;
  344. end;
  345. function TSQLite.LastInsertRow: integer;
  346. begin
  347. if fIsOpen then
  348. Result := SQLite_Last_Insert_RowID(fSQLite)
  349. else
  350. Result := -1;
  351. end;
  352. function TSQLite.ErrorMessage(ErrNo: Integer): string;
  353. begin
  354. if ErrNo = 0 then
  355. Result := MsgNoError
  356. else
  357. Result := SQLite_Error_String(ErrNo);
  358. end;
  359. function TSQLite.IsComplete(Sql: String): boolean;
  360. var Psql : pchar;
  361. begin
  362. Psql:=StrAlloc (length(Sql)+1);
  363. strpcopy(Psql,Sql);
  364. Writeln('Testing: ',psql);
  365. Result := SQLite_Complete(Psql)<>0;
  366. strdispose(Psql);
  367. end;
  368. function TSQLite.DatabaseDetails(Table: TStrings): boolean;
  369. begin
  370. Result := Query('SELECT * FROM SQLITE_MASTER;', Table);
  371. end;
  372. initialization
  373. finalization
  374. end.