1 {*********************************************************}
3 { Zeos Database Objects }
4 { Interbase Database Connectivity Classes }
6 { Originally written by Sergey Merkuriev }
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 ZDbcInterbase6Statement;
58 uses Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils, Types,
59 ZDbcIntfs, ZDbcStatement, ZDbcInterbase6, ZDbcInterbase6Utils,
60 ZDbcInterbase6ResultSet, ZPlainFirebirdInterbaseConstants, ZCompatibility,
61 ZDbcLogging, ZVariant, ZMessages;
65 {** Implements Generic Interbase6 Statement. }
66 TZInterbase6Statement = class(TZAbstractStatement)
69 FStatusVector: TARRAY_ISC_STATUS;
70 FIBConnection: IZInterbase6Connection;
72 function CheckInterbase6Error(const Sql: string = '') : Integer;
74 constructor Create(Connection: IZConnection; Info: TStrings);
76 function ExecuteQuery(const SQL: RawByteString): IZResultSet; override;
77 function ExecuteUpdate(const SQL: RawByteString): Integer; override;
78 function Execute(const SQL: RawByteString): Boolean; override;
81 {** Implements Prepared SQL Statement. }
83 { TZInterbase6PreparedStatement }
85 TZInterbase6PreparedStatement = class(TZAbstractPreparedStatement)
88 FParamSQLData: IZParamsSQLDA;
89 FStatusVector: TARRAY_ISC_STATUS;
90 FIBConnection: IZInterbase6Connection;
93 SQLData: IZResultSQLDA;
94 StmtHandle: TISC_STMT_HANDLE;
95 StatementType: TZIbSqlStatementType;
97 procedure PrepareInParameters; override;
98 procedure SetASQL(const Value: RawByteString); override;
99 procedure SetWSQL(const Value: ZWideString); override;
100 procedure BindInParameters; override;
101 procedure UnPrepareInParameters; override;
102 function CheckInterbase6Error(const Sql: string = '') : Integer;
104 constructor Create(Connection: IZConnection; const SQL: string; Info: TStrings);
105 destructor Destroy; override;
107 procedure Prepare; override;
108 procedure Unprepare; override;
110 function ExecuteQueryPrepared: IZResultSet; override;
111 function ExecuteUpdatePrepared: Integer; override;
112 function ExecutePrepared: Boolean; override;
115 TZInterbase6CallableStatement = class(TZAbstractPreparedCallableStatement)
117 FCachedBlob: boolean;
118 FParamSQLData: IZParamsSQLDA;
119 FResultSQLData: IZResultSQLDA;
120 FStmtHandle: TISC_STMT_HANDLE;
121 FStatementType: TZIbSqlStatementType;
122 FStatusVector: TARRAY_ISC_STATUS;
123 FIBConnection: IZInterbase6Connection;
125 procedure CheckInterbase6Error(const Sql: string = '');
126 procedure FetchOutParams(Value: IZResultSQLDA);
127 function GetProcedureSql(SelectProc: boolean): string;
129 procedure PrepareInParameters; override;
130 procedure BindInParameters; override;
131 procedure UnPrepareInParameters; override;
133 constructor Create(Connection: IZConnection; const SQL: string; Info: TStrings);
134 destructor Destroy; override;
135 procedure Unprepare; override;
137 function ExecuteQueryPrepared: IZResultSet; override;
138 function ExecuteUpdatePrepared: Integer; override;
139 function ExecutePrepared: Boolean; override;
144 uses ZSysUtils, ZDbcUtils;
146 { TZInterbase6Statement }
149 Check interbase error status
150 @param Sql the used sql tring
152 @return ErrorCode for possible Database Disconnect
154 function TZInterbase6Statement.CheckInterbase6Error(const Sql: string = '') : Integer;
156 Result := ZDbcInterbase6Utils.CheckInterbase6Error(FIBConnection.GetPlainDriver,
157 FStatusVector, lcExecute, SQL);
162 Constructs this object and assignes the main properties.
163 @param Connection a database connection object.
164 @param Handle a connection handle pointer.
165 @param Dialect a dialect Interbase SQL must be 1 or 2 or 3.
166 @param Info a statement parameters.
168 constructor TZInterbase6Statement.Create(Connection: IZConnection;
171 inherited Create(Connection, Info);
173 FIBConnection := Connection as IZInterbase6Connection;
174 ResultSetType := rtScrollInsensitive;
175 FCachedBlob := StrToBoolEx(DefineStatementParameter(Self, 'cashedblob', 'true'));
179 Destroys this object and cleanups the memory.
182 Executes an SQL statement that returns a single <code>ResultSet</code> object.
183 @param sql typically this is a static SQL <code>SELECT</code> statement
184 @return a <code>ResultSet</code> object that contains the data produced by the
185 given query; never <code>null</code>
188 function TZInterbase6Statement.ExecuteQuery(const SQL: RawByteString): IZResultSet;
191 SQLData: IZResultSQLDA;
192 StmtHandle: TISC_STMT_HANDLE;
193 StatementType: TZIbSqlStatementType;
194 iError : Integer; //For closing the database //AVZ
198 {$IFNDEF UNICODE}ASQL := SQL;{$ENDIF} //preprepares SQL and sets AnsiSQL(ASQL)
199 with FIBConnection do
201 SQLData := TZResultSQLDA.Create(GetPlainDriver, GetDBHandle, GetTrHandle, ConSettings);
203 StatementType := ZDbcInterbase6Utils.PrepareStatement(GetPlainDriver,
204 GetDBHandle, GetTrHandle, GetDialect, ASQL, SSQL, StmtHandle);
206 PrepareResultSqlData(GetPlainDriver, GetDBHandle, GetDialect,
207 SSQL, StmtHandle, SQLData);
209 DriverManager.LogMessage(lcExecute, GetPlainDriver.GetProtocol, SSQL);
211 GetPlainDriver.isc_dsql_execute(@FStatusVector, GetTrHandle,
212 @StmtHandle, GetDialect, SQLData.GetData);
213 iError := CheckInterbase6Error(SSQL);
215 if (StatementType in [stSelect, stExecProc])
216 and (SQLData.GetFieldCount <> 0) then
218 if CursorName <> '' then
220 Cursor := CursorName;
221 GetPlainDriver.isc_dsql_set_cursor_name(@FStatusVector,
222 @StmtHandle, PAnsiChar(Cursor), 0);
223 CheckInterbase6Error(SSQL);
226 Result := CreateIBResultSet(SSQL, Self,
227 TZInterbase6ResultSet.Create(Self, LogSQL, StmtHandle, Cursor, SQLData, FCachedBlob));
230 if (iError <> DISCONNECT_ERROR) then
231 raise EZSQLException.Create(SCanNotRetrieveResultSetData);
235 FreeStatement(GetPlainDriver, StmtHandle, DSQL_drop); //Free Stmt handle only if Execution fails. Otherwise the ResultSet will do this
244 Executes an SQL <code>INSERT</code>, <code>UPDATE</code> or
245 <code>DELETE</code> statement. In addition,
246 SQL statements that return nothing, such as SQL DDL statements,
249 @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
250 <code>DELETE</code> statement or an SQL statement that returns nothing
251 @return either the row count for <code>INSERT</code>, <code>UPDATE</code>
252 or <code>DELETE</code> statements, or 0 for SQL statements that return nothing
255 function TZInterbase6Statement.ExecuteUpdate(const SQL: RawByteString): Integer;
257 StmtHandle: TISC_STMT_HANDLE;
258 StatementType: TZIbSqlStatementType;
262 with FIBConnection do
265 {$IFNDEF UNICODE}ASQL := SQL;{$ENDIF} //preprepares SQL and sets AnsiSQL(ASQL)
266 StatementType := ZDbcInterbase6Utils.PrepareStatement(GetPlainDriver,
267 GetDBHandle, GetTrHandle, GetDialect, ASQL, SSQL, StmtHandle);
269 DriverManager.LogMessage(lcExecute, GetPlainDriver.GetProtocol, SSQL);
271 GetPlainDriver.isc_dsql_execute2(@FStatusVector, GetTrHandle,
272 @StmtHandle, GetDialect, nil, nil);
273 CheckInterbase6Error(SSQL);
275 case StatementType of
276 stCommit, stRollback, stUnknown: Result := -1;
279 Result := GetAffectedRows(GetPlainDriver, StmtHandle, StatementType);
280 LastUpdateCount := Result;
284 { Autocommit statement. }
285 if Connection.GetAutoCommit then
287 { Logging SQL Command }
289 FreeStatement(GetPlainDriver, StmtHandle, DSQL_drop); //Free Stmt handle because of single executions without a prepared state
296 Executes an SQL statement that may return multiple results.
297 Under some (uncommon) situations a single SQL statement may return
298 multiple result sets and/or update counts. Normally you can ignore
299 this unless you are (1) executing a stored procedure that you know may
300 return multiple results or (2) you are dynamically executing an
301 unknown SQL string. The methods <code>execute</code>,
302 <code>getMoreResults</code>, <code>getResultSet</code>,
303 and <code>getUpdateCount</code> let you navigate through multiple results.
305 The <code>execute</code> method executes an SQL statement and indicates the
306 form of the first result. You can then use the methods
307 <code>getResultSet</code> or <code>getUpdateCount</code>
308 to retrieve the result, and <code>getMoreResults</code> to
309 move to any subsequent result(s).
311 @param sql any SQL statement
312 @return <code>true</code> if the next result is a <code>ResultSet</code> object;
313 <code>false</code> if it is an update count or there are no more results
319 function TZInterbase6Statement.Execute(const SQL: RawByteString): Boolean;
322 SQLData: IZResultSQLDA;
323 StmtHandle: TISC_STMT_HANDLE;
324 StatementType: TZIbSqlStatementType;
327 with FIBConnection do
331 {$IFNDEF UNICODE}ASQL := SQL;{$ENDIF} //preprepares SQL and sets AnsiSQL(ASQL)
332 StatementType := ZDbcInterbase6Utils.PrepareStatement(GetPlainDriver,
333 GetDBHandle, GetTrHandle, GetDialect, ASQL, LogSQL, StmtHandle);
335 { Check statement type }
336 // if not (StatementType in [stExecProc]) then
337 // raise EZSQLException.Create(SStatementIsNotAllowed);
339 { Create Result SQLData if statement returns result }
340 if StatementType in [stSelect, stExecProc] then
342 SQLData := TZResultSQLDA.Create(GetPlainDriver, GetDBHandle, GetTrHandle, ConSettings);
343 PrepareResultSqlData(GetPlainDriver, GetDBHandle, GetDialect, LogSQL,
344 StmtHandle, SQLData);
347 DriverManager.LogMessage(lcExecute, GetPlainDriver.GetProtocol, SSQL);
348 { Execute prepared statement }
349 GetPlainDriver.isc_dsql_execute(@FStatusVector, GetTrHandle,
350 @StmtHandle, GetDialect, nil);
351 CheckInterbase6Error(LogSQL);
352 { Set updated rows count }
353 LastUpdateCount := GetAffectedRows(GetPlainDriver, StmtHandle, StatementType);
355 case StatementType of
356 stInsert, stDelete, stUpdate, stSelectForUpdate: Result := False;
361 { Create ResultSet if possible else free Stateent Handle }
362 if (StatementType in [stSelect, stExecProc])
363 and (SQLData.GetFieldCount <> 0) then
365 if CursorName <> '' then
367 Cursor := CursorName;
369 GetPlainDriver.isc_dsql_set_cursor_name(@FStatusVector,
370 @StmtHandle, PAnsiChar(Cursor), 0);
371 CheckInterbase6Error(sSQL);
374 LastResultSet := CreateIBResultSet(SSQL, Self,
375 TZInterbase6ResultSet.Create(Self, SSQL, StmtHandle, Cursor,
376 SQLData, FCachedBlob));
380 LastResultSet := nil;
381 FreeStatement(GetPlainDriver, StmtHandle, DSQL_drop);
384 { Autocommit statement. }
385 if Connection.GetAutoCommit then
390 FreeStatement(GetPlainDriver, StmtHandle, DSQL_drop); //Free Stmt handle because of single executions without a prepared state
398 { TZInterbase6PreparedStatement }
400 procedure TZInterbase6PreparedStatement.PrepareInParameters;
402 StatusVector: TARRAY_ISC_STATUS;
404 With FIBConnection do
406 {create the parameter bind structure}
407 FParamSQLData := TZParamsSQLDA.Create(GetPlainDriver, GetDBHandle, GetTrHandle, ConSettings);
409 GetPlainDriver.isc_dsql_describe_bind(@StatusVector, @StmtHandle, GetDialect,
410 FParamSQLData.GetData);
411 ZDbcInterbase6Utils.CheckInterbase6Error(GetPlainDriver, StatusVector, lcExecute, SSQL);
413 { Resize XSQLDA structure if needed }
414 if FParamSQLData.GetData^.sqld > FParamSQLData.GetData^.sqln then
416 FParamSQLData.AllocateSQLDA;
417 GetPlainDriver.isc_dsql_describe_bind(@StatusVector, @StmtHandle, GetDialect,FParamSQLData.GetData);
418 ZDbcInterbase6Utils.CheckInterbase6Error(GetPlainDriver, StatusVector, lcExecute, SSQL);
421 FParamSQLData.InitFields(True);
423 inherited PrepareInParameters;
426 procedure TZInterbase6PreparedStatement.SetASQL(const Value: RawByteString);
428 if ( ASQL <> Value ) and Prepared then
430 inherited SetASQL(Value);
433 procedure TZInterbase6PreparedStatement.SetWSQL(const Value: ZWideString);
435 if ( WSQL <> Value ) and Prepared then
437 inherited SetWSQL(Value);
440 procedure TZInterbase6PreparedStatement.BindInParameters;
442 BindSQLDAInParameters(FIBConnection.GetPlainDriver, InParamValues,
443 InParamTypes, InParamCount, FParamSQLData, GetConnection.GetConSettings);
444 inherited BindInParameters;
447 procedure TZInterbase6PreparedStatement.UnPrepareInParameters;
449 if assigned(FParamSQLData) then
450 FParamSQLData.FreeParamtersValues;
454 Check interbase error status
455 @param Sql the used sql tring
457 @return Integer - Error Code to test for graceful database disconnection
459 function TZInterbase6PreparedStatement.CheckInterbase6Error(const Sql: string) : Integer;
461 Result := ZDbcInterbase6Utils.CheckInterbase6Error(FIBConnection.GetPlainDriver,
462 FStatusVector, lcExecute, SQL);
466 Constructs this object and assignes the main properties.
467 @param Connection a database connection object.
468 @param Handle a connection handle pointer.
469 @param Dialect a dialect Interbase SQL must be 1 or 2 or 3.
470 @param Info a statement parameters.
472 constructor TZInterbase6PreparedStatement.Create(Connection: IZConnection;
473 const SQL: string; Info: TStrings);
475 inherited Create(Connection, SQL, Info);
477 FIBConnection := Connection as IZInterbase6Connection;
478 ResultSetType := rtScrollInsensitive;
479 FCachedBlob := StrToBoolEx(DefineStatementParameter(Self, 'cashedblob', 'true'));
485 destructor TZInterbase6PreparedStatement.Destroy;
488 FreeStatement(FIBConnection.GetPlainDriver, StmtHandle, DSQL_drop);
491 procedure TZInterbase6PreparedStatement.Prepare;
493 with FIBConnection do
495 StatementType := ZDbcInterbase6Utils.PrepareStatement(GetPlainDriver,
496 GetDBHandle, GetTrHandle, GetDialect, ASQL, LogSQL, StmtHandle); //allocate handle if required or reuse it
498 if StatementType in [stSelect, stExecProc] then
500 SQLData := TZResultSQLDA.Create(GetPlainDriver, GetDBHandle,
501 GetTrHandle , ConSettings);
502 PrepareResultSqlData(GetPlainDriver, GetDBHandle, GetDialect,
503 SQL, StmtHandle, SQLData);
506 CheckInterbase6Error(SQL);
507 LogPrepStmtMessage(lcPrepStmt, SQL);
511 procedure TZInterbase6PreparedStatement.Unprepare;
513 if StmtHandle <> 0 then //check if prepare did fail. otherwise we unprepare the handle
514 FreeStatement(FIBConnection.GetPlainDriver, StmtHandle, DSQL_UNPREPARE); //unprepare avoids new allocation for the stmt handle
519 Executes any kind of SQL statement.
520 Some prepared statements return multiple results; the <code>execute</code>
521 method handles these complex statements as well as the simpler
522 form of statements handled by the methods <code>executeQuery</code>
523 and <code>executeUpdate</code>.
524 @see Statement#execute
527 function TZInterbase6PreparedStatement.ExecutePrepared: Boolean;
533 with FIBConnection do
538 if (StatementType = stSelect) then //AVZ Get many rows - only need to use execute not execute2
539 GetPlainDriver.isc_dsql_execute(@FStatusVector, GetTrHandle, @StmtHandle,
540 GetDialect, FParamSQLData.GetData)
543 CursorName := 'ExecProc'+RandomString(12); //AVZ - Need a way to return one row so we give the cursor a name
544 if (SQLData = nil) then
545 GetPlainDriver.isc_dsql_execute2(@FStatusVector, GetTrHandle, @StmtHandle,
546 GetDialect, FParamSQLData.GetData, nil) //not expecting a result
548 GetPlainDriver.isc_dsql_execute2(@FStatusVector, GetTrHandle, @StmtHandle,
549 GetDialect, FParamSQLData.GetData, SQLData.GetData); //expecting a result
552 CheckInterbase6Error(SQL);
554 LastUpdateCount := GetAffectedRows(GetPlainDriver, StmtHandle, StatementType);
556 case StatementType of
566 { Create ResultSet if possible else free Statement Handle }
567 if (StatementType in [stSelect, stExecProc])
568 and (SQLData.GetFieldCount <> 0) then
570 LastResultSet := CreateIBResultSet(SQL, Self,
571 TZInterbase6ResultSet.Create(Self, SQL, StmtHandle, Cursor,
572 SQLData, FCachedBlob));
576 LastResultSet := nil;
579 { Autocommit statement. }
580 if Connection.GetAutoCommit then
585 {EH: do not Close the Stmt if execution fails !!}
586 //FreeStatement(GetPlainDriver, StmtHandle, DSQL_CLOSE); //AVZ
591 inherited ExecutePrepared;
596 Executes the SQL query in this <code>PreparedStatement</code> object
597 and returns the result set generated by the query.
599 @return a <code>ResultSet</code> object that contains the data produced by the
600 query; never <code>null</code>
603 function TZInterbase6PreparedStatement.ExecuteQueryPrepared: IZResultSet;
605 iError : Integer; //Check for database disconnect AVZ
610 with FIBConnection do
615 if (StatementType = stSelect) then //AVZ Get many rows - only need to use execute not execute2
616 GetPlainDriver.isc_dsql_execute(@FStatusVector, GetTrHandle, @StmtHandle,
617 GetDialect, FParamSQLData.GetData)
620 CursorName := 'ExecProc'+RandomString(12); //AVZ - Need a way to return one row so we give the cursor a name
621 if (SQLData = nil) then
622 GetPlainDriver.isc_dsql_execute2(@FStatusVector, GetTrHandle, @StmtHandle,
623 GetDialect, FParamSQLData.GetData, nil) //not expecting a result
625 GetPlainDriver.isc_dsql_execute2(@FStatusVector, GetTrHandle, @StmtHandle,
626 GetDialect, FParamSQLData.GetData, SQLData.GetData); //expecting a result
629 iError := CheckInterbase6Error(SQL);
631 if (StatementType in [stSelect, stExecProc]) and (SQLData.GetFieldCount <> 0) then
633 if CursorName <> '' then
635 Cursor := CursorName;
636 GetPlainDriver.isc_dsql_set_cursor_name(@FStatusVector,
637 @StmtHandle, PAnsiChar(Cursor), 0);
638 iError := CheckInterbase6Error(SQL);
641 if (iError <> DISCONNECT_ERROR) then
642 Result := CreateIBResultSet(LogSQL, Self, TZInterbase6ResultSet.Create(Self, LogSQL, StmtHandle, Cursor, SQLData, FCachedBlob));
645 if (iError <> DISCONNECT_ERROR) then //AVZ
646 raise EZSQLException.Create(SCanNotRetrieveResultSetData)
652 //The cursor will be already closed for exec2
653 if (Pos('ExecProc', String(CursorName)) <> 0) then
656 {EH: do not Close the Stmt if execution fails !! This will be done on unprepare}
657 //FreeStatement(GetPlainDriver, StmtHandle, DSQL_CLOSE); //AVZ
662 inherited ExecuteQueryPrepared;
667 Executes the SQL INSERT, UPDATE or DELETE statement
668 in this <code>PreparedStatement</code> object.
670 SQL statements that return nothing, such as SQL DDL statements,
673 @return either the row count for INSERT, UPDATE or DELETE statements;
674 or 0 for SQL statements that return nothing
677 function TZInterbase6PreparedStatement.ExecuteUpdatePrepared: Integer;
679 iError : Integer; //Implementation for graceful disconnect AVZ
686 with FIBConnection do
690 GetPlainDriver.isc_dsql_execute(@FStatusVector, GetTrHandle,
691 @StmtHandle, GetDialect, FParamSQLData.GetData);
692 iError := CheckInterbase6Error(SQL);
694 Result := GetAffectedRows(GetPlainDriver, StmtHandle, StatementType);
695 LastUpdateCount := Result;
697 case StatementType of
698 stCommit, stRollback, stUnknown: Result := -1;
699 stSelect: FreeStatement(GetPlainDriver, StmtHandle, DSQL_CLOSE); //AVZ
702 { Autocommit statement. }
703 if Connection.GetAutoCommit and ( StatementType <> stSelect ) then
706 inherited ExecuteUpdatePrepared;
708 //Trail for the disconnection of the database gracefully - AVZ
709 if (iError = DISCONNECT_ERROR) then
711 Result := DISCONNECT_ERROR;
718 { TZInterbase6CallableStatement }
721 Check interbase error status
722 @param Sql the used sql tring
724 procedure TZInterbase6CallableStatement.CheckInterbase6Error(const Sql: string);
726 ZDbcInterbase6Utils.CheckInterbase6Error(FIBConnection.GetPlainDriver,
727 FStatusVector, lcExecute, SQL);
731 Constructs this object and assignes the main properties.
732 @param Connection a database connection object.
733 @param Handle a connection handle pointer.
734 @param Dialect a dialect Interbase SQL must be 1 or 2 or 3.
735 @param Info a statement parameters.
737 constructor TZInterbase6CallableStatement.Create(Connection: IZConnection;
738 const SQL: string; Info: TStrings);
740 inherited Create(Connection, SQL, Info);
742 FIBConnection := Connection as IZInterbase6Connection;
743 ResultSetType := rtScrollInsensitive;
744 FCachedBlob := StrToBoolEx(DefineStatementParameter(Self, 'cashedblob', 'true'));
745 with FIBConnection do
747 FParamSQLData := TZParamsSQLDA.Create(GetPlainDriver, GetDBHandle,
748 GetTrHandle, ConSettings);
749 FResultSQLData := TZResultSQLDA.Create(GetPlainDriver, GetDBHandle,
750 GetTrHandle, ConSettings);
754 procedure TZInterbase6CallableStatement.PrepareInParameters;
756 with FIBConnection do
758 { Prepare statement }
759 FStatementType := ZDbcInterbase6Utils.PrepareStatement(GetPlainDriver,
760 GetDBHandle, GetTrHandle, GetDialect, ZPlainString(ProcSql), ProcSQL, FStmtHandle);
761 PrepareResultSqlData(GetPlainDriver, GetDBHandle, GetDialect,
762 SQL, FStmtHandle, FResultSQLData);
763 PrepareParameters(GetPlainDriver, ProcSql, GetDialect, FStmtHandle, FParamSQLData);
767 procedure TZInterbase6CallableStatement.BindInParameters;
769 BindSQLDAInParameters(FIBConnection.GetPlainDriver, InParamValues,
770 InParamTypes, InParamCount, FParamSQLData, ConSettings);
771 inherited BindInParameters;
774 procedure TZInterbase6CallableStatement.UnPrepareInParameters;
776 if assigned(FParamSQLData) then
777 FParamSQLData.FreeParamtersValues;
780 procedure TZInterbase6CallableStatement.Unprepare;
783 FreeStatement(FIBConnection.GetPlainDriver, FStmtHandle, DSQL_unprepare);
784 if FStmtHandle <> 0 then // Free statement-hande! On the other hand: Exception!
786 FreeStatement(FIBConnection.GetPlainDriver, FStmtHandle, DSQL_drop);
791 destructor TZInterbase6CallableStatement.Destroy;
794 if FStmtHandle <> 0 then
795 FreeStatement(FIBConnection.GetPlainDriver, FStmtHandle, DSQL_drop);
796 FResultSQLData := nil;
797 FParamSQLData := nil;
801 Executes any kind of SQL statement.
802 Some prepared statements return multiple results; the <code>execute</code>
803 method handles these complex statements as well as the simpler
804 form of statements handled by the methods <code>executeQuery</code>
805 and <code>executeUpdate</code>.
806 @see Statement#execute
809 function TZInterbase6CallableStatement.ExecutePrepared: Boolean;
814 with FIBConnection do
816 ProcSql := GetProcedureSql(False);
818 DriverManager.LogMessage(lcExecute, GetPlainDriver.GetProtocol, SQL);
820 GetPlainDriver.isc_dsql_execute2(@FStatusVector, GetTrHandle, @FStmtHandle,
821 GetDialect, FParamSQLData.GetData, Self.FResultSQLData.GetData);
822 CheckInterbase6Error(SQL);
824 LastUpdateCount := GetAffectedRows(GetPlainDriver, FStmtHandle, FStatementType);
826 case FStatementType of
827 stInsert, stDelete, stUpdate, stSelectForUpdate: Result := False;
832 { Create ResultSet if possible else free Stateent Handle, ResultSQlData and
834 if (FStatementType in [stSelect, stExecProc])
835 and (FResultSQLData.GetFieldCount <> 0) then
837 Cursor := RandomString(12);
838 LastResultSet := CreateIBResultSet(SQL, Self,
839 TZInterbase6ResultSet.Create(Self, SQL, FStmtHandle, Cursor, FResultSQLData, FCachedBlob));
843 { Fetch data and fill Output params }
844 FetchOutParams(FResultSQLData);
845 FreeStatement(GetPlainDriver, FStmtHandle, DSQL_CLOSE); //AVZ
846 LastResultSet := nil;
849 { Autocommit statement. }
850 if Connection.GetAutoCommit then
856 //FreeStatement(GetPlainDriver, FStmtHandle, DSQL_CLOSE); //AVZ
865 Executes the SQL query in this <code>PreparedStatement</code> object
866 and returns the result set generated by the query.
868 @return a <code>ResultSet</code> object that contains the data produced by the
869 query; never <code>null</code>
872 function TZInterbase6CallableStatement.ExecuteQueryPrepared: IZResultSet;
876 with FIBConnection do
878 ProcSql := GetProcedureSql(True); //Prepares the Statement
881 DriverManager.LogMessage(lcExecute, GetPlainDriver.GetProtocol, ProcSql);
883 if (FStatementType = stSelect) then //AVZ Get many rows - only need to use execute not execute2
884 GetPlainDriver.isc_dsql_execute(@FStatusVector, GetTrHandle, @FStmtHandle,
885 GetDialect, FParamSQLData.GetData)
888 CursorName := 'ExecProc'+RandomString(12); //AVZ - Need a way to return one row so we give the cursor a name
889 GetPlainDriver.isc_dsql_execute2(@FStatusVector, GetTrHandle, @FStmtHandle,
890 GetDialect, FParamSQLData.GetData, FResultSQLData.GetData);
893 CheckInterbase6Error(ProcSql);
895 if (FStatementType in [stSelect, stExecProc]) and (FResultSQLData.GetFieldCount <> 0) then
897 if CursorName <> '' then
899 Cursor := CursorName;
900 GetPlainDriver.isc_dsql_set_cursor_name(@FStatusVector, @FStmtHandle, PAnsiChar(Cursor), 0);
901 CheckInterbase6Error(ProcSql);
904 Result := CreateIBResultSet(ProcSql, Self,
905 TZInterbase6ResultSet.Create(Self, ProcSql, FStmtHandle, Cursor,
906 FResultSQLData, FCachedBlob));
912 //FreeStatement(GetPlainDriver, FStmtHandle, DSQL_unprepare); //AVZ
921 Executes the SQL INSERT, UPDATE or DELETE statement
922 in this <code>PreparedStatement</code> object.
924 SQL statements that return nothing, such as SQL DDL statements,
927 @return either the row count for INSERT, UPDATE or DELETE statements;
928 or 0 for SQL statements that return nothing
930 function TZInterbase6CallableStatement.ExecuteUpdatePrepared: Integer;
932 with FIBConnection do
935 ProcSQL := Self.GetProcedureSql(False);
938 DriverManager.LogMessage(lcExecute, GetPlainDriver.GetProtocol, ProcSQL);
940 GetPlainDriver.isc_dsql_execute2(@FStatusVector, GetTrHandle, @FStmtHandle,
941 GetDialect, FParamSQLData.GetData, FResultSQLData.GetData);
942 CheckInterbase6Error(ProcSql);
944 Result := GetAffectedRows(GetPlainDriver, FStmtHandle, FStatementType);
945 LastUpdateCount := Result;
946 { Fetch data and fill Output params }
947 FetchOutParams(FResultSQLData);
948 { Autocommit statement. }
949 if Connection.GetAutoCommit then
953 //FreeStatement(GetPlainDriver, FStmtHandle, DSQL_unprepare); //AVZ -- unprepare the statement - not close it
960 Set output parameters values from TZResultSQLDA.
961 @param Value a TZResultSQLDA object.
963 procedure TZInterbase6CallableStatement.FetchOutParams(
964 Value: IZResultSQLDA);
966 ParamIndex, I: Integer;
970 for ParamIndex := 0 to OutParamCount - 1 do
972 if not (FDBParamTypes[ParamIndex] in [2, 3, 4]) then // ptOutput, ptInputOutput, ptResult
975 if I >= Value.GetFieldCount then
978 if Value.IsNull(I) then
979 DefVarManager.SetNull(Temp)
981 case Value.GetFieldSqlType(I) of
983 DefVarManager.SetAsBoolean(Temp, Value.GetBoolean(I));
985 DefVarManager.SetAsInteger(Temp, Value.GetByte(I));
987 DefVarManager.SetAsBytes(Temp, Value.GetBytes(I));
989 DefVarManager.SetAsInteger(Temp, Value.GetShort(I));
991 DefVarManager.SetAsInteger(Temp, Value.GetInt(I));
993 DefVarManager.SetAsInteger(Temp, Value.GetLong(I));
995 DefVarManager.SetAsFloat(Temp, Value.GetFloat(I));
997 DefVarManager.SetAsFloat(Temp, Value.GetDouble(I));
999 DefVarManager.SetAsFloat(Temp, Value.GetBigDecimal(I));
1001 DefVarManager.SetAsString(Temp, ZDbcString(Value.GetString(I)));
1003 DefVarManager.SetAsUnicodeString(Temp, ZDbcUnicodeString(Value.GetString(I)));
1005 DefVarManager.SetAsDateTime(Temp, Value.GetDate(I));
1007 DefVarManager.SetAsDateTime(Temp, Value.GetTime(I));
1009 DefVarManager.SetAsDateTime(Temp, Value.GetTimestamp(I));
1011 OutParamValues[ParamIndex] := Temp;
1017 Create sql string for calling stored procedure.
1018 @param SelectProc indicate use <b>EXECUTE PROCEDURE</b> or
1019 <b>SELECT</b> staement
1020 @return a Stored Procedure SQL string
1022 function TZInterbase6CallableStatement.GetProcedureSql(SelectProc: boolean): string;
1024 function GenerateParamsStr(Count: integer): string;
1028 Result := ''; //init Result -> FPC
1029 for I := 0 to Count - 1 do
1032 Result := Result + ',';
1033 Result := Result + '?';
1041 InParams := GenerateParamsStr(High(InParamValues) + 1);
1042 if InParams <> '' then
1043 InParams := '(' + InParams + ')';
1046 Result := 'SELECT * FROM ' + SQL + InParams
1048 Result := 'EXECUTE PROCEDURE ' + SQL + InParams;