diff --git a/README.md b/README.md index 155d3094..b8c557b6 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,7 @@ This project has **Preview** release status. Known limitations are listed [here] - [Cloud Spanner product documentation](https://cloud.google.com/spanner/docs) - [SQLAlchemy product documentation](https://www.sqlalchemy.org/) -Quick Start ------------ +## Quick Start In order to use this package, you first need to go through the following steps: @@ -19,8 +18,7 @@ In order to use this package, you first need to go through the following steps: 3. [Enable the Google Cloud Spanner API.](https://cloud.google.com/spanner) 4. [Setup Authentication.](https://googleapis.dev/python/google-api-core/latest/auth.html) -Installation ------------ +## Installation To install an in-development version of the package, clone its Git-repository: ``` @@ -32,9 +30,9 @@ python setup.py install ``` During setup the dialect will be registered with entry points. -A Minimal App ------------ -**Create a table** +## A Minimal App + +### Create a table ```python from sqlalchemy import ( Column, @@ -59,7 +57,8 @@ user = Table( metadata.create_all(engine) ``` -**Insert a row** + +### Insert a row ```python from sqlalchemy import ( MetaData, @@ -76,7 +75,7 @@ with engine.begin() as connection: connection.execute(user.insert(), {"user_id": 1, "user_name": "Full Name"}) ``` -**Read** +### Read ```python from sqlalchemy import MetaData, Table, create_engine, select @@ -90,16 +89,16 @@ with engine.begin() as connection: print(row) ``` -Migration ------------ +## Migration + SQLAlchemy uses [Alembic](https://alembic.sqlalchemy.org/en/latest/#) tool to organize database migrations. **Warning!** A migration script can produce a lot of DDL statements. If each of the statements are executed separately, performance issues can occur. To avoid these, it's highly recommended to use the [Alembic batch context](https://alembic.sqlalchemy.org/en/latest/batch.html) feature to pack DDL statements into groups of statements. -Features and limitations ------------ -**Interleaved tables** +## Features and limitations + +### Interleaved tables Cloud Spanner dialect includes two dialect-specific arguments for `Table` constructor, which help to define interleave relations: `spanner_interleave_in` - a parent table name `spanner_inverleave_on_delete_cascade` - a flag specifying if `ON DELETE CASCADE` statement must be used for the interleave relation @@ -126,7 +125,7 @@ client = Table( client.create(engine) ``` -**Unique constraints** +### Unique constraints Cloud Spanner doesn't support direct UNIQUE constraints creation. In order to achieve column values uniqueness UNIQUE indexes should be used. Instead of direct UNIQUE constraint creation: @@ -147,7 +146,7 @@ Table( Index("uix_1", "col1", unique=True), ) ``` -**Autocommit mode** +### Autocommit mode Spanner dialect supports both `SERIALIZABLE` and `AUTOCOMMIT` isolation levels. `SERIALIZABLE` is the default one, where transactions need to be committed manually. `AUTOCOMMIT` mode corresponds to automatically committing of a query right in its execution time. Isolation level change example: @@ -158,7 +157,7 @@ eng = create_engine("spanner:///projects/project-id/instances/instance-id/databa autocommit_engine = eng.execution_options(isolation_level="AUTOCOMMIT") ``` -**ReadOnly transactions** +### ReadOnly transactions By default, transactions produced by a Spanner connection are in ReadWrite mode. However, some applications require an ability to grant ReadOnly access to users/methods; for these cases Spanner dialect supports the `read_only` execution option, which switches a connection into ReadOnly mode: ```python with engine.connect().execution_options(read_only=True) as connection: @@ -168,13 +167,13 @@ Note that execution options are applied lazily - on the `execute()` method call, ReadOnly/ReadWrite mode of a connection can't be changed while a transaction is in progress - first you must commit or rollback it. -**DDL and transactions** +### DDL and transactions DDL statements are executed outside the regular transactions mechanism, which means DDL statements will not be rolled back on normal transaction rollback. -**Dropping a table** +### Dropping a table Cloud Spanner, by default, doesn't drop tables, which have secondary indexes and/or foreign key constraints. In Spanner dialect for SQLAlchemy, however, this restriction is omitted - if a table you are trying to delete has indexes/foreign keys, they will be dropped automatically right before dropping the table. -**Data types** +### Data types Data types table mapping SQLAlchemy types to Cloud Spanner types: | SQLAlchemy | Spanner | @@ -193,14 +192,13 @@ Data types table mapping SQLAlchemy types to Cloud Spanner types: | NUMERIC | NUMERIC | -**Other limitations** +### Other limitations - WITH RECURSIVE statement is not supported. - Named schemas are not supported. - Temporary tables are not supported, real tables are used instead. - Numeric type dimensions (scale and precision) are constant. See the [docs](https://cloud.google.com/spanner/docs/data-types#numeric_types). -Best practices ------------ +## Best practices When a SQLAlchemy function is called, a new connection to a database is established and a Spanner session object is fetched. In case of connectionless execution these fetches are done for every `execute()` call, which can cause a significant latency. To avoid initiating a Spanner session on every `execute()` call it's recommended to write code in connection-bounded fashion. Once a `Connection()` object is explicitly initiated, it fetches a Spanner session object and uses it for all the following calls made on this `Connection()` object. Non-optimal connectionless use: @@ -216,8 +214,8 @@ with engine.begin() as connection: ``` Connectionless way of use is also deprecated since SQLAlchemy 2.0 and soon will be removed (see in [SQLAlchemy docs](https://docs.sqlalchemy.org/en/14/core/connections.html#connectionless-execution-implicit-execution)). -Running tests ------------- +## Running tests + Spanner dialect includes a compliance, migration and unit test suite. To run the tests the `nox` package commands can be used: ``` # Run the whole suite @@ -226,11 +224,10 @@ $ nox # Run a particular test session $ nox -s migration_test ``` -**Running tests on Spanner emulator** +### Running tests on Spanner emulator The dialect test suite can be runned on [Spanner emulator](https://cloud.google.com/spanner/docs/emulator). Several tests, relating to `NULL` values of data types, are skipped when executed on emulator. -Contributing ------------- +## Contributing Contributions to this library are welcome and encouraged. Please report issues, file feature requests, and send pull requests. See [CONTRIBUTING](https://github.com/cloudspannerecosystem/python-spanner-sqlalchemy/blob/main/contributing.md) for more information on how to get started.