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

File Contents

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