diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..330f57d782 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,16 @@ +ARG VARIANT="3.8" +FROM mcr.microsoft.com/devcontainers/python:${VARIANT} + +#install nox +COPY requirements.txt /requirements.txt +RUN python3 -m pip install --upgrade --quiet --require-hashes -r requirements.txt + +# install gh +RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ +&& chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ +&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ +&& apt-get update \ +&& apt-get install gh -y + +# install gloud sdk +RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - && apt-get update -y && apt-get install google-cloud-cli -y diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..7b0126cb8a --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,13 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/python +{ + "name": "Python 3", + "build": { + // Sets the run context to one level up instead of the .devcontainer folder. + "args": { "VARIANT": "3.8" }, + // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. + "dockerfile": "Dockerfile" + }, + + "postCreateCommand": "bash .devcontainer/postCreate.sh" +} diff --git a/.devcontainer/postCreate.sh b/.devcontainer/postCreate.sh new file mode 100644 index 0000000000..3a4cdff317 --- /dev/null +++ b/.devcontainer/postCreate.sh @@ -0,0 +1,3 @@ +echo "Post Create Starting" + +nox -s unit-3.8 \ No newline at end of file diff --git a/.devcontainer/requirements.in b/.devcontainer/requirements.in new file mode 100644 index 0000000000..936886199b --- /dev/null +++ b/.devcontainer/requirements.in @@ -0,0 +1 @@ +nox>=2022.11.21 \ No newline at end of file diff --git a/.devcontainer/requirements.txt b/.devcontainer/requirements.txt new file mode 100644 index 0000000000..a4d4017860 --- /dev/null +++ b/.devcontainer/requirements.txt @@ -0,0 +1,38 @@ +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# pip-compile --generate-hashes requirements.in +# +argcomplete==3.1.1 \ + --hash=sha256:35fa893a88deea85ea7b20d241100e64516d6af6d7b0ae2bed1d263d26f70948 \ + --hash=sha256:6c4c563f14f01440aaffa3eae13441c5db2357b5eec639abe7c0b15334627dff + # via nox +colorlog==6.7.0 \ + --hash=sha256:0d33ca236784a1ba3ff9c532d4964126d8a2c44f1f0cb1d2b0728196f512f662 \ + --hash=sha256:bd94bd21c1e13fac7bd3153f4bc3a7dc0eb0974b8bc2fdf1a989e474f6e582e5 + # via nox +distlib==0.3.7 \ + --hash=sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057 \ + --hash=sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8 + # via virtualenv +filelock==3.12.2 \ + --hash=sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81 \ + --hash=sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec + # via virtualenv +nox==2023.4.22 \ + --hash=sha256:0b1adc619c58ab4fa57d6ab2e7823fe47a32e70202f287d78474adcc7bda1891 \ + --hash=sha256:46c0560b0dc609d7d967dc99e22cb463d3c4caf54a5fda735d6c11b5177e3a9f + # via -r requirements.in +packaging==23.1 \ + --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ + --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f + # via nox +platformdirs==3.9.1 \ + --hash=sha256:1b42b450ad933e981d56e59f1b97495428c9bd60698baab9f3eb3d00d5822421 \ + --hash=sha256:ad8291ae0ae5072f66c16945166cb11c63394c7a3ad1b1bc9828ca3162da8c2f + # via virtualenv +virtualenv==20.24.1 \ + --hash=sha256:01aacf8decd346cf9a865ae85c0cdc7f64c8caa07ff0d8b1dfc1733d10677442 \ + --hash=sha256:2ef6a237c31629da6442b0bcaa3999748108c7166318d1f55cc9f8d7294e97bd + # via nox diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 17c21d96d6..a3da1b0d4c 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:f8ca7655fa8a449cadcabcbce4054f593dcbae7aeeab34aa3fcc8b5cf7a93c9e -# created: 2023-07-21T02:12:46.49799314Z + digest: sha256:3e3800bb100af5d7f9e810d48212b37812c1856d20ffeafb99ebe66461b61fc7 +# created: 2023-08-02T10:53:29.114535628Z diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml index fc2092ed7f..68b2d1df54 100644 --- a/.github/blunderbuss.yml +++ b/.github/blunderbuss.yml @@ -1,2 +1,2 @@ assign_issues: - - asthamohta + - harshachinta diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index b563eb2844..029bd342de 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -20,9 +20,9 @@ cachetools==5.2.0 \ --hash=sha256:6a94c6402995a99c3970cc7e4884bb60b4a8639938157eeed436098bf9831757 \ --hash=sha256:f9f17d2aec496a9aa6b76f53e3b614c965223c061982d434d160f930c698a9db # via google-auth -certifi==2022.12.7 \ - --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ - --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 +certifi==2023.7.22 \ + --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ + --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9 # via requests cffi==1.15.1 \ --hash=sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5 \ @@ -113,30 +113,30 @@ commonmark==0.9.1 \ --hash=sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60 \ --hash=sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9 # via rich -cryptography==41.0.2 \ - --hash=sha256:01f1d9e537f9a15b037d5d9ee442b8c22e3ae11ce65ea1f3316a41c78756b711 \ - --hash=sha256:079347de771f9282fbfe0e0236c716686950c19dee1b76240ab09ce1624d76d7 \ - --hash=sha256:182be4171f9332b6741ee818ec27daff9fb00349f706629f5cbf417bd50e66fd \ - --hash=sha256:192255f539d7a89f2102d07d7375b1e0a81f7478925b3bc2e0549ebf739dae0e \ - --hash=sha256:2a034bf7d9ca894720f2ec1d8b7b5832d7e363571828037f9e0c4f18c1b58a58 \ - --hash=sha256:342f3767e25876751e14f8459ad85e77e660537ca0a066e10e75df9c9e9099f0 \ - --hash=sha256:439c3cc4c0d42fa999b83ded80a9a1fb54d53c58d6e59234cfe97f241e6c781d \ - --hash=sha256:49c3222bb8f8e800aead2e376cbef687bc9e3cb9b58b29a261210456a7783d83 \ - --hash=sha256:674b669d5daa64206c38e507808aae49904c988fa0a71c935e7006a3e1e83831 \ - --hash=sha256:7a9a3bced53b7f09da251685224d6a260c3cb291768f54954e28f03ef14e3766 \ - --hash=sha256:7af244b012711a26196450d34f483357e42aeddb04128885d95a69bd8b14b69b \ - --hash=sha256:7d230bf856164de164ecb615ccc14c7fc6de6906ddd5b491f3af90d3514c925c \ - --hash=sha256:84609ade00a6ec59a89729e87a503c6e36af98ddcd566d5f3be52e29ba993182 \ - --hash=sha256:9a6673c1828db6270b76b22cc696f40cde9043eb90373da5c2f8f2158957f42f \ - --hash=sha256:9b6d717393dbae53d4e52684ef4f022444fc1cce3c48c38cb74fca29e1f08eaa \ - --hash=sha256:9c3fe6534d59d071ee82081ca3d71eed3210f76ebd0361798c74abc2bcf347d4 \ - --hash=sha256:a719399b99377b218dac6cf547b6ec54e6ef20207b6165126a280b0ce97e0d2a \ - --hash=sha256:b332cba64d99a70c1e0836902720887fb4529ea49ea7f5462cf6640e095e11d2 \ - --hash=sha256:d124682c7a23c9764e54ca9ab5b308b14b18eba02722b8659fb238546de83a76 \ - --hash=sha256:d73f419a56d74fef257955f51b18d046f3506270a5fd2ac5febbfa259d6c0fa5 \ - --hash=sha256:f0dc40e6f7aa37af01aba07277d3d64d5a03dc66d682097541ec4da03cc140ee \ - --hash=sha256:f14ad275364c8b4e525d018f6716537ae7b6d369c094805cae45300847e0894f \ - --hash=sha256:f772610fe364372de33d76edcd313636a25684edb94cee53fd790195f5989d14 +cryptography==41.0.3 \ + --hash=sha256:0d09fb5356f975974dbcb595ad2d178305e5050656affb7890a1583f5e02a306 \ + --hash=sha256:23c2d778cf829f7d0ae180600b17e9fceea3c2ef8b31a99e3c694cbbf3a24b84 \ + --hash=sha256:3fb248989b6363906827284cd20cca63bb1a757e0a2864d4c1682a985e3dca47 \ + --hash=sha256:41d7aa7cdfded09b3d73a47f429c298e80796c8e825ddfadc84c8a7f12df212d \ + --hash=sha256:42cb413e01a5d36da9929baa9d70ca90d90b969269e5a12d39c1e0d475010116 \ + --hash=sha256:4c2f0d35703d61002a2bbdcf15548ebb701cfdd83cdc12471d2bae80878a4207 \ + --hash=sha256:4fd871184321100fb400d759ad0cddddf284c4b696568204d281c902fc7b0d81 \ + --hash=sha256:5259cb659aa43005eb55a0e4ff2c825ca111a0da1814202c64d28a985d33b087 \ + --hash=sha256:57a51b89f954f216a81c9d057bf1a24e2f36e764a1ca9a501a6964eb4a6800dd \ + --hash=sha256:652627a055cb52a84f8c448185922241dd5217443ca194d5739b44612c5e6507 \ + --hash=sha256:67e120e9a577c64fe1f611e53b30b3e69744e5910ff3b6e97e935aeb96005858 \ + --hash=sha256:6af1c6387c531cd364b72c28daa29232162010d952ceb7e5ca8e2827526aceae \ + --hash=sha256:6d192741113ef5e30d89dcb5b956ef4e1578f304708701b8b73d38e3e1461f34 \ + --hash=sha256:7efe8041897fe7a50863e51b77789b657a133c75c3b094e51b5e4b5cec7bf906 \ + --hash=sha256:84537453d57f55a50a5b6835622ee405816999a7113267739a1b4581f83535bd \ + --hash=sha256:8f09daa483aedea50d249ef98ed500569841d6498aa9c9f4b0531b9964658922 \ + --hash=sha256:95dd7f261bb76948b52a5330ba5202b91a26fbac13ad0e9fc8a3ac04752058c7 \ + --hash=sha256:a74fbcdb2a0d46fe00504f571a2a540532f4c188e6ccf26f1f178480117b33c4 \ + --hash=sha256:a983e441a00a9d57a4d7c91b3116a37ae602907a7618b882c8013b5762e80574 \ + --hash=sha256:ab8de0d091acbf778f74286f4989cf3d1528336af1b59f3e5d2ebca8b5fe49e1 \ + --hash=sha256:aeb57c421b34af8f9fe830e1955bf493a86a7996cc1338fe41b30047d16e962c \ + --hash=sha256:ce785cf81a7bdade534297ef9e490ddff800d956625020ab2ec2780a556c313e \ + --hash=sha256:d0d651aa754ef58d75cec6edfbd21259d93810b73f6ec246436a21b7841908de # via # gcp-releasetool # secretstorage diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9e3898fd1c..19409cbd37 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,6 +26,6 @@ repos: hooks: - id: black - repo: https://github.com/pycqa/flake8 - rev: 3.9.2 + rev: 6.1.0 hooks: - id: flake8 diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9f49ec500c..051da005ab 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.38.0" + ".": "3.39.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index f8f39f053a..27f441b82d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ [1]: https://pypi.org/project/google-cloud-spanner/#history +## [3.39.0](https://github.com/googleapis/python-spanner/compare/v3.38.0...v3.39.0) (2023-08-02) + + +### Features + +* Foreign key on delete cascade action testing and samples ([#910](https://github.com/googleapis/python-spanner/issues/910)) ([681c8ee](https://github.com/googleapis/python-spanner/commit/681c8eead40582addf75e02c159ea1ff9d6de85e)) + + +### Documentation + +* Minor formatting ([#991](https://github.com/googleapis/python-spanner/issues/991)) ([60efc42](https://github.com/googleapis/python-spanner/commit/60efc426cf26c4863d81743a5545c5f296308815)) + ## [3.38.0](https://github.com/googleapis/python-spanner/compare/v3.37.0...v3.38.0) (2023-07-21) diff --git a/google/cloud/spanner_admin_database_v1/gapic_version.py b/google/cloud/spanner_admin_database_v1/gapic_version.py index e0c31c2ce4..51483c89bf 100644 --- a/google/cloud/spanner_admin_database_v1/gapic_version.py +++ b/google/cloud/spanner_admin_database_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "3.38.0" # {x-release-please-version} +__version__ = "3.39.0" # {x-release-please-version} diff --git a/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py b/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py index fa0d9a059c..4cd1d4756a 100644 --- a/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py +++ b/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py @@ -1293,42 +1293,11 @@ async def sample_set_iam_policy(): **JSON example:** - { - "bindings": [ - { - "role": - "roles/resourcemanager.organizationAdmin", - "members": [ "user:mike@example.com", - "group:admins@example.com", - "domain:google.com", - "serviceAccount:my-project-id@appspot.gserviceaccount.com" - ] - - }, { "role": - "roles/resourcemanager.organizationViewer", - "members": [ "user:eve@example.com" ], - "condition": { "title": "expirable access", - "description": "Does not grant access after - Sep 2020", "expression": "request.time < - timestamp('2020-10-01T00:00:00.000Z')", } } - - ], "etag": "BwWWja0YfJA=", "version": 3 - - } + :literal:`\` { "bindings": [ { "role": "roles/resourcemanager.organizationAdmin", "members": [ "user:mike@example.com", "group:admins@example.com", "domain:google.com", "serviceAccount:my-project-id@appspot.gserviceaccount.com" ] }, { "role": "roles/resourcemanager.organizationViewer", "members": [ "user:eve@example.com" ], "condition": { "title": "expirable access", "description": "Does not grant access after Sep 2020", "expression": "request.time < timestamp('2020-10-01T00:00:00.000Z')", } } ], "etag": "BwWWja0YfJA=", "version": 3 }`\ \` **YAML example:** - bindings: - members: - user:\ mike@example.com - - group:\ admins@example.com - domain:google.com - - serviceAccount:\ my-project-id@appspot.gserviceaccount.com - role: roles/resourcemanager.organizationAdmin - - members: - user:\ eve@example.com role: - roles/resourcemanager.organizationViewer - condition: title: expirable access description: - Does not grant access after Sep 2020 expression: - request.time < - timestamp('2020-10-01T00:00:00.000Z') etag: - BwWWja0YfJA= version: 3 + :literal:`\` bindings: - members: - user:mike@example.com - group:admins@example.com - domain:google.com - serviceAccount:my-project-id@appspot.gserviceaccount.com role: roles/resourcemanager.organizationAdmin - members: - user:eve@example.com role: roles/resourcemanager.organizationViewer condition: title: expirable access description: Does not grant access after Sep 2020 expression: request.time < timestamp('2020-10-01T00:00:00.000Z') etag: BwWWja0YfJA= version: 3`\ \` For a description of IAM and its features, see the [IAM @@ -1467,42 +1436,11 @@ async def sample_get_iam_policy(): **JSON example:** - { - "bindings": [ - { - "role": - "roles/resourcemanager.organizationAdmin", - "members": [ "user:mike@example.com", - "group:admins@example.com", - "domain:google.com", - "serviceAccount:my-project-id@appspot.gserviceaccount.com" - ] - - }, { "role": - "roles/resourcemanager.organizationViewer", - "members": [ "user:eve@example.com" ], - "condition": { "title": "expirable access", - "description": "Does not grant access after - Sep 2020", "expression": "request.time < - timestamp('2020-10-01T00:00:00.000Z')", } } - - ], "etag": "BwWWja0YfJA=", "version": 3 - - } + :literal:`\` { "bindings": [ { "role": "roles/resourcemanager.organizationAdmin", "members": [ "user:mike@example.com", "group:admins@example.com", "domain:google.com", "serviceAccount:my-project-id@appspot.gserviceaccount.com" ] }, { "role": "roles/resourcemanager.organizationViewer", "members": [ "user:eve@example.com" ], "condition": { "title": "expirable access", "description": "Does not grant access after Sep 2020", "expression": "request.time < timestamp('2020-10-01T00:00:00.000Z')", } } ], "etag": "BwWWja0YfJA=", "version": 3 }`\ \` **YAML example:** - bindings: - members: - user:\ mike@example.com - - group:\ admins@example.com - domain:google.com - - serviceAccount:\ my-project-id@appspot.gserviceaccount.com - role: roles/resourcemanager.organizationAdmin - - members: - user:\ eve@example.com role: - roles/resourcemanager.organizationViewer - condition: title: expirable access description: - Does not grant access after Sep 2020 expression: - request.time < - timestamp('2020-10-01T00:00:00.000Z') etag: - BwWWja0YfJA= version: 3 + :literal:`\` bindings: - members: - user:mike@example.com - group:admins@example.com - domain:google.com - serviceAccount:my-project-id@appspot.gserviceaccount.com role: roles/resourcemanager.organizationAdmin - members: - user:eve@example.com role: roles/resourcemanager.organizationViewer condition: title: expirable access description: Does not grant access after Sep 2020 expression: request.time < timestamp('2020-10-01T00:00:00.000Z') etag: BwWWja0YfJA= version: 3`\ \` For a description of IAM and its features, see the [IAM diff --git a/google/cloud/spanner_admin_database_v1/services/database_admin/client.py b/google/cloud/spanner_admin_database_v1/services/database_admin/client.py index f41c0ec86a..b6f2d1f1e7 100644 --- a/google/cloud/spanner_admin_database_v1/services/database_admin/client.py +++ b/google/cloud/spanner_admin_database_v1/services/database_admin/client.py @@ -1559,42 +1559,11 @@ def sample_set_iam_policy(): **JSON example:** - { - "bindings": [ - { - "role": - "roles/resourcemanager.organizationAdmin", - "members": [ "user:mike@example.com", - "group:admins@example.com", - "domain:google.com", - "serviceAccount:my-project-id@appspot.gserviceaccount.com" - ] - - }, { "role": - "roles/resourcemanager.organizationViewer", - "members": [ "user:eve@example.com" ], - "condition": { "title": "expirable access", - "description": "Does not grant access after - Sep 2020", "expression": "request.time < - timestamp('2020-10-01T00:00:00.000Z')", } } - - ], "etag": "BwWWja0YfJA=", "version": 3 - - } + :literal:`\` { "bindings": [ { "role": "roles/resourcemanager.organizationAdmin", "members": [ "user:mike@example.com", "group:admins@example.com", "domain:google.com", "serviceAccount:my-project-id@appspot.gserviceaccount.com" ] }, { "role": "roles/resourcemanager.organizationViewer", "members": [ "user:eve@example.com" ], "condition": { "title": "expirable access", "description": "Does not grant access after Sep 2020", "expression": "request.time < timestamp('2020-10-01T00:00:00.000Z')", } } ], "etag": "BwWWja0YfJA=", "version": 3 }`\ \` **YAML example:** - bindings: - members: - user:\ mike@example.com - - group:\ admins@example.com - domain:google.com - - serviceAccount:\ my-project-id@appspot.gserviceaccount.com - role: roles/resourcemanager.organizationAdmin - - members: - user:\ eve@example.com role: - roles/resourcemanager.organizationViewer - condition: title: expirable access description: - Does not grant access after Sep 2020 expression: - request.time < - timestamp('2020-10-01T00:00:00.000Z') etag: - BwWWja0YfJA= version: 3 + :literal:`\` bindings: - members: - user:mike@example.com - group:admins@example.com - domain:google.com - serviceAccount:my-project-id@appspot.gserviceaccount.com role: roles/resourcemanager.organizationAdmin - members: - user:eve@example.com role: roles/resourcemanager.organizationViewer condition: title: expirable access description: Does not grant access after Sep 2020 expression: request.time < timestamp('2020-10-01T00:00:00.000Z') etag: BwWWja0YfJA= version: 3`\ \` For a description of IAM and its features, see the [IAM @@ -1730,42 +1699,11 @@ def sample_get_iam_policy(): **JSON example:** - { - "bindings": [ - { - "role": - "roles/resourcemanager.organizationAdmin", - "members": [ "user:mike@example.com", - "group:admins@example.com", - "domain:google.com", - "serviceAccount:my-project-id@appspot.gserviceaccount.com" - ] - - }, { "role": - "roles/resourcemanager.organizationViewer", - "members": [ "user:eve@example.com" ], - "condition": { "title": "expirable access", - "description": "Does not grant access after - Sep 2020", "expression": "request.time < - timestamp('2020-10-01T00:00:00.000Z')", } } - - ], "etag": "BwWWja0YfJA=", "version": 3 - - } + :literal:`\` { "bindings": [ { "role": "roles/resourcemanager.organizationAdmin", "members": [ "user:mike@example.com", "group:admins@example.com", "domain:google.com", "serviceAccount:my-project-id@appspot.gserviceaccount.com" ] }, { "role": "roles/resourcemanager.organizationViewer", "members": [ "user:eve@example.com" ], "condition": { "title": "expirable access", "description": "Does not grant access after Sep 2020", "expression": "request.time < timestamp('2020-10-01T00:00:00.000Z')", } } ], "etag": "BwWWja0YfJA=", "version": 3 }`\ \` **YAML example:** - bindings: - members: - user:\ mike@example.com - - group:\ admins@example.com - domain:google.com - - serviceAccount:\ my-project-id@appspot.gserviceaccount.com - role: roles/resourcemanager.organizationAdmin - - members: - user:\ eve@example.com role: - roles/resourcemanager.organizationViewer - condition: title: expirable access description: - Does not grant access after Sep 2020 expression: - request.time < - timestamp('2020-10-01T00:00:00.000Z') etag: - BwWWja0YfJA= version: 3 + :literal:`\` bindings: - members: - user:mike@example.com - group:admins@example.com - domain:google.com - serviceAccount:my-project-id@appspot.gserviceaccount.com role: roles/resourcemanager.organizationAdmin - members: - user:eve@example.com role: roles/resourcemanager.organizationViewer condition: title: expirable access description: Does not grant access after Sep 2020 expression: request.time < timestamp('2020-10-01T00:00:00.000Z') etag: BwWWja0YfJA= version: 3`\ \` For a description of IAM and its features, see the [IAM diff --git a/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py b/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py index b210297f8c..bd35307fcc 100644 --- a/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py +++ b/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py @@ -1725,54 +1725,54 @@ def __call__( :: - { - "bindings": [ - { - "role": "roles/resourcemanager.organizationAdmin", - "members": [ - "user:mike@example.com", - "group:admins@example.com", - "domain:google.com", - "serviceAccount:my-project-id@appspot.gserviceaccount.com" - ] - }, - { - "role": "roles/resourcemanager.organizationViewer", - "members": [ - "user:eve@example.com" - ], - "condition": { - "title": "expirable access", - "description": "Does not grant access after Sep 2020", - "expression": "request.time < - timestamp('2020-10-01T00:00:00.000Z')", - } - } - ], - "etag": "BwWWja0YfJA=", - "version": 3 - } + { + "bindings": [ + { + "role": "roles/resourcemanager.organizationAdmin", + "members": [ + "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + }, + { + "role": "roles/resourcemanager.organizationViewer", + "members": [ + "user:eve@example.com" + ], + "condition": { + "title": "expirable access", + "description": "Does not grant access after Sep 2020", + "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", + } + } + ], + "etag": "BwWWja0YfJA=", + "version": 3 + } **YAML example:** :: - bindings: - - members: - - user:mike@example.com - - group:admins@example.com - - domain:google.com - - serviceAccount:my-project-id@appspot.gserviceaccount.com - role: roles/resourcemanager.organizationAdmin - - members: - - user:eve@example.com - role: roles/resourcemanager.organizationViewer - condition: - title: expirable access - description: Does not grant access after Sep 2020 - expression: request.time < timestamp('2020-10-01T00:00:00.000Z') - etag: BwWWja0YfJA= - version: 3 + bindings: + - members: + - user:mike@example.com + - group:admins@example.com + - domain:google.com + - serviceAccount:my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin + - members: + - user:eve@example.com + role: roles/resourcemanager.organizationViewer + condition: + title: expirable access + description: Does not grant access after Sep 2020 + expression: request.time < timestamp('2020-10-01T00:00:00.000Z') + etag: BwWWja0YfJA= + version: 3 For a description of IAM and its features, see the `IAM documentation `__. @@ -2452,54 +2452,54 @@ def __call__( :: - { - "bindings": [ - { - "role": "roles/resourcemanager.organizationAdmin", - "members": [ - "user:mike@example.com", - "group:admins@example.com", - "domain:google.com", - "serviceAccount:my-project-id@appspot.gserviceaccount.com" - ] - }, - { - "role": "roles/resourcemanager.organizationViewer", - "members": [ - "user:eve@example.com" - ], - "condition": { - "title": "expirable access", - "description": "Does not grant access after Sep 2020", - "expression": "request.time < - timestamp('2020-10-01T00:00:00.000Z')", - } - } - ], - "etag": "BwWWja0YfJA=", - "version": 3 - } + { + "bindings": [ + { + "role": "roles/resourcemanager.organizationAdmin", + "members": [ + "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + }, + { + "role": "roles/resourcemanager.organizationViewer", + "members": [ + "user:eve@example.com" + ], + "condition": { + "title": "expirable access", + "description": "Does not grant access after Sep 2020", + "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", + } + } + ], + "etag": "BwWWja0YfJA=", + "version": 3 + } **YAML example:** :: - bindings: - - members: - - user:mike@example.com - - group:admins@example.com - - domain:google.com - - serviceAccount:my-project-id@appspot.gserviceaccount.com - role: roles/resourcemanager.organizationAdmin - - members: - - user:eve@example.com - role: roles/resourcemanager.organizationViewer - condition: - title: expirable access - description: Does not grant access after Sep 2020 - expression: request.time < timestamp('2020-10-01T00:00:00.000Z') - etag: BwWWja0YfJA= - version: 3 + bindings: + - members: + - user:mike@example.com + - group:admins@example.com + - domain:google.com + - serviceAccount:my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin + - members: + - user:eve@example.com + role: roles/resourcemanager.organizationViewer + condition: + title: expirable access + description: Does not grant access after Sep 2020 + expression: request.time < timestamp('2020-10-01T00:00:00.000Z') + etag: BwWWja0YfJA= + version: 3 For a description of IAM and its features, see the `IAM documentation `__. diff --git a/google/cloud/spanner_admin_instance_v1/gapic_version.py b/google/cloud/spanner_admin_instance_v1/gapic_version.py index e0c31c2ce4..51483c89bf 100644 --- a/google/cloud/spanner_admin_instance_v1/gapic_version.py +++ b/google/cloud/spanner_admin_instance_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "3.38.0" # {x-release-please-version} +__version__ = "3.39.0" # {x-release-please-version} diff --git a/google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py b/google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py index b523f171dc..f6dbc4e73d 100644 --- a/google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py +++ b/google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py @@ -1915,42 +1915,11 @@ async def sample_set_iam_policy(): **JSON example:** - { - "bindings": [ - { - "role": - "roles/resourcemanager.organizationAdmin", - "members": [ "user:mike@example.com", - "group:admins@example.com", - "domain:google.com", - "serviceAccount:my-project-id@appspot.gserviceaccount.com" - ] - - }, { "role": - "roles/resourcemanager.organizationViewer", - "members": [ "user:eve@example.com" ], - "condition": { "title": "expirable access", - "description": "Does not grant access after - Sep 2020", "expression": "request.time < - timestamp('2020-10-01T00:00:00.000Z')", } } - - ], "etag": "BwWWja0YfJA=", "version": 3 - - } + :literal:`\` { "bindings": [ { "role": "roles/resourcemanager.organizationAdmin", "members": [ "user:mike@example.com", "group:admins@example.com", "domain:google.com", "serviceAccount:my-project-id@appspot.gserviceaccount.com" ] }, { "role": "roles/resourcemanager.organizationViewer", "members": [ "user:eve@example.com" ], "condition": { "title": "expirable access", "description": "Does not grant access after Sep 2020", "expression": "request.time < timestamp('2020-10-01T00:00:00.000Z')", } } ], "etag": "BwWWja0YfJA=", "version": 3 }`\ \` **YAML example:** - bindings: - members: - user:\ mike@example.com - - group:\ admins@example.com - domain:google.com - - serviceAccount:\ my-project-id@appspot.gserviceaccount.com - role: roles/resourcemanager.organizationAdmin - - members: - user:\ eve@example.com role: - roles/resourcemanager.organizationViewer - condition: title: expirable access description: - Does not grant access after Sep 2020 expression: - request.time < - timestamp('2020-10-01T00:00:00.000Z') etag: - BwWWja0YfJA= version: 3 + :literal:`\` bindings: - members: - user:mike@example.com - group:admins@example.com - domain:google.com - serviceAccount:my-project-id@appspot.gserviceaccount.com role: roles/resourcemanager.organizationAdmin - members: - user:eve@example.com role: roles/resourcemanager.organizationViewer condition: title: expirable access description: Does not grant access after Sep 2020 expression: request.time < timestamp('2020-10-01T00:00:00.000Z') etag: BwWWja0YfJA= version: 3`\ \` For a description of IAM and its features, see the [IAM @@ -2085,42 +2054,11 @@ async def sample_get_iam_policy(): **JSON example:** - { - "bindings": [ - { - "role": - "roles/resourcemanager.organizationAdmin", - "members": [ "user:mike@example.com", - "group:admins@example.com", - "domain:google.com", - "serviceAccount:my-project-id@appspot.gserviceaccount.com" - ] - - }, { "role": - "roles/resourcemanager.organizationViewer", - "members": [ "user:eve@example.com" ], - "condition": { "title": "expirable access", - "description": "Does not grant access after - Sep 2020", "expression": "request.time < - timestamp('2020-10-01T00:00:00.000Z')", } } - - ], "etag": "BwWWja0YfJA=", "version": 3 - - } + :literal:`\` { "bindings": [ { "role": "roles/resourcemanager.organizationAdmin", "members": [ "user:mike@example.com", "group:admins@example.com", "domain:google.com", "serviceAccount:my-project-id@appspot.gserviceaccount.com" ] }, { "role": "roles/resourcemanager.organizationViewer", "members": [ "user:eve@example.com" ], "condition": { "title": "expirable access", "description": "Does not grant access after Sep 2020", "expression": "request.time < timestamp('2020-10-01T00:00:00.000Z')", } } ], "etag": "BwWWja0YfJA=", "version": 3 }`\ \` **YAML example:** - bindings: - members: - user:\ mike@example.com - - group:\ admins@example.com - domain:google.com - - serviceAccount:\ my-project-id@appspot.gserviceaccount.com - role: roles/resourcemanager.organizationAdmin - - members: - user:\ eve@example.com role: - roles/resourcemanager.organizationViewer - condition: title: expirable access description: - Does not grant access after Sep 2020 expression: - request.time < - timestamp('2020-10-01T00:00:00.000Z') etag: - BwWWja0YfJA= version: 3 + :literal:`\` bindings: - members: - user:mike@example.com - group:admins@example.com - domain:google.com - serviceAccount:my-project-id@appspot.gserviceaccount.com role: roles/resourcemanager.organizationAdmin - members: - user:eve@example.com role: roles/resourcemanager.organizationViewer condition: title: expirable access description: Does not grant access after Sep 2020 expression: request.time < timestamp('2020-10-01T00:00:00.000Z') etag: BwWWja0YfJA= version: 3`\ \` For a description of IAM and its features, see the [IAM diff --git a/google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py b/google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py index 1245c2554e..dd94cacafb 100644 --- a/google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py +++ b/google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py @@ -2109,42 +2109,11 @@ def sample_set_iam_policy(): **JSON example:** - { - "bindings": [ - { - "role": - "roles/resourcemanager.organizationAdmin", - "members": [ "user:mike@example.com", - "group:admins@example.com", - "domain:google.com", - "serviceAccount:my-project-id@appspot.gserviceaccount.com" - ] - - }, { "role": - "roles/resourcemanager.organizationViewer", - "members": [ "user:eve@example.com" ], - "condition": { "title": "expirable access", - "description": "Does not grant access after - Sep 2020", "expression": "request.time < - timestamp('2020-10-01T00:00:00.000Z')", } } - - ], "etag": "BwWWja0YfJA=", "version": 3 - - } + :literal:`\` { "bindings": [ { "role": "roles/resourcemanager.organizationAdmin", "members": [ "user:mike@example.com", "group:admins@example.com", "domain:google.com", "serviceAccount:my-project-id@appspot.gserviceaccount.com" ] }, { "role": "roles/resourcemanager.organizationViewer", "members": [ "user:eve@example.com" ], "condition": { "title": "expirable access", "description": "Does not grant access after Sep 2020", "expression": "request.time < timestamp('2020-10-01T00:00:00.000Z')", } } ], "etag": "BwWWja0YfJA=", "version": 3 }`\ \` **YAML example:** - bindings: - members: - user:\ mike@example.com - - group:\ admins@example.com - domain:google.com - - serviceAccount:\ my-project-id@appspot.gserviceaccount.com - role: roles/resourcemanager.organizationAdmin - - members: - user:\ eve@example.com role: - roles/resourcemanager.organizationViewer - condition: title: expirable access description: - Does not grant access after Sep 2020 expression: - request.time < - timestamp('2020-10-01T00:00:00.000Z') etag: - BwWWja0YfJA= version: 3 + :literal:`\` bindings: - members: - user:mike@example.com - group:admins@example.com - domain:google.com - serviceAccount:my-project-id@appspot.gserviceaccount.com role: roles/resourcemanager.organizationAdmin - members: - user:eve@example.com role: roles/resourcemanager.organizationViewer condition: title: expirable access description: Does not grant access after Sep 2020 expression: request.time < timestamp('2020-10-01T00:00:00.000Z') etag: BwWWja0YfJA= version: 3`\ \` For a description of IAM and its features, see the [IAM @@ -2276,42 +2245,11 @@ def sample_get_iam_policy(): **JSON example:** - { - "bindings": [ - { - "role": - "roles/resourcemanager.organizationAdmin", - "members": [ "user:mike@example.com", - "group:admins@example.com", - "domain:google.com", - "serviceAccount:my-project-id@appspot.gserviceaccount.com" - ] - - }, { "role": - "roles/resourcemanager.organizationViewer", - "members": [ "user:eve@example.com" ], - "condition": { "title": "expirable access", - "description": "Does not grant access after - Sep 2020", "expression": "request.time < - timestamp('2020-10-01T00:00:00.000Z')", } } - - ], "etag": "BwWWja0YfJA=", "version": 3 - - } + :literal:`\` { "bindings": [ { "role": "roles/resourcemanager.organizationAdmin", "members": [ "user:mike@example.com", "group:admins@example.com", "domain:google.com", "serviceAccount:my-project-id@appspot.gserviceaccount.com" ] }, { "role": "roles/resourcemanager.organizationViewer", "members": [ "user:eve@example.com" ], "condition": { "title": "expirable access", "description": "Does not grant access after Sep 2020", "expression": "request.time < timestamp('2020-10-01T00:00:00.000Z')", } } ], "etag": "BwWWja0YfJA=", "version": 3 }`\ \` **YAML example:** - bindings: - members: - user:\ mike@example.com - - group:\ admins@example.com - domain:google.com - - serviceAccount:\ my-project-id@appspot.gserviceaccount.com - role: roles/resourcemanager.organizationAdmin - - members: - user:\ eve@example.com role: - roles/resourcemanager.organizationViewer - condition: title: expirable access description: - Does not grant access after Sep 2020 expression: - request.time < - timestamp('2020-10-01T00:00:00.000Z') etag: - BwWWja0YfJA= version: 3 + :literal:`\` bindings: - members: - user:mike@example.com - group:admins@example.com - domain:google.com - serviceAccount:my-project-id@appspot.gserviceaccount.com role: roles/resourcemanager.organizationAdmin - members: - user:eve@example.com role: roles/resourcemanager.organizationViewer condition: title: expirable access description: Does not grant access after Sep 2020 expression: request.time < timestamp('2020-10-01T00:00:00.000Z') etag: BwWWja0YfJA= version: 3`\ \` For a description of IAM and its features, see the [IAM diff --git a/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py b/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py index 808a3bfd1d..c743fa011d 100644 --- a/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py +++ b/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py @@ -1086,54 +1086,54 @@ def __call__( :: - { - "bindings": [ - { - "role": "roles/resourcemanager.organizationAdmin", - "members": [ - "user:mike@example.com", - "group:admins@example.com", - "domain:google.com", - "serviceAccount:my-project-id@appspot.gserviceaccount.com" - ] - }, - { - "role": "roles/resourcemanager.organizationViewer", - "members": [ - "user:eve@example.com" - ], - "condition": { - "title": "expirable access", - "description": "Does not grant access after Sep 2020", - "expression": "request.time < - timestamp('2020-10-01T00:00:00.000Z')", - } - } - ], - "etag": "BwWWja0YfJA=", - "version": 3 - } + { + "bindings": [ + { + "role": "roles/resourcemanager.organizationAdmin", + "members": [ + "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + }, + { + "role": "roles/resourcemanager.organizationViewer", + "members": [ + "user:eve@example.com" + ], + "condition": { + "title": "expirable access", + "description": "Does not grant access after Sep 2020", + "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", + } + } + ], + "etag": "BwWWja0YfJA=", + "version": 3 + } **YAML example:** :: - bindings: - - members: - - user:mike@example.com - - group:admins@example.com - - domain:google.com - - serviceAccount:my-project-id@appspot.gserviceaccount.com - role: roles/resourcemanager.organizationAdmin - - members: - - user:eve@example.com - role: roles/resourcemanager.organizationViewer - condition: - title: expirable access - description: Does not grant access after Sep 2020 - expression: request.time < timestamp('2020-10-01T00:00:00.000Z') - etag: BwWWja0YfJA= - version: 3 + bindings: + - members: + - user:mike@example.com + - group:admins@example.com + - domain:google.com + - serviceAccount:my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin + - members: + - user:eve@example.com + role: roles/resourcemanager.organizationViewer + condition: + title: expirable access + description: Does not grant access after Sep 2020 + expression: request.time < timestamp('2020-10-01T00:00:00.000Z') + etag: BwWWja0YfJA= + version: 3 For a description of IAM and its features, see the `IAM documentation `__. @@ -1715,54 +1715,54 @@ def __call__( :: - { - "bindings": [ - { - "role": "roles/resourcemanager.organizationAdmin", - "members": [ - "user:mike@example.com", - "group:admins@example.com", - "domain:google.com", - "serviceAccount:my-project-id@appspot.gserviceaccount.com" - ] - }, - { - "role": "roles/resourcemanager.organizationViewer", - "members": [ - "user:eve@example.com" - ], - "condition": { - "title": "expirable access", - "description": "Does not grant access after Sep 2020", - "expression": "request.time < - timestamp('2020-10-01T00:00:00.000Z')", - } - } - ], - "etag": "BwWWja0YfJA=", - "version": 3 - } + { + "bindings": [ + { + "role": "roles/resourcemanager.organizationAdmin", + "members": [ + "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + }, + { + "role": "roles/resourcemanager.organizationViewer", + "members": [ + "user:eve@example.com" + ], + "condition": { + "title": "expirable access", + "description": "Does not grant access after Sep 2020", + "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", + } + } + ], + "etag": "BwWWja0YfJA=", + "version": 3 + } **YAML example:** :: - bindings: - - members: - - user:mike@example.com - - group:admins@example.com - - domain:google.com - - serviceAccount:my-project-id@appspot.gserviceaccount.com - role: roles/resourcemanager.organizationAdmin - - members: - - user:eve@example.com - role: roles/resourcemanager.organizationViewer - condition: - title: expirable access - description: Does not grant access after Sep 2020 - expression: request.time < timestamp('2020-10-01T00:00:00.000Z') - etag: BwWWja0YfJA= - version: 3 + bindings: + - members: + - user:mike@example.com + - group:admins@example.com + - domain:google.com + - serviceAccount:my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin + - members: + - user:eve@example.com + role: roles/resourcemanager.organizationViewer + condition: + title: expirable access + description: Does not grant access after Sep 2020 + expression: request.time < timestamp('2020-10-01T00:00:00.000Z') + etag: BwWWja0YfJA= + version: 3 For a description of IAM and its features, see the `IAM documentation `__. diff --git a/google/cloud/spanner_dbapi/parser.py b/google/cloud/spanner_dbapi/parser.py index 1d84daa531..f5c1d0edf7 100644 --- a/google/cloud/spanner_dbapi/parser.py +++ b/google/cloud/spanner_dbapi/parser.py @@ -52,7 +52,7 @@ def __repr__(self): return self.__str__() def __eq__(self, other): - if type(self) != type(other): + if type(self) is not type(other): return False if self.name != other.name: return False @@ -95,7 +95,7 @@ def __len__(self): return len(self.argv) def __eq__(self, other): - if type(self) != type(other): + if type(self) is not type(other): return False if len(self) != len(other): diff --git a/google/cloud/spanner_v1/_helpers.py b/google/cloud/spanner_v1/_helpers.py index 4f708b20cf..e0e2bfdbd0 100644 --- a/google/cloud/spanner_v1/_helpers.py +++ b/google/cloud/spanner_v1/_helpers.py @@ -81,7 +81,7 @@ def _merge_query_options(base, merge): If the resultant object only has empty fields, returns None. """ combined = base or ExecuteSqlRequest.QueryOptions() - if type(combined) == dict: + if type(combined) is dict: combined = ExecuteSqlRequest.QueryOptions( optimizer_version=combined.get("optimizer_version", ""), optimizer_statistics_package=combined.get( @@ -89,7 +89,7 @@ def _merge_query_options(base, merge): ), ) merge = merge or ExecuteSqlRequest.QueryOptions() - if type(merge) == dict: + if type(merge) is dict: merge = ExecuteSqlRequest.QueryOptions( optimizer_version=merge.get("optimizer_version", ""), optimizer_statistics_package=merge.get("optimizer_statistics_package", ""), diff --git a/google/cloud/spanner_v1/backup.py b/google/cloud/spanner_v1/backup.py index 2f54cf2167..1fcffbe05a 100644 --- a/google/cloud/spanner_v1/backup.py +++ b/google/cloud/spanner_v1/backup.py @@ -95,7 +95,7 @@ def __init__( self._max_expire_time = None self._referencing_backups = None self._database_dialect = None - if type(encryption_config) == dict: + if type(encryption_config) is dict: if source_backup: self._encryption_config = CopyBackupEncryptionConfig( **encryption_config diff --git a/google/cloud/spanner_v1/batch.py b/google/cloud/spanner_v1/batch.py index 6b71e6d825..41e4460c30 100644 --- a/google/cloud/spanner_v1/batch.py +++ b/google/cloud/spanner_v1/batch.py @@ -175,7 +175,7 @@ def commit(self, return_commit_stats=False, request_options=None): if request_options is None: request_options = RequestOptions() - elif type(request_options) == dict: + elif type(request_options) is dict: request_options = RequestOptions(request_options) request_options.transaction_tag = self.transaction_tag diff --git a/google/cloud/spanner_v1/client.py b/google/cloud/spanner_v1/client.py index 955fd94820..5fac1dd9e6 100644 --- a/google/cloud/spanner_v1/client.py +++ b/google/cloud/spanner_v1/client.py @@ -143,7 +143,7 @@ def __init__( ): self._emulator_host = _get_spanner_emulator_host() - if client_options and type(client_options) == dict: + if client_options and type(client_options) is dict: self._client_options = google.api_core.client_options.from_dict( client_options ) diff --git a/google/cloud/spanner_v1/database.py b/google/cloud/spanner_v1/database.py index 9df479519f..1d211f7d6d 100644 --- a/google/cloud/spanner_v1/database.py +++ b/google/cloud/spanner_v1/database.py @@ -436,7 +436,7 @@ def create(self): db_name = f'"{db_name}"' else: db_name = f"`{db_name}`" - if type(self._encryption_config) == dict: + if type(self._encryption_config) is dict: self._encryption_config = EncryptionConfig(**self._encryption_config) request = CreateDatabaseRequest( @@ -621,7 +621,7 @@ def execute_partitioned_dml( ) if request_options is None: request_options = RequestOptions() - elif type(request_options) == dict: + elif type(request_options) is dict: request_options = RequestOptions(request_options) request_options.transaction_tag = None @@ -806,7 +806,7 @@ def restore(self, source): """ if source is None: raise ValueError("Restore source not specified") - if type(self._encryption_config) == dict: + if type(self._encryption_config) is dict: self._encryption_config = RestoreDatabaseEncryptionConfig( **self._encryption_config ) @@ -1011,7 +1011,7 @@ def __init__(self, database, request_options=None): self._session = self._batch = None if request_options is None: self._request_options = RequestOptions() - elif type(request_options) == dict: + elif type(request_options) is dict: self._request_options = RequestOptions(request_options) else: self._request_options = request_options diff --git a/google/cloud/spanner_v1/gapic_version.py b/google/cloud/spanner_v1/gapic_version.py index e0c31c2ce4..51483c89bf 100644 --- a/google/cloud/spanner_v1/gapic_version.py +++ b/google/cloud/spanner_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "3.38.0" # {x-release-please-version} +__version__ = "3.39.0" # {x-release-please-version} diff --git a/google/cloud/spanner_v1/snapshot.py b/google/cloud/spanner_v1/snapshot.py index 6d17bfc386..573042aa11 100644 --- a/google/cloud/spanner_v1/snapshot.py +++ b/google/cloud/spanner_v1/snapshot.py @@ -247,7 +247,7 @@ def read( if request_options is None: request_options = RequestOptions() - elif type(request_options) == dict: + elif type(request_options) is dict: request_options = RequestOptions(request_options) if self._read_only: @@ -414,7 +414,7 @@ def execute_sql( if request_options is None: request_options = RequestOptions() - elif type(request_options) == dict: + elif type(request_options) is dict: request_options = RequestOptions(request_options) if self._read_only: # Transaction tags are not supported for read only transactions. diff --git a/google/cloud/spanner_v1/transaction.py b/google/cloud/spanner_v1/transaction.py index dee99a0c6f..d564d0d488 100644 --- a/google/cloud/spanner_v1/transaction.py +++ b/google/cloud/spanner_v1/transaction.py @@ -215,7 +215,7 @@ def commit(self, return_commit_stats=False, request_options=None): if request_options is None: request_options = RequestOptions() - elif type(request_options) == dict: + elif type(request_options) is dict: request_options = RequestOptions(request_options) if self.transaction_tag is not None: request_options.transaction_tag = self.transaction_tag @@ -352,7 +352,7 @@ def execute_update( if request_options is None: request_options = RequestOptions() - elif type(request_options) == dict: + elif type(request_options) is dict: request_options = RequestOptions(request_options) request_options.transaction_tag = self.transaction_tag @@ -463,7 +463,7 @@ def batch_update(self, statements, request_options=None): if request_options is None: request_options = RequestOptions() - elif type(request_options) == dict: + elif type(request_options) is dict: request_options = RequestOptions(request_options) request_options.transaction_tag = self.transaction_tag diff --git a/noxfile.py b/noxfile.py index eaf653cd07..95fe0d2365 100644 --- a/noxfile.py +++ b/noxfile.py @@ -25,6 +25,7 @@ import nox +FLAKE8_VERSION = "flake8==6.1.0" BLACK_VERSION = "black==22.3.0" ISORT_VERSION = "isort==5.10.1" LINT_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] @@ -83,7 +84,7 @@ def lint(session): Returns a failure if the linters find linting errors or sufficiently serious code quality issues. """ - session.install("flake8", BLACK_VERSION) + session.install(FLAKE8_VERSION, BLACK_VERSION) session.run( "black", "--check", diff --git a/samples/generated_samples/snippet_metadata_google.spanner.admin.database.v1.json b/samples/generated_samples/snippet_metadata_google.spanner.admin.database.v1.json index 111a3cfca1..d3212818a6 100644 --- a/samples/generated_samples/snippet_metadata_google.spanner.admin.database.v1.json +++ b/samples/generated_samples/snippet_metadata_google.spanner.admin.database.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-spanner-admin-database", - "version": "3.38.0" + "version": "3.39.0" }, "snippets": [ { diff --git a/samples/generated_samples/snippet_metadata_google.spanner.admin.instance.v1.json b/samples/generated_samples/snippet_metadata_google.spanner.admin.instance.v1.json index 6368c573e5..930cce10b1 100644 --- a/samples/generated_samples/snippet_metadata_google.spanner.admin.instance.v1.json +++ b/samples/generated_samples/snippet_metadata_google.spanner.admin.instance.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-spanner-admin-instance", - "version": "3.38.0" + "version": "3.39.0" }, "snippets": [ { diff --git a/samples/generated_samples/snippet_metadata_google.spanner.v1.json b/samples/generated_samples/snippet_metadata_google.spanner.v1.json index c71c768c3d..de9817cd50 100644 --- a/samples/generated_samples/snippet_metadata_google.spanner.v1.json +++ b/samples/generated_samples/snippet_metadata_google.spanner.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-spanner", - "version": "3.38.0" + "version": "3.39.0" }, "snippets": [ { diff --git a/samples/samples/conftest.py b/samples/samples/conftest.py index c63548c460..5b1af63876 100644 --- a/samples/samples/conftest.py +++ b/samples/samples/conftest.py @@ -38,20 +38,19 @@ def sample_name(): """Sample testcase modules must define this fixture. - The name is used to label the instance created by the sample, to - aid in debugging leaked instances. - """ - raise NotImplementedError( - "Define 'sample_name' fixture in sample test driver") + The name is used to label the instance created by the sample, to + aid in debugging leaked instances. + """ + raise NotImplementedError("Define 'sample_name' fixture in sample test driver") @pytest.fixture(scope="module") def database_dialect(): """Database 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. - """ + The dialect is used to initialize the dialect for the database. + It can either be GoogleStandardSql or PostgreSql. + """ # By default, we consider GOOGLE_STANDARD_SQL dialect. Other specific tests # can override this if required. return DatabaseDialect.GOOGLE_STANDARD_SQL @@ -105,7 +104,7 @@ def multi_region_instance_id(): @pytest.fixture(scope="module") def instance_config(spanner_client): return "{}/instanceConfigs/{}".format( - spanner_client.project_name, "regional-us-central1" + spanner_client.project_name, "regional-us-central1" ) @@ -116,20 +115,20 @@ def multi_region_instance_config(spanner_client): @pytest.fixture(scope="module") def sample_instance( - spanner_client, - cleanup_old_instances, - instance_id, - instance_config, - sample_name, + spanner_client, + cleanup_old_instances, + instance_id, + instance_config, + sample_name, ): sample_instance = spanner_client.instance( - instance_id, - instance_config, - labels={ - "cloud_spanner_samples": "true", - "sample_name": sample_name, - "created": str(int(time.time())), - }, + instance_id, + instance_config, + labels={ + "cloud_spanner_samples": "true", + "sample_name": sample_name, + "created": str(int(time.time())), + }, ) op = retry_429(sample_instance.create)() op.result(INSTANCE_CREATION_TIMEOUT) # block until completion @@ -151,20 +150,20 @@ def sample_instance( @pytest.fixture(scope="module") def multi_region_instance( - spanner_client, - cleanup_old_instances, - multi_region_instance_id, - multi_region_instance_config, - sample_name, + spanner_client, + cleanup_old_instances, + multi_region_instance_id, + multi_region_instance_config, + sample_name, ): multi_region_instance = spanner_client.instance( - multi_region_instance_id, - multi_region_instance_config, - labels={ - "cloud_spanner_samples": "true", - "sample_name": sample_name, - "created": str(int(time.time())), - }, + multi_region_instance_id, + multi_region_instance_config, + labels={ + "cloud_spanner_samples": "true", + "sample_name": sample_name, + "created": str(int(time.time())), + }, ) op = retry_429(multi_region_instance.create)() op.result(INSTANCE_CREATION_TIMEOUT) # block until completion @@ -188,31 +187,37 @@ def multi_region_instance( def database_id(): """Id for the database used in samples. - Sample testcase modules can override as needed. - """ + Sample testcase modules can override as needed. + """ return "my-database-id" +@pytest.fixture(scope="module") +def bit_reverse_sequence_database_id(): + """Id for the database used in bit reverse sequence samples. + + Sample testcase modules can override as needed. + """ + return "sequence-database-id" + + @pytest.fixture(scope="module") def database_ddl(): """Sequence of DDL statements used to set up the database. - Sample testcase modules can override as needed. - """ + Sample testcase modules can override as needed. + """ return [] @pytest.fixture(scope="module") def sample_database( - spanner_client, - sample_instance, - database_id, - database_ddl, - database_dialect): + spanner_client, sample_instance, database_id, database_ddl, database_dialect +): if database_dialect == DatabaseDialect.POSTGRESQL: sample_database = sample_instance.database( - database_id, - database_dialect=DatabaseDialect.POSTGRESQL, + database_id, + database_dialect=DatabaseDialect.POSTGRESQL, ) if not sample_database.exists(): @@ -220,12 +225,11 @@ def sample_database( operation.result(OPERATION_TIMEOUT_SECONDS) request = spanner_admin_database_v1.UpdateDatabaseDdlRequest( - database=sample_database.name, - statements=database_ddl, + database=sample_database.name, + statements=database_ddl, ) - operation =\ - spanner_client.database_admin_api.update_database_ddl(request) + operation = spanner_client.database_admin_api.update_database_ddl(request) operation.result(OPERATION_TIMEOUT_SECONDS) yield sample_database @@ -234,8 +238,8 @@ def sample_database( return sample_database = sample_instance.database( - database_id, - ddl_statements=database_ddl, + database_id, + ddl_statements=database_ddl, ) if not sample_database.exists(): @@ -247,11 +251,43 @@ def sample_database( sample_database.drop() +@pytest.fixture(scope="module") +def bit_reverse_sequence_database( + spanner_client, sample_instance, bit_reverse_sequence_database_id, database_dialect +): + if database_dialect == DatabaseDialect.POSTGRESQL: + bit_reverse_sequence_database = sample_instance.database( + bit_reverse_sequence_database_id, + database_dialect=DatabaseDialect.POSTGRESQL, + ) + + if not bit_reverse_sequence_database.exists(): + operation = bit_reverse_sequence_database.create() + operation.result(OPERATION_TIMEOUT_SECONDS) + + yield bit_reverse_sequence_database + + bit_reverse_sequence_database.drop() + return + + bit_reverse_sequence_database = sample_instance.database( + bit_reverse_sequence_database_id + ) + + if not bit_reverse_sequence_database.exists(): + operation = bit_reverse_sequence_database.create() + operation.result(OPERATION_TIMEOUT_SECONDS) + + yield bit_reverse_sequence_database + + bit_reverse_sequence_database.drop() + + @pytest.fixture(scope="module") def kms_key_name(spanner_client): return "projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}".format( - spanner_client.project, - "us-central1", - "spanner-test-keyring", - "spanner-test-cmek", + spanner_client.project, + "us-central1", + "spanner-test-keyring", + "spanner-test-cmek", ) diff --git a/samples/samples/pg_snippets.py b/samples/samples/pg_snippets.py index f53fe1d4dd..51ddec6906 100644 --- a/samples/samples/pg_snippets.py +++ b/samples/samples/pg_snippets.py @@ -39,19 +39,19 @@ def create_instance(instance_id): spanner_client = spanner.Client() config_name = "{}/instanceConfigs/regional-us-central1".format( - spanner_client.project_name + spanner_client.project_name ) instance = spanner_client.instance( - instance_id, - configuration_name=config_name, - display_name="This is a display name.", - node_count=1, - labels={ - "cloud_spanner_samples": "true", - "sample_name": "snippets-create_instance-explicit", - "created": str(int(time.time())), - }, + instance_id, + configuration_name=config_name, + display_name="This is a display name.", + node_count=1, + labels={ + "cloud_spanner_samples": "true", + "sample_name": "snippets-create_instance-explicit", + "created": str(int(time.time())), + }, ) operation = instance.create() @@ -72,8 +72,8 @@ def create_database(instance_id, database_id): instance = spanner_client.instance(instance_id) database = instance.database( - database_id, - database_dialect=DatabaseDialect.POSTGRESQL, + database_id, + database_dialect=DatabaseDialect.POSTGRESQL, ) operation = database.create() @@ -88,9 +88,9 @@ def create_database(instance_id, database_id): def create_table_using_ddl(database_name): spanner_client = spanner.Client() request = spanner_admin_database_v1.UpdateDatabaseDdlRequest( - database=database_name, - statements=[ - """CREATE TABLE Singers ( + database=database_name, + statements=[ + """CREATE TABLE Singers ( SingerId bigint NOT NULL, FirstName character varying(1024), LastName character varying(1024), @@ -99,13 +99,13 @@ def create_table_using_ddl(database_name): 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""", - ], + ], ) operation = spanner_client.database_admin_api.update_database_ddl(request) operation.result(OPERATION_TIMEOUT_SECONDS) @@ -127,27 +127,27 @@ def insert_data(instance_id, database_id): with database.batch() as batch: batch.insert( - table="Singers", - columns=("SingerId", "FirstName", "LastName"), - values=[ - (1, "Marc", "Richards"), - (2, "Catalina", "Smith"), - (3, "Alice", "Trentor"), - (4, "Lea", "Martin"), - (5, "David", "Lomond"), - ], + table="Singers", + columns=("SingerId", "FirstName", "LastName"), + values=[ + (1, "Marc", "Richards"), + (2, "Catalina", "Smith"), + (3, "Alice", "Trentor"), + (4, "Lea", "Martin"), + (5, "David", "Lomond"), + ], ) batch.insert( - table="Albums", - columns=("SingerId", "AlbumId", "AlbumTitle"), - values=[ - (1, 1, "Total Junk"), - (1, 2, "Go, Go, Go"), - (2, 1, "Green"), - (2, 2, "Forever Hold Your Peace"), - (2, 3, "Terrified"), - ], + table="Albums", + columns=("SingerId", "AlbumId", "AlbumTitle"), + values=[ + (1, 1, "Total Junk"), + (1, 2, "Go, Go, Go"), + (2, 1, "Green"), + (2, 2, "Forever Hold Your Peace"), + (2, 3, "Terrified"), + ], ) print("Inserted data.") @@ -198,7 +198,7 @@ def query_data(instance_id, database_id): with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT SingerId, AlbumId, AlbumTitle FROM Albums" + "SELECT SingerId, AlbumId, AlbumTitle FROM Albums" ) for row in results: @@ -218,8 +218,7 @@ def read_data(instance_id, database_id): with database.snapshot() as snapshot: keyset = spanner.KeySet(all_=True) results = snapshot.read( - table="Albums", columns=("SingerId", "AlbumId", "AlbumTitle"), - keyset=keyset + table="Albums", columns=("SingerId", "AlbumId", "AlbumTitle"), keyset=keyset ) for row in results: @@ -237,7 +236,7 @@ def add_column(instance_id, database_id): database = instance.database(database_id) operation = database.update_ddl( - ["ALTER TABLE Albums ADD COLUMN MarketingBudget BIGINT"] + ["ALTER TABLE Albums ADD COLUMN MarketingBudget BIGINT"] ) print("Waiting for operation to complete...") @@ -266,9 +265,9 @@ def update_data(instance_id, database_id): with database.batch() as batch: batch.update( - table="Albums", - columns=("SingerId", "AlbumId", "MarketingBudget"), - values=[(1, 1, 100000), (2, 2, 500000)], + table="Albums", + columns=("SingerId", "AlbumId", "MarketingBudget"), + values=[(1, 1, 100000), (2, 2, 500000)], ) print("Updated data.") @@ -297,10 +296,10 @@ def update_albums(transaction): # Read the second album budget. second_album_keyset = spanner.KeySet(keys=[(2, 2)]) second_album_result = transaction.read( - table="Albums", - columns=("MarketingBudget",), - keyset=second_album_keyset, - limit=1, + table="Albums", + columns=("MarketingBudget",), + keyset=second_album_keyset, + limit=1, ) second_album_row = list(second_album_result)[0] second_album_budget = second_album_row[0] @@ -310,16 +309,15 @@ def update_albums(transaction): if second_album_budget < transfer_amount: # Raising an exception will automatically roll back the # transaction. - raise ValueError( - "The second album doesn't have enough funds to transfer") + raise ValueError("The second album doesn't have enough funds to transfer") # Read the first album's budget. first_album_keyset = spanner.KeySet(keys=[(1, 1)]) first_album_result = transaction.read( - table="Albums", - columns=("MarketingBudget",), - keyset=first_album_keyset, - limit=1, + table="Albums", + columns=("MarketingBudget",), + keyset=first_album_keyset, + limit=1, ) first_album_row = list(first_album_result)[0] first_album_budget = first_album_row[0] @@ -328,15 +326,15 @@ def update_albums(transaction): second_album_budget -= transfer_amount first_album_budget += transfer_amount print( - "Setting first album's budget to {} and the second album's " - "budget to {}.".format(first_album_budget, second_album_budget) + "Setting first album's budget to {} and the second album's " + "budget to {}.".format(first_album_budget, second_album_budget) ) # Update the rows. transaction.update( - table="Albums", - columns=("SingerId", "AlbumId", "MarketingBudget"), - values=[(1, 1, first_album_budget), (2, 2, second_album_budget)], + table="Albums", + columns=("SingerId", "AlbumId", "MarketingBudget"), + values=[(1, 1, first_album_budget), (2, 2, second_album_budget)], ) database.run_in_transaction(update_albums) @@ -363,7 +361,7 @@ def query_data_with_new_column(instance_id, database_id): with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT SingerId, AlbumId, MarketingBudget FROM Albums" + "SELECT SingerId, AlbumId, MarketingBudget FROM Albums" ) for row in results: @@ -381,7 +379,7 @@ def add_index(instance_id, database_id): database = instance.database(database_id) operation = database.update_ddl( - ["CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"] + ["CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"] ) print("Waiting for operation to complete...") @@ -410,10 +408,10 @@ def read_data_with_index(instance_id, database_id): with database.snapshot() as snapshot: keyset = spanner.KeySet(all_=True) results = snapshot.read( - table="Albums", - columns=("AlbumId", "AlbumTitle"), - keyset=keyset, - index="AlbumsByAlbumTitle", + table="Albums", + columns=("AlbumId", "AlbumTitle"), + keyset=keyset, + index="AlbumsByAlbumTitle", ) for row in results: @@ -431,10 +429,10 @@ def add_storing_index(instance_id, database_id): database = instance.database(database_id) operation = database.update_ddl( - [ - "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle)" - "INCLUDE (MarketingBudget)" - ] + [ + "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle)" + "INCLUDE (MarketingBudget)" + ] ) print("Waiting for operation to complete...") @@ -466,15 +464,14 @@ def read_data_with_storing_index(instance_id, database_id): with database.snapshot() as snapshot: keyset = spanner.KeySet(all_=True) results = snapshot.read( - table="Albums", - columns=("AlbumId", "AlbumTitle", "MarketingBudget"), - keyset=keyset, - index="AlbumsByAlbumTitle2", + table="Albums", + columns=("AlbumId", "AlbumTitle", "MarketingBudget"), + keyset=keyset, + index="AlbumsByAlbumTitle2", ) for row in results: - print("AlbumId: {}, AlbumTitle: {}, " "MarketingBudget: {}".format( - *row)) + print("AlbumId: {}, AlbumTitle: {}, " "MarketingBudget: {}".format(*row)) # [END spanner_postgresql_read_data_with_storing_index] @@ -494,7 +491,7 @@ def read_only_transaction(instance_id, database_id): with database.snapshot(multi_use=True) as snapshot: # Read using SQL. results = snapshot.execute_sql( - "SELECT SingerId, AlbumId, AlbumTitle FROM Albums" + "SELECT SingerId, AlbumId, AlbumTitle FROM Albums" ) print("Results from first read:") @@ -506,8 +503,7 @@ def read_only_transaction(instance_id, database_id): # return the same data. keyset = spanner.KeySet(all_=True) results = snapshot.read( - table="Albums", columns=("SingerId", "AlbumId", "AlbumTitle"), - keyset=keyset + table="Albums", columns=("SingerId", "AlbumId", "AlbumTitle"), keyset=keyset ) print("Results from second read:") @@ -529,11 +525,11 @@ def insert_with_dml(instance_id, database_id): def insert_singers(transaction): row_ct = transaction.execute_update( - "INSERT INTO Singers (SingerId, FirstName, LastName) VALUES " - "(12, 'Melissa', 'Garcia'), " - "(13, 'Russell', 'Morales'), " - "(14, 'Jacqueline', 'Long'), " - "(15, 'Dylan', 'Shaw')" + "INSERT INTO Singers (SingerId, FirstName, LastName) VALUES " + "(12, 'Melissa', 'Garcia'), " + "(13, 'Russell', 'Morales'), " + "(14, 'Jacqueline', 'Long'), " + "(15, 'Dylan', 'Shaw')" ) print("{} record(s) inserted.".format(row_ct)) @@ -542,7 +538,7 @@ def insert_singers(transaction): def insert_with_dml_returning(instance_id, database_id): - """Inserts sample data into the given database using a DML statement having a RETURNING clause. """ + """Inserts sample data into the given database using a DML statement having a RETURNING clause.""" # [START spanner_postgresql_dml_insert_returning] # instance_id = "your-spanner-instance" # database_id = "your-spanner-db-id" @@ -584,9 +580,9 @@ def query_data_with_parameter(instance_id, database_id): with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT SingerId, FirstName, LastName FROM Singers " "WHERE LastName = $1", - params={"p1": "Garcia"}, - param_types={"p1": spanner.param_types.STRING}, + "SELECT SingerId, FirstName, LastName FROM Singers " "WHERE LastName = $1", + params={"p1": "Garcia"}, + param_types={"p1": spanner.param_types.STRING}, ) for row in results: @@ -608,7 +604,7 @@ def transfer_budget(transaction): # Transfer marketing budget from one album to another. Performed in a # single transaction to ensure that the transfer is atomic. second_album_result = transaction.execute_sql( - "SELECT MarketingBudget from Albums " "WHERE SingerId = 2 and AlbumId = 2" + "SELECT MarketingBudget from Albums " "WHERE SingerId = 2 and AlbumId = 2" ) second_album_row = list(second_album_result)[0] second_album_budget = second_album_row[0] @@ -620,8 +616,8 @@ def transfer_budget(transaction): # will be rerun by the client library if second_album_budget >= transfer_amount: first_album_result = transaction.execute_sql( - "SELECT MarketingBudget from Albums " - "WHERE SingerId = 1 and AlbumId = 1" + "SELECT MarketingBudget from Albums " + "WHERE SingerId = 1 and AlbumId = 1" ) first_album_row = list(first_album_result)[0] first_album_budget = first_album_row[0] @@ -631,26 +627,26 @@ def transfer_budget(transaction): # Update first album transaction.execute_update( - "UPDATE Albums " - "SET MarketingBudget = $1 " - "WHERE SingerId = 1 and AlbumId = 1", - params={"p1": first_album_budget}, - param_types={"p1": spanner.param_types.INT64}, + "UPDATE Albums " + "SET MarketingBudget = $1 " + "WHERE SingerId = 1 and AlbumId = 1", + params={"p1": first_album_budget}, + param_types={"p1": spanner.param_types.INT64}, ) # Update second album transaction.execute_update( - "UPDATE Albums " - "SET MarketingBudget = $1 " - "WHERE SingerId = 2 and AlbumId = 2", - params={"p1": second_album_budget}, - param_types={"p1": spanner.param_types.INT64}, + "UPDATE Albums " + "SET MarketingBudget = $1 " + "WHERE SingerId = 2 and AlbumId = 2", + params={"p1": second_album_budget}, + param_types={"p1": spanner.param_types.INT64}, ) print( - "Transferred {} from Album2's budget to Album1's".format( - transfer_amount - ) + "Transferred {} from Album2's budget to Album1's".format( + transfer_amount + ) ) database.run_in_transaction(transfer_budget) @@ -671,9 +667,9 @@ def read_stale_data(instance_id, database_id): with database.snapshot(exact_staleness=staleness) as snapshot: keyset = spanner.KeySet(all_=True) results = snapshot.read( - table="Albums", - columns=("SingerId", "AlbumId", "MarketingBudget"), - keyset=keyset, + table="Albums", + columns=("SingerId", "AlbumId", "MarketingBudget"), + keyset=keyset, ) for row in results: @@ -706,13 +702,12 @@ def update_data_with_timestamp(instance_id, database_id): with database.batch() as batch: batch.update( - table="Albums", - columns=( - "SingerId", "AlbumId", "MarketingBudget", "LastUpdateTime"), - values=[ - (1, 1, 1000000, spanner.COMMIT_TIMESTAMP), - (2, 2, 750000, spanner.COMMIT_TIMESTAMP), - ], + table="Albums", + columns=("SingerId", "AlbumId", "MarketingBudget", "LastUpdateTime"), + values=[ + (1, 1, 1000000, spanner.COMMIT_TIMESTAMP), + (2, 2, 750000, spanner.COMMIT_TIMESTAMP), + ], ) print("Updated data.") @@ -730,17 +725,16 @@ def add_timestamp_column(instance_id, database_id): database = instance.database(database_id) operation = database.update_ddl( - [ - "ALTER TABLE Albums ADD COLUMN LastUpdateTime SPANNER.COMMIT_TIMESTAMP"] + ["ALTER TABLE Albums ADD COLUMN LastUpdateTime SPANNER.COMMIT_TIMESTAMP"] ) print("Waiting for operation to complete...") operation.result(OPERATION_TIMEOUT_SECONDS) print( - 'Altered table "Albums" on database {} on instance {}.'.format( - database_id, instance_id - ) + 'Altered table "Albums" on database {} on instance {}.'.format( + database_id, instance_id + ) ) @@ -767,8 +761,8 @@ def query_data_with_timestamp(instance_id, database_id): with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT SingerId, AlbumId, MarketingBudget FROM Albums " - "ORDER BY LastUpdateTime DESC" + "SELECT SingerId, AlbumId, MarketingBudget FROM Albums " + "ORDER BY LastUpdateTime DESC" ) for row in results: @@ -787,9 +781,9 @@ def create_table_with_timestamp(instance_id, database_id): database = instance.database(database_id) request = spanner_admin_database_v1.UpdateDatabaseDdlRequest( - database=database.name, - statements=[ - """CREATE TABLE Performances ( + database=database.name, + statements=[ + """CREATE TABLE Performances ( SingerId BIGINT NOT NULL, VenueId BIGINT NOT NULL, EventDate Date, @@ -797,7 +791,7 @@ def create_table_with_timestamp(instance_id, database_id): LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL, PRIMARY KEY (SingerId, VenueId, EventDate)) INTERLEAVE IN PARENT Singers ON DELETE CASCADE""" - ], + ], ) operation = spanner_client.database_admin_api.update_database_ddl(request) @@ -805,9 +799,9 @@ def create_table_with_timestamp(instance_id, database_id): operation.result(OPERATION_TIMEOUT_SECONDS) print( - "Created Performances table on database {} on instance {}".format( - database_id, instance_id - ) + "Created Performances table on database {} on instance {}".format( + database_id, instance_id + ) ) @@ -825,14 +819,13 @@ def insert_data_with_timestamp(instance_id, database_id): with database.batch() as batch: batch.insert( - table="Performances", - columns=( - "SingerId", "VenueId", "EventDate", "Revenue", "LastUpdateTime"), - values=[ - (1, 4, "2017-10-05", 11000, spanner.COMMIT_TIMESTAMP), - (1, 19, "2017-11-02", 15000, spanner.COMMIT_TIMESTAMP), - (2, 42, "2017-12-23", 7000, spanner.COMMIT_TIMESTAMP), - ], + table="Performances", + columns=("SingerId", "VenueId", "EventDate", "Revenue", "LastUpdateTime"), + values=[ + (1, 4, "2017-10-05", 11000, spanner.COMMIT_TIMESTAMP), + (1, 19, "2017-11-02", 15000, spanner.COMMIT_TIMESTAMP), + (2, 42, "2017-12-23", 7000, spanner.COMMIT_TIMESTAMP), + ], ) print("Inserted data.") @@ -853,8 +846,8 @@ def insert_data_with_dml(instance_id, database_id): def insert_singers(transaction): row_ct = transaction.execute_update( - "INSERT INTO Singers (SingerId, FirstName, LastName) " - " VALUES (10, 'Virginia', 'Watson')" + "INSERT INTO Singers (SingerId, FirstName, LastName) " + " VALUES (10, 'Virginia', 'Watson')" ) print("{} record(s) inserted.".format(row_ct)) @@ -875,9 +868,9 @@ def update_data_with_dml(instance_id, database_id): def update_albums(transaction): row_ct = transaction.execute_update( - "UPDATE Albums " - "SET MarketingBudget = MarketingBudget * 2 " - "WHERE SingerId = 1 and AlbumId = 1" + "UPDATE Albums " + "SET MarketingBudget = MarketingBudget * 2 " + "WHERE SingerId = 1 and AlbumId = 1" ) print("{} record(s) updated.".format(row_ct)) @@ -929,7 +922,7 @@ def delete_data_with_dml(instance_id, database_id): def delete_singers(transaction): row_ct = transaction.execute_update( - "DELETE FROM Singers WHERE FirstName = 'Alice'" + "DELETE FROM Singers WHERE FirstName = 'Alice'" ) print("{} record(s) deleted.".format(row_ct)) @@ -939,7 +932,7 @@ def delete_singers(transaction): def delete_data_with_dml_returning(instance_id, database_id): - """Deletes sample data from the database using a DML statement having a RETURNING clause. """ + """Deletes sample data from the database using a DML statement having a RETURNING clause.""" # [START spanner_postgresql_dml_delete_returning] # instance_id = "your-spanner-instance" # database_id = "your-spanner-db-id" @@ -980,14 +973,14 @@ def dml_write_read_transaction(instance_id, database_id): def write_then_read(transaction): # Insert record. row_ct = transaction.execute_update( - "INSERT INTO Singers (SingerId, FirstName, LastName) " - " VALUES (11, 'Timothy', 'Campbell')" + "INSERT INTO Singers (SingerId, FirstName, LastName) " + " VALUES (11, 'Timothy', 'Campbell')" ) print("{} record(s) inserted.".format(row_ct)) # Read newly inserted record. results = transaction.execute_sql( - "SELECT FirstName, LastName FROM Singers WHERE SingerId = 11" + "SELECT FirstName, LastName FROM Singers WHERE SingerId = 11" ) for result in results: print("FirstName: {}, LastName: {}".format(*result)) @@ -1007,7 +1000,7 @@ def update_data_with_partitioned_dml(instance_id, database_id): database = instance.database(database_id) row_ct = database.execute_partitioned_dml( - "UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1" + "UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1" ) print("{} records updated.".format(row_ct)) @@ -1023,8 +1016,7 @@ def delete_data_with_partitioned_dml(instance_id, database_id): instance = spanner_client.instance(instance_id) database = instance.database(database_id) - row_ct = database.execute_partitioned_dml( - "DELETE FROM Singers WHERE SingerId > 10") + row_ct = database.execute_partitioned_dml("DELETE FROM Singers WHERE SingerId > 10") print("{} record(s) deleted.".format(row_ct)) # [END spanner_postgresql_dml_partitioned_delete] @@ -1043,20 +1035,19 @@ def update_with_batch_dml(instance_id, database_id): database = instance.database(database_id) insert_statement = ( - "INSERT INTO Albums " - "(SingerId, AlbumId, AlbumTitle, MarketingBudget) " - "VALUES (1, 3, 'Test Album Title', 10000)" + "INSERT INTO Albums " + "(SingerId, AlbumId, AlbumTitle, MarketingBudget) " + "VALUES (1, 3, 'Test Album Title', 10000)" ) update_statement = ( - "UPDATE Albums " - "SET MarketingBudget = MarketingBudget * 2 " - "WHERE SingerId = 1 and AlbumId = 3" + "UPDATE Albums " + "SET MarketingBudget = MarketingBudget * 2 " + "WHERE SingerId = 1 and AlbumId = 3" ) def update_albums(transaction): - status, row_cts = transaction.batch_update( - [insert_statement, update_statement]) + status, row_cts = transaction.batch_update([insert_statement, update_statement]) if status.code != OK: # Do handling here. @@ -1064,8 +1055,7 @@ def update_albums(transaction): # `commit` is called by `run_in_transaction`. return - print( - "Executed {} SQL statements using Batch DML.".format(len(row_cts))) + print("Executed {} SQL statements using Batch DML.".format(len(row_cts))) database.run_in_transaction(update_albums) # [END spanner_postgresql_dml_batch_update] @@ -1081,9 +1071,9 @@ def create_table_with_datatypes(instance_id, database_id): database = instance.database(database_id) request = spanner_admin_database_v1.UpdateDatabaseDdlRequest( - database=database.name, - statements=[ - """CREATE TABLE Venues ( + database=database.name, + statements=[ + """CREATE TABLE Venues ( VenueId BIGINT NOT NULL, VenueName character varying(100), VenueInfo BYTEA, @@ -1093,7 +1083,7 @@ def create_table_with_datatypes(instance_id, database_id): Revenue NUMERIC, LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL, PRIMARY KEY (VenueId))""" - ], + ], ) operation = spanner_client.database_admin_api.update_database_ddl(request) @@ -1101,9 +1091,9 @@ def create_table_with_datatypes(instance_id, database_id): operation.result(OPERATION_TIMEOUT_SECONDS) print( - "Created Venues table on database {} on instance {}".format( - database_id, instance_id - ) + "Created Venues table on database {} on instance {}".format( + database_id, instance_id + ) ) # [END spanner_postgresql_create_table_with_datatypes] @@ -1122,49 +1112,49 @@ def insert_datatypes_data(instance_id, database_id): exampleBytes3 = base64.b64encode("Hello World 3".encode()) with database.batch() as batch: batch.insert( - table="Venues", - columns=( - "VenueId", - "VenueName", - "VenueInfo", - "Capacity", - "OutdoorVenue", - "PopularityScore", - "Revenue", - "LastUpdateTime", - ), - values=[ - ( - 4, - "Venue 4", - exampleBytes1, - 1800, - False, - 0.85543, - decimal.Decimal("215100.10"), - spanner.COMMIT_TIMESTAMP, - ), - ( - 19, - "Venue 19", - exampleBytes2, - 6300, - True, - 0.98716, - decimal.Decimal("1200100.00"), - spanner.COMMIT_TIMESTAMP, - ), - ( - 42, - "Venue 42", - exampleBytes3, - 3000, - False, - 0.72598, - decimal.Decimal("390650.99"), - spanner.COMMIT_TIMESTAMP, + table="Venues", + columns=( + "VenueId", + "VenueName", + "VenueInfo", + "Capacity", + "OutdoorVenue", + "PopularityScore", + "Revenue", + "LastUpdateTime", ), - ], + values=[ + ( + 4, + "Venue 4", + exampleBytes1, + 1800, + False, + 0.85543, + decimal.Decimal("215100.10"), + spanner.COMMIT_TIMESTAMP, + ), + ( + 19, + "Venue 19", + exampleBytes2, + 6300, + True, + 0.98716, + decimal.Decimal("1200100.00"), + spanner.COMMIT_TIMESTAMP, + ), + ( + 42, + "Venue 42", + exampleBytes3, + 3000, + False, + 0.72598, + decimal.Decimal("390650.99"), + spanner.COMMIT_TIMESTAMP, + ), + ], ) print("Inserted data.") @@ -1186,10 +1176,10 @@ def query_data_with_bool(instance_id, database_id): with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT VenueId, VenueName, OutdoorVenue FROM Venues " - "WHERE OutdoorVenue = $1", - params=param, - param_types=param_type, + "SELECT VenueId, VenueName, OutdoorVenue FROM Venues " + "WHERE OutdoorVenue = $1", + params=param, + param_types=param_type, ) for row in results: @@ -1212,9 +1202,9 @@ def query_data_with_bytes(instance_id, database_id): with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT VenueId, VenueName FROM Venues " "WHERE VenueInfo = $1", - params=param, - param_types=param_type, + "SELECT VenueId, VenueName FROM Venues " "WHERE VenueInfo = $1", + params=param, + param_types=param_type, ) for row in results: @@ -1237,15 +1227,14 @@ def query_data_with_float(instance_id, database_id): with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT VenueId, VenueName, PopularityScore FROM Venues " - "WHERE PopularityScore > $1", - params=param, - param_types=param_type, + "SELECT VenueId, VenueName, PopularityScore FROM Venues " + "WHERE PopularityScore > $1", + params=param, + param_types=param_type, ) for row in results: - print( - "VenueId: {}, VenueName: {}, PopularityScore: {}".format(*row)) + print("VenueId: {}, VenueName: {}, PopularityScore: {}".format(*row)) # [END spanner_postgresql_query_with_float_parameter] @@ -1264,9 +1253,9 @@ def query_data_with_int(instance_id, database_id): with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT VenueId, VenueName, Capacity FROM Venues " "WHERE Capacity >= $1", - params=param, - param_types=param_type, + "SELECT VenueId, VenueName, Capacity FROM Venues " "WHERE Capacity >= $1", + params=param, + param_types=param_type, ) for row in results: @@ -1289,9 +1278,9 @@ def query_data_with_string(instance_id, database_id): with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT VenueId, VenueName FROM Venues " "WHERE VenueName = $1", - params=param, - param_types=param_type, + "SELECT VenueId, VenueName FROM Venues " "WHERE VenueName = $1", + params=param, + param_types=param_type, ) for row in results: @@ -1312,18 +1301,19 @@ def query_data_with_timestamp_parameter(instance_id, database_id): # [END spanner_postgresql_query_with_timestamp_parameter] # Avoid time drift on the local machine. # https://github.com/GoogleCloudPlatform/python-docs-samples/issues/4197. - example_timestamp = (datetime.datetime.utcnow() + datetime.timedelta(days=1) - ).isoformat() + "Z" + example_timestamp = ( + datetime.datetime.utcnow() + datetime.timedelta(days=1) + ).isoformat() + "Z" # [START spanner_postgresql_query_with_timestamp_parameter] param = {"p1": example_timestamp} param_type = {"p1": param_types.TIMESTAMP} with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT VenueId, VenueName, LastUpdateTime FROM Venues " - "WHERE LastUpdateTime < $1", - params=param, - param_types=param_type, + "SELECT VenueId, VenueName, LastUpdateTime FROM Venues " + "WHERE LastUpdateTime < $1", + params=param, + param_types=param_type, ) for row in results: @@ -1350,13 +1340,13 @@ def update_data_with_numeric(instance_id, database_id): with database.batch() as batch: batch.update( - table="Venues", - columns=("VenueId", "Revenue"), - values=[ - (4, decimal.Decimal("35000")), - (19, decimal.Decimal("104500")), - (42, decimal.Decimal("99999999999999999999999999999.99")), - ], + table="Venues", + columns=("VenueId", "Revenue"), + values=[ + (4, decimal.Decimal("35000")), + (19, decimal.Decimal("104500")), + (42, decimal.Decimal("99999999999999999999999999999.99")), + ], ) print("Updated data.") @@ -1380,9 +1370,9 @@ def query_data_with_numeric_parameter(instance_id, database_id): with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT VenueId, Revenue FROM Venues WHERE Revenue < $1", - params=param, - param_types=param_type, + "SELECT VenueId, Revenue FROM Venues WHERE Revenue < $1", + params=param, + param_types=param_type, ) for row in results: @@ -1396,17 +1386,17 @@ def create_client_with_query_options(instance_id, database_id): # instance_id = "your-spanner-instance" # database_id = "your-spanner-db-id" spanner_client = spanner.Client( - query_options={ - "optimizer_version": "1", - "optimizer_statistics_package": "latest", - } + query_options={ + "optimizer_version": "1", + "optimizer_statistics_package": "latest", + } ) instance = spanner_client.instance(instance_id) database = instance.database(database_id) with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT VenueId, VenueName, LastUpdateTime FROM Venues" + "SELECT VenueId, VenueName, LastUpdateTime FROM Venues" ) for row in results: @@ -1425,11 +1415,11 @@ def query_data_with_query_options(instance_id, database_id): with database.snapshot() as snapshot: results = snapshot.execute_sql( - "SELECT VenueId, VenueName, LastUpdateTime FROM Venues", - query_options={ - "optimizer_version": "1", - "optimizer_statistics_package": "latest", - }, + "SELECT VenueId, VenueName, LastUpdateTime FROM Venues", + query_options={ + "optimizer_version": "1", + "optimizer_statistics_package": "latest", + }, ) for row in results: @@ -1511,9 +1501,7 @@ def update_data_with_jsonb(instance_id, database_id): JsonObject( [ JsonObject({"name": None, "open": True}), - JsonObject( - {"name": "room 2", "open": False} - ), + JsonObject({"name": "room 2", "open": False}), ] ), ), @@ -1564,15 +1552,127 @@ def query_data_with_jsonb_parameter(instance_id, database_id): # [END spanner_postgresql_jsonb_query_parameter] +# [START spanner_postgresql_create_sequence] +def create_sequence(instance_id, database_id): + """Creates the Sequence and insert data""" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + + request = spanner_admin_database_v1.UpdateDatabaseDdlRequest( + database=database.name, + statements=[ + "CREATE SEQUENCE Seq BIT_REVERSED_POSITIVE", + """CREATE TABLE Customers ( + CustomerId BIGINT DEFAULT nextval('Seq'), + CustomerName character varying(1024), + PRIMARY KEY (CustomerId) + )""", + ], + ) + operation = spanner_client.database_admin_api.update_database_ddl(request) + print("Waiting for operation to complete...") + operation.result(OPERATION_TIMEOUT_SECONDS) + + print( + "Created Seq sequence and Customers table, where the key column CustomerId uses the sequence as a default value on database {} on instance {}".format( + database_id, instance_id + ) + ) + + def insert_customers(transaction): + results = transaction.execute_sql( + "INSERT INTO Customers (CustomerName) VALUES " + "('Alice'), " + "('David'), " + "('Marc') " + "RETURNING CustomerId" + ) + for result in results: + print("Inserted customer record with Customer Id: {}".format(*result)) + print( + "Number of customer records inserted is {}".format( + results.stats.row_count_exact + ) + ) + + database.run_in_transaction(insert_customers) + + +# [END spanner_postgresql_create_sequence] + +# [START spanner_postgresql_alter_sequence] +def alter_sequence(instance_id, database_id): + """Alters the Sequence and insert data""" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + + operation = database.update_ddl(["ALTER SEQUENCE Seq SKIP RANGE 1000 5000000"]) + + print("Waiting for operation to complete...") + operation.result(OPERATION_TIMEOUT_SECONDS) + + print( + "Altered Seq sequence to skip an inclusive range between 1000 and 5000000 on database {} on instance {}".format( + database_id, instance_id + ) + ) + + def insert_customers(transaction): + results = transaction.execute_sql( + "INSERT INTO Customers (CustomerName) VALUES " + "('Lea'), " + "('Cataline'), " + "('Smith') " + "RETURNING CustomerId" + ) + for result in results: + print("Inserted customer record with Customer Id: {}".format(*result)) + print( + "Number of customer records inserted is {}".format( + results.stats.row_count_exact + ) + ) + + database.run_in_transaction(insert_customers) + + +# [END spanner_postgresql_alter_sequence] + +# [START spanner_postgresql_drop_sequence] +def drop_sequence(instance_id, database_id): + """Drops the Sequence""" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + + operation = database.update_ddl( + [ + "ALTER TABLE Customers ALTER COLUMN CustomerId DROP DEFAULT", + "DROP SEQUENCE Seq", + ] + ) + + print("Waiting for operation to complete...") + operation.result(OPERATION_TIMEOUT_SECONDS) + + print( + "Altered Customers table to drop DEFAULT from CustomerId column and dropped the Seq sequence on database {} on instance {}".format( + database_id, instance_id + ) + ) + + +# [END spanner_postgresql_drop_sequence] + if __name__ == "__main__": # noqa: C901 parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument("instance_id", help="Your Cloud Spanner instance ID.") parser.add_argument( - "--database-id", help="Your Cloud Spanner database ID.", - default="example_db" + "--database-id", help="Your Cloud Spanner database ID.", default="example_db" ) subparsers = parser.add_subparsers(dest="command") @@ -1586,98 +1686,91 @@ def query_data_with_jsonb_parameter(instance_id, database_id): subparsers.add_parser("add_column", help=add_column.__doc__) subparsers.add_parser("update_data", help=update_data.__doc__) subparsers.add_parser( - "query_data_with_new_column", help=query_data_with_new_column.__doc__ + "query_data_with_new_column", help=query_data_with_new_column.__doc__ ) - subparsers.add_parser("read_write_transaction", - help=read_write_transaction.__doc__) - subparsers.add_parser("read_only_transaction", - help=read_only_transaction.__doc__) + subparsers.add_parser("read_write_transaction", help=read_write_transaction.__doc__) + subparsers.add_parser("read_only_transaction", help=read_only_transaction.__doc__) subparsers.add_parser("add_index", help=add_index.__doc__) - subparsers.add_parser("read_data_with_index", - help=read_data_with_index.__doc__) + subparsers.add_parser("read_data_with_index", help=read_data_with_index.__doc__) subparsers.add_parser("add_storing_index", help=add_storing_index.__doc__) - subparsers.add_parser("read_data_with_storing_index", - help=read_data_with_storing_index.__doc__) subparsers.add_parser( - "create_table_with_timestamp", help=create_table_with_timestamp.__doc__ + "read_data_with_storing_index", help=read_data_with_storing_index.__doc__ ) subparsers.add_parser( - "insert_data_with_timestamp", help=insert_data_with_timestamp.__doc__ + "create_table_with_timestamp", help=create_table_with_timestamp.__doc__ ) - subparsers.add_parser("add_timestamp_column", - help=add_timestamp_column.__doc__) subparsers.add_parser( - "update_data_with_timestamp", help=update_data_with_timestamp.__doc__ + "insert_data_with_timestamp", help=insert_data_with_timestamp.__doc__ ) + subparsers.add_parser("add_timestamp_column", help=add_timestamp_column.__doc__) subparsers.add_parser( - "query_data_with_timestamp", help=query_data_with_timestamp.__doc__ + "update_data_with_timestamp", help=update_data_with_timestamp.__doc__ ) - subparsers.add_parser("insert_data_with_dml", - help=insert_data_with_dml.__doc__) - subparsers.add_parser("update_data_with_dml", - help=update_data_with_dml.__doc__) - subparsers.add_parser("update_data_with_dml", - help=update_data_with_dml_returning.__doc__) - subparsers.add_parser("delete_data_with_dml", - help=delete_data_with_dml.__doc__) - subparsers.add_parser("delete_data_with_dml_returning", - help=delete_data_with_dml_returning.__doc__) subparsers.add_parser( - "dml_write_read_transaction", help=dml_write_read_transaction.__doc__ + "query_data_with_timestamp", help=query_data_with_timestamp.__doc__ + ) + subparsers.add_parser("insert_data_with_dml", help=insert_data_with_dml.__doc__) + subparsers.add_parser("update_data_with_dml", help=update_data_with_dml.__doc__) + subparsers.add_parser( + "update_data_with_dml", help=update_data_with_dml_returning.__doc__ + ) + subparsers.add_parser("delete_data_with_dml", help=delete_data_with_dml.__doc__) + subparsers.add_parser( + "delete_data_with_dml_returning", help=delete_data_with_dml_returning.__doc__ + ) + subparsers.add_parser( + "dml_write_read_transaction", help=dml_write_read_transaction.__doc__ ) subparsers.add_parser("insert_with_dml", help=insert_with_dml.__doc__) - subparsers.add_parser("insert_with_dml_returning", help=insert_with_dml_returning.__doc__) subparsers.add_parser( - "query_data_with_parameter", help=query_data_with_parameter.__doc__ + "insert_with_dml_returning", help=insert_with_dml_returning.__doc__ + ) + subparsers.add_parser( + "query_data_with_parameter", help=query_data_with_parameter.__doc__ ) subparsers.add_parser( - "write_with_dml_transaction", help=write_with_dml_transaction.__doc__ + "write_with_dml_transaction", help=write_with_dml_transaction.__doc__ ) subparsers.add_parser( - "update_data_with_partitioned_dml", - help=update_data_with_partitioned_dml.__doc__, + "update_data_with_partitioned_dml", + help=update_data_with_partitioned_dml.__doc__, ) subparsers.add_parser( - "delete_data_with_partitioned_dml", - help=delete_data_with_partitioned_dml.__doc__, + "delete_data_with_partitioned_dml", + help=delete_data_with_partitioned_dml.__doc__, ) - subparsers.add_parser("update_with_batch_dml", - help=update_with_batch_dml.__doc__) + subparsers.add_parser("update_with_batch_dml", help=update_with_batch_dml.__doc__) subparsers.add_parser( - "create_table_with_datatypes", help=create_table_with_datatypes.__doc__ + "create_table_with_datatypes", help=create_table_with_datatypes.__doc__ ) - subparsers.add_parser("insert_datatypes_data", - help=insert_datatypes_data.__doc__) - subparsers.add_parser("query_data_with_bool", - help=query_data_with_bool.__doc__) - subparsers.add_parser("query_data_with_bytes", - help=query_data_with_bytes.__doc__) - subparsers.add_parser("query_data_with_float", - help=query_data_with_float.__doc__) - subparsers.add_parser("query_data_with_int", - help=query_data_with_int.__doc__) - subparsers.add_parser("query_data_with_string", - help=query_data_with_string.__doc__) + subparsers.add_parser("insert_datatypes_data", help=insert_datatypes_data.__doc__) + subparsers.add_parser("query_data_with_bool", help=query_data_with_bool.__doc__) + subparsers.add_parser("query_data_with_bytes", help=query_data_with_bytes.__doc__) + subparsers.add_parser("query_data_with_float", help=query_data_with_float.__doc__) + subparsers.add_parser("query_data_with_int", help=query_data_with_int.__doc__) + subparsers.add_parser("query_data_with_string", help=query_data_with_string.__doc__) subparsers.add_parser( - "query_data_with_timestamp_parameter", - help=query_data_with_timestamp_parameter.__doc__, + "query_data_with_timestamp_parameter", + help=query_data_with_timestamp_parameter.__doc__, ) subparsers.add_parser( - "update_data_with_numeric", - help=update_data_with_numeric.__doc__, + "update_data_with_numeric", + help=update_data_with_numeric.__doc__, ) subparsers.add_parser( - "query_data_with_numeric_parameter", - help=query_data_with_numeric_parameter.__doc__, + "query_data_with_numeric_parameter", + help=query_data_with_numeric_parameter.__doc__, ) subparsers.add_parser( - "query_data_with_query_options", - help=query_data_with_query_options.__doc__ + "query_data_with_query_options", help=query_data_with_query_options.__doc__ ) subparsers.add_parser( - "create_client_with_query_options", - help=create_client_with_query_options.__doc__, + "create_client_with_query_options", + help=create_client_with_query_options.__doc__, ) + subparsers.add_parser("create_sequence", help=create_sequence.__doc__) + subparsers.add_parser("alter_sequence", help=alter_sequence.__doc__) + subparsers.add_parser("drop_sequence", help=drop_sequence.__doc__) args = parser.parse_args() diff --git a/samples/samples/pg_snippets_test.py b/samples/samples/pg_snippets_test.py index 679b818ed1..d4f08499d2 100644 --- a/samples/samples/pg_snippets_test.py +++ b/samples/samples/pg_snippets_test.py @@ -190,8 +190,7 @@ def test_read_write_transaction(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["add_column"]) def test_query_data_with_new_column(capsys, instance_id, sample_database): - snippets.query_data_with_new_column(instance_id, - sample_database.database_id) + snippets.query_data_with_new_column(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "SingerId: 1, AlbumId: 1, MarketingBudget: 300000" in out assert "SingerId: 2, AlbumId: 2, MarketingBudget: 300000" in out @@ -222,8 +221,7 @@ def test_add_storing_index(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["add_storing_index"]) def test_read_data_with_storing_index(capsys, instance_id, sample_database): - snippets.read_data_with_storing_index(instance_id, - sample_database.database_id) + snippets.read_data_with_storing_index(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "300000" in out @@ -245,8 +243,7 @@ def test_add_timestamp_column(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["add_timestamp_column"]) def test_update_data_with_timestamp(capsys, instance_id, sample_database): - snippets.update_data_with_timestamp(instance_id, - sample_database.database_id) + snippets.update_data_with_timestamp(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "Updated data" in out @@ -261,16 +258,14 @@ def test_query_data_with_timestamp(capsys, instance_id, sample_database): @pytest.mark.dependency(name="create_table_with_timestamp") def test_create_table_with_timestamp(capsys, instance_id, sample_database): - snippets.create_table_with_timestamp(instance_id, - sample_database.database_id) + snippets.create_table_with_timestamp(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "Created Performances table on database" in out @pytest.mark.dependency(depends=["create_table_with_timestamp"]) def test_insert_data_with_timestamp(capsys, instance_id, sample_database): - snippets.insert_data_with_timestamp(instance_id, - sample_database.database_id) + snippets.insert_data_with_timestamp(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "Inserted data." in out @@ -312,8 +307,7 @@ def test_delete_data_with_dml_returning(capsys, instance_id, sample_database): @pytest.mark.dependency(name="dml_write_read_transaction") def test_dml_write_read_transaction(capsys, instance_id, sample_database): - snippets.dml_write_read_transaction(instance_id, - sample_database.database_id) + snippets.dml_write_read_transaction(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "1 record(s) inserted." in out assert "FirstName: Timothy, LastName: Campbell" in out @@ -342,24 +336,21 @@ def test_query_data_with_parameter(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["add_column"]) def test_write_with_dml_transaction(capsys, instance_id, sample_database): - snippets.write_with_dml_transaction(instance_id, - sample_database.database_id) + snippets.write_with_dml_transaction(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "Transferred 200000 from Album2's budget to Album1's" in out @pytest.mark.dependency(depends=["add_column"]) def update_data_with_partitioned_dml(capsys, instance_id, sample_database): - snippets.update_data_with_partitioned_dml(instance_id, - sample_database.database_id) + snippets.update_data_with_partitioned_dml(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "3 record(s) updated" in out @pytest.mark.dependency(depends=["insert_with_dml", "insert_with_dml_returning"]) def test_delete_data_with_partitioned_dml(capsys, instance_id, sample_database): - snippets.delete_data_with_partitioned_dml(instance_id, - sample_database.database_id) + snippets.delete_data_with_partitioned_dml(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "9 record(s) deleted" in out @@ -373,15 +364,14 @@ def test_update_with_batch_dml(capsys, instance_id, sample_database): @pytest.mark.dependency(name="create_table_with_datatypes") def test_create_table_with_datatypes(capsys, instance_id, sample_database): - snippets.create_table_with_datatypes(instance_id, - sample_database.database_id) + snippets.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="insert_datatypes_data", - depends=["create_table_with_datatypes"], + name="insert_datatypes_data", + depends=["create_table_with_datatypes"], ) def test_insert_datatypes_data(capsys, instance_id, sample_database): snippets.insert_datatypes_data(instance_id, sample_database.database_id) @@ -434,19 +424,16 @@ def test_update_data_with_numeric(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["insert_datatypes_data"]) -def test_query_data_with_numeric_parameter(capsys, instance_id, - sample_database): - snippets.query_data_with_numeric_parameter(instance_id, - sample_database.database_id) +def test_query_data_with_numeric_parameter(capsys, instance_id, sample_database): + snippets.query_data_with_numeric_parameter(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "VenueId: 4, Revenue: 35000" in out @pytest.mark.dependency(depends=["insert_datatypes_data"]) -def test_query_data_with_timestamp_parameter(capsys, instance_id, - sample_database): +def test_query_data_with_timestamp_parameter(capsys, instance_id, sample_database): snippets.query_data_with_timestamp_parameter( - instance_id, sample_database.database_id + instance_id, sample_database.database_id ) out, _ = capsys.readouterr() assert "VenueId: 4, VenueName: Venue 4, LastUpdateTime:" in out @@ -456,8 +443,7 @@ def test_query_data_with_timestamp_parameter(capsys, instance_id, @pytest.mark.dependency(depends=["insert_datatypes_data"]) def test_query_data_with_query_options(capsys, instance_id, sample_database): - snippets.query_data_with_query_options(instance_id, - sample_database.database_id) + snippets.query_data_with_query_options(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "VenueId: 4, VenueName: Venue 4, LastUpdateTime:" in out assert "VenueId: 19, VenueName: Venue 19, LastUpdateTime:" in out @@ -466,8 +452,7 @@ def test_query_data_with_query_options(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["insert_datatypes_data"]) def test_create_client_with_query_options(capsys, instance_id, sample_database): - snippets.create_client_with_query_options(instance_id, - sample_database.database_id) + snippets.create_client_with_query_options(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "VenueId: 4, VenueName: Venue 4, LastUpdateTime:" in out assert "VenueId: 19, VenueName: Venue 19, LastUpdateTime:" in out @@ -494,3 +479,36 @@ def test_query_data_with_jsonb_parameter(capsys, instance_id, sample_database): snippets.query_data_with_jsonb_parameter(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "VenueId: 19, VenueDetails: {'open': True, 'rating': 9}" in out + + +def test_create_sequence(capsys, instance_id, bit_reverse_sequence_database): + snippets.create_sequence(instance_id, bit_reverse_sequence_database.database_id) + out, _ = capsys.readouterr() + assert ( + "Created Seq sequence and Customers table, where the key column CustomerId uses the sequence as a default value on database" + in out + ) + assert "Number of customer records inserted is 3" in out + assert "Inserted customer record with Customer Id:" in out + + +@pytest.mark.dependency(depends=["create_sequence"]) +def test_alter_sequence(capsys, instance_id, bit_reverse_sequence_database): + snippets.alter_sequence(instance_id, bit_reverse_sequence_database.database_id) + out, _ = capsys.readouterr() + assert ( + "Altered Seq sequence to skip an inclusive range between 1000 and 5000000 on database" + in out + ) + assert "Number of customer records inserted is 3" in out + assert "Inserted customer record with Customer Id:" in out + + +@pytest.mark.dependency(depends=["alter_sequence"]) +def test_drop_sequence(capsys, instance_id, bit_reverse_sequence_database): + snippets.drop_sequence(instance_id, bit_reverse_sequence_database.database_id) + out, _ = capsys.readouterr() + assert ( + "Altered Customers table to drop DEFAULT from CustomerId column and dropped the Seq sequence on database" + in out + ) diff --git a/samples/samples/snippets.py b/samples/samples/snippets.py index 57590551ad..82fb95a0dd 100644 --- a/samples/samples/snippets.py +++ b/samples/samples/snippets.py @@ -35,6 +35,7 @@ from google.iam.v1 import policy_pb2 from google.cloud.spanner_v1.data_types import JsonObject from google.protobuf import field_mask_pb2 # type: ignore + OPERATION_TIMEOUT_SECONDS = 240 @@ -207,8 +208,7 @@ def update_database(instance_id, database_id): operation = db.update(["enable_drop_protection"]) - print("Waiting for update operation for {} to complete...".format( - db.name)) + print("Waiting for update operation for {} to complete...".format(db.name)) operation.result(OPERATION_TIMEOUT_SECONDS) print("Updated database {}.".format(db.name)) @@ -1423,7 +1423,7 @@ def delete_singers(transaction): def delete_data_with_dml_returning(instance_id, database_id): - """Deletes sample data from the database using a DML statement having a THEN RETURN clause. """ + """Deletes sample data from the database using a DML statement having a THEN RETURN clause.""" # [START spanner_dml_delete_returning] # instance_id = "your-spanner-instance" # database_id = "your-spanner-db-id" @@ -1559,7 +1559,7 @@ def insert_singers(transaction): def insert_with_dml_returning(instance_id, database_id): - """Inserts sample data into the given database using a DML statement having a THEN RETURN clause. """ + """Inserts sample data into the given database using a DML statement having a THEN RETURN clause.""" # [START spanner_dml_insert_returning] # instance_id = "your-spanner-instance" # database_id = "your-spanner-db-id" @@ -1748,7 +1748,7 @@ def update_albums(transaction): def create_table_with_datatypes(instance_id, database_id): - """Creates a table with supported datatypes. """ + """Creates a table with supported datatypes.""" # [START spanner_create_table_with_datatypes] # instance_id = "your-spanner-instance" # database_id = "your-spanner-db-id" @@ -2449,6 +2449,221 @@ def enable_fine_grained_access( # [END spanner_enable_fine_grained_access] +# [START spanner_create_table_with_foreign_key_delete_cascade] +def create_table_with_foreign_key_delete_cascade(instance_id, database_id): + """Creates a table with foreign key delete cascade action""" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + + operation = database.update_ddl( + [ + """CREATE TABLE Customers ( + CustomerId INT64 NOT NULL, + CustomerName STRING(62) NOT NULL, + ) PRIMARY KEY (CustomerId) + """, + """ + CREATE TABLE ShoppingCarts ( + CartId INT64 NOT NULL, + CustomerId INT64 NOT NULL, + CustomerName STRING(62) NOT NULL, + CONSTRAINT FKShoppingCartsCustomerId FOREIGN KEY (CustomerId) + REFERENCES Customers (CustomerId) ON DELETE CASCADE + ) PRIMARY KEY (CartId) + """, + ] + ) + + print("Waiting for operation to complete...") + operation.result(OPERATION_TIMEOUT_SECONDS) + + print( + """Created Customers and ShoppingCarts table with FKShoppingCartsCustomerId + foreign key constraint on database {} on instance {}""".format( + database_id, instance_id + ) + ) + + +# [END spanner_create_table_with_foreign_key_delete_cascade] + + +# [START spanner_alter_table_with_foreign_key_delete_cascade] +def alter_table_with_foreign_key_delete_cascade(instance_id, database_id): + """Alters a table with foreign key delete cascade action""" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + + operation = database.update_ddl( + [ + """ALTER TABLE ShoppingCarts + ADD CONSTRAINT FKShoppingCartsCustomerName + FOREIGN KEY (CustomerName) + REFERENCES Customers(CustomerName) + ON DELETE CASCADE""" + ] + ) + + print("Waiting for operation to complete...") + operation.result(OPERATION_TIMEOUT_SECONDS) + + print( + """Altered ShoppingCarts table with FKShoppingCartsCustomerName + foreign key constraint on database {} on instance {}""".format( + database_id, instance_id + ) + ) + + +# [END spanner_alter_table_with_foreign_key_delete_cascade] + + +# [START spanner_drop_foreign_key_constraint_delete_cascade] +def drop_foreign_key_constraint_delete_cascade(instance_id, database_id): + """Alter table to drop foreign key delete cascade action""" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + + operation = database.update_ddl( + [ + """ALTER TABLE ShoppingCarts + DROP CONSTRAINT FKShoppingCartsCustomerName""" + ] + ) + + print("Waiting for operation to complete...") + operation.result(OPERATION_TIMEOUT_SECONDS) + + print( + """Altered ShoppingCarts table to drop FKShoppingCartsCustomerName + foreign key constraint on database {} on instance {}""".format( + database_id, instance_id + ) + ) + + +# [END spanner_drop_foreign_key_constraint_delete_cascade] + + +# [START spanner_create_sequence] +def create_sequence(instance_id, database_id): + """Creates the Sequence and insert data""" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + + operation = database.update_ddl( + [ + "CREATE SEQUENCE Seq OPTIONS (sequence_kind = 'bit_reversed_positive')", + """CREATE TABLE Customers ( + CustomerId INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(Sequence Seq)), + CustomerName STRING(1024) + ) PRIMARY KEY (CustomerId)""", + ] + ) + + print("Waiting for operation to complete...") + operation.result(OPERATION_TIMEOUT_SECONDS) + + print( + "Created Seq sequence and Customers table, where the key column CustomerId uses the sequence as a default value on database {} on instance {}".format( + database_id, instance_id + ) + ) + + def insert_customers(transaction): + results = transaction.execute_sql( + "INSERT INTO Customers (CustomerName) VALUES " + "('Alice'), " + "('David'), " + "('Marc') " + "THEN RETURN CustomerId" + ) + for result in results: + print("Inserted customer record with Customer Id: {}".format(*result)) + print( + "Number of customer records inserted is {}".format( + results.stats.row_count_exact + ) + ) + + database.run_in_transaction(insert_customers) + + +# [END spanner_create_sequence] + +# [START spanner_alter_sequence] +def alter_sequence(instance_id, database_id): + """Alters the Sequence and insert data""" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + + operation = database.update_ddl( + [ + "ALTER SEQUENCE Seq SET OPTIONS (skip_range_min = 1000, skip_range_max = 5000000)" + ] + ) + + print("Waiting for operation to complete...") + operation.result(OPERATION_TIMEOUT_SECONDS) + + print( + "Altered Seq sequence to skip an inclusive range between 1000 and 5000000 on database {} on instance {}".format( + database_id, instance_id + ) + ) + + def insert_customers(transaction): + results = transaction.execute_sql( + "INSERT INTO Customers (CustomerName) VALUES " + "('Lea'), " + "('Cataline'), " + "('Smith') " + "THEN RETURN CustomerId" + ) + for result in results: + print("Inserted customer record with Customer Id: {}".format(*result)) + print( + "Number of customer records inserted is {}".format( + results.stats.row_count_exact + ) + ) + + database.run_in_transaction(insert_customers) + + +# [END spanner_alter_sequence] + +# [START spanner_drop_sequence] +def drop_sequence(instance_id, database_id): + """Drops the Sequence""" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + + operation = database.update_ddl( + [ + "ALTER TABLE Customers ALTER COLUMN CustomerId DROP DEFAULT", + "DROP SEQUENCE Seq", + ] + ) + + print("Waiting for operation to complete...") + operation.result(OPERATION_TIMEOUT_SECONDS) + + print( + "Altered Customers table to drop DEFAULT from CustomerId column and dropped the Seq sequence on database {} on instance {}".format( + database_id, instance_id + ) + ) + + +# [END spanner_drop_sequence] + if __name__ == "__main__": # noqa: C901 parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter @@ -2481,7 +2696,9 @@ def enable_fine_grained_access( query_data_with_index_parser.add_argument("--end_title", default="Goo") subparsers.add_parser("read_data_with_index", help=read_data_with_index.__doc__) subparsers.add_parser("add_storing_index", help=add_storing_index.__doc__) - subparsers.add_parser("read_data_with_storing_index", help=read_data_with_storing_index.__doc__) + subparsers.add_parser( + "read_data_with_storing_index", help=read_data_with_storing_index.__doc__ + ) subparsers.add_parser( "create_table_with_timestamp", help=create_table_with_timestamp.__doc__ ) @@ -2507,9 +2724,13 @@ def enable_fine_grained_access( subparsers.add_parser("insert_data_with_dml", help=insert_data_with_dml.__doc__) subparsers.add_parser("log_commit_stats", help=log_commit_stats.__doc__) subparsers.add_parser("update_data_with_dml", help=update_data_with_dml.__doc__) - subparsers.add_parser("update_data_with_dml_returning", help=update_data_with_dml_returning.__doc__) + subparsers.add_parser( + "update_data_with_dml_returning", help=update_data_with_dml_returning.__doc__ + ) subparsers.add_parser("delete_data_with_dml", help=delete_data_with_dml.__doc__) - subparsers.add_parser("delete_data_with_dml_returning", help=delete_data_with_dml_returning.__doc__) + subparsers.add_parser( + "delete_data_with_dml_returning", help=delete_data_with_dml_returning.__doc__ + ) subparsers.add_parser( "update_data_with_dml_timestamp", help=update_data_with_dml_timestamp.__doc__ ) @@ -2520,7 +2741,9 @@ def enable_fine_grained_access( "update_data_with_dml_struct", help=update_data_with_dml_struct.__doc__ ) subparsers.add_parser("insert_with_dml", help=insert_with_dml.__doc__) - subparsers.add_parser("insert_with_dml_returning", help=insert_with_dml_returning.__doc__) + subparsers.add_parser( + "insert_with_dml_returning", help=insert_with_dml_returning.__doc__ + ) subparsers.add_parser( "query_data_with_parameter", help=query_data_with_parameter.__doc__ ) @@ -2565,6 +2788,10 @@ def enable_fine_grained_access( "read_data_with_database_role", help=read_data_with_database_role.__doc__ ) subparsers.add_parser("list_database_roles", help=list_database_roles.__doc__) + subparsers.add_parser("create_sequence", help=create_sequence.__doc__) + subparsers.add_parser("alter_sequence", help=alter_sequence.__doc__) + subparsers.add_parser("drop_sequence", help=drop_sequence.__doc__) + enable_fine_grained_access_parser = subparsers.add_parser( "enable_fine_grained_access", help=enable_fine_grained_access.__doc__ ) diff --git a/samples/samples/snippets_test.py b/samples/samples/snippets_test.py index b8e1e093a1..22b5b6f944 100644 --- a/samples/samples/snippets_test.py +++ b/samples/samples/snippets_test.py @@ -114,7 +114,7 @@ def user_managed_instance_config_name(spanner_client): name = f"custom-python-samples-config-{uuid.uuid4().hex[:10]}" yield name snippets.delete_instance_config( - "{}/instanceConfigs/{}".format(spanner_client.project_name, name) + "{}/instanceConfigs/{}".format(spanner_client.project_name, name) ) return @@ -143,8 +143,8 @@ def test_create_database_explicit(sample_instance, create_database_id): def test_create_instance_with_processing_units(capsys, lci_instance_id): processing_units = 500 retry_429(snippets.create_instance_with_processing_units)( - lci_instance_id, - processing_units, + lci_instance_id, + processing_units, ) out, _ = capsys.readouterr() assert lci_instance_id in out @@ -155,9 +155,7 @@ def test_create_instance_with_processing_units(capsys, lci_instance_id): def test_update_database(capsys, instance_id, sample_database): - snippets.update_database( - instance_id, sample_database.database_id - ) + snippets.update_database(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "Updated database {}.".format(sample_database.name) in out @@ -168,10 +166,10 @@ def test_update_database(capsys, instance_id, sample_database): def test_create_database_with_encryption_config( - capsys, instance_id, cmek_database_id, kms_key_name + capsys, instance_id, cmek_database_id, kms_key_name ): snippets.create_database_with_encryption_key( - instance_id, cmek_database_id, kms_key_name + instance_id, cmek_database_id, kms_key_name ) out, _ = capsys.readouterr() assert cmek_database_id in out @@ -193,10 +191,10 @@ def test_list_instance_config(capsys): @pytest.mark.dependency(name="create_instance_config") def test_create_instance_config( - capsys, user_managed_instance_config_name, base_instance_config_id + capsys, user_managed_instance_config_name, base_instance_config_id ): snippets.create_instance_config( - user_managed_instance_config_name, base_instance_config_id + user_managed_instance_config_name, base_instance_config_id ) out, _ = capsys.readouterr() assert "Created instance configuration" in out @@ -213,9 +211,9 @@ def test_update_instance_config(capsys, user_managed_instance_config_name): def test_delete_instance_config(capsys, user_managed_instance_config_name): spanner_client = spanner.Client() snippets.delete_instance_config( - "{}/instanceConfigs/{}".format( - spanner_client.project_name, user_managed_instance_config_name - ) + "{}/instanceConfigs/{}".format( + spanner_client.project_name, user_managed_instance_config_name + ) ) out, _ = capsys.readouterr() assert "successfully deleted" in out @@ -234,15 +232,15 @@ def test_list_databases(capsys, instance_id): def test_create_database_with_default_leader( - capsys, - multi_region_instance, - multi_region_instance_id, - default_leader_database_id, - default_leader, + capsys, + multi_region_instance, + multi_region_instance_id, + default_leader_database_id, + default_leader, ): retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) retry_429(snippets.create_database_with_default_leader)( - multi_region_instance_id, default_leader_database_id, default_leader + multi_region_instance_id, default_leader_database_id, default_leader ) out, _ = capsys.readouterr() assert default_leader_database_id in out @@ -250,15 +248,15 @@ def test_create_database_with_default_leader( def test_update_database_with_default_leader( - capsys, - multi_region_instance, - multi_region_instance_id, - default_leader_database_id, - default_leader, + capsys, + multi_region_instance, + multi_region_instance_id, + default_leader_database_id, + default_leader, ): retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) retry_429(snippets.update_database_with_default_leader)( - multi_region_instance_id, default_leader_database_id, default_leader + multi_region_instance_id, default_leader_database_id, default_leader ) out, _ = capsys.readouterr() assert default_leader_database_id in out @@ -272,14 +270,14 @@ def test_get_database_ddl(capsys, instance_id, sample_database): def test_query_information_schema_database_options( - capsys, - multi_region_instance, - multi_region_instance_id, - default_leader_database_id, - default_leader, + capsys, + multi_region_instance, + multi_region_instance_id, + default_leader_database_id, + default_leader, ): snippets.query_information_schema_database_options( - multi_region_instance_id, default_leader_database_id + multi_region_instance_id, default_leader_database_id ) out, _ = capsys.readouterr() assert default_leader in out @@ -351,8 +349,7 @@ def test_read_write_transaction(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["add_column"]) def test_query_data_with_new_column(capsys, instance_id, sample_database): - snippets.query_data_with_new_column(instance_id, - sample_database.database_id) + snippets.query_data_with_new_column(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "SingerId: 1, AlbumId: 1, MarketingBudget: 300000" in out assert "SingerId: 2, AlbumId: 2, MarketingBudget: 300000" in out @@ -392,8 +389,7 @@ def test_add_storing_index(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["add_storing_index"]) def test_read_data_with_storing_index(capsys, instance_id, sample_database): - snippets.read_data_with_storing_index(instance_id, - sample_database.database_id) + snippets.read_data_with_storing_index(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "300000" in out @@ -415,8 +411,7 @@ def test_add_timestamp_column(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["add_timestamp_column"]) def test_update_data_with_timestamp(capsys, instance_id, sample_database): - snippets.update_data_with_timestamp(instance_id, - sample_database.database_id) + snippets.update_data_with_timestamp(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "Updated data" in out @@ -431,16 +426,14 @@ def test_query_data_with_timestamp(capsys, instance_id, sample_database): @pytest.mark.dependency(name="create_table_with_timestamp") def test_create_table_with_timestamp(capsys, instance_id, sample_database): - snippets.create_table_with_timestamp(instance_id, - sample_database.database_id) + snippets.create_table_with_timestamp(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "Created Performances table on database" in out @pytest.mark.dependency(depends=["create_table_with_timestamp"]) def test_insert_data_with_timestamp(capsys, instance_id, sample_database): - snippets.insert_data_with_timestamp(instance_id, - sample_database.database_id) + snippets.insert_data_with_timestamp(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "Inserted data." in out @@ -461,8 +454,7 @@ def test_query_with_struct(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["write_struct_data"]) def test_query_with_array_of_struct(capsys, instance_id, sample_database): - snippets.query_with_array_of_struct(instance_id, - sample_database.database_id) + snippets.query_with_array_of_struct(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "SingerId: 8" in out assert "SingerId: 7" in out @@ -530,16 +522,14 @@ def test_delete_data_with_dml_returning(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["add_timestamp_column"]) def test_update_data_with_dml_timestamp(capsys, instance_id, sample_database): - snippets.update_data_with_dml_timestamp(instance_id, - sample_database.database_id) + snippets.update_data_with_dml_timestamp(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "2 record(s) updated." in out @pytest.mark.dependency(name="dml_write_read_transaction") def test_dml_write_read_transaction(capsys, instance_id, sample_database): - snippets.dml_write_read_transaction(instance_id, - sample_database.database_id) + snippets.dml_write_read_transaction(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "1 record(s) inserted." in out assert "FirstName: Timothy, LastName: Campbell" in out @@ -547,8 +537,7 @@ def test_dml_write_read_transaction(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["dml_write_read_transaction"]) def test_update_data_with_dml_struct(capsys, instance_id, sample_database): - snippets.update_data_with_dml_struct(instance_id, - sample_database.database_id) + snippets.update_data_with_dml_struct(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "1 record(s) updated" in out @@ -576,24 +565,21 @@ def test_query_data_with_parameter(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["add_column"]) def test_write_with_dml_transaction(capsys, instance_id, sample_database): - snippets.write_with_dml_transaction(instance_id, - sample_database.database_id) + snippets.write_with_dml_transaction(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "Transferred 200000 from Album2's budget to Album1's" in out @pytest.mark.dependency(depends=["add_column"]) def update_data_with_partitioned_dml(capsys, instance_id, sample_database): - snippets.update_data_with_partitioned_dml(instance_id, - sample_database.database_id) + snippets.update_data_with_partitioned_dml(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "3 record(s) updated" in out @pytest.mark.dependency(depends=["insert_with_dml"]) def test_delete_data_with_partitioned_dml(capsys, instance_id, sample_database): - snippets.delete_data_with_partitioned_dml(instance_id, - sample_database.database_id) + snippets.delete_data_with_partitioned_dml(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "6 record(s) deleted" in out @@ -607,15 +593,14 @@ def test_update_with_batch_dml(capsys, instance_id, sample_database): @pytest.mark.dependency(name="create_table_with_datatypes") def test_create_table_with_datatypes(capsys, instance_id, sample_database): - snippets.create_table_with_datatypes(instance_id, - sample_database.database_id) + snippets.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="insert_datatypes_data", - depends=["create_table_with_datatypes"], + name="insert_datatypes_data", + depends=["create_table_with_datatypes"], ) def test_insert_datatypes_data(capsys, instance_id, sample_database): snippets.insert_datatypes_data(instance_id, sample_database.database_id) @@ -677,8 +662,8 @@ def test_query_data_with_string(capsys, instance_id, sample_database): @pytest.mark.dependency( - name="add_numeric_column", - depends=["create_table_with_datatypes"], + name="add_numeric_column", + depends=["create_table_with_datatypes"], ) def test_add_numeric_column(capsys, instance_id, sample_database): snippets.add_numeric_column(instance_id, sample_database.database_id) @@ -694,17 +679,15 @@ def test_update_data_with_numeric(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["add_numeric_column"]) -def test_query_data_with_numeric_parameter(capsys, instance_id, - sample_database): - snippets.query_data_with_numeric_parameter(instance_id, - sample_database.database_id) +def test_query_data_with_numeric_parameter(capsys, instance_id, sample_database): + snippets.query_data_with_numeric_parameter(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "VenueId: 4, Revenue: 35000" in out @pytest.mark.dependency( - name="add_json_column", - depends=["create_table_with_datatypes"], + name="add_json_column", + depends=["create_table_with_datatypes"], ) def test_add_json_column(capsys, instance_id, sample_database): snippets.add_json_column(instance_id, sample_database.database_id) @@ -721,17 +704,15 @@ def test_update_data_with_json(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["add_json_column"]) def test_query_data_with_json_parameter(capsys, instance_id, sample_database): - snippets.query_data_with_json_parameter(instance_id, - sample_database.database_id) + snippets.query_data_with_json_parameter(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "VenueId: 19, VenueDetails: {'open': True, 'rating': 9}" in out @pytest.mark.dependency(depends=["insert_datatypes_data"]) -def test_query_data_with_timestamp_parameter(capsys, instance_id, - sample_database): +def test_query_data_with_timestamp_parameter(capsys, instance_id, sample_database): snippets.query_data_with_timestamp_parameter( - instance_id, sample_database.database_id + instance_id, sample_database.database_id ) out, _ = capsys.readouterr() assert "VenueId: 4, VenueName: Venue 4, LastUpdateTime:" in out @@ -741,8 +722,7 @@ def test_query_data_with_timestamp_parameter(capsys, instance_id, @pytest.mark.dependency(depends=["insert_datatypes_data"]) def test_query_data_with_query_options(capsys, instance_id, sample_database): - snippets.query_data_with_query_options(instance_id, - sample_database.database_id) + snippets.query_data_with_query_options(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "VenueId: 4, VenueName: Venue 4, LastUpdateTime:" in out assert "VenueId: 19, VenueName: Venue 19, LastUpdateTime:" in out @@ -751,8 +731,7 @@ def test_query_data_with_query_options(capsys, instance_id, sample_database): @pytest.mark.dependency(depends=["insert_datatypes_data"]) def test_create_client_with_query_options(capsys, instance_id, sample_database): - snippets.create_client_with_query_options(instance_id, - sample_database.database_id) + snippets.create_client_with_query_options(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "VenueId: 4, VenueName: Venue 4, LastUpdateTime:" in out assert "VenueId: 19, VenueName: Venue 19, LastUpdateTime:" in out @@ -794,3 +773,75 @@ def test_list_database_roles(capsys, instance_id, sample_database): snippets.list_database_roles(instance_id, sample_database.database_id) out, _ = capsys.readouterr() assert "new_parent" in out + + +@pytest.mark.dependency(name="create_table_with_foreign_key_delete_cascade") +def test_create_table_with_foreign_key_delete_cascade( + capsys, instance_id, sample_database +): + snippets.create_table_with_foreign_key_delete_cascade( + instance_id, sample_database.database_id + ) + out, _ = capsys.readouterr() + assert ( + "Created Customers and ShoppingCarts table with FKShoppingCartsCustomerId" + in out + ) + + +@pytest.mark.dependency( + name="alter_table_with_foreign_key_delete_cascade", + depends=["create_table_with_foreign_key_delete_cascade"], +) +def test_alter_table_with_foreign_key_delete_cascade( + capsys, instance_id, sample_database +): + snippets.alter_table_with_foreign_key_delete_cascade( + instance_id, sample_database.database_id + ) + out, _ = capsys.readouterr() + assert "Altered ShoppingCarts table with FKShoppingCartsCustomerName" in out + + +@pytest.mark.dependency(depends=["alter_table_with_foreign_key_delete_cascade"]) +def test_drop_foreign_key_contraint_delete_cascade( + capsys, instance_id, sample_database +): + snippets.drop_foreign_key_constraint_delete_cascade( + instance_id, sample_database.database_id + ) + out, _ = capsys.readouterr() + assert "Altered ShoppingCarts table to drop FKShoppingCartsCustomerName" in out + + +def test_create_sequence(capsys, instance_id, bit_reverse_sequence_database): + snippets.create_sequence(instance_id, bit_reverse_sequence_database.database_id) + out, _ = capsys.readouterr() + assert ( + "Created Seq sequence and Customers table, where the key column CustomerId uses the sequence as a default value on database" + in out + ) + assert "Number of customer records inserted is 3" in out + assert "Inserted customer record with Customer Id:" in out + + +@pytest.mark.dependency(depends=["create_sequence"]) +def test_alter_sequence(capsys, instance_id, bit_reverse_sequence_database): + snippets.alter_sequence(instance_id, bit_reverse_sequence_database.database_id) + out, _ = capsys.readouterr() + assert ( + "Altered Seq sequence to skip an inclusive range between 1000 and 5000000 on database" + in out + ) + assert "Number of customer records inserted is 3" in out + assert "Inserted customer record with Customer Id:" in out + + +@pytest.mark.dependency(depends=["alter_sequence"]) +def test_drop_sequence(capsys, instance_id, bit_reverse_sequence_database): + snippets.drop_sequence(instance_id, bit_reverse_sequence_database.database_id) + out, _ = capsys.readouterr() + assert ( + "Altered Customers table to drop DEFAULT from CustomerId column and dropped the Seq sequence on database" + in out + ) diff --git a/tests/_fixtures.py b/tests/_fixtures.py index 0bd8fe163a..b6f4108490 100644 --- a/tests/_fixtures.py +++ b/tests/_fixtures.py @@ -68,6 +68,19 @@ email STRING(MAX), deleted BOOL NOT NULL ) PRIMARY KEY(id, commit_ts DESC); + +CREATE TABLE Customers ( + CustomerId INT64 NOT NULL, + CustomerName STRING(62) NOT NULL, + ) PRIMARY KEY (CustomerId); + + CREATE TABLE ShoppingCarts ( + CartId INT64 NOT NULL, + CustomerId INT64 NOT NULL, + CustomerName STRING(62) NOT NULL, + CONSTRAINT FKShoppingCartsCustomerId FOREIGN KEY (CustomerId) + REFERENCES Customers (CustomerId) ON DELETE CASCADE + ) PRIMARY KEY (CartId); """ EMULATOR_DDL = """\ @@ -157,6 +170,19 @@ name VARCHAR(16), PRIMARY KEY (id)); CREATE INDEX name ON contacts(first_name, last_name); +CREATE TABLE Customers ( + CustomerId BIGINT, + CustomerName VARCHAR(62) NOT NULL, + PRIMARY KEY (CustomerId)); + + CREATE TABLE ShoppingCarts ( + CartId BIGINT, + CustomerId BIGINT NOT NULL, + CustomerName VARCHAR(62) NOT NULL, + CONSTRAINT "FKShoppingCartsCustomerId" FOREIGN KEY (CustomerId) + REFERENCES Customers (CustomerId) ON DELETE CASCADE, + PRIMARY KEY (CartId) + ); """ DDL_STATEMENTS = [stmt.strip() for stmt in DDL.split(";") if stmt.strip()] diff --git a/tests/system/test_database_api.py b/tests/system/test_database_api.py index 269fd00684..153567810a 100644 --- a/tests/system/test_database_api.py +++ b/tests/system/test_database_api.py @@ -21,13 +21,16 @@ from google.iam.v1 import policy_pb2 from google.cloud import spanner_v1 from google.cloud.spanner_v1.pool import FixedSizePool, PingingPool +from google.cloud.spanner_admin_database_v1 import DatabaseDialect from google.type import expr_pb2 from . import _helpers from . import _sample_data -from google.cloud.spanner_admin_database_v1 import DatabaseDialect DBAPI_OPERATION_TIMEOUT = 240 # seconds +FKADC_CUSTOMERS_COLUMNS = ("CustomerId", "CustomerName") +FKADC_SHOPPING_CARTS_COLUMNS = ("CartId", "CustomerId", "CustomerName") +ALL_KEYSET = spanner_v1.KeySet(all_=True) @pytest.fixture(scope="module") @@ -572,6 +575,135 @@ def _unit_of_work(transaction, name): assert len(rows) == 2 +def test_insertion_in_referencing_table_fkadc(not_emulator, shared_database): + with shared_database.batch() as batch: + batch.insert( + table="Customers", + columns=FKADC_CUSTOMERS_COLUMNS, + values=[ + (1, "Marc"), + (2, "Catalina"), + ], + ) + + with shared_database.batch() as batch: + batch.insert( + table="ShoppingCarts", + columns=FKADC_SHOPPING_CARTS_COLUMNS, + values=[ + (1, 1, "Marc"), + ], + ) + + with shared_database.snapshot() as snapshot: + rows = list( + snapshot.read( + "ShoppingCarts", ("CartId", "CustomerId", "CustomerName"), ALL_KEYSET + ) + ) + + assert len(rows) == 1 + + +def test_insertion_in_referencing_table_error_fkadc(not_emulator, shared_database): + with pytest.raises(exceptions.FailedPrecondition): + with shared_database.batch() as batch: + batch.insert( + table="ShoppingCarts", + columns=FKADC_SHOPPING_CARTS_COLUMNS, + values=[ + (4, 4, "Naina"), + ], + ) + + +def test_insertion_then_deletion_in_referenced_table_fkadc( + not_emulator, shared_database +): + with shared_database.batch() as batch: + batch.insert( + table="Customers", + columns=FKADC_CUSTOMERS_COLUMNS, + values=[ + (3, "Sara"), + ], + ) + + with shared_database.batch() as batch: + batch.insert( + table="ShoppingCarts", + columns=FKADC_SHOPPING_CARTS_COLUMNS, + values=[ + (3, 3, "Sara"), + ], + ) + + with shared_database.snapshot() as snapshot: + rows = list(snapshot.read("ShoppingCarts", ["CartId"], ALL_KEYSET)) + + assert [3] in rows + + with shared_database.batch() as batch: + batch.delete(table="Customers", keyset=spanner_v1.KeySet(keys=[[3]])) + + with shared_database.snapshot() as snapshot: + rows = list(snapshot.read("ShoppingCarts", ["CartId"], ALL_KEYSET)) + + assert [3] not in rows + + +def test_insert_then_delete_referenced_key_error_fkadc(not_emulator, shared_database): + with pytest.raises(exceptions.FailedPrecondition): + with shared_database.batch() as batch: + batch.insert( + table="Customers", + columns=FKADC_CUSTOMERS_COLUMNS, + values=[ + (3, "Sara"), + ], + ) + batch.delete(table="Customers", keyset=spanner_v1.KeySet(keys=[[3]])) + + +def test_insert_referencing_key_then_delete_referenced_key_error_fkadc( + not_emulator, shared_database +): + with shared_database.batch() as batch: + batch.insert( + table="Customers", + columns=FKADC_CUSTOMERS_COLUMNS, + values=[ + (4, "Huda"), + ], + ) + + with pytest.raises(exceptions.FailedPrecondition): + with shared_database.batch() as batch: + batch.insert( + table="ShoppingCarts", + columns=FKADC_SHOPPING_CARTS_COLUMNS, + values=[ + (4, 4, "Huda"), + ], + ) + batch.delete(table="Customers", keyset=spanner_v1.KeySet(keys=[[4]])) + + +def test_information_schema_referential_constraints_fkadc( + not_emulator, shared_database +): + with shared_database.snapshot() as snapshot: + rows = list( + snapshot.execute_sql( + "SELECT DELETE_RULE " + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS " + "WHERE CONSTRAINT_NAME = 'FKShoppingCartsCustomerId'" + ) + ) + + assert any("CASCADE" in stmt for stmt in rows) + + def test_update_database_success( not_emulator, shared_database, shared_instance, database_operation_timeout ): diff --git a/tests/unit/test_batch.py b/tests/unit/test_batch.py index a7f4451379..0199d44033 100644 --- a/tests/unit/test_batch.py +++ b/tests/unit/test_batch.py @@ -273,7 +273,7 @@ def _test_commit_with_request_options(self, request_options=None): self.assertEqual(committed, now) self.assertEqual(batch.committed, committed) - if type(request_options) == dict: + if type(request_options) is dict: expected_request_options = RequestOptions(request_options) else: expected_request_options = request_options diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index f8bcb709cb..600efd5dc8 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -72,7 +72,7 @@ def _constructor_test_helper( expected_client_info = MUT._CLIENT_INFO kwargs["client_options"] = client_options - if type(client_options) == dict: + if type(client_options) is dict: expected_client_options = google.api_core.client_options.from_dict( client_options ) diff --git a/tests/unit/test_instance.py b/tests/unit/test_instance.py index f9d1fec6b8..0a7dbccb81 100644 --- a/tests/unit/test_instance.py +++ b/tests/unit/test_instance.py @@ -818,7 +818,7 @@ def test_list_backup_operations_defaults(self): retry=mock.ANY, timeout=mock.ANY, ) - self.assertTrue(all([type(op) == Operation for op in ops])) + self.assertTrue(all([type(op) is Operation for op in ops])) def test_list_backup_operations_w_options(self): from google.api_core.operation import Operation @@ -865,7 +865,7 @@ def test_list_backup_operations_w_options(self): retry=mock.ANY, timeout=mock.ANY, ) - self.assertTrue(all([type(op) == Operation for op in ops])) + self.assertTrue(all([type(op) is Operation for op in ops])) def test_list_database_operations_defaults(self): from google.api_core.operation import Operation @@ -923,7 +923,7 @@ def test_list_database_operations_defaults(self): retry=mock.ANY, timeout=mock.ANY, ) - self.assertTrue(all([type(op) == Operation for op in ops])) + self.assertTrue(all([type(op) is Operation for op in ops])) def test_list_database_operations_w_options(self): from google.api_core.operation import Operation @@ -988,7 +988,7 @@ def test_list_database_operations_w_options(self): retry=mock.ANY, timeout=mock.ANY, ) - self.assertTrue(all([type(op) == Operation for op in ops])) + self.assertTrue(all([type(op) is Operation for op in ops])) def test_type_string_to_type_pb_hit(self): from google.cloud.spanner_admin_database_v1 import ( diff --git a/tests/unit/test_snapshot.py b/tests/unit/test_snapshot.py index 285328387c..5d2afb4fe6 100644 --- a/tests/unit/test_snapshot.py +++ b/tests/unit/test_snapshot.py @@ -654,7 +654,7 @@ def _read_helper( if request_options is None: request_options = RequestOptions() - elif type(request_options) == dict: + elif type(request_options) is dict: request_options = RequestOptions(request_options) if partition is not None: # 'limit' and 'partition' incompatible @@ -889,7 +889,7 @@ def _execute_sql_helper( if request_options is None: request_options = RequestOptions() - elif type(request_options) == dict: + elif type(request_options) is dict: request_options = RequestOptions(request_options) result_set = derived.execute_sql( diff --git a/tests/unit/test_transaction.py b/tests/unit/test_transaction.py index 4eb42027f7..85359dac19 100644 --- a/tests/unit/test_transaction.py +++ b/tests/unit/test_transaction.py @@ -383,7 +383,7 @@ def _commit_helper( expected_request_options = RequestOptions( transaction_tag=self.TRANSACTION_TAG ) - elif type(request_options) == dict: + elif type(request_options) is dict: expected_request_options = RequestOptions(request_options) expected_request_options.transaction_tag = self.TRANSACTION_TAG expected_request_options.request_tag = None @@ -534,7 +534,7 @@ def _execute_update_helper( if request_options is None: request_options = RequestOptions() - elif type(request_options) == dict: + elif type(request_options) is dict: request_options = RequestOptions(request_options) row_count = transaction.execute_update( @@ -717,7 +717,7 @@ def _batch_update_helper(self, error_after=None, count=0, request_options=None): if request_options is None: request_options = RequestOptions() - elif type(request_options) == dict: + elif type(request_options) is dict: request_options = RequestOptions(request_options) status, row_counts = transaction.batch_update(