ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBExtract.pas
Revision: 143
Committed: Fri Feb 23 12:11:21 2018 UTC (6 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 124739 byte(s)
Log Message:
Fixes Merged

File Contents

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