diff --git a/FAnsi.sln.DotSettings b/FAnsi.sln.DotSettings new file mode 100644 index 00000000..a44f51d4 --- /dev/null +++ b/FAnsi.sln.DotSettings @@ -0,0 +1,6 @@ + + DB + FROM + SELECT + SQL + WHERE \ No newline at end of file diff --git a/FAnsiSql/DatabaseOperationArgs.cs b/FAnsiSql/DatabaseOperationArgs.cs index 433709e6..ccbbd556 100644 --- a/FAnsiSql/DatabaseOperationArgs.cs +++ b/FAnsiSql/DatabaseOperationArgs.cs @@ -49,8 +49,9 @@ public DatabaseOperationArgs(IManagedTransaction transactionIfAny, int timeoutIn /// public int ExecuteNonQuery(DbCommand cmd) { - return Execute(cmd, ()=>cmd.ExecuteNonQueryAsync(CancellationToken)); + return Execute(cmd, () => cmd.ExecuteNonQueryAsync(CancellationToken)); } + /// /// Sets the timeout and cancellation on then runs with the /// (if any) and blocks till the call completes. diff --git a/FAnsiSql/Discovery/BulkCopy.cs b/FAnsiSql/Discovery/BulkCopy.cs index 1d7c6b10..807aaded 100644 --- a/FAnsiSql/Discovery/BulkCopy.cs +++ b/FAnsiSql/Discovery/BulkCopy.cs @@ -149,8 +149,20 @@ protected void ConvertStringTypesToHardTypes(DataTable dt) //if the DataColumn is part of the Primary Key of the DataTable (in memory) //then we need to update the primary key to include the new column not the old one - if (dt.PrimaryKey != null && dt.PrimaryKey.Contains(dataColumn)) - dt.PrimaryKey = dt.PrimaryKey.Except(new[] { dataColumn }).Union(new[] { newColumn }).ToArray(); + if (dt.PrimaryKey != null) + { + var modified = false; + // Need to invoke the PrimaryKey setter to update the internal state of the DataTable, can't just modify existing array! + var newKey = Array.ConvertAll(dt.PrimaryKey, c => + { + if (c != dataColumn) return c; + + modified = true; + return newColumn; + }); + if (modified) + dt.PrimaryKey = newKey; + } var oldOrdinal = dataColumn.Ordinal; diff --git a/FAnsiSql/Discovery/ConnectionStringDefaults/ConnectionStringKeywordAccumulator.cs b/FAnsiSql/Discovery/ConnectionStringDefaults/ConnectionStringKeywordAccumulator.cs index 60467eb2..dc7171f6 100644 --- a/FAnsiSql/Discovery/ConnectionStringDefaults/ConnectionStringKeywordAccumulator.cs +++ b/FAnsiSql/Discovery/ConnectionStringDefaults/ConnectionStringKeywordAccumulator.cs @@ -86,8 +86,6 @@ public void AddOrUpdateKeyword(string keyword, string value, ConnectionStringKey } //now iterate all the keys we had before and add those too, if the key count doesn't change for any of them we know it's a duplicate semantically - if (_builder.Keys == null) return null; - foreach (var current in _keywords) { var keysBefore = _builder.Keys.Count; diff --git a/FAnsiSql/Discovery/DatabaseColumnRequest.cs b/FAnsiSql/Discovery/DatabaseColumnRequest.cs index ab864f9d..75e9d8e7 100644 --- a/FAnsiSql/Discovery/DatabaseColumnRequest.cs +++ b/FAnsiSql/Discovery/DatabaseColumnRequest.cs @@ -59,7 +59,7 @@ public sealed class DatabaseColumnRequest(string columnName, DatabaseTypeRequest /// public string? Collation { get; set; } - public DatabaseColumnRequest(string columnName, string explicitDbType, bool allowNulls = true) : this(columnName, (DatabaseTypeRequest?)null, allowNulls) + public DatabaseColumnRequest(string columnName, string? explicitDbType, bool allowNulls = true) : this(columnName, (DatabaseTypeRequest?)null, allowNulls) { ExplicitDbType = explicitDbType; } @@ -69,7 +69,8 @@ public DatabaseColumnRequest(string columnName, string explicitDbType, bool allo /// /// /// - public string GetSQLDbType(ITypeTranslater typeTranslater) => ExplicitDbType??typeTranslater.GetSQLDBTypeForCSharpType(TypeRequested); + public string GetSQLDbType(ITypeTranslater typeTranslater) => + ExplicitDbType ?? typeTranslater.GetSQLDBTypeForCSharpType(TypeRequested); public string GetRuntimeName() => ColumnName; } \ No newline at end of file diff --git a/FAnsiSql/Discovery/DiscoveredColumn.cs b/FAnsiSql/Discovery/DiscoveredColumn.cs index 9a6f963a..61b115e5 100644 --- a/FAnsiSql/Discovery/DiscoveredColumn.cs +++ b/FAnsiSql/Discovery/DiscoveredColumn.cs @@ -1,4 +1,5 @@ -using FAnsi.Discovery.QuerySyntax; +using System; +using FAnsi.Discovery.QuerySyntax; using FAnsi.Naming; using TypeGuesser; @@ -57,14 +58,13 @@ public sealed class DiscoveredColumn(DiscoveredTable table, string name, bool al /// public string? Format { get; set; } - private readonly string _name = name; private readonly IQuerySyntaxHelper _querySyntaxHelper = table.Database.Server.GetQuerySyntaxHelper(); /// /// The unqualified name of the column e.g. "MyCol" /// /// - public string GetRuntimeName() => _querySyntaxHelper.GetRuntimeName(_name); + public string GetRuntimeName() => _querySyntaxHelper.GetRuntimeName(name); /// /// The fully qualified name of the column e.g. [MyDb].dbo.[MyTable].[MyCol] or `MyDb`.`MyCol` @@ -86,7 +86,7 @@ public string GetFullyQualifiedName() => _querySyntaxHelper.EnsureFullyQualified /// Returns the name of the column /// /// - public override string ToString() => _name; + public override string ToString() => name; /// /// Generates a primed with the of this column. This can be used to inspect new @@ -100,7 +100,7 @@ public string GetFullyQualifiedName() => _querySyntaxHelper.EnsureFullyQualified /// /// /// - private bool Equals(DiscoveredColumn other) => string.Equals(_name, other._name) && Equals(Table, other.Table); + private bool Equals(DiscoveredColumn other) => string.Equals(name, other.ToString()) && Equals(Table, other.Table); /// /// Based on column name and Table @@ -120,17 +120,11 @@ public override bool Equals(object? obj) /// Based on column name and Table /// /// - public override int GetHashCode() - { - unchecked - { - return ((_name?.GetHashCode() ?? 0) * 397) ^ (Table?.GetHashCode() ?? 0); - } - } + public override int GetHashCode() => HashCode.Combine(name, Table); /// /// Returns the wrapped e.g. "[MyCol]" name of the column including escaping e.g. if you wanted to name a column "][nquisitor" (which would return "[]][nquisitor]"). Use to return the full name including table/database/schema. /// /// - public string? GetWrappedName() => Table.GetQuerySyntaxHelper().EnsureWrapped(GetRuntimeName()); + public string GetWrappedName() => Table.GetQuerySyntaxHelper().EnsureWrapped(GetRuntimeName()); } \ No newline at end of file diff --git a/FAnsiSql/Discovery/DiscoveredDataType.cs b/FAnsiSql/Discovery/DiscoveredDataType.cs index 5d6c1e25..c3de7903 100644 --- a/FAnsiSql/Discovery/DiscoveredDataType.cs +++ b/FAnsiSql/Discovery/DiscoveredDataType.cs @@ -81,7 +81,7 @@ public DiscoveredDataType(DbDataReader r, string sqlType, DiscoveredColumn? colu public void Resize(int newSize, IManagedTransaction? managedTransaction = null) { var toReplace = GetLengthIfString(); - + if(newSize == toReplace) return; @@ -120,7 +120,7 @@ public void Resize(int numberOfDigitsBeforeDecimalPoint, int numberOfDigitsAfter var newDataType = _column?.Table.GetQuerySyntaxHelper() .TypeTranslater.GetSQLDBTypeForCSharpType(new DatabaseTypeRequest(typeof(decimal), null, new DecimalSize(numberOfDigitsBeforeDecimalPoint, numberOfDigitsAfterDecimalPoint))) ?? - throw new InvalidOperationException($"Failed to calculate new DB type"); + throw new InvalidOperationException("Failed to calculate new DB type"); AlterTypeTo(newDataType, managedTransaction); } @@ -155,7 +155,7 @@ public void AlterTypeTo(string newType, IManagedTransaction? managedTransaction } } - SQLType = newType; + SQLType = newType; } /// diff --git a/FAnsiSql/Discovery/DiscoveredDatabase.cs b/FAnsiSql/Discovery/DiscoveredDatabase.cs index 2cc2230b..5c1bbb83 100644 --- a/FAnsiSql/Discovery/DiscoveredDatabase.cs +++ b/FAnsiSql/Discovery/DiscoveredDatabase.cs @@ -84,7 +84,7 @@ public IEnumerable DiscoverTableValuedFunctions(I /// Returns the wrapped e.g. "[MyDatabase]" name of the database including escaping e.g. if you wanted to name a database "][nquisitor" (which would return "[]][nquisitor]"). /// /// - public string? GetWrappedName() => _querySyntaxHelper.EnsureWrapped(GetRuntimeName()); + public string GetWrappedName() => _querySyntaxHelper.EnsureWrapped(GetRuntimeName()); /// /// Creates an expectation (See ) that there is a table with the given name in the database. @@ -127,7 +127,7 @@ public DiscoveredTable ExpectTable(string tableName, string? schema = null, Tabl /// public bool Exists(IManagedTransaction? transaction = null) { - return Server.DiscoverDatabases().Any(db => db.GetRuntimeName()?.Equals(GetRuntimeName(), StringComparison.InvariantCultureIgnoreCase) == true); + return Server.DiscoverDatabases().Any(db => db.GetRuntimeName().Equals(GetRuntimeName(), StringComparison.InvariantCultureIgnoreCase)); } /// @@ -303,11 +303,5 @@ public override bool Equals(object? obj) /// Based on Server and database name /// /// - public override int GetHashCode() - { - unchecked - { - return ((Server != null ? Server.GetHashCode() : 0) * 397) ^ (_database != null ? StringComparer.OrdinalIgnoreCase.GetHashCode(_database) : 0); - } - } + public override int GetHashCode() => HashCode.Combine(Server, _database); } \ No newline at end of file diff --git a/FAnsiSql/Discovery/DiscoveredDatabaseHelper.cs b/FAnsiSql/Discovery/DiscoveredDatabaseHelper.cs index 59640def..bf0a23e9 100644 --- a/FAnsiSql/Discovery/DiscoveredDatabaseHelper.cs +++ b/FAnsiSql/Discovery/DiscoveredDatabaseHelper.cs @@ -19,7 +19,7 @@ namespace FAnsi.Discovery; /// DBMS specific implementation of all functionality that relates to interacting with existing databases (dropping databases, creating tables, finding stored procedures etc). For /// database creation see /// -public abstract class DiscoveredDatabaseHelper:IDiscoveredDatabaseHelper +public abstract class DiscoveredDatabaseHelper : IDiscoveredDatabaseHelper { public abstract IEnumerable ListTables(DiscoveredDatabase parent, IQuerySyntaxHelper querySyntaxHelper, DbConnection connection, string database, bool includeViews, DbTransaction? transaction = null); @@ -41,7 +41,7 @@ public DiscoveredTable CreateTable(CreateTableArgs args) ? args.ExplicitColumnDefinitions.ToList() : []; - if(args.DataTable != null) + if (args.DataTable != null) { ThrowIfObjectColumns(args.DataTable); @@ -50,7 +50,7 @@ public DiscoveredTable CreateTable(CreateTableArgs args) foreach (DataColumn column in args.DataTable.Columns) { //do we have an explicit overriding column definition? - var overriding = customRequests.SingleOrDefault(c => c.ColumnName.Equals(column.ColumnName,StringComparison.CurrentCultureIgnoreCase)); + var overriding = customRequests.SingleOrDefault(c => c.ColumnName.Equals(column.ColumnName, StringComparison.CurrentCultureIgnoreCase)); //yes if (overriding != null) @@ -84,7 +84,7 @@ public DiscoveredTable CreateTable(CreateTableArgs args) var guesser = GetGuesser(column); guesser.Culture = args.Culture; - CopySettings(guesser,args); + CopySettings(guesser, args); guesser.AdjustToCompensateForValues(column); @@ -92,9 +92,9 @@ public DiscoveredTable CreateTable(CreateTableArgs args) if (column.GetDoNotReType()) guesser.Guess.CSharpType = column.DataType; - typeDictionary.Add(column.ColumnName,guesser); + typeDictionary.Add(column.ColumnName, guesser); - columns.Add(new DatabaseColumnRequest(column.ColumnName, guesser.Guess, column.AllowDBNull) { IsPrimaryKey = args.DataTable.PrimaryKey.Contains(column)}); + columns.Add(new DatabaseColumnRequest(column.ColumnName, guesser.Guess, column.AllowDBNull) { IsPrimaryKey = args.DataTable.PrimaryKey.Contains(column) }); } } } @@ -123,7 +123,7 @@ public DiscoveredTable CreateTable(CreateTableArgs args) var tbl = args.Database.ExpectTable(args.TableName, args.Schema); //unless we are being asked to create it empty then upload the DataTable to it - if(args.DataTable != null && !args.CreateEmpty) + if (args is { DataTable: not null, CreateEmpty: false }) { using var bulk = tbl.BeginBulkInsert(args.Culture); bulk.DateTimeDecider.Settings.ExplicitDateFormats = args.GuessSettings.ExplicitDateFormats; @@ -153,7 +153,7 @@ public void ThrowIfObjectColumns(DataTable dt) { var objCol = dt.Columns.Cast().FirstOrDefault(static c => c.DataType == typeof(object)); - if(objCol != null) + if (objCol != null) throw new NotSupportedException( string.Format( FAnsiStrings.DataTable_Column__0__was_of_DataType__1___this_is_not_allowed___Use_String_for_untyped_data, @@ -174,7 +174,7 @@ public virtual string GetCreateTableSql(DiscoveredDatabase database, string tabl bool cascadeDelete, string? schema) { if (string.IsNullOrWhiteSpace(tableName)) - throw new ArgumentNullException(nameof(tableName),FAnsiStrings.DiscoveredDatabaseHelper_GetCreateTableSql_Table_name_cannot_be_null); + throw new ArgumentNullException(nameof(tableName), FAnsiStrings.DiscoveredDatabaseHelper_GetCreateTableSql_Table_name_cannot_be_null); var bodySql = new StringBuilder(); @@ -204,7 +204,7 @@ public virtual string GetCreateTableSql(DiscoveredDatabase database, string tabl var pks = columns.Where(static c => c.IsPrimaryKey).ToArray(); if (pks.Length != 0) - bodySql.Append(GetPrimaryKeyDeclarationSql(tableName, pks,syntaxHelper)); + bodySql.Append(GetPrimaryKeyDeclarationSql(tableName, pks, syntaxHelper)); if (foreignKeyPairs != null) { @@ -227,7 +227,7 @@ public virtual string GetCreateTableSql(DiscoveredDatabase database, string tabl /// /// /// - protected virtual string GetCreateTableSqlLineForColumn(DatabaseColumnRequest col, string datatype, IQuerySyntaxHelper syntaxHelper) => $"{syntaxHelper.EnsureWrapped(col.ColumnName)} {datatype} {(col.Default != MandatoryScalarFunctions.None ? $"default {syntaxHelper.GetScalarFunctionSql(col.Default)}" : "")} {(string.IsNullOrWhiteSpace(col.Collation) ? "" : $"COLLATE {col.Collation}")} {(col.AllowNulls && !col.IsPrimaryKey ? " NULL" : " NOT NULL")} {(col.IsAutoIncrement ? syntaxHelper.GetAutoIncrementKeywordIfAny() : "")}"; + protected virtual string GetCreateTableSqlLineForColumn(DatabaseColumnRequest col, string datatype, IQuerySyntaxHelper syntaxHelper) => $"{syntaxHelper.EnsureWrapped(col.ColumnName)} {datatype} {(col.Default != MandatoryScalarFunctions.None ? $"default {syntaxHelper.GetScalarFunctionSql(col.Default)}" : "")} {(string.IsNullOrWhiteSpace(col.Collation) ? "" : $"COLLATE {col.Collation}")} {(col is { AllowNulls: true, IsPrimaryKey: false } ? " NULL" : " NOT NULL")} {(col.IsAutoIncrement ? syntaxHelper.GetAutoIncrementKeywordIfAny() : "")}"; public virtual string GetForeignKeyConstraintSql(string foreignTable, IQuerySyntaxHelper syntaxHelper, Dictionary foreignKeyPairs, bool cascadeDelete, string? constraintName) @@ -273,7 +273,7 @@ public void ExecuteBatchNonQuery(string sql, DbConnection conn, DbTransaction? t ExecuteBatchNonQuery(sql, conn, transaction, out _, timeout); } - private static readonly string[] separator = ["\n", "\r"]; + private static readonly string[] Separator = ["\n", "\r"]; /// /// Executes the given SQL against the database + sends GO delimited statements as separate batches @@ -306,11 +306,11 @@ public void ExecuteBatchNonQuery(string sql, DbConnection conn, DbTransaction? t sql += "\nGO"; // make sure last batch is executed. try { - foreach (var line in sql.Split(separator, StringSplitOptions.RemoveEmptyEntries)) + foreach (var line in sql.Split(Separator, StringSplitOptions.RemoveEmptyEntries)) { lineNumber++; - if (line.Trim().Equals("GO",StringComparison.CurrentCultureIgnoreCase)) + if (line.Trim().Equals("GO", StringComparison.CurrentCultureIgnoreCase)) { var executeSql = sqlBatch.ToString(); if (string.IsNullOrWhiteSpace(executeSql)) diff --git a/FAnsiSql/Discovery/DiscoveredServer.cs b/FAnsiSql/Discovery/DiscoveredServer.cs index 35a4bab5..3be1439c 100644 --- a/FAnsiSql/Discovery/DiscoveredServer.cs +++ b/FAnsiSql/Discovery/DiscoveredServer.cs @@ -377,14 +377,10 @@ public DiscoveredDatabase CreateDatabase(string newDatabaseName) /// /// /// - private bool Equals(DiscoveredServer other) - { - if (Builder == null || other.Builder == null) - return Equals(Builder, other.Builder) && DatabaseType == other.DatabaseType; - + private bool Equals(DiscoveredServer other) => //server is the same if they are pointed at the same server - return string.Equals(Builder.ConnectionString, other.Builder.ConnectionString, StringComparison.OrdinalIgnoreCase) && DatabaseType == other.DatabaseType; - } + DatabaseType == other.DatabaseType && + string.Equals(Builder.ConnectionString, other.Builder.ConnectionString, StringComparison.OrdinalIgnoreCase); /// /// Equality based on Builder.ConnectionString and DatabaseType diff --git a/FAnsiSql/Discovery/DiscoveredTable.cs b/FAnsiSql/Discovery/DiscoveredTable.cs index abd83e05..6bb01b06 100644 --- a/FAnsiSql/Discovery/DiscoveredTable.cs +++ b/FAnsiSql/Discovery/DiscoveredTable.cs @@ -171,7 +171,7 @@ public DataTable GetDataTable(DatabaseOperationArgs args, int topX = int.MaxValu { var col = dt.Columns.Add(c.GetRuntimeName()); col.AllowDBNull = c.AllowNulls; - col.DataType = c.DataType.GetCSharpDataType(); + col.DataType = c.DataType?.GetCSharpDataType(); } Helper.FillDataTableWithTopX(args, this, topX, dt); @@ -441,15 +441,15 @@ public int Insert(Dictionary toInsert, CultureInfo? cu var syntaxHelper = GetQuerySyntaxHelper(); var server = Database.Server; - var _parameterNames = syntaxHelper.GetParameterNamesFor(toInsert.Keys.ToArray(), static c => c.GetRuntimeName()); + var parameterNames = syntaxHelper.GetParameterNamesFor(toInsert.Keys.ToArray(), static c => c.GetRuntimeName()); using var connection = Database.Server.GetManagedConnection(transaction); var sql = - $"INSERT INTO {GetFullyQualifiedName()}({string.Join(",", toInsert.Keys.Select(c => syntaxHelper.EnsureWrapped(c.GetRuntimeName())))}) VALUES ({string.Join(",", toInsert.Keys.Select(c => _parameterNames[c]))})"; + $"INSERT INTO {GetFullyQualifiedName()}({string.Join(",", toInsert.Keys.Select(c => syntaxHelper.EnsureWrapped(c.GetRuntimeName())))}) VALUES ({string.Join(",", toInsert.Keys.Select(c => parameterNames[c]))})"; using var cmd = server.Helper.GetCommand(sql, connection.Connection, connection.Transaction); foreach (var p in toInsert - .Select(kvp => new { kvp, parameter = server.Helper.GetParameter(_parameterNames[kvp.Key]) }) + .Select(kvp => new { kvp, parameter = server.Helper.GetParameter(parameterNames[kvp.Key]) }) .Select(t => GetQuerySyntaxHelper().GetParameter(t.parameter, t.kvp.Key, t.kvp.Value, culture))) cmd.Parameters.Add(p); @@ -545,16 +545,7 @@ public override bool Equals(object? obj) /// Based on table name, schema, database and TableType /// /// - public override int GetHashCode() - { - unchecked - { - var hashCode = StringComparer.OrdinalIgnoreCase.GetHashCode(GetSchemaWithDefaultForNull() ?? string.Empty); - hashCode = (hashCode * 397) ^ (Database != null ? Database.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (int)TableType; - return hashCode; - } - } + public override int GetHashCode() => HashCode.Combine(TableName, GetSchemaWithDefaultForNull(), Database, TableType); public DiscoveredRelationship AddForeignKey(DiscoveredColumn foreignKey, DiscoveredColumn primaryKey, bool cascadeDeletes, string? constraintName = null, DatabaseOperationArgs? args = null) => AddForeignKey(new Dictionary { { foreignKey, primaryKey } }, cascadeDeletes, constraintName, args); diff --git a/FAnsiSql/Discovery/DiscoveredTableHelper.cs b/FAnsiSql/Discovery/DiscoveredTableHelper.cs index d9740a6d..cb7252a9 100644 --- a/FAnsiSql/Discovery/DiscoveredTableHelper.cs +++ b/FAnsiSql/Discovery/DiscoveredTableHelper.cs @@ -76,7 +76,7 @@ public string ScriptTableCreation(DiscoveredTable table, bool dropPrimaryKeys, b foreach (var c in table.DiscoverColumns()) { - var sqlType = c.DataType.SQLType; + var sqlType = c.DataType?.SQLType; if (c.IsAutoIncrement && convertIdentityToInt) sqlType = "int"; @@ -90,7 +90,7 @@ public string ScriptTableCreation(DiscoveredTable table, bool dropPrimaryKeys, b var fromtt = table.Database.Server.GetQuerySyntaxHelper().TypeTranslater; var tott = toCreateTable?.Database.Server.GetQuerySyntaxHelper().TypeTranslater ?? throw new InvalidOperationException($"Unable to retrieve type translator for {toCreateTable}"); - sqlType = fromtt.TranslateSQLDBType(c.DataType.SQLType, tott); + sqlType = fromtt.TranslateSQLDBType(c.DataType?.SQLType, tott); } var colRequest = new DatabaseColumnRequest(c.GetRuntimeName(), sqlType, c.AllowNulls || dropNullability) @@ -99,7 +99,7 @@ public string ScriptTableCreation(DiscoveredTable table, bool dropPrimaryKeys, b IsAutoIncrement = c.IsAutoIncrement && !convertIdentityToInt }; - colRequest.AllowNulls = colRequest.AllowNulls && !colRequest.IsAutoIncrement; + colRequest.AllowNulls = colRequest is { AllowNulls: true, IsAutoIncrement: false }; //if there is a collation if (!string.IsNullOrWhiteSpace(c.Collation) && (toCreateTable == null || !isToDifferentDatabaseType)) diff --git a/FAnsiSql/Discovery/IDiscoveredTableHelper.cs b/FAnsiSql/Discovery/IDiscoveredTableHelper.cs index 5dabb0ac..f5681d61 100644 --- a/FAnsiSql/Discovery/IDiscoveredTableHelper.cs +++ b/FAnsiSql/Discovery/IDiscoveredTableHelper.cs @@ -15,6 +15,7 @@ public interface IDiscoveredTableHelper { /// /// The table to fetch records from + /// How many results to retrieve string GetTopXSqlForTable(IHasFullyQualifiedNameToo table, int topX); IEnumerable DiscoverColumns(DiscoveredTable discoveredTable, IManagedConnection connection, string database); diff --git a/FAnsiSql/Discovery/QuerySyntax/Aggregation/AggregateCustomLineCollection.cs b/FAnsiSql/Discovery/QuerySyntax/Aggregation/AggregateCustomLineCollection.cs index 73d13341..de6e6a0d 100644 --- a/FAnsiSql/Discovery/QuerySyntax/Aggregation/AggregateCustomLineCollection.cs +++ b/FAnsiSql/Discovery/QuerySyntax/Aggregation/AggregateCustomLineCollection.cs @@ -28,37 +28,42 @@ private void Validate() //if we have any axis bits if (Axis == null && AxisSelect == null && AxisGroupBy == null) return; //we must have all the axis bits - if(AxisSelect == null || AxisGroupBy is null or null) + if (AxisSelect == null || AxisGroupBy == null) throw new AggregateCustomLineCollectionException(FAnsiStrings.AggregateCustomLineCollection_Validate_AggregateCustomLineCollection_is_missing_some__but_not_all__Axis_components); } /// /// The single aggregate function line e.g. "count(distinct chi) as Fish," /// - public CustomLine? CountSelect => Lines.SingleOrDefault(static l => l.Role == CustomLineRole.CountFunction && l.LocationToInsert == QueryComponent.QueryTimeColumn); + public CustomLine? CountSelect => Lines.SingleOrDefault(static l => + l is { Role: CustomLineRole.CountFunction, LocationToInsert: QueryComponent.QueryTimeColumn }); /// /// The (optional) single line of SELECT SQL which is the Axis join column e.g. "[MyDb]..[mytbl].[AdmissionDate] as Admt," /// - public CustomLine? AxisSelect => Lines.SingleOrDefault(static l => l.Role == CustomLineRole.Axis && l.LocationToInsert == QueryComponent.QueryTimeColumn); + public CustomLine? AxisSelect => Lines.SingleOrDefault(static l => + l is { Role: CustomLineRole.Axis, LocationToInsert: QueryComponent.QueryTimeColumn }); /// /// The (optional) single line of GROUP BY SQL which matches exactly the SQL of /// - public CustomLine? AxisGroupBy => Lines.SingleOrDefault(static l => l.LocationToInsert == QueryComponent.GroupBy && l.Role == CustomLineRole.Axis); + public CustomLine? AxisGroupBy => Lines.SingleOrDefault(static l => + l is { LocationToInsert: QueryComponent.GroupBy, Role: CustomLineRole.Axis }); /// /// The (optional) single line of SELECT SQL which is the dynamic pivot column e.g. "[MyDb]..[mytbl].[Healthboard] as hb," /// - public CustomLine? PivotSelect => Lines.SingleOrDefault(static l => l.Role == CustomLineRole.Pivot && l.LocationToInsert == QueryComponent.QueryTimeColumn); + public CustomLine? PivotSelect => Lines.SingleOrDefault(static l => + l is { Role: CustomLineRole.Pivot, LocationToInsert: QueryComponent.QueryTimeColumn }); /// /// The (optional) single line of ORDER BY SQL which restricts which records are returned when doing a dynamic pivot e.g. only dynamic pivot on the /// top 5 drugs ordered by SUM of prescriptions /// - public CustomLine? TopXOrderBy => Lines.SingleOrDefault(static l => l.LocationToInsert == QueryComponent.OrderBy && l.Role == CustomLineRole.TopX); + public CustomLine? TopXOrderBy => Lines.SingleOrDefault(static l => + l is { LocationToInsert: QueryComponent.OrderBy, Role: CustomLineRole.TopX }); /// /// Returns all concatenated SQL for all between the inclusive boundaries from/to diff --git a/FAnsiSql/Discovery/QuerySyntax/Aggregation/AggregateHelper.cs b/FAnsiSql/Discovery/QuerySyntax/Aggregation/AggregateHelper.cs index fa2de438..9a25c488 100644 --- a/FAnsiSql/Discovery/QuerySyntax/Aggregation/AggregateHelper.cs +++ b/FAnsiSql/Discovery/QuerySyntax/Aggregation/AggregateHelper.cs @@ -4,7 +4,7 @@ namespace FAnsi.Discovery.QuerySyntax.Aggregation; -public abstract class AggregateHelper:IAggregateHelper +public abstract class AggregateHelper : IAggregateHelper { public string BuildAggregate(List queryLines, IQueryAxis? axisIfAny) { @@ -20,7 +20,7 @@ public string BuildAggregate(List queryLines, IQueryAxis? axisIfAny) //pivot (no axis) if (lines.AxisSelect == null) - return BuildPivotOnlyAggregate(lines,GetPivotOnlyNonPivotColumn(lines)); + return BuildPivotOnlyAggregate(lines, GetPivotOnlyNonPivotColumn(lines)); //pivot and axis return BuildPivotAndAxisAggregate(lines); @@ -28,8 +28,8 @@ public string BuildAggregate(List queryLines, IQueryAxis? axisIfAny) private static CustomLine GetPivotOnlyNonPivotColumn(AggregateCustomLineCollection query) { - var nonPivotColumn = query.Lines.Where(static l => l.LocationToInsert == QueryComponent.QueryTimeColumn && l.Role == CustomLineRole.None).ToArray(); - if(nonPivotColumn.Length != 1) + var nonPivotColumn = query.Lines.Where(static l => l is { LocationToInsert: QueryComponent.QueryTimeColumn, Role: CustomLineRole.None }).ToArray(); + if (nonPivotColumn.Length != 1) throw new Exception("Pivot is only valid when there are 3 SELECT columns, an aggregate (e.g. count(*)), a pivot and a final column"); return nonPivotColumn[0]; @@ -48,7 +48,7 @@ private static CustomLine GetPivotOnlyNonPivotColumn(AggregateCustomLineCollecti /// protected abstract string BuildAxisAggregate(AggregateCustomLineCollection query); - protected abstract string BuildPivotOnlyAggregate(AggregateCustomLineCollection query,CustomLine nonPivotColumn); + protected abstract string BuildPivotOnlyAggregate(AggregateCustomLineCollection query, CustomLine nonPivotColumn); protected abstract string BuildPivotAndAxisAggregate(AggregateCustomLineCollection query); @@ -60,21 +60,25 @@ private static CustomLine GetPivotOnlyNonPivotColumn(AggregateCustomLineCollecti /// /// /// - protected void WrapAxisColumnWithDatePartFunction(AggregateCustomLineCollection query, string axisColumnAlias) + protected void WrapAxisColumnWithDatePartFunction(AggregateCustomLineCollection query, string? axisColumnAlias) { - if(string.IsNullOrWhiteSpace(axisColumnAlias)) - throw new ArgumentNullException(nameof(axisColumnAlias)); + ArgumentException.ThrowIfNullOrWhiteSpace(axisColumnAlias, nameof(axisColumnAlias)); + if (query.Axis?.AxisIncrement == null) + throw new InvalidOperationException("No axis increment in query"); - var axisGroupBy = query.AxisGroupBy; - var axisColumnWithoutAlias = query.AxisSelect.GetTextWithoutAlias(query.SyntaxHelper); + var axisGroupBy = query.AxisGroupBy ?? + throw new InvalidOperationException("No axis grouping in query"); + var axisColumnWithoutAlias = query.AxisSelect?.GetTextWithoutAlias(query.SyntaxHelper) ?? + throw new InvalidOperationException("No axis column in query"); - var axisColumnEndedWithComma = query.AxisSelect.Text.EndsWith(','); - query.AxisSelect.Text = - $"{GetDatePartOfColumn(query.Axis?.AxisIncrement ?? throw new InvalidOperationException("No axis in query"), axisColumnWithoutAlias)} AS {axisColumnAlias}{(axisColumnEndedWithComma ? "," : "")}"; + var axisColumnEndedWithComma = query.AxisSelect?.Text.EndsWith(',') == true; + if (query.AxisSelect != null) + query.AxisSelect.Text = + $"{GetDatePartOfColumn(query.Axis.AxisIncrement, axisColumnWithoutAlias)} AS {axisColumnAlias}{(axisColumnEndedWithComma ? "," : "")}"; var groupByEndedWithComma = axisGroupBy.Text.EndsWith(','); axisGroupBy.Text = GetDatePartOfColumn(query.Axis.AxisIncrement, axisColumnWithoutAlias) + (groupByEndedWithComma ? "," : ""); } - public abstract string GetDatePartOfColumn(AxisIncrement increment, string columnSql); + public abstract string GetDatePartOfColumn(AxisIncrement? axisIncrement, string columnSql); } \ No newline at end of file diff --git a/FAnsiSql/Discovery/QuerySyntax/IQuerySyntaxHelper.cs b/FAnsiSql/Discovery/QuerySyntax/IQuerySyntaxHelper.cs index c97cfc98..9d7d99b6 100644 --- a/FAnsiSql/Discovery/QuerySyntax/IQuerySyntaxHelper.cs +++ b/FAnsiSql/Discovery/QuerySyntax/IQuerySyntaxHelper.cs @@ -90,7 +90,7 @@ public interface IQuerySyntaxHelper /// /// /// - string Escape(string sql); + string? Escape(string? sql); TopXResponse HowDoWeAchieveTopX(int x); string GetParameterDeclaration(string proposedNewParameterName, DatabaseTypeRequest request); @@ -138,7 +138,7 @@ public interface IQuerySyntaxHelper /// /// /// If was badly formed, blank etc - void SplitLineIntoOuterMostMethodAndContents(string lineToSplit, out string method, out string contents); + void SplitLineIntoOuterMostMethodAndContents(string? lineToSplit, out string method, out string contents); /// /// The SQL that would be valid for a CREATE TABLE statement that would result in a given column becoming auto increment e.g. "IDENTITY(1,1)" diff --git a/FAnsiSql/Discovery/QuerySyntax/QueryComponent.cs b/FAnsiSql/Discovery/QuerySyntax/QueryComponent.cs index e7014a6f..86e3288a 100644 --- a/FAnsiSql/Discovery/QuerySyntax/QueryComponent.cs +++ b/FAnsiSql/Discovery/QuerySyntax/QueryComponent.cs @@ -14,5 +14,4 @@ public enum QueryComponent Having, OrderBy, Postfix //after everything else in the query (including WHERE containers and any ORDER BYs) - } \ No newline at end of file diff --git a/FAnsiSql/Discovery/QuerySyntaxHelper.cs b/FAnsiSql/Discovery/QuerySyntaxHelper.cs index ee6ed770..ca99917d 100644 --- a/FAnsiSql/Discovery/QuerySyntaxHelper.cs +++ b/FAnsiSql/Discovery/QuerySyntaxHelper.cs @@ -64,7 +64,7 @@ public abstract partial class QuerySyntaxHelper( public ITypeTranslater TypeTranslater { get; private set; } = translater; - private readonly Dictionary factories = []; + private readonly Dictionary _factories = []; public IAggregateHelper AggregateHelper { get; private set; } = aggregateHelper; public IUpdateHelper UpdateHelper { get; set; } = updateHelper; @@ -89,7 +89,7 @@ public static HashSet GetAllParameterNamesFromQuery(string query) if (string.IsNullOrWhiteSpace(query)) return []; - var toReturn = new HashSet(ParameterNameRegex.Matches(query).Cast().Select(static match => match.Groups[1].Value.Trim()), StringComparer.InvariantCultureIgnoreCase); + var toReturn = new HashSet(ParameterNameRegex.Matches(query).Select(static match => match.Groups[1].Value.Trim()), StringComparer.InvariantCultureIgnoreCase); return toReturn; } @@ -178,7 +178,7 @@ public virtual string EnsureFullyQualified(string? databaseName, string? schema, : //table valued functions do not support database name being in the column level selection list area of sql queries $"{EnsureFullyQualified(databaseName, schema, tableName)}.{EnsureWrapped(GetRuntimeName(columnName))}"; - public virtual string Escape(string sql) => string.IsNullOrWhiteSpace(sql) ? sql : sql.Replace("'", "''"); + public virtual string? Escape([NotNullIfNotNull(nameof(sql))] string? sql) => string.IsNullOrWhiteSpace(sql) ? sql : sql.Replace("'", "''"); public abstract TopXResponse HowDoWeAchieveTopX(int x); public virtual string GetParameterDeclaration(string proposedNewParameterName, DatabaseTypeRequest request) => GetParameterDeclaration(proposedNewParameterName, TypeTranslater.GetSQLDBTypeForCSharpType(request)); @@ -224,7 +224,7 @@ public virtual bool SplitLineIntoSelectSQLAndAlias(string lineToSplit, out strin public abstract string GetScalarFunctionSql(MandatoryScalarFunctions function); /// - public void SplitLineIntoOuterMostMethodAndContents(string lineToSplit, out string method, out string contents) + public void SplitLineIntoOuterMostMethodAndContents(string? lineToSplit, out string method, out string contents) { if (string.IsNullOrWhiteSpace(lineToSplit)) throw new ArgumentException( @@ -262,11 +262,7 @@ public static string MakeHeaderNameSensible(string header) //replace anything that isn't a digit, letter or underscore with emptiness (except spaces - these will go but first...) //also accept anything above ASCII 256 - var r = HeaderNameCharRegex(); - - var adjustedHeader = r.Replace(header, ""); - - var sb = new StringBuilder(adjustedHeader); + var sb = new StringBuilder(HeaderNameCharRegex().Replace(header, "")); //Camel case after spaces for (var i = 0; i < sb.Length; i++) @@ -275,13 +271,10 @@ public static string MakeHeaderNameSensible(string header) //and that character is a lower case letter sb[i + 1] = char.ToUpper(sb[i + 1]); - adjustedHeader = sb.ToString().Replace(" ", ""); + sb.Replace(" ", ""); //if it starts with a digit (illegal) put an underscore before it - if (StartsDigitsRe().IsMatch(adjustedHeader)) - adjustedHeader = $"_{adjustedHeader}"; - - return adjustedHeader; + return char.IsAsciiDigit(sb[0]) ? $"_{sb}" : sb.ToString(); } public string GetSensibleEntityNameFromString(string? potentiallyDodgyName) @@ -302,7 +295,7 @@ public string GetSensibleEntityNameFromString(string? potentiallyDodgyName) public abstract string GetAutoIncrementKeywordIfAny(); public abstract Dictionary GetSQLFunctionsDictionary(); - public bool IsBasicallyNull(object value) + public bool IsBasicallyNull(object? value) { if (value is string stringValue) return string.IsNullOrWhiteSpace(stringValue); @@ -328,18 +321,18 @@ public DbParameter GetParameter(DbParameter p, DiscoveredColumn discoveredColumn { culture ??= CultureInfo.InvariantCulture; - if (!factories.ContainsKey(culture)) - factories.Add(culture, new TypeDeciderFactory(culture)); + if (!_factories.ContainsKey(culture)) + _factories.Add(culture, new TypeDeciderFactory(culture)); var tt = TypeTranslater; - p.DbType = tt.GetDbTypeForSQLDBType(discoveredColumn.DataType.SQLType); - var cSharpType = tt.GetCSharpTypeForSQLDBType(discoveredColumn.DataType.SQLType); + p.DbType = tt.GetDbTypeForSQLDBType(discoveredColumn.DataType?.SQLType); + var cSharpType = tt.GetCSharpTypeForSQLDBType(discoveredColumn.DataType?.SQLType); if (IsBasicallyNull(value)) p.Value = DBNull.Value; - else if (value is string strVal && factories[culture].IsSupported(cSharpType)) //if the input is a string and it's for a hard type e.g. TimeSpan + else if (value is string strVal && _factories[culture].IsSupported(cSharpType)) //if the input is a string and it's for a hard type e.g. TimeSpan { - var decider = factories[culture].Create(cSharpType); + var decider = _factories[culture].Create(cSharpType); var o = decider.Parse(strVal); if (o is DateTime d) o = FormatDateTimeForDbParameter(d); @@ -444,7 +437,7 @@ public bool IsValidColumnName(string columnName, [NotNullWhen(false)] out string protected virtual object FormatTimespanForDbParameter(TimeSpan timeSpan) => timeSpan; #region Equality Members - protected bool Equals(QuerySyntaxHelper other) + protected bool Equals(QuerySyntaxHelper? other) { if (other == null) return false; @@ -473,7 +466,7 @@ public Dictionary GetParameterNamesFor(T[] columns, Func GetParameterNamesFor(T[] columns, Func /// /// - string GetSQLDBTypeForCSharpType(DatabaseTypeRequest request); + string GetSQLDBTypeForCSharpType(DatabaseTypeRequest? request); /// /// Returns the System.Data.DbType (e.g. DbType.String) for the specified proprietary database type (e.g. "varchar(max)") @@ -29,7 +29,7 @@ public interface ITypeTranslater /// /// /// - DbType GetDbTypeForSQLDBType(string sqlType); + DbType GetDbTypeForSQLDBType(string? sqlType); /// @@ -80,5 +80,5 @@ public interface ITypeTranslater /// /// /// - string TranslateSQLDBType(string sqlType, ITypeTranslater destinationTypeTranslater); + string? TranslateSQLDBType(string? sqlType, ITypeTranslater destinationTypeTranslater); } \ No newline at end of file diff --git a/FAnsiSql/Discovery/TypeTranslation/TypeTranslater.cs b/FAnsiSql/Discovery/TypeTranslation/TypeTranslater.cs index 939df941..4cbc8e3f 100644 --- a/FAnsiSql/Discovery/TypeTranslation/TypeTranslater.cs +++ b/FAnsiSql/Discovery/TypeTranslation/TypeTranslater.cs @@ -53,8 +53,10 @@ protected TypeTranslater(Regex dateRegex, int maxStringWidthBeforeMax, int strin StringWidthWhenNotSupplied = stringWidthWhenNotSupplied; } - public string GetSQLDBTypeForCSharpType(DatabaseTypeRequest request) + public string GetSQLDBTypeForCSharpType(DatabaseTypeRequest? request) { + ArgumentNullException.ThrowIfNull(request); + var t = request.CSharpType; if (t == typeof(bool) || t == typeof(bool?)) @@ -62,7 +64,7 @@ public string GetSQLDBTypeForCSharpType(DatabaseTypeRequest request) if (t == typeof(byte)) return GetByteDataType(); - + if (t == typeof(short) || t == typeof(short) || t == typeof(ushort) || t == typeof(short?) || t == typeof(ushort?)) return GetSmallIntDataType(); @@ -98,7 +100,7 @@ public string GetSQLDBTypeForCSharpType(DatabaseTypeRequest request) private static string GetByteDataType() => "tinyint"; - private static string GetFloatingPointDataType(DecimalSize decimalSize) + private static string GetFloatingPointDataType(DecimalSize? decimalSize) { if (decimalSize == null || decimalSize.IsEmpty) return "decimal(20,10)"; @@ -115,7 +117,7 @@ protected string GetStringDataType(int? maxExpectedStringWidth) if (maxExpectedStringWidth > MaxStringWidthBeforeMax) return GetStringDataTypeWithUnlimitedWidth(); - + return GetStringDataTypeImpl(maxExpectedStringWidth.Value); } @@ -131,7 +133,7 @@ private string GetUnicodeStringDataType(int? maxExpectedStringWidth) if (maxExpectedStringWidth > MaxStringWidthBeforeMax) return GetUnicodeStringDataTypeWithUnlimitedWidth(); - + return GetUnicodeStringDataTypeImpl(maxExpectedStringWidth.Value); } @@ -210,8 +212,13 @@ public Type GetCSharpTypeForSQLDBType(string? sqlType) => public bool IsSupportedSQLDBType(string sqlType) => TryGetCSharpTypeForSQLDBType(sqlType) != null; /// - public DbType GetDbTypeForSQLDBType(string sqlType) + public DbType GetDbTypeForSQLDBType(string? sqlType) { + if (string.IsNullOrWhiteSpace(sqlType)) + throw new TypeNotMappedException(string.Format( + FAnsiStrings + .TypeTranslater_GetCSharpTypeForSQLDBType_No_CSharp_type_mapping_exists_for_SQL_type___0____TypeTranslater_was___1___, + sqlType, GetType().Name)); if (IsBit(sqlType)) return DbType.Boolean; @@ -233,13 +240,13 @@ public DbType GetDbTypeForSQLDBType(string sqlType) if (IsString(sqlType)) return DbType.String; - + if (IsDate(sqlType)) return DbType.DateTime; if (IsTime(sqlType)) return DbType.Time; - + if (IsByteArray(sqlType)) return DbType.Object; @@ -252,7 +259,7 @@ public DbType GetDbTypeForSQLDBType(string sqlType) sqlType, GetType().Name)); } - public virtual DatabaseTypeRequest GetDataTypeRequestForSQLDBType(string sqlType) + public virtual DatabaseTypeRequest GetDataTypeRequestForSQLDBType(string? sqlType) { var cSharpType = GetCSharpTypeForSQLDBType(sqlType); @@ -269,11 +276,10 @@ public virtual DatabaseTypeRequest GetDataTypeRequestForSQLDBType(string sqlType if (cSharpType == typeof(TimeSpan)) lengthIfString = GetStringLengthForTimeSpan(); - + var request = new DatabaseTypeRequest(cSharpType, lengthIfString, digits); - if (cSharpType == typeof(string)) - request.Unicode = IsUnicode(sqlType); + if (cSharpType == typeof(string) && sqlType != null) request.Unicode = IsUnicode(sqlType); return request; } @@ -284,20 +290,20 @@ public virtual DatabaseTypeRequest GetDataTypeRequestForSQLDBType(string sqlType /// /// /// - private static bool IsUnicode(string sqlType) => sqlType != null && sqlType.StartsWith("n", StringComparison.CurrentCultureIgnoreCase); + private static bool IsUnicode(string sqlType) => sqlType.StartsWith("n", StringComparison.OrdinalIgnoreCase); public virtual Guesser GetGuesserFor(DiscoveredColumn discoveredColumn) => GetGuesserFor(discoveredColumn, 0); protected Guesser GetGuesserFor(DiscoveredColumn discoveredColumn, int extraLengthPerNonAsciiCharacter) { - var reqType = GetDataTypeRequestForSQLDBType(discoveredColumn.DataType.SQLType); + var reqType = GetDataTypeRequestForSQLDBType(discoveredColumn.DataType?.SQLType); return new Guesser(reqType) { ExtraLengthPerNonAsciiCharacter = extraLengthPerNonAsciiCharacter }; } - public virtual int GetLengthIfString(string sqlType) + public virtual int GetLengthIfString(string? sqlType) { if (string.IsNullOrWhiteSpace(sqlType)) return -1; @@ -315,7 +321,7 @@ public virtual int GetLengthIfString(string sqlType) return -1; } - public DecimalSize? GetDigitsBeforeAndAfterDecimalPointIfDecimal(string sqlType) + public DecimalSize? GetDigitsBeforeAndAfterDecimalPointIfDecimal(string? sqlType) { if (string.IsNullOrWhiteSpace(sqlType)) return null; @@ -328,7 +334,7 @@ public virtual int GetLengthIfString(string sqlType) return new DecimalSize(precision - scale, scale); } - public string TranslateSQLDBType(string sqlType, ITypeTranslater destinationTypeTranslater) + public string TranslateSQLDBType(string? sqlType, ITypeTranslater destinationTypeTranslater) { //e.g. data_type is datetime2 (i.e. Sql Server), this returns System.DateTime var requested = GetDataTypeRequestForSQLDBType(sqlType); diff --git a/FAnsiSql/Implementation/ImplementationManager.cs b/FAnsiSql/Implementation/ImplementationManager.cs index 2df7a1d7..705296b6 100644 --- a/FAnsiSql/Implementation/ImplementationManager.cs +++ b/FAnsiSql/Implementation/ImplementationManager.cs @@ -62,7 +62,7 @@ public static IImplementation GetImplementation(DbConnection connection) .ImplementationManager_GetImplementation_No_implementation_found_for_ADO_Net_object_of_Type__0_, connection.GetType())); } - private static IImplementation GetImplementation(Func condition, string errorIfNotFound) => Instance?._implementations.FirstOrDefault(condition)??throw new ImplementationNotFoundException(errorIfNotFound); + private static IImplementation GetImplementation(Func condition, string errorIfNotFound) => Instance._implementations.FirstOrDefault(condition)??throw new ImplementationNotFoundException(errorIfNotFound); /// /// Returns all currently loaded implementations or null if no implementations have been loaded diff --git a/FAnsiSql/Implementations/MicrosoftSQL/Aggregation/MicrosoftSQLAggregateHelper.cs b/FAnsiSql/Implementations/MicrosoftSQL/Aggregation/MicrosoftSQLAggregateHelper.cs index 46b294ff..9b2c879c 100644 --- a/FAnsiSql/Implementations/MicrosoftSQL/Aggregation/MicrosoftSQLAggregateHelper.cs +++ b/FAnsiSql/Implementations/MicrosoftSQL/Aggregation/MicrosoftSQLAggregateHelper.cs @@ -47,7 +47,7 @@ SELECT @currentDate /// /// /// - public override string GetDatePartOfColumn(AxisIncrement increment, string columnSql) => + public override string GetDatePartOfColumn(AxisIncrement? increment, string columnSql) => increment switch { AxisIncrement.Day => @@ -90,10 +90,12 @@ public string GetDatePartBasedEqualsBetweenColumns(AxisIncrement increment, stri protected override string BuildAxisAggregate(AggregateCustomLineCollection query) { - var countAlias = query.CountSelect.GetAliasFromText(query.SyntaxHelper); - var axisColumnAlias = query.AxisSelect.GetAliasFromText(query.SyntaxHelper) ?? "joinDt"; + ArgumentNullException.ThrowIfNull(query.Axis); - WrapAxisColumnWithDatePartFunction(query,axisColumnAlias); + var countAlias = query.CountSelect?.GetAliasFromText(query.SyntaxHelper); + var axisColumnAlias = query.AxisSelect?.GetAliasFromText(query.SyntaxHelper) ?? "joinDt"; + + WrapAxisColumnWithDatePartFunction(query, axisColumnAlias); return string.Format( @@ -130,6 +132,7 @@ ORDER BY protected override string BuildPivotAndAxisAggregate(AggregateCustomLineCollection query) { + ArgumentNullException.ThrowIfNull(query.Axis); var syntaxHelper = query.SyntaxHelper; var part1 = GetPivotPart1(query, out var pivotAlias, out var countAlias, out var axisColumnAlias); @@ -246,21 +249,21 @@ ORDER BY return part1 + part2; } - private string GetPivotPart1(AggregateCustomLineCollection query, out string pivotAlias, out string countAlias, out string? axisColumnAlias) + private string GetPivotPart1(AggregateCustomLineCollection query, out string? pivotAlias, out string? countAlias, out string? axisColumnAlias) { var syntaxHelper = query.SyntaxHelper; //find the pivot column e.g. 'hb_extract AS Healthboard' var pivotSelectLine = query.PivotSelect; - var pivotSqlWithoutAlias = pivotSelectLine.GetTextWithoutAlias(syntaxHelper); - pivotAlias = pivotSelectLine.GetAliasFromText(syntaxHelper); + var pivotSqlWithoutAlias = pivotSelectLine?.GetTextWithoutAlias(syntaxHelper); + pivotAlias = pivotSelectLine?.GetAliasFromText(syntaxHelper); //ensure it has an RHS if (string.IsNullOrWhiteSpace(pivotAlias)) pivotAlias = syntaxHelper.GetRuntimeName(pivotSqlWithoutAlias); - var countSqlWithoutAlias = query.CountSelect.GetTextWithoutAlias(syntaxHelper); - countAlias = query.CountSelect.GetAliasFromText(syntaxHelper); + var countSqlWithoutAlias = query.CountSelect?.GetTextWithoutAlias(syntaxHelper); + countAlias = query.CountSelect?.GetAliasFromText(syntaxHelper); var axisColumnWithoutAlias = query.AxisSelect?.GetTextWithoutAlias(query.SyntaxHelper); axisColumnAlias = query.AxisSelect?.GetAliasFromText(query.SyntaxHelper) ?? "joinDt"; diff --git a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftQuerySyntaxHelper.cs b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftQuerySyntaxHelper.cs index 612a6fdf..c76b22df 100644 --- a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftQuerySyntaxHelper.cs +++ b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftQuerySyntaxHelper.cs @@ -87,7 +87,7 @@ 3617 when string.IsNullOrWhiteSpace(sqlE.Message) => true, /// /// /// - private string? GetRuntimeNameWithDoubledClosingSquareBrackets(string s) => GetRuntimeName(s)?.Replace("]", "]]"); + private string GetRuntimeNameWithDoubledClosingSquareBrackets(string s) => GetRuntimeName(s).Replace("]", "]]"); public override string EnsureFullyQualified(string? databaseName, string? schema, string tableName) { diff --git a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLBulkCopy.cs b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLBulkCopy.cs index d41ec3a5..2a8cf64f 100644 --- a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLBulkCopy.cs +++ b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLBulkCopy.cs @@ -121,33 +121,35 @@ private Exception AttemptLineByLineInsert(Exception e, SqlBulkCopy insert, DataT foreach (DataRow dr in dt.Rows) try { - investigationOneLineAtATime.WriteToServer(new[] { dr }); //try one line + investigationOneLineAtATime.WriteToServer([dr]); //try one line line++; } catch (Exception exception) { - if (BcpColIdToString(investigationOneLineAtATime, exception as SqlException, out var result, out var badMapping)) - { - if (badMapping is null || !dt.Columns.Contains(badMapping.SourceColumn)) - return new Exception( - string.Format( - SR - .MicrosoftSQLBulkCopy_AttemptLineByLineInsert_BulkInsert_failed_on_data_row__0___1_, - line, result), e); - - var sourceValue = dr[badMapping.SourceColumn]; - var destColumn = TargetTableColumns.SingleOrDefault(c =>c.GetRuntimeName().Equals(badMapping.DestinationColumn)); - - if (destColumn != null) - return new FileLoadException( - string.Format(SR.MicrosoftSQLBulkCopy_AttemptLineByLineInsert_BulkInsert_failed_on_data_row__0__the_complaint_was_about_source_column____1____which_had_value____2____destination_data_type_was____3____4__5_, line, badMapping.SourceColumn, sourceValue, destColumn.DataType, Environment.NewLine, result), exception); - - return new Exception(string.Format(SR.MicrosoftSQLBulkCopy_AttemptLineByLineInsert_BulkInsert_failed_on_data_row__0___1_, line, result), e); - } - - return new FileLoadException( - string.Format(SR.MicrosoftSQLBulkCopy_AttemptLineByLineInsert_Second_Pass_Exception__Failed_to_load_data_row__0__the_following_values_were_rejected_by_the_database___1__2__3_, line, Environment.NewLine, string.Join(Environment.NewLine,dr.ItemArray), firstPass), - exception); + if (!BcpColIdToString(investigationOneLineAtATime, exception as SqlException, out var result, + out var badMapping)) + return new FileLoadException( + string.Format( + SR + .MicrosoftSQLBulkCopy_AttemptLineByLineInsert_Second_Pass_Exception__Failed_to_load_data_row__0__the_following_values_were_rejected_by_the_database___1__2__3_, + line, Environment.NewLine, string.Join(Environment.NewLine, dr.ItemArray), firstPass), + exception); + + if (badMapping is null || !dt.Columns.Contains(badMapping.SourceColumn)) + return new Exception( + string.Format( + SR + .MicrosoftSQLBulkCopy_AttemptLineByLineInsert_BulkInsert_failed_on_data_row__0___1_, + line, result), e); + + var sourceValue = dr[badMapping.SourceColumn]; + var destColumn = TargetTableColumns.SingleOrDefault(c =>c.GetRuntimeName().Equals(badMapping.DestinationColumn)); + + if (destColumn != null) + return new FileLoadException( + string.Format(SR.MicrosoftSQLBulkCopy_AttemptLineByLineInsert_BulkInsert_failed_on_data_row__0__the_complaint_was_about_source_column____1____which_had_value____2____destination_data_type_was____3____4__5_, line, badMapping.SourceColumn, sourceValue, destColumn.DataType, Environment.NewLine, result), exception); + + return new Exception(string.Format(SR.MicrosoftSQLBulkCopy_AttemptLineByLineInsert_BulkInsert_failed_on_data_row__0___1_, line, result), e); } //it worked... how!? diff --git a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLColumnHelper.cs b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLColumnHelper.cs index ba167349..6f741bbf 100644 --- a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLColumnHelper.cs +++ b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLColumnHelper.cs @@ -22,7 +22,7 @@ public string GetTopXSqlForColumn(IHasRuntimeName database, IHasFullyQualifiedNa public string GetAlterColumnToSql(DiscoveredColumn column, string newType, bool allowNulls) { - if (column.DataType.SQLType != "bit" || newType == "bit") + if (column.DataType?.SQLType != "bit" || newType == "bit") return $"ALTER TABLE {column.Table.GetFullyQualifiedName()} ALTER COLUMN {column.GetWrappedName()} {newType} {(allowNulls ? "NULL" : "NOT NULL")}"; diff --git a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLDatabaseHelper.cs b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLDatabaseHelper.cs index 52a00495..3d88e93f 100644 --- a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLDatabaseHelper.cs +++ b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLDatabaseHelper.cs @@ -51,23 +51,20 @@ public override IEnumerable ListTables(DiscoveredDatabase paren public override IEnumerable ListTableValuedFunctions(DiscoveredDatabase parent, IQuerySyntaxHelper querySyntaxHelper, DbConnection connection, string database, DbTransaction? transaction = null) { - using (DbCommand cmd = new SqlCommand( - $"use {querySyntaxHelper.EnsureWrapped(database)};select name,\r\n (select name from sys.schemas s where s.schema_id = o.schema_id) as schema_name\r\n from sys.objects o\r\nWHERE type_desc = 'SQL_INLINE_TABLE_VALUED_FUNCTION' OR type_desc = 'SQL_TABLE_VALUED_FUNCTION' OR type_desc ='CLR_TABLE_VALUED_FUNCTION'", (SqlConnection)connection)) + using DbCommand cmd = new SqlCommand( + $"use {querySyntaxHelper.EnsureWrapped(database)};select name,\r\n (select name from sys.schemas s where s.schema_id = o.schema_id) as schema_name\r\n from sys.objects o\r\nWHERE type_desc = 'SQL_INLINE_TABLE_VALUED_FUNCTION' OR type_desc = 'SQL_TABLE_VALUED_FUNCTION' OR type_desc ='CLR_TABLE_VALUED_FUNCTION'", (SqlConnection)connection); + cmd.Transaction = transaction; + using var r = cmd.ExecuteReader(); + while (r.Read()) { - cmd.Transaction = transaction; - - using var r = cmd.ExecuteReader(); - while (r.Read()) - { - var schema = r["schema_name"] as string; - - if (string.Equals("dbo", schema)) - schema = null; - var name = r["name"].ToString(); - if (name != null) - yield return new DiscoveredTableValuedFunction(parent, name, querySyntaxHelper, schema); - } + var schema = r["schema_name"] as string; + + if (string.Equals("dbo", schema)) + schema = null; + var name = r["name"].ToString(); + if (name != null) + yield return new DiscoveredTableValuedFunction(parent, name, querySyntaxHelper, schema); } } @@ -89,7 +86,7 @@ public override IEnumerable ListStoredprocedures(DbCo public override void DropDatabase(DiscoveredDatabase database) { - var userIsCurrentlyInDatabase = database.Server.GetCurrentDatabase().GetRuntimeName().Equals(database.GetRuntimeName()); + var userIsCurrentlyInDatabase = database.Server.GetCurrentDatabase()?.GetRuntimeName().Equals(database.GetRuntimeName()) == true; var serverConnectionBuilder = new SqlConnectionStringBuilder(database.Server.Builder.ConnectionString); if (userIsCurrentlyInDatabase) @@ -168,7 +165,7 @@ INNER JOIN sys.[databases] d WHERE d.[name] = 'master' AND type = 0 """; - string dataFolder; + string? dataFolder; // Create a new server so we don't mutate database.Server and cause a whole lot of side-effects in other code, e.g. attachers var server = database.Server; @@ -194,12 +191,12 @@ INNER JOIN sys.[databases] d // detach! sql = $@"EXEC sys.sp_detach_db '{dbLiteralName}';"; - using(var cmd = new SqlCommand(sql, con)) + using (var cmd = new SqlCommand(sql, con)) cmd.ExecuteNonQuery(); // get data-files path from SQL Server - using(var cmd = new SqlCommand(getDefaultSqlServerDatabaseDirectory, con)) - dataFolder = (string)cmd.ExecuteScalar(); + using (var cmd = new SqlCommand(getDefaultSqlServerDatabaseDirectory, con)) + dataFolder = cmd.ExecuteScalar() as string; return dataFolder == null ? null : new DirectoryInfo(dataFolder); } diff --git a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLTableHelper.cs b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLTableHelper.cs index f4d8c2fd..9485caf1 100644 --- a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLTableHelper.cs +++ b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLTableHelper.cs @@ -40,7 +40,8 @@ public override IEnumerable DiscoverColumns(DiscoveredTable di ? $"{discoveredTable.GetRuntimeName()}.{r["COLUMN_NAME"]}" : r["COLUMN_NAME"].ToString(); - var toAdd = new DiscoveredColumn(discoveredTable, columnName, isNullable) + var toAdd = new DiscoveredColumn(discoveredTable, + columnName ?? throw new InvalidOperationException("Anonymous column found"), isNullable) { IsAutoIncrement = Convert.ToBoolean(r["is_identity"]), Collation = r["collation_name"] as string @@ -56,7 +57,7 @@ public override IEnumerable DiscoverColumns(DiscoveredTable di /// /// /// - private static string? GetObjectName(DiscoveredTable table) + private static string GetObjectName(DiscoveredTable table) { var syntax = table.GetQuerySyntaxHelper(); @@ -160,7 +161,9 @@ public override void CreatePrimaryKey(DatabaseOperationArgs args, DiscoveredTabl { using var connection = args.GetManagedConnection(table); var columnHelper = GetColumnHelper(); - foreach (var alterSql in discoverColumns.Where(static dc => dc.AllowNulls).Select(col => columnHelper.GetAlterColumnToSql(col, col.DataType.SQLType, false))) + foreach (var alterSql in discoverColumns.Where(static dc => dc.AllowNulls).Select(col => + columnHelper.GetAlterColumnToSql(col, + col.DataType?.SQLType ?? throw new InvalidOperationException("Missing type"), false))) { using var alterCmd = table.GetCommand(alterSql, connection.Connection, connection.Transaction); args.ExecuteNonQuery(alterCmd); @@ -249,7 +252,9 @@ public override DiscoveredRelationship[] DiscoverRelationships(DiscoveredTable t toReturn.Add(current.Name, current); } - current.AddKeys(r["PKCOLUMN_NAME"].ToString(), r["FKCOLUMN_NAME"].ToString(), transaction); + current.AddKeys( + r["PKCOLUMN_NAME"].ToString() ?? throw new InvalidOperationException("Unnamed primary key column"), + r["FKCOLUMN_NAME"].ToString() ?? throw new InvalidOperationException("Unnamed foreign key column"), transaction); } } diff --git a/FAnsiSql/Implementations/MySql/Aggregation/MySqlAggregateHelper.cs b/FAnsiSql/Implementations/MySql/Aggregation/MySqlAggregateHelper.cs index 7fed9fb3..bb3d3caa 100644 --- a/FAnsiSql/Implementations/MySql/Aggregation/MySqlAggregateHelper.cs +++ b/FAnsiSql/Implementations/MySql/Aggregation/MySqlAggregateHelper.cs @@ -8,21 +8,28 @@ namespace FAnsi.Implementations.MySql.Aggregation; public sealed class MySqlAggregateHelper : AggregateHelper { public static readonly MySqlAggregateHelper Instance = new(); - private MySqlAggregateHelper() { } - private static string GetDateAxisTableDeclaration(IQueryAxis axis) + + private MySqlAggregateHelper() + { + } + + private static string GetDateAxisTableDeclaration(IQueryAxis? axis) { + ArgumentNullException.ThrowIfNull(axis); + //if the axis is days then there are likely to be thousands of them but if we start adding thousands of years //mysql date falls over with overflow exceptions var thousands = - axis.AxisIncrement == AxisIncrement.Day ? - """ - JOIN - (SELECT 0 thousands - UNION ALL SELECT 1000 UNION ALL SELECT 2000 UNION ALL SELECT 3000 - UNION ALL SELECT 4000 UNION ALL SELECT 5000 UNION ALL SELECT 6000 - UNION ALL SELECT 7000 UNION ALL SELECT 8000 UNION ALL SELECT 9000 - ) thousands - """ : ""; + axis.AxisIncrement == AxisIncrement.Day + ? """ + JOIN + (SELECT 0 thousands + UNION ALL SELECT 1000 UNION ALL SELECT 2000 UNION ALL SELECT 3000 + UNION ALL SELECT 4000 UNION ALL SELECT 5000 UNION ALL SELECT 6000 + UNION ALL SELECT 7000 UNION ALL SELECT 8000 UNION ALL SELECT 9000 + ) thousands + """ + : ""; var plusThousands = axis.AxisIncrement == AxisIncrement.Day ? "+ thousands" : ""; @@ -68,7 +75,7 @@ UNION ALL SELECT 700 UNION ALL SELECT 800 UNION ALL SELECT 900 """; } - public override string GetDatePartOfColumn(AxisIncrement increment, string columnSql) + public override string GetDatePartOfColumn(AxisIncrement? increment, string columnSql) { return increment switch { @@ -85,8 +92,8 @@ public override string GetDatePartOfColumn(AxisIncrement increment, string colum protected override string BuildAxisAggregate(AggregateCustomLineCollection query) { - var countAlias = query.CountSelect.GetAliasFromText(query.SyntaxHelper); - var axisColumnAlias = query.AxisSelect.GetAliasFromText(query.SyntaxHelper) ?? "joinDt"; + var countAlias = query.CountSelect?.GetAliasFromText(query.SyntaxHelper); + var axisColumnAlias = query.AxisSelect?.GetAliasFromText(query.SyntaxHelper) ?? "joinDt"; WrapAxisColumnWithDatePartFunction(query, axisColumnAlias); @@ -114,7 +121,7 @@ ORDER BY string.Join(Environment.NewLine, query.Lines.Where(static c => c.LocationToInsert < QueryComponent.SELECT)), GetDateAxisTableDeclaration(query.Axis), - GetDatePartOfColumn(query.Axis.AxisIncrement, "dateAxis.dt"), + GetDatePartOfColumn(query.Axis?.AxisIncrement, "dateAxis.dt"), countAlias, //the entire query @@ -126,6 +133,9 @@ ORDER BY protected override string BuildPivotAndAxisAggregate(AggregateCustomLineCollection query) { + ArgumentNullException.ThrowIfNull(query.Axis); + ArgumentNullException.ThrowIfNull(query.AxisSelect); + var axisColumnWithoutAlias = query.AxisSelect.GetTextWithoutAlias(query.SyntaxHelper); var part1 = GetPivotPart1(query); @@ -168,11 +178,11 @@ ORDER BY string.Join(Environment.NewLine, query.Lines.Where(static l => l.LocationToInsert < QueryComponent.SELECT)), GetDateAxisTableDeclaration(query.Axis), part1, - query.SyntaxHelper.Escape(GetDatePartOfColumn(query.Axis.AxisIncrement, "dateAxis.dt")), + query.SyntaxHelper.Escape(GetDatePartOfColumn(query.Axis?.AxisIncrement, "dateAxis.dt")), string.Join(Environment.NewLine, query.Lines.Where(static c => c.LocationToInsert == QueryComponent.SELECT)), //the from including all table joins and where but no calendar table join - query.SyntaxHelper.Escape(GetDatePartOfColumn(query.Axis.AxisIncrement, axisColumnWithoutAlias)), + query.SyntaxHelper.Escape(GetDatePartOfColumn(query.Axis?.AxisIncrement, axisColumnWithoutAlias)), //the order by (should be count so that heavy populated columns come first) string.Join(Environment.NewLine, query.Lines.Where(static c => c.LocationToInsert is >= QueryComponent.FROM and <= QueryComponent.WHERE).Select(x => query.SyntaxHelper.Escape(x.Text))) @@ -231,9 +241,9 @@ ORDER BY /// private static string GetPivotPart1(AggregateCustomLineCollection query) { - var pivotSqlWithoutAlias = query.PivotSelect.GetTextWithoutAlias(query.SyntaxHelper); + var pivotSqlWithoutAlias = query.PivotSelect?.GetTextWithoutAlias(query.SyntaxHelper); - var countSqlWithoutAlias = query.CountSelect.GetTextWithoutAlias(query.SyntaxHelper); + var countSqlWithoutAlias = query.CountSelect?.GetTextWithoutAlias(query.SyntaxHelper); query.SyntaxHelper.SplitLineIntoOuterMostMethodAndContents(countSqlWithoutAlias, out var aggregateMethod, out var aggregateParameter); @@ -258,13 +268,15 @@ private static string GetPivotPart1(AggregateCustomLineCollection query) //theres an explicit topX so order by it verbatim instead var topXOrderByLine = - query.Lines.SingleOrDefault(static c => c.LocationToInsert == QueryComponent.OrderBy && c.Role == CustomLineRole.TopX); + query.Lines.SingleOrDefault(static c => c is + { LocationToInsert: QueryComponent.OrderBy, Role: CustomLineRole.TopX }); if (topXOrderByLine != null) orderBy = topXOrderByLine.Text; //if theres a topX limit postfix line (See MySqlQuerySyntaxHelper.HowDoWeAchieveTopX) add that too var topXLimitLine = - query.Lines.SingleOrDefault(static c => c.LocationToInsert == QueryComponent.Postfix && c.Role == CustomLineRole.TopX); + query.Lines.SingleOrDefault(static c => c is + { LocationToInsert: QueryComponent.Postfix, Role: CustomLineRole.TopX }); var topXLimitSqlIfAny = topXLimitLine != null ? topXLimitLine.Text : ""; var havingSqlIfAny = string.Join(Environment.NewLine, diff --git a/FAnsiSql/Implementations/MySql/MySqlBulkCopy.cs b/FAnsiSql/Implementations/MySql/MySqlBulkCopy.cs index 39b41da0..7d561c43 100644 --- a/FAnsiSql/Implementations/MySql/MySqlBulkCopy.cs +++ b/FAnsiSql/Implementations/MySql/MySqlBulkCopy.cs @@ -39,7 +39,7 @@ public override int UploadImpl(DataTable dt) var sb = new StringBuilder(commandPrefix, 1 << 22); - var matches = matchedColumns.Keys.Select(column => (matchedColumns[column].DataType.SQLType, column.Ordinal)).ToArray(); + var matches = matchedColumns.Keys.Select(column => (matchedColumns[column].DataType?.SQLType, column.Ordinal)).ToArray(); foreach (DataRow dr in dt.Rows) { sb.Append('('); @@ -71,30 +71,42 @@ public override int UploadImpl(DataTable dt) return affected; } - private string ConstructIndividualValue(string dataType, object value) + private string ConstructIndividualValue(string? dataType, object? value) { - dataType = dataType.ToUpper(); - dataType = BracketsRe().Replace(dataType, "").Trim(); - - if (value is DateTime valueDateTime) - switch (dataType) + if (value is DateTime valueDateTime && dataType is { Length: > 0 }) + { + var dt = dataType.AsSpan().Trim(); + switch (dt[0]) { - case "DATE": - return $"'{valueDateTime:yyyy-MM-dd}'"; - case "TIMESTAMP" or "DATETIME": - return $"'{valueDateTime:yyyy-MM-dd HH:mm:ss}'"; - case "TIME": - return $"'{valueDateTime:HH:mm:ss}'"; + case 'd': + case 'D': + if (dt.Equals("date", StringComparison.OrdinalIgnoreCase)) + return $"'{valueDateTime:yyyy-MM-dd}'"; + if (dt.Equals("datetime", StringComparison.OrdinalIgnoreCase)) + return $"'{valueDateTime:yyyy-MM-dd HH:mm:ss}'"; + + break; + + case 't': + case 'T': + if (dt.Equals("time", StringComparison.OrdinalIgnoreCase)) + return $"'{valueDateTime:HH:mm:ss}'"; + if (dt.Equals("timestamp", StringComparison.OrdinalIgnoreCase)) + return $"'{valueDateTime:yyyy-MM-dd HH:mm:ss}'"; + + break; } + } if (value == null || value == DBNull.Value) return "NULL"; - return ConstructIndividualValue(dataType, value.ToString()); + return ConstructIndividualValue(dataType, value.ToString() ?? "NULL"); } - private string ConstructIndividualValue(string dataType, string value) + private string ConstructIndividualValue(string? dataType, string value) { + dataType = BracketsRe().Replace(dataType?.ToUpper() ?? "", "").Trim(); return dataType switch { "BIT" => value, diff --git a/FAnsiSql/Implementations/MySql/MySqlDatabaseHelper.cs b/FAnsiSql/Implementations/MySql/MySqlDatabaseHelper.cs index 41b96ce3..e20b5d79 100644 --- a/FAnsiSql/Implementations/MySql/MySqlDatabaseHelper.cs +++ b/FAnsiSql/Implementations/MySql/MySqlDatabaseHelper.cs @@ -3,7 +3,6 @@ using System.Data; using System.Data.Common; using System.IO; -using System.Linq; using FAnsi.Discovery; using FAnsi.Discovery.QuerySyntax; using MySqlConnector; @@ -14,7 +13,7 @@ public sealed class MySqlDatabaseHelper : DiscoveredDatabaseHelper { public override IEnumerable ListTableValuedFunctions(DiscoveredDatabase parent, IQuerySyntaxHelper querySyntaxHelper, DbConnection connection, string database, DbTransaction? transaction = null) => - Enumerable.Empty(); + []; public override DiscoveredStoredprocedure[] ListStoredprocedures(DbConnectionStringBuilder builder, string database) => throw new NotImplementedException(); diff --git a/FAnsiSql/Implementations/MySql/MySqlQuerySyntaxHelper.cs b/FAnsiSql/Implementations/MySql/MySqlQuerySyntaxHelper.cs index f2712105..56f308ea 100644 --- a/FAnsiSql/Implementations/MySql/MySqlQuerySyntaxHelper.cs +++ b/FAnsiSql/Implementations/MySql/MySqlQuerySyntaxHelper.cs @@ -21,7 +21,7 @@ public sealed class MySqlQuerySyntaxHelper : QuerySyntaxHelper public override string CloseQualifier => "`"; - private MySqlQuerySyntaxHelper() : base(MySqlTypeTranslater.Instance, MySqlAggregateHelper.Instance,MySqlUpdateHelper.Instance,DatabaseType.MySql)//no specific type translation required + private MySqlQuerySyntaxHelper() : base(MySqlTypeTranslater.Instance, MySqlAggregateHelper.Instance, MySqlUpdateHelper.Instance, DatabaseType.MySql)//no specific type translation required { } @@ -34,7 +34,7 @@ private MySqlQuerySyntaxHelper() : base(MySqlTypeTranslater.Instance, MySqlAggre /// /// /// - private string? GetRuntimeNameWithDoubledBackticks(string s) => GetRuntimeName(s)?.Replace("`", "``"); + private string GetRuntimeNameWithDoubledBackticks(string s) => GetRuntimeName(s).Replace("`", "``"); protected override string UnescapeWrappedNameBody(string name) => name.Replace("``", "`"); @@ -47,32 +47,32 @@ public override string EnsureFullyQualified(string? databaseName, string? schema return $"{EnsureWrapped(databaseName)}{DatabaseTableSeparator}{EnsureWrapped(tableName)}"; } - public override TopXResponse HowDoWeAchieveTopX(int x) => new($"LIMIT {x}",QueryComponent.Postfix); + public override TopXResponse HowDoWeAchieveTopX(int x) => new($"LIMIT {x}", QueryComponent.Postfix); public override string GetParameterDeclaration(string proposedNewParameterName, string sqlType) => //MySql doesn't require parameter declaration you just start using it like javascript $"/* {proposedNewParameterName} */"; - public override string Escape(string sql) + public override string Escape(string? sql) { // https://dev.mysql.com/doc/refman/8.0/en/string-literals.html - var r = new StringBuilder(sql.Length); - foreach (var c in sql) + var r = new StringBuilder(sql?.Length ?? 0); + foreach (var c in sql ?? "") r.Append(c switch { - '\0' => "\\0", + '\0' => "\\0", '\'' => "\\'", '"' => "\"", - '\b' => "\\b", - '\n' => "\\n", - '\r' => "\\r", - '\t' => "\\t", - '\u001a' => "\\Z", + '\b' => "\\b", + '\n' => "\\n", + '\r' => "\\r", + '\t' => "\\t", + '\u001a' => "\\Z", '\\' => "\\", // Pattern matching only: // '%' => "\\%", // '_' => "\\_", - _ => $"{c}" + _ => $"{c}" }); return r.ToString(); } diff --git a/FAnsiSql/Implementations/MySql/MySqlTableHelper.cs b/FAnsiSql/Implementations/MySql/MySqlTableHelper.cs index 08e87340..ed078f6e 100644 --- a/FAnsiSql/Implementations/MySql/MySqlTableHelper.cs +++ b/FAnsiSql/Implementations/MySql/MySqlTableHelper.cs @@ -72,7 +72,7 @@ public override IEnumerable DiscoverColumns(DiscoveredTable di r.Close(); } - private static bool YesNoToBool(object o) + private static bool YesNoToBool(object? o) { if (o is bool b) return b; diff --git a/FAnsiSql/Implementations/MySql/MySqlTypeTranslater.cs b/FAnsiSql/Implementations/MySql/MySqlTypeTranslater.cs index 7b98a5c9..eb4c2aa7 100644 --- a/FAnsiSql/Implementations/MySql/MySqlTypeTranslater.cs +++ b/FAnsiSql/Implementations/MySql/MySqlTypeTranslater.cs @@ -23,15 +23,39 @@ private MySqlTypeTranslater() : base(DateRe(), 4000, 4000) LongRegex = LongRe(); } - public override int GetLengthIfString(string sqlType) => - sqlType.ToUpperInvariant() switch + public override int GetLengthIfString(string? sqlType) + { + if (!(sqlType?.Length > 0)) + return sqlType != null && AlsoStringRegex.IsMatch(sqlType) ? int.MaxValue : base.GetLengthIfString(sqlType); + + switch (sqlType[0]) { - "TINYTEXT" => 1 << 8, - "TEXT" => 1 << 16, - "MEDIUMTEXT" => 1 << 24, - "LONGTEXT" => int.MaxValue, // Should be 1<<32 but that overflows... - _ => AlsoStringRegex.IsMatch(sqlType) ? int.MaxValue : base.GetLengthIfString(sqlType) - }; + case 'l': + case 'L': + if (sqlType.Equals("longtext", StringComparison.OrdinalIgnoreCase)) + return int.MaxValue; + + break; + + case 'm': + case 'M': + if (sqlType.Equals("mediumtext", StringComparison.OrdinalIgnoreCase)) + return 1 << 24; + + break; + + case 't': + case 'T': + if (sqlType.Equals("text", StringComparison.OrdinalIgnoreCase)) + return 1 << 16; + if (sqlType.Equals("tinytext", StringComparison.OrdinalIgnoreCase)) + return 1 << 8; + + break; + } + + return AlsoStringRegex.IsMatch(sqlType) ? int.MaxValue : base.GetLengthIfString(sqlType); + } public override string GetStringDataTypeWithUnlimitedWidth() => "longtext"; diff --git a/FAnsiSql/Implementations/Oracle/Aggregation/OracleAggregateHelper.cs b/FAnsiSql/Implementations/Oracle/Aggregation/OracleAggregateHelper.cs index 56b362f1..f1f254b0 100644 --- a/FAnsiSql/Implementations/Oracle/Aggregation/OracleAggregateHelper.cs +++ b/FAnsiSql/Implementations/Oracle/Aggregation/OracleAggregateHelper.cs @@ -11,7 +11,7 @@ public sealed class OracleAggregateHelper : AggregateHelper private OracleAggregateHelper() {} protected override IQuerySyntaxHelper GetQuerySyntaxHelper() => OracleQuerySyntaxHelper.Instance; - public override string GetDatePartOfColumn(AxisIncrement increment, string columnSql) => + public override string GetDatePartOfColumn(AxisIncrement? increment, string columnSql) => increment switch { AxisIncrement.Day => columnSql, @@ -29,11 +29,11 @@ private static string GetDateAxisTableDeclaration(IQueryAxis axis) var startDateSql = //is it a date in some format or other? - DateTime.TryParse(axis.StartDate.Trim('\'', '"'), out var start) + DateTime.TryParse(axis.StartDate?.Trim('\'', '"'), out var start) ? $"to_date('{start:yyyyMMdd}','yyyymmdd')" : $"to_date(to_char({axis.StartDate}, 'YYYYMMDD'), 'yyyymmdd')"; //assume its some Oracle specific syntax that results in a date - var endDateSql = DateTime.TryParse(axis.EndDate.Trim('\'', '"'), out var end) + var endDateSql = DateTime.TryParse(axis.EndDate?.Trim('\'', '"'), out var end) ? $"to_date('{end:yyyyMMdd}','yyyymmdd')" : $"to_date(to_char({axis.EndDate}, 'YYYYMMDD'), 'yyyymmdd')"; //assume its some Oracle specific syntax that results in a date e.g. CURRENT_TIMESTAMP @@ -81,6 +81,8 @@ from dual protected override string BuildAxisAggregate(AggregateCustomLineCollection query) { + ArgumentNullException.ThrowIfNull(query.Axis); + //we are trying to produce something like this: /* with calendar as ( @@ -100,8 +102,8 @@ group by dt order by dt*/ - var countAlias = query.CountSelect.GetAliasFromText(query.SyntaxHelper); - var axisColumnAlias = query.AxisSelect.GetAliasFromText(query.SyntaxHelper) ?? "joinDt"; + var countAlias = query.CountSelect?.GetAliasFromText(query.SyntaxHelper); + var axisColumnAlias = query.AxisSelect?.GetAliasFromText(query.SyntaxHelper) ?? "joinDt"; WrapAxisColumnWithDatePartFunction(query, axisColumnAlias); diff --git a/FAnsiSql/Implementations/Oracle/OracleBulkCopy.cs b/FAnsiSql/Implementations/Oracle/OracleBulkCopy.cs index 2ba62fa7..a598c16d 100644 --- a/FAnsiSql/Implementations/Oracle/OracleBulkCopy.cs +++ b/FAnsiSql/Implementations/Oracle/OracleBulkCopy.cs @@ -44,8 +44,12 @@ public override int UploadImpl(DataTable dt) foreach (var (dataColumn, discoveredColumn) in mapping) { var p = _server.AddParameterWithValueToCommand(parameterNames[dataColumn], cmd, DBNull.Value); + if (discoveredColumn.DataType?.SQLType == null) + continue; + p.DbType = tt.GetDbTypeForSQLDBType(discoveredColumn.DataType.SQLType); + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (p.DbType) { case DbType.DateTime: diff --git a/FAnsiSql/Implementations/Oracle/OracleQuerySyntaxHelper.cs b/FAnsiSql/Implementations/Oracle/OracleQuerySyntaxHelper.cs index 62aaa3d0..2f3007c9 100644 --- a/FAnsiSql/Implementations/Oracle/OracleQuerySyntaxHelper.cs +++ b/FAnsiSql/Implementations/Oracle/OracleQuerySyntaxHelper.cs @@ -31,7 +31,9 @@ private OracleQuerySyntaxHelper() : base(OracleTypeTranslater.Instance, OracleAg { var answer = base.GetRuntimeName(s); - return string.IsNullOrWhiteSpace(answer) ? s : + return string.IsNullOrWhiteSpace(answer) + ? s + : //upper it because oracle loves uppercase stuff answer.Trim('"').ToUpper(); } @@ -82,9 +84,7 @@ protected override object FormatTimespanForDbParameter(TimeSpan timeSpan) => //Value must be a DateTime even if DBParameter is of Type DbType.Time Convert.ToDateTime(timeSpan.ToString()); - private static readonly HashSet ReservedWords = new(new[] - { - + private static readonly HashSet ReservedWords = new([ "ACCESS", "ACCOUNT", "ACTIVATE", @@ -565,6 +565,6 @@ protected override object FormatTimespanForDbParameter(TimeSpan timeSpan) => "XID", "YEAR", "ZONE" - }, StringComparer.CurrentCultureIgnoreCase); + ], StringComparer.CurrentCultureIgnoreCase); } \ No newline at end of file diff --git a/FAnsiSql/Implementations/Oracle/OracleTypeTranslater.cs b/FAnsiSql/Implementations/Oracle/OracleTypeTranslater.cs index 87e9deff..efc6986e 100644 --- a/FAnsiSql/Implementations/Oracle/OracleTypeTranslater.cs +++ b/FAnsiSql/Implementations/Oracle/OracleTypeTranslater.cs @@ -59,7 +59,9 @@ protected override bool IsString(string sqlType) => protected override bool IsFloatingPoint(string sqlType) => base.IsFloatingPoint(sqlType) || AlsoFloatingPointRegex.IsMatch(sqlType); - public override int GetLengthIfString(string sqlType) => AlsoStringRegex.IsMatch(sqlType) ? int.MaxValue : base.GetLengthIfString(sqlType); + public override int GetLengthIfString(string? sqlType) => sqlType != null && AlsoStringRegex.IsMatch(sqlType) + ? int.MaxValue + : base.GetLengthIfString(sqlType); protected override bool IsSmallInt(string sqlType) => //yup you ask for one of these, you will get a NUMBER(38) https://docs.oracle.com/cd/A58617_01/server.804/a58241/ch5.htm diff --git a/FAnsiSql/Implementations/PostgreSql/Aggregation/PostgreSqlAggregateHelper.cs b/FAnsiSql/Implementations/PostgreSql/Aggregation/PostgreSqlAggregateHelper.cs index 2bf0d2cc..bbe31990 100644 --- a/FAnsiSql/Implementations/PostgreSql/Aggregation/PostgreSqlAggregateHelper.cs +++ b/FAnsiSql/Implementations/PostgreSql/Aggregation/PostgreSqlAggregateHelper.cs @@ -22,10 +22,10 @@ protected override string BuildAxisAggregate(AggregateCustomLineCollection query _ => throw new ArgumentOutOfRangeException(nameof(query),$"Invalid AxisIncrement {query.Axis?.AxisIncrement}") }; - var countAlias = query.CountSelect.GetAliasFromText(query.SyntaxHelper); - var axisColumnAlias = query.AxisSelect.GetAliasFromText(query.SyntaxHelper) ?? "joinDt"; + var countAlias = query.CountSelect?.GetAliasFromText(query.SyntaxHelper); + var axisColumnAlias = query.AxisSelect?.GetAliasFromText(query.SyntaxHelper) ?? "joinDt"; - WrapAxisColumnWithDatePartFunction(query,axisColumnAlias); + WrapAxisColumnWithDatePartFunction(query, axisColumnAlias); var sql = string.Format(""" @@ -63,7 +63,7 @@ ORDER BY protected override string BuildPivotAndAxisAggregate(AggregateCustomLineCollection query) => throw new NotImplementedException(); - public override string GetDatePartOfColumn(AxisIncrement increment, string columnSql) => + public override string GetDatePartOfColumn(AxisIncrement? increment, string columnSql) => increment switch { AxisIncrement.Day => $"{columnSql}::date", diff --git a/FAnsiSql/Implementations/PostgreSql/PostgreSqlDatabaseHelper.cs b/FAnsiSql/Implementations/PostgreSql/PostgreSqlDatabaseHelper.cs index 00f29bee..75150317 100644 --- a/FAnsiSql/Implementations/PostgreSql/PostgreSqlDatabaseHelper.cs +++ b/FAnsiSql/Implementations/PostgreSql/PostgreSqlDatabaseHelper.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Data.Common; using System.IO; -using System.Linq; using FAnsi.Discovery; using FAnsi.Discovery.QuerySyntax; using Npgsql; @@ -77,11 +76,11 @@ public override IEnumerable ListTables(DiscoveredDatabase paren public override IEnumerable ListTableValuedFunctions(DiscoveredDatabase parent, IQuerySyntaxHelper querySyntaxHelper, DbConnection connection, string database, DbTransaction? transaction = null) => - Enumerable.Empty(); + []; public override DiscoveredStoredprocedure[] ListStoredprocedures(DbConnectionStringBuilder builder, string database) => - Array.Empty(); + []; public override IDiscoveredTableHelper GetTableHelper() => PostgreSqlTableHelper.Instance; @@ -120,7 +119,7 @@ FROM pg_stat_activity protected override string GetCreateTableSqlLineForColumn(DatabaseColumnRequest col, string datatype, IQuerySyntaxHelper syntaxHelper) => //Collations generally have to be in quotes (unless maybe they are very weird user generated ones?) - $"{syntaxHelper.EnsureWrapped(col.ColumnName)} {datatype} {(col.Default != MandatoryScalarFunctions.None ? $"default {syntaxHelper.GetScalarFunctionSql(col.Default)}" : "")} {(string.IsNullOrWhiteSpace(col.Collation) ? "" : $"COLLATE \"{col.Collation.Trim('"')}\"")} {(col.AllowNulls && !col.IsPrimaryKey ? " NULL" : " NOT NULL")} {(col.IsAutoIncrement ? syntaxHelper.GetAutoIncrementKeywordIfAny() : "")}"; + $"{syntaxHelper.EnsureWrapped(col.ColumnName)} {datatype} {(col.Default != MandatoryScalarFunctions.None ? $"default {syntaxHelper.GetScalarFunctionSql(col.Default)}" : "")} {(string.IsNullOrWhiteSpace(col.Collation) ? "" : $"COLLATE \"{col.Collation.Trim('"')}\"")} {(col is { AllowNulls: true, IsPrimaryKey: false } ? " NULL" : " NOT NULL")} {(col.IsAutoIncrement ? syntaxHelper.GetAutoIncrementKeywordIfAny() : "")}"; public override DirectoryInfo Detach(DiscoveredDatabase database) => throw new NotImplementedException(); diff --git a/FAnsiSql/Implementations/PostgreSql/PostgreSqlServerHelper.cs b/FAnsiSql/Implementations/PostgreSql/PostgreSqlServerHelper.cs index c65ffe55..7374ef75 100644 --- a/FAnsiSql/Implementations/PostgreSql/PostgreSqlServerHelper.cs +++ b/FAnsiSql/Implementations/PostgreSql/PostgreSqlServerHelper.cs @@ -47,7 +47,7 @@ public override void CreateDatabase(DbConnectionStringBuilder builder, IHasRunti public override string? GetExplicitPasswordIfAny(DbConnectionStringBuilder builder) => ((NpgsqlConnectionStringBuilder)builder).Password; - public override Version? GetVersion(DiscoveredServer server) + public override Version GetVersion(DiscoveredServer server) { using var con = new NpgsqlConnection(server.Builder.ConnectionString); con.Open(); diff --git a/FAnsiSql/Implementations/PostgreSql/PostgreSqlSyntaxHelper.cs b/FAnsiSql/Implementations/PostgreSql/PostgreSqlSyntaxHelper.cs index b87ab6c8..9a5a1bdf 100644 --- a/FAnsiSql/Implementations/PostgreSql/PostgreSqlSyntaxHelper.cs +++ b/FAnsiSql/Implementations/PostgreSql/PostgreSqlSyntaxHelper.cs @@ -45,7 +45,7 @@ protected override object FormatDateTimeForDbParameter(DateTime dateTime) => /// /// /// - private string? GetRuntimeNameWithDoubledDoubleQuotes(string s) => GetRuntimeName(s)?.Replace("\"", "\"\""); + private string GetRuntimeNameWithDoubledDoubleQuotes(string s) => GetRuntimeName(s).Replace("\"", "\"\""); protected override string UnescapeWrappedNameBody(string name) => name.Replace("\"\"", "\""); diff --git a/FAnsiSql/Implementations/PostgreSql/PostgreSqlTableHelper.cs b/FAnsiSql/Implementations/PostgreSql/PostgreSqlTableHelper.cs index a03efe7b..2a3201ec 100644 --- a/FAnsiSql/Implementations/PostgreSql/PostgreSqlTableHelper.cs +++ b/FAnsiSql/Implementations/PostgreSql/PostgreSqlTableHelper.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Configuration.Internal; using System.Data; using System.Data.Common; using System.Globalization; diff --git a/Tests/FAnsiTests/Aggregation/CalendarWithPivotAggregationTests.cs b/Tests/FAnsiTests/Aggregation/CalendarWithPivotAggregationTests.cs index 742b85a9..1f973593 100644 --- a/Tests/FAnsiTests/Aggregation/CalendarWithPivotAggregationTests.cs +++ b/Tests/FAnsiTests/Aggregation/CalendarWithPivotAggregationTests.cs @@ -58,17 +58,17 @@ public void Test_Calendar_WithPivot(DatabaseType type, bool easy) //pivot columns should ordered by sum of pivot values (T has the highest followed by E...) - /*joinDt T E&, %a' mp;E F G - 2001 3 1 0 1 - 2002 2 1 2 0 - 2003 2 0 0 0 - 2004 0 0 0 0 - 2005 0 1 0 0 - 2006 0 0 0 0 - 2007 0 0 0 0 - 2008 0 0 0 0 - 2009 0 0 0 0 - 2010 0 0 0 0 + /*joinDt T E&, %a' mp;E F G + 2001 3 1 0 1 + 2002 2 1 2 0 + 2003 2 0 0 0 + 2004 0 0 0 0 + 2005 0 1 0 0 + 2006 0 0 0 0 + 2007 0 0 0 0 + 2008 0 0 0 0 + 2009 0 0 0 0 + 2010 0 0 0 0 */ Assert.That(dt.Rows, Has.Count.EqualTo(10)); //there are 10 years between 2001 and 2010 even though not all years are represented in the data diff --git a/Tests/FAnsiTests/Aggregation/PivotAggregationTests.cs b/Tests/FAnsiTests/Aggregation/PivotAggregationTests.cs index 873a16da..a88e1fcd 100644 --- a/Tests/FAnsiTests/Aggregation/PivotAggregationTests.cs +++ b/Tests/FAnsiTests/Aggregation/PivotAggregationTests.cs @@ -34,11 +34,11 @@ public void Test_PivotOnlyCount(DatabaseType type) con.Open(); //Expected Test Results: /* - Cat 2001-01-01 00:00:00.0000000 2002-01-01 00:00:00.0000000 2002-02-01 00:00:00.0000000 2002-03-02 00:00:00.0000000 2003-01-01 00:00:00.0000000 2003-04-02 00:00:00.0000000 2005-01-01 00:00:00.0000000 2001-01-02 00:00:00.0000000 - E&, %a' mp;E 1 1 0 0 0 0 1 0 - F 0 2 0 0 0 0 0 0 - G 1 0 0 0 0 0 0 0 - T 2 0 1 1 1 1 0 1 + Cat 2001-01-01 00:00:00.0000000 2002-01-01 00:00:00.0000000 2002-02-01 00:00:00.0000000 2002-03-02 00:00:00.0000000 2003-01-01 00:00:00.0000000 2003-04-02 00:00:00.0000000 2005-01-01 00:00:00.0000000 2001-01-02 00:00:00.0000000 + E&, %a' mp;E 1 1 0 0 0 0 1 0 + F 0 2 0 0 0 0 0 0 + G 1 0 0 0 0 0 0 0 + T 2 0 1 1 1 1 0 1 */ var cmd = svr.GetCommand(sql, con); diff --git a/Tests/FAnsiTests/CrossPlatformTests.cs b/Tests/FAnsiTests/CrossPlatformTests.cs index 56dbbd4a..65e592d1 100644 --- a/Tests/FAnsiTests/CrossPlatformTests.cs +++ b/Tests/FAnsiTests/CrossPlatformTests.cs @@ -15,31 +15,31 @@ namespace FAnsiTests; -public sealed class CrossPlatformTests:DatabaseTests +public sealed class CrossPlatformTests : DatabaseTests { - [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))] + [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))] public void TestTableCreation_NullTableName(DatabaseType type) { var db = GetTestDatabase(type); - Assert.Throws(() => db.CreateTable("",new DataTable())); + Assert.Throws(() => db.CreateTable("", new DataTable())); } - [TestCase(DatabaseType.MicrosoftSQLServer,"01/01/2007 00:00:00")] - [TestCase(DatabaseType.MySql,"1/1/2007 00:00:00")] - [TestCase(DatabaseType.MySql,"01/01/2007 00:00:00")] - [TestCase(DatabaseType.Oracle,"01/01/2007 00:00:00")] - [TestCase(DatabaseType.MicrosoftSQLServer,"2007-01-01 00:00:00")] - [TestCase(DatabaseType.MySql,"2007-01-01 00:00:00")] - [TestCase(DatabaseType.Oracle,"2007-01-01 00:00:00")] - [TestCase(DatabaseType.PostgreSql,"01/01/2007 00:00:00")] - [TestCase(DatabaseType.PostgreSql,"2007-01-01 00:00:00")] - public void DateColumnTests_NoTime(DatabaseType type,object input) + [TestCase(DatabaseType.MicrosoftSQLServer, "01/01/2007 00:00:00")] + [TestCase(DatabaseType.MySql, "1/1/2007 00:00:00")] + [TestCase(DatabaseType.MySql, "01/01/2007 00:00:00")] + [TestCase(DatabaseType.Oracle, "01/01/2007 00:00:00")] + [TestCase(DatabaseType.MicrosoftSQLServer, "2007-01-01 00:00:00")] + [TestCase(DatabaseType.MySql, "2007-01-01 00:00:00")] + [TestCase(DatabaseType.Oracle, "2007-01-01 00:00:00")] + [TestCase(DatabaseType.PostgreSql, "01/01/2007 00:00:00")] + [TestCase(DatabaseType.PostgreSql, "2007-01-01 00:00:00")] + public void DateColumnTests_NoTime(DatabaseType type, object input) { var db = GetTestDatabase(type); - var tbl = db.CreateTable("MyTable",[new DatabaseColumnRequest("MyDate",new DatabaseTypeRequest(typeof(DateTime)))]); + var tbl = db.CreateTable("MyTable", [new DatabaseColumnRequest("MyDate", new DatabaseTypeRequest(typeof(DateTime)))]); - tbl.Insert(new Dictionary { { "MyDate",input } }); + tbl.Insert(new Dictionary { { "MyDate", input } }); using (var blk = tbl.BeginBulkInsert()) { @@ -51,31 +51,31 @@ public void DateColumnTests_NoTime(DatabaseType type,object input) } var result = tbl.GetDataTable(); - var expectedDate = new DateTime(2007,1,1); + var expectedDate = new DateTime(2007, 1, 1); Assert.Multiple(() => { - Assert.That(result.Rows[0][0],Is.EqualTo(expectedDate)); - Assert.That(result.Rows[1][0],Is.EqualTo(expectedDate)); + Assert.That(result.Rows[0][0], Is.EqualTo(expectedDate)); + Assert.That(result.Rows[1][0], Is.EqualTo(expectedDate)); }); } - [TestCase(DatabaseType.MicrosoftSQLServer,"2/28/1993 5:36:27 AM","en-US")] - [TestCase(DatabaseType.MySql,"2/28/1993 5:36:27 AM","en-US")] - [TestCase(DatabaseType.Oracle,"2/28/1993 5:36:27 AM","en-US")] - [TestCase(DatabaseType.MicrosoftSQLServer,"28/2/1993 5:36:27 AM","en-GB")] - [TestCase(DatabaseType.MySql,"28/2/1993 5:36:27 AM","en-GB")] - [TestCase(DatabaseType.Oracle,"28/2/1993 5:36:27 AM","en-GB")] - [TestCase(DatabaseType.PostgreSql,"2/28/1993 5:36:27 AM","en-US")] - [TestCase(DatabaseType.PostgreSql,"28/2/1993 5:36:27 AM","en-GB")] - public void DateColumnTests_UkUsFormat_Explicit(DatabaseType type,object input,string culture) + [TestCase(DatabaseType.MicrosoftSQLServer, "2/28/1993 5:36:27 AM", "en-US")] + [TestCase(DatabaseType.MySql, "2/28/1993 5:36:27 AM", "en-US")] + [TestCase(DatabaseType.Oracle, "2/28/1993 5:36:27 AM", "en-US")] + [TestCase(DatabaseType.MicrosoftSQLServer, "28/2/1993 5:36:27 AM", "en-GB")] + [TestCase(DatabaseType.MySql, "28/2/1993 5:36:27 AM", "en-GB")] + [TestCase(DatabaseType.Oracle, "28/2/1993 5:36:27 AM", "en-GB")] + [TestCase(DatabaseType.PostgreSql, "2/28/1993 5:36:27 AM", "en-US")] + [TestCase(DatabaseType.PostgreSql, "28/2/1993 5:36:27 AM", "en-GB")] + public void DateColumnTests_UkUsFormat_Explicit(DatabaseType type, object input, string culture) { var db = GetTestDatabase(type); - var tbl = db.CreateTable("MyTable",[new DatabaseColumnRequest("MyDate",new DatabaseTypeRequest(typeof(DateTime)))]); + var tbl = db.CreateTable("MyTable", [new DatabaseColumnRequest("MyDate", new DatabaseTypeRequest(typeof(DateTime)))]); var cultureInfo = new CultureInfo(culture); //basic insert - tbl.Insert(new Dictionary { { "MyDate",input } },cultureInfo); + tbl.Insert(new Dictionary { { "MyDate", input } }, cultureInfo); //then bulk insert, both need to work using (var blk = tbl.BeginBulkInsert(cultureInfo)) @@ -88,11 +88,11 @@ public void DateColumnTests_UkUsFormat_Explicit(DatabaseType type,object input,s } var result = tbl.GetDataTable(); - var expectedDate = new DateTime(1993,2,28,5,36,27); + var expectedDate = new DateTime(1993, 2, 28, 5, 36, 27); Assert.Multiple(() => { - Assert.That(result.Rows[0][0],Is.EqualTo(expectedDate)); - Assert.That(result.Rows[1][0],Is.EqualTo(expectedDate)); + Assert.That(result.Rows[0][0], Is.EqualTo(expectedDate)); + Assert.That(result.Rows[1][0], Is.EqualTo(expectedDate)); }); } @@ -104,14 +104,14 @@ public void DateColumnTests_UkUsFormat_Explicit(DatabaseType type,object input,s /// /// /// - [TestCase(DatabaseType.MicrosoftSQLServer,"2/28/1993 5:36:27 AM","en-US")] - [TestCase(DatabaseType.MySql,"2/28/1993 5:36:27 AM","en-US")] - [TestCase(DatabaseType.Oracle,"2/28/1993 5:36:27 AM","en-US")] - [TestCase(DatabaseType.PostgreSql,"2/28/1993 5:36:27 AM","en-US")] - public void DateColumnTests_PrimaryKeyColumn(DatabaseType type,object input,string culture) + [TestCase(DatabaseType.MicrosoftSQLServer, "2/28/1993 5:36:27 AM", "en-US")] + [TestCase(DatabaseType.MySql, "2/28/1993 5:36:27 AM", "en-US")] + [TestCase(DatabaseType.Oracle, "2/28/1993 5:36:27 AM", "en-US")] + [TestCase(DatabaseType.PostgreSql, "2/28/1993 5:36:27 AM", "en-US")] + public void DateColumnTests_PrimaryKeyColumn(DatabaseType type, object input, string culture) { var db = GetTestDatabase(type); - var tbl = db.CreateTable("MyTable",[ + var tbl = db.CreateTable("MyTable", [ new DatabaseColumnRequest("MyDate",new DatabaseTypeRequest(typeof(DateTime))) {IsPrimaryKey = true } ]); @@ -127,30 +127,30 @@ public void DateColumnTests_PrimaryKeyColumn(DatabaseType type,object input,stri dt.PrimaryKey = [dt.Columns[0]]; blk.Upload(dt); - Assert.That(dt.PrimaryKey,Has.Length.EqualTo(1)); - Assert.That(dt.PrimaryKey[0].ColumnName,Is.EqualTo("MyDate")); + Assert.That(dt.PrimaryKey, Has.Length.EqualTo(1)); + Assert.That(dt.PrimaryKey[0].ColumnName, Is.EqualTo("MyDate")); } var result = tbl.GetDataTable(); - var expectedDate = new DateTime(1993,2,28,5,36,27); - Assert.That(result.Rows[0][0],Is.EqualTo(expectedDate)); + var expectedDate = new DateTime(1993, 2, 28, 5, 36, 27); + Assert.That(result.Rows[0][0], Is.EqualTo(expectedDate)); } - [TestCase(DatabaseType.MicrosoftSQLServer,"00:00:00")] - [TestCase(DatabaseType.MySql,"00:00:00")] - [TestCase(DatabaseType.Oracle,"00:00:00")] - [TestCase(DatabaseType.MicrosoftSQLServer,"00:00")] - [TestCase(DatabaseType.MySql,"00:00")] - [TestCase(DatabaseType.Oracle,"00:00")] - [TestCase(DatabaseType.PostgreSql,"00:00:00")] - [TestCase(DatabaseType.PostgreSql,"00:00")] - public void DateColumnTests_TimeOnly_Midnight(DatabaseType type,object input) + [TestCase(DatabaseType.MicrosoftSQLServer, "00:00:00")] + [TestCase(DatabaseType.MySql, "00:00:00")] + [TestCase(DatabaseType.Oracle, "00:00:00")] + [TestCase(DatabaseType.MicrosoftSQLServer, "00:00")] + [TestCase(DatabaseType.MySql, "00:00")] + [TestCase(DatabaseType.Oracle, "00:00")] + [TestCase(DatabaseType.PostgreSql, "00:00:00")] + [TestCase(DatabaseType.PostgreSql, "00:00")] + public void DateColumnTests_TimeOnly_Midnight(DatabaseType type, object input) { var db = GetTestDatabase(type); - var tbl = db.CreateTable("MyTable",[new DatabaseColumnRequest("MyTime",new DatabaseTypeRequest(typeof(TimeSpan)))]); + var tbl = db.CreateTable("MyTable", [new DatabaseColumnRequest("MyTime", new DatabaseTypeRequest(typeof(TimeSpan)))]); - tbl.Insert(new Dictionary { { "MyTime",input } }); + tbl.Insert(new Dictionary { { "MyTime", input } }); using (var blk = tbl.BeginBulkInsert()) { @@ -162,19 +162,19 @@ public void DateColumnTests_TimeOnly_Midnight(DatabaseType type,object input) } var result = tbl.GetDataTable(); - var expectedTime = new TimeSpan(0,0,0,0); + var expectedTime = new TimeSpan(0, 0, 0, 0); var resultTimeSpans = //Oracle is a bit special it only stores whole dates then has server side settings about how much to return (like a format string) type == DatabaseType.Oracle - ? new[] { (DateTime)result.Rows[0][0],(DateTime)result.Rows[1][0] }.Select(static dt => dt.TimeOfDay) + ? new[] { (DateTime)result.Rows[0][0], (DateTime)result.Rows[1][0] }.Select(static dt => dt.TimeOfDay) .Cast().ToArray() - : [result.Rows[0][0],result.Rows[1][0]]; + : [result.Rows[0][0], result.Rows[1][0]]; Assert.Multiple(() => { - Assert.That(resultTimeSpans[0],Is.EqualTo(expectedTime)); - Assert.That(resultTimeSpans[1],Is.EqualTo(expectedTime)); + Assert.That(resultTimeSpans[0], Is.EqualTo(expectedTime)); + Assert.That(resultTimeSpans[1], Is.EqualTo(expectedTime)); }); } @@ -212,20 +212,20 @@ public void TestOracleTimespans() } } */ - [TestCase(DatabaseType.MicrosoftSQLServer,"13:11:10")] - [TestCase(DatabaseType.MySql,"13:11:10")] - [TestCase(DatabaseType.Oracle,"13:11:10")] - [TestCase(DatabaseType.MicrosoftSQLServer,"13:11")] - [TestCase(DatabaseType.MySql,"13:11")] - [TestCase(DatabaseType.Oracle,"13:11")] - [TestCase(DatabaseType.PostgreSql,"13:11:10")] - [TestCase(DatabaseType.PostgreSql,"13:11")] - public void DateColumnTests_TimeOnly_Afternoon(DatabaseType type,object input) + [TestCase(DatabaseType.MicrosoftSQLServer, "13:11:10")] + [TestCase(DatabaseType.MySql, "13:11:10")] + [TestCase(DatabaseType.Oracle, "13:11:10")] + [TestCase(DatabaseType.MicrosoftSQLServer, "13:11")] + [TestCase(DatabaseType.MySql, "13:11")] + [TestCase(DatabaseType.Oracle, "13:11")] + [TestCase(DatabaseType.PostgreSql, "13:11:10")] + [TestCase(DatabaseType.PostgreSql, "13:11")] + public void DateColumnTests_TimeOnly_Afternoon(DatabaseType type, object input) { var db = GetTestDatabase(type); - var tbl = db.CreateTable("MyTable",[new DatabaseColumnRequest("MyTime",new DatabaseTypeRequest(typeof(TimeSpan)))]); + var tbl = db.CreateTable("MyTable", [new DatabaseColumnRequest("MyTime", new DatabaseTypeRequest(typeof(TimeSpan)))]); - tbl.Insert(new Dictionary { { "MyTime",input } }); + tbl.Insert(new Dictionary { { "MyTime", input } }); using (var blk = tbl.BeginBulkInsert()) { @@ -237,40 +237,40 @@ public void DateColumnTests_TimeOnly_Afternoon(DatabaseType type,object input) } var result = tbl.GetDataTable(); - var expectedTime = new TimeSpan(13,11,00); + var expectedTime = new TimeSpan(13, 11, 00); var resultTimeSpans = //Oracle is a bit special it only stores whole dates then has server side settings about how much to return (like a format string) type == DatabaseType.Oracle - ? new[] { (DateTime)result.Rows[0][0],(DateTime)result.Rows[1][0] }.Select(static dt => dt.TimeOfDay) + ? new[] { (DateTime)result.Rows[0][0], (DateTime)result.Rows[1][0] }.Select(static dt => dt.TimeOfDay) .Cast().ToArray() - : [result.Rows[0][0],result.Rows[1][0]]; + : [result.Rows[0][0], result.Rows[1][0]]; foreach (var t in resultTimeSpans.Cast()) { if (t.Seconds>0) - Assert.That(t.Seconds,Is.EqualTo(10)); + Assert.That(t.Seconds, Is.EqualTo(10)); - var eval = t.Subtract(new TimeSpan(0,0,0,t.Seconds)); - Assert.That(eval,Is.EqualTo(expectedTime)); + var eval = t.Subtract(new TimeSpan(0, 0, 0, t.Seconds)); + Assert.That(eval, Is.EqualTo(expectedTime)); } } [Test] - [TestCase(DatabaseType.MicrosoftSQLServer,"int","-23.00")] - [TestCase(DatabaseType.MicrosoftSQLServer,"int","23.0")] - [TestCase(DatabaseType.MicrosoftSQLServer,"bit","0")] - [TestCase(DatabaseType.MicrosoftSQLServer,"int","00.0")] - [TestCase(DatabaseType.MicrosoftSQLServer,"int","-24")] - [TestCase(DatabaseType.MySql,"int","-23.00")] - [TestCase(DatabaseType.MySql,"int","-25")] - [TestCase(DatabaseType.MySql,"boolean","0")] - [TestCase(DatabaseType.PostgreSql,"int","-23.00")] - [TestCase(DatabaseType.PostgreSql,"int","23.0")] - [TestCase(DatabaseType.PostgreSql,"boolean","0")] - [TestCase(DatabaseType.PostgreSql,"int","00.0")] - [TestCase(DatabaseType.PostgreSql,"int","-24")] - public void TypeConsensusBetweenGuesserAndDiscoveredTableTest(DatabaseType dbType,string datatType,string insertValue) + [TestCase(DatabaseType.MicrosoftSQLServer, "int", "-23.00")] + [TestCase(DatabaseType.MicrosoftSQLServer, "int", "23.0")] + [TestCase(DatabaseType.MicrosoftSQLServer, "bit", "0")] + [TestCase(DatabaseType.MicrosoftSQLServer, "int", "00.0")] + [TestCase(DatabaseType.MicrosoftSQLServer, "int", "-24")] + [TestCase(DatabaseType.MySql, "int", "-23.00")] + [TestCase(DatabaseType.MySql, "int", "-25")] + [TestCase(DatabaseType.MySql, "boolean", "0")] + [TestCase(DatabaseType.PostgreSql, "int", "-23.00")] + [TestCase(DatabaseType.PostgreSql, "int", "23.0")] + [TestCase(DatabaseType.PostgreSql, "boolean", "0")] + [TestCase(DatabaseType.PostgreSql, "int", "00.0")] + [TestCase(DatabaseType.PostgreSql, "int", "-24")] + public void TypeConsensusBetweenGuesserAndDiscoveredTableTest(DatabaseType dbType, string datatType, string insertValue) { var database = GetTestDatabase(dbType); @@ -288,9 +288,9 @@ public void TypeConsensusBetweenGuesserAndDiscoveredTableTest(DatabaseType dbTyp var tt = tbl.GetQuerySyntaxHelper().TypeTranslater; c.AdjustToCompensateForValue(insertValue); - database.CreateTable(tbl.GetRuntimeName(),dt); + database.CreateTable(tbl.GetRuntimeName(), dt); - Assert.That(c.GetSqlDBType(tt),Is.EqualTo(datatType)); + Assert.That(c.GetSqlDBType(tt), Is.EqualTo(datatType)); var expectedDataType = datatType; @@ -305,14 +305,14 @@ public void TypeConsensusBetweenGuesserAndDiscoveredTableTest(DatabaseType dbTyp Assert.Multiple(() => { - Assert.That(tbl.DiscoverColumn("mycol").DataType?.SQLType,Is.EqualTo(expectedDataType)); - Assert.That(tbl.GetRowCount(),Is.EqualTo(1)); + Assert.That(tbl.DiscoverColumn("mycol").DataType?.SQLType, Is.EqualTo(expectedDataType)); + Assert.That(tbl.GetRowCount(), Is.EqualTo(1)); }); tbl.Drop(); } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))] + [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))] public void ForeignKeyCreationTest(DatabaseType type) { var database = GetTestDatabase(type); @@ -325,16 +325,16 @@ public void ForeignKeyCreationTest(DatabaseType type) var parentIdPkCol = tblParent.DiscoverColumn("ID"); - var parentIdFkCol = new DatabaseColumnRequest("Parent_ID",new DatabaseTypeRequest(typeof(int))); + var parentIdFkCol = new DatabaseColumnRequest("Parent_ID", new DatabaseTypeRequest(typeof(int))); var tblChild = database.CreateTable("Child", [ parentIdFkCol, new DatabaseColumnRequest("ChildName",new DatabaseTypeRequest(typeof(string),10)) //varchar(10) - ],new Dictionary + ], new Dictionary { {parentIdFkCol, parentIdPkCol} - },true); + }, true); try { using (var intoParent = tblParent.BeginBulkInsert()) @@ -343,8 +343,8 @@ public void ForeignKeyCreationTest(DatabaseType type) dt.Columns.Add("ID"); dt.Columns.Add("Name"); - dt.Rows.Add(1,"Bob"); - dt.Rows.Add(2,"Frank"); + dt.Rows.Add(1, "Bob"); + dt.Rows.Add(2, "Frank"); intoParent.Upload(dt); } @@ -354,35 +354,35 @@ public void ForeignKeyCreationTest(DatabaseType type) con.Open(); var cmd = tblParent.Database.Server.GetCommand( - $"INSERT INTO {tblChild.GetFullyQualifiedName()} VALUES (100,'chucky')",con); + $"INSERT INTO {tblChild.GetFullyQualifiedName()} VALUES (100,'chucky')", con); //violation of fk - Assert.That(() => cmd.ExecuteNonQuery(),Throws.Exception); + Assert.That(() => cmd.ExecuteNonQuery(), Throws.Exception); tblParent.Database.Server.GetCommand( - $"INSERT INTO {tblChild.GetFullyQualifiedName()} VALUES (1,'chucky')",con).ExecuteNonQuery(); + $"INSERT INTO {tblChild.GetFullyQualifiedName()} VALUES (1,'chucky')", con).ExecuteNonQuery(); tblParent.Database.Server.GetCommand( - $"INSERT INTO {tblChild.GetFullyQualifiedName()} VALUES (1,'chucky2')",con).ExecuteNonQuery(); + $"INSERT INTO {tblChild.GetFullyQualifiedName()} VALUES (1,'chucky2')", con).ExecuteNonQuery(); } Assert.Multiple(() => { - Assert.That(tblParent.GetRowCount(),Is.EqualTo(2)); - Assert.That(tblChild.GetRowCount(),Is.EqualTo(2)); + Assert.That(tblParent.GetRowCount(), Is.EqualTo(2)); + Assert.That(tblChild.GetRowCount(), Is.EqualTo(2)); }); using (var con = tblParent.Database.Server.GetConnection()) { con.Open(); - var cmd = tblParent.Database.Server.GetCommand($"DELETE FROM {tblParent.GetFullyQualifiedName()}",con); + var cmd = tblParent.Database.Server.GetCommand($"DELETE FROM {tblParent.GetFullyQualifiedName()}", con); cmd.ExecuteNonQuery(); } Assert.Multiple(() => { - Assert.That(tblParent.GetRowCount(),Is.EqualTo(0)); - Assert.That(tblChild.GetRowCount(),Is.EqualTo(0)); + Assert.That(tblParent.GetRowCount(), Is.EqualTo(0)); + Assert.That(tblChild.GetRowCount(), Is.EqualTo(0)); }); } finally @@ -392,8 +392,8 @@ public void ForeignKeyCreationTest(DatabaseType type) } } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypesWithBoolFlags))] - public void ForeignKeyCreationTest_TwoColumns(DatabaseType type,bool cascadeDelete) + [TestCaseSource(typeof(All), nameof(All.DatabaseTypesWithBoolFlags))] + public void ForeignKeyCreationTest_TwoColumns(DatabaseType type, bool cascadeDelete) { var database = GetTestDatabase(type); @@ -407,19 +407,19 @@ public void ForeignKeyCreationTest_TwoColumns(DatabaseType type,bool cascadeDele var parentIdPkCol1 = tblParent.DiscoverColumn("ID1"); var parentIdPkCol2 = tblParent.DiscoverColumn("ID2"); - var parentIdFkCol1 = new DatabaseColumnRequest("Parent_ID1",new DatabaseTypeRequest(typeof(int))); - var parentIdFkCol2 = new DatabaseColumnRequest("Parent_ID2",new DatabaseTypeRequest(typeof(int))); + var parentIdFkCol1 = new DatabaseColumnRequest("Parent_ID1", new DatabaseTypeRequest(typeof(int))); + var parentIdFkCol2 = new DatabaseColumnRequest("Parent_ID2", new DatabaseTypeRequest(typeof(int))); var tblChild = database.CreateTable("Child", [ parentIdFkCol1, parentIdFkCol2, new DatabaseColumnRequest("ChildName",new DatabaseTypeRequest(typeof(string),10)) //varchar(10) - ],new Dictionary + ], new Dictionary { {parentIdFkCol1,parentIdPkCol1}, {parentIdFkCol2,parentIdPkCol2} - },cascadeDelete); + }, cascadeDelete); using (var intoParent = tblParent.BeginBulkInsert()) { @@ -428,7 +428,7 @@ public void ForeignKeyCreationTest_TwoColumns(DatabaseType type,bool cascadeDele dt.Columns.Add("ID2"); dt.Columns.Add("Name"); - dt.Rows.Add(1,2,"Bob"); + dt.Rows.Add(1, 2, "Bob"); intoParent.Upload(dt); } @@ -438,46 +438,46 @@ public void ForeignKeyCreationTest_TwoColumns(DatabaseType type,bool cascadeDele con.Open(); var cmd = tblParent.Database.Server.GetCommand( - $"INSERT INTO {tblChild.GetFullyQualifiedName()} VALUES (1,3,'chucky')",con); + $"INSERT INTO {tblChild.GetFullyQualifiedName()} VALUES (1,3,'chucky')", con); //violation of fk - Assert.That(() => cmd.ExecuteNonQuery(),Throws.Exception); + Assert.That(() => cmd.ExecuteNonQuery(), Throws.Exception); tblParent.Database.Server.GetCommand( - $"INSERT INTO {tblChild.GetFullyQualifiedName()} VALUES (1,2,'chucky')",con).ExecuteNonQuery(); + $"INSERT INTO {tblChild.GetFullyQualifiedName()} VALUES (1,2,'chucky')", con).ExecuteNonQuery(); tblParent.Database.Server.GetCommand( - $"INSERT INTO {tblChild.GetFullyQualifiedName()} VALUES (1,2,'chucky2')",con).ExecuteNonQuery(); + $"INSERT INTO {tblChild.GetFullyQualifiedName()} VALUES (1,2,'chucky2')", con).ExecuteNonQuery(); } Assert.Multiple(() => { - Assert.That(tblParent.GetRowCount(),Is.EqualTo(1)); - Assert.That(tblChild.GetRowCount(),Is.EqualTo(2)); + Assert.That(tblParent.GetRowCount(), Is.EqualTo(1)); + Assert.That(tblChild.GetRowCount(), Is.EqualTo(2)); }); using (var con = tblParent.Database.Server.GetConnection()) { con.Open(); - var cmd = tblParent.Database.Server.GetCommand($"DELETE FROM {tblParent.GetFullyQualifiedName()}",con); + var cmd = tblParent.Database.Server.GetCommand($"DELETE FROM {tblParent.GetFullyQualifiedName()}", con); if (cascadeDelete) { cmd.ExecuteNonQuery(); Assert.Multiple(() => { - Assert.That(tblParent.GetRowCount(),Is.EqualTo(0)); - Assert.That(tblChild.GetRowCount(),Is.EqualTo(0)); + Assert.That(tblParent.GetRowCount(), Is.EqualTo(0)); + Assert.That(tblChild.GetRowCount(), Is.EqualTo(0)); }); } else { //no cascade deletes so the query should crash on violation of fk constraint - Assert.That(() => cmd.ExecuteNonQuery(),Throws.Exception); + Assert.That(() => cmd.ExecuteNonQuery(), Throws.Exception); } } } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))] + [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))] public void CreateMaxVarcharColumns(DatabaseType type) { var database = GetTestDatabase(type); @@ -496,14 +496,14 @@ public void CreateMaxVarcharColumns(DatabaseType type) { Assert.That(tbl.Exists()); - Assert.That(tbl.DiscoverColumn("Field1").DataType?.GetLengthIfString(),Is.GreaterThanOrEqualTo(4000)); - Assert.That(tbl.DiscoverColumn("Field2").DataType?.GetLengthIfString(),Is.GreaterThanOrEqualTo(1000)); // unknown size should be at least 1k? that seems sensible - Assert.That(tbl.DiscoverColumn("Field6").DataType?.GetLengthIfString(),Is.EqualTo(10)); + Assert.That(tbl.DiscoverColumn("Field1").DataType?.GetLengthIfString(), Is.GreaterThanOrEqualTo(4000)); + Assert.That(tbl.DiscoverColumn("Field2").DataType?.GetLengthIfString(), Is.GreaterThanOrEqualTo(1000)); // unknown size should be at least 1k? that seems sensible + Assert.That(tbl.DiscoverColumn("Field6").DataType?.GetLengthIfString(), Is.EqualTo(10)); }); } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))] + [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))] public void CreateMaxVarcharColumnFromDataTable(DatabaseType type) { var database = GetTestDatabase(type); @@ -518,19 +518,19 @@ public void CreateMaxVarcharColumnFromDataTable(DatabaseType type) dt.Rows.Add(sb.ToString()); - var tbl = database.CreateTable("MassiveTable",dt); + var tbl = database.CreateTable("MassiveTable", dt); Assert.Multiple(() => { Assert.That(tbl.Exists()); - Assert.That(tbl.DiscoverColumn("MassiveColumn").DataType?.GetLengthIfString(),Is.GreaterThanOrEqualTo(8000)); + Assert.That(tbl.DiscoverColumn("MassiveColumn").DataType?.GetLengthIfString(), Is.GreaterThanOrEqualTo(8000)); }); using var dt2 = tbl.GetDataTable(); - Assert.That(dt2.Rows[0][0],Is.EqualTo(sb.ToString())); + Assert.That(dt2.Rows[0][0], Is.EqualTo(sb.ToString())); } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))] + [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))] public void CreateDateColumnFromDataTable(DatabaseType type) { var database = GetTestDatabase(type); @@ -539,16 +539,16 @@ public void CreateDateColumnFromDataTable(DatabaseType type) dt.Columns.Add("DateColumn"); dt.Rows.Add("2001-01-22"); - var tbl = database.CreateTable("DateTable",dt); + var tbl = database.CreateTable("DateTable", dt); Assert.That(tbl.Exists()); dt = tbl.GetDataTable(); - Assert.That(dt.Rows[0][0],Is.EqualTo(new DateTime(2001,01,22))); + Assert.That(dt.Rows[0][0], Is.EqualTo(new DateTime(2001, 01, 22))); } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypesWithBoolFlags))] - public void AddColumnTest(DatabaseType type,bool useTransaction) + [TestCaseSource(typeof(All), nameof(All.DatabaseTypesWithBoolFlags))] + public void AddColumnTest(DatabaseType type, bool useTransaction) { const string newColumnName = "My Fun New Column[Lol]"; //<- lets make sure dodgy names are also supported @@ -566,7 +566,7 @@ public void AddColumnTest(DatabaseType type,bool useTransaction) Assert.That(tbl.Exists()); //column should be varchar(100) - Assert.That(tbl.DiscoverColumn("Field1").DataType?.GetLengthIfString(),Is.EqualTo(100)); + Assert.That(tbl.DiscoverColumn("Field1").DataType?.GetLengthIfString(), Is.EqualTo(100)); //and should be a primary key Assert.That(tbl.DiscoverColumn("Field1").IsPrimaryKey); @@ -576,12 +576,12 @@ public void AddColumnTest(DatabaseType type,bool useTransaction) if (useTransaction) { using var con = database.Server.BeginNewTransactedConnection(); - tbl.AddColumn(newColumnName,new DatabaseTypeRequest(typeof(DateTime)),true,new DatabaseOperationArgs { TimeoutInSeconds = 1000,TransactionIfAny = con.ManagedTransaction }); + tbl.AddColumn(newColumnName, new DatabaseTypeRequest(typeof(DateTime)), true, new DatabaseOperationArgs { TimeoutInSeconds = 1000, TransactionIfAny = con.ManagedTransaction }); con.ManagedTransaction?.CommitAndCloseConnection(); } else { - tbl.AddColumn(newColumnName,new DatabaseTypeRequest(typeof(DateTime)),true,1000); + tbl.AddColumn(newColumnName, new DatabaseTypeRequest(typeof(DateTime)), true, 1000); } @@ -591,7 +591,7 @@ public void AddColumnTest(DatabaseType type,bool useTransaction) //and should have a type of datetime as requested var typeCreated = newCol.DataType?.SQLType; var tt = database.Server.GetQuerySyntaxHelper().TypeTranslater; - Assert.That(tt.GetCSharpTypeForSQLDBType(typeCreated),Is.EqualTo(typeof(DateTime))); + Assert.That(tt.GetCSharpTypeForSQLDBType(typeCreated), Is.EqualTo(typeof(DateTime))); var fieldsToAlter = new List(["Field1", newColumnName]); @@ -608,7 +608,7 @@ public void AddColumnTest(DatabaseType type,bool useTransaction) newCol = tbl.DiscoverColumn(fieldName); //ALTER the column to varchar(10) - var newTypeCSharp = new DatabaseTypeRequest(typeof(string),10); + var newTypeCSharp = new DatabaseTypeRequest(typeof(string), 10); var newTypeSql = tt.GetSQLDBTypeForCSharpType(newTypeCSharp); newCol.DataType?.AlterTypeTo(newTypeSql); @@ -616,7 +616,7 @@ public void AddColumnTest(DatabaseType type,bool useTransaction) newCol = tbl.DiscoverColumn(fieldName); //make sure the type change happened - Assert.That(newCol.DataType?.GetLengthIfString(),Is.EqualTo(10)); + Assert.That(newCol.DataType?.GetLengthIfString(), Is.EqualTo(10)); } Assert.Multiple(() => @@ -624,22 +624,22 @@ public void AddColumnTest(DatabaseType type,bool useTransaction) //and should still be a primary key Assert.That(tbl.DiscoverColumn("Field1").IsPrimaryKey); //and should not be a primary key - Assert.That(tbl.DiscoverColumn(newColumnName).IsPrimaryKey,Is.False); + Assert.That(tbl.DiscoverColumn(newColumnName).IsPrimaryKey, Is.False); }); } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))] + [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))] public void ChangeDatabaseShouldNotAffectOriginalConnectionString_Test(DatabaseType type) { var database1 = GetTestDatabase(type); var stringBefore = database1.Server.Builder.ConnectionString; database1.Server.ExpectDatabase("SomeOtherDb"); - Assert.That(database1.Server.Builder.ConnectionString,Is.EqualTo(stringBefore)); + Assert.That(database1.Server.Builder.ConnectionString, Is.EqualTo(stringBefore)); } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypesWithTwoBoolFlags))] - public void TestDistincting(DatabaseType type,bool useTransaction,bool dodgyNames) + [TestCaseSource(typeof(All), nameof(All.DatabaseTypesWithTwoBoolFlags))] + public void TestDistincting(DatabaseType type, bool useTransaction, bool dodgyNames) { var database = GetTestDatabase(type); @@ -656,24 +656,24 @@ public void TestDistincting(DatabaseType type,bool useTransaction,bool dodgyName dt.Columns.Add("Field2"); dt.Columns.Add(dodgyNames ? ",,,," : "Field3"); - dt.Rows.Add("dave","2001-01-01","50"); - dt.Rows.Add("dave","2001-01-01","50"); - dt.Rows.Add("dave","2001-01-01","50"); - dt.Rows.Add("dave","2001-01-01","50"); - dt.Rows.Add("frank","2001-01-01","50"); - dt.Rows.Add("frank","2001-01-01","50"); - dt.Rows.Add("frank","2001-01-01","51"); + dt.Rows.Add("dave", "2001-01-01", "50"); + dt.Rows.Add("dave", "2001-01-01", "50"); + dt.Rows.Add("dave", "2001-01-01", "50"); + dt.Rows.Add("dave", "2001-01-01", "50"); + dt.Rows.Add("frank", "2001-01-01", "50"); + dt.Rows.Add("frank", "2001-01-01", "50"); + dt.Rows.Add("frank", "2001-01-01", "51"); Assert.Multiple(() => { - Assert.That(tbl.Database.DiscoverTables(false),Has.Length.EqualTo(1)); - Assert.That(tbl.GetRowCount(),Is.EqualTo(0)); + Assert.That(tbl.Database.DiscoverTables(false), Has.Length.EqualTo(1)); + Assert.That(tbl.GetRowCount(), Is.EqualTo(0)); }); using (var insert = tbl.BeginBulkInsert()) insert.Upload(dt); - Assert.That(tbl.GetRowCount(),Is.EqualTo(7)); + Assert.That(tbl.GetRowCount(), Is.EqualTo(7)); if (useTransaction) { @@ -686,56 +686,56 @@ public void TestDistincting(DatabaseType type,bool useTransaction,bool dodgyName Assert.Multiple(() => { - Assert.That(tbl.GetRowCount(),Is.EqualTo(3)); - Assert.That(tbl.Database.DiscoverTables(false),Has.Length.EqualTo(1)); + Assert.That(tbl.GetRowCount(), Is.EqualTo(3)); + Assert.That(tbl.Database.DiscoverTables(false), Has.Length.EqualTo(1)); }); } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))] + [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))] public void TestIntDataTypes(DatabaseType type) { var database = GetTestDatabase(type); var dt = new DataTable(); - dt.Columns.Add("MyCol",typeof(decimal)); + dt.Columns.Add("MyCol", typeof(decimal)); dt.Rows.Add("100"); dt.Rows.Add("105"); dt.Rows.Add("1"); - var tbl = database.CreateTable("IntTestTable",dt); + var tbl = database.CreateTable("IntTestTable", dt); dt = tbl.GetDataTable(); Assert.Multiple(() => { - Assert.That(dt.Rows.OfType().Count(static r => Convert.ToInt32(r[0]) == 100),Is.EqualTo(1)); - Assert.That(dt.Rows.OfType().Count(static r => Convert.ToInt32(r[0]) == 105),Is.EqualTo(1)); - Assert.That(dt.Rows.OfType().Count(static r => Convert.ToInt32(r[0]) == 1),Is.EqualTo(1)); + Assert.That(dt.Rows.OfType().Count(static r => Convert.ToInt32(r[0]) == 100), Is.EqualTo(1)); + Assert.That(dt.Rows.OfType().Count(static r => Convert.ToInt32(r[0]) == 105), Is.EqualTo(1)); + Assert.That(dt.Rows.OfType().Count(static r => Convert.ToInt32(r[0]) == 1), Is.EqualTo(1)); }); var col = tbl.DiscoverColumn("MyCol"); col.DataType?.AlterTypeTo("decimal(5,2)"); var size = tbl.DiscoverColumn("MyCol").DataType?.GetDecimalSize(); - Assert.That(size,Is.EqualTo(new DecimalSize(3,2))); //3 before decimal place 2 after; + Assert.That(size, Is.EqualTo(new DecimalSize(3, 2))); //3 before decimal place 2 after; Assert.Multiple(() => { - Assert.That(size.NumbersBeforeDecimalPlace,Is.EqualTo(3)); - Assert.That(size.NumbersAfterDecimalPlace,Is.EqualTo(2)); - Assert.That(size.Precision,Is.EqualTo(5)); - Assert.That(size.Scale,Is.EqualTo(2)); + Assert.That(size.NumbersBeforeDecimalPlace, Is.EqualTo(3)); + Assert.That(size.NumbersAfterDecimalPlace, Is.EqualTo(2)); + Assert.That(size.Precision, Is.EqualTo(5)); + Assert.That(size.Scale, Is.EqualTo(2)); }); dt = tbl.GetDataTable(); Assert.Multiple(() => { - Assert.That(dt.Rows.OfType().Count(static r => Convert.ToDecimal(r[0]) == new decimal(100.0f)),Is.EqualTo(1)); - Assert.That(dt.Rows.OfType().Count(static r => Convert.ToDecimal(r[0]) == new decimal(105.0f)),Is.EqualTo(1)); - Assert.That(dt.Rows.OfType().Count(static r => Convert.ToDecimal(r[0]) == new decimal(1.0f)),Is.EqualTo(1)); + Assert.That(dt.Rows.OfType().Count(static r => Convert.ToDecimal(r[0]) == new decimal(100.0f)), Is.EqualTo(1)); + Assert.That(dt.Rows.OfType().Count(static r => Convert.ToDecimal(r[0]) == new decimal(105.0f)), Is.EqualTo(1)); + Assert.That(dt.Rows.OfType().Count(static r => Convert.ToDecimal(r[0]) == new decimal(1.0f)), Is.EqualTo(1)); }); } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))] + [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))] public void TestFloatDataTypes(DatabaseType type) { var database = GetTestDatabase(type); @@ -747,45 +747,45 @@ public void TestFloatDataTypes(DatabaseType type) dt.Rows.Add("105"); dt.Rows.Add("2.1"); - var tbl = database.CreateTable("DecimalTestTable",dt); + var tbl = database.CreateTable("DecimalTestTable", dt); dt =tbl.GetDataTable(); Assert.Multiple(() => { - Assert.That(dt.Rows.OfType().Count(static r => Convert.ToDecimal(r[0]) == new decimal(100.0f)),Is.EqualTo(1)); - Assert.That(dt.Rows.OfType().Count(static r => Convert.ToDecimal(r[0]) == new decimal(105.0f)),Is.EqualTo(1)); - Assert.That(dt.Rows.OfType().Count(static r => Convert.ToDecimal(r[0]) == new decimal(2.1f)),Is.EqualTo(1)); + Assert.That(dt.Rows.OfType().Count(static r => Convert.ToDecimal(r[0]) == new decimal(100.0f)), Is.EqualTo(1)); + Assert.That(dt.Rows.OfType().Count(static r => Convert.ToDecimal(r[0]) == new decimal(105.0f)), Is.EqualTo(1)); + Assert.That(dt.Rows.OfType().Count(static r => Convert.ToDecimal(r[0]) == new decimal(2.1f)), Is.EqualTo(1)); }); var col = tbl.DiscoverColumn("MyCol"); var size = col.DataType?.GetDecimalSize(); - Assert.That(size,Is.EqualTo(new DecimalSize(3,1))); //3 before decimal place 2 after; + Assert.That(size, Is.EqualTo(new DecimalSize(3, 1))); //3 before decimal place 2 after; Assert.Multiple(() => { - Assert.That(size.NumbersBeforeDecimalPlace,Is.EqualTo(3)); - Assert.That(size.NumbersAfterDecimalPlace,Is.EqualTo(1)); - Assert.That(size.Precision,Is.EqualTo(4)); - Assert.That(size.Scale,Is.EqualTo(1)); + Assert.That(size.NumbersBeforeDecimalPlace, Is.EqualTo(3)); + Assert.That(size.NumbersAfterDecimalPlace, Is.EqualTo(1)); + Assert.That(size.Precision, Is.EqualTo(4)); + Assert.That(size.Scale, Is.EqualTo(1)); }); col.DataType?.AlterTypeTo("decimal(5,2)"); size = tbl.DiscoverColumn("MyCol").DataType?.GetDecimalSize(); - Assert.That(size,Is.EqualTo(new DecimalSize(3,2))); //3 before decimal place 2 after; + Assert.That(size, Is.EqualTo(new DecimalSize(3, 2))); //3 before decimal place 2 after; Assert.Multiple(() => { - Assert.That(size.NumbersBeforeDecimalPlace,Is.EqualTo(3)); - Assert.That(size.NumbersAfterDecimalPlace,Is.EqualTo(2)); - Assert.That(size.Precision,Is.EqualTo(5)); - Assert.That(size.Scale,Is.EqualTo(2)); + Assert.That(size.NumbersBeforeDecimalPlace, Is.EqualTo(3)); + Assert.That(size.NumbersAfterDecimalPlace, Is.EqualTo(2)); + Assert.That(size.Precision, Is.EqualTo(5)); + Assert.That(size.Scale, Is.EqualTo(2)); }); } - [TestCase(DatabaseType.MySql,"_-o-_",":>0<:")] - [TestCase(DatabaseType.MicrosoftSQLServer,"_-o-_",":>0<:")] - [TestCase(DatabaseType.PostgreSql,"_-o-_",":>0<:")] - public void HorribleDatabaseAndTableNames(DatabaseType type,string horribleDatabaseName,string horribleTableName) + [TestCase(DatabaseType.MySql, "_-o-_", ":>0<:")] + [TestCase(DatabaseType.MicrosoftSQLServer, "_-o-_", ":>0<:")] + [TestCase(DatabaseType.PostgreSql, "_-o-_", ":>0<:")] + public void HorribleDatabaseAndTableNames(DatabaseType type, string horribleDatabaseName, string horribleTableName) { AssertCanCreateDatabases(); @@ -796,9 +796,9 @@ public void HorribleDatabaseAndTableNames(DatabaseType type,string horribleDatab database.Server.CreateDatabase(horribleDatabaseName); database = database.Server.ExpectDatabase(horribleDatabaseName); - if(type != DatabaseType.PostgreSql) + if (type != DatabaseType.PostgreSql) database.Create(true); - + SqlConnection.ClearAllPools(); try @@ -815,31 +815,31 @@ public void HorribleDatabaseAndTableNames(DatabaseType type,string horribleDatab dt.Columns.Add("Field2"); dt.Columns.Add("Field3"); - dt.Rows.Add("dave","2001-01-01","50"); - dt.Rows.Add("dave","2001-01-01","50"); - dt.Rows.Add("dave","2001-01-01","50"); - dt.Rows.Add("dave","2001-01-01","50"); - dt.Rows.Add("frank","2001-01-01","50"); - dt.Rows.Add("frank","2001-01-01","50"); - dt.Rows.Add("frank","2001-01-01","51"); + dt.Rows.Add("dave", "2001-01-01", "50"); + dt.Rows.Add("dave", "2001-01-01", "50"); + dt.Rows.Add("dave", "2001-01-01", "50"); + dt.Rows.Add("dave", "2001-01-01", "50"); + dt.Rows.Add("frank", "2001-01-01", "50"); + dt.Rows.Add("frank", "2001-01-01", "50"); + dt.Rows.Add("frank", "2001-01-01", "51"); Assert.Multiple(() => { - Assert.That(tbl.Database.DiscoverTables(false),Has.Length.EqualTo(1)); - Assert.That(tbl.GetRowCount(),Is.EqualTo(0)); + Assert.That(tbl.Database.DiscoverTables(false), Has.Length.EqualTo(1)); + Assert.That(tbl.GetRowCount(), Is.EqualTo(0)); }); using (var insert = tbl.BeginBulkInsert()) insert.Upload(dt); - Assert.That(tbl.GetRowCount(),Is.EqualTo(7)); + Assert.That(tbl.GetRowCount(), Is.EqualTo(7)); tbl.MakeDistinct(); Assert.Multiple(() => { - Assert.That(tbl.GetRowCount(),Is.EqualTo(3)); - Assert.That(tbl.Database.DiscoverTables(false),Has.Length.EqualTo(1)); + Assert.That(tbl.GetRowCount(), Is.EqualTo(3)); + Assert.That(tbl.Database.DiscoverTables(false), Has.Length.EqualTo(1)); }); tbl.Truncate(); @@ -855,15 +855,15 @@ public void HorribleDatabaseAndTableNames(DatabaseType type,string horribleDatab } } - [TestCase(DatabaseType.MySql,"my (database)","my (table)","my (col)")] - [TestCase(DatabaseType.MicrosoftSQLServer,"my (database)","my (table)","my (col)")] - [TestCase(DatabaseType.Oracle,"my (database)","my (table)","my (col)")] - [TestCase(DatabaseType.MySql,"my.database","my.table","my.col")] - [TestCase(DatabaseType.MicrosoftSQLServer,"my.database","my.table","my.col")] - [TestCase(DatabaseType.Oracle,"my.database","my.table","my.col")] - [TestCase(DatabaseType.PostgreSql,"my (database)","my (table)","my (col)")] - [TestCase(DatabaseType.PostgreSql,"my.database","my.table","my.col")] - public void UnsupportedEntityNames(DatabaseType type,string horribleDatabaseName,string horribleTableName,string columnName) + [TestCase(DatabaseType.MySql, "my (database)", "my (table)", "my (col)")] + [TestCase(DatabaseType.MicrosoftSQLServer, "my (database)", "my (table)", "my (col)")] + [TestCase(DatabaseType.Oracle, "my (database)", "my (table)", "my (col)")] + [TestCase(DatabaseType.MySql, "my.database", "my.table", "my.col")] + [TestCase(DatabaseType.MicrosoftSQLServer, "my.database", "my.table", "my.col")] + [TestCase(DatabaseType.Oracle, "my.database", "my.table", "my.col")] + [TestCase(DatabaseType.PostgreSql, "my (database)", "my (table)", "my (col)")] + [TestCase(DatabaseType.PostgreSql, "my.database", "my.table", "my.col")] + public void UnsupportedEntityNames(DatabaseType type, string horribleDatabaseName, string horribleTableName, string columnName) { var database = GetTestDatabase(type); @@ -873,12 +873,12 @@ public void UnsupportedEntityNames(DatabaseType type,string horribleDatabaseName //ExpectDatabase with illegal name Assert.That( Assert.Throws(() => database.Server.ExpectDatabase(horribleDatabaseName)) - ?.Message,Does.Match("Database .* contained unsupported .* characters")); + ?.Message, Does.Match("Database .* contained unsupported .* characters")); //ExpectTable with illegal name Assert.That( Assert.Throws(() => database.ExpectTable(horribleTableName)) - ?.Message,Does.Match("Table .* contained unsupported .* characters")); + ?.Message, Does.Match("Table .* contained unsupported .* characters")); //CreateTable with illegal name Assert.That( @@ -886,7 +886,7 @@ public void UnsupportedEntityNames(DatabaseType type,string horribleDatabaseName [ new DatabaseColumnRequest("a", new DatabaseTypeRequest(typeof(string), 10)) ])) - ?.Message,Does.Match("Table .* contained unsupported .* characters")); + ?.Message, Does.Match("Table .* contained unsupported .* characters")); //CreateTable with (column) illegal name Assert.That( @@ -894,7 +894,7 @@ public void UnsupportedEntityNames(DatabaseType type,string horribleDatabaseName [ new DatabaseColumnRequest(columnName, new DatabaseTypeRequest(typeof(string), 10)) ])) - ?.Message,Does.Match("Column .* contained unsupported .* characters")); + ?.Message, Does.Match("Column .* contained unsupported .* characters")); }); AssertCanCreateDatabases(); @@ -902,20 +902,20 @@ public void UnsupportedEntityNames(DatabaseType type,string horribleDatabaseName //CreateDatabase with illegal name Assert.That( Assert.Throws(() => database.Server.CreateDatabase(horribleDatabaseName)) - ?.Message,Does.Match("Database .* contained unsupported .* characters")); + ?.Message, Does.Match("Database .* contained unsupported .* characters")); } - [TestCase(DatabaseType.MySql,"_-o-_",":>0<:","-_")] - [TestCase(DatabaseType.MySql,"Comment","SSSS","Space Out")] - [TestCase(DatabaseType.MicrosoftSQLServer,"_-o-_",":>0<:","-_")] - [TestCase(DatabaseType.MicrosoftSQLServer,"Comment","SSSS","Space Out")] - [TestCase(DatabaseType.Oracle,"_-o-_",":>0<:","-_")] - [TestCase(DatabaseType.Oracle,"Comment","Comment","Comment")] //reserved keyword in Oracle - [TestCase(DatabaseType.Oracle,"Comment","SSSS","Space Out")] - [TestCase(DatabaseType.PostgreSql,"_-o-_",":>0<:","-_")] - [TestCase(DatabaseType.PostgreSql,"Comment","Comment","Comment")] //reserved keyword in Oracle - [TestCase(DatabaseType.PostgreSql,"Comment","SSSS","Space Out")] - public void HorribleColumnNames(DatabaseType type,string horribleDatabaseName,string horribleTableName,string columnName) + [TestCase(DatabaseType.MySql, "_-o-_", ":>0<:", "-_")] + [TestCase(DatabaseType.MySql, "Comment", "SSSS", "Space Out")] + [TestCase(DatabaseType.MicrosoftSQLServer, "_-o-_", ":>0<:", "-_")] + [TestCase(DatabaseType.MicrosoftSQLServer, "Comment", "SSSS", "Space Out")] + [TestCase(DatabaseType.Oracle, "_-o-_", ":>0<:", "-_")] + [TestCase(DatabaseType.Oracle, "Comment", "Comment", "Comment")] //reserved keyword in Oracle + [TestCase(DatabaseType.Oracle, "Comment", "SSSS", "Space Out")] + [TestCase(DatabaseType.PostgreSql, "_-o-_", ":>0<:", "-_")] + [TestCase(DatabaseType.PostgreSql, "Comment", "Comment", "Comment")] //reserved keyword in Oracle + [TestCase(DatabaseType.PostgreSql, "Comment", "SSSS", "Space Out")] + public void HorribleColumnNames(DatabaseType type, string horribleDatabaseName, string horribleTableName, string columnName) { AssertCanCreateDatabases(); @@ -931,20 +931,20 @@ public void HorribleColumnNames(DatabaseType type,string horribleDatabaseName,st dt.Rows.Add("dave"); dt.PrimaryKey = [dt.Columns[0]]; - var tbl = database.CreateTable(horribleTableName,dt); + var tbl = database.CreateTable(horribleTableName, dt); Assert.Multiple(() => { - Assert.That(tbl.GetRowCount(),Is.EqualTo(1)); + Assert.That(tbl.GetRowCount(), Is.EqualTo(1)); Assert.That(tbl.DiscoverColumns().Single().IsPrimaryKey); - Assert.That(tbl.GetDataTable().Rows,Has.Count.EqualTo(1)); + Assert.That(tbl.GetDataTable().Rows, Has.Count.EqualTo(1)); }); - tbl.Insert(new Dictionary { { columnName,"fff" } }); + tbl.Insert(new Dictionary { { columnName, "fff" } }); - Assert.That(tbl.GetDataTable().Rows,Has.Count.EqualTo(2)); + Assert.That(tbl.GetDataTable().Rows, Has.Count.EqualTo(2)); } finally { @@ -952,7 +952,7 @@ public void HorribleColumnNames(DatabaseType type,string horribleDatabaseName,st } } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))] + [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))] public void CreateTable_AutoIncrementColumnTest(DatabaseType type) { var database = GetTestDatabase(type); @@ -976,23 +976,23 @@ public void CreateTable_AutoIncrementColumnTest(DatabaseType type) using (var bulkInsert = tbl.BeginBulkInsert()) bulkInsert.Upload(dt); - Assert.That(tbl.GetRowCount(),Is.EqualTo(1)); + Assert.That(tbl.GetRowCount(), Is.EqualTo(1)); var result = tbl.GetDataTable(); - Assert.That(result.Rows,Has.Count.EqualTo(1)); + Assert.That(result.Rows, Has.Count.EqualTo(1)); Assert.Multiple(() => { - Assert.That(result.Rows[0]["IdColumn"],Is.EqualTo(1)); + Assert.That(result.Rows[0]["IdColumn"], Is.EqualTo(1)); Assert.That(tbl.DiscoverColumn("IdColumn").IsAutoIncrement); - Assert.That(tbl.DiscoverColumn("Name").IsAutoIncrement,Is.False); + Assert.That(tbl.DiscoverColumn("Name").IsAutoIncrement, Is.False); }); - var autoIncrement = tbl.Insert(new Dictionary { { "Name","Tony" } }); - Assert.That(autoIncrement,Is.EqualTo(2)); + var autoIncrement = tbl.Insert(new Dictionary { { "Name", "Tony" } }); + Assert.That(autoIncrement, Is.EqualTo(2)); } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))] + [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))] public void CreateTable_DefaultTest_Date(DatabaseType type) { var database = GetTestDatabase(type); @@ -1024,14 +1024,14 @@ public void CreateTable_DefaultTest_Date(DatabaseType type) Assert.Multiple(() => { - Assert.That(databaseValue.Year,Is.EqualTo(currentValue.Year)); - Assert.That(databaseValue.Month,Is.EqualTo(currentValue.Month)); - Assert.That(databaseValue.Day,Is.EqualTo(currentValue.Day)); - Assert.That(databaseValue.Hour,Is.EqualTo(currentValue.Hour)); + Assert.That(databaseValue.Year, Is.EqualTo(currentValue.Year)); + Assert.That(databaseValue.Month, Is.EqualTo(currentValue.Month)); + Assert.That(databaseValue.Day, Is.EqualTo(currentValue.Day)); + Assert.That(databaseValue.Hour, Is.EqualTo(currentValue.Hour)); }); } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))] + [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))] public void CreateTable_DefaultTest_Guid(DatabaseType type) { var database = GetTestDatabase(type); @@ -1047,7 +1047,7 @@ public void CreateTable_DefaultTest_Guid(DatabaseType type) //we need this extension on the server to work using var con = database.Server.GetConnection(); con.Open(); - using var cmd = database.Server.GetCommand("CREATE EXTENSION IF NOT EXISTS pgcrypto;",con); + using var cmd = database.Server.GetCommand("CREATE EXTENSION IF NOT EXISTS pgcrypto;", con); cmd.ExecuteNonQuery(); break; } @@ -1076,10 +1076,10 @@ public void CreateTable_DefaultTest_Guid(DatabaseType type) var databaseValue = (string)dt2.Rows.Cast().Single()["MyGuid"]; - Assert.That(databaseValue,Is.Not.Null); + Assert.That(databaseValue, Is.Not.Null); } - [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))] + [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))] public void Test_BulkInserting_LotsOfDates(DatabaseType type) { var culture = new CultureInfo("en-gb"); @@ -1093,13 +1093,13 @@ public void Test_BulkInserting_LotsOfDates(DatabaseType type) ]); //test basic insert - foreach (var s in someDates) - tbl.Insert(new Dictionary + foreach (var s in _someDates) + tbl.Insert(new Dictionary { {"ID",1}, {"MyDate",s}, {"MyString",Guid.NewGuid().ToString()} - },culture + }, culture ); @@ -1109,20 +1109,20 @@ public void Test_BulkInserting_LotsOfDates(DatabaseType type) dt.Columns.Add("mydate"); dt.Columns.Add("mystring"); - foreach (var s in someDates) - dt.Rows.Add(2,s,Guid.NewGuid().ToString()); + foreach (var s in _someDates) + dt.Rows.Add(2, s, Guid.NewGuid().ToString()); - Assert.That(tbl.GetRowCount(),Is.EqualTo(someDates.Length)); + Assert.That(tbl.GetRowCount(), Is.EqualTo(_someDates.Length)); using (var bulkInsert = tbl.BeginBulkInsert(culture)) { bulkInsert.Upload(dt); } - Assert.That(tbl.GetRowCount(),Is.EqualTo(someDates.Length*2)); + Assert.That(tbl.GetRowCount(), Is.EqualTo(_someDates.Length*2)); } - private readonly string[] someDates = [ + private readonly string[] _someDates = [ "22\\5\\19", "22/5/19", "22-5-19", @@ -1371,11 +1371,11 @@ public void Test_BulkInserting_LotsOfDates(DatabaseType type) public void DateTimeTypeDeciderPerformance() { var d = new DateTimeTypeDecider(new CultureInfo("en-gb")); - var dt = new DateTime(2019,5,22,8,59,36); + var dt = new DateTime(2019, 5, 22, 8, 59, 36); foreach (var f in DateTimeTypeDecider.DateFormatsDM) d.Parse(dt.ToString(f)); foreach (var f in DateTimeTypeDecider.TimeFormats) d.Parse(dt.ToString(f)); - Assert.That(d.Parse("28/2/1993 5:36:27 AM"),Is.EqualTo(new DateTime(1993,2,28,5,36,27))); + Assert.That(d.Parse("28/2/1993 5:36:27 AM"), Is.EqualTo(new DateTime(1993, 2, 28, 5, 36, 27))); } } \ No newline at end of file diff --git a/Tests/FAnsiTests/DatabaseTests.cs b/Tests/FAnsiTests/DatabaseTests.cs index 62933cbc..29796671 100644 --- a/Tests/FAnsiTests/DatabaseTests.cs +++ b/Tests/FAnsiTests/DatabaseTests.cs @@ -73,7 +73,7 @@ public void CheckFiles() if (databaseType != DatabaseType.PostgreSql) continue; var server = GetTestServer(DatabaseType.PostgreSql); - if (server.DiscoverDatabases().All(db => db.GetWrappedName()?.Contains(_testScratchDatabase) != true)) server.CreateDatabase(_testScratchDatabase); + if (server.DiscoverDatabases().All(db => db.GetWrappedName().Contains(_testScratchDatabase) != true)) server.CreateDatabase(_testScratchDatabase); } } diff --git a/Tests/FAnsiTests/Table/TableValuedFunctionTests.cs b/Tests/FAnsiTests/Table/TableValuedFunctionTests.cs index c4fa5e93..d54ac819 100644 --- a/Tests/FAnsiTests/Table/TableValuedFunctionTests.cs +++ b/Tests/FAnsiTests/Table/TableValuedFunctionTests.cs @@ -4,11 +4,11 @@ namespace FAnsiTests.Table; -internal sealed class TableValuedFunctionTests:DatabaseTests +internal sealed class TableValuedFunctionTests : DatabaseTests { [TestCase("dbo")] [TestCase("Omg")] - public void Test_DropTableValuedFunction(string schema) + public void Test_DropTableValuedFunction(string? schema) { var db = GetTestDatabase(DatabaseType.MicrosoftSQLServer); @@ -17,12 +17,14 @@ public void Test_DropTableValuedFunction(string schema) con.Open(); //create the schema if it doesn't exist yet - if(schema != null) - db.Server.GetCommand($@" -IF NOT EXISTS ( SELECT * - FROM sys.schemas - WHERE name = N'{schema}' ) -EXEC('CREATE SCHEMA {schema}')", con).ExecuteNonQuery(); + if (schema != null) + db.Server.GetCommand($""" + + IF NOT EXISTS ( SELECT * + FROM sys.schemas + WHERE name = N'{schema}' ) + EXEC('CREATE SCHEMA {schema}') + """, con).ExecuteNonQuery(); var sql = $@"CREATE FUNCTION {schema}.MyAwesomeFunction @@ -53,7 +55,7 @@ Name varchar(50) RETURN END"; - db.Server.GetCommand(sql,con).ExecuteNonQuery(); + db.Server.GetCommand(sql, con).ExecuteNonQuery(); } var tvf = db.DiscoverTableValuedFunctions().Single(); diff --git a/Tests/FAnsiTests/Table/UpdateTests.cs b/Tests/FAnsiTests/Table/UpdateTests.cs index eed0f46b..e8587923 100644 --- a/Tests/FAnsiTests/Table/UpdateTests.cs +++ b/Tests/FAnsiTests/Table/UpdateTests.cs @@ -30,7 +30,7 @@ public void Test_UpdateTableFromJoin(DatabaseType dbType) tbl1 = db.CreateTable("HighScoresTable", dt1); } - using(var dt2 = new DataTable()) + using (var dt2 = new DataTable()) { dt2.Columns.Add("Name"); dt2.Columns.Add("Score"); @@ -50,24 +50,24 @@ public void Test_UpdateTableFromJoin(DatabaseType dbType) var score = syntaxHelper.EnsureWrapped("Score"); var name = syntaxHelper.EnsureWrapped("Name"); - queryLines.Add(new CustomLine($"t1.{highScore} = t2.{score}",QueryComponent.SET)); - queryLines.Add(new CustomLine($"t1.{highScore} < t2.{score} OR t1.{highScore} is null",QueryComponent.WHERE)); - queryLines.Add(new CustomLine($"t1.{name} = t2.{name}",QueryComponent.JoinInfoJoin)); + queryLines.Add(new CustomLine($"t1.{highScore} = t2.{score}", QueryComponent.SET)); + queryLines.Add(new CustomLine($"t1.{highScore} < t2.{score} OR t1.{highScore} is null", QueryComponent.WHERE)); + queryLines.Add(new CustomLine($"t1.{name} = t2.{name}", QueryComponent.JoinInfoJoin)); var sql = updateHelper.BuildUpdate(tbl1, tbl2, queryLines); using var con = db.Server.GetConnection(); con.Open(); - using (var cmd = db.Server.GetCommand(sql,con)) + using (var cmd = db.Server.GetCommand(sql, con)) Assert.That(cmd.ExecuteNonQuery(), Is.EqualTo(1)); //Frank should have got a new high score of 900 - using (var cmd = db.Server.GetCommand($"SELECT {highScore} from {tbl1.GetFullyQualifiedName()} WHERE {name} = 'Frank'",con)) - Assert.That(cmd.ExecuteScalar(),Is.EqualTo(900)); + using (var cmd = db.Server.GetCommand($"SELECT {highScore} from {tbl1.GetFullyQualifiedName()} WHERE {name} = 'Frank'", con)) + Assert.That(cmd.ExecuteScalar(), Is.EqualTo(900)); //Dave should have his old score of 100 - using (var cmd = db.Server.GetCommand($"SELECT {highScore} from {tbl1.GetFullyQualifiedName()} WHERE {name} = 'Dave'",con)) - Assert.That(cmd.ExecuteScalar(),Is.EqualTo(100)); + using (var cmd = db.Server.GetCommand($"SELECT {highScore} from {tbl1.GetFullyQualifiedName()} WHERE {name} = 'Dave'", con)) + Assert.That(cmd.ExecuteScalar(), Is.EqualTo(100)); } } \ No newline at end of file diff --git a/Tests/FAnsiTests/TypeTranslation/DatatypeComputerTests.cs b/Tests/FAnsiTests/TypeTranslation/DatatypeComputerTests.cs index 1b4a7cb0..2b89f094 100644 --- a/Tests/FAnsiTests/TypeTranslation/DatatypeComputerTests.cs +++ b/Tests/FAnsiTests/TypeTranslation/DatatypeComputerTests.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using FAnsi.Discovery.TypeTranslation; using FAnsi.Implementations.MicrosoftSQL; using NUnit.Framework; using TypeGuesser; diff --git a/Tests/FAnsiTests/TypeTranslation/TypeTranslaterUnitTests.cs b/Tests/FAnsiTests/TypeTranslation/TypeTranslaterUnitTests.cs index e503b284..917efcf7 100644 --- a/Tests/FAnsiTests/TypeTranslation/TypeTranslaterUnitTests.cs +++ b/Tests/FAnsiTests/TypeTranslation/TypeTranslaterUnitTests.cs @@ -13,9 +13,9 @@ internal sealed class TypeTranslaterUnitTests /// /// /// - [TestCase(DatabaseType.MicrosoftSQLServer,"varchar2(10)",true)] + [TestCase(DatabaseType.MicrosoftSQLServer, "varchar2(10)", true)] [TestCase(DatabaseType.MicrosoftSQLServer, "monkeychar7", true)] - public void Test_IsSupportedType(DatabaseType dbType,string sqlDbType,bool expectedOutcome) + public void Test_IsSupportedType(DatabaseType dbType, string sqlDbType, bool expectedOutcome) { var tt = ImplementationManager.GetImplementation(dbType).GetQuerySyntaxHelper().TypeTranslater; Assert.That(tt.IsSupportedSQLDBType(sqlDbType), Is.EqualTo(expectedOutcome), $"Unexpected result for IsSupportedSQLDBType with {dbType}. Input was '{sqlDbType}' expected {expectedOutcome}");