sqlitedb.pas 11 KB

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