ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBExtract.pas
Revision: 45
Committed: Tue Dec 6 10:33:46 2016 UTC (7 years, 11 months ago) by tony
Content type: text/x-pascal
File size: 110606 byte(s)
Log Message:
Committing updates for Release R2-0-0

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