From b6c71b1c5fcf5d63f8eed62fd25b4748f12499cd Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Fri, 20 Dec 2024 00:33:07 +0100 Subject: [PATCH 1/4] Npgsql: Update README --- by-language/csharp-npgsql/README.rst | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/by-language/csharp-npgsql/README.rst b/by-language/csharp-npgsql/README.rst index ee47ee1b..82def11d 100644 --- a/by-language/csharp-npgsql/README.rst +++ b/by-language/csharp-npgsql/README.rst @@ -21,10 +21,8 @@ data provider for PostgreSQL, `crate-npgsql`_. CrateDB versions 4.2 and later work with the vanilla `Npgsql - .NET Access to PostgreSQL`_ driver without the need for a plugin. -Please note that Npgsql 5 is not supported starting with CrateDB 4.8.4, you -will need Npgsql 6 or newer. - -.NET 7, 8, and 9 are supported, .NET 3.1, 4.6, 5.0, and 6.0 may still work. +The example program in this folder is validated on .NET 8 and 9, +using Npgsql 8.x and 9.x. ***** @@ -37,38 +35,41 @@ To invoke a CrateDB instance for evaluation purposes, run:: Invoke example program:: - dotnet run --framework=net8.0 + dotnet run To connect to CrateDB Cloud, use a command like:: - dotnet run --framework=net8.0 -- \ + dotnet run -- \ --host=clustername.aks1.westeurope.azure.cratedb.net --ssl-mode=Require \ --username=foobar --password='X8F%Shn)TESTvF5ac7%eW4NM' Explore all available connection options:: - dotnet run --framework=net8.0 -- --help + dotnet run -- --help + +.. note:: + Use the ``--framework=net8.0`` option to target a specific .NET framework version. Tests ===== For running the test scenarios wrapped into a xUnit test suite, invoke:: - dotnet test --framework=net8.0 + dotnet test To generate a Cobertura code coverage report, run:: - dotnet test --framework=net8.0 --collect:"XPlat Code Coverage" + dotnet test --collect:"XPlat Code Coverage" For running the tests against a remote database, use, for example:: export CRATEDB_DSN='Host=clustername.aks1.westeurope.azure.cratedb.net;Port=5432;SSL Mode=Require;Username=foobar;Password=X8F%Shn)TESTvF5ac7%eW4NM;Database=testdrive' - dotnet test --framework=net8.0 + dotnet test For running tests selectively, use:: - dotnet test --framework=net8.0 --filter SystemQueryExample + dotnet test --filter SystemQueryExample Troubleshooting @@ -82,7 +83,7 @@ If you observe an error like this when invoking the program or test case:: please adjust ``demo.csproj`` like that:: -- net6.0;net8.0 +- net$(NETCoreAppMaximumVersion) + net6.0;net8.0;net9.0 From be0835d86cc444dba0323aab798f378344414550 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Thu, 19 Dec 2024 22:12:30 +0100 Subject: [PATCH 2/4] Npgsql: Use `EnableDynamicJson` to unlock better container type mappings Vanilla Npgsql provides good enough support to handle CrateDB's ARRAY and OBJECT types better than just plain strings. This patch demonstrates type mappings to native .NET `List` and `Dictionary` types, as well as type mappings to custom .NET POCO types. --- by-language/csharp-npgsql/BasicPoco.cs | 22 +++++ by-language/csharp-npgsql/DemoProgram.cs | 12 ++- by-language/csharp-npgsql/DemoTypes.cs | 80 ++++++++++++------- .../csharp-npgsql/tests/DemoProgramTest.cs | 80 +++++++++++++------ 4 files changed, 141 insertions(+), 53 deletions(-) create mode 100644 by-language/csharp-npgsql/BasicPoco.cs diff --git a/by-language/csharp-npgsql/BasicPoco.cs b/by-language/csharp-npgsql/BasicPoco.cs new file mode 100644 index 00000000..bde7ff96 --- /dev/null +++ b/by-language/csharp-npgsql/BasicPoco.cs @@ -0,0 +1,22 @@ +namespace demo; + +public class BasicPoco +{ + + public string? name { get; set; } + public int? age { get; set; } + + public override bool Equals(object obj) + { + var other = (BasicPoco) obj; + return name == other.name && age == other.age; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override string ToString() => "Name: " + name + " Age: " + age; + +} diff --git a/by-language/csharp-npgsql/DemoProgram.cs b/by-language/csharp-npgsql/DemoProgram.cs index b0670c27..edc50749 100644 --- a/by-language/csharp-npgsql/DemoProgram.cs +++ b/by-language/csharp-npgsql/DemoProgram.cs @@ -18,12 +18,18 @@ await Parser.Default.ParseArguments(args) var connString = $"Host={options.Host};Port={options.Port};SSL Mode={options.SslMode};" + $"Username={options.Username};Password={options.Password};Database={options.Database}"; Console.WriteLine($"Connecting to {connString}\n"); - await using var conn = new NpgsqlConnection(connString); - conn.Open(); + + var dataSourceBuilder = new NpgsqlDataSourceBuilder(connString); + dataSourceBuilder.EnableDynamicJson(); + await using var dataSource = dataSourceBuilder.Build(); + await using var conn = dataSource.OpenConnection(); + await DatabaseWorkloads.SystemQueryExample(conn); await DatabaseWorkloads.BasicConversationExample(conn); await DatabaseWorkloads.UnnestExample(conn); - await DatabaseWorkloadsMore.AllTypesExample(conn); + await DatabaseWorkloadsMore.AllTypesNativeExample(conn); + await DatabaseWorkloadsMore.ObjectPocoExample(conn); + await DatabaseWorkloadsMore.ArrayPocoExample(conn); conn.Close(); }); diff --git a/by-language/csharp-npgsql/DemoTypes.cs b/by-language/csharp-npgsql/DemoTypes.cs index 6f2ad062..865426cc 100644 --- a/by-language/csharp-npgsql/DemoTypes.cs +++ b/by-language/csharp-npgsql/DemoTypes.cs @@ -57,9 +57,9 @@ public class AllTypesRecord public class DatabaseWorkloadsMore { - public static async Task AllTypesExample(NpgsqlConnection conn) + public static async Task AllTypesNativeExample(NpgsqlConnection conn) { - Console.WriteLine("Running AllTypesExample"); + Console.WriteLine("Running AllTypesNativeExample"); // Submit DDL, create database schema. await using (var cmd = new NpgsqlCommand("DROP TABLE IF EXISTS testdrive.example", conn)) @@ -154,11 +154,8 @@ INSERT INTO testdrive.example ( cmd.Parameters.AddWithValue("timestamp_tz", "1970-01-02T00:00:00+01:00"); cmd.Parameters.AddWithValue("timestamp_notz", "1970-01-02T00:00:00"); cmd.Parameters.AddWithValue("ip", "127.0.0.1"); - cmd.Parameters.AddWithValue("array", new List{"foo", "bar"}); - // FIXME: System.NotSupportedException: Cannot resolve 'hstore' to a fully qualified datatype name. The datatype was not found in the current database info. - // https://github.com/crate/zk/issues/26 - // cmd.Parameters.AddWithValue("object", new Dictionary(){{"foo", "bar"}}); - cmd.Parameters.AddWithValue("object", """{"foo": "bar"}"""); + cmd.Parameters.AddWithValue("array", NpgsqlDbType.Json, new List{"foo", "bar"}); + cmd.Parameters.AddWithValue("object", NpgsqlDbType.Json, new Dictionary{{"foo", "bar"}}); cmd.Parameters.AddWithValue("geopoint", new List{85.43, 66.23}); // TODO: Check if `GEO_SHAPE` types can be represented by real .NET or Npgsql data types. cmd.Parameters.AddWithValue("geoshape", "POLYGON ((5 5, 10 5, 10 10, 5 10, 5 5))"); @@ -185,20 +182,23 @@ INSERT INTO testdrive.example ( } - public static async Task ContainerTypesExample(NpgsqlConnection conn) + public static async Task ProvisionPoco(NpgsqlConnection conn) { - Console.WriteLine("Running AllTypesExample"); + /*** + * Verify Npgsql POCO mapping with CrateDB. + * https://www.npgsql.org/doc/types/json.html#poco-mapping + */ + Console.WriteLine("Running ProvisionPoco"); // Submit DDL, create database schema. - await using (var cmd = new NpgsqlCommand("DROP TABLE IF EXISTS testdrive.container", conn)) + await using (var cmd = new NpgsqlCommand("DROP TABLE IF EXISTS testdrive.poco", conn)) { cmd.ExecuteNonQuery(); } await using (var cmd = new NpgsqlCommand(""" - CREATE TABLE testdrive.container ( - -- Container types - "array" ARRAY(STRING), + CREATE TABLE testdrive.poco ( + "array" ARRAY(OBJECT(DYNAMIC)), "object" OBJECT(DYNAMIC) ); """, conn)) @@ -208,7 +208,7 @@ CREATE TABLE testdrive.container ( // Insert single data point. await using (var cmd = new NpgsqlCommand(""" - INSERT INTO testdrive.container ( + INSERT INTO testdrive.poco ( "array", "object" ) VALUES ( @@ -217,32 +217,58 @@ INSERT INTO testdrive.container ( ); """, conn)) { - Console.WriteLine(cmd); - // FIXME: While doing conversations with ARRAY types works natively, - // it doesn't work for OBJECT types. - // Yet, they can be submitted as STRING in JSON format. - cmd.Parameters.AddWithValue("array", new List{"foo", "bar"}); - cmd.Parameters.AddWithValue("object", """{"foo": "bar"}"""); + cmd.Parameters.AddWithValue("object", NpgsqlDbType.Json, new BasicPoco { name = "Hotzenplotz" }); + cmd.Parameters.AddWithValue("array", NpgsqlDbType.Json, new List + { + new BasicPoco { name = "Hotzenplotz" }, + new BasicPoco { name = "Petrosilius", age = 42 }, + }); cmd.ExecuteNonQuery(); } // Flush data. - await using (var cmd = new NpgsqlCommand("REFRESH TABLE testdrive.container", conn)) + await using (var cmd = new NpgsqlCommand("REFRESH TABLE testdrive.poco", conn)) { cmd.ExecuteNonQuery(); } + } + + public static async Task ObjectPocoExample(NpgsqlConnection conn) + { + Console.WriteLine("Running ObjectPocoExample"); + + // Provision data. + await ProvisionPoco(conn); + // Query back data. - await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.container", conn)) + await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.poco", conn)) await using (var reader = cmd.ExecuteReader()) { - var dataTable = new DataTable(); - dataTable.Load(reader); - var payload = JsonConvert.SerializeObject(dataTable); - Console.WriteLine(payload); - return (DataTable) dataTable; + reader.Read(); + var obj = reader.GetFieldValue("object"); + Console.WriteLine(obj); + return obj; } + } + + public static async Task> ArrayPocoExample(NpgsqlConnection conn) + { + Console.WriteLine("Running ArrayPocoExample"); + // Provision data. + await ProvisionPoco(conn); + + // Query back data. + await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.poco", conn)) + await using (var reader = cmd.ExecuteReader()) + { + reader.Read(); + var obj = reader.GetFieldValue>("array"); + Console.WriteLine(obj[0]); + Console.WriteLine(obj[1]); + return obj; + } } } diff --git a/by-language/csharp-npgsql/tests/DemoProgramTest.cs b/by-language/csharp-npgsql/tests/DemoProgramTest.cs index c0bfa08c..707bd664 100644 --- a/by-language/csharp-npgsql/tests/DemoProgramTest.cs +++ b/by-language/csharp-npgsql/tests/DemoProgramTest.cs @@ -21,8 +21,11 @@ public DatabaseFixture() CRATEDB_DSN = $"Host=localhost;Port=5432;Username=crate;Password=;Database=testdrive"; } Console.WriteLine($"Connecting to {CRATEDB_DSN}\n"); - Db = new NpgsqlConnection(CRATEDB_DSN); - Db.Open(); + + var dataSourceBuilder = new NpgsqlDataSourceBuilder(CRATEDB_DSN); + dataSourceBuilder.EnableDynamicJson(); + using var dataSource = dataSourceBuilder.Build(); + Db = dataSource.OpenConnection(); } public void Dispose() @@ -83,12 +86,12 @@ public async Task TestUnnestExample() } [Fact] - public async Task TestAllTypesExample() + public async Task TestAllTypesNativeExample() { var conn = fixture.Db; - // Invoke database workload. - var task = DatabaseWorkloadsMore.AllTypesExample(conn); + // Provision data. + var task = DatabaseWorkloadsMore.AllTypesNativeExample(conn); var dt = await task.WaitAsync(TimeSpan.FromSeconds(0.5)); // Check results. @@ -112,12 +115,18 @@ public async Task TestAllTypesExample() Assert.Equal("127.0.0.1", row["ip"]); // Container types - // FIXME: While doing conversations with ARRAY types works natively, - // it doesn't work for OBJECT types. - // Yet, they can be submitted as STRING in JSON format. Assert.Equal(new List{"foo", "bar"}, row["array"]); Assert.Equal("""{"foo":"bar"}""", row["object"]); + // Note: While it works on the ingress side to communicate `Dictionary` types, + // this kind of equality check does not work on the egress side, + // presenting an error that indicates a different internal representation, + // or a programming error ;]. + // + // Expected: [["foo"] = "bar"] + // Actual: {"foo":"bar"} + // Assert.Equal(new Dictionary{{"foo", "bar"}}, row["object"]); + // Geospatial types // TODO: Unlock native data types? // GEO_POINT and GEO_SHAPE types can be marshalled back and forth using STRING. @@ -135,20 +144,12 @@ public async Task TestContainerTypesExample() { var conn = fixture.Db; - // Invoke database workload. - var task = DatabaseWorkloadsMore.ContainerTypesExample(conn); - var dt = await task.WaitAsync(TimeSpan.FromSeconds(0.5)); - - // Check results. - var row = dt.Rows[0]; - // FIXME: While doing conversations with ARRAY types works natively, - // it doesn't work for OBJECT types. - // Yet, they can be submitted as STRING in JSON format. - Assert.Equal(new List{"foo", "bar"}, row["array"]); - Assert.Equal("""{"foo":"bar"}""", row["object"]); + // Provision data. + var task = DatabaseWorkloadsMore.AllTypesNativeExample(conn); + await task.WaitAsync(TimeSpan.FromSeconds(0.5)); - // Run a special query indexing into ARRAY types. - await using (var cmd = new NpgsqlCommand("""SELECT "array[2]" AS foo FROM testdrive.container""", conn)) + // Run an SQL query indexing into ARRAY types. + await using (var cmd = new NpgsqlCommand("""SELECT "array[2]" AS foo FROM testdrive.example""", conn)) await using (var reader = cmd.ExecuteReader()) { var dataTable = new DataTable(); @@ -156,8 +157,8 @@ public async Task TestContainerTypesExample() Assert.Equal("bar", dataTable.Rows[0]["foo"]); } - // Run a special query indexing into OBJECT types. - await using (var cmd = new NpgsqlCommand("""SELECT "object['foo']" AS foo FROM testdrive.container""", conn)) + // Run an SQL query indexing into OBJECT types. + await using (var cmd = new NpgsqlCommand("""SELECT "object['foo']" AS foo FROM testdrive.example""", conn)) await using (var reader = cmd.ExecuteReader()) { var dataTable = new DataTable(); @@ -167,5 +168,38 @@ public async Task TestContainerTypesExample() } + [Fact] + public async Task TestObjectPocoExample() + { + var conn = fixture.Db; + + // Invoke database workload. + var task = DatabaseWorkloadsMore.ObjectPocoExample(conn); + var obj = await task.WaitAsync(TimeSpan.FromSeconds(0.5)); + + // Validate the outcome. + Assert.Equal(new BasicPoco { name = "Hotzenplotz" }, obj); + + } + + [Fact] + public async Task TestArrayPocoExample() + { + var conn = fixture.Db; + + // Invoke database workload. + var task = DatabaseWorkloadsMore.ArrayPocoExample(conn); + var obj = await task.WaitAsync(TimeSpan.FromSeconds(0.5)); + + // Validate the outcome. + var reference = new List + { + new BasicPoco { name = "Hotzenplotz" }, + new BasicPoco { name = "Petrosilius", age = 42 }, + }; + Assert.Equal(reference, obj); + + } + } } From 970c25968cc92225b582d881b8b9572c540c36d8 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Fri, 20 Dec 2024 00:32:21 +0100 Subject: [PATCH 3/4] Npgsql: Demonstrate OBJECT type mapping using `JsonDocument` CrateDB's OBJECT data type can also be communicated using .NET's native `System.Text.Json.JsonDocument` type. --- by-language/csharp-npgsql/DemoProgram.cs | 2 + by-language/csharp-npgsql/DemoTypes.cs | 79 ++++++++++++++++++- .../csharp-npgsql/tests/DemoProgramTest.cs | 14 ++++ 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/by-language/csharp-npgsql/DemoProgram.cs b/by-language/csharp-npgsql/DemoProgram.cs index edc50749..39582254 100644 --- a/by-language/csharp-npgsql/DemoProgram.cs +++ b/by-language/csharp-npgsql/DemoProgram.cs @@ -28,6 +28,8 @@ await Parser.Default.ParseArguments(args) await DatabaseWorkloads.BasicConversationExample(conn); await DatabaseWorkloads.UnnestExample(conn); await DatabaseWorkloadsMore.AllTypesNativeExample(conn); + await DatabaseWorkloadsMore.ObjectJsonDocumentExample(conn); + // await DatabaseWorkloadsMore.ArrayJsonDocumentExample(conn); await DatabaseWorkloadsMore.ObjectPocoExample(conn); await DatabaseWorkloadsMore.ArrayPocoExample(conn); conn.Close(); diff --git a/by-language/csharp-npgsql/DemoTypes.cs b/by-language/csharp-npgsql/DemoTypes.cs index 865426cc..fcbc637d 100644 --- a/by-language/csharp-npgsql/DemoTypes.cs +++ b/by-language/csharp-npgsql/DemoTypes.cs @@ -1,9 +1,8 @@ #nullable enable using System; -using System.Collections; using System.Collections.Generic; using System.Data; -using System.Reflection; +using System.Text.Json; using System.Threading.Tasks; using Newtonsoft.Json; using Npgsql; @@ -57,9 +56,9 @@ public class AllTypesRecord public class DatabaseWorkloadsMore { - public static async Task AllTypesNativeExample(NpgsqlConnection conn) + public static async Task ProvisionAllTypes(NpgsqlConnection conn) { - Console.WriteLine("Running AllTypesNativeExample"); + Console.WriteLine("Running ProvisionAllTypes"); // Submit DDL, create database schema. await using (var cmd = new NpgsqlCommand("DROP TABLE IF EXISTS testdrive.example", conn)) @@ -169,6 +168,15 @@ INSERT INTO testdrive.example ( cmd.ExecuteNonQuery(); } + } + + public static async Task AllTypesNativeExample(NpgsqlConnection conn) + { + Console.WriteLine("Running AllTypesNativeExample"); + + // Provision data. + await ProvisionAllTypes(conn); + // Query back data. await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.example", conn)) await using (var reader = cmd.ExecuteReader()) @@ -182,6 +190,69 @@ INSERT INTO testdrive.example ( } + public static async Task ObjectJsonDocumentExample(NpgsqlConnection conn) + { + Console.WriteLine("Running ObjectJsonDocumentExample"); + + // Provision data. + await ProvisionAllTypes(conn); + + // This test uses the central DDL, but a blank slate to focus on the test case at hand. + await using (var cmd = new NpgsqlCommand("DELETE FROM testdrive.example", conn)) + { + cmd.ExecuteNonQuery(); + } + + await using (var cmd = new NpgsqlCommand(""" + INSERT INTO testdrive.example ( + "object" + ) VALUES ( + @object + ) + """, conn)) + { + cmd.Parameters.AddWithValue("object", NpgsqlDbType.Json, JsonDocument.Parse("""{"foo":"bar"}""")); + cmd.ExecuteNonQuery(); + } + + // Flush data. + await using (var cmd = new NpgsqlCommand("REFRESH TABLE testdrive.example", conn)) + { + cmd.ExecuteNonQuery(); + } + + // Query back data. + await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.example", conn)) + await using (var reader = cmd.ExecuteReader()) + { + reader.Read(); + var obj = reader.GetFieldValue("object"); + Console.WriteLine(obj); + return obj; + } + } + + public static async Task> ArrayJsonDocumentExample(NpgsqlConnection conn) + { + Console.WriteLine("Running ArrayJsonDocumentExample"); + + // Provision data. + await ProvisionAllTypes(conn); + + // Query back data. + await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.example", conn)) + await using (var reader = cmd.ExecuteReader()) + { + reader.Read(); + // TODO: System.InvalidCastException: Reading as 'System.Text.Json.JsonDocument' or [1] + // is not supported for fields having DataTypeName 'character varying[]'. + // [1] `System.Collections.Generic.List`1[[System.Text.Json.JsonDocument]` + var obj = reader.GetFieldValue>("array"); + Console.WriteLine(obj); + return obj; + } + } + public static async Task ProvisionPoco(NpgsqlConnection conn) { /*** diff --git a/by-language/csharp-npgsql/tests/DemoProgramTest.cs b/by-language/csharp-npgsql/tests/DemoProgramTest.cs index 707bd664..dafa5238 100644 --- a/by-language/csharp-npgsql/tests/DemoProgramTest.cs +++ b/by-language/csharp-npgsql/tests/DemoProgramTest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; +using System.Text.Json; using System.Threading.Tasks; using Npgsql; using Xunit; @@ -168,6 +169,19 @@ public async Task TestContainerTypesExample() } + [Fact] + public async Task TestObjectJsonDocumentExample() + { + var conn = fixture.Db; + + // Invoke database workload. + var task = DatabaseWorkloadsMore.ObjectJsonDocumentExample(conn); + var obj = await task.WaitAsync(TimeSpan.FromSeconds(0.5)); + + // Validate the outcome. + Assert.Equal("""{"foo":"bar"}""", JsonSerializer.Serialize(obj)); + } + [Fact] public async Task TestObjectPocoExample() { From 8e75e66b573369ce70f3c6b46d39cfdf1b126079 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Fri, 20 Dec 2024 01:58:34 +0100 Subject: [PATCH 4/4] Npgsql: Harmonize DDL vs. data provisioning in `DemoTypes` --- by-language/csharp-npgsql/DemoProgram.cs | 12 ++- by-language/csharp-npgsql/DemoTypes.cs | 96 +++++++++---------- .../csharp-npgsql/tests/DemoProgramTest.cs | 10 +- 3 files changed, 57 insertions(+), 61 deletions(-) diff --git a/by-language/csharp-npgsql/DemoProgram.cs b/by-language/csharp-npgsql/DemoProgram.cs index 39582254..ec12a076 100644 --- a/by-language/csharp-npgsql/DemoProgram.cs +++ b/by-language/csharp-npgsql/DemoProgram.cs @@ -27,11 +27,13 @@ await Parser.Default.ParseArguments(args) await DatabaseWorkloads.SystemQueryExample(conn); await DatabaseWorkloads.BasicConversationExample(conn); await DatabaseWorkloads.UnnestExample(conn); - await DatabaseWorkloadsMore.AllTypesNativeExample(conn); - await DatabaseWorkloadsMore.ObjectJsonDocumentExample(conn); - // await DatabaseWorkloadsMore.ArrayJsonDocumentExample(conn); - await DatabaseWorkloadsMore.ObjectPocoExample(conn); - await DatabaseWorkloadsMore.ArrayPocoExample(conn); + + var dwt = new DatabaseWorkloadsTypes(conn); + await dwt.AllTypesNativeExample(); + await dwt.ObjectJsonDocumentExample(); + // await dwt.ArrayJsonDocumentExample(); + await dwt.ObjectPocoExample(); + await dwt.ArrayPocoExample(); conn.Close(); }); diff --git a/by-language/csharp-npgsql/DemoTypes.cs b/by-language/csharp-npgsql/DemoTypes.cs index fcbc637d..641a3521 100644 --- a/by-language/csharp-npgsql/DemoTypes.cs +++ b/by-language/csharp-npgsql/DemoTypes.cs @@ -53,12 +53,19 @@ public class AllTypesRecord public IList? FloatVector { get; set; } } - public class DatabaseWorkloadsMore + public class DatabaseWorkloadsTypes { - public static async Task ProvisionAllTypes(NpgsqlConnection conn) + public DatabaseWorkloadsTypes(NpgsqlConnection conn) { - Console.WriteLine("Running ProvisionAllTypes"); + this.conn = conn; + } + + private NpgsqlConnection conn; + + public async Task CreateTable() + { + Console.WriteLine("Running CreateTable"); // Submit DDL, create database schema. await using (var cmd = new NpgsqlCommand("DROP TABLE IF EXISTS testdrive.example", conn)) @@ -86,6 +93,7 @@ char CHARACTER(5), -- Container types "array" ARRAY(STRING), "object" OBJECT(DYNAMIC), + "array_object" ARRAY(OBJECT(DYNAMIC)), -- Geospatial types geopoint GEO_POINT, geoshape GEO_SHAPE, @@ -96,6 +104,11 @@ float_vector FLOAT_VECTOR(3) { cmd.ExecuteNonQuery(); } + } + + public async Task InsertRecord() + { + Console.WriteLine("Running InsertRecord"); // Insert single data point. await using (var cmd = new NpgsqlCommand(""" @@ -162,20 +175,26 @@ INSERT INTO testdrive.example ( cmd.ExecuteNonQuery(); } + await RefreshTable(); + + } + + public async Task RefreshTable() + { // Flush data. await using (var cmd = new NpgsqlCommand("REFRESH TABLE testdrive.example", conn)) { cmd.ExecuteNonQuery(); } - } - public static async Task AllTypesNativeExample(NpgsqlConnection conn) + public async Task AllTypesNativeExample() { Console.WriteLine("Running AllTypesNativeExample"); // Provision data. - await ProvisionAllTypes(conn); + await CreateTable(); + await InsertRecord(); // Query back data. await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.example", conn)) @@ -190,18 +209,12 @@ public static async Task AllTypesNativeExample(NpgsqlConnection conn) } - public static async Task ObjectJsonDocumentExample(NpgsqlConnection conn) + public async Task ObjectJsonDocumentExample() { Console.WriteLine("Running ObjectJsonDocumentExample"); // Provision data. - await ProvisionAllTypes(conn); - - // This test uses the central DDL, but a blank slate to focus on the test case at hand. - await using (var cmd = new NpgsqlCommand("DELETE FROM testdrive.example", conn)) - { - cmd.ExecuteNonQuery(); - } + await CreateTable(); await using (var cmd = new NpgsqlCommand(""" INSERT INTO testdrive.example ( @@ -216,10 +229,7 @@ INSERT INTO testdrive.example ( } // Flush data. - await using (var cmd = new NpgsqlCommand("REFRESH TABLE testdrive.example", conn)) - { - cmd.ExecuteNonQuery(); - } + await RefreshTable(); // Query back data. await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.example", conn)) @@ -232,12 +242,13 @@ INSERT INTO testdrive.example ( } } - public static async Task> ArrayJsonDocumentExample(NpgsqlConnection conn) + public async Task> ArrayJsonDocumentExample() { Console.WriteLine("Running ArrayJsonDocumentExample"); // Provision data. - await ProvisionAllTypes(conn); + await CreateTable(); + await InsertRecord(); // Query back data. await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.example", conn)) @@ -253,34 +264,18 @@ public static async Task> ArrayJsonDocumentExample(NpgsqlConn } } - public static async Task ProvisionPoco(NpgsqlConnection conn) + public async Task InsertPoco() { /*** * Verify Npgsql POCO mapping with CrateDB. * https://www.npgsql.org/doc/types/json.html#poco-mapping */ - Console.WriteLine("Running ProvisionPoco"); - - // Submit DDL, create database schema. - await using (var cmd = new NpgsqlCommand("DROP TABLE IF EXISTS testdrive.poco", conn)) - { - cmd.ExecuteNonQuery(); - } - - await using (var cmd = new NpgsqlCommand(""" - CREATE TABLE testdrive.poco ( - "array" ARRAY(OBJECT(DYNAMIC)), - "object" OBJECT(DYNAMIC) - ); - """, conn)) - { - cmd.ExecuteNonQuery(); - } + Console.WriteLine("Running InsertPoco"); // Insert single data point. await using (var cmd = new NpgsqlCommand(""" - INSERT INTO testdrive.poco ( - "array", + INSERT INTO testdrive.example ( + "array_object", "object" ) VALUES ( @array, @@ -298,22 +293,20 @@ INSERT INTO testdrive.poco ( } // Flush data. - await using (var cmd = new NpgsqlCommand("REFRESH TABLE testdrive.poco", conn)) - { - cmd.ExecuteNonQuery(); - } + await RefreshTable(); } - public static async Task ObjectPocoExample(NpgsqlConnection conn) + public async Task ObjectPocoExample() { Console.WriteLine("Running ObjectPocoExample"); // Provision data. - await ProvisionPoco(conn); + await CreateTable(); + await InsertPoco(); // Query back data. - await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.poco", conn)) + await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.example", conn)) await using (var reader = cmd.ExecuteReader()) { reader.Read(); @@ -323,19 +316,20 @@ public static async Task ObjectPocoExample(NpgsqlConnection conn) } } - public static async Task> ArrayPocoExample(NpgsqlConnection conn) + public async Task> ArrayPocoExample() { Console.WriteLine("Running ArrayPocoExample"); // Provision data. - await ProvisionPoco(conn); + await CreateTable(); + await InsertPoco(); // Query back data. - await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.poco", conn)) + await using (var cmd = new NpgsqlCommand("SELECT * FROM testdrive.example", conn)) await using (var reader = cmd.ExecuteReader()) { reader.Read(); - var obj = reader.GetFieldValue>("array"); + var obj = reader.GetFieldValue>("array_object"); Console.WriteLine(obj[0]); Console.WriteLine(obj[1]); return obj; diff --git a/by-language/csharp-npgsql/tests/DemoProgramTest.cs b/by-language/csharp-npgsql/tests/DemoProgramTest.cs index dafa5238..a84528e4 100644 --- a/by-language/csharp-npgsql/tests/DemoProgramTest.cs +++ b/by-language/csharp-npgsql/tests/DemoProgramTest.cs @@ -92,7 +92,7 @@ public async Task TestAllTypesNativeExample() var conn = fixture.Db; // Provision data. - var task = DatabaseWorkloadsMore.AllTypesNativeExample(conn); + var task = new DatabaseWorkloadsTypes(conn).AllTypesNativeExample(); var dt = await task.WaitAsync(TimeSpan.FromSeconds(0.5)); // Check results. @@ -146,7 +146,7 @@ public async Task TestContainerTypesExample() var conn = fixture.Db; // Provision data. - var task = DatabaseWorkloadsMore.AllTypesNativeExample(conn); + var task = new DatabaseWorkloadsTypes(conn).AllTypesNativeExample(); await task.WaitAsync(TimeSpan.FromSeconds(0.5)); // Run an SQL query indexing into ARRAY types. @@ -175,7 +175,7 @@ public async Task TestObjectJsonDocumentExample() var conn = fixture.Db; // Invoke database workload. - var task = DatabaseWorkloadsMore.ObjectJsonDocumentExample(conn); + var task = new DatabaseWorkloadsTypes(conn).ObjectJsonDocumentExample(); var obj = await task.WaitAsync(TimeSpan.FromSeconds(0.5)); // Validate the outcome. @@ -188,7 +188,7 @@ public async Task TestObjectPocoExample() var conn = fixture.Db; // Invoke database workload. - var task = DatabaseWorkloadsMore.ObjectPocoExample(conn); + var task = new DatabaseWorkloadsTypes(conn).ObjectPocoExample(); var obj = await task.WaitAsync(TimeSpan.FromSeconds(0.5)); // Validate the outcome. @@ -202,7 +202,7 @@ public async Task TestArrayPocoExample() var conn = fixture.Db; // Invoke database workload. - var task = DatabaseWorkloadsMore.ArrayPocoExample(conn); + var task = new DatabaseWorkloadsTypes(conn).ArrayPocoExample(); var obj = await task.WaitAsync(TimeSpan.FromSeconds(0.5)); // Validate the outcome.