Skip to content

Commit 50d30d9

Browse files
fix: add Postgres JSON types
1 parent e4c9ca4 commit 50d30d9

36 files changed

Lines changed: 1137 additions & 371 deletions

Drivers/Generators/CommonGen.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public static string GetMethodParameterList(string argInterface, IEnumerable<Par
1414
: $"{argInterface} {Variable.Args.AsVarName()}")}";
1515
}
1616

17-
private Func<string, bool, bool, string>? GetWriterFn(Column column, Query query)
17+
public Func<string, bool, bool, string>? GetWriterFn(Column column, Query query)
1818
{
1919
var csharpType = dbDriver.GetCsharpTypeWithoutNullableSuffix(column, query);
2020
var writerFn = dbDriver.ColumnMappings.GetValueOrDefault(csharpType)?.WriterFn;

Drivers/NpgsqlDriver.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using Microsoft.CodeAnalysis.CSharp.Syntax;
22
using Plugin;
33
using SqlcGenCsharp.Drivers.Generators;
4-
using System;
54
using System.Collections.Generic;
65
using System.Linq;
76
using System.Text.RegularExpressions;
@@ -27,8 +26,11 @@ public NpgsqlDriver(
2726
columnMapping.DbTypes.Add(dbTypeToAdd, dbType.Value);
2827
}
2928
}
29+
CommonGen = new CommonGen(this);
3030
}
3131

32+
private CommonGen CommonGen { get; }
33+
3234
public sealed override Dictionary<string, ColumnMapping> ColumnMappings { get; } =
3335
new()
3436
{
@@ -88,7 +90,7 @@ public NpgsqlDriver(
8890
["JsonElement"] = new(
8991
new()
9092
{
91-
{ "json", new() }
93+
{ "json", new(NpgsqlTypeOverride: "NpgsqlDbType.Jsonb") }
9294
},
9395
readerFn: ordinal => $"JsonSerializer.Deserialize<JsonElement>(reader.GetString({ordinal}))",
9496
writerFn: (el, notNull, isDapper) =>
@@ -119,8 +121,7 @@ public NpgsqlDriver(
119121
{ "serial", new(NpgsqlTypeOverride: "NpgsqlDbType.Integer") }
120122
},
121123
ordinal => $"reader.GetInt32({ordinal})",
122-
ordinal => $"reader.GetFieldValue<int[]>({ordinal})",
123-
convertFunc: IntTo32
124+
ordinal => $"reader.GetFieldValue<int[]>({ordinal})"
124125
),
125126
["float"] = new(
126127
new()
@@ -401,7 +402,10 @@ string AddRowsToCopyCommand()
401402
.Select(p =>
402403
{
403404
var typeOverride = GetColumnDbTypeOverride(p.Column);
404-
var partialStmt = $"await {writerVar}.WriteAsync({rowVar}.{p.Column.Name.ToPascalCase()}";
405+
var param = $"{rowVar}.{p.Column.Name.ToPascalCase()}";
406+
var writerFn = CommonGen.GetWriterFn(p.Column, query);
407+
var paramToWrite = writerFn is null ? param : writerFn(param, p.Column.NotNull, false);
408+
var partialStmt = $"await {writerVar}.WriteAsync({paramToWrite}";
405409
return typeOverride is null
406410
? $"{partialStmt});"
407411
: $"{partialStmt}, {typeOverride});";

docs/04_Postgres.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ we consider support for the different data types separately for batch inserts an
5454
| tsvector |||
5555
| tsquery |||
5656
| uuid |||
57-
| json | | |
57+
| json | | 🚫 |
5858
| jsonb |||
5959
| jsonpath |||
6060

end2end/EndToEndScaffold/Config.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public enum KnownTestType
4949
PostgresDateTimeCopyFrom,
5050
PostgresArrayCopyFrom,
5151
PostgresGeoDataTypes,
52+
PostgresJsonDataTypes,
53+
PostgresInvalidJson,
5254

5355
ArrayAsParam,
5456
MultipleArraysAsParams,
@@ -66,6 +68,7 @@ public enum KnownTestType
6668
MySqlScopedSchemaEnum,
6769
MySqlJsonDataTypes,
6870
MySqlJsonCopyFrom,
71+
MySqlInvalidJson,
6972

7073
MySqlStringCopyFrom,
7174
MySqlIntegerCopyFrom,
@@ -108,6 +111,7 @@ internal static class Config
108111
KnownTestType.MySqlEnumDataType,
109112
KnownTestType.MySqlScopedSchemaEnum,
110113
KnownTestType.MySqlJsonDataTypes,
114+
KnownTestType.MySqlInvalidJson,
111115
KnownTestType.MySqlJsonCopyFrom,
112116
KnownTestType.MySqlDataTypesOverride,
113117

@@ -148,6 +152,7 @@ internal static class Config
148152
KnownTestType.MySqlEnumDataType,
149153
KnownTestType.MySqlScopedSchemaEnum,
150154
KnownTestType.MySqlJsonDataTypes,
155+
KnownTestType.MySqlInvalidJson,
151156
KnownTestType.MySqlJsonCopyFrom,
152157
KnownTestType.MySqlDataTypesOverride,
153158

@@ -187,6 +192,8 @@ internal static class Config
187192
KnownTestType.PostgresArrayDataTypes,
188193
KnownTestType.PostgresGeoDataTypes,
189194
KnownTestType.PostgresDataTypesOverride,
195+
KnownTestType.PostgresJsonDataTypes,
196+
KnownTestType.PostgresInvalidJson,
190197

191198
KnownTestType.PostgresStringCopyFrom,
192199
KnownTestType.PostgresIntegerCopyFrom,
@@ -223,6 +230,8 @@ internal static class Config
223230
KnownTestType.PostgresArrayDataTypes,
224231
KnownTestType.PostgresGeoDataTypes,
225232
KnownTestType.PostgresDataTypesOverride,
233+
KnownTestType.PostgresJsonDataTypes,
234+
KnownTestType.PostgresInvalidJson,
226235

227236
KnownTestType.PostgresStringCopyFrom,
228237
KnownTestType.PostgresIntegerCopyFrom,

end2end/EndToEndScaffold/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ private static string GetFileContents(string testClassName, bool isLegacyDotnet)
4141

4242
var optionalUsingPostgresTypes = config.TestNamespace.Contains("Npgsql") ? "using NpgsqlTypes;" : string.Empty;
4343
var namespaceToTest = isLegacyDotnet ? config.LegacyTestNamespace : config.TestNamespace;
44-
var optionalUsingSystemTextJson = config.TestNamespace.Contains("MySqlConnector") ? "using System.Text.Json;" : string.Empty;
44+
var optionalUsingSystemTextJson = config.TestNamespace.Contains("MySqlConnector") || config.TestNamespace.Contains("Npgsql") ? "using System.Text.Json;" : string.Empty;
4545

4646
return ParseCompilationUnit(
4747
$$"""

end2end/EndToEndScaffold/Templates/MySqlTests.cs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,8 @@ await QuerySql.InsertMysqlTypes(new QuerySql.InsertMysqlTypesArgs
272272
{
273273
Impl = $$"""
274274
[Test]
275-
[TestCase(100, "D", "\u4321", "\u2345", "Parasite", "Clockwork Orange", "Dr. Strangelove", "Interview with a Vampire", "Memento", "{\"age\": 420, \"name\": \"Dazed and Confused\"}")]
276-
[TestCase(10, null, null, null, null, null, null, null, null, null)]
275+
[TestCase(100, "D", "\u4321", "\u2345", "Parasite", "Clockwork Orange", "Dr. Strangelove", "Interview with a Vampire", "Memento")]
276+
[TestCase(10, null, null, null, null, null, null, null, null)]
277277
public async Task TestStringCopyFrom(
278278
int batchSize,
279279
string cChar,
@@ -283,8 +283,7 @@ public async Task TestStringCopyFrom(
283283
string cTinytext,
284284
string cMediumtext,
285285
string cText,
286-
string cLongtext,
287-
string cJsonStringOverride)
286+
string cLongtext)
288287
{
289288
var batchArgs = Enumerable.Range(0, batchSize)
290289
.Select(_ => new QuerySql.InsertMysqlTypesBatchArgs
@@ -296,8 +295,7 @@ public async Task TestStringCopyFrom(
296295
CTinytext = cTinytext,
297296
CMediumtext = cMediumtext,
298297
CText = cText,
299-
CLongtext = cLongtext,
300-
CJsonStringOverride = cJsonStringOverride
298+
CLongtext = cLongtext
301299
})
302300
.ToList();
303301
await QuerySql.InsertMysqlTypesBatch(batchArgs);
@@ -311,8 +309,7 @@ public async Task TestStringCopyFrom(
311309
CTinytext = cTinytext,
312310
CMediumtext = cMediumtext,
313311
CText = cText,
314-
CLongtext = cLongtext,
315-
CJsonStringOverride = cJsonStringOverride
312+
CLongtext = cLongtext
316313
};
317314
318315
var actual = await QuerySql.GetMysqlTypesCnt();
@@ -329,7 +326,6 @@ void AssertSingularEquals(QuerySql.GetMysqlTypesCntRow x, QuerySql.GetMysqlTypes
329326
Assert.That(x.CMediumtext, Is.EqualTo(y.CMediumtext));
330327
Assert.That(x.CText, Is.EqualTo(y.CText));
331328
Assert.That(x.CLongtext, Is.EqualTo(y.CLongtext));
332-
Assert.That(x.CJsonStringOverride, Is.EqualTo(y.CJsonStringOverride));
333329
}
334330
}
335331
"""
@@ -656,19 +652,22 @@ private static bool SingularEquals(QuerySql.GetFirstExtendedBioByTypeRow x, Quer
656652
[Test]
657653
[TestCase("{\"age\": 42, \"name\": \"The Hitchhiker's Guide to the Galaxy\"}")]
658654
[TestCase(null)]
659-
public async Task TestMySqlJsonDataType(string cJson)
655+
public async Task TestMySqlJsonDataType(
656+
string cJson)
660657
{
661658
JsonElement? cParsedJson = null;
662659
if (cJson != null)
663660
cParsedJson = JsonDocument.Parse(cJson).RootElement;
664661
665662
await QuerySql.InsertMysqlTypes(new QuerySql.InsertMysqlTypesArgs
666663
{
667-
CJson = cParsedJson
664+
CJson = cParsedJson,
665+
CJsonStringOverride = cJson
668666
});
669667
var expected = new QuerySql.GetMysqlTypesRow
670668
{
671-
CJson = cParsedJson
669+
CJson = cParsedJson,
670+
CJsonStringOverride = cJson
672671
};
673672
var actual = await QuerySql.GetMysqlTypes();
674673
AssertSingularEquals(expected, actual{{Consts.UnknownRecordValuePlaceholder}});
@@ -678,10 +677,25 @@ void AssertSingularEquals(QuerySql.GetMysqlTypesRow x, QuerySql.GetMysqlTypesRow
678677
Assert.That(x.CJson.HasValue, Is.EqualTo(y.CJson.HasValue));
679678
if (x.CJson.HasValue)
680679
Assert.That(x.CJson.Value.GetRawText(), Is.EqualTo(y.CJson.Value.GetRawText()));
680+
Assert.That(x.CJsonStringOverride, Is.EqualTo(y.CJsonStringOverride));
681681
}
682682
}
683683
"""
684684
},
685+
[KnownTestType.MySqlInvalidJson] = new TestImpl
686+
{
687+
Impl = $$"""
688+
[Test]
689+
public void TestMySqlInvalidJson()
690+
{
691+
Assert.ThrowsAsync<MySqlConnector.MySqlException>(async () => await
692+
QuerySql.InsertMysqlTypes(new QuerySql.InsertMysqlTypesArgs
693+
{
694+
CJsonStringOverride = "SOME INVALID JSON"
695+
}));
696+
}
697+
"""
698+
},
685699
[KnownTestType.MySqlJsonCopyFrom] = new TestImpl
686700
{
687701
Impl = $$"""

end2end/EndToEndScaffold/Templates/PostgresTests.cs

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -260,17 +260,16 @@ public async Task TestIntegerCopyFrom(
260260
CBigint = cBigint
261261
};
262262
var actual = await QuerySql.GetPostgresTypesCnt();
263+
AssertSingularEquals(expected, actual{{Consts.UnknownRecordValuePlaceholder}});
263264
264-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.Cnt, Is.EqualTo(expected.Cnt));
265-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CBoolean, Is.EqualTo(expected.CBoolean));
266-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CSmallint, Is.EqualTo(expected.CSmallint));
267-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CInteger, Is.EqualTo(expected.CInteger));
268-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CBigint, Is.EqualTo(expected.CBigint));
269-
}
270-
271-
private static void AssertSingularEquals(QuerySql.GetPostgresTypesCntRow expected, QuerySql.GetPostgresTypesCntRow actual)
272-
{
273-
265+
void AssertSingularEquals(QuerySql.GetPostgresTypesCntRow x, QuerySql.GetPostgresTypesCntRow y)
266+
{
267+
Assert.That(x.Cnt, Is.EqualTo(y.Cnt));
268+
Assert.That(x.CBoolean, Is.EqualTo(y.CBoolean));
269+
Assert.That(x.CSmallint, Is.EqualTo(y.CSmallint));
270+
Assert.That(x.CInteger, Is.EqualTo(y.CInteger));
271+
Assert.That(x.CBigint, Is.EqualTo(y.CBigint));
272+
}
274273
}
275274
"""
276275
},
@@ -364,6 +363,57 @@ public async Task TestDateTimeCopyFrom(
364363
}
365364
"""
366365
},
366+
[KnownTestType.PostgresJsonDataTypes] = new TestImpl
367+
{
368+
Impl = $$"""
369+
[Test]
370+
[TestCase("{\"name\": \"Swordfishtrombones\", \"year\": 1983}")]
371+
[TestCase(null)]
372+
public async Task TestPostgresJsonDataTypes(string cJson)
373+
{
374+
JsonElement? cParsedJson = null;
375+
if (cJson != null)
376+
cParsedJson = JsonDocument.Parse(cJson).RootElement;
377+
378+
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs
379+
{
380+
CJson = cParsedJson,
381+
CJsonStringOverride = cJson
382+
});
383+
384+
var expected = new QuerySql.GetPostgresTypesRow
385+
{
386+
CJson = cParsedJson,
387+
CJsonStringOverride = cJson
388+
};
389+
390+
var actual = await QuerySql.GetPostgresTypes();
391+
AssertSingularEquals(expected, actual{{Consts.UnknownRecordValuePlaceholder}});
392+
393+
void AssertSingularEquals(QuerySql.GetPostgresTypesRow x, QuerySql.GetPostgresTypesRow y)
394+
{
395+
Assert.That(x.CJson.HasValue, Is.EqualTo(y.CJson.HasValue));
396+
if (x.CJson.HasValue)
397+
Assert.That(x.CJson.Value.GetRawText(), Is.EqualTo(y.CJson.Value.GetRawText()));
398+
Assert.That(x.CJsonStringOverride, Is.EqualTo(y.CJsonStringOverride));
399+
}
400+
}
401+
"""
402+
},
403+
[KnownTestType.PostgresInvalidJson] = new TestImpl
404+
{
405+
Impl = $$"""
406+
[Test]
407+
public void TestPostgresInvalidJson()
408+
{
409+
Assert.ThrowsAsync<Npgsql.PostgresException>(async () => await
410+
QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs
411+
{
412+
CJsonStringOverride = "SOME INVALID JSON"
413+
}));
414+
}
415+
"""
416+
},
367417
[KnownTestType.PostgresArrayCopyFrom] = new TestImpl
368418
{
369419
Impl = $$"""

0 commit comments

Comments
 (0)