zeoslib  UNKNOWN
 All Files
ZDbcASA.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { Interbase Database Connectivity Classes }
5 { }
6 { Originally written by Sergey Merkuriev }
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 ZDbcASA;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses
59  ZCompatibility, Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} Contnrs, SysUtils,
60  ZDbcIntfs, ZDbcConnection, ZPlainASADriver, ZTokenizer, ZDbcGenericResolver,
61  ZURL, ZPlainDriver, ZGenericSqlAnalyser, ZPlainASAConstants;
62 
63 type
64  {** Implements a ASA Database Driver. }
65  {$WARNINGS OFF}
66  TZASADriver = class(TZAbstractDriver)
67  public
68  constructor Create; override;
69  function Connect(const Url: TZURL): IZConnection; override;
70  function GetMajorVersion: Integer; override;
71  function GetMinorVersion: Integer; override;
72  function GetTokenizer: IZTokenizer; override;
73  function GetStatementAnalyser: IZStatementAnalyser; override;
74  end;
75  {$WARNINGS ON}
76 
77  {** Represents a ASA specific connection interface. }
78  IZASAConnection = interface (IZConnection)
79  ['{FAAAFCE0-F550-4098-96C6-580145813EBF}']
80  function GetDBHandle: PZASASQLCA;
81  function GetPlainDriver: IZASAPlainDriver;
82 // procedure CreateNewDatabase(SQL: String);
83  end;
84 
85  {** Implements ASA Database Connection. }
86  TZASAConnection = class(TZAbstractConnection, IZASAConnection)
87  private
88  FSQLCA: TZASASQLCA;
89  FHandle: PZASASQLCA;
90  private
91  procedure StartTransaction; virtual;
92  function DetermineASACharSet: String;
93  protected
94  procedure InternalCreate; override;
95  public
96  destructor Destroy; override;
97 
98  function GetDBHandle: PZASASQLCA;
99  function GetPlainDriver: IZASAPlainDriver;
100 // procedure CreateNewDatabase(SQL: String);
101 
102  function CreateRegularStatement(Info: TStrings): IZStatement; override;
103  function CreatePreparedStatement(const SQL: string; Info: TStrings):
104  IZPreparedStatement; override;
105  function CreateCallableStatement(const SQL: string; Info: TStrings):
106  IZCallableStatement; override;
107 
108  procedure Commit; override;
109  procedure Rollback; override;
110  procedure SetOption(Temporary: Integer; User: PAnsiChar; const Option: string;
111  const Value: string);
112 
113  procedure Open; override;
114  procedure Close; override;
115  end;
116 
117  {** Implements a specialized cached resolver for ASA. }
118  TZASACachedResolver = class(TZGenericCachedResolver)
119  public
120  function FormCalculateStatement(Columns: TObjectList): string; override;
121  end;
122 
123 
124 var
125  {** The common driver manager object. }
126  ASADriver: IZDriver;
127 
128 implementation
129 
130 uses
131  ZDbcASAMetadata, ZDbcASAStatement, ZDbcASAUtils, ZSybaseToken,
132  ZSybaseAnalyser, ZDbcLogging{$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF};
133 
134 { TZASADriver }
135 
136 {**
137  Attempts to make a database connection to the given URL.
138  The driver should return "null" if it realizes it is the wrong kind
139  of driver to connect to the given URL. This will be common, as when
140  the JDBC driver manager is asked to connect to a given URL it passes
141  the URL to each loaded driver in turn.
142 
143  <P>The driver should raise a SQLException if it is the right
144  driver to connect to the given URL, but has trouble connecting to
145  the database.
146 
147  <P>The java.util.Properties argument can be used to passed arbitrary
148  string tag/value pairs as connection arguments.
149  Normally at least "user" and "password" properties should be
150  included in the Properties.
151 
152  @param url the URL of the database to which to connect
153  @param info a list of arbitrary string tag/value pairs as
154  connection arguments. Normally at least a "user" and
155  "password" property should be included.
156  @return a <code>Connection</code> object that represents a
157  connection to the URL
158 }
159 {$WARNINGS OFF}
160 function TZASADriver.Connect(const Url: TZURL): IZConnection;
161 begin
162  Result := TZASAConnection.Create(Url);
163 end;
164 {$WARNINGS ON}
165 
166 {**
167  Constructs this object with default properties.
168 }
169 constructor TZASADriver.Create;
170 begin
171  inherited Create;
172  AddSupportedProtocol(AddPlainDriverToCache(TZASA7PlainDriver.Create));
173  AddSupportedProtocol(AddPlainDriverToCache(TZASA8PlainDriver.Create));
174  AddSupportedProtocol(AddPlainDriverToCache(TZASA9PlainDriver.Create));
175  AddSupportedProtocol(AddPlainDriverToCache(TZASA12PlainDriver.Create));
176 end;
177 
178 {**
179  Gets the driver's major version number. Initially this should be 1.
180  @return this driver's major version number
181 }
182 function TZASADriver.GetMajorVersion: Integer;
183 begin
184  Result := 1;
185 end;
186 
187 {**
188  Gets the driver's minor version number. Initially this should be 0.
189  @return this driver's minor version number
190 }
191 function TZASADriver.GetMinorVersion: Integer;
192 begin
193  Result := 0;
194 end;
195 
196 {**
197  Gets a SQL syntax tokenizer.
198  @returns a SQL syntax tokenizer object.
199 }
200 function TZASADriver.GetTokenizer: IZTokenizer;
201 begin
202  Result := TZSybaseTokenizer.Create; { thread save! Allways return a new Tokenizer! }
203 end;
204 
205 {**
206  Creates a statement analyser object.
207  @returns a statement analyser object.
208 }
209 function TZASADriver.GetStatementAnalyser: IZStatementAnalyser;
210 begin
211  Result := TZSybaseStatementAnalyser.Create; { thread save! Allways return a new Analyser! }
212 end;
213 
214 { TZASAConnection }
215 
216 {**
217  Releases a Connection's database and JDBC resources
218  immediately instead of waiting for
219  them to be automatically released.
220 
221  <P><B>Note:</B> A Connection is automatically closed when it is
222  garbage collected. Certain fatal errors also result in a closed
223  Connection.
224 }
225 procedure TZASAConnection.Close;
226 begin
227  if Closed or (not Assigned(PlainDriver))then
228  Exit;
229 
230  if AutoCommit then
231  Commit
232  else
233  Rollback;
234 
235  GetPlainDriver.db_string_disconnect( FHandle, nil);
236  CheckASAError( GetPlainDriver, FHandle, lcDisconnect);
237 
238  FHandle := nil;
239  if GetPlainDriver.db_fini( @FSQLCA) = 0 then
240  begin
241  DriverManager.LogError( lcConnect, PlainDriver.GetProtocol, 'Inititalizing SQLCA',
242  0, 'Error closing SQLCA');
243  raise EZSQLException.CreateWithCode( 0,
244  'Error closing SQLCA');
245  end;
246 
247  DriverManager.LogMessage(lcDisconnect, PlainDriver.GetProtocol,
248  Format('DISCONNECT FROM "%s"', [Database]));
249 
250  inherited Close;
251 end;
252 
253 {**
254  Commit current transaction
255 }
256 procedure TZASAConnection.Commit;
257 begin
258  if Closed or AutoCommit then
259  Exit;
260 
261  if FHandle <> nil then
262  begin
263  GetPlainDriver.db_commit( FHandle, 0);
264  CheckASAError( GetPlainDriver, FHandle, lcTransaction);
265  DriverManager.LogMessage(lcTransaction,
266  PlainDriver.GetProtocol, 'TRANSACTION COMMIT');
267  end;
268 end;
269 
270 {**
271  Constructs this object and assignes the main properties.
272 }
273 procedure TZASAConnection.InternalCreate;
274 begin
275  Self.FMetadata := TZASADatabaseMetadata.Create(Self, URL);
276 end;
277 
278 {**
279  Creates a <code>CallableStatement</code> object for calling
280  database stored procedures.
281  The <code>CallableStatement</code> object provides
282  methods for setting up its IN and OUT parameters, and
283  methods for executing the call to a stored procedure.
284 
285  <P><B>Note:</B> This method is optimized for handling stored
286  procedure call statements. Some drivers may send the call
287  statement to the database when the method <code>prepareCall</code>
288  is done; others
289  may wait until the <code>CallableStatement</code> object
290  is executed. This has no
291  direct effect on users; however, it does affect which method
292  throws certain SQLExceptions.
293 
294  Result sets created using the returned CallableStatement will have
295  forward-only type and read-only concurrency, by default.
296 
297  @param sql a SQL statement that may contain one or more '?'
298  parameter placeholders. Typically this statement is a JDBC
299  function call escape string.
300  @param Info a statement parameters.
301  @return a new CallableStatement object containing the
302  pre-compiled SQL statement
303 }
304 function TZASAConnection.CreateCallableStatement(const SQL: string;
305  Info: TStrings): IZCallableStatement;
306 begin
307  if IsClosed then
308  Open;
309  Result := TZASACallableStatement.Create(Self, SQL, Info);
310 end;
311 
312 {**
313  Creates a <code>PreparedStatement</code> object for sending
314  parameterized SQL statements to the database.
315 
316  A SQL statement with or without IN parameters can be
317  pre-compiled and stored in a PreparedStatement object. This
318  object can then be used to efficiently execute this statement
319  multiple times.
320 
321  <P><B>Note:</B> This method is optimized for handling
322  parametric SQL statements that benefit from precompilation. If
323  the driver supports precompilation,
324  the method <code>prepareStatement</code> will send
325  the statement to the database for precompilation. Some drivers
326  may not support precompilation. In this case, the statement may
327  not be sent to the database until the <code>PreparedStatement</code> is
328  executed. This has no direct effect on users; however, it does
329  affect which method throws certain SQLExceptions.
330 
331  Result sets created using the returned PreparedStatement will have
332  forward-only type and read-only concurrency, by default.
333 
334  @param sql a SQL statement that may contain one or more '?' IN
335  parameter placeholders
336  @return a new PreparedStatement object containing the
337  pre-compiled statement
338 }
339 function TZASAConnection.CreatePreparedStatement(const SQL: string;
340  Info: TStrings): IZPreparedStatement;
341 begin
342  if IsClosed then
343  Open;
344  Result := TZASAPreparedStatement.Create(Self, SQL, Info);
345 end;
346 
347 {**
348  Creates a <code>Statement</code> object for sending
349  SQL statements to the database.
350  SQL statements without parameters are normally
351  executed using Statement objects. If the same SQL statement
352  is executed many times, it is more efficient to use a
353  <code>PreparedStatement</code> object.
354  <P>
355  Result sets created using the returned <code>Statement</code>
356  object will by default have forward-only type and read-only concurrency.
357 
358  @param Info a statement parameters.
359  @return a new Statement object
360 }
361 function TZASAConnection.CreateRegularStatement(
362  Info: TStrings): IZStatement;
363 begin
364  if IsClosed then
365  Open;
366  Result := TZASAStatement.Create(Self, Info);
367 end;
368 
369 {**
370  Destroys this object and cleanups the memory.
371 }
372 destructor TZASAConnection.Destroy;
373 begin
374  if not Closed then
375  Close;
376 
377  inherited;
378 end;
379 
380 {**
381  Get database connection handle.
382  @return database handle
383 }
384 function TZASAConnection.GetDBHandle: PZASASQLCA;
385 begin
386  Result := FHandle;
387 end;
388 
389 {**
390  Return native interbase plain driver
391  @return plain driver
392 }
393 function TZASAConnection.GetPlainDriver: IZASAPlainDriver;
394 begin
395  Result := PlainDriver as IZASAPlainDriver;
396 end;
397 
398 {**
399  Opens a connection to database server with specified parameters.
400 }
401 procedure TZASAConnection.Open;
402 var
403  ConnectionString, Links: string;
404 begin
405  if not Closed then
406  Exit;
407 
408  FHandle := nil;
409  ConnectionString := '';
410  try
411  if GetPlainDriver.db_init( @FSQLCA) = 0 then
412  begin
413  DriverManager.LogError( lcConnect, PlainDriver.GetProtocol, 'Inititalizing SQLCA',
414  0, 'Error initializing SQLCA');
415  raise EZSQLException.CreateWithCode( 0,
416  'Error initializing SQLCA');
417  end;
418  FHandle := @FSQLCA;
419 
420  if HostName <> '' then
421  ConnectionString := ConnectionString + 'ENG="' + HostName + '"; ';
422  if User <> '' then
423  ConnectionString := ConnectionString + 'UID="' + User + '"; ';
424  if Password <> '' then
425  ConnectionString := ConnectionString + 'PWD="' + Password + '"; ';
426  if Database <> '' then
427  begin
428  if CompareText( ExtractFileExt( Database), '.db') = 0 then
429  ConnectionString := ConnectionString + 'DBF="' + Database + '"; '
430  else
431  ConnectionString := ConnectionString + 'DBN="' + Database + '"; ';
432  end;
433 
434  Links := '';
435  if Info.Values['CommLinks'] <> ''
436  then Links := 'CommLinks=' + Info.Values['CommLinks'];
437  if Info.Values['LINKS'] <> ''
438  then Links := 'LINKS=' + Info.Values['LINKS'];
439  if (Links = '') and (Port <> 0)
440  then Links := 'LINKS=tcpip(PORT=' + IntToStr(Port) + ')';
441  if Links <> ''
442  then ConnectionString := ConnectionString + Links + '; ';
443 
444  GetPlainDriver.db_string_connect(FHandle, PAnsiChar(AnsiString(ConnectionString)));
445  CheckASAError( GetPlainDriver, FHandle, lcConnect);
446 
447  DriverManager.LogMessage(lcConnect, PlainDriver.GetProtocol,
448  Format('CONNECT TO "%s" AS USER "%s"', [Database, User]));
449 
450  if ( FClientCodePage <> '' ) then
451  if ( GetPlainDriver.db_change_char_charset(FHandle, PAnsiChar(AnsiString(FClientCodePage))) = 0 ) or
452  ( GetPlainDriver.db_change_nchar_charset(FHandle, PAnsiChar(AnsiString(FClientCodePage))) = 0 ) then
453  CheckASAError( GetPlainDriver, FHandle, lcOther, 'Set client CharacterSet failed.');
454 
455  StartTransaction;
456 
457  //SetConnOptions RowCount;
458 
459  except
460  on E: Exception do
461  begin
462  if Assigned( FHandle) then
463  GetPlainDriver.db_fini( FHandle);
464  FHandle := nil;
465  raise;
466  end;
467  end;
468 
469  inherited Open;
470 
471  if FClientCodePage = '' then
472  CheckCharEncoding(DetermineASACharSet);
473 end;
474 
475 {**
476  Drops all changes made since the previous
477  commit/rollback and releases any database locks currently held
478  by this Connection. This method should be used only when auto-
479  commit has been disabled.
480  @see #setAutoCommit
481 }
482 procedure TZASAConnection.Rollback;
483 begin
484  if Closed or AutoCommit then
485  Exit;
486 
487  if Assigned( FHandle) then
488  begin
489  GetPlainDriver.db_rollback( FHandle, 0);
490  CheckASAError( GetPlainDriver, FHandle, lcTransaction);
491  DriverManager.LogMessage(lcTransaction,
492  PlainDriver.GetProtocol, 'TRANSACTION ROLLBACK');
493  end;
494 end;
495 
496 procedure TZASAConnection.SetOption(Temporary: Integer; User: PAnsiChar;
497  const Option: string; const Value: string);
498 var
499  SQLDA: PASASQLDA;
500  Sz: Integer;
501  S: string;
502 begin
503  if Assigned( FHandle) then
504  begin
505  Sz := SizeOf( TASASQLDA) - 32767 * SizeOf( TZASASQLVAR);
506  SQLDA := AllocMem( Sz);
507  try
508  {$IFDEF WITH_STRPLCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrPLCopy( SQLDA.sqldaid, 'SQLDA ', 8);
509  SQLDA.sqldabc := Sz;
510  SQLDA.sqln := 1;
511  SQLDA.sqld := 1;
512  SQLDA.sqlVar[0].sqlType := DT_STRING;
513  SQLDA.sqlVar[0].sqlLen := Length( Value)+1;
514  SQLDA.sqlVar[0].sqlData := PAnsiChar(AnsiString(Value));
515  GetPlainDriver.db_setoption(FHandle, Temporary, User, PAnsiChar(AnsiString(Option)), SQLDA);
516 
517  CheckASAError( GetPlainDriver, FHandle, lcOther);
518  S := String(User);
519  DriverManager.LogMessage( lcOther, PlainDriver.GetProtocol,
520  Format( 'SET OPTION %s.%s = %s', [ S, Option, Value]));
521  finally
522  FreeMem( SQLDA);
523  end;
524  end;
525 end;
526 
527 {**
528  Start transaction
529 }
530 procedure TZASAConnection.StartTransaction;
531 var
532  ASATL: integer;
533 begin
534  if AutoCommit then
535  SetOption( 1, nil, 'CHAINED', 'OFF')
536  else
537  SetOption( 1, nil, 'CHAINED', 'ON');
538  ASATL := Ord( TransactIsolationLevel);
539  if ASATL > 1 then
540  ASATL := ASATL - 1;
541  SetOption( 1, nil, 'ISOLATION_LEVEL', IntToStr( ASATL));
542 end;
543 
544 function TZASAConnection.DetermineASACharSet: String;
545 var
546  Stmt: IZStatement;
547  RS: IZResultSet;
548 begin
549  Stmt := Self.CreateRegularStatement(Info);
550  RS := Stmt.ExecuteQuery('SELECT DB_PROPERTY( ''CharSet'')');
551  if RS.Next then
552  Result := RS.GetString(1)
553  else
554  Result := '';
555  RS := nil;
556  Stmt.Close;
557  Stmt := nil;
558 end;
559 
560 { TZASACachedResolver }
561 
562 {**
563  Forms a where clause for SELECT statements to calculate default values.
564  @param Columns a collection of key columns.
565  @param OldRowAccessor an accessor object to old column values.
566 }
567 function TZASACachedResolver.FormCalculateStatement(
568  Columns: TObjectList): string;
569 var
570  I: Integer;
571  Current: TZResolverParameter;
572 begin
573  Result := '';
574  if Columns.Count = 0 then
575  Exit;
576 
577  for I := 0 to Columns.Count - 1 do
578  begin
579  Current := TZResolverParameter(Columns[I]);
580  if Result <> '' then
581  Result := Result + ',';
582  if Current.DefaultValue <> '' then
583  Result := Result + Current.DefaultValue
584  else
585  Result := Result + 'NULL';
586  end;
587  Result := 'SELECT ' + Result;
588 end;
589 
590 initialization
591  ASADriver := TZASADriver.Create;
592  DriverManager.RegisterDriver(ASADriver);
593 
594 finalization
595  if Assigned(DriverManager) then
596  DriverManager.DeregisterDriver(ASADriver);
597  ASADriver := nil;
598 end.