--- ibx/trunk/runtime/IBExtract.pas 2016/12/06 10:33:46 46 +++ ibx/trunk/runtime/IBExtract.pas 2017/01/09 15:31:51 47 @@ -23,7 +23,7 @@ { } {************************************************************************} -{ Syntax Enhancements Supported: +{ Syntax Enhancements Supported (by Firebird Version no.): Multi-action triggers (1.5) CREATE SEQUENCE (2.0) @@ -61,6 +61,8 @@ type TExtractTypes = Set of TExtractType; + TProcDDLType = (pdCreateProc,pdCreateStub,pdAlterProc); + { TIBExtract } TIBExtract = class(TComponent) @@ -71,6 +73,7 @@ type FDatabaseInfo: TIBDatabaseInfo; FShowSystem: Boolean; { Private declarations } + procedure Add2MetaData(const Msg: string; IsError: boolean=true); function GetDatabase: TIBDatabase; function GetIndexSegments ( indexname : String) : String; function GetTransaction: TIBTransaction; @@ -79,18 +82,22 @@ type procedure SetTransaction(const Value: TIBTransaction); function PrintValidation(ToValidate : String; flag : Boolean) : String; procedure ShowGrants(MetaObject: String; Terminator : String); + procedure ShowGrantsTo(MetaObject: String; ObjectType: integer; + Terminator: String); procedure ShowGrantRoles(Terminator : String); procedure GetProcedureArgs(Proc : String); protected - function ExtractDDL(Flag : Boolean; TableName : String) : Boolean; + function ExtractDDL(Flag: Boolean; TableName: String; IncludeData: boolean = + false): Boolean; function ExtractListTable(RelationName, NewName: String; DomainFlag: Boolean): Boolean; procedure ExtractListView (ViewName : String); procedure ListData(ObjectName : String); procedure ListRoles(ObjectName : String = ''); procedure ListGrants; - procedure ListProcs(ProcedureName : String = ''); + procedure ListProcs(ProcDDLType: TProcDDLType = pdCreateProc; ProcedureName : String = ''; + IncludeGrants:boolean=false); procedure ListAllTables(flag : Boolean); - procedure ListTriggers(AlterTrigger, IncludeBody: boolean; ObjectName : String = ''; ExtractType : TExtractType = etTrigger); + procedure ListTriggers(ObjectName: String=''; ExtractTypes: TExtractTypes = [etTrigger]); procedure ListCheck(ObjectName : String = ''; ExtractType : TExtractType = etCheck); function PrintSet(var Used : Boolean) : String; procedure ListCreateDb(TargetDb : String = ''); @@ -99,9 +106,10 @@ type procedure ListFilters(FilterName : String = ''); procedure ListForeign(ObjectName : String = ''; ExtractType : TExtractType = etForeign); procedure ListFunctions(FunctionName : String = ''); - procedure ListGenerators(GeneratorName : String = ''); + procedure ListGenerators(GeneratorName : String = ''; ExtractTypes: TExtractTypes=[]); procedure ListIndex(ObjectName : String = ''; ExtractType : TExtractType = etIndex); procedure ListViews(ViewName : String = ''); + procedure Notification(AComponent: TComponent; Operation: TOperation); override; { Protected declarations } public @@ -112,7 +120,6 @@ type function GetFieldType(FieldType, FieldSubType, FieldScale, FieldSize, FieldPrec, FieldLen : Integer) : String; function GetCharacterSets(CharSetId, Collation : integer; CollateOnly : Boolean) : String; - procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure ExtractObject(ObjectType : TExtractObjectTypes; ObjectName : String = ''; ExtractTypes : TExtractTypes = []); property DatabaseInfo : TIBDatabaseInfo read FDatabaseInfo; @@ -146,14 +153,16 @@ const priv_DELETE = 16; priv_EXECUTE = 32; priv_REFERENCES = 64; + priv_USAGE = 128; - PrivTypes : Array[0..5] of TPrivTypes = ( + PrivTypes : Array[0..6] of TPrivTypes = ( (PrivFlag : priv_DELETE; PrivString : 'DELETE' ), (PrivFlag : priv_EXECUTE; PrivString : 'EXECUTE' ), (PrivFlag : priv_INSERT; PrivString : 'INSERT' ), (PrivFlag : priv_SELECT; PrivString : 'SELECT' ), (PrivFlag : priv_UPDATE; PrivString : 'UPDATE' ), - (PrivFlag : priv_REFERENCES; PrivString : 'REFERENCES')); + (PrivFlag : priv_REFERENCES; PrivString : 'REFERENCES'), + (PrivFlag : priv_USAGE; PrivString : 'USAGE' )); ColumnTypes : TSQLTypes = ( (SqlType : blr_short; TypeName : 'SMALLINT'), { NTX: keyword } @@ -195,6 +204,7 @@ const ODS_VERSION10 = 10; { V6.0 features. SQL delimited idetifier, SQLDATE, and 64-bit exact numeric type } + ODS_VERSION12 = 12; {Firebird 3} { flags for RDB$FILE_FLAGS } FILE_shadow = 1; @@ -233,10 +243,9 @@ const implementation -uses FBMessages; +uses FBMessages, IBDataOutput; const - NEWLINE = #13#10; TERM = ';'; ProcTerm = '^'; @@ -322,7 +331,7 @@ begin inherited; end; -function TIBExtract.ExtractDDL(Flag: Boolean; TableName: String) : Boolean; +function TIBExtract.ExtractDDL(Flag: Boolean; TableName: String; IncludeData: boolean = false) : Boolean; var DidConnect : Boolean; DidStart : Boolean; @@ -359,15 +368,20 @@ begin ListFunctions; ListDomains; ListAllTables(flag); + if IncludeData then + ListData(''); ListIndex; ListForeign; - ListGenerators; + if IncludeData then + ListGenerators('',[etData]) + else + ListGenerators; ListViews; ListCheck; ListException; - ListTriggers(false,false); - ListProcs; - ListTriggers(true,true); + ListProcs(pdCreateStub); + ListTriggers; + ListProcs(pdAlterProc); ListGrants; end; @@ -414,7 +428,7 @@ const ' (RELC.RDB$CONSTRAINT_TYPE = ''PRIMARY KEY'' OR ' + ' RELC.RDB$CONSTRAINT_TYPE = ''UNIQUE'') AND ' + ' RELC.RDB$RELATION_NAME = :RELATIONNAME ' + - 'ORDER BY RELC.RDB$CONSTRAINT_NAME'; + 'ORDER BY RELC.RDB$CONSTRAINT_TYPE desc, RELC.RDB$CONSTRAINT_NAME'; GetGeneratorSQL = 'SELECT * FROM RDB$GENERATORS WHERE RDB$GENERATOR_NAME = :GENERATOR'; @@ -445,6 +459,7 @@ begin qryGenerators := TIBSQL.Create(FDatabase); try qryTables.SQL.Add(TableListSQL); + RelationName := trim(RelationName); qryTables.Params.ByName('RelationName').AsString := RelationName; qryTables.ExecQuery; qryPrecision.SQL.Add(PrecisionSQL); @@ -458,8 +473,8 @@ begin if (not qryTables.FieldByName('RDB$OWNER_NAME').IsNull) and (Trim(qryTables.FieldByName('RDB$OWNER_NAME').AsString) <> '') then FMetaData.Add(Format('%s/* Table: %s, Owner: %s */%s', - [NEWLINE, RelationName, - qryTables.FieldByName('RDB$OWNER_NAME').AsString, NEWLINE])); + [LineEnding, RelationName, + qryTables.FieldByName('RDB$OWNER_NAME').AsString, LineEnding])); if TableType > 3 then CreateTable := 'CREATE GLOBAL TEMPORARY TABLE' else @@ -497,7 +512,7 @@ begin (qryTables.FieldByName('RDB$FIELD_NAME1').AsString[5] in ['0'..'9'])) and (qryTables.FieldByName('RDB$SYSTEM_FLAG').AsInteger <> 1) then begin - Column := Column + QuoteIdentifier(FDatabase.SQLDialect, qryTables.FieldByName('RDB$FIELD_NAME1').AsString); + Column := Column + QuoteIdentifier(FDatabase.SQLDialect, trim(qryTables.FieldByName('RDB$FIELD_NAME1').AsString)); { International character sets } if (qryTables.FieldByName('RDB$FIELD_TYPE').AsInteger in [blr_text, blr_varying]) and (not qryTables.FieldByName('RDB$COLLATION_ID').IsNull) @@ -701,10 +716,10 @@ begin end; if ValidRelation then begin - FMetaData.Add(') '); if TableType = 4 then - FMetaData.Add('ON COMMIT PRESERVE ROWS '); - FMetaData.Add(Term); + FMetaData.Add(' ) ON COMMIT PRESERVE ROWS ' + TERM) + else + FMetaData.Add(')' + TERM); end; finally qryTables.Free; @@ -833,6 +848,11 @@ begin FTransaction.Commit; end; +procedure TIBExtract.Add2MetaData(const Msg: string; IsError: boolean); +begin + FMetaData.Add(Msg); +end; + function TIBExtract.GetDatabase: TIBDatabase; begin result := FDatabase; @@ -888,15 +908,15 @@ begin Result := 'ON '; case TypeID of $2000: - Result += 'CONNECT '; + Result += 'CONNECT'; $2001: - Result += 'DISCONNECT '; + Result += 'DISCONNECT'; $2002: - Result +='TRANSACTION START '; + Result +='TRANSACTION START'; $2003: - Result += 'TRANSACTION COMMIT '; + Result += 'TRANSACTION COMMIT'; $2004: - Result += 'TRANSACTION ROLLBACK '; + Result += 'TRANSACTION ROLLBACK'; end; end else @@ -937,9 +957,18 @@ const ' RDB$SECURITY_CLASS STARTING WITH ''SQL$'' ' + 'ORDER BY RDB$RELATION_NAME'; - ProcedureSQL = 'select * from RDB$PROCEDURES ' + + ProcedureSQL = 'select * from RDB$PROCEDURES '+ + 'Where RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL ' + 'Order BY RDB$PROCEDURE_NAME'; + ExceptionSQL = 'select * from RDB$EXCEPTIONS '+ + 'Where RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL ' + + 'Order BY RDB$EXCEPTION_NAME'; + + GeneratorSQL = 'select * from RDB$GENERATORS '+ + 'Where RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL ' + + 'Order BY RDB$GENERATOR_NAME'; + var qryRoles : TIBSQL; RelationName : String; @@ -969,6 +998,30 @@ begin ShowGrantRoles(Term); + qryRoles.SQL.Text := ExceptionSQL; + qryRoles.ExecQuery; + try + while not qryRoles.Eof do + begin + ShowGrants(Trim(qryRoles.FieldByName('RDB$EXCEPTION_NAME').AsString), Term); + qryRoles.Next; + end; + finally + qryRoles.Close; + end; + + qryRoles.SQL.Text := GeneratorSQL; + qryRoles.ExecQuery; + try + while not qryRoles.Eof do + begin + ShowGrants(Trim(qryRoles.FieldByName('RDB$GENERATOR_NAME').AsString), Term); + qryRoles.Next; + end; + finally + qryRoles.Close; + end; + qryRoles.SQL.Text := ProcedureSQL; qryRoles.ExecQuery; try @@ -996,13 +1049,25 @@ end; procname -- Name of procedure to investigate } -procedure TIBExtract.ListProcs(ProcedureName : String); +procedure TIBExtract.ListProcs(ProcDDLType: TProcDDLType; + ProcedureName: String; IncludeGrants: boolean); const CreateProcedureStr1 = 'CREATE PROCEDURE %s '; CreateProcedureStr2 = 'BEGIN EXIT; END %s%s'; - ProcedureSQL = - 'SELECT * FROM RDB$PROCEDURES ' + - 'ORDER BY RDB$PROCEDURE_NAME'; + ProcedureSQL = {Order procedures by dependency order and then procedure name} + 'with recursive Procs as ( ' + + 'Select RDB$PROCEDURE_NAME, 1 as ProcLevel from RDB$PROCEDURES ' + + 'UNION ALL ' + + 'Select D.RDB$DEPENDED_ON_NAME, ProcLevel + 1 From RDB$DEPENDENCIES D ' + + 'JOIN Procs on Procs.RDB$PROCEDURE_NAME = D.RDB$DEPENDENT_NAME ' + + ' and Procs.RDB$PROCEDURE_NAME <> D.RDB$DEPENDED_ON_NAME ' + + 'JOIN RDB$PROCEDURES P On P.RDB$PROCEDURE_NAME = D.RDB$DEPENDED_ON_NAME ' + + ' ) ' + + 'SELECT * FROM RDB$PROCEDURES P ' + + 'JOIN ( ' + + 'Select RDB$PROCEDURE_NAME, max(ProcLevel) as ProcLevel From Procs ' + + 'Group By RDB$PROCEDURE_NAME) A On A.RDB$PROCEDURE_NAME = P.RDB$PROCEDURE_NAME ' + + 'Order by A.ProcLevel desc, P.RDB$PROCEDURE_NAME asc'; ProcedureNameSQL = 'SELECT * FROM RDB$PROCEDURES ' + @@ -1014,14 +1079,13 @@ var ProcName : String; SList : TStrings; Header : Boolean; + begin Header := true; qryProcedures := TIBSQL.Create(FDatabase); SList := TStringList.Create; try -{ First the dummy procedures - create the procedures with their parameters } if ProcedureName = '' then qryProcedures.SQL.Text := ProcedureSQL else @@ -1029,6 +1093,7 @@ begin qryProcedures.SQL.Text := ProcedureNameSQL; qryProcedures.Params.ByName('ProcedureName').AsString := ProcedureName; end; + qryProcedures.ExecQuery; while not qryProcedures.Eof do begin @@ -1037,37 +1102,56 @@ begin FMetaData.Add('COMMIT WORK;'); FMetaData.Add('SET AUTODDL OFF;'); FMetaData.Add(Format('SET TERM %s %s', [ProcTerm, Term])); - FMetaData.Add(Format('%s/* Stored procedures */%s', [NEWLINE, NEWLINE])); + FMetaData.Add(Format('%s/* Stored procedures */%s', [LineEnding, LineEnding])); Header := false; end; ProcName := Trim(qryProcedures.FieldByName('RDB$PROCEDURE_NAME').AsString); - FMetaData.Add(Format(CreateProcedureStr1, [QuoteIdentifier(FDatabase.SQLDialect, - ProcName)])); - GetProcedureArgs(ProcName); - FMetaData.Add(Format(CreateProcedureStr2, [ProcTerm, NEWLINE])); - qryProcedures.Next; - end; - qryProcedures.Close; - qryProcedures.ExecQuery; - while not qryProcedures.Eof do - begin - SList.Clear; - ProcName := Trim(qryProcedures.FieldByName('RDB$PROCEDURE_NAME').AsString); - FMetaData.Add(Format('%sALTER PROCEDURE %s ', [NEWLINE, - QuoteIdentifier(FDatabase.SQLDialect, ProcName)])); - GetProcedureArgs(ProcName); - - if not qryProcedures.FieldByName('RDB$PROCEDURE_SOURCE').IsNull then - SList.Text := SList.Text + qryProcedures.FieldByName('RDB$PROCEDURE_SOURCE').AsString; - SList.Add(Format(' %s%s', [ProcTerm, NEWLINE])); - FMetaData.AddStrings(SList); + case ProcDDLType of + pdCreateStub: + begin + FMetaData.Add(Format(CreateProcedureStr1, [QuoteIdentifier(FDatabase.SQLDialect, + ProcName)])); + GetProcedureArgs(ProcName); + FMetaData.Add(Format(CreateProcedureStr2, [ProcTerm, LineEnding])); + end; + + pdCreateProc: + begin + FMetaData.Add(Format(CreateProcedureStr1, [QuoteIdentifier(FDatabase.SQLDialect, + ProcName)])); + GetProcedureArgs(ProcName); + if not qryProcedures.FieldByName('RDB$PROCEDURE_SOURCE').IsNull then + begin + SList.Text := qryProcedures.FieldByName('RDB$PROCEDURE_SOURCE').AsString; + SList.Add(Format(' %s%s', [ProcTerm, LineEnding])); + FMetaData.AddStrings(SList); + end + else + FMetaData.Add(Format(CreateProcedureStr2, [ProcTerm, LineEnding])); + end; + + pdAlterProc: + begin + FMetaData.Add(Format('%sALTER PROCEDURE %s ', [LineEnding, + QuoteIdentifier(FDatabase.SQLDialect, ProcName)])); + GetProcedureArgs(ProcName); + + if not qryProcedures.FieldByName('RDB$PROCEDURE_SOURCE').IsNull then + begin + SList.Text := qryProcedures.FieldByName('RDB$PROCEDURE_SOURCE').AsString; + SList.Add(Format(' %s%s', [ProcTerm, LineEnding])); + FMetaData.AddStrings(SList); + end + else + FMetaData.Add(Format(CreateProcedureStr2, [ProcTerm, LineEnding])); + end; + end; + if IncludeGrants then + ShowGrantsTo(ProcName,obj_procedure,ProcTerm); qryProcedures.Next; end; - -{ This query gets the procedure name and the source. We then nest a query - to retrieve the parameters. Alter is used, because the procedures are - already there} + qryProcedures.Close; if not Header then begin @@ -1128,14 +1212,14 @@ end; Lists triggers in general on non-system tables with sql source only. } -procedure TIBExtract.ListTriggers(AlterTrigger, IncludeBody: boolean; - ObjectName: String; ExtractType: TExtractType); +procedure TIBExtract.ListTriggers(ObjectName: String; ExtractTypes: TExtractTypes + ); const { Query gets the trigger info for non-system triggers with source that are not part of an SQL constraint } TriggerSQL = - 'SELECT * FROM RDB$TRIGGERS TRG JOIN RDB$RELATIONS REL ON ' + + 'SELECT * FROM RDB$TRIGGERS TRG Left Outer JOIN RDB$RELATIONS REL ON ' + ' TRG.RDB$RELATION_NAME = REL.RDB$RELATION_NAME ' + 'WHERE ' + ' (REL.RDB$SYSTEM_FLAG <> 1 OR REL.RDB$SYSTEM_FLAG IS NULL) AND ' + @@ -1156,7 +1240,7 @@ const ' TRG.RDB$TRIGGER_SEQUENCE, TRG.RDB$TRIGGER_NAME'; TriggerByNameSQL = - 'SELECT * FROM RDB$TRIGGERS TRG JOIN RDB$RELATIONS REL ON ' + + 'SELECT * FROM RDB$TRIGGERS TRG Left Outer JOIN RDB$RELATIONS REL ON ' + ' TRG.RDB$RELATION_NAME = REL.RDB$RELATION_NAME ' + 'WHERE ' + ' TRG.RDB$TRIGGER_NAME = :TriggerName AND ' + @@ -1180,7 +1264,7 @@ begin qryTriggers.SQL.Text := TriggerSQL else begin - if ExtractType = etTable then + if etTable in ExtractTypes then begin qryTriggers.SQL.Text := TriggerNameSQL; qryTriggers.Params.ByName('TableName').AsString := ObjectName; @@ -1197,9 +1281,9 @@ begin SList.Clear; if Header then begin - FMetaData.Add(Format('SET TERM %s %s%s', [Procterm, Term, NEWLINE])); + FMetaData.Add(Format('SET TERM %s %s%s', [Procterm, Term, LineEnding])); FMetaData.Add(Format('%s/* Triggers only will work for SQL triggers */%s', - [NEWLINE, NEWLINE])); + [LineEnding, LineEnding])); Header := false; end; TriggerName := qryTriggers.FieldByName('RDB$TRIGGER_NAME').AsString; @@ -1215,24 +1299,26 @@ begin if qryTriggers.FieldByName('RDB$FLAGS').AsInteger <> 1 then SList.Add('/* '); - if AlterTrigger then - SList.Add(Format('Alter TRIGGER %s ',[QuoteIdentifier(FDatabase.SQLDialect, TriggerName)])) - else - SList.Add(Format('CREATE TRIGGER %s FOR %s %s%s %s POSITION %d', - [QuoteIdentifier(FDatabase.SQLDialect, TriggerName), - QuoteIdentifier(FDatabase.SQLDialect, RelationName), - NEWLINE, InActive, - GetTriggerType(qryTriggers.FieldByName('RDB$TRIGGER_TYPE').AsInteger), - qryTriggers.FieldByName('RDB$TRIGGER_SEQUENCE').AsInteger])); - if IncludeBody and not qryTriggers.FieldByName('RDB$TRIGGER_SOURCE').IsNull then - SList.Text := SList.Text + - qryTriggers.FieldByName('RDB$TRIGGER_SOURCE').AsString + {Database or Transaction trigger} + SList.Add(Format('CREATE TRIGGER %s%s%s %s POSITION %d', + [QuoteIdentifier(FDatabase.SQLDialect, TriggerName), + LineEnding, InActive, + GetTriggerType(qryTriggers.FieldByName('RDB$TRIGGER_TYPE').AsInteger), + qryTriggers.FieldByName('RDB$TRIGGER_SEQUENCE').AsInteger])); + + if RelationName <> '' then + SList.Add('ON ' + QuoteIdentifier(FDatabase.SQLDialect, RelationName)); + + if not qryTriggers.FieldByName('RDB$TRIGGER_SOURCE').IsNull then + SList.Add(qryTriggers.FieldByName('RDB$TRIGGER_SOURCE').AsString) else - SList.Text := SList.Text + 'AS BEGIN EXIT; END'; - SList.Add(' ' + ProcTerm + NEWLINE); + SList.Add('AS BEGIN EXIT; END'); + SList.Add(' ' + ProcTerm); if qryTriggers.FieldByName('RDB$FLAGS').AsInteger <> 1 then SList.Add(' */'); FMetaData.AddStrings(SList); + if etGrant in ExtractTypes then + ShowGrantsTo(TriggerName,obj_trigger,ProcTerm); qryTriggers.Next; end; if not Header then @@ -1317,7 +1403,7 @@ begin if not qryChecks.FieldByName('RDB$TRIGGER_SOURCE').IsNull then SList.Text := SList.Text + qryChecks.FieldByName('RDB$TRIGGER_SOURCE').AsString; - SList.Strings[SList.Count - 1] := SList.Strings[SList.Count - 1] + (Term) + NEWLINE; + SList.Strings[SList.Count - 1] := SList.Strings[SList.Count - 1] + (Term) + LineEnding; FMetaData.AddStrings(SList); qryChecks.Next; end; @@ -1376,7 +1462,7 @@ begin NoDb := true; end; Buffer := Buffer + 'CREATE DATABASE ' + QuotedStr(TargetDb) + ' PAGE_SIZE ' + - IntToStr(FDatabaseInfo.PageSize) + NEWLINE; + IntToStr(FDatabaseInfo.PageSize) + LineEnding; FMetaData.Add(Buffer); Buffer := ''; @@ -1387,7 +1473,7 @@ begin if not qryDB.EOF then Buffer := Format(' DEFAULT CHARACTER SET %s', - [qryDB.FieldByName('RDB$CHARACTER_SET_NAME').AsString]); + [trim(qryDB.FieldByName('RDB$CHARACTER_SET_NAME').AsString)]); if NoDB then Buffer := Buffer + Term + ' */' else @@ -1402,7 +1488,7 @@ begin begin if First then begin - FMetaData.Add(NEWLINE + '/* Add secondary files in comments '); + FMetaData.Add(LineEnding + '/* Add secondary files in comments '); First := false; end; //end_if @@ -1427,7 +1513,7 @@ begin if FileFlags = 0 then begin Buffer := Format('%sALTER DATABASE ADD FILE ''%s''', - [NEWLINE, qryDB.FieldByName('RDB$FILE_NAME').AsString]); + [LineEnding, qryDB.FieldByName('RDB$FILE_NAME').AsString]); if FileStart <> 0 then Buffer := Buffer + Format(' STARTING %d', [FileStart]); if FileLength <> 0 then @@ -1436,7 +1522,7 @@ begin end; //end_if if (FileFlags and FILE_cache) <> 0 then FMetaData.Add(Format('%sALTER DATABASE ADD CACHE ''%s'' LENGTH %d', - [NEWLINE, qryDB.FieldByName('RDB$FILE_NAME').AsString, FileLength])); + [LineEnding, qryDB.FieldByName('RDB$FILE_NAME').AsString, FileLength])); Buffer := ''; if (FileFlags and FILE_shadow) <> 0 then @@ -1447,7 +1533,7 @@ begin else begin Buffer := Format('%sCREATE SHADOW %d ''%s'' ', - [NEWLINE, qryDB.FieldByName('RDB$SHADOW_NUMBER').AsInteger, + [LineEnding, qryDB.FieldByName('RDB$SHADOW_NUMBER').AsInteger, qryDB.FieldByName('RDB$FILE_NAME').AsString]); if (FileFlags and FILE_inactive) <> 0 then Buffer := Buffer + 'INACTIVE '; @@ -1488,7 +1574,7 @@ begin begin if NoDB then Buffer := '/* '; - Buffer := Buffer + NEWLINE + 'ALTER DATABASE ADD '; + Buffer := Buffer + LineEnding + 'ALTER DATABASE ADD '; First := false; end; //end_if if FirstFile then @@ -1498,11 +1584,11 @@ begin begin if (FileFlags and LOG_overflow) <> 0 then Buffer := Buffer + Format(')%s OVERFLOW ''%s''', - [NEWLINE, qryDB.FieldByName('RDB$FILE_NAME').AsString]) + [LineEnding, qryDB.FieldByName('RDB$FILE_NAME').AsString]) else if (FileFlags and LOG_serial) <> 0 then Buffer := Buffer + Format('%s BASE_NAME ''%s''', - [NEWLINE, qryDB.FieldByName('RDB$FILE_NAME').AsString]) + [LineEnding, qryDB.FieldByName('RDB$FILE_NAME').AsString]) { Since we are fetching order by FILE_FLAGS, the LOG_0verflow will be last. It will only appear if there were named round robin, so we must close the parens first } @@ -1513,7 +1599,7 @@ begin if FirstFile then Buffer := Buffer + '(' else - Buffer := Buffer + Format(',%s ', [NEWLINE]); + Buffer := Buffer + Format(',%s ', [LineEnding]); FirstFile := false; Buffer := Buffer + Format('''%s''', [qryDB.FieldByName('RDB$FILE_NAME').AsString]); @@ -1547,9 +1633,9 @@ begin if not First then begin if NoDB then - FMetaData.Add(Format('%s */%s', [NEWLINE, NEWLINE])) + FMetaData.Add(Format('%s */%s', [LineEnding, LineEnding])) else - FMetaData.Add(Format('%s%s%s', [Term, NEWLINE, NEWLINE])); + FMetaData.Add(Format('%s%s%s', [Term, LineEnding, LineEnding])); end; finally qryDB.Free; @@ -1651,9 +1737,13 @@ var Result := Result + Format(' SEGMENT SIZE %d', [qryDomains.FieldByName('RDB$SEGMENT_LENGTH').AsInteger]); end //end_if else - if (qryDomains.FieldByName('RDB$FIELD_TYPE').AsInteger in [blr_text, blr_varying]) and - (not qryDomains.FieldByName('RDB$CHARACTER_LENGTH').IsNull) then - Result := Result + Format('(%d)', [qryDomains.FieldByName('RDB$FIELD_LENGTH').AsInteger]); + if (qryDomains.FieldByName('RDB$FIELD_TYPE').AsInteger in [blr_text, blr_varying]) then + begin + if not qryDomains.FieldByName('RDB$CHARACTER_LENGTH').IsNull then + Result := Result + Format('(%d)', [qryDomains.FieldByName('RDB$CHARACTER_LENGTH').AsInteger]) + else + Result := Result + Format('(%d)', [qryDomains.FieldByName('RDB$FIELD_LENGTH').AsInteger]); + end; { since the character set is part of the field type, display that information now. } @@ -1664,15 +1754,15 @@ var Result := GetArrayField(qryDomains.FieldByName('RDB$FIELD_SOURCE').AsString); if not qryDomains.FieldByName('RDB$DEFAULT_SOURCE').IsNull then - Result := Result + Format('%s%s %s', [NEWLINE, TAB, + Result := Result + Format('%s%s %s', [LineEnding, TAB, qryDomains.FieldByName('RDB$DEFAULT_SOURCE').AsString]); if not qryDomains.FieldByName('RDB$VALIDATION_SOURCE').IsNull then if Pos('CHECK', AnsiUpperCase(qryDomains.FieldByName('RDB$VALIDATION_SOURCE').AsString)) = 1 then - Result := Result + Format('%s%s %s', [NEWLINE, TAB, + Result := Result + Format('%s%s %s', [LineEnding, TAB, qryDomains.FieldByName('RDB$VALIDATION_SOURCE').AsString]) else - Result := Result + Format('%s%s /* %s */', [NEWLINE, TAB, + Result := Result + Format('%s%s /* %s */', [LineEnding, TAB, qryDomains.FieldByName('RDB$VALIDATION_SOURCE').AsString]); if qryDomains.FieldByName('RDB$NULL_FLAG').AsInteger = 1 then @@ -2041,7 +2131,7 @@ begin if First then begin FMEtaData.Add(Format('%s/* External Function declarations */%s', - [NEWLINE, NEWLINE])); + [LineEnding, LineEnding])); First := false; end; //end_if { Start new function declaration } @@ -2168,7 +2258,7 @@ begin FMetaData.Add(Format('ENTRY_POINT ''%s'' MODULE_NAME ''%s''%s%s%s', [qryFunctions.FieldByName('RDB$ENTRYPOINT').AsString, qryFunctions.FieldByName('RDB$MODULE_NAME').AsString, - Term, NEWLINE, NEWLINE])); + Term, LineEnding, LineEnding])); qryFunctions.Next; end; @@ -2184,7 +2274,8 @@ end; Functional description Re create all non-system generators } -procedure TIBExtract.ListGenerators(GeneratorName : String = ''); +procedure TIBExtract.ListGenerators(GeneratorName: String; + ExtractTypes: TExtractTypes); const GeneratorSQL = 'SELECT RDB$GENERATOR_NAME ' + @@ -2200,11 +2291,16 @@ const ' (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG <> 1) ' + 'ORDER BY RDB$GENERATOR_NAME'; + GeneratorValueSQL = + 'SELECT GEN_ID(%s,0) as GENERATORVALUE From RDB$Database'; + var qryGenerator : TIBSQL; + qryValue: TIBSQL; GenName : String; begin qryGenerator := TIBSQL.Create(FDatabase); + qryValue := TIBSQL.Create(FDatabase); try if GeneratorName = '' then qryGenerator.SQL.Text := GeneratorSQL @@ -2229,10 +2325,24 @@ begin FMetaData.Add(Format('CREATE SEQUENCE %s%s', [QuoteIdentifier(FDatabase.SQLDialect, GenName), Term])); + if etData in ExtractTypes then + begin + qryValue.SQL.Text := Format(GeneratorValueSQL,[GenName]); + qryValue.ExecQuery; + try + if not qryValue.EOF then + FMetaData.Add(Format('ALTER SEQUENCE %s RESTART WITH %d;', + [QuoteIdentifier(FDatabase.SQLDialect, GenName), + qryValue.FieldByName('GENERATORVALUE').AsInteger])); + finally + qryValue.Close; + end; + end; qryGenerator.Next; end; finally qryGenerator.Free; + qryValue.Free; end; end; @@ -2306,9 +2416,9 @@ begin if First then begin if ObjectName = '' then - FMetaData.Add(NEWLINE + '/* Index definitions for all user tables */' + NEWLINE) + FMetaData.Add(LineEnding + '/* Index definitions for all user tables */' + LineEnding) else - FMetaData.Add(NEWLINE + '/* Index definitions for ' + ObjectName + ' */' + NEWLINE); + FMetaData.Add(LineEnding + '/* Index definitions for ' + ObjectName + ' */' + LineEnding); First := false; end; //end_if @@ -2348,13 +2458,30 @@ end; procedure TIBExtract.ListViews(ViewName : String); const ViewSQL = + 'with recursive Views as ( ' + + ' Select RDB$RELATION_NAME, 1 as ViewLevel from RDB$RELATIONS ' + + ' Where RDB$RELATION_TYPE = 1 and RDB$SYSTEM_FLAG = 0 '+ + ' UNION ALL ' + + ' Select D.RDB$DEPENDED_ON_NAME, ViewLevel + 1 From RDB$DEPENDENCIES D ' + + ' JOIN Views on Views.RDB$RELATION_NAME = D.RDB$DEPENDENT_NAME ' + + ' and Views.RDB$RELATION_NAME <> D.RDB$DEPENDED_ON_NAME ' + + ' JOIN RDB$RELATIONS R On R.RDB$RELATION_NAME = D.RDB$DEPENDED_ON_NAME ' + + ')' + + 'SELECT R.RDB$RELATION_NAME, R.RDB$OWNER_NAME, R.RDB$VIEW_SOURCE FROM RDB$RELATIONS R ' + + 'JOIN ( ' + + 'Select RDB$RELATION_NAME, max(ViewLevel) as ViewLevel From Views ' + + 'Group By RDB$RELATION_NAME) A On A.RDB$RELATION_NAME = R.RDB$RELATION_NAME ' + + 'Where R.RDB$RELATION_TYPE = 1 and R.RDB$SYSTEM_FLAG = 0 '+ + 'Order by A.ViewLevel desc, R.RDB$RELATION_NAME asc'; + +{ 'SELECT RDB$RELATION_NAME, RDB$OWNER_NAME, RDB$VIEW_SOURCE ' + 'FROM RDB$RELATIONS ' + 'WHERE ' + ' (RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL) AND ' + ' NOT RDB$VIEW_BLR IS NULL AND ' + ' RDB$FLAGS = 1 ' + - 'ORDER BY RDB$RELATION_ID'; + 'ORDER BY RDB$RELATION_ID'; } ViewNameSQL = 'SELECT RDB$RELATION_NAME, RDB$OWNER_NAME, RDB$VIEW_SOURCE ' + @@ -2392,8 +2519,8 @@ begin while not qryView.Eof do begin SList.Add(Format('%s/* View: %s, Owner: %s */%s', - [NEWLINE, qryView.FieldByName('RDB$RELATION_NAME').AsString, - qryView.FieldByName('RDB$OWNER_NAME').AsString, NEWLINE])); + [LineEnding, qryView.FieldByName('RDB$RELATION_NAME').AsString, + qryView.FieldByName('RDB$OWNER_NAME').AsString, LineEnding])); SList.Add(Format('CREATE VIEW %s (', [QuoteIdentifier(FDatabase.SQLDialect, qryView.FieldByName('RDB$RELATION_NAME').AsString)])); @@ -2410,10 +2537,10 @@ begin SList.Strings[SList.Count - 1] := SList.Strings[SList.Count - 1] + ', '; end; qryColumns.Close; - SList.Text := SList.Text + Format(') AS%s', [NEWLINE]); + SList.Text := SList.Text + Format(') AS%s', [LineEnding]); if not qryView.FieldByName('RDB$VIEW_SOURCE').IsNull then SList.Text := SList.Text + qryView.FieldByName('RDB$VIEW_SOURCE').AsString; - SList.Text := SList.Text + Format('%s%s', [Term, NEWLINE]); + SList.Text := SList.Text + Format('%s%s', [Term, LineEnding]); FMetaData.AddStrings(SList); SList.Clear; qryView.Next; @@ -2442,7 +2569,7 @@ begin Used := true; end else - Result := Format(', %s ', [NEWLINE]); + Result := Format(', %s ', [LineEnding]); end; { @@ -2519,7 +2646,7 @@ begin end; FMetaData.Clear; case ObjectType of - eoDatabase : ExtractDDL(true, ''); + eoDatabase : ExtractDDL(true, '', etData in ExtractTypes); eoDomain : if etTable in ExtractTypes then ListDomains(ObjectName, etTable) @@ -2539,7 +2666,12 @@ begin if etCheck in ExtractTypes then ListCheck(ObjectName, etTable); if etTrigger in ExtractTypes then - ListTriggers(false,true,ObjectName, etTable); + begin + if etGrant in ExtractTypes then + ListTriggers(ObjectName, [etTable,etGrant]) + else + ListTriggers(ObjectName, [etTable]); + end; if etGrant in ExtractTypes then ShowGrants(ObjectName, Term); if etData in ExtractTypes then @@ -2554,20 +2686,40 @@ begin if ObjectName <> '' then begin if etTrigger in ExtractTypes then - ListTriggers(false,true,ObjectName, etTable); + begin + if etGrant in ExtractTypes then + ListTriggers(ObjectName, [etTable,etGrant]) + else + ListTriggers(ObjectName, [etTable]); + end; + if etGrant in ExtractTypes then + ShowGrants(ObjectName, Term); end; end; - eoProcedure : ListProcs(ObjectName); + eoProcedure : + begin + ListProcs(pdCreateProc,ObjectName,etGrant in ExtractTypes); + if (ObjectName <> '' ) and (etGrant in ExtractTypes) then + ShowGrants(ObjectName, Term); + end; eoFunction : ListFunctions(ObjectName); - eoGenerator : ListGenerators(ObjectName); + eoGenerator : ListGenerators(ObjectName,ExtractTypes); eoException : ListException(ObjectName); eoBLOBFilter : ListFilters(ObjectName); eoRole : ListRoles(ObjectName); eoTrigger : if etTable in ExtractTypes then - ListTriggers(false,true,ObjectName, etTable) + begin + if etGrant in ExtractTypes then + ListTriggers(ObjectName, [etTable,etGrant]) + else + ListTriggers(ObjectName, [etTable]) + end else - ListTriggers(false,true,ObjectName); + if etGrant in ExtractTypes then + ListTriggers(ObjectName,[etTrigger,etGrant]) + else + ListTriggers(ObjectName); eoForeign : if etTable in ExtractTypes then ListForeign(ObjectName, etTable) @@ -2657,251 +2809,194 @@ end; procedure TIBExtract.ShowGrants(MetaObject: String; Terminator: String); const - { This query only finds tables, eliminating owner privileges } - OwnerPrivSQL = - 'SELECT PRV.RDB$USER, PRV.RDB$GRANT_OPTION, PRV.RDB$FIELD_NAME, ' + - ' PRV.RDB$USER_TYPE, PRV.RDB$PRIVILEGE ' + - 'FROM RDB$USER_PRIVILEGES PRV, RDB$RELATIONS REL ' + - 'WHERE ' + - ' PRV.RDB$RELATION_NAME = :METAOBJECT AND ' + - ' REL.RDB$RELATION_NAME = :METAOBJECT AND ' + - ' PRV.RDB$PRIVILEGE <> ''M'' AND ' + - ' REL.RDB$OWNER_NAME <> PRV.RDB$USER ' + - 'ORDER BY PRV.RDB$USER, PRV.RDB$FIELD_NAME, PRV.RDB$GRANT_OPTION'; - - ProcPrivSQL = - 'SELECT PRV.RDB$USER, PRV.RDB$GRANT_OPTION, PRV.RDB$FIELD_NAME, ' + - ' PRV.RDB$USER_TYPE, PRV.RDB$PRIVILEGE, PRV.RDB$RELATION_NAME ' + - 'FROM RDB$USER_PRIVILEGES PRV, RDB$PROCEDURES PRC ' + - 'where ' + - ' PRV.RDB$OBJECT_TYPE = 5 AND ' + - ' PRV.RDB$RELATION_NAME = :METAOBJECT AND ' + - ' PRC.RDB$PROCEDURE_NAME = :METAOBJECT AND ' + - ' PRV.RDB$PRIVILEGE = ''X'' AND ' + - ' PRC.RDB$OWNER_NAME <> PRV.RDB$USER ' + - 'ORDER BY PRV.RDB$USER, PRV.RDB$FIELD_NAME, PRV.RDB$GRANT_OPTION'; - - RolePrivSQL = - 'SELECT * FROM RDB$USER_PRIVILEGES ' + - 'WHERE ' + - ' RDB$OBJECT_TYPE = 13 AND ' + - ' RDB$USER_TYPE = 8 AND ' + - ' RDB$RELATION_NAME = :METAOBJECT AND ' + - ' RDB$PRIVILEGE = ''M'' ' + - 'ORDER BY RDB$USER'; + GrantsBaseSelect = + 'Select Trim(RDB$USER) as RDB$USER,List("Privileges") as Privileges, '+ + 'coalesce(RDB$GRANT_OPTION,0) as RDB$GRANT_OPTION,METAOBJECTNAME, '+ + 'RDB$USER_TYPE, RDB$OBJECT_TYPE, '+ + 'case RDB$OBJECT_TYPE '+ + 'When 0 then ''TABLE'' '+ + 'When 5 then ''PROCEDURE'' '+ + 'When 7 then ''EXCEPTION'' '+ + 'When 11 then ''CHARACTER SET'' '+ + 'When 14 then ''GENERATOR'' '+ + 'ELSE NULL END as OBJECT_TYPE_NAME, '+ + 'case RDB$USER_TYPE '+ + 'When 5 then ''PROCEDURE'' '+ + 'When 2 then ''TRIGGER'' '+ + 'When 8 then ''USER'' '+ + 'When 13 then ''ROLE'' '+ + 'ELSE NULL END as USER_TYPE_NAME, '+ + 'case '+ + 'When coalesce(RDB$GRANT_OPTION,0) <> 0 and RDB$USER_TYPE = 13 then '' WITH ADMIN OPTION'' '+ + 'When coalesce(RDB$GRANT_OPTION,0) <> 0 and RDB$USER_TYPE <> 13 then '' WITH GRANT OPTION'' '+ + 'ELSE '''' End as GRANTOPTION '+ + 'From ( '+ + 'Select PR.RDB$USER,PR.RDB$RELATION_NAME as METAOBJECTNAME, LIST(DISTINCT Trim(Case PR.RDB$PRIVILEGE '+ + 'When ''X'' then ''EXECUTE'' '+ + 'When ''S'' then ''SELECT'' '+ + 'When ''U'' then ''UPDATE'' '+ + 'When ''D'' then ''DELETE'' '+ + 'When ''R'' then ''REFERENCES'' '+ + 'When ''G'' then ''USAGE'' '+ + 'When ''I'' then ''INSERT'' end )) as "Privileges", '+ + 'PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE,OW.RDB$OWNER_NAME '+ + 'FROM RDB$USER_PRIVILEGES PR '+ + 'JOIN ObjectOwners OW On OW.METAOBJECTNAME = PR.RDB$RELATION_NAME and OW.ObjectType = PR.RDB$OBJECT_TYPE '+ + 'Where PR.RDB$PRIVILEGE <> ''M'' and (PR.RDB$PRIVILEGE <> ''U'' or PR.RDB$FIELD_NAME is null) '+ + 'Group By PR.RDB$USER,PR.RDB$RELATION_NAME,PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE,OW.RDB$OWNER_NAME '+ + 'UNION '+ + 'Select PR.RDB$USER,PR.RDB$RELATION_NAME, ''Update('' || List(Trim(PR.RDB$FIELD_NAME)) || '')'', '+ + 'PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE, OW.RDB$OWNER_NAME '+ + 'FROM RDB$USER_PRIVILEGES PR '+ + 'JOIN ObjectOwners OW On OW.METAOBJECTNAME = PR.RDB$RELATION_NAME and OW.ObjectType = PR.RDB$OBJECT_TYPE '+ + 'Where PR.RDB$PRIVILEGE = ''U'' and PR.RDB$FIELD_NAME is not null '+ + 'Group By PR.RDB$USER,PR.RDB$RELATION_NAME,PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE, OW.RDB$OWNER_NAME) '+ + 'Where METAOBJECTNAME = :METAOBJECTNAME and RDB$USER <> RDB$OWNER_NAME '+ + 'Group By RDB$USER,RDB$GRANT_OPTION, RDB$USER_TYPE, RDB$OBJECT_TYPE,METAOBJECTNAME '+ + 'ORDER BY RDB$USER, RDB$OBJECT_TYPE'; + + GrantsSQL12 = + 'with ObjectOwners As ( '+ + 'Select RDB$RELATION_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 0 as ObjectType '+ + 'From RDB$RELATIONS '+ + 'UNION '+ + 'Select RDB$PROCEDURE_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 5 as ObjectType '+ + 'From RDB$PROCEDURES '+ + 'UNION '+ + 'Select RDB$EXCEPTION_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 7 as ObjectType '+ + 'From RDB$EXCEPTIONS '+ + 'UNION '+ + 'Select RDB$GENERATOR_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 14 as ObjectType '+ + 'From RDB$GENERATORS '+ + 'UNION '+ + 'Select RDB$CHARACTER_SET_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 11 as ObjectType '+ + 'From RDB$CHARACTER_SETS '+ + ') '+ GrantsBaseSelect; + + GrantsSQL = + 'with ObjectOwners As ( '+ + 'Select RDB$RELATION_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 0 as ObjectType '+ + 'From RDB$RELATIONS '+ + 'UNION '+ + 'Select RDB$PROCEDURE_NAME as METAOBJECTNAME, RDB$OWNER_NAME, 5 as ObjectType '+ + 'From RDB$PROCEDURES '+ + 'UNION '+ + 'Select RDB$EXCEPTION_NAME as METAOBJECTNAME, ''SYSDBA'', 7 as ObjectType '+ + 'From RDB$EXCEPTIONS '+ + 'UNION '+ + 'Select RDB$GENERATOR_NAME as METAOBJECTNAME, ''SYSDBA'', 14 as ObjectType '+ + 'From RDB$GENERATORS '+ + 'UNION '+ + 'Select RDB$CHARACTER_SET_NAME as METAOBJECTNAME, ''SYSDBA'', 11 as ObjectType '+ + 'From RDB$CHARACTER_SETS '+ + ') '+ GrantsBaseSelect; -var - PrevUser, PrevField, WithOption, - PrivString, ColString, UserString, - FieldName, User : String; - c : Char; - PrevOption, PrivFlags, GrantOption : Integer; - First, PrevFieldNull : Boolean; - qryOwnerPriv : TIBSQL; - - { Given a bit-vector of privileges, turn it into a - string list. } - function MakePrivString(cflags : Integer) : String; - var - i : Integer; - begin - Result := ''; - for i := Low(PrivTypes) to High(PrivTypes) do - begin - if (cflags and PrivTypes[i].PrivFlag) <> 0 then - begin - if Result <> '' then - Result := Result + ', '; - Result := Result + PrivTypes[i].PrivString; - end; //end_if - end; //end_for - end; //end_fcn MakePrivDtring +var qryOwnerPriv : TIBSQL; begin if MetaObject = '' then exit; - First := true; - PrevOption := -1; - PrevUser := ''; - PrivString := ''; - ColString := ''; - WithOption := ''; - PrivFlags := 0; - PrevFieldNull := false; - PrevField := ''; - qryOwnerPriv := TIBSQL.Create(FDatabase); try - qryOwnerPriv.SQL.Text := OwnerPrivSQL; - qryOwnerPriv.Params.ByName('metaobject').AsString := MetaObject; + if FDatabaseInfo.ODSMajorVersion >= ODS_VERSION12 then + qryOwnerPriv.SQL.Text := GrantsSQL12 + else + qryOwnerPriv.SQL.Text := GrantsSQL; + qryOwnerPriv.Params.ByName('METAOBJECTNAME').AsString := MetaObject; qryOwnerPriv.ExecQuery; while not qryOwnerPriv.Eof do begin - { Sometimes grant options are null, sometimes 0. Both same } - if qryOwnerPriv.FieldByName('RDB$GRANT_OPTION').IsNull then - GrantOption := 0 - else - GrantOption := qryOwnerPriv.FieldByName('RDB$GRANT_OPTION').AsInteger; - - if qryOwnerPriv.FieldByName('RDB$FIELD_NAME').IsNull then - FieldName := '' - else - FieldName := qryOwnerPriv.FieldByName('RDB$FIELD_NAME').AsString; - - User := Trim(qryOwnerPriv.FieldByName('RDB$USER').AsString); - { Print a new grant statement for each new user or change of option } - - if ((PrevUser <> '') and (PrevUser <> User)) or - ((Not First) and - (PrevFieldNull <> qryOwnerPriv.FieldByName('RDB$FIELD_NAME').IsNull)) or - ((not PrevFieldNull) and (PrevField <> FieldName)) or - ((PrevOption <> -1) and (PrevOption <> GrantOption)) then - begin - PrivString := MakePrivString(PrivFlags); - - First := false; - FMetaData.Add(Format('GRANT %s%s ON %s TO %s%s%s', [PrivString, - ColString, QuoteIdentifier(FDatabase.SQLDialect, MetaObject), - UserString, WithOption, Terminator])); - { re-initialize strings } - - PrivString := ''; - WithOption := ''; - ColString := ''; - PrivFlags := 0; - end; //end_if - - PrevUser := User; - PrevOption := GrantOption; - PrevFieldNull := qryOwnerPriv.FieldByName('RDB$FIELD_NAME').IsNull; - PrevField := FieldName; - - case qryOwnerPriv.FieldByName('RDB$USER_TYPE').AsInteger of - obj_relation, - obj_view, - obj_trigger, - obj_procedure, - obj_sql_role: - UserString := QuoteIdentifier(FDatabase.SQLDialect, User); - else - UserString := User; - end; //end_case - - case qryOwnerPriv.FieldByName('RDB$USER_TYPE').AsInteger of - obj_view : - UserString := 'VIEW ' + UserString; - obj_trigger : - UserString := 'TRIGGER '+ UserString; - obj_procedure : - UserString := 'PROCEDURE ' + UserString; - end; //end_case - - c := qryOwnerPriv.FieldByName('RDB$PRIVILEGE').AsString[1]; - - case c of - 'S' : PrivFlags := PrivFlags or priv_SELECT; - 'I' : PrivFlags := PrivFlags or priv_INSERT; - 'U' : PrivFlags := PrivFlags or priv_UPDATE; - 'D' : PrivFlags := PrivFlags or priv_DELETE; - 'R' : PrivFlags := PrivFlags or priv_REFERENCES; - 'X' : ; - { Execute should not be here -- special handling below } - else - PrivFlags := PrivFlags or priv_UNKNOWN; - end; //end_switch - - { Column level privileges for update only } - - if FieldName = '' then - ColString := '' - else - ColString := Format(' (%s)', [QuoteIdentifier(FDatabase.SQLDialect, FieldName)]); - - if GrantOption <> 0 then - WithOption := ' WITH GRANT OPTION'; - + FMetaData.Add(Format('GRANT %s ON %s "%s" TO %s "%s" %s%s', [ + qryOwnerPriv.FieldByName('Privileges').AsString, + qryOwnerPriv.FieldByName('OBJECT_TYPE_NAME').AsString, + qryOwnerPriv.FieldByName('METAOBJECTNAME').AsString, + qryOwnerPriv.FieldByName('USER_TYPE_NAME').AsString, + qryOwnerPriv.FieldByName('RDB$USER').AsString, + qryOwnerPriv.FieldByName('GRANTOPTION').AsString, + Terminator])); qryOwnerPriv.Next; end; - { Print last case if there was anything to print } - if PrevOption <> -1 then - begin - PrivString := MakePrivString(PrivFlags); - First := false; - FMetaData.Add(Format('GRANT %s%s ON %s TO %s%s%s', [PrivString, - ColString, QuoteIdentifier(FDatabase.SQLDialect, MetaObject), - UserString, WithOption, Terminator])); - { re-initialize strings } - end; //end_if qryOwnerPriv.Close; + finally + qryOwnerPriv.Free; + end; +end; - if First then - begin - { Part two is for stored procedures only } - qryOwnerPriv.SQL.Text := ProcPrivSQL; - qryOwnerPriv.Params.ByName('metaobject').AsString := MetaObject; - qryOwnerPriv.ExecQuery; - while not qryOwnerPriv.Eof do - begin - First := false; - User := Trim(qryOwnerPriv.FieldByName('RDB$USER').AsString); - - case qryOwnerPriv.FieldByName('RDB$USER_TYPE').AsInteger of - obj_relation, - obj_view, - obj_trigger, - obj_procedure, - obj_sql_role: - UserString := QuoteIdentifier(FDatabase.SQLDialect, User); - else - UserString := User; - end; //end_case - case qryOwnerPriv.FieldByName('RDB$USER_TYPE').AsInteger of - obj_view : - UserString := 'VIEW ' + UserString; - obj_trigger : - UserString := 'TRIGGER '+ UserString; - obj_procedure : - UserString := 'PROCEDURE ' + UserString; - end; //end_case +procedure TIBExtract.ShowGrantsTo(MetaObject: String; ObjectType: integer; Terminator: String); +const + GrantsSQL = + 'Select Trim(RDB$USER) as RDB$USER,List("Privileges") as Privileges, '+ + 'coalesce(RDB$GRANT_OPTION,0) as RDB$GRANT_OPTION,METAOBJECTNAME, '+ + 'RDB$USER_TYPE, RDB$OBJECT_TYPE, '+ + 'case RDB$OBJECT_TYPE '+ + 'When 0 then ''TABLE'' '+ + 'When 5 then ''PROCEDURE'' '+ + 'When 7 then ''EXCEPTION'' '+ + 'When 11 then ''CHARACTER SET'' '+ + 'ELSE NULL END as OBJECT_TYPE_NAME, '+ + 'case RDB$USER_TYPE '+ + 'When 5 then ''PROCEDURE'' '+ + 'When 2 then ''TRIGGER'' '+ + 'When 8 then ''USER'' '+ + 'When 13 then ''ROLE'' '+ + 'ELSE NULL END as USER_TYPE_NAME, '+ + 'case '+ + 'When coalesce(RDB$GRANT_OPTION,0) <> 0 and RDB$USER_TYPE = 13 then '' WITH ADMIN OPTION'' '+ + 'When coalesce(RDB$GRANT_OPTION,0) <> 0 and RDB$USER_TYPE <> 13 then '' WITH GRANT OPTION'' '+ + 'ELSE '''' End as GRANTOPTION '+ + 'From ( '+ + 'Select PR.RDB$USER,PR.RDB$RELATION_NAME as METAOBJECTNAME, LIST(DISTINCT Trim(Case PR.RDB$PRIVILEGE '+ + 'When ''X'' then ''EXECUTE'' '+ + 'When ''S'' then ''SELECT'' '+ + 'When ''U'' then ''UPDATE'' '+ + 'When ''D'' then ''DELETE'' '+ + 'When ''R'' then ''REFERENCES'' '+ + 'When ''G'' then ''USAGE'' '+ + 'When ''I'' then ''INSERT'' end )) as "Privileges", '+ + 'PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE '+ + 'FROM RDB$USER_PRIVILEGES PR '+ + 'Where PR.RDB$PRIVILEGE <> ''M'' and (PR.RDB$PRIVILEGE <> ''U'' or PR.RDB$FIELD_NAME is null) '+ + 'Group By PR.RDB$USER,PR.RDB$RELATION_NAME,PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE '+ + 'UNION '+ + 'Select PR.RDB$USER,PR.RDB$RELATION_NAME, ''Update('' || List(Trim(PR.RDB$FIELD_NAME)) || '')'', '+ + 'PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE '+ + 'FROM RDB$USER_PRIVILEGES PR '+ + 'Where PR.RDB$PRIVILEGE = ''U'' and PR.RDB$FIELD_NAME is not null '+ + 'Group By PR.RDB$USER,PR.RDB$RELATION_NAME,PR.RDB$GRANT_OPTION, PR.RDB$USER_TYPE, PR.RDB$OBJECT_TYPE) '+ + 'Where RDB$USER = :METAOBJECTNAME and RDB$USER_TYPE = :USERTYPE '+ + 'Group By RDB$USER,RDB$GRANT_OPTION, RDB$USER_TYPE, RDB$OBJECT_TYPE, METAOBJECTNAME '+ + 'ORDER BY METAOBJECTNAME'; - if qryOwnerPriv.FieldByName('RDB$GRANT_OPTION').AsInteger = 1 then - WithOption := ' WITH GRANT OPTION' - else - WithOption := ''; +var qryOwnerPriv : TIBSQL; - FMetaData.Add(Format('GRANT EXECUTE ON PROCEDURE %s TO %s%s%s', - [QuoteIdentifier(FDatabase.SQLDialect, MetaObject), UserString, - WithOption, terminator])); +begin + if MetaObject = '' then + exit; - qryOwnerPriv.Next; - end; - qryOwnerPriv.Close; - end; - if First then + qryOwnerPriv := TIBSQL.Create(FDatabase); + try + qryOwnerPriv.SQL.Text := GrantsSQL; + qryOwnerPriv.Params.ByName('METAOBJECTNAME').AsString := MetaObject; + qryOwnerPriv.Params.ByName('USERTYPE').AsInteger := ObjectType; + qryOwnerPriv.ExecQuery; + while not qryOwnerPriv.Eof do begin - qryOwnerPriv.SQL.Text := RolePrivSQL; - qryOwnerPriv.Params.ByName('metaobject').AsString := MetaObject; - qryOwnerPriv.ExecQuery; - while not qryOwnerPriv.Eof do - begin - if qryOwnerPriv.FieldByName('RDB$GRANT_OPTION').AsInteger = 1 then - WithOption := ' WITH ADMIN OPTION' - else - WithOption := ''; - - FMetaData.Add(Format('GRANT %s TO %s%s%s', - [QuoteIdentifier(FDatabase.SQLDialect, qryOwnerPriv.FieldByName('RDB$RELATION_NAME').AsString), - qryOwnerPriv.FieldByName('RDB$USER_NAME').AsString, - WithOption, terminator])); - - qryOwnerPriv.Next; - end; + FMetaData.Add(Format('GRANT %s ON %s "%s" TO %s "%s" %s%s', [ + qryOwnerPriv.FieldByName('Privileges').AsString, + qryOwnerPriv.FieldByName('OBJECT_TYPE_NAME').AsString, + qryOwnerPriv.FieldByName('METAOBJECTNAME').AsString, + qryOwnerPriv.FieldByName('USER_TYPE_NAME').AsString, + qryOwnerPriv.FieldByName('RDB$USER').AsString, + qryOwnerPriv.FieldByName('GRANTOPTION').AsString, + Terminator])); + qryOwnerPriv.Next; end; qryOwnerPriv.Close; finally qryOwnerPriv.Free; end; + FMetaData.Add(''); end; { ShowGrantRoles @@ -2941,7 +3036,7 @@ begin WithOption := ''; FMetaData.Add(Format('GRANT %s TO %s%s%s%s', [ QuoteIdentifier(FDatabase.SQLDialect, qryRole.FieldByName('RDB$RELATION_NAME').AsString), - UserString, WithOption, Terminator, NEWLINE])); + UserString, WithOption, Terminator, LineEnding])); qryRole.Next; end; @@ -3021,9 +3116,13 @@ var end; break; end; - if (qryHeader.FieldByName('RDB$FIELD_TYPE').AsInteger in [blr_text, blr_varying]) and - (not qryHeader.FieldByName('RDB$CHARACTER_LENGTH').IsNull) then - Result := Result + Format('(%d)', [qryHeader.FieldByName('RDB$FIELD_LENGTH').AsInteger]); + if (qryHeader.FieldByName('RDB$FIELD_TYPE').AsInteger in [blr_text, blr_varying]) then + begin + if not qryHeader.FieldByName('RDB$CHARACTER_LENGTH').IsNull then + Result := Result + Format('(%d)', [qryHeader.FieldByName('RDB$CHARACTER_LENGTH').AsInteger]) + else + Result := Result + Format('(%d)', [qryHeader.FieldByName('RDB$FIELD_LENGTH').AsInteger]); + end; { Show international character sets and collations } @@ -3084,7 +3183,7 @@ begin if FirstTime then begin FirstTime := false; - FMetaData.Add('RETURNS' + NEWLINE + '('); + FMetaData.Add('RETURNS' + LineEnding + '('); end; Line := FormatParamStr; @@ -3119,66 +3218,66 @@ end; procedure TIBExtract.ListData(ObjectName: String); const - SelectSQL = 'SELECT * FROM %s'; -var - qrySelect : TIBSQL; - Line : String; - i : Integer; + SelectFieldListSQL = 'Select List(RDB$FIELD_NAME) From ( '+ + 'Select RF.RDB$FIELD_NAME From RDB$RELATION_FIELDS RF '+ + 'JOIN RDB$FIELDS F On F.RDB$FIELD_NAME = RF.RDB$FIELD_SOURCE '+ + 'Where F.RDB$COMPUTED_BLR is NULL and RF.RDB$RELATION_NAME = Upper(:Relation) '+ + 'Order by RF.RDB$FIELD_POSITION asc)'; + + TableSQL = + 'SELECT * FROM RDB$RELATIONS ' + + 'WHERE ' + + ' (RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL) AND ' + + ' RDB$VIEW_BLR IS NULL ' + + 'ORDER BY RDB$RELATION_NAME'; + +var FieldList: string; + begin - qrySelect := TIBSQL.Create(FDatabase); - try - qrySelect.SQL.Text := Format(SelectSQL, - [QuoteIdentifier(FDatabase.SQLDialect, ObjectName)]); - qrySelect.ExecQuery; - while not qrySelect.Eof do - begin - Line := 'INSERT INTO ' + QuoteIdentifier(FDatabase.SQLDialect, ObjectName) + ' ('; - for i := 0 to qrySelect.FieldCount - 1 do - if (qrySelect.Fields[i].SQLType <> SQL_ARRAY) and - (qrySelect.Fields[i].SQLType <> SQL_BLOB) then - begin - Line := Line + QuoteIdentifier(FDatabase.SQLDialect, qrySelect.Fields[i].Name); - if i <> (qrySelect.FieldCount - 1) then - Line := Line + ', '; - end; - Line := Line + ') VALUES ('; - for i := 0 to qrySelect.FieldCount - 1 do + if ObjectName = '' then {List all} + begin + with TIBSQL.Create(self) do + try + Database := FDatabase; + SQL.Text := TableSQL; + ExecQuery; + while not EOF do begin - if qrySelect.Fields[i].IsNull and - (qrySelect.Fields[i].SQLType <> SQL_ARRAY) and - (qrySelect.Fields[i].SQLType <> SQL_BLOB) then - begin - Line := Line + 'NULL'; - if i <> (qrySelect.FieldCount - 1) then - Line := Line + ', '; - end - else - case qrySelect.Fields[i].SQLType of - SQL_TEXT, SQL_VARYING, SQL_TYPE_DATE, - SQL_TYPE_TIME, SQL_TIMESTAMP : - begin - Line := Line + QuotedStr(qrySelect.Fields[i].AsString); - if i <> (qrySelect.FieldCount - 1) then - Line := Line + ', '; - end; - SQL_SHORT, SQL_LONG, SQL_INT64, - SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT, SQL_BOOLEAN: - begin - Line := Line + qrySelect.Fields[i].AsString; - if i <> (qrySelect.FieldCount - 1) then - Line := Line + ', '; - end; - SQL_ARRAY, SQL_BLOB : ; - else - IBError(ibxeInvalidDataConversion, [nil]); - end; + ListData(Trim(FieldByName('RDB$RELATION_NAME').AsString)); + Next; end; - Line := Line + ')' + Term; - FMetaData.Add(Line); - qrySelect.Next; + finally + Free; + end; + end + else + begin + FieldList := '*'; + with TIBSQL.Create(self) do + try + Database := FDatabase; + SQL.Text := SelectFieldListSQL; + Params[0].AsString := ObjectName; + ExecQuery; + try + if not EOF then + FieldList := Fields[0].AsString; + finally + Close; + end; + finally + Free + end; + + with TIBInsertStmtsOut.Create(self) do + try + Database := FDatabase; + DataOut(Format('Select %s From %s',[FieldList,QuoteIdentifier(FDatabase.SQLDialect, ObjectName)]), + Add2MetaData); + FMetaData.Add('COMMIT;'); + finally + Free end; - finally - qrySelect.Free; end; end;