Repository navigation
Releases: davyddd/ddsql
Releases · davyddd/ddsql
Release list
v0.4.1
🔄 Changed
- Requires
ddutils >= 0.3.1, < 0.5.0: ddutils 0.4.0 removedretry_once_after_exceptionand changedsafe_call, neither of which this library uses. - The
NotImplementedErrorof an adapter without a serializer names what is expected, an instance ofBaseSerializer; theSQLBasedocstring no longer claims a raw SQL string is accepted asquery, it takes aQuery.
📝 Docs
- README documents what the code does: the adapter example defines
atomic, aConnectionManagerFactoryfrom ddutils; the serializer sendsbytes, mappings,timeandtimedeltatoserialize_other_object, which raisesNotImplementedErrorunless overridden, an empty collection renders as(), which is not valid afterIN, andinf/nanrender as such;SQL(query).postgresbuilds a new adapter on every access, sousing()has to be chained withexecute();with_paramsmutates and returns the sameSQLobject, so anSQLinstance is one execution; a concrete adapter withoutserializerand anSQLBasewithout adapters fail at class definition;Querytakes any callable accepting the columns as keyword arguments asmodel, runs an async Jinja environment withtrim_blocks,lstrip_blocksandkeep_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
🔄 Changed
ddsql.connectionsmoved to ddutils 0.3.1 asddutils.connections:ConnectionManagerandConnectionManagerFactorywere 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 insidefactory(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
🔄 Changed
serialize_valueserializes anEnummember by its value. Aclass Kind(int, Enum)member rendered as'Kind.A', because anEnummixin formats throughEnum.__str__since Python 3.11, and a plainEnumholding adatetimeor aUUIDraisedNotImplementedError; both now render as their value would. A(str, Enum)member already worked and keeps working.bytesandbytearraygo toserialize_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. Overrideserialize_other_objectto render them for your dialect.- Requires
ddutils >= 0.3.0, < 0.4.0.
Full Changelog: v0.3.0...v0.3.1
v0.3.0
🔄 Changed
- The connection alias is called
aliaseverywhere:Adapter.alias,ConnectionManagerFactory(alias=...)andregistry(alias), matching theConnectionAliasTtype.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=...)andregistry(using=...)→alias=....
Full Changelog: v0.2.0...v0.3.0
v0.2.0
🚀 Added
ddsql.connections: the plumbing between aScopedRegistry(ddutils) and the code that runs queries.ConnectionManagerFactory(registries, default, connection_manager_class=ConnectionManager)resolves a connection alias to its registry, like Django'susing, and opens a connection manager on it;registry(using)returns the registry itself.ConnectionManageris 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 overregistry.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()._executereadsself.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
AdapterandSQLBasefailed at class creation withAttributeError: __parameters__: their__init_subclass__did not callsuper().__init_subclass__(), soGenericnever ran. Both now chain tosuper(). - The serializer check runs for concrete adapters only; abstract intermediate adapter classes without
_executeno longer need a placeholderserializer.
Full Changelog: v0.1.0...v0.2.0
v0.1.0
🔄 Changed
Query(path=...)takes aPathto 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.SQLBaserecognises adapters declared on an intermediate base class: a subclass of yourSQLclass no longer fails with "Subclasses must define at least one adapter". The check no longer instantiates adapters with an emptysql.SQL.postgresaccessed on the class returns theAdapterDescriptoritself, likeproperty; 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_DIRenvironment variable is gone. Build the full path in the application and pass it toQuery:SQL_TEMPLATES_DIR = Path(__file__).parent / 'templates' / 'sql' query = Query(model=User, path=SQL_TEMPLATES_DIR / 'users' / 'get_by_id.sql')
-
Query(path=...)acceptsPathonly, notstr.
Full Changelog: v0.0.4...v0.1.0
v0.0.4
🐛 Fixed
ClickhouseSerializer.serialize_datetimesilently dropped microseconds:parseDateTimeBestEffortreturns a second-precisionDateTime. It now rendersparseDateTime64BestEffort(..., 6), which keeps microseconds and honours the UTC offset.PostgresSerializer.serialize_datetimerenders timezone-aware values as::timestamptzinstead 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
🐛 Fixed
- String values are escaped before being placed into SQL.
BaseSerializerandPostgresSerializerdouble single quotes (SQL standard);PostgresSerializerswitches to theE'...'escape syntax for backslashes and control characters and rejectsNUL;ClickhouseSerializeruses 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
🐛 Fixed
SQLBase.with_params()is typed to return the subclass it was called on, soSQL(query).with_params(...).postgresresolves to the project's adapters in type checkers instead of a bareSQLBase.
Full Changelog: v0.0.1...v0.0.2
v0.0.1
🚀 Added
First public release: SQL as Jinja templates with safe value serialization, executed through adapters you plug in.
Query: a SQL template (inlinetextor a file) bound to a result model; renders with parameters and wraps rows intoResult(get(),get_list(), rawrows).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:
BaseSerializerwithserialize_valueforNone, bool, numbers,Decimal, str,UUID,date,datetimeand collections, plusPostgresSerializer(::uuid,::timestamp,::datecasts) andClickhouseSerializer(toUUID,parseDateTimeBestEffort,toDate).serialize_valueis available inside templates.
Supports Python 3.9–3.14, ships py.typed.