ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBStoredProc.pas
Revision: 107
Committed: Thu Jan 18 14:37:40 2018 UTC (6 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 19984 byte(s)
Log Message:
Fixes merged

File Contents

# User Rev Content
1 tony 33 {************************************************************************}
2     { }
3     { Borland Delphi Visual Component Library }
4     { InterBase Express core components }
5     { }
6     { Copyright (c) 1998-2000 Inprise Corporation }
7     { }
8     { InterBase Express is based in part on the product }
9     { Free IB Components, written by Gregory H. Deatz for }
10     { Hoagland, Longo, Moran, Dunst & Doukas Company. }
11     { Free IB Components is used under license. }
12     { }
13     { The contents of this file are subject to the InterBase }
14     { Public License Version 1.0 (the "License"); you may not }
15     { use this file except in compliance with the License. You }
16     { may obtain a copy of the License at http://www.Inprise.com/IPL.html }
17     { Software distributed under the License is distributed on }
18     { an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either }
19     { express or implied. See the License for the specific language }
20     { governing rights and limitations under the License. }
21     { The Original Code was created by InterBase Software Corporation }
22     { and its successors. }
23     { Portions created by Inprise Corporation are Copyright (C) Inprise }
24     { Corporation. All Rights Reserved. }
25     { Contributor(s): Jeff Overcash }
26     { }
27     { IBX For Lazarus (Firebird Express) }
28     { Contributor: Tony Whyman, MWA Software http://www.mwasoftware.co.uk }
29     { Portions created by MWA Software are copyright McCallum Whyman }
30     { Associates Ltd 2011 }
31     { }
32     {************************************************************************}
33    
34     unit IBStoredProc;
35    
36     {$Mode Delphi}
37    
38 tony 39 {$codepage UTF8}
39    
40 tony 33 interface
41    
42     uses SysUtils, Classes, DB, IB, IBDatabase, IBCustomDataSet,
43 tony 45 IBSQL, IBUtils;
44 tony 33
45     { TIBStoredProc }
46     type
47    
48     TIBStoredProc = class(TIBCustomDataSet)
49     private
50 tony 107 FPackageName: string;
51 tony 45 FStmtHandle: IStatement;
52 tony 33 FProcName: string;
53     FParams: TParams;
54     FPrepared: Boolean;
55     FNameList: TStrings;
56 tony 107 FPackageNameList: TStrings;
57     function GetPackageNames: TStrings;
58     procedure GetPackageNamesFromServer;
59     procedure SetPackageName(AValue: string);
60 tony 33 procedure SetParamsList(Value: TParams);
61     procedure FreeStatement;
62     function GetStoredProcedureNames: TStrings;
63     procedure GetStoredProcedureNamesFromServer;
64     procedure CreateParamDesc;
65     procedure SetParams;
66     procedure SetParamsFromCursor;
67     procedure GenerateSQL;
68     procedure FetchDataIntoOutputParams;
69     procedure ReadParamData(Reader: TReader);
70     procedure WriteParamData(Writer: TWriter);
71 tony 107 procedure UpdateQuery;
72 tony 33 protected
73    
74     procedure DefineProperties(Filer: TFiler); override;
75     procedure SetFiltered(Value: Boolean); override;
76     procedure InitFieldDefs; override;
77     function GetParamsCount: Word;
78     procedure SetPrepared(Value: Boolean);
79     procedure SetPrepare(Value: Boolean);
80     procedure SetProcName(Value: string);
81     procedure Disconnect; override;
82     procedure InternalOpen; override;
83    
84     public
85     constructor Create(AOwner: TComponent); override;
86     destructor Destroy; override;
87     procedure CopyParams(Value: TParams);
88     procedure ExecProc;
89     function ParamByName(const Value: string): TParam;
90     procedure Prepare;
91     procedure UnPrepare;
92     property ParamCount: Word read GetParamsCount;
93 tony 45 property StmtHandle: IStatement read FStmtHandle;
94 tony 33 property Prepared: Boolean read FPrepared write SetPrepare;
95     property StoredProcedureNames: TStrings read GetStoredProcedureNames;
96 tony 107 property PackageNames: TStrings read GetPackageNames;
97 tony 33
98     published
99 tony 107 property PackageName: string read FPackageName write SetPackageName;
100 tony 33 property StoredProcName: string read FProcName write SetProcName;
101     property Params: TParams read FParams write SetParamsList;
102     property Filtered;
103    
104     property BeforeDatabaseDisconnect;
105     property AfterDatabaseDisconnect;
106     property DatabaseFree;
107     property BeforeTransactionEnd;
108     property AfterTransactionEnd;
109     property TransactionFree;
110     property OnFilterRecord;
111     end;
112    
113     implementation
114    
115 tony 45 uses FBMessages;
116 tony 33
117     { TIBStoredProc }
118    
119     constructor TIBStoredProc.Create(AOwner: TComponent);
120     begin
121     inherited Create(AOwner);
122     FParams := TParams.Create (self);
123     FNameList := TStringList.Create;
124 tony 107 FPackageNameList := TStringList.Create;
125 tony 33 end;
126    
127     destructor TIBStoredProc.Destroy;
128     begin
129 tony 45 Destroying;
130     Disconnect;
131     if assigned (FParams) then FParams.Free;
132 tony 107 if assigned(FNameList) then FNameList.Free;
133     if assigned(FPackageNameList) then FPackageNameList.Free;
134 tony 33 inherited Destroy;
135     end;
136    
137     procedure TIBStoredProc.Disconnect;
138     begin
139     Close;
140     UnPrepare;
141     end;
142    
143     procedure TIBStoredProc.ExecProc;
144     var
145     DidActivate: Boolean;
146     begin
147     CheckInActive;
148     if StoredProcName = '' then
149     IBError(ibxeNoStoredProcName, [nil]);
150     ActivateConnection;
151     DidActivate := ActivateTransaction;
152     try
153     SetPrepared(True);
154     if DataSource <> nil then SetParamsFromCursor;
155     if FParams.Count > 0 then SetParams;
156     InternalExecQuery;
157     FetchDataIntoOutputParams;
158     finally
159     if DidActivate then
160     DeactivateTransaction;
161     end;
162     end;
163    
164     procedure TIBStoredProc.SetProcName(Value: string);
165     begin
166 tony 107 if Value = FProcName then Exit;
167     CheckInactive;
168     FProcName := Value;
169     UpdateQuery;
170 tony 33 end;
171    
172     function TIBStoredProc.GetParamsCount: Word;
173     begin
174     Result := FParams.Count;
175     end;
176    
177     procedure TIBStoredProc.SetFiltered(Value: Boolean);
178     begin
179     if(Filtered <> Value) then
180     begin
181     inherited SetFiltered(value);
182     if Active then
183     begin
184     Close;
185     Open;
186     end;
187     end
188     else
189     inherited SetFiltered(value);
190     end;
191    
192     procedure TIBStoredProc.InitFieldDefs;
193     begin
194 tony 107 if (SelectSQL.Text = '') and (FProcName <> '') and (Database <> nil) then
195 tony 33 GenerateSQL;
196     inherited InitFieldDefs;
197     end;
198    
199     procedure TIBStoredProc.GenerateSQL;
200    
201     var Params: TStringList;
202    
203     function FormatParameter(Dialect: Integer; Value: String): String;
204     var j: integer;
205     begin
206     Value := Trim(Value);
207     if Dialect = 1 then
208     Result := AnsiUpperCase(Value)
209     else
210     begin
211     j := 1;
212     Value := Space2Underscore(AnsiUpperCase(Value));
213     Result := Value;
214     while Params.IndexOf(Result) <> -1 do
215     begin
216     Result := Value + IntToStr(j);
217     Inc(j)
218     end;
219     Params.Add(Result)
220     end;
221     end;
222    
223 tony 107 {Trim and make uppercase unless not an SQL Identifier when leave as it is}
224     function FormatProcName(Dialect: Integer; Value: String): String;
225     begin
226     Value := Trim(Value);
227     if (Dialect = 1) or IsSQLIdentifier(Value) then
228     Result := AnsiUpperCase(Value)
229     else
230     Result := SQLSafeString(Value);
231     end;
232    
233 tony 33 var
234     Query : TIBSQL;
235     input : string;
236     begin
237     input := '';
238     if FProcName = '' then
239     IBError(ibxeNoStoredProcName,[nil]);
240     ActivateConnection;
241     Database.InternalTransaction.StartTransaction;
242     Params := TStringList.Create;
243     Query := TIBSQL.Create(self);
244     try
245     Query.Database := DataBase;
246     Query.Transaction := Database.InternalTransaction;
247 tony 107 if DatabaseInfo.ODSMajorVersion < 12 then
248     Query.SQL.Text := 'SELECT RDB$PARAMETER_NAME, RDB$PARAMETER_TYPE ' + {do not localize}
249     'FROM RDB$PROCEDURE_PARAMETERS ' + {do not localize}
250     'WHERE RDB$PROCEDURE_NAME = ' + {do not localize}
251     '''' + FormatProcName(Database.SQLDialect, FProcName) + '''' +
252     ' ORDER BY RDB$PARAMETER_NUMBER' {do not localize}
253     else
254     if FPackageName = '' then
255     Query.SQL.Text := 'SELECT RDB$PARAMETER_NAME, RDB$PARAMETER_TYPE ' + {do not localize}
256     'FROM RDB$PROCEDURE_PARAMETERS ' + {do not localize}
257     'WHERE RDB$PROCEDURE_NAME = ' + {do not localize}
258     '''' + FormatProcName(Database.SQLDialect, FProcName) + '''' +
259     'AND RDB$PACKAGE_NAME IS NULL' + {do not localize}
260     ' ORDER BY RDB$PARAMETER_NUMBER' {do not localize}
261     else
262     Query.SQL.Text := 'SELECT RDB$PARAMETER_NAME, RDB$PARAMETER_TYPE ' + {do not localize}
263     'FROM RDB$PROCEDURE_PARAMETERS ' + {do not localize}
264     'WHERE RDB$PROCEDURE_NAME = ' + {do not localize}
265     '''' + FormatProcName(Database.SQLDialect, FProcName) + '''' +
266     'AND RDB$PACKAGE_NAME = ' + {do not localize}
267     '''' + FormatProcName(Database.SQLDialect, FPackageName) + '''' +
268     ' ORDER BY RDB$PARAMETER_NUMBER'; {do not localize}
269 tony 33 Query.Prepare;
270     Query.GoToFirstRecordOnExecute := False;
271     Query.ExecQuery;
272 tony 45 while (not Query.EOF) and Query.Next do begin
273     if (Query.FieldByName('RDB$PARAMETER_TYPE').AsInteger = 0) then begin {do not localize}
274 tony 33 if (input <> '') then
275     input := input + ', :' +
276     FormatParameter(Database.SQLDialect, Query.Current.ByName('RDB$PARAMETER_NAME').AsString) else {do not localize}
277     input := ':' +
278     FormatParameter(Database.SQLDialect, Query.Current.ByName('RDB$PARAMETER_NAME').AsString); {do not localize}
279     end
280     end;
281 tony 107 if FPackageName = '' then
282     SelectSQL.Text := 'Execute Procedure ' + {do not localize}
283     QuoteIdentifierIfNeeded(Database.SQLDialect, FProcName) + ' ' + input
284     else
285     SelectSQL.Text := 'Execute Procedure ' + {do not localize}
286     QuoteIdentifierIfNeeded(Database.SQLDialect,FPackageName) + '.' +
287     QuoteIdentifierIfNeeded(Database.SQLDialect, FProcName) + ' ' + input;
288     // writeln(SelectSQL.Text);
289 tony 33 finally
290     Query.Free;
291     Params.Free;
292     Database.InternalTransaction.Commit;
293     end;
294     end;
295    
296     procedure TIBStoredProc.CreateParamDesc;
297     var
298     i : integer;
299     DataType : TFieldType;
300     begin
301     DataType := ftUnknown;
302 tony 45 for i := 0 to QSelect.MetaData.Count - 1 do begin
303     case QSelect.MetaData[i].SQLtype of
304 tony 33 SQL_TYPE_DATE: DataType := ftDate;
305     SQL_TYPE_TIME: DataType := ftTime;
306     SQL_TIMESTAMP: DataType := ftDateTime;
307     SQL_SHORT:
308 tony 45 if QSelect.MetaData[i].getScale = 0 then
309 tony 33 DataType := ftSmallInt
310     else
311     DataType := ftBCD;
312     SQL_LONG:
313 tony 45 if QSelect.MetaData[i].getScale = 0 then
314 tony 33 DataType := ftInteger
315 tony 45 else if QSelect.MetaData[i].getScale >= -4 then
316 tony 33 DataType := ftBCD
317     else
318     DataType := ftFloat;
319     SQL_INT64:
320 tony 45 if QSelect.MetaData[i].getScale = 0 then
321 tony 33 DataType := ftLargeInt
322 tony 45 else if QSelect.MetaData[i].getScale >= -4 then
323 tony 33 DataType := ftBCD
324     else
325     DataType := ftFloat;
326     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT: DataType := ftFloat;
327     SQL_BOOLEAN:
328     DataType := ftBoolean;
329     SQL_TEXT: DataType := ftString;
330     SQL_VARYING:
331 tony 45 if QSelect.MetaData[i].GetSize < 1024 then
332 tony 33 DataType := ftString
333     else DataType := ftBlob;
334     SQL_BLOB, SQL_ARRAY, SQL_QUAD: DataType := ftBlob;
335     end;
336 tony 45 FParams.CreateParam(DataType, Trim(QSelect.MetaData[i].Name), ptOutput);
337 tony 33 end;
338    
339     DataType := ftUnknown;
340     for i := 0 to QSelect.Params.Count - 1 do begin
341 tony 45 case QSelect.Params[i].GetSQLtype of
342 tony 33 SQL_TYPE_DATE: DataType := ftDate;
343     SQL_TYPE_TIME: DataType := ftTime;
344     SQL_TIMESTAMP: DataType := ftDateTime;
345     SQL_SHORT:
346 tony 45 if QSelect.Params[i].getScale = 0 then
347 tony 33 DataType := ftSmallInt
348     else
349     DataType := ftBCD;
350     SQL_LONG:
351 tony 45 if QSelect.Params[i].getScale = 0 then
352 tony 33 DataType := ftInteger
353 tony 45 else if QSelect.Params[i].getScale >= -4 then
354 tony 33 DataType := ftBCD
355     else DataType := ftFloat;
356     SQL_INT64:
357 tony 45 if QSelect.Params[i].getScale = 0 then
358 tony 33 DataType := ftLargeInt
359 tony 45 else if QSelect.Params[i].getScale >= -4 then
360 tony 33 DataType := ftBCD
361     else DataType := ftFloat;
362     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT: DataType := ftFloat;
363     SQL_BOOLEAN:
364     DataType := ftBoolean;
365     SQL_TEXT: DataType := ftString;
366     SQL_VARYING:
367 tony 45 if QSelect.Params[i].GetSize < 1024 then
368 tony 33 DataType := ftString
369     else DataType := ftBlob;
370     SQL_BLOB, SQL_ARRAY, SQL_QUAD: DataType := ftBlob;
371     end;
372     FParams.CreateParam(DataType, Trim(QSelect.Params[i].Name), ptInput);
373     end;
374     end;
375    
376     procedure TIBStoredProc.SetPrepared(Value: Boolean);
377     begin
378     if Prepared <> Value then
379     begin
380     if Value then
381     try
382     if SelectSQL.Text = '' then GenerateSQL;
383     InternalPrepare;
384     if FParams.Count = 0 then CreateParamDesc;
385     FPrepared := True;
386     except
387     FreeStatement;
388     raise;
389     end
390     else FreeStatement;
391     end;
392    
393     end;
394    
395     procedure TIBStoredProc.Prepare;
396     begin
397     SetPrepared(True);
398     end;
399    
400     procedure TIBStoredProc.UnPrepare;
401     begin
402     SetPrepared(False);
403     end;
404    
405     procedure TIBStoredProc.FreeStatement;
406     begin
407     InternalUnPrepare;
408     FPrepared := False;
409     end;
410    
411     procedure TIBStoredProc.SetPrepare(Value: Boolean);
412     begin
413     if Value then Prepare
414     else UnPrepare;
415     end;
416    
417     procedure TIBStoredProc.CopyParams(Value: TParams);
418     begin
419     if not Prepared and (FParams.Count = 0) then
420     try
421     Prepare;
422     Value.Assign(FParams);
423     finally
424     UnPrepare;
425     end else
426     Value.Assign(FParams);
427     end;
428    
429     procedure TIBStoredProc.SetParamsList(Value: TParams);
430     begin
431     CheckInactive;
432     if Prepared then
433     begin
434     SetPrepared(False);
435     FParams.Assign(Value);
436     SetPrepared(True);
437     end else
438     FParams.Assign(Value);
439     end;
440    
441 tony 107 procedure TIBStoredProc.SetPackageName(AValue: string);
442     begin
443     if FPackageName = AValue then Exit;
444     CheckInactive;
445     FPackageName := AValue;
446     FProcName := '';
447     UpdateQuery;
448     end;
449    
450     procedure TIBStoredProc.GetPackageNamesFromServer;
451     var
452     Query : TIBSQL;
453     begin
454     ActivateConnection;
455     if (csReading in ComponentState) or (DatabaseInfo.ODSMajorVersion < 12) then Exit;
456     Database.InternalTransaction.StartTransaction;
457     Query := TIBSQL.Create(self);
458     try
459     Query.GoToFirstRecordOnExecute := False;
460     Query.Database := DataBase;
461     Query.Transaction := Database.InternalTransaction;
462     Query.SQL.Text := 'Select distinct RDB$PACKAGE_NAME from RDB$PROCEDURES Where RDB$PACKAGE_NAME is not null'; {do not localize}
463     Query.Prepare;
464     Query.ExecQuery;
465     while (not Query.EOF) and Query.Next do
466     FPackageNameList.Add(TrimRight(Query.Current.ByName('RDB$PACKAGE_NAME').AsString)); {do not localize}
467     finally
468     Query.Free;
469     Database.InternalTransaction.Commit;
470     end;
471     end;
472    
473     function TIBStoredProc.GetPackageNames: TStrings;
474     begin
475     FPackageNameList.Clear;
476     GetPackageNamesFromServer;
477     Result := FPackageNameList;
478     end;
479    
480 tony 33 function TIBStoredProc.ParamByName(const Value: string): TParam;
481     begin
482     Prepare;
483     Result := FParams.ParamByName(Value);
484     end;
485    
486     function TIBStoredProc.GetStoredProcedureNames: TStrings;
487     begin
488     FNameList.clear;
489     GetStoredProcedureNamesFromServer;
490     Result := FNameList;
491     end;
492    
493     procedure TIBStoredProc.GetStoredProcedureNamesFromServer;
494     var
495     Query : TIBSQL;
496     begin
497     if not (csReading in ComponentState) then begin
498     ActivateConnection;
499     Database.InternalTransaction.StartTransaction;
500     Query := TIBSQL.Create(self);
501     try
502     Query.GoToFirstRecordOnExecute := False;
503     Query.Database := DataBase;
504     Query.Transaction := Database.InternalTransaction;
505 tony 107 if DatabaseInfo.ODSMajorVersion < 12 then
506     Query.SQL.Text := 'Select RDB$PROCEDURE_NAME from RDB$PROCEDURES' {do not localize}
507     else
508     if FPackageName = '' then
509     Query.SQL.Text := 'Select RDB$PROCEDURE_NAME from RDB$PROCEDURES Where RDB$PACKAGE_NAME is NULL' {do not localize}
510     else
511     Query.SQL.Text := 'Select RDB$PROCEDURE_NAME from RDB$PROCEDURES Where RDB$PACKAGE_NAME = ''' + {do not localize}
512     SQLSafeString(FPackageName) + '''';
513 tony 33 Query.Prepare;
514     Query.ExecQuery;
515 tony 45 while (not Query.EOF) and Query.Next do
516 tony 33 FNameList.Add(TrimRight(Query.Current.ByName('RDB$PROCEDURE_NAME').AsString)); {do not localize}
517     finally
518     Query.Free;
519     Database.InternalTransaction.Commit;
520     end;
521     end;
522     end;
523    
524     procedure TIBStoredProc.SetParams;
525     var
526     i : integer;
527     j: integer;
528     begin
529     i := 0;
530     for j := 0 to FParams.Count - 1 do
531     begin
532     if (Params[j].ParamType <> ptInput) then
533     continue;
534     if not Params[j].Bound then
535     IBError(ibxeRequiredParamNotSet, [Params[j].Name]);
536     if Params[j].IsNull then
537     SQLParams[i].IsNull := True
538     else begin
539     SQLParams[i].IsNull := False;
540     case Params[j].DataType of
541     ftString:
542     SQLParams[i].AsString := Params[j].AsString;
543     ftSmallint, ftWord:
544     SQLParams[i].AsShort := Params[j].AsSmallInt;
545     ftBoolean:
546     SQLParams[i].AsBoolean := Params[j].AsBoolean;
547     ftInteger:
548     SQLParams[i].AsLong := Params[j].AsInteger;
549     ftLargeInt:
550     SQLParams[i].AsInt64 := Params[j].AsLargeInt;
551     ftFloat, ftCurrency:
552     SQLParams[i].AsDouble := Params[j].AsFloat;
553     ftBCD:
554     SQLParams[i].AsCurrency := Params[j].AsCurrency;
555     ftDate:
556     SQLParams[i].AsDate := Params[j].AsDateTime;
557     ftTime:
558     SQLParams[i].AsTime := Params[j].AsDateTime;
559     ftDateTime:
560     SQLParams[i].AsDateTime := Params[j].AsDateTime;
561     ftBlob, ftMemo:
562     SQLParams[i].AsString := Params[j].AsString;
563     else
564     IBError(ibxeNotSupported, [nil]);
565     end;
566     end;
567     Inc(i);
568     end;
569     end;
570    
571     procedure TIBStoredProc.SetParamsFromCursor;
572     var
573     I: Integer;
574     DataSet: TDataSet;
575     begin
576     if DataSource <> nil then
577     begin
578     DataSet := DataSource.DataSet;
579     if DataSet <> nil then
580     begin
581     DataSet.FieldDefs.Update;
582     for I := 0 to FParams.Count - 1 do
583     with FParams[I] do
584     if (not Bound) and
585     ((ParamType = ptInput) or (ParamType = ptInputOutput)) then
586     AssignField(DataSet.FieldByName(Name));
587     end;
588     end;
589     end;
590    
591     procedure TIBStoredProc.FetchDataIntoOutputParams;
592     var
593     i,j : Integer;
594     begin
595     j := 0;
596     for i := 0 to FParams.Count - 1 do
597     with Params[I] do
598     if ParamType = ptOutput then begin
599     Value := QSelect.Fields[j].Value;
600     Inc(j);
601     end;
602     end;
603    
604     procedure TIBStoredProc.InternalOpen;
605     begin
606     IBError(ibxeIsAExecuteProcedure,[nil]);
607     end;
608    
609     procedure TIBStoredProc.DefineProperties(Filer: TFiler);
610    
611     function WriteData: Boolean;
612     begin
613     if Filer.Ancestor <> nil then
614     Result := not FParams.IsEqual(TIBStoredProc(Filer.Ancestor).FParams) else
615     Result := FParams.Count > 0;
616     end;
617    
618     begin
619     inherited DefineProperties(Filer);
620     Filer.DefineProperty('ParamData', ReadParamData, WriteParamData, WriteData); {do not localize}
621     end;
622    
623     procedure TIBStoredProc.WriteParamData(Writer: TWriter);
624     begin
625     Writer.WriteCollection(Params);
626     end;
627    
628 tony 107 procedure TIBStoredProc.UpdateQuery;
629     begin
630     if not (csReading in ComponentState) then
631     begin
632     FreeStatement;
633     FParams.Clear;
634     if (FProcName <> '') and (Database <> nil) then
635     begin
636     GenerateSQL;
637     if csDesigning in ComponentState then
638     begin
639     Prepare; {Fills the Params collection}
640     UnPrepare;
641     end;
642     end;
643     end
644     else
645     begin
646     if (FProcName <> '') and (Database <> nil) then
647     GenerateSQL;
648     end;
649     end;
650    
651 tony 33 procedure TIBStoredProc.ReadParamData(Reader: TReader);
652     begin
653     Reader.ReadValue;
654     Reader.ReadCollection(Params);
655     end;
656    
657     end.