diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 863202f..c58c2ac 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -37,3 +37,20 @@ jobs:
uses: github/codeql-action/analyze@v3
with:
category: "/language:csharp"
+ upload: False
+ output: sarif-results
+
+ - name: filter-sarif
+ uses: advanced-security/filter-sarif@v1
+ with:
+ patterns: |
+ +**/*
+ -**/*.g.cs
+ -**/Tests/**
+ input: sarif-results/csharp.sarif
+ output: sarif-results/csharp.sarif
+
+ - name: Upload SARIF
+ uses: github/codeql-action/upload-sarif@v3
+ with:
+ sarif_file: sarif-results/csharp.sarif
diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml
index 102adbf..d8126c1 100644
--- a/.github/workflows/dotnet-core.yml
+++ b/.github/workflows/dotnet-core.yml
@@ -1,4 +1,4 @@
-name: .NET Core
+name: Build and test
on:
push
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0537b77..ba92555 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [3.2.0] - 2023-03-04
+
+- Target .Net 8.0
+- Enable AOT compatibility, though upstream dependencies still have issues
+- Nullable annotations enabled (some warnings remain)
+- Bump HIC.TypeGuesser from 1.1.0 to 1.2.3
+- Bump Microsoft.Data.SqlClient from 5.1.1 to 5.2.0
+- Bump MySqlConnector from 2.2.6 to 2.3.5
+- Bump Npgsql from 7.0.4 to 8.0.2
+- Bump Oracle.ManagedDataAccess.Core from 3.21.100 to 3.21.130
+
## [3.1.1] - 2023-09-01
- Bugfix: MySQL text was erroneously capped at 64k (TEXT) instead of LONGTEXT (4GiB)
@@ -354,9 +365,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed Drop table to work correctly with Views
- Exists now works correctly for Views (previously it would return true if there was no view but a table with the same name)
-[Unreleased]: https://github.com/HicServices/FAnsiSql/compare/3.1.1...develop
-[3.1.1]: https://github.com/HicServices/FAnsiSql/compare/3.1.0...3.1.1
-[3.1.0]: https://github.com/HicServices/FAnsiSql/compare/3.0.1...3.1.0
+[Unreleased]: https://github.com/HicServices/FAnsiSql/compare/v3.2.0...develop
+[3.2.0]: https://github.com/HicServices/FAnsiSql/compare/v3.1.1...v3.2.0
+[3.1.1]: https://github.com/HicServices/FAnsiSql/compare/v3.1.0...v3.1.1
+[3.1.0]: https://github.com/HicServices/FAnsiSql/compare/3.0.1...v3.1.0
[3.0.1]: https://github.com/HicServices/FAnsiSql/compare/3.0.0...3.0.1
[3.0.0]: https://github.com/HicServices/FAnsiSql/compare/2.0.5...3.0.0
[2.0.5]: https://github.com/HicServices/FAnsiSql/compare/2.0.4...2.0.5
diff --git a/FAnsiSql/Connections/IManagedConnection.cs b/FAnsiSql/Connections/IManagedConnection.cs
index ef3ae5b..f53ca9c 100644
--- a/FAnsiSql/Connections/IManagedConnection.cs
+++ b/FAnsiSql/Connections/IManagedConnection.cs
@@ -22,7 +22,7 @@ public interface IManagedConnection : IDisposable
///
/// Optional - transaction being run (See . If this is not null then should also be not null.
///
- IManagedTransaction ManagedTransaction { get; }
+ IManagedTransaction? ManagedTransaction { get; }
///
/// True to close the connection in the Dispose step. If opened the connection itself during construction then this flag will default
diff --git a/FAnsiSql/Connections/ManagedConnection.cs b/FAnsiSql/Connections/ManagedConnection.cs
index 65f8a24..17e0e62 100644
--- a/FAnsiSql/Connections/ManagedConnection.cs
+++ b/FAnsiSql/Connections/ManagedConnection.cs
@@ -15,12 +15,12 @@ public sealed class ManagedConnection : IManagedConnection
public DbTransaction? Transaction { get; }
///
- public IManagedTransaction ManagedTransaction { get; }
+ public IManagedTransaction? ManagedTransaction { get; }
///
public bool CloseOnDispose { get; set; }
- internal ManagedConnection(DiscoveredServer discoveredServer, IManagedTransaction managedTransaction)
+ internal ManagedConnection(DiscoveredServer discoveredServer, IManagedTransaction? managedTransaction)
{
//get a new connection or use the existing one within the transaction
Connection = discoveredServer.GetConnection(managedTransaction);
@@ -37,7 +37,7 @@ internal ManagedConnection(DiscoveredServer discoveredServer, IManagedTransactio
Connection.Open();
}
- public ManagedConnection Clone() => (ManagedConnection) MemberwiseClone();
+ public ManagedConnection Clone() => (ManagedConnection)MemberwiseClone();
///
/// Closes and disposes the DbConnection unless this class is part of an
diff --git a/FAnsiSql/Discovery/BulkCopy.cs b/FAnsiSql/Discovery/BulkCopy.cs
index a26c227..a4f84c3 100644
--- a/FAnsiSql/Discovery/BulkCopy.cs
+++ b/FAnsiSql/Discovery/BulkCopy.cs
@@ -3,6 +3,7 @@
using System.Data;
using System.Globalization;
using System.Linq;
+using System.Threading;
using FAnsi.Connections;
using TypeGuesser;
using TypeGuesser.Deciders;
@@ -10,7 +11,7 @@
namespace FAnsi.Discovery;
///
-public abstract class BulkCopy:IBulkCopy
+public abstract class BulkCopy : IBulkCopy
{
public CultureInfo Culture { get; }
@@ -28,7 +29,9 @@ public abstract class BulkCopy:IBulkCopy
/// The cached columns found on the . If you alter the table midway through a bulk insert you must
/// call to refresh this.
///
- protected DiscoveredColumn[] TargetTableColumns;
+ protected DiscoveredColumn[] TargetTableColumns => _targetTableColumns.Value;
+
+ private Lazy _targetTableColumns;
///
/// When calling GetMapping if there are DataColumns in the input table that you are trying to bulk insert that are not matched
@@ -55,7 +58,9 @@ protected BulkCopy(DiscoveredTable targetTable, IManagedConnection connection, C
Culture = culture;
TargetTable = targetTable;
Connection = connection;
- InvalidateTableSchema();
+ _targetTableColumns = new Lazy(
+ () => TargetTable.DiscoverColumns(Connection.ManagedTransaction),
+ LazyThreadSafetyMode.ExecutionAndPublication);
AllowUnmatchedInputColumns = false;
DateTimeDecider = new DateTimeTypeDecider(culture);
}
@@ -68,7 +73,9 @@ protected BulkCopy(DiscoveredTable targetTable, IManagedConnection connection, C
///
public void InvalidateTableSchema()
{
- TargetTableColumns = TargetTable.DiscoverColumns(Connection.ManagedTransaction);
+ _targetTableColumns = new Lazy(
+ () => TargetTable.DiscoverColumns(Connection.ManagedTransaction),
+ LazyThreadSafetyMode.ExecutionAndPublication);
}
///
@@ -100,7 +107,7 @@ public virtual int Upload(DataTable dt)
///
protected void ConvertStringTypesToHardTypes(DataTable dt)
{
- var dict = GetMapping(dt.Columns.Cast(),out _);
+ var dict = GetMapping(dt.Columns.Cast(), out _);
var factory = new TypeDeciderFactory(Culture);
@@ -108,17 +115,17 @@ protected void ConvertStringTypesToHardTypes(DataTable dt)
var deciders = factory.Dictionary;
//for each column in the destination
- foreach(var (dataColumn, discoveredColumn) in dict)
+ foreach (var (dataColumn, discoveredColumn) in dict)
{
//if the destination column is a problematic type
- var dataType = discoveredColumn.DataType.GetCSharpDataType();
+ var dataType = discoveredColumn.DataType?.GetCSharpDataType();
if (!deciders.TryGetValue(dataType, out var decider)) continue;
//if it's already not a string then that's fine (hopefully it's a legit Type e.g. DateTime!)
- if(dataColumn.DataType != typeof(string))
+ if (dataColumn.DataType != typeof(string))
continue;
//create a new column hard typed to DateTime
- var newColumn = dt.Columns.Add($"{dataColumn.ColumnName}_{Guid.NewGuid()}",dataType);
+ var newColumn = dt.Columns.Add($"{dataColumn.ColumnName}_{Guid.NewGuid()}", dataType);
//if it's a DateTime decider then guess DateTime culture based on values in the table
if (decider is DateTimeTypeDecider)
@@ -129,30 +136,30 @@ protected void ConvertStringTypesToHardTypes(DataTable dt)
}
- foreach(DataRow dr in dt.Rows)
+ foreach (DataRow dr in dt.Rows)
try
{
//parse the value
- dr[newColumn] = decider.Parse(dr[dataColumn] as string)??DBNull.Value;
+ dr[newColumn] = dr[dataColumn] is string v ? decider.Parse(v) ?? DBNull.Value : DBNull.Value;
}
catch (Exception ex)
{
- throw new Exception($"Failed to parse value '{dr[dataColumn]}' in column '{dataColumn}'",ex);
+ throw new Exception($"Failed to parse value '{dr[dataColumn]}' in column '{dataColumn}'", ex);
}
//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 && dt.PrimaryKey.Contains(dataColumn))
+ dt.PrimaryKey = dt.PrimaryKey.Except(new[] { dataColumn }).Union(new[] { newColumn }).ToArray();
- var oldOrdinal = dataColumn.Ordinal;
+ var oldOrdinal = dataColumn.Ordinal;
//drop the original column
dt.Columns.Remove(dataColumn);
//rename the hard typed column to match the old column name
newColumn.ColumnName = dataColumn.ColumnName;
- if(oldOrdinal != -1)
+ if (oldOrdinal != -1)
newColumn.SetOrdinal(oldOrdinal);
}
}
@@ -198,5 +205,5 @@ protected Dictionary GetMapping(IEnumerable
///
///
- protected Dictionary GetMapping(IEnumerable inputColumns) => GetMapping(inputColumns, out _);
+ protected Dictionary GetMapping(IEnumerable inputColumns) => GetMapping(inputColumns, out _);
}
\ No newline at end of file
diff --git a/FAnsiSql/Discovery/Constraints/DiscoveredRelationship.cs b/FAnsiSql/Discovery/Constraints/DiscoveredRelationship.cs
index 3c74a05..de2d3b4 100644
--- a/FAnsiSql/Discovery/Constraints/DiscoveredRelationship.cs
+++ b/FAnsiSql/Discovery/Constraints/DiscoveredRelationship.cs
@@ -43,8 +43,8 @@ public sealed class DiscoveredRelationship(string fkName, DiscoveredTable pkTabl
///
public CascadeRule CascadeDelete { get; private set; } = deleteRule;
- private DiscoveredColumn[] _pkColumns;
- private DiscoveredColumn[] _fkColumns;
+ private DiscoveredColumn[]? _pkColumns;
+ private DiscoveredColumn[]? _fkColumns;
///
/// Discovers and adds the provided pair to . Column names must be members of and (respectively)
@@ -52,16 +52,13 @@ public sealed class DiscoveredRelationship(string fkName, DiscoveredTable pkTabl
///
///
///
- public void AddKeys(string primaryKeyCol, string foreignKeyCol,IManagedTransaction? transaction = null)
+ public void AddKeys(string primaryKeyCol, string foreignKeyCol, IManagedTransaction? transaction = null)
{
- if (_pkColumns == null)
- {
- _pkColumns = PrimaryKeyTable.DiscoverColumns(transaction);
- _fkColumns = ForeignKeyTable.DiscoverColumns(transaction);
- }
+ _pkColumns ??= PrimaryKeyTable.DiscoverColumns(transaction);
+ _fkColumns ??= ForeignKeyTable.DiscoverColumns(transaction);
Keys.Add(
- _pkColumns.Single(c=>c.GetRuntimeName().Equals(primaryKeyCol,StringComparison.CurrentCultureIgnoreCase)),
+ _pkColumns.Single(c => c.GetRuntimeName().Equals(primaryKeyCol, StringComparison.CurrentCultureIgnoreCase)),
_fkColumns.Single(c => c.GetRuntimeName().Equals(foreignKeyCol, StringComparison.CurrentCultureIgnoreCase))
);
}
diff --git a/FAnsiSql/Discovery/DiscoveredColumn.cs b/FAnsiSql/Discovery/DiscoveredColumn.cs
index 7b4f8af..a5b85e0 100644
--- a/FAnsiSql/Discovery/DiscoveredColumn.cs
+++ b/FAnsiSql/Discovery/DiscoveredColumn.cs
@@ -1,4 +1,5 @@
-using FAnsi.Discovery.QuerySyntax;
+using System.Diagnostics.CodeAnalysis;
+using FAnsi.Discovery.QuerySyntax;
using FAnsi.Naming;
using TypeGuesser;
@@ -29,7 +30,7 @@ public sealed class DiscoveredColumn(DiscoveredTable table, string name, bool al
///
/// True if the column allows rows with nulls in this column
///
- public bool AllowNulls { get; } = allowsNulls;
+ public readonly bool AllowNulls = allowsNulls;
///
/// True if the column is part of the primary key (a primary key can consist of mulitple columns)
@@ -50,12 +51,12 @@ public sealed class DiscoveredColumn(DiscoveredTable table, string name, bool al
///
/// The data type of the column found (includes String Length and Scale/Precision).
///
- public DiscoveredDataType DataType { get; set; }
+ public DiscoveredDataType? DataType { get; set; }
///
/// The character set of the column (if char)
///
- public string Format { get; set; }
+ public string? Format { get; set; }
private readonly string _name = name;
private readonly IQuerySyntaxHelper _querySyntaxHelper = table.Database.Server.GetQuerySyntaxHelper();
@@ -64,13 +65,14 @@ public sealed class DiscoveredColumn(DiscoveredTable table, string name, bool al
/// 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`
///
///
- public string GetFullyQualifiedName() => _querySyntaxHelper.EnsureFullyQualified(Table.Database.GetRuntimeName(),Table.Schema, Table.GetRuntimeName(), GetRuntimeName(), Table is DiscoveredTableValuedFunction);
+ public string GetFullyQualifiedName() => _querySyntaxHelper.EnsureFullyQualified(Table.Database.GetRuntimeName(),
+ Table.Schema, Table.GetRuntimeName(), GetRuntimeName(), Table is DiscoveredTableValuedFunction);
///
@@ -131,5 +133,5 @@ public override int GetHashCode()
/// 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/DiscoveredDatabase.cs b/FAnsiSql/Discovery/DiscoveredDatabase.cs
index eb0601f..8123ec2 100644
--- a/FAnsiSql/Discovery/DiscoveredDatabase.cs
+++ b/FAnsiSql/Discovery/DiscoveredDatabase.cs
@@ -78,7 +78,7 @@ public IEnumerable DiscoverTableValuedFunctions(I
/// Returns the name of the database without any qualifiers
///
///
- public string? GetRuntimeName() => _querySyntaxHelper.GetRuntimeName(_database);
+ public string GetRuntimeName() => _querySyntaxHelper.GetRuntimeName(_database);
///
/// 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]").
diff --git a/FAnsiSql/Discovery/DiscoveredDatabaseHelper.cs b/FAnsiSql/Discovery/DiscoveredDatabaseHelper.cs
index 143b9f9..a776726 100644
--- a/FAnsiSql/Discovery/DiscoveredDatabaseHelper.cs
+++ b/FAnsiSql/Discovery/DiscoveredDatabaseHelper.cs
@@ -283,7 +283,7 @@ public void ExecuteBatchNonQuery(string sql, DbConnection conn, DbTransaction? t
///
/// Line number the batch started at and the time it took to complete it
/// Timeout in seconds to run each batch in the
- public void ExecuteBatchNonQuery(string sql, DbConnection conn, DbTransaction transaction, out Dictionary performanceFigures, int timeout = 30)
+ public void ExecuteBatchNonQuery(string sql, DbConnection conn, DbTransaction? transaction, out Dictionary performanceFigures, int timeout = 30)
{
performanceFigures = [];
diff --git a/FAnsiSql/Discovery/DiscoveredServer.cs b/FAnsiSql/Discovery/DiscoveredServer.cs
index 3f4d37f..7334782 100644
--- a/FAnsiSql/Discovery/DiscoveredServer.cs
+++ b/FAnsiSql/Discovery/DiscoveredServer.cs
@@ -21,9 +21,9 @@ public sealed class DiscoveredServer : IMightNotExist
public DbConnectionStringBuilder Builder { get; set; }
///
- /// The currently used database
+ /// The currently used database, if any
///
- private DiscoveredDatabase _currentDatabase;
+ private DiscoveredDatabase? _currentDatabase;
///
/// Stateless helper class with DBMS specific implementation of the logic required by .
@@ -38,7 +38,7 @@ public sealed class DiscoveredServer : IMightNotExist
///
/// The server's name as specified in e.g. localhost\sqlexpress
///
- public string Name => Helper.GetServerName(Builder);
+ public string? Name => Helper.GetServerName(Builder);
///
/// Returns the username portion of if specified
@@ -90,13 +90,13 @@ public DiscoveredServer(string? connectionString, DatabaseType databaseType)
/// Optional username to set in the connection string
/// Optional password to set in the connection string
///
- public DiscoveredServer(string server,string database, DatabaseType databaseType,string usernameIfAny,string passwordIfAny)
+ public DiscoveredServer(string server, string database, DatabaseType databaseType, string usernameIfAny, string passwordIfAny)
{
Helper = ImplementationManager.GetImplementation(databaseType).GetServerHelper();
- Builder = Helper.GetConnectionStringBuilder(server,database,usernameIfAny,passwordIfAny);
+ Builder = Helper.GetConnectionStringBuilder(server, database, usernameIfAny, passwordIfAny);
- if(!string.IsNullOrWhiteSpace(database))
+ if (!string.IsNullOrWhiteSpace(database))
_currentDatabase = ExpectDatabase(database);
}
@@ -185,7 +185,7 @@ public DiscoveredDatabase ExpectDatabase(string database)
public void TestConnection(int timeoutInMillis = 10000)
{
using var con = Helper.GetConnection(Builder);
- using(var tokenSource = new CancellationTokenSource(timeoutInMillis))
+ using (var tokenSource = new CancellationTokenSource(timeoutInMillis))
using (var openTask = con.OpenAsync(tokenSource.Token))
{
try
@@ -226,7 +226,7 @@ public void TestConnection(int timeoutInMillis = 10000)
///
///
///
- public bool RespondsWithinTime(int timeoutInSeconds, out Exception exception) => Helper.RespondsWithinTime(Builder, timeoutInSeconds, out exception);
+ public bool RespondsWithinTime(int timeoutInSeconds, out Exception? exception) => Helper.RespondsWithinTime(Builder, timeoutInSeconds, out exception);
///
/// Connects to the server and returns a list of databases found as objects
@@ -279,13 +279,13 @@ public bool Exists(IManagedTransaction? transaction = null)
/// Returns the database that is currently pointed at.
///
///
- public DiscoveredDatabase GetCurrentDatabase()
+ public DiscoveredDatabase? GetCurrentDatabase()
{
//Is the database name persisted in the connection string?
var dbName = Helper.GetCurrentDatabase(Builder);
//yes
- if(!string.IsNullOrWhiteSpace(dbName))
+ if (!string.IsNullOrWhiteSpace(dbName))
return ExpectDatabase(dbName);
//no (e.g. Oracle or no default database specified in connection string)
@@ -311,7 +311,7 @@ public void EnableAsync()
public void ChangeDatabase(string newDatabase)
{
//change the connection string to point to the newDatabase
- Builder = Helper.ChangeDatabase(Builder,newDatabase);
+ Builder = Helper.ChangeDatabase(Builder, newDatabase);
//for DBMS that do not persist database in connection string (Oracle), we must persist this change
_currentDatabase = ExpectDatabase(newDatabase);
@@ -321,7 +321,7 @@ public void ChangeDatabase(string newDatabase)
/// Returns the server
///
///
- public override string ToString() => Name;
+ public override string? ToString() => Name;
///
/// Creates a new database with the given .
@@ -337,8 +337,8 @@ public DiscoveredDatabase CreateDatabase(string newDatabaseName)
Helper.CreateDatabase(Builder, db);
- if(!db.Exists())
- throw new Exception(string.Format(FAnsiStrings.DiscoveredServer_CreateDatabase_Helper___0___tried_to_create_database___1___but_the_database_didn_t_exist_after_the_creation_attempt, Helper.GetType().Name,newDatabaseName));
+ if (!db.Exists())
+ throw new Exception(string.Format(FAnsiStrings.DiscoveredServer_CreateDatabase_Helper___0___tried_to_create_database___1___but_the_database_didn_t_exist_after_the_creation_attempt, Helper.GetType().Name, newDatabaseName));
return db;
}
@@ -348,7 +348,7 @@ public DiscoveredDatabase CreateDatabase(string newDatabaseName)
/// should be wrapped with a using statement since it is .
///
///
- public IManagedConnection BeginNewTransactedConnection() => new ManagedConnection(this, Helper.BeginTransaction(Builder)){CloseOnDispose = true};
+ public IManagedConnection BeginNewTransactedConnection() => new ManagedConnection(this, Helper.BeginTransaction(Builder)) { CloseOnDispose = true };
///
/// Opens a new or reuses an existing one (if is provided).
@@ -410,5 +410,5 @@ public override bool Equals(object? obj)
/// Returns the version number of the DBMS e.g. MySql 5.7
///
///
- public Version GetVersion() => Helper.GetVersion(this);
+ public Version? GetVersion() => Helper.GetVersion(this);
}
\ No newline at end of file
diff --git a/FAnsiSql/Discovery/DiscoveredTable.cs b/FAnsiSql/Discovery/DiscoveredTable.cs
index d1e1e8e..cc24e1e 100644
--- a/FAnsiSql/Discovery/DiscoveredTable.cs
+++ b/FAnsiSql/Discovery/DiscoveredTable.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Threading;
@@ -88,7 +89,7 @@ public virtual bool Exists(IManagedTransaction? transaction = null)
/// Returns the unqualified name of the table e.g. "MyTable"
///
///
- public virtual string? GetRuntimeName() => QuerySyntaxHelper.GetRuntimeName(TableName);
+ public virtual string GetRuntimeName() => QuerySyntaxHelper.GetRuntimeName(TableName);
///
/// Returns the fully qualified (including schema if appropriate) name of the table e.g. [MyDb].dbo.[MyTable] or `MyDb`.`MyTable`
@@ -100,7 +101,7 @@ public virtual bool Exists(IManagedTransaction? transaction = null)
/// Returns the wrapped e.g. "[MyTbl]" name of the table including escaping e.g. if you wanted to name a table "][nquisitor" (which would return "[]][nquisitor]"). Use to return the full name including table/database/schema.
///
///
- public string? GetWrappedName() => QuerySyntaxHelper.EnsureWrapped(GetRuntimeName());
+ public string GetWrappedName() => QuerySyntaxHelper.EnsureWrapped(GetRuntimeName());
///
/// Connects to the server and returns a list of columns found in the table as .
diff --git a/FAnsiSql/Discovery/QuerySyntax/IQuerySyntaxHelper.cs b/FAnsiSql/Discovery/QuerySyntax/IQuerySyntaxHelper.cs
index 8330447..55b04c4 100644
--- a/FAnsiSql/Discovery/QuerySyntax/IQuerySyntaxHelper.cs
+++ b/FAnsiSql/Discovery/QuerySyntax/IQuerySyntaxHelper.cs
@@ -41,16 +41,16 @@ public interface IQuerySyntaxHelper
///
/// The character that is used to qualify database entity names e.g. "[" for "[My Table]"
///
- string OpenQualifier {get;}
+ string OpenQualifier { get; }
///
/// The character that is used to end qualifying database entity names e.g. "]" for "[My Table]". For some DBMS this is the same as
///
- string CloseQualifier {get;}
+ string CloseQualifier { get; }
///
/// Separator between table and column names (and database, schema etc). Usually "."
///
- string DatabaseTableSeparator {get; }
+ string DatabaseTableSeparator { get; }
///
/// Characters which are not permitted in column names by FAnsi
@@ -59,6 +59,7 @@ public interface IQuerySyntaxHelper
char ParameterSymbol { get; }
+ [return: NotNullIfNotNull(nameof(s))]
string? GetRuntimeName(string? s);
bool TryGetRuntimeName(string s, out string? name);
@@ -78,6 +79,7 @@ public interface IQuerySyntaxHelper
///
///
///
+ [return: NotNullIfNotNull(nameof(databaseOrTableName))]
string? EnsureWrapped(string? databaseOrTableName);
string EnsureFullyQualified(string? databaseName, string? schemaName, string tableName);
@@ -163,7 +165,7 @@ public interface IQuerySyntaxHelper
/// The column the parameter is for loading - this is used to determine the DbType for the paramter
/// The value to populate into the command, this will be converted to DBNull.Value if the value is nullish
///
- DbParameter GetParameter(DbParameter p, DiscoveredColumn discoveredColumn,object value);
+ DbParameter GetParameter(DbParameter p, DiscoveredColumn discoveredColumn, object value);
///
/// Gets a DbParameter hard typed with the correct DbType for the discoveredColumn and the Value set to the correct Value representation (e.g. DBNull for nulls or whitespace).
@@ -174,7 +176,7 @@ public interface IQuerySyntaxHelper
/// The value to populate into the command, this will be converted to DBNull.Value if the value is nullish
///
///
- DbParameter GetParameter(DbParameter p, DiscoveredColumn discoveredColumn,object value,CultureInfo culture);
+ DbParameter GetParameter(DbParameter p, DiscoveredColumn discoveredColumn, object value, CultureInfo? culture);
///
/// Throws if the supplied name is invalid (because it is too long or contains unsupported characters)
@@ -200,7 +202,7 @@ public interface IQuerySyntaxHelper
///
/// Returns false if the supplied name is invalid (because it is too long or contains unsupported characters)
///
- bool IsValidTableName(string tableName, [NotNullWhen(false)] out string reason);
+ bool IsValidTableName(string tableName, [NotNullWhen(false)] out string? reason);
///
/// Returns false if the supplied name is invalid (because it is too long or contains unsupported characters)
diff --git a/FAnsiSql/Discovery/QuerySyntaxHelper.cs b/FAnsiSql/Discovery/QuerySyntaxHelper.cs
index db83112..5816f3c 100644
--- a/FAnsiSql/Discovery/QuerySyntaxHelper.cs
+++ b/FAnsiSql/Discovery/QuerySyntaxHelper.cs
@@ -37,7 +37,7 @@ public abstract partial class QuerySyntaxHelper(
public abstract int MaximumColumnLength { get; }
///
- public virtual char[] IllegalNameChars { get; } = ['.','(',')'];
+ public virtual char[] IllegalNameChars { get; } = ['.', '(', ')'];
///
/// Regex for identifying parameters in blocks of SQL (starts with @ or : (Oracle)
@@ -48,17 +48,17 @@ public abstract partial class QuerySyntaxHelper(
///
/// Symbols (for all database types) which denote wrapped entity names e.g. [dbo].[mytable] contains qualifiers '[' and ']'
///
- public static readonly char[] TableNameQualifiers = ['[', ']', '`' ,'"'];
+ public static readonly char[] TableNameQualifiers = ['[', ']', '`', '"'];
///
- public abstract string OpenQualifier {get;}
+ public abstract string OpenQualifier { get; }
///
- public abstract string CloseQualifier {get;}
+ public abstract string CloseQualifier { get; }
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;
@@ -85,7 +85,7 @@ private Regex GetAliasRegex() =>
public string AliasPrefix => GetAliasConst();
//Only look at the start of the string or following an equals or white space and stop at word boundaries
- private static readonly Regex ParameterNameRegex = new ($@"(?:^|[\s+\-*/\\=(,])+{ParameterNamesRegex}\b");
+ private static readonly Regex ParameterNameRegex = new($@"(?:^|[\s+\-*/\\=(,])+{ParameterNamesRegex}\b");
///
/// Lists the names of all parameters required by the supplied whereSql e.g. @bob = 'bob' would return "@bob"
@@ -111,7 +111,7 @@ public static string GetParameterNameFromDeclarationSQL(string parameterSQL)
public bool IsValidParameterName(string parameterSQL) => ParameterNamesRegex.IsMatch(parameterSQL);
-
+ [return: NotNullIfNotNull(nameof(s))]
public virtual string? GetRuntimeName(string? s)
{
if (string.IsNullOrWhiteSpace(s))
@@ -131,11 +131,11 @@ public static string GetParameterNameFromDeclarationSQL(string parameterSQL)
//Last symbol with no whitespace
var lastWord = s[(s.LastIndexOf('.') + 1)..].Trim();
- if(string.IsNullOrWhiteSpace(lastWord) || lastWord.Length<2)
+ if (string.IsNullOrWhiteSpace(lastWord) || lastWord.Length < 2)
return lastWord;
//trim off any brackets e.g. return "My Table" for "[My Table]"
- if(lastWord.StartsWith(OpenQualifier, StringComparison.Ordinal) && lastWord.EndsWith(CloseQualifier, StringComparison.Ordinal))
+ if (lastWord.StartsWith(OpenQualifier, StringComparison.Ordinal) && lastWord.EndsWith(CloseQualifier, StringComparison.Ordinal))
return UnescapeWrappedNameBody(lastWord[1..^1]);
return lastWord;
@@ -172,16 +172,16 @@ public virtual bool TryGetRuntimeName(string s, out string? name)
return databaseOrTableName;
if (databaseOrTableName.Contains(DatabaseTableSeparator))
- throw new Exception(string.Format(FAnsiStrings.QuerySyntaxHelper_EnsureWrapped_String_passed_to_EnsureWrapped___0___contained_separators__not_allowed____Prohibited_Separator_is___1__,databaseOrTableName, DatabaseTableSeparator));
+ throw new Exception(string.Format(FAnsiStrings.QuerySyntaxHelper_EnsureWrapped_String_passed_to_EnsureWrapped___0___contained_separators__not_allowed____Prohibited_Separator_is___1__, databaseOrTableName, DatabaseTableSeparator));
return EnsureWrappedImpl(databaseOrTableName);
}
public abstract string EnsureWrappedImpl(string databaseOrTableName);
- public abstract string EnsureFullyQualified(string databaseName, string? schema, string tableName);
+ public abstract string EnsureFullyQualified(string? databaseName, string? schema, string tableName);
- public virtual string EnsureFullyQualified(string databaseName, string? schema, string tableName, string columnName, bool isTableValuedFunction = false) =>
+ public virtual string EnsureFullyQualified(string? databaseName, string? schema, string tableName, string columnName, bool isTableValuedFunction = false) =>
isTableValuedFunction ? $"{GetRuntimeName(tableName)}.{GetRuntimeName(columnName)}"
: //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))}";
@@ -202,7 +202,7 @@ public virtual string EnsureFullyQualified(string databaseName, string? schema,
///
///
///
- public virtual bool SplitLineIntoSelectSQLAndAlias(string lineToSplit, out string selectSQL, out string? alias)
+ public virtual bool SplitLineIntoSelectSQLAndAlias(string lineToSplit, out string selectSQL, [NotNullWhen(true)] out string? alias)
{
//Ths line is expected to be some SELECT sql so remove trailing whitespace and commas etc
lineToSplit = lineToSplit.TrimEnd(',', ' ', '\n', '\r');
@@ -212,7 +212,7 @@ public virtual bool SplitLineIntoSelectSQLAndAlias(string lineToSplit, out strin
switch (matches.Count)
{
case > 1:
- throw new SyntaxErrorException(string.Format(FAnsiStrings.QuerySyntaxHelper_SplitLineIntoSelectSQLAndAlias_,matches.Count,lineToSplit));
+ throw new SyntaxErrorException(string.Format(FAnsiStrings.QuerySyntaxHelper_SplitLineIntoSelectSQLAndAlias_, matches.Count, lineToSplit));
case 0:
selectSQL = lineToSplit;
alias = null;
@@ -361,9 +361,9 @@ public DbParameter GetParameter(DbParameter p, DiscoveredColumn discoveredColumn
else
p.Value = value;
}
- catch(Exception ex)
+ catch (Exception ex)
{
- throw new Exception(string.Format(FAnsiStrings.QuerySyntaxHelper_GetParameter_Could_not_GetParameter_for_column___0__, discoveredColumn.GetFullyQualifiedName()),ex);
+ throw new Exception(string.Format(FAnsiStrings.QuerySyntaxHelper_GetParameter_Could_not_GetParameter_for_column___0__, discoveredColumn.GetFullyQualifiedName()), ex);
}
return p;
@@ -376,12 +376,12 @@ public void ValidateDatabaseName(string? databaseName)
}
public void ValidateTableName(string tableName)
{
- if(!IsValidTableName(tableName,out var reason))
+ if (!IsValidTableName(tableName, out var reason))
throw new RuntimeNameException(reason);
}
public void ValidateColumnName(string columnName)
{
- if(!IsValidColumnName(columnName,out var reason))
+ if (!IsValidColumnName(columnName, out var reason))
throw new RuntimeNameException(reason);
}
@@ -473,7 +473,7 @@ public override bool Equals(object? obj)
#endregion
- public Dictionary GetParameterNamesFor(T[] columns, Func toStringFunc) where T : notnull
+ public Dictionary GetParameterNamesFor(T[] columns, Func toStringFunc) where T : notnull
{
var toReturn = new Dictionary();
@@ -488,10 +488,10 @@ public Dictionary GetParameterNamesFor(T[] columns, Func
/// Translates a database proprietary type e.g. 'decimal(10,2)' into a C# type e.g. 'typeof(decimal)'
diff --git a/FAnsiSql/Discovery/TypeTranslation/TypeTranslater.cs b/FAnsiSql/Discovery/TypeTranslation/TypeTranslater.cs
index e34025f..92231c9 100644
--- a/FAnsiSql/Discovery/TypeTranslation/TypeTranslater.cs
+++ b/FAnsiSql/Discovery/TypeTranslation/TypeTranslater.cs
@@ -7,7 +7,7 @@
namespace FAnsi.Discovery.TypeTranslation;
///
-public abstract partial class TypeTranslater:ITypeTranslater
+public abstract partial class TypeTranslater : ITypeTranslater
{
private const string StringSizeRegexPattern = @"\(([0-9]+)\)";
private const string DecimalsBeforeAndAfterPattern = @"\(([0-9]+),([0-9]+)\)";
@@ -20,7 +20,7 @@ public abstract partial class TypeTranslater:ITypeTranslater
protected Regex SmallIntRegex = SmallIntRe();
protected Regex IntRegex = IntRe();
protected Regex LongRegex = LongRe();
- protected Regex DateRegex;
+ protected readonly Regex DateRegex;
protected Regex TimeRegex = TimeRe();
private static readonly Regex StringRegex = StringRe();
private static readonly Regex ByteArrayRegex = ByteArrayRe();
@@ -44,8 +44,9 @@ public abstract partial class TypeTranslater:ITypeTranslater
///
///
///
- protected TypeTranslater(int maxStringWidthBeforeMax, int stringWidthWhenNotSupplied)
+ protected TypeTranslater(Regex dateRegex, int maxStringWidthBeforeMax, int stringWidthWhenNotSupplied)
{
+ DateRegex = dateRegex;
MaxStringWidthBeforeMax = maxStringWidthBeforeMax;
StringWidthWhenNotSupplied = stringWidthWhenNotSupplied;
}
@@ -63,10 +64,10 @@ public string GetSQLDBTypeForCSharpType(DatabaseTypeRequest request)
if (t == typeof(short) || t == typeof(short) || t == typeof(ushort) || t == typeof(short?) || t == typeof(ushort?))
return GetSmallIntDataType();
- if (t == typeof(int) || t == typeof(int) || t == typeof(uint) || t == typeof(int?) || t == typeof(uint?))
+ if (t == typeof(int) || t == typeof(int) || t == typeof(uint) || t == typeof(int?) || t == typeof(uint?))
return GetIntDataType();
-
- if (t == typeof (long) || t == typeof(ulong) || t == typeof(long?) || t == typeof(ulong?))
+
+ if (t == typeof(long) || t == typeof(ulong) || t == typeof(long?) || t == typeof(ulong?))
return GetBigIntDataType();
if (t == typeof(float) || t == typeof(float?) || t == typeof(double) ||
@@ -81,11 +82,11 @@ public string GetSQLDBTypeForCSharpType(DatabaseTypeRequest request)
if (t == typeof(TimeSpan) || t == typeof(TimeSpan?))
return GetTimeDataType();
-
- if (t == typeof (byte[]))
+
+ if (t == typeof(byte[]))
return GetByteArrayDataType();
- if (t == typeof (Guid))
+ if (t == typeof(Guid))
return GetGuidDataType();
throw new TypeNotMappedException(string.Format(FAnsiStrings.TypeTranslater_GetSQLDBTypeForCSharpType_Unsure_what_SQL_type_to_use_for_CSharp_Type___0_____TypeTranslater_was___1__, t.Name, GetType().Name));
@@ -152,7 +153,7 @@ private string GetUnicodeStringDataType(int? maxExpectedStringWidth)
[return:
DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties |
DynamicallyAccessedMemberTypes.PublicFields)]
- public Type GetCSharpTypeForSQLDBType(string sqlType) =>
+ public Type GetCSharpTypeForSQLDBType(string? sqlType) =>
TryGetCSharpTypeForSQLDBType(sqlType) ??
throw new TypeNotMappedException(string.Format(
FAnsiStrings
@@ -163,8 +164,10 @@ public Type GetCSharpTypeForSQLDBType(string sqlType) =>
[return:
DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties |
DynamicallyAccessedMemberTypes.PublicFields)]
- public Type? TryGetCSharpTypeForSQLDBType(string sqlType)
+ public Type? TryGetCSharpTypeForSQLDBType(string? sqlType)
{
+ if (string.IsNullOrWhiteSpace(sqlType)) return null;
+
if (IsBit(sqlType))
return typeof(bool);
@@ -279,7 +282,7 @@ 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 != null && sqlType.StartsWith("n", StringComparison.CurrentCultureIgnoreCase);
public virtual Guesser GetGuesserFor(DiscoveredColumn discoveredColumn) => GetGuesserFor(discoveredColumn, 0);
diff --git a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftQuerySyntaxHelper.cs b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftQuerySyntaxHelper.cs
index 6a7f109..612a6fd 100644
--- a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftQuerySyntaxHelper.cs
+++ b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftQuerySyntaxHelper.cs
@@ -11,8 +11,8 @@ namespace FAnsi.Implementations.MicrosoftSQL;
///
public sealed class MicrosoftQuerySyntaxHelper : QuerySyntaxHelper
{
- public static readonly MicrosoftQuerySyntaxHelper Instance=new();
- private MicrosoftQuerySyntaxHelper() : base(MicrosoftSQLTypeTranslater.Instance,new MicrosoftSQLAggregateHelper(),new MicrosoftSQLUpdateHelper(),DatabaseType.MicrosoftSQLServer)
+ public static readonly MicrosoftQuerySyntaxHelper Instance = new();
+ private MicrosoftQuerySyntaxHelper() : base(MicrosoftSQLTypeTranslater.Instance, new MicrosoftSQLAggregateHelper(), new MicrosoftSQLUpdateHelper(), DatabaseType.MicrosoftSQLServer)
{
}
@@ -80,30 +80,32 @@ 3617 when string.IsNullOrWhiteSpace(sqlE.Message) => true,
public override string EnsureWrappedImpl(string databaseOrTableName) => $"[{GetRuntimeNameWithDoubledClosingSquareBrackets(databaseOrTableName)}]";
- protected override string UnescapeWrappedNameBody(string name) => name.Replace("]]","]");
+ protected override string UnescapeWrappedNameBody(string name) => name.Replace("]]", "]");
///
/// Returns the runtime name of the string with all ending square brackets escaped by doubling up (but resulting string is not wrapped itself)
///
///
///
- 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)
+ public override string EnsureFullyQualified(string? databaseName, string? schema, string tableName)
{
//if there is no schema address it as db..table (which is the same as db.dbo.table in Microsoft SQL Server)
if (string.IsNullOrWhiteSpace(schema))
- return EnsureWrapped( GetRuntimeName(databaseName)) + DatabaseTableSeparator + DatabaseTableSeparator + EnsureWrapped(GetRuntimeName(tableName));
+ return
+ $"{EnsureWrapped(GetRuntimeName(databaseName))}{DatabaseTableSeparator}{DatabaseTableSeparator}{EnsureWrapped(GetRuntimeName(tableName))}";
//there is a schema so add it in
- return EnsureWrapped(GetRuntimeName(databaseName)) + DatabaseTableSeparator + EnsureWrapped(GetRuntimeName(schema)) + DatabaseTableSeparator + EnsureWrapped( GetRuntimeName(tableName));
+ return
+ $"{EnsureWrapped(GetRuntimeName(databaseName))}{DatabaseTableSeparator}{EnsureWrapped(GetRuntimeName(schema))}{DatabaseTableSeparator}{EnsureWrapped(GetRuntimeName(tableName))}";
}
- public override string EnsureFullyQualified(string databaseName, string schema, string tableName, string columnName, bool isTableValuedFunction = false)
+ public override string EnsureFullyQualified(string? databaseName, string? schema, string tableName, string columnName, bool isTableValuedFunction = false)
{
if (isTableValuedFunction)
return GetRuntimeName(tableName) + DatabaseTableSeparator + EnsureWrapped(GetRuntimeName(columnName));//table valued functions do not support database name being in the column level selection list area of sql queries
- return EnsureFullyQualified(databaseName,schema,tableName) + DatabaseTableSeparator + EnsureWrapped(GetRuntimeName(columnName));
+ return EnsureFullyQualified(databaseName, schema, tableName) + DatabaseTableSeparator + EnsureWrapped(GetRuntimeName(columnName));
}
}
\ No newline at end of file
diff --git a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLServerHelper.cs b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLServerHelper.cs
index d86b334..85f75b0 100644
--- a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLServerHelper.cs
+++ b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLServerHelper.cs
@@ -25,19 +25,19 @@ private MicrosoftSQLServerHelper() : base(DatabaseType.MicrosoftSQLServer)
#region Up Typing
public override DbCommand GetCommand(string s, DbConnection con, DbTransaction? transaction = null) => new SqlCommand(s, (SqlConnection)con, transaction as SqlTransaction);
- public override DbDataAdapter GetDataAdapter(DbCommand cmd) => new SqlDataAdapter((SqlCommand) cmd);
+ public override DbDataAdapter GetDataAdapter(DbCommand cmd) => new SqlDataAdapter((SqlCommand)cmd);
- public override DbCommandBuilder GetCommandBuilder(DbCommand cmd) => new SqlCommandBuilder((SqlDataAdapter) GetDataAdapter(cmd));
+ public override DbCommandBuilder GetCommandBuilder(DbCommand cmd) => new SqlCommandBuilder((SqlDataAdapter)GetDataAdapter(cmd));
- public override DbParameter GetParameter(string parameterName) => new SqlParameter(parameterName,null);
+ public override DbParameter GetParameter(string parameterName) => new SqlParameter(parameterName, null);
public override DbConnection GetConnection(DbConnectionStringBuilder builder) => new SqlConnection(builder.ConnectionString);
protected override DbConnectionStringBuilder GetConnectionStringBuilderImpl(string connectionString) => new SqlConnectionStringBuilder(connectionString);
- protected override DbConnectionStringBuilder GetConnectionStringBuilderImpl(string server, string database, string username, string password)
+ protected override DbConnectionStringBuilder GetConnectionStringBuilderImpl(string server, string? database, string username, string password)
{
- var toReturn = new SqlConnectionStringBuilder { DataSource = server};
+ var toReturn = new SqlConnectionStringBuilder { DataSource = server };
if (!string.IsNullOrWhiteSpace(username))
{
toReturn.UserID = username;
@@ -46,12 +46,12 @@ protected override DbConnectionStringBuilder GetConnectionStringBuilderImpl(stri
else
toReturn.IntegratedSecurity = true;
- if(!string.IsNullOrWhiteSpace(database))
+ if (!string.IsNullOrWhiteSpace(database))
toReturn.InitialCatalog = database;
return toReturn;
}
- public static string GetDatabaseNameFrom(DbConnectionStringBuilder builder) => ((SqlConnectionStringBuilder) builder).InitialCatalog;
+ public static string GetDatabaseNameFrom(DbConnectionStringBuilder builder) => ((SqlConnectionStringBuilder)builder).InitialCatalog;
#endregion
@@ -74,10 +74,10 @@ public override string[] ListDatabases(DbConnection con)
{
var databases = new List();
- using(var cmd = GetCommand("select name [Database] from master..sysdatabases", con))
+ using (var cmd = GetCommand("select name [Database] from master..sysdatabases", con))
using (var r = cmd.ExecuteReader())
while (r.Read())
- databases.Add((string) r["Database"]);
+ databases.Add((string)r["Database"]);
con.Close();
return [.. databases];
@@ -85,7 +85,7 @@ public override string[] ListDatabases(DbConnection con)
public override DbConnectionStringBuilder EnableAsync(DbConnectionStringBuilder builder)
{
- var b = (SqlConnectionStringBuilder) builder;
+ var b = (SqlConnectionStringBuilder)builder;
b.MultipleActiveResultSets = true;
@@ -113,7 +113,7 @@ public override void CreateDatabase(DbConnectionStringBuilder builder, IHasRunti
cmd.ExecuteNonQuery();
}
- public override Dictionary DescribeServer(DbConnectionStringBuilder builder)
+ public override Dictionary DescribeServer(DbConnectionStringBuilder builder)
{
var toReturn = new Dictionary();
@@ -126,8 +126,8 @@ public override Dictionary DescribeServer(DbConnectionStringBuild
try
{
using var dt = new DataTable();
- using(var cmd = new SqlCommand("EXEC master..xp_fixeddrives",con))
- using(var da = new SqlDataAdapter(cmd))
+ using (var cmd = new SqlCommand("EXEC master..xp_fixeddrives", con))
+ using (var da = new SqlDataAdapter(cmd))
da.Fill(dt);
foreach (DataRow row in dt.Rows)
@@ -142,15 +142,15 @@ public override Dictionary DescribeServer(DbConnectionStringBuild
return toReturn;
}
- public override string GetExplicitUsernameIfAny(DbConnectionStringBuilder builder)
+ public override string? GetExplicitUsernameIfAny(DbConnectionStringBuilder builder)
{
- var u = ((SqlConnectionStringBuilder) builder).UserID;
- return string.IsNullOrWhiteSpace(u) ? null: u;
+ var u = ((SqlConnectionStringBuilder)builder).UserID;
+ return string.IsNullOrWhiteSpace(u) ? null : u;
}
- public override string GetExplicitPasswordIfAny(DbConnectionStringBuilder builder)
+ public override string? GetExplicitPasswordIfAny(DbConnectionStringBuilder builder)
{
- var pwd = ((SqlConnectionStringBuilder) builder).Password;
+ var pwd = ((SqlConnectionStringBuilder)builder).Password;
return string.IsNullOrWhiteSpace(pwd) ? null : pwd;
}
@@ -165,14 +165,14 @@ protected override void EnforceKeywords(DbConnectionStringBuilder builder)
if (msb.Authentication != SqlAuthenticationMethod.NotSpecified) msb.IntegratedSecurity = false;
}
- public override Version GetVersion(DiscoveredServer server)
+ public override Version? GetVersion(DiscoveredServer server)
{
using var con = server.GetConnection();
con.Open();
- using var cmd = server.GetCommand("SELECT @@VERSION",con);
+ using var cmd = server.GetCommand("SELECT @@VERSION", con);
using var r = cmd.ExecuteReader();
- if(r.Read())
- return r[0] == DBNull.Value ? null: CreateVersionFromString((string)r[0]);
+ if (r.Read())
+ return r[0] == DBNull.Value ? null : CreateVersionFromString((string)r[0]);
return null;
}
diff --git a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLTableHelper.cs b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLTableHelper.cs
index 353ee7a..2a0ed6b 100644
--- a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLTableHelper.cs
+++ b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLTableHelper.cs
@@ -47,7 +47,7 @@ public override DiscoveredColumn[] DiscoverColumns(DiscoveredTable discoveredTab
}
}
- if(toReturn.Count == 0)
+ if (toReturn.Count == 0)
throw new Exception($"Could not find any columns in table {discoveredTable}");
//don't bother looking for pks if it is a table valued function
@@ -56,7 +56,7 @@ public override DiscoveredColumn[] DiscoverColumns(DiscoveredTable discoveredTab
var pks = ListPrimaryKeys(connection, discoveredTable);
- foreach (var c in toReturn.Where(c => pks.Any(pk=>pk.Equals(c.GetRuntimeName()))))
+ foreach (var c in toReturn.Where(c => pks.Any(pk => pk.Equals(c.GetRuntimeName()))))
c.IsPrimaryKey = true;
return [.. toReturn];
@@ -97,11 +97,11 @@ public override void DropTable(DbConnection connection, DiscoveredTable tableToD
case TableType.Table:
cmd = new SqlCommand($"DROP TABLE {tableToDrop.GetFullyQualifiedName()}", (SqlConnection)connection);
break;
- case TableType.TableValuedFunction :
- DropFunction(connection,(DiscoveredTableValuedFunction) tableToDrop);
+ case TableType.TableValuedFunction:
+ DropFunction(connection, (DiscoveredTableValuedFunction)tableToDrop);
return;
default:
- throw new ArgumentOutOfRangeException(nameof(tableToDrop),$"Unknown table type {tableToDrop.TableType}");
+ throw new ArgumentOutOfRangeException(nameof(tableToDrop), $"Unknown table type {tableToDrop.TableType}");
}
using (cmd)
@@ -110,7 +110,7 @@ public override void DropTable(DbConnection connection, DiscoveredTable tableToD
public override void DropFunction(DbConnection connection, DiscoveredTableValuedFunction functionToDrop)
{
- using var cmd = new SqlCommand($"DROP FUNCTION {functionToDrop.Schema??"dbo"}.{functionToDrop.GetRuntimeName()}", (SqlConnection)connection);
+ using var cmd = new SqlCommand($"DROP FUNCTION {functionToDrop.Schema ?? "dbo"}.{functionToDrop.GetRuntimeName()}", (SqlConnection)connection);
cmd.ExecuteNonQuery();
}
@@ -122,7 +122,7 @@ public override void DropColumn(DbConnection connection, DiscoveredColumn column
}
- public override IEnumerable DiscoverTableValuedFunctionParameters(DbConnection connection,DiscoveredTableValuedFunction discoveredTableValuedFunction, DbTransaction transaction)
+ public override IEnumerable DiscoverTableValuedFunctionParameters(DbConnection connection, DiscoveredTableValuedFunction discoveredTableValuedFunction, DbTransaction transaction)
{
var toReturn = new List();
@@ -161,7 +161,7 @@ sys.parameters.precision AS PRECISION
return toReturn.ToArray();
}
- public override IBulkCopy BeginBulkInsert(DiscoveredTable discoveredTable,IManagedConnection connection,CultureInfo culture) => new MicrosoftSQLBulkCopy(discoveredTable,connection,culture);
+ public override IBulkCopy BeginBulkInsert(DiscoveredTable discoveredTable, IManagedConnection connection, CultureInfo culture) => new MicrosoftSQLBulkCopy(discoveredTable, connection, culture);
public override void CreatePrimaryKey(DatabaseOperationArgs args, DiscoveredTable table, DiscoveredColumn[] discoverColumns)
{
@@ -180,18 +180,18 @@ public override void CreatePrimaryKey(DatabaseOperationArgs args, DiscoveredTabl
throw new AlterFailedException(string.Format(FAnsiStrings.DiscoveredTableHelper_CreatePrimaryKey_Failed_to_create_primary_key_on_table__0__using_columns___1__, table, string.Join(",", discoverColumns.Select(static c => c.GetRuntimeName()))), e);
}
- base.CreatePrimaryKey(args,table, discoverColumns);
+ base.CreatePrimaryKey(args, table, discoverColumns);
}
- public override DiscoveredRelationship[] DiscoverRelationships(DiscoveredTable table,DbConnection connection, IManagedTransaction? transaction = null)
+ public override DiscoveredRelationship[] DiscoverRelationships(DiscoveredTable table, DbConnection connection, IManagedTransaction? transaction = null)
{
- var toReturn = new Dictionary();
+ var toReturn = new Dictionary();
const string sql = "exec sp_fkeys @pktable_name = @table, @pktable_qualifier=@database, @pktable_owner=@schema";
using (var cmd = table.GetCommand(sql, connection))
{
- if(transaction != null)
+ if (transaction != null)
cmd.Transaction = transaction.Transaction;
var p = cmd.CreateParameter();
@@ -216,22 +216,22 @@ public override DiscoveredRelationship[] DiscoverRelationships(DiscoveredTable t
var da = table.Database.Server.GetDataAdapter(cmd);
da.Fill(dt);
- foreach(DataRow r in dt.Rows)
+ foreach (DataRow r in dt.Rows)
{
- var fkName = r["FK_NAME"].ToString();
+ var fkName = r["FK_NAME"].ToString() ?? throw new InvalidOperationException("Null foreign key name returned");
//could be a 2+ columns foreign key?
if (!toReturn.TryGetValue(fkName, out var current))
{
- var pkdb = r["PKTABLE_QUALIFIER"].ToString();
+ var pkdb = r["PKTABLE_QUALIFIER"].ToString() ?? throw new InvalidOperationException("Null primary key database name returned");
var pkschema = r["PKTABLE_OWNER"].ToString();
- var pktableName = r["PKTABLE_NAME"].ToString();
+ var pktableName = r["PKTABLE_NAME"].ToString() ?? throw new InvalidOperationException("Null primary key table name returned");
var pktable = table.Database.Server.ExpectDatabase(pkdb).ExpectTable(pktableName, pkschema);
- var fkdb = r["FKTABLE_QUALIFIER"].ToString();
+ var fkdb = r["FKTABLE_QUALIFIER"].ToString() ?? throw new InvalidOperationException("Null foreign key database name returned");
var fkschema = r["FKTABLE_OWNER"].ToString();
- var fktableName = r["FKTABLE_NAME"].ToString();
+ var fktableName = r["FKTABLE_NAME"].ToString() ?? throw new InvalidOperationException("Null foreign key name returned");
var fktable = table.Database.Server.ExpectDatabase(fkdb).ExpectTable(fktableName, fkschema);
@@ -254,11 +254,11 @@ public override DiscoveredRelationship[] DiscoverRelationships(DiscoveredTable t
2 = set null
3 = set default*/
- current = new DiscoveredRelationship(fkName,pktable,fktable,deleteRule);
- toReturn.Add(current.Name,current);
+ current = new DiscoveredRelationship(fkName, pktable, fktable, deleteRule);
+ toReturn.Add(current.Name, current);
}
- current.AddKeys(r["PKCOLUMN_NAME"].ToString(), r["FKCOLUMN_NAME"].ToString(),transaction);
+ current.AddKeys(r["PKCOLUMN_NAME"].ToString(), r["FKCOLUMN_NAME"].ToString(), transaction);
}
}
@@ -278,7 +278,7 @@ protected override string GetRenameTableSql(DiscoveredTable discoveredTable, str
return $"exec sp_rename '{syntax.Escape(oldName)}', '{syntax.Escape(newName)}'";
}
- public override void MakeDistinct(DatabaseOperationArgs args,DiscoveredTable discoveredTable)
+ public override void MakeDistinct(DatabaseOperationArgs args, DiscoveredTable discoveredTable)
{
var syntax = discoveredTable.GetQuerySyntaxHelper();
@@ -293,9 +293,9 @@ where RowNum > 1
""";
var columnList = string.Join(",",
- discoveredTable.DiscoverColumns().Select(c=>syntax.EnsureWrapped(c.GetRuntimeName())));
+ discoveredTable.DiscoverColumns().Select(c => syntax.EnsureWrapped(c.GetRuntimeName())));
- var sqlToExecute = string.Format(sql,columnList,discoveredTable.GetFullyQualifiedName());
+ var sqlToExecute = string.Format(sql, columnList, discoveredTable.GetFullyQualifiedName());
var server = discoveredTable.Database.Server;
@@ -310,16 +310,16 @@ where RowNum > 1
private string GetSQLType_FromSpColumnsResult(DbDataReader r)
{
var columnType = r["TYPE_NAME"] as string;
- var lengthQualifier = "";
-
- if (HasPrecisionAndScale(columnType))
- lengthQualifier = $"({r["PRECISION"]},{r["SCALE"]})";
- else
- if (RequiresLength(columnType)) lengthQualifier = $"({AdjustForUnicodeAndNegativeOne(columnType, Convert.ToInt32(r["LENGTH"]))})";
if (columnType == "text")
return "varchar(max)";
+ var lengthQualifier = "";
+
+ if (HasPrecisionAndScale(columnType ?? throw new InvalidOperationException("Null type name returned")))
+ lengthQualifier = $"({r["PRECISION"]},{r["SCALE"]})";
+ else if (RequiresLength(columnType)) lengthQualifier = $"({AdjustForUnicodeAndNegativeOne(columnType, Convert.ToInt32(r["LENGTH"]))})";
+
return columnType + lengthQualifier;
}
@@ -329,7 +329,7 @@ private static object AdjustForUnicodeAndNegativeOne(string columnType, int leng
return "max";
if (columnType.Contains("nvarchar") || columnType.Contains("nchar") || columnType.Contains("ntext"))
- return length/2;
+ return length / 2;
return length;
}
@@ -363,7 +363,7 @@ ORDER BY OBJECT_NAME(ic.OBJECT_ID), ic.key_ordinal
cmd.Transaction = con.Transaction;
using var r = cmd.ExecuteReader();
while (r.Read())
- toReturn.Add((string) r["ColumnName"]);
+ toReturn.Add((string)r["ColumnName"]);
r.Close();
}
diff --git a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLTypeTranslater.cs b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLTypeTranslater.cs
index c4917b7..6321589 100644
--- a/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLTypeTranslater.cs
+++ b/FAnsiSql/Implementations/MicrosoftSQL/MicrosoftSQLTypeTranslater.cs
@@ -9,9 +9,8 @@ public sealed partial class MicrosoftSQLTypeTranslater : TypeTranslater
private static readonly Regex AlsoBinaryRegex = AlsoBinaryRe();
- private MicrosoftSQLTypeTranslater() : base(8000, 4000)
+ private MicrosoftSQLTypeTranslater() : base(DateRe(), 8000, 4000)
{
- DateRegex = DateRe();
}
protected override string GetDateDateTimeDataType() => "datetime2";
diff --git a/FAnsiSql/Implementations/MySql/MySqlServerHelper.cs b/FAnsiSql/Implementations/MySql/MySqlServerHelper.cs
index 7becab9..cd9f2c0 100644
--- a/FAnsiSql/Implementations/MySql/MySqlServerHelper.cs
+++ b/FAnsiSql/Implementations/MySql/MySqlServerHelper.cs
@@ -11,10 +11,10 @@ namespace FAnsi.Implementations.MySql;
public sealed class MySqlServerHelper : DiscoveredServerHelper
{
- public static readonly MySqlServerHelper Instance=new();
+ public static readonly MySqlServerHelper Instance = new();
static MySqlServerHelper()
{
- AddConnectionStringKeyword(DatabaseType.MySql, "AllowUserVariables","True",ConnectionStringKeywordPriority.ApiRule);
+ AddConnectionStringKeyword(DatabaseType.MySql, "AllowUserVariables", "True", ConnectionStringKeywordPriority.ApiRule);
AddConnectionStringKeyword(DatabaseType.MySql, "CharSet", "utf8", ConnectionStringKeywordPriority.ApiRule);
}
@@ -35,7 +35,7 @@ public override DbCommand GetCommand(string s, DbConnection con, DbTransaction?
public override DbCommandBuilder GetCommandBuilder(DbCommand cmd) =>
new MySqlCommandBuilder((MySqlDataAdapter)GetDataAdapter(cmd));
- public override DbParameter GetParameter(string parameterName) => new MySqlParameter(parameterName,null);
+ public override DbParameter GetParameter(string parameterName) => new MySqlParameter(parameterName, null);
public override DbConnection GetConnection(DbConnectionStringBuilder builder) =>
new MySqlConnection(builder.ConnectionString);
@@ -43,14 +43,14 @@ public override DbConnection GetConnection(DbConnectionStringBuilder builder) =>
protected override DbConnectionStringBuilder GetConnectionStringBuilderImpl(string connectionString) =>
new MySqlConnectionStringBuilder(connectionString);
- protected override DbConnectionStringBuilder GetConnectionStringBuilderImpl(string server, string database, string username, string password)
+ protected override DbConnectionStringBuilder GetConnectionStringBuilderImpl(string server, string? database, string username, string password)
{
var toReturn = new MySqlConnectionStringBuilder
{
Server = server
};
- if(!string.IsNullOrWhiteSpace(database))
+ if (!string.IsNullOrWhiteSpace(database))
toReturn.Database = database;
if (!string.IsNullOrWhiteSpace(username))
@@ -77,25 +77,25 @@ public override void CreateDatabase(DbConnectionStringBuilder builder, IHasRunti
using var con = new MySqlConnection(b.ConnectionString);
con.Open();
- using var cmd = GetCommand($"CREATE DATABASE `{newDatabaseName.GetRuntimeName()}`",con);
+ using var cmd = GetCommand($"CREATE DATABASE `{newDatabaseName.GetRuntimeName()}`", con);
cmd.CommandTimeout = CreateDatabaseTimeoutInSeconds;
cmd.ExecuteNonQuery();
}
public override Dictionary DescribeServer(DbConnectionStringBuilder builder) => throw new NotImplementedException();
- public override string GetExplicitUsernameIfAny(DbConnectionStringBuilder builder) => ((MySqlConnectionStringBuilder) builder).UserID;
+ public override string GetExplicitUsernameIfAny(DbConnectionStringBuilder builder) => ((MySqlConnectionStringBuilder)builder).UserID;
public override string GetExplicitPasswordIfAny(DbConnectionStringBuilder builder) => ((MySqlConnectionStringBuilder)builder).Password;
- public override Version GetVersion(DiscoveredServer server)
+ public override Version? GetVersion(DiscoveredServer server)
{
using var con = server.GetConnection();
con.Open();
- using var cmd = server.GetCommand("show variables like \"version\"",con);
+ using var cmd = server.GetCommand("show variables like \"version\"", con);
using var r = cmd.ExecuteReader();
if (r.Read())
- return r["Value"] == DBNull.Value ? null: CreateVersionFromString((string)r["Value"]);
+ return r["Value"] == DBNull.Value ? null : CreateVersionFromString((string)r["Value"]);
return null;
}
diff --git a/FAnsiSql/Implementations/MySql/MySqlTypeTranslater.cs b/FAnsiSql/Implementations/MySql/MySqlTypeTranslater.cs
index a2543e6..db061fd 100644
--- a/FAnsiSql/Implementations/MySql/MySqlTypeTranslater.cs
+++ b/FAnsiSql/Implementations/MySql/MySqlTypeTranslater.cs
@@ -14,14 +14,13 @@ public sealed partial class MySqlTypeTranslater : TypeTranslater
private static readonly Regex AlsoStringRegex = AlsoStringRe();
private static readonly Regex AlsoFloatingPoint = AlsoFloatingPointRe();
- private MySqlTypeTranslater() : base(4000, 4000)
+ private MySqlTypeTranslater() : base(DateRe(), 4000, 4000)
{
//match bigint and bigint(20) etc
ByteRegex = ByteRe();
SmallIntRegex = SmallIntRe();
IntRegex = IntRe();
LongRegex = LongRe();
- DateRegex = DateRe();
}
public override int GetLengthIfString(string sqlType) =>
diff --git a/FAnsiSql/Implementations/Oracle/OracleQuerySyntaxHelper.cs b/FAnsiSql/Implementations/Oracle/OracleQuerySyntaxHelper.cs
index 8c447ec..62aaa3d 100644
--- a/FAnsiSql/Implementations/Oracle/OracleQuerySyntaxHelper.cs
+++ b/FAnsiSql/Implementations/Oracle/OracleQuerySyntaxHelper.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using FAnsi.Discovery;
using FAnsi.Discovery.QuerySyntax;
using FAnsi.Implementations.Oracle.Aggregation;
@@ -9,7 +10,7 @@ namespace FAnsi.Implementations.Oracle;
public sealed class OracleQuerySyntaxHelper : QuerySyntaxHelper
{
- public static readonly OracleQuerySyntaxHelper Instance=new();
+ public static readonly OracleQuerySyntaxHelper Instance = new();
public override int MaximumDatabaseLength => 30; // JS 2023-05-11 Can be longer, but Oracle RAC limits to 30
public override int MaximumTableLength => 30;
public override int MaximumColumnLength => 30;
@@ -19,13 +20,14 @@ public sealed class OracleQuerySyntaxHelper : QuerySyntaxHelper
public override string CloseQualifier => "\"";
- private OracleQuerySyntaxHelper() : base(OracleTypeTranslater.Instance, OracleAggregateHelper.Instance,OracleUpdateHelper.Instance,DatabaseType.Oracle)//no custom translater
+ private OracleQuerySyntaxHelper() : base(OracleTypeTranslater.Instance, OracleAggregateHelper.Instance, OracleUpdateHelper.Instance, DatabaseType.Oracle)//no custom translater
{
}
public override char ParameterSymbol => ':';
- public override string GetRuntimeName(string s)
+ [return: NotNullIfNotNull(nameof(s))]
+ public override string? GetRuntimeName(string? s)
{
var answer = base.GetRuntimeName(s);
@@ -38,7 +40,7 @@ public override string GetRuntimeName(string s)
public override string EnsureWrappedImpl(string databaseOrTableName) => $"\"{GetRuntimeName(databaseOrTableName)}\"";
- public override string EnsureFullyQualified(string databaseName, string schema, string tableName)
+ public override string EnsureFullyQualified(string? databaseName, string? schema, string tableName)
{
//if there is no schema address it as db..table (which is the same as db.dbo.table in Microsoft SQL Server)
if (!string.IsNullOrWhiteSpace(schema))
@@ -47,7 +49,7 @@ public override string EnsureFullyQualified(string databaseName, string schema,
return $"\"{GetRuntimeName(databaseName)}\"{DatabaseTableSeparator}\"{GetRuntimeName(tableName)}\"";
}
- public override string EnsureFullyQualified(string databaseName, string schema, string tableName, string columnName, bool isTableValuedFunction = false) => $"{EnsureFullyQualified(databaseName, schema, tableName)}.\"{GetRuntimeName(columnName)}\"";
+ public override string EnsureFullyQualified(string? databaseName, string? schema, string tableName, string columnName, bool isTableValuedFunction = false) => $"{EnsureFullyQualified(databaseName, schema, tableName)}.\"{GetRuntimeName(columnName)}\"";
public override TopXResponse HowDoWeAchieveTopX(int x) => new($"OFFSET 0 ROWS FETCH NEXT {x} ROWS ONLY", QueryComponent.Postfix);
@@ -80,7 +82,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(new[]
{
"ACCESS",
@@ -563,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 49b6da6..87e9def 100644
--- a/FAnsiSql/Implementations/Oracle/OracleTypeTranslater.cs
+++ b/FAnsiSql/Implementations/Oracle/OracleTypeTranslater.cs
@@ -20,10 +20,10 @@ public sealed partial class OracleTypeTranslater:TypeTranslater
private static readonly Regex AlsoStringRegex = AlsoStringRegexImpl();
- private OracleTypeTranslater(): base(4000, 4000)
+ private OracleTypeTranslater() : base(DateRegexImpl(), 4000, 4000)
{
- DateRegex = DateRegexImpl();
}
+
protected override string GetStringDataTypeImpl(int maxExpectedStringWidth) => $"varchar2({maxExpectedStringWidth})";
protected override string GetUnicodeStringDataTypeImpl(int maxExpectedStringWidth) => $"nvarchar2({maxExpectedStringWidth})";
diff --git a/FAnsiSql/Implementations/PostgreSql/PostgreSqlSyntaxHelper.cs b/FAnsiSql/Implementations/PostgreSql/PostgreSqlSyntaxHelper.cs
index 72458e0..08de9a9 100644
--- a/FAnsiSql/Implementations/PostgreSql/PostgreSqlSyntaxHelper.cs
+++ b/FAnsiSql/Implementations/PostgreSql/PostgreSqlSyntaxHelper.cs
@@ -10,7 +10,7 @@ namespace FAnsi.Implementations.PostgreSql;
public sealed class PostgreSqlSyntaxHelper : QuerySyntaxHelper
{
public static readonly PostgreSqlSyntaxHelper Instance = new();
- private PostgreSqlSyntaxHelper() : base(PostgreSqlTypeTranslater.Instance,PostgreSqlAggregateHelper.Instance,PostgreSqlUpdateHelper.Instance, DatabaseType.PostgreSql)
+ private PostgreSqlSyntaxHelper() : base(PostgreSqlTypeTranslater.Instance, PostgreSqlAggregateHelper.Instance, PostgreSqlUpdateHelper.Instance, DatabaseType.PostgreSql)
{
}
@@ -30,7 +30,7 @@ protected override object FormatDateTimeForDbParameter(DateTime dateTime) =>
// Starting with 4.0.0 npgsql crashes if it has to read a DateTime Unspecified Kind
// See : https://github.com/npgsql/efcore.pg/issues/2000
// Also it doesn't support DateTime.Kind.Local
- dateTime.Kind == DateTimeKind.Unspecified ? dateTime.ToUniversalTime():dateTime;
+ dateTime.Kind == DateTimeKind.Unspecified ? dateTime.ToUniversalTime() : dateTime;
public override string EnsureWrappedImpl(string databaseOrTableName) => $"\"{GetRuntimeNameWithDoubledDoubleQuotes(databaseOrTableName)}\"";
@@ -39,25 +39,19 @@ 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("\"\"","\"");
+ protected override string UnescapeWrappedNameBody(string name) => name.Replace("\"\"", "\"");
- public override string EnsureFullyQualified(string databaseName, string schema, string tableName)
- {
+ public override string EnsureFullyQualified(string? databaseName, string? schema, string tableName) =>
//if there is no schema address it as db..table (which is the same as db.dbo.table in Microsoft SQL Server)
- if(string.IsNullOrWhiteSpace(schema))
- return EnsureWrapped(databaseName) + DatabaseTableSeparator + DefaultPostgresSchema + DatabaseTableSeparator + EnsureWrapped(tableName);
-
- //there is a schema so add it in
- return EnsureWrapped(databaseName) + DatabaseTableSeparator + EnsureWrapped(schema) + DatabaseTableSeparator + EnsureWrapped(tableName);
- }
+ $"{EnsureWrapped(databaseName)}{DatabaseTableSeparator}{(string.IsNullOrWhiteSpace(schema) ? DefaultPostgresSchema : EnsureWrapped(schema))}{DatabaseTableSeparator}{EnsureWrapped(tableName)}";
- public override string EnsureFullyQualified(string databaseName, string schema, string tableName, string columnName,
+ public override string EnsureFullyQualified(string? databaseName, string? schema, string tableName, string columnName,
bool isTableValuedFunction = false)
{
if (isTableValuedFunction)
- return $"{EnsureWrapped(tableName)}.{EnsureWrapped(GetRuntimeName(columnName))}";//table valued functions do not support database name being in the column level selection list area of sql queries
+ return $"{EnsureWrapped(tableName)}.{EnsureWrapped(GetRuntimeName(columnName))}"; //table valued functions do not support database name being in the column level selection list area of sql queries
return $"{EnsureFullyQualified(databaseName, schema, tableName)}.\"{GetRuntimeName(columnName)}\"";
}
diff --git a/FAnsiSql/Implementations/PostgreSql/PostgreSqlTypeTranslater.cs b/FAnsiSql/Implementations/PostgreSql/PostgreSqlTypeTranslater.cs
index 61c0837..508a417 100644
--- a/FAnsiSql/Implementations/PostgreSql/PostgreSqlTypeTranslater.cs
+++ b/FAnsiSql/Implementations/PostgreSql/PostgreSqlTypeTranslater.cs
@@ -8,9 +8,9 @@ namespace FAnsi.Implementations.PostgreSql;
public sealed partial class PostgreSqlTypeTranslater : TypeTranslater
{
public static readonly PostgreSqlTypeTranslater Instance = new();
- private PostgreSqlTypeTranslater() : base(8000, 4000)
+
+ private PostgreSqlTypeTranslater() : base(DateRegexImpl(), 8000, 4000)
{
- DateRegex = DateRegexImpl();
TimeRegex = TimeRegexImpl(); //space is important
}
diff --git a/README.md b/README.md
index bb51f5c..b90a10a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# FAnsiSql
-[![.NET Core](https://github.com/HicServices/FAnsiSql/actions/workflows/dotnet-core.yml/badge.svg)](https://github.com/HicServices/FAnsiSql/actions/workflows/dotnet-core.yml) [![Total alerts](https://img.shields.io/lgtm/alerts/g/HicServices/FAnsiSql.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/HicServices/FAnsiSql/alerts/) [![NuGet Badge](https://buildstats.info/nuget/HIC.FAnsiSql)](https://www.nuget.org/packages/HIC.FansiSql/)
+[![Build and test](https://github.com/HicServices/FAnsiSql/actions/workflows/dotnet-core.yml/badge.svg)](https://github.com/HicServices/FAnsiSql/actions/workflows/dotnet-core.yml)
+[![CodeQL](https://github.com/HicServices/FAnsiSql/actions/workflows/codeql.yml/badge.svg)](https://github.com/HicServices/FAnsiSql/actions/workflows/codeql.yml)
+[![NuGet Badge](https://buildstats.info/nuget/HIC.FAnsiSql)](https://www.nuget.org/packages/HIC.FansiSql/)
- [Nuget](https://www.nuget.org/packages/HIC.FansiSql/)
- [Dependencies](./Packages.md)
diff --git a/SharedAssemblyInfo.cs b/SharedAssemblyInfo.cs
index 7d4f566..2d99543 100644
--- a/SharedAssemblyInfo.cs
+++ b/SharedAssemblyInfo.cs
@@ -2,10 +2,10 @@
[assembly: AssemblyCompany("Health Informatics Centre, University of Dundee")]
[assembly: AssemblyProduct("FAnsiSql")]
-[assembly: AssemblyCopyright("Copyright (c) 2018 - 2023")]
+[assembly: AssemblyCopyright("Copyright (c) 2018 - 2024")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("3.1.1")]
-[assembly: AssemblyFileVersion("3.1.1")]
-[assembly: AssemblyInformationalVersion("3.1.1")]
+[assembly: AssemblyVersion("3.2.0")]
+[assembly: AssemblyFileVersion("3.2.0")]
+[assembly: AssemblyInformationalVersion("3.2.0")]
diff --git a/Tests/FAnsiTests/Table/CreateTableTests.cs b/Tests/FAnsiTests/Table/CreateTableTests.cs
index ef72506..f9eb224 100644
--- a/Tests/FAnsiTests/Table/CreateTableTests.cs
+++ b/Tests/FAnsiTests/Table/CreateTableTests.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Data;
+using System.IO;
using System.Linq;
using System.Reflection;
using FAnsi;
@@ -12,9 +13,9 @@
namespace FAnsiTests.Table;
-internal sealed class CreateTableTests:DatabaseTests
+internal sealed class CreateTableTests : DatabaseTests
{
- [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))]
+ [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))]
public void CreateSimpleTable_Exists(DatabaseType type)
{
var db = GetTestDatabase(type);
@@ -30,7 +31,7 @@ public void CreateSimpleTable_Exists(DatabaseType type)
Assert.That(table.Exists(), Is.False);
}
- [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))]
+ [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))]
public void TestTableCreation(DatabaseType type)
{
var database = GetTestDatabase(type);
@@ -54,14 +55,14 @@ public void TestTableCreation(DatabaseType type)
Assert.That(tbl.Exists());
- var colsDictionary = tbl.DiscoverColumns().ToDictionary(static k => k.GetRuntimeName(), static v => v, StringComparer.InvariantCultureIgnoreCase);
+ var colsDictionary = tbl.DiscoverColumns().ToDictionary(static k => k.GetRuntimeName() ?? throw new InvalidDataException(), static v => v, StringComparer.InvariantCultureIgnoreCase);
var name = colsDictionary["name"];
Assert.Multiple(() =>
{
- Assert.That(name.DataType.GetLengthIfString(), Is.EqualTo(10));
+ Assert.That(name.DataType?.GetLengthIfString(), Is.EqualTo(10));
Assert.That(name.AllowNulls, Is.EqualTo(false));
- Assert.That(syntaxHelper.TypeTranslater.GetCSharpTypeForSQLDBType(name.DataType.SQLType), Is.EqualTo(typeof(string)));
+ Assert.That(syntaxHelper.TypeTranslater.GetCSharpTypeForSQLDBType(name.DataType?.SQLType), Is.EqualTo(typeof(string)));
Assert.That(name.IsPrimaryKey);
});
@@ -70,8 +71,8 @@ public void TestTableCreation(DatabaseType type)
Assert.Multiple(() =>
{
Assert.That(foreignName.AllowNulls, Is.EqualTo(false));//because it is part of the primary key we ignored the users request about nullability
- Assert.That(foreignName.DataType.GetLengthIfString(), Is.EqualTo(7));
- Assert.That(syntaxHelper.TypeTranslater.GetCSharpTypeForSQLDBType(foreignName.DataType.SQLType), Is.EqualTo(typeof(string)));
+ Assert.That(foreignName.DataType?.GetLengthIfString(), Is.EqualTo(7));
+ Assert.That(syntaxHelper.TypeTranslater.GetCSharpTypeForSQLDBType(foreignName.DataType?.SQLType), Is.EqualTo(typeof(string)));
Assert.That(foreignName.IsPrimaryKey);
});
@@ -122,7 +123,7 @@ [new DatabaseColumnRequest("Name", "VARCHAR2(10)")]
table.Drop();
}
- [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))]
+ [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))]
public void CreateSimpleTable_VarcharTypeCorrect(DatabaseType type)
{
var db = GetTestDatabase(type);
@@ -160,7 +161,7 @@ public void CreateSimpleTable_VarcharTypeCorrect(DatabaseType type)
Assert.That(table.Exists(), Is.False);
}
- [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))]
+ [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))]
public void CreateTable_PrimaryKey_FromDataTable(DatabaseType databaseType)
{
var database = GetTestDatabase(databaseType);
@@ -175,7 +176,7 @@ public void CreateTable_PrimaryKey_FromDataTable(DatabaseType databaseType)
Assert.That(table.DiscoverColumn("Name").IsPrimaryKey);
}
- [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))]
+ [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))]
public void CreateTable_PrimaryKey_FromColumnRequest(DatabaseType databaseType)
{
var database = GetTestDatabase(databaseType);
@@ -196,7 +197,7 @@ public void CreateTable_PrimaryKey_FromColumnRequest(DatabaseType databaseType)
[TestCase(DatabaseType.MicrosoftSQLServer, "Latin1_General_CS_AS_KS_WS")]
[TestCase(DatabaseType.MySql, "latin1_german1_ci")]
- [TestCase(DatabaseType.PostgreSql,"de-DE-x-icu")]
+ [TestCase(DatabaseType.PostgreSql, "de-DE-x-icu")]
//[TestCase(DatabaseType.Oracle, "BINARY_CI")] //Requires 12.2+ oracle https://www.experts-exchange.com/questions/29102764/SQL-Statement-to-create-case-insensitive-columns-and-or-tables-in-Oracle.html
public void CreateTable_CollationTest(DatabaseType type, string collation)
{
@@ -214,13 +215,13 @@ public void CreateTable_CollationTest(DatabaseType type, string collation)
Assert.That(tbl.DiscoverColumn("Name").Collation, Is.EqualTo(collation));
}
- [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))]
+ [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))]
public void CreateTable_BoolStrings(DatabaseType type)
{
var db = GetTestDatabase(type);
using var dt = new DataTable();
dt.TableName = "MyTable";
- dt.Columns.Add("MyBoolCol",typeof(bool));
+ dt.Columns.Add("MyBoolCol", typeof(bool));
dt.Rows.Add("true");
var tbl = db.CreateTable("MyTable", dt);
@@ -305,12 +306,12 @@ public void Test_OracleBit_IsNotStringAnyMore()
[TestCase(DatabaseType.MicrosoftSQLServer, "Æther")]
[TestCase(DatabaseType.MicrosoftSQLServer, "乗")]
[TestCase(DatabaseType.Oracle, "didn’t")]
- [TestCase(DatabaseType.Oracle,"Æther")]
+ [TestCase(DatabaseType.Oracle, "Æther")]
[TestCase(DatabaseType.Oracle, "乗")]
//[TestCase(DatabaseType.MySql, "didn’t")]
//[TestCase(DatabaseType.MySql, "Æther")]
//[TestCase(DatabaseType.MySql,"乗")]
- public void Test_CreateTable_UnicodeStrings(DatabaseType type,string testString)
+ public void Test_CreateTable_UnicodeStrings(DatabaseType type, string testString)
{
var db = GetTestDatabase(type);
@@ -318,13 +319,13 @@ public void Test_CreateTable_UnicodeStrings(DatabaseType type,string testString)
dt.Columns.Add("Yay");
dt.Rows.Add(testString);
- var table = db.CreateTable("GoGo",dt);
+ var table = db.CreateTable("GoGo", dt);
//find the table column created
var col = table.DiscoverColumn("Yay");
//value fetched from database should match the one inserted
- var dbValue = (string) table.GetDataTable().Rows[0][0];
+ var dbValue = (string)table.GetDataTable().Rows[0][0];
Assert.That(dbValue, Is.EqualTo(testString));
table.Drop();
@@ -337,7 +338,7 @@ public void Test_CreateTable_UnicodeStrings(DatabaseType type,string testString)
Assert.That(comp.Guess.Unicode);
}
- [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))]
+ [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))]
public void Test_CreateTable_UnicodeNames(DatabaseType dbType)
{
var db = GetTestDatabase(dbType);
@@ -359,11 +360,11 @@ public void Test_CreateTable_UnicodeNames(DatabaseType dbType)
var col = table.DiscoverColumn("微笑");
Assert.That(col.GetRuntimeName(), Is.EqualTo("微笑"));
- table.Insert(new Dictionary {{ "微笑","10" } });
+ table.Insert(new Dictionary { { "微笑", "10" } });
Assert.That(table.GetRowCount(), Is.EqualTo(2));
- table.Insert(new Dictionary {{ col,"11" } });
+ table.Insert(new Dictionary { { col, "11" } });
Assert.That(table.GetRowCount(), Is.EqualTo(3));
@@ -371,7 +372,7 @@ public void Test_CreateTable_UnicodeNames(DatabaseType dbType)
dt2.Columns.Add("微笑");
dt2.Rows.Add(23);
- using(var bulk = table.BeginBulkInsert())
+ using (var bulk = table.BeginBulkInsert())
bulk.Upload(dt2);
Assert.That(table.GetRowCount(), Is.EqualTo(4));
@@ -401,22 +402,22 @@ public void Test_CreateTable_TF(DatabaseType dbType)
tbl.Drop();
}
- [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))]
+ [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))]
public void Test_CreateTable_DoNotRetype(DatabaseType dbType)
{
//T and F is normally True and False. If you want to keep it as a string set DoNotRetype
var db = GetTestDatabase(dbType);
using var dt = new DataTable();
- dt.Columns.Add("Hb");
+ var hb = dt.Columns.Add("Hb");
dt.Rows.Add("T");
dt.Rows.Add("F");
//do not retype string to bool
- dt.Columns["Hb"].SetDoNotReType(true);
+ hb.SetDoNotReType(true);
var tbl = db.CreateTable("T1", dt);
- Assert.That(tbl.DiscoverColumn("Hb").DataType.GetCSharpDataType(), Is.EqualTo(typeof(string)));
+ Assert.That(tbl.DiscoverColumn("Hb").DataType?.GetCSharpDataType(), Is.EqualTo(typeof(string)));
var dt2 = tbl.GetDataTable();
var values = dt2.Rows.Cast().Select(static c => (string)c[0]).ToArray();
@@ -438,17 +439,17 @@ public void Test_DataTableClone_ExtendedProperties()
//the default Type for a DataColumn is string
Assert.That(dt.Columns[0].DataType, Is.EqualTo(typeof(string)));
- dt.Columns["C1"]?.ExtendedProperties.Add("ff",true);
+ dt.Columns["C1"]?.ExtendedProperties.Add("ff", true);
var dt2 = dt.Clone();
- Assert.That(dt2.Columns["C1"]?.ExtendedProperties.ContainsKey("ff")??false);
+ Assert.That(dt2.Columns["C1"]?.ExtendedProperties.ContainsKey("ff") ?? false);
}
[Test]
public void Test_GetDoNotRetype_OnlyStringColumns()
{
using var dt = new DataTable();
- dt.Columns.Add("C1",typeof(int));
+ dt.Columns.Add("C1", typeof(int));
dt.SetDoNotReType(true);
@@ -464,17 +465,17 @@ public void Test_GetDoNotRetype_OnlyStringColumns()
///
/// Tests how CreateTable interacts with of type Object
///
- [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))]
+ [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))]
public void CreateTable_ObjectColumns_StringContent(DatabaseType dbType)
{
//T and F is normally True and False. If you want to keep it as a string set DoNotRetype
var db = GetTestDatabase(dbType);
var dt = new DataTable();
- dt.Columns.Add("Hb",typeof(object));
+ dt.Columns.Add("Hb", typeof(object));
dt.Rows.Add("T");
dt.Rows.Add("F");
- var ex = Assert.Throws(()=>db.CreateTable("T1", dt));
+ var ex = Assert.Throws(() => db.CreateTable("T1", dt));
Assert.That(ex?.Message, Does.Contain("System.Object"));
@@ -484,8 +485,8 @@ public void CreateTable_ObjectColumns_StringContent(DatabaseType dbType)
/// Tests how we can customize how "T" and "F" etc are interpreted (either as boolean true/false or as string). This test
/// uses the static defaults in .
///
- [TestCase(DatabaseType.MicrosoftSQLServer,true)]
- [TestCase(DatabaseType.MicrosoftSQLServer,false)]
+ [TestCase(DatabaseType.MicrosoftSQLServer, true)]
+ [TestCase(DatabaseType.MicrosoftSQLServer, false)]
public void CreateTable_GuessSettings_StaticDefaults_TF(DatabaseType dbType, bool treatAsBoolean)
{
//T and F is normally True and False. If you want to keep it as a string set DoNotRetype
@@ -517,7 +518,7 @@ public void CreateTable_GuessSettings_StaticDefaults_TF(DatabaseType dbType, boo
}
}
- [TestCaseSource(typeof(All),nameof(All.DatabaseTypes))]
+ [TestCaseSource(typeof(All), nameof(All.DatabaseTypes))]
public void TestSomething(DatabaseType dbType)
{
var db = GetTestDatabase(dbType);
@@ -555,8 +556,8 @@ public void TestSomething(DatabaseType dbType)
/// Tests how we can customize how "T" and "F" etc are interpreted (either as boolean true/false or as string). This test
/// uses the injection.
///
- [TestCase(DatabaseType.MicrosoftSQLServer,true)]
- [TestCase(DatabaseType.MicrosoftSQLServer,false)]
+ [TestCase(DatabaseType.MicrosoftSQLServer, true)]
+ [TestCase(DatabaseType.MicrosoftSQLServer, false)]
public void CreateTable_GuessSettings_InArgs_TF(DatabaseType dbType, bool treatAsBoolean)
{
//T and F is normally True and False. If you want to keep it as a string set DoNotRetype
@@ -586,7 +587,7 @@ public void CreateTable_GuessSettings_InArgs_TF(DatabaseType dbType, bool treatA
});
}
- [TestCaseSource(typeof(All),nameof(All.DatabaseTypesWithBoolFlags))]
+ [TestCaseSource(typeof(All), nameof(All.DatabaseTypesWithBoolFlags))]
public void CreateTable_GuessSettings_ExplicitDateTimeFormat(DatabaseType dbType, bool useCustomDate)
{
//Values like 013020 would normally be treated as string data (due to leading zero) but maybe the user wants it to be a date?
@@ -595,7 +596,7 @@ public void CreateTable_GuessSettings_ExplicitDateTimeFormat(DatabaseType dbType
dt.Columns.Add("DateCol");
dt.Rows.Add("013020");
- var args = new CreateTableArgs(db,"Hb",null,dt,false);
+ var args = new CreateTableArgs(db, "Hb", null, dt, false);
Assert.Multiple(() =>
{
Assert.That(GuessSettingsFactory.Defaults.ExplicitDateFormats, Is.EqualTo(args.GuessSettings.ExplicitDateFormats), "Default should match the static default");
@@ -603,15 +604,15 @@ public void CreateTable_GuessSettings_ExplicitDateTimeFormat(DatabaseType dbType
});
//change the args settings to treat this date format
- args.GuessSettings.ExplicitDateFormats = useCustomDate ? ["MMddyy"] :null;
+ args.GuessSettings.ExplicitDateFormats = useCustomDate ? ["MMddyy"] : null;
var tbl = db.CreateTable(args);
var col = tbl.DiscoverColumn("DateCol");
- Assert.That(col.DataType.GetCSharpDataType(), Is.EqualTo(useCustomDate ? typeof(DateTime): typeof(string)));
+ Assert.That(col.DataType.GetCSharpDataType(), Is.EqualTo(useCustomDate ? typeof(DateTime) : typeof(string)));
var dtDown = tbl.GetDataTable();
- Assert.That(dtDown.Rows[0][0], Is.EqualTo(useCustomDate ? new DateTime(2020,01,30): "013020"));
+ Assert.That(dtDown.Rows[0][0], Is.EqualTo(useCustomDate ? new DateTime(2020, 01, 30) : "013020"));
}
[Test]
public void GuessSettings_CopyProperties()