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