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