Skip to content

Releases: davyddd/ddsql

v0.4.1

Choose a tag to compare

@github-actions github-actions released this 19 Oct 18:09
957417e

🔄 Changed

  • Requires ddutils >= 0.3.1, < 0.5.0: ddutils 0.4.0 removed retry_once_after_exception and changed safe_call, neither of which this library uses.
  • The NotImplementedError of an adapter without a serializer names what is expected, an instance of BaseSerializer; the SQLBase docstring no longer claims a raw SQL string is accepted as query, it takes a Query.

📝 Docs

  • README documents what the code does: the adapter example defines atomic, a ConnectionManagerFactory from ddutils; the serializer sends bytes, mappings, time and timedelta to serialize_other_object, which raises NotImplementedError unless overridden, an empty collection renders as (), which is not valid after IN, and inf / nan render as such; SQL(query).postgres builds a new adapter on every access, so using() has to be chained with execute(); with_params mutates and returns the same SQL object, so an SQL instance is one execution; a concrete adapter without serializer and an SQLBase without adapters fail at class definition; Query takes any callable accepting the columns as keyword arguments as model, runs an async Jinja environment with trim_blocks, lstrip_blocks and keep_trailing_newline, strips every line of the rendered SQL and installs the serializer's functions as globals, so they work inside macros. The layout follows dd-lib-stub, with a Compatibility section.

Full Changelog: v0.4.0...v0.4.1

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 14 Oct 14:58
138fa85

🔄 Changed

  • ddsql.connections moved to ddutils 0.3.1 as ddutils.connections: ConnectionManager and ConnectionManagerFactory were born here for Postgres and ClickHouse but route any client the same way, so Redis, Kafka and Aerospike now share them. Adapters are unchanged and keep running a query inside factory(alias=self.alias).

⚠️ Breaking changes

  • from ddsql.connections import ... → from ddutils.connections import ...; there is no re-export.

Full Changelog: v0.3.1...v0.4.0

v0.3.1

Choose a tag to compare

@github-actions github-actions released this 14 Oct 11:50
32465ec

🔄 Changed

  • serialize_value serializes an Enum member by its value. A class Kind(int, Enum) member rendered as 'Kind.A', because an Enum mixin formats through Enum.__str__ since Python 3.11, and a plain Enum holding a datetime or a UUID raised NotImplementedError; both now render as their value would. A (str, Enum) member already worked and keeps working.
  • bytes and bytearray go to serialize_other_object, like any other type missing from the serialization table. They are collections of ints, so they used to render as (222, 173), a valid-looking literal with the wrong meaning. Override serialize_other_object to render them for your dialect.
  • Requires ddutils >= 0.3.0, < 0.4.0.

Full Changelog: v0.3.0...v0.3.1

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 04 Oct 15:02
8f73bf3

🔄 Changed

  • The connection alias is called alias everywhere: Adapter.alias, ConnectionManagerFactory(alias=...) and registry(alias), matching the ConnectionAliasT type. Adapter.using(alias) stays as the Django-like verb that sets it. One concept had three names in 0.2.0 (using, db, alias), which is what this release fixes.

⚠️ Breaking changes

  • Adapter.db → Adapter.alias.
  • ConnectionManagerFactory.__call__(using=...) and registry(using=...) → alias=....

Full Changelog: v0.2.0...v0.3.0

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 04 Oct 14:27
1a97a15

🚀 Added

  • ddsql.connections: the plumbing between a ScopedRegistry (ddutils) and the code that runs queries. ConnectionManagerFactory(registries, default, connection_manager_class=ConnectionManager) resolves a connection alias to its registry, like Django's using, and opens a connection manager on it; registry(using) returns the registry itself. ConnectionManager is the default manager: it hands out the registry's pooled connection and releases nothing on exit, which is all a ClickHouse client needs. Transactional databases pass their own manager (a unit of work over registry.scope()), because it depends on the driver.
  • Adapter.using(db): the repository picks the database right before executing, SQL(query).postgres.using(PostgresDB.REPLICA).execute(). _execute reads self.db (None = default) and passes it to whatever opens the connection. A fresh adapter is bound on every attribute access, so the choice never leaks between calls.

🐛 Fixed

  • Generic subclasses of Adapter and SQLBase failed at class creation with AttributeError: __parameters__: their __init_subclass__ did not call super().__init_subclass__(), so Generic never ran. Both now chain to super().
  • The serializer check runs for concrete adapters only; abstract intermediate adapter classes without _execute no longer need a placeholder serializer.

Full Changelog: v0.1.0...v0.2.0

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 02 Oct 18:24
08452ae

🔄 Changed

  • Query(path=...) takes a Path to the SQL file and checks that the file exists when the query is constructed, so a wrong path fails on import rather than on the first request. {% include %} inside a template resolves relative to the template's directory.
  • SQLBase recognises adapters declared on an intermediate base class: a subclass of your SQL class no longer fails with "Subclasses must define at least one adapter". The check no longer instantiates adapters with an empty sql.
  • SQL.postgres accessed on the class returns the AdapterDescriptor itself, like property; on an instance it still returns the adapter bound to that query.
  • Minimum Python is 3.11 (was 3.9); requires ddutils >= 0.2.0.

⚠️ Breaking changes

  • The SQL_TEMPLATES_DIR environment variable is gone. Build the full path in the application and pass it to Query:

    SQL_TEMPLATES_DIR = Path(__file__).parent / 'templates' / 'sql'
    query = Query(model=User, path=SQL_TEMPLATES_DIR / 'users' / 'get_by_id.sql')
  • Query(path=...) accepts Path only, not str.

Full Changelog: v0.0.4...v0.1.0

v0.0.4

Choose a tag to compare

@github-actions github-actions released this 01 Oct 15:55

🐛 Fixed

  • ClickhouseSerializer.serialize_datetime silently dropped microseconds: parseDateTimeBestEffort returns a second-precision DateTime. It now renders parseDateTime64BestEffort(..., 6), which keeps microseconds and honours the UTC offset.
  • PostgresSerializer.serialize_datetime renders timezone-aware values as ::timestamptz instead of ::timestamp, which ignored the offset of the literal. Naive values are still ::timestamp.

Full Changelog: v0.0.3...v0.0.4

v0.0.3

Choose a tag to compare

@github-actions github-actions released this 23 Jul 11:14

🐛 Fixed

  • String values are escaped before being placed into SQL. BaseSerializer and PostgresSerializer double single quotes (SQL standard); PostgresSerializer switches to the E'...' escape syntax for backslashes and control characters and rejects NUL; ClickhouseSerializer uses backslash escapes for quotes, backslashes and control characters. Previously a quote inside a value broke the query and opened the door to injection.

Full Changelog: v0.0.2...v0.0.3

v0.0.2

Choose a tag to compare

@github-actions github-actions released this 14 Feb 21:23

🐛 Fixed

  • SQLBase.with_params() is typed to return the subclass it was called on, so SQL(query).with_params(...).postgres resolves to the project's adapters in type checkers instead of a bare SQLBase.

Full Changelog: v0.0.1...v0.0.2

v0.0.1

Choose a tag to compare

@github-actions github-actions released this 14 Dec 18:39

🚀 Added

First public release: SQL as Jinja templates with safe value serialization, executed through adapters you plug in.

  • Query: a SQL template (inline text or a file) bound to a result model; renders with parameters and wraps rows into Result (get(), get_list(), raw rows).
  • SQLBase: declares the adapters of a project; with_params() collects template parameters, SQL(query).postgres.execute() runs the query through the chosen adapter.
  • Adapter / AdapterDescriptor: the glue to a real connection; a subclass provides _execute() and a serializer.
  • Serializers: BaseSerializer with serialize_value for None, bool, numbers, Decimal, str, UUID, date, datetime and collections, plus PostgresSerializer (::uuid, ::timestamp, ::date casts) and ClickhouseSerializer (toUUID, parseDateTimeBestEffort, toDate). serialize_value is available inside templates.

Supports Python 3.9–3.14, ships py.typed.