Skip to content

Commit 4c62ecf

Browse files
JanLikaramotl
authored andcommitted
Implement server_default for SA columns
CrateDB's SQLAlchemy dialect now handles the `server_default` when generating table DDL. Fix #454.
1 parent 7b86801 commit 4c62ecf

File tree

4 files changed

+60
-2
lines changed

4 files changed

+60
-2
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Unreleased
1616
SQLAlchemy 2.0 by adding the new ``insert_returning`` and ``update_returning`` flags
1717
in the CrateDB dialect.
1818

19+
- SQLAlchemy DDL: Allow setting ``server_default`` on columns to enable server-generated defaults.
1920

2021
2023/03/30 0.31.0
2122
=================

docs/sqlalchemy.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ system <sa:orm_declarative_mapping>`:
206206
... name_ft = sa.Column(sa.String)
207207
... quote_ft = sa.Column(sa.String)
208208
... even_more_details = sa.Column(sa.String, crate_columnstore=False)
209+
... created_at = sa.Column(sa.DateTime, server_default=sa.func.now())
209210
...
210211
... __mapper_args__ = {
211212
... 'exclude_properties': ['name_ft', 'quote_ft']
@@ -221,13 +222,14 @@ In this example, we:
221222
- Use standard SQLAlchemy types for the ``id``, ``name``, and ``quote`` columns
222223
- Use ``nullable=False`` to define a ``NOT NULL`` constraint
223224
- Disable indexing of the ``name`` column using ``crate_index=False``
224-
- Disable the columnstore of the ``even_more_details`` column using ``crate_columnstore=False``
225225
- Define a computed column ``name_normalized`` (based on ``name``) that
226226
translates into a generated column
227227
- Use the `Object`_ extension type for the ``details`` column
228228
- Use the `ObjectArray`_ extension type for the ``more_details`` column
229229
- Set up the ``name_ft`` and ``quote_ft`` fulltext indexes, but exclude them from
230230
the mapping (so SQLAlchemy doesn't try to update them as if they were columns)
231+
- Disable the columnstore of the ``even_more_details`` column using ``crate_columnstore=False``
232+
- Add a ``created_at`` column whose default value is set by CrateDB's ``now()`` function.
231233

232234
.. TIP::
233235

src/crate/client/sqlalchemy/compiler.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,10 @@ class CrateDDLCompiler(compiler.DDLCompiler):
108108
def get_column_specification(self, column, **kwargs):
109109
colspec = self.preparer.format_column(column) + " " + \
110110
self.dialect.type_compiler.process(column.type)
111-
# TODO: once supported add default here
111+
112+
default = self.get_column_default_string(column)
113+
if default is not None:
114+
colspec += " DEFAULT " + default
112115

113116
if column.computed is not None:
114117
colspec += " " + self.process(column.computed)

src/crate/client/sqlalchemy/tests/create_table_test.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,55 @@ class DummyTable(self.Base):
259259

260260
with self.assertRaises(sa.exc.CompileError):
261261
self.Base.metadata.create_all(bind=self.engine)
262+
263+
def test_column_server_default_text_func(self):
264+
class DummyTable(self.Base):
265+
__tablename__ = 't'
266+
pk = sa.Column(sa.String, primary_key=True)
267+
a = sa.Column(sa.DateTime, server_default=sa.text("now()"))
268+
269+
self.Base.metadata.create_all(bind=self.engine)
270+
fake_cursor.execute.assert_called_with(
271+
('\nCREATE TABLE t (\n\t'
272+
'pk STRING NOT NULL, \n\t'
273+
'a TIMESTAMP DEFAULT now(), \n\t'
274+
'PRIMARY KEY (pk)\n)\n\n'), ())
275+
276+
def test_column_server_default_string(self):
277+
class DummyTable(self.Base):
278+
__tablename__ = 't'
279+
pk = sa.Column(sa.String, primary_key=True)
280+
a = sa.Column(sa.String, server_default="Zaphod")
281+
282+
self.Base.metadata.create_all(bind=self.engine)
283+
fake_cursor.execute.assert_called_with(
284+
('\nCREATE TABLE t (\n\t'
285+
'pk STRING NOT NULL, \n\t'
286+
'a STRING DEFAULT \'Zaphod\', \n\t'
287+
'PRIMARY KEY (pk)\n)\n\n'), ())
288+
289+
def test_column_server_default_func(self):
290+
class DummyTable(self.Base):
291+
__tablename__ = 't'
292+
pk = sa.Column(sa.String, primary_key=True)
293+
a = sa.Column(sa.DateTime, server_default=sa.func.now())
294+
295+
self.Base.metadata.create_all(bind=self.engine)
296+
fake_cursor.execute.assert_called_with(
297+
('\nCREATE TABLE t (\n\t'
298+
'pk STRING NOT NULL, \n\t'
299+
'a TIMESTAMP DEFAULT now(), \n\t'
300+
'PRIMARY KEY (pk)\n)\n\n'), ())
301+
302+
def test_column_server_default_text_constant(self):
303+
class DummyTable(self.Base):
304+
__tablename__ = 't'
305+
pk = sa.Column(sa.String, primary_key=True)
306+
answer = sa.Column(sa.Integer, server_default=sa.text("42"))
307+
308+
self.Base.metadata.create_all(bind=self.engine)
309+
fake_cursor.execute.assert_called_with(
310+
('\nCREATE TABLE t (\n\t'
311+
'pk STRING NOT NULL, \n\t'
312+
'answer INT DEFAULT 42, \n\t'
313+
'PRIMARY KEY (pk)\n)\n\n'), ())

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy