source: Network/MysqlClient/USqlDatabase.pas

Last change on this file was 106, checked in by george, 14 years ago
  • Modified: TStringListEx replaced by TListString in TSqlDatabase unit.
File size: 11.4 KB
Line 
1unit USqlDatabase;
2
3{$mode Delphi}{$H+}
4
5// Modified: 2010-12-24
6
7interface
8
9uses
10 SysUtils, Classes, Dialogs, mysql50, TypInfo,
11 SpecializedDictionary, SpecializedList;
12
13type
14 EQueryError = class(Exception);
15
16 TClientCapabilities = (_CLIENT_LONG_PASSWORD, _CLIENT_FOUND_ROWS,
17 _CLIENT_LONG_FLAG, _CLIENT_CONNECT_WITH_DB, _CLIENT_NO_SCHEMA,
18 _CLIENT_COMPRESS, _CLIENT_ODBC, _CLIENT_LOCAL_FILES, _CLIENT_IGNORE_SPACE,
19 _CLIENT_INTERACTIVE, _CLIENT_SSL, _CLIENT_IGNORE_SIGPIPE, _CLIENT_TRANSACTIONS);
20
21 TSetClientCapabilities = set of TClientCapabilities;
22
23 TDbRows = class(TListObject)
24 private
25 function GetData(Index: Integer): TDictionaryStringString;
26 procedure SetData(Index: Integer; const Value: TDictionaryStringString);
27 public
28 property Data[Index: Integer]: TDictionaryStringString read GetData write SetData; default;
29 destructor Destroy; override;
30 end;
31
32 TSqlDatabase = class
33 procedure mySQLClient1ConnectError(Sender: TObject; Msg: String);
34 private
35 FSession: PMYSQL;
36 FConnected: Boolean;
37 FDatabase: string;
38 function GetConnected: Boolean;
39 function GetLastErrorMessage: string;
40 function GetLastErrorNumber: Integer;
41 function GetCharset: string;
42 procedure SetDatabase(const Value: string);
43 public
44 Hostname: string;
45 UserName: string;
46 Password: string;
47 Encoding: string;
48 Table: string;
49 RepeatLastAction: Boolean;
50 LastQuery: string;
51 procedure CreateDatabase;
52 procedure CreateTable(Name: string);
53 procedure CreateColumn(Table, ColumnName: string; ColumnType: TTypeKind);
54 procedure Query(DbRows: TDbRows; Data: string);
55 procedure Select(DbRows: TDbRows; ATable: string; Filter: string = '*'; Condition: string = '1');
56 procedure Delete(ATable: string; Condition: string = '1');
57 procedure Insert(ATable: string; Data: TDictionaryStringString);
58 procedure Update(ATable: string; Data: TDictionaryStringString; Condition: string = '1');
59 procedure Replace(ATable: string; Data: TDictionaryStringString);
60 procedure Connect;
61 procedure Disconnect;
62 function LastInsertId: Integer;
63 property LastErrorMessage: string read GetLastErrorMessage;
64 property LastErrorNumber: Integer read GetLastErrorNumber;
65 property Connected: Boolean read GetConnected;
66 constructor Create;
67 destructor Destroy; override;
68 property Charset: string read GetCharset;
69 property Database: string read FDatabase write SetDatabase;
70 end;
71
72 function MySQLFloatToStr(F: Real): string;
73 function MySQLStrToFloat(S: string): Real;
74 function SQLToDateTime(Value: string): TDateTime;
75 function DateTimeToSQL(Value: TDateTime): string;
76
77implementation
78
79uses
80 DateUtils, Math;
81
82resourcestring
83 SDatabaseQueryError = 'Database query error: "%s"';
84
85const
86 CLIENT_LONG_PASSWORD = 1; // new more secure passwords
87 CLIENT_FOUND_ROWS = 2; // Found instead of affected rows
88 CLIENT_LONG_FLAG = 4; // Get all column flags
89 CLIENT_CONNECT_WITH_DB = 8; // One can specify db on connect
90 CLIENT_NO_SCHEMA = 16; // Don't allow database.table.column
91 CLIENT_COMPRESS = 32; // Can use compression protcol
92 CLIENT_ODBC = 64; // Odbc client
93 CLIENT_LOCAL_FILES = 128; // Can use LOAD DATA LOCAL
94 CLIENT_IGNORE_SPACE = 256; // Ignore spaces before '('
95 CLIENT_INTERACTIVE = 1024; // This is an interactive client
96 CLIENT_SSL = 2048; // Switch to SSL after handshake
97 CLIENT_IGNORE_SIGPIPE = 4096; // IGNORE sigpipes
98 CLIENT_TRANSACTIONS = 8192; // Client knows about transactions
99
100function MySQLFloatToStr(F: Real): string;
101var
102 S: string;
103begin
104 S := FloatToStr(F);
105 if Pos(',', S) > 0 then S[Pos(',', S)] := '.';
106 Result := S;
107end;
108
109function MySQLStrToFloat(S: string): Real;
110begin
111 if Pos('.', S) > 0 then S[Pos('.', S)] := ',';
112 Result := StrToFloat(S);
113end;
114
115function StrToStr(Value: string): string;
116begin
117 Result := Value;
118end;
119
120function SQLToDateTime(Value: string): TDateTime;
121var
122 Parts: TListString;
123 DateParts: TListString;
124 TimeParts: TListString;
125begin
126 try
127 Parts := TListString.Create;
128 DateParts := TListString.Create;
129 TimeParts := TListString.Create;
130
131 Parts.Explode(Value, ' ', StrToStr);
132 DateParts.Explode(Parts[0], '-', StrToStr);
133 Result := EncodeDate(StrToInt(DateParts[0]), StrToInt(DateParts[1]),
134 StrToInt(DateParts[2]));
135 if Parts.Count > 1 then begin
136 TimeParts.Explode(Parts[1], ':', StrToStr);
137 Result := Result + EncodeTime(StrToInt(TimeParts[0]), StrToInt(TimeParts[1]),
138 StrToInt(TimeParts[2]), 0);
139 end;
140 finally
141 DateParts.Free;
142 TimeParts.Free;
143 Parts.Free;
144 end;
145end;
146
147function DateTimeToSQL(Value: TDateTime): string;
148begin
149 Result := FormatDateTime('yyyy-mm-dd hh.nn.ss', Value);
150end;
151
152{ TSqlDatabase }
153
154procedure TSqlDatabase.Connect;
155var
156 NewSession: PMYSQL;
157 Rows: TDbRows;
158begin
159 RepeatLastAction := False;
160// mySQLClient1.Connect;
161 FSession := mysql_init(FSession);
162// FSession.charset := 'latin2';
163 NewSession := mysql_real_connect(FSession, PChar(HostName), PChar(UserName),
164 PChar(Password), PChar(Database), 3306, nil, CLIENT_LONG_PASSWORD + CLIENT_CONNECT_WITH_DB);
165 if Assigned(NewSession) then begin
166 FConnected := True;
167 FSession := NewSession;
168 end else FConnected := False;
169
170 if LastErrorNumber <> 0 then
171 raise EQueryError.Create(Format(SDatabaseQueryError, [LastErrorMessage]));
172
173 try
174 Rows := TDbRows.Create;
175 Query(Rows, 'SET NAMES ' + Encoding);
176 finally
177 Rows.Free;
178 end;
179end;
180
181procedure TSqlDatabase.Insert(ATable: string; Data: TDictionaryStringString);
182var
183 DbNames: string;
184 DbValues: string;
185 I: Integer;
186 Value: string;
187 DbResult: TDbRows;
188begin
189 Table := ATable;
190 DbNames := '';
191 DbValues := '';
192 for I := 0 to Data.Count - 1 do begin
193 Value := Data.Items[I].Value;
194 StringReplace(Value, '"', '\"', [rfReplaceAll]);
195 if Value = 'NOW()' then DbValues := DbValues + ',' + Value
196 else DbValues := DbValues + ',"' + Value + '"';
197 DbNames := DbNames + ',`' + Data.Keys[I] + '`';
198 end;
199 System.Delete(DbNames, 1, 1);
200 System.Delete(DbValues, 1, 1);
201 try
202 DbResult := TDbRows.Create;
203 Query(DbResult, 'INSERT INTO `' + Table + '` (' + DbNames + ') VALUES (' + DbValues + ')');
204 finally
205 DbResult.Free;
206 end;
207end;
208
209procedure TSqlDatabase.Query(DbRows: TDbRows; Data: string);
210var
211 I, II: Integer;
212 DbResult: PMYSQL_RES;
213 DbRow: MYSQL_ROW;
214begin
215 DbRows.Clear;
216 //DebugLog('SqlDatabase query: '+Data);
217 RepeatLastAction := False;
218 LastQuery := Data;
219 mysql_query(FSession, PChar(Data));
220 if LastErrorNumber <> 0 then begin
221 raise EQueryError.Create(Format(SDatabaseQueryError, [LastErrorMessage]));
222 end;
223
224 DbResult := mysql_store_result(FSession);
225 if Assigned(DbResult) then begin
226 DbRows.Count := mysql_num_rows(DbResult);
227 for I := 0 to DbRows.Count - 1 do begin
228 DbRow := mysql_fetch_row(DbResult);
229 DbRows[I] := TDictionaryStringString.Create;
230 with DbRows[I] do begin
231 for II := 0 to mysql_num_fields(DbResult) - 1 do begin
232 Add(mysql_fetch_field_direct(DbResult, II)^.Name,
233 PChar((DbRow + II)^));
234 end;
235 end;
236 end;
237 end;
238 mysql_free_result(DbResult);
239end;
240
241procedure TSqlDatabase.Replace(ATable: string; Data: TDictionaryStringString);
242var
243 DbNames: string;
244 DbValues: string;
245 Value: string;
246 I: Integer;
247 DbResult: TDbRows;
248begin
249 Table := ATable;
250 DbNames := '';
251 DbValues := '';
252 for I := 0 to Data.Count - 1 do begin
253 Value := Data.Items[I].Value;
254 StringReplace(Value, '"', '\"', [rfReplaceAll]);
255 if Value = 'NOW()' then DbValues := DbValues + ',' + Value
256 else DbValues := DbValues + ',"' + Value + '"';
257 DbNames := DbNames + ',`' + Data.Keys[I] + '`';
258 end;
259 System.Delete(DbNames, 1, 1);
260 System.Delete(DbValues, 1, 1);
261 try
262 DbResult := TDbRows.Create;
263 Query(DbResult, 'REPLACE INTO `' + Table + '` (' + DbNames + ') VALUES (' + DbValues + ')');
264 finally
265 DbResult.Free;
266 end;
267end;
268
269procedure TSqlDatabase.Select(DbRows: TDbRows; ATable: string; Filter: string = '*'; Condition: string = '1');
270begin
271 Table := ATable;
272 Query(DbRows, 'SELECT ' + Filter + ' FROM `' + Table + '` WHERE ' + Condition);
273end;
274
275procedure TSqlDatabase.Update(ATable: string; Data: TDictionaryStringString; Condition: string = '1');
276var
277 DbValues: string;
278 Value: string;
279 I: Integer;
280 DbResult: TDbRows;
281begin
282 Table := ATable;
283 DbValues := '';
284 for I := 0 to Data.Count - 1 do begin
285 Value := Data.Items[I].Value;
286 StringReplace(Value, '"', '\"', [rfReplaceAll]);
287 if Value = 'NOW()' then DbValues := DbValues + ',' + Value
288 else DbValues := DbValues + ',' + Data.Keys[I] + '=' + '"' + Value + '"';
289 end;
290 System.Delete(DbValues, 1, 1);
291 try
292 DbResult := TDbRows.Create;
293 Query(DbResult, 'UPDATE `' + Table + '` SET (' + DbValues + ') WHERE ' + Condition);
294 finally
295 DbResult.Free;
296 end;
297end;
298
299procedure TSqlDatabase.mySQLClient1ConnectError(Sender: TObject; Msg: String);
300begin
301// LastError := Msg + '('+IntToStr(mySQLClient1.LastErrorNumber)+')';
302end;
303
304procedure TSqlDatabase.Delete(ATable: string; Condition: string = '1');
305var
306 DbResult: TDbRows;
307begin
308 Table := ATable;
309 try
310 DbResult := TDbRows.Create;
311 Query(DbResult, 'DELETE FROM `' + Table + '` WHERE ' + Condition);
312 finally
313 DbResult.Free;
314 end;
315end;
316
317function TSqlDatabase.GetConnected: Boolean;
318begin
319 Result := FConnected;
320end;
321
322procedure TSqlDatabase.Disconnect;
323begin
324 mysql_close(FSession);
325 FConnected := False;
326end;
327
328constructor TSqlDatabase.Create;
329begin
330 inherited;
331 FSession := nil;
332 Encoding := 'utf8';
333end;
334
335function TSqlDatabase.LastInsertId: Integer;
336begin
337 Result := mysql_insert_id(FSession);
338end;
339
340function TSqlDatabase.GetLastErrorMessage: string;
341begin
342 Result := mysql_error(FSession);
343end;
344
345function TSqlDatabase.GetLastErrorNumber: Integer;
346begin
347 Result := mysql_errno(FSession);
348end;
349
350procedure TSqlDatabase.CreateDatabase;
351var
352 TempDatabase: string;
353 DbRows: TDbRows;
354begin
355 TempDatabase := Database;
356 Database := 'mysql';
357 Connect;
358 try
359 DbRows := TDbRows.Create;
360 Query(DbRows, 'CREATE DATABASE ' + TempDatabase);
361 finally
362 DbRows.Free;
363 end;
364 Disconnect;
365 Database := TempDatabase;
366end;
367
368procedure TSqlDatabase.CreateTable(Name: string);
369var
370 DbRows: TDbRows;
371begin
372 try
373 DbRows := TDbRows.Create;
374 Query(DbRows, 'CREATE TABLE `' + Name + '`' +
375 ' (`Id` INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (`Id`));');
376 finally
377 DbRows.Free;
378 end;
379end;
380
381procedure TSqlDatabase.CreateColumn(Table, ColumnName: string;
382 ColumnType: TTypeKind);
383const
384 ColTypes: array[0..17] of string = ('', 'INT', 'CHAR', 'INT', 'DOUBLE',
385 'VARCHAR(255)', 'SET', 'INT', '', '', 'TEXT', 'TEXT', '', '', '', '', '', '');
386var
387 DbRows: TDbRows;
388begin
389 try
390 DbRows := TDbRows.Create;
391 Query(DbRows, 'ALTER TABLE `' + Table + '` ADD `' + ColumnName + '` ' +
392 ColTypes[Integer(ColumnType)] + ' NOT NULL');
393 finally
394 DbRows.Free;
395 end;
396end;
397
398destructor TSqlDatabase.Destroy;
399begin
400 if Connected then Disconnect;
401end;
402
403function TSqlDatabase.GetCharset: string;
404begin
405 Result := mysql_character_set_name(FSession);
406end;
407
408procedure TSqlDatabase.SetDatabase(const Value: string);
409begin
410 FDatabase := Value;
411 if FConnected then mysql_select_db(FSession, PChar(FDatabase));
412end;
413
414{ TDbRows }
415
416destructor TDbRows.Destroy;
417begin
418 inherited;
419end;
420
421function TDbRows.GetData(Index: Integer): TDictionaryStringString;
422begin
423 Result := TDictionaryStringString(Items[Index]);
424end;
425
426procedure TDbRows.SetData(Index: Integer; const Value: TDictionaryStringString);
427begin
428 Items[Index] := Value;
429end;
430
431end.
432
433
Note: See TracBrowser for help on using the repository browser.