diff --git a/CHANGES.txt b/CHANGES.txt index 81e81543..141b0667 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,14 @@ Unreleased ========== +2023/04/18 0.31.1 +================= + +- SQLAlchemy Core: Re-enable support for ``INSERT/UPDATE...RETURNING`` in + SQLAlchemy 2.0 by adding the new ``insert_returning`` and ``update_returning`` flags + in the CrateDB dialect. + + 2023/03/30 0.31.0 ================= diff --git a/docs/_extra/robots.txt b/docs/_extra/robots.txt index baa43f3c..412dae65 100644 --- a/docs/_extra/robots.txt +++ b/docs/_extra/robots.txt @@ -1,2 +1,4 @@ -Sitemap: https://crate.io/docs/python/en/latest/site.xml User-agent: * +Disallow: / + +Sitemap: https://crate.io/docs/python/en/latest/site.xml diff --git a/docs/by-example/sqlalchemy/advanced-querying.rst b/docs/by-example/sqlalchemy/advanced-querying.rst index 863373e4..9108bb49 100644 --- a/docs/by-example/sqlalchemy/advanced-querying.rst +++ b/docs/by-example/sqlalchemy/advanced-querying.rst @@ -257,6 +257,73 @@ Now, verify that the data is present in the database: ["('Write Tests',)"] +``INSERT...RETURNING`` +====================== + +The ``RETURNING`` clause can be used to retrieve the result rows of an ``INSERT`` +operation. It may be specified using the ``Insert.returning()`` method. + +The first step is to define the table: + + >>> from sqlalchemy import insert + + >>> class User(Base): + ... __tablename__ = 'user' + ... __table_args__ = { + ... 'crate_number_of_replicas': '0' + ... } + ... id = sa.Column(sa.String, primary_key=True, default=gen_key) + ... username = sa.Column(sa.String) + ... email = sa.Column(sa.String) + + >>> Base.metadata.create_all(bind=engine) + +Now, let's use the returning clause on our insert to retrieve the values inserted: + + >>> stmt = insert(User).values(username='Crate', email='crate@crate.io').returning(User.username, User.email) + >>> result = session.execute(stmt) + >>> session.commit() + >>> print([str(r) for r in result]) + ["('Crate', 'crate@crate.io')"] + +The following ``INSERT...RETURNING`` statement was issued to the database:: + + INSERT INTO user (id, username, email) + VALUES (:id, :username, :email) + RETURNING user.id, user.username, user.email + +``UPDATE...RETURNING`` + +The ``RETURNING`` clause can also be used with an ``UPDATE`` operation to return +specified rows to be returned on execution. It can be specified using the +``Update.returning()`` method. + + +We can reuse the user table previously created in the ``INSERT...RETURNING`` section. + +Insert a user and get the user id: + + >>> from sqlalchemy import insert, update + + >>> stmt = insert(User).values(username='Arthur Dent', email='arthur_dent@crate.io').returning(User.id, User.username, User.email) + >>> result = session.execute(stmt) + >>> session.commit() + >>> uid = [r[0] for r in result][0] + +Now let's update the user: + + >>> stmt = update(User).where(User.id == uid).values(username='Tricia McMillan', email='tricia_mcmillan@crate.io').returning(User.username, User.email) + >>> res = session.execute(stmt) + >>> session.commit() + >>> print([str(r) for r in res]) + ["('Tricia McMillan', 'tricia_mcmillan@crate.io')"] + +The following ``UPDATE...RETURNING`` statement was issued to the database:: + + UPDATE user SET username=:username, email=:email + WHERE user.id = :id_1 + RETURNING user.username, user.email + .. hidden: Disconnect from database >>> session.close() diff --git a/setup.py b/setup.py index a11c4e0f..be1b8a5c 100644 --- a/setup.py +++ b/setup.py @@ -65,7 +65,7 @@ def read(path): 'backports.zoneinfo<1; python_version<"3.9"'], test=['tox>=3,<5', 'zope.testing>=4,<6', - 'zope.testrunner>=5,<6', + 'zope.testrunner>=5,<7', 'zc.customdoctests>=1.0.1,<2', 'createcoverage>=1,<2', 'stopit>=1.1.2,<2', diff --git a/src/crate/client/__init__.py b/src/crate/client/__init__.py index e47ffb51..59cbb6e0 100644 --- a/src/crate/client/__init__.py +++ b/src/crate/client/__init__.py @@ -29,7 +29,7 @@ # version string read from setup.py using a regex. Take care not to break the # regex! -__version__ = "0.31.0" +__version__ = "0.31.1" apilevel = "2.0" threadsafety = 2 diff --git a/src/crate/client/sqlalchemy/dialect.py b/src/crate/client/sqlalchemy/dialect.py index f615e5f6..5737f994 100644 --- a/src/crate/client/sqlalchemy/dialect.py +++ b/src/crate/client/sqlalchemy/dialect.py @@ -180,6 +180,8 @@ class CrateDialect(default.DefaultDialect): supports_statement_cache = True colspecs = colspecs implicit_returning = True + insert_returning = True + update_returning = True def __init__(self, *args, **kwargs): super(CrateDialect, self).__init__(*args, **kwargs)