ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBExtract.pas
Revision: 226
Committed: Tue Apr 3 10:52:43 2018 UTC (6 years ago) by tony
Content type: text/x-pascal
File size: 131512 byte(s)
Log Message:
Fixes Merged

File Contents

# User Rev Content
1 tony 209 {************************************************************************}
2     { }
3     { The contents of this file are subject to the InterBase }
4     { Public License Version 1.0 (the "License"); you may not }
5     { use this file except in compliance with the License. You }
6     { may obtain a copy of the License at http://www.Inprise.com/IPL.html }
7     { Software distributed under the License is distributed on }
8     { an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either }
9     { express or implied. See the License for the specific language }
10     { governing rights and limitations under the License. }
11     { }
12     { The Original Code was created by Jeff Overcash. }
13     { Portions based upon code by Inprise Corporation are Copyright (C) }
14     { Inprise Corporation. All Rights Reserved. }
15     { }
16     { IBX Version 4.2 or higher required }
17     { Contributor(s): Jeff Overcash }
18     { }
19     { IBX For Lazarus (Firebird Express) }
20     { Contributor: Tony Whyman, MWA Software http://www.mwasoftware.co.uk }
21     { Portions created by MWA Software are copyright McCallum Whyman }
22     { Associates Ltd 2011 - 2018 }
23     { }
24     {************************************************************************}
25    
26     { Syntax Enhancements Supported (by Firebird Version no.):
27    
28     Multi-action triggers (1.5)
29     CREATE SEQUENCE (2.0)
30     Database Triggers (2.1)
31     Global Temporary Tables (2.1)
32     Boolean Type (3.0)
33     Identity Column Type (3.0)
34     DDL Triggers (3.0)
35     }
36    
37     unit IBExtract;
38    
39     {$Mode Delphi}
40     {$codepage UTF8}
41    
42     interface
43    
44     uses
45     {$IFDEF WINDOWS }
46     Windows,
47     {$ELSE}
48     unix,
49     {$ENDIF}
50     SysUtils, Classes, IBDatabase, IBDatabaseInfo,
51     IBSQL, IBUtils, IBHeader, IB;
52    
53     type
54     TExtractObjectTypes =
55 tony 221 (eoDatabase, eoDomain, eoTable, eoView, eoProcedure, eoFunction, eoPackage,
56 tony 209 eoGenerator, eoException, eoBLOBFilter, eoRole, eoTrigger, eoForeign,
57     eoIndexes, eoChecks, eoData);
58    
59     TExtractType =
60     (etDomain, etTable, etRole, etTrigger, etForeign,
61 tony 226 etIndex, etData, etGrant, etCheck, etGrantsToUser,
62     etDatabaseTriggers, etDDLTriggers);
63 tony 209
64     TExtractTypes = Set of TExtractType;
65    
66     TProcDDLType = (pdCreateProc,pdCreateStub,pdAlterProc);
67    
68 tony 221 TPackageDDLType = (paHeader,paBody,paBoth);
69    
70 tony 209 { TIBExtract }
71    
72     TIBExtract = class(TComponent)
73     private
74     FAlwaysQuoteIdentifiers: boolean;
75 tony 225 FCaseSensitiveObjectNames: boolean;
76 tony 209 FDatabase : TIBDatabase;
77     FTransaction : TIBTransaction;
78     FMetaData: TStrings;
79     FDatabaseInfo: TIBDatabaseInfo;
80     FShowSystem: Boolean;
81     { Private declarations }
82     procedure Add2MetaData(const Msg: string; IsError: boolean=true);
83     function GetDatabase: TIBDatabase;
84     function GetIndexSegments ( indexname : String) : String;
85     function GetTransaction: TIBTransaction;
86     function GetTriggerType(TypeID: Int64): string;
87     procedure SetDatabase(const Value: TIBDatabase);
88     procedure SetTransaction(const Value: TIBTransaction);
89     function PrintValidation(ToValidate : String; flag : Boolean) : String;
90     procedure ShowGrants(MetaObject: String; Terminator : String; NoUserGrants: boolean=false);
91     procedure ShowGrantsTo(MetaObject: String; ObjectType: integer;
92     Terminator: String);
93     procedure ShowGrantRoles(Terminator : String);
94     procedure GetProcedureArgs(Proc : String);
95     protected
96     function ExtractDDL(Flag: Boolean; TableName: String; ExtractTypes: TExtractTypes =
97     []): Boolean;
98     function ExtractListTable(RelationName, NewName: String; DomainFlag: Boolean): Boolean;
99     procedure ExtractListView (ViewName : String);
100     procedure ListData(ObjectName : String);
101     procedure ListRoles(ObjectName : String = ''; IncludeGrants:boolean=false);
102     procedure ListGrants(ExtractTypes : TExtractTypes = []);
103 tony 221 procedure ListPackages(PackageDDLType: TPackageDDLType; PackageName: string = ''; IncludeGrants:boolean = false);
104 tony 209 procedure ListProcs(ProcDDLType: TProcDDLType = pdCreateProc; ProcedureName : String = '';
105     IncludeGrants:boolean=false);
106     procedure ListAllTables(flag : Boolean);
107     procedure ListTriggers(ObjectName: String=''; ExtractTypes: TExtractTypes = [etTrigger]);
108     procedure ListCheck(ObjectName : String = ''; ExtractType : TExtractType = etCheck);
109     function PrintSet(var Used : Boolean) : String;
110     procedure ListCreateDb(TargetDb : String = '');
111     procedure ListDomains(ObjectName : String = ''; ExtractType : TExtractType = etDomain);
112     procedure ListException(ExceptionName : String = '');
113     procedure ListFilters(FilterName : String = '');
114     procedure ListForeign(ObjectName : String = ''; ExtractType : TExtractType = etForeign);
115     procedure ListFunctions(FunctionName : String = '');
116     procedure ListGenerators(GeneratorName : String = ''; ExtractTypes: TExtractTypes=[]);
117     procedure ListIndex(ObjectName : String = ''; ExtractType : TExtractType = etIndex);
118     procedure ListViews(ViewName : String = '');
119     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
120     function QuoteIdentifier(Value: String): String;
121    
122     { Protected declarations }
123     public
124     { Public declarations }
125     constructor Create(AOwner : TComponent); override;
126     destructor Destroy; override;
127     function GetArrayField(FieldName : String) : String;
128     function GetFieldType(FieldType, FieldSubType, FieldScale, FieldSize,
129     FieldPrec, FieldLen : Integer) : String;
130     function GetCharacterSets(CharSetId, Collation : integer; CollateOnly : Boolean) : String;
131     procedure ExtractObject(ObjectType : TExtractObjectTypes; ObjectName : String = '';
132     ExtractTypes : TExtractTypes = []);
133     property DatabaseInfo : TIBDatabaseInfo read FDatabaseInfo;
134     property Items : TStrings read FMetaData;
135    
136     published
137     { Published declarations }
138     property Database : TIBDatabase read GetDatabase write SetDatabase;
139     property Transaction : TIBTransaction read GetTransaction write SetTransaction;
140     property ShowSystem: Boolean read FShowSystem write FShowSystem;
141     property AlwaysQuoteIdentifiers: boolean read FAlwaysQuoteIdentifiers write FAlwaysQuoteIdentifiers;
142 tony 225 property CaseSensitiveObjectNames: boolean read FCaseSensitiveObjectNames write FCaseSensitiveObjectNames;
143 tony 209 end;
144    
145     TSQLType = record
146     SqlType : Integer;
147     TypeName : String;
148     end;
149    
150     TPrivTypes = record
151     PrivFlag : Integer;
152     PrivString : String;
153     end;
154    
155     TSQLTypes = Array[0..14] of TSQLType;
156    
157     const
158    
159     priv_UNKNOWN = 1;
160     priv_SELECT = 2;
161     priv_INSERT = 4;
162     priv_UPDATE = 8;
163     priv_DELETE = 16;
164     priv_EXECUTE = 32;
165     priv_REFERENCES = 64;
166     priv_USAGE = 128;
167    
168     PrivTypes : Array[0..6] of TPrivTypes = (
169     (PrivFlag : priv_DELETE; PrivString : 'DELETE' ),
170     (PrivFlag : priv_EXECUTE; PrivString : 'EXECUTE' ),
171     (PrivFlag : priv_INSERT; PrivString : 'INSERT' ),
172     (PrivFlag : priv_SELECT; PrivString : 'SELECT' ),
173     (PrivFlag : priv_UPDATE; PrivString : 'UPDATE' ),
174     (PrivFlag : priv_REFERENCES; PrivString : 'REFERENCES'),
175     (PrivFlag : priv_USAGE; PrivString : 'USAGE' ));
176    
177     ColumnTypes : TSQLTypes = (
178     (SqlType : blr_short; TypeName : 'SMALLINT'), { NTX: keyword }
179     (SqlType : blr_long; TypeName : 'INTEGER'), { NTX: keyword }
180     (SqlType : blr_quad; TypeName : 'QUAD'), { NTX: keyword }
181     (SqlType : blr_float; TypeName : 'FLOAT'), { NTX: keyword }
182     (SqlType : blr_text; TypeName : 'CHAR'), { NTX: keyword }
183     (SqlType : blr_double; TypeName : 'DOUBLE PRECISION'), { NTX: keyword }
184     (SqlType : blr_varying; TypeName : 'VARCHAR'), { NTX: keyword }
185     (SqlType : blr_cstring; TypeName : 'CSTRING'), { NTX: keyword }
186     (SqlType : blr_blob_id; TypeName : 'BLOB_ID'), { NTX: keyword }
187     (SqlType : blr_blob; TypeName : 'BLOB'), { NTX: keyword }
188     (SqlType : blr_sql_time; TypeName : 'TIME'), { NTX: keyword }
189     (SqlType : blr_sql_date; TypeName : 'DATE'), { NTX: keyword }
190     (SqlType : blr_timestamp; TypeName : 'TIMESTAMP'), { NTX: keyword }
191     (SqlType : blr_int64; TypeName : 'INT64'),
192     (SqlType : blr_bool; TypeName : 'BOOLEAN'));
193    
194     SubTypes : Array[0..8] of String = (
195     'UNKNOWN', { NTX: keyword }
196     'TEXT', { NTX: keyword }
197     'BLR', { NTX: keyword }
198     'ACL', { NTX: keyword }
199     'RANGES', { NTX: keyword }
200     'SUMMARY', { NTX: keyword }
201     'FORMAT', { NTX: keyword }
202     'TRANSACTION_DESCRIPTION', { NTX: keyword }
203     'EXTERNAL_FILE_DESCRIPTION'); { NTX: keyword }
204    
205     IntegralSubtypes : Array[0..2] of String = (
206     'UNKNOWN', { Defined type, NTX: keyword }
207     'NUMERIC', { NUMERIC, NTX: keyword }
208     'DECIMAL'); { DECIMAL, NTX: keyword }
209    
210     ODS_VERSION6 = 6; { on-disk structure as of v3.0 }
211     ODS_VERSION7 = 7; { new on disk structure for fixing index bug }
212     ODS_VERSION8 = 8; { new btree structure to support pc semantics }
213     ODS_VERSION9 = 9; { btree leaf pages are always propogated up }
214     ODS_VERSION10 = 10; { V6.0 features. SQL delimited idetifier,
215     SQLDATE, and 64-bit exact numeric
216     type }
217     ODS_VERSION12 = 12; {Firebird 3}
218    
219     { flags for RDB$FILE_FLAGS }
220     FILE_shadow = 1;
221     FILE_inactive = 2;
222     FILE_manual = 4;
223     FILE_cache = 8;
224     FILE_conditional = 16;
225    
226     { flags for RDB$LOG_FILES }
227     LOG_serial = 1;
228     LOG_default = 2;
229     LOG_raw = 4;
230     LOG_overflow = 8;
231    
232    
233    
234     MAX_INTSUBTYPES = 2;
235     MAXSUBTYPES = 8; { Top of subtypes array }
236    
237     { Object types used in RDB$DEPENDENCIES and RDB$USER_PRIVILEGES }
238    
239     obj_relation = 0;
240     obj_view = 1;
241     obj_trigger = 2;
242     obj_computed = 3;
243     obj_validation = 4;
244     obj_procedure = 5;
245     obj_expression_index = 6;
246     obj_exception = 7;
247     obj_user = 8;
248     obj_field = 9;
249     obj_index = 10;
250     obj_count = 11;
251     obj_user_group = 12;
252     obj_sql_role = 13;
253 tony 221 obj_package = 18;
254 tony 209
255     implementation
256    
257     uses FBMessages, IBDataOutput;
258    
259     const
260     TERM = ';';
261     ProcTerm = '^';
262    
263     CollationSQL =
264     'SELECT CST.RDB$CHARACTER_SET_NAME, COL.RDB$COLLATION_NAME, CST.RDB$DEFAULT_COLLATE_NAME ' +
265     'FROM RDB$COLLATIONS COL JOIN RDB$CHARACTER_SETS CST ON ' +
266     ' COL.RDB$CHARACTER_SET_ID = CST.RDB$CHARACTER_SET_ID ' +
267     'WHERE ' +
268     ' COL.RDB$COLLATION_ID = :COLLATION AND ' +
269     ' CST.RDB$CHARACTER_SET_ID = :CHAR_SET_ID ' +
270     'ORDER BY COL.RDB$COLLATION_NAME, CST.RDB$CHARACTER_SET_NAME';
271    
272     NonCollationSQL =
273     'SELECT CST.RDB$CHARACTER_SET_NAME ' +
274     'FROM RDB$CHARACTER_SETS CST ' +
275     'WHERE CST.RDB$CHARACTER_SET_ID = :CHARSETID ' +
276     'ORDER BY CST.RDB$CHARACTER_SET_NAME';
277    
278     PrecisionSQL =
279     'SELECT * FROM RDB$FIELDS ' +
280     'WHERE RDB$FIELD_NAME = :FIELDNAME';
281    
282     ArraySQL =
283     'SELECT * FROM RDB$FIELD_DIMENSIONS FDIM ' +
284     'WHERE ' +
285     ' FDIM.RDB$FIELD_NAME = :FIELDNAME ' +
286     'ORDER BY FDIM.RDB$DIMENSION';
287    
288     type
289     TTriggerPhase = (tpNone,tpCreate,tpAlter,tpDrop);
290    
291     TDDLTriggerMap = record
292     ObjectName: string;
293     Bits: integer;
294     Bit1: TTriggerPhase;
295     Bit2: TTriggerPhase;
296     Bit3: TTriggerPhase;
297     end;
298    
299     const
300     DDLTriggers : array [0..15] of TDDLTriggerMap = (
301     (ObjectName: 'TABLE'; Bits: 3; Bit1: tpCreate; Bit2: tpAlter; Bit3: tpDrop),
302     (ObjectName: 'PROCEDURE'; Bits: 3; Bit1: tpCreate; Bit2: tpAlter; Bit3: tpDrop),
303     (ObjectName: 'FUNCTION'; Bits: 3; Bit1: tpCreate; Bit2: tpAlter; Bit3: tpDrop),
304     (ObjectName: 'TRIGGER'; Bits: 3; Bit1: tpCreate; Bit2: tpAlter; Bit3: tpDrop),
305     (ObjectName: 'Empty slot'; Bits: 3; Bit1: tpNone; Bit2: tpNone; Bit3: tpNone),
306     (ObjectName: 'EXCEPTION'; Bits: 3; Bit1: tpCreate; Bit2: tpAlter; Bit3: tpDrop),
307     (ObjectName: 'VIEW'; Bits: 3; Bit1: tpCreate; Bit2: tpAlter; Bit3: tpDrop),
308     (ObjectName: 'DOMAIN'; Bits: 3; Bit1: tpCreate; Bit2: tpAlter; Bit3: tpDrop),
309     (ObjectName: 'ROLE'; Bits: 3; Bit1: tpCreate; Bit2: tpAlter; Bit3: tpDrop),
310     (ObjectName: 'INDEX'; Bits: 3; Bit1: tpCreate; Bit2: tpAlter; Bit3: tpDrop),
311     (ObjectName: 'SEQUENCE'; Bits: 3; Bit1: tpCreate; Bit2: tpAlter; Bit3: tpDrop),
312     (ObjectName: 'USER'; Bits: 3; Bit1: tpCreate; Bit2: tpAlter; Bit3: tpDrop),
313     (ObjectName: 'COLLATION'; Bits: 2; Bit1: tpCreate; Bit2: tpDrop; Bit3: tpNone),
314     (ObjectName: 'CHARACTER SET'; Bits: 1; Bit1: tpAlter; Bit2: tpNone; Bit3: tpNone),
315     (ObjectName: 'PACKAGE'; Bits: 3; Bit1: tpCreate; Bit2: tpAlter; Bit3: tpDrop),
316     (ObjectName: 'PACKAGE BODY'; Bits: 2; Bit1: tpCreate; Bit2: tpDrop; Bit3: tpNone)
317     );
318    
319     { TIBExtract }
320    
321     { ArrayDimensions
322     Functional description
323     Retrieves the dimensions of arrays and prints them.
324    
325     Parameters: fieldname -- the actual name of the array field }
326    
327     function TIBExtract.GetArrayField(FieldName: String): String;
328     var
329     qryArray : TIBSQL;
330     begin
331     qryArray := TIBSQL.Create(FDatabase);
332     Result := '';
333     qryArray.SQL.Add(ArraySQL);
334     qryArray.Params.ByName('FieldName').AsString := FieldName;
335     qryArray.ExecQuery;
336    
337     { Format is [lower:upper, lower:upper,..] }
338    
339     if not qryArray.Eof then
340     begin
341     Result := '[';
342     while not qryArray.Eof do
343     begin
344     if (qryArray.FieldByName('RDB$DIMENSION').AsInteger > 0) then
345     Result := Result + ', ';
346     Result := Result + qryArray.FieldByName('RDB$LOWER_BOUND').AsString + ':' +
347     qryArray.FieldByName('RDB$UPPER_BOUND').AsString;
348     qryArray.Next;
349     end;
350     Result := Result + '] ';
351     end;
352    
353     qryArray.Free;
354    
355     end;
356    
357     constructor TIBExtract.Create(AOwner: TComponent);
358     begin
359     inherited;
360     FMetaData := TStringList.Create;
361     FDatabaseInfo := TIBDatabaseInfo.Create(nil);
362     FDatabaseInfo.Database := FDatabase;
363     if AOwner is TIBDatabase then
364     Database := TIBDatabase(AOwner);
365     if AOwner is TIBTransaction then
366     Transaction := TIBTransaction(AOwner);
367     end;
368    
369     destructor TIBExtract.Destroy;
370     begin
371     FMetaData.Free;
372     FDatabasEInfo.Free;
373     inherited;
374     end;
375    
376     function TIBExtract.ExtractDDL(Flag: Boolean; TableName: String;
377     ExtractTypes: TExtractTypes): Boolean;
378     var
379     DidConnect : Boolean;
380     DidStart : Boolean;
381     begin
382     Result := true;
383     DidConnect := false;
384     DidStart := false;
385    
386     if not FDatabase.Connected then
387     begin
388     FDatabase.Connected := true;
389     didConnect := true;
390     end;
391    
392     FMetaData.Add(Format('SET SQL DIALECT %d;', [FDatabase.SQLDialect]));
393     FMetaData.Add('SET AUTODDL ON;');
394     FMetaData.Add('');
395    
396     if not FTransaction.Active then
397     begin
398     FTransaction.StartTransaction;
399     DidStart := true;
400     end;
401    
402     if TableName <> '' then
403     begin
404     if not ExtractListTable(TableName, '', true) then
405     Result := false;
406     end
407     else
408     begin
409     ListCreateDb;
410     ListFilters;
411     ListFunctions;
412     ListDomains;
413     ListAllTables(flag);
414     if etData in ExtractTypes then
415     ListData('');
416     ListIndex;
417     ListForeign;
418     if etData in ExtractTypes then
419     ListGenerators('',[etData])
420     else
421     ListGenerators;
422     ListViews;
423     ListCheck;
424     ListException;
425 tony 221 if DatabaseInfo.ODSMajorVersion >= ODS_VERSION12 then
426     ListPackages(paHeader);
427 tony 209 ListProcs(pdCreateStub);
428     ListTriggers;
429 tony 221 if DatabaseInfo.ODSMajorVersion >= ODS_VERSION12 then
430     ListPackages(paBody);
431 tony 209 ListProcs(pdAlterProc);
432     ListGrants(ExtractTypes);
433     end;
434    
435     if DidStart then
436     FTransaction.Commit;
437    
438     if DidConnect then
439     FDatabase.Connected := false;
440     end;
441    
442     { ExtractListTable
443     Functional description
444     Shows columns, types, info for a given table name
445     and text of views.
446     If a new_name is passed, substitute it for relation_name
447    
448     relation_name -- Name of table to investigate
449     new_name -- Name of a new name for a replacement table
450     domain_flag -- extract needed domains before the table }
451    
452     function TIBExtract.ExtractListTable(RelationName, NewName: String;
453     DomainFlag: Boolean): Boolean;
454     const
455     TableListSQL =
456     'SELECT * FROM RDB$RELATIONS REL JOIN RDB$RELATION_FIELDS RFR ON ' + {Do Not Localize}
457     ' RFR.RDB$RELATION_NAME = REL.RDB$RELATION_NAME JOIN RDB$FIELDS FLD ON ' +
458     ' RFR.RDB$FIELD_SOURCE = FLD.RDB$FIELD_NAME ' +
459     'WHERE REL.RDB$RELATION_NAME = :RelationName ' +
460     'ORDER BY RFR.RDB$FIELD_POSITION, RFR.RDB$FIELD_NAME';
461    
462     ConstraintSQL =
463     'SELECT RCO.RDB$CONSTRAINT_NAME, RDB$CONSTRAINT_TYPE, RDB$RELATION_NAME, ' +
464     'RDB$DEFERRABLE, RDB$INITIALLY_DEFERRED, RDB$INDEX_NAME, RDB$TRIGGER_NAME ' +
465     'FROM RDB$RELATION_CONSTRAINTS RCO, RDB$CHECK_CONSTRAINTS CON ' +
466     'WHERE ' +
467     ' CON.RDB$TRIGGER_NAME = :FIELDNAME AND ' +
468     ' CON.RDB$CONSTRAINT_NAME = RCO.RDB$CONSTRAINT_NAME AND ' +
469     ' RCO.RDB$CONSTRAINT_TYPE = ''NOT NULL'' AND ' +
470     ' RCO.RDB$RELATION_NAME = :RELATIONNAME';
471    
472     RelConstraintsSQL =
473     'SELECT * FROM RDB$RELATION_CONSTRAINTS RELC ' +
474     'WHERE ' +
475     ' (RELC.RDB$CONSTRAINT_TYPE = ''PRIMARY KEY'' OR ' +
476     ' RELC.RDB$CONSTRAINT_TYPE = ''UNIQUE'') AND ' +
477     ' RELC.RDB$RELATION_NAME = :RELATIONNAME ' +
478     'ORDER BY RELC.RDB$CONSTRAINT_TYPE desc, RELC.RDB$CONSTRAINT_NAME';
479    
480     GetGeneratorSQL =
481     'SELECT * FROM RDB$GENERATORS WHERE RDB$GENERATOR_NAME = :GENERATOR';
482    
483     var
484     Collation, CharSetId : integer;
485     i : integer;
486     Column, Constraint : String;
487     SubType : integer;
488     IntChar : integer;
489     qryTables, qryPrecision, qryConstraints, qryRelConstraints, qryGenerators : TIBSQL;
490     PrecisionKnown, ValidRelation : Boolean;
491     FieldScale, FieldType : Integer;
492     CreateTable: string;
493     TableType: integer;
494     begin
495     Result := true;
496     IntChar := 0;
497     ValidRelation := false;
498    
499     if DomainFlag then
500     ListDomains(RelationName);
501     qryTables := TIBSQL.Create(FDatabase);
502     qryPrecision := TIBSQL.Create(FDatabase);
503     qryConstraints := TIBSQL.Create(FDatabase);
504     qryRelConstraints := TIBSQL.Create(FDatabase);
505     qryGenerators := TIBSQL.Create(FDatabase);
506     try
507     qryTables.SQL.Add(TableListSQL);
508     RelationName := trim(RelationName);
509     qryTables.Params.ByName('RelationName').AsString := RelationName;
510     qryTables.ExecQuery;
511     qryPrecision.SQL.Add(PrecisionSQL);
512     qryConstraints.SQL.Add(ConstraintSQL);
513     qryRelConstraints.SQL.Add(RelConstraintsSQL);
514     qryGenerators.SQL.Add(GetGeneratorSQL);
515     if not qryTables.Eof then
516     begin
517     ValidRelation := true;
518     TableType := qryTables.FieldByName('RDB$RELATION_TYPE').AsInteger;
519     if (not qryTables.FieldByName('RDB$OWNER_NAME').IsNull) and
520     (Trim(qryTables.FieldByName('RDB$OWNER_NAME').AsString) <> '') then
521     FMetaData.Add(Format('%s/* Table: %s, Owner: %s */%s',
522     [LineEnding, RelationName,
523     qryTables.FieldByName('RDB$OWNER_NAME').AsString, LineEnding]));
524     if TableType > 3 then
525     CreateTable := 'CREATE GLOBAL TEMPORARY TABLE'
526     else
527     CreateTable := 'CREATE TABLE';
528     if NewName <> '' then
529     FMetaData.Add(Format('%s %s ', [CreateTable,QuoteIdentifier(NewName)]))
530     else
531     FMetaData.Add(Format('%s %s ', [CreateTable,QuoteIdentifier(RelationName)]));
532     if not qryTables.FieldByName('RDB$EXTERNAL_FILE').IsNull then
533     FMetaData.Add(Format('EXTERNAL FILE %s ',
534     [QuotedStr(qryTables.FieldByName('RDB$EXTERNAL_FILE').AsString)]));
535     FMetaData.Add('(');
536     end;
537    
538     while not qryTables.Eof do
539     begin
540     Column := ' ' + QuoteIdentifier( qryTables.FieldByName('RDB$FIELD_NAME').AsString) + TAB;
541    
542     { Check first for computed fields, then domains.
543     If this is a known domain, then just print the domain rather than type
544     Domains won't have length, array, or blob definitions, but they
545     may have not null, default and check overriding their definitions }
546    
547     if not qryTables.FieldByName('rdb$computed_blr').IsNull then
548     begin
549     Column := Column + ' COMPUTED BY ';
550     if not qryTables.FieldByName('RDB$COMPUTED_SOURCE').IsNull then
551     Column := Column + PrintValidation(qryTables.FieldByName('RDB$COMPUTED_SOURCE').AsString, true);
552     end
553     else
554     begin
555     FieldType := qryTables.FieldByName('RDB$FIELD_TYPE').AsInteger;
556     FieldScale := qryTables.FieldByName('RDB$FIELD_SCALE').AsInteger;
557     if not ((Copy(qryTables.FieldByName('RDB$FIELD_NAME1').AsString, 1, 4) = 'RDB$') and
558     (qryTables.FieldByName('RDB$FIELD_NAME1').AsString[5] in ['0'..'9'])) and
559     (qryTables.FieldByName('RDB$SYSTEM_FLAG').AsInteger <> 1) then
560     begin
561     Column := Column + QuoteIdentifier( trim(qryTables.FieldByName('RDB$FIELD_NAME1').AsString));
562     { International character sets }
563     if (qryTables.FieldByName('RDB$FIELD_TYPE').AsInteger in [blr_text, blr_varying])
564     and (not qryTables.FieldByName('RDB$COLLATION_ID').IsNull)
565     and (qryTables.FieldByName('RDB$COLLATION_ID').AsInteger <> 0) then
566     begin
567     Collation := qryTables.FieldByName('RDB$COLLATION_ID').AsInteger;
568     Column := Column + GetCharacterSets(qryTables.FieldByName('RDB$CHARACTER_SET_ID').AsShort,
569     Collation, true);
570     end;
571     end
572     else
573     begin
574     { Look through types array }
575     for i := Low(Columntypes) to High(ColumnTypes) do
576     begin
577     PrecisionKnown := false;
578     if qryTables.FieldByname('RDB$FIELD_TYPE').AsShort = ColumnTypes[i].SQLType then
579     begin
580    
581     if FDatabaseInfo.ODSMajorVersion >= ODS_VERSION10 then
582     begin
583     { Handle Integral subtypes NUMERIC and DECIMAL }
584     if qryTables.FieldByName('RDB$FIELD_TYPE').AsInteger in
585     [blr_short, blr_long, blr_int64] then
586     begin
587     qryPrecision.Params.ByName('FIELDNAME').AsString :=
588     qryTables.FieldByName('RDB$FIELD_NAME1').AsString;
589     qryPrecision.ExecQuery;
590    
591     { We are ODS >= 10 and could be any Dialect }
592     if not qryPrecision.FieldByName('RDB$FIELD_PRECISION').IsNull then
593     begin
594     { We are Dialect >=3 since FIELD_PRECISION is non-NULL }
595     if (qryPrecision.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger > 0) and
596     (qryPrecision.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger <= MAX_INTSUBTYPES) then
597     begin
598     Column := column + Format('%s(%d, %d)',
599     [IntegralSubtypes[qryPrecision.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger],
600     qryPrecision.FieldByName('RDB$FIELD_PRECISION').AsInteger,
601     -qryPrecision.FieldByName('RDB$FIELD_SCALE').AsInteger]);
602     PrecisionKnown := TRUE;
603     end;
604     end;
605     qryPrecision.Close;
606     end;
607     end;
608    
609     if PrecisionKnown = FALSE then
610     begin
611     { Take a stab at numerics and decimals }
612     if (FieldType = blr_short) and (FieldScale < 0) then
613     Column := Column + Format('NUMERIC(4, %d)', [-FieldScale])
614     else
615     if (FieldType = blr_long) and (FieldScale < 0) then
616     Column := Column + Format('NUMERIC(9, %d)', [-FieldScale])
617     else
618     if (FieldType = blr_double) and (FieldScale < 0) then
619     Column := Column + Format('NUMERIC(15, %d)', [-FieldScale])
620     else
621     Column := Column + ColumnTypes[i].TypeName;
622     end;
623     end;
624     end;
625     if FieldType in [blr_text, blr_varying] then
626     if qryTables.FieldByName('RDB$CHARACTER_LENGTH').IsNull then
627     Column := Column + Format('(%d)', [qryTables.FieldByName('RDB$FIELD_LENGTH').AsInteger])
628     else
629     Column := Column + Format('(%d)', [qryTables.FieldByName('RDB$CHARACTER_LENGTH').AsInteger]);
630    
631     { Catch arrays after printing the type }
632    
633     if not qryTables.FieldByName('RDB$DIMENSIONS').IsNull and (qryTables.FieldByName('RDB$DIMENSIONS').AsInteger > 0) then
634     Column := column + GetArrayField(qryTables.FieldByName('RDB$FIELD_SOURCE').AsString);
635    
636     if FieldType = blr_blob then
637     begin
638     subtype := qryTables.FieldByName('RDB$FIELD_SUB_TYPE').AsShort;
639     Column := Column + ' SUB_TYPE ';
640     if (subtype > 0) and (subtype <= MAXSUBTYPES) then
641     Column := Column + SubTypes[subtype]
642     else
643     Column := Column + IntToStr(subtype);
644     column := Column + Format(' SEGMENT SIZE %d',
645     [qryTables.FieldByName('RDB$SEGMENT_LENGTH').AsInteger]);
646     end;
647    
648     { International character sets }
649     if ((FieldType in [blr_text, blr_varying]) or
650     (FieldType = blr_blob)) and
651     (not qryTables.FieldByName('RDB$CHARACTER_SET_ID').IsNull) and
652     (qryTables.FieldByName('RDB$CHARACTER_SET_ID').AsInteger <> 0) then
653     begin
654     { Override rdb$fields id with relation_fields if present }
655    
656     CharSetId := 0;
657     if not qryTables.FieldByName('RDB$CHARACTER_SET_ID').IsNull then
658     CharSetId := qryTables.FieldByName('RDB$CHARACTER_SET_ID').AsInteger;
659    
660     Column := Column + GetCharacterSets(CharSetId, 0, false);
661     intchar := 1;
662     end;
663     end;
664    
665     {Firebird 3 introduces IDENTITY columns. We need to check for them here}
666     if qryTables.HasField('RDB$GENERATOR_NAME') and not qryTables.FieldByName('RDB$GENERATOR_NAME').IsNull then
667     begin
668     qryGenerators.ParamByName('GENERATOR').AsString := qryTables.FieldByName('RDB$GENERATOR_NAME').AsString;
669     qryGenerators.ExecQuery;
670     if not qryGenerators.Eof then
671     begin
672     Column := Column + Format(' GENERATED BY DEFAULT AS IDENTITY (START WITH %d)',
673 tony 221 [qryGenerators.FieldByName('RDB$INITIAL_VALUE').AsInt64]);
674 tony 209 end;
675     qryGenerators.Close;
676     end;
677    
678     { Handle defaults for columns }
679     { Originally This called PrintMetadataTextBlob,
680     should no longer need }
681     if not qryTables.FieldByName('RDB$DEFAULT_SOURCE').IsNull then
682     Column := Column + ' ' + qryTables.FieldByName('RDB$DEFAULT_SOURCE').AsString;
683    
684    
685     { The null flag is either 1 or null (for nullable) . if there is
686     a constraint name, print that too. Domains cannot have named
687     constraints. The column name is in rdb$trigger_name in
688     rdb$check_constraints. We hope we get at most one row back. }
689    
690     if qryTables.FieldByName('RDB$NULL_FLAG').AsInteger = 1 then
691     begin
692     qryConstraints.Params.ByName('FIELDNAME').AsString := qryTables.FieldByName('RDB$FIELD_NAME').AsString;
693     qryConstraints.Params.ByName('RELATIONNAME').AsString := qryTables.FieldByName('RDB$RELATION_NAME').AsString;
694     qryConstraints.ExecQuery;
695    
696     while not qryConstraints.Eof do
697     begin
698     if Pos('INTEG', qryConstraints.FieldByName('RDB$CONSTRAINT_NAME').AsString) <> 1 then
699     Column := Column + Format(' CONSTRAINT %s',
700     [ QuoteIdentifier(
701     qryConstraints.FieldByName('RDB$CONSTRAINT_NAME').AsString)]);
702     qryConstraints.Next;
703     end;
704     qryConstraints.Close;
705     Column := Column + ' NOT NULL';
706     end;
707    
708     if ((FieldType in [blr_text, blr_varying]) or
709     (FieldType = blr_blob)) and
710     (not qryTables.FieldByName('RDB$CHARACTER_SET_ID').IsNull) and
711     (qryTables.FieldByName('RDB$CHARACTER_SET_ID').AsInteger <> 0) and
712     (intchar <> 0) then
713     begin
714     Collation := 0;
715     if not qryTables.FieldByName('RDB$COLLATION_ID1').IsNull then
716     Collation := qryTables.FieldByName('RDB$COLLATION_ID1').AsInteger
717     else
718     if not qryTables.FieldByName('RDB$COLLATION_ID').IsNull then
719     Collation := qryTables.FieldByName('RDB$COLLATION_ID').AsInteger;
720    
721     CharSetId := 0;
722     if not qryTables.FieldByName('RDB$CHARACTER_SET_ID').IsNull then
723     CharSetId := qryTables.FieldByName('RDB$CHARACTER_SET_ID').AsInteger;
724    
725     if Collation <> 0 then
726     Column := Column + GetCharacterSets(CharSetId, Collation, true);
727     end;
728     end;
729     qryTables.Next;
730     if not qryTables.Eof then
731     Column := Column + ',';
732     FMetaData.Add(Column);
733     end;
734    
735     { Do primary and unique keys only. references come later }
736    
737     qryRelConstraints.Params.ByName('relationname').AsString := RelationName;
738     qryRelConstraints.ExecQuery;
739     while not qryRelConstraints.Eof do
740     begin
741     Constraint := '';
742     FMetaData.Strings[FMetaData.Count - 1] := FMetaData.Strings[FMetaData.Count - 1] + ',';
743     { If the name of the constraint is not INTEG..., print it }
744     if Pos('INTEG', qryRelConstraints.FieldByName('RDB$CONSTRAINT_NAME').AsString) <> 1 then
745     Constraint := Constraint + 'CONSTRAINT ' +
746     QuoteIdentifier(
747     qryRelConstraints.FieldByName('RDB$CONSTRAINT_NAME').AsString);
748    
749    
750     if Pos('PRIMARY', qryRelConstraints.FieldByName('RDB$CONSTRAINT_TYPE').AsString) = 1 then
751     begin
752     FMetaData.Add(Constraint + Format(' PRIMARY KEY (%s)',
753     [GetIndexSegments(qryRelConstraints.FieldByName('RDB$INDEX_NAME').AsString)]));
754     end
755     else
756     if Pos('UNIQUE', qryRelConstraints.FieldByName('RDB$CONSTRAINT_TYPE').AsString) = 1 then
757     begin
758     FMetaData.Add(Constraint + Format(' UNIQUE (%s)',
759     [GetIndexSegments(qryRelConstraints.FieldByName('RDB$INDEX_NAME').AsString)]));
760     end;
761     qryRelConstraints.Next;
762     end;
763     if ValidRelation then
764     begin
765     if TableType = 4 then
766     FMetaData.Add(' ) ON COMMIT PRESERVE ROWS ' + TERM)
767     else
768     FMetaData.Add(')' + TERM);
769     end;
770     finally
771     qryTables.Free;
772     qryPrecision.Free;
773     qryConstraints.Free;
774     qryRelConstraints.Free;
775     qryGenerators.Free;
776     end;
777     end;
778    
779     { ExtractListView
780     Functional description
781     Show text of the specified view.
782     Use a SQL query to get the info and print it.
783     Note: This should also contain check option }
784    
785     procedure TIBExtract.ExtractListView(ViewName: String);
786     const
787     ViewsSQL = 'SELECT * FROM RDB$RELATIONS REL ' +
788     ' WHERE ' +
789     ' (REL.RDB$SYSTEM_FLAG <> 1 OR REL.RDB$SYSTEM_FLAG IS NULL) AND ' +
790     ' NOT REL.RDB$VIEW_BLR IS NULL AND ' +
791     ' REL.RDB$RELATION_NAME = :VIEWNAME AND ' +
792     ' REL.RDB$FLAGS = 1 ' +
793     'ORDER BY REL.RDB$RELATION_ID ';
794    
795     ColumnsSQL = 'SELECT * FROM RDB$RELATION_FIELDS RFR ' +
796     'WHERE ' +
797     ' RFR.RDB$RELATION_NAME = :RELATIONNAME ' +
798     'ORDER BY RFR.RDB$FIELD_POSITION ';
799    
800     var
801     qryViews, qryColumns : TIBSQL;
802     RelationName, ColList : String;
803     begin
804     ColList := '';
805     qryViews := TIBSQL.Create(FDatabase);
806     qryColumns := TIBSQL.Create(FDatabase);
807     try
808     qryViews.SQL.Add(ViewsSQL);
809     qryViews.Params.ByName('viewname').AsString := ViewName;
810     qryViews.ExecQuery;
811     while not qryViews.Eof do
812     begin
813     FMetaData.Add('');
814     RelationName := QuoteIdentifier(
815     qryViews.FieldByName('RDB$RELATION_NAME').AsString);
816     FMetaData.Add(Format('%s/* View: %s, Owner: %s */%s', [
817     RelationName,
818     Trim(qryViews.FieldByName('RDB$OWNER_NAME').AsString)]));
819     FMetaData.Add('');
820     FMetaData.Add(Format('CREATE VIEW %s (', [RelationName]));
821    
822     { Get Column List}
823     qryColumns.SQL.Add(ColumnsSQL);
824     qryColumns.Params.ByName('relationname').AsString := RelationName;
825     qryColumns.ExecQuery;
826     while not qryColumns.Eof do
827     begin
828     ColList := ColList + QuoteIdentifier(
829     qryColumns.FieldByName('RDB$FIELD_NAME').AsString);
830     qryColumns.Next;
831     if not qryColumns.Eof then
832     ColList := ColList + ', ';
833     end;
834     FMetaData.Add(ColList + ') AS');
835     FMetaData.Add(qryViews.FieldByName('RDB$VIEW_SOURCE').AsString + Term);
836     qryViews.Next;
837     end;
838     finally
839     qryViews.Free;
840     qryColumns.Free;
841     end;
842     end;
843    
844     function TIBExtract.GetCharacterSets(CharSetId, Collation: integer;
845     CollateOnly: Boolean): String;
846     var
847     CharSetSQL : TIBSQL;
848     DidActivate : Boolean;
849     begin
850     Result := '';
851     if not FTransaction.Active then
852     begin
853     FTransaction.StartTransaction;
854     DidActivate := true;
855     end
856     else
857     DidActivate := false;
858     CharSetSQL := TIBSQL.Create(FDatabase);
859     try
860     if Collation <> 0 then
861     begin
862     CharSetSQL.SQL.Add(CollationSQL);
863     CharSetSQL.Params.ByName('Char_Set_Id').AsInteger := CharSetId;
864     CharSetSQL.Params.ByName('Collation').AsInteger := Collation;
865     CharSetSQL.ExecQuery;
866    
867     { Is specified collation the default collation for character set? }
868     if (Trim(CharSetSQL.FieldByName('RDB$DEFAULT_COLLATE_NAME').AsString) =
869     Trim(CharSetSQL.FieldByName('RDB$COLLATION_NAME').AsString)) then
870     begin
871     if not CollateOnly then
872     Result := ' CHARACTER SET ' + Trim(CharSetSQL.FieldByName('RDB$CHARACTER_SET_NAME').AsString);
873     end
874     else
875     if CollateOnly then
876     Result := ' COLLATE ' + Trim(CharSetSQL.FieldByName('RDB$COLLATION_NAME').AsString)
877     else
878     Result := ' CHARACTER SET ' +
879     Trim(CharSetSQL.FieldByName('RDB$CHARACTER_SET_NAME').AsString) +
880     ' COLLATE ' +
881     Trim(CharSetSQL.FieldByName('RDB$COLLATION_NAME').AsString);
882     end
883     else
884     if CharSetId <> 0 then
885     begin
886     CharSetSQL.SQL.Add(NonCollationSQL);
887     CharSetSQL.Params.ByName('CharSetId').AsShort := CharSetId;
888     CharSetSQL.ExecQuery;
889     Result := ' CHARACTER SET ' + Trim(CharSetSQL.FieldByName('RDB$CHARACTER_SET_NAME').AsString);
890     end;
891     finally
892     CharSetSQL.Free;
893     end;
894     if DidActivate then
895     FTransaction.Commit;
896     end;
897    
898     procedure TIBExtract.Add2MetaData(const Msg: string; IsError: boolean);
899     begin
900     FMetaData.Add(Msg);
901     end;
902    
903     function TIBExtract.GetDatabase: TIBDatabase;
904     begin
905     result := FDatabase;
906     end;
907    
908     { GetIndexSegments
909     Functional description
910     returns the list of columns in an index. }
911    
912     function TIBExtract.GetIndexSegments(indexname: String): String;
913     const
914     IndexNamesSQL =
915     'SELECT * FROM RDB$INDEX_SEGMENTS SEG ' +
916     'WHERE SEG.RDB$INDEX_NAME = :INDEXNAME ' +
917     'ORDER BY SEG.RDB$FIELD_POSITION';
918    
919     var
920     qryColNames : TIBSQL;
921     begin
922     { Query to get column names }
923     Result := '';
924     qryColNames := TIBSQL.Create(FDatabase);
925     try
926     qryColNames.SQL.Add(IndexNamesSQL);
927     qryColNames.Params.ByName('IndexName').AsString := IndexName;
928     qryColNames.ExecQuery;
929     while not qryColNames.Eof do
930     begin
931     { Place a comma and a blank between each segment column name }
932    
933     Result := Result + QuoteIdentifier(
934     qryColNames.FieldByName('RDB$FIELD_NAME').AsString);
935     qryColNames.Next;
936     if not qryColNames.Eof then
937     Result := Result + ', ';
938     end;
939     finally
940     qryColNames.Free;
941     end;
942     end;
943    
944     function TIBExtract.GetTransaction: TIBTransaction;
945     begin
946     Result := FTransaction;
947     end;
948    
949     function TIBExtract.GetTriggerType(TypeID: Int64): string;
950     const
951     AllDDLTriggers = $7FFFFFFFFFFFDFFF shr 1;
952     var separator: string;
953     i: integer;
954    
955     function GetDDLEvent(Phase: TTriggerPhase; ObjectName: string): string;
956     begin
957     Result := '';
958     case Phase of
959     tpCreate:
960     Result := separator + 'CREATE ' + ObjectName;
961     tpAlter:
962     Result := separator + 'ALTER ' + ObjectName;
963     tpDrop:
964     Result := separator + 'Drop ' + ObjectName;
965     end;
966     if Result <> '' then
967     separator := ' OR ';
968     end;
969    
970     begin
971     if TypeID and $2000 <> 0 then
972     {database trigger}
973     begin
974     Result := 'ON ';
975     case TypeID of
976     $2000:
977     Result += 'CONNECT';
978     $2001:
979     Result += 'DISCONNECT';
980     $2002:
981     Result +='TRANSACTION START';
982     $2003:
983     Result += 'TRANSACTION COMMIT';
984     $2004:
985     Result += 'TRANSACTION ROLLBACK';
986     end;
987     end
988     else
989     if TypeID and $4000 <> 0 then
990     {DDL Trigger}
991     begin
992     if TypeID and $01 <> 0 then
993     Result := 'AFTER '
994     else
995     Result := 'BEFORE ';
996     TypeID := TypeID shr 1;
997     separator := '';
998     i := 0;
999     if TypeID = AllDDLTriggers then
1000     Result += 'ANY DDL STATEMENT'
1001     else
1002     repeat
1003     if (DDLTriggers[i].Bits > 0) and (TypeID and $01 <> 0) then
1004     Result += GetDDLEvent(DDLTriggers[i].Bit1,DDLTriggers[i].ObjectName);
1005    
1006     if (DDLTriggers[i].Bits > 1) and (TypeID and $02 <> 0) then
1007     Result += GetDDLEvent(DDLTriggers[i].Bit2,DDLTriggers[i].ObjectName);
1008    
1009     if (DDLTriggers[i].Bits > 2) and (TypeID and $04 <> 0) then
1010     Result += GetDDLEvent(DDLTriggers[i].Bit3,DDLTriggers[i].ObjectName);
1011     TypeID := TypeID shr DDLTriggers[i].Bits;
1012     Inc(i);
1013     until TypeID = 0;
1014     end
1015     else
1016     {Normal Trigger}
1017     begin
1018     Inc(TypeID);
1019     if TypeID and $01 <> 0 then
1020     Result := 'AFTER '
1021     else
1022     Result := 'BEFORE ';
1023     TypeID := TypeID shr 1;
1024     separator := '';
1025     repeat
1026     Result += separator;
1027     separator := ' or ';
1028     case TypeID and $03 of
1029     1:
1030     Result += 'INSERT';
1031     2:
1032     Result += 'UPDATE';
1033     3:
1034     Result += 'DELETE';
1035     end;
1036     TypeID := TypeID shr 2;
1037     until TypeID = 0
1038     end
1039     end;
1040    
1041     { ListAllGrants
1042     Functional description
1043     Print the permissions on all user tables.
1044     Get separate permissions on table/views and then procedures }
1045    
1046     procedure TIBExtract.ListGrants(ExtractTypes: TExtractTypes);
1047     const
1048     SecuritySQL = 'SELECT * FROM RDB$RELATIONS ' +
1049     'WHERE ' +
1050     ' (RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL) AND ' +
1051     ' RDB$SECURITY_CLASS STARTING WITH ''SQL$'' ' +
1052     'ORDER BY RDB$RELATION_NAME';
1053    
1054     DomainSQL = 'select RDB$FIELD_NAME from RDB$FIELDS '+
1055     'where RDB$SYSTEM_FLAG <> 1 and RDB$FIELD_NAME not Similar to ''RDB$%|SEC$%|MON$%|SQL$%'' '+
1056     'order BY RDB$FIELD_NAME';
1057    
1058     CharacterSetSQL = 'Select * From RDB$CHARACTER_SETS '+
1059     'Where RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL ' +
1060     'Order by RDB$CHARACTER_SET_NAME';
1061    
1062     CollationsSQL = 'Select * From RDB$COLLATIONS '+
1063     'Where RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL ' +
1064     'Order by RDB$COLLATION_NAME';
1065    
1066     ProcedureSQL = 'select * from RDB$PROCEDURES '+
1067     'Where RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL ' +
1068     'Order BY RDB$PROCEDURE_NAME';
1069    
1070 tony 221 PackagesSQL = 'select * from RDB$PACKAGES '+
1071     'Where RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL ' +
1072     'Order BY RDB$PACKAGE_NAME';
1073    
1074 tony 209 ExceptionSQL = 'select * from RDB$EXCEPTIONS '+
1075     'Where RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL ' +
1076     'Order BY RDB$EXCEPTION_NAME';
1077    
1078     GeneratorSQL = 'select * from RDB$GENERATORS '+
1079     'Where RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL ' +
1080     'Order BY RDB$GENERATOR_NAME';
1081    
1082     MetadataGrantsSQL =
1083     'Select PR.RDB$USER, PR.RDB$USER_TYPE, '+
1084     'T.RDB$TYPE_NAME as USER_TYPE_NAME, '+
1085     'Case PR.RDB$PRIVILEGE '+
1086     ' When ''C'' then ''CREATE'' '+
1087     ' When ''O'' then ''DROP ANY'' '+
1088     ' When ''L'' then ''ALTER ANY'' End as Privilege, '+
1089     'Case PR.RDB$RELATION_NAME '+
1090     ' When ''SQL$COLLATIONS'' then ''COLLATION'' '+
1091     ' When ''SQL$CHARSETS'' then ''CHARACTER SET'' '+
1092     ' When ''SQL$DATABASE'' then ''DATABASE'' '+
1093     ' When ''SQL$DOMAINS'' then ''DOMAIN'' '+
1094     ' When ''SQL$EXCEPTIONS'' then ''EXCEPTION'' '+
1095     ' When ''SQL$FILTERS'' then ''FILTER'' '+
1096     ' When ''SQL$FUNCTIONS'' then ''FUNCTION'' '+
1097     ' When ''SQL$GENERATORS'' then ''GENERATOR'' '+
1098     ' When ''SQL$PACKAGES'' then ''PACKAGE'' '+
1099     ' When ''SQL$PROCEDURES'' then ''PROCEDURE'' '+
1100     ' When ''SQL$ROLES'' then ''ROLE'' '+
1101     ' When ''SQL$VIEWS'' then ''VIEW'' '+
1102     ' When ''SQL$TABLES'' then ''TABLE'' End as METAOBJECTNAME,'+
1103    
1104     'case when coalesce(RDB$GRANT_OPTION,0) <> 0 then '' WITH GRANT OPTION'' '+
1105     'ELSE '''' End as GRANTOPTION '+
1106     'FROM RDB$USER_PRIVILEGES PR '+
1107     'JOIN RDB$TYPES T On T.RDB$TYPE = PR.RDB$USER_TYPE and T.RDB$FIELD_NAME = ''RDB$OBJECT_TYPE'' '+
1108     'Where PR.RDB$RELATION_NAME like ''SQL$%'' and PR.RDB$PRIVILEGE in (''L'',''C'',''O'')';
1109    
1110     var
1111     qryRoles : TIBSQL;
1112     RelationName : String;
1113     begin
1114     ListRoles;
1115     qryRoles := TIBSQL.Create(FDatabase);
1116     try
1117     { This version of cursor gets only sql tables identified by security class
1118     and misses views, getting only null view_source }
1119    
1120     FMetaData.Add('');
1121     FMetaData.Add('/* Grant permissions for this database */');
1122     FMetaData.Add('');
1123    
1124     try
1125     qryRoles.SQL.Text := SecuritySQL;
1126     qryRoles.ExecQuery;
1127     while not qryRoles.Eof do
1128     begin
1129     RelationName := Trim(qryRoles.FieldByName('rdb$relation_Name').AsString);
1130     ShowGrants(RelationName, Term, not (etGrantsToUser in ExtractTypes));
1131     qryRoles.Next;
1132     end;
1133     finally
1134     qryRoles.Close;
1135     end;
1136    
1137     if etGrantsToUser in ExtractTypes then
1138     ShowGrantRoles(Term);
1139    
1140     if FDatabaseInfo.ODSMajorVersion >= ODS_VERSION12 then
1141     begin
1142     qryRoles.SQL.Text := DomainSQL;
1143     qryRoles.ExecQuery;
1144     try
1145     while not qryRoles.Eof do
1146     begin
1147     ShowGrants(Trim(qryRoles.FieldByName('RDB$FIELD_NAME').AsString), Term,
1148     not (etGrantsToUser in ExtractTypes));
1149     qryRoles.Next;
1150     end;
1151     finally
1152     qryRoles.Close;
1153     end;
1154    
1155     qryRoles.SQL.Text := CharacterSetSQL;
1156     qryRoles.ExecQuery;
1157     try
1158     while not qryRoles.Eof do
1159     begin
1160     ShowGrants(Trim(qryRoles.FieldByName('RDB$CHARACTER_SET_NAME').AsString), Term,
1161     not (etGrantsToUser in ExtractTypes));
1162     qryRoles.Next;
1163     end;
1164     finally
1165     qryRoles.Close;
1166     end;
1167    
1168     qryRoles.SQL.Text := CollationsSQL;
1169     qryRoles.ExecQuery;
1170     try
1171     while not qryRoles.Eof do
1172     begin
1173     ShowGrants(Trim(qryRoles.FieldByName('RDB$COLLATION_NAME').AsString), Term,
1174     not (etGrantsToUser in ExtractTypes));
1175     qryRoles.Next;
1176     end;
1177     finally
1178     qryRoles.Close;
1179     end;
1180    
1181     qryRoles.SQL.Text := ExceptionSQL;
1182     qryRoles.ExecQuery;
1183     try
1184     while not qryRoles.Eof do
1185     begin
1186     ShowGrants(Trim(qryRoles.FieldByName('RDB$EXCEPTION_NAME').AsString), Term,
1187     not (etGrantsToUser in ExtractTypes));
1188     qryRoles.Next;
1189     end;
1190     finally
1191     qryRoles.Close;
1192     end;
1193    
1194     qryRoles.SQL.Text := GeneratorSQL;
1195     qryRoles.ExecQuery;
1196     try
1197     while not qryRoles.Eof do
1198     begin
1199     ShowGrants(Trim(qryRoles.FieldByName('RDB$GENERATOR_NAME').AsString), Term,
1200     not (etGrantsToUser in ExtractTypes));
1201     qryRoles.Next;
1202     end;
1203     finally
1204     qryRoles.Close;
1205     end;
1206     end;
1207    
1208     qryRoles.SQL.Text := ProcedureSQL;
1209     qryRoles.ExecQuery;
1210     try
1211     while not qryRoles.Eof do
1212     begin
1213     ShowGrants(Trim(qryRoles.FieldByName('RDB$PROCEDURE_NAME').AsString), Term,
1214     not (etGrantsToUser in ExtractTypes));
1215     qryRoles.Next;
1216     end;
1217     finally
1218     qryRoles.Close;
1219     end;
1220    
1221 tony 221 if FDatabaseInfo.ODSMajorVersion >= ODS_VERSION12 then
1222     begin
1223     qryRoles.SQL.Text := PackagesSQL;
1224     qryRoles.ExecQuery;
1225     try
1226     while not qryRoles.Eof do
1227     begin
1228     ShowGrants(Trim(qryRoles.FieldByName('RDB$PACKAGE_NAME').AsString), Term,
1229     not (etGrantsToUser in ExtractTypes));
1230     qryRoles.Next;
1231     end;
1232     finally
1233     qryRoles.Close;
1234     end;
1235     end;
1236    
1237 tony 209 {Metadata Grants}
1238     if FDatabaseInfo.ODSMajorVersion >= ODS_VERSION12 then
1239     begin
1240     qryRoles.SQL.Text := MetadataGrantsSQL;
1241     qryRoles.ExecQuery;
1242     while not qryRoles.Eof do
1243     begin
1244     if (etGrantsToUser in ExtractTypes) or
1245     (qryRoles.FieldByName('RDB$USER_TYPE').AsInteger <> obj_user) or
1246     (qryRoles.FieldByName('RDB$USER').AsString = 'PUBLIC') then
1247     FMetaData.Add(Format('GRANT %s %s TO %s %s %s%s', [
1248     qryRoles.FieldByName('Privilege').AsString,
1249     qryRoles.FieldByName('METAOBJECTNAME').AsString,
1250     qryRoles.FieldByName('USER_TYPE_NAME').AsString,
1251     QuoteIdentifier(qryRoles.FieldByName('RDB$USER').AsString),
1252     qryRoles.FieldByName('GRANTOPTION').AsString,
1253     Term]));
1254     qryRoles.Next;
1255     end;
1256     qryRoles.Close;
1257     end;
1258     finally
1259     qryRoles.Free;
1260     end;
1261     end;
1262    
1263 tony 221 procedure TIBExtract.ListPackages(PackageDDLType: TPackageDDLType;
1264     PackageName: string; IncludeGrants: boolean);
1265     const
1266     PackageSQL = 'Select * From RDB$PACKAGES order by RDB$PACKAGE_NAME';
1267     PackageNameSQL = 'Select * From RDB$PACKAGES Where RDB$PACKAGE_NAME = :PackageName order by RDB$PACKAGE_NAME';
1268     PackageHeaderSQL = 'CREATE PACKAGE %s%sAS%s';
1269     PackageBodySQL = 'CREATE PACKAGE BODY %s%sAS%s';
1270     var
1271     qryPackages : TIBSQL;
1272     Header : Boolean;
1273     SList : TStrings;
1274     aPackageName: string;
1275     begin
1276     Header := true;
1277     qryPackages := TIBSQL.Create(FDatabase);
1278     SList := TStringList.Create;
1279     try
1280     if PackageName = '' then
1281     qryPackages.SQL.Text := PackageSQL
1282     else
1283     begin
1284     qryPackages.SQL.Text := PackageNameSQL;
1285     qryPackages.ParamByName('PackageName').AsString := PackageName;
1286     end;
1287    
1288     qryPackages.ExecQuery;
1289     while not qryPackages.Eof do
1290     begin
1291     if Header then
1292     begin
1293     FMetaData.Add('COMMIT WORK;');
1294     FMetaData.Add('SET AUTODDL OFF;');
1295     FMetaData.Add(Format('SET TERM %s %s', [ProcTerm, Term]));
1296     FMetaData.Add(Format('%s/* Package Definitions */%s', [LineEnding, LineEnding]));
1297     Header := false;
1298     end;
1299    
1300     aPackageName := qryPackages.FieldByName('RDB$PACKAGE_NAME').AsString;
1301     if PackageDDLType in [paHeader,paBoth] then
1302     begin
1303     FMetaData.Add(Format(PackageHeaderSQL,[aPackageName,
1304     LineEnding,LineEnding]));
1305     SList.Text := qryPackages.FieldByName('RDB$PACKAGE_HEADER_SOURCE').AsString;
1306     SList.Add(Format(' %s%s', [ProcTerm, LineEnding]));
1307     FMetaData.AddStrings(SList);
1308     end;
1309    
1310     if PackageDDLType in [paBody,paBoth] then
1311     begin
1312     FMetaData.Add(Format(PackageBodySQL,[aPackageName,
1313     LineEnding,LineEnding]));
1314     SList.Text := qryPackages.FieldByName('RDB$PACKAGE_BODY_SOURCE').AsString;
1315     SList.Add(Format(' %s%s', [ProcTerm, LineEnding]));
1316     FMetaData.AddStrings(SList);
1317     end;
1318    
1319     if IncludeGrants then
1320     ShowGrantsTo(aPackageName,obj_package,ProcTerm);
1321     qryPackages.Next;
1322     end;
1323     qryPackages.Close;
1324    
1325     if not Header then
1326     begin
1327     FMetaData.Add(Format('SET TERM %s %s', [Term, ProcTerm]));
1328     FMetaData.Add('COMMIT WORK;');
1329     FMetaData.Add('SET AUTODDL ON;');
1330     end;
1331     finally
1332     SList.Free;
1333     qryPackages.Free;
1334     end;
1335     end;
1336    
1337 tony 209 { ListAllProcs
1338     Functional description
1339     Shows text of a stored procedure given a name.
1340     or lists procedures if no argument.
1341     Since procedures may reference each other, we will create all
1342     dummy procedures of the correct name, then alter these to their
1343     correct form.
1344     Add the parameter names when these procedures are created.
1345    
1346     procname -- Name of procedure to investigate }
1347    
1348     procedure TIBExtract.ListProcs(ProcDDLType: TProcDDLType;
1349     ProcedureName: String; IncludeGrants: boolean);
1350     const
1351     CreateProcedureStr1 = 'CREATE PROCEDURE %s ';
1352     CreateProcedureStr2 = 'BEGIN EXIT; END %s%s';
1353 tony 221 CreateProcedureStr3 = 'BEGIN SUSPEND; EXIT; END %s%s';
1354 tony 209 ProcedureSQL = {Order procedures by dependency order and then procedure name}
1355     'with recursive Procs as ( ' +
1356     'Select RDB$PROCEDURE_NAME, 1 as ProcLevel from RDB$PROCEDURES ' +
1357     'UNION ALL ' +
1358     'Select D.RDB$DEPENDED_ON_NAME, ProcLevel + 1 From RDB$DEPENDENCIES D ' +
1359     'JOIN Procs on Procs.RDB$PROCEDURE_NAME = D.RDB$DEPENDENT_NAME ' +
1360     ' and Procs.RDB$PROCEDURE_NAME <> D.RDB$DEPENDED_ON_NAME ' +
1361     'JOIN RDB$PROCEDURES P On P.RDB$PROCEDURE_NAME = D.RDB$DEPENDED_ON_NAME ' +
1362     ' ) ' +
1363     'SELECT * FROM RDB$PROCEDURES P ' +
1364     'JOIN ( ' +
1365     'Select RDB$PROCEDURE_NAME, max(ProcLevel) as ProcLevel From Procs ' +
1366     'Group By RDB$PROCEDURE_NAME) A On A.RDB$PROCEDURE_NAME = P.RDB$PROCEDURE_NAME ' +
1367     'Order by A.ProcLevel desc, P.RDB$PROCEDURE_NAME asc';
1368    
1369 tony 221 ProcedureSQLODS12 = {Order procedures by dependency order and then procedure name}
1370     'with recursive Procs as ( ' +
1371     'Select RDB$PROCEDURE_NAME, 1 as ProcLevel from RDB$PROCEDURES ' +
1372     'UNION ALL ' +
1373     'Select D.RDB$DEPENDED_ON_NAME, ProcLevel + 1 From RDB$DEPENDENCIES D ' +
1374     'JOIN Procs on Procs.RDB$PROCEDURE_NAME = D.RDB$DEPENDENT_NAME ' +
1375     ' and Procs.RDB$PROCEDURE_NAME <> D.RDB$DEPENDED_ON_NAME ' +
1376     'JOIN RDB$PROCEDURES P On P.RDB$PROCEDURE_NAME = D.RDB$DEPENDED_ON_NAME ' +
1377     ' ) ' +
1378     'SELECT * FROM RDB$PROCEDURES P ' +
1379     'JOIN ( ' +
1380     'Select RDB$PROCEDURE_NAME, max(ProcLevel) as ProcLevel From Procs ' +
1381     'Group By RDB$PROCEDURE_NAME) A On A.RDB$PROCEDURE_NAME = P.RDB$PROCEDURE_NAME ' +
1382     'Where P.RDB$PACKAGE_NAME is NULL '+
1383     'Order by A.ProcLevel desc, P.RDB$PROCEDURE_NAME asc';
1384 tony 209 ProcedureNameSQL =
1385     'SELECT * FROM RDB$PROCEDURES ' +
1386     'WHERE RDB$PROCEDURE_NAME = :ProcedureName ' +
1387     'ORDER BY RDB$PROCEDURE_NAME';
1388    
1389     var
1390     qryProcedures : TIBSQL;
1391     ProcName : String;
1392     SList : TStrings;
1393     Header : Boolean;
1394    
1395     begin
1396    
1397     Header := true;
1398     qryProcedures := TIBSQL.Create(FDatabase);
1399     SList := TStringList.Create;
1400     try
1401     if ProcedureName = '' then
1402 tony 221 begin
1403     if DatabaseInfo.ODSMajorVersion < ODS_VERSION12 then
1404     qryProcedures.SQL.Text := ProcedureSQL
1405     else
1406     qryProcedures.SQL.Text := ProcedureSQLODS12;
1407     end
1408 tony 209 else
1409     begin
1410     qryProcedures.SQL.Text := ProcedureNameSQL;
1411     qryProcedures.Params.ByName('ProcedureName').AsString := ProcedureName;
1412     end;
1413    
1414     qryProcedures.ExecQuery;
1415     while not qryProcedures.Eof do
1416     begin
1417     if Header then
1418     begin
1419     FMetaData.Add('COMMIT WORK;');
1420     FMetaData.Add('SET AUTODDL OFF;');
1421     FMetaData.Add(Format('SET TERM %s %s', [ProcTerm, Term]));
1422     FMetaData.Add(Format('%s/* Stored procedures */%s', [LineEnding, LineEnding]));
1423     Header := false;
1424     end;
1425     ProcName := Trim(qryProcedures.FieldByName('RDB$PROCEDURE_NAME').AsString);
1426    
1427     case ProcDDLType of
1428     pdCreateStub:
1429     begin
1430     FMetaData.Add(Format(CreateProcedureStr1, [QuoteIdentifier(
1431     ProcName)]));
1432     GetProcedureArgs(ProcName);
1433 tony 221 if qryProcedures.FieldByName('RDB$PROCEDURE_TYPE').AsInteger = 1 then
1434     FMetaData.Add(Format(CreateProcedureStr3, [ProcTerm, LineEnding]))
1435     else
1436     FMetaData.Add(Format(CreateProcedureStr2, [ProcTerm, LineEnding]));
1437 tony 209 end;
1438    
1439     pdCreateProc:
1440     begin
1441     FMetaData.Add(Format(CreateProcedureStr1, [QuoteIdentifier(
1442     ProcName)]));
1443     GetProcedureArgs(ProcName);
1444     if not qryProcedures.FieldByName('RDB$PROCEDURE_SOURCE').IsNull then
1445     begin
1446     SList.Text := qryProcedures.FieldByName('RDB$PROCEDURE_SOURCE').AsString;
1447     SList.Add(Format(' %s%s', [ProcTerm, LineEnding]));
1448     FMetaData.AddStrings(SList);
1449     end
1450     else
1451     FMetaData.Add(Format(CreateProcedureStr2, [ProcTerm, LineEnding]));
1452     end;
1453    
1454     pdAlterProc:
1455     begin
1456     FMetaData.Add(Format('%sALTER PROCEDURE %s ', [LineEnding,
1457     QuoteIdentifier( ProcName)]));
1458     GetProcedureArgs(ProcName);
1459    
1460     if not qryProcedures.FieldByName('RDB$PROCEDURE_SOURCE').IsNull then
1461     begin
1462     SList.Text := qryProcedures.FieldByName('RDB$PROCEDURE_SOURCE').AsString;
1463     SList.Add(Format(' %s%s', [ProcTerm, LineEnding]));
1464     FMetaData.AddStrings(SList);
1465     end
1466     else
1467     FMetaData.Add(Format(CreateProcedureStr2, [ProcTerm, LineEnding]));
1468     end;
1469     end;
1470     if IncludeGrants then
1471     ShowGrantsTo(ProcName,obj_procedure,ProcTerm);
1472     qryProcedures.Next;
1473     end;
1474     qryProcedures.Close;
1475    
1476     if not Header then
1477     begin
1478     FMetaData.Add(Format('SET TERM %s %s', [Term, ProcTerm]));
1479     FMetaData.Add('COMMIT WORK;');
1480     FMetaData.Add('SET AUTODDL ON;');
1481     end;
1482     finally
1483     qryProcedures.Free;
1484     SList.Free;
1485     end;
1486     end;
1487    
1488     { ListAllTables
1489     Functional description
1490     Extract the names of all user tables from
1491     rdb$relations. Filter SQL tables by
1492     security class after we fetch them
1493     Parameters: flag -- 0, get all tables }
1494    
1495     procedure TIBExtract.ListAllTables(flag: Boolean);
1496     const
1497     TableSQL =
1498     'SELECT * FROM RDB$RELATIONS ' +
1499     'WHERE ' +
1500     ' (RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL) AND ' +
1501     ' RDB$VIEW_BLR IS NULL ' +
1502     'ORDER BY RDB$RELATION_NAME';
1503    
1504     var
1505     qryTables : TIBSQL;
1506     begin
1507     { This version of cursor gets only sql tables identified by security class
1508     and misses views, getting only null view_source }
1509    
1510     qryTables := TIBSQL.Create(FDatabase);
1511     try
1512     qryTables.SQL.Text := TableSQL;
1513     qryTables.ExecQuery;
1514     while not qryTables.Eof do
1515     begin
1516     if ((qryTables.FieldByName('RDB$FLAGS').AsInteger <> 1) and
1517     (not Flag)) then
1518     continue;
1519     if flag or (Pos('SQL$', qryTables.FieldByName('RDB$SECURITY_CLASS').AsString) <> 1) then
1520     ExtractListTable(qryTables.FieldByName('RDB$RELATION_NAME').AsString,
1521     '', false);
1522    
1523     qryTables.Next;
1524     end;
1525     finally
1526     qryTables.Free;
1527     end;
1528     end;
1529    
1530     { ListAllTriggers
1531     Functional description
1532     Lists triggers in general on non-system
1533     tables with sql source only. }
1534    
1535     procedure TIBExtract.ListTriggers(ObjectName: String; ExtractTypes: TExtractTypes
1536     );
1537     const
1538     { Query gets the trigger info for non-system triggers with
1539     source that are not part of an SQL constraint }
1540    
1541     TriggerSQL =
1542     'SELECT * FROM RDB$TRIGGERS TRG Left Outer JOIN RDB$RELATIONS REL ON ' +
1543     ' TRG.RDB$RELATION_NAME = REL.RDB$RELATION_NAME ' +
1544     'WHERE ' +
1545     ' (REL.RDB$SYSTEM_FLAG <> 1 OR REL.RDB$SYSTEM_FLAG IS NULL) AND ' +
1546     ' NOT EXISTS (SELECT * FROM RDB$CHECK_CONSTRAINTS CHK WHERE ' +
1547     ' TRG.RDB$TRIGGER_NAME = CHK.RDB$TRIGGER_NAME) ' +
1548     'ORDER BY TRG.RDB$RELATION_NAME, TRG.RDB$TRIGGER_TYPE, ' +
1549     ' TRG.RDB$TRIGGER_SEQUENCE, TRG.RDB$TRIGGER_NAME';
1550    
1551     TriggerNameSQL =
1552     'SELECT * FROM RDB$TRIGGERS TRG JOIN RDB$RELATIONS REL ON ' +
1553     ' TRG.RDB$RELATION_NAME = REL.RDB$RELATION_NAME ' +
1554     'WHERE ' +
1555     ' REL.RDB$RELATION_NAME = :TableName AND ' +
1556     ' (REL.RDB$SYSTEM_FLAG <> 1 OR REL.RDB$SYSTEM_FLAG IS NULL) AND ' +
1557     ' NOT EXISTS (SELECT * FROM RDB$CHECK_CONSTRAINTS CHK WHERE ' +
1558     ' TRG.RDB$TRIGGER_NAME = CHK.RDB$TRIGGER_NAME) ' +
1559     'ORDER BY TRG.RDB$RELATION_NAME, TRG.RDB$TRIGGER_TYPE, ' +
1560     ' TRG.RDB$TRIGGER_SEQUENCE, TRG.RDB$TRIGGER_NAME';
1561    
1562     TriggerByNameSQL =
1563     'SELECT * FROM RDB$TRIGGERS TRG Left Outer JOIN RDB$RELATIONS REL ON ' +
1564     ' TRG.RDB$RELATION_NAME = REL.RDB$RELATION_NAME ' +
1565     'WHERE ' +
1566     ' TRG.RDB$TRIGGER_NAME = :TriggerName AND ' +
1567     ' (REL.RDB$SYSTEM_FLAG <> 1 OR REL.RDB$SYSTEM_FLAG IS NULL) AND ' +
1568     ' NOT EXISTS (SELECT * FROM RDB$CHECK_CONSTRAINTS CHK WHERE ' +
1569     ' TRG.RDB$TRIGGER_NAME = CHK.RDB$TRIGGER_NAME) ' +
1570     'ORDER BY TRG.RDB$RELATION_NAME, TRG.RDB$TRIGGER_TYPE, ' +
1571     ' TRG.RDB$TRIGGER_SEQUENCE, TRG.RDB$TRIGGER_NAME';
1572    
1573     var
1574     Header : Boolean;
1575     TriggerName, RelationName, InActive: String;
1576     qryTriggers : TIBSQL;
1577     SList : TStrings;
1578     begin
1579     Header := true;
1580 tony 226 if [etTable,etTrigger ] * ExtractTypes <> [] then
1581     ExtractTypes -= [etDatabaseTriggers,etDDLTriggers];
1582 tony 209 SList := TStringList.Create;
1583     qryTriggers := TIBSQL.Create(FDatabase);
1584     try
1585     if ObjectName = '' then
1586     qryTriggers.SQL.Text := TriggerSQL
1587     else
1588     begin
1589     if etTable in ExtractTypes then
1590     begin
1591     qryTriggers.SQL.Text := TriggerNameSQL;
1592     qryTriggers.Params.ByName('TableName').AsString := ObjectName;
1593     end
1594     else
1595     begin
1596     qryTriggers.SQL.Text := TriggerByNameSQL;
1597     qryTriggers.Params.ByName('TriggerName').AsString := ObjectName;
1598     end;
1599     end;
1600     qryTriggers.ExecQuery;
1601     while not qryTriggers.Eof do
1602     begin
1603     SList.Clear;
1604     if Header then
1605     begin
1606     FMetaData.Add(Format('SET TERM %s %s%s', [Procterm, Term, LineEnding]));
1607     FMetaData.Add(Format('%s/* Triggers only will work for SQL triggers */%s',
1608     [LineEnding, LineEnding]));
1609     Header := false;
1610     end;
1611     TriggerName := qryTriggers.FieldByName('RDB$TRIGGER_NAME').AsString;
1612     RelationName := qryTriggers.FieldByName('RDB$RELATION_NAME').AsString;
1613     if qryTriggers.FieldByName('RDB$TRIGGER_INACTIVE').IsNull then
1614     InActive := 'INACTIVE'
1615     else
1616     if qryTriggers.FieldByName('RDB$TRIGGER_INACTIVE').AsInteger = 1 then
1617     InActive := 'INACTIVE'
1618     else
1619     InActive := 'ACTIVE';
1620    
1621 tony 226 if (ExtractTypes * [etDatabaseTriggers,etDDLTriggers] = []) or
1622     ((etDatabaseTriggers in ExtractTypes) and (qryTriggers.FieldByName('RDB$TRIGGER_TYPE').AsInt64 and $2000 <> 0)) or
1623     ((etDDLTriggers in ExtractTypes) and (qryTriggers.FieldByName('RDB$TRIGGER_TYPE').AsInt64 and $4000 <> 0))
1624     then
1625     begin
1626     if qryTriggers.FieldByName('RDB$FLAGS').AsInteger <> 1 then
1627     SList.Add('/* ');
1628 tony 209
1629 tony 226 {Database or Transaction trigger}
1630     SList.Add(Format('CREATE TRIGGER %s%s%s %s POSITION %d',
1631     [QuoteIdentifier( TriggerName),
1632     LineEnding, InActive,
1633     GetTriggerType(qryTriggers.FieldByName('RDB$TRIGGER_TYPE').AsInt64),
1634     qryTriggers.FieldByName('RDB$TRIGGER_SEQUENCE').AsInteger]));
1635 tony 209
1636 tony 226 if RelationName <> '' then
1637     SList.Add('ON ' + QuoteIdentifier( RelationName));
1638 tony 209
1639 tony 226 if not qryTriggers.FieldByName('RDB$TRIGGER_SOURCE').IsNull then
1640     SList.Add(qryTriggers.FieldByName('RDB$TRIGGER_SOURCE').AsString)
1641     else
1642     SList.Add('AS BEGIN EXIT; END');
1643     SList.Add(' ' + ProcTerm);
1644     if qryTriggers.FieldByName('RDB$FLAGS').AsInteger <> 1 then
1645     SList.Add(' */');
1646     FMetaData.AddStrings(SList);
1647     if etGrant in ExtractTypes then
1648     ShowGrantsTo(TriggerName,obj_trigger,ProcTerm);
1649     end;
1650 tony 209 qryTriggers.Next;
1651     end;
1652     if not Header then
1653     begin
1654     FMetaData.Add('COMMIT WORK ' + ProcTerm);
1655     FMetaData.Add('SET TERM ' + Term + ProcTerm);
1656     end;
1657     finally
1658     qryTriggers.Free;
1659     SList.Free;
1660     end;
1661     end;
1662    
1663     { ListCheck
1664     Functional description
1665     List check constraints for all objects to allow forward references }
1666    
1667     procedure TIBExtract.ListCheck(ObjectName : String; ExtractType : TExtractType);
1668     const
1669     { Query gets the check clauses for triggers stored for check constraints }
1670     CheckSQL =
1671     'SELECT * FROM RDB$TRIGGERS TRG JOIN RDB$CHECK_CONSTRAINTS CHK ON ' +
1672     ' TRG.RDB$TRIGGER_NAME = CHK.RDB$TRIGGER_NAME ' +
1673     'WHERE ' +
1674     ' TRG.RDB$TRIGGER_TYPE = 1 AND ' +
1675     ' EXISTS (SELECT RDB$CONSTRAINT_NAME FROM RDB$RELATION_CONSTRAINTS RELC WHERE ' +
1676     ' CHK.RDB$CONSTRAINT_NAME = RELC.RDB$CONSTRAINT_NAME) ' +
1677     'ORDER BY CHK.RDB$CONSTRAINT_NAME';
1678    
1679     CheckNameSQL =
1680     'SELECT * FROM RDB$TRIGGERS TRG JOIN RDB$CHECK_CONSTRAINTS CHK ON ' +
1681     ' TRG.RDB$TRIGGER_NAME = CHK.RDB$TRIGGER_NAME ' +
1682     'WHERE ' +
1683     ' TRG.RDB$RELATION_NAME = :TableName AND ' +
1684     ' TRG.RDB$TRIGGER_TYPE = 1 AND ' +
1685     ' EXISTS (SELECT RDB$CONSTRAINT_NAME FROM RDB$RELATION_CONSTRAINTS RELC WHERE ' +
1686     ' CHK.RDB$CONSTRAINT_NAME = RELC.RDB$CONSTRAINT_NAME) ' +
1687     'ORDER BY CHK.RDB$CONSTRAINT_NAME';
1688    
1689     CheckByNameSQL =
1690     'SELECT * FROM RDB$TRIGGERS TRG JOIN RDB$CHECK_CONSTRAINTS CHK ON ' +
1691     ' TRG.RDB$TRIGGER_NAME = CHK.RDB$TRIGGER_NAME ' +
1692     'WHERE ' +
1693     ' TRG.RDB$TRIGGER_NAME = :TriggerName AND ' +
1694     ' TRG.RDB$TRIGGER_TYPE = 1 AND ' +
1695     ' EXISTS (SELECT RDB$CONSTRAINT_NAME FROM RDB$RELATION_CONSTRAINTS RELC WHERE ' +
1696     ' CHK.RDB$CONSTRAINT_NAME = RELC.RDB$CONSTRAINT_NAME) ' +
1697     'ORDER BY CHK.RDB$CONSTRAINT_NAME';
1698    
1699     var
1700     qryChecks : TIBSQL;
1701     SList : TStrings;
1702     RelationName : String;
1703     begin
1704     qryChecks := TIBSQL.Create(FDatabase);
1705     SList := TStringList.Create;
1706     try
1707     if ObjectName = '' then
1708     qryChecks.SQL.Text := CheckSQL
1709     else
1710     if ExtractType = etTable then
1711     begin
1712     qryChecks.SQL.Text := CheckNameSQL;
1713     qryChecks.Params.ByName('TableName').AsString := ObjectName;
1714     end
1715     else
1716     begin
1717     qryChecks.SQL.Text := CheckByNameSQL;
1718     qryChecks.Params.ByName('TriggerName').AsString := ObjectName;
1719     end;
1720     qryChecks.ExecQuery;
1721     while not qryChecks.Eof do
1722     begin
1723     SList.Clear;
1724     RelationName := qryChecks.FieldByName('RDB$RELATION_NAME').AsString;
1725     SList.Add(Format('ALTER TABLE %s ADD',
1726     [QuoteIdentifier( RelationName)]));
1727     if Pos('INTEG', qryChecks.FieldByName('RDB$CONSTRAINT_NAME').AsString) <> 1 then
1728     SList.Add(Format('%sCONSTRAINT %s ', [TAB,
1729     QuoteIdentifier( qryChecks.FieldByName('RDB$CONSTRAINT_NAME').AsString)]));
1730    
1731     if not qryChecks.FieldByName('RDB$TRIGGER_SOURCE').IsNull then
1732     SList.Text := SList.Text + qryChecks.FieldByName('RDB$TRIGGER_SOURCE').AsString;
1733    
1734     SList.Strings[SList.Count - 1] := SList.Strings[SList.Count - 1] + (Term) + LineEnding;
1735     FMetaData.AddStrings(SList);
1736     qryChecks.Next;
1737     end;
1738     finally
1739     qryChecks.Free;
1740     SList.Free;
1741     end;
1742     end;
1743    
1744     { ListCreateDb
1745     Functional description
1746     Print the create database command if requested. At least put
1747     the page size in a comment with the extracted db name }
1748    
1749     procedure TIBExtract.ListCreateDb(TargetDb : String);
1750     const
1751     CharInfoSQL =
1752     'SELECT * FROM RDB$DATABASE DBP ' +
1753     'WHERE NOT DBP.RDB$CHARACTER_SET_NAME IS NULL ' +
1754     ' AND DBP.RDB$CHARACTER_SET_NAME <> '' ''';
1755    
1756     FilesSQL =
1757     'select * from RDB$FILES ' +
1758     'order BY RDB$SHADOW_NUMBER, RDB$FILE_SEQUENCE';
1759    
1760     LogsSQL =
1761     'SELECT * FROM RDB$LOG_FILES ' +
1762     'ORDER BY RDB$FILE_FLAGS, RDB$FILE_SEQUENCE';
1763    
1764     var
1765     NoDb, First, FirstFile, HasWal, SetUsed : Boolean;
1766     Buffer : String;
1767     qryDB : TIBSQL;
1768     FileFlags, FileLength, FileSequence, FileStart : Integer;
1769    
1770     function GetLongDatabaseInfo(DatabaseInfoCommand: Integer): LongInt;
1771     begin
1772     with Database.Attachment.GetDBInformation([DatabaseInfoCommand]) do
1773     if (Count > 0) and (Items[0].GetItemType = DatabaseInfoCommand) then
1774     Result := Items[0].AsInteger
1775     else
1776     IBError(ibxeUnexpectedDatabaseInfoResp,[nil]);
1777     end;
1778    
1779     begin
1780     NoDb := FALSE;
1781     First := TRUE;
1782     FirstFile := TRUE;
1783     HasWal := FALSE;
1784     SetUsed := FALSE;
1785     Buffer := '';
1786     if TargetDb = '' then
1787     begin
1788     Buffer := '/* ';
1789     TargetDb := FDatabase.DatabaseName;
1790     NoDb := true;
1791     end;
1792     Buffer := Buffer + 'CREATE DATABASE ' + QuotedStr(TargetDb) + ' PAGE_SIZE ' +
1793     IntToStr(FDatabaseInfo.PageSize) + LineEnding;
1794     FMetaData.Add(Buffer);
1795     Buffer := '';
1796    
1797     qryDB := TIBSQL.Create(FDatabase);
1798     try
1799     qryDB.SQL.Text := CharInfoSQL;
1800     qryDB.ExecQuery;
1801    
1802     if not qryDB.EOF then
1803     Buffer := Format(' DEFAULT CHARACTER SET %s',
1804     [trim(qryDB.FieldByName('RDB$CHARACTER_SET_NAME').AsString)]);
1805     if NoDB then
1806     Buffer := Buffer + Term + ' */'
1807     else
1808     Buffer := Buffer + Term;
1809     FMetaData.Add(Buffer);
1810     qryDB.Close;
1811     {List secondary files and shadows as
1812     alter db and create shadow in comment}
1813     qryDB.SQL.Text := FilesSQL;
1814     qryDB.ExecQuery;
1815     while not qryDB.Eof do
1816     begin
1817     if First then
1818     begin
1819     FMetaData.Add(LineEnding + '/* Add secondary files in comments ');
1820     First := false;
1821     end; //end_if
1822    
1823     if qryDB.FieldByName('RDB$FILE_FLAGS').IsNull then
1824     FileFlags := 0
1825     else
1826     FileFlags := qryDB.FieldByName('RDB$FILE_FLAGS').AsInteger;
1827     if qryDB.FieldByName('RDB$FILE_LENGTH').IsNull then
1828     FileLength := 0
1829     else
1830     FileLength := qryDB.FieldByName('RDB$FILE_LENGTH').AsInteger;
1831     if qryDB.FieldByName('RDB$FILE_SEQUENCE').IsNull then
1832     FileSequence := 0
1833     else
1834     FileSequence := qryDB.FieldByName('RDB$FILE_SEQUENCE').AsInteger;
1835     if qryDB.FieldByName('RDB$FILE_START').IsNull then
1836     FileStart := 0
1837     else
1838     FileStart := qryDB.FieldByName('RDB$FILE_START').AsInteger;
1839    
1840     { Pure secondary files }
1841     if FileFlags = 0 then
1842     begin
1843     Buffer := Format('%sALTER DATABASE ADD FILE ''%s''',
1844     [LineEnding, qryDB.FieldByName('RDB$FILE_NAME').AsString]);
1845     if FileStart <> 0 then
1846     Buffer := Buffer + Format(' STARTING %d', [FileStart]);
1847     if FileLength <> 0 then
1848     Buffer := Buffer + Format(' LENGTH %d', [FileLength]);
1849     FMetaData.Add(Buffer);
1850     end; //end_if
1851     if (FileFlags and FILE_cache) <> 0 then
1852     FMetaData.Add(Format('%sALTER DATABASE ADD CACHE ''%s'' LENGTH %d',
1853     [LineEnding, qryDB.FieldByName('RDB$FILE_NAME').AsString, FileLength]));
1854    
1855     Buffer := '';
1856     if (FileFlags and FILE_shadow) <> 0 then
1857     begin
1858     if FileSequence <> 0 then
1859     Buffer := Format('%sFILE ''%s''',
1860     [TAB, qryDB.FieldByName('RDB$FILE_NAME').AsString])
1861     else
1862     begin
1863     Buffer := Format('%sCREATE SHADOW %d ''%s'' ',
1864     [LineEnding, qryDB.FieldByName('RDB$SHADOW_NUMBER').AsInteger,
1865     qryDB.FieldByName('RDB$FILE_NAME').AsString]);
1866     if (FileFlags and FILE_inactive) <> 0 then
1867     Buffer := Buffer + 'INACTIVE ';
1868     if (FileFlags and FILE_manual) <> 0 then
1869     Buffer := Buffer + 'MANUAL '
1870     else
1871     Buffer := Buffer + 'AUTO ';
1872     if (FileFlags and FILE_conditional) <> 0 then
1873     Buffer := Buffer + 'CONDITIONAL ';
1874     end; //end_else
1875     if FileLength <> 0 then
1876     Buffer := Buffer + Format('LENGTH %d ', [FileLength]);
1877     if FileStart <> 0 then
1878     Buffer := Buffer + Format('STARTING %d ', [FileStart]);
1879     FMetaData.Add(Buffer);
1880     end; //end_if
1881     qryDB.Next;
1882     end;
1883     qryDB.Close;
1884    
1885     qryDB.SQL.Text := LogsSQL;
1886     qryDB.ExecQuery;
1887     while not qryDB.Eof do
1888     begin
1889    
1890     if qryDB.FieldByName('RDB$FILE_FLAGS').IsNull then
1891     FileFlags := 0
1892     else
1893     FileFlags := qryDB.FieldByName('RDB$FILE_FLAGS').AsInteger;
1894     if qryDB.FieldByName('RDB$FILE_LENGTH').IsNull then
1895     FileLength := 0
1896     else
1897     FileLength := qryDB.FieldByName('RDB$FILE_LENGTH').AsInteger;
1898    
1899     Buffer := '';
1900     HasWal := true;
1901     if First then
1902     begin
1903     if NoDB then
1904     Buffer := '/* ';
1905     Buffer := Buffer + LineEnding + 'ALTER DATABASE ADD ';
1906     First := false;
1907     end; //end_if
1908     if FirstFile then
1909     Buffer := Buffer + 'LOGFILE ';
1910     { Overflow files also have the serial bit set }
1911     if (FileFlags and LOG_default) = 0 then
1912     begin
1913     if (FileFlags and LOG_overflow) <> 0 then
1914     Buffer := Buffer + Format(')%s OVERFLOW ''%s''',
1915     [LineEnding, qryDB.FieldByName('RDB$FILE_NAME').AsString])
1916     else
1917     if (FileFlags and LOG_serial) <> 0 then
1918     Buffer := Buffer + Format('%s BASE_NAME ''%s''',
1919     [LineEnding, qryDB.FieldByName('RDB$FILE_NAME').AsString])
1920     { Since we are fetching order by FILE_FLAGS, the LOG_0verflow will
1921     be last. It will only appear if there were named round robin,
1922     so we must close the parens first }
1923    
1924     { We have round robin and overflow file specifications }
1925     else
1926     begin
1927     if FirstFile then
1928     Buffer := Buffer + '('
1929     else
1930     Buffer := Buffer + Format(',%s ', [LineEnding]);
1931     FirstFile := false;
1932    
1933     Buffer := Buffer + Format('''%s''', [qryDB.FieldByName('RDB$FILE_NAME').AsString]);
1934     end; //end_else
1935     end;
1936     { Any file can have a length }
1937     if FileLength <> 0 then
1938     Buffer := Buffer + Format(' SIZE %d ', [FileLength]);
1939     FMetaData.Add(Buffer);
1940     qryDB.Next;
1941     end;
1942     qryDB.Close;
1943     Buffer := '';
1944     if HasWal then
1945     begin
1946     Buffer := Buffer + PrintSet(SetUsed);
1947     Buffer := Buffer + Format('NUM_LOG_BUFFERS = %d',
1948     [GetLongDatabaseInfo(isc_info_num_wal_buffers)]);
1949     Buffer := Buffer + PrintSet(SetUsed);
1950     Buffer := Buffer + Format('LOG_BUFFER_SIZE = %d',
1951     [GetLongDatabaseInfo(isc_info_wal_buffer_size)]);
1952     Buffer := Buffer + PrintSet(SetUsed);
1953     Buffer := Buffer + Format('GROUP_COMMIT_WAIT_TIME = %d',
1954     [GetLongDatabaseInfo(isc_info_wal_grpc_wait_usecs)]);
1955     Buffer := Buffer + PrintSet(SetUsed);
1956     Buffer := Buffer + Format('CHECK_POINT_LENGTH = %d',
1957     [GetLongDatabaseInfo(isc_info_wal_ckpt_length)]);
1958     FMetaData.Add(Buffer);
1959    
1960     end;
1961     if not First then
1962     begin
1963     if NoDB then
1964     FMetaData.Add(Format('%s */%s', [LineEnding, LineEnding]))
1965     else
1966     FMetaData.Add(Format('%s%s%s', [Term, LineEnding, LineEnding]));
1967     end;
1968     finally
1969     qryDB.Free;
1970     end;
1971    
1972     (*
1973     *)
1974     end;
1975    
1976     { ListDomainTable
1977     Functional description
1978     List domains as identified by fields with any constraints on them
1979     for the named table
1980    
1981     Parameters: table_name == only extract domains for this table }
1982    
1983     procedure TIBExtract.ListDomains(ObjectName: String; ExtractType : TExtractType);
1984     const
1985     DomainSQL =
1986     'SELECT distinct fld.* FROM RDB$FIELDS FLD JOIN RDB$RELATION_FIELDS RFR ON ' +
1987     ' RFR.RDB$FIELD_SOURCE = FLD.RDB$FIELD_NAME ' +
1988     'WHERE RFR.RDB$RELATION_NAME = :TABLE_NAME ' +
1989     'ORDER BY FLD.RDB$FIELD_NAME';
1990    
1991     DomainByNameSQL =
1992     'SELECT * FROM RDB$FIELDS FLD ' +
1993     'WHERE FLD.RDB$FIELD_NAME = :DomainName ' +
1994     'ORDER BY FLD.RDB$FIELD_NAME';
1995    
1996     AllDomainSQL =
1997     'select * from RDB$FIELDS ' +
1998     'where RDB$SYSTEM_FLAG <> 1 ' +
1999     'order BY RDB$FIELD_NAME';
2000    
2001     var
2002     First : Boolean;
2003     qryDomains : TIBSQL;
2004     FieldName, Line : String;
2005    
2006     function FormatDomainStr : String;
2007     var
2008     i, SubType : Integer;
2009     PrecisionKnown : Boolean;
2010     begin
2011     Result := '';
2012     for i := Low(ColumnTypes) to High(ColumnTypes) do
2013     if qryDomains.FieldByName('RDB$FIELD_TYPE').AsInteger = ColumnTypes[i].SQLType then
2014     begin
2015     PrecisionKnown := FALSE;
2016     if FDatabaseInfo.ODSMajorVersion >= ODS_VERSION10 then
2017     begin
2018     if qryDomains.FieldByName('RDB$FIELD_TYPE').AsInteger in [blr_short, blr_long, blr_int64] then
2019     begin
2020     { We are ODS >= 10 and could be any Dialect }
2021     if (FDatabaseInfo.DBSQLDialect >= 3) and
2022     (not qryDomains.FieldByName('RDB$FIELD_PRECISION').IsNull) and
2023     (qryDomains.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger > 0) and
2024     (qryDomains.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger <= MAX_INTSUBTYPES) then
2025     begin
2026     Result := Result + Format('%s(%d, %d)', [
2027     IntegralSubtypes [qryDomains.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger],
2028     qryDomains.FieldByName('RDB$FIELD_PRECISION').AsInteger,
2029     -1 * qryDomains.FieldByName('RDB$FIELD_SCALE').AsInteger]);
2030     PrecisionKnown := true;
2031     end;
2032     end;
2033     end;
2034     if PrecisionKnown = false then
2035     begin
2036     { Take a stab at numerics and decimals }
2037     if (qryDomains.FieldByName('RDB$FIELD_TYPE').AsInteger = blr_short) and
2038     (qryDomains.FieldByName('RDB$FIELD_SCALE').AsInteger < 0) then
2039     Result := Result + Format('NUMERIC(4, %d)',
2040     [-qryDomains.FieldByName('RDB$FIELD_SCALE').AsInteger] )
2041     else
2042     if (qryDomains.FieldByName('RDB$FIELD_TYPE').AsInteger = blr_long) and
2043     (qryDomains.FieldByName('RDB$FIELD_SCALE').AsInteger < 0) then
2044     Result := Result + Format('NUMERIC(9, %d)',
2045     [-qryDomains.FieldByName('RDB$FIELD_SCALE').AsInteger] )
2046     else
2047     if (qryDomains.FieldByName('RDB$FIELD_TYPE').AsInteger = blr_double) and
2048     (qryDomains.FieldByName('RDB$FIELD_SCALE').AsInteger < 0) then
2049     Result := Result + Format('NUMERIC(15, %d)',
2050     [-qryDomains.FieldByName('RDB$FIELD_SCALE').AsInteger] )
2051     else
2052     Result := Result + ColumnTypes[i].TypeName;
2053     end;
2054     break;
2055     end;
2056    
2057     if qryDomains.FieldByName('RDB$FIELD_TYPE').AsInteger = blr_blob then
2058     begin
2059     subtype := qryDomains.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger;
2060     Result := Result + ' SUB_TYPE ';
2061     if (subtype > 0) and (subtype <= MAXSUBTYPES) then
2062     Result := Result + SubTypes[subtype]
2063     else
2064     Result := Result + Format('%d', [subtype]);
2065     Result := Result + Format(' SEGMENT SIZE %d', [qryDomains.FieldByName('RDB$SEGMENT_LENGTH').AsInteger]);
2066     end //end_if
2067     else
2068     if (qryDomains.FieldByName('RDB$FIELD_TYPE').AsInteger in [blr_text, blr_varying]) then
2069     begin
2070     if not qryDomains.FieldByName('RDB$CHARACTER_LENGTH').IsNull then
2071     Result := Result + Format('(%d)', [qryDomains.FieldByName('RDB$CHARACTER_LENGTH').AsInteger])
2072     else
2073     Result := Result + Format('(%d)', [qryDomains.FieldByName('RDB$FIELD_LENGTH').AsInteger]);
2074     end;
2075    
2076     { since the character set is part of the field type, display that
2077     information now. }
2078     if not qryDomains.FieldByName('RDB$CHARACTER_SET_ID').IsNull then
2079     Result := Result + GetCharacterSets(qryDomains.FieldByName('RDB$CHARACTER_SET_ID').AsInteger,
2080     0, FALSE);
2081     if not qryDomains.FieldByName('RDB$DIMENSIONS').IsNull then
2082     Result := GetArrayField(qryDomains.FieldByName('RDB$FIELD_SOURCE').AsString);
2083    
2084     if not qryDomains.FieldByName('RDB$DEFAULT_SOURCE').IsNull then
2085     Result := Result + Format('%s%s %s', [LineEnding, TAB,
2086     qryDomains.FieldByName('RDB$DEFAULT_SOURCE').AsString]);
2087    
2088     if not qryDomains.FieldByName('RDB$VALIDATION_SOURCE').IsNull then
2089     if Pos('CHECK', AnsiUpperCase(qryDomains.FieldByName('RDB$VALIDATION_SOURCE').AsString)) = 1 then
2090     Result := Result + Format('%s%s %s', [LineEnding, TAB,
2091     qryDomains.FieldByName('RDB$VALIDATION_SOURCE').AsString])
2092     else
2093     Result := Result + Format('%s%s /* %s */', [LineEnding, TAB,
2094     qryDomains.FieldByName('RDB$VALIDATION_SOURCE').AsString]);
2095    
2096     if qryDomains.FieldByName('RDB$NULL_FLAG').AsInteger = 1 then
2097     Result := Result + ' NOT NULL';
2098    
2099     { Show the collation order if one has been specified. If the collation
2100     order is the default for the character set being used, then no collation
2101     order will be shown ( because it isn't needed ).
2102    
2103     If the collation id is 0, then the default for the character set is
2104     being used so there is no need to retrieve the collation information.}
2105    
2106     if (not qryDomains.FieldByName('RDB$COLLATION_ID').IsNull) and
2107     (qryDomains.FieldByName('RDB$COLLATION_ID').AsInteger <> 0) then
2108     Result := Result + GetCharacterSets(qryDomains.FieldByName('RDB$CHARACTER_SET_ID').AsInteger,
2109     qryDomains.FieldByName('RDB$COLLATION_ID').AsInteger, true);
2110     end;
2111    
2112     begin
2113     First := true;
2114     qryDomains := TIBSQL.Create(FDatabase);
2115     try
2116     if ObjectName <> '' then
2117     begin
2118     if ExtractType = etTable then
2119     begin
2120     qryDomains.SQL.Text := DomainSQL;
2121     qryDomains.Params.ByName('table_name').AsString := ObjectName;
2122     end
2123     else
2124     begin
2125     qryDomains.SQL.Text := DomainByNameSQL;
2126     qryDomains.Params.ByName('DomainName').AsString := ObjectName;
2127     end;
2128     end
2129     else
2130     qryDomains.SQL.Text := AllDomainSQL;
2131    
2132     qryDomains.ExecQuery;
2133     while not qryDomains.Eof do
2134     begin
2135     FieldName := qryDomains.FieldByName('RDB$FIELD_NAME').AsString;
2136     { Skip over artifical domains }
2137     if (Pos('RDB$',FieldName) = 1) and
2138     (FieldName[5] in ['0'..'9']) and
2139     (qryDomains.FieldByName('RDB$SYSTEM_FLAG').AsInteger <> 1) then
2140     begin
2141     qryDomains.Next;
2142     continue;
2143     end;
2144    
2145     if First then
2146     begin
2147     FMetaData.Add('/* Domain definitions */');
2148     First := false;
2149     end;
2150    
2151     Line := Format('CREATE DOMAIN %s AS ', [FieldName]);
2152     Line := Line + FormatDomainStr + Term;
2153     FMetaData.Add(Line);
2154     qryDomains.Next;
2155     end;
2156     finally
2157     qryDomains.Free;
2158     end;
2159     end;
2160    
2161     { ListException
2162     Functional description
2163     List all exceptions defined in the database
2164    
2165     Parameters: none }
2166    
2167     procedure TIBExtract.ListException(ExceptionName : String = '');
2168     const
2169     ExceptionSQL =
2170     'select * from RDB$EXCEPTIONS ' +
2171     'ORDER BY RDB$EXCEPTION_NAME';
2172    
2173     ExceptionNameSQL =
2174     'select * from RDB$EXCEPTIONS ' +
2175     'WHERE RDB$EXCEPTION_NAME = :ExceptionName ' +
2176     'ORDER BY RDB$EXCEPTION_NAME';
2177    
2178     var
2179     First : Boolean;
2180     qryException : TIBSQL;
2181     begin
2182     First := true;
2183     qryException := TIBSQL.Create(FDatabase);
2184     try
2185     if ExceptionName = '' then
2186     qryException.SQL.Text := ExceptionSQL
2187     else
2188     begin
2189     qryException.SQL.Text := ExceptionNameSQL;
2190     qryException.Params.ByName('ExceptionName').AsString := ExceptionName;
2191     end;
2192    
2193     qryException.ExecQuery;
2194     while not qryException.Eof do
2195     begin
2196     if First then
2197     begin
2198     FMetaData.Add('');
2199     FMetaData.Add('/* Exceptions */');
2200     FMetaData.Add('');
2201     First := false;
2202     end; //end_if
2203    
2204     FMetaData.Add(Format('CREATE EXCEPTION %s %s%s',
2205     [QuoteIdentifier( qryException.FieldByName('RDB$EXCEPTION_NAME').AsString),
2206     QuotedStr(qryException.FieldByName('RDB$MESSAGE').AsString), Term]));
2207     qryException.Next;
2208     end;
2209     finally
2210     qryException.Free;
2211     end;
2212     end;
2213    
2214     { ListFilters
2215    
2216     Functional description
2217     List all blob filters
2218    
2219     Parameters: none
2220     Results in
2221     DECLARE FILTER <fname> INPUT_TYPE <blob_sub_type> OUTPUT_TYPE <blob_subtype>
2222     ENTRY_POINT <string> MODULE_NAME <string> }
2223    
2224     procedure TIBExtract.ListFilters(FilterName : String = '');
2225     const
2226     FiltersSQL =
2227     'SELECT * FROM RDB$FILTERS ' +
2228     'ORDER BY RDB$FUNCTION_NAME';
2229     FilterNameSQL =
2230     'SELECT * FROM RDB$FILTERS ' +
2231     'WHERE RDB$FUNCTION_NAME = :FunctionName ' +
2232     'ORDER BY RDB$FUNCTION_NAME';
2233    
2234     var
2235     First : Boolean;
2236     qryFilters : TIBSQL;
2237     begin
2238     First := true;
2239     qryFilters := TIBSQL.Create(FDatabase);
2240     try
2241     if FilterName = '' then
2242     qryFilters.SQL.Text := FiltersSQL
2243     else
2244     begin
2245     qryFilters.SQL.Text := FilterNameSQL;
2246     qryFilters.Params.ByName('FunctionName').AsString := FilterName;
2247     end;
2248     qryFilters.ExecQuery;
2249     while not qryFilters.Eof do
2250     begin
2251     if First then
2252     begin
2253     FMetaData.Add('');
2254     FMetaData.Add('/* BLOB Filter declarations */');
2255     FMetaData.Add('');
2256     First := false;
2257     end; //end_if
2258    
2259     FMetaData.Add(Format('DECLARE FILTER %s INPUT_TYPE %d OUTPUT_TYPE %d',
2260     [qryFilters.FieldByName('RDB$FUNCTION_NAME').AsString,
2261     qryFilters.FieldByName('RDB$INPUT_SUB_TYPE').AsInteger,
2262     qryFilters.FieldByName('RDB$OUTPUT_SUB_TYPE').AsInteger]));
2263     FMetaData.Add(Format('%sENTRY_POINT ''%s'' MODULE_NAME ''%s''%s%',
2264     [TAB, qryFilters.FieldByName('RDB$ENTRYPOINT').AsString,
2265     qryFilters.FieldByName('RDB$MODULE_NAME').AsString, Term]));
2266     FMetaData.Add('');
2267    
2268     qryFilters.Next;
2269     end;
2270    
2271     finally
2272     qryFilters.Free;
2273     end;
2274     end;
2275    
2276     { ListForeign
2277     Functional description
2278     List all foreign key constraints and alter the tables }
2279    
2280     procedure TIBExtract.ListForeign(ObjectName : String; ExtractType : TExtractType);
2281     const
2282     { Static queries for obtaining foreign constraints, where RELC1 is the
2283     foreign key constraints, RELC2 is the primary key lookup and REFC
2284     is the join table }
2285     ForeignSQL =
2286     'SELECT REFC.RDB$UPDATE_RULE REFC_UPDATE_RULE, REFC.RDB$DELETE_RULE REFC_DELETE_RULE, ' +
2287     ' RELC1.RDB$RELATION_NAME RELC1_RELATION_NAME, RELC2.RDB$RELATION_NAME RELC2_RELATION_NAME, ' +
2288     ' RELC1.RDB$INDEX_NAME RELC1_INDEX_NAME, RELC1.RDB$CONSTRAINT_NAME RELC1_CONSTRAINT_NAME, ' +
2289     ' RELC2.RDB$INDEX_NAME RELC2_INDEX_NAME ' +
2290     'FROM RDB$REF_CONSTRAINTS REFC, RDB$RELATION_CONSTRAINTS RELC1, ' +
2291     ' RDB$RELATION_CONSTRAINTS RELC2 ' +
2292     'WHERE ' +
2293     ' RELC1.RDB$CONSTRAINT_TYPE = ''FOREIGN KEY'' AND ' +
2294     ' REFC.RDB$CONST_NAME_UQ = RELC2.RDB$CONSTRAINT_NAME AND ' +
2295     ' REFC.RDB$CONSTRAINT_NAME = RELC1.RDB$CONSTRAINT_NAME AND ' +
2296     ' (RELC2.RDB$CONSTRAINT_TYPE = ''UNIQUE'' OR ' +
2297     ' RELC2.RDB$CONSTRAINT_TYPE = ''PRIMARY KEY'') ' +
2298     'ORDER BY RELC1.RDB$RELATION_NAME, RELC1.RDB$CONSTRAINT_NAME';
2299    
2300     ForeignNameSQL =
2301     'SELECT REFC.RDB$UPDATE_RULE REFC_UPDATE_RULE, REFC.RDB$DELETE_RULE REFC_DELETE_RULE, ' +
2302     ' RELC1.RDB$RELATION_NAME RELC1_RELATION_NAME, RELC2.RDB$RELATION_NAME RELC2_RELATION_NAME, ' +
2303     ' RELC1.RDB$INDEX_NAME RELC1_INDEX_NAME, RELC1.RDB$CONSTRAINT_NAME RELC1_CONSTRAINT_NAME, ' +
2304     ' RELC2.RDB$INDEX_NAME RELC2_INDEX_NAME ' +
2305     'FROM RDB$REF_CONSTRAINTS REFC, RDB$RELATION_CONSTRAINTS RELC1, ' +
2306     ' RDB$RELATION_CONSTRAINTS RELC2 ' +
2307     'WHERE ' +
2308     ' RELC1.RDB$RELATION_NAME = :TableName AND ' +
2309     ' RELC1.RDB$CONSTRAINT_TYPE = ''FOREIGN KEY'' AND ' +
2310     ' REFC.RDB$CONST_NAME_UQ = RELC2.RDB$CONSTRAINT_NAME AND ' +
2311     ' REFC.RDB$CONSTRAINT_NAME = RELC1.RDB$CONSTRAINT_NAME AND ' +
2312     ' (RELC2.RDB$CONSTRAINT_TYPE = ''UNIQUE'' OR ' +
2313     ' RELC2.RDB$CONSTRAINT_TYPE = ''PRIMARY KEY'') ' +
2314     'ORDER BY RELC1.RDB$RELATION_NAME, RELC1.RDB$CONSTRAINT_NAME';
2315    
2316     ForeignByNameSQL =
2317     'SELECT REFC.RDB$UPDATE_RULE REFC_UPDATE_RULE, REFC.RDB$DELETE_RULE REFC_DELETE_RULE, ' +
2318     ' RELC1.RDB$RELATION_NAME RELC1_RELATION_NAME, RELC2.RDB$RELATION_NAME RELC2_RELATION_NAME, ' +
2319     ' RELC1.RDB$INDEX_NAME RELC1_INDEX_NAME, RELC1.RDB$CONSTRAINT_NAME RELC1_CONSTRAINT_NAME, ' +
2320     ' RELC2.RDB$INDEX_NAME RELC2_INDEX_NAME ' +
2321     'FROM RDB$REF_CONSTRAINTS REFC, RDB$RELATION_CONSTRAINTS RELC1, ' +
2322     ' RDB$RELATION_CONSTRAINTS RELC2 ' +
2323     'WHERE ' +
2324     ' RELC1.RDB$CONSTRAINT_NAME = :ConstraintName AND ' +
2325     ' RELC1.RDB$CONSTRAINT_TYPE = ''FOREIGN KEY'' AND ' +
2326     ' REFC.RDB$CONST_NAME_UQ = RELC2.RDB$CONSTRAINT_NAME AND ' +
2327     ' REFC.RDB$CONSTRAINT_NAME = RELC1.RDB$CONSTRAINT_NAME AND ' +
2328     ' (RELC2.RDB$CONSTRAINT_TYPE = ''UNIQUE'' OR ' +
2329     ' RELC2.RDB$CONSTRAINT_TYPE = ''PRIMARY KEY'') ' +
2330     'ORDER BY RELC1.RDB$RELATION_NAME, RELC1.RDB$CONSTRAINT_NAME';
2331    
2332     var
2333     qryForeign : TIBSQL;
2334     Line : String;
2335    
2336     begin
2337     qryForeign := TIBSQL.Create(FDatabase);
2338     try
2339     if ObjectName = '' then
2340     qryForeign.SQL.Text := ForeignSQL
2341     else
2342     begin
2343     if ExtractType = etTable then
2344     begin
2345     qryForeign.SQL.Text := ForeignNameSQL;
2346     qryForeign.Params.ByName('TableName').AsString := ObjectName;
2347     end
2348     else
2349     begin
2350     qryForeign.SQL.Text := ForeignByNameSQL;
2351     qryForeign.Params.ByName('ConstraintName').AsString := ObjectName;
2352     end;
2353     end;
2354     qryForeign.ExecQuery;
2355     while not qryForeign.Eof do
2356     begin
2357     Line := Format('ALTER TABLE %s ADD ', [QuoteIdentifier(
2358     qryForeign.FieldByName('RELC1_RELATION_NAME').AsString)]);
2359    
2360     { If the name of the constraint is not INTEG..., print it.
2361     INTEG... are internally generated names. }
2362     if (not qryForeign.FieldByName('RELC1_CONSTRAINT_NAME').IsNull) and
2363     ( Pos('INTEG', qryForeign.FieldByName('RELC1_CONSTRAINT_NAME').AsString) <> 1) then
2364     Line := Line + Format('CONSTRAINT %s ', [QuoteIdentifier(
2365     Trim(qryForeign.FieldByName('RELC1_CONSTRAINT_NAME').AsString))]);
2366    
2367     Line := Line + Format('FOREIGN KEY (%s) REFERENCES %s ', [
2368     GetIndexSegments(qryForeign.FieldByName('RELC1_INDEX_NAME').AsString),
2369     Trim(qryForeign.FieldByName('RELC2_RELATION_NAME').AsString)]);
2370    
2371     Line := Line + Format('(%s)',
2372     [GetIndexSegments(qryForeign.FieldByName('RELC2_INDEX_NAME').AsString)]);
2373    
2374     { Add the referential actions, if any }
2375     if (not qryForeign.FieldByName('REFC_UPDATE_RULE').IsNull) and
2376     (Trim(qryForeign.FieldByName('REFC_UPDATE_RULE').AsString) <> 'RESTRICT') then
2377     Line := Line + Format(' ON UPDATE %s',
2378     [Trim(qryForeign.FieldByName('REFC_UPDATE_RULE').AsString)]);
2379    
2380     if (not qryForeign.FieldByName('REFC_DELETE_RULE').IsNull) and
2381     (Trim(qryForeign.FieldByName('REFC_DELETE_RULE').AsString) <> 'RESTRICT') then
2382     Line := Line + Format(' ON DELETE %s',
2383     [Trim(qryForeign.FieldByName('REFC_DELETE_RULE').AsString)]);
2384    
2385     Line := Line + Term;
2386     FMetaData.Add(Line);
2387     qryForeign.Next;
2388     end;
2389     finally
2390     qryForeign.Free;
2391     end;
2392     end;
2393    
2394     { ListFunctions
2395    
2396     Functional description
2397     List all external functions
2398    
2399     Parameters: none
2400     Results in
2401     DECLARE EXTERNAL FUNCTION function_name
2402     CHAR [256] , INTEGER, ....
2403     RETURNS INTEGER BY VALUE
2404     ENTRY_POINT entrypoint MODULE_NAME module; }
2405    
2406     procedure TIBExtract.ListFunctions(FunctionName : String = '');
2407     const
2408     FunctionSQL =
2409     'SELECT * FROM RDB$FUNCTIONS WHERE RDB$SYSTEM_FLAG = 0 ' +
2410     'ORDER BY RDB$FUNCTION_NAME';
2411    
2412     FunctionNameSQL =
2413     'SELECT * FROM RDB$FUNCTIONS ' +
2414     'WHERE RDB$FUNCTION_NAME = :FunctionName ' +
2415     'ORDER BY RDB$FUNCTION_NAME';
2416    
2417     FunctionArgsSQL =
2418     'SELECT * FROM RDB$FUNCTION_ARGUMENTS ' +
2419     'WHERE ' +
2420     ' :FUNCTION_NAME = RDB$FUNCTION_NAME ' +
2421     'ORDER BY RDB$ARGUMENT_POSITION';
2422    
2423     FuncArgsPosSQL =
2424     'SELECT * FROM RDB$FUNCTION_ARGUMENTS ' +
2425     'WHERE ' +
2426     ' RDB$FUNCTION_NAME = :RDB$FUNCTION_NAME AND ' +
2427     ' RDB$ARGUMENT_POSITION = :RDB$ARGUMENT_POSITION';
2428    
2429     CharSetSQL =
2430     'SELECT * FROM RDB$CHARACTER_SETS ' +
2431     'WHERE ' +
2432     ' RDB$CHARACTER_SET_ID = :CHARACTER_SET_ID';
2433    
2434     var
2435     qryFunctions, qryFuncArgs, qryCharSets, qryFuncPos : TIBSQL;
2436     First, FirstArg, DidCharset, PrecisionKnown : Boolean;
2437     ReturnBuffer, TypeBuffer, Line : String;
2438     i, FieldType : Integer;
2439     begin
2440     First := true;
2441     qryFunctions := TIBSQL.Create(FDatabase);
2442     qryFuncArgs := TIBSQL.Create(FDatabase);
2443     qryFuncPos := TIBSQL.Create(FDatabase);
2444     qryCharSets := TIBSQL.Create(FDatabase);
2445     try
2446     if FunctionName = '' then
2447     qryFunctions.SQL.Text := FunctionSQL
2448     else
2449     begin
2450     qryFunctions.SQL.Text := FunctionNameSQL;
2451     qryFunctions.Params.ByName('FunctionName').AsString := FunctionName;
2452     end;
2453     qryFuncArgs.SQL.Text := FunctionArgsSQL;
2454     qryFuncPos.SQL.Text := FuncArgsPosSQL;
2455     qryCharSets.SQL.Text := CharSetSQL;
2456     qryFunctions.ExecQuery;
2457     while not qryFunctions.Eof do
2458     begin
2459     if First then
2460     begin
2461     FMEtaData.Add(Format('%s/* External Function declarations */%s',
2462     [LineEnding, LineEnding]));
2463     First := false;
2464     end; //end_if
2465     { Start new function declaration }
2466     FMetaData.Add(Format('DECLARE EXTERNAL FUNCTION %s',
2467     [qryFunctions.FieldByName('RDB$FUNCTION_NAME').AsString]));
2468     Line := '';
2469    
2470     FirstArg := true;
2471     qryFuncArgs.Params.ByName('FUNCTION_NAME').AsString :=
2472     qryFunctions.FieldByName('RDB$FUNCTION_NAME').AsString;
2473    
2474     qryFuncArgs.ExecQuery;
2475     while not qryFuncArgs.Eof do
2476     begin
2477     { Find parameter type }
2478     i := 0;
2479     FieldType := qryFuncArgs.FieldByName('RDB$FIELD_TYPE').AsInteger;
2480     while FieldType <> ColumnTypes[i].SQLType do
2481     Inc(i);
2482    
2483     { Print length where appropriate }
2484     if FieldType in [ blr_text, blr_varying, blr_cstring] then
2485     begin
2486     DidCharset := false;
2487    
2488     qryCharSets.Params.ByName('CHARACTER_SET_ID').AsString :=
2489     qryFuncArgs.FieldByName('RDB$CHARACTER_SET_ID').AsString;
2490     qryCharSets.ExecQuery;
2491     while not qryCharSets.Eof do
2492     begin
2493     DidCharset := true;
2494     TypeBuffer := Format('%s(%d) CHARACTER SET %s',
2495     [ColumnTypes[i].TypeName,
2496     qryFuncArgs.FieldByName('RDB$FIELD_LENGTH').AsInteger div
2497     Max(1,qryCharSets.FieldByName('RDB$BYTES_PER_CHARACTER').AsInteger),
2498     qryCharSets.FieldByName('RDB$CHARACTER_SET_NAME').AsString]);
2499     qryCharSets.Next;
2500     end;
2501     qryCharSets.Close;
2502     if not DidCharset then
2503     TypeBuffer := Format('%s(%d)', [ColumnTypes[i].TypeName,
2504     qryFuncArgs.FieldByName('RDB$FIELD_LENGTH').AsInteger]);
2505     end //end_if
2506     else
2507     begin
2508     PrecisionKnown := false;
2509     if (FDatabaseInfo.ODSMajorVersion >= ODS_VERSION10) and
2510     (FieldType in [blr_short, blr_long, blr_int64]) then
2511     begin
2512     qryFuncPos.Params.ByName('RDB$FUNCTION_NAME').AsString :=
2513     qryFuncArgs.FieldByName('RDB$FUNCTION_NAME').AsString;
2514     qryFuncPos.Params.ByName('RDB$ARGUMENT_POSITION').AsInteger :=
2515     qryFuncArgs.FieldByName('RDB$ARGUMENT_POSITION').AsInteger;
2516    
2517     qryFuncPos.ExecQuery;
2518     while not qryFuncPos.Eof do
2519     begin
2520     { We are ODS >= 10 and could be any Dialect }
2521     if not qryFuncPos.FieldByName('RDB$FIELD_PRECISION').IsNull then
2522     begin
2523     { We are Dialect >=3 since FIELD_PRECISION is non-NULL }
2524     if (qryFuncPos.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger > 0) and
2525     (qryFuncPos.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger <= MAX_INTSUBTYPES) then
2526     begin
2527     TypeBuffer := Format('%s(%d, %d)',
2528     [IntegralSubtypes[qryFuncPos.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger],
2529     qryFuncPos.FieldByName('RDB$FIELD_PRECISION').AsInteger,
2530     -qryFuncPos.FieldByName('RDB$FIELD_SCALE').AsInteger] );
2531     PrecisionKnown := true;
2532     end; //end_if
2533     end; { if field_precision is not null }
2534     qryFuncPos.Next;
2535     end;
2536     qryFuncPos.Close;
2537     end; { if major_ods >= ods_version10 && }
2538     if not PrecisionKnown then
2539     begin
2540     { Take a stab at numerics and decimals }
2541     if (FieldType = blr_short) and
2542     (qryFuncArgs.FieldByName('RDB$FIELD_SCALE').AsInteger < 0) then
2543     TypeBuffer := Format('NUMERIC(4, %d)',
2544     [-qryFuncArgs.FieldByName('RDB$FIELD_SCALE').AsInteger])
2545     else
2546     if (FieldType = blr_long) and
2547     (qryFuncArgs.FieldByName('RDB$FIELD_SCALE').AsInteger < 0) then
2548     TypeBuffer := Format('NUMERIC(9, %d)',
2549     [-qryFuncArgs.FieldByName('RDB$FIELD_SCALE').AsInteger])
2550     else
2551     if (FieldType = blr_double) and
2552     (qryFuncArgs.FieldByName('RDB$FIELD_SCALE').AsInteger < 0) then
2553     TypeBuffer := Format('NUMERIC(15, %d)',
2554     [-qryFuncArgs.FieldByName('RDB$FIELD_SCALE').AsInteger])
2555     else
2556     TypeBuffer := ColumnTypes[i].TypeName;
2557     end; { if not PrecisionKnown }
2558     end; { if FCHAR or VARCHAR or CSTRING ... else }
2559    
2560     if qryFunctions.FieldByName('RDB$RETURN_ARGUMENT').AsInteger =
2561     qryFuncArgs.FieldByName('RDB$ARGUMENT_POSITION').AsInteger then
2562     begin
2563     ReturnBuffer := 'RETURNS ' + TypeBuffer;
2564     if qryFuncArgs.FieldByName('RDB$MECHANISM').AsInteger = 0 then
2565     ReturnBuffer := ReturnBuffer + ' BY VALUE ';
2566     if qryFuncArgs.FieldByName('RDB$MECHANISM').AsInteger < 0 then
2567     ReturnBuffer := ReturnBuffer + ' FREE_IT';
2568     end
2569     else
2570     begin
2571     { First arg needs no comma }
2572     if FirstArg then
2573     begin
2574     Line := Line + TypeBuffer;
2575     FirstArg := false;
2576     end
2577     else
2578     Line := Line + ', ' + TypeBuffer;
2579     end; //end_else
2580     qryFuncArgs.Next;
2581     end;
2582     qryFuncArgs.Close;
2583    
2584     FMetaData.Add(Line);
2585     FMetaData.Add(ReturnBuffer);
2586     FMetaData.Add(Format('ENTRY_POINT ''%s'' MODULE_NAME ''%s''%s%s%s',
2587     [qryFunctions.FieldByName('RDB$ENTRYPOINT').AsString,
2588     qryFunctions.FieldByName('RDB$MODULE_NAME').AsString,
2589     Term, LineEnding, LineEnding]));
2590    
2591     qryFunctions.Next;
2592     end;
2593     finally
2594     qryFunctions.Free;
2595     qryFuncArgs.Free;
2596     qryCharSets.Free;
2597     qryFuncPos.Free;
2598     end;
2599     end;
2600    
2601     { ListGenerators
2602     Functional description
2603     Re create all non-system generators }
2604    
2605     procedure TIBExtract.ListGenerators(GeneratorName: String;
2606     ExtractTypes: TExtractTypes);
2607     const
2608     GeneratorSQL =
2609     'SELECT RDB$GENERATOR_NAME ' +
2610     'FROM RDB$GENERATORS ' +
2611     'WHERE ' +
2612     ' (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG <> 1) ' +
2613     'ORDER BY RDB$GENERATOR_NAME';
2614    
2615     GeneratorNameSQL =
2616     'SELECT RDB$GENERATOR_NAME ' +
2617     'FROM RDB$GENERATORS ' +
2618     'WHERE RDB$GENERATOR_NAME = :GeneratorName AND ' +
2619     ' (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG <> 1) ' +
2620     'ORDER BY RDB$GENERATOR_NAME';
2621    
2622     GeneratorValueSQL =
2623     'SELECT GEN_ID(%s,0) as GENERATORVALUE From RDB$Database';
2624    
2625     var
2626     qryGenerator : TIBSQL;
2627     qryValue: TIBSQL;
2628     GenName : String;
2629     begin
2630     qryGenerator := TIBSQL.Create(FDatabase);
2631     qryValue := TIBSQL.Create(FDatabase);
2632     try
2633     if GeneratorName = '' then
2634     qryGenerator.SQL.Text := GeneratorSQL
2635     else
2636     begin
2637     qryGenerator.SQL.Text := GeneratorNameSQL;
2638     qryGenerator.Params.ByName('GeneratorName').AsString := GeneratorName;
2639     end;
2640     qryGenerator.ExecQuery;
2641     FMetaData.Add('');
2642     while not qryGenerator.Eof do
2643     begin
2644     GenName := qryGenerator.FieldByName('RDB$GENERATOR_NAME').AsString;
2645     if ((Pos('RDB$',GenName) = 1) and
2646     (GenName[5] in ['0'..'9'])) or
2647     ((Pos('SQL$',GenName) = 1) and
2648     (GenName[5] in ['0'..'9'])) then
2649     begin
2650     qryGenerator.Next;
2651     continue;
2652     end;
2653     FMetaData.Add(Format('CREATE SEQUENCE %s%s',
2654     [QuoteIdentifier( GenName),
2655     Term]));
2656     if etData in ExtractTypes then
2657     begin
2658     qryValue.SQL.Text := Format(GeneratorValueSQL,[GenName]);
2659     qryValue.ExecQuery;
2660     try
2661     if not qryValue.EOF then
2662     FMetaData.Add(Format('ALTER SEQUENCE %s RESTART WITH %d;',
2663     [QuoteIdentifier( GenName),
2664 tony 221 qryValue.FieldByName('GENERATORVALUE').AsInt64]));
2665 tony 209 finally
2666     qryValue.Close;
2667     end;
2668     end;
2669     qryGenerator.Next;
2670     end;
2671     finally
2672     qryGenerator.Free;
2673     qryValue.Free;
2674     end;
2675     end;
2676    
2677     { ListIndex
2678     Functional description
2679     Define all non-constraint indices
2680     Use a static SQL query to get the info and print it.
2681    
2682     Uses get_index_segment to provide a key list for each index }
2683    
2684     procedure TIBExtract.ListIndex(ObjectName : String; ExtractType : TExtractType);
2685     const
2686     IndexSQL =
2687     'SELECT IDX.RDB$RELATION_NAME, IDX.RDB$INDEX_NAME, IDX.RDB$UNIQUE_FLAG, ' +
2688     ' IDX.RDB$INDEX_TYPE ' +
2689     'FROM RDB$INDICES IDX JOIN RDB$RELATIONS RELC ON ' +
2690     ' IDX.RDB$RELATION_NAME = RELC.RDB$RELATION_NAME ' +
2691     'WHERE (RELC.RDB$SYSTEM_FLAG <> 1 OR RELC.RDB$SYSTEM_FLAG IS NULL) AND ' +
2692     ' NOT EXISTS (SELECT * FROM RDB$RELATION_CONSTRAINTS RC ' +
2693     ' WHERE RC.RDB$INDEX_NAME = IDX.RDB$INDEX_NAME) ' +
2694     'ORDER BY IDX.RDB$RELATION_NAME, IDX.RDB$INDEX_NAME';
2695    
2696     IndexNameSQL =
2697     'SELECT IDX.RDB$RELATION_NAME, IDX.RDB$INDEX_NAME, IDX.RDB$UNIQUE_FLAG, ' +
2698     ' IDX.RDB$INDEX_TYPE ' +
2699     'FROM RDB$INDICES IDX JOIN RDB$RELATIONS RELC ON ' +
2700     ' IDX.RDB$RELATION_NAME = RELC.RDB$RELATION_NAME ' +
2701     'WHERE (RELC.RDB$SYSTEM_FLAG <> 1 OR RELC.RDB$SYSTEM_FLAG IS NULL) AND ' +
2702     ' RELC.RDB$RELATION_NAME = :RelationName AND ' +
2703     ' NOT EXISTS (SELECT * FROM RDB$RELATION_CONSTRAINTS RC ' +
2704     ' WHERE RC.RDB$INDEX_NAME = IDX.RDB$INDEX_NAME) ' +
2705     'ORDER BY IDX.RDB$RELATION_NAME, IDX.RDB$INDEX_NAME';
2706    
2707     IndexByNameSQL =
2708     'SELECT IDX.RDB$RELATION_NAME, IDX.RDB$INDEX_NAME, IDX.RDB$UNIQUE_FLAG, ' +
2709     ' IDX.RDB$INDEX_TYPE ' +
2710     'FROM RDB$INDICES IDX JOIN RDB$RELATIONS RELC ON ' +
2711     ' IDX.RDB$RELATION_NAME = RELC.RDB$RELATION_NAME ' +
2712     'WHERE (RELC.RDB$SYSTEM_FLAG <> 1 OR RELC.RDB$SYSTEM_FLAG IS NULL) AND ' +
2713     ' IDX.RDB$INDEX_NAME = :IndexName AND ' +
2714     ' NOT EXISTS (SELECT * FROM RDB$RELATION_CONSTRAINTS RC ' +
2715     ' WHERE RC.RDB$INDEX_NAME = IDX.RDB$INDEX_NAME) ' +
2716     'ORDER BY IDX.RDB$RELATION_NAME, IDX.RDB$INDEX_NAME';
2717    
2718     var
2719     qryIndex : TIBSQL;
2720     First : Boolean;
2721     Unique, IdxType, Line : String;
2722     begin
2723     First := true;
2724     qryIndex := TIBSQL.Create(FDatabase);
2725     try
2726     if ObjectName = '' then
2727     qryIndex.SQL.Text := IndexSQL
2728     else
2729     begin
2730     if ExtractType = etTable then
2731     begin
2732     qryIndex.SQL.Text := IndexNameSQL;
2733     qryIndex.Params.ByName('RelationName').AsString := ObjectName;
2734     end
2735     else
2736     begin
2737     qryIndex.SQL.Text := IndexByNameSQL;
2738     qryIndex.Params.ByName('IndexName').AsString := ObjectName;
2739     end;
2740     end;
2741     qryIndex.ExecQuery;
2742     while not qryIndex.Eof do
2743     begin
2744     if First then
2745     begin
2746     if ObjectName = '' then
2747     FMetaData.Add(LineEnding + '/* Index definitions for all user tables */' + LineEnding)
2748     else
2749     FMetaData.Add(LineEnding + '/* Index definitions for ' + ObjectName + ' */' + LineEnding);
2750     First := false;
2751     end; //end_if
2752    
2753     if qryIndex.FieldByName('RDB$UNIQUE_FLAG').AsInteger = 1 then
2754     Unique := ' UNIQUE'
2755     else
2756     Unique := '';
2757    
2758     if qryIndex.FieldByName('RDB$INDEX_TYPE').AsInteger = 1 then
2759     IdxType := ' DESCENDING'
2760     else
2761     IdxType := '';
2762    
2763     Line := Format('CREATE%s%s INDEX %s ON %s(', [Unique, IdxType,
2764     QuoteIdentifier(
2765     qryIndex.FieldByName('RDB$INDEX_NAME').AsString),
2766     QuoteIdentifier(
2767     qryIndex.FieldByName('RDB$RELATION_NAME').AsString)]);
2768    
2769     Line := Line + GetIndexSegments(qryIndex.FieldByName('RDB$INDEX_NAME').AsString) +
2770     ')' + Term;
2771    
2772     FMetaData.Add(Line);
2773     qryIndex.Next;
2774     end;
2775     finally
2776     qryIndex.Free;
2777     end;
2778     end;
2779    
2780     { ListViews
2781     Functional description
2782     Show text of views.
2783     Use a SQL query to get the info and print it.
2784     Note: This should also contain check option }
2785    
2786     procedure TIBExtract.ListViews(ViewName : String);
2787     const
2788     ViewSQL =
2789     'with recursive Views as ( ' +
2790     ' Select RDB$RELATION_NAME, 1 as ViewLevel from RDB$RELATIONS ' +
2791     ' Where RDB$RELATION_TYPE = 1 and RDB$SYSTEM_FLAG = 0 '+
2792     ' UNION ALL ' +
2793     ' Select D.RDB$DEPENDED_ON_NAME, ViewLevel + 1 From RDB$DEPENDENCIES D ' +
2794     ' JOIN Views on Views.RDB$RELATION_NAME = D.RDB$DEPENDENT_NAME ' +
2795     ' and Views.RDB$RELATION_NAME <> D.RDB$DEPENDED_ON_NAME ' +
2796     ' JOIN RDB$RELATIONS R On R.RDB$RELATION_NAME = D.RDB$DEPENDED_ON_NAME ' +
2797     ')' +
2798     'SELECT R.RDB$RELATION_NAME, R.RDB$OWNER_NAME, R.RDB$VIEW_SOURCE FROM RDB$RELATIONS R ' +
2799     'JOIN ( ' +
2800     'Select RDB$RELATION_NAME, max(ViewLevel) as ViewLevel From Views ' +
2801     'Group By RDB$RELATION_NAME) A On A.RDB$RELATION_NAME = R.RDB$RELATION_NAME ' +
2802     'Where R.RDB$RELATION_TYPE = 1 and R.RDB$SYSTEM_FLAG = 0 '+
2803     'Order by A.ViewLevel desc, R.RDB$RELATION_NAME asc';
2804    
2805     {
2806     'SELECT RDB$RELATION_NAME, RDB$OWNER_NAME, RDB$VIEW_SOURCE ' +
2807     'FROM RDB$RELATIONS ' +
2808     'WHERE ' +
2809     ' (RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL) AND ' +
2810     ' NOT RDB$VIEW_BLR IS NULL AND ' +
2811     ' RDB$FLAGS = 1 ' +
2812     'ORDER BY RDB$RELATION_ID'; }
2813    
2814     ViewNameSQL =
2815     'SELECT RDB$RELATION_NAME, RDB$OWNER_NAME, RDB$VIEW_SOURCE ' +
2816     'FROM RDB$RELATIONS ' +
2817     'WHERE ' +
2818     ' (RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL) AND ' +
2819     ' NOT RDB$VIEW_BLR IS NULL AND ' +
2820     ' RDB$FLAGS = 1 AND ' +
2821     ' RDB$RELATION_NAME = :ViewName ' +
2822     'ORDER BY RDB$RELATION_ID';
2823    
2824     ColumnSQL =
2825     'SELECT RDB$FIELD_NAME FROM RDB$RELATION_FIELDS ' +
2826     'WHERE ' +
2827     ' RDB$RELATION_NAME = :RELATION_NAME ' +
2828     'ORDER BY RDB$FIELD_POSITION';
2829    
2830     var
2831     qryView, qryColumns : TIBSQL;
2832     SList : TStrings;
2833     begin
2834     qryView := TIBSQL.Create(FDatabase);
2835     qryColumns := TIBSQL.Create(FDatabase);
2836     SList := TStringList.Create;
2837     try
2838     if ViewName = '' then
2839     qryView.SQL.Text := ViewSQL
2840     else
2841     begin
2842     qryView.SQL.Text := ViewNameSQL;
2843     qryView.Params.ByName('ViewName').AsString := ViewName;
2844     end;
2845     qryColumns.SQL.Text := ColumnSQL;
2846     qryView.ExecQuery;
2847     while not qryView.Eof do
2848     begin
2849     SList.Add(Format('%s/* View: %s, Owner: %s */%s',
2850     [LineEnding, qryView.FieldByName('RDB$RELATION_NAME').AsString,
2851     qryView.FieldByName('RDB$OWNER_NAME').AsString, LineEnding]));
2852    
2853     SList.Add(Format('CREATE VIEW %s (', [QuoteIdentifier(
2854     qryView.FieldByName('RDB$RELATION_NAME').AsString)]));
2855    
2856     qryColumns.Params.ByName('RELATION_NAME').AsString :=
2857     qryView.FieldByName('RDB$RELATION_NAME').AsString;
2858     qryColumns.ExecQuery;
2859     while not qryColumns.Eof do
2860     begin
2861     SList.Add(' ' + QuoteIdentifier(
2862     qryColumns.FieldByName('RDB$FIELD_NAME').AsString));
2863     qryColumns.Next;
2864     if not qryColumns.Eof then
2865     SList.Strings[SList.Count - 1] := SList.Strings[SList.Count - 1] + ', ';
2866     end;
2867     qryColumns.Close;
2868     SList.Text := SList.Text + Format(') AS%s', [LineEnding]);
2869     if not qryView.FieldByName('RDB$VIEW_SOURCE').IsNull then
2870     SList.Text := SList.Text + qryView.FieldByName('RDB$VIEW_SOURCE').AsString;
2871     SList.Text := SList.Text + Format('%s%s', [Term, LineEnding]);
2872     FMetaData.AddStrings(SList);
2873     SList.Clear;
2874     qryView.Next;
2875     end;
2876     finally
2877     qryView.Free;
2878     qryColumns.Free;
2879     SList.Free;
2880     end;
2881     end;
2882    
2883     { PrintSet
2884     Functional description
2885     print (using ISQL_printf) the word "SET"
2886     if the first line of the ALTER DATABASE
2887     settings options. Also, add trailing
2888     comma for end of prior line if needed.
2889    
2890     uses Print_buffer, a global }
2891    
2892     function TIBExtract.PrintSet(var Used: Boolean) : String;
2893     begin
2894     if not Used then
2895     begin
2896     Result := ' SET ';
2897     Used := true;
2898     end
2899     else
2900     Result := Format(', %s ', [LineEnding]);
2901     end;
2902    
2903     {
2904     PrintValidation
2905     Functional description
2906     This does some minor syntax adjustmet for extracting
2907     validation blobs and computed fields.
2908     if it does not start with the word CHECK
2909     if this is a computed field blob,look for () or insert them.
2910     if flag = false, this is a validation clause,
2911     if flag = true, this is a computed field }
2912    
2913     function TIBExtract.PrintValidation(ToValidate: String;
2914     flag: Boolean): String;
2915     var
2916     IsSQL : Boolean;
2917     begin
2918     IsSql := false;
2919    
2920     Result := '';
2921     ToValidate := Trim(ToValidate);
2922    
2923     if flag then
2924     begin
2925     if ToValidate[1] = '(' then
2926     IsSQL := true;
2927     end
2928     else
2929     if (Pos(ToValidate, 'check') = 1) or (Pos(ToValidate, 'CHECK') = 1) then
2930     IsSQL := TRUE;
2931    
2932     if not IsSQL then
2933     begin
2934     if Flag then
2935     Result := Result + '/* ' + ToValidate + ' */'
2936     else
2937     Result := Result + '(' + ToValidate + ')';
2938     end
2939     else
2940     Result := ToValidate;
2941     end;
2942    
2943     procedure TIBExtract.SetDatabase(const Value: TIBDatabase);
2944     begin
2945     if (csLoading in ComponentState) or (FDatabase <> Value) then
2946     begin
2947     FDatabase := Value;
2948     if (not Assigned(FTransaction)) and (FDatabase <> nil) then
2949     Transaction := FDatabase.DefaultTransaction;
2950     FDatabaseInfo.Database := FDatabase;
2951     end;
2952     end;
2953    
2954     procedure TIBExtract.SetTransaction(const Value: TIBTransaction);
2955     begin
2956     if FTransaction <> Value then
2957     begin
2958     FTransaction := Value;
2959     if (not Assigned(FDatabase)) and (FTransaction <> nil) then
2960     Database := FTransaction.DefaultDatabase;
2961     end;
2962     end;
2963    
2964     procedure TIBExtract.ExtractObject(ObjectType : TExtractObjectTypes;
2965     ObjectName : String = ''; ExtractTypes : TExtractTypes = []);
2966     var
2967     DidActivate : Boolean;
2968     begin
2969     DidActivate := false;
2970     if not FTransaction.Active then
2971     begin
2972     FTransaction.StartTransaction;
2973     DidActivate := true;
2974     end;
2975     FMetaData.Clear;
2976 tony 225 if not CaseSensitiveObjectNames then
2977     ObjectName := ExtractIdentifier(FDatabaseInfo.DBSQLDialect,ObjectName);
2978 tony 209 case ObjectType of
2979     eoDatabase : ExtractDDL(true, '', ExtractTypes);
2980     eoDomain :
2981     if etTable in ExtractTypes then
2982     ListDomains(ObjectName, etTable)
2983     else
2984     ListDomains(ObjectName);
2985     eoTable :
2986     begin
2987     if ObjectName <> '' then
2988     begin
2989     if etDomain in ExtractTypes then
2990     ListDomains(ObjectName, etTable);
2991     ExtractListTable(ObjectName, '', false);
2992     if etIndex in ExtractTypes then
2993     ListIndex(ObjectName, etTable);
2994     if etForeign in ExtractTypes then
2995     ListForeign(ObjectName, etTable);
2996     if etCheck in ExtractTypes then
2997     ListCheck(ObjectName, etTable);
2998     if etTrigger in ExtractTypes then
2999     begin
3000     if etGrant in ExtractTypes then
3001     ListTriggers(ObjectName, [etTable,etGrant])
3002     else
3003     ListTriggers(ObjectName, [etTable]);
3004     end;
3005     if etGrant in ExtractTypes then
3006     ShowGrants(ObjectName, Term);
3007     if etData in ExtractTypes then
3008     ListData(ObjectName);
3009     end
3010     else
3011     ListAllTables(true);
3012     end;
3013     eoView :
3014     begin
3015     ListViews(ObjectName);
3016     if ObjectName <> '' then
3017     begin
3018     if etTrigger in ExtractTypes then
3019     begin
3020     if etGrant in ExtractTypes then
3021     ListTriggers(ObjectName, [etTable,etGrant])
3022     else
3023     ListTriggers(ObjectName, [etTable]);
3024     end;
3025     if etGrant in ExtractTypes then
3026     ShowGrants(ObjectName, Term);
3027     end;
3028     end;
3029     eoProcedure :
3030     begin
3031     ListProcs(pdCreateProc,ObjectName,etGrant in ExtractTypes);
3032     if (ObjectName <> '' ) and (etGrant in ExtractTypes) then
3033     ShowGrants(ObjectName, Term);
3034     end;
3035 tony 221 eoPackage:
3036     begin
3037     if DatabaseInfo.ODSMajorVersion >= ODS_VERSION12 then
3038     begin
3039     ListPackages(paBoth,ObjectName, etGrant in ExtractTypes);
3040     if (ObjectName <> '' ) and (etGrant in ExtractTypes) then
3041     ShowGrants(ObjectName, Term);
3042     end
3043     else
3044     IBError(ibxeODSVersionRequired,['12.0']);
3045     end;
3046 tony 209 eoFunction : ListFunctions(ObjectName);
3047     eoGenerator : ListGenerators(ObjectName,ExtractTypes);
3048     eoException : ListException(ObjectName);
3049     eoBLOBFilter : ListFilters(ObjectName);
3050     eoRole : ListRoles(ObjectName,etGrant in ExtractTypes);
3051     eoTrigger :
3052     if etTable in ExtractTypes then
3053 tony 226 ListTriggers(ObjectName, ExtractTypes * [etTable,etGrant])
3054 tony 209 else
3055 tony 226 if ExtractTypes * [etDatabaseTriggers,etDDLTriggers] = [] then
3056     ListTriggers(ObjectName,[etTrigger] + (ExtractTypes * [etGrant]))
3057 tony 209 else
3058 tony 226 ListTriggers(ObjectName,ExtractTypes * [etDatabaseTriggers,etDDLTriggers, etGrant]);
3059 tony 209 eoForeign :
3060     if etTable in ExtractTypes then
3061     ListForeign(ObjectName, etTable)
3062     else
3063     ListForeign(ObjectName);
3064     eoIndexes :
3065     if etTable in ExtractTypes then
3066     ListIndex(ObjectName, etTable)
3067     else
3068     ListIndex(ObjectName);
3069     eoChecks :
3070     if etTable in ExtractTypes then
3071     ListCheck(ObjectName, etTable)
3072     else
3073     ListCheck(ObjectName);
3074     eoData : ListData(ObjectName);
3075     end;
3076     if DidActivate then
3077     FTransaction.Commit;
3078     end;
3079    
3080     function TIBExtract.GetFieldType(FieldType, FieldSubType, FieldScale,
3081     FieldSize, FieldPrec, FieldLen: Integer): String;
3082     var
3083     i : Integer;
3084     PrecisionKnown : Boolean;
3085     begin
3086     Result := '';
3087     for i := Low(ColumnTypes) to High(ColumnTypes) do
3088     if FieldType = ColumnTypes[i].SQLType then
3089     begin
3090     PrecisionKnown := FALSE;
3091     if FDatabaseInfo.ODSMajorVersion >= ODS_VERSION10 then
3092     begin
3093     if FieldType in [blr_short, blr_long, blr_int64] then
3094     begin
3095     { We are ODS >= 10 and could be any Dialect }
3096     if (FDatabaseInfo.DBSQLDialect >= 3) and
3097     (FieldPrec <> 0) and
3098     (FieldSubType > 0) and
3099     (FieldSubType <= MAX_INTSUBTYPES) then
3100     begin
3101     Result := Result + Format('%s(%d, %d)', [
3102     IntegralSubtypes [FieldSubType],
3103     FieldPrec,
3104     -1 * FieldScale]);
3105     PrecisionKnown := true;
3106     end;
3107     end;
3108     end;
3109     if PrecisionKnown = false then
3110     begin
3111     { Take a stab at numerics and decimals }
3112     if (FieldType = blr_short) and
3113     (FieldScale < 0) then
3114     Result := Result + Format('NUMERIC(4, %d)',
3115     [-FieldScale] )
3116     else
3117     if (FieldType = blr_long) and
3118     (FieldScale < 0) then
3119     Result := Result + Format('NUMERIC(9, %d)',
3120     [-FieldScale] )
3121     else
3122     if (FieldType = blr_double) and
3123     (FieldScale < 0) then
3124     Result := Result + Format('NUMERIC(15, %d)',
3125     [-FieldScale] )
3126     else
3127     Result := Result + ColumnTypes[i].TypeName;
3128     end;
3129     break;
3130     end;
3131     if (FieldType in [blr_text, blr_varying]) and
3132     (FieldSize <> 0) then
3133     Result := Result + Format('(%d)', [FieldSize]);
3134     end;
3135    
3136     { S H O W _ g r a n t s
3137     Functional description
3138     Show grants for given object name
3139     This function is also called by extract for privileges.
3140     It must extract granted privileges on tables/views to users,
3141     - these may be compound, so put them on the same line.
3142     Grant execute privilege on procedures to users
3143     Grant various privileges to procedures.
3144     All privileges may have the with_grant option set. }
3145    
3146     procedure TIBExtract.ShowGrants(MetaObject: String; Terminator: String;
3147     NoUserGrants: boolean);
3148     const
3149     GrantsBaseSelect =
3150     'Select Trim(RDB$USER) as RDB$USER,List("Privileges") as Privileges, '+
3151     'coalesce(RDB$GRANT_OPTION,0) as RDB$GRANT_OPTION,METAOBJECTNAME, '+
3152     'RDB$USER_TYPE, RDB$OBJECT_TYPE, '+
3153     'case T2.RDB$TYPE_NAME '+
3154     ' When ''RELATION'' then ''TABLE'' '+
3155     ' When ''FIELD'' then ''DOMAIN'' '+
3156     'Else T2.RDB$TYPE_NAME End as OBJECT_TYPE_NAME, '+
3157     'T1.RDB$TYPE_NAME as USER_TYPE_NAME, '+
3158     'case '+
3159     'When coalesce(RDB$GRANT_OPTION,0) <> 0 and RDB$USER_TYPE = 13 then '' WITH ADMIN OPTION'' '+
3160     'When coalesce(RDB$GRANT_OPTION,0) <> 0 and RDB$USER_TYPE <> 13 then '' WITH GRANT OPTION'' '+
3161     'ELSE '''' End as GRANTOPTION, '+
3162     'case When RDB$OWNER_NAME = RDB$GRANTOR then NULL '+
3163     'else Trim(RDB$GRANTOR) End as GRANTEDBY '+
3164     'From ( '+
3165     'Select PR.RDB$USER,PR.RDB$RELATION_NAME as METAOBJECTNAME, LIST(DISTINCT Trim(Case PR.RDB$PRIVILEGE '+
3166     'When ''X'' then ''EXECUTE'' '+
3167     'When ''S'' then ''SELECT'' '+
3168     'When ''U'' then ''UPDATE'' '+
3169     'When ''D'' then ''DELETE'' '+
3170     'When ''R'' then ''REFERENCES'' '+
3171     'When ''G'' then ''USAGE'' '+
3172     'When ''I'' then ''INSERT'' '+
3173     'end )) as "Privileges", '+
3174     'PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE,OW.RDB$OWNER_NAME,PR.RDB$GRANTOR '+
3175     'FROM RDB$USER_PRIVILEGES PR '+
3176     'JOIN ObjectOwners OW On OW.METAOBJECTNAME = PR.RDB$RELATION_NAME and OW.ObjectType = PR.RDB$OBJECT_TYPE '+
3177     'Where PR.RDB$PRIVILEGE <> ''M'' and (PR.RDB$PRIVILEGE <> ''U'' or PR.RDB$FIELD_NAME is null) '+
3178     'Group By PR.RDB$USER,PR.RDB$RELATION_NAME,PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE,OW.RDB$OWNER_NAME,PR.RDB$GRANTOR '+
3179     'UNION '+
3180     'Select PR.RDB$USER,PR.RDB$RELATION_NAME, ''Update('' || List(Trim(PR.RDB$FIELD_NAME)) || '')'', '+
3181     'PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE, OW.RDB$OWNER_NAME,PR.RDB$GRANTOR '+
3182     'FROM RDB$USER_PRIVILEGES PR '+
3183     'JOIN ObjectOwners OW On OW.METAOBJECTNAME = PR.RDB$RELATION_NAME and OW.ObjectType = PR.RDB$OBJECT_TYPE '+
3184     'Where PR.RDB$PRIVILEGE = ''U'' and PR.RDB$FIELD_NAME is not null '+
3185     'Group By PR.RDB$USER,PR.RDB$RELATION_NAME,PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE, OW.RDB$OWNER_NAME,PR.RDB$GRANTOR) A '+
3186     'JOIN RDB$TYPES T1 On T1.RDB$TYPE = RDB$USER_TYPE and T1.RDB$FIELD_NAME = ''RDB$OBJECT_TYPE'' '+
3187     'JOIN RDB$TYPES T2 On T2.RDB$TYPE = RDB$OBJECT_TYPE and T2.RDB$FIELD_NAME = ''RDB$OBJECT_TYPE'' '+
3188     'Where METAOBJECTNAME = :METAOBJECTNAME and RDB$USER <> RDB$OWNER_NAME '+
3189     'Group By RDB$USER,RDB$GRANT_OPTION, RDB$USER_TYPE, RDB$OBJECT_TYPE,METAOBJECTNAME,RDB$GRANTOR,T2.RDB$TYPE_NAME,T1.RDB$TYPE_NAME,RDB$OWNER_NAME '+
3190     'ORDER BY RDB$USER, RDB$OBJECT_TYPE';
3191    
3192     GrantsSQL12 =
3193     'with ObjectOwners As ( '+
3194     ' Select RDB$RELATION_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 0 as ObjectType '+
3195     ' From RDB$RELATIONS '+
3196     ' UNION '+
3197     ' Select RDB$PROCEDURE_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 5 as ObjectType '+
3198     ' From RDB$PROCEDURES '+
3199     ' UNION '+
3200     ' Select RDB$EXCEPTION_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 7 as ObjectType '+
3201     ' From RDB$EXCEPTIONS '+
3202     ' UNION '+
3203     ' Select RDB$FIELD_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 9 as ObjectType '+
3204     ' From RDB$FIELDS Where RDB$FIELD_NAME not Similar to ''RDB$%|SEC$%|MON$%|SQL$%'' '+
3205     ' UNION '+
3206     ' Select RDB$GENERATOR_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 14 as ObjectType '+
3207     ' From RDB$GENERATORS '+
3208     ' UNION '+
3209     ' Select RDB$CHARACTER_SET_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 11 as ObjectType '+
3210     ' From RDB$CHARACTER_SETS '+
3211     ' UNION '+
3212     ' Select RDB$COLLATION_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 17 as ObjectType '+
3213     ' From RDB$COLLATIONS '+
3214 tony 221 ' UNION '+
3215     ' Select RDB$PACKAGE_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 18 as ObjectType '+
3216     ' From RDB$PACKAGES '+
3217 tony 209 ') '+ GrantsBaseSelect;
3218    
3219     GrantsSQL =
3220     'with ObjectOwners As ( '+
3221     'Select RDB$RELATION_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 0 as ObjectType '+
3222     'From RDB$RELATIONS '+
3223     'UNION '+
3224     'Select RDB$PROCEDURE_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 5 as ObjectType '+
3225     'From RDB$PROCEDURES '+
3226     'UNION '+
3227     'Select RDB$EXCEPTION_NAME as METAOBJECTNAME, ''SYSDBA'', 7 as ObjectType '+
3228     'From RDB$EXCEPTIONS '+
3229     'UNION '+
3230     'Select RDB$GENERATOR_NAME as METAOBJECTNAME, ''SYSDBA'', 14 as ObjectType '+
3231     'From RDB$GENERATORS '+
3232     'UNION '+
3233     'Select RDB$CHARACTER_SET_NAME as METAOBJECTNAME, ''SYSDBA'', 11 as ObjectType '+
3234     'From RDB$CHARACTER_SETS '+
3235     ') '+ GrantsBaseSelect;
3236    
3237     var qryOwnerPriv : TIBSQL;
3238    
3239     begin
3240     if MetaObject = '' then
3241     exit;
3242    
3243     qryOwnerPriv := TIBSQL.Create(FDatabase);
3244     try
3245     if FDatabaseInfo.ODSMajorVersion >= ODS_VERSION12 then
3246     qryOwnerPriv.SQL.Text := GrantsSQL12
3247     else
3248     qryOwnerPriv.SQL.Text := GrantsSQL;
3249     qryOwnerPriv.Params.ByName('METAOBJECTNAME').AsString := MetaObject;
3250     qryOwnerPriv.ExecQuery;
3251     while not qryOwnerPriv.Eof do
3252     begin
3253     if not NoUserGrants or (qryOwnerPriv.FieldByName('RDB$USER_TYPE').AsInteger <> obj_user)
3254     or (qryOwnerPriv.FieldByName('RDB$USER').AsString = 'PUBLIC') then
3255     begin
3256     if qryOwnerPriv.FieldByName('GRANTEDBY').IsNull then
3257     FMetaData.Add(Format('GRANT %s ON %s %s TO %s %s %s %s', [
3258     qryOwnerPriv.FieldByName('Privileges').AsString,
3259     qryOwnerPriv.FieldByName('OBJECT_TYPE_NAME').AsString,
3260     QuoteIdentifier(qryOwnerPriv.FieldByName('METAOBJECTNAME').AsString),
3261     qryOwnerPriv.FieldByName('USER_TYPE_NAME').AsString,
3262     QuoteIdentifier(qryOwnerPriv.FieldByName('RDB$USER').AsString),
3263     qryOwnerPriv.FieldByName('GRANTOPTION').AsString,
3264     Terminator]))
3265     else
3266     FMetaData.Add(Format('GRANT %s ON %s %s TO %s %s %s GRANTED BY %s %s', [
3267     qryOwnerPriv.FieldByName('Privileges').AsString,
3268     qryOwnerPriv.FieldByName('OBJECT_TYPE_NAME').AsString,
3269     QuoteIdentifier(qryOwnerPriv.FieldByName('METAOBJECTNAME').AsString),
3270     qryOwnerPriv.FieldByName('USER_TYPE_NAME').AsString,
3271     QuoteIdentifier(qryOwnerPriv.FieldByName('RDB$USER').AsString),
3272     qryOwnerPriv.FieldByName('GRANTOPTION').AsString,
3273     QuoteIdentifier(qryOwnerPriv.FieldByName('GRANTEDBY').AsString),
3274     Terminator]));
3275     end;
3276     qryOwnerPriv.Next;
3277     end;
3278     qryOwnerPriv.Close;
3279     finally
3280     qryOwnerPriv.Free;
3281     end;
3282     end;
3283    
3284     procedure TIBExtract.ShowGrantsTo(MetaObject: String; ObjectType: integer; Terminator: String);
3285     const
3286     GrantsSQL =
3287     'Select Trim(RDB$USER) as RDB$USER,List("Privileges") as Privileges, '+
3288     'coalesce(RDB$GRANT_OPTION,0) as RDB$GRANT_OPTION,METAOBJECTNAME, '+
3289     'RDB$USER_TYPE, RDB$OBJECT_TYPE, '+
3290     'case RDB$OBJECT_TYPE '+
3291     'When 0 then ''TABLE'' '+
3292     'When 5 then ''PROCEDURE'' '+
3293     'When 7 then ''EXCEPTION'' '+
3294     'When 11 then ''CHARACTER SET'' '+
3295     'ELSE NULL END as OBJECT_TYPE_NAME, '+
3296     'case RDB$USER_TYPE '+
3297     'When 5 then ''PROCEDURE'' '+
3298     'When 2 then ''TRIGGER'' '+
3299     'When 8 then ''USER'' '+
3300     'When 13 then ''ROLE'' '+
3301     'ELSE NULL END as USER_TYPE_NAME, '+
3302     'case '+
3303     'When coalesce(RDB$GRANT_OPTION,0) <> 0 and RDB$USER_TYPE = 13 then '' WITH ADMIN OPTION'' '+
3304     'When coalesce(RDB$GRANT_OPTION,0) <> 0 and RDB$USER_TYPE <> 13 then '' WITH GRANT OPTION'' '+
3305     'ELSE '''' End as GRANTOPTION '+
3306     'From ( '+
3307     'Select PR.RDB$USER,PR.RDB$RELATION_NAME as METAOBJECTNAME, LIST(DISTINCT Trim(Case PR.RDB$PRIVILEGE '+
3308     'When ''X'' then ''EXECUTE'' '+
3309     'When ''S'' then ''SELECT'' '+
3310     'When ''U'' then ''UPDATE'' '+
3311     'When ''D'' then ''DELETE'' '+
3312     'When ''R'' then ''REFERENCES'' '+
3313     'When ''G'' then ''USAGE'' '+
3314     'When ''I'' then ''INSERT'' end )) as "Privileges", '+
3315     'PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE '+
3316     'FROM RDB$USER_PRIVILEGES PR '+
3317     'Where PR.RDB$PRIVILEGE <> ''M'' and (PR.RDB$PRIVILEGE <> ''U'' or PR.RDB$FIELD_NAME is null) '+
3318     'Group By PR.RDB$USER,PR.RDB$RELATION_NAME,PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE '+
3319     'UNION '+
3320     'Select PR.RDB$USER,PR.RDB$RELATION_NAME, ''Update('' || List(Trim(PR.RDB$FIELD_NAME)) || '')'', '+
3321     'PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE '+
3322     'FROM RDB$USER_PRIVILEGES PR '+
3323     'Where PR.RDB$PRIVILEGE = ''U'' and PR.RDB$FIELD_NAME is not null '+
3324     'Group By PR.RDB$USER,PR.RDB$RELATION_NAME,PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE) '+
3325     'Where RDB$USER = :METAOBJECTNAME and RDB$USER_TYPE = :USERTYPE '+
3326     'Group By RDB$USER,RDB$GRANT_OPTION, RDB$USER_TYPE, RDB$OBJECT_TYPE, METAOBJECTNAME '+
3327     'ORDER BY METAOBJECTNAME';
3328    
3329     var qryOwnerPriv : TIBSQL;
3330    
3331     begin
3332     if MetaObject = '' then
3333     exit;
3334    
3335     qryOwnerPriv := TIBSQL.Create(FDatabase);
3336     try
3337     qryOwnerPriv.SQL.Text := GrantsSQL;
3338     qryOwnerPriv.Params.ByName('METAOBJECTNAME').AsString := MetaObject;
3339     qryOwnerPriv.Params.ByName('USERTYPE').AsInteger := ObjectType;
3340     qryOwnerPriv.ExecQuery;
3341     while not qryOwnerPriv.Eof do
3342     begin
3343     FMetaData.Add(Format('GRANT %s ON %s %s TO %s %s %s%s', [
3344     qryOwnerPriv.FieldByName('Privileges').AsString,
3345     qryOwnerPriv.FieldByName('OBJECT_TYPE_NAME').AsString,
3346     QuoteIdentifier(qryOwnerPriv.FieldByName('METAOBJECTNAME').AsString),
3347     qryOwnerPriv.FieldByName('USER_TYPE_NAME').AsString,
3348     QuoteIdentifier(qryOwnerPriv.FieldByName('RDB$USER').AsString),
3349     qryOwnerPriv.FieldByName('GRANTOPTION').AsString,
3350     Terminator]));
3351     qryOwnerPriv.Next;
3352     end;
3353     qryOwnerPriv.Close;
3354     finally
3355     qryOwnerPriv.Free;
3356     end;
3357     FMetaData.Add('');
3358     end;
3359    
3360     { ShowGrantRoles
3361     Functional description
3362     Show grants for given role name
3363     This function is also called by extract for privileges.
3364     All membership privilege may have the with_admin option set. }
3365    
3366     procedure TIBExtract.ShowGrantRoles(Terminator: String);
3367     const
3368     RoleSQL =
3369     'SELECT RDB$USER, RDB$GRANT_OPTION, RDB$RELATION_NAME ' +
3370     'FROM RDB$USER_PRIVILEGES ' +
3371     'WHERE ' +
3372     ' RDB$OBJECT_TYPE = %d AND ' +
3373     ' RDB$USER_TYPE = %d AND ' +
3374     ' RDB$PRIVILEGE = ''M'' ' +
3375     'ORDER BY RDB$RELATION_NAME, RDB$USER';
3376    
3377     var
3378     WithOption, UserString : String;
3379     qryRole : TIBSQL;
3380    
3381     begin
3382     qryRole := TIBSQL.Create(FDatabase);
3383     try
3384     qryRole.SQL.Text := Format(RoleSQL, [obj_sql_role, obj_user]);
3385     qryRole.ExecQuery;
3386     while not qryRole.Eof do
3387     begin
3388     UserString := Trim(qryRole.FieldByName('RDB$USER').AsString);
3389    
3390     if (not qryRole.FieldByName('RDB$GRANT_OPTION').IsNull) and
3391     (qryRole.FieldByName('RDB$GRANT_OPTION').AsInteger = 1) then
3392     WithOption := ' WITH ADMIN OPTION'
3393     else
3394     WithOption := '';
3395     FMetaData.Add(Format('GRANT %s TO %s%s%s%s',
3396     [ QuoteIdentifier( qryRole.FieldByName('RDB$RELATION_NAME').AsString),
3397     UserString, WithOption, Terminator, LineEnding]));
3398    
3399     qryRole.Next;
3400     end;
3401     finally
3402     qryRole.Free;
3403     end;
3404     end;
3405    
3406     { GetProcedureArgs
3407     Functional description
3408     This function extract the procedure parameters and adds it to the
3409     extract file }
3410    
3411     procedure TIBExtract.GetProcedureArgs(Proc: String);
3412     const
3413     { query to retrieve the input parameters. }
3414     ProcHeaderSQL =
3415     'SELECT * ' +
3416     ' FROM RDB$PROCEDURE_PARAMETERS PRM JOIN RDB$FIELDS FLD ON ' +
3417     ' PRM.RDB$FIELD_SOURCE = FLD.RDB$FIELD_NAME ' +
3418     'WHERE ' +
3419     ' PRM.RDB$PROCEDURE_NAME = :PROCNAME AND ' +
3420     ' PRM.RDB$PARAMETER_TYPE = :Input ' +
3421     'ORDER BY PRM.RDB$PARAMETER_NUMBER';
3422    
3423     var
3424     FirstTime, PrecisionKnown : Boolean;
3425     Line : String;
3426     qryHeader : TIBSQL;
3427    
3428     function FormatParamStr : String;
3429     var
3430     i, CollationID, CharSetID : Integer;
3431     begin
3432     Result := Format(' %s ', [qryHeader.FieldByName('RDB$PARAMETER_NAME').AsString]);
3433     for i := Low(ColumnTypes) to High(ColumnTypes) do
3434     if qryHeader.FieldByName('RDB$FIELD_TYPE').AsInteger = ColumnTypes[i].SQLType then
3435     begin
3436     PrecisionKnown := FALSE;
3437     if FDatabaseInfo.ODSMajorVersion >= ODS_VERSION10 then
3438     begin
3439     if qryHeader.FieldByName('RDB$FIELD_TYPE').AsInteger in [blr_short, blr_long, blr_int64] then
3440     begin
3441     { We are ODS >= 10 and could be any Dialect }
3442     if (FDatabaseInfo.DBSQLDialect >= 3) and
3443     (not qryHeader.FieldByName('RDB$FIELD_PRECISION').IsNull) and
3444     (qryHeader.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger > 0) and
3445     (qryHeader.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger <= MAX_INTSUBTYPES) then
3446     begin
3447     Result := Result + Format('%s(%d, %d)', [
3448     IntegralSubtypes [qryHeader.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger],
3449     qryHeader.FieldByName('RDB$FIELD_PRECISION').AsInteger,
3450     -1 * qryHeader.FieldByName('RDB$FIELD_SCALE').AsInteger]);
3451     PrecisionKnown := true;
3452     end;
3453     end;
3454     end;
3455     if PrecisionKnown = false then
3456     begin
3457     { Take a stab at numerics and decimals }
3458     if (qryHeader.FieldByName('RDB$FIELD_TYPE').AsInteger = blr_short) and
3459     (qryHeader.FieldByName('RDB$FIELD_SCALE').AsInteger < 0) then
3460     Result := Result + Format('NUMERIC(4, %d)',
3461     [-qryHeader.FieldByName('RDB$FIELD_SCALE').AsInteger] )
3462     else
3463     if (qryHeader.FieldByName('RDB$FIELD_TYPE').AsInteger = blr_long) and
3464     (qryHeader.FieldByName('RDB$FIELD_SCALE').AsInteger < 0) then
3465     Result := Result + Format('NUMERIC(9, %d)',
3466     [-qryHeader.FieldByName('RDB$FIELD_SCALE').AsInteger] )
3467     else
3468     if (qryHeader.FieldByName('RDB$FIELD_TYPE').AsInteger = blr_double) and
3469     (qryHeader.FieldByName('RDB$FIELD_SCALE').AsInteger < 0) then
3470     Result := Result + Format('NUMERIC(15, %d)',
3471     [-qryHeader.FieldByName('RDB$FIELD_SCALE').AsInteger] )
3472     else
3473     Result := Result + ColumnTypes[i].TypeName;
3474     end;
3475     break;
3476     end;
3477     if (qryHeader.FieldByName('RDB$FIELD_TYPE').AsInteger in [blr_text, blr_varying]) then
3478     begin
3479     if not qryHeader.FieldByName('RDB$CHARACTER_LENGTH').IsNull then
3480     Result := Result + Format('(%d)', [qryHeader.FieldByName('RDB$CHARACTER_LENGTH').AsInteger])
3481     else
3482     Result := Result + Format('(%d)', [qryHeader.FieldByName('RDB$FIELD_LENGTH').AsInteger]);
3483     end;
3484    
3485     { Show international character sets and collations }
3486    
3487     if (not qryHeader.FieldByName('RDB$COLLATION_ID').IsNull) or
3488     (not qryHeader.FieldByName('RDB$CHARACTER_SET_ID').IsNull) then
3489     begin
3490     if qryHeader.FieldByName('RDB$COLLATION_ID').IsNull then
3491     CollationId := 0
3492     else
3493     CollationId := qryHeader.FieldByName('RDB$COLLATION_ID').AsInteger;
3494    
3495     if qryHeader.FieldByName('RDB$CHARACTER_SET_ID').IsNull then
3496     CharSetId := 0
3497     else
3498     CharSetId := qryHeader.FieldByName('RDB$CHARACTER_SET_ID').AsInteger;
3499    
3500     Result := Result + GetCharacterSets(CharSetId, CollationId, false);
3501     end;
3502     end;
3503    
3504     begin
3505     FirstTime := true;
3506     qryHeader := TIBSQL.Create(FDatabase);
3507     try
3508     qryHeader.SQL.Text := ProcHeaderSQL;
3509     qryHeader.Params.ByName('procname').AsString := Proc;
3510     qryHeader.Params.ByName('Input').AsInteger := 0;
3511     qryHeader.ExecQuery;
3512     while not qryHeader.Eof do
3513     begin
3514     if FirstTime then
3515     begin
3516     FirstTime := false;
3517     FMetaData.Add('(');
3518     end;
3519    
3520     Line := FormatParamStr;
3521    
3522     qryHeader.Next;
3523     if not qryHeader.Eof then
3524     Line := Line + ',';
3525     FMetaData.Add(Line);
3526     end;
3527    
3528     { If there was at least one param, close parens }
3529     if not FirstTime then
3530     begin
3531     FMetaData.Add( ')');
3532     end;
3533    
3534     FirstTime := true;
3535     qryHeader.Close;
3536     qryHeader.Params.ByName('Input').AsInteger := 1;
3537     qryHeader.ExecQuery;
3538    
3539     while not qryHeader.Eof do
3540     begin
3541     if FirstTime then
3542     begin
3543     FirstTime := false;
3544     FMetaData.Add('RETURNS' + LineEnding + '(');
3545     end;
3546    
3547     Line := FormatParamStr;
3548    
3549     qryHeader.Next;
3550     if not qryHeader.Eof then
3551     Line := Line + ',';
3552     FMetaData.Add(Line);
3553     end;
3554    
3555     { If there was at least one param, close parens }
3556     if not FirstTime then
3557     begin
3558     FMetaData.Add( ')');
3559     end;
3560    
3561     FMetaData.Add('AS');
3562     finally
3563     qryHeader.Free;
3564     end;
3565     end;
3566    
3567     procedure TIBExtract.Notification(AComponent: TComponent;
3568     Operation: TOperation);
3569     begin
3570     inherited;
3571     if (AComponent = FDatabase) and (Operation = opRemove) then
3572     FDatabase := nil;
3573     if (AComponent = FTransaction) and (Operation = opRemove) then
3574     FTransaction := nil;
3575     end;
3576    
3577     function TIBExtract.QuoteIdentifier(Value: String): String;
3578     begin
3579     if AlwaysQuoteIdentifiers then
3580     Result := IBUtils.QuoteIdentifier(FDatabase.DBSQLDialect,Value)
3581     else
3582     Result := IBUtils.QuoteIdentifierIfNeeded(FDatabase.DBSQLDialect,Value)
3583     end;
3584    
3585     procedure TIBExtract.ListData(ObjectName: String);
3586     const
3587     SelectFieldListSQL = 'Select List(RDB$FIELD_NAME) From ( '+
3588     'Select RF.RDB$FIELD_NAME From RDB$RELATION_FIELDS RF '+
3589     'JOIN RDB$FIELDS F On F.RDB$FIELD_NAME = RF.RDB$FIELD_SOURCE '+
3590     'Where F.RDB$COMPUTED_BLR is NULL and RF.RDB$RELATION_NAME = Upper(:Relation) '+
3591     'Order by RF.RDB$FIELD_POSITION asc)';
3592    
3593     TableSQL =
3594     'SELECT * FROM RDB$RELATIONS ' +
3595     'WHERE ' +
3596     ' (RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL) AND ' +
3597     ' RDB$VIEW_BLR IS NULL ' +
3598     'ORDER BY RDB$RELATION_NAME';
3599    
3600     var FieldList: string;
3601    
3602     begin
3603     if ObjectName = '' then {List all}
3604     begin
3605     with TIBSQL.Create(self) do
3606     try
3607     Database := FDatabase;
3608     SQL.Text := TableSQL;
3609     ExecQuery;
3610     FMetaData.Add('/* Data Starts */');
3611     while not EOF do
3612     begin
3613     ListData(Trim(FieldByName('RDB$RELATION_NAME').AsString));
3614     Next;
3615     end;
3616     FMetaData.Add('/* Data Ends */');
3617     finally
3618     Free;
3619     end;
3620     end
3621     else
3622     begin
3623     FieldList := '*';
3624     with TIBSQL.Create(self) do
3625     try
3626     Database := FDatabase;
3627     SQL.Text := SelectFieldListSQL;
3628     Params[0].AsString := ObjectName;
3629     ExecQuery;
3630     try
3631     if not EOF then
3632     FieldList := Fields[0].AsString;
3633     finally
3634     Close;
3635     end;
3636     finally
3637     Free
3638     end;
3639    
3640     with TIBInsertStmtsOut.Create(self) do
3641     try
3642     Database := FDatabase;
3643     if DataOut(Format('Select %s From %s',[FieldList,QuoteIdentifier( ObjectName)]),
3644     Add2MetaData) then
3645     FMetaData.Add('COMMIT;');
3646     finally
3647     Free
3648     end;
3649     end;
3650     end;
3651    
3652     procedure TIBExtract.ListRoles(ObjectName: String; IncludeGrants: boolean);
3653     const
3654     RolesSQL =
3655     'select * from RDB$ROLES WHERE RDB$SYSTEM_FLAG = 0 ' +
3656     'order by RDB$ROLE_NAME';
3657    
3658     RolesByNameSQL =
3659     'select * from RDB$ROLES ' +
3660     'WHERE RDB$ROLE_NAME = :RoleName ' +
3661     'order by RDB$ROLE_NAME';
3662    
3663     var
3664     qryRoles : TIBSQL;
3665     PrevOwner, RoleName, OwnerName : String;
3666     begin
3667     {Process GRANT roles}
3668     qryRoles := TIBSQL.Create(FDatabase);
3669     try
3670     if FDatabaseInfo.ODSMajorVersion >= ODS_VERSION9 then
3671     begin
3672     PrevOwner := '';
3673     FMetaData.Add('');
3674     FMetaData.Add('/* Grant Roles for this database */');
3675     FMetaData.Add('');
3676    
3677     if ObjectName = '' then
3678     qryRoles.SQL.Text := RolesSQL
3679     else
3680     begin
3681     qryRoles.SQL.Text := RolesByNameSQL;
3682     qryRoles.Params.ByName('RoleName').AsString := ObjectName;
3683     end;
3684     qryRoles.ExecQuery;
3685     try
3686     while not qryRoles.Eof do
3687     begin
3688     RoleName := QuoteIdentifier(
3689     qryRoles.FieldByName('rdb$Role_Name').AsString);
3690     OwnerName := Trim(qryRoles.FieldByName('rdb$Owner_Name').AsString);
3691     if PrevOwner <> OwnerName then
3692     begin
3693     FMetaData.Add('');
3694     FMetaData.Add(Format('/* Role: %s, Owner: %s */', [RoleName, OwnerName]));
3695     FMetaData.Add('');
3696     PrevOwner := OwnerName;
3697     end;
3698     FMetaData.Add('CREATE ROLE ' + RoleName + Term);
3699     if IncludeGrants then
3700     ShowGrantsTo(qryRoles.FieldByName('rdb$Role_Name').AsString,obj_sql_role,Term);
3701     qryRoles.Next;
3702     end;
3703     finally
3704     qryRoles.Close;
3705     end;
3706     end;
3707     finally
3708     qryRoles.Free;
3709     end;
3710     end;
3711    
3712     end.
3713    
3714