/// <summary> /// Initializes a new instance of the <see cref="DatabaseRelationalKey"/> class. /// </summary> /// <param name="childTableName">The child table name.</param> /// <param name="childKey">The child key.</param> /// <param name="parentTableName">The parent table name.</param> /// <param name="parentKey">The parent key.</param> /// <param name="deleteAction">The delete action.</param> /// <param name="updateAction">The update action.</param> /// <exception cref="ArgumentException"> /// <paramref name="updateAction"/> or <paramref name="deleteAction"/> will throw this exception if given an invalid enum value. /// Alternatively if the child key is not a foreign key this will also be thrown. /// Furthermore, if the parent key is not a unique or primary key, this will also be thrown. /// </exception> /// <exception cref="ArgumentNullException"><paramref name="parentTableName"/> or <paramref name="childTableName"/> or <paramref name="parentKey"/> or <paramref name="childKey"/> is <c>null</c></exception> public DatabaseRelationalKey(Identifier childTableName, IDatabaseKey childKey, Identifier parentTableName, IDatabaseKey parentKey, ReferentialAction deleteAction, ReferentialAction updateAction) { if (!deleteAction.IsValid()) { throw new ArgumentException($"The { nameof(ReferentialAction) } provided must be a valid enum.", nameof(deleteAction)); } if (!updateAction.IsValid()) { throw new ArgumentException($"The { nameof(ReferentialAction) } provided must be a valid enum.", nameof(updateAction)); } ChildTable = childTableName ?? throw new ArgumentNullException(nameof(childTableName)); ChildKey = childKey ?? throw new ArgumentNullException(nameof(childKey)); ParentTable = parentTableName ?? throw new ArgumentNullException(nameof(parentTableName)); ParentKey = parentKey ?? throw new ArgumentNullException(nameof(parentKey)); if (ChildKey.KeyType != DatabaseKeyType.Foreign) { throw new ArgumentException($"The child key must be a foreign key, instead given a key of type '{ childKey.KeyType }'.", nameof(childKey)); } if (ParentKey.KeyType != DatabaseKeyType.Primary && ParentKey.KeyType != DatabaseKeyType.Unique) { throw new ArgumentException($"The parent key must be a primary or unique key, instead given a key of type '{ parentKey.KeyType }'.", nameof(parentKey)); } DeleteAction = deleteAction; UpdateAction = updateAction; }
public ReflectionForeignKey(Identifier name, IDatabaseKey targetKey, IReadOnlyCollection <IDatabaseColumn> columns) : base(name, DatabaseKeyType.Foreign, columns) { if (targetKey == null) { throw new ArgumentNullException(nameof(targetKey)); } if (targetKey.KeyType != DatabaseKeyType.Primary && targetKey.KeyType != DatabaseKeyType.Unique) { throw new ArgumentException("The parent key given to a foreign key must be a primary or unique key. Instead given: " + targetKey.KeyType.ToString(), nameof(targetKey)); } if (columns.Count != targetKey.Columns.Count) { throw new ArgumentException("The number of columns given to a foreign key must match the number of columns in the target key", nameof(columns)); } var columnTypes = columns.Select(c => c.Type).ToList(); var targetColumnTypes = targetKey.Columns.Select(c => c.Type).ToList(); // if we're dealing with computed columns, we can't get the types easily so avoid checking the types var anyComputed = columns.Any(c => c.IsComputed) || targetKey.Columns.Any(c => c.IsComputed); var columnTypesCompatible = ColumnTypesCompatible(columnTypes, targetColumnTypes); if (!anyComputed && !columnTypesCompatible) { throw new ArgumentException("Incompatible column types between source and target key columns.", nameof(columns)); } }
private static bool IsChildKeyUnique(IRelationalDatabaseTable table, IDatabaseKey key) { if (table == null) { throw new ArgumentNullException(nameof(table)); } if (key == null) { throw new ArgumentNullException(nameof(key)); } var keyColumnNames = key.Columns.Select(c => c.Name.LocalName).ToList(); var matchesPkColumns = table.PrimaryKey .Match( pk => { var pkColumnNames = pk.Columns.Select(c => c.Name.LocalName).ToList(); return(keyColumnNames.SequenceEqual(pkColumnNames)); }, () => false ); if (matchesPkColumns) { return(true); } var matchesUkColumns = table.UniqueKeys.Any(uk => { var ukColumnNames = uk.Columns.Select(c => c.Name.LocalName).ToList(); return(keyColumnNames.SequenceEqual(ukColumnNames)); }); if (matchesUkColumns) { return(true); } var uniqueIndexes = table.Indexes.Where(i => i.IsUnique).ToList(); if (uniqueIndexes.Count == 0) { return(false); } return(uniqueIndexes.Any(i => { var indexColumnExpressions = i.Columns .Select(ic => ic.DependentColumns.Select(dc => dc.Name.LocalName).FirstOrDefault() ?? ic.Expression) .ToList(); return keyColumnNames.SequenceEqual(indexColumnExpressions); })); }
public static void Ctor_GivenUniqueKeysWithNullValue_ThrowsArgumentNullException() { Identifier tableName = "test_table"; var columns = new[] { Mock.Of <IDatabaseColumn>() }; var primaryKey = Option <IDatabaseKey> .None; var uniqueKeys = new IDatabaseKey[] { null }; var parentKeys = Array.Empty <IDatabaseRelationalKey>(); var childKeys = Array.Empty <IDatabaseRelationalKey>(); var indexes = Array.Empty <IDatabaseIndex>(); var checks = Array.Empty <IDatabaseCheckConstraint>(); var triggers = Array.Empty <IDatabaseTrigger>(); Assert.That(() => new RelationalDatabaseTable(tableName, columns, primaryKey, uniqueKeys, parentKeys, childKeys, indexes, checks, triggers), Throws.ArgumentNullException); }
public Constraints.PrimaryKeyConstraint MapPrimaryKey(Identifier tableName, IDatabaseKey primaryKey) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } if (primaryKey == null) { throw new ArgumentNullException(nameof(primaryKey)); } var pkConstraintName = primaryKey.Name.Match(pkName => pkName.LocalName, () => string.Empty); var columnNames = primaryKey.Columns.Select(c => c.Name.LocalName).ToList(); return(new Constraints.PrimaryKeyConstraint( tableName, pkConstraintName, columnNames )); }
public Constraints.UniqueKey MapUniqueKey(Identifier tableName, IDatabaseKey uniqueKey) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } if (uniqueKey == null) { throw new ArgumentNullException(nameof(uniqueKey)); } var ukConstraintName = uniqueKey.Name.Match(ukName => ukName.LocalName, () => string.Empty); var columnNames = uniqueKey.Columns.Select(c => c.Name.LocalName).ToList(); return(new Constraints.UniqueKey( tableName, ukConstraintName, columnNames )); }
private static void ValidateColumnSetsCompatible(Identifier childTableName, IDatabaseKey childKey, Identifier parentTableName, IDatabaseKey parentKey) { var anyComputed = childKey.Columns.Any(c => c.IsComputed) || parentKey.Columns.Any(c => c.IsComputed); if (anyComputed) { return; } var childKeyColumnTypes = childKey.Columns.Select(c => c.Type); var parentKeyColumnTypes = parentKey.Columns.Select(c => c.Type); var compatible = childKeyColumnTypes .Zip(parentKeyColumnTypes, (a, b) => new { Column = a, TargetColumn = b }) .All(cc => IsTypeEquivalent(cc.Column, cc.TargetColumn)); if (!compatible) { throw new Exception($"The column sets in the foreign key { childKey.Name } from { childTableName } to { parentKey.Name } in { parentTableName } are not compatible. Please check the declarations to ensure the column types are safe to create."); } }
public ReflectionRelationalKey(Identifier childTableName, IDatabaseKey childKey, Identifier parentTableName, IDatabaseKey parentKey, ReferentialAction deleteAction, ReferentialAction updateAction) { if (childTableName == null) { throw new ArgumentNullException(nameof(childTableName)); } if (childKey == null) { throw new ArgumentNullException(nameof(childKey)); } if (parentTableName == null) { throw new ArgumentNullException(nameof(parentTableName)); } if (parentKey == null) { throw new ArgumentNullException(nameof(parentKey)); } if (!deleteAction.IsValid()) { throw new ArgumentException($"The { nameof(ReferentialAction) } provided must be a valid enum.", nameof(deleteAction)); } if (!updateAction.IsValid()) { throw new ArgumentException($"The { nameof(ReferentialAction) } provided must be a valid enum.", nameof(updateAction)); } // perform validation var relationalKey = new DatabaseRelationalKey(childTableName, childKey, parentTableName, parentKey, deleteAction, updateAction); ChildTable = relationalKey.ChildTable; ChildKey = relationalKey.ChildKey; ParentTable = relationalKey.ParentTable; ParentKey = relationalKey.ParentKey; DeleteAction = relationalKey.DeleteAction; UpdateAction = relationalKey.UpdateAction; ValidateColumnSetsCompatible(ChildTable, ChildKey, ParentTable, ParentKey); }
public static int GetKeyHash(this IDatabaseKey key, Identifier tableName) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } var builder = new HashCode(); builder.Add(tableName.GetHashCode()); builder.Add(key.KeyType.GetHashCode()); foreach (var column in key.Columns) { builder.Add(column.Name?.LocalName?.GetHashCode() ?? 0); } return(builder.ToHashCode()); }
private InvocationExpressionSyntax BuildTableUniqueKeyForBuilder(IRelationalDatabaseTable table, IDatabaseKey uniqueKey) { if (table == null) { throw new ArgumentNullException(nameof(table)); } if (uniqueKey == null) { throw new ArgumentNullException(nameof(uniqueKey)); } var schemaNamespace = NameTranslator.SchemaToNamespace(table.Name); var className = NameTranslator.TableToClassName(table.Name); var qualifiedClassName = !schemaNamespace.IsNullOrWhiteSpace() ? schemaNamespace + "." + className : className; var entity = GetEntityBuilder(qualifiedClassName); var ukBuilder = InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, entity, IdentifierName(nameof(EntityTypeBuilder.HasAlternateKey)))) .WithArgumentList( ArgumentList( SingletonSeparatedList( Argument( GenerateColumnSet(className, uniqueKey.Columns, false))))); uniqueKey.Name.IfSome(ukName => { ukBuilder = InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, ukBuilder, IdentifierName(nameof(RelationalKeyBuilderExtensions.HasName)))) .WithArgumentList( ArgumentList( SingletonSeparatedList( Argument( LiteralExpression( SyntaxKind.StringLiteralExpression, Literal(ukName.LocalName)))))); }); return(ukBuilder); }
/// <summary> /// Analyses a database table and its primary key. Reports messages when the primary key columns are not the first columns in the table. /// </summary> /// <param name="table">A database table.</param> /// <param name="primaryKey">The primary key for the database table.</param> /// <returns>A set of linting messages used for reporting. An empty set indicates no issues discovered.</returns> /// <exception cref="ArgumentNullException"><paramref name="table"/> or <paramref name="primaryKey"/> is <c>null</c>.</exception> protected IEnumerable <IRuleMessage> AnalyseTable(IRelationalDatabaseTable table, IDatabaseKey primaryKey) { if (table == null) { throw new ArgumentNullException(nameof(table)); } if (primaryKey == null) { throw new ArgumentNullException(nameof(primaryKey)); } var tableColumns = table.Columns; if (tableColumns.Empty()) { return(Array.Empty <IRuleMessage>()); } var pkColumnName = primaryKey.Columns.Single().Name; var firstColumnName = table.Columns[0].Name; if (pkColumnName == firstColumnName) { return(Array.Empty <IRuleMessage>()); } var message = BuildMessage(table.Name); return(new[] { message }); }