zeoslib  UNKNOWN
 All Files
ZDbcOracleStatement.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { Oracle Database Connectivity Classes }
5 { }
6 { Originally written by Sergey Seroukhov }
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 ZDbcOracleStatement;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses
59  Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils, Types,
60  ZSysUtils, ZDbcIntfs, ZDbcStatement, ZDbcLogging, ZPlainOracleDriver,
61  ZCompatibility, ZVariant, ZDbcOracleUtils, ZPlainOracleConstants;
62 
63 type
64 
65  {** Defines a Oracle specific statement. }
66  IZOracleStatement = interface(IZStatement)
67  ['{8644E5B6-1E0F-493F-B6AC-40D70CCEA13A}']
68 
69  function GetStatementHandle: POCIStmt;
70  end;
71 
72  {** Implements Generic Oracle Statement. }
73  TZOracleStatement = class(TZAbstractStatement, IZOracleStatement)
74  private
75  FPlainDriver: IZOraclePlainDriver;
76 
77  public
78  constructor Create(PlainDriver: IZOraclePlainDriver;
79  Connection: IZConnection; Info: TStrings);
80  destructor Destroy; 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 
86  function GetStatementHandle: POCIStmt;
87  end;
88 
89  {** Implements Prepared SQL Statement. }
90  TZOraclePreparedStatement = class(TZAbstractPreparedStatement)
91  private
92  FPrepared: Boolean;
93  FHandle: POCIStmt;
94  FErrorHandle: POCIError;
95  FPlainDriver: IZOraclePlainDriver;
96  FExecStatement: IZStatement;
97  FLastStatement: IZStatement;
98  FInVars: PZSQLVars;
99 
100  procedure SetLastStatement(LastStatement: IZStatement);
101  function GetExecStatement: IZStatement;
102  function ConvertToOracleSQLQuery(SQL: string): RawByteString;
103 
104  protected
105  property Prepared: Boolean read FPrepared write FPrepared;
106  property Handle: POCIStmt read FHandle write FHandle;
107  property ErrorHandle: POCIError read FErrorHandle write FErrorHandle;
108  property ExecStatement: IZStatement read FExecStatement write FExecStatement;
109  property LastStatement: IZStatement read FLastStatement write SetLastStatement;
110  property InVars: PZSQLVars read FInVars write FInVars;
111  public
112  constructor Create(PlainDriver: IZOraclePlainDriver;
113  Connection: IZConnection; const SQL: string; Info: TStrings);
114  destructor Destroy; override;
115 
116  procedure Close; override;
117  procedure Prepare; override;
118  function ExecuteQuery(const SQL: RawByteString): IZResultSet; override;
119  function ExecuteUpdate(const SQL: RawByteString): Integer; override;
120  function Execute(const SQL: RawByteString): Boolean; override;
121 
122  function ExecuteQueryPrepared: IZResultSet; override;
123  function ExecuteUpdatePrepared: Integer; override;
124  function ExecutePrepared: Boolean; override;
125 
126  function GetStatementHandle: POCIStmt;
127  end;
128 
129  TZOracleCallableStatement = class(TZAbstractCallableStatement,
130  IZParamNamedCallableStatement)
131  private
132  FOutParamCount: Integer;
133  FErrorHandle: POCIError;
134  FInVars: PZSQLVars;
135  FPlainDriver:IZOraclePlainDriver;
136  FPrepared:boolean;
137  FHandle: POCIStmt;
138  FOracleParams: TZOracleParams;
139  FOracleParamsCount: Integer;
140  FParamNames: TStringDynArray;
141  PackageIncludedList: TStrings;
142  procedure ArrangeInParams;
143  procedure FetchOutParamsFromOracleVars;
144  protected
145  function GetProcedureSql(SelectProc: boolean): RawByteString;
146  procedure SetInParam(ParameterIndex: Integer; SQLType: TZSQLType;
147  const Value: TZVariant); override;
148  procedure RegisterParamTypeAndName(const ParameterIndex:integer;
149  const ParamTypeName, ParamName: String; Const ColumnSize, Precision: Integer);
150  public
151  procedure RegisterOutParameter(ParameterIndex: Integer; SQLType: Integer); override;
152  procedure RegisterParamType(ParameterIndex: integer; ParamType: Integer); override;
153  procedure Prepare; override;
154  function IsNull(ParameterIndex: Integer): Boolean;override;
155 
156  Function ExecuteUpdatePrepared: Integer; override;
157  function ExecuteQueryPrepared: IZResultSet; override;
158  constructor Create(Connection: IZConnection; const pProcName: string; Info: TStrings);
159  destructor Destroy; override;
160  procedure ClearParameters; override;
161  end;
162 
163 implementation
164 
165 uses
166  ZTokenizer, ZDbcOracle, ZDbcOracleResultSet
167  {$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF};
168 
169 { TZOracleStatement }
170 
171 {**
172  Constructs this object and assignes the main properties.
173  @param PlainDriver a Oracle plain driver.
174  @param Connection a database connection object.
175  @param Info a statement parameters.
176  @param Handle a connection handle pointer.
177 }
178 constructor TZOracleStatement.Create(PlainDriver: IZOraclePlainDriver;
179  Connection: IZConnection; Info: TStrings);
180 begin
181  inherited Create(Connection, Info);
182  FPlainDriver := PlainDriver;
183  ResultSetType := rtForwardOnly;
184 end;
185 
186 {**
187  Destroys this object and cleanups the memory.
188 }
189 destructor TZOracleStatement.Destroy;
190 begin
191  inherited Destroy;
192 end;
193 
194 {**
195  Executes an SQL statement that returns a single <code>ResultSet</code> object.
196  @param sql typically this is a static SQL <code>SELECT</code> statement
197  @return a <code>ResultSet</code> object that contains the data produced by the
198  given query; never <code>null</code>
199 }
200 function TZOracleStatement.ExecuteQuery(const SQL: RawByteString): IZResultSet;
201 var
202  Handle: POCIStmt;
203  ErrorHandle: POCIError;
204 begin
205  AllocateOracleStatementHandles(FPlainDriver, Connection, Handle, ErrorHandle);
206  ASQL := SQL;
207  try
208  PrepareOracleStatement(FPlainDriver, ASQL, LogSQL, Handle, ErrorHandle,
209  StrToIntDef(Info.Values['prefetch_count'], 100), ConSettings);
210  Result := CreateOracleResultSet(FPlainDriver, Self, LogSQL,
211  Handle, ErrorHandle);
212  except
213  FreeOracleStatementHandles(FPlainDriver, Handle, ErrorHandle);
214  raise;
215  end;
216 
217  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
218 end;
219 
220 {**
221  Executes an SQL <code>INSERT</code>, <code>UPDATE</code> or
222  <code>DELETE</code> statement. In addition,
223  SQL statements that return nothing, such as SQL DDL statements,
224  can be executed.
225 
226  @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
227  <code>DELETE</code> statement or an SQL statement that returns nothing
228  @return either the row count for <code>INSERT</code>, <code>UPDATE</code>
229  or <code>DELETE</code> statements, or 0 for SQL statements that return nothing
230 }
231 function TZOracleStatement.ExecuteUpdate(const SQL: RawByteString): Integer;
232 var
233  Handle: POCIStmt;
234  ErrorHandle: POCIError;
235 begin
236  AllocateOracleStatementHandles(FPlainDriver, Connection, Handle, ErrorHandle);
237  ASQL := SQL;
238  try
239  PrepareOracleStatement(FPlainDriver, ASQL, LogSQL, Handle, ErrorHandle,
240  StrToIntDef(Info.Values['prefetch_count'], 100), ConSettings);
241  ExecuteOracleStatement(FPlainDriver, Connection, LogSQL, Handle, ErrorHandle);
242  Result := GetOracleUpdateCount(FPlainDriver, Handle, ErrorHandle);
243  finally
244  FreeOracleStatementHandles(FPlainDriver, Handle, ErrorHandle);
245  end;
246 
247  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
248 
249  { Autocommit statement. }
250  if Connection.GetAutoCommit then
251  Connection.Commit;
252 end;
253 
254 {**
255  Executes an SQL statement that may return multiple results.
256  Under some (uncommon) situations a single SQL statement may return
257  multiple result sets and/or update counts. Normally you can ignore
258  this unless you are (1) executing a stored procedure that you know may
259  return multiple results or (2) you are dynamically executing an
260  unknown SQL string. The methods <code>execute</code>,
261  <code>getMoreResults</code>, <code>getResultSet</code>,
262  and <code>getUpdateCount</code> let you navigate through multiple results.
263 
264  The <code>execute</code> method executes an SQL statement and indicates the
265  form of the first result. You can then use the methods
266  <code>getResultSet</code> or <code>getUpdateCount</code>
267  to retrieve the result, and <code>getMoreResults</code> to
268  move to any subsequent result(s).
269 
270  @param sql any SQL statement
271  @return <code>true</code> if the next result is a <code>ResultSet</code> object;
272  <code>false</code> if it is an update count or there are no more results
273 }
274 function TZOracleStatement.Execute(const SQL: RawByteString): Boolean;
275 var
276  Handle: POCIStmt;
277  ErrorHandle: POCIError;
278  StatementType: ub2;
279 begin
280  Result := False;
281  AllocateOracleStatementHandles(FPlainDriver, Connection, Handle, ErrorHandle);
282  ASQL := SQL;
283  try
284  PrepareOracleStatement(FPlainDriver, ASQL, LogSQL, Handle, ErrorHandle,
285  StrToIntDef(Info.Values['prefetch_count'], 100), ConSettings);
286 
287  StatementType := 0;
288  FPlainDriver.AttrGet(Handle, OCI_HTYPE_STMT, @StatementType, nil,
289  OCI_ATTR_STMT_TYPE, ErrorHandle);
290 
291  if StatementType = OCI_STMT_SELECT then
292  begin
293  LastResultSet := CreateOracleResultSet(FPlainDriver, Self,
294  LogSQL, Handle, ErrorHandle);
295  Result := LastResultSet <> nil;
296  end
297  else
298  begin
299  ExecuteOracleStatement(FPlainDriver, Connection, LogSQL,
300  Handle, ErrorHandle);
301  LastUpdateCount := GetOracleUpdateCount(FPlainDriver, Handle, ErrorHandle);
302  end;
303  finally
304  if not Result then
305  FreeOracleStatementHandles(FPlainDriver, Handle, ErrorHandle);
306  end;
307 
308  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
309 
310  { Autocommit statement. }
311  if not Result and Connection.GetAutoCommit then
312  Connection.Commit;
313 end;
314 
315 {**
316  Gets statement handle.
317  @return statement handle.
318 }
319 function TZOracleStatement.GetStatementHandle: POCIStmt;
320 begin
321  Result := nil;
322 end;
323 
324 { TZOraclePreparedStatement }
325 
326 {**
327  Constructs this object and assignes the main properties.
328  @param PlainDriver a Oracle plain driver.
329  @param Connection a database connection object.
330  @param Info a statement parameters.
331  @param Handle a connection handle pointer.
332 }
333 constructor TZOraclePreparedStatement.Create(
334  PlainDriver: IZOraclePlainDriver; Connection: IZConnection;
335  const SQL: string; Info: TStrings);
336 begin
337  inherited Create(Connection, SQL, Info);
338  FPlainDriver := PlainDriver;
339  ResultSetType := rtForwardOnly;
340  ASQL := ConvertToOracleSQLQuery(SQL);
341  FPrepared := False;
342 end;
343 
344 {**
345  Destroys this object and cleanups the memory.
346 }
347 destructor TZOraclePreparedStatement.Destroy;
348 begin
349  inherited Destroy;
350 end;
351 
352 {**
353  Sets a reference to the last statement.
354  @param LastStatement the last statement interface.
355 }
356 procedure TZOraclePreparedStatement.SetLastStatement(
357  LastStatement: IZStatement);
358 begin
359  if FLastStatement <> nil then
360  FLastStatement.Close;
361  FLastStatement := LastStatement;
362 end;
363 
364 {**
365  Creates a temporary statement which executes queries.
366  @param Info a statement parameters.
367  @return a created statement object.
368 }
369 function TZOraclePreparedStatement.GetExecStatement: IZStatement;
370 begin
371  if ExecStatement = nil then
372  begin
373  ExecStatement := TZOracleStatement.Create(FPlainDriver, Connection, Info);
374 
375  ExecStatement.SetMaxFieldSize(GetMaxFieldSize);
376  ExecStatement.SetMaxRows(GetMaxRows);
377  ExecStatement.SetEscapeProcessing(EscapeProcessing);
378  ExecStatement.SetQueryTimeout(GetQueryTimeout);
379  ExecStatement.SetCursorName(CursorName);
380 
381  ExecStatement.SetFetchDirection(GetFetchDirection);
382  ExecStatement.SetFetchSize(GetFetchSize);
383  ExecStatement.SetResultSetConcurrency(GetResultSetConcurrency);
384  ExecStatement.SetResultSetType(GetResultSetType);
385  end;
386  Result := ExecStatement;
387 end;
388 
389 {**
390  Converts an SQL query into Oracle format.
391  @param SQL a query with parameters defined with '?'
392  @returns a query with parameters in Oracle format ':pN'.
393 }
394 function TZOraclePreparedStatement.ConvertToOracleSQLQuery(SQL: string): RawByteString;
395 var
396  I, N: Integer;
397  Tokens: TStrings;
398 begin
399  if Pos('?', SQL) > 0 then
400  begin
401  Tokens := Connection.GetDriver.GetTokenizer.
402  TokenizeBufferToList(SQL, [toUnifyWhitespaces]);
403  try
404  Result := '';
405  N := 0;
406  for I := 0 to Tokens.Count - 1 do
407  if Tokens[I] = '?' then
408  begin
409  Inc(N);
410  Result := Result + ':P' + RawByteString(IntToStr(N));
411  end else
412  Result := Result + ZPlainString(Tokens[I]);
413  finally
414  Tokens.Free;
415  end;
416  end else
417  Result := GetEncodedSQL(SQL);
418 end;
419 
420 {**
421  Closes this statement and frees all resources.
422 }
423 procedure TZOraclePreparedStatement.Close;
424 begin
425  inherited Close;
426  if LastStatement <> nil then
427  begin
428  FLastStatement.Close;
429  FLastStatement := nil;
430  end;
431  FreeOracleStatementHandles(FPlainDriver, FHandle, FErrorHandle);
432  FreeOracleSQLVars(FPlainDriver, FInVars, (Connection as IZOracleConnection).GetConnectionHandle, FErrorHandle, ConSettings);
433 end;
434 
435 {**
436  Executes an SQL statement that may return multiple results.
437  Under some (uncommon) situations a single SQL statement may return
438  multiple result sets and/or update counts. Normally you can ignore
439  this unless you are (1) executing a stored procedure that you know may
440  return multiple results or (2) you are dynamically executing an
441  unknown SQL string. The methods <code>execute</code>,
442  <code>getMoreResults</code>, <code>getResultSet</code>,
443  and <code>getUpdateCount</code> let you navigate through multiple results.
444 
445  The <code>execute</code> method executes an SQL statement and indicates the
446  form of the first result. You can then use the methods
447  <code>getResultSet</code> or <code>getUpdateCount</code>
448  to retrieve the result, and <code>getMoreResults</code> to
449  move to any subsequent result(s).
450 
451  @param sql any SQL statement
452  @return <code>true</code> if the next result is a <code>ResultSet</code> object;
453  <code>false</code> if it is an update count or there are no more results
454 }
455 function TZOraclePreparedStatement.Execute(const SQL: RawByteString): Boolean;
456 begin
457  LastStatement := GetExecStatement;
458  Result := LastStatement.Execute(SQL);
459  if Result then
460  LastResultSet := LastStatement.GetResultSet
461  else
462  LastUpdateCount := LastStatement.GetUpdateCount;
463 end;
464 
465 {**
466  Executes an SQL statement that returns a single <code>ResultSet</code> object.
467  @param sql typically this is a static SQL <code>SELECT</code> statement
468  @return a <code>ResultSet</code> object that contains the data produced by the
469  given query; never <code>null</code>
470 }
471 function TZOraclePreparedStatement.ExecuteQuery(const SQL: RawByteString): IZResultSet;
472 begin
473  Result := GetExecStatement.ExecuteQuery(SQL);
474 end;
475 
476 {**
477  Executes an SQL <code>INSERT</code>, <code>UPDATE</code> or
478  <code>DELETE</code> statement. In addition,
479  SQL statements that return nothing, such as SQL DDL statements,
480  can be executed.
481 
482  @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
483  <code>DELETE</code> statement or an SQL statement that returns nothing
484  @return either the row count for <code>INSERT</code>, <code>UPDATE</code>
485  or <code>DELETE</code> statements, or 0 for SQL statements that return nothing
486 }
487 function TZOraclePreparedStatement.ExecuteUpdate(const SQL: RawByteString): Integer;
488 begin
489  Result := GetExecStatement.ExecuteUpdate(SQL);
490  LastUpdateCount := Result;
491 end;
492 
493 {**
494  Prepares an SQL statement
495 }
496 procedure TZOraclePreparedStatement.Prepare;
497 var
498  I: Integer;
499  Status: Integer;
500  TypeCode: ub2;
501  CurrentVar: PZSQLVar;
502 begin
503  if not Prepared then
504  begin
505  { Allocates statement handles. }
506  if (FHandle = nil) or (FErrorHandle = nil) then
507  begin
508  AllocateOracleStatementHandles(FPlainDriver, Connection,
509  FHandle, FErrorHandle);
510  end;
511 
512  PrepareOracleStatement(FPlainDriver, ASQL, LogSQL, Handle, ErrorHandle,
513  StrToIntDef(Info.Values['prefetch_count'], 100), ConSettings);
514 
515  AllocateOracleSQLVars(FInVars, InParamCount);
516  InVars^.ActualNum := InParamCount;
517 
518  for I := 0 to InParamCount - 1 do
519  begin
520  CurrentVar := @FInVars.Variables[I + 1];
521  CurrentVar.Handle := nil;
522 
523  { Artificially define Oracle internal type. }
524  if InParamTypes[I] in [stBytes, stBinaryStream] then
525  TypeCode := SQLT_BLOB
526  else if InParamTypes[I] = stAsciiStream then
527  TypeCode := SQLT_CLOB
528  else if InParamTypes[I] = stUnicodeStream then
529  TypeCode := SQLT_CLOB
530  else TypeCode := SQLT_STR;
531 
532  InitializeOracleVar(FPlainDriver, Connection, CurrentVar,
533  InParamTypes[I], TypeCode, 1024);
534 
535  Status := FPlainDriver.BindByPos(FHandle, CurrentVar.BindHandle,
536  FErrorHandle, I + 1, CurrentVar.Data, CurrentVar.Length,
537  CurrentVar.TypeCode, @CurrentVar.Indicator, nil, nil, 0, nil,
538  OCI_DEFAULT);
539  CheckOracleError(FPlainDriver, FErrorHandle, Status, lcExecute, LogSQL);
540  end;
541 
542  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
543  Prepared := True;
544  end;
545 end;
546 
547 {**
548  Executes the SQL query in this <code>PreparedStatement</code> object
549  and returns the result set generated by the query.
550 
551  @return a <code>ResultSet</code> object that contains the data produced by the
552  query; never <code>null</code>
553 }
554 function TZOraclePreparedStatement.ExecutePrepared: Boolean;
555 var
556  StatementType: ub2;
557 begin
558  Result := False;
559 
560  { Prepares a statement. }
561  if not Prepared then
562  Prepare;
563 
564  { Loads binded variables with values. }
565  LoadOracleVars(FPlainDriver, Connection, ErrorHandle,
566  FInVars, InParamValues, ChunkSize);
567 
568  StatementType := 0;
569  FPlainDriver.AttrGet(Handle, OCI_HTYPE_STMT, @StatementType, nil,
570  OCI_ATTR_STMT_TYPE, ErrorHandle);
571 
572  if StatementType = OCI_STMT_SELECT then
573  begin
574  { Executes the statement and gets a resultset. }
575  LastResultSet := CreateOracleResultSet(FPlainDriver, Self,
576  SQL, Handle, ErrorHandle);
577  Result := LastResultSet <> nil;
578  end
579  else
580  begin
581  { Executes the statement and gets a result. }
582  ExecuteOracleStatement(FPlainDriver, Connection, LogSQL,
583  Handle, ErrorHandle);
584  LastUpdateCount := GetOracleUpdateCount(FPlainDriver, Handle, ErrorHandle);
585  end;
586 
587  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
588 
589  { Unloads binded variables with values. }
590  UnloadOracleVars(FInVars);
591 
592  { Autocommit statement. }
593  if not Result and Connection.GetAutoCommit then
594  Connection.Commit;
595 end;
596 
597 {**
598  Executes the SQL query in this <code>PreparedStatement</code> object
599  and returns the result set generated by the query.
600 
601  @return a <code>ResultSet</code> object that contains the data produced by the
602  query; never <code>null</code>
603 }
604 function TZOraclePreparedStatement.ExecuteQueryPrepared: IZResultSet;
605 begin
606  { Prepares a statement. }
607  if not Prepared then
608  Prepare;
609 
610  { Loads binded variables with values. }
611  LoadOracleVars(FPlainDriver, Connection, ErrorHandle,
612  FInVars, InParamValues,ChunkSize);
613 
614  { Executes the statement and gets a resultset. }
615  Result := CreateOracleResultSet(FPlainDriver, Self, SQL,
616  Handle, ErrorHandle);
617 
618  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, SQL);
619 
620  { Unloads binded variables with values. }
621  UnloadOracleVars(FInVars);
622 end;
623 
624 {**
625  Executes the SQL INSERT, UPDATE or DELETE statement
626  in this <code>PreparedStatement</code> object.
627  In addition,
628  SQL statements that return nothing, such as SQL DDL statements,
629  can be executed.
630 
631  @return either the row count for INSERT, UPDATE or DELETE statements;
632  or 0 for SQL statements that return nothing
633 }
634 function TZOraclePreparedStatement.ExecuteUpdatePrepared: Integer;
635 var
636  StatementType: ub2;
637  ResultSet: IZResultSet;
638 begin
639  { Prepares a statement. }
640  if not Prepared then
641  Prepare;
642 
643  { Loads binded variables with values. }
644  LoadOracleVars(FPlainDriver, Connection, ErrorHandle,
645  FInVars, InParamValues, ChunkSize);
646 
647  try
648  StatementType := 0;
649  FPlainDriver.AttrGet(Handle, OCI_HTYPE_STMT, @StatementType, nil,
650  OCI_ATTR_STMT_TYPE, ErrorHandle);
651 
652  if StatementType = OCI_STMT_SELECT then
653  begin
654  { Executes the statement and gets a resultset. }
655  ResultSet := CreateOracleResultSet(FPlainDriver, Self,
656  SQL, Handle, ErrorHandle);
657  try
658  while ResultSet.Next do;
659  LastUpdateCount := ResultSet.GetRow;
660  finally
661  ResultSet.Close;
662  end;
663  end
664  else
665  begin
666  { Executes the statement and gets a result. }
667  ExecuteOracleStatement(FPlainDriver, Connection, LogSQL,
668  Handle, ErrorHandle);
669  LastUpdateCount := GetOracleUpdateCount(FPlainDriver, Handle, ErrorHandle);
670  end;
671  Result := LastUpdateCount;
672 
673  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
674  finally
675  { Unloads binded variables with values. }
676  UnloadOracleVars(FInVars);
677  end;
678 
679  { Autocommit statement. }
680  if Connection.GetAutoCommit then
681  Connection.Commit;
682 end;
683 
684 {**
685  Gets statement handle.
686  @return statement handle.
687 }
688 function TZOraclePreparedStatement.GetStatementHandle: POCIStmt;
689 begin
690  Result := FHandle;
691 end;
692 
693 procedure TZOracleCallableStatement.Prepare;
694 var
695  I: Integer;
696  Status: Integer;
697  TypeCode: ub2;
698  CurrentVar: PZSQLVar;
699  SQLType:TZSQLType;
700 begin
701  if not FPrepared then
702  begin
703  ArrangeInParams; //need to sort ReturnValues for functions
704  ASQL := GetProcedureSql(False);
705  SetLength(FParamNames, FOracleParamsCount);
706  for i := 0 to FOracleParamsCount -1 do
707  FParamNames[I] := Self.FOracleParams[I].pName;
708 
709  { Allocates statement handles. }
710  if (FHandle = nil) or (FErrorHandle = nil) then
711  begin
712  AllocateOracleStatementHandles(FPlainDriver, Connection,
713  FHandle, FErrorHandle);
714  end;
715 
716  PrepareOracleStatement(FPlainDriver, ASQL, LogSQL, FHandle, FErrorHandle,
717  StrToIntDef(Info.Values['prefetch_count'], 100), ConSettings);
718  //make sure eventual old buffers are cleaned
719  FreeOracleSQLVars(FPlainDriver, FInVars, (Connection as IZOracleConnection).GetConnectionHandle, FErrorHandle, ConSettings);
720  AllocateOracleSQLVars(FInVars, FOracleParamsCount);
721  FInVars^.ActualNum := FOracleParamsCount;
722 
723  for I := 0 to FOracleParamsCount - 1 do
724  begin
725  CurrentVar := @FInVars.Variables[I + 1];
726  CurrentVar.Handle := nil;
727  SQLType := TZSQLType(FOracleParams[I].pSQLType);
728 
729  { Artificially define Oracle internal type. }
730  if SQLType = stBinaryStream then
731  TypeCode := SQLT_BLOB
732  else if SQLType in [stAsciiStream, stUnicodeStream] then
733  TypeCode := SQLT_CLOB
734  else TypeCode := SQLT_STR;
735 
736  InitializeOracleVar(FPlainDriver, Connection, CurrentVar,
737  SQLType, TypeCode, 1024);
738 
739  Status := FPlainDriver.BindByPos(FHandle, CurrentVar.BindHandle,
740  FErrorHandle, I + 1, CurrentVar.Data, CurrentVar.Length,
741  CurrentVar.TypeCode, @CurrentVar.Indicator, nil, nil, 0, nil,
742  OCI_DEFAULT);
743  CheckOracleError(FPlainDriver, FErrorHandle, Status, lcExecute, LogSQL);
744  end;
745  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
746  end;
747 end;
748 
749 
750 procedure TZOracleCallableStatement.RegisterOutParameter(ParameterIndex,
751  SQLType: Integer);
752 begin
753  inherited RegisterOutParameter(ParameterIndex,SQLType);
754  with FOracleParams[ParameterIndex-1] do
755  begin
756  if not GetConnection.UseMetadata then
757  pName := 'pOut'+IntToStr(ParameterIndex);
758  pSQLType := SQLType;
759  end;
760 end;
761 
762 procedure TZOracleCallableStatement.RegisterParamType(ParameterIndex: integer;
763  ParamType: Integer);
764 begin
765  inherited RegisterParamType(ParameterIndex, ParamType);
766  if ParameterIndex > High(FOracleParams) then
767  SetLength(FOracleParams, ParameterIndex);
768  if ParameterIndex > FOracleParamsCount then
769  FOracleParamsCount := ParameterIndex;
770  FOracleParams[ParameterIndex-1].pType := ParamType;
771  FOracleParams[ParameterIndex-1].pParamIndex := ParameterIndex;
772  if ParamType in [2,3,4] then //ptInOut, ptOut, ptResult
773  begin
774  Inc(FOutParamCount);
775  FOracleParams[ParameterIndex-1].pOutIndex := FOutParamCount;
776  end;
777 end;
778 
779 procedure TZOracleCallableStatement.SetInParam(ParameterIndex: Integer;
780  SQLType: TZSQLType; const Value: TZVariant);
781 var
782  AConnection: IZConnection;
783 
784  function GetOracleParamIndexOfParameterIndex: Integer;
785  var I: Integer;
786  begin
787  Result := 0;
788  for i := 0 to high(FOracleParams) do
789  if ParameterIndex = FOracleParams[i].pParamIndex then
790  begin
791  Result := I;
792  Break;
793  end;
794  end;
795 
796 begin
797  inherited SetInParam(ParameterIndex, SQLType, Value);
798  with FOracleParams[GetOracleParamIndexOfParameterIndex] do
799  begin
800  AConnection := GetConnection;
801  if Assigned(AConnection) and ( not AConnection.UseMetadata ) then
802  pName := 'p'+IntToStr(ParameterIndex);
803  pSQLType := ord(SQLType);
804  pValue := Value;
805  end;
806 end;
807 
808 procedure TZOracleCallableStatement.RegisterParamTypeAndName(const ParameterIndex: integer;
809  const ParamTypeName, ParamName: String; Const ColumnSize, Precision: Integer);
810 var
811  iPos: Integer;
812  ProcName: String;
813 begin
814  FOracleParams[ParameterIndex].pName := ParamName;
815  FOracleParams[ParameterIndex].pTypeName := ParamTypeName;
816  iPos := Pos('.', ParamName);
817  if iPos > 0 then
818  begin
819  ProcName := Copy(ParamName, 1, iPos-1); //extract function or Procedure names
820  FOracleParams[ParameterIndex].pProcIndex := PackageIncludedList.IndexOf(ProcName); //check index
821  if FOracleParams[ParameterIndex].pProcIndex = -1 then //if not exists
822  FOracleParams[ParameterIndex].pProcIndex := PackageIncludedList.Add(ProcName); //Add to List
823  end
824  else //No package
825  FOracleParams[ParameterIndex].pProcIndex := 0;
826 end;
827 
828 procedure TZOracleCallableStatement.ArrangeInParams;
829 var
830  I, J, NewProcIndex, StartProcIndex: Integer;
831  TempVars: TZVariantDynArray;
832  TempOraVar: TZOracleParam;
833 begin
834  NewProcIndex := -1;
835  StartProcIndex := 0;
836  if IsFunction then
837  begin
838  for i := 0 to high(FOracleParams) do
839  begin
840  if not ( FOracleParams[i].pProcIndex = NewProcIndex ) then
841  begin
842  NewProcIndex := FOracleParams[i].pProcIndex;
843  StartProcIndex := I;
844  end;
845  if ( FOracleParams[i].pType = 4 ) then
846  begin
847  DefVarManager.SetNull(FOracleParams[i].pValue);
848  if not (i = StartProcIndex) then
849  begin
850  TempOraVar := FOracleParams[I];
851  for J := I downto StartProcIndex+1 do
852  FOracleParams[j] := FOracleParams[j-1];
853  FOracleParams[StartProcIndex] := TempOraVar;
854  end;
855  end;
856  end;
857  SetLength(TempVars, Length(FOracleParams));
858  for i := 0 to high(FOracleParams) do
859  TempVars[i] := FOracleParams[i].pValue;
860  InParamValues := TempVars;
861  end;
862 end;
863 
864 procedure TZOracleCallableStatement.FetchOutParamsFromOracleVars;
865 var
866  CurrentVar: PZSQLVar;
867  LobLocator: POCILobLocator;
868  I: integer;
869  TempBlob: IZBlob;
870 
871  procedure SetOutParam(CurrentVar: PZSQLVar; Index: Integer);
872  var
873  OracleConnection :IZOracleConnection;
874  Year:SmallInt;
875  Month, Day:Byte; Hour, Min, Sec:ub1; MSec: ub4;
876  dTmp:TDateTime;
877  ps: PAnsiChar;
878  begin
879  case CurrentVar.TypeCode of
880  SQLT_INT: DefVarManager.SetAsInteger( outParamValues[Index], PLongInt(CurrentVar.Data)^ );
881  SQLT_FLT: DefVarManager.SetAsFloat( outParamValues[Index], PDouble(CurrentVar.Data)^ );
882  SQLT_STR:
883  begin
884  GetMem(ps,1025);
885  try
886  {$IFDEF WITH_STRLCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrLCopy( ps, (CurrentVar.Data), 1024);
887  DefVarManager.SetAsString( OutParamValues[Index], ZDbcString(ps) );
888  finally
889  FreeMem(ps);
890  end;
891  end;
892  SQLT_TIMESTAMP:
893  begin
894  OracleConnection := Connection as IZOracleConnection;
895  FPlainDriver.DateTimeGetDate(
896  OracleConnection.GetConnectionHandle ,
897  FErrorHandle, PPOCIDescriptor(CurrentVar.Data)^,
898  Year, Month, Day);
899  FPlainDriver.DateTimeGetTime(
900  OracleConnection.GetConnectionHandle ,
901  FErrorHandle, PPOCIDescriptor(CurrentVar.Data)^,
902  Hour, Min, Sec,MSec);
903  dTmp := EncodeDate(year,month,day )+EncodeTime(Hour,min,sec,msec) ;
904  DefVarManager.SetAsDateTime( outParamValues[Index], dTmp );
905  end;
906  SQLT_BLOB, SQLT_CLOB, SQLT_BFILEE, SQLT_CFILEE:
907  begin
908  if CurrentVar.Indicator >= 0 then
909  LobLocator := PPOCIDescriptor(CurrentVar.Data)^
910  else
911  LobLocator := nil;
912 
913  OracleConnection := Connection as IZOracleConnection;
914  TempBlob := TZOracleBlob.Create(FPlainDriver, nil, 0, OracleConnection,
915  LobLocator, CurrentVar.ColType, GetChunkSize);
916  (TempBlob as IZOracleBlob).ReadBlob;
917  DefVarManager.SetAsInterface(outParamValues[Index], TempBlob);
918  TempBlob := nil;
919  end;
920  SQLT_NTY:
921  DefVarManager.SetAsInterface(outParamValues[Index],
922  TZOracleBlob.CreateWithStream(nil, GetConnection));
923  end;
924  end;
925 begin
926  for I := 0 to FOracleParamsCount -1 do
927  if FOracleParams[i].pType in [2,3,4] then
928  begin
929  CurrentVar:= @FInVars.Variables[I+1];
930  CurrentVar.Data := CurrentVar.DupData;
931  SetOutParam(CurrentVar, FOracleParams[i].pParamIndex-1);
932  end;
933 end;
934 
935 function TZOracleCallableStatement.GetProcedureSql(SelectProc: boolean): RawByteString;
936 var
937  sFunc: string;
938  I, IncludeCount, LastIndex: Integer;
939  PackageBody: TStrings;
940  TempResult: String;
941 
942  function GenerateParamsStr(Count: integer): string;
943  var
944  I: integer;
945  begin
946  for I := 0 to Count - 1 do
947  begin
948  if ( FDBParamTypes[I] = 4 ) then //ptResult
949  begin
950  sFunc := ' :'+FOracleParams[0].pName+' := ';
951  continue;
952  end;
953  if Result <> '' then
954  Result := Result + ',';
955  if IsFunction then
956  Result := Result + ':'+FOracleParams[I+1].pName
957  else
958  Result := Result + ':'+FOracleParams[I].pName;
959  end;
960  Result := '('+Result+')'
961  end;
962 
963 var
964  InParams: string;
965 begin
966  sFunc := '';
967  if PackageIncludedList.Count > 0 then
968  begin
969  PackageBody := TStringList.Create;
970  PackageBody.Add('BEGIN');
971  LastIndex := 0;
972  for IncludeCount := 0 to PackageIncludedList.Count -1 do
973  begin
974  InParams := '';
975  sFunc := '';
976  for i := LastIndex to high(FOracleParams) do
977  if IncludeCount = FOracleParams[i].pProcIndex then
978  if ( FOracleParams[I].pType = 4 ) then //ptResult
979  sFunc := ' :'+StringReplace(FOracleParams[I].pName, '.', '', [rfReplaceAll])+' := '
980  else
981  if InParams <> '' then
982  InParams := InParams +', :'+StringReplace(FOracleParams[I].pName, '.', '', [rfReplaceAll])
983  else
984  InParams := InParams +':'+StringReplace(FOracleParams[I].pName, '.', '', [rfReplaceAll])
985  else
986  begin
987  LastIndex := I;
988  break;
989  end;
990  PackageBody.Add('BEGIN '+sFunc+SQL+
991  '.'+GetConnection.GetMetadata.GetIdentifierConvertor.Quote(PackageIncludedList[IncludeCount])+'('+InParams+'); END;');
992  end;
993  PackageBody.Add('END;');
994  TempResult := TrimRight(PackageBody.Text);
995  FreeAndNil(PackageBody);
996  end
997  else
998  begin
999  InParams := GenerateParamsStr( FOracleParamsCount );
1000  TempResult := 'BEGIN ' + sFunc +SQL + InParams+'; END;';
1001  end;
1002  Result := ZPlainString(TempResult);
1003 end;
1004 
1005 function TZOracleCallableStatement.IsNull(ParameterIndex: Integer): Boolean;
1006 begin
1007  result := inherited IsNull(ParameterIndex);
1008 end;
1009 
1010 procedure TZOracleCallableStatement.ClearParameters;
1011 begin
1012  inherited;
1013  FOracleParamsCount := 0;
1014  SetLength(FOracleParams, 0);
1015 end;
1016 
1017 constructor TZOracleCallableStatement.Create(Connection: IZConnection;
1018  const pProcName: string; Info: TStrings);
1019 begin
1020 
1021  inherited Create(Connection, pProcName, Info);
1022 
1023  FOracleParamsCount := 0;
1024  FPlainDriver := Connection.GetIZPlainDriver as IZOraclePlainDriver;
1025  ResultSetType := rtForwardOnly;
1026  FPrepared := False;
1027  PackageIncludedList := TStringList.Create;
1028  FOutParamCount := 0;
1029 end;
1030 
1031 destructor TZOracleCallableStatement.Destroy;
1032 begin
1033  FreeOracleSQLVars(FPlainDriver, FInVars, (Connection as IZOracleConnection).GetConnectionHandle, FErrorHandle, ConSettings);
1034  PackageIncludedList.Free;
1035  inherited;
1036 end;
1037 
1038 function TZOracleCallableStatement.ExecuteUpdatePrepared: Integer;
1039 begin
1040  { Prepares a statement. }
1041  if not Prepared then
1042  Prepare;
1043 
1044  { Loads binded variables with values. }
1045  LoadOracleVars(FPlainDriver , Connection, FErrorHandle,
1046  FInVars, InParamValues, ChunkSize);
1047 
1048  try
1049  ExecuteOracleStatement(FPlainDriver, Connection, LogSQL,
1050  FHandle, FErrorHandle);
1051  LastUpdateCount := GetOracleUpdateCount(FPlainDriver, FHandle, FErrorHandle);
1052  FetchOutParamsFromOracleVars;
1053  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
1054  finally
1055  { Unloads binded variables with values. }
1056  UnloadOracleVars(FInVars);
1057  end;
1058 
1059  { Autocommit statement. }
1060  if Connection.GetAutoCommit then
1061  Connection.Commit;
1062 
1063  Result := LastUpdateCount;
1064 end;
1065 
1066 function TZOracleCallableStatement.ExecuteQueryPrepared: IZResultSet;
1067 begin
1068  { Prepares a statement. }
1069  if not Prepared then
1070  Prepare;
1071 
1072  { Loads binded variables with values. }
1073  LoadOracleVars(FPlainDriver , Connection, FErrorHandle,
1074  FInVars, InParamValues, ChunkSize);
1075 
1076  try
1077  ExecuteOracleStatement(FPlainDriver, Connection, LogSQL,
1078  FHandle, FErrorHandle);
1079  FetchOutParamsFromOracleVars;
1080  LastResultSet := CreateOracleResultSet(FPlainDriver, Self, LogSQL,
1081  FHandle, FErrorHandle, FInVars, FOracleParams);
1082  Result := LastResultSet;
1083  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
1084  finally
1085  { Unloads binded variables with values. }
1086  UnloadOracleVars(FInVars);
1087  end;
1088 end;
1089 
1090 end.