ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBExtract.pas
(Generate patch)

Comparing ibx/trunk/runtime/IBExtract.pas (file contents):
Revision 39 by tony, Tue May 17 08:14:52 2016 UTC vs.
Revision 45 by tony, Tue Dec 6 10:33:46 2016 UTC

# Line 23 | Line 23
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}
29 {$IF FPC_FULLVERSION >= 20700 }
39   {$codepage UTF8}
31 {$ENDIF}
40  
41   interface
42  
# Line 39 | Line 47 | uses
47    unix,
48   {$ENDIF}
49    SysUtils, Classes, IBDatabase, IBDatabaseInfo,
50 <  IBSQL, IBUtils, IBHeader, IB, IBIntf;
50 >  IBSQL, IBUtils, IBHeader, IB;
51  
52   type
53    TExtractObjectTypes =
# Line 53 | Line 61 | type
61  
62    TExtractTypes = Set of TExtractType;
63  
64 +  { TIBExtract }
65 +
66    TIBExtract = class(TComponent)
67    private
68      FDatabase : TIBDatabase;
# Line 64 | Line 74 | type
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;
# Line 72 | Line 83 | type
83      procedure GetProcedureArgs(Proc : String);
84    protected
85      function ExtractDDL(Flag : Boolean; TableName : String) : Boolean;
86 <    function ExtractListTable(RelationName, NewName : String; DomainFlag : Boolean) : 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(ObjectName : String = ''; ExtractType : TExtractType = etTrigger);
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 = '');
# Line 124 | Line 135 | type
135      PrivString : String;
136    end;
137  
138 <  TSQLTypes = Array[0..13] of TSQLType;
138 >  TSQLTypes = Array[0..14] of TSQLType;
139  
140   const
141  
# Line 158 | Line 169 | const
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'));
172 >    (SqlType : blr_int64; TypeName : 'INT64'),
173 >    (SqlType : blr_bool; TypeName : 'BOOLEAN'));
174  
175    SubTypes : Array[0..8] of String = (
176      'UNKNOWN',                  { NTX: keyword }
# Line 171 | Line 183 | const
183      'TRANSACTION_DESCRIPTION',  { NTX: keyword }
184      'EXTERNAL_FILE_DESCRIPTION');       { NTX: keyword }
185  
174  TriggerTypes : Array[0..6] of String = (
175    '',
176    'BEFORE INSERT',                    { NTX: keyword }
177    'AFTER INSERT',                             { NTX: keyword }
178    'BEFORE UPDATE',                    { NTX: keyword }
179    'AFTER UPDATE',                             { NTX: keyword }
180    'BEFORE DELETE',                    { NTX: keyword }
181    'AFTER DELETE');                    { NTX: keyword }
182
186    IntegralSubtypes : Array[0..2] of String = (
187      'UNKNOWN',                  { Defined type, NTX: keyword }
188      'NUMERIC',                  { NUMERIC, NTX: keyword }
# Line 230 | Line 233 | const
233  
234   implementation
235  
236 + uses FBMessages;
237 +
238   const
239    NEWLINE = #13#10;
240    TERM = ';';
# Line 273 | Line 278 | var
278    qryArray : TIBSQL;
279   begin
280    qryArray := TIBSQL.Create(FDatabase);
281 <  Result := '[';
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 <  while not qryArray.Eof do
288 >  if not qryArray.Eof then
289    begin
290 <    if (qryArray.FieldByName('RDB$DIMENSION').AsInteger > 0) then
291 <      Result := Result + ', ';
292 <    Result := Result + qryArray.FieldByName('RDB$LOWER_BOUND').AsString + ':' +
293 <           qryArray.FieldByName('RDB$UPPER_BOUND').AsString;
294 <    qryArray.Next;
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  
292  Result := Result + '] ';
302    qryArray.Free;
303    
304   end;
# Line 329 | Line 338 | begin
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
# Line 355 | Line 365 | begin
365      ListViews;
366      ListCheck;
367      ListException;
368 +    ListTriggers(false,false);
369      ListProcs;
370 <    ListTriggers;
370 >    ListTriggers(true,true);
371      ListGrants;
372    end;
373  
# Line 378 | Line 389 | end;
389          domain_flag -- extract needed domains before the table }
390  
391   function TIBExtract.ExtractListTable(RelationName, NewName: String;
392 <  DomainFlag: Boolean) : Boolean;
392 >  DomainFlag: Boolean): Boolean;
393   const
394    TableListSQL =
395      'SELECT * FROM RDB$RELATIONS REL JOIN RDB$RELATION_FIELDS RFR ON ' + {Do Not Localize}
# Line 405 | Line 416 | const
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 : TIBSQL;
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 := '';
# Line 426 | Line 442 | begin
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;
# Line 433 | Line 450 | begin
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('CREATE TABLE %s ', [QuoteIdentifier(FDatabase.SQLDialect,NewName)]))
468 >        FMetaData.Add(Format('%s %s ', [CreateTable,QuoteIdentifier(FDatabase.SQLDialect,NewName)]))
469        else
470 <        FMetaData.Add(Format('CREATE TABLE %s ', [QuoteIdentifier(FDatabase.SQLDialect,RelationName)]));
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)]));
# Line 546 | Line 569 | begin
569  
570            { Catch arrays after printing the type  }
571  
572 <          if not qryTables.FieldByName('RDB$DIMENSIONS').IsNull then
573 <            Column := column + GetArrayField(qryTables.FieldByName('RDB$FIELD_NAME').AsString);
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
# Line 578 | Line 601 | begin
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 }
# Line 664 | Line 700 | begin
700        qryRelConstraints.Next;
701      end;
702      if ValidRelation then
703 <      FMetaData.Add(')' + Term);
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  
# Line 743 | Line 785 | var
785    CharSetSQL : TIBSQL;
786    DidActivate : Boolean;
787   begin
788 +  Result := '';
789    if not FTransaction.Active then
790    begin
791      FTransaction.StartTransaction;
# Line 799 | Line 842 | end;
842     Functional description
843          returns the list of columns in an index. }
844  
845 < function TIBExtract.GetIndexSegments(IndexName: String): String;
845 > function TIBExtract.GetIndexSegments(indexname: String): String;
846   const
847    IndexNamesSQL =
848      'SELECT * FROM RDB$INDEX_SEGMENTS SEG ' +
# Line 836 | Line 879 | 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.
# Line 1040 | Line 1128 | end;
1128          Lists triggers in general on non-system
1129          tables with sql source only. }
1130  
1131 < procedure TIBExtract.ListTriggers(ObjectName : String; ExtractType : TExtractType);
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 }
# Line 1126 | Line 1215 | begin
1215        if qryTriggers.FieldByName('RDB$FLAGS').AsInteger <> 1 then
1216          SList.Add('/* ');
1217  
1218 <      SList.Add(Format('CREATE TRIGGER %s FOR %s %s%s %s POSITION %d',
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 <           TriggerTypes[qryTriggers.FieldByName('RDB$TRIGGER_TYPE').AsInteger],
1225 >           GetTriggerType(qryTriggers.FieldByName('RDB$TRIGGER_TYPE').AsInteger),
1226             qryTriggers.FieldByName('RDB$TRIGGER_SEQUENCE').AsInteger]));
1227 <      if not qryTriggers.FieldByName('RDB$TRIGGER_SOURCE').IsNull then
1227 >      if IncludeBody and not qryTriggers.FieldByName('RDB$TRIGGER_SOURCE').IsNull then
1228          SList.Text := SList.Text +
1229 <              qryTriggers.FieldByName('RDB$TRIGGER_SOURCE').AsString;
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(' */');
# Line 1243 | Line 1337 | 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 != '' ''';
1340 >    '  AND DBP.RDB$CHARACTER_SET_NAME <> '' ''';
1341  
1342    FilesSQL =
1343      'select * from RDB$FILES ' +
# Line 1260 | Line 1354 | var
1354    FileFlags, FileLength, FileSequence, FileStart : Integer;
1355  
1356    function GetLongDatabaseInfo(DatabaseInfoCommand: Integer): LongInt;
1263  var
1264    local_buffer: array[0..IBLocalBufferLength - 1] of Char;
1265    length: Integer;
1266    _DatabaseInfoCommand: Char;
1357    begin
1358 <    _DatabaseInfoCommand := Char(DatabaseInfoCommand);
1359 <    FDatabaseInfo.Call(isc_database_info(StatusVector, @FDatabase.Handle, 1, @_DatabaseInfoCommand,
1360 <                           IBLocalBufferLength, local_buffer), True);
1361 <    length := isc_vax_integer(@local_buffer[1], 2);
1362 <    result := isc_vax_integer(@local_buffer[3], length);
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;
1366 >  NoDb := FALSE;
1367    First := TRUE;
1368    FirstFile := TRUE;
1369    HasWal := FALSE;
# Line 1295 | Line 1385 | begin
1385      qryDB.SQL.Text := CharInfoSQL;
1386      qryDB.ExecQuery;
1387  
1388 <    Buffer := Format(' DEFAULT CHARACTER SET %s',
1389 <      [qryDB.FieldByName('RDB$CHARACTER_SET_NAME').AsString]);
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 + ' */'
1392 >      Buffer := Buffer + Term + ' */'
1393      else
1394        Buffer := Buffer + Term;
1395      FMetaData.Add(Buffer);
# Line 1570 | Line 1661 | var
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(FieldName);
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,
# Line 1897 | Line 1988 | end;
1988   procedure TIBExtract.ListFunctions(FunctionName : String = '');
1989   const
1990    FunctionSQL =
1991 <    'SELECT * FROM RDB$FUNCTIONS ' +
1991 >    'SELECT * FROM RDB$FUNCTIONS WHERE RDB$SYSTEM_FLAG = 0 ' +
1992      'ORDER BY RDB$FUNCTION_NAME';
1993  
1994    FunctionNameSQL =
# Line 2135 | Line 2226 | begin
2226          qryGenerator.Next;
2227          continue;
2228        end;
2229 <      FMetaData.Add(Format('CREATE GENERATOR %s%s',
2229 >      FMetaData.Add(Format('CREATE SEQUENCE %s%s',
2230          [QuoteIdentifier(FDatabase.SQLDialect, GenName),
2231           Term]));
2232        qryGenerator.Next;
# Line 2448 | Line 2539 | begin
2539          if etCheck in ExtractTypes then
2540            ListCheck(ObjectName, etTable);
2541          if etTrigger in ExtractTypes then
2542 <          ListTriggers(ObjectName, etTable);
2542 >          ListTriggers(false,true,ObjectName, etTable);
2543          if etGrant in ExtractTypes then
2544            ShowGrants(ObjectName, Term);
2545          if etData in ExtractTypes then
# Line 2457 | Line 2548 | begin
2548        else
2549          ListAllTables(true);
2550      end;
2551 <    eoView : ListViews(ObjectName);
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);
# Line 2466 | Line 2565 | begin
2565      eoRole : ListRoles(ObjectName);
2566      eoTrigger :
2567        if etTable in ExtractTypes then
2568 <        ListTriggers(ObjectName, etTable)
2568 >        ListTriggers(false,true,ObjectName, etTable)
2569        else
2570 <        ListTriggers(ObjectName);
2570 >        ListTriggers(false,true,ObjectName);
2571      eoForeign :
2572        if etTable in ExtractTypes then
2573          ListForeign(ObjectName, etTable)
# Line 2553 | Line 2652 | end;
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 privilegs to procedures.
2655 >   Grant various privileges to procedures.
2656     All privileges may have the with_grant option set. }
2657  
2658 < procedure TIBExtract.ShowGrants(MetaObject, Terminator: String);
2658 > procedure TIBExtract.ShowGrants(MetaObject: String; Terminator: String);
2659   const
2660    { This query only finds tables, eliminating owner privileges }
2661    OwnerPrivSQL =
# Line 2606 | Line 2705 | var
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
# Line 3033 | Line 3133 | begin
3133      while not qrySelect.Eof do
3134      begin
3135        Line := 'INSERT INTO ' + QuoteIdentifier(FDatabase.SQLDialect, ObjectName) + ' (';
3136 <      for i := 0 to qrySelect.Current.Count - 1 do
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.Current.Count - 1) then
3141 >          if i <> (qrySelect.FieldCount - 1) then
3142              Line := Line + ', ';
3143          end;
3144        Line := Line + ') VALUES (';
3145 <      for i := 0 to qrySelect.Current.Count - 1 do
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.Current.Count - 1) then
3152 >          if i <> (qrySelect.FieldCount - 1) then
3153              Line := Line + ', ';
3154          end
3155          else
# Line 3058 | Line 3158 | begin
3158            SQL_TYPE_TIME, SQL_TIMESTAMP :
3159            begin
3160              Line := Line + QuotedStr(qrySelect.Fields[i].AsString);
3161 <            if i <> (qrySelect.Current.Count - 1) then
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.Current.Count - 1) then
3168 >            if i <> (qrySelect.FieldCount - 1) then
3169                Line := Line + ', ';
3170            end;
3171            SQL_ARRAY, SQL_BLOB : ;
# Line 3085 | Line 3185 | end;
3185   procedure TIBExtract.ListRoles(ObjectName: String);
3186   const
3187    RolesSQL =
3188 <    'select * from RDB$ROLES ' +
3188 >    'select * from RDB$ROLES WHERE RDB$SYSTEM_FLAG = 0 ' +
3189      'order by RDB$ROLE_NAME';
3190  
3191    RolesByNameSQL =

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines