瀏覽代碼

Show domain-based field types in table management screen. Moving non-GUI shared functions to turbocommon.

Reinier Olislagers 11 年之前
父節點
當前提交
349249daf7
共有 7 個文件被更改,包括 156 次插入113 次删除
  1. 0 1
      comparison.pas
  2. 10 97
      main.pas
  3. 2 2
      newgen.pas
  4. 2 2
      scriptdb.pas
  5. 4 3
      systables.pas
  6. 13 7
      tablemanage.pas
  7. 125 1
      turbocommon.pas

+ 0 - 1
comparison.pas

@@ -1562,7 +1562,6 @@ var
   DomainSize: Integer;
   DomainSize: Integer;
   Line: string;
   Line: string;
 begin
 begin
-  //todo: align this with regular script code for domains, including collations
   if FModifiedDomainsList.Count > 0 then
   if FModifiedDomainsList.Count > 0 then
   begin
   begin
     FQueryWindow.meQuery.Lines.Add('');
     FQueryWindow.meQuery.Lines.Add('');

+ 10 - 97
main.pas

@@ -7,18 +7,10 @@ interface
 uses
 uses
   Classes, SysUtils, IBConnection, sqldb, memds, FileUtil, LResources, Forms,
   Classes, SysUtils, IBConnection, sqldb, memds, FileUtil, LResources, Forms,
   Controls, Graphics, Dialogs, Menus, ComCtrls, Reg, QueryWindow, Grids,
   Controls, Graphics, Dialogs, Menus, ComCtrls, Reg, QueryWindow, Grids,
-  ExtCtrls, Buttons, StdCtrls, TableManage,dbugintf, turbocommon;
+  ExtCtrls, Buttons, StdCtrls, TableManage, dbugintf, turbocommon;
 
 
 {$i turbocommon.inc}
 {$i turbocommon.inc}
 
 
-const
-  // Some field types used in e.g. RDB$FIELDS
-  //todo (low priority): perhaps move to enumeration with fixed constant values
-  BlobType = 261;
-  CharType = 14;
-  CStringType = 40; // probably null-terminated string used for UDFs
-  VarCharType = 37;
-
 type
 type
   TDatabaseRec = record
   TDatabaseRec = record
     Index: Integer;
     Index: Integer;
@@ -221,11 +213,6 @@ type
     function DeleteRegistration(Index: Integer): Boolean;
     function DeleteRegistration(Index: Integer): Boolean;
     // Returns BLOB subtype clause depending on subtype
     // Returns BLOB subtype clause depending on subtype
     function GetBlobSubTypeName(SubType: integer): string;
     function GetBlobSubTypeName(SubType: integer): string;
-    // Returns field type DDL given a RDB$FIELD_TYPE value as well
-    // as subtype/length/scale (use -1 for empty/unknown values)
-    function GetFBTypeName(Index: Integer;
-      SubType: integer=-1; FieldLength: integer=-1; Precision: integer=-1;
-      Scale: integer=-1): string;
     // Get name of index used for primary key
     // Get name of index used for primary key
     // Also returns name of constraint used
     // Also returns name of constraint used
     function GetPrimaryKeyIndexName(DatabaseIndex: Integer; ATableName: string; var ConstraintName: string): string;
     function GetPrimaryKeyIndexName(DatabaseIndex: Integer; ATableName: string; var ConstraintName: string): string;
@@ -250,6 +237,7 @@ type
     procedure FillAndShowConstraintsForm(Form: TfmTableManage; ATableName: string; dbIndex: Integer);
     procedure FillAndShowConstraintsForm(Form: TfmTableManage; ATableName: string; dbIndex: Integer);
     procedure ShowCompleteQueryWindow(DatabaseIndex: Integer; ATitle, AQueryText: string;
     procedure ShowCompleteQueryWindow(DatabaseIndex: Integer; ATitle, AQueryText: string;
       OnCommitProcedure: TNotifyEvent = nil);
       OnCommitProcedure: TNotifyEvent = nil);
+    // Gets fields info and fills TableManage form(!) grids with info
     procedure ViewTableFields(ATableName: string; dbIndex: Integer; AStringGrid: TStringGrid);
     procedure ViewTableFields(ATableName: string; dbIndex: Integer; AStringGrid: TStringGrid);
     procedure ShowIndicesManagement(AForm: TForm; DatabaseIndex: Integer; ATableName: string);
     procedure ShowIndicesManagement(AForm: TForm; DatabaseIndex: Integer; ATableName: string);
     function GetTableNames(dbIndex: Integer): string;
     function GetTableNames(dbIndex: Integer): string;
@@ -272,8 +260,6 @@ type
 var
 var
   fmMain: TfmMain;
   fmMain: TfmMain;
 
 
-// Tries to guess if an RDB$RELATION_FIELDS.RDB$FIELD_SOURCE domain name for a column is system-generated.
-function IsFieldDomainSystemGenerated(FieldSource: string): boolean;
 
 
 implementation
 implementation
 
 
@@ -286,13 +272,6 @@ uses CreateDb, ViewView, ViewTrigger, ViewSProc, ViewGen, NewTable, NewGen,
      PermissionManage, CopyTable, About, NewEditField, dbInfo, Comparison;
      PermissionManage, CopyTable, About, NewEditField, dbInfo, Comparison;
 
 
 
 
-function IsFieldDomainSystemGenerated(FieldSource: string): boolean;
-begin
-  // Unfortunately there does not seem to be a way to search the system tables to find out
-  // if the constraint name is system-generated
-  result:=(pos('RDB$',uppercase(Trim(FieldSource)))=1);
-end;
-
 procedure TfmMain.mnExitClick(Sender: TObject);
 procedure TfmMain.mnExitClick(Sender: TObject);
 begin
 begin
   Close;
   Close;
@@ -2371,6 +2350,7 @@ begin
     ATab.Tag:= dbIndex;
     ATab.Tag:= dbIndex;
     fmTableManage.Init(dbIndex, SelNode.Text);
     fmTableManage.Init(dbIndex, SelNode.Text);
     fmTableManage.PageControl1.TabIndex:= 0;
     fmTableManage.PageControl1.TabIndex:= 0;
+    // Fields
     ViewTableFields(SelNode.Text, dbIndex, fmTableManage.sgFields);
     ViewTableFields(SelNode.Text, dbIndex, fmTableManage.sgFields);
 
 
     // Indices
     // Indices
@@ -2380,7 +2360,7 @@ begin
     FillAndShowConstraintsForm(fmTableManage, SelNode.Text, dbIndex);
     FillAndShowConstraintsForm(fmTableManage, SelNode.Text, dbIndex);
 
 
     // Triggers
     // Triggers
-    fmTableManage.ViewTriggers;
+    fmTableManage.FillTriggers;
 
 
     // Permissions
     // Permissions
     fmTableManage.FillPermissions;
     fmTableManage.FillPermissions;
@@ -3089,10 +3069,13 @@ begin
 end;
 end;
 
 
 (***************  View Table Fields/ Fields Management  ***************)
 (***************  View Table Fields/ Fields Management  ***************)
-
+{ todo: should be moved to tablemanage.pas if possible; even better split out
+between non-GUI query part and GUI updater part}
 procedure TfmMain.ViewTableFields(ATableName: string; dbIndex: Integer;
 procedure TfmMain.ViewTableFields(ATableName: string; dbIndex: Integer;
   AStringGrid: TStringGrid);
   AStringGrid: TStringGrid);
 var
 var
+  FieldSize: integer;
+  FieldType: string;
   i: Integer;
   i: Integer;
   PKFieldsList: TStringList;
   PKFieldsList: TStringList;
   DefaultValue: string;
   DefaultValue: string;
@@ -3117,21 +3100,8 @@ begin
         Cells[1, RowCount - 1]:= Trim(FieldByName('Field_Name').AsString);
         Cells[1, RowCount - 1]:= Trim(FieldByName('Field_Name').AsString);
 
 
         // Field Type
         // Field Type
-        Cells[2, RowCount - 1]:= GetFBTypeName(FieldByName('field_type_int').AsInteger,
-          FieldByName('Field_Sub_Type').AsInteger,
-          FieldByName('Field_Length').AsInteger,
-          FieldByName('Field_Precision').AsInteger,
-          FieldByName('Field_Scale').AsInteger);
-
-        // Correct field type if it is an array type
-        // Array should really be [upper_bound dim0,upperbound dim1,..]
-        // but for now don't bother as arrays are not supported anyway
-        // Assume dimension 0, just fill in upper bound
-        if not(FieldByName('array_upper_bound').IsNull) then
-          Cells[2, RowCount - 1]:=Cells[2, RowCount - 1] +
-            ' [' +
-            SQLQuery1.FieldByName('array_upper_bound').AsString +
-            ']';
+        GetFieldType(SQLQuery1,FieldType,FieldSize);
+        Cells[2, RowCount - 1]:= FieldType;
 
 
         // Computed fields (Calculated)
         // Computed fields (Calculated)
         if FieldByName('computed_source').AsString <> '' then
         if FieldByName('computed_source').AsString <> '' then
@@ -4138,63 +4108,6 @@ begin
   end;
   end;
 end;
 end;
 
 
-(**************  Get Firebird Type name  *****************)
-
-function TfmMain.GetFBTypeName(Index: Integer;
-  SubType: integer=-1; FieldLength: integer=-1;
-  Precision: integer=-1; Scale: integer=-1
-  ): string;
-begin
-  //todo: (low priority) add Firebird 3.0 beta BOOLEAN datatype number
-  case Index of
-    // See also
-    // http://firebirdsql.org/manual/migration-mssql-data-types.html
-    // http://stackoverflow.com/questions/12070162/how-can-i-get-the-table-description-fields-and-types-from-firebird-with-dbexpr
-    BlobType : Result:= 'BLOB';
-    14 : Result:= 'CHAR';
-    CStringType : Result:= 'CSTRING'; // probably null-terminated string used for UDFs
-    12 : Result:= 'DATE';
-    11 : Result:= 'D_FLOAT';
-    16 : Result:= 'BIGINT'; // Further processed below
-    27 : Result:= 'DOUBLE PRECISION';
-    10 : Result:= 'FLOAT';
-    8  : Result:= 'INTEGER'; // further processed below
-    9  : Result:= 'QUAD'; // unknown what this is=> see IB6 Language Reference RDB$FIELD_TYPE
-    7  : Result:= 'SMALLINT'; // further processed below
-    13 : Result:= 'TIME';
-    35 : Result:= 'TIMESTAMP';
-    VarCharType : Result:= 'VARCHAR';
-  else
-    Result:= 'Unknown Type';
-  end;
-  // Subtypes for numeric types
-  if Index in [7, 8, 16] then
-  begin
-    if SubType = 0 then {integer}
-    begin
-      case Index of
-        7: Result:= 'SMALLINT';
-        8: Result:= 'INTEGER';
-        16: Result:= 'BIGINT';
-      end;
-    end
-    else
-    begin
-      // Numeric/decimal: use precision/scale
-      if SubType = 1 then
-        Result:= 'Numeric('
-      else
-      if SubType = 2 then
-        Result:= 'Decimal(';
-
-      if Precision=-1 then {sensible default}
-        Result:= Result + '2,'
-      else
-        Result:= Result + IntToStr(Precision)+',';
-      Result:= Result + IntToStr(Abs(Scale)) + ') ';
-    end;
-  end;
-end;
 
 
 (*******************  Get Primary Key fields  ************************)
 (*******************  Get Primary Key fields  ************************)
 
 

+ 2 - 2
newgen.pas

@@ -6,7 +6,7 @@ interface
 
 
 uses
 uses
   Classes, SysUtils, IBConnection, sqldb, FileUtil, LResources, Forms, Controls,
   Classes, SysUtils, IBConnection, sqldb, FileUtil, LResources, Forms, Controls,
-  Graphics, Dialogs, StdCtrls, Buttons;
+  Graphics, Dialogs, StdCtrls, Buttons, turbocommon;
 
 
 type
 type
 
 
@@ -96,7 +96,7 @@ begin
     cbFields.Clear;
     cbFields.Clear;
     while not fmMain.SQLQuery1.EOF do
     while not fmMain.SQLQuery1.EOF do
     begin
     begin
-      FType:= fmMain.GetFBTypeName(fmMain.SQLQuery1.FieldByName('field_type_int').AsInteger,
+      FType:= GetFBTypeName(fmMain.SQLQuery1.FieldByName('field_type_int').AsInteger,
         fmMain.SQLQuery1.FieldByName('field_sub_type').AsInteger,
         fmMain.SQLQuery1.FieldByName('field_sub_type').AsInteger,
         fmMain.SQLQuery1.FieldByName('field_length').AsInteger,
         fmMain.SQLQuery1.FieldByName('field_length').AsInteger,
         fmMain.SQLQuery1.FieldByName('field_precision').AsInteger,
         fmMain.SQLQuery1.FieldByName('field_precision').AsInteger,

+ 2 - 2
scriptdb.pas

@@ -5,7 +5,7 @@ unit Scriptdb;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils;
+  Classes, SysUtils, turbocommon;
 
 
 
 
 function ScriptAllRoles(dbIndex: Integer; var List: TStringList): Boolean;
 function ScriptAllRoles(dbIndex: Integer; var List: TStringList): Boolean;
@@ -240,7 +240,7 @@ begin
         begin
         begin
           // Field type is not based on a domain but a standard SQL type
           // Field type is not based on a domain but a standard SQL type
           // Field type
           // Field type
-          FieldLine:= FieldLine + fmMain.GetFBTypeName(FieldByName('field_type_int').AsInteger,
+          FieldLine:= FieldLine + GetFBTypeName(FieldByName('field_type_int').AsInteger,
             FieldByName('field_sub_type').AsInteger,
             FieldByName('field_sub_type').AsInteger,
             FieldByName('field_length').AsInteger,
             FieldByName('field_length').AsInteger,
             FieldByName('field_precision').AsInteger,
             FieldByName('field_precision').AsInteger,

+ 4 - 3
systables.pas

@@ -6,7 +6,7 @@ interface
 
 
 uses
 uses
   Classes, SysUtils, sqldb, IBConnection, FileUtil, LResources, Forms, Controls,
   Classes, SysUtils, sqldb, IBConnection, FileUtil, LResources, Forms, Controls,
-  Dialogs, dbugintf;
+  Dialogs, dbugintf, turbocommon;
 
 
 type
 type
 
 
@@ -657,7 +657,7 @@ begin
 
 
   if sqQuery.RecordCount > 0 then
   if sqQuery.RecordCount > 0 then
   begin
   begin
-    DomainType:= fmMain.GetFBTypeName(sqQuery.FieldByName('RDB$FIELD_TYPE').AsInteger,
+    DomainType:= GetFBTypeName(sqQuery.FieldByName('RDB$FIELD_TYPE').AsInteger,
       sqQuery.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger,
       sqQuery.FieldByName('RDB$FIELD_SUB_TYPE').AsInteger,
       sqQuery.FieldByName('RDB$FIELD_LENGTH').AsInteger,
       sqQuery.FieldByName('RDB$FIELD_LENGTH').AsInteger,
       sqQuery.FieldByName('RDB$FIELD_PRECISION').AsInteger,
       sqQuery.FieldByName('RDB$FIELD_PRECISION').AsInteger,
@@ -915,12 +915,13 @@ begin
   begin
   begin
     with sqQuery do
     with sqQuery do
     begin
     begin
+      //todo: harmonize with implementation in turbocommon
       if (FieldByName('field_source').IsNull) or
       if (FieldByName('field_source').IsNull) or
         (trim(FieldByName('field_source').AsString)='') or
         (trim(FieldByName('field_source').AsString)='') or
         (IsFieldDomainSystemGenerated(trim(FieldByname('field_source').AsString))) then
         (IsFieldDomainSystemGenerated(trim(FieldByname('field_source').AsString))) then
       begin
       begin
         // Field type is not based on a domain but a standard SQL type
         // Field type is not based on a domain but a standard SQL type
-        FieldType:= fmMain.GetFBTypeName(FieldByName('field_type_int').AsInteger,
+        FieldType:= GetFBTypeName(FieldByName('field_type_int').AsInteger,
           FieldByName('field_sub_type').AsInteger,
           FieldByName('field_sub_type').AsInteger,
           FieldByName('field_length').AsInteger,
           FieldByName('field_length').AsInteger,
           FieldByName('field_precision').AsInteger,
           FieldByName('field_precision').AsInteger,

+ 13 - 7
tablemanage.pas

@@ -89,9 +89,14 @@ type
   public
   public
     PKeyName, ConstraintName: string;
     PKeyName, ConstraintName: string;
     procedure Init(dbIndex: Integer; TableName: string);
     procedure Init(dbIndex: Integer; TableName: string);
+    // Fill grid with constraint info
+    { Todo: fillconstraints relies on a query being set correctly elsewhere; get
+    rid of that - see e.g. FillPermissions }
     procedure FillConstraints(dbIndex: Integer);
     procedure FillConstraints(dbIndex: Integer);
-    procedure ViewTriggers;
+    // Get info on permissions and fill grid with it
     procedure FillPermissions;
     procedure FillPermissions;
+    // Get info on triggers and fill grid with it
+    procedure FillTriggers;
   end;
   end;
 
 
 var
 var
@@ -357,7 +362,7 @@ end;
 procedure TfmTableManage.bbRefreshTriggersClick(Sender: TObject);
 procedure TfmTableManage.bbRefreshTriggersClick(Sender: TObject);
 begin
 begin
   FSQLTrans.Commit;
   FSQLTrans.Commit;
-  ViewTriggers;
+  FillTriggers;
   Parent.Show;
   Parent.Show;
   Show;
   Show;
 end;
 end;
@@ -379,10 +384,8 @@ begin
     Cells[2, RowCount - 1]:= FieldByName('OtherFieldName').AsString;
     Cells[2, RowCount - 1]:= FieldByName('OtherFieldName').AsString;
     Cells[3, RowCount - 1]:= FieldByName('KeyName').AsString;
     Cells[3, RowCount - 1]:= FieldByName('KeyName').AsString;
     Next;
     Next;
-
   end;
   end;
   SQLQuery1.Close;
   SQLQuery1.Close;
-
 end;
 end;
 
 
 procedure TfmTableManage.cbIndexTypeChange(Sender: TObject);
 procedure TfmTableManage.cbIndexTypeChange(Sender: TObject);
@@ -470,11 +473,11 @@ begin
   SQLQuery1.Close;
   SQLQuery1.Close;
 end;
 end;
 
 
-procedure TfmTableManage.ViewTriggers;
+procedure TfmTableManage.FillTriggers;
 begin
 begin
   SQLQuery1.Close;
   SQLQuery1.Close;
-  SQLQuery1.SQL.Text:= 'SELECT RDB$Trigger_Name, RDB$Trigger_Inactive FROM RDB$TRIGGERS WHERE RDB$SYSTEM_FLAG=0 ' +
-    'and RDB$Relation_Name = ''' + FTableName + '''';
+  SQLQuery1.SQL.Text:= Format('SELECT RDB$Trigger_Name, RDB$Trigger_Inactive FROM RDB$TRIGGERS WHERE RDB$SYSTEM_FLAG=0 ' +
+    'and RDB$Relation_Name = ''%s'' ',[FTableName]);
   SQLQuery1.Open;
   SQLQuery1.Open;
   sgTriggers.RowCount:= 1;
   sgTriggers.RowCount:= 1;
   with sgTriggers, SQLQuery1 do
   with sgTriggers, SQLQuery1 do
@@ -499,6 +502,9 @@ var
   ObjType: Integer;
   ObjType: Integer;
   Permissions: string;
   Permissions: string;
 begin
 begin
+  {todo: analyse transaction behaviour. Why do we have an explicit commit here? Also,
+  get rid of the implicit rollbacks and use a separate read only transaction for extracting
+  DDL and other read only info}
   FSQLTrans.Commit;
   FSQLTrans.Commit;
   UsersList:= TStringList.Create;
   UsersList:= TStringList.Create;
   try
   try

+ 125 - 1
turbocommon.pas

@@ -7,7 +7,28 @@ unit turbocommon;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils;
+  Classes, SysUtils, sqldb;
+
+const
+  // Some field types used in e.g. RDB$FIELDS
+  //todo (low priority): perhaps move to enumeration with fixed constant values
+  BlobType = 261;
+  CharType = 14;
+  CStringType = 40; // probably null-terminated string used for UDFs
+  VarCharType = 37;
+
+// Given field retrieval query in FieldQuery, return field type and size.
+// Includes support for field types that are domains and arrays
+procedure GetFieldType(FieldQuery: TSQLQuery; var FieldType: string; var FieldSize: integer);
+
+// Returns field type DDL given a RDB$FIELD_TYPE value as well
+// as subtype/length/scale (use -1 for empty/unknown values)
+function GetFBTypeName(Index: Integer;
+  SubType: integer=-1; FieldLength: integer=-1; Precision: integer=-1;
+  Scale: integer=-1): string;
+
+// Tries to guess if an RDB$RELATION_FIELDS.RDB$FIELD_SOURCE domain name for a column is system-generated.
+function IsFieldDomainSystemGenerated(FieldSource: string): boolean;
 
 
 // Given TIBConnection parameters, sets transaction isolation level
 // Given TIBConnection parameters, sets transaction isolation level
 procedure SetTransactionIsolation(Params: TStringList);
 procedure SetTransactionIsolation(Params: TStringList);
@@ -22,5 +43,108 @@ begin
   Params.Add('isc_tpb_nowait');
   Params.Add('isc_tpb_nowait');
 end;
 end;
 
 
+procedure GetFieldType(FieldQuery: TSQLQuery; var FieldType: string; var FieldSize: integer);
+// Requires FieldQuery to be the correct field retrieval query.
+// todo: migrate field retrieval query to turbocommon
+begin
+  FieldType:= '';
+  FieldSize:= 0;
+
+  if (FieldQuery.FieldByName('field_source').IsNull) or
+    (trim(FieldQuery.FieldByName('field_source').AsString)='') or
+    (IsFieldDomainSystemGenerated(trim(FieldQuery.FieldByname('field_source').AsString))) then
+  begin
+    // Field type is not based on a domain but a standard SQL type
+    FieldType:= GetFBTypeName(FieldQuery.FieldByName('field_type_int').AsInteger,
+      FieldQuery.FieldByName('field_sub_type').AsInteger,
+      FieldQuery.FieldByName('field_length').AsInteger,
+      FieldQuery.FieldByName('field_precision').AsInteger,
+      FieldQuery.FieldByName('field_scale').AsInteger);
+    // Array should really be [lowerbound:upperbound] (if dimension is 0)
+    // but for now don't bother as arrays are not supported anyway
+    // Assume 0 dimension, 1 lower bound; just fill in upper bound
+    if not(FieldQuery.FieldByName('array_upper_bound').IsNull) then
+      FieldType := FieldType +
+        ' [' +
+        FieldQuery.FieldByName('array_upper_bound').AsString +
+        ']';
+    if FieldQuery.FieldByName('field_type_int').AsInteger = VarCharType then
+      FieldSize:= FieldQuery.FieldByName('characterlength').AsInteger
+    else
+      FieldSize:= FieldQuery.FieldByName('field_length').AsInteger;
+  end
+  else
+  begin
+    // Field is based on a domain
+    FieldType:= trim(FieldQuery.FieldByName('field_source').AsString);
+  end;
+end;
+
+(**************  Get Firebird Type name  *****************)
+
+function GetFBTypeName(Index: Integer;
+  SubType: integer=-1; FieldLength: integer=-1;
+  Precision: integer=-1; Scale: integer=-1
+  ): string;
+begin
+  //todo: (low priority) add Firebird 3.0 beta BOOLEAN datatype number
+  case Index of
+    // See also
+    // http://firebirdsql.org/manual/migration-mssql-data-types.html
+    // http://stackoverflow.com/questions/12070162/how-can-i-get-the-table-description-fields-and-types-from-firebird-with-dbexpr
+    BlobType : Result:= 'BLOB';
+    14 : Result:= 'CHAR';
+    CStringType : Result:= 'CSTRING'; // probably null-terminated string used for UDFs
+    12 : Result:= 'DATE';
+    11 : Result:= 'D_FLOAT';
+    16 : Result:= 'BIGINT'; // Further processed below
+    27 : Result:= 'DOUBLE PRECISION';
+    10 : Result:= 'FLOAT';
+    8  : Result:= 'INTEGER'; // further processed below
+    9  : Result:= 'QUAD'; // unknown what this is=> see IB6 Language Reference RDB$FIELD_TYPE
+    7  : Result:= 'SMALLINT'; // further processed below
+    13 : Result:= 'TIME';
+    35 : Result:= 'TIMESTAMP';
+    VarCharType : Result:= 'VARCHAR';
+  else
+    Result:= 'Unknown Type';
+  end;
+  // Subtypes for numeric types
+  if Index in [7, 8, 16] then
+  begin
+    if SubType = 0 then {integer}
+    begin
+      case Index of
+        7: Result:= 'SMALLINT';
+        8: Result:= 'INTEGER';
+        16: Result:= 'BIGINT';
+      end;
+    end
+    else
+    begin
+      // Numeric/decimal: use precision/scale
+      if SubType = 1 then
+        Result:= 'Numeric('
+      else
+      if SubType = 2 then
+        Result:= 'Decimal(';
+
+      if Precision=-1 then {sensible default}
+        Result:= Result + '2,'
+      else
+        Result:= Result + IntToStr(Precision)+',';
+      Result:= Result + IntToStr(Abs(Scale)) + ') ';
+    end;
+  end;
+end;
+
+function IsFieldDomainSystemGenerated(FieldSource: string): boolean;
+begin
+  // Unfortunately there does not seem to be a way to search the system tables to find out
+  // if the constraint name is system-generated
+  result:=(pos('RDB$',uppercase(Trim(FieldSource)))=1);
+end;
+
+
 end.
 end.