1 {*********************************************************}
3 { Zeos Database Objects }
4 { DBLib Statement common functionality }
6 { Originally written by Janos Fegyverneki }
8 {*********************************************************}
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
13 { License Agreement: }
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. }
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. }
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) }
46 { http://www.sourceforge.net/projects/zeoslib. }
49 { Zeos Development Group. }
50 {********************************************************@}
52 unit ZDbcDbLibStatement;
58 uses Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
59 ZCompatibility, ZClasses, ZSysUtils, ZCollections, ZDbcIntfs, ZDbcStatement,
60 ZDbcDbLib, ZPlainDbLibConstants, ZPlainDbLibDriver;
63 {** Implements Generic DBLib Statement. }
64 TZDBLibStatement = class(TZAbstractStatement)
66 FDBLibConnection: IZDBLibConnection;
67 FPlainDriver: IZDBLibPlainDriver;
69 FResults: IZCollection;
70 FRetrievedResultSet: IZResultSet;
71 FRetrievedUpdateCount: Integer;
73 procedure InternalExecuteStatement(SQL: RawByteString);
74 procedure FetchResults; virtual;
77 constructor Create(Connection: IZConnection; Info: TStrings);
78 procedure Close; override;
80 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;
87 {** Implements Prepared SQL Statement. With emulation}
88 TZDBLibPreparedStatementEmulated = class(TZEmulatedPreparedStatement)
90 FPlainDriver: IZDBLibPlainDriver;
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;
98 constructor Create(Connection: IZConnection; SQL: string; Info: TStrings);
99 function GetMetaData: IZResultSetMetaData; override;
101 function ExecuteQueryPrepared: IZResultSet; override;
102 function ExecuteUpdatePrepared: Integer; override;
103 function ExecutePrepared: Boolean; override;
106 TZDBLibCallableStatement = class(TZAbstractCallableStatement)
109 FDBLibConnection: IZDBLibConnection;
110 FPlainDriver: IZDBLibPlainDriver;
112 FLastRowsAffected: Integer;//Workaround for sybase
113 FRetrievedResultSet: IZResultSet;
114 FRetrievedUpdateCount: Integer;
116 procedure FetchResults; virtual;
117 procedure FetchRowCount; virtual;
120 procedure SetInParamCount(NewParamCount: Integer); override;
123 constructor Create(Connection: IZConnection; ProcName: string; Info: TStrings);
124 procedure Close; override;
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;
136 {** Interface for storing counter. }
137 IZUpdateCount = interface(IZInterface)
138 ['{03219BB4-E07F-4A50-80CD-291FEA629697}']
139 procedure SetCount(Value: Integer);
140 function GetCount: Integer;
143 TZUpdateCount = class(TInterfacedObject, IZUpdateCount)
147 constructor Create(ACount: Integer);
148 procedure SetCount(Value: Integer); virtual;
149 function GetCount: Integer; virtual;
150 property Count: Integer read GetCount write SetCount;
156 Types, ZDbcLogging, ZDbcCachedResultSet, ZDbcDbLibUtils, ZDbcDbLibResultSet,
157 ZVariant{$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF};
159 constructor TZUpdateCount.Create(ACount: Integer);
165 procedure TZUpdateCount.SetCount(Value: Integer);
170 function TZUpdateCount.GetCount: Integer;
178 Constructs this object and assignes the main properties.
179 @param Connection a database connection object.
180 @param Info a statement parameters.
182 constructor TZDBLibStatement.Create(Connection: IZConnection; Info: TStrings);
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;
193 procedure TZDBLibStatement.Close;
198 for i := 0 to FResults.Count -1 do
199 if supports(FResults[i], IZResultSet, RS) then //possible IZUpdateCount
202 FRetrievedResultSet := nil;
207 Executes a Statement.
208 Used internally to execute statements.
210 @param Handle a DBLib connection handle.
211 @sql string containing the statements to execute
213 procedure TZDBLibStatement.InternalExecuteStatement(SQL: RawByteString);
214 var Ansi: RawByteString;
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])
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]);
223 FHandle := FDBLibConnection.GetConnectionHandle;
224 FPlainDriver := FDBLibConnection.GetPlainDriver;
225 if FPlainDriver.dbcancel(FHandle) <> DBSUCCEED then
226 FDBLibConnection.CheckDBLibError(lcExecute, LogSQL);
228 if FPlainDriver.dbcmd(FHandle, PAnsiChar(Ansi)) <> DBSUCCEED then
229 FDBLibConnection.CheckDBLibError(lcExecute, LogSQL);
231 if FPlainDriver.dbsqlexec(FHandle) <> DBSUCCEED then
232 FDBLibConnection.CheckDBLibError(lcExecute, LogSQL);
233 DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
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>.
242 <P>There are no more results when the following is true:
244 <code>(!getMoreResults() && (getUpdateCount() == -1)</code>
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
251 function TZDBLibStatement.GetMoreResults: Boolean;
253 ResultSet: IZResultSet;
254 UpdateCount: IZUpdateCount;
257 FRetrievedResultSet := nil;
258 FRetrievedUpdateCount := -1;
259 if FResults.Count > 0 then
262 Result := FResults.Items[0].QueryInterface(IZResultSet, ResultSet) = 0;
265 FRetrievedResultSet := ResultSet;
266 FRetrievedUpdateCount := 0;
270 if FResults.Items[0].QueryInterface(IZUpdateCount, UpdateCount) = 0 then
271 FRetrievedUpdateCount := UpdateCount.GetCount;
282 Fetches all results and creates a cachedresultset object for each resultset
283 and a ZUpdateCount object for each count value.
285 procedure TZDBLibStatement.FetchResults;
287 NativeResultSet: TZDBLibResultSet;
288 CachedResultSet: TZCachedResultSet;
290 RowsAffected: Integer;
292 for RowsAffected := 0 to FResults.Count -1 do
293 if Supports(FResults[RowsAffected], IZResultSet, RS) then
296 //Sybase does not seem to return dbCount at all, so a workaround is made
298 while FPlainDriver.dbresults(FHandle) = DBSUCCEED do
300 if FPlainDriver.dbcmdrow(FHandle) = DBSUCCEED then
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);
314 RowsAffected := FPlainDriver.dbCount(FHandle);
315 if RowsAffected > -1 then
316 FResults.Add(TZUpdateCount.Create(RowsAffected));
318 FPlainDriver.dbCanQuery(FHandle);
320 FDBLibConnection.CheckDBLibError(lcOther, 'FETCHRESULTS');
322 if not FDBLibConnection.FreeTDS then
323 if RowsAffected = -1 then
325 FDBLibConnection.InternalExecuteStatement('select @@rowcount');
327 FPlainDriver.dbresults(FHandle);
328 NativeResultSet := TZDBLibResultSet.Create(Self, 'select @@rowcount');
330 if NativeResultset.Next then
331 RowsAffected := NativeResultSet.GetInt(1);
333 NativeResultSet.Close;
335 FResults.Add(TZUpdateCount.Create(RowsAffected));
337 FPlainDriver.dbCancel(FHandle);
339 FDBLibConnection.CheckDBLibError(lcOther, 'FETCHRESULTS');
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>
349 function TZDBLibStatement.ExecuteQuery(const SQL: RawByteString): IZResultSet;
355 InternalExecuteStatement(ASQL);
358 if GetMoreResults then
359 Result := FRetrievedResultSet
360 else if FRetrievedUpdateCount = -1 then
364 FRetrievedResultSet := nil;
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,
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
379 function TZDBLibStatement.ExecuteUpdate(const SQL: RawByteString): Integer;
383 InternalExecuteStatement(ASQL);
386 Result := FRetrievedUpdateCount;
387 FRetrievedResultSet := nil;
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.
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).
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
410 function TZDBLibStatement.Execute(const SQL: RawByteString): Boolean;
414 InternalExecuteStatement(ASQL);
416 Result := GetMoreResults;
417 LastResultSet := FRetrievedResultSet;
418 LastUpdateCount := FRetrievedUpdateCount;
419 FRetrievedResultSet := nil;
422 { TZDBLibPreparedStatementEmulated }
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.
430 constructor TZDBLibPreparedStatementEmulated.Create(Connection: IZConnection;
431 SQL: string; Info: TStrings);
433 inherited Create(Connection, SQL, Info);
434 FPlainDriver := (Connection as IZDBLibConnection).GetPlainDriver;
435 ResultSetType := rtScrollInsensitive;
436 FNeedNCharDetection := True;
440 Converts an string into escape DBLib format.
441 @param Value a regular string.
442 @return a string in DBLib escape format.
444 function TZDBLibPreparedStatementEmulated.GetEscapeString(Value: string): string;
446 Result := AnsiQuotedStr(Value, '''');
449 function TZDBLibPreparedStatementEmulated.PrepareAnsiSQLQuery: RawByteString;
457 Tokens := TokenizeSQLQuery;
459 for I := 0 to Tokens.Count - 1 do
461 if Tokens[I] = '?' then
463 Result := Result + PrepareAnsiSQLParam(ParamIndex, ((i > 0) and (Tokens[i-1] = 'N')));
467 Result := Result + ZPlainString(Tokens[I]);
470 if GetConnection.AutoEncodeStrings then
471 Result := GetConnection.GetDriver.GetTokenizer.GetEscapeString(Result);
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.
479 function TZDBLibPreparedStatementEmulated.PrepareAnsiSQLParam(ParamIndex: Integer;
480 const NChar: Boolean): RawByteString;
482 if InParamCount <= ParamIndex then
486 Result := PrepareSQLParameter(InParamValues[ParamIndex],
487 InParamTypes[ParamIndex], ConSettings, FPlainDriver, NChar);
492 Gets the number, types and properties of a <code>ResultSet</code>
494 @return the description of a <code>ResultSet</code> object's columns
496 function TZDBLibPreparedStatementEmulated.GetMetaData: IZResultSetMetaData;
502 Executes the SQL query in this <code>PreparedStatement</code> object
503 and returns the result set generated by the query.
505 @return a <code>ResultSet</code> object that contains the data produced by the
506 query; never <code>null</code>
508 function TZDBLibPreparedStatementEmulated.ExecutePrepared: Boolean;
510 Result := inherited Execute(PrepareAnsiSQLQuery);
514 Executes the SQL query in this <code>PreparedStatement</code> object
515 and returns the result set generated by the query.
517 @return a <code>ResultSet</code> object that contains the data produced by the
518 query; never <code>null</code>
520 function TZDBLibPreparedStatementEmulated.ExecuteQueryPrepared: IZResultSet;
522 Result := inherited ExecuteQuery(PrepareAnsiSQLQuery);
526 Executes the SQL INSERT, UPDATE or DELETE statement
527 in this <code>PreparedStatement</code> object.
529 SQL statements that return nothing, such as SQL DDL statements,
532 @return either the row count for INSERT, UPDATE or DELETE statements;
533 or 0 for SQL statements that return nothing
535 function TZDBLibPreparedStatementEmulated.ExecuteUpdatePrepared: Integer;
537 Result := inherited ExecuteUpdate(PrepareAnsiSQLQuery);
541 Creates a temporary statement which executes queries.
542 @param Info a statement parameters.
543 @return a created statement object.
545 function TZDBLibPreparedStatementEmulated.CreateExecStatement: IZStatement;
547 Result := TZDBLibStatement.Create(Connection, Info);
550 constructor TZDBLibCallableStatement.Create(Connection: IZConnection;
551 ProcName: string; Info: TStrings);
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;
561 procedure TZDBLibCallableStatement.Close;
563 FRetrievedResultSet := nil;
567 procedure TZDBLibCallableStatement.FetchResults;
569 NativeResultSet: TZDBLibResultSet;
570 CachedResultSet: TZCachedResultSet;
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
576 if FPlainDriver.dbcmdrow(FHandle) = DBSUCCEED then
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);
590 FLastRowsAffected := FPlainDriver.dbCount(FHandle);
591 if FLastRowsAffected > -1 then
592 FResultSets.Add(TZUpdateCount.Create(FLastRowsAffected));
595 FDBLibConnection.CheckDBLibError(lcOther, 'FETCHRESULTS');
598 procedure TZDBLibCallableStatement.FetchRowCount;
600 NativeResultSet: TZDBLibResultSet;
602 //Sybase does not seem to return dbCount at all, so a workaround is made
603 if FLastRowsAffected = -1 then
605 FDBLibConnection.InternalExecuteStatement('select @@rowcount');
607 FPlainDriver.dbresults(FHandle);
608 NativeResultSet := TZDBLibResultSet.Create(Self, 'select @@rowcount');
610 if NativeResultset.Next then
611 FLastRowsAffected := NativeResultSet.GetInt(1);
613 NativeResultset.Close;
615 FResultSets.Add(TZUpdateCount.Create(FLastRowsAffected));
617 FPlainDriver.dbCancel(FHandle);
619 FDBLibConnection.CheckDBLibError(lcOther, 'FETCHRESULTS');
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>.
629 <P>There are no more results when the following is true:
631 <code>(!getMoreResults() && (getUpdateCount() == -1)</code>
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
638 function TZDBLibCallableStatement.GetMoreResults: Boolean;
640 ResultSet: IZResultSet;
641 UpdateCount: IZUpdateCount;
644 FRetrievedResultSet := nil;
645 FRetrievedUpdateCount := -1;
646 if FResultSets.Count > 0 then
649 Result := Supports(FResultSets[0], IZResultSet, ResultSet);
652 FRetrievedResultSet := ResultSet;
653 FRetrievedUpdateCount := 0;
656 if Supports(FResultSets[0], IZUpdateCount, UpdateCount) then
657 FRetrievedUpdateCount := UpdateCount.GetCount;
658 FResultSets.Delete(0);
666 function TZDBLibCallableStatement.ExecuteQueryPrepared: IZResultSet;
668 if not ExecutePrepared then
669 while not GetMoreResults and (FRetrievedUpdateCount <> -1) do;
670 Result := FRetrievedResultSet;
671 FRetrievedResultSet := nil;
674 function TZDBLibCallableStatement.ExecuteUpdatePrepared: Integer;
676 if ExecutePrepared then
677 while GetMoreResults and (FRetrievedUpdateCount = -1) do;
678 Result := FRetrievedUpdateCount;
679 FRetrievedResultSet := nil;
682 procedure TZDBLibCallableStatement.RegisterOutParameter(ParameterIndex: Integer;
685 SetOutParamCount(ParameterIndex);
686 OutParamTypes[ParameterIndex - 1] := TZSqlType(SqlType);
688 //Count inparams must equal count outparams to correct set paramters
689 if InParamCount < ParameterIndex then
690 SetInParamCount(ParameterIndex);
693 function TZDBLibCallableStatement.ExecutePrepared: Boolean;
696 I, ParamIndex, DatLen: Integer;
704 DatString: RawByteString;
706 DatDBDATETIME: DBDATETIME;
707 DatBytes: TByteDynArray;
709 ParamType: TZSQLType;
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');
716 for I := 1 to InParamCount - 1 do//The 0 parameter is the return value
719 if OutParamTypes[I] <> stUnknown then
720 RetParam := DBRPCRETURN;
722 ParamType := InParamTypes[I];
723 if ParamType = stUnknown then
724 ParamType := OutParamTypes[I];
726 if DefVarManager.IsNull(InParamValues[I]) and (InParamTypes[I] <> stUnknown) then
728 if FDBLibConnection.FreeTDS then
729 FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
730 ConvertSqlTypeToFreeTDSType(InParamTypes[I]), -1, 0, nil)
732 FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
733 ConvertSqlTypeToDBLibType(InParamTypes[I]), -1, 0, nil)
740 DatBoolean := SoftVarManager.GetAsBoolean(InParamValues[I]);
741 FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
742 FPlainDriver.GetVariables.datatypes[Z_SQLINT1], -1, -1, @DatBoolean);
746 DatByte := Byte(SoftVarManager.GetAsInteger(InParamValues[I]));
747 FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
748 FPlainDriver.GetVariables.datatypes[Z_SQLINT1], -1, -1, @DatByte);
752 DatShort := SmallInt(SoftVarManager.GetAsInteger(InParamValues[I]));
753 FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
754 FPlainDriver.GetVariables.datatypes[Z_SQLINT2], -1, -1, @DatShort);
758 DatInteger := Integer(SoftVarManager.GetAsInteger(InParamValues[I]));
759 FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
760 FPlainDriver.GetVariables.datatypes[Z_SQLINT4], -1, -1, @DatInteger);
764 DatFloat := SoftVarManager.GetAsFloat(InParamValues[I]);
765 FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
766 FPlainDriver.GetVariables.datatypes[Z_SQLFLT4], -1, -1, @DatFloat);
768 stDouble, stBigDecimal:
770 DatDouble := SoftVarManager.GetAsFloat(InParamValues[I]);
771 FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
772 FPlainDriver.GetVariables.datatypes[Z_SQLFLT8], -1, -1, @DatDouble);
776 DatString := ZPlainString(SoftVarManager.GetAsString(InParamValues[I]));
777 if DatString = ''then
780 DatLen := Length(DatString);
781 FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
782 FPlainDriver.GetVariables.datatypes[Z_SQLCHAR], MaxInt, DatLen, PAnsiChar(DatString));
786 DatString := UTF8Encode(SoftVarManager.GetAsUnicodeString(InParamValues[I]));
787 if DatString = '' then
790 DatLen := Length(DatString);
791 FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
792 FPlainDriver.GetVariables.datatypes[Z_SQLCHAR], MaxInt, DatLen, PAnsiChar(DatString));
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));
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));
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));
815 stAsciiStream, stUnicodeStream, stBinaryStream:
817 TempBlob := SoftVarManager.GetAsInterface(InParamValues[I]) as IZBlob;
818 DatString := TempBlob.GetString;
819 if DatString = '' then
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))
827 FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
828 FPlainDriver.GetVariables.datatypes[Z_SQLTEXT], FPlainDriver.GetVariables.dboptions[Z_TEXTSIZE], DatLen, PAnsiChar(DatString));
832 DatString := AnsiString(SoftVarManager.GetAsString(InParamValues[I]));
833 FPlainDriver.dbRpcParam(FHandle, nil, RetParam,
834 FPlainDriver.GetVariables.datatypes[Z_SQLBINARY], MaxInt, Length(DatString), PAnsiChar(DatString));
837 FPlainDriver.dbRpcParam(FHandle, nil, 0, FPlainDriver.GetVariables.datatypes[Z_SQLCHAR], 0, 0, nil);
842 if FPLainDriver.dbRpcExec(FHandle) <> DBSUCCEED then
843 FDBLibConnection.CheckDBLibError(lcOther, 'EXECUTEPREPARED:dbRPCExec');
845 Result := GetMoreResults;
847 if FPLainDriver.dbHasRetStat(FHandle) then
848 DefVarManager.SetAsInteger(Temp, FPlainDriver.dbRetStatus(FHandle))
851 OutParamValues[0] := Temp; //set function RETURN_VALUE
854 for I := 1 to OutParamCount - 1 do
856 if OutParamTypes[I] = stUnknown then
858 if FPlainDriver.dbRetData(FHandle, ParamIndex) = nil then
862 if FDBLibConnection.FreeTDS then
863 case FPLainDriver.dbRetType(FHandle, ParamIndex) of
864 TDSSQLCHAR, TDSSQLBINARY:
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)));
873 DefVarManager.SetAsInteger(Temp,
874 PByte(FPlainDriver.dbRetData(FHandle, ParamIndex))^);
876 DefVarManager.SetAsInteger(Temp,
877 PSmallInt(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
879 DefVarManager.SetAsInteger(Temp,
880 PInteger(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
882 DefVarManager.SetAsFloat(Temp,
883 PSingle(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
885 DefVarManager.SetAsFloat(Temp,
886 PDouble(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
889 FPlainDriver.dbConvert(FHandle, TDSSQLMONEY4,
890 FPlainDriver.dbRetData(FHandle, ParamIndex), 4, TDSSQLMONEY,
892 DefVarManager.SetAsFloat(Temp, DatMoney);
895 DefVarManager.SetAsFloat(Temp,
896 PCurrency(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
899 FPLainDriver.dbConvert(FHandle, TDSSQLDECIMAL,
900 FPLainDriver.dbRetData(FHandle, ParamIndex),
901 FPLainDriver.dbRetLen(FHandle, ParamIndex),
902 TDSSQLFLT8, @DatDouble, 8);
903 DefVarManager.SetAsFloat(Temp, DatDouble);
907 FPLainDriver.dbConvert(FHandle, TDSSQLNUMERIC,
908 FPLainDriver.dbRetData(FHandle, ParamIndex),
909 FPLainDriver.dbRetLen(FHandle, ParamIndex),
910 TDSSQLFLT8, @DatDouble, 8);
911 DefVarManager.SetAsFloat(Temp, DatDouble);
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));
923 DatDBDATETIME := PDBDATETIME(
924 FPLainDriver.dbRetData(FHandle, ParamIndex))^;
925 DefVarManager.SetAsDateTime(Temp,
926 DatDBDATETIME.dtdays + 2 + (DatDBDATETIME.dttime / 25920000));
932 case FPLainDriver.dbRetType(FHandle, ParamIndex) of
933 DBLIBSQLCHAR, DBLIBSQLBINARY:
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)));
942 DefVarManager.SetAsInteger(Temp,
943 PByte(FPlainDriver.dbRetData(FHandle, ParamIndex))^);
945 DefVarManager.SetAsInteger(Temp,
946 PSmallInt(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
948 DefVarManager.SetAsInteger(Temp,
949 PInteger(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
951 DefVarManager.SetAsFloat(Temp,
952 PSingle(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
954 DefVarManager.SetAsFloat(Temp,
955 PDouble(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
958 FPlainDriver.dbConvert(FHandle, DBLIBSQLMONEY4,
959 FPlainDriver.dbRetData(FHandle, ParamIndex), 4, DBLIBSQLMONEY,
961 DefVarManager.SetAsFloat(Temp, DatMoney);
964 DefVarManager.SetAsFloat(Temp,
965 PCurrency(FPLainDriver.dbRetData(FHandle, ParamIndex))^);
968 FPLainDriver.dbConvert(FHandle, DBLIBSQLDECIMAL,
969 FPLainDriver.dbRetData(FHandle, ParamIndex),
970 FPLainDriver.dbRetLen(FHandle, ParamIndex),
971 DBLIBSQLFLT8, @DatDouble, 8);
972 DefVarManager.SetAsFloat(Temp, DatDouble);
976 FPLainDriver.dbConvert(FHandle, DBLIBSQLNUMERIC,
977 FPLainDriver.dbRetData(FHandle, ParamIndex),
978 FPLainDriver.dbRetLen(FHandle, ParamIndex),
979 DBLIBSQLFLT8, @DatDouble, 8);
980 DefVarManager.SetAsFloat(Temp, DatDouble);
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));
992 DatDBDATETIME := PDBDATETIME(
993 FPLainDriver.dbRetData(FHandle, ParamIndex))^;
994 DefVarManager.SetAsDateTime(Temp,
995 DatDBDATETIME.dtdays + 2 + (DatDBDATETIME.dttime / 25920000));
1001 OutParamValues[I] := Temp;
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
1010 DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol,
1011 Format('EXEC %s', [SQL]));
1014 procedure TZDBLibCallableStatement.SetInParamCount(NewParamCount: Integer);
1016 inherited SetInParamCount(NewParamCount);
1018 if OutParamCount < NewParamCount then
1019 SetOutParamCount(NewParamCount);