-
-
Notifications
You must be signed in to change notification settings - Fork 136
/
Copy pathmormot.db.rad.bde.pas
304 lines (256 loc) · 8.69 KB
/
mormot.db.rad.bde.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/// Database Framework for BDE TDataSet Connection
// - this unit is a part of the Open Source Synopse mORMot framework 2,
// licensed under a MPL/GPL/LGPL three license - see LICENSE.md
unit mormot.db.rad.bde;
{
*****************************************************************************
Legacy BDE Database Access for mormot.db.rad
- BDE Database Engine Connection
*****************************************************************************
}
interface
{$ifdef FPC} // the old and deprecated BDE is a Delphi-specific "feature"
implementation // to compile a void unit on FPC
{$else}
{$I ..\mormot.defines.inc}
uses
sysutils,
classes,
variants,
mormot.core.base,
mormot.core.os,
mormot.core.unicode,
mormot.core.text,
mormot.core.data,
mormot.core.datetime,
mormot.core.variants,
mormot.core.rtti,
mormot.core.json,
mormot.core.buffers,
mormot.core.log,
mormot.db.core,
mormot.db.sql,
mormot.db.rad,
DBTables;
{ ************ BDE Database Engine Connection }
type
/// Exception type associated to the direct BDE connection
ESqlDBBDE = class(ESqlDBDataset);
/// implement properties shared by BDE connections
TSqlDBBDEConnectionProperties = class(TSqlDBDatasetConnectionProperties)
protected
/// initialize fForeignKeys content with all foreign keys of this DB
// - do nothing by now (BDE metadata may be used in the future)
procedure GetForeignKeys; override;
/// this overridden method will retrieve the kind of DBMS from the main connection
function GetDbms: TSqlDBDefinition; override;
public
/// initialize the properties to connect to the BDE engine
// - aServerName shall contain the BDE Alias name
// - aDatabaseName is ignored
constructor Create(const aServerName, aDatabaseName, aUserID, aPassWord: RawUtf8); override;
/// create a new connection
// - caller is responsible of freeing this instance
// - this overridden method will create an TSqlDBBDEConnection instance
function NewConnection: TSqlDBConnection; override;
end;
/// implements a direct connection via the BDE access layer
TSqlDBBDEConnection = class(TSqlDBConnectionThreadSafe)
protected
fDatabase: TDatabase;
fSession: TSession;
fDbms: TSqlDBDefinition;
fDbmsName: RawUtf8;
public
/// prepare a connection to a specified BDE database server
constructor Create(aProperties: TSqlDBConnectionProperties); override;
/// release memory and connection
destructor Destroy; override;
/// connect to the specified BDE server
// - should raise an ESqlDBBDE on error
procedure Connect; override;
/// stop connection to the specified BDE database server
// - should raise an ESqlDBBDE on error
procedure Disconnect; override;
/// return TRUE if Connect has been already successfully called
function IsConnected: boolean; override;
/// create a new statement instance
function NewStatement: TSqlDBStatement; override;
/// begin a Transaction for this connection
procedure StartTransaction; override;
/// commit changes of a Transaction for this connection
// - StartTransaction method must have been called before
procedure Commit; override;
/// discard changes of a Transaction for this connection
// - StartTransaction method must have been called before
procedure Rollback; override;
/// access to the associated BDE connection instance
property Database: TDatabase
read fDatabase;
published
/// the remote DBMS name, as retrieved at BDE connection creation
property DbmsName: RawUtf8
read fDbmsName;
/// the remote DBMS type, as retrieved at BDE connection creation
property Dbms: TSqlDBDefinition
read fDbms;
end;
/// implements a statement via a BDE connection
TSqlDBBDEStatement = class(TSqlDBDatasetStatement)
protected
/// initialize and set fQuery internal field as expected
procedure DatasetCreate; override;
/// set fQueryParams internal field as expected
function DatasetPrepare(const aSQL: string): boolean; override;
/// execute underlying TQuery.ExecSQL
procedure DatasetExecSQL; override;
public
end;
implementation
{ ************ BDE Database Engine Connection }
{ TSqlDBBDEConnectionProperties }
constructor TSqlDBBDEConnectionProperties.Create(const aServerName,
aDatabaseName, aUserID, aPassWord: RawUtf8);
begin
inherited Create(aServerName, aDatabaseName, aUserID, aPassWord);
{$ifndef UNICODE}
fForceInt64AsFloat := true; // BDE is old and deprecated :(
{$endif UNICODE}
end;
procedure TSqlDBBDEConnectionProperties.GetForeignKeys;
begin
{ TODO : get FOREIGN KEYS from BDE metadata ? }
end;
function TSqlDBBDEConnectionProperties.NewConnection: TSqlDBConnection;
begin
result := TSqlDBBDEConnection.Create(self);
TSqlDBBDEConnection(result).InternalProcess(speCreated);
end;
function TSqlDBBDEConnectionProperties.GetDbms: TSqlDBDefinition;
begin
if fDbms = dUnknown then
// retrieve DBMS type from alias driver name
fDbms := (MainConnection as TSqlDBBDEConnection).Dbms;
result := fDbms;
end;
{ TSqlDBBDEConnection }
procedure TSqlDBBDEConnection.Commit;
begin
inherited Commit;
try
fDatabase.Commit;
except
inc(fTransactionCount); // the transaction is still active
raise;
end;
end;
var
BDEConnectionCount: integer = 0;
constructor TSqlDBBDEConnection.Create(aProperties: TSqlDBConnectionProperties);
const
PCHARS: array[0 .. 3] of PAnsiChar = (
'ORACLE', 'MSSQL', 'MSACCESS', nil);
TYPES: array[-1 .. high(PCHARS) - 1] of TSqlDBDefinition = (
dDefault, dOracle, dMSSQL, dJet);
var alias: string;
begin
inherited Create(aProperties);
fDatabase := TDatabase.Create(nil);
fSession := TSession.Create(nil);
fSession.AutoSessionName := true;
fDatabase.SessionName := fSession.SessionName;
fDatabase.LoginPrompt := false;
inc(BDEConnectionCount);
alias := Utf8ToString(fProperties.ServerName);
fDatabase.DatabaseName := 'SynDB' + alias + IntToStr(BDEConnectionCount);
fDatabase.AliasName := alias;
fDatabase.Params.Text := Format('USER NAME=%s'#13#10'PASSWORD=%s',
[Utf8ToString(fProperties.UserID), Utf8ToString(fProperties.PassWord)]);
fDbmsName := StringToUtf8(fSession.GetAliasDriverName(alias));
fDbms := TYPES[IdemPPChar(pointer(fDbmsName), @PCHARS)];
end;
procedure TSqlDBBDEConnection.Connect;
var Log: ISynLog;
begin
if (fSession = nil) or
(fDatabase = nil) then
ESqlDBBDE.RaiseUtf8('%.Connect() on % failed: Database=nil',
[self, fProperties.ServerName]);
Log := SynDBLog.Enter('Connect to Alias=%', [fDatabase.AliasName], self);
try
fSession.Open;
fDatabase.Open;
inherited Connect; // notify any re-connection
except
on E: Exception do
begin
Disconnect; // clean up on fail
raise;
end;
end;
end;
destructor TSqlDBBDEConnection.Destroy;
begin
try
Disconnect;
except
on Exception do
end;
inherited;
FreeAndNil(fDatabase);
FreeAndNil(fSession);
end;
procedure TSqlDBBDEConnection.Disconnect;
begin
try
inherited Disconnect; // flush any cached statements
finally
if fDatabase <> nil then
fDatabase.Close;
if (fSession <> nil) and fSession.Active then
fSession.Close;
end;
end;
function TSqlDBBDEConnection.IsConnected: boolean;
begin
result := Assigned(fDatabase) and
fDatabase.Connected;
end;
function TSqlDBBDEConnection.NewStatement: TSqlDBStatement;
begin
result := TSqlDBBDEStatement.Create(self);
end;
procedure TSqlDBBDEConnection.Rollback;
begin
inherited Rollback;
fDatabase.Rollback;
end;
procedure TSqlDBBDEConnection.StartTransaction;
begin
inherited StartTransaction;
fDatabase.StartTransaction;
end;
{ TSqlDBBDEStatement }
procedure TSqlDBBDEStatement.DatasetCreate;
begin
fQuery := DBTables.TQuery.Create(nil);
with DBTables.TQuery(fQuery) do
begin
DatabaseName := (fConnection as TSqlDBBDEConnection).Database.DatabaseName;
SessionName := TSqlDBBDEConnection(fConnection).Database.Session.SessionName;
end;
end;
function TSqlDBBDEStatement.DatasetPrepare(const aSQL: string): boolean;
begin
(fQuery as DBTables.TQuery).SQL.Text := aSQL;
fQueryParams := DBTables.TQuery(fQuery).Params;
result := fQueryParams <> nil;
end;
procedure TSqlDBBDEStatement.DatasetExecSQL;
begin
(fQuery as DBTables.TQuery).ExecSQL;
end;
initialization
TSqlDBBDEConnectionProperties.RegisterClassNameForDefinition;
{$endif FPC} // the old and deprecated BDE is a Delphi-specific "feature"
end.