diff --git a/spanner/api/Spanner.Samples.Tests/AddColumnAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/AddColumnAsyncPostgreTest.cs new file mode 100644 index 00000000000..98e83d073ac --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/AddColumnAsyncPostgreTest.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 AddColumnAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly AddColumnAsyncPostgreSample _sample; + + public AddColumnAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new AddColumnAsyncPostgreSample(); + } + + [Fact] + public async Task TestAddColumnAsyncPostgre() + { + //Act. + await _sample.AddColumnAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + } +} 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..bc7ee61fa86 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/CreateInterleavedTablesAsyncPostgreTest.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. + +using Google.Cloud.Spanner.Data; +using System.Collections.Generic; +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 ListTableNamesAsync(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _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)); + } + + private async Task> ListTableNamesAsync(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 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; + } +} diff --git a/spanner/api/Spanner.Samples.Tests/ExecutePartitionedDmlAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/ExecutePartitionedDmlAsyncPostgreTest.cs new file mode 100644 index 00000000000..1b8f7beb030 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/ExecutePartitionedDmlAsyncPostgreTest.cs @@ -0,0 +1,57 @@ +// 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.Data; +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. + // Insert data that cannot be inserted by any other tests to avoid errors. + await InsertDataAsync(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + // Act. + var result = await _sample.ExecutePartitionedDmlAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + Assert.Equal(2, result); + } + + private async Task InsertDataAsync(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(); + batchCommand.Add("INSERT INTO Singers (SingerId, FirstName, LastName) VALUES (12, 'Elvis', 'Presley')"); + batchCommand.Add("INSERT INTO Singers (SingerId, FirstName, LastName) VALUES (13, 'John', 'Lennon')"); + + await batchCommand.ExecuteNonQueryAsync(); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/GetInformationSchemaAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/GetInformationSchemaAsyncPostgreTest.cs new file mode 100644 index 00000000000..1613bc59a5c --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/GetInformationSchemaAsyncPostgreTest.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 GetInformationSchemaAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly GetInformationSchemaAsyncPostgreSample _sample; + + public GetInformationSchemaAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new GetInformationSchemaAsyncPostgreSample(); + } + + [Fact] + public async Task TestGetInformationSchemaAsyncPostgre() + { + // Act. + var result = await _sample.GetInformationSchemaAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + // Assert. + // These two tables will always exist. + Assert.Contains(result, r => r.Name == "albums" && r.Schema == "public" && r.UserDefinedType == "undefined"); + Assert.Contains(result, r => r.Name == "singers" && r.Schema == "public" && r.UserDefinedType == "undefined"); + } +} 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..01341b1fdd8 --- /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.True(count >= 3); // Other tests may have inserted data. + } +} 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..d6d25d4f9bb --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/OrderNullsAsyncPostgreTest.cs @@ -0,0 +1,94 @@ +// 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 Google.Cloud.Spanner.Common.V1; +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. + await CreateTableForOrderingAsync(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + // Act. + var result = await _sample.OrderNullsAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + //Assert. + Assert.Collection(result, + // Alice, Bruce, null. + item1 => Assert.Equal("Alice", item1), + item2 => Assert.Equal("Bruce", item2), + item3 => Assert.Null(item3), + + // null, Bruce, Alice. + item4 => Assert.Null(item4), + item5 => Assert.Equal("Bruce", item5), + item6 => Assert.Equal("Alice", item6), + + // null, Alice, Bruce. + item7 => Assert.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.Null(item12)); + } + + private async Task CreateTableForOrderingAsync(string projectId, string instanceId, string databaseId) + { + DatabaseAdminClient databaseAdminClient = await DatabaseAdminClient.CreateAsync(); + + // Create a table. + var singersTable = @"CREATE TABLE SingersForOrder ( + 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; + } + } +} diff --git a/spanner/api/Spanner.Samples.Tests/QueryUsingParametersAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/QueryUsingParametersAsyncPostgreTest.cs new file mode 100644 index 00000000000..70a2e5ff864 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/QueryUsingParametersAsyncPostgreTest.cs @@ -0,0 +1,54 @@ +// 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.Data; +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. + // Insert data that cannot be inserted by any other tests to avoid errors. + await InsertDataAsync(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + // Act. + var result = await _sample.QueryUsingParametersAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + //Assert. + Assert.Single(result); + } + + private async Task InsertDataAsync(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.CreateDmlCommand("INSERT INTO Singers(SingerId, FirstName, LastName) VALUES(10, 'Sonu', 'Nigam')"); + await command.ExecuteNonQueryAsync(); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/SpannerFixture.cs b/spanner/api/Spanner.Samples.Tests/SpannerFixture.cs index 3ab90eb4913..14ee5598b98 100644 --- a/spanner/api/Spanner.Samples.Tests/SpannerFixture.cs +++ b/spanner/api/Spanner.Samples.Tests/SpannerFixture.cs @@ -35,6 +35,7 @@ public class SpannerFixture : IAsyncLifetime, ICollectionFixture // 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-postgre-{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(); @@ -385,6 +387,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..73efb6fefe4 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/UpdateUsingDmlAsyncPostgreTest.cs @@ -0,0 +1,53 @@ +// 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.Data; +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. + await InsertDataAsync(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + // Act. + var count = await _sample.UpdateUsingDmlAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + // Assert. + Assert.Equal(1, count); + } + + private async Task InsertDataAsync(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.CreateDmlCommand("INSERT INTO Singers(SingerId, FirstName, LastName) VALUES(11, 'Elton', 'John')"); + await command.ExecuteNonQueryAsync(); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/UseFunctionAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/UseFunctionAsyncPostgreTest.cs new file mode 100644 index 00000000000..fba20ea15ff --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/UseFunctionAsyncPostgreTest.cs @@ -0,0 +1,41 @@ +// 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 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.Equal(DateTime.Parse("2010-09-13T04:32:03Z").ToUniversalTime(), result); + } +} diff --git a/spanner/api/Spanner.Samples.Tests/UsePgNumericAsyncPostgreTest.cs b/spanner/api/Spanner.Samples.Tests/UsePgNumericAsyncPostgreTest.cs new file mode 100644 index 00000000000..0fdcbc142c7 --- /dev/null +++ b/spanner/api/Spanner.Samples.Tests/UsePgNumericAsyncPostgreTest.cs @@ -0,0 +1,44 @@ +// 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.V1; +using System.Threading.Tasks; +using Xunit; + +[Collection(nameof(SpannerFixture))] +public class UsePgNumericAsyncPostgreTest +{ + private readonly SpannerFixture _spannerFixture; + + private readonly UsePgNumericAsyncPostgreSample _sample; + + public UsePgNumericAsyncPostgreTest(SpannerFixture spannerFixture) + { + _spannerFixture = spannerFixture; + _sample = new UsePgNumericAsyncPostgreSample(); + } + + [Fact] + public async Task TestUsePgNumericAsyncPostgre() + { + // Act. + var result = await _sample.UsePgNumericAsyncPostgre(_spannerFixture.ProjectId, _spannerFixture.InstanceId, _spannerFixture.PostgreSqlDatabaseId); + + //Assert. + Assert.Collection(result, + item1 => Assert.Equal(PgNumeric.Parse("3150.25"), item1.Revenue), + item2 => Assert.Equal(PgNumeric.Parse("NaN"), item2.Revenue), + item3 => Assert.Null(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..3015ee1c622 --- /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 on Singers table."); + } +} +// [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..cc457d68eb4 --- /dev/null +++ b/spanner/api/Spanner.Samples/CastDataTypesAsyncPostgre.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_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..84f480116aa --- /dev/null +++ b/spanner/api/Spanner.Samples/ConnectToDatabaseAsyncPostgre.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. + +// [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..fcb9a52d0fe --- /dev/null +++ b/spanner/api/Spanner.Samples/CreateDatabaseAsyncPostgre.cs @@ -0,0 +1,83 @@ +// 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; + } + + // PostgreSQL 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..e7eab3e3030 --- /dev/null +++ b/spanner/api/Spanner.Samples/CreateInterleavedTablesAsyncPostgre.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_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(); + + // 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..698e170ec39 --- /dev/null +++ b/spanner/api/Spanner.Samples/ExecutePartitionedDmlAsyncPostgre.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. + +// [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(); + + // See https://cloud.google.com/spanner/docs/dml-partitioned for more information. + using var cmd = connection.CreateDmlCommand("DELETE FROM Singers WHERE SingerId > 11"); + + // The returned count is a lower bound of the number of records that was deleted. + long rowCount = await cmd.ExecutePartitionedUpdateAsync(); + + Console.WriteLine($"At least {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..170ec3b320f --- /dev/null +++ b/spanner/api/Spanner.Samples/GetInformationSchemaAsyncPostgre.cs @@ -0,0 +1,74 @@ +// 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(); + + // 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.IsDBNull(reader.GetOrdinal("user_defined_type_catalog")) ? null + : reader.GetFieldValue("user_defined_type_catalog"); + var userDefinedTypeSchema = reader.IsDBNull(reader.GetOrdinal("user_defined_type_schema")) ? null + : reader.GetFieldValue("user_defined_type_schema"); + var userDefinedTypeName = reader.IsDBNull(reader.GetOrdinal("user_defined_type_name")) ? null + : reader.GetFieldValue("user_defined_type_name"); + + informationSchema.UserDefinedType = string.IsNullOrWhiteSpace(userDefinedTypeName) ? "undefined" + : $"{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..f10812e3d7b --- /dev/null +++ b/spanner/api/Spanner.Samples/IdentifierCaseSensitivityAsyncPostgre.cs @@ -0,0 +1,121 @@ +// 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) + { + DatabaseAdminClient databaseAdminClient = await DatabaseAdminClient.CreateAsync(); + + // 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."); + + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + 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..d5fd30e66b4 --- /dev/null +++ b/spanner/api/Spanner.Samples/InsertUsingBatchDmlAsyncPostgre.cs @@ -0,0 +1,54 @@ +// 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. + 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..40b3ade7104 --- /dev/null +++ b/spanner/api/Spanner.Samples/InsertUsingDmlWithParametersAsyncPostgre.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. + +// [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(); + + 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..c34017b45e7 --- /dev/null +++ b/spanner/api/Spanner.Samples/OrderNullsAsyncPostgre.cs @@ -0,0 +1,85 @@ +// 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.Data; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +public class OrderNullsAsyncPostgreSample +{ + public async Task> OrderNullsAsyncPostgre(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 batchCommand = connection.CreateBatchDmlCommand(); + + // Insert data in SingersForOrder table. + batchCommand.Add("INSERT INTO SingersForOrder (SingerId, Name) VALUES (1, 'Alice')"); + batchCommand.Add("INSERT INTO SingersForOrder (SingerId, Name) VALUES (2, 'Bruce')"); + batchCommand.Add("INSERT INTO SingersForOrder (SingerId, Name) VALUES ($1, $2)", + new SpannerParameterCollection + { + {"p1", SpannerDbType.Int64, 3 }, + {"p2", SpannerDbType.String, DBNull.Value } + }); + await batchCommand.ExecuteNonQueryAsync(); + + var orderedNames = new List(); + + // This returns the singers in the order Alice, Bruce, null. + var selectCommand = connection.CreateSelectCommand("SELECT name FROM SingersForOrder ORDER BY name"); + using var reader = await selectCommand.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + orderedNames.Add(reader.IsDBNull(reader.GetOrdinal("name")) ? null + : reader.GetFieldValue("name")); + } + + // This returns the singers in the order null, Bruce, Alice. + selectCommand = connection.CreateSelectCommand("SELECT name FROM SingersForOrder ORDER BY name DESC"); + using var dataReader = await selectCommand.ExecuteReaderAsync(); + while (await dataReader.ReadAsync()) + { + orderedNames.Add(dataReader.IsDBNull(dataReader.GetOrdinal("name")) ? null + : dataReader.GetFieldValue("name")); + } + + // This returns the singers in the order null, Alice, Bruce. + selectCommand = connection.CreateSelectCommand("SELECT name FROM SingersForOrder ORDER BY name NULLS FIRST"); + using var nullFirstReader = await selectCommand.ExecuteReaderAsync(); + while (await nullFirstReader.ReadAsync()) + { + orderedNames.Add(nullFirstReader.IsDBNull(nullFirstReader.GetOrdinal("name")) ? null + : nullFirstReader.GetFieldValue("name")); + } + + // This returns the singers in the order Bruce, Alice, null. + selectCommand = connection.CreateSelectCommand("SELECT name FROM SingersForOrder ORDER BY name NULLS LAST"); + using var nullLastReader = await selectCommand.ExecuteReaderAsync(); + while (await nullLastReader.ReadAsync()) + { + orderedNames.Add(nullLastReader.IsDBNull(nullLastReader.GetOrdinal("name")) ? null + : nullLastReader.GetFieldValue("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..a5af8494c88 --- /dev/null +++ b/spanner/api/Spanner.Samples/QueryUsingParametersAsyncPostgre.cs @@ -0,0 +1,62 @@ +// 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 + { + {"p1", SpannerDbType.String, "N%" } + }); + + var list = new List(); + using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + list.Add(new Singer + { + // See https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS + // to understand why column names are in lower case. + Id = reader.GetFieldValue("singerid"), + FirstName = reader.GetFieldValue("firstname"), + LastName = reader.GetFieldValue("lastname") + }); + } + + 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/UpdateUsingDmlAsyncPostgre.cs b/spanner/api/Spanner.Samples/UpdateUsingDmlAsyncPostgre.cs new file mode 100644 index 00000000000..63e2c149242 --- /dev/null +++ b/spanner/api/Spanner.Samples/UpdateUsingDmlAsyncPostgre.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_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 = 11", + 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..8f41e4d9af2 --- /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/UsePgNumericAsyncPostgre.cs b/spanner/api/Spanner.Samples/UsePgNumericAsyncPostgre.cs new file mode 100644 index 00000000000..997b5914b4c --- /dev/null +++ b/spanner/api/Spanner.Samples/UsePgNumericAsyncPostgre.cs @@ -0,0 +1,123 @@ +// 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 UsePgNumericAsyncPostgreSample +{ + public async Task> UsePgNumericAsyncPostgre(string projectId, string instanceId, string 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 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."); + + string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; + 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.NaN } // We can also use PgNumeric.Parse("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 } // We can use null as well. + }); + + 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()) + { + result.Add(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")) ? null : reader.GetFieldValue("revenues") + }); + } + + return result; + } + + public struct Venue + { + public long Id { get; set; } + + public string Name { get; set; } + + public PgNumeric? Revenue { get; set; } + } +} +// [END spanner_postgresql_numeric_datatype]