ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBExtract.pas
Revision: 5
Committed: Fri Feb 18 16:26:16 2011 UTC (13 years, 2 months ago) by tony
Content type: text/x-pascal
File size: 107429 byte(s)
Log Message:
Committing updates for Release pre-release

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