Skip to content

Commit 2efe6bc

Browse files
feat: add support for JSONB Postgres field
1 parent 440817b commit 2efe6bc

25 files changed

Lines changed: 328 additions & 120 deletions

File tree

Drivers/NpgsqlDriver.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ public NpgsqlDriver(
9090
["JsonElement"] = new(
9191
new()
9292
{
93-
{ "json", new(NpgsqlTypeOverride: "NpgsqlDbType.Jsonb") }
93+
{ "json", new() },
94+
{ "jsonb", new() }
9495
},
9596
readerFn: ordinal => $"JsonSerializer.Deserialize<JsonElement>(reader.GetString({ordinal}))",
9697
writerFn: (el, notNull, isDapper) =>

docs/04_Postgres.md

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

6161
</details>

end2end/EndToEndScaffold/Templates/PostgresTests.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,16 @@ await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs
118118
CMoney = cMoney
119119
};
120120
var actual = await QuerySql.GetPostgresTypes();
121-
122-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CReal, Is.EqualTo(expected.CReal));
123-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CNumeric, Is.EqualTo(expected.CNumeric));
124-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CDecimal, Is.EqualTo(expected.CDecimal));
125-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CDoublePrecision, Is.EqualTo(expected.CDoublePrecision));
126-
Assert.That(actual{{Consts.UnknownRecordValuePlaceholder}}.CMoney, Is.EqualTo(expected.CMoney));
121+
AssertSingularEquals(expected, actual{{Consts.UnknownRecordValuePlaceholder}});
122+
123+
void AssertSingularEquals(QuerySql.GetPostgresTypesRow x, QuerySql.GetPostgresTypesRow y)
124+
{
125+
Assert.That(x.CReal, Is.EqualTo(y.CReal));
126+
Assert.That(x.CNumeric, Is.EqualTo(y.CNumeric));
127+
Assert.That(x.CDecimal, Is.EqualTo(y.CDecimal));
128+
Assert.That(x.CDoublePrecision, Is.EqualTo(y.CDoublePrecision));
129+
Assert.That(x.CMoney, Is.EqualTo(y.CMoney));
130+
}
127131
}
128132
"""
129133
},
@@ -395,12 +399,14 @@ public async Task TestPostgresJsonDataTypes(string cJson)
395399
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs
396400
{
397401
CJson = cParsedJson,
402+
CJsonb = cParsedJson,
398403
CJsonStringOverride = cJson
399404
});
400405
401406
var expected = new QuerySql.GetPostgresTypesRow
402407
{
403408
CJson = cParsedJson,
409+
CJsonb = cParsedJson,
404410
CJsonStringOverride = cJson
405411
};
406412
@@ -412,6 +418,9 @@ void AssertSingularEquals(QuerySql.GetPostgresTypesRow x, QuerySql.GetPostgresTy
412418
Assert.That(x.CJson.HasValue, Is.EqualTo(y.CJson.HasValue));
413419
if (x.CJson.HasValue)
414420
Assert.That(x.CJson.Value.GetRawText(), Is.EqualTo(y.CJson.Value.GetRawText()));
421+
Assert.That(x.CJsonb.HasValue, Is.EqualTo(y.CJsonb.HasValue));
422+
if (x.CJsonb.HasValue)
423+
Assert.That(x.CJsonb.Value.GetRawText(), Is.EqualTo(y.CJsonb.Value.GetRawText()));
415424
Assert.That(x.CJsonStringOverride, Is.EqualTo(y.CJsonStringOverride));
416425
}
417426
}

end2end/EndToEndTests/NpgsqlDapperTester.generated.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -350,11 +350,15 @@ public async Task TestPostgresFloatingPointTypes(float? cReal, decimal? cNumeric
350350
CMoney = cMoney
351351
};
352352
var actual = await QuerySql.GetPostgresTypes();
353-
Assert.That(actual.CReal, Is.EqualTo(expected.CReal));
354-
Assert.That(actual.CNumeric, Is.EqualTo(expected.CNumeric));
355-
Assert.That(actual.CDecimal, Is.EqualTo(expected.CDecimal));
356-
Assert.That(actual.CDoublePrecision, Is.EqualTo(expected.CDoublePrecision));
357-
Assert.That(actual.CMoney, Is.EqualTo(expected.CMoney));
353+
AssertSingularEquals(expected, actual);
354+
void AssertSingularEquals(QuerySql.GetPostgresTypesRow x, QuerySql.GetPostgresTypesRow y)
355+
{
356+
Assert.That(x.CReal, Is.EqualTo(y.CReal));
357+
Assert.That(x.CNumeric, Is.EqualTo(y.CNumeric));
358+
Assert.That(x.CDecimal, Is.EqualTo(y.CDecimal));
359+
Assert.That(x.CDoublePrecision, Is.EqualTo(y.CDoublePrecision));
360+
Assert.That(x.CMoney, Is.EqualTo(y.CMoney));
361+
}
358362
}
359363

360364
[Test]
@@ -664,10 +668,11 @@ public async Task TestPostgresJsonDataTypes(string cJson)
664668
JsonElement? cParsedJson = null;
665669
if (cJson != null)
666670
cParsedJson = JsonDocument.Parse(cJson).RootElement;
667-
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs { CJson = cParsedJson, CJsonStringOverride = cJson });
671+
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs { CJson = cParsedJson, CJsonb = cParsedJson, CJsonStringOverride = cJson });
668672
var expected = new QuerySql.GetPostgresTypesRow
669673
{
670674
CJson = cParsedJson,
675+
CJsonb = cParsedJson,
671676
CJsonStringOverride = cJson
672677
};
673678
var actual = await QuerySql.GetPostgresTypes();
@@ -677,6 +682,9 @@ void AssertSingularEquals(QuerySql.GetPostgresTypesRow x, QuerySql.GetPostgresTy
677682
Assert.That(x.CJson.HasValue, Is.EqualTo(y.CJson.HasValue));
678683
if (x.CJson.HasValue)
679684
Assert.That(x.CJson.Value.GetRawText(), Is.EqualTo(y.CJson.Value.GetRawText()));
685+
Assert.That(x.CJsonb.HasValue, Is.EqualTo(y.CJsonb.HasValue));
686+
if (x.CJsonb.HasValue)
687+
Assert.That(x.CJsonb.Value.GetRawText(), Is.EqualTo(y.CJsonb.Value.GetRawText()));
680688
Assert.That(x.CJsonStringOverride, Is.EqualTo(y.CJsonStringOverride));
681689
}
682690
}

end2end/EndToEndTests/NpgsqlTester.generated.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -350,11 +350,15 @@ public async Task TestPostgresFloatingPointTypes(float? cReal, decimal? cNumeric
350350
CMoney = cMoney
351351
};
352352
var actual = await QuerySql.GetPostgresTypes();
353-
Assert.That(actual.Value.CReal, Is.EqualTo(expected.CReal));
354-
Assert.That(actual.Value.CNumeric, Is.EqualTo(expected.CNumeric));
355-
Assert.That(actual.Value.CDecimal, Is.EqualTo(expected.CDecimal));
356-
Assert.That(actual.Value.CDoublePrecision, Is.EqualTo(expected.CDoublePrecision));
357-
Assert.That(actual.Value.CMoney, Is.EqualTo(expected.CMoney));
353+
AssertSingularEquals(expected, actual.Value);
354+
void AssertSingularEquals(QuerySql.GetPostgresTypesRow x, QuerySql.GetPostgresTypesRow y)
355+
{
356+
Assert.That(x.CReal, Is.EqualTo(y.CReal));
357+
Assert.That(x.CNumeric, Is.EqualTo(y.CNumeric));
358+
Assert.That(x.CDecimal, Is.EqualTo(y.CDecimal));
359+
Assert.That(x.CDoublePrecision, Is.EqualTo(y.CDoublePrecision));
360+
Assert.That(x.CMoney, Is.EqualTo(y.CMoney));
361+
}
358362
}
359363

360364
[Test]
@@ -664,10 +668,11 @@ public async Task TestPostgresJsonDataTypes(string cJson)
664668
JsonElement? cParsedJson = null;
665669
if (cJson != null)
666670
cParsedJson = JsonDocument.Parse(cJson).RootElement;
667-
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs { CJson = cParsedJson, CJsonStringOverride = cJson });
671+
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs { CJson = cParsedJson, CJsonb = cParsedJson, CJsonStringOverride = cJson });
668672
var expected = new QuerySql.GetPostgresTypesRow
669673
{
670674
CJson = cParsedJson,
675+
CJsonb = cParsedJson,
671676
CJsonStringOverride = cJson
672677
};
673678
var actual = await QuerySql.GetPostgresTypes();
@@ -677,6 +682,9 @@ void AssertSingularEquals(QuerySql.GetPostgresTypesRow x, QuerySql.GetPostgresTy
677682
Assert.That(x.CJson.HasValue, Is.EqualTo(y.CJson.HasValue));
678683
if (x.CJson.HasValue)
679684
Assert.That(x.CJson.Value.GetRawText(), Is.EqualTo(y.CJson.Value.GetRawText()));
685+
Assert.That(x.CJsonb.HasValue, Is.EqualTo(y.CJsonb.HasValue));
686+
if (x.CJsonb.HasValue)
687+
Assert.That(x.CJsonb.Value.GetRawText(), Is.EqualTo(y.CJsonb.Value.GetRawText()));
680688
Assert.That(x.CJsonStringOverride, Is.EqualTo(y.CJsonStringOverride));
681689
}
682690
}

end2end/EndToEndTestsLegacy/NpgsqlDapperTester.generated.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -350,11 +350,15 @@ public async Task TestPostgresFloatingPointTypes(float? cReal, decimal? cNumeric
350350
CMoney = cMoney
351351
};
352352
var actual = await QuerySql.GetPostgresTypes();
353-
Assert.That(actual.CReal, Is.EqualTo(expected.CReal));
354-
Assert.That(actual.CNumeric, Is.EqualTo(expected.CNumeric));
355-
Assert.That(actual.CDecimal, Is.EqualTo(expected.CDecimal));
356-
Assert.That(actual.CDoublePrecision, Is.EqualTo(expected.CDoublePrecision));
357-
Assert.That(actual.CMoney, Is.EqualTo(expected.CMoney));
353+
AssertSingularEquals(expected, actual);
354+
void AssertSingularEquals(QuerySql.GetPostgresTypesRow x, QuerySql.GetPostgresTypesRow y)
355+
{
356+
Assert.That(x.CReal, Is.EqualTo(y.CReal));
357+
Assert.That(x.CNumeric, Is.EqualTo(y.CNumeric));
358+
Assert.That(x.CDecimal, Is.EqualTo(y.CDecimal));
359+
Assert.That(x.CDoublePrecision, Is.EqualTo(y.CDoublePrecision));
360+
Assert.That(x.CMoney, Is.EqualTo(y.CMoney));
361+
}
358362
}
359363

360364
[Test]
@@ -664,10 +668,11 @@ public async Task TestPostgresJsonDataTypes(string cJson)
664668
JsonElement? cParsedJson = null;
665669
if (cJson != null)
666670
cParsedJson = JsonDocument.Parse(cJson).RootElement;
667-
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs { CJson = cParsedJson, CJsonStringOverride = cJson });
671+
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs { CJson = cParsedJson, CJsonb = cParsedJson, CJsonStringOverride = cJson });
668672
var expected = new QuerySql.GetPostgresTypesRow
669673
{
670674
CJson = cParsedJson,
675+
CJsonb = cParsedJson,
671676
CJsonStringOverride = cJson
672677
};
673678
var actual = await QuerySql.GetPostgresTypes();
@@ -677,6 +682,9 @@ void AssertSingularEquals(QuerySql.GetPostgresTypesRow x, QuerySql.GetPostgresTy
677682
Assert.That(x.CJson.HasValue, Is.EqualTo(y.CJson.HasValue));
678683
if (x.CJson.HasValue)
679684
Assert.That(x.CJson.Value.GetRawText(), Is.EqualTo(y.CJson.Value.GetRawText()));
685+
Assert.That(x.CJsonb.HasValue, Is.EqualTo(y.CJsonb.HasValue));
686+
if (x.CJsonb.HasValue)
687+
Assert.That(x.CJsonb.Value.GetRawText(), Is.EqualTo(y.CJsonb.Value.GetRawText()));
680688
Assert.That(x.CJsonStringOverride, Is.EqualTo(y.CJsonStringOverride));
681689
}
682690
}

end2end/EndToEndTestsLegacy/NpgsqlTester.generated.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -350,11 +350,15 @@ public async Task TestPostgresFloatingPointTypes(float? cReal, decimal? cNumeric
350350
CMoney = cMoney
351351
};
352352
var actual = await QuerySql.GetPostgresTypes();
353-
Assert.That(actual.CReal, Is.EqualTo(expected.CReal));
354-
Assert.That(actual.CNumeric, Is.EqualTo(expected.CNumeric));
355-
Assert.That(actual.CDecimal, Is.EqualTo(expected.CDecimal));
356-
Assert.That(actual.CDoublePrecision, Is.EqualTo(expected.CDoublePrecision));
357-
Assert.That(actual.CMoney, Is.EqualTo(expected.CMoney));
353+
AssertSingularEquals(expected, actual);
354+
void AssertSingularEquals(QuerySql.GetPostgresTypesRow x, QuerySql.GetPostgresTypesRow y)
355+
{
356+
Assert.That(x.CReal, Is.EqualTo(y.CReal));
357+
Assert.That(x.CNumeric, Is.EqualTo(y.CNumeric));
358+
Assert.That(x.CDecimal, Is.EqualTo(y.CDecimal));
359+
Assert.That(x.CDoublePrecision, Is.EqualTo(y.CDoublePrecision));
360+
Assert.That(x.CMoney, Is.EqualTo(y.CMoney));
361+
}
358362
}
359363

360364
[Test]
@@ -664,10 +668,11 @@ public async Task TestPostgresJsonDataTypes(string cJson)
664668
JsonElement? cParsedJson = null;
665669
if (cJson != null)
666670
cParsedJson = JsonDocument.Parse(cJson).RootElement;
667-
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs { CJson = cParsedJson, CJsonStringOverride = cJson });
671+
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs { CJson = cParsedJson, CJsonb = cParsedJson, CJsonStringOverride = cJson });
668672
var expected = new QuerySql.GetPostgresTypesRow
669673
{
670674
CJson = cParsedJson,
675+
CJsonb = cParsedJson,
671676
CJsonStringOverride = cJson
672677
};
673678
var actual = await QuerySql.GetPostgresTypes();
@@ -677,6 +682,9 @@ void AssertSingularEquals(QuerySql.GetPostgresTypesRow x, QuerySql.GetPostgresTy
677682
Assert.That(x.CJson.HasValue, Is.EqualTo(y.CJson.HasValue));
678683
if (x.CJson.HasValue)
679684
Assert.That(x.CJson.Value.GetRawText(), Is.EqualTo(y.CJson.Value.GetRawText()));
685+
Assert.That(x.CJsonb.HasValue, Is.EqualTo(y.CJsonb.HasValue));
686+
if (x.CJsonb.HasValue)
687+
Assert.That(x.CJsonb.Value.GetRawText(), Is.EqualTo(y.CJsonb.Value.GetRawText()));
680688
Assert.That(x.CJsonStringOverride, Is.EqualTo(y.CJsonStringOverride));
681689
}
682690
}

examples/NpgsqlDapperExample/Models.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public class PostgresType
4141
public string? CText { get; init; }
4242
public JsonElement? CJson { get; init; }
4343
public JsonElement? CJsonStringOverride { get; init; }
44+
public JsonElement? CJsonb { get; init; }
4445
public string[]? CTextArray { get; init; }
4546
public int[]? CIntegerArray { get; init; }
4647
};

examples/NpgsqlDapperExample/QuerySql.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ public async Task<List<GetAuthorsByBookNameRow>> GetAuthorsByBookName(GetAuthors
518518
}
519519
}
520520

521-
private const string InsertPostgresTypesSql = "INSERT INTO postgres_types(c_boolean, c_bit, c_smallint, c_integer, c_bigint, c_real, c_numeric, c_decimal, c_double_precision, c_money, c_date, c_time, c_timestamp, c_timestamp_with_tz, c_char, c_varchar, c_character_varying, c_bpchar, c_text, c_json, c_json_string_override, c_bytea, c_text_array, c_integer_array) VALUES ( @c_boolean, @c_bit, @c_smallint, @c_integer, @c_bigint, @c_real, @c_numeric, @c_decimal, @c_double_precision, @c_money, @c_date, @c_time, @c_timestamp, @c_timestamp_with_tz, @c_char, @c_varchar, @c_character_varying, @c_bpchar, @c_text, @c_json :: json, @c_json_string_override :: json, @c_bytea, @c_text_array, @c_integer_array ) ";
521+
private const string InsertPostgresTypesSql = "INSERT INTO postgres_types(c_boolean, c_bit, c_smallint, c_integer, c_bigint, c_real, c_numeric, c_decimal, c_double_precision, c_money, c_date, c_time, c_timestamp, c_timestamp_with_tz, c_char, c_varchar, c_character_varying, c_bpchar, c_text, c_json, c_json_string_override, c_jsonb, c_bytea, c_text_array, c_integer_array) VALUES ( @c_boolean, @c_bit, @c_smallint, @c_integer, @c_bigint, @c_real, @c_numeric, @c_decimal, @c_double_precision, @c_money, @c_date, @c_time, @c_timestamp, @c_timestamp_with_tz, @c_char, @c_varchar, @c_character_varying, @c_bpchar, @c_text, @c_json :: json, @c_json_string_override :: json, @c_jsonb :: jsonb, @c_bytea, @c_text_array, @c_integer_array ) ";
522522
public class InsertPostgresTypesArgs
523523
{
524524
public bool? CBoolean { get; init; }
@@ -542,6 +542,7 @@ public class InsertPostgresTypesArgs
542542
public string? CText { get; init; }
543543
public JsonElement? CJson { get; init; }
544544
public string? CJsonStringOverride { get; init; }
545+
public JsonElement? CJsonb { get; init; }
545546
public byte[]? CBytea { get; init; }
546547
public string[]? CTextArray { get; init; }
547548
public int[]? CIntegerArray { get; init; }
@@ -570,6 +571,7 @@ public async Task InsertPostgresTypes(InsertPostgresTypesArgs args)
570571
queryParams.Add("c_text", args.CText);
571572
queryParams.Add("c_json", args.CJson.HasValue ? args.CJson.Value.GetRawText() : null);
572573
queryParams.Add("c_json_string_override", args.CJsonStringOverride);
574+
queryParams.Add("c_jsonb", args.CJsonb.HasValue ? args.CJsonb.Value.GetRawText() : null);
573575
queryParams.Add("c_bytea", args.CBytea);
574576
queryParams.Add("c_text_array", args.CTextArray);
575577
queryParams.Add("c_integer_array", args.CIntegerArray);
@@ -652,7 +654,7 @@ public async Task InsertPostgresTypesBatch(List<InsertPostgresTypesBatchArgs> ar
652654
}
653655
}
654656

655-
private const string GetPostgresTypesSql = "SELECT c_boolean, c_bit, c_smallint, c_integer, c_bigint, c_decimal, c_numeric, c_real, c_double_precision, c_money, c_date, c_time, c_timestamp, c_timestamp_with_tz, c_char, c_varchar, c_character_varying, c_bpchar, c_bytea, c_text, c_json, c_json_string_override, c_text_array, c_integer_array FROM postgres_types LIMIT 1";
657+
private const string GetPostgresTypesSql = "SELECT c_boolean, c_bit, c_smallint, c_integer, c_bigint, c_decimal, c_numeric, c_real, c_double_precision, c_money, c_date, c_time, c_timestamp, c_timestamp_with_tz, c_char, c_varchar, c_character_varying, c_bpchar, c_bytea, c_text, c_json, c_json_string_override, c_jsonb, c_text_array, c_integer_array FROM postgres_types LIMIT 1";
656658
public class GetPostgresTypesRow
657659
{
658660
public bool? CBoolean { get; init; }
@@ -677,6 +679,7 @@ public class GetPostgresTypesRow
677679
public string? CText { get; init; }
678680
public JsonElement? CJson { get; init; }
679681
public string? CJsonStringOverride { get; init; }
682+
public JsonElement? CJsonb { get; init; }
680683
public string[]? CTextArray { get; init; }
681684
public int[]? CIntegerArray { get; init; }
682685
};

0 commit comments

Comments
 (0)