Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: samples and tests for admin database APIs #1099

Merged
merged 13 commits into from
Feb 26, 2024
105 changes: 105 additions & 0 deletions samples/samples/admin/pg_samples.py
@@ -0,0 +1,105 @@
#!/usr/bin/env python

# Copyright 2024 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.

"""This application demonstrates how to do basic operations using Cloud
Spanner PostgreSql dialect.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we be adding create_database sample for postgres? I think that is needed for customers to understand how to set a PG database.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

For more information, see the README.rst under /spanner.
"""
from google.cloud import spanner
from google.cloud.spanner_admin_database_v1.types import spanner_database_admin

OPERATION_TIMEOUT_SECONDS = 240


def create_table_with_datatypes(instance_id, database_id):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing pg_snippets already has this sample with autogenerated code. Do we still need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we need this because there is dependency on this sample in other tests.

"""Creates a table with supported datatypes."""
# [START spanner_postgresql_create_table_with_datatypes]
# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"
spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)

request = spanner_database_admin.UpdateDatabaseDdlRequest(
database=database.name,
statements=[
"""CREATE TABLE Venues (
VenueId BIGINT NOT NULL,
VenueName character varying(100),
VenueInfo BYTEA,
Capacity BIGINT,
OutdoorVenue BOOL,
PopularityScore FLOAT8,
Revenue NUMERIC,
LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL,
PRIMARY KEY (VenueId))"""
],
)
operation = spanner_client.database_admin_api.update_database_ddl(request)

print("Waiting for operation to complete...")
operation.result(OPERATION_TIMEOUT_SECONDS)

print(
"Created Venues table on database {} on instance {}".format(
database_id, instance_id
)
)
# [END spanner_postgresql_create_table_with_datatypes]


# [START spanner_postgresql_jsonb_add_column]
def add_jsonb_column(instance_id, database_id):
"""
Alters Venues tables in the database adding a JSONB column.
You can create the table by running the `create_table_with_datatypes`
sample or by running this DDL statement against your database:
CREATE TABLE Venues (
VenueId BIGINT NOT NULL,
VenueName character varying(100),
VenueInfo BYTEA,
Capacity BIGINT,
OutdoorVenue BOOL,
PopularityScore FLOAT8,
Revenue NUMERIC,
LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL,
PRIMARY KEY (VenueId))
"""
# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"

spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)

request = spanner_database_admin.UpdateDatabaseDdlRequest(
database=database.name,
statements=["ALTER TABLE Venues ADD COLUMN VenueDetails JSONB"],
)

operation = spanner_client.database_admin_api.update_database_ddl(request)

print("Waiting for operation to complete...")
operation.result(OPERATION_TIMEOUT_SECONDS)

print(
'Altered table "Venues" on database {} on instance {}.'.format(
database_id, instance_id
)
)


# [END spanner_postgresql_jsonb_add_column]
119 changes: 119 additions & 0 deletions samples/samples/admin/pg_samples_test.py
@@ -0,0 +1,119 @@
# Copyright 2024 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.

import uuid

import pg_samples as samples
import pytest
from google.api_core import exceptions
from google.cloud.spanner_admin_database_v1.types.common import DatabaseDialect
from test_utils.retry import RetryErrors

CREATE_TABLE_SINGERS = """\
CREATE TABLE Singers (
SingerId BIGINT NOT NULL,
FirstName CHARACTER VARYING(1024),
LastName CHARACTER VARYING(1024),
SingerInfo BYTEA,
FullName CHARACTER VARYING(2048)
GENERATED ALWAYS AS (FirstName || ' ' || LastName) STORED,
PRIMARY KEY (SingerId)
)
"""

CREATE_TABLE_ALBUMS = """\
CREATE TABLE Albums (
SingerId BIGINT NOT NULL,
AlbumId BIGINT NOT NULL,
AlbumTitle CHARACTER VARYING(1024),
PRIMARY KEY (SingerId, AlbumId)
) INTERLEAVE IN PARENT Singers ON DELETE CASCADE
"""

retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15)


@pytest.fixture(scope="module")
def sample_name():
return "pg_snippets"


@pytest.fixture(scope="module")
def database_dialect():
"""Spanner dialect to be used for this sample.
The dialect is used to initialize the dialect for the database.
It can either be GoogleStandardSql or PostgreSql.
"""
return DatabaseDialect.POSTGRESQL


@pytest.fixture(scope="module")
def create_instance_id():
"""Id for the low-cost instance."""
return f"create-instance-{uuid.uuid4().hex[:10]}"


@pytest.fixture(scope="module")
def lci_instance_id():
"""Id for the low-cost instance."""
return f"lci-instance-{uuid.uuid4().hex[:10]}"


@pytest.fixture(scope="module")
def database_id():
return f"test-db-{uuid.uuid4().hex[:10]}"


@pytest.fixture(scope="module")
def create_database_id():
return f"create-db-{uuid.uuid4().hex[:10]}"


@pytest.fixture(scope="module")
def cmek_database_id():
return f"cmek-db-{uuid.uuid4().hex[:10]}"


@pytest.fixture(scope="module")
def default_leader_database_id():
return f"leader_db_{uuid.uuid4().hex[:10]}"


@pytest.fixture(scope="module")
def database_ddl():
"""Sequence of DDL statements used to set up the database.
Sample testcase modules can override as needed.
"""
return [CREATE_TABLE_SINGERS, CREATE_TABLE_ALBUMS]


@pytest.fixture(scope="module")
def default_leader():
"""Default leader for multi-region instances."""
return "us-east4"


@pytest.mark.dependency(name="create_table_with_datatypes")
def test_create_table_with_datatypes(capsys, instance_id, sample_database):
samples.create_table_with_datatypes(instance_id, sample_database.database_id)
out, _ = capsys.readouterr()
assert "Created Venues table on database" in out


@pytest.mark.dependency(name="add_jsonb_column", depends=["insert_datatypes_data"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dependency for this test insert_datatypes_data is not there. How do we validate the sample since it will always get skipped.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated dependency to create_table_with_datatypes since we need tables to be created.

def test_add_jsonb_column(capsys, instance_id, sample_database):
samples.add_jsonb_column(instance_id, sample_database.database_id)
out, _ = capsys.readouterr()
assert "Waiting for operation to complete..." in out
assert 'Altered table "Venues" on database ' in out