More Roundtrip tests
Some checks are pending
CI / Test (1.23) (push) Waiting to run
CI / Test (1.24) (push) Waiting to run
CI / Test (1.25) (push) Waiting to run
CI / Lint (push) Waiting to run
CI / Build (push) Waiting to run

This commit is contained in:
2025-12-17 22:52:24 +02:00
parent 5e1448dcdb
commit a427aa5537
23 changed files with 22897 additions and 1319 deletions

View File

@@ -33,7 +33,7 @@ func (r *Reader) ReadDatabase() (*models.Database, error) {
return nil, fmt.Errorf("failed to read file: %w", err)
}
var dctx DCTXDictionary
var dctx models.DCTXDictionary
if err := xml.Unmarshal(data, &dctx); err != nil {
return nil, fmt.Errorf("failed to parse DCTX XML: %w", err)
}
@@ -70,7 +70,7 @@ func (r *Reader) ReadTable() (*models.Table, error) {
}
// convertToDatabase converts a DCTX dictionary to a Database model
func (r *Reader) convertToDatabase(dctx *DCTXDictionary) (*models.Database, error) {
func (r *Reader) convertToDatabase(dctx *models.DCTXDictionary) (*models.Database, error) {
dbName := dctx.Name
if dbName == "" {
dbName = "database"
@@ -81,7 +81,7 @@ func (r *Reader) convertToDatabase(dctx *DCTXDictionary) (*models.Database, erro
// Create GUID mappings for tables and keys
tableGuidMap := make(map[string]string) // GUID -> table name
keyGuidMap := make(map[string]*DCTXKey) // GUID -> key definition
keyGuidMap := make(map[string]*models.DCTXKey) // GUID -> key definition
keyTableMap := make(map[string]string) // key GUID -> table name
fieldGuidMaps := make(map[string]map[string]string) // table name -> field GUID -> field name
@@ -135,7 +135,7 @@ func (r *Reader) convertToDatabase(dctx *DCTXDictionary) (*models.Database, erro
}
// hasSQLOption checks if a DCTX table has the SQL option set to "1"
func (r *Reader) hasSQLOption(dctxTable *DCTXTable) bool {
func (r *Reader) hasSQLOption(dctxTable *models.DCTXTable) bool {
for _, option := range dctxTable.Options {
if option.Property == "SQL" && option.PropertyValue == "1" {
return true
@@ -144,8 +144,21 @@ func (r *Reader) hasSQLOption(dctxTable *DCTXTable) bool {
return false
}
// collectFieldGuids recursively collects all field GUIDs from a field and its nested fields
func (r *Reader) collectFieldGuids(dctxField *models.DCTXField, guidMap map[string]string) {
// Store the current field's GUID if available
if dctxField.Guid != "" && dctxField.Name != "" {
guidMap[dctxField.Guid] = r.sanitizeName(dctxField.Name)
}
// Recursively process nested fields (for GROUP types)
for i := range dctxField.Fields {
r.collectFieldGuids(&dctxField.Fields[i], guidMap)
}
}
// convertTable converts a DCTX table to a Table model
func (r *Reader) convertTable(dctxTable *DCTXTable) (*models.Table, map[string]string, error) {
func (r *Reader) convertTable(dctxTable *models.DCTXTable) (*models.Table, map[string]string, error) {
tableName := r.sanitizeName(dctxTable.Name)
table := models.InitTable(tableName, "public")
table.Description = dctxTable.Description
@@ -154,10 +167,8 @@ func (r *Reader) convertTable(dctxTable *DCTXTable) (*models.Table, map[string]s
// Process fields
for _, dctxField := range dctxTable.Fields {
// Store GUID to name mapping
if dctxField.Guid != "" && dctxField.Name != "" {
fieldGuidMap[dctxField.Guid] = r.sanitizeName(dctxField.Name)
}
// Recursively collect all field GUIDs (including nested fields in GROUP types)
r.collectFieldGuids(&dctxField, fieldGuidMap)
columns, err := r.convertField(&dctxField, table.Name)
if err != nil {
@@ -174,7 +185,7 @@ func (r *Reader) convertTable(dctxTable *DCTXTable) (*models.Table, map[string]s
}
// convertField converts a DCTX field to Column(s)
func (r *Reader) convertField(dctxField *DCTXField, tableName string) ([]*models.Column, error) {
func (r *Reader) convertField(dctxField *models.DCTXField, tableName string) ([]*models.Column, error) {
var columns []*models.Column
// Handle GROUP fields (nested structures)
@@ -286,7 +297,7 @@ func (r *Reader) mapDataType(clarionType string, size int) (sqlType string, prec
}
// processKeys processes DCTX keys and converts them to indexes and primary keys
func (r *Reader) processKeys(dctxTable *DCTXTable, table *models.Table, fieldGuidMap map[string]string) error {
func (r *Reader) processKeys(dctxTable *models.DCTXTable, table *models.Table, fieldGuidMap map[string]string) error {
for _, dctxKey := range dctxTable.Keys {
err := r.convertKey(&dctxKey, table, fieldGuidMap)
if err != nil {
@@ -297,7 +308,7 @@ func (r *Reader) processKeys(dctxTable *DCTXTable, table *models.Table, fieldGui
}
// convertKey converts a DCTX key to appropriate constraint/index
func (r *Reader) convertKey(dctxKey *DCTXKey, table *models.Table, fieldGuidMap map[string]string) error {
func (r *Reader) convertKey(dctxKey *models.DCTXKey, table *models.Table, fieldGuidMap map[string]string) error {
var columns []string
// Extract column names from key components
@@ -349,7 +360,7 @@ func (r *Reader) convertKey(dctxKey *DCTXKey, table *models.Table, fieldGuidMap
}
// Handle regular index
index := models.InitIndex(r.sanitizeName(dctxKey.Name))
index := models.InitIndex(r.sanitizeName(dctxKey.Name), table.Name, table.Schema)
index.Table = table.Name
index.Schema = table.Schema
index.Columns = columns
@@ -361,7 +372,7 @@ func (r *Reader) convertKey(dctxKey *DCTXKey, table *models.Table, fieldGuidMap
}
// processRelations processes DCTX relations and creates foreign keys
func (r *Reader) processRelations(dctx *DCTXDictionary, schema *models.Schema, tableGuidMap map[string]string, keyGuidMap map[string]*DCTXKey, fieldGuidMaps map[string]map[string]string) error {
func (r *Reader) processRelations(dctx *models.DCTXDictionary, schema *models.Schema, tableGuidMap map[string]string, keyGuidMap map[string]*models.DCTXKey, fieldGuidMaps map[string]map[string]string) error {
for i := range dctx.Relations {
relation := &dctx.Relations[i]
// Get table names from GUIDs
@@ -390,19 +401,23 @@ func (r *Reader) processRelations(dctx *DCTXDictionary, schema *models.Schema, t
var fkColumns, pkColumns []string
// Try to use explicit field mappings
// NOTE: DCTX format has backwards naming - ForeignMapping contains primary table fields,
// and PrimaryMapping contains foreign table fields
if len(relation.ForeignMappings) > 0 && len(relation.PrimaryMappings) > 0 {
foreignFieldMap := fieldGuidMaps[foreignTableName]
primaryFieldMap := fieldGuidMaps[primaryTableName]
// ForeignMapping actually contains fields from the PRIMARY table
for _, mapping := range relation.ForeignMappings {
if fieldName, exists := foreignFieldMap[mapping.Field]; exists {
fkColumns = append(fkColumns, fieldName)
if fieldName, exists := primaryFieldMap[mapping.Field]; exists {
pkColumns = append(pkColumns, fieldName)
}
}
// PrimaryMapping actually contains fields from the FOREIGN table
for _, mapping := range relation.PrimaryMappings {
if fieldName, exists := primaryFieldMap[mapping.Field]; exists {
pkColumns = append(pkColumns, fieldName)
if fieldName, exists := foreignFieldMap[mapping.Field]; exists {
fkColumns = append(fkColumns, fieldName)
}
}
}