Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 25 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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:
```
Expand All @@ -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,
Expand All @@ -59,7 +57,8 @@ user = Table(

metadata.create_all(engine)
```
**Insert a row**

### Insert a row
```python
from sqlalchemy import (
MetaData,
Expand All @@ -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

Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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 |
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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.
Expand Down