zeoslib  UNKNOWN
 All Files
ZStoredProcedure.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { Abstract StoredProc component }
5 { }
6 { Originally written by Sergey Seroukhov }
7 { & Janos Fegyverneki }
8 { }
9 {*********************************************************}
10 
11 {@********************************************************}
12 { Copyright (c) 1999-2012 Zeos Development Group }
13 { }
14 { License Agreement: }
15 { }
16 { This library is distributed in the hope that it will be }
17 { useful, but WITHOUT ANY WARRANTY; without even the }
18 { implied warranty of MERCHANTABILITY or FITNESS FOR }
19 { A PARTICULAR PURPOSE. See the GNU Lesser General }
20 { Public License for more details. }
21 { }
22 { The source code of the ZEOS Libraries and packages are }
23 { distributed under the Library GNU General Public }
24 { License (see the file COPYING / COPYING.ZEOS) }
25 { with the following modification: }
26 { As a special exception, the copyright holders of this }
27 { library give you permission to link this library with }
28 { independent modules to produce an executable, }
29 { regardless of the license terms of these independent }
30 { modules, and to copy and distribute the resulting }
31 { executable under terms of your choice, provided that }
32 { you also meet, for each linked independent module, }
33 { the terms and conditions of the license of that module. }
34 { An independent module is a module which is not derived }
35 { from or based on this library. If you modify this }
36 { library, you may extend this exception to your version }
37 { of the library, but you are not obligated to do so. }
38 { If you do not wish to do so, delete this exception }
39 { statement from your version. }
40 { }
41 { }
42 { The project web site is located on: }
43 { http://zeos.firmos.at (FORUM) }
44 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
45 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
46 { }
47 { http://www.sourceforge.net/projects/zeoslib. }
48 { }
49 { }
50 { Zeos Development Group. }
51 {********************************************************@}
52 
53 unit ZStoredProcedure;
54 
55 interface
56 
57 {$I ZComponent.inc}
58 
59 uses
60  Types, SysUtils, Classes, {$IFDEF MSEgui}mclasses, mdb{$ELSE}DB{$ENDIF},
61  ZDbcIntfs, ZAbstractDataset, ZCompatibility;
62 
63 type
64 
65  {**
66  Abstract dataset to access to stored procedures.
67  }
68  TZStoredProc = class(TZAbstractDataset)
69  private
70  FMetaResultSet: IZResultset;
71  procedure RetrieveParamValues;
72  function GetStoredProcName: string;
73  procedure SetStoredProcName(const Value: string);
74  function GetParamType(const Value: TZProcedureColumnType): TParamType;
75  protected
76  function CreateStatement(const SQL: string; Properties: TStrings):
77  IZPreparedStatement; override;
78  procedure SetStatementParams(Statement: IZPreparedStatement;
79  ParamNames: TStringDynArray; Params: TParams;
80  DataLink: TDataLink); override;
81  procedure InternalOpen; override;
82  procedure InternalClose; override;
83 
84  protected
85  {$IFDEF WITH_IPROVIDER}
86  function PSIsSQLBased: Boolean; override;
87  procedure PSExecute; override;
88  {$IFDEF WITH_IPROVIDERWIDE}
89  function PSGetTableNameW: WideString; override;
90  {$ELSE}
91  function PSGetTableName: string; override;
92  {$ENDIF}
93  procedure PSSetCommandText(const ACommandText: string); override;
94  {$ENDIF}
95 
96  public
97  procedure ExecProc; virtual;
98 
99  procedure FirstResultSet;
100  procedure PreviousResultSet;
101  procedure NextResultSet;
102  procedure LastResultSet;
103  procedure SetResultSet(const Index: Integer);
104  function ResultSetCount: Integer;
105  function BOR: Boolean;
106  function EOR: Boolean;
107  published
108  property Active;
109  property ParamCheck;
110  property Params;
111  property ShowRecordTypes;
112  property Options;
113  property StoredProcName: string read GetStoredProcName
114  write SetStoredProcName;
115  end;
116 
117 implementation
118 
119 uses
120  ZAbstractRODataset, ZMessages, ZDatasetUtils
121  {$IFDEF WITH_ASBYTES}, ZSysUtils{$ENDIF}
122  {$IFDEF WITH_INLINE_ANSICOMPARETEXT}, Windows{$ENDIF};
123 
124 { TZStoredProc }
125 
126 {**
127  Creates a DBC statement for the query.
128  @param SQL an SQL query.
129  @param Properties a statement specific properties.
130  @returns a created DBC statement.
131 }
132 {$IFDEF FPC}
133  {$HINTS OFF}
134 {$ENDIF}
135 function TZStoredProc.CreateStatement(const SQL: string; Properties: TStrings):
136  IZPreparedStatement;
137 var
138  I: Integer;
139  CallableStatement: IZCallableStatement;
140  Catalog, Schema, ObjectName: string;
141 begin
142  CallableStatement := Connection.DbcConnection.PrepareCallWithParams(
143  Trim(SQL), Properties);
144 
145  CallableStatement.ClearParameters;
146 
147  if Supports(CallableStatement, IZParamNamedCallableStatement) then
148  if Assigned(FMetaResultSet) then
149  FMetaResultSet.BeforeFirst
150  else
151  begin //i need allways all types to cast and there names
152  SplitQualifiedObjectName(Trim(SQL), Catalog, Schema, ObjectName);
153  ObjectName := Connection.DbcConnection.GetMetadata.AddEscapeCharToWildcards(ObjectName);
154  FMetaResultSet := Connection.DbcConnection.GetMetadata.GetProcedureColumns(Catalog, Schema, ObjectName, '');
155  end;
156 
157  for I := 0 to Params.Count - 1 do
158  begin
159  CallableStatement.RegisterParamType( I+1, ord(Params[I].ParamType));
160 
161  if Params[I].ParamType in [ptResult, ptOutput, ptInputOutput] then
162  CallableStatement.RegisterOutParameter(I + 1,
163  Ord(ConvertDatasetToDbcType(Params[I].DataType)));
164 
165  if Supports(CallableStatement, IZParamNamedCallableStatement) and
166  Assigned(FMetaResultSet) then
167  if FMetaResultSet.Next then
168  (CallableStatement as IZParamNamedCallableStatement).RegisterParamTypeAndName(
169  I, FMetaResultSet.GetString(7), Params[i].Name, FMetaResultSet.GetInt(8),
170  FMetaResultSet.GetInt(9));
171  end;
172  Result := CallableStatement;
173 end;
174 {$IFDEF FPC}
175  {$HINTS ON}
176 {$ENDIF}
177 
178 {**
179  Fill prepared statement with parameters.
180  @param Statement a prepared SQL statement.
181  @param ParamNames an array of parameter names.
182  @param Params a collection of SQL parameters.
183  @param DataLink a datalink to get parameters.
184 }
185 {$IFDEF FPC}
186  {$HINTS OFF}
187 {$ENDIF}
188 procedure TZStoredProc.SetStatementParams(Statement: IZPreparedStatement;
189  ParamNames: TStringDynArray; Params: TParams; DataLink: TDataLink);
190 var
191  I: Integer;
192  Param: TParam;
193 begin
194  for I := 0 to Params.Count - 1 do
195  begin
196  Param := Params[I];
197 
198  if Params[I].ParamType in [ptResult, ptOutput] then
199  Continue;
200 
201  SetStatementParam(I+1, Statement, Param);
202  end;
203 end;
204 {$IFDEF FPC}
205  {$HINTS ON}
206 {$ENDIF}
207 
208 {**
209  Retrieves parameter values from callable statement.
210 }
211 procedure TZStoredProc.RetrieveParamValues;
212 var
213  I: Integer;
214  Param: TParam;
215  FCallableStatement: IZCallableStatement;
216  TempBlob: IZBlob;
217 begin
218  if Assigned(Statement) then
219  Statement.QueryInterface(IZCallableStatement, FCallableStatement);
220  if not Assigned(FCallableStatement) then
221  Exit;
222 
223  for I := 0 to Params.Count - 1 do
224  begin
225  Param := Params[I];
226 
227  if not (Param.ParamType in [ptResult, ptOutput, ptInputOutput]) then
228  Continue;
229 
230  if FCallableStatement.IsNull(I + 1) then
231  Param.Clear
232  else
233  case Param.DataType of
234  ftBoolean:
235  Param.AsBoolean := FCallableStatement.GetBoolean(I + 1);
236  ftSmallInt:
237  Param.AsSmallInt := FCallableStatement.GetShort(I + 1);
238  ftInteger, ftAutoInc:
239  Param.AsInteger := FCallableStatement.GetInt(I + 1);
240  ftFloat:
241  Param.AsFloat := FCallableStatement.GetDouble(I + 1);
242  ftLargeInt:
243  Param.Value := FCallableStatement.GetLong(I + 1);
244  ftString:
245  begin
246  Param.AsString := FCallableStatement.GetString(I + 1);
247  {$IFDEF UNICODE}Param.DataType := ftString;{$ENDIF} //Hack: D12_UP sets ftWideString on assigning a UnicodeString
248  end;
249  ftWideString:
250  {$IFDEF WITH_FTWIDESTRING}Param.AsWideString{$ELSE}Param.Value{$ENDIF} := FCallableStatement.GetUnicodeString(I + 1);
251  ftMemo:
252  begin
253  Param.AsMemo := FCallableStatement.GetString(I + 1);
254  {$IFDEF UNICODE}Param.DataType := ftMemo;{$ENDIF} //Hack: D12_UP sets ftWideMemo on assigning a UnicodeString
255  end;
256  {$IFDEF WITH_WIDEMEMO}
257  ftWideMemo:
258  begin
259  Param.AsWideString := FCallableStatement.GetUnicodeString(I + 1);
260  Param.DataType := ftWideMemo;
261  end;
262  {$ENDIF}
263  ftBytes, ftVarBytes:
264  Param.Value := FCallableStatement.GetBytes(I + 1);
265  ftDate:
266  Param.AsDate := FCallableStatement.GetDate(I + 1);
267  ftTime:
268  Param.AsTime := FCallableStatement.GetTime(I + 1);
269  ftDateTime:
270  Param.AsDateTime := FCallableStatement.GetTimestamp(I + 1);
271  ftBlob:
272  begin
273  TempBlob := FCallableStatement.GetValue(I +1).VInterface as IZBlob;
274  if not TempBlob.IsEmpty then
275  Param.SetBlobData({$IFDEF WITH_TVALUEBUFFER}TValueBuffer{$ENDIF}(TempBlob.GetBuffer), TempBlob.Length);
276  TempBlob := nil;
277  end
278  else
279  raise EZDatabaseError.Create(SUnKnownParamDataType);
280  end;
281  end;
282 end;
283 
284 {**
285  Performs internal query opening.
286 }
287 procedure TZStoredProc.InternalOpen;
288 begin
289  inherited InternalOpen;
290 
291  RetrieveParamValues;
292 end;
293 
294 {**
295  Performs internal query closing.
296 }
297 procedure TZStoredProc.InternalClose;
298 begin
299  inherited InternalClose;
300 end;
301 
302 function TZStoredProc.GetStoredProcName: string;
303 begin
304  Result := Trim(SQL.Text);
305 end;
306 
307 {$IFDEF FPC}
308  {$HINTS OFF}
309 {$ENDIF}
310 procedure TZStoredProc.SetStoredProcName(const Value: string);
311 var
312  OldParams: TParams;
313  Catalog, Schema, ObjectName: string;
314  ColumnType: Integer;
315 begin
316  if AnsiCompareText(Trim(SQL.Text), Trim(Value)) <> 0 then
317  begin
318  SQL.Text := Value;
319  if ParamCheck and (Value <> '') and not (csLoading in ComponentState) and Assigned(Connection) then
320  begin
321  CheckConnected;
322  Connection.ShowSQLHourGlass;
323  try
324  SplitQualifiedObjectName(Value,
325  Connection.DbcConnection.GetMetadata.GetDatabaseInfo.SupportsCatalogsInProcedureCalls,
326  Connection.DbcConnection.GetMetadata.GetDatabaseInfo.SupportsSchemasInProcedureCalls,
327  Catalog, Schema, ObjectName);
328  ObjectName := Connection.DbcConnection.GetMetadata.AddEscapeCharToWildcards(ObjectName);
329  FMetaResultSet := Connection.DbcConnection.GetMetadata.GetProcedureColumns(Catalog, Schema, ObjectName, '');
330  OldParams := TParams.Create;
331  try
332  OldParams.Assign(Params);
333  Params.Clear;
334  while FMetaResultSet.Next do
335  begin
336  ColumnType := FMetaResultSet.GetIntByName('COLUMN_TYPE');
337  if ColumnType >= 0 then //-1 is result column
338  Params.CreateParam(ConvertDbcToDatasetType(TZSqlType(FMetaResultSet.GetIntByName('DATA_TYPE'))),
339  FMetaResultSet.GetStringByName('COLUMN_NAME'),
340  GetParamType(TZProcedureColumnType(ColumnType)));
341  end;
342  Params.AssignValues(OldParams);
343  finally
344  OldParams.Free;
345  end;
346  finally
347  Connection.HideSQLHourGlass;
348  end;
349  end;
350  end;
351 end;
352 {$IFDEF FPC}
353  {$HINTS ON}
354 {$ENDIF}
355 
356 procedure TZStoredProc.ExecProc;
357 begin
358  Connection.ShowSQLHourGlass;
359  try
360  if Active then
361  Close;
362  ExecSQL;
363  RetrieveParamValues;
364  finally
365  Connection.HideSQLHourGlass;
366  end;
367 end;
368 
369 {**
370  Procedure the First retrieved resultset if the givens
371 }
372 procedure TZStoredProc.FirstResultSet;
373 begin
374  if Assigned(Statement) then
375  if (Statement as IZCallableStatement).HasMoreResultSets then
376  SetAnotherResultset((Statement as IZCallableStatement).GetFirstResultSet);
377 end;
378 
379 {**
380  Procedure the Previous retrieved resultset if the givens
381 }
382 procedure TZStoredProc.PreviousResultSet;
383 begin
384  if Assigned(Statement) then
385  if (Statement as IZCallableStatement).HasMoreResultSets then
386  SetAnotherResultset((Statement as IZCallableStatement).GetPreviousResultSet);
387 end;
388 
389 {**
390  Procedure the Next retrieved resultset if the givens
391 }
392 procedure TZStoredProc.NextResultSet;
393 begin
394  if Assigned(Statement) then
395  if (Statement as IZCallableStatement).HasMoreResultSets then
396  SetAnotherResultset((Statement as IZCallableStatement).GetNextResultSet);
397 end;
398 
399 {**
400  Procedure the Last retrieved resultset if the givens
401 }
402 procedure TZStoredProc.LastResultSet;
403 begin
404  if Assigned(Statement) then
405  if (Statement as IZCallableStatement).HasMoreResultSets then
406  SetAnotherResultset((Statement as IZCallableStatement).GetLastResultSet);
407 end;
408 
409 {**
410  Retrieves a ResultSet by his index.
411  @param Integer the index of the Resultset
412  @result <code>IZResultSet</code> of the Index or nil.
413 }
414 procedure TZStoredProc.SetResultSet(const Index: Integer);
415 begin
416  if Assigned(Statement) then
417  if ( Index < 0 ) or ( Index > (Statement as IZCallableStatement).GetResultSetCount -1 ) then
418  raise Exception.Create(Format(SListIndexError, [Index]))
419  else
420  SetAnotherResultset((Statement as IZCallableStatement).GetResultSetByIndex(Index));
421 end;
422 
423 {**
424  Returns the Count of retrived ResultSets.
425  @result <code>Integer</code> Count
426 }
427 function TZStoredProc.ResultSetCount: Integer;
428 begin
429  Result := 0;
430  if Assigned(Statement) then
431  if (Statement as IZCallableStatement).HasMoreResultSets then
432  Result := (Statement as IZCallableStatement).GetResultSetCount;
433 end;
434 
435 {**
436  First ResultSet?
437  @result <code>True</code> if first ResultSet
438 }
439 function TZStoredProc.BOR: Boolean;
440 begin
441  Result := True;
442  if Assigned(Statement) then
443  if (Statement as IZCallableStatement).HasMoreResultSets then
444  Result := (Statement as IZCallableStatement).BOR;
445 end;
446 
447 {**
448  Last ResultSet?
449  @result <code>True</code> if Last ResultSet
450 }
451 function TZStoredProc.EOR: Boolean;
452 begin
453  Result := True;
454  if Assigned(Statement) then
455  if (Statement as IZCallableStatement).HasMoreResultSets then
456  Result := (Statement as IZCallableStatement).EOR;
457 end;
458 
459 {**
460  Converts procedure column type to dataset param type.
461  @param Value a initial procedure column type.
462  @return a corresponding param type.
463 }
464 function TZStoredProc.GetParamType(const Value: TZProcedureColumnType): TParamType;
465 begin
466  case Value of
467  pctIn:
468  Result := ptInput;
469  pctInOut:
470  Result := ptInputOutput;
471  pctOut:
472  Result := ptOutput;
473  pctReturn:
474  Result := ptResult;
475  pctResultSet:
476  Result := ptResult;
477  else
478  Result := ptUnknown;
479  end;
480 end;
481 
482 {$IFDEF WITH_IPROVIDER}
483 {**
484  Checks if dataset can execute SQL queries?
485  @returns <code>True</code> if the query can execute SQL.
486 }
487 function TZStoredProc.PSIsSQLBased: Boolean;
488 begin
489  Result := False;
490 end;
491 
492 {**
493  Gets the name of the stored procedure.
494  @returns the name of this stored procedure.
495 }
496 {$IFDEF WITH_IPROVIDERWIDE}
497 function TZStoredProc.PSGetTableNameW: WideString;
498 {$ELSE}
499 function TZStoredProc.PSGetTableName: string;
500 {$ENDIF}
501 begin
502  Result := StoredProcName;
503 end;
504 
505 {**
506  Executes this stored procedure.
507 }
508 procedure TZStoredProc.PSExecute;
509 begin
510  ExecProc;
511 end;
512 
513 {**
514  Assignes a new name for this stored procedure.
515  @param ACommandText a new name for this stored procedure.
516 }
517 procedure TZStoredProc.PSSetCommandText(const ACommandText: string);
518 begin
519  StoredProcName := ACommandText;
520 end;
521 {$ENDIF}
522 
523 end.