ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBExtract.pas
Revision: 39
Committed: Tue May 17 08:14:52 2016 UTC (8 years, 6 months ago) by tony
Content type: text/x-pascal
File size: 107867 byte(s)
Log Message:
Committing updates for Release R1-4-1

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