diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs
new file mode 100644
index 00000000..3bbc9237
--- /dev/null
+++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Data;
+using System.Linq;
+using DotNetProjects.Migrator.Framework;
+using NUnit.Framework;
+
+namespace Migrator.Tests.Providers.PostgreSQL;
+
+[TestFixture]
+[Category("Postgre")]
+public class PostgreSQLTransformationProvider_GetColumnsDefaultTypeTests : PostgreSQLTransformationProviderTestBase
+{
+ private const decimal DecimalDefaultValue = 14.56565m;
+
+ [Test]
+ public void GetColumns_DataTypeResolveSucceeds()
+ {
+ // Arrange
+ var dateTimeDefaultValue = new DateTime(2000, 1, 2, 3, 4, 5, DateTimeKind.Utc);
+ var guidDefaultValue = Guid.NewGuid();
+
+ const string testTableName = "MyDefaultTestTable";
+
+ const string dateTimeColumnName1 = "datetimecolumn1";
+ const string dateTimeColumnName2 = "datetimecolumn2";
+ const string decimalColumnName1 = "decimalcolumn";
+ const string guidColumnName1 = "guidcolumn1";
+ const string booleanColumnName1 = "booleancolumn1";
+ const string int32ColumnName1 = "int32column1";
+ const string int64ColumnName1 = "int64column1";
+ const string stringColumnName1 = "stringcolumn1";
+ const string binaryColumnName1 = "binarycolumn1";
+
+ // Should be extended by remaining types
+ Provider.AddTable(testTableName,
+ new Column(dateTimeColumnName1, DbType.DateTime, dateTimeDefaultValue),
+ new Column(dateTimeColumnName2, DbType.DateTime2, dateTimeDefaultValue),
+ new Column(decimalColumnName1, DbType.Decimal, DecimalDefaultValue),
+ new Column(guidColumnName1, DbType.Guid, guidDefaultValue),
+
+ // other boolean default values are tested in another test
+ new Column(booleanColumnName1, DbType.Boolean, true),
+
+ new Column(int32ColumnName1, DbType.Int32, defaultValue: 43),
+ new Column(int64ColumnName1, DbType.Int64, defaultValue: 88),
+ new Column(stringColumnName1, DbType.String, defaultValue: "Hello"),
+ new Column(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 })
+ );
+
+ // Act
+ var columns = Provider.GetColumns(testTableName);
+
+ // Assert
+ var dateTimeColumn1 = columns.Single(x => x.Name == dateTimeColumnName1);
+ var dateTimeColumn2 = columns.Single(x => x.Name == dateTimeColumnName2);
+ var decimalColumn1 = columns.Single(x => x.Name == decimalColumnName1);
+ var guidColumn1 = columns.Single(x => x.Name == guidColumnName1);
+ var booleanColumn1 = columns.Single(x => x.Name == booleanColumnName1);
+ var int32Column1 = columns.Single(x => x.Name == int32ColumnName1);
+ var int64Column1 = columns.Single(x => x.Name == int64ColumnName1);
+ var stringColumn1 = columns.Single(x => x.Name == stringColumnName1);
+ var binarycolumn1 = columns.Single(x => x.Name == binaryColumnName1);
+
+ Assert.That(dateTimeColumn1.DefaultValue, Is.EqualTo(dateTimeDefaultValue));
+ Assert.That(dateTimeColumn2.DefaultValue, Is.EqualTo(dateTimeDefaultValue));
+ Assert.That(decimalColumn1.DefaultValue, Is.EqualTo(DecimalDefaultValue));
+ Assert.That(guidColumn1.DefaultValue, Is.EqualTo(guidDefaultValue));
+ Assert.That(booleanColumn1.DefaultValue, Is.True);
+ Assert.That(int32Column1.DefaultValue, Is.EqualTo(43));
+ Assert.That(int64Column1.DefaultValue, Is.EqualTo(88));
+ Assert.That(stringColumn1.DefaultValue, Is.EqualTo("Hello"));
+ Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 }));
+ }
+}
diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsTypeTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsTypeTests.cs
new file mode 100644
index 00000000..8c169b08
--- /dev/null
+++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsTypeTests.cs
@@ -0,0 +1,74 @@
+using System.Data;
+using System.Linq;
+using DotNetProjects.Migrator.Framework;
+using NUnit.Framework;
+
+namespace Migrator.Tests.Providers.PostgreSQL;
+
+[TestFixture]
+[Category("Postgre")]
+public class PostgreSQLTransformationProvider_GetColumnTypeTests : PostgreSQLTransformationProviderTestBase
+{
+ [Test]
+ public void GetColumns_DataTypeResolveSucceeds()
+ {
+ // Arrange
+ const string testTableName = "MyDefaultTestTable";
+ const string dateTimeColumnName1 = "datetimecolumn1";
+ const string dateTimeColumnName2 = "datetimecolumn2";
+ const string decimalColumnName1 = "decimalcolumn";
+ const string guidColumnName1 = "guidcolumn1";
+ const string booleanColumnName1 = "booleancolumn1";
+ const string int32ColumnName1 = "int32column1";
+ const string int64ColumnName1 = "int64column1";
+ const string stringColumnName1 = "stringcolumn1";
+ const string stringColumnName2 = "stringcolumn2";
+ const string binaryColumnName1 = "binarycolumn";
+
+ // Should be extended by remaining types
+ Provider.AddTable(testTableName,
+ new Column(dateTimeColumnName1, DbType.DateTime),
+ new Column(dateTimeColumnName2, DbType.DateTime2),
+ new Column(decimalColumnName1, DbType.Decimal),
+ new Column(guidColumnName1, DbType.Guid),
+ new Column(booleanColumnName1, DbType.Boolean),
+ new Column(int32ColumnName1, DbType.Int32),
+ new Column(int64ColumnName1, DbType.Int64),
+ new Column(stringColumnName1, DbType.String),
+ new Column(stringColumnName2, DbType.String) { Size = 30 },
+ new Column(binaryColumnName1, DbType.Binary)
+ );
+
+ // Act
+ var columns = Provider.GetColumns(testTableName);
+
+ var dateTimeColumn1 = columns.Single(x => x.Name == dateTimeColumnName1);
+ var dateTimeColumn2 = columns.Single(x => x.Name == dateTimeColumnName2);
+ var decimalColumn1 = columns.Single(x => x.Name == decimalColumnName1);
+ var guidColumn1 = columns.Single(x => x.Name == guidColumnName1);
+ var booleanColumn1 = columns.Single(x => x.Name == booleanColumnName1);
+ var int32Column1 = columns.Single(x => x.Name == int32ColumnName1);
+ var int64column1 = columns.Single(x => x.Name == int64ColumnName1);
+ var stringColumn1 = columns.Single(x => x.Name == stringColumnName1);
+ var stringColumn2 = columns.Single(x => x.Name == stringColumnName2);
+ var binaryColumn1 = columns.Single(x => x.Name == binaryColumnName1);
+
+
+ // Assert
+ Assert.That(dateTimeColumn1.Type, Is.EqualTo(DbType.DateTime));
+ Assert.That(dateTimeColumn1.Precision, Is.EqualTo(3));
+ Assert.That(dateTimeColumn2.Type, Is.EqualTo(DbType.DateTime2));
+ Assert.That(dateTimeColumn2.Precision, Is.EqualTo(6));
+ Assert.That(decimalColumn1.Type, Is.EqualTo(DbType.Decimal));
+ Assert.That(decimalColumn1.Precision, Is.EqualTo(19));
+ Assert.That(decimalColumn1.Scale, Is.EqualTo(5));
+ Assert.That(guidColumn1.Type, Is.EqualTo(DbType.Guid));
+ Assert.That(booleanColumn1.Type, Is.EqualTo(DbType.Boolean));
+ Assert.That(int32Column1.Type, Is.EqualTo(DbType.Int32));
+ Assert.That(int64column1.Type, Is.EqualTo(DbType.Int64));
+ Assert.That(stringColumn1.Type, Is.EqualTo(DbType.String));
+ Assert.That(stringColumn2.Type, Is.EqualTo(DbType.String));
+ Assert.That(stringColumn2.Size, Is.EqualTo(30));
+ Assert.That(binaryColumn1.Type, Is.EqualTo(DbType.Binary));
+ }
+}
diff --git a/src/Migrator/Framework/Column.cs b/src/Migrator/Framework/Column.cs
index 6d497bc3..765a98ac 100644
--- a/src/Migrator/Framework/Column.cs
+++ b/src/Migrator/Framework/Column.cs
@@ -11,6 +11,7 @@
#endregion
+using System;
using System.Data;
namespace DotNetProjects.Migrator.Framework;
@@ -20,6 +21,8 @@ namespace DotNetProjects.Migrator.Framework;
///
public class Column : IColumn, IDbField
{
+ private object _defaultValue;
+
public Column(string name)
{
Name = name;
@@ -147,13 +150,34 @@ public DbType Type
public int Size { get; set; }
+ ///
+ /// Gets or sets the precision for NUMERIC/DECIMAL
+ ///
public int? Precision { get; set; }
+ ///
+ /// Gets or sets the scale for NUMERIC/DECIMAL
+ ///
public int? Scale { get; set; }
public ColumnProperty ColumnProperty { get; set; }
- public object DefaultValue { get; set; }
+ public object DefaultValue
+ {
+ get => _defaultValue;
+ set
+ {
+ if (value is DateTime defaultValueDateTime)
+ {
+ if (defaultValueDateTime.Kind != DateTimeKind.Utc)
+ {
+ throw new Exception("We only accept UTC values as default DateTime values.");
+ }
+ }
+
+ _defaultValue = value;
+ }
+ }
public bool IsIdentity
{
diff --git a/src/Migrator/Providers/Dialect.cs b/src/Migrator/Providers/Dialect.cs
index d66ae1fc..7a8d9e49 100644
--- a/src/Migrator/Providers/Dialect.cs
+++ b/src/Migrator/Providers/Dialect.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Data;
+using System.Globalization;
using DotNetProjects.Migrator.Framework;
namespace DotNetProjects.Migrator.Providers;
@@ -344,7 +345,9 @@ public virtual string Default(object defaultValue)
}
else if (defaultValue is Guid)
{
- return string.Format("DEFAULT '{0}'", defaultValue.ToString());
+ var guidValue = string.Format("DEFAULT '{0}'", defaultValue.ToString());
+
+ return guidValue;
}
else if (defaultValue is DateTime)
{
@@ -355,6 +358,16 @@ public virtual string Default(object defaultValue)
defaultValue = ((string)defaultValue).Replace("'", "''");
defaultValue = "'" + defaultValue + "'";
}
+ else if (defaultValue is decimal)
+ {
+ // .ToString("N") does not exist in old .NET version
+ defaultValue = Convert.ToString(defaultValue, CultureInfo.InvariantCulture);
+ }
+ else if (defaultValue is byte[] byteArray)
+ {
+ var convertedString = BitConverter.ToString(byteArray).Replace("-", "").ToLower();
+ defaultValue = $"'\\x{convertedString}'";
+ }
return string.Format("DEFAULT {0}", defaultValue);
}
diff --git a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs
index 9923db1f..07d00246 100644
--- a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs
+++ b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs
@@ -18,8 +18,13 @@ public PostgreSQLDialect()
RegisterColumnType(DbType.Byte, "int2");
RegisterColumnType(DbType.Currency, "decimal(16,4)");
RegisterColumnType(DbType.Date, "date");
- RegisterColumnType(DbType.DateTime, "timestamptz");
- RegisterColumnType(DbType.DateTime2, "timestamptz");
+
+ // 8 bytes - resolution 1 microsecond
+ RegisterColumnType(DbType.DateTime, "timestamp(3)");
+
+ // 8 bytes - resolution 1 microsecond
+ // We do not use timezone any more - this is near a datetime2 in SQL Server
+ RegisterColumnType(DbType.DateTime2, "timestamp(6)");
RegisterColumnType(DbType.DateTimeOffset, "timestamptz");
RegisterColumnType(DbType.Decimal, "decimal(19,5)");
RegisterColumnType(DbType.Decimal, 19, "decimal(18, $l)");
diff --git a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs
index c6f391ab..9cc1a855 100644
--- a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs
+++ b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs
@@ -16,6 +16,9 @@
using System.Collections.Generic;
using System.Data;
using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
using Index = DotNetProjects.Migrator.Framework.Index;
namespace DotNetProjects.Migrator.Providers.Impl.PostgreSQL;
@@ -25,6 +28,8 @@ namespace DotNetProjects.Migrator.Providers.Impl.PostgreSQL;
///
public class PostgreSQLTransformationProvider : TransformationProvider
{
+ private Regex stripSingleQuoteRegEx = new("(?<=')[^']*(?=')");
+
public PostgreSQLTransformationProvider(Dialect dialect, string connectionString, string defaultSchema, string scope, string providerName)
: base(dialect, connectionString, defaultSchema, scope)
{
@@ -246,73 +251,276 @@ public override string[] GetTables()
public override Column[] GetColumns(string table)
{
+ var stringBuilder = new StringBuilder();
+ stringBuilder.AppendLine("SELECT");
+ stringBuilder.AppendLine(" COLUMN_NAME,");
+ stringBuilder.AppendLine(" IS_NULLABLE,");
+ stringBuilder.AppendLine(" COLUMN_DEFAULT,");
+ stringBuilder.AppendLine(" DATA_TYPE,");
+ stringBuilder.AppendLine(" DATETIME_PRECISION,");
+ stringBuilder.AppendLine(" CHARACTER_MAXIMUM_LENGTH,");
+ stringBuilder.AppendLine(" NUMERIC_PRECISION,");
+ stringBuilder.AppendLine(" NUMERIC_SCALE");
+ stringBuilder.AppendLine($"FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'public' AND TABLE_NAME = lower('{table}');");
+
var columns = new List();
+
using (var cmd = CreateCommand())
- using (
- var reader =
- ExecuteQuery(cmd,
- string.Format("select COLUMN_NAME, IS_NULLABLE, COLUMN_DEFAULT from information_schema.columns where table_schema = 'public' AND table_name = lower('{0}');", table)))
+ using (var reader = ExecuteQuery(cmd, stringBuilder.ToString()))
{
- // FIXME: Mostly duplicated code from the Transformation provider just to support stupid case-insensitivty of Postgre
while (reader.Read())
{
- var column = new Column(reader[0].ToString(), DbType.String);
- var isNullable = reader.GetString(1) == "YES";
- var defaultValue = reader.GetValue(2);
-
- column.ColumnProperty |= isNullable ? ColumnProperty.Null : ColumnProperty.NotNull;
+ var defaultValueOrdinal = reader.GetOrdinal("COLUMN_DEFAULT");
+ var characterMaximumLengthOrdinal = reader.GetOrdinal("CHARACTER_MAXIMUM_LENGTH");
+ var dateTimePrecisionOrdinal = reader.GetOrdinal("DATETIME_PRECISION");
+ var numericPrecisionOrdinal = reader.GetOrdinal("NUMERIC_PRECISION");
+ var numericScaleOrdinal = reader.GetOrdinal("NUMERIC_SCALE");
+
+ var columnName = reader.GetString(reader.GetOrdinal("COLUMN_NAME"));
+ var isNullable = reader.GetString(reader.GetOrdinal("IS_NULLABLE")) == "YES";
+ var defaultValueString = reader.IsDBNull(defaultValueOrdinal) ? null : reader.GetString(defaultValueOrdinal);
+ var dataTypeString = reader.GetString(reader.GetOrdinal("DATA_TYPE"));
+ var dateTimePrecision = reader.IsDBNull(dateTimePrecisionOrdinal) ? null : (int?)reader.GetInt32(dateTimePrecisionOrdinal);
+ var characterMaximumLength = reader.IsDBNull(characterMaximumLengthOrdinal) ? null : (int?)reader.GetInt32(characterMaximumLengthOrdinal);
+ var numericPrecision = reader.IsDBNull(numericPrecisionOrdinal) ? null : (int?)reader.GetInt32(numericPrecisionOrdinal);
+ var numericScale = reader.IsDBNull(numericScaleOrdinal) ? null : (int?)reader.GetInt32(numericScaleOrdinal);
+
+ DbType dbType = 0;
+ int? precision = null;
+ int? scale = null;
+ int? size = null;
+
+ if (new[] { "timestamptz", "timestamp with time zone" }.Contains(dataTypeString))
+ {
+ dbType = DbType.DateTimeOffset;
+ precision = dateTimePrecision;
+ }
+ else if (dataTypeString == "timestamp" || dataTypeString == "timestamp without time zone")
+ {
+ // 6 is the maximum in PostgreSQL
+ if (dateTimePrecision > 5)
+ {
+ dbType = DbType.DateTime2;
+ }
+ else
+ {
+ dbType = DbType.DateTime;
+ }
- if (defaultValue != null && defaultValue != DBNull.Value)
+ precision = dateTimePrecision;
+ }
+ else if (dataTypeString == "smallint")
+ {
+ dbType = DbType.Int16;
+ }
+ else if (dataTypeString == "integer")
+ {
+ dbType = DbType.Int32;
+ }
+ else if (dataTypeString == "bigint")
+ {
+ dbType = DbType.Int64;
+ }
+ else if (dataTypeString == "numeric")
+ {
+ dbType = DbType.Decimal;
+ precision = numericPrecision;
+ scale = numericScale;
+ }
+ else if (dataTypeString == "real")
+ {
+ dbType = DbType.Single;
+ }
+ else if (dataTypeString == "money")
+ {
+ dbType = DbType.Currency;
+ }
+ else if (dataTypeString == "date")
+ {
+ dbType = DbType.Date;
+ }
+ else if (dataTypeString == "byte")
+ {
+ dbType = DbType.Binary;
+ }
+ else if (dataTypeString == "uuid")
+ {
+ dbType = DbType.Guid;
+ }
+ else if (dataTypeString == "xml")
+ {
+ dbType = DbType.Xml;
+ }
+ else if (dataTypeString == "time")
+ {
+ dbType = DbType.Time;
+ }
+ else if (dataTypeString == "interval")
+ {
+ throw new NotImplementedException();
+ }
+ else if (dataTypeString == "boolean")
+ {
+ dbType = DbType.Boolean;
+ }
+ else if (dataTypeString == "text" || dataTypeString == "character varying")
+ {
+ dbType = DbType.String;
+ size = characterMaximumLength;
+ }
+ else if (dataTypeString == "bytea")
+ {
+ dbType = DbType.Binary;
+ }
+ else if (dataTypeString == "character" || dataTypeString.StartsWith("character("))
{
- column.DefaultValue = defaultValue;
+ throw new NotSupportedException("Data type 'character' detected. 'character' is not supported. Use 'text' or 'character varying' instead.");
}
+ else
+ {
+ throw new NotImplementedException("The data type is not implemented. Please file an issue.");
+ }
+
+ var column = new Column(columnName, dbType)
+ {
+ Precision = precision,
+ Scale = scale,
+ // Size should be nullable
+ Size = size ?? 0
+ };
+
+ column.ColumnProperty |= isNullable ? ColumnProperty.Null : ColumnProperty.NotNull;
- if (column.DefaultValue != null)
+ if (defaultValueString != null)
{
if (column.Type == DbType.Int16 || column.Type == DbType.Int32 || column.Type == DbType.Int64)
{
- column.DefaultValue = long.Parse(column.DefaultValue.ToString());
+ column.DefaultValue = long.Parse(defaultValueString.ToString());
}
else if (column.Type == DbType.UInt16 || column.Type == DbType.UInt32 || column.Type == DbType.UInt64)
{
- column.DefaultValue = ulong.Parse(column.DefaultValue.ToString());
+ column.DefaultValue = ulong.Parse(defaultValueString.ToString());
}
else if (column.Type == DbType.Double || column.Type == DbType.Single)
{
- column.DefaultValue = double.Parse(column.DefaultValue.ToString());
+ column.DefaultValue = double.Parse(defaultValueString.ToString());
}
else if (column.Type == DbType.Boolean)
{
- column.DefaultValue = column.DefaultValue.ToString().Trim() == "1" || column.DefaultValue.ToString().Trim().ToUpper() == "TRUE" || column.DefaultValue.ToString().Trim() == "YES";
+ var truthy = new[] { "1", "TRUE", "YES", "'true'", "on", "'on'", "t", "'t'" };
+ var falsy = new[] { "0", "FALSE", "NO", "'false'", "off", "'off'", "f", "'f'" };
+
+ if (truthy.Any(x => x.Equals(defaultValueString.Trim(), StringComparison.OrdinalIgnoreCase)))
+ {
+ column.DefaultValue = true;
+ }
+ else if (falsy.Any(x => x.Equals(defaultValueString.Trim(), StringComparison.OrdinalIgnoreCase)))
+ {
+ column.DefaultValue = false;
+ }
+ else
+ {
+ throw new NotImplementedException($"Cannot interpret the given default value in column '{column.Name}'");
+ }
}
else if (column.Type == DbType.DateTime || column.Type == DbType.DateTime2)
{
- if (column.DefaultValue is string defVal)
+ if (defaultValueString.StartsWith("'"))
{
- var dt = defVal;
- if (defVal.StartsWith("'"))
+ var match = stripSingleQuoteRegEx.Match(defaultValueString);
+
+ if (!match.Success)
{
- dt = defVal.Substring(1, defVal.Length - 2);
+ throw new Exception("Postgre default value for date time: Single quotes around the date time string are expected.");
}
- var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
- column.DefaultValue = d;
+ var timeString = match.Value;
+
+ // We convert to UTC since we restrict date time default values to UTC on default value definition.
+ var dateTimeExtracted = DateTime.ParseExact(timeString, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
+
+ column.DefaultValue = dateTimeExtracted;
+ }
+ else
+ {
+ throw new NotImplementedException();
}
}
else if (column.Type == DbType.Guid)
{
- if (column.DefaultValue is string defVal)
+ if (defaultValueString.StartsWith("'"))
+ {
+ var match = stripSingleQuoteRegEx.Match(defaultValueString);
+
+ if (!match.Success)
+ {
+ throw new Exception("Postgre default value for uniqueidentifier: Single quotes around the Guid string are expected.");
+ }
+
+ column.DefaultValue = Guid.Parse(match.Value);
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+ }
+ else if (column.Type == DbType.Decimal)
+ {
+ column.DefaultValue = decimal.Parse(defaultValueString, CultureInfo.InvariantCulture);
+ }
+ else if (column.Type == DbType.String)
+ {
+ if (defaultValueString.StartsWith("'"))
+ {
+ var match = stripSingleQuoteRegEx.Match(defaultValueString);
+
+ if (!match.Success)
+ {
+ throw new Exception("Postgre default value for date time: Single quotes around the date time string are expected.");
+ }
+
+ column.DefaultValue = match.Value;
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+ }
+ else if (column.Type == DbType.Binary)
+ {
+ if (defaultValueString.StartsWith("'"))
{
- var dt = defVal;
- if (defVal.StartsWith("'"))
+ var match = stripSingleQuoteRegEx.Match(defaultValueString);
+
+ if (!match.Success)
+ {
+ throw new Exception("Postgre default value for bytea: Single quotes around the bytea string are expected.");
+ }
+
+ var singleQuoteString = match.Value;
+
+ if (!singleQuoteString.StartsWith("\\x"))
{
- dt = defVal.Substring(1, defVal.Length - 2);
+ throw new Exception(@"Postgre \x notation expected.");
}
- var d = Guid.Parse(dt);
- column.DefaultValue = d;
+ var hexString = singleQuoteString.Substring(2);
+
+ // Not available in old .NET version: Convert.FromHexString(hexString);
+
+ column.DefaultValue = Enumerable.Range(0, hexString.Length / 2)
+ .Select(x => Convert.ToByte(hexString.Substring(x * 2, 2), 16))
+ .ToArray();
+ }
+ else
+ {
+ throw new NotImplementedException();
}
}
+ else
+ {
+ throw new NotImplementedException();
+ }
}
columns.Add(column);
diff --git a/src/Migrator/Providers/TransformationProvider.cs b/src/Migrator/Providers/TransformationProvider.cs
index 023e76c5..f7f6b9e6 100644
--- a/src/Migrator/Providers/TransformationProvider.cs
+++ b/src/Migrator/Providers/TransformationProvider.cs
@@ -1784,6 +1784,14 @@ public virtual List GetPrimaryKeys(IEnumerable columns)
public virtual void AddColumnDefaultValue(string table, string column, object defaultValue)
{
+ if (defaultValue is DateTime defaultValueDateTime)
+ {
+ if (defaultValueDateTime.Kind != DateTimeKind.Utc)
+ {
+ throw new Exception("Only UTC values are accepted as default DateTime values.");
+ }
+ }
+
table = QuoteTableNameIfRequired(table);
column = this.QuoteColumnNameIfRequired(column);
var def = Dialect.Default(defaultValue);