From 1a36f4a42c3bbcf27786f6d505274b928932c307 Mon Sep 17 00:00:00 2001 From: Rishabh Verma Date: Wed, 2 Mar 2022 10:32:26 +0530 Subject: [PATCH] First draft of PostgreSQL samples and tests --- .../AddColumnAsyncPostgreTest.cs | 43 ++++++ .../AddStoringIndexAsyncPostgreTest.cs | 37 +++++ .../CastDataTypesAsyncPostgreTest.cs | 47 +++++++ .../ConnectToDatabaseAsyncPostgreTest.cs | 40 ++++++ .../CreateDatabaseAsyncPostgreTest.cs | 47 +++++++ ...CreateInterleavedTablesAsyncPostgreTest.cs | 46 +++++++ .../ExecutePartitionedDmlAsyncPostgreTest.cs | 50 +++++++ .../GetInformationSchemaAsyncPostgreTest.cs | 59 ++++++++ ...entifierCaseSensitivityAsyncPostgreTest.cs | 42 ++++++ .../InsertUsingBatchDmlAsyncPostgreTest.cs | 40 ++++++ ...tUsingDmlWithParametersAsyncPostgreTest.cs | 40 ++++++ .../OrderNullsAsyncPostgreTest.cs | 64 +++++++++ .../QueryUsingParametersAsyncPostgreTest.cs | 51 +++++++ .../Spanner.Samples.Tests/SpannerFixture.cs | 56 +++++++- .../UpdateUsingDmlAsyncPostgreTest.cs | 48 +++++++ .../UseFunctionAsyncPostgreTest.cs | 40 ++++++ .../UsePgNumericDataTypeAsyncPostgreTest.cs | 43 ++++++ .../Spanner.Samples/AddColumnAsyncPostgre.cs | 36 +++++ .../AddStoringIndexAsyncPostgre.cs | 34 +++++ .../CastDataTypesAsyncPostgre.cs | 67 +++++++++ .../ConnectToDatabaseAsyncPostgre.cs | 36 +++++ .../CreateDatabaseAsyncPostgre.cs | 87 ++++++++++++ .../CreateInterleavedTablesAsyncPostgre.cs | 68 +++++++++ .../ExecutePartitionedDmlAsyncPostgre.cs | 43 ++++++ .../GetInformationSchemaAsyncPostgre.cs | 73 ++++++++++ .../IdentifierCaseSensitivityAsyncPostgre.cs | 127 +++++++++++++++++ .../InsertUsingBatchDmlAsyncPostgre.cs | 55 ++++++++ ...nsertUsingDmlWithParametersAsyncPostgre.cs | 45 ++++++ .../Spanner.Samples/OrderNullsAsyncPostgre.cs | 129 ++++++++++++++++++ .../QueryUsingParametersAsyncPostgre.cs | 66 +++++++++ .../Spanner.Samples/Spanner.Samples.csproj | 2 +- .../UpdateUsingDmlAsyncPostgre.cs | 42 ++++++ .../UseFunctionAsyncPostgre.cs | 39 ++++++ .../UsePgNumericDataTypeAsyncPostgre.cs | 128 +++++++++++++++++ 34 files changed, 1868 insertions(+), 2 deletions(-) create mode 100644 spanner/api/Spanner.Samples.Tests/AddColumnAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/AddStoringIndexAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/CastDataTypesAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/ConnectToDatabaseAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/CreateDatabaseAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/CreateInterleavedTablesAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/ExecutePartitionedDmlAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/GetInformationSchemaAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/IdentifierCaseSensitivityAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/InsertUsingBatchDmlAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/InsertUsingDmlWithParametersAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/OrderNullsAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/QueryUsingParametersAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/UpdateUsingDmlAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/UseFunctionAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples.Tests/UsePgNumericDataTypeAsyncPostgreTest.cs create mode 100644 spanner/api/Spanner.Samples/AddColumnAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/AddStoringIndexAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/CastDataTypesAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/ConnectToDatabaseAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/CreateDatabaseAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/CreateInterleavedTablesAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/ExecutePartitionedDmlAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/GetInformationSchemaAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/IdentifierCaseSensitivityAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/InsertUsingBatchDmlAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/InsertUsingDmlWithParametersAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/OrderNullsAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/QueryUsingParametersAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/UpdateUsingDmlAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/UseFunctionAsyncPostgre.cs create mode 100644 spanner/api/Spanner.Samples/UsePgNumericDataTypeAsyncPostgre.cs diff --git a/spanner/api/Spanner.Samples.Tests/AddColumnAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/AddColumnAsyncPostgreTest.cs new file mode 100644 index 00000000000..c6332432eb8 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/AddColumnAsyncPostgreTest.cs @@ -0,0 +1,43 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class AddColumnAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly AddColumnAsyncPostgreSample _sample; + + public AddColumnAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new AddColumnAsyncPostgreSample(); + } + + [Fact] + public async Task TestAddColumnAsyncPostgre() + { + // Arrange. + // Create a new database to avoid flakiness. + var databaseId = $"my-db-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"; + await _spannerFixture.CreatePostgreSqlDatabaseAsync(databaseId); + + //Act. + await _sample.AddColumnAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, databaseId); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/AddStoringIndexAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/AddStoringIndexAsyncPostgreTest.cs new file mode 100644 index 00000000000..f032122e0c6 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/AddStoringIndexAsyncPostgreTest.cs @@ -0,0 +1,37 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class AddStoringIndexAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly AddStoringIndexAsyncPostgreSample _sample; + + public AddStoringIndexAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new AddStoringIndexAsyncPostgreSample(); + } + + [Fact] + public async Task TestAddStoringIndexAsyncPostgre() + { + //Act. + await _sample.AddStoringIndexAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/CastDataTypesAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/CastDataTypesAsyncPostgreTest.cs new file mode 100644 index 00000000000..b0f7315fff9 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/CastDataTypesAsyncPostgreTest.cs @@ -0,0 +1,47 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class CastDataTypesAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly CastDataTypesAsyncPostgreSample _sample; + + public CastDataTypesAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new CastDataTypesAsyncPostgreSample(); + } + + [Fact] + public async Task TestCastDataTypesAsyncPostgre() + { + // Act. + var result = await _sample.CastDataTypesAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + //Assert. + Assert.Equal("1", result.String); + Assert.Equal(2, result.Integer); + Assert.Equal(3, result.Decimal); + Assert.Equal("34", BitConverter.ToString(result.Bytes)); + Assert.Equal(5.00, result.Float); + Assert.True(result.Bool); + Assert.Equal("2021-11-03 09:35:01Z", result.Timestamp.ToString("u")); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/ConnectToDatabaseAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/ConnectToDatabaseAsyncPostgreTest.cs new file mode 100644 index 00000000000..83ee91f0673 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/ConnectToDatabaseAsyncPostgreTest.cs @@ -0,0 +1,40 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class ConnectToDatabaseAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly ConnectToDatabaseAsyncPostgreSample _sample; + + public ConnectToDatabaseAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new ConnectToDatabaseAsyncPostgreSample(); + } + + [Fact] + public async Task TestConnectToDatabaseAsyncPostgre() + { + // Act. + var result = await _sample.ConnectToDatabaseAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + //Assert. + Assert.Equal($"Hello from Spanner PostgreSQL!", result); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/CreateDatabaseAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/CreateDatabaseAsyncPostgreTest.cs new file mode 100644 index 00000000000..d575c0db183 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/CreateDatabaseAsyncPostgreTest.cs @@ -0,0 +1,47 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Google.Cloud.Spanner.Admin.Database.V1; +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class CreateDatabaseAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly CreateDatabaseAsyncPostgreSample _sample; + + public CreateDatabaseAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new CreateDatabaseAsyncPostgreSample(); + } + + [Fact] + public async Task TestCreateDatabaseAsyncPostgre() + { + // Arrange. + var databaseId = $"my-db-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"; + + // Act. + await _sample.CreateDatabaseAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, databaseId); + + // Assert. + var databases = _spannerFixture.GetDatabases(); + Assert.Contains(databases, d => d.DatabaseName.DatabaseId == databaseId && d.DatabaseDialect == DatabaseDialect.Postgresql); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/CreateInterleavedTablesAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/CreateInterleavedTablesAsyncPostgreTest.cs new file mode 100644 index 00000000000..a231ab18bd0 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/CreateInterleavedTablesAsyncPostgreTest.cs @@ -0,0 +1,46 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class CreateInterleavedTablesAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly CreateInterleavedTablesAsyncPostgreSample _sample; + + public CreateInterleavedTablesAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new CreateInterleavedTablesAsyncPostgreSample(); + } + + [Fact] + public async Task TestCreateInterleavedTablesAsyncPostgre() + { + // Act. + await _sample.CreateInterleavedTablesAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + //Assert. + var tables = await _spannerFixture.ListTableNamesAsync(_spannerFixture.PostgreSqlDatabaseId); + Assert.Collection(tables.OrderBy(j => j), + item1 => Assert.Equal("albums", item1), + item2 => Assert.Equal("authors", item2), + item3 => Assert.Equal("books", item3), + item4 => Assert.Equal("singers", item4)); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/ExecutePartitionedDmlAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/ExecutePartitionedDmlAsyncPostgreTest.cs new file mode 100644 index 00000000000..877ff29328e --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/ExecutePartitionedDmlAsyncPostgreTest.cs @@ -0,0 +1,50 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class ExecutePartitionedDmlAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly ExecutePartitionedDmlAsyncPostgreSample _sample; + + public ExecutePartitionedDmlAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new ExecutePartitionedDmlAsyncPostgreSample(); + } + + [Fact] + public async Task TestExecutePartitionedDmlAsyncPostgre() + { + // Arrange. + var databaseId = $"my-db-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"; + + // Create Database. + var createDatabaseAsyncPostgreSample = new CreateDatabaseAsyncPostgreSample(); + await createDatabaseAsyncPostgreSample.CreateDatabaseAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, databaseId); + + // Insert data. + var insertDataSample = new InsertUsingDmlWithParametersAsyncPostgreSample(); + await insertDataSample.InsertUsingDmlWithParametersAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, databaseId); + + // Act. + var result = await _sample.ExecutePartitionedDmlAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, databaseId); + Assert.Equal(2, result); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/GetInformationSchemaAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/GetInformationSchemaAsyncPostgreTest.cs new file mode 100644 index 00000000000..52894325cf4 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/GetInformationSchemaAsyncPostgreTest.cs @@ -0,0 +1,59 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class GetInformationSchemaAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly GetInformationSchemaAsyncPostgreSample _sample; + + public GetInformationSchemaAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new GetInformationSchemaAsyncPostgreSample(); + } + + [Fact] + public async Task TestGetInformationSchemaAsyncPostgre() + { + // Arrange. Lets create a brand new database, so that tests are not flaky. + var databaseId = $"my-db-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"; + var createDatabaseSample = new CreateDatabaseAsyncPostgreSample(); + await createDatabaseSample.CreateDatabaseAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, databaseId); + + // Act. + var result = await _sample.GetInformationSchemaAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, databaseId); + + //Assert. + Assert.Collection(result.OrderBy(j => j.Name), + item1 => + { + Assert.Equal("public", item1.Schema); + Assert.Equal("albums", item1.Name); + Assert.Equal("null", item1.UserDefinedType); + }, + item2 => + { + Assert.Equal("public", item2.Schema); + Assert.Equal("singers", item2.Name); + Assert.Equal("null", item2.UserDefinedType); + }); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/IdentifierCaseSensitivityAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/IdentifierCaseSensitivityAsyncPostgreTest.cs new file mode 100644 index 00000000000..8c128bbaee9 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/IdentifierCaseSensitivityAsyncPostgreTest.cs @@ -0,0 +1,42 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class IdentifierCaseSensitivityAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly IdentifierCaseSensitivityAsyncPostgreSample _sample; + + public IdentifierCaseSensitivityAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new IdentifierCaseSensitivityAsyncPostgreSample(); + } + + [Fact] + public async Task TestIdentifierCaseSensitivityAsyncPostgre() + { + // Act. + var count = await _sample.IdentifierCaseSensitivityAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + //Assert. + // The last insert operation inserts one row. If we reach till the last insert statement without any issue, + // row inserted count of one should be returned. + Assert.Equal(1, count); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/InsertUsingBatchDmlAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/InsertUsingBatchDmlAsyncPostgreTest.cs new file mode 100644 index 00000000000..38c11cd190d --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/InsertUsingBatchDmlAsyncPostgreTest.cs @@ -0,0 +1,40 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class InsertUsingBatchDmlAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly InsertUsingBatchDmlAsyncPostgreSample _sample; + + public InsertUsingBatchDmlAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new InsertUsingBatchDmlAsyncPostgreSample(); + } + + [Fact] + public async Task TestInsertUsingBatchDmlAsyncPostgre() + { + // Act. + var count = await _sample.InsertUsingBatchDmlAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + // Assert. + Assert.Equal(3, count); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/InsertUsingDmlWithParametersAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/InsertUsingDmlWithParametersAsyncPostgreTest.cs new file mode 100644 index 00000000000..46fec6b1ad2 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/InsertUsingDmlWithParametersAsyncPostgreTest.cs @@ -0,0 +1,40 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class InsertUsingDmlWithParametersAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly InsertUsingDmlWithParametersAsyncPostgreSample _sample; + + public InsertUsingDmlWithParametersAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new InsertUsingDmlWithParametersAsyncPostgreSample(); + } + + [Fact] + public async Task TestInsertUsingDmlWithParametersAsyncPostgre() + { + // Act. + var count = await _sample.InsertUsingDmlWithParametersAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + // Assert. + Assert.Equal(2, count); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/OrderNullsAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/OrderNullsAsyncPostgreTest.cs new file mode 100644 index 00000000000..3244b3999f1 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/OrderNullsAsyncPostgreTest.cs @@ -0,0 +1,64 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class OrderNullsAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly OrderNullsAsyncPostgreSample _sample; + + public OrderNullsAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new OrderNullsAsyncPostgreSample(); + } + + [Fact] + public async Task TestOrderNullsAsyncPostgre() + { + // Arrange. + var databaseId = $"my-db-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"; + await _spannerFixture.CreateEmptyPostgreSqlDatabaseAsync(databaseId); + + // Act. + var result = await _sample.OrderNullsAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, databaseId); + + //Assert. + Assert.Collection(result, + // Alice, Bruce, NULL. + item1 => Assert.Equal("Alice", item1), + item2 => Assert.Equal("Bruce", item2), + item3 => Assert.Equal("NULL", item3), + + // NULL, Bruce, Alice. + item4 => Assert.Equal("NULL", item4), + item5 => Assert.Equal("Bruce", item5), + item6 => Assert.Equal("Alice", item6), + + // NULL, Alice, Bruce. + item7 => Assert.Equal("NULL", item7), + item8 => Assert.Equal("Alice", item8), + item9 => Assert.Equal("Bruce", item9), + + // Bruce, Alice, NULL. + item10 => Assert.Equal("Alice", item10), + item11 => Assert.Equal("Bruce", item11), + item12 => Assert.Equal("NULL", item12)); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/QueryUsingParametersAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/QueryUsingParametersAsyncPostgreTest.cs new file mode 100644 index 00000000000..9ae23f581e5 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/QueryUsingParametersAsyncPostgreTest.cs @@ -0,0 +1,51 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class QueryUsingParametersAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly QueryUsingParametersAsyncPostgreSample _sample; + + public QueryUsingParametersAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new QueryUsingParametersAsyncPostgreSample(); + } + + [Fact] + public async Task TestQueryUsingParametersAsyncPostgre() + { + //Arrange. + // Create database. + var databaseId = $"my-db-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"; + var createDatabaseSample = new CreateDatabaseAsyncPostgreSample(); + await createDatabaseSample.CreateDatabaseAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, databaseId); + + // Insert data. + var insertDataSample = new InsertUsingDmlWithParametersAsyncPostgreSample(); + await insertDataSample.InsertUsingDmlWithParametersAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, databaseId); + + // Act. + var result = await _sample.QueryUsingParametersAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, databaseId); + + //Assert. + Assert.Single(result); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/SpannerFixture.cs b/spanner/api/Spanner.Samples.Tests/SpannerFixture.cs index 6298ac90ca2..2f811e16d6b 100644 --- a/spanner/api/Spanner.Samples.Tests/SpannerFixture.cs +++ b/spanner/api/Spanner.Samples.Tests/SpannerFixture.cs @@ -30,11 +30,12 @@ [CollectionDefinition(nameof(SpannerFixture))] public class SpannerFixture : IAsyncLifetime, ICollectionFixture -{ +{ public string ProjectId { get; } = Environment.GetEnvironmentVariable("GOOGLE_PROJECT_ID"); // Allow environment variables to override the default instance and database names. public string InstanceId { get; } = Environment.GetEnvironmentVariable("TEST_SPANNER_INSTANCE") ?? "my-instance"; public string DatabaseId { get; } = Environment.GetEnvironmentVariable("TEST_SPANNER_DATABASE") ?? $"my-db-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"; + public string PostgreSqlDatabaseId { get; } = Environment.GetEnvironmentVariable("TEST_SPANNER_POSTGRESQL_DATABASE") ?? $"my-db-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"; public string BackupDatabaseId { get; } = "my-test-database"; public string BackupId { get; } = "my-test-database-backup"; public string ToBeCancelledBackupId { get; } = $"my-backup-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"; @@ -91,6 +92,7 @@ public async Task InitializeAsync() await DeleteStaleInstancesAsync(); await InitializeDatabaseAsync(); await InitializeBackupAsync(); + await InitializePostgreSqlDatabaseAsync(); // Create encryption key for creating an encrypted database and optionally backing up and restoring an encrypted database. await InitializeEncryptionKeys(); @@ -100,6 +102,46 @@ public async Task InitializeAsync() } } + public async Task> ListTableNamesAsync(string databaseId) + { + string connectionString = $"Data Source=projects/{ProjectId}/instances/{InstanceId}/databases/{databaseId}"; + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + var command = connection.CreateSelectCommand("SELECT table_name FROM INFORMATION_SCHEMA.tables WHERE table_schema='public' OR table_schema=''"); + + var tableNames = new List(); + using var reader = await command.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + tableNames.Add(reader.GetFieldValue("table_name")); + } + + return tableNames; + } + + public async Task CreateEmptyPostgreSqlDatabaseAsync(string databaseId) + { + DatabaseAdminClient databaseAdminClient = await DatabaseAdminClient.CreateAsync(); + + // Create the CreateDatabaseRequest with PostgreSQL dialect and execute it. + // There cannot be Extra DDL statements while creating PostgreSQL. + var createDatabaseRequest = new CreateDatabaseRequest + { + ParentAsInstanceName = InstanceName.FromProjectInstance(ProjectId, InstanceId), + CreateStatement = $"CREATE DATABASE \"{databaseId}\"", + DatabaseDialect = DatabaseDialect.Postgresql + }; + + var createOperation = await databaseAdminClient.CreateDatabaseAsync(createDatabaseRequest); + + var completedResponse = await createOperation.PollUntilCompletedAsync(); + if (completedResponse.IsFaulted) + { + throw completedResponse.Exception; + } + } + public Task DisposeAsync() { DeleteInstance(InstanceIdWithProcessingUnits); @@ -388,6 +430,18 @@ private async Task InitializeBackupAsync() } } + private async Task InitializePostgreSqlDatabaseAsync() + { + await CreatePostgreSqlDatabaseAsync(PostgreSqlDatabaseId); + } + + public async Task CreatePostgreSqlDatabaseAsync(string databaseId) + { + // Create PostgreSQL database. + CreateDatabaseAsyncPostgreSample createDatabaseAsyncSample = new CreateDatabaseAsyncPostgreSample(); + await createDatabaseAsyncSample.CreateDatabaseAsyncPostgre(ProjectId, InstanceId, databaseId); + } + public async Task CreateVenuesTableAndInsertDataAsync(string databaseId) { // Create connection to Cloud Spanner. diff --git a/spanner/api/Spanner.Samples.Tests/UpdateUsingDmlAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/UpdateUsingDmlAsyncPostgreTest.cs new file mode 100644 index 00000000000..fc9e7697722 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/UpdateUsingDmlAsyncPostgreTest.cs @@ -0,0 +1,48 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class UpdateUsingDmlAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly UpdateUsingDmlAsyncPostgreSample _sample; + + public UpdateUsingDmlAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new UpdateUsingDmlAsyncPostgreSample(); + } + + [Fact] + public async Task TestUpdateUsingDmlAsyncPostgre() + { + // Arrange. + var databaseId = $"my-db-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"; + await _spannerFixture.CreatePostgreSqlDatabaseAsync(databaseId); + + var insertData = new InsertUsingBatchDmlAsyncPostgreSample(); + await insertData.InsertUsingBatchDmlAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, databaseId); + + // Act. + var count = await _sample.UpdateUsingDmlAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + // Assert. + Assert.Equal(1, count); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/UseFunctionAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/UseFunctionAsyncPostgreTest.cs new file mode 100644 index 00000000000..4ed737bbe12 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/UseFunctionAsyncPostgreTest.cs @@ -0,0 +1,40 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class UseFunctionAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly UseFunctionAsyncPostgreSample _sample; + + public UseFunctionAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new UseFunctionAsyncPostgreSample(); + } + + [Fact] + public async Task TestUseFunctionAsyncPostgre() + { + // Act. + var result = await _sample.UseFunctionAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + // Assert. + Assert.Contains("2010-09-13T04:32:03Z", result); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/UsePgNumericDataTypeAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/UsePgNumericDataTypeAsyncPostgreTest.cs new file mode 100644 index 00000000000..115e3936bc1 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/UsePgNumericDataTypeAsyncPostgreTest.cs @@ -0,0 +1,43 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class UsePgNumericDataTypeAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly UsePgNumericDataTypeAsyncPostgreSample _sample; + + public UsePgNumericDataTypeAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new UsePgNumericDataTypeAsyncPostgreSample(); + } + + [Fact] + public async Task TestUsePgNumericDataTypeAsyncPostgre() + { + // Act. + var result = await _sample.UsePgNumericDataTypeAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + //Assert. + Assert.Collection(result, + item1 => Assert.Contains("3150.25", item1.Revenue), + item2 => Assert.Contains("NaN", item2.Revenue), + item3 => Assert.Contains("", item3.Revenue)); + } +} diff --git a/spanner/api/Spanner.Samples/AddColumnAsyncPostgre.cs b/spanner/api/Spanner.Samples/AddColumnAsyncPostgre.cs new file mode 100644 index 00000000000..f8c772f018e --- /dev/null +++ b/spanner/api/Spanner.Samples/AddColumnAsyncPostgre.cs @@ -0,0 +1,36 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_add_column] + +using Google.Cloud.Spanner.Data; +using System; +using System.Threading.Tasks; + +public class AddColumnAsyncPostgreSample +{ + public async Task AddColumnAsyncPostgre(string projectId, string instanceId, string databaseId) + { + // PostgreSQL database with Singers table already exists. + // Alter the Singers table. + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + string alterStatement = "ALTER TABLE Singers ADD COLUMN Age INTEGER"; + + using var connection = new SpannerConnection(connectionString); + using var updateCmd = connection.CreateDdlCommand(alterStatement); + await updateCmd.ExecuteNonQueryAsync(); + Console.WriteLine("Added the Age column in Singers table."); + } +} +// [END spanner_postgresql_add_column] diff --git a/spanner/api/Spanner.Samples/AddStoringIndexAsyncPostgre.cs b/spanner/api/Spanner.Samples/AddStoringIndexAsyncPostgre.cs new file mode 100644 index 00000000000..dcc1a277e9e --- /dev/null +++ b/spanner/api/Spanner.Samples/AddStoringIndexAsyncPostgre.cs @@ -0,0 +1,34 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_create_storing_index] + +using Google.Cloud.Spanner.Data; +using System; +using System.Threading.Tasks; + +public class AddStoringIndexAsyncPostgreSample +{ + public async Task AddStoringIndexAsyncPostgre(string projectId, string instanceId, string databaseId) + { + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + string createIndexStatement = "CREATE INDEX SingersBySingerName ON Singers(FirstName) INCLUDE(LastName, SingerInfo)"; + + using var connection = new SpannerConnection(connectionString); + using var createCmd = connection.CreateDdlCommand(createIndexStatement); + await createCmd.ExecuteNonQueryAsync(); + Console.WriteLine("Added the SingersBySingerName index."); + } +} +// [END spanner_postgresql_create_storing_index] diff --git a/spanner/api/Spanner.Samples/CastDataTypesAsyncPostgre.cs b/spanner/api/Spanner.Samples/CastDataTypesAsyncPostgre.cs new file mode 100644 index 00000000000..24f04eaf925 --- /dev/null +++ b/spanner/api/Spanner.Samples/CastDataTypesAsyncPostgre.cs @@ -0,0 +1,67 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_cast_data_type] +using Google.Cloud.Spanner.Data; +using System; +using System.Threading.Tasks; + +public class CastDataTypesAsyncPostgreSample +{ + public async Task CastDataTypesAsyncPostgre(string projectId, string instanceId, string databaseId) + { + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + // The `::` cast operator can be used to cast from one data type to another. + var commandText = "SELECT 1::varchar as str, '2'::int as int, 3::decimal as dec, '4'::bytea as bytes, " + + "5::float as float, 'true'::bool as bool, '2021-11-03T09:35:01UTC'::timestamptz as timestamp"; + var command = connection.CreateSelectCommand(commandText); + + using var reader = await command.ExecuteReaderAsync(); + var result = new CastDataType(); + while (await reader.ReadAsync()) + { + result.String = reader.GetFieldValue("str"); + result.Integer = reader.GetFieldValue("int"); + result.Decimal = reader.GetFieldValue("dec"); + result.Bytes = reader.GetFieldValue("bytes"); + result.Float = reader.GetFieldValue("float"); + result.Bool = reader.GetFieldValue("bool"); + result.Timestamp = reader.GetFieldValue("timestamp"); + } + + return result; + } + + public struct CastDataType + { + public string String { get; set; } + + public int Integer { get; set; } + + public decimal Decimal { get; set; } + + public byte[] Bytes { get; set; } + + public float Float { get; set; } + + public bool Bool { get; set; } + + public DateTime Timestamp { get; set; } + } +} +// [END spanner_postgresql_cast_data_type] diff --git a/spanner/api/Spanner.Samples/ConnectToDatabaseAsyncPostgre.cs b/spanner/api/Spanner.Samples/ConnectToDatabaseAsyncPostgre.cs new file mode 100644 index 00000000000..bff9137664c --- /dev/null +++ b/spanner/api/Spanner.Samples/ConnectToDatabaseAsyncPostgre.cs @@ -0,0 +1,36 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_connect_postgresql_database] +using Google.Cloud.Spanner.Data; +using System; +using System.Threading.Tasks; + +public class ConnectToDatabaseAsyncPostgreSample +{ + public async Task ConnectToDatabaseAsyncPostgre(string projectId, string instanceId, string databaseId) + { + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + var command = connection.CreateSelectCommand("SELECT 'Hello from Spanner PostgreSQL!'"); + + var result = await command.ExecuteScalarAsync(); + Console.WriteLine(result); + return result; + } +} +// [END spanner_connect_postgresql_database] diff --git a/spanner/api/Spanner.Samples/CreateDatabaseAsyncPostgre.cs b/spanner/api/Spanner.Samples/CreateDatabaseAsyncPostgre.cs new file mode 100644 index 00000000000..20e33f46943 --- /dev/null +++ b/spanner/api/Spanner.Samples/CreateDatabaseAsyncPostgre.cs @@ -0,0 +1,87 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_create_database] + +using Google.Cloud.Spanner.Admin.Database.V1; +using Google.Cloud.Spanner.Common.V1; +using System; +using System.Threading.Tasks; + +public class CreateDatabaseAsyncPostgreSample +{ + public async Task CreateDatabaseAsyncPostgre(string projectId, string instanceId, string databaseId) + { + DatabaseAdminClient databaseAdminClient = await DatabaseAdminClient.CreateAsync(); + + // Create the CreateDatabaseRequest with PostgreSQL dialect and execute it. + // There cannot be Extra DDL statements while creating PostgreSQL. + var createDatabaseRequest = new CreateDatabaseRequest + { + ParentAsInstanceName = InstanceName.FromProjectInstance(projectId, instanceId), + CreateStatement = $"CREATE DATABASE \"{databaseId}\"", + DatabaseDialect = DatabaseDialect.Postgresql + }; + + var createOperation = await databaseAdminClient.CreateDatabaseAsync(createDatabaseRequest); + + // Wait until the operation has finished. + Console.WriteLine("Waiting for the database to be created."); + var completedResponse = await createOperation.PollUntilCompletedAsync(); + if (completedResponse.IsFaulted) + { + Console.WriteLine($"Error while creating PostgreSQL database: {completedResponse.Exception}"); + throw completedResponse.Exception; + } + + // Postgres Database is created. Now, we can create the tables. + // Define create table statement for table #1 in PostgreSQL syntax. + var createSingersTable = + @"CREATE TABLE Singers ( + SingerId bigint NOT NULL PRIMARY KEY, + FirstName varchar(1024), + LastName varchar(1024), + Rating numeric, + SingerInfo bytea + )"; + + // Define create table statement for table #2 in PostgreSQL syntax. + var createAlbumsTable = + @"CREATE TABLE Albums ( + AlbumId bigint NOT NULL PRIMARY KEY, + SingerId bigint NOT NULL REFERENCES Singers (SingerId), + AlbumTitle text + )"; + + DatabaseName databaseName = DatabaseName.FromProjectInstanceDatabase(projectId, instanceId, databaseId); + + // Create UpdateDatabaseRequest to create the tables. + var updateDatabaseRequest = new UpdateDatabaseDdlRequest + { + DatabaseAsDatabaseName = databaseName, + Statements = { createSingersTable, createAlbumsTable } + }; + + var updateOperation = await databaseAdminClient.UpdateDatabaseDdlAsync(updateDatabaseRequest); + // Wait until the operation has finished. + Console.WriteLine("Waiting for the tables to be created."); + var updateResponse = await updateOperation.PollUntilCompletedAsync(); + if (updateResponse.IsFaulted) + { + Console.WriteLine($"Error while updating database: {updateResponse.Exception}"); + throw updateResponse.Exception; + } + } +} +// [END spanner_postgresql_create_database] diff --git a/spanner/api/Spanner.Samples/CreateInterleavedTablesAsyncPostgre.cs b/spanner/api/Spanner.Samples/CreateInterleavedTablesAsyncPostgre.cs new file mode 100644 index 00000000000..d9056cbcebe --- /dev/null +++ b/spanner/api/Spanner.Samples/CreateInterleavedTablesAsyncPostgre.cs @@ -0,0 +1,68 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_interleaved_table] +using Google.Cloud.Spanner.Admin.Database.V1; +using Google.Cloud.Spanner.Common.V1; +using System; +using System.Threading.Tasks; + +public class CreateInterleavedTablesAsyncPostgreSample +{ + public async Task CreateInterleavedTablesAsyncPostgre(string projectId, string instanceId, string databaseId) + { + DatabaseAdminClient databaseAdminClient = await DatabaseAdminClient.CreateAsync(); + + // PostgreSQL database with databaseId is already created. + // The Spanner PostgreSQL dialect extends the PostgreSQL dialect with certain Spanner + // specific features, such as interleaved tables. + // See https://cloud.google.com/spanner/docs/postgresql/data-definition-language#create_table + // for the full CREATE TABLE syntax. + var ddlStatements = + @"CREATE TABLE Authors + (AuthorId bigint NOT NULL, + FirstName varchar(1024), + LastName varchar(1024), + Rating double precision, + PRIMARY KEY (AuthorId)); + CREATE TABLE Books + (AuthorId bigint NOT NULL, + BookId bigint NOT NULL, + BookTitle text, + PRIMARY KEY (AuthorId, BookId) + ) INTERLEAVE IN PARENT Authors ON DELETE CASCADE;"; + + DatabaseName databaseName = DatabaseName.FromProjectInstanceDatabase(projectId, instanceId, databaseId); + + // Create UpdateDatabaseRequest to create the tables. + var updateDatabaseRequest = new UpdateDatabaseDdlRequest + { + DatabaseAsDatabaseName = databaseName, + Statements = { ddlStatements } + }; + + var updateOperation = await databaseAdminClient.UpdateDatabaseDdlAsync(updateDatabaseRequest); + // Wait until the operation has finished. + Console.WriteLine("Waiting for the interleaved tables to be created."); + var updateResponse = await updateOperation.PollUntilCompletedAsync(); + if (updateResponse.IsFaulted) + { + Console.WriteLine($"Error while updating database: {updateResponse.Exception}"); + throw updateResponse.Exception; + } + + Console.WriteLine($"Created interleaved table hierarchy using PostgreSQL dialect."); + } +} +// [END spanner_postgresql_interleaved_table] diff --git a/spanner/api/Spanner.Samples/ExecutePartitionedDmlAsyncPostgre.cs b/spanner/api/Spanner.Samples/ExecutePartitionedDmlAsyncPostgre.cs new file mode 100644 index 00000000000..a9e5c7d045d --- /dev/null +++ b/spanner/api/Spanner.Samples/ExecutePartitionedDmlAsyncPostgre.cs @@ -0,0 +1,43 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_partitioned_dml] +using Google.Cloud.Spanner.Data; +using System; +using System.Threading.Tasks; + +public class ExecutePartitionedDmlAsyncPostgreSample +{ + public async Task ExecutePartitionedDmlAsyncPostgre(string projectId, string instanceId, string databaseId) + { + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + // Spanner PostgreSQL has the same transaction limits as normal Spanner. This includes a + // maximum of 20,000 mutations in a single read/write transaction. Large update operations can + // be executed using Partitioned DML. This is also supported on Spanner PostgreSQL. + // See https://cloud.google.com/spanner/docs/dml-partitioned for more information. + + using var cmd = connection.CreateDmlCommand("DELETE FROM Singers WHERE SingerId > 3"); + + // The returned count is the lower bound of the number of records that was deleted. + long rowCount = await cmd.ExecutePartitionedUpdateAsync(); + + Console.WriteLine($"{rowCount} row(s) deleted..."); + return rowCount; + } +} +// [END spanner_postgresql_partitioned_dml] diff --git a/spanner/api/Spanner.Samples/GetInformationSchemaAsyncPostgre.cs b/spanner/api/Spanner.Samples/GetInformationSchemaAsyncPostgre.cs new file mode 100644 index 00000000000..252ce6226fa --- /dev/null +++ b/spanner/api/Spanner.Samples/GetInformationSchemaAsyncPostgre.cs @@ -0,0 +1,73 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_information_schema] +using Google.Cloud.Spanner.Data; +using System.Collections.Generic; +using System.Threading.Tasks; + +public class GetInformationSchemaAsyncPostgreSample +{ + public async Task> GetInformationSchemaAsyncPostgre(string projectId, string instanceId, string databaseId) + { + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + // The Spanner INFORMATION_SCHEMA tables can be used to query the metadata of tables and columns + // of PostgreSQL databases. The returned results will include additional PostgreSQL metadata columns. + // Get all the user tables in the database. PostgreSQL uses the `public` schema for user tables. + var command = connection.CreateSelectCommand("SELECT table_schema, table_name, " + // The following columns are only available for PostgreSQL databases. + + "user_defined_type_catalog, " + + "user_defined_type_schema, " + + "user_defined_type_name " + + "FROM INFORMATION_SCHEMA.tables " + + "WHERE table_schema='public'"); + + using var reader = await command.ExecuteReaderAsync(); + var result = new List(); + while (await reader.ReadAsync()) + { + var informationSchema = new InformationSchema + { + Schema = reader.GetFieldValue("table_schema"), + Name = reader.GetFieldValue("table_name") + }; + + var userDefinedTypeCatalog = $"{reader.GetFieldValue("user_defined_type_catalog")}"; + var userDefinedTypeSchema = $"{reader.GetFieldValue("user_defined_type_schema")}"; + var userDefinedTypeName = $"{reader.GetFieldValue("user_defined_type_name")}"; + + informationSchema.UserDefinedType = + string.IsNullOrWhiteSpace(userDefinedTypeName) ? "null" + : $"{userDefinedTypeCatalog}.{userDefinedTypeSchema}.{userDefinedTypeName}"; + + result.Add(informationSchema); + } + + return result; + } + + public struct InformationSchema + { + public string Schema { get; set; } + + public string Name { get; set; } + + public string UserDefinedType { get; set; } + } +} +// [END spanner_postgresql_information_schema] diff --git a/spanner/api/Spanner.Samples/IdentifierCaseSensitivityAsyncPostgre.cs b/spanner/api/Spanner.Samples/IdentifierCaseSensitivityAsyncPostgre.cs new file mode 100644 index 00000000000..f192963efcc --- /dev/null +++ b/spanner/api/Spanner.Samples/IdentifierCaseSensitivityAsyncPostgre.cs @@ -0,0 +1,127 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_identifier_case_sensitivity] + +using Google.Cloud.Spanner.Admin.Database.V1; +using Google.Cloud.Spanner.Common.V1; +using Google.Cloud.Spanner.Data; +using System; +using System.Threading.Tasks; + +public class IdentifierCaseSensitivityAsyncPostgreSample +{ + public async Task IdentifierCaseSensitivityAsyncPostgre(string projectId, string instanceId, string databaseId) + { + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + + DatabaseAdminClient databaseAdminClient = await DatabaseAdminClient.CreateAsync(); + + // PostgreSQL Database already exists. + // Spanner PostgreSQL follows the case sensitivity rules of PostgreSQL. This means that: + // 1. Identifiers that are not double-quoted are folded to lower case. + // 2. Identifiers that are double-quoted retain their case and are case-sensitive. + // See https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS + // for more information. + string ddlStatement = "CREATE TABLE Concerts (" + // ConcertId will be folded to `concertid`. + + " ConcertId bigint NOT NULL PRIMARY KEY," + // Location and Time are double-quoted and will therefore retain their + // mixed case and are case-sensitive. This means that any statement that + // references any of these columns must use double quotes. + + " \"Location\" varchar(1024) NOT NULL," + + " \"Time\" timestamptz NOT NULL" + + ")"; + + var updateDatabaseDDLRequest = new UpdateDatabaseDdlRequest + { + DatabaseAsDatabaseName = DatabaseName.FromProjectInstanceDatabase(projectId, instanceId, databaseId), + Statements = { ddlStatement } + }; + + // Update database schema by adding new table. + var updateOperation = await databaseAdminClient.UpdateDatabaseDdlAsync(updateDatabaseDDLRequest); + + // Wait until the operation has finished. + Console.WriteLine("Waiting for the Concerts table to be created."); + var response = await updateOperation.PollUntilCompletedAsync(); + if (response.IsFaulted) + { + Console.WriteLine($"Error while creating table: {response.Exception}"); + throw response.Exception; + } + + Console.WriteLine($"Created table with case sensitive names in database ${databaseId} using PostgreSQL dialect."); + + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + // PostgreSQL case sensitivity with mutations. + // Mutations: Column names in mutations are always case-insensitive, regardless whether the + // columns were double-quoted or not during creation. + using var cmd = connection.CreateInsertCommand("Concerts", new SpannerParameterCollection + { + { "ConcertId", SpannerDbType.Int64, 1 }, + { "Location", SpannerDbType.String, "Venue 1" }, + { "Time", SpannerDbType.Timestamp, DateTime.UtcNow } + }); + await cmd.ExecuteNonQueryAsync(); + + // PostgreSQL case sensitivity with queries. + var selectCommand = connection.CreateSelectCommand("SELECT * FROM Concerts"); + using var reader = await selectCommand.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + Console.WriteLine("ConcertId : " + // ConcertId was not double quoted while table creation, so it is automatically folded to lower case. + // Accessing the column by its name in a result set must therefore use all lower-case letters. + + reader.GetFieldValue("concertid") + // Location and Time were double - quoted during creation, + // and retain their mixed case when returned in a result set. + + " Location : " + + reader.GetFieldValue("Location") + + " Time : " + + reader.GetFieldValue("Time")); + } + + // PostgreSQL case sensitivity with aliases. + // Aliases : They are also identifiers, and specifying an alias in double quotes will make the alias retain its case. + var selectAliasCommand = connection.CreateSelectCommand("SELECT concertid AS \"ConcertId\", \"Location\" AS \"venue\", \"Time\" FROM Concerts"); + using var dataReader = await selectAliasCommand.ExecuteReaderAsync(); + while (await dataReader.ReadAsync()) + { + // The aliases are double-quoted and therefore retain their mixed case. + Console.WriteLine("ConcertId : " + + dataReader.GetFieldValue("ConcertId") + + " Location : " + + dataReader.GetFieldValue("venue") + + " Time : " + + dataReader.GetFieldValue("Time")); + } + + // PostgreSQL case sensitivity with DML statements. + // DML statements must also follow the PostgreSQL case rules. + var dmlCommand = connection.CreateDmlCommand("INSERT INTO Concerts (ConcertId, \"Location\", \"Time\") VALUES($1, $2, $3)", + new SpannerParameterCollection + { + { "p1", SpannerDbType.Int64, 2 }, + { "p2", SpannerDbType.String, "Venue 2" }, + { "p3", SpannerDbType.Timestamp, DateTime.UtcNow } + }); + + var count = await dmlCommand.ExecuteNonQueryAsync(); + return count; + } +} +// [END spanner_postgresql_identifier_case_sensitivity] diff --git a/spanner/api/Spanner.Samples/InsertUsingBatchDmlAsyncPostgre.cs b/spanner/api/Spanner.Samples/InsertUsingBatchDmlAsyncPostgre.cs new file mode 100644 index 00000000000..f0d1a0f3bb4 --- /dev/null +++ b/spanner/api/Spanner.Samples/InsertUsingBatchDmlAsyncPostgre.cs @@ -0,0 +1,55 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_batch_dml] +using Google.Cloud.Spanner.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +public class InsertUsingBatchDmlAsyncPostgreSample +{ + public async Task InsertUsingBatchDmlAsyncPostgre(string projectId, string instanceId, string databaseId) + { + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + SpannerBatchCommand batchCommand = connection.CreateBatchDmlCommand(); + + // Simple PostgreSQL DML statement. + batchCommand.Add("INSERT INTO Singers (SingerId, FirstName, LastName) VALUES (1, 'Alice', 'Henderson')"); + batchCommand.Add("INSERT INTO Singers (SingerId, FirstName, LastName) VALUES (2, 'Bruce', 'Allison')"); + + // Example of parameterized PostgreSQL DML statement. Named parameters are not supported in current Spanner PostgreSQL implementation. + // Notice the syntax difference in postitional parameters. The parameters used are $1, $2, $3 and the values are bound on position. + // $1 binds to p1, $2 binds to p2 and so on. + batchCommand.Add("INSERT INTO Singers (SingerId, FirstName, LastName) VALUES ($1, $2, $3)", + new SpannerParameterCollection + { + { "p1", SpannerDbType.Int64, 3L }, + { "p2", SpannerDbType.String, "Olivia" }, + { "p3", SpannerDbType.String, "Garcia" } + }); + + IEnumerable affectedRows = await batchCommand.ExecuteNonQueryAsync(); + + int count = affectedRows.Count(); + Console.WriteLine($"Executed {count} PostgreSQL statements using Batch DML."); + return count; + } +} +// [END spanner_postgresql_batch_dml] diff --git a/spanner/api/Spanner.Samples/InsertUsingDmlWithParametersAsyncPostgre.cs b/spanner/api/Spanner.Samples/InsertUsingDmlWithParametersAsyncPostgre.cs new file mode 100644 index 00000000000..c8261ce5acb --- /dev/null +++ b/spanner/api/Spanner.Samples/InsertUsingDmlWithParametersAsyncPostgre.cs @@ -0,0 +1,45 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_dml_with_parameters] +using Google.Cloud.Spanner.Data; +using System.Threading.Tasks; + +public class InsertUsingDmlWithParametersAsyncPostgreSample +{ + public async Task InsertUsingDmlWithParametersAsyncPostgre(string projectId, string instanceId, string databaseId) + { + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + // Spanner implementation of PostgreSQL supports positional parameters. Named parameters are not supported. + var command = connection.CreateDmlCommand("INSERT INTO Singers (SingerId, FirstName, LastName) " + + "VALUES ($1, $2, $3), ($4, $5, $6)", + new SpannerParameterCollection + { + { "p1", SpannerDbType.Int64, 4 }, + { "p2", SpannerDbType.String, "Bruce" }, + { "p3", SpannerDbType.String, "Allison" }, + { "p4", SpannerDbType.Int64, 5 }, + { "p5", SpannerDbType.String, "George" }, + { "p6", SpannerDbType.String, "Harrison" } + }); + + // The value of count should be 2, as 2 rows are inserted by DML. + var count = await command.ExecuteNonQueryAsync(); + return count; + } +} +// [END spanner_postgresql_dml_with_parameters] diff --git a/spanner/api/Spanner.Samples/OrderNullsAsyncPostgre.cs b/spanner/api/Spanner.Samples/OrderNullsAsyncPostgre.cs new file mode 100644 index 00000000000..ddd568b9db8 --- /dev/null +++ b/spanner/api/Spanner.Samples/OrderNullsAsyncPostgre.cs @@ -0,0 +1,129 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_order_nulls] +using Google.Cloud.Spanner.Admin.Database.V1; +using Google.Cloud.Spanner.Common.V1; +using Google.Cloud.Spanner.Data; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +public class OrderNullsAsyncPostgreSample +{ + public async Task> OrderNullsAsyncPostgre(string projectId, string instanceId, string databaseId) + { + DatabaseAdminClient databaseAdminClient = await DatabaseAdminClient.CreateAsync(); + + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + + // Create a table, insert data and then select using orderby clause. + var singersTable = + @"CREATE TABLE Singers ( + SingerId bigint NOT NULL PRIMARY KEY, + Name varchar(1024))"; + + DatabaseName databaseName = DatabaseName.FromProjectInstanceDatabase(projectId, instanceId, databaseId); + + // Create UpdateDatabaseRequest to create the table. + var updateDatabaseRequest = new UpdateDatabaseDdlRequest + { + DatabaseAsDatabaseName = databaseName, + Statements = { singersTable } + }; + + var updateOperation = await databaseAdminClient.UpdateDatabaseDdlAsync(updateDatabaseRequest); + // Wait until the operation has finished. + Console.WriteLine("Waiting for the table to be created."); + var updateResponse = await updateOperation.PollUntilCompletedAsync(); + if (updateResponse.IsFaulted) + { + Console.WriteLine($"Error while creating the table : {updateResponse.Exception}"); + throw updateResponse.Exception; + } + + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + var batchCommand = connection.CreateBatchDmlCommand(); + + // Insert data. + batchCommand.Add("INSERT INTO Singers (SingerId, Name) VALUES (1, 'Alice')"); + batchCommand.Add("INSERT INTO Singers (SingerId, Name) VALUES (2, 'Bruce')"); + batchCommand.Add("INSERT INTO Singers (SingerId, Name) VALUES ($1, $2)", + new SpannerParameterCollection + { + {"p1", SpannerDbType.Int64, 3 }, + {"p2", SpannerDbType.String, DBNull.Value } + }); + await batchCommand.ExecuteNonQueryAsync(); + + // Spanner PostgreSQL follows the ORDER BY rules for NULL values of PostgreSQL. This means + // that: + // 1. NULL values are ordered last by default when a query result is ordered in ascending + // order. + // 2. NULL values are ordered first by default when a query result is ordered in descending + // order. + // 3. NULL values can be ordered first or last by specifying NULLS FIRST or NULLS LAST in the + // ORDER BY clause. + var orderedNames = new List(); + + // This returns the singers in the order Alice, Bruce, null. + var selectCommand = connection.CreateSelectCommand("SELECT name FROM singers ORDER BY name"); + using var reader = await selectCommand.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + var name = + reader.IsDBNull(reader.GetOrdinal("name")) ? "NULL" + : reader.GetFieldValue("name"); + orderedNames.Add(name); + } + + // This returns the singers in the order null, Bruce, Alice. + selectCommand = connection.CreateSelectCommand("SELECT name FROM singers ORDER BY name DESC"); + using var dataReader = await selectCommand.ExecuteReaderAsync(); + while (await dataReader.ReadAsync()) + { + var name = + dataReader.IsDBNull(dataReader.GetOrdinal("name")) ? "NULL" + : dataReader.GetFieldValue("name"); + orderedNames.Add(name); + } + + // This returns the singers in the order null, Alice, Bruce. + selectCommand = connection.CreateSelectCommand("SELECT name FROM singers ORDER BY name NULLS FIRST"); + using var nullFirstReader = await selectCommand.ExecuteReaderAsync(); + while (await nullFirstReader.ReadAsync()) + { + var name = + nullFirstReader.IsDBNull(nullFirstReader.GetOrdinal("name")) ? "NULL" + : nullFirstReader.GetFieldValue("name"); + orderedNames.Add(name); + } + + // This returns the singers in the order Bruce, Alice, null. + selectCommand = connection.CreateSelectCommand("SELECT name FROM singers ORDER BY name NULLS LAST"); + using var nullLastReader = await selectCommand.ExecuteReaderAsync(); + while (await nullLastReader.ReadAsync()) + { + var name = + nullLastReader.IsDBNull(nullLastReader.GetOrdinal("name")) ? "NULL" + : nullLastReader.GetFieldValue("name"); + orderedNames.Add(name); + } + + return orderedNames; + } +} +// [END spanner_postgresql_order_nulls] diff --git a/spanner/api/Spanner.Samples/QueryUsingParametersAsyncPostgre.cs b/spanner/api/Spanner.Samples/QueryUsingParametersAsyncPostgre.cs new file mode 100644 index 00000000000..1240bff1246 --- /dev/null +++ b/spanner/api/Spanner.Samples/QueryUsingParametersAsyncPostgre.cs @@ -0,0 +1,66 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_query_parameter] +using Google.Cloud.Spanner.Data; +using System.Collections.Generic; +using System.Threading.Tasks; + +public class QueryUsingParametersAsyncPostgreSample +{ + public async Task> QueryUsingParametersAsyncPostgre(string projectId, string instanceId, string databaseId) + { + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + using var cmd = connection.CreateSelectCommand("SELECT SingerId, FirstName, LastName FROM Singers WHERE LastName LIKE $1", + new SpannerParameterCollection + { + // Use 'p1' to bind to the parameter with index 1. + {"p1", SpannerDbType.String, "A%" } + }); + + var list = new List(); + using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + var singer = new Singer + { + // Note that the PostgreSQL dialect will return all column names in lower case, unless the + // columns have been created with case-sensitive column names. See + // https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS + // for more information on PostgreSQL identifiers. + Id = reader.GetFieldValue("singerid"), + FirstName = reader.GetFieldValue("firstname"), + LastName = reader.GetFieldValue("lastname") + }; + + list.Add(singer); + } + + return list; + } + + public struct Singer + { + public long Id { get; set; } + + public string FirstName { get; set; } + + public string LastName { get; set; } + } +} +// [END spanner_postgresql_query_parameter] diff --git a/spanner/api/Spanner.Samples/Spanner.Samples.csproj b/spanner/api/Spanner.Samples/Spanner.Samples.csproj index 136ccc0251e..b9677149f55 100644 --- a/spanner/api/Spanner.Samples/Spanner.Samples.csproj +++ b/spanner/api/Spanner.Samples/Spanner.Samples.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 diff --git a/spanner/api/Spanner.Samples/UpdateUsingDmlAsyncPostgre.cs b/spanner/api/Spanner.Samples/UpdateUsingDmlAsyncPostgre.cs new file mode 100644 index 00000000000..d0a2a7c1ffd --- /dev/null +++ b/spanner/api/Spanner.Samples/UpdateUsingDmlAsyncPostgre.cs @@ -0,0 +1,42 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_dml_getting_started_update] +using Google.Cloud.Spanner.Data; +using Google.Cloud.Spanner.V1; +using System; +using System.Threading.Tasks; + +public class UpdateUsingDmlAsyncPostgreSample +{ + public async Task UpdateUsingDmlAsyncPostgre(string projectId, string instanceId, string databaseId) + { + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + using var cmd = connection.CreateDmlCommand("UPDATE Singers SET Rating = $1 WHERE SingerId = 1", + new SpannerParameterCollection + { + { "p1", SpannerDbType.PgNumeric, PgNumeric.Parse("4.0") } + }); + + var rowCount = await cmd.ExecuteNonQueryAsync(); + + Console.WriteLine($"{rowCount} row(s) updated..."); + return rowCount; + } +} +// [END spanner_postgresql_dml_getting_started_update] diff --git a/spanner/api/Spanner.Samples/UseFunctionAsyncPostgre.cs b/spanner/api/Spanner.Samples/UseFunctionAsyncPostgre.cs new file mode 100644 index 00000000000..9d7649750ab --- /dev/null +++ b/spanner/api/Spanner.Samples/UseFunctionAsyncPostgre.cs @@ -0,0 +1,39 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_functions] +using Google.Cloud.Spanner.Data; +using System; +using System.Threading.Tasks; + +public class UseFunctionAsyncPostgreSample +{ + public async Task UseFunctionAsyncPostgre(string projectId, string instanceId, string databaseId) + { + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + // Use the PostgreSQL `to_timestamp` function to convert a number of seconds since epoch to a + // timestamp. 1284352323 seconds = Monday, September 13, 2010 4:32:03 AM. + var command = connection.CreateSelectCommand("SELECT to_timestamp(1284352323) AS t"); + + var result = await command.ExecuteScalarAsync(); + Console.WriteLine(result); + return result; + } +} + +// [END spanner_postgresql_functions] diff --git a/spanner/api/Spanner.Samples/UsePgNumericDataTypeAsyncPostgre.cs b/spanner/api/Spanner.Samples/UsePgNumericDataTypeAsyncPostgre.cs new file mode 100644 index 00000000000..600f362478b --- /dev/null +++ b/spanner/api/Spanner.Samples/UsePgNumericDataTypeAsyncPostgre.cs @@ -0,0 +1,128 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START spanner_postgresql_numeric_datatype] +using Google.Cloud.Spanner.Admin.Database.V1; +using Google.Cloud.Spanner.Common.V1; +using Google.Cloud.Spanner.Data; +using Google.Cloud.Spanner.V1; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +public class UsePgNumericDataTypeAsyncPostgreSample +{ + public async Task> UsePgNumericDataTypeAsyncPostgre(string projectId, string instanceId, string databaseId) + { + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + + DatabaseAdminClient databaseAdminClient = await DatabaseAdminClient.CreateAsync(); + + // Create a table that includes a column with data type NUMERIC. As the database has been + // created with the PostgreSQL dialect, the data type that is used will be the PostgreSQL + // NUMERIC/DECIMAL data type. + var ddlStatement = + @"CREATE TABLE Venues(" + + " VenueId bigint NOT NULL PRIMARY KEY," + + " Name varchar(1024) NOT NULL," + + " Revenues numeric" + + ")"; + + DatabaseName databaseName = DatabaseName.FromProjectInstanceDatabase(projectId, instanceId, databaseId); + + // Create UpdateDatabaseRequest to create the table. + var updateDatabaseRequest = new UpdateDatabaseDdlRequest + { + DatabaseAsDatabaseName = databaseName, + Statements = { ddlStatement } + }; + + var updateOperation = await databaseAdminClient.UpdateDatabaseDdlAsync(updateDatabaseRequest); + // Wait until the operation has finished. + Console.WriteLine("Waiting for the table to be created."); + var updateResponse = await updateOperation.PollUntilCompletedAsync(); + if (updateResponse.IsFaulted) + { + Console.WriteLine($"Error while creating Venues table : {updateResponse.Exception}"); + throw updateResponse.Exception; + } + + Console.WriteLine("Created Venues table."); + + using var connection = new SpannerConnection(connectionString); + await connection.OpenAsync(); + + var batchCommand = connection.CreateBatchDmlCommand(); + // Insert a venue. + batchCommand.Add("INSERT INTO Venues (VenueId, Name, Revenues) " + + "VALUES ($1, $2, $3)", + new SpannerParameterCollection + { + { "p1", SpannerDbType.Int64, 1 }, + { "p2", SpannerDbType.String, "Venue 1" }, + { "p3", SpannerDbType.PgNumeric, PgNumeric.Parse("3150.25") } + }); + // Insert a Venue with a NaN (Not a Number) value for the Revenues column. + batchCommand.Add("INSERT INTO Venues (VenueId, Name, Revenues) " + + "VALUES ($1, $2, $3)", + new SpannerParameterCollection + { + { "p1", SpannerDbType.Int64, 2 }, + { "p2", SpannerDbType.String, "Venue 2" }, + { "p3", SpannerDbType.PgNumeric, PgNumeric.Parse("NaN") } // We can also use PgNumeric.NaN. + }); + // Insert a Venue with a NULL value for the Revenues column. + batchCommand.Add("INSERT INTO Venues (VenueId, Name, Revenues) " + + "VALUES ($1, $2, $3)", + new SpannerParameterCollection + { + { "p1", SpannerDbType.Int64, 3 }, + { "p2", SpannerDbType.String, "Venue 3" }, + { "p3", SpannerDbType.PgNumeric, DBNull.Value } + }); + + var count = await batchCommand.ExecuteNonQueryAsync(); + + // Get all Venues and inspect the Revenues values. + var command = connection.CreateSelectCommand("Select * FROM Venues"); + + using var reader = await command.ExecuteReaderAsync(); + List result = new List(); + while (await reader.ReadAsync()) + { + var venue = new Venue + { + Id = reader.GetFieldValue("venueid"), + Name = reader.GetFieldValue("name"), + // PgNumeric can have DBNull values, so check for DBNull. + Revenue = reader.IsDBNull(reader.GetOrdinal("revenues")) ? "" : reader.GetFieldValue("revenues").ToString() + }; + + result.Add(venue); + } + + return result; + } + + public struct Venue + { + public long Id { get; set; } + + public string Name { get; set; } + + public string Revenue { get; set; } + } +} + +// [END spanner_postgresql_numeric_datatype]