Pārlūkot izejas kodu

Start supporting foreign keys in script database - still needs work for compound keys. Use some field names instead of field numbers for maintainability/readability

Reinier Olislagers 11 gadi atpakaļ
vecāks
revīzija
7a6cb086a0
5 mainītis faili ar 66 papildinājumiem un 49 dzēšanām
  1. 4 4
      comparison.pas
  2. 1 1
      main.pas
  3. 20 12
      scriptdb.pas
  4. 32 24
      systables.pas
  5. 9 8
      tablemanage.pas

+ 4 - 4
comparison.pas

@@ -506,7 +506,7 @@ begin
         begin
           Line:= 'alter table ' + ATableName + ' add constraint ' + ConstraintName +
             ' foreign key (' + CurrentFieldName + ') references ' +  OtherTableName  +
-            ' (' + dmSysTables.GetConstraintForiegnKeyFields(OtherFieldName, dmSysTables.sqQuery) + ') ';
+            ' (' + dmSysTables.GetConstraintForeignKeyFields(OtherFieldName, dmSysTables.sqQuery) + ') ';
           if Trim(UpdateRule) <> 'RESTRICT' then
             Line:= Line + ' on update ' + Trim(UpdateRule);
           if Trim(DeleteRule) <> 'RESTRICT' then
@@ -799,14 +799,14 @@ begin
         OtherTablename, OtherFieldName, UpdateRole, DeleteRole));
 
     if Exist then
-      OtherFieldNames:= dmSysTables.GetConstraintForiegnKeyFields(OtherFieldName, dmSysTables.sqQuery);
+      OtherFieldNames:= dmSysTables.GetConstraintForeignKeyFields(OtherFieldName, dmSysTables.sqQuery);
 
     if Exist then
     Exist:= dmSysTables.GetConstraintInfo(cbComparedDatabase.ItemIndex, ATableName, AConstraintName, CKeyName,
       CCurrentTableName, CCurrentFieldName, COtherTablename, COtherFieldName, CUpdateRole, CDeleteRole);
 
     if Exist then
-      COtherFieldNames:= dmSysTables.GetConstraintForiegnKeyFields(COtherFieldName, dmSysTables.sqQuery);
+      COtherFieldNames:= dmSysTables.GetConstraintForeignKeyFields(COtherFieldName, dmSysTables.sqQuery);
 
     if not Exist then
       meLog.Lines.Add(' -- Error: Constraint: ' + AConstraintName + ' not exist on table: ' + ATableName);
@@ -1380,7 +1380,7 @@ begin
 
         Line:= 'alter table ' + ATableName + ' add constraint ' + AConstraintName +
           ' foreign key (' + CurrentFieldName + ') references ' +  OtherTableName  +
-          ' (' + dmSysTables.GetConstraintForiegnKeyFields(OtherFieldName, dmSysTables.sqQuery) + ') ';
+          ' (' + dmSysTables.GetConstraintForeignKeyFields(OtherFieldName, dmSysTables.sqQuery) + ') ';
         if Trim(UpdateRule) <> 'RESTRICT' then
           Line:= Line + ' on update ' + Trim(UpdateRule);
         if Trim(DeleteRule) <> 'RESTRICT' then

+ 1 - 1
main.pas

@@ -2071,7 +2071,7 @@ begin
       begin
          Line:= 'alter table ' + ATableName + ' add constraint ' + sqQuery.Fields[0].AsString +
            ' foreign key (' + sqQuery.Fields[3].AsString + ') references ' +  sqQuery.Fields[4].AsString  +
-           ' (' + dmSysTables.GetConstraintForiegnKeyFields(sqQuery.Fields[5].AsString, fmMain.SQLQuery1) + ') ';
+           ' (' + dmSysTables.GetConstraintForeignKeyFields(sqQuery.Fields[5].AsString, fmMain.SQLQuery1) + ') ';
          if Trim(sqQuery.Fields[6].AsString) <> 'RESTRICT' then
            Line:= Line + ' on update ' + Trim(sqQuery.Fields[6].AsString);
          if Trim(sqQuery.Fields[7].AsString) <> 'RESTRICT' then

+ 20 - 12
scriptdb.pas

@@ -429,22 +429,30 @@ begin
     for i:= 0 to TablesList.Count - 1 do
     with dmSysTables do
     begin
-      { to do: foreign keys are not picked up while FlameRobin does, e.g. this for employee.fdb:
-      ALTER TABLE EMPLOYEE ADD CONSTRAINT INTEG_28
-        FOREIGN KEY (DEPT_NO) REFERENCES DEPARTMENT (DEPT_NO);
+      {to do: for each constraint that occurs multiple times add the fields in one line eg
+      alter table EMPLOYEE add constraint INTEG_29 foreign key (JOB_CODE) references JOB (JOB_CODE) ;
+      alter table EMPLOYEE add constraint INTEG_29 foreign key (JOB_GRADE) references JOB (JOB_GRADE) ;
+      alter table EMPLOYEE add constraint INTEG_29 foreign key (JOB_COUNTRY) references JOB (JOB_COUNTRY) ;
+      should be 1 line
       }
       GetTableConstraints(TablesList[i], sqQuery);
       while not sqQuery.EOF do
       begin
-         Line:= 'alter table ' + TablesList[i] + ' add constraint ' + sqQuery.Fields[0].AsString +
-           ' foreign key (' + sqQuery.Fields[3].AsString + ') references ' +  sqQuery.Fields[4].AsString  +
-           ' (' + dmSysTables.GetConstraintForiegnKeyFields(sqQuery.Fields[5].AsString, fmMain.SQLQuery1) + ') ';
-         if Trim(sqQuery.Fields[6].AsString) <> 'RESTRICT' then
-           Line:= Line + ' on update ' + Trim(sqQuery.Fields[6].AsString);
-         if Trim(sqQuery.Fields[7].AsString) <> 'RESTRICT' then
-           Line:= Line + ' on delete ' + Trim(sqQuery.Fields[7].AsString);
-         List.Add(Line + ';');
-         sqQuery.Next;
+        // We're using fieldbyname here instead of fields[x] because of maintainability and probably
+        // low performance impact.
+        // If performance is an issue, define field variables outside the loop and reference them instead
+        Line:= 'alter table ' + TablesList[i] +
+          ' add constraint ' + sqQuery.FieldByName('ConstName').AsString +
+          ' foreign key (' + sqQuery.FieldByName('CurrentFieldName').AsString +
+          ') references ' +  sqQuery.FieldByName('OtherTableName').AsString +
+          ' (' + sqQuery.FieldByName('OtherFieldName').AsString +
+          ')';
+        if Trim(sqQuery.FieldByName('UpdateRule').AsString) <> 'RESTRICT' then
+          Line:= Line + ' on update ' + Trim(sqQuery.FieldByName('UpdateRule').AsString);
+        if Trim(sqQuery.FieldByName('DeleteRule').AsString) <> 'RESTRICT' then
+          Line:= Line + ' on delete ' + Trim(sqQuery.FieldByName('DeleteRule').AsString);
+        List.Add(Line + ';');
+        sqQuery.Next;
       end;
       sqQuery.Close;
     end;

+ 32 - 24
systables.pas

@@ -26,6 +26,7 @@ type
       var TriggerPosition: Integer): Boolean;
     function ScriptTrigger(dbIndex: Integer; ATriggerName: string; List: TStrings;
       AsCreate: Boolean = False): Boolean;
+    // Used e.g. in scripting foreign keys
     function GetTableConstraints(ATableName: string; var SqlQuery: TSQLQuery;
       ConstraintsList: TStringList = nil): Boolean;
 
@@ -37,7 +38,7 @@ type
     function GetExceptionInfo(ExceptionName: string; var Msg, Description, SqlQuery: string): Boolean;
     procedure GetDomainInfo(dbIndex: Integer; DomainName: string; var DomainType: string;
       var DomainSize: Integer; var DefaultValue: string);
-    function GetConstraintForiegnKeyFields(AIndexName: string; SqlQuery: TSQLQuery): string;
+    function GetConstraintForeignKeyFields(AIndexName: string; SqlQuery: TSQLQuery): string;
 
     function GetDBUsers(dbIndex: Integer; ObjectName: string = ''): string;
     function GetDBObjectsForPermissions(dbIndex: Integer; AObjectType: Integer = -1): string;
@@ -272,19 +273,27 @@ function TdmSysTables.GetTableConstraints(ATableName: string; var SqlQuery: TSQL
    ConstraintsList: TStringList = nil): Boolean;
 begin
   SqlQuery.Close;
-  SqlQuery.SQL.Text:= 'select Trim(Refc.RDB$Constraint_Name) as ConstName, ' +
-    'Trim(Refc.RDB$CONST_NAME_UQ) as KeyName, ' +
-    'Trim(Ind.RDB$Relation_Name) as CurrentTableName, ' +
-    'Trim(Seg.RDB$Field_name) as CurrentFieldName, ' +
-    'Trim(Con.RDB$Relation_Name) as OtherTableName, ' +
-    'Trim(Ind.RDB$Foreign_key) as OtherFieldName, ' +
-    'RDB$Update_Rule as UpdateRule, RDB$Delete_Rule as DeleteRule ' +
-    'from RDB$RELATION_CONSTRAINTS Con, rdb$REF_Constraints Refc, RDB$INDEX_SEGMENTS Seg, ' +
-    'RDB$INDICES Ind ' +
-    'where Con.RDB$Constraint_Name = Refc.RDB$Const_Name_UQ ' +
-    '  and Refc.RDB$Constraint_Name = Ind.RDB$Index_Name' +
-    '  and Refc.RDB$Constraint_Name = Seg.RDB$Index_Name' +
-    '  and Ind.RDB$Relation_Name = ''' + UpperCase(ATableName) + '''';
+// Note that this query differs from the way constraints are
+// presented in GetConstraintsOfTable.
+// to do: find out what the differences are and indicate better in code/comments
+  SQLQuery.SQL.Text:='select '+
+    'trim(rc.rdb$constraint_name) as ConstName, '+
+    'trim(rfc.rdb$const_name_uq) as KeyName, '+
+    'trim(rc2.rdb$relation_name) as OtherTableName, '+
+    'trim(flds_pk.rdb$field_name) as OtherFieldName, '+
+    'trim(rc.rdb$relation_name) as CurrentTableName, '+
+    'trim(flds_fk.rdb$field_name) as CurrentFieldName, '+
+    'trim(rfc.rdb$update_rule) as UpdateRule, '+
+    'trim(rfc.rdb$delete_rule) as DeleteRule '+
+    'from rdb$relation_constraints AS rc '+
+    'inner join rdb$ref_constraints as rfc on (rc.rdb$constraint_name = rfc.rdb$constraint_name) '+
+    'inner join rdb$index_segments as flds_fk on (flds_fk.rdb$index_name = rc.rdb$index_name) ' +
+    'inner join rdb$relation_constraints as rc2 on (rc2.rdb$constraint_name = rfc.rdb$const_name_uq) ' +
+    'inner join rdb$index_segments as flds_pk on ' +
+    '((flds_pk.rdb$index_name = rc2.rdb$index_name) and (flds_fk.rdb$field_position = flds_pk.rdb$field_position)) ' +
+    'where rc.rdb$constraint_type = ''FOREIGN KEY'' '+
+    'and rc.rdb$relation_name = ''' + UpperCase(ATableName) + ''' '+
+    'order by rc.rdb$constraint_name, flds_fk.rdb$field_position ';
   SqlQuery.Open;
   Result:= SqlQuery.RecordCount > 0;
   with SqlQuery do
@@ -298,7 +307,6 @@ begin
     end;
     First;
   end;
-
 end;
 
 (**********  Get Constraints for a table Info  ********************)
@@ -308,14 +316,14 @@ function TdmSysTables.GetConstraintsOfTable(ATableName: string; var SqlQuery: TS
 begin
   SqlQuery.Close;
   SQLQuery.SQL.Text:='select '+
-  'rc.rdb$constraint_name as ConstName, '+
-  'rfc.rdb$const_name_uq as KeyName, '+
-  'rc2.rdb$relation_name as CurrentTableName, '+
-  'flds_pk.rdb$field_name as CurrentFieldName, '+
-  'rc.rdb$relation_name as OtherTableName, '+
-  'flds_fk.rdb$field_name as OtherFieldName, '+
-  'rfc.rdb$update_rule as UpdateRule, '+
-  'rfc.rdb$delete_rule as DeleteRule '+
+  'trim(rc.rdb$constraint_name) as ConstName, '+
+  'trim(rfc.rdb$const_name_uq) as KeyName, '+
+  'trim(rc2.rdb$relation_name) as CurrentTableName, '+
+  'trim(flds_pk.rdb$field_name) as CurrentFieldName, '+
+  'trim(rc.rdb$relation_name) as OtherTableName, '+
+  'trim(flds_fk.rdb$field_name) as OtherFieldName, '+
+  'trim(rfc.rdb$update_rule) as UpdateRule, '+
+  'trim(rfc.rdb$delete_rule) as DeleteRule '+
   'from rdb$relation_constraints AS rc '+
   'inner join rdb$ref_constraints as rfc on (rc.rdb$constraint_name = rfc.rdb$constraint_name) '+
   'inner join rdb$index_segments as flds_fk on (flds_fk.rdb$index_name = rc.rdb$index_name) ' +
@@ -461,7 +469,7 @@ end;
 
 (*************  Get constraint foreign key fields  *************)
 
-function TdmSysTables.GetConstraintForiegnKeyFields(AIndexName: string; SqlQuery: TSQLQuery): string;
+function TdmSysTables.GetConstraintForeignKeyFields(AIndexName: string; SqlQuery: TSQLQuery): string;
 begin
   SQLQuery.Close;
   SQLQuery.SQL.Text:= 'select RDB$Index_Name as IndexName, RDB$Field_name as FieldName from RDB$INDEX_SEGMENTS ' +

+ 9 - 8
tablemanage.pas

@@ -456,17 +456,18 @@ begin
   fdbIndex:= dbIndex;
   sgConstraints.RowCount:= 1;
   with sgConstraints do
+  // SQLQuery1 should have been filled by GetTableConstraints
   while not SQLQuery1.EOF do
   begin
     RowCount:= RowCount + 1;
-    Cells[0, RowCount - 1]:= SQLQuery1.Fields[0].AsString;
-    Cells[1, RowCount - 1]:= SQLQuery1.Fields[1].AsString;
-    Cells[2, RowCount - 1]:= SQLQuery1.Fields[3].AsString;
-    Cells[3, RowCount - 1]:= SQLQuery1.Fields[4].AsString;
-    Cells[4, RowCount - 1]:= dmSysTables.GetConstraintForiegnKeyFields(SQLQuery1.Fields[5].AsString, SQLQuery2);
-
-    Cells[5, RowCount - 1]:= SQLQuery1.Fields[6].AsString;
-    Cells[6, RowCount - 1]:= SQLQuery1.Fields[7].AsString;
+    Cells[0, RowCount - 1]:= SQLQuery1.FieldByName('ConstName').AsString;
+    Cells[1, RowCount - 1]:= SQLQuery1.FieldByName('KeyName').AsString;
+    Cells[2, RowCount - 1]:= SQLQuery1.FieldByName('OtherFieldName').AsString;
+    Cells[3, RowCount - 1]:= SQLQuery1.FieldByName('CurrentTableName').AsString;
+    Cells[4, RowCount - 1]:= dmSysTables.GetConstraintForeignKeyFields(SQLQuery1.FieldByName('CurrentFieldName').AsString, SQLQuery2);
+
+    Cells[5, RowCount - 1]:= SQLQuery1.FieldByName('UpdateRule').AsString;
+    Cells[6, RowCount - 1]:= SQLQuery1.FieldByName('DeleteRule').AsString;
     SQLQuery1.Next;
   end;
   SQLQuery1.Close;