Skip to content

Commit 44feb44

Browse files
feat: support more array types in Postgres
1 parent 2efe6bc commit 44feb44

24 files changed

Lines changed: 1301 additions & 215 deletions

File tree

Drivers/NpgsqlDriver.cs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ public NpgsqlDriver(
4141
{ "bigint", new() },
4242
{ "bigserial", new() }
4343
},
44-
ordinal => $"reader.GetInt64({ordinal})",
44+
readerFn: ordinal => $"reader.GetInt64({ordinal})",
45+
readerArrayFn: ordinal => $"reader.GetFieldValue<long[]>({ordinal})",
4546
convertFunc: IntTo64
4647
),
4748
["byte[]"] = new(
@@ -56,7 +57,8 @@ public NpgsqlDriver(
5657
{ "tinyblob", new() },
5758
{ "varbinary", new() }
5859
},
59-
ordinal => $"reader.GetFieldValue<byte[]>({ordinal})"
60+
readerFn: ordinal => $"reader.GetFieldValue<byte[]>({ordinal})",
61+
readerArrayFn: ordinal => $"reader.GetFieldValue<byte[][]>({ordinal})"
6062
),
6163
["string"] = new(
6264
new()
@@ -76,7 +78,8 @@ public NpgsqlDriver(
7678
{
7779
{ "time", new(NpgsqlTypeOverride: "NpgsqlDbType.Time") }
7880
},
79-
ordinal => $"reader.GetFieldValue<TimeSpan>({ordinal})"
81+
readerFn: ordinal => $"reader.GetFieldValue<TimeSpan>({ordinal})",
82+
readerArrayFn: ordinal => $"reader.GetFieldValue<TimeSpan[]>({ordinal})"
8083
),
8184
["DateTime"] = new(
8285
new()
@@ -85,7 +88,8 @@ public NpgsqlDriver(
8588
{ "timestamp", new(NpgsqlTypeOverride: "NpgsqlDbType.Timestamp") },
8689
{ "timestamptz", new(NpgsqlTypeOverride: "NpgsqlDbType.TimestampTz") }
8790
},
88-
ordinal => $"reader.GetDateTime({ordinal})"
91+
readerFn: ordinal => $"reader.GetDateTime({ordinal})",
92+
readerArrayFn: ordinal => $"reader.GetFieldValue<DateTime[]>({ordinal})"
8993
),
9094
["JsonElement"] = new(
9195
new()
@@ -110,8 +114,8 @@ public NpgsqlDriver(
110114
{
111115
{ "int2", new(NpgsqlTypeOverride: "NpgsqlDbType.Smallint") }
112116
},
113-
ordinal => $"reader.GetInt16({ordinal})",
114-
ordinal => $"reader.GetFieldValue<short[]>({ordinal})"
117+
readerFn: ordinal => $"reader.GetInt16({ordinal})",
118+
readerArrayFn: ordinal => $"reader.GetFieldValue<short[]>({ordinal})"
115119
),
116120
["int"] = new(
117121
new()
@@ -121,15 +125,16 @@ public NpgsqlDriver(
121125
{ "int4", new(NpgsqlTypeOverride: "NpgsqlDbType.Integer") },
122126
{ "serial", new(NpgsqlTypeOverride: "NpgsqlDbType.Integer") }
123127
},
124-
ordinal => $"reader.GetInt32({ordinal})",
125-
ordinal => $"reader.GetFieldValue<int[]>({ordinal})"
128+
readerFn: ordinal => $"reader.GetInt32({ordinal})",
129+
readerArrayFn: ordinal => $"reader.GetFieldValue<int[]>({ordinal})"
126130
),
127131
["float"] = new(
128132
new()
129133
{
130134
{ "float4", new(NpgsqlTypeOverride: "NpgsqlDbType.Real") }
131135
},
132-
ordinal => $"reader.GetFloat({ordinal})"
136+
readerFn: ordinal => $"reader.GetFloat({ordinal})",
137+
readerArrayFn: ordinal => $"reader.GetFieldValue<float[]>({ordinal})"
133138
),
134139
["decimal"] = new(
135140
new()
@@ -138,29 +143,32 @@ public NpgsqlDriver(
138143
{ "decimal", new(NpgsqlTypeOverride: "NpgsqlDbType.Numeric") },
139144
{ "money", new(NpgsqlTypeOverride: "NpgsqlDbType.Money") }
140145
},
141-
ordinal => $"reader.GetDecimal({ordinal})"
146+
readerFn: ordinal => $"reader.GetDecimal({ordinal})",
147+
readerArrayFn: ordinal => $"reader.GetFieldValue<decimal[]>({ordinal})"
142148
),
143149
["double"] = new(
144150
new()
145151
{
146152
{ "float8", new(NpgsqlTypeOverride: "NpgsqlDbType.Double") }
147153
},
148-
ordinal => $"reader.GetDouble({ordinal})"
154+
readerFn: ordinal => $"reader.GetDouble({ordinal})",
155+
readerArrayFn: ordinal => $"reader.GetFieldValue<double[]>({ordinal})"
149156
),
150157
["bool"] = new(
151158
new()
152159
{
153160
{ "bool", new() },
154161
{ "boolean", new() }
155162
},
156-
ordinal => $"reader.GetBoolean({ordinal})"
163+
readerFn: ordinal => $"reader.GetBoolean({ordinal})",
164+
readerArrayFn: ordinal => $"reader.GetFieldValue<bool[]>({ordinal})"
157165
),
158166
["NpgsqlPoint"] = new(
159167
new()
160168
{
161169
{ "point", new(NpgsqlTypeOverride: "NpgsqlDbType.Point") }
162170
},
163-
ordinal => $"reader.GetFieldValue<NpgsqlPoint>({ordinal})",
171+
readerFn: ordinal => $"reader.GetFieldValue<NpgsqlPoint>({ordinal})",
164172
usingDirective: "NpgsqlTypes",
165173
sqlMapper: "RegisterNpgsqlTypeHandler<NpgsqlPoint>();"
166174
),

end2end/EndToEndScaffold/Templates/PostgresTests.cs

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -170,33 +170,87 @@ await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs
170170
[KnownTestType.PostgresArrayDataTypes] = new TestImpl
171171
{
172172
Impl = $$"""
173+
public static IEnumerable<TestCaseData> PostgresArrayTypesTestCases
174+
{
175+
get
176+
{
177+
yield return new TestCaseData(
178+
new byte[] { 0x45, 0x42 },
179+
new bool[] { true, false },
180+
new string[] { "Makeshift", "Swahili" },
181+
new int[] { 543, -4234 },
182+
new decimal[] { 1.2345678m, 2.3456789m },
183+
new DateTime[] { new DateTime(2021, 1, 1), new DateTime(2022, 2, 2) },
184+
new DateTime[] { new DateTime(2023, 3, 3), new DateTime(2024, 4, 4) }
185+
).SetName("Arrays with values");
186+
187+
yield return new TestCaseData(
188+
new byte[] { },
189+
new bool[] { },
190+
new string[] { },
191+
new int[] { },
192+
new decimal[] { },
193+
new DateTime[] { },
194+
new DateTime[] { }
195+
).SetName("Arrays with null values");
196+
197+
yield return new TestCaseData(
198+
null,
199+
null,
200+
null,
201+
null,
202+
null,
203+
null,
204+
null
205+
).SetName("Null Array Types");
206+
}
207+
}
208+
173209
[Test]
174-
[TestCase(new byte[] { 0x45, 0x42 }, new string[] { "Party", "Fight" }, new int[] { 543, -4234 })]
175-
[TestCase(new byte[] { }, new string[] { }, new int[] { })]
176-
[TestCase(null, null, null)]
210+
[TestCaseSource(nameof(PostgresArrayTypesTestCases))]
177211
public async Task TestPostgresArrayTypes(
178212
byte[] cBytea,
213+
bool[] cBooleanArray,
179214
string[] cTextArray,
180-
int[] cIntegerArray)
215+
int[] cIntegerArray,
216+
decimal[] cDecimalArray,
217+
DateTime[] cDateArray,
218+
DateTime[] cTimestampArray)
181219
{
182220
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs
183221
{
184222
CBytea = cBytea,
223+
CBooleanArray = cBooleanArray,
185224
CTextArray = cTextArray,
186-
CIntegerArray = cIntegerArray
225+
CIntegerArray = cIntegerArray,
226+
CDecimalArray = cDecimalArray,
227+
CDateArray = cDateArray,
228+
CTimestampArray = cTimestampArray
187229
});
188230
189231
var expected = new QuerySql.GetPostgresTypesRow
190232
{
191-
CBytea = cBytea,
233+
CBytea = cBytea,
234+
CBooleanArray = cBooleanArray,
192235
CTextArray = cTextArray,
193-
CIntegerArray = cIntegerArray
236+
CIntegerArray = cIntegerArray,
237+
CDecimalArray = cDecimalArray,
238+
CDateArray = cDateArray,
239+
CTimestampArray = cTimestampArray
194240
};
195241
var actual = await QuerySql.GetPostgresTypes();
196-
197-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CBytea, Is.EqualTo(expected.CBytea));
198-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CTextArray, Is.EqualTo(expected.CTextArray));
199-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CIntegerArray, Is.EqualTo(expected.CIntegerArray));
242+
AssertSingularEquals(expected, actual{{Consts.UnknownRecordValuePlaceholder}});
243+
244+
void AssertSingularEquals(QuerySql.GetPostgresTypesRow x, QuerySql.GetPostgresTypesRow y)
245+
{
246+
Assert.That(x.CBytea, Is.EqualTo(y.CBytea));
247+
Assert.That(x.CTextArray, Is.EqualTo(y.CTextArray));
248+
Assert.That(x.CIntegerArray, Is.EqualTo(y.CIntegerArray));
249+
Assert.That(x.CBooleanArray, Is.EqualTo(y.CBooleanArray));
250+
Assert.That(x.CDecimalArray, Is.EqualTo(y.CDecimalArray));
251+
Assert.That(x.CDateArray, Is.EqualTo(y.CDateArray));
252+
Assert.That(x.CTimestampArray, Is.EqualTo(y.CTimestampArray));
253+
}
200254
}
201255
"""
202256
},
@@ -329,13 +383,17 @@ public async Task TestFloatingPointCopyFrom(
329383
CMoney = cMoney
330384
};
331385
var actual = await QuerySql.GetPostgresTypesCnt();
332-
333-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.Cnt, Is.EqualTo(expected.Cnt));
334-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CReal, Is.EqualTo(expected.CReal));
335-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CDecimal, Is.EqualTo(expected.CDecimal));
336-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CNumeric, Is.EqualTo(expected.CNumeric));
337-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CDoublePrecision, Is.EqualTo(expected.CDoublePrecision));
338-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CMoney, Is.EqualTo(expected.CMoney));
386+
AssertSingularEquals(expected, actual{{Consts.UnknownRecordValuePlaceholder}});
387+
388+
void AssertSingularEquals(QuerySql.GetPostgresTypesCntRow x, QuerySql.GetPostgresTypesCntRow y)
389+
{
390+
Assert.That(x.Cnt, Is.EqualTo(y.Cnt));
391+
Assert.That(x.CReal, Is.EqualTo(y.CReal));
392+
Assert.That(x.CDecimal, Is.EqualTo(y.CDecimal));
393+
Assert.That(x.CNumeric, Is.EqualTo(y.CNumeric));
394+
Assert.That(x.CDoublePrecision, Is.EqualTo(y.CDoublePrecision));
395+
Assert.That(x.CMoney, Is.EqualTo(y.CMoney));
396+
}
339397
}
340398
"""
341399
},
@@ -375,12 +433,16 @@ public async Task TestDateTimeCopyFrom(
375433
CTimestampWithTz = cTimestampWithTz,
376434
};
377435
var actual = await QuerySql.GetPostgresTypesCnt();
378-
379-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.Cnt, Is.EqualTo(expected.Cnt));
380-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CDate, Is.EqualTo(expected.CDate));
381-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CTime, Is.EqualTo(expected.CTime));
382-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CTimestamp, Is.EqualTo(expected.CTimestamp));
383-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CTimestampWithTz, Is.EqualTo(expected.CTimestampWithTz));
436+
AssertSingularEquals(expected, actual{{Consts.UnknownRecordValuePlaceholder}});
437+
438+
void AssertSingularEquals(QuerySql.GetPostgresTypesCntRow x, QuerySql.GetPostgresTypesCntRow y)
439+
{
440+
Assert.That(x.Cnt, Is.EqualTo(y.Cnt));
441+
Assert.That(x.CDate, Is.EqualTo(y.CDate));
442+
Assert.That(x.CTime, Is.EqualTo(y.CTime));
443+
Assert.That(x.CTimestamp, Is.EqualTo(y.CTimestamp));
444+
Assert.That(x.CTimestampWithTz, Is.EqualTo(y.CTimestampWithTz));
445+
}
384446
}
385447
"""
386448
},

0 commit comments

Comments
 (0)