ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBExtract.pas
Revision: 140
Committed: Wed Jan 24 16:31:11 2018 UTC (6 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 119198 byte(s)
Log Message:
Fixes Merged

File Contents

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