zeoslib  UNKNOWN
 All Files
ZDbcASAStatement.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 ZDbcASAStatement;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
59  ZDbcIntfs, ZDbcStatement, ZDbcASA, ZDbcASAUtils, ZDbcASAResultSet,
60  ZPlainASADriver, ZCompatibility, ZDbcLogging, ZVariant, ZMessages;
61 
62 type
63 
64  {** Implements Generic ASA Statement. }
65  TZASAStatement = class(TZAbstractStatement)
66  private
67  FCachedBlob: Boolean;
68  FStmtNum: SmallInt;
69  FASAConnection: IZASAConnection;
70  FSQLData: IZASASQLDA;
71  FMoreResults: Boolean;
72  function InternalExecuteQuery(const SQL: RawByteString): IZResultSet;
73  public
74  constructor Create(Connection: IZConnection; Info: TStrings);
75  destructor Destroy; override;
76 
77  procedure Close; override;
78  procedure Cancel; override;
79  function GetWarnings: EZSQLWarning; override;
80  procedure ClearWarnings; override;
81  function GetMoreResults: Boolean; override;
82  function ExecuteQuery(const SQL: RawByteString): IZResultSet; override;
83  function ExecuteUpdate(const SQL: RawByteString): Integer; override;
84  function Execute(const SQL: RawByteString): Boolean; override;
85  end;
86 
87  {** Implements Prepared SQL Statement. }
88  TZASAPreparedStatement = class(TZAbstractPreparedStatement)
89  private
90  FCachedBlob: boolean;
91  FStmtNum: SmallInt;
92  FASAConnection: IZASAConnection;
93  FParamSQLData: IZASASQLDA;
94  FSQLData: IZASASQLDA;
95  FMoreResults: Boolean;
96  FPrepared: Boolean;
97  public
98  constructor Create(Connection: IZConnection; const SQL: string; Info: TStrings);
99  destructor Destroy; override;
100 
101  procedure Close; override;
102  procedure Cancel; override;
103  function GetWarnings: EZSQLWarning; override;
104  procedure ClearWarnings; override;
105  function GetMoreResults: Boolean; override;
106  function ExecuteQuery(const SQL: RawByteString): IZResultSet; override;
107  function ExecuteUpdate(const SQL: RawByteString): Integer; override;
108  function Execute(const SQL: RawByteString): Boolean; override;
109 
110  function ExecuteQueryPrepared: IZResultSet; override;
111  function ExecuteUpdatePrepared: Integer; override;
112  function ExecutePrepared: Boolean; override;
113  end;
114 
115  TZASACallableStatement = class(TZAbstractCallableStatement)
116  private
117  FCachedBlob: boolean;
118  FStmtNum: SmallInt;
119  FASAConnection: IZASAConnection;
120  FParamSQLData: IZASASQLDA;
121  FSQLData: IZASASQLDA;
122  FMoreResults: Boolean;
123  FPrepared: Boolean;
124  protected
125  procedure FetchOutParams( Value: IZASASQLDA);
126  function GetProcedureSQL: RawByteString;
127  public
128  constructor Create(Connection: IZConnection; const SQL: string; Info: TStrings);
129  destructor Destroy; override;
130 
131  procedure Close; override;
132  procedure Cancel; override;
133  function GetWarnings: EZSQLWarning; override;
134  procedure ClearWarnings; override;
135  function GetMoreResults: Boolean; override;
136  function ExecuteQuery(const SQL: RawByteString): IZResultSet; override;
137  function ExecuteUpdate(const SQL: RawByteString): Integer; override;
138  function Execute(const SQL: RawByteString): Boolean; override;
139 
140  function ExecuteQueryPrepared: IZResultSet; override;
141  function ExecuteUpdatePrepared: Integer; override;
142  function ExecutePrepared: Boolean; override;
143  end;
144 
145 implementation
146 
147 uses ZSysUtils, ZDbcUtils, ZPlainASAConstants;
148 
149 { TZASAStatement }
150 
151 function TZASAStatement.InternalExecuteQuery(const SQL: RawByteString): IZResultSet;
152 var
153  Cursor: AnsiString;
154  CursorOptions: SmallInt;
155 begin
156  Close;
157  with FASAConnection do
158  begin
159  try
160  GetPlainDriver.db_prepare_describe( GetDBHandle, nil, @FStmtNum,
161  PAnsiChar(ASQL), FSQLData.GetData, SQL_PREPARE_DESCRIBE_STMTNUM +
162  SQL_PREPARE_DESCRIBE_OUTPUT + SQL_PREPARE_DESCRIBE_VARRESULT, 0);
163  ZDbcASAUtils.CheckASAError(GetPlainDriver, GetDBHandle, lcExecute, LogSQL);
164 
165  FMoreResults := GetDBHandle.sqlerrd[2] = 0;
166  if not FMoreResults then
167  begin
168  if FSQLData.GetData^.sqld <= 0 then
169  begin
170  Result := nil;
171  Exit;
172  end
173  else
174  if ( FSQLData.GetData^.sqld > FSQLData.GetData^.sqln) then
175  begin
176  FSQLData.AllocateSQLDA( FSQLData.GetData^.sqld);
177  GetPlainDriver.db_describe( GetDBHandle, nil, @FStmtNum,
178  FSQLData.GetData, SQL_DESCRIBE_OUTPUT);
179  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute, LogSQL);
180  end;
181  FSQLData.InitFields;
182  end;
183  if ResultSetConcurrency = rcUpdatable then
184  CursorOptions := CUR_OPEN_DECLARE + CUR_UPDATE
185  else
186  CursorOptions := CUR_OPEN_DECLARE + CUR_READONLY;
187  if ResultSetType = rtScrollInsensitive then
188  CursorOptions := CursorOptions + CUR_INSENSITIVE;
189  Cursor := CursorName;
190  GetPlainDriver.db_open(GetDBHandle, PAnsiChar(Cursor), nil, @FStmtNum,
191  nil, FetchSize, 0, CursorOptions);
192  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute, LogSQL);
193  Closed := false;
194  if FMoreResults then
195  DescribeCursor( FASAConnection, FSQLData, Cursor, LogSQL);
196 
197  LastUpdateCount := -1;
198  Result := GetCachedResultSet( LogSQL, Self,
199  TZASAResultSet.Create( Self, LogSQL, FStmtNum, Cursor, FSQLData, nil,
200  FCachedBlob));
201  { Logging SQL Command }
202  DriverManager.LogMessage( lcExecute, GetPlainDriver.GetProtocol, LogSQL);
203  except
204  on E: Exception do
205  begin
206  Self.Close;
207  raise;
208  end;
209  end;
210  end;
211 end;
212 {**
213  Constructs this object and assignes the main properties.
214  @param Connection a database connection object.
215  @param Handle a connection handle pointer.
216  @param Dialect a dialect Interbase SQL must be 1 or 2 or 3.
217  @param Info a statement parameters.
218 }
219 constructor TZASAStatement.Create(Connection: IZConnection;
220  Info: TStrings);
221 begin
222  inherited Create(Connection, Info);
223 
224  FASAConnection := Connection as IZASAConnection;
225  FetchSize := BlockSize;
226  ResultSetConcurrency := rcUpdatable;
227  ResultSetType := rtScrollSensitive;
228  FCachedBlob := StrToBoolEx(DefineStatementParameter(Self, 'cashedblob', 'true'));
229  CursorName := AnsiString(RandomString(12));
230  FSQLData := TZASASQLDA.Create( FASAConnection.GetPlainDriver,
231  FASAConnection.GetDBHandle, CursorName, ConSettings);
232 end;
233 
234 destructor TZASAStatement.Destroy;
235 begin
236  FSQLData := nil;
237  inherited;
238 end;
239 
240 procedure TZASAStatement.Close;
241 begin
242  if not Closed then
243  begin
244  FASAConnection.GetPlainDriver.db_close(FASAConnection.GetDBHandle, PAnsiChar(CursorName));
245  Closed := false;
246  end;
247  if FStmtNum <> 0 then
248  begin
249  FASAConnection.GetPlainDriver.db_dropstmt( FASAConnection.GetDBHandle, nil,
250  nil, @FStmtNum);
251  FStmtNum := 0;
252  end;
253  inherited;
254 end;
255 
256 procedure TZASAStatement.Cancel;
257 begin
258  with FASAConnection do
259  begin
260  GetPlainDriver.db_cancel_request( GetDBHandle);
261  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute);
262  end;
263 end;
264 
265 function TZASAStatement.GetWarnings: EZSQLWarning;
266 begin
267  Result := inherited GetWarnings;
268 end;
269 
270 procedure TZASAStatement.ClearWarnings;
271 begin
272  inherited;
273 end;
274 
275 function TZASAStatement.GetMoreResults: Boolean;
276 var
277  SQLData: IZASASQLDA;
278 begin
279  Result := FMoreResults;
280  if FMoreResults then
281  begin
282  with FASAConnection do
283  begin
284  GetPlainDriver.db_resume(GetDBHandle, PAnsiChar(CursorName));
285  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute);
286  if GetDBHandle.sqlcode = SQLE_PROCEDURE_COMPLETE then
287  Result := false
288  else
289  begin
290  SQLData := TZASAResultSet(LastResultSet).SQLData;
291  DescribeCursor( FASAConnection, TZASASQLDA( SQLData), CursorName, '');
292  end;
293  end;
294  end;
295 end;
296 
297 {**
298  Executes an SQL statement that returns a single <code>ResultSet</code> object.
299  @param sql typically this is a static SQL <code>SELECT</code> statement
300  @return a <code>ResultSet</code> object that contains the data produced by the
301  given query; never <code>null</code>
302 }
303 function TZASAStatement.ExecuteQuery(const SQL: RawByteString): IZResultSet;
304 begin
305  ASQL := SQL;
306  Result := InternalExecuteQuery(ASQL);
307  if Result = nil then
308  raise EZSQLException.Create( SCanNotRetrieveResultSetData)
309 end;
310 
311 {**
312  Executes an SQL <code>INSERT</code>, <code>UPDATE</code> or
313  <code>DELETE</code> statement. In addition,
314  SQL statements that return nothing, such as SQL DDL statements,
315  can be executed.
316 
317  @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
318  <code>DELETE</code> statement or an SQL statement that returns nothing
319  @return either the row count for <code>INSERT</code>, <code>UPDATE</code>
320  or <code>DELETE</code> statements, or 0 for SQL statements that return nothing
321 }
322 {$HINTS OFF}
323 function TZASAStatement.ExecuteUpdate(const SQL: RawByteString): Integer;
324 begin
325  ASQL := SQL;
326  Close;
327  Result := -1;
328  with FASAConnection do
329  begin
330  GetPlainDriver.db_execute_imm(GetDBHandle, PAnsiChar(ASQL));
331  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute, LogSQL);
332 
333  Result := GetDBHandle.sqlErrd[2];
334  LastUpdateCount := Result;
335 
336  { Logging SQL Command }
337  DriverManager.LogMessage(lcExecute, GetPlainDriver.GetProtocol, LogSQL);
338  end;
339 end;
340 {$HINTS ON}
341 
342 {**
343  Executes an SQL statement that may return multiple results.
344  Under some (uncommon) situations a single SQL statement may return
345  multiple result sets and/or update counts. Normally you can ignore
346  this unless you are (1) executing a stored procedure that you know may
347  return multiple results or (2) you are dynamically executing an
348  unknown SQL string. The methods <code>execute</code>,
349  <code>getMoreResults</code>, <code>getResultSet</code>,
350  and <code>getUpdateCount</code> let you navigate through multiple results.
351 
352  The <code>execute</code> method executes an SQL statement and indicates the
353  form of the first result. You can then use the methods
354  <code>getResultSet</code> or <code>getUpdateCount</code>
355  to retrieve the result, and <code>getMoreResults</code> to
356  move to any subsequent result(s).
357 
358  @param sql any SQL statement
359  @return <code>true</code> if the next result is a <code>ResultSet</code> object;
360  <code>false</code> if it is an update count or there are no more results
361  @see #getResultSet
362  @see #getUpdateCount
363  @see #getMoreResults
364 }
365 function TZASAStatement.Execute(const SQL: RawByteString): Boolean;
366 begin
367  ASQL := SQL;
368  LastResultSet := InternalExecuteQuery(ASQL);
369  Result := Assigned(LastResultSet);
370  if not Result then
371  ExecuteUpdate(ASQL);
372 end;
373 
374 { TZASAPreparedStatement }
375 
376 {**
377  Constructs this object and assignes the main properties.
378  @param Connection a database connection object.
379  @param Handle a connection handle pointer.
380  @param Dialect a dialect Interbase SQL must be 1 or 2 or 3.
381  @param Info a statement parameters.
382 }
383 constructor TZASAPreparedStatement.Create(Connection: IZConnection;
384  const SQL: string; Info: TStrings);
385 begin
386  inherited Create(Connection, SQL, Info);
387 
388  FASAConnection := Connection as IZASAConnection;
389  FetchSize := BlockSize;
390  ResultSetConcurrency := rcUpdatable;
391  ResultSetType := rtScrollSensitive;
392  FCachedBlob := StrToBoolEx(DefineStatementParameter(Self, 'cashedblob', 'true'));
393  CursorName := AnsiString(RandomString(12));
394  FParamSQLData := TZASASQLDA.Create( FASAConnection.GetPlainDriver,
395  FASAConnection.GetDBHandle, CursorName, ConSettings);
396  FSQLData := TZASASQLDA.Create( FASAConnection.GetPlainDriver,
397  FASAConnection.GetDBHandle, CursorName, ConSettings);
398  ASAPrepare( FASAConnection, FSQLData, FParamSQLData, ASQL, LogSQL, @FStmtNum, FPrepared,
399  FMoreResults);
400 end;
401 
402 destructor TZASAPreparedStatement.Destroy;
403 begin
404  FSQLData := nil;
405  FParamSQLData := nil;
406  inherited;
407 end;
408 
409 procedure TZASAPreparedStatement.Close;
410 begin
411  if not Closed then
412  begin
413  FASAConnection.GetPlainDriver.db_close( FASAConnection.GetDBHandle, PAnsiChar(CursorName));
414  Closed := false;
415  end;
416  if FStmtNum <> 0 then
417  begin
418  FASAConnection.GetPlainDriver.db_dropstmt( FASAConnection.GetDBHandle, nil, nil, @FStmtNum);
419  FStmtNum := 0;
420  end;
421  inherited;
422 end;
423 
424 procedure TZASAPreparedStatement.Cancel;
425 begin
426  with FASAConnection do
427  begin
428  GetPlainDriver.db_cancel_request( GetDBHandle);
429  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute, SQL);
430  end;
431 end;
432 
433 function TZASAPreparedStatement.GetWarnings: EZSQLWarning;
434 begin
435  Result := inherited GetWarnings;
436 end;
437 
438 procedure TZASAPreparedStatement.ClearWarnings;
439 begin
440  inherited;
441 end;
442 
443 function TZASAPreparedStatement.GetMoreResults: Boolean;
444 begin
445  Result := FMoreResults;
446  if FMoreResults then
447  begin
448  with FASAConnection do
449  begin
450  GetPlainDriver.db_resume(GetDBHandle, PAnsiChar(CursorName));
451  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute);
452  if GetDBHandle.sqlcode = SQLE_PROCEDURE_COMPLETE then
453  Result := false
454  else
455  DescribeCursor( FASAConnection, TZASASQLDA( FSQLData), CursorName, '');
456  end;
457  end;
458 end;
459 
460 {**
461  Executes an SQL statement that may return multiple results.
462  Under some (uncommon) situations a single SQL statement may return
463  multiple result sets and/or update counts. Normally you can ignore
464  this unless you are (1) executing a stored procedure that you know may
465  return multiple results or (2) you are dynamically executing an
466  unknown SQL string. The methods <code>execute</code>,
467  <code>getMoreResults</code>, <code>getResultSet</code>,
468  and <code>getUpdateCount</code> let you navigate through multiple results.
469 
470  The <code>execute</code> method executes an SQL statement and indicates the
471  form of the first result. You can then use the methods
472  <code>getResultSet</code> or <code>getUpdateCount</code>
473  to retrieve the result, and <code>getMoreResults</code> to
474  move to any subsequent result(s).
475 
476  @param sql any SQL statement
477  @return <code>true</code> if the next result is a <code>ResultSet</code> object;
478  <code>false</code> if it is an update count or there are no more results
479  @see #getResultSet
480  @see #getUpdateCount
481  @see #getMoreResults
482 }
483 
484 function TZASAPreparedStatement.Execute(const SQL: RawByteString): Boolean;
485 begin
486  if ASQL <> SQL then
487  begin
488  Close;
489  ASQL := SQL;
490  ASAPrepare( FASAConnection, FSQLData, FParamSQLData, ASQL, LogSQL, @FStmtNum, FPrepared,
491  FMoreResults);
492  end;
493  Result := ExecutePrepared;
494 end;
495 
496 {**
497  Executes any kind of SQL statement.
498  Some prepared statements return multiple results; the <code>execute</code>
499  method handles these complex statements as well as the simpler
500  form of statements handled by the methods <code>executeQuery</code>
501  and <code>executeUpdate</code>.
502  @see Statement#execute
503 }
504 function TZASAPreparedStatement.ExecutePrepared: Boolean;
505 begin
506  if FMoreResults or ( FSQLData.GetData.sqld > 0) then
507  begin
508  LastResultSet := ExecuteQueryPrepared;
509  Result := true;
510  end
511  else
512  begin
513  ExecuteUpdatePrepared;
514  Result := false;
515  end;
516 end;
517 
518 {**
519  Executes an SQL statement that returns a single <code>ResultSet</code> object.
520  @param sql typically this is a static SQL <code>SELECT</code> statement
521  @return a <code>ResultSet</code> object that contains the data produced by the
522  given query; never <code>null</code>
523 }
524 function TZASAPreparedStatement.ExecuteQuery(const SQL: RawByteString): IZResultSet;
525 begin
526  if ASQL <> SQL then
527  begin
528  Close;
529  ASQL := SQL;
530  ASAPrepare( FASAConnection, FSQLData, FParamSQLData, ASQL, LogSQL, @FStmtNum,
531  FPrepared, FMoreResults);
532  end;
533  Result := ExecuteQueryPrepared;
534 end;
535 
536 {**
537  Executes the SQL query in this <code>PreparedStatement</code> object
538  and returns the result set generated by the query.
539 
540  @return a <code>ResultSet</code> object that contains the data produced by the
541  query; never <code>null</code>
542 }
543 {$HINTS OFF}
544 function TZASAPreparedStatement.ExecuteQueryPrepared: IZResultSet;
545 var
546  Cursor: AnsiString;
547  CursorOptions: SmallInt;
548 begin
549  with FASAConnection do
550  begin
551  PrepareParameters( GetPlainDriver, InParamValues, InParamTypes,
552  InParamCount, FParamSQLData, FASAConnection.GetConSettings);
553  if ResultSetConcurrency = rcUpdatable then
554  CursorOptions := CUR_OPEN_DECLARE + CUR_UPDATE
555  else
556  CursorOptions := CUR_OPEN_DECLARE + CUR_READONLY;
557  if ResultSetType = rtScrollInsensitive then
558  CursorOptions := CursorOptions + CUR_INSENSITIVE;
559  Cursor := CursorName;
560  GetPlainDriver.db_open(GetDBHandle, PAnsiChar(Cursor), nil, @FStmtNum,
561  FParamSQLData.GetData, FetchSize, 0, CursorOptions);
562  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute,
563  SQL);
564  Closed := false;
565  try
566  if FMoreResults then
567  DescribeCursor( FASAConnection, TZASASQLDA( FSQLData), Cursor, '');
568 
569  LastUpdateCount := -1;
570  Result := GetCachedResultSet( SQL, Self,
571  TZASAResultSet.Create( Self, SQL, FStmtNum, Cursor, FSQLData, nil,
572  FCachedBlob));
573 
574  { Logging SQL Command }
575  DriverManager.LogMessage( lcExecute, GetPlainDriver.GetProtocol, LogSQL);
576  except
577  on E: Exception do
578  begin
579  Self.Close;
580  raise;
581  end;
582  end;
583  end;
584 end;
585 {$HINTS ON}
586 
587 {**
588  Executes an SQL <code>INSERT</code>, <code>UPDATE</code> or
589  <code>DELETE</code> statement. In addition,
590  SQL statements that return nothing, such as SQL DDL statements,
591  can be executed.
592 
593  @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
594  <code>DELETE</code> statement or an SQL statement that returns nothing
595  @return either the row count for <code>INSERT</code>, <code>UPDATE</code>
596  or <code>DELETE</code> statements, or 0 for SQL statements that return nothing
597 }
598 function TZASAPreparedStatement.ExecuteUpdate(const SQL: RawByteString): Integer;
599 begin
600  if ASQL <> SQL then
601  begin
602  Close;
603  ASQL := SQL;
604  ASAPrepare( FASAConnection, FSQLData, FParamSQLData, ASQL, LogSQL, @FStmtNum,
605  FPrepared, FMoreResults);
606  end;
607  Result := ExecuteUpdatePrepared;
608 end;
609 
610 {**
611  Executes the SQL INSERT, UPDATE or DELETE statement
612  in this <code>PreparedStatement</code> object.
613  In addition,
614  SQL statements that return nothing, such as SQL DDL statements,
615  can be executed.
616 
617  @return either the row count for INSERT, UPDATE or DELETE statements;
618  or 0 for SQL statements that return nothing
619 }
620 {$HINTS OFF}
621 function TZASAPreparedStatement.ExecuteUpdatePrepared: Integer;
622 begin
623  Result := -1;
624  with FASAConnection do
625  begin
626 
627  PrepareParameters( GetPlainDriver, InParamValues, InParamTypes,
628  InParamCount, FParamSQLData, FASAConnection.GetConSettings);
629  GetPlainDriver.db_execute_into( GetDBHandle, nil, nil, @FStmtNum,
630  FParamSQLData.GetData, nil);
631  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute, SQL,
632  SQLE_TOO_MANY_RECORDS);
633 
634  Result := GetDBHandle.sqlErrd[2];
635  LastUpdateCount := Result;
636 
637  { Logging SQL Command }
638  DriverManager.LogMessage(lcExecute, GetPlainDriver.GetProtocol, SQL);
639  end;
640 end;
641 {$HINTS ON}
642 
643 
644 { TZASACallableStatement }
645 
646 {**
647  Constructs this object and assignes the main properties.
648  @param Connection a database connection object.
649  @param Handle a connection handle pointer.
650  @param Info a statement parameters.
651 }
652 constructor TZASACallableStatement.Create(Connection: IZConnection;
653  const SQL: string; Info: TStrings);
654 begin
655  inherited Create(Connection, SQL, Info);
656 
657  FASAConnection := Connection as IZASAConnection;
658  FetchSize := BlockSize;
659  ResultSetConcurrency := rcUpdatable;
660  ResultSetType := rtScrollSensitive;
661  FCachedBlob := StrToBoolEx(DefineStatementParameter(Self, 'cashedblob', 'true'));
662  CursorName := AnsiString(RandomString(12));
663  FParamSQLData := TZASASQLDA.Create( FASAConnection.GetPlainDriver,
664  FASAConnection.GetDBHandle, CursorName, ConSettings);
665  FSQLData := TZASASQLDA.Create( FASAConnection.GetPlainDriver,
666  FASAConnection.GetDBHandle, CursorName, ConSettings);
667 end;
668 
669 destructor TZASACallableStatement.Destroy;
670 begin
671  FSQLData := nil;
672  FParamSQLData := nil;
673  inherited;
674 end;
675 
676 procedure TZASACallableStatement.Close;
677 begin
678  if not Closed then
679  begin
680  FASAConnection.GetPlainDriver.db_close(FASAConnection.GetDBHandle, PAnsiChar(CursorName));
681  Closed := false;
682  end;
683  if FStmtNum <> 0 then
684  begin
685  FASAConnection.GetPlainDriver.db_dropstmt( FASAConnection.GetDBHandle, nil,
686  nil, @FStmtNum);
687  FStmtNum := 0;
688  end;
689  inherited;
690 end;
691 
692 procedure TZASACallableStatement.Cancel;
693 begin
694  with FASAConnection do
695  begin
696  GetPlainDriver.db_cancel_request( GetDBHandle);
697  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute, SQL);
698  end;
699 end;
700 
701 function TZASACallableStatement.GetWarnings: EZSQLWarning;
702 begin
703  Result := inherited GetWarnings;
704 end;
705 
706 procedure TZASACallableStatement.ClearWarnings;
707 begin
708  inherited;
709 end;
710 
711 function TZASACallableStatement.GetMoreResults: Boolean;
712 begin
713  Result := FMoreResults;
714  if FMoreResults then
715  begin
716  with FASAConnection do
717  begin
718  GetPlainDriver.db_resume(GetDBHandle, PAnsiChar(CursorName));
719  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute);
720  if GetDBHandle.sqlcode = SQLE_PROCEDURE_COMPLETE then
721  Result := false
722  else
723  DescribeCursor( FASAConnection, TZASASQLDA( FSQLData), CursorName, '');
724  end;
725  end;
726 end;
727 
728 {**
729  Executes an SQL statement that may return multiple results.
730  Under some (uncommon) situations a single SQL statement may return
731  multiple result sets and/or update counts. Normally you can ignore
732  this unless you are (1) executing a stored procedure that you know may
733  return multiple results or (2) you are dynamically executing an
734  unknown SQL string. The methods <code>execute</code>,
735  <code>getMoreResults</code>, <code>getResultSet</code>,
736  and <code>getUpdateCount</code> let you navigate through multiple results.
737 
738  The <code>execute</code> method executes an SQL statement and indicates the
739  form of the first result. You can then use the methods
740  <code>getResultSet</code> or <code>getUpdateCount</code>
741  to retrieve the result, and <code>getMoreResults</code> to
742  move to any subsequent result(s).
743 
744  @param sql any SQL statement
745  @return <code>true</code> if the next result is a <code>ResultSet</code> object;
746  <code>false</code> if it is an update count or there are no more results
747  @see #getResultSet
748  @see #getUpdateCount
749  @see #getMoreResults
750 }
751 
752 function TZASACallableStatement.Execute(const SQL: RawByteString): Boolean;
753 var
754  ProcSQL: RawByteString;
755 begin
756  TrimInParameters;
757  ProcSQL := GetProcedureSQL;
758  if not FPrepared or ( ASQL <> ProcSQL) then
759  begin
760  Close;
761  ASQL := ProcSQL;
762  ASAPrepare( FASAConnection, FSQLData, FParamSQLData, ASQL, LogSQL, @FStmtNum,
763  FPrepared, FMoreResults);
764  end;
765  Result := ExecutePrepared;
766 end;
767 
768 {**
769  Executes any kind of SQL statement.
770  Some prepared statements return multiple results; the <code>execute</code>
771  method handles these complex statements as well as the simpler
772  form of statements handled by the methods <code>executeQuery</code>
773  and <code>executeUpdate</code>.
774  @see Statement#execute
775 }
776 function TZASACallableStatement.ExecutePrepared: Boolean;
777 begin
778  if not FPrepared then
779  Result := Execute(ASQL)
780  else
781  begin
782  if FMoreResults or ( ( FSQLData.GetData.sqld > 0) and
783  ( FSQLData.GetData.sqlVar[0].sqlInd^ and DT_PROCEDURE_OUT = 0)) then
784  begin
785  LastResultSet := ExecuteQueryPrepared;
786  Result := true;
787  end
788  else
789  begin
790  ExecuteUpdatePrepared;
791  Result := false;
792  end;
793  end;
794 end;
795 
796 {**
797  Executes an SQL statement that returns a single <code>ResultSet</code> object.
798  @param sql typically this is a static SQL <code>SELECT</code> statement
799  @return a <code>ResultSet</code> object that contains the data produced by the
800  given query; never <code>null</code>
801 }
802 function TZASACallableStatement.ExecuteQuery(const SQL: RawByteString): IZResultSet;
803 var
804  ProcSQL: RawByteString;
805 begin
806  TrimInParameters;
807  ProcSQL := GetProcedureSQL;
808  if not FPrepared or ( ASQL <> ProcSQL) then
809  begin
810  Close;
811  ASQL := ProcSQL;
812  ASAPrepare( FASAConnection, FSQLData, FParamSQLData, ASQL, LogSQL, @FStmtNum,
813  FPrepared, FMoreResults);
814  end;
815  Result := ExecuteQueryPrepared;
816 end;
817 
818 {**
819  Executes the SQL query in this <code>PreparedStatement</code> object
820  and returns the result set generated by the query.
821 
822  @return a <code>ResultSet</code> object that contains the data produced by the
823  query; never <code>null</code>
824 }
825 {$HINTS OFF}
826 function TZASACallableStatement.ExecuteQueryPrepared: IZResultSet;
827 var
828  Cursor: AnsiString;
829  CursorOptions: SmallInt;
830 begin
831  if not FPrepared then
832  Result := ExecuteQuery(ASQL)
833  else
834  begin
835  with FASAConnection do
836  begin
837  PrepareParameters( GetPlainDriver, InParamValues, InParamTypes,
838  InParamCount, FParamSQLData, FASAConnection.GetConSettings);
839  if ResultSetConcurrency = rcUpdatable then
840  CursorOptions := CUR_OPEN_DECLARE + CUR_UPDATE
841  else
842  CursorOptions := CUR_OPEN_DECLARE + CUR_READONLY;
843  if ResultSetType = rtScrollInsensitive then
844  CursorOptions := CursorOptions + CUR_INSENSITIVE;
845  Cursor := CursorName;
846  GetPlainDriver.db_open(GetDBHandle, PAnsiChar(Cursor), nil, @FStmtNum,
847  FParamSQLData.GetData, FetchSize, 0, CursorOptions);
848  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute, LogSQL);
849  Closed := false;
850  try
851  if FMoreResults then
852  DescribeCursor( FASAConnection, TZASASQLDA( FSQLData), Cursor, LogSQL);
853 
854  LastUpdateCount := -1;
855  Result := GetCachedResultSet( LogSQL, Self,
856  TZASAResultSet.Create( Self, LogSQL, FStmtNum, Cursor, FSQLData, nil,
857  FCachedBlob));
858 
859  { Logging SQL Command }
860  DriverManager.LogMessage( lcExecute, GetPlainDriver.GetProtocol, LogSQL);
861  except
862  on E: Exception do
863  begin
864  Self.Close;
865  raise;
866  end;
867  end;
868  end;
869  end;
870 end;
871 {$HINTS ON}
872 
873 {**
874  Executes an SQL <code>INSERT</code>, <code>UPDATE</code> or
875  <code>DELETE</code> statement. In addition,
876  SQL statements that return nothing, such as SQL DDL statements,
877  can be executed.
878 
879  @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
880  <code>DELETE</code> statement or an SQL statement that returns nothing
881  @return either the row count for <code>INSERT</code>, <code>UPDATE</code>
882  or <code>DELETE</code> statements, or 0 for SQL statements that return nothing
883 }
884 function TZASACallableStatement.ExecuteUpdate(const SQL: RawByteString): Integer;
885 var
886  ProcSQL: RawByteString;
887 begin
888  TrimInParameters;
889  ProcSQL := GetProcedureSQL;
890  if not FPrepared or ( ASQL <> ProcSQL) then
891  begin
892  Close;
893  ASQL := ProcSQL;
894  ASAPrepare( FASAConnection, FSQLData, FParamSQLData, ASQL, LogSQL, @FStmtNum,
895  FPrepared, FMoreResults);
896  end;
897  Result := ExecuteUpdatePrepared;
898 end;
899 
900 {**
901  Executes the SQL INSERT, UPDATE or DELETE statement
902  in this <code>PreparedStatement</code> object.
903  In addition,
904  SQL statements that return nothing, such as SQL DDL statements,
905  can be executed.
906 
907  @return either the row count for INSERT, UPDATE or DELETE statements;
908  or 0 for SQL statements that return nothing
909 }
910 function TZASACallableStatement.ExecuteUpdatePrepared: Integer;
911 begin
912  if not FPrepared then
913  Result := ExecuteUpdate( SQL)
914  else
915  begin
916 // Result := -1;
917  with FASAConnection do
918  begin
919 
920  PrepareParameters( GetPlainDriver, InParamValues, InParamTypes,
921  InParamCount, FParamSQLData, FASAConnection.GetConSettings);
922  GetPlainDriver.db_execute_into( GetDBHandle, nil, nil, @FStmtNum,
923  FParamSQLData.GetData, FSQLData.GetData);
924  ZDbcASAUtils.CheckASAError( GetPlainDriver, GetDBHandle, lcExecute, SQL);
925 
926  Result := GetDBHandle.sqlErrd[2];
927  LastUpdateCount := Result;
928  { Fetch data and fill Output params }
929  FetchOutParams( FSQLData);
930 
931  { Logging SQL Command }
932  DriverManager.LogMessage(lcExecute, GetPlainDriver.GetProtocol, SQL);
933  end;
934  end;
935 end;
936 
937 {**
938  Set output parameters values from IZResultSQLDA.
939  @param Value a IZASASQLDA object.
940 }
941 procedure TZASACallableStatement.FetchOutParams( Value: IZASASQLDA);
942 var
943  I: Integer;
944  L: LongWord;
945  Temp: TZVariant;
946  TempBlob: IZBlob;
947  P: Pointer;
948 begin
949  SetOutParamCount(Value.GetFieldCount);
950  for I := 0 to Value.GetFieldCount-1 do
951  begin
952  if Value.IsNull(I) then
953  DefVarManager.SetNull(Temp)
954  else
955  case Value.GetFieldSqlType(I) of
956  stBoolean:
957  DefVarManager.SetAsBoolean(Temp, Value.GetBoolean(I));
958  stByte:
959  DefVarManager.SetAsInteger(Temp, Value.GetByte(I));
960  stShort:
961  DefVarManager.SetAsInteger(Temp, Value.GetShort(I));
962  stInteger:
963  DefVarManager.SetAsInteger(Temp, Value.GetInt(I));
964  stLong:
965  DefVarManager.SetAsInteger(Temp, Value.GetLong(I));
966  stFloat:
967  DefVarManager.SetAsFloat(Temp, Value.GetFloat(I));
968  stDouble:
969  DefVarManager.SetAsFloat(Temp, Value.GetDouble(I));
970  stBigDecimal:
971  DefVarManager.SetAsFloat(Temp, Value.GetBigDecimal(I));
972  stString:
973  DefVarManager.SetAsString(Temp, ZDbcString(Value.GetString(I)));
974  stUnicodeString:
975  DefVarManager.SetAsUnicodeString(Temp, ZDbcUnicodeString(Value.GetString(I)));
976  stBytes:
977  DefVarManager.SetAsBytes( Temp, Value.GetBytes( I));
978  stDate:
979  DefVarManager.SetAsDateTime(Temp, Value.GetDate(I));
980  stTime:
981  DefVarManager.SetAsDateTime(Temp, Value.GetTime(I));
982  stTimestamp:
983  DefVarManager.SetAsDateTime(Temp, Value.GetTimestamp(I));
984  stAsciiStream,
985  stUnicodeStream,
986  stBinaryStream:
987  begin
988  GetMem( P, PZASABlobStruct( Value.GetData.sqlvar[I].sqlData).untrunc_len);
989  Value.ReadBlobToMem( I, P, L);
990  TempBlob := TZASABlob.CreateWithData( P, L, GetConnection);
991  FreeMem(P);
992  DefVarManager.SetAsInterface( Temp, TempBlob);
993  end;
994  end;
995  OutParamValues[I] := Temp;
996  end;
997 end;
998 
999 {**
1000  Create sql string for calling stored procedure.
1001  @param SelectProc indicate use <b>EXECUTE PROCEDURE</b> or
1002  <b>SELECT</b> staement
1003  @return a Stored Procedure SQL string
1004 }
1005 function TZASACallableStatement.GetProcedureSql: RawByteString;
1006 
1007  function GenerateParamsStr(Count: integer): string;
1008  var
1009  I: integer;
1010  begin
1011  for I := 0 to Count - 1 do
1012  begin
1013  if Result <> '' then
1014  Result := Result + ',';
1015  Result := Result + '?';
1016  end;
1017  end;
1018 
1019 var
1020  InParams: string;
1021 begin
1022  InParams := GenerateParamsStr( High( InParamValues) + 1);
1023  if InParams <> '' then
1024  InParams := '(' + InParams + ')';
1025  Result := ZPlainString('call ' + SQL + InParams);
1026 end;
1027 
1028 end.
1029 
1030