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