zeoslib  UNKNOWN
 All Files
ZDbcDbLibStatement.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { DBLib Statement common functionality }
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 ZDbcDbLibStatement;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
59  ZCompatibility, ZClasses, ZSysUtils, ZCollections, ZDbcIntfs, ZDbcStatement,
60  ZDbcDbLib, ZPlainDbLibConstants, ZPlainDbLibDriver;
61 
62 type
63  {** Implements Generic DBLib Statement. }
64  TZDBLibStatement = class(TZAbstractStatement)
65  protected
66  FDBLibConnection: IZDBLibConnection;
67  FPlainDriver: IZDBLibPlainDriver;
68  FHandle: PDBPROCESS;
69  FResults: IZCollection;
70  FRetrievedResultSet: IZResultSet;
71  FRetrievedUpdateCount: Integer;
72 
73  procedure InternalExecuteStatement(SQL: RawByteString);
74  procedure FetchResults; virtual;
75 
76  public
77  constructor Create(Connection: IZConnection; Info: TStrings);
78  procedure Close; override;
79 
80  function GetMoreResults: Boolean; override;
81 
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. With emulation}
88  TZDBLibPreparedStatementEmulated = class(TZEmulatedPreparedStatement)
89  private
90  FPlainDriver: IZDBLibPlainDriver;
91  protected
92  function GetEscapeString(Value: string): string;
93  function PrepareAnsiSQLQuery: RawByteString; override;
94  function PrepareAnsiSQLParam(ParamIndex: Integer;
95  const NChar: Boolean): RawByteString; reintroduce;
96  function CreateExecStatement: IZStatement; override;
97  public
98  constructor Create(Connection: IZConnection; SQL: string; Info: TStrings);
99  function GetMetaData: IZResultSetMetaData; override;
100 
101  function ExecuteQueryPrepared: IZResultSet; override;
102  function ExecuteUpdatePrepared: Integer; override;
103  function ExecutePrepared: Boolean; override;
104  end;
105 
106  TZDBLibCallableStatement = class(TZAbstractCallableStatement)
107  private
108  FSQL: string;
109  FDBLibConnection: IZDBLibConnection;
110  FPlainDriver: IZDBLibPlainDriver;
111  FHandle: PDBPROCESS;
112  FLastRowsAffected: Integer;//Workaround for sybase
113  FRetrievedResultSet: IZResultSet;
114  FRetrievedUpdateCount: Integer;
115 
116  procedure FetchResults; virtual;
117  procedure FetchRowCount; virtual;
118 
119  protected
120  procedure SetInParamCount(NewParamCount: Integer); override;
121 
122  public
123  constructor Create(Connection: IZConnection; ProcName: string; Info: TStrings);
124  procedure Close; override;
125 
126  procedure RegisterOutParameter(ParameterIndex: Integer;
127  SqlType: Integer); override;
128  function GetMoreResults: Boolean; override;
129  function ExecuteQueryPrepared: IZResultSet; override;
130  function ExecuteUpdatePrepared: Integer; override;
131  function ExecutePrepared: Boolean; override;
132 
133  end;
134 
135 type
136  {** Interface for storing counter. }
137  IZUpdateCount = interface(IZInterface)
138  ['{03219BB4-E07F-4A50-80CD-291FEA629697}']
139  procedure SetCount(Value: Integer);
140  function GetCount: Integer;
141  end;
142 
143  TZUpdateCount = class(TInterfacedObject, IZUpdateCount)
144  private
145  FCount: Integer;
146  public
147  constructor Create(ACount: Integer);
148  procedure SetCount(Value: Integer); virtual;
149  function GetCount: Integer; virtual;
150  property Count: Integer read GetCount write SetCount;
151  end;
152 
153 implementation
154 
155 uses
156  Types, ZDbcLogging, ZDbcCachedResultSet, ZDbcDbLibUtils, ZDbcDbLibResultSet,
157  ZVariant{$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF};
158 
159 constructor TZUpdateCount.Create(ACount: Integer);
160 begin
161  inherited Create;
162  FCount := ACount;
163 end;
164 
165 procedure TZUpdateCount.SetCount(Value: Integer);
166 begin
167  FCount := Value;
168 end;
169 
170 function TZUpdateCount.GetCount: Integer;
171 begin
172  Result := FCount;
173 end;
174 
175 { TZDBLibStatement }
176 
177 {**
178  Constructs this object and assignes the main properties.
179  @param Connection a database connection object.
180  @param Info a statement parameters.
181 }
182 constructor TZDBLibStatement.Create(Connection: IZConnection; Info: TStrings);
183 begin
184  inherited Create(Connection, Info);
185  Connection.QueryInterface(IZDBLibConnection, FDBLibConnection);
186  if Assigned(FDBLibConnection) then
187  FPLainDriver := FDBLibConnection.GetPlainDriver;
188  FHandle := FDBLibConnection.GetConnectionHandle;
189  ResultSetType := rtScrollInsensitive;
190  FResults := TZCollection.Create;
191 end;
192 
193 procedure TZDBLibStatement.Close;
194 var
195  I: Integer;
196  RS: IZResultSet;
197 begin
198  for i := 0 to FResults.Count -1 do
199  if supports(FResults[i], IZResultSet, RS) then //possible IZUpdateCount
200  RS.Close;
201  FResults.Clear;
202  FRetrievedResultSet := nil;
203  inherited Close;
204 end;
205 
206 {**
207  Executes a Statement.
208  Used internally to execute statements.
209 
210  @param Handle a DBLib connection handle.
211  @sql string containing the statements to execute
212 }
213 procedure TZDBLibStatement.InternalExecuteStatement(SQL: RawByteString);
214 var Ansi: RawByteString;
215 begin
216  if FDBLibConnection.GetProvider = dpMsSQL then
217  //This one is to avoid a bug in dblib interface as it drops a single backslash before line end
218  Ansi := {$IFDEF WITH_UNITANSISTRINGS}AnsiStrings.{$ENDIF}StringReplace(SQL, '\'#13, '\\'#13, [rfReplaceAll])
219  else
220  //This one is to avoid sybase error: Invalid operator for datatype op: is null type: VOID TYPE
221  Ansi := {$IFDEF WITH_UNITANSISTRINGS}AnsiStrings.{$ENDIF}StringReplace(SQL, ' AND NULL IS NULL', '', [rfReplaceAll]);
222 
223  FHandle := FDBLibConnection.GetConnectionHandle;
224  FPlainDriver := FDBLibConnection.GetPlainDriver;
225  if FPlainDriver.dbcancel(FHandle) <> DBSUCCEED then
226  FDBLibConnection.CheckDBLibError(lcExecute, LogSQL);
227 
228  if FPlainDriver.dbcmd(FHandle, PAnsiChar(Ansi)) <> DBSUCCEED then
229  FDBLibConnection.CheckDBLibError(lcExecute, LogSQL);
230 
231  if FPlainDriver.dbsqlexec(FHandle) <> DBSUCCEED then
232  FDBLibConnection.CheckDBLibError(lcExecute, LogSQL);
233  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
234 end;
235 
236 {**
237  Moves to a <code>Statement</code> object's next result. It returns
238  <code>true</code> if this result is a <code>ResultSet</code> object.
239  This method also implicitly closes any current <code>ResultSet</code>
240  object obtained with the method <code>getResultSet</code>.
241 
242  <P>There are no more results when the following is true:
243  <PRE>
244  <code>(!getMoreResults() && (getUpdateCount() == -1)</code>
245  </PRE>
246 
247  @return <code>true</code> if the next result is a <code>ResultSet</code> object;
248  <code>false</code> if it is an update count or there are no more results
249  @see #execute
250 }
251 function TZDBLibStatement.GetMoreResults: Boolean;
252 var
253  ResultSet: IZResultSet;
254  UpdateCount: IZUpdateCount;
255 begin
256  Result := False;
257  FRetrievedResultSet := nil;
258  FRetrievedUpdateCount := -1;
259  if FResults.Count > 0 then
260  begin
261  try
262  Result := FResults.Items[0].QueryInterface(IZResultSet, ResultSet) = 0;
263  if Result then
264  begin
265  FRetrievedResultSet := ResultSet;
266  FRetrievedUpdateCount := 0;
267  end
268  else
269  begin
270  if FResults.Items[0].QueryInterface(IZUpdateCount, UpdateCount) = 0 then
271  FRetrievedUpdateCount := UpdateCount.GetCount;
272  end;
273  FResults.Delete(0);
274  finally
275  ResultSet := nil;
276  UpdateCount := nil;
277  end;
278  end;
279 end;
280 
281 {**
282  Fetches all results and creates a cachedresultset object for each resultset
283  and a ZUpdateCount object for each count value.
284 }
285 procedure TZDBLibStatement.FetchResults;
286 var
287  NativeResultSet: TZDBLibResultSet;
288  CachedResultSet: TZCachedResultSet;
289  RS: IZResultSet;
290  RowsAffected: Integer;
291 begin
292  for RowsAffected := 0 to FResults.Count -1 do
293  if Supports(FResults[RowsAffected], IZResultSet, RS) then
294  RS.Close;
295  FResults.Clear;
296 //Sybase does not seem to return dbCount at all, so a workaround is made
297  RowsAffected := -2;
298  while FPlainDriver.dbresults(FHandle) = DBSUCCEED do
299  begin
300  if FPlainDriver.dbcmdrow(FHandle) = DBSUCCEED then
301  begin
302  NativeResultSet := TZDBLibResultSet.Create(Self, LogSQL);
303  NativeResultSet.SetConcurrency(rcReadOnly);
304  CachedResultSet := TZCachedResultSet.Create(NativeResultSet,
305  LogSQL, TZDBLibCachedResolver.Create(Self, NativeResultSet.GetMetaData), ConSettings);
306  CachedResultSet.SetType(rtScrollInsensitive);//!!!Cached resultsets are allways this
307  CachedResultSet.Last;
308  CachedResultSet.BeforeFirst; //!!!Just to invoke fetchall
309  CachedResultSet.SetConcurrency(GetResultSetConcurrency);
310  FResults.Add(CachedResultSet);
311  end
312  else
313  begin
314  RowsAffected := FPlainDriver.dbCount(FHandle);
315  if RowsAffected > -1 then
316  FResults.Add(TZUpdateCount.Create(RowsAffected));
317  end;
318  FPlainDriver.dbCanQuery(FHandle);
319  end;
320  FDBLibConnection.CheckDBLibError(lcOther, 'FETCHRESULTS');
321 
322  if not FDBLibConnection.FreeTDS then
323  if RowsAffected = -1 then
324  begin
325  FDBLibConnection.InternalExecuteStatement('select @@rowcount');
326  try
327  FPlainDriver.dbresults(FHandle);
328  NativeResultSet := TZDBLibResultSet.Create(Self, 'select @@rowcount');
329  try
330  if NativeResultset.Next then
331  RowsAffected := NativeResultSet.GetInt(1);
332  finally
333  NativeResultSet.Close;
334  end;
335  FResults.Add(TZUpdateCount.Create(RowsAffected));
336  finally
337  FPlainDriver.dbCancel(FHandle);
338  end;
339  FDBLibConnection.CheckDBLibError(lcOther, 'FETCHRESULTS');
340  end;
341 end;
342 
343 {**
344  Executes an SQL statement that returns a single <code>ResultSet</code> object.
345  @param sql typically this is a static SQL <code>SELECT</code> statement
346  @return a <code>ResultSet</code> object that contains the data produced by the
347  given query; never <code>null</code>
348 }
349 function TZDBLibStatement.ExecuteQuery(const SQL: RawByteString): IZResultSet;
350 begin
351  Result := nil;
352  if ASQL <> SQL then
353  ASQL := SQL;
354  try
355  InternalExecuteStatement(ASQL);
356  FetchResults;
357  repeat
358  if GetMoreResults then
359  Result := FRetrievedResultSet
360  else if FRetrievedUpdateCount = -1 then
361  Break;
362  until False;
363  finally
364  FRetrievedResultSet := nil;
365  end;
366 end;
367 
368 {**
369  Executes an SQL <code>INSERT</code>, <code>UPDATE</code> or
370  <code>DELETE</code> statement. In addition,
371  SQL statements that return nothing, such as SQL DDL statements,
372  can be executed.
373 
374  @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
375  <code>DELETE</code> statement or an SQL statement that returns nothing
376  @return either the row count for <code>INSERT</code>, <code>UPDATE</code>
377  or <code>DELETE</code> statements, or 0 for SQL statements that return nothing
378 }
379 function TZDBLibStatement.ExecuteUpdate(const SQL: RawByteString): Integer;
380 begin
381  if ASQL <> SQL then
382  ASQL := SQL;
383  InternalExecuteStatement(ASQL);
384  FetchResults;
385  GetMoreResults;
386  Result := FRetrievedUpdateCount;
387  FRetrievedResultSet := nil;
388 end;
389 
390 {**
391  Executes an SQL statement that may return multiple results.
392  Under some (uncommon) situations a single SQL statement may return
393  multiple result sets and/or update counts. Normally you can ignore
394  this unless you are (1) executing a stored procedure that you know may
395  return multiple results or (2) you are dynamically executing an
396  unknown SQL string. The methods <code>execute</code>,
397  <code>getMoreResults</code>, <code>getResultSet</code>,
398  and <code>getUpdateCount</code> let you navigate through multiple results.
399 
400  The <code>execute</code> method executes an SQL statement and indicates the
401  form of the first result. You can then use the methods
402  <code>getResultSet</code> or <code>getUpdateCount</code>
403  to retrieve the result, and <code>getMoreResults</code> to
404  move to any subsequent result(s).
405 
406  @param sql any SQL statement
407  @return <code>true</code> if the next result is a <code>ResultSet</code> object;
408  <code>false</code> if it is an update count or there are no more results
409 }
410 function TZDBLibStatement.Execute(const SQL: RawByteString): Boolean;
411 begin
412  if ASQL <> SQL then
413  ASQL := SQL;
414  InternalExecuteStatement(ASQL);
415  FetchResults;
416  Result := GetMoreResults;
417  LastResultSet := FRetrievedResultSet;
418  LastUpdateCount := FRetrievedUpdateCount;
419  FRetrievedResultSet := nil;
420 end;
421 
422 { TZDBLibPreparedStatementEmulated }
423 
424 {**
425  Constructs this object and assignes the main properties.
426  @param Connection a database connection object.
427  @param Info a statement parameters.
428  @param Handle a connection handle pointer.
429 }
430 constructor TZDBLibPreparedStatementEmulated.Create(Connection: IZConnection;
431  SQL: string; Info: TStrings);
432 begin
433  inherited Create(Connection, SQL, Info);
434  FPlainDriver := (Connection as IZDBLibConnection).GetPlainDriver;
435  ResultSetType := rtScrollInsensitive;
436  FNeedNCharDetection := True;
437 end;
438 
439 {**
440  Converts an string into escape DBLib format.
441  @param Value a regular string.
442  @return a string in DBLib escape format.
443 }
444 function TZDBLibPreparedStatementEmulated.GetEscapeString(Value: string): string;
445 begin
446  Result := AnsiQuotedStr(Value, '''');
447 end;
448 
449 function TZDBLibPreparedStatementEmulated.PrepareAnsiSQLQuery: RawByteString;
450 var
451  I: Integer;
452  ParamIndex: Integer;
453  Tokens: TStrings;
454 begin
455  ParamIndex := 0;
456  Result := '';
457  Tokens := TokenizeSQLQuery;
458 
459  for I := 0 to Tokens.Count - 1 do
460  begin
461  if Tokens[I] = '?' then
462  begin
463  Result := Result + PrepareAnsiSQLParam(ParamIndex, ((i > 0) and (Tokens[i-1] = 'N')));
464  Inc(ParamIndex);
465  end
466  else
467  Result := Result + ZPlainString(Tokens[I]);
468  end;
469  {$IFNDEF UNICODE}
470  if GetConnection.AutoEncodeStrings then
471  Result := GetConnection.GetDriver.GetTokenizer.GetEscapeString(Result);
472  {$ENDIF}
473 end;
474 {**
475  Prepares an SQL parameter for the query.
476  @param ParameterIndex the first parameter is 1, the second is 2, ...
477  @return a string representation of the parameter.
478 }
479 function TZDBLibPreparedStatementEmulated.PrepareAnsiSQLParam(ParamIndex: Integer;
480  const NChar: Boolean): RawByteString;
481 begin
482  if InParamCount <= ParamIndex then
483  Result := 'NULL'
484  else
485  begin
486  Result := PrepareSQLParameter(InParamValues[ParamIndex],
487  InParamTypes[ParamIndex], ConSettings, FPlainDriver, NChar);
488  end;
489 end;
490 
491 {**
492  Gets the number, types and properties of a <code>ResultSet</code>
493  object's columns.
494  @return the description of a <code>ResultSet</code> object's columns
495 }
496 function TZDBLibPreparedStatementEmulated.GetMetaData: IZResultSetMetaData;
497 begin
498  Result := nil;
499 end;
500 
501 {**
502  Executes the SQL query in this <code>PreparedStatement</code> object
503  and returns the result set generated by the query.
504 
505  @return a <code>ResultSet</code> object that contains the data produced by the
506  query; never <code>null</code>
507 }
508 function TZDBLibPreparedStatementEmulated.ExecutePrepared: Boolean;
509 begin
510  Result := inherited Execute(PrepareAnsiSQLQuery);
511 end;
512 
513 {**
514  Executes the SQL query in this <code>PreparedStatement</code> object
515  and returns the result set generated by the query.
516 
517  @return a <code>ResultSet</code> object that contains the data produced by the
518  query; never <code>null</code>
519 }
520 function TZDBLibPreparedStatementEmulated.ExecuteQueryPrepared: IZResultSet;
521 begin
522  Result := inherited ExecuteQuery(PrepareAnsiSQLQuery);
523 end;
524 
525 {**
526  Executes the SQL INSERT, UPDATE or DELETE statement
527  in this <code>PreparedStatement</code> object.
528  In addition,
529  SQL statements that return nothing, such as SQL DDL statements,
530  can be executed.
531 
532  @return either the row count for INSERT, UPDATE or DELETE statements;
533  or 0 for SQL statements that return nothing
534 }
535 function TZDBLibPreparedStatementEmulated.ExecuteUpdatePrepared: Integer;
536 begin
537  Result := inherited ExecuteUpdate(PrepareAnsiSQLQuery);
538 end;
539 
540 {**
541  Creates a temporary statement which executes queries.
542  @param Info a statement parameters.
543  @return a created statement object.
544 }
545 function TZDBLibPreparedStatementEmulated.CreateExecStatement: IZStatement;
546 begin
547  Result := TZDBLibStatement.Create(Connection, Info);
548 end;
549 
550 constructor TZDBLibCallableStatement.Create(Connection: IZConnection;
551  ProcName: string; Info: TStrings);
552 begin
553  inherited Create(Connection, ProcName, Info);
554  Connection.QueryInterface(IZDBLibConnection, FDBLibConnection);
555  if Assigned(FDBLibConnection) then
556  FPLainDriver := FDBLibConnection.GetPlainDriver;
557  FHandle := FDBLibConnection.GetConnectionHandle;
558  ResultSetType := rtScrollInsensitive;
559 end;
560 
561 procedure TZDBLibCallableStatement.Close;
562 begin
563  FRetrievedResultSet := nil;
564  inherited Close;
565 end;
566 
567 procedure TZDBLibCallableStatement.FetchResults;
568 var
569  NativeResultSet: TZDBLibResultSet;
570  CachedResultSet: TZCachedResultSet;
571 begin
572 //Sybase does not seem to return dbCount at all, so a workaround is made
573  FLastRowsAffected := -2;
574  while FPlainDriver.dbresults(FHandle) = DBSUCCEED do
575  begin
576  if FPlainDriver.dbcmdrow(FHandle) = DBSUCCEED then
577  begin
578  NativeResultSet := TZDBLibResultSet.Create(Self, FSQL);
579  NativeResultSet.SetConcurrency(rcReadOnly);
580  CachedResultSet := TZCachedResultSet.Create(NativeResultSet, FSQL,
581  TZDBLibCachedResolver.Create(Self, NativeResultSet.GetMetaData), ConSettings);
582  CachedResultSet.SetType(rtScrollInsensitive);//!!!Cached resultsets are allways this
583  CachedResultSet.Last;
584  CachedResultSet.BeforeFirst; //!!!Just to invoke fetchall
585  CachedResultSet.SetConcurrency(GetResultSetConcurrency);
586  FResultSets.Add(CachedResultSet);
587  end
588  else
589  begin
590  FLastRowsAffected := FPlainDriver.dbCount(FHandle);
591  if FLastRowsAffected > -1 then
592  FResultSets.Add(TZUpdateCount.Create(FLastRowsAffected));
593  end;
594  end;
595  FDBLibConnection.CheckDBLibError(lcOther, 'FETCHRESULTS');
596 end;
597 
598 procedure TZDBLibCallableStatement.FetchRowCount;
599 var
600  NativeResultSet: TZDBLibResultSet;
601 begin
602 //Sybase does not seem to return dbCount at all, so a workaround is made
603  if FLastRowsAffected = -1 then
604  begin
605  FDBLibConnection.InternalExecuteStatement('select @@rowcount');
606  try
607  FPlainDriver.dbresults(FHandle);
608  NativeResultSet := TZDBLibResultSet.Create(Self, 'select @@rowcount');
609  try
610  if NativeResultset.Next then
611  FLastRowsAffected := NativeResultSet.GetInt(1);
612  finally
613  NativeResultset.Close;
614  end;
615  FResultSets.Add(TZUpdateCount.Create(FLastRowsAffected));
616  finally
617  FPlainDriver.dbCancel(FHandle);
618  end;
619  FDBLibConnection.CheckDBLibError(lcOther, 'FETCHRESULTS');
620  end;
621 end;
622 
623 {**
624  Moves to a <code>Statement</code> object's next result. It returns
625  <code>true</code> if this result is a <code>ResultSet</code> object.
626  This method also implicitly closes any current <code>ResultSet</code>
627  object obtained with the method <code>getResultSet</code>.
628 
629  <P>There are no more results when the following is true:
630  <PRE>
631  <code>(!getMoreResults() && (getUpdateCount() == -1)</code>
632  </PRE>
633 
634  @return <code>true</code> if the next result is a <code>ResultSet</code> object;
635  <code>false</code> if it is an update count or there are no more results
636  @see #execute
637 }
638 function TZDBLibCallableStatement.GetMoreResults: Boolean;
639 var
640  ResultSet: IZResultSet;
641  UpdateCount: IZUpdateCount;
642 begin
643  Result := False;
644  FRetrievedResultSet := nil;
645  FRetrievedUpdateCount := -1;
646  if FResultSets.Count > 0 then
647  begin
648  try
649  Result := Supports(FResultSets[0], IZResultSet, ResultSet);
650  if Result then
651  begin
652  FRetrievedResultSet := ResultSet;
653  FRetrievedUpdateCount := 0;
654  end
655  else
656  if Supports(FResultSets[0], IZUpdateCount, UpdateCount) then
657  FRetrievedUpdateCount := UpdateCount.GetCount;
658  FResultSets.Delete(0);
659  finally
660  ResultSet := nil;
661  UpdateCount := nil;
662  end;
663  end;
664 end;
665 
666 function TZDBLibCallableStatement.ExecuteQueryPrepared: IZResultSet;
667 begin
668  if not ExecutePrepared then
669  while not GetMoreResults and (FRetrievedUpdateCount <> -1) do;
670  Result := FRetrievedResultSet;
671  FRetrievedResultSet := nil;
672 end;
673 
674 function TZDBLibCallableStatement.ExecuteUpdatePrepared: Integer;
675 begin
676  if ExecutePrepared then
677  while GetMoreResults and (FRetrievedUpdateCount = -1) do;
678  Result := FRetrievedUpdateCount;
679  FRetrievedResultSet := nil;
680 end;
681 
682 procedure TZDBLibCallableStatement.RegisterOutParameter(ParameterIndex: Integer;
683  SqlType: Integer);
684 begin
685  SetOutParamCount(ParameterIndex);
686  OutParamTypes[ParameterIndex - 1] := TZSqlType(SqlType);
687 
688  //Count inparams must equal count outparams to correct set paramters
689  if InParamCount < ParameterIndex then
690  SetInParamCount(ParameterIndex);
691 end;
692 
693 function TZDBLibCallableStatement.ExecutePrepared: Boolean;
694 var
695  S: RawByteString;
696  I, ParamIndex, DatLen: Integer;
697  RetParam: Byte;
698  DatBoolean: Boolean;
699  DatByte: Byte;
700  DatShort: SmallInt;
701  DatInteger: Integer;
702  DatFloat: Single;
703  DatDouble: Double;
704  DatString: RawByteString;
705  DatMoney: Currency;
706  DatDBDATETIME: DBDATETIME;
707  DatBytes: TByteDynArray;
708  Temp: TZVariant;
709  ParamType: TZSQLType;
710  TempBlob: IZBlob;
711 begin
712  S := {$IFDEF WITH_UNITANSISTRINGS}AnsiStrings.{$ENDIF}Trim(ASql);
713  if FPLainDriver.dbRPCInit(FHandle, Pointer(S), 0) <> DBSUCCEED then
714  FDBLibConnection.CheckDBLibError(lcOther, 'EXECUTEPREPARED:dbRPCInit');
715 
716  for I := 1 to InParamCount - 1 do//The 0 parameter is the return value
717  begin
718  RetParam := 0;
719  if OutParamTypes[I] <> stUnknown then
720  RetParam := DBRPCRETURN;
721 
722  ParamType := InParamTypes[I];
723  if ParamType = stUnknown then
724  ParamType := OutParamTypes[I];
725 
726  if DefVarManager.IsNull(InParamValues[I]) and (InParamTypes[I] <> stUnknown) then
727  begin
728  if FDBLibConnection.FreeTDS then
729  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
730  ConvertSqlTypeToFreeTDSType(InParamTypes[I]), -1, 0, nil)
731  else
732  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
733  ConvertSqlTypeToDBLibType(InParamTypes[I]), -1, 0, nil)
734  end
735  else
736  begin
737  case ParamType of
738  stBoolean:
739  begin
740  DatBoolean := SoftVarManager.GetAsBoolean(InParamValues[I]);
741  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
742  FPlainDriver.GetVariables.datatypes[Z_SQLINT1], -1, -1, @DatBoolean);
743  end;
744  stByte:
745  begin
746  DatByte := Byte(SoftVarManager.GetAsInteger(InParamValues[I]));
747  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
748  FPlainDriver.GetVariables.datatypes[Z_SQLINT1], -1, -1, @DatByte);
749  end;
750  stShort:
751  begin
752  DatShort := SmallInt(SoftVarManager.GetAsInteger(InParamValues[I]));
753  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
754  FPlainDriver.GetVariables.datatypes[Z_SQLINT2], -1, -1, @DatShort);
755  end;
756  stInteger, stLong:
757  begin
758  DatInteger := Integer(SoftVarManager.GetAsInteger(InParamValues[I]));
759  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
760  FPlainDriver.GetVariables.datatypes[Z_SQLINT4], -1, -1, @DatInteger);
761  end;
762  stFloat:
763  begin
764  DatFloat := SoftVarManager.GetAsFloat(InParamValues[I]);
765  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
766  FPlainDriver.GetVariables.datatypes[Z_SQLFLT4], -1, -1, @DatFloat);
767  end;
768  stDouble, stBigDecimal:
769  begin
770  DatDouble := SoftVarManager.GetAsFloat(InParamValues[I]);
771  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
772  FPlainDriver.GetVariables.datatypes[Z_SQLFLT8], -1, -1, @DatDouble);
773  end;
774  stString:
775  begin
776  DatString := ZPlainString(SoftVarManager.GetAsString(InParamValues[I]));
777  if DatString = ''then
778  DatLen := 1
779  else
780  DatLen := Length(DatString);
781  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
782  FPlainDriver.GetVariables.datatypes[Z_SQLCHAR], MaxInt, DatLen, PAnsiChar(DatString));
783  end;
784  stUnicodeString:
785  begin
786  DatString := UTF8Encode(SoftVarManager.GetAsUnicodeString(InParamValues[I]));
787  if DatString = '' then
788  DatLen := 1
789  else
790  DatLen := Length(DatString);
791  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
792  FPlainDriver.GetVariables.datatypes[Z_SQLCHAR], MaxInt, DatLen, PAnsiChar(DatString));
793  end;
794  stDate:
795  begin
796  DatString := AnsiString(FormatDateTime('yyyymmdd',
797  SoftVarManager.GetAsDateTime(InParamValues[I])));
798  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
799  FPlainDriver.GetVariables.datatypes[Z_SQLCHAR], MaxInt, Length(DatString), PAnsiChar(DatString));
800  end;
801  stTime:
802  begin
803  DatString := AnsiString(FormatDateTime('hh":"mm":"ss":"zzz',
804  SoftVarManager.GetAsDateTime(InParamValues[I])));
805  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
806  FPlainDriver.GetVariables.datatypes[Z_SQLCHAR], MaxInt, Length(DatString), PAnsiChar(DatString));
807  end;
808  stTimeStamp:
809  begin
810  DatString := AnsiString(FormatDateTime('yyyymmdd hh":"mm":"ss":"zzz',
811  SoftVarManager.GetAsDateTime(InParamValues[I])));
812  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
813  FPlainDriver.GetVariables.datatypes[Z_SQLCHAR], MaxInt, Length(DatString), PAnsiChar(DatString));
814  end;
815  stAsciiStream, stUnicodeStream, stBinaryStream:
816  begin
817  TempBlob := SoftVarManager.GetAsInterface(InParamValues[I]) as IZBlob;
818  DatString := TempBlob.GetString;
819  if DatString = '' then
820  DatLen := 1
821  else
822  DatLen := Length(DatString);
823  if ParamType = stBinaryStream then
824  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
825  FPlainDriver.GetVariables.datatypes[Z_SQLBINARY], MaxInt, Length(DatString), PAnsiChar(DatString))
826  else
827  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
828  FPlainDriver.GetVariables.datatypes[Z_SQLTEXT], FPlainDriver.GetVariables.dboptions[Z_TEXTSIZE], DatLen, PAnsiChar(DatString));
829  end;
830  stBytes:
831  begin
832  DatString := AnsiString(SoftVarManager.GetAsString(InParamValues[I]));
833  FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
834  FPlainDriver.GetVariables.datatypes[Z_SQLBINARY], MaxInt, Length(DatString), PAnsiChar(DatString));
835  end;
836  else
837  FPlainDriver.dbRpcParam(FHandle, nil, 0, FPlainDriver.GetVariables.datatypes[Z_SQLCHAR], 0, 0, nil);
838  end;
839  end;
840  end;
841 
842  if FPLainDriver.dbRpcExec(FHandle) <> DBSUCCEED then
843  FDBLibConnection.CheckDBLibError(lcOther, 'EXECUTEPREPARED:dbRPCExec');
844  FetchResults;
845  Result := GetMoreResults;
846 
847  if FPLainDriver.dbHasRetStat(FHandle) then
848  DefVarManager.SetAsInteger(Temp, FPlainDriver.dbRetStatus(FHandle))
849  else
850  Temp := NullVariant;
851  OutParamValues[0] := Temp; //set function RETURN_VALUE
852 
853  ParamIndex := 1;
854  for I := 1 to OutParamCount - 1 do
855  begin
856  if OutParamTypes[I] = stUnknown then
857  Continue;
858  if FPlainDriver.dbRetData(FHandle, ParamIndex) = nil then
859  Temp := NullVariant
860  else
861  begin
862  if FDBLibConnection.FreeTDS then
863  case FPLainDriver.dbRetType(FHandle, ParamIndex) of
864  TDSSQLCHAR, TDSSQLBINARY:
865  begin
866  DatLen := FPLainDriver.dbRetLen(FHandle, ParamIndex);
867  SetLength(DatBytes, DatLen);
868  Move(PAnsiChar(FPLainDriver.dbRetData(FHandle, ParamIndex))^,
869  DatBytes[0], Length(DatBytes));
870  DefVarManager.SetAsString(Temp, String(BytesToStr(DatBytes)));
871  end;
872  TDSSQLINT1:
873  DefVarManager.SetAsInteger(Temp,
874  PByte(FPlainDriver.dbRetData(FHandle, ParamIndex))^);
875  TDSSQLINT2:
876  DefVarManager.SetAsInteger(Temp,
877  PSmallInt(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
878  TDSSQLINT4:
879  DefVarManager.SetAsInteger(Temp,
880  PInteger(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
881  TDSSQLFLT4:
882  DefVarManager.SetAsFloat(Temp,
883  PSingle(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
884  TDSSQLFLT8:
885  DefVarManager.SetAsFloat(Temp,
886  PDouble(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
887  TDSSQLMONEY4:
888  begin
889  FPlainDriver.dbConvert(FHandle, TDSSQLMONEY4,
890  FPlainDriver.dbRetData(FHandle, ParamIndex), 4, TDSSQLMONEY,
891  @DatMoney, 8);
892  DefVarManager.SetAsFloat(Temp, DatMoney);
893  end;
894  TDSSQLMONEY:
895  DefVarManager.SetAsFloat(Temp,
896  PCurrency(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
897  TDSSQLDECIMAL:
898  begin
899  FPLainDriver.dbConvert(FHandle, TDSSQLDECIMAL,
900  FPLainDriver.dbRetData(FHandle, ParamIndex),
901  FPLainDriver.dbRetLen(FHandle, ParamIndex),
902  TDSSQLFLT8, @DatDouble, 8);
903  DefVarManager.SetAsFloat(Temp, DatDouble);
904  end;
905  TDSSQLNUMERIC:
906  begin
907  FPLainDriver.dbConvert(FHandle, TDSSQLNUMERIC,
908  FPLainDriver.dbRetData(FHandle, ParamIndex),
909  FPLainDriver.dbRetLen(FHandle, ParamIndex),
910  TDSSQLFLT8, @DatDouble, 8);
911  DefVarManager.SetAsFloat(Temp, DatDouble);
912  end;
913  TDSSQLDATETIM4:
914  begin
915  FPLainDriver.dbConvert(FHandle, TDSSQLDATETIM4,
916  FPLainDriver.dbRetData(FHandle, ParamIndex), 4,
917  TDSSQLDATETIME, @DatDBDATETIME, 8);
918  DefVarManager.SetAsDateTime(Temp,
919  DatDBDATETIME.dtdays + 2 + (DatDBDATETIME.dttime / 25920000));
920  end;
921  TDSSQLDATETIME:
922  begin
923  DatDBDATETIME := PDBDATETIME(
924  FPLainDriver.dbRetData(FHandle, ParamIndex))^;
925  DefVarManager.SetAsDateTime(Temp,
926  DatDBDATETIME.dtdays + 2 + (DatDBDATETIME.dttime / 25920000));
927  end;
928  else
929  Temp := NullVariant;
930  end
931  else
932  case FPLainDriver.dbRetType(FHandle, ParamIndex) of
933  DBLIBSQLCHAR, DBLIBSQLBINARY:
934  begin
935  DatLen := FPLainDriver.dbRetLen(FHandle, ParamIndex);
936  SetLength(DatBytes, DatLen);
937  Move(PAnsiChar(FPLainDriver.dbRetData(FHandle, ParamIndex))^,
938  DatBytes[0], Length(DatBytes));
939  DefVarManager.SetAsString(Temp, String(BytesToStr(DatBytes)));
940  end;
941  DBLIBSQLINT1:
942  DefVarManager.SetAsInteger(Temp,
943  PByte(FPlainDriver.dbRetData(FHandle, ParamIndex))^);
944  DBLIBSQLINT2:
945  DefVarManager.SetAsInteger(Temp,
946  PSmallInt(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
947  DBLIBSQLINT4:
948  DefVarManager.SetAsInteger(Temp,
949  PInteger(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
950  DBLIBSQLFLT4:
951  DefVarManager.SetAsFloat(Temp,
952  PSingle(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
953  DBLIBSQLFLT8:
954  DefVarManager.SetAsFloat(Temp,
955  PDouble(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
956  DBLIBSQLMONEY4:
957  begin
958  FPlainDriver.dbConvert(FHandle, DBLIBSQLMONEY4,
959  FPlainDriver.dbRetData(FHandle, ParamIndex), 4, DBLIBSQLMONEY,
960  @DatMoney, 8);
961  DefVarManager.SetAsFloat(Temp, DatMoney);
962  end;
963  DBLIBSQLMONEY:
964  DefVarManager.SetAsFloat(Temp,
965  PCurrency(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
966  DBLIBSQLDECIMAL:
967  begin
968  FPLainDriver.dbConvert(FHandle, DBLIBSQLDECIMAL,
969  FPLainDriver.dbRetData(FHandle, ParamIndex),
970  FPLainDriver.dbRetLen(FHandle, ParamIndex),
971  DBLIBSQLFLT8, @DatDouble, 8);
972  DefVarManager.SetAsFloat(Temp, DatDouble);
973  end;
974  DBLIBSQLNUMERIC:
975  begin
976  FPLainDriver.dbConvert(FHandle, DBLIBSQLNUMERIC,
977  FPLainDriver.dbRetData(FHandle, ParamIndex),
978  FPLainDriver.dbRetLen(FHandle, ParamIndex),
979  DBLIBSQLFLT8, @DatDouble, 8);
980  DefVarManager.SetAsFloat(Temp, DatDouble);
981  end;
982  DBLIBSQLDATETIM4:
983  begin
984  FPLainDriver.dbConvert(FHandle, DBLIBSQLDATETIM4,
985  FPLainDriver.dbRetData(FHandle, ParamIndex), 4,
986  DBLIBSQLDATETIME, @DatDBDATETIME, 8);
987  DefVarManager.SetAsDateTime(Temp,
988  DatDBDATETIME.dtdays + 2 + (DatDBDATETIME.dttime / 25920000));
989  end;
990  DBLIBSQLDATETIME:
991  begin
992  DatDBDATETIME := PDBDATETIME(
993  FPLainDriver.dbRetData(FHandle, ParamIndex))^;
994  DefVarManager.SetAsDateTime(Temp,
995  DatDBDATETIME.dtdays + 2 + (DatDBDATETIME.dttime / 25920000));
996  end;
997  else
998  Temp := NullVariant;
999  end;
1000  end;
1001  OutParamValues[I] := Temp;
1002  Inc(ParamIndex);
1003  end;
1004 
1005 //Workaround for sybase. the dbCount does not work, so a select @@rowcount is
1006 //made but this cleared the returned output parameters, so this is moved here
1007 //after reading the output parameters
1008  FetchRowCount;
1009 
1010  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol,
1011  Format('EXEC %s', [SQL]));
1012 end;
1013 
1014 procedure TZDBLibCallableStatement.SetInParamCount(NewParamCount: Integer);
1015 begin
1016  inherited SetInParamCount(NewParamCount);
1017 
1018  if OutParamCount < NewParamCount then
1019  SetOutParamCount(NewParamCount);
1020 end;
1021 
1022 end.
1023 
1024