Skip to content

Commit 3ba250d

Browse files
add support for time (without timezone) data type in postgres
1 parent 1e3a03e commit 3ba250d

File tree

17 files changed

+155
-102
lines changed

17 files changed

+155
-102
lines changed

Drivers/DbDriver.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public abstract class DbDriver
1717

1818
private HashSet<string> NullableTypesInDotnetCore { get; } = ["string", "object", "byte[]"]; // TODO add arrays in here in a non hard-coded manner
1919

20-
private HashSet<string> NullableTypes { get; } = ["bool", "byte", "short", "int", "long", "float", "double", "decimal", "DateTime"];
20+
private HashSet<string> NullableTypes { get; } = ["bool", "byte", "short", "int", "long", "float", "double", "decimal", "DateTime", "TimeSpan"];
2121

2222
protected abstract List<ColumnMapping> ColumnMappings { get; }
2323

Drivers/NpgsqlDriver.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,19 @@ public NpgsqlDriver(Options options, Dictionary<string, Table> tables) : base(op
5151
{ "mediumtext", new DbTypeInfo() },
5252
{ "text", new DbTypeInfo() },
5353
{ "bpchar", new DbTypeInfo() },
54-
{ "time", new DbTypeInfo() },
5554
{ "tinytext", new DbTypeInfo() },
5655
{ "varchar", new DbTypeInfo() }
5756
}, ordinal => $"reader.GetString({ordinal})",
5857
ordinal => $"reader.GetFieldValue<string[]>({ordinal})"),
58+
new("TimeSpan",
59+
new Dictionary<string, DbTypeInfo>
60+
{
61+
{ "time", new DbTypeInfo(NpgsqlTypeOverride: "NpgsqlDbType.Time") }, // in .Net Core can also use TimeOnly
62+
}, ordinal => $"reader.GetFieldValue<TimeSpan>({ordinal})"),
5963
new("DateTime",
6064
new Dictionary<string, DbTypeInfo>
6165
{
62-
{ "date", new DbTypeInfo(NpgsqlTypeOverride: "NpgsqlDbType.Date") },
66+
{ "date", new DbTypeInfo(NpgsqlTypeOverride: "NpgsqlDbType.Date") }, // in .Net Core can also use DateOnly
6367
{ "timestamp", new DbTypeInfo(NpgsqlTypeOverride: "NpgsqlDbType.Timestamp") },
6468
{ "timestamptz", new DbTypeInfo(NpgsqlTypeOverride: "NpgsqlDbType.TimestampTz") },
6569
}, ordinal => $"reader.GetDateTime({ordinal})"),

docs/04_Postgres.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ we consider support for the different data types separately for batch inserts an
3030
| date |||
3131
| timestamp, timestamp without time zone |||
3232
| timestamp with time zone |||
33-
| time, time without time zone | | |
33+
| time, time without time zone | | |
3434
| time with time zone |||
3535
| interval |||
3636
| char |||

end2end/EndToEndScaffold/Templates.cs

+18-9
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,9 @@ public async Task TestMultipleArrays()
483483
{
484484
Impl = $$"""
485485
[Test]
486-
[TestCase(true, 35, -23423, 4235235263, 3.83f, 4.5534, 998.432, -8403284.321435, 42332.53, "2000-1-30", "1983-11-3 02:01:22", "2022-10-2 15:44:01+09:00", "E", "It takes a nation of millions to hold us back", "Rebel Without a Pause", "Prophets of Rage", new byte[] { 0x45, 0x42 }, new string[] { "Party", "Fight" }, new int[] { 543, -4234 })]
487-
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { }, new string[] { }, new int[] { })]
488-
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
486+
[TestCase(true, 35, -23423, 4235235263, 3.83f, 4.5534, 998.432, -8403284.321435, 42332.53, "2000-1-30", "12:13:14", "1983-11-3 02:01:22", "2022-10-2 15:44:01+09:00", "E", "It takes a nation of millions to hold us back", "Rebel Without a Pause", "Prophets of Rage", new byte[] { 0x45, 0x42 }, new string[] { "Party", "Fight" }, new int[] { 543, -4234 })]
487+
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { }, new string[] { }, new int[] { })]
488+
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
489489
public async Task TestPostgresTypes(
490490
bool cBoolean,
491491
short cSmallint,
@@ -497,8 +497,9 @@ public async Task TestPostgresTypes(
497497
double cDoublePrecision,
498498
decimal cMoney,
499499
DateTime cDate,
500-
DateTime cTimestamp,
501-
DateTime cTimestampWithTz,
500+
TimeSpan? cTime,
501+
DateTime? cTimestamp,
502+
DateTime? cTimestampWithTz,
502503
string cChar,
503504
string cVarchar,
504505
string cCharacterVarying,
@@ -519,6 +520,7 @@ await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs
519520
CDoublePrecision = cDoublePrecision,
520521
CMoney = cMoney,
521522
CDate = cDate,
523+
CTime = cTime,
522524
CTimestamp = cTimestamp,
523525
CTimestampWithTz = cTimestampWithTz,
524526
CChar = cChar,
@@ -542,6 +544,7 @@ await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs
542544
CDoublePrecision = cDoublePrecision,
543545
CMoney = cMoney,
544546
CDate = cDate,
547+
CTime = cTime,
545548
CTimestamp = cTimestamp,
546549
CTimestampWithTz = cTimestampWithTz,
547550
CChar = cChar,
@@ -568,6 +571,7 @@ private static void AssertSingularEquals(QuerySql.GetPostgresTypesRow expected,
568571
Assert.That(actual.CDoublePrecision, Is.EqualTo(expected.CDoublePrecision));
569572
Assert.That(actual.CMoney, Is.EqualTo(expected.CMoney));
570573
Assert.That(actual.CDate, Is.EqualTo(expected.CDate));
574+
Assert.That(actual.CTime, Is.EqualTo(expected.CTime));
571575
Assert.That(actual.CTimestamp, Is.EqualTo(expected.CTimestamp));
572576
Assert.That(actual.CTimestampWithTz, Is.EqualTo(expected.CTimestampWithTz));
573577
Assert.That(actual.CChar, Is.EqualTo(expected.CChar));
@@ -584,10 +588,10 @@ private static void AssertSingularEquals(QuerySql.GetPostgresTypesRow expected,
584588
{
585589
Impl = $$"""
586590
[Test]
587-
[TestCase(100, true, 3, 453, -1445214231, 666.6f, 336.3431, -99.999, -1377.996, -43242.43, "1973-12-3", "1960-11-3 02:01:22", "2030-07-20 15:44:01+09:00", "z", "Sex Pistols", "Anarchy in the U.K", "Never Mind the Bollocks...", new byte[] { 0x53, 0x56 })]
588-
[TestCase(500, false, -4, 867, 8768769709, -64.8f, -324.8671, 127.4793, 423.9869, 32143.99, "2024-12-31", "1999-3-1 03:00:10", "1999-9-13 08:30:11-04:00", "1", "Fugazi", "Waiting Room", "13 Songs", new byte[] { 0x03 })]
589-
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { })]
590-
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
591+
[TestCase(100, true, 3, 453, -1445214231, 666.6f, 336.3431, -99.999, -1377.996, -43242.43, "1973-12-3", "00:34:00", "1960-11-3 02:01:22", "2030-07-20 15:44:01+09:00", "z", "Sex Pistols", "Anarchy in the U.K", "Never Mind the Bollocks...", new byte[] { 0x53, 0x56 })]
592+
[TestCase(500, false, -4, 867, 8768769709, -64.8f, -324.8671, 127.4793, 423.9869, 32143.99, "2024-12-31", "03:06:44", "1999-3-1 03:00:10", "1999-9-13 08:30:11-04:00", "1", "Fugazi", "Waiting Room", "13 Songs", new byte[] { 0x03 })]
593+
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { })]
594+
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
591595
public async Task TestCopyFrom(
592596
int batchSize,
593597
bool cBoolean,
@@ -600,6 +604,7 @@ public async Task TestCopyFrom(
600604
double cDoublePrecision,
601605
decimal cMoney,
602606
DateTime? cDate,
607+
TimeSpan? cTime,
603608
DateTime? cTimestamp,
604609
DateTime? cTimestampWithTz,
605610
string cChar,
@@ -611,6 +616,7 @@ public async Task TestCopyFrom(
611616
DateTime? cTimestampWithTzAsUtc = null;
612617
if (cTimestampWithTz != null)
613618
cTimestampWithTzAsUtc = DateTime.SpecifyKind(cTimestampWithTz.Value, DateTimeKind.Utc);
619+
614620
var batchArgs = Enumerable.Range(0, batchSize)
615621
.Select(_ => new QuerySql.InsertPostgresTypesBatchArgs
616622
{
@@ -624,6 +630,7 @@ public async Task TestCopyFrom(
624630
CDoublePrecision = cDoublePrecision,
625631
CMoney = cMoney,
626632
CDate = cDate,
633+
CTime = cTime,
627634
CTimestamp = cTimestamp,
628635
CTimestampWithTz = cTimestampWithTzAsUtc,
629636
CChar = cChar,
@@ -647,6 +654,7 @@ public async Task TestCopyFrom(
647654
CDoublePrecision = cDoublePrecision,
648655
CMoney = cMoney,
649656
CDate = cDate,
657+
CTime = cTime,
650658
CTimestamp = cTimestamp,
651659
CTimestampWithTz = cTimestampWithTz,
652660
CChar = cChar,
@@ -672,6 +680,7 @@ private static void AssertSingularEquals(QuerySql.GetPostgresTypesAggRow expecte
672680
Assert.That(actual.CDoublePrecision, Is.EqualTo(expected.CDoublePrecision));
673681
Assert.That(actual.CMoney, Is.EqualTo(expected.CMoney));
674682
Assert.That(actual.CDate, Is.EqualTo(expected.CDate));
683+
Assert.That(actual.CTime, Is.EqualTo(expected.CTime));
675684
Assert.That(actual.CTimestamp, Is.EqualTo(expected.CTimestamp));
676685
Assert.That(actual.CTimestampWithTz, Is.EqualTo(expected.CTimestampWithTz));
677686
Assert.That(actual.CChar, Is.EqualTo(expected.CChar));

end2end/EndToEndTests/NpgsqlDapperTester.generated.cs

+15-11
Original file line numberDiff line numberDiff line change
@@ -125,16 +125,16 @@ private static bool SingularEquals(QuerySql.GetAuthorByIdRow x, QuerySql.GetAuth
125125
}
126126

127127
[Test]
128-
[TestCase(100, true, 3, 453, -1445214231, 666.6f, 336.3431, -99.999, -1377.996, -43242.43, "1973-12-3", "1960-11-3 02:01:22", "2030-07-20 15:44:01+09:00", "z", "Sex Pistols", "Anarchy in the U.K", "Never Mind the Bollocks...", new byte[] { 0x53, 0x56 })]
129-
[TestCase(500, false, -4, 867, 8768769709, -64.8f, -324.8671, 127.4793, 423.9869, 32143.99, "2024-12-31", "1999-3-1 03:00:10", "1999-9-13 08:30:11-04:00", "1", "Fugazi", "Waiting Room", "13 Songs", new byte[] { 0x03 })]
130-
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { })]
131-
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
132-
public async Task TestCopyFrom(int batchSize, bool cBoolean, short cSmallint, int cInteger, long cBigint, float cReal, decimal cDecimal, decimal cNumeric, double cDoublePrecision, decimal cMoney, DateTime? cDate, DateTime? cTimestamp, DateTime? cTimestampWithTz, string cChar, string cVarchar, string cCharacterVarying, string cText, byte[] cBytea)
128+
[TestCase(100, true, 3, 453, -1445214231, 666.6f, 336.3431, -99.999, -1377.996, -43242.43, "1973-12-3", "00:34:00", "1960-11-3 02:01:22", "2030-07-20 15:44:01+09:00", "z", "Sex Pistols", "Anarchy in the U.K", "Never Mind the Bollocks...", new byte[] { 0x53, 0x56 })]
129+
[TestCase(500, false, -4, 867, 8768769709, -64.8f, -324.8671, 127.4793, 423.9869, 32143.99, "2024-12-31", "03:06:44", "1999-3-1 03:00:10", "1999-9-13 08:30:11-04:00", "1", "Fugazi", "Waiting Room", "13 Songs", new byte[] { 0x03 })]
130+
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { })]
131+
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
132+
public async Task TestCopyFrom(int batchSize, bool cBoolean, short cSmallint, int cInteger, long cBigint, float cReal, decimal cDecimal, decimal cNumeric, double cDoublePrecision, decimal cMoney, DateTime? cDate, TimeSpan? cTime, DateTime? cTimestamp, DateTime? cTimestampWithTz, string cChar, string cVarchar, string cCharacterVarying, string cText, byte[] cBytea)
133133
{
134134
DateTime? cTimestampWithTzAsUtc = null;
135135
if (cTimestampWithTz != null)
136136
cTimestampWithTzAsUtc = DateTime.SpecifyKind(cTimestampWithTz.Value, DateTimeKind.Utc);
137-
var batchArgs = Enumerable.Range(0, batchSize).Select(_ => new QuerySql.InsertPostgresTypesBatchArgs { CBoolean = cBoolean, CSmallint = cSmallint, CInteger = cInteger, CBigint = cBigint, CReal = cReal, CDecimal = cDecimal, CNumeric = cNumeric, CDoublePrecision = cDoublePrecision, CMoney = cMoney, CDate = cDate, CTimestamp = cTimestamp, CTimestampWithTz = cTimestampWithTzAsUtc, CChar = cChar, CVarchar = cVarchar, CCharacterVarying = cCharacterVarying, CText = cText, CBytea = cBytea }).ToList();
137+
var batchArgs = Enumerable.Range(0, batchSize).Select(_ => new QuerySql.InsertPostgresTypesBatchArgs { CBoolean = cBoolean, CSmallint = cSmallint, CInteger = cInteger, CBigint = cBigint, CReal = cReal, CDecimal = cDecimal, CNumeric = cNumeric, CDoublePrecision = cDoublePrecision, CMoney = cMoney, CDate = cDate, CTime = cTime, CTimestamp = cTimestamp, CTimestampWithTz = cTimestampWithTzAsUtc, CChar = cChar, CVarchar = cVarchar, CCharacterVarying = cCharacterVarying, CText = cText, CBytea = cBytea }).ToList();
138138
await QuerySql.InsertPostgresTypesBatch(batchArgs);
139139
var expected = new QuerySql.GetPostgresTypesAggRow
140140
{
@@ -149,6 +149,7 @@ public async Task TestCopyFrom(int batchSize, bool cBoolean, short cSmallint, in
149149
CDoublePrecision = cDoublePrecision,
150150
CMoney = cMoney,
151151
CDate = cDate,
152+
CTime = cTime,
152153
CTimestamp = cTimestamp,
153154
CTimestampWithTz = cTimestampWithTz,
154155
CChar = cChar,
@@ -174,6 +175,7 @@ private static void AssertSingularEquals(QuerySql.GetPostgresTypesAggRow expecte
174175
Assert.That(actual.CDoublePrecision, Is.EqualTo(expected.CDoublePrecision));
175176
Assert.That(actual.CMoney, Is.EqualTo(expected.CMoney));
176177
Assert.That(actual.CDate, Is.EqualTo(expected.CDate));
178+
Assert.That(actual.CTime, Is.EqualTo(expected.CTime));
177179
Assert.That(actual.CTimestamp, Is.EqualTo(expected.CTimestamp));
178180
Assert.That(actual.CTimestampWithTz, Is.EqualTo(expected.CTimestampWithTz));
179181
Assert.That(actual.CChar, Is.EqualTo(expected.CChar));
@@ -366,12 +368,12 @@ public async Task TestMultipleArrays()
366368
}
367369

368370
[Test]
369-
[TestCase(true, 35, -23423, 4235235263, 3.83f, 4.5534, 998.432, -8403284.321435, 42332.53, "2000-1-30", "1983-11-3 02:01:22", "2022-10-2 15:44:01+09:00", "E", "It takes a nation of millions to hold us back", "Rebel Without a Pause", "Prophets of Rage", new byte[] { 0x45, 0x42 }, new string[] { "Party", "Fight" }, new int[] { 543, -4234 })]
370-
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { }, new string[] { }, new int[] { })]
371-
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
372-
public async Task TestPostgresTypes(bool cBoolean, short cSmallint, int cInteger, long cBigint, float cReal, decimal cNumeric, decimal cDecimal, double cDoublePrecision, decimal cMoney, DateTime cDate, DateTime cTimestamp, DateTime cTimestampWithTz, string cChar, string cVarchar, string cCharacterVarying, string cText, byte[] cBytea, string[] cTextArray, int[] cIntegerArray)
371+
[TestCase(true, 35, -23423, 4235235263, 3.83f, 4.5534, 998.432, -8403284.321435, 42332.53, "2000-1-30", "12:13:14", "1983-11-3 02:01:22", "2022-10-2 15:44:01+09:00", "E", "It takes a nation of millions to hold us back", "Rebel Without a Pause", "Prophets of Rage", new byte[] { 0x45, 0x42 }, new string[] { "Party", "Fight" }, new int[] { 543, -4234 })]
372+
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { }, new string[] { }, new int[] { })]
373+
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
374+
public async Task TestPostgresTypes(bool cBoolean, short cSmallint, int cInteger, long cBigint, float cReal, decimal cNumeric, decimal cDecimal, double cDoublePrecision, decimal cMoney, DateTime cDate, TimeSpan? cTime, DateTime? cTimestamp, DateTime? cTimestampWithTz, string cChar, string cVarchar, string cCharacterVarying, string cText, byte[] cBytea, string[] cTextArray, int[] cIntegerArray)
373375
{
374-
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs { CBoolean = cBoolean, CSmallint = cSmallint, CInteger = cInteger, CBigint = cBigint, CReal = cReal, CNumeric = cNumeric, CDecimal = cDecimal, CDoublePrecision = cDoublePrecision, CMoney = cMoney, CDate = cDate, CTimestamp = cTimestamp, CTimestampWithTz = cTimestampWithTz, CChar = cChar, CVarchar = cVarchar, CCharacterVarying = cCharacterVarying, CText = cText, CBytea = cBytea, CTextArray = cTextArray, CIntegerArray = cIntegerArray });
376+
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs { CBoolean = cBoolean, CSmallint = cSmallint, CInteger = cInteger, CBigint = cBigint, CReal = cReal, CNumeric = cNumeric, CDecimal = cDecimal, CDoublePrecision = cDoublePrecision, CMoney = cMoney, CDate = cDate, CTime = cTime, CTimestamp = cTimestamp, CTimestampWithTz = cTimestampWithTz, CChar = cChar, CVarchar = cVarchar, CCharacterVarying = cCharacterVarying, CText = cText, CBytea = cBytea, CTextArray = cTextArray, CIntegerArray = cIntegerArray });
375377
var expected = new QuerySql.GetPostgresTypesRow
376378
{
377379
CBoolean = cBoolean,
@@ -384,6 +386,7 @@ public async Task TestPostgresTypes(bool cBoolean, short cSmallint, int cInteger
384386
CDoublePrecision = cDoublePrecision,
385387
CMoney = cMoney,
386388
CDate = cDate,
389+
CTime = cTime,
387390
CTimestamp = cTimestamp,
388391
CTimestampWithTz = cTimestampWithTz,
389392
CChar = cChar,
@@ -410,6 +413,7 @@ private static void AssertSingularEquals(QuerySql.GetPostgresTypesRow expected,
410413
Assert.That(actual.CDoublePrecision, Is.EqualTo(expected.CDoublePrecision));
411414
Assert.That(actual.CMoney, Is.EqualTo(expected.CMoney));
412415
Assert.That(actual.CDate, Is.EqualTo(expected.CDate));
416+
Assert.That(actual.CTime, Is.EqualTo(expected.CTime));
413417
Assert.That(actual.CTimestamp, Is.EqualTo(expected.CTimestamp));
414418
Assert.That(actual.CTimestampWithTz, Is.EqualTo(expected.CTimestampWithTz));
415419
Assert.That(actual.CChar, Is.EqualTo(expected.CChar));

0 commit comments

Comments
 (0)