ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBExtract.pas
Revision: 45
Committed: Tue Dec 6 10:33:46 2016 UTC (7 years, 4 months ago) by tony
Content type: text/x-pascal
File size: 110606 byte(s)
Log Message:
Committing updates for Release R2-0-0

File Contents

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