source: tools/dbc_export/USqlDatabase.pas

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