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