diff --git a/DbSeeder/Schema/ForeignKey.cs b/DbSeeder/Schema/ForeignKey.cs new file mode 100644 index 0000000..2b9728b --- /dev/null +++ b/DbSeeder/Schema/ForeignKey.cs @@ -0,0 +1,7 @@ +namespace DbSeeder.Schema; + +public record ForeignKey( + Table Table, + Column Column, + Table RefTable, + Column RefColumn); \ No newline at end of file diff --git a/DbSeeder/Schema/PrimaryKey.cs b/DbSeeder/Schema/PrimaryKey.cs new file mode 100644 index 0000000..9f27c86 --- /dev/null +++ b/DbSeeder/Schema/PrimaryKey.cs @@ -0,0 +1,5 @@ +namespace DbSeeder.Schema; + +public record PrimaryKey( + Table Table, + Column Column); \ No newline at end of file diff --git a/DbSeeder/Schema/SqlSchema.cs b/DbSeeder/Schema/SqlSchema.cs index b2460ac..1ee0ba5 100644 --- a/DbSeeder/Schema/SqlSchema.cs +++ b/DbSeeder/Schema/SqlSchema.cs @@ -3,10 +3,12 @@ public class SqlSchema { private readonly List _tables = []; + public IReadOnlyList
Tables => _tables; public void AddTable(Table table) - { - _tables.Add(table); - } + => _tables.Add(table); + + public Table? GetTableByName(string tableName) + => _tables.FirstOrDefault(t => t.Name.Equals(tableName, StringComparison.OrdinalIgnoreCase)); } diff --git a/DbSeeder/Schema/SqlSchemaBuilder.cs b/DbSeeder/Schema/SqlSchemaBuilder.cs index 470c867..8ca0ec2 100644 --- a/DbSeeder/Schema/SqlSchemaBuilder.cs +++ b/DbSeeder/Schema/SqlSchemaBuilder.cs @@ -54,21 +54,19 @@ private void TraverseCreateStatement(SyntaxTreeNode createStatementNode) private Table CreateTable(SyntaxTreeNode tableRoot) { var tableName = tableRoot.Value; - var table = new Table(tableName); + var table = new Table(tableName, _sqlSchema!); // Actually, now we expect that table root contains only cols, but in the future more sub-nodes can be added, // so we have to be sure that we're working exactly with a node that contains cols. var colsRoot = tableRoot.Children.First(x => x?.Type is SyntaxTreeNodeType.TableColumns); var cols = ParseColumns(colsRoot!); - table.Columns.AddRange(cols); + table.AddColumns(cols); return table; } private IEnumerable ParseColumns(SyntaxTreeNode colsRoot) - { - return colsRoot.Children.Select(GetColumn!).ToList(); - } + => colsRoot.Children.Select(GetColumn!).ToList(); private Column GetColumn(SyntaxTreeNode columnNode) { diff --git a/DbSeeder/Schema/Table.cs b/DbSeeder/Schema/Table.cs index 06bbb06..3cded09 100644 --- a/DbSeeder/Schema/Table.cs +++ b/DbSeeder/Schema/Table.cs @@ -1,7 +1,74 @@ namespace DbSeeder.Schema; -public class Table(string name) +public class Table(string name, SqlSchema schema) { + private readonly List _columns = []; + private readonly List _foreignKeys = []; + + // There are some rules how to deal if PK is not defined + // ex: find the first attribute with NOT NULL & UNIQUE constraints + public PrimaryKey? PrimaryKey { get; private set; } public string Name { get; } = name; - public List Columns { get; } = []; + public IReadOnlyList Columns => _columns; + public IReadOnlyList ForeignKeys => _foreignKeys; + + public void AddColumns(IEnumerable columns) + { + foreach (var column in columns) + { + AddColumn(column); + } + } + + private void AddColumn(Column column) + { + ArgumentNullException.ThrowIfNull(column); + _columns.Add(column); + + if (column.IsPrimaryKey) + { + SetTablePrimaryKey(column); + } + else if (column.IsForeignKey) + { + SetTableForeignKey(column); + } + } + + private void SetTablePrimaryKey(Column column) + { + if (PrimaryKey != null) + { + throw new ArgumentException("Table can not contains more that one PRIMARY KEY"); + } + + PrimaryKey = new PrimaryKey(this, column); + } + + private void SetTableForeignKey(Column column) + { + ArgumentNullException.ThrowIfNull(column.ForeignKeyRef); + + var refTable = schema.GetTableByName(column.ForeignKeyRef.TableName); + if (refTable is null) + { + throw new InvalidOperationException($"Referenced table {column.ForeignKeyRef.TableName} is not exists " + + $"in current schema. Validate the order of the create statements, " + + $"it's matter"); + } + + var refColumn = refTable.GetColumnByName(column.ForeignKeyRef.ColumnName); + if (refColumn is null) + { + throw new InvalidOperationException($"Referenced table {column.ForeignKeyRef.TableName} is not exists " + + $"in current schema. Validate the order of the create statements, " + + $"it's matter"); + } + + var fk = new ForeignKey(this, column, refTable, refColumn); + _foreignKeys.Add(fk); + } + + private Column? GetColumnByName(string columnName) + => Columns.FirstOrDefault(c => c.Name.Equals(columnName, StringComparison.Ordinal)); }