4.0rc3
Pre-release
Pre-release
Breaking changes:
table.foreign_keysnow returnsForeignKeyobjects that are dataclasses rather thannamedtupleinstances, so they can no longer be unpacked or indexed as(table, column, other_table,other_column)tuples - access their fields by name instead. Compound (multi-column) foreign keys are now represented as a singleForeignKeywithis_compound=Trueand populatedcolumns/other_columnstuples, wherecolumnandother_columnareNone. Previously they were returned as oneForeignKeyper column, misleadingly suggesting several independent foreign keys. See Upgrading from 3.x to 4.0 for details. (#594)- Removed support for using
sqlean.pyas a drop-in replacement for the Python standard librarysqlite3module.sqlite-utilswill now usepysqlite3if it is installed, otherwise it will usesqlite3from the standard library. - The
db.ensure_autocommit_off()context manager has been renamed todb.ensure_autocommit_on(), because the old name described the opposite of what it did. The method temporarily puts the connection into driver-level autocommit mode - by settingisolation_level = None- so that statements such asPRAGMA journal_mode=walcan run outside of an implicit transaction. (#705)
Compound foreign key support:
- Tables can now be created with compound foreign keys, by passing tuples of column names in
foreign_keys=:foreign_keys=[(("campus_name", "dept_code"), "departments")]. The referenced columns default to the compound primary key of the other table. Compound keys are rendered as table-levelFOREIGN KEYconstraints in the generated schema. See Compound foreign keys. table.transform()now preserves compound foreign keys, applying any column renames to them. Dropping a column that is part of a compound foreign key drops the whole constraint, matching the existing single-column behavior.drop_foreign_keys=accepts a bare column name - dropping any foreign key that column participates in - or a tuple of columns to target a compound key precisely.table.add_foreign_key()anddb.add_foreign_keys()accept tuples of column names to add a compound foreign key to an existing table.db.index_foreign_keys()creates a single composite index for a compound foreign key.
Other foreign key improvements:
ForeignKeynow exposeson_deleteandon_updatefields reflecting the foreign key’sONDELETE/ON UPDATEactions, andtable.transform()preserves those actions. Previously a transform silently stripped clauses such asON DELETE CASCADEfrom the table schema.table.add_foreign_key()accepts newon_delete=andon_update=parameters for creating foreign keys with actions, e.g.table.add_foreign_key("author_id", "authors", "id",on_delete="CASCADE"). (#530)- Foreign keys declared as
REFERENCES other_tablewith no explicit column are now resolved to the other table’s primary key bytable.foreign_keys, instead of reportingother_column=None. - Fixed a
TypeErrorwhen sortingForeignKeyobjects where some were compound.
Case-insensitive column matching:
Column names passed to Python API methods are now matched against the table schema case-insensitively, mirroring how SQLite itself treats identifiers. Previously many methods accepted mixed-case identifiers in the SQL they generated but then failed - or silently did nothing - when performing Python-side comparisons against the schema. (#760) Fixes include:
table.insert()andtable.upsert()now populatetable.last_pkcorrectly when thepk=argument uses different casing to the table schema or the record keys - previously this raised aKeyErrorafter the row had already been written.- Upserts no longer raise or misbehave when the casing of
pk=differs from the casing of the record keys. The primary key columns are correctly excluded from the generatedDO UPDATE SETclause. table.transform()argumentstypes=,rename=,drop=,pk=,not_null=,defaults=,column_order=anddrop_foreign_keys=all resolve column names case-insensitively. Previously options likerename={"name": "title"}against a column calledNamewere silently ignored.db.create_table(..., transform=True)now recognizes existing columns that differ only by case, instead of attempting to add them again and failing withduplicate column name. The casing used in the existing schema is preserved.table.lookup()returns the primary key value even ifpk=casing differs from the schema, and recognizes existing unique indexes case-insensitively instead of creating redundant ones.table.extract()andtable.convert()- includingmulti=Trueandoutput=- accept column names in any casing.- Foreign key columns are validated and recorded using the casing of the actual schema columns, in
foreign_keys=when creating tables,db.add_foreign_keys(),table.add_foreign_key()andtable.add_column(fk_col=...). Duplicate foreign key detection is also case-insensitive. table.create()withpk=,not_null=,defaults=orcolumn_order=referencing columns using different casing no longer creates an unwanted extra primary key column or raises aValueError.
Everything else:
- Fixed a bug where
table.transform()could convertDEFAULT TRUE,DEFAULT FALSEandDEFAULTNULLcolumn defaults into quoted string defaults when rebuilding a table. Thanks, Vincent Gao. (#764)