zeoslib  UNKNOWN
 All Files
ZDbcAdo.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { ADO Connectivity Classes }
5 { }
6 { Originally written by Janos Fegyverneki }
7 { }
8 {*********************************************************}
9 
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
12 { }
13 { License Agreement: }
14 { }
15 { This library is distributed in the hope that it will be }
16 { useful, but WITHOUT ANY WARRANTY; without even the }
17 { implied warranty of MERCHANTABILITY or FITNESS FOR }
18 { A PARTICULAR PURPOSE. See the GNU Lesser General }
19 { Public License for more details. }
20 { }
21 { The source code of the ZEOS Libraries and packages are }
22 { distributed under the Library GNU General Public }
23 { License (see the file COPYING / COPYING.ZEOS) }
24 { with the following modification: }
25 { As a special exception, the copyright holders of this }
26 { library give you permission to link this library with }
27 { independent modules to produce an executable, }
28 { regardless of the license terms of these independent }
29 { modules, and to copy and distribute the resulting }
30 { executable under terms of your choice, provided that }
31 { you also meet, for each linked independent module, }
32 { the terms and conditions of the license of that module. }
33 { An independent module is a module which is not derived }
34 { from or based on this library. If you modify this }
35 { library, you may extend this exception to your version }
36 { of the library, but you are not obligated to do so. }
37 { If you do not wish to do so, delete this exception }
38 { statement from your version. }
39 { }
40 { }
41 { The project web site is located on: }
42 { http://zeos.firmos.at (FORUM) }
43 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
44 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
45 { }
46 { http://www.sourceforge.net/projects/zeoslib. }
47 { }
48 { }
49 { Zeos Development Group. }
50 {********************************************************@}
51 
52 unit ZDbcAdo;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses
59  Types, Classes, ZDbcConnection, ZDbcIntfs, ZCompatibility, ZPlainDriver,
60  ZPlainAdoDriver, ZPlainAdo, ZURL, ZTokenizer;
61 
62 type
63  {** Implements Ado Database Driver. }
64  {$WARNINGS OFF}
65  TZAdoDriver = class(TZAbstractDriver)
66  public
67  constructor Create; override;
68  function Connect(const Url: TZURL): IZConnection; override;
69  function GetMajorVersion: Integer; override;
70  function GetMinorVersion: Integer; override;
71  function GetTokenizer: IZTokenizer; override;
72  end;
73  {$WARNINGS ON}
74 
75  {** Represents an Ado specific connection interface. }
76  IZAdoConnection = interface (IZConnection)
77  ['{50D1AF76-0174-41CD-B90B-4FB770EFB14F}']
78  function GetAdoConnection: ZPlainAdo.Connection;
79  procedure InternalExecuteStatement(const SQL: string);
80  procedure CheckAdoError;
81  end;
82 
83  {** Implements a generic Ado Connection. }
84  TZAdoConnection = class(TZAbstractConnection, IZAdoConnection)
85  private
86  procedure ReStartTransactionSupport;
87  protected
88  FAdoConnection: ZPlainAdo.Connection;
89  function GetAdoConnection: ZPlainAdo.Connection;
90  procedure InternalExecuteStatement(const SQL: string);
91  procedure CheckAdoError;
92  procedure StartTransaction;
93  procedure InternalCreate; override;
94  public
95  destructor Destroy; override;
96 
97  function GetBinaryEscapeString(const Value: TByteDynArray): String; overload; override;
98  function GetBinaryEscapeString(const Value: RawByteString): String; overload; override;
99  function CreateRegularStatement(Info: TStrings): IZStatement; override;
100  function CreatePreparedStatement(const SQL: string; Info: TStrings):
101  IZPreparedStatement; override;
102  function CreateCallableStatement(const SQL: string; Info: TStrings):
103  IZCallableStatement; override;
104 
105  function NativeSQL(const SQL: string): string; override;
106 
107  procedure SetAutoCommit(Value: Boolean); override;
108  procedure SetTransactionIsolation(Level: TZTransactIsolationLevel); override;
109 
110  procedure Commit; override;
111  procedure Rollback; override;
112 
113  procedure Open; override;
114  procedure Close; override;
115 
116  procedure SetReadOnly(ReadOnly: Boolean); override;
117 
118  procedure SetCatalog(const Catalog: string); override;
119  function GetCatalog: string; override;
120 
121  function GetWarnings: EZSQLWarning; override;
122  procedure ClearWarnings; override;
123  end;
124 
125 var
126  {** The common driver manager object. }
127  AdoDriver: IZDriver;
128 
129 implementation
130 
131 uses
132  Variants,
133  SysUtils, ActiveX, ZDbcUtils, ZDbcLogging, ZAdoToken, ZSysUtils,
134  ZDbcAdoStatement, ZDbcAdoMetaData;
135 
136 const //adXactUnspecified
137  IL: array[TZTransactIsolationLevel] of TOleEnum = (adXactChaos, adXactReadUncommitted, adXactReadCommitted, adXactRepeatableRead, adXactSerializable);
138 
139 { TZDBLibDriver }
140 
141 {**
142  Constructs this object with default properties.
143 }
144 constructor TZAdoDriver.Create;
145 begin
146  inherited Create;
147  AddSupportedProtocol(AddPlainDriverToCache(TZAdoPlainDriver.Create));
148 end;
149 
150 {**
151  Attempts to make a database connection to the given URL.
152 }
153 {$WARNINGS OFF}
154 function TZAdoDriver.Connect(const Url: TZURL): IZConnection;
155 begin
156  Result := TZAdoConnection.Create(Url);
157 end;
158 {$WARNINGS ON}
159 
160 {**
161  Gets the driver's major version number. Initially this should be 1.
162  @return this driver's major version number
163 }
164 function TZAdoDriver.GetMajorVersion: Integer;
165 begin
166  Result := 1;
167 end;
168 
169 {**
170  Gets the driver's minor version number. Initially this should be 0.
171  @return this driver's minor version number
172 }
173 function TZAdoDriver.GetMinorVersion: Integer;
174 begin
175  Result := 0;
176 end;
177 
178 function TZAdoDriver.GetTokenizer: IZTokenizer;
179 begin
180  Result := TZAdoSQLTokenizer.Create; { thread save! Allways return a new Tokenizer! }
181 end;
182 
183 threadvar
184  AdoCoInitialized: integer;
185 
186 procedure CoInit;
187 begin
188  inc(AdoCoInitialized);
189  if AdoCoInitialized=1 then
190  CoInitialize(nil);
191 end;
192 
193 procedure CoUninit;
194 begin
195  assert(AdoCoInitialized>0);
196  dec(AdoCoInitialized);
197  if AdoCoInitialized=0 then
198  CoUninitialize;
199 end;
200 { TZAdoConnection }
201 
202 procedure TZAdoConnection.InternalCreate;
203 begin
204  CoInit;
205  FAdoConnection := CoConnection.Create;
206  Self.FMetadata := TZAdoDatabaseMetadata.Create(Self, URL);
207  Open;
208 end;
209 
210 {**
211  Destroys this object and cleanups the memory.
212 }
213 destructor TZAdoConnection.Destroy;
214 begin
215  Close;
216  FAdoConnection := nil;
217  inherited Destroy;
218  CoUninit;
219 end;
220 
221 {**
222  Just return the Ado Connection
223 }
224 function TZAdoConnection.GetAdoConnection: ZPlainAdo.Connection;
225 begin
226  Result := FAdoConnection;
227 end;
228 
229 {**
230  Executes simple statements internally.
231 }
232 procedure TZAdoConnection.InternalExecuteStatement(const SQL: string);
233 var
234  RowsAffected: OleVariant;
235 begin
236  try
237  FAdoConnection.Execute(SQL, RowsAffected, adExecuteNoRecords);
238  DriverManager.LogMessage(lcExecute, PlainDriver.GetProtocol, SQL);
239  except
240  on E: Exception do
241  begin
242  DriverManager.LogError(lcExecute, PlainDriver.GetProtocol, SQL, 0, E.Message);
243  raise;
244  end;
245  end;
246 end;
247 
248 procedure TZAdoConnection.CheckAdoError;
249 begin
250 end;
251 
252 {**
253  Starts a transaction support.
254 }
255 procedure TZAdoConnection.ReStartTransactionSupport;
256 begin
257  if Closed then Exit;
258 
259  if not (AutoCommit or (GetTransactionIsolation = tiNone)) then
260  StartTransaction;
261 end;
262 
263 {**
264  Opens a connection to database server with specified parameters.
265 }
266 procedure TZAdoConnection.Open;
267 var
268  LogMessage: string;
269 begin
270  if not Closed then Exit;
271 
272  LogMessage := Format('CONNECT TO "%s" AS USER "%s"', [Database, User]);
273  try
274  if ReadOnly then
275  FAdoConnection.Set_Mode(adModeRead)
276  else
277  FAdoConnection.Set_Mode(adModeUnknown);
278  FAdoConnection.Open(Database, User, Password, -1{adConnectUnspecified});
279  FAdoConnection.Set_CursorLocation(adUseClient);
280  DriverManager.LogMessage(lcConnect, PLainDriver.GetProtocol, LogMessage);
281  if FClientCodePage <> 'CP_ADO' then CheckCharEncoding('CP_ADO', True)
282  except
283  on E: Exception do
284  begin
285  DriverManager.LogError(lcConnect, PlainDriver.GetProtocol, LogMessage, 0, E.Message);
286  raise;
287  end;
288  end;
289 
290  inherited Open;
291 
292  FAdoConnection.IsolationLevel := IL[GetTransactionIsolation];
293  ReStartTransactionSupport;
294 end;
295 
296 function TZAdoConnection.GetBinaryEscapeString(const Value: TByteDynArray): String;
297 begin
298  Result := GetSQLHexString(PAnsiChar(Value), Length(Value), True);
299  if GetAutoEncodeStrings then
300  Result := GetDriver.GetTokenizer.GetEscapeString(Result)
301 end;
302 
303 function TZAdoConnection.GetBinaryEscapeString(const Value: RawByteString): String;
304 begin
305  Result := GetSQLHexString(PAnsiChar(Value), Length(Value), True);
306  if GetAutoEncodeStrings then
307  Result := GetDriver.GetTokenizer.GetEscapeString(Result)
308 end;
309 
310 {**
311  Creates a <code>Statement</code> object for sending
312  SQL statements to the database.
313  SQL statements without parameters are normally
314  executed using Statement objects. If the same SQL statement
315  is executed many times, it is more efficient to use a
316  <code>PreparedStatement</code> object.
317  <P>
318  Result sets created using the returned <code>Statement</code>
319  object will by default have forward-only type and read-only concurrency.
320 
321  @param Info a statement parameters.
322  @return a new Statement object
323 }
324 function TZAdoConnection.CreateRegularStatement(Info: TStrings): IZStatement;
325 begin
326  if IsClosed then Open;
327  Result := TZAdoStatement.Create(Self, Info);
328 end;
329 
330 {**
331  Creates a <code>PreparedStatement</code> object for sending
332  parameterized SQL statements to the database.
333 
334  A SQL statement with or without IN parameters can be
335  pre-compiled and stored in a PreparedStatement object. This
336  object can then be used to efficiently execute this statement
337  multiple times.
338 
339  <P><B>Note:</B> This method is optimized for handling
340  parametric SQL statements that benefit from precompilation. If
341  the driver supports precompilation,
342  the method <code>prepareStatement</code> will send
343  the statement to the database for precompilation. Some drivers
344  may not support precompilation. In this case, the statement may
345  not be sent to the database until the <code>PreparedStatement</code> is
346  executed. This has no direct effect on users; however, it does
347  affect which method throws certain SQLExceptions.
348 
349  Result sets created using the returned PreparedStatement will have
350  forward-only type and read-only concurrency, by default.
351 
352  @param sql a SQL statement that may contain one or more '?' IN
353  parameter placeholders
354  @param Info a statement parameters.
355  @return a new PreparedStatement object containing the
356  pre-compiled statement
357 }
358 function TZAdoConnection.CreatePreparedStatement(
359  const SQL: string; Info: TStrings): IZPreparedStatement;
360 begin
361  if IsClosed then Open;
362  Result := TZAdoPreparedStatement.Create(Self, SQL, Info);
363 end;
364 
365 {**
366  Creates a <code>CallableStatement</code> object for calling
367  database stored procedures.
368  The <code>CallableStatement</code> object provides
369  methods for setting up its IN and OUT parameters, and
370  methods for executing the call to a stored procedure.
371 
372  <P><B>Note:</B> This method is optimized for handling stored
373  procedure call statements. Some drivers may send the call
374  statement to the database when the method <code>prepareCall</code>
375  is done; others
376  may wait until the <code>CallableStatement</code> object
377  is executed. This has no
378  direct effect on users; however, it does affect which method
379  throws certain SQLExceptions.
380 
381  Result sets created using the returned CallableStatement will have
382  forward-only type and read-only concurrency, by default.
383 
384  @param sql a SQL statement that may contain one or more '?'
385  parameter placeholders. Typically this statement is a JDBC
386  function call escape string.
387  @param Info a statement parameters.
388  @return a new CallableStatement object containing the
389  pre-compiled SQL statement
390 }
391 function TZAdoConnection.CreateCallableStatement(const SQL: string; Info: TStrings):
392  IZCallableStatement;
393 begin
394  if IsClosed then Open;
395  Result := TZAdoCallableStatement.Create(Self, SQL, Info);
396 end;
397 
398 {**
399  Converts the given SQL statement into the system's native SQL grammar.
400  A driver may convert the JDBC sql grammar into its system's
401  native SQL grammar prior to sending it; this method returns the
402  native form of the statement that the driver would have sent.
403 
404  @param sql a SQL statement that may contain one or more '?'
405  parameter placeholders
406  @return the native form of this statement
407 }
408 function TZAdoConnection.NativeSQL(const SQL: string): string;
409 begin
410  Result := SQL;
411 end;
412 
413 {**
414  Sets this connection's auto-commit mode.
415  If a connection is in auto-commit mode, then all its SQL
416  statements will be executed and committed as individual
417  transactions. Otherwise, its SQL statements are grouped into
418  transactions that are terminated by a call to either
419  the method <code>commit</code> or the method <code>rollback</code>.
420  By default, new connections are in auto-commit mode.
421 
422  The commit occurs when the statement completes or the next
423  execute occurs, whichever comes first. In the case of
424  statements returning a ResultSet, the statement completes when
425  the last row of the ResultSet has been retrieved or the
426  ResultSet has been closed. In advanced cases, a single
427  statement may return multiple results as well as output
428  parameter values. In these cases the commit occurs when all results and
429  output parameter values have been retrieved.
430 
431  @param autoCommit true enables auto-commit; false disables auto-commit.
432 }
433 procedure TZAdoConnection.SetAutoCommit(Value: Boolean);
434 begin
435  if AutoCommit = Value then Exit;
436  if not Closed and Value then
437  begin
438  if (FAdoConnection.State = adStateOpen) and
439  (GetTransactionIsolation <> tiNone) then
440  begin
441  FAdoConnection.CommitTrans;
442  DriverManager.LogMessage(lcExecute, PlainDriver.GetProtocol, 'COMMIT');
443  end;
444  end;
445  inherited;
446  ReStartTransactionSupport;
447 end;
448 
449 {**
450  Attempts to change the transaction isolation level to the one given.
451  The constants defined in the interface <code>Connection</code>
452  are the possible transaction isolation levels.
453 
454  <P><B>Note:</B> This method cannot be called while
455  in the middle of a transaction.
456 
457  @param level one of the TRANSACTION_* isolation values with the
458  exception of TRANSACTION_NONE; some databases may not support other values
459  @see DatabaseMetaData#supportsTransactionIsolationLevel
460 }
461 procedure TZAdoConnection.SetTransactionIsolation(
462  Level: TZTransactIsolationLevel);
463 begin
464  if GetTransactionIsolation = Level then Exit;
465 
466  if not Closed and not AutoCommit and (GetTransactionIsolation <> tiNone) then
467  begin
468  FAdoConnection.CommitTrans;
469  DriverManager.LogMessage(lcExecute, PlainDriver.GetProtocol, 'COMMIT');
470  end;
471 
472  inherited;
473 
474  if not Closed then
475  FAdoConnection.IsolationLevel := IL[Level];
476 
477  RestartTransactionSupport;
478 end;
479 
480 {**
481  Starts a new transaction. Used internally.
482 }
483 procedure TZAdoConnection.StartTransaction;
484 var
485  LogMessage: string;
486 begin
487  LogMessage := 'BEGIN TRANSACTION';
488  try
489  FAdoConnection.BeginTrans;
490  DriverManager.LogMessage(lcExecute, PlainDriver.GetProtocol, LogMessage);
491  except
492  on E: Exception do
493  begin
494  DriverManager.LogError(lcExecute, PlainDriver.GetProtocol, LogMessage, 0, E.Message);
495  raise;
496  end;
497  end;
498 end;
499 
500 {**
501  Makes all changes made since the previous
502  commit/rollback permanent and releases any database locks
503  currently held by the Connection. This method should be
504  used only when auto-commit mode has been disabled.
505  @see #setAutoCommit
506 }
507 procedure TZAdoConnection.Commit;
508 var
509  LogMessage: string;
510 begin
511  LogMessage := 'COMMIT';
512  if not (AutoCommit or (GetTransactionIsolation = tiNone)) then
513  try
514  FAdoConnection.CommitTrans;
515  DriverManager.LogMessage(lcExecute, PlainDriver.GetProtocol, LogMessage);
516  StartTransaction;
517  except
518  on E: Exception do
519  begin
520  DriverManager.LogError(lcExecute, PlainDriver.GetProtocol, LogMessage, 0, E.Message);
521  raise;
522  end;
523  end;
524 end;
525 
526 {**
527  Drops all changes made since the previous
528  commit/rollback and releases any database locks currently held
529  by this Connection. This method should be used only when auto-
530  commit has been disabled.
531  @see #setAutoCommit
532 }
533 procedure TZAdoConnection.Rollback;
534 var
535  LogMessage: string;
536 begin
537  LogMessage := 'ROLLBACK';
538  if not (AutoCommit or (GetTransactionIsolation = tiNone)) then
539  try
540  FAdoConnection.RollbackTrans;
541  DriverManager.LogMessage(lcExecute, PlainDriver.GetProtocol, LogMessage);
542  StartTransaction;
543  except
544  on E: Exception do
545  begin
546  DriverManager.LogError(lcExecute, PlainDriver.GetProtocol, LogMessage, 0, E.Message);
547  raise;
548  end;
549  end;
550 end;
551 
552 {**
553  Releases a Connection's database and JDBC resources
554  immediately instead of waiting for
555  them to be automatically released.
556 
557  <P><B>Note:</B> A Connection is automatically closed when it is
558  garbage collected. Certain fatal errors also result in a closed
559  Connection.
560 }
561 procedure TZAdoConnection.Close;
562 var
563  LogMessage: string;
564 begin
565  if Closed or (not Assigned(PlainDriver)) then
566  Exit;
567 
568  SetAutoCommit(True);
569 
570  LogMessage := Format('CLOSE CONNECTION TO "%s"', [Database]);
571  try
572  if FAdoConnection.State = adStateOpen then
573  FAdoConnection.Close;
574  DriverManager.LogMessage(lcExecute, PlainDriver.GetProtocol, LogMessage);
575  except
576  on E: Exception do
577  begin
578  DriverManager.LogError(lcExecute, PlainDriver.GetProtocol, LogMessage, 0, E.Message);
579  raise;
580  end;
581  end;
582 
583  inherited;
584 end;
585 
586 {**
587  Puts this connection in read-only mode as a hint to enable
588  database optimizations.
589 
590  <P><B>Note:</B> This method cannot be called while in the
591  middle of a transaction.
592 
593  @param readOnly true enables read-only mode; false disables
594  read-only mode.
595 }
596 procedure TZAdoConnection.SetReadOnly(ReadOnly: Boolean);
597 begin
598  inherited;
599 end;
600 
601 {**
602  Sets a catalog name in order to select
603  a subspace of this Connection's database in which to work.
604  If the driver does not support catalogs, it will
605  silently ignore this request.
606 }
607 procedure TZAdoConnection.SetCatalog(const Catalog: string);
608 var
609  LogMessage: string;
610 begin
611  if Closed then Exit;
612 
613  LogMessage := Format('SET CATALOG %s', [Catalog]);
614  try
615  FAdoConnection.DefaultDatabase := Catalog;
616  DriverManager.LogMessage(lcExecute, PlainDriver.GetProtocol, LogMessage);
617  except
618  on E: Exception do
619  begin
620  DriverManager.LogError(lcExecute, PlainDriver.GetProtocol, LogMessage, 0, E.Message);
621  raise;
622  end;
623  end;
624 end;
625 
626 {**
627  Returns the Connection's current catalog name.
628  @return the current catalog name or null
629 }
630 function TZAdoConnection.GetCatalog: string;
631 begin
632  Result := FAdoConnection.DefaultDatabase;
633 end;
634 
635 {**
636  Returns the first warning reported by calls on this Connection.
637  <P><B>Note:</B> Subsequent warnings will be chained to this
638  SQLWarning.
639  @return the first SQLWarning or null
640 }
641 function TZAdoConnection.GetWarnings: EZSQLWarning;
642 begin
643  Result := nil;
644 end;
645 
646 {**
647  Clears all warnings reported for this <code>Connection</code> object.
648  After a call to this method, the method <code>getWarnings</code>
649  returns null until a new warning is reported for this Connection.
650 }
651 procedure TZAdoConnection.ClearWarnings;
652 begin
653 end;
654 
655 initialization
656  AdoCoInitialized := 0;
657  AdoDriver := TZAdoDriver.Create;
658  DriverManager.RegisterDriver(AdoDriver);
659 finalization
660  if Assigned(DriverManager) then
661  DriverManager.DeregisterDriver(AdoDriver);
662  AdoDriver := nil;
663 end.