Use db.table() and db.view() in tests, closes #838

This commit is contained in:
Simon Willison 2026-08-12 13:40:55 -07:00
commit 38fe466700
43 changed files with 1321 additions and 1254 deletions

View file

@ -3,11 +3,13 @@ import pytest
@pytest.fixture
def db(fresh_db):
fresh_db["one_index"].insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db["one_index"].create_index(["name"])
fresh_db["two_indexes"].insert({"id": 1, "name": "Cleo", "species": "dog"}, pk="id")
fresh_db["two_indexes"].create_index(["name"])
fresh_db["two_indexes"].create_index(["species"])
fresh_db.table("one_index").insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db.table("one_index").create_index(["name"])
fresh_db.table("two_indexes").insert(
{"id": 1, "name": "Cleo", "species": "dog"}, pk="id"
)
fresh_db.table("two_indexes").create_index(["name"])
fresh_db.table("two_indexes").create_index(["species"])
return fresh_db
@ -17,7 +19,7 @@ def test_analyze_whole_database(db):
assert set(db.table_names()).issuperset(
{"one_index", "two_indexes", "sqlite_stat1"}
)
assert list(db["sqlite_stat1"].rows) == [
assert list(db.table("sqlite_stat1").rows) == [
{"tbl": "two_indexes", "idx": "idx_two_indexes_species", "stat": "1 1"},
{"tbl": "two_indexes", "idx": "idx_two_indexes_name", "stat": "1 1"},
{"tbl": "one_index", "idx": "idx_one_index_name", "stat": "1 1"},
@ -30,12 +32,12 @@ def test_analyze_one_table(db, method):
if method == "db_method_with_name":
db.analyze("one_index")
elif method == "table_method":
db["one_index"].analyze()
db.table("one_index").analyze()
assert set(db.table_names()).issuperset(
{"one_index", "two_indexes", "sqlite_stat1"}
)
assert list(db["sqlite_stat1"].rows) == [
assert list(db.table("sqlite_stat1").rows) == [
{"tbl": "one_index", "idx": "idx_one_index_name", "stat": "1 1"}
]
@ -46,6 +48,6 @@ def test_analyze_index_by_name(db):
assert set(db.table_names()).issuperset(
{"one_index", "two_indexes", "sqlite_stat1"}
)
assert list(db["sqlite_stat1"].rows) == [
assert list(db.table("sqlite_stat1").rows) == [
{"tbl": "two_indexes", "idx": "idx_two_indexes_species", "stat": "1 1"},
]

View file

@ -9,7 +9,7 @@ from sqlite_utils.db import ColumnDetails, Database
@pytest.fixture
def db_to_analyze(fresh_db):
stuff = fresh_db["stuff"]
stuff = fresh_db.table("stuff")
stuff.insert_all(
[
{"id": 1, "owner": "Terryterryterry", "size": 5},
@ -45,7 +45,7 @@ def big_db_to_analyze_path(tmpdir):
"all_null": None,
}
)
db["stuff"].insert_all(to_insert)
db.table("stuff").insert_all(to_insert)
return path
@ -126,7 +126,7 @@ def big_db_to_analyze_path(tmpdir):
)
def test_analyze_column(db_to_analyze, column, extra_kwargs, expected):
assert (
db_to_analyze["stuff"].analyze_column(
db_to_analyze.table("stuff").analyze_column(
column, common_limit=2, value_truncate=5, **extra_kwargs
)
== expected
@ -186,7 +186,7 @@ def test_analyze_table_save(db_to_analyze_path):
cli.cli, ["analyze-tables", db_to_analyze_path, "--save"]
)
assert result.exit_code == 0
rows = list(Database(db_to_analyze_path)["_analyze_tables_"].rows)
rows = list(Database(db_to_analyze_path).table("_analyze_tables_").rows)
assert rows == [
{
"table": "stuff",
@ -248,7 +248,7 @@ def test_analyze_table_save_no_most_no_least_options(
args.append("--no-least")
result = CliRunner().invoke(cli.cli, args)
assert result.exit_code == 0
rows = list(Database(big_db_to_analyze_path)["_analyze_tables_"].rows)
rows = list(Database(big_db_to_analyze_path).table("_analyze_tables_").rows)
expected = {
"table": "stuff",
"column": "category",
@ -297,13 +297,13 @@ def test_analyze_table_column_all_nulls(big_db_to_analyze_path):
def test_analyze_table_validate_columns(tmpdir, args, expected_error):
path = str(tmpdir / "test_validate_columns.db")
db = Database(path)
db["one"].insert(
db.table("one").insert(
{
"id": 1,
"name": "one",
}
)
db["two"].insert(
db.table("two").insert(
{
"id": 1,
"age": 5,

View file

@ -45,30 +45,30 @@ def test_iter_complete_sql_statements(sql, expected):
def test_atomic_commits(fresh_db):
with fresh_db.atomic():
fresh_db["dogs"].insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db.table("dogs").insert({"id": 1, "name": "Cleo"}, pk="id")
assert list(fresh_db["dogs"].rows) == [{"id": 1, "name": "Cleo"}]
assert list(fresh_db.table("dogs").rows) == [{"id": 1, "name": "Cleo"}]
def test_atomic_rolls_back(fresh_db):
with pytest.raises(RuntimeError), fresh_db.atomic():
fresh_db["dogs"].insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db.table("dogs").insert({"id": 1, "name": "Cleo"}, pk="id")
raise RuntimeError("boom")
assert not fresh_db["dogs"].exists()
assert not fresh_db.table("dogs").exists()
def test_nested_atomic_rolls_back_to_savepoint(fresh_db):
fresh_db["dogs"].create({"id": int, "name": str}, pk="id")
fresh_db.table("dogs").create({"id": int, "name": str}, pk="id")
with fresh_db.atomic():
fresh_db["dogs"].insert({"id": 1, "name": "Cleo"})
fresh_db.table("dogs").insert({"id": 1, "name": "Cleo"})
with pytest.raises(RuntimeError), fresh_db.atomic():
fresh_db["dogs"].insert({"id": 2, "name": "Pancakes"})
fresh_db.table("dogs").insert({"id": 2, "name": "Pancakes"})
raise RuntimeError("boom")
fresh_db["dogs"].insert({"id": 3, "name": "Marnie"})
fresh_db.table("dogs").insert({"id": 3, "name": "Marnie"})
assert list(fresh_db["dogs"].rows) == [
assert list(fresh_db.table("dogs").rows) == [
{"id": 1, "name": "Cleo"},
{"id": 3, "name": "Marnie"},
]
@ -76,12 +76,12 @@ def test_nested_atomic_rolls_back_to_savepoint(fresh_db):
def test_outer_atomic_rolls_back_released_savepoint(fresh_db):
with pytest.raises(RuntimeError), fresh_db.atomic():
fresh_db["dogs"].insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db.table("dogs").insert({"id": 1, "name": "Cleo"}, pk="id")
with fresh_db.atomic():
fresh_db["dogs"].insert({"id": 2, "name": "Pancakes"})
fresh_db.table("dogs").insert({"id": 2, "name": "Pancakes"})
raise RuntimeError("boom")
assert not fresh_db["dogs"].exists()
assert not fresh_db.table("dogs").exists()
def test_executescript_does_not_commit_open_atomic_block(fresh_db):
@ -97,41 +97,41 @@ def test_executescript_does_not_commit_open_atomic_block(fresh_db):
""")
raise RuntimeError("boom")
assert not fresh_db["dogs"].exists()
assert not fresh_db.table("dogs").exists()
def test_transform_does_not_commit_open_atomic_block(fresh_db):
fresh_db["dogs"].insert({"id": 1, "name": "Cleo", "age": "5"}, pk="id")
fresh_db.table("dogs").insert({"id": 1, "name": "Cleo", "age": "5"}, pk="id")
with pytest.raises(RuntimeError), fresh_db.atomic():
fresh_db["dogs"].insert({"id": 2, "name": "Pancakes", "age": "6"})
fresh_db["dogs"].transform(rename={"age": "dog_age"})
fresh_db.table("dogs").insert({"id": 2, "name": "Pancakes", "age": "6"})
fresh_db.table("dogs").transform(rename={"age": "dog_age"})
raise RuntimeError("boom")
assert (
fresh_db["dogs"].schema
fresh_db.table("dogs").schema
== 'CREATE TABLE "dogs" (\n "id" INTEGER PRIMARY KEY,\n "name" TEXT,\n "age" TEXT\n)'
)
assert list(fresh_db["dogs"].rows) == [
assert list(fresh_db.table("dogs").rows) == [
{"id": 1, "name": "Cleo", "age": "5"},
]
def test_transform_parent_table_with_foreign_keys_in_atomic(fresh_db):
fresh_db.conn.execute("PRAGMA foreign_keys=ON")
fresh_db["authors"].insert({"id": 1, "name": "Tina"}, pk="id")
fresh_db["books"].insert(
fresh_db.table("authors").insert({"id": 1, "name": "Tina"}, pk="id")
fresh_db.table("books").insert(
{"id": 1, "title": "Book", "author_id": 1},
pk="id",
foreign_keys={"author_id"},
)
with fresh_db.atomic():
fresh_db["authors"].transform(rename={"name": "full_name"})
fresh_db.table("authors").transform(rename={"name": "full_name"})
assert fresh_db.conn.execute("PRAGMA foreign_keys").fetchone()[0]
assert (
fresh_db["authors"].schema
fresh_db.table("authors").schema
== 'CREATE TABLE "authors" (\n "id" INTEGER PRIMARY KEY,\n "full_name" TEXT\n)'
)
assert fresh_db.execute("PRAGMA foreign_key_check").fetchall() == []
@ -139,19 +139,19 @@ def test_transform_parent_table_with_foreign_keys_in_atomic(fresh_db):
def test_transform_parent_table_with_foreign_keys_rolls_back(fresh_db):
fresh_db.conn.execute("PRAGMA foreign_keys=ON")
fresh_db["authors"].insert({"id": 1, "name": "Tina"}, pk="id")
fresh_db["books"].insert(
fresh_db.table("authors").insert({"id": 1, "name": "Tina"}, pk="id")
fresh_db.table("books").insert(
{"id": 1, "title": "Book", "author_id": 1},
pk="id",
foreign_keys={"author_id"},
)
with pytest.raises(RuntimeError), fresh_db.atomic():
fresh_db["authors"].transform(rename={"name": "full_name"})
fresh_db.table("authors").transform(rename={"name": "full_name"})
raise RuntimeError("boom")
assert (
fresh_db["authors"].schema
fresh_db.table("authors").schema
== 'CREATE TABLE "authors" (\n "id" INTEGER PRIMARY KEY,\n "name" TEXT\n)'
)
assert fresh_db.conn.execute("PRAGMA foreign_keys").fetchone()[0]
@ -160,49 +160,51 @@ def test_transform_parent_table_with_foreign_keys_rolls_back(fresh_db):
def test_transform_detects_foreign_key_check_violations(fresh_db):
fresh_db.conn.execute("PRAGMA foreign_keys=ON")
fresh_db["authors"].insert({"id": 1, "name": "Tina"}, pk="id")
fresh_db["books"].insert({"id": 1, "author_id": 2}, pk="id")
fresh_db.table("authors").insert({"id": 1, "name": "Tina"}, pk="id")
fresh_db.table("books").insert({"id": 1, "author_id": 2}, pk="id")
with pytest.raises(sqlite3.IntegrityError):
fresh_db["books"].transform(add_foreign_keys=(("author_id", "authors", "id"),))
fresh_db.table("books").transform(
add_foreign_keys=(("author_id", "authors", "id"),)
)
assert fresh_db["books"].foreign_keys == []
assert fresh_db.table("books").foreign_keys == []
assert fresh_db.conn.execute("PRAGMA foreign_keys").fetchone()[0]
def test_atomic_inside_manual_transaction_uses_savepoint(fresh_db):
fresh_db["t"].insert({"id": 1}, pk="id")
fresh_db.table("t").insert({"id": 1}, pk="id")
fresh_db.execute("begin")
with fresh_db.atomic():
fresh_db["t"].insert({"id": 2}, pk="id")
fresh_db.table("t").insert({"id": 2}, pk="id")
# Nothing is committed until the user's own transaction commits
assert fresh_db.conn.in_transaction
fresh_db.rollback()
assert [r["id"] for r in fresh_db["t"].rows] == [1]
assert [r["id"] for r in fresh_db.table("t").rows] == [1]
# And with a commit instead, the atomic block's writes persist
fresh_db.execute("begin")
with fresh_db.atomic():
fresh_db["t"].insert({"id": 3}, pk="id")
fresh_db.table("t").insert({"id": 3}, pk="id")
fresh_db.commit()
assert [r["id"] for r in fresh_db["t"].rows] == [1, 3]
assert [r["id"] for r in fresh_db.table("t").rows] == [1, 3]
def test_begin_commit_rollback(tmpdir):
path = str(tmpdir / "test.db")
db = Database(path)
db["t"].insert({"id": 1}, pk="id")
db.table("t").insert({"id": 1}, pk="id")
db.begin()
db["t"].insert({"id": 2}, pk="id")
db.table("t").insert({"id": 2}, pk="id")
assert db.conn.in_transaction
db.rollback()
assert not db.conn.in_transaction
assert [r["id"] for r in db["t"].rows] == [1]
assert [r["id"] for r in db.table("t").rows] == [1]
db.begin()
db["t"].insert({"id": 3}, pk="id")
db.table("t").insert({"id": 3}, pk="id")
db.commit()
db.close()
db2 = Database(path)
assert [r["id"] for r in db2["t"].rows] == [1, 3]
assert [r["id"] for r in db2.table("t").rows] == [1, 3]
db2.close()
@ -222,7 +224,7 @@ def test_commit_and_rollback_without_transaction_are_noops(fresh_db):
def test_execute_write_commits_immediately(tmpdir):
path = str(tmpdir / "test.db")
db = Database(path)
db["t"].insert({"id": 1}, pk="id")
db.table("t").insert({"id": 1}, pk="id")
db.execute("insert into t (id) values (2)")
# No implicit transaction is left open
assert not db.conn.in_transaction
@ -234,24 +236,24 @@ def test_execute_write_commits_immediately(tmpdir):
def test_execute_write_respects_explicit_transaction(fresh_db):
fresh_db["t"].insert({"id": 1}, pk="id")
fresh_db.table("t").insert({"id": 1}, pk="id")
fresh_db.begin()
fresh_db.execute("insert into t (id) values (2)")
# Still inside the explicit transaction - not committed
assert fresh_db.conn.in_transaction
fresh_db.rollback()
assert [r["id"] for r in fresh_db["t"].rows] == [1]
assert [r["id"] for r in fresh_db.table("t").rows] == [1]
def test_execute_comment_prefixed_begin_leaves_transaction_open(fresh_db):
# A BEGIN hidden behind a leading comment must not be auto-committed
# out from under the caller
fresh_db["t"].insert({"id": 1}, pk="id")
fresh_db.table("t").insert({"id": 1}, pk="id")
fresh_db.execute("-- start a transaction\nbegin")
assert fresh_db.conn.in_transaction
fresh_db.execute("insert into t (id) values (2)")
fresh_db.rollback()
assert [r["id"] for r in fresh_db["t"].rows] == [1]
assert [r["id"] for r in fresh_db.table("t").rows] == [1]
def _sqlite_accepts_bom():
@ -269,12 +271,12 @@ def test_execute_prefixed_begin_leaves_transaction_open(fresh_db, begin_sql):
# out from under the caller
if begin_sql.startswith("\ufeff") and not _sqlite_accepts_bom():
pytest.skip("This SQLite version rejects a leading byte order mark")
fresh_db["t"].insert({"id": 1}, pk="id")
fresh_db.table("t").insert({"id": 1}, pk="id")
fresh_db.execute(begin_sql)
assert fresh_db.conn.in_transaction
fresh_db.execute("insert into t (id) values (2)")
fresh_db.rollback()
assert [r["id"] for r in fresh_db["t"].rows] == [1]
assert [r["id"] for r in fresh_db.table("t").rows] == [1]
def test_execute_failed_write_rolls_back_implicit_transaction(tmpdir):
@ -282,40 +284,40 @@ def test_execute_failed_write_rolls_back_implicit_transaction(tmpdir):
# that would silently disable auto-commit for every subsequent write
path = str(tmpdir / "test.db")
db = Database(path)
db["t"].insert({"id": 1}, pk="id")
db.table("t").insert({"id": 1}, pk="id")
with pytest.raises(sqlite3.IntegrityError):
db.execute("insert into t (id) values (1)")
assert not db.conn.in_transaction
# Subsequent writes commit as normal and survive closing the connection
db["other"].insert({"id": 2})
db.table("other").insert({"id": 2})
db.close()
db2 = Database(path)
assert db2["other"].exists()
assert db2.table("other").exists()
db2.close()
def test_execute_failed_write_preserves_explicit_transaction(fresh_db):
# A failed write inside an explicit transaction must not roll back
# the caller's earlier work - only the caller decides that
fresh_db["t"].insert({"id": 1}, pk="id")
fresh_db.table("t").insert({"id": 1}, pk="id")
fresh_db.begin()
fresh_db.execute("insert into t (id) values (2)")
with pytest.raises(sqlite3.IntegrityError):
fresh_db.execute("insert into t (id) values (1)")
assert fresh_db.conn.in_transaction
fresh_db.commit()
assert [r["id"] for r in fresh_db["t"].rows] == [1, 2]
assert [r["id"] for r in fresh_db.table("t").rows] == [1, 2]
def test_execute_failed_write_inside_atomic_preserves_block(fresh_db):
# A caught failure inside an atomic() block must leave the block's
# transaction open so its other work still commits
fresh_db["t"].insert({"id": 1}, pk="id")
fresh_db.table("t").insert({"id": 1}, pk="id")
with fresh_db.atomic():
fresh_db.execute("insert into t (id) values (2)")
with pytest.raises(sqlite3.IntegrityError):
fresh_db.execute("insert into t (id) values (1)")
assert [r["id"] for r in fresh_db["t"].rows] == [1, 2]
assert [r["id"] for r in fresh_db.table("t").rows] == [1, 2]
def test_query_returning_commits_after_iteration(tmpdir):
@ -325,7 +327,7 @@ def test_query_returning_commits_after_iteration(tmpdir):
_pytest.skip("RETURNING requires SQLite 3.35.0 or higher")
path = str(tmpdir / "test.db")
db = Database(path)
db["t"].insert({"id": 1}, pk="id")
db.table("t").insert({"id": 1}, pk="id")
rows = list(db.query("insert into t (id) values (2) returning id"))
assert rows == [{"id": 2}]
assert not db.conn.in_transaction
@ -375,7 +377,7 @@ def test_nested_atomic_preserves_error_from_transaction_destroying_trigger(
def test_atomic_preserves_error_from_insert_or_rollback(fresh_db):
fresh_db["t"].insert({"id": 1}, pk="id")
fresh_db.table("t").insert({"id": 1}, pk="id")
with pytest.raises(sqlite3.IntegrityError), fresh_db.atomic():
fresh_db.execute("insert or rollback into t (id) values (1)")
assert not fresh_db.conn.in_transaction

View file

@ -6,10 +6,10 @@ def test_attach(tmpdir):
bar_path = str(tmpdir / "bar.db")
db = Database(foo_path)
with db.conn:
db["foo"].insert({"id": 1, "text": "foo"})
db.table("foo").insert({"id": 1, "text": "foo"})
db2 = Database(bar_path)
with db2.conn:
db2["bar"].insert({"id": 1, "text": "bar"})
db2.table("bar").insert({"id": 1, "text": "bar"})
db.attach("bar", bar_path)
assert db.execute(
"select * from foo union all select * from bar.bar"

View file

@ -72,13 +72,13 @@ def test_views(db_path):
def test_tables_fts4(db_path):
Database(db_path)["Gosh"].enable_fts(["c2"], fts_version="FTS4")
Database(db_path).table("Gosh").enable_fts(["c2"], fts_version="FTS4")
result = CliRunner().invoke(cli.cli, ["tables", "--fts4", db_path])
assert '[{"table": "Gosh_fts"}]' == result.output.strip()
def test_tables_fts5(db_path):
Database(db_path)["Gosh"].enable_fts(["c2"], fts_version="FTS5")
Database(db_path).table("Gosh").enable_fts(["c2"], fts_version="FTS5")
result = CliRunner().invoke(cli.cli, ["tables", "--fts5", db_path])
assert '[{"table": "Gosh_fts"}]' == result.output.strip()
@ -86,7 +86,7 @@ def test_tables_fts5(db_path):
def test_tables_counts_and_columns(db_path):
db = Database(db_path)
with db.conn:
db["lots"].insert_all([{"id": i, "age": i + 1} for i in range(30)])
db.table("lots").insert_all([{"id": i, "age": i + 1} for i in range(30)])
result = CliRunner().invoke(cli.cli, ["tables", "--counts", "--columns", db_path])
assert (
'[{"table": "Gosh", "count": 0, "columns": ["c1", "c2", "c3"]},\n'
@ -121,7 +121,7 @@ def test_tables_counts_and_columns(db_path):
def test_tables_counts_and_columns_csv(db_path, format, expected):
db = Database(db_path)
with db.conn:
db["lots"].insert_all([{"id": i, "age": i + 1} for i in range(30)])
db.table("lots").insert_all([{"id": i, "age": i + 1} for i in range(30)])
result = CliRunner().invoke(
cli.cli, ["tables", "--counts", "--columns", format, db_path]
)
@ -131,7 +131,7 @@ def test_tables_counts_and_columns_csv(db_path, format, expected):
def test_tables_schema(db_path):
db = Database(db_path)
with db.conn:
db["lots"].insert_all([{"id": i, "age": i + 1} for i in range(30)])
db.table("lots").insert_all([{"id": i, "age": i + 1} for i in range(30)])
result = CliRunner().invoke(cli.cli, ["tables", "--schema", db_path])
assert (
'[{"table": "Gosh", "schema": "CREATE TABLE Gosh (c1 text, c2 text, c3 text)"},\n'
@ -183,7 +183,7 @@ def test_tables_schema(db_path):
def test_output_table(db_path, options, expected):
db = Database(db_path)
with db.conn:
db["rows"].insert_all(
db.table("rows").insert_all(
[
{
"c1": f"verb{i}",
@ -207,7 +207,7 @@ def test_output_table_no_headers(db_path, fmt_option):
# tabulate formats and the column names were always printed.
db = Database(db_path)
with db.conn:
db["dogs"].insert_all(
db.table("dogs").insert_all(
[
{"id": 1, "name": "Cleo", "age": 4},
{"id": 2, "name": "Pancakes", "age": 2},
@ -244,14 +244,14 @@ def test_output_table_no_headers(db_path, fmt_option):
def test_create_index(db_path):
db = Database(db_path)
assert [] == db["Gosh"].indexes
assert [] == db.table("Gosh").indexes
result = CliRunner().invoke(cli.cli, ["create-index", db_path, "Gosh", "c1"])
assert result.exit_code == 0
assert [
Index(
seq=0, name="idx_Gosh_c1", unique=0, origin="c", partial=0, columns=["c1"]
)
] == db["Gosh"].indexes
] == db.table("Gosh").indexes
# Try with a custom name
result = CliRunner().invoke(
cli.cli, ["create-index", db_path, "Gosh", "c2", "--name", "blah"]
@ -262,7 +262,7 @@ def test_create_index(db_path):
Index(
seq=1, name="idx_Gosh_c1", unique=0, origin="c", partial=0, columns=["c1"]
),
] == db["Gosh"].indexes
] == db.table("Gosh").indexes
# Try a two-column unique index
create_index_unique_args = [
"create-index",
@ -283,7 +283,7 @@ def test_create_index(db_path):
partial=0,
columns=["c1", "c2"],
)
] == db["Gosh2"].indexes
] == db.table("Gosh2").indexes
# Trying to create the same index should fail
assert CliRunner().invoke(cli.cli, create_index_unique_args).exit_code != 0
# ... unless we use --if-not-exists or --ignore
@ -296,11 +296,11 @@ def test_create_index(db_path):
def test_drop_index(db_path):
db = Database(db_path)
db["Gosh"].create_index(["c1"])
assert [index.name for index in db["Gosh"].indexes] == ["idx_Gosh_c1"]
db.table("Gosh").create_index(["c1"])
assert [index.name for index in db.table("Gosh").indexes] == ["idx_Gosh_c1"]
result = CliRunner().invoke(cli.cli, ["drop-index", db_path, "Gosh", "idx_Gosh_c1"])
assert result.exit_code == 0
assert db["Gosh"].indexes == []
assert db.table("Gosh").indexes == []
result = CliRunner().invoke(cli.cli, ["drop-index", db_path, "Gosh", "idx_Gosh_c1"])
assert result.exit_code == 1
@ -315,7 +315,7 @@ def test_drop_index(db_path):
def test_create_index_analyze(db_path):
db = Database(db_path)
assert "sqlite_stat1" not in db.table_names()
assert [] == db["Gosh"].indexes
assert [] == db.table("Gosh").indexes
result = CliRunner().invoke(
cli.cli, ["create-index", db_path, "Gosh", "c1", "--analyze"]
)
@ -325,7 +325,7 @@ def test_create_index_analyze(db_path):
def test_create_index_desc(db_path):
db = Database(db_path)
assert [] == db["Gosh"].indexes
assert [] == db.table("Gosh").indexes
result = CliRunner().invoke(cli.cli, ["create-index", db_path, "Gosh", "--", "-c1"])
assert result.exit_code == 0
assert (
@ -361,12 +361,12 @@ def test_create_index_desc(db_path):
def test_add_column(db_path, col_name, col_type, expected_schema):
db = Database(db_path)
db.create_table("dogs", {"name": str})
assert db["dogs"].schema == 'CREATE TABLE "dogs" (\n "name" TEXT\n)'
assert db.table("dogs").schema == 'CREATE TABLE "dogs" (\n "name" TEXT\n)'
args = ["add-column", db_path, "dogs", col_name]
if col_type is not None:
args.append(col_type)
assert CliRunner().invoke(cli.cli, args).exit_code == 0
assert db["dogs"].schema == expected_schema
assert db.table("dogs").schema == expected_schema
@pytest.mark.parametrize("ignore", (True, False))
@ -385,7 +385,7 @@ def test_add_column_ignore(db_path, ignore):
def test_add_column_not_null_default(db_path):
db = Database(db_path)
db.create_table("dogs", {"name": str})
assert db["dogs"].schema == 'CREATE TABLE "dogs" (\n "name" TEXT\n)'
assert db.table("dogs").schema == 'CREATE TABLE "dogs" (\n "name" TEXT\n)'
args = [
"add-column",
db_path,
@ -395,7 +395,7 @@ def test_add_column_not_null_default(db_path):
"dogs'dawg",
]
assert CliRunner().invoke(cli.cli, args).exit_code == 0
assert db["dogs"].schema == (
assert db.table("dogs").schema == (
'CREATE TABLE "dogs" (\n'
' "name" TEXT\n'
", \"nickname\" TEXT NOT NULL DEFAULT 'dogs''dawg')"
@ -415,10 +415,10 @@ def test_add_column_not_null_default(db_path):
)
def test_add_foreign_key(db_path, args, assert_message):
db = Database(db_path)
db["authors"].insert_all(
db.table("authors").insert_all(
[{"id": 1, "name": "Sally"}, {"id": 2, "name": "Asheesh"}], pk="id"
)
db["books"].insert_all(
db.table("books").insert_all(
[
{"title": "Hedgehogs of the world", "author_id": 1},
{"title": "How to train your wolf", "author_id": 2},
@ -431,7 +431,7 @@ def test_add_foreign_key(db_path, args, assert_message):
ForeignKey(
table="books", column="author_id", other_table="authors", other_column="id"
)
] == db["books"].foreign_keys
] == db.table("books").foreign_keys
# Error if we try to add it twice:
result = CliRunner().invoke(
@ -460,14 +460,14 @@ def test_add_foreign_key(db_path, args, assert_message):
def test_add_column_foreign_key(db_path):
db = Database(db_path)
db["authors"].insert({"id": 1, "name": "Sally"}, pk="id")
db["books"].insert({"title": "Hedgehogs of the world"})
db.table("authors").insert({"id": 1, "name": "Sally"}, pk="id")
db.table("books").insert({"title": "Hedgehogs of the world"})
# Add an author_id foreign key column to the books table
result = CliRunner().invoke(
cli.cli, ["add-column", db_path, "books", "author_id", "--fk", "authors"]
)
assert result.exit_code == 0, result.output
assert db["books"].schema == (
assert db.table("books").schema == (
'CREATE TABLE "books" (\n'
' "title" TEXT,\n'
' "author_id" INTEGER REFERENCES "authors"("id")\n'
@ -488,7 +488,7 @@ def test_add_column_foreign_key(db_path):
],
)
assert result.exit_code == 0, result.output
assert db["books"].schema == (
assert db.table("books").schema == (
'CREATE TABLE "books" (\n'
' "title" TEXT,\n'
' "author_id" INTEGER REFERENCES "authors"("id"),\n'
@ -505,7 +505,7 @@ def test_add_column_foreign_key(db_path):
def test_suggest_alter_if_column_missing(db_path):
db = Database(db_path)
db["authors"].insert({"id": 1, "name": "Sally"}, pk="id")
db.table("authors").insert({"id": 1, "name": "Sally"}, pk="id")
result = CliRunner().invoke(
cli.cli,
["insert", db_path, "authors", "-"],
@ -521,27 +521,27 @@ def test_suggest_alter_if_column_missing(db_path):
def test_index_foreign_keys(db_path):
test_add_column_foreign_key(db_path)
db = Database(db_path)
assert [] == db["books"].indexes
assert [] == db.table("books").indexes
result = CliRunner().invoke(cli.cli, ["index-foreign-keys", db_path])
assert result.exit_code == 0
assert [["author_id"], ["author_name_ref"]] == [
i.columns for i in db["books"].indexes
i.columns for i in db.table("books").indexes
]
def test_enable_fts(db_path):
db = Database(db_path)
assert db["Gosh"].detect_fts() is None
assert db.table("Gosh").detect_fts() is None
result = CliRunner().invoke(
cli.cli, ["enable-fts", db_path, "Gosh", "c1", "--fts4"]
)
assert result.exit_code == 0
assert "Gosh_fts" == db["Gosh"].detect_fts()
assert "Gosh_fts" == db.table("Gosh").detect_fts()
# Table names with restricted chars are handled correctly.
# colons and dots are restricted characters for table names.
db["http://example.com"].create({"c1": str, "c2": str, "c3": str})
assert db["http://example.com"].detect_fts() is None
db.table("http://example.com").create({"c1": str, "c2": str, "c3": str})
assert db.table("http://example.com").detect_fts() is None
result = CliRunner().invoke(
cli.cli,
[
@ -555,7 +555,7 @@ def test_enable_fts(db_path):
],
)
assert result.exit_code == 0
assert "http://example.com_fts" == db["http://example.com"].detect_fts()
assert "http://example.com_fts" == db.table("http://example.com").detect_fts()
# Check tokenize was set to porter
assert (
'CREATE VIRTUAL TABLE "http://example.com_fts" USING FTS4 (\n'
@ -563,19 +563,19 @@ def test_enable_fts(db_path):
" tokenize='porter',\n"
' content="http://example.com"'
"\n)"
) == db["http://example.com_fts"].schema
db["http://example.com"].drop()
) == db.table("http://example.com_fts").schema
db.table("http://example.com").drop()
def test_enable_fts_replace(db_path):
db = Database(db_path)
assert db["Gosh"].detect_fts() is None
assert db.table("Gosh").detect_fts() is None
result = CliRunner().invoke(
cli.cli, ["enable-fts", db_path, "Gosh", "c1", "--fts4"]
)
assert result.exit_code == 0
assert "Gosh_fts" == db["Gosh"].detect_fts()
assert db["Gosh_fts"].columns_dict == {"c1": str}
assert "Gosh_fts" == db.table("Gosh").detect_fts()
assert db.table("Gosh_fts").columns_dict == {"c1": str}
# This should throw an error
result2 = CliRunner().invoke(
@ -589,11 +589,11 @@ def test_enable_fts_replace(db_path):
cli.cli, ["enable-fts", db_path, "Gosh", "c2", "--fts4", "--replace"]
)
assert result3.exit_code == 0
assert db["Gosh_fts"].columns_dict == {"c2": str}
assert db.table("Gosh_fts").columns_dict == {"c2": str}
def test_enable_fts_with_triggers(db_path):
Database(db_path)["Gosh"].insert_all([{"c1": "baz"}])
Database(db_path).table("Gosh").insert_all([{"c1": "baz"}])
exit_code = (
CliRunner()
.invoke(
@ -612,12 +612,12 @@ def test_enable_fts_with_triggers(db_path):
)
assert [("baz",)] == search("baz")
Database(db_path)["Gosh"].insert_all([{"c1": "martha"}])
Database(db_path).table("Gosh").insert_all([{"c1": "martha"}])
assert [("martha",)] == search("martha")
def test_populate_fts(db_path):
Database(db_path)["Gosh"].insert_all([{"c1": "baz"}])
Database(db_path).table("Gosh").insert_all([{"c1": "baz"}])
exit_code = (
CliRunner()
.invoke(cli.cli, ["enable-fts", db_path, "Gosh", "c1", "--fts4"])
@ -633,7 +633,7 @@ def test_populate_fts(db_path):
)
assert [("baz",)] == search("baz")
Database(db_path)["Gosh"].insert_all([{"c1": "martha"}])
Database(db_path).table("Gosh").insert_all([{"c1": "martha"}])
assert [] == search("martha")
exit_code = (
CliRunner().invoke(cli.cli, ["populate-fts", db_path, "Gosh", "c1"]).exit_code
@ -645,7 +645,7 @@ def test_populate_fts(db_path):
def test_disable_fts(db_path):
db = Database(db_path)
assert {"Gosh", "Gosh2"} == set(db.table_names())
db["Gosh"].enable_fts(["c1"], create_triggers=True)
db.table("Gosh").enable_fts(["c1"], create_triggers=True)
assert {
"Gosh_fts",
"Gosh_fts_idx",
@ -677,7 +677,7 @@ def test_optimize(db_path, tables):
db = Database(db_path)
with db.conn:
for table in ("Gosh", "Gosh2"):
db[table].insert_all(
db.table(table).insert_all(
[
{
"c1": f"verb{i}",
@ -687,8 +687,8 @@ def test_optimize(db_path, tables):
for i in range(10000)
]
)
db["Gosh"].enable_fts(["c1", "c2", "c3"], fts_version="FTS4")
db["Gosh2"].enable_fts(["c1", "c2", "c3"], fts_version="FTS5")
db.table("Gosh").enable_fts(["c1", "c2", "c3"], fts_version="FTS4")
db.table("Gosh2").enable_fts(["c1", "c2", "c3"], fts_version="FTS5")
size_before_optimize = os.stat(db_path).st_size
result = CliRunner().invoke(cli.cli, ["optimize", db_path] + tables)
assert result.exit_code == 0
@ -713,22 +713,22 @@ def test_rebuild_fts_fixes_docsize_error(db_path):
for i in range(10000)
]
with db.conn:
db["fts5_table"].insert_all(records, pk="c1")
db["fts5_table"].enable_fts(
db.table("fts5_table").insert_all(records, pk="c1")
db.table("fts5_table").enable_fts(
["c1", "c2", "c3"], fts_version="FTS5", create_triggers=True
)
# Search should work
assert list(db["fts5_table"].search("verb1"))
assert list(db.table("fts5_table").search("verb1"))
# Replicate docsize error from this issue for FTS5
# https://github.com/simonw/sqlite-utils/issues/149
assert db["fts5_table_fts_docsize"].count == 10000
db["fts5_table"].insert_all(records, replace=True)
assert db["fts5_table"].count == 10000
assert db["fts5_table_fts_docsize"].count == 20000
assert db.table("fts5_table_fts_docsize").count == 10000
db.table("fts5_table").insert_all(records, replace=True)
assert db.table("fts5_table").count == 10000
assert db.table("fts5_table_fts_docsize").count == 20000
# Running rebuild-fts should fix this
result = CliRunner().invoke(cli.cli, ["rebuild-fts", db_path, "fts5_table"])
assert result.exit_code == 0
assert db["fts5_table_fts_docsize"].count == 10000
assert db.table("fts5_table_fts_docsize").count == 10000
@pytest.mark.parametrize(
@ -741,7 +741,7 @@ def test_rebuild_fts_fixes_docsize_error(db_path):
def test_query_csv(db_path, format, expected):
db = Database(db_path)
with db.conn:
db["dogs"].insert_all(
db.table("dogs").insert_all(
[
{"id": 1, "age": 4, "name": "Cleo"},
{"id": 2, "age": 2, "name": "Pancakes"},
@ -793,7 +793,7 @@ _one_query = "select id, name, age from dogs where id = 1"
def test_query_json(db_path, sql, args, expected):
db = Database(db_path)
with db.conn:
db["dogs"].insert_all(
db.table("dogs").insert_all(
[
{"id": 1, "age": 4, "name": "Cleo"},
{"id": 2, "age": 2, "name": "Pancakes"},
@ -807,7 +807,7 @@ def test_query_sql_from_stdin(db_path):
# https://github.com/simonw/sqlite-utils/issues/765
db = Database(db_path)
with db.conn:
db["dogs"].insert_all(
db.table("dogs").insert_all(
[
{"id": 1, "age": 4, "name": "Cleo"},
{"id": 2, "age": 2, "name": "Pancakes"},
@ -1004,7 +1004,7 @@ LOREM_IPSUM_COMPRESSED = (
def test_query_json_binary(db_path):
db = Database(db_path)
with db.conn:
db["files"].insert(
db.table("files").insert(
{
"name": "lorem.txt",
"sz": 16984,
@ -1059,7 +1059,7 @@ def test_query_params(db_path, sql, params, expected):
def test_query_json_with_json_cols(db_path):
db = Database(db_path)
with db.conn:
db["dogs"].insert(
db.table("dogs").insert(
{
"id": 1,
"name": "Cleo",
@ -1088,7 +1088,7 @@ def test_query_json_with_json_cols(db_path):
def test_query_json_unicode_not_escaped_by_default(db_path):
db = Database(db_path)
with db.conn:
db["text"].insert({"id": 1, "text": "Japanese 日本語"}, pk="id")
db.table("text").insert({"id": 1, "text": "Japanese 日本語"}, pk="id")
result = CliRunner().invoke(cli.cli, [db_path, "select id, text from text"])
assert result.exit_code == 0
assert result.output.strip() == '[{"id": 1, "text": "Japanese 日本語"}]'
@ -1102,7 +1102,7 @@ def test_query_json_unicode_not_escaped_by_default(db_path):
def test_query_json_ascii_option(db_path, command):
db = Database(db_path)
with db.conn:
db["text"].insert({"id": 1, "text": "Japanese 日本語"}, pk="id")
db.table("text").insert({"id": 1, "text": "Japanese 日本語"}, pk="id")
if command == "query":
args = [db_path, "select id, text from text", "--ascii"]
else:
@ -1118,7 +1118,7 @@ def test_query_json_ascii_option(db_path, command):
[(b"\x00\x0fbinary", True), ("this is text", False), (1, False), (1.5, False)],
)
def test_query_raw(db_path, content, is_binary):
Database(db_path)["files"].insert({"content": content})
Database(db_path).table("files").insert({"content": content})
result = CliRunner().invoke(
cli.cli, [db_path, "select content from files", "--raw"]
)
@ -1133,7 +1133,7 @@ def test_query_raw(db_path, content, is_binary):
[(b"\x00\x0fbinary", True), ("this is text", False), (1, False), (1.5, False)],
)
def test_query_raw_lines(db_path, content, is_binary):
Database(db_path)["files"].insert_all({"content": content} for _ in range(3))
Database(db_path).table("files").insert_all({"content": content} for _ in range(3))
result = CliRunner().invoke(
cli.cli, [db_path, "select content from files", "--raw-lines"]
)
@ -1215,7 +1215,7 @@ def test_query_memory_does_not_create_file(tmpdir):
def test_rows(db_path, args, expected):
db = Database(db_path)
with db.conn:
db["dogs"].insert_all(
db.table("dogs").insert_all(
[
{"id": 1, "age": 4, "name": "Cleo"},
{"id": 2, "age": 2, "name": "Pancakes"},
@ -1240,7 +1240,7 @@ def test_upsert(db_path, tmpdir):
catch_exceptions=False,
)
assert result.exit_code == 0, result.output
assert 2 == db["dogs"].count
assert 2 == db.table("dogs").count
# Now run the upsert to update just their ages
upsert_dogs = [
{"id": 1, "age": 5},
@ -1295,8 +1295,8 @@ def test_upsert_pk_inferred_from_existing_table(db_path, tmpdir):
def test_upsert_analyze(db_path, tmpdir):
db = Database(db_path)
db["rows"].insert({"id": 1, "foo": "x", "n": 3}, pk="id")
db["rows"].create_index(["n"])
db.table("rows").insert({"id": 1, "foo": "x", "n": 3}, pk="id")
db.table("rows").create_index(["n"])
assert "sqlite_stat1" not in db.table_names()
result = CliRunner().invoke(
cli.cli,
@ -1310,7 +1310,7 @@ def test_upsert_analyze(db_path, tmpdir):
def test_upsert_flatten(tmpdir):
db_path = str(tmpdir / "flat.db")
db = Database(db_path)
db["upsert_me"].insert({"id": 1, "name": "Example"}, pk="id")
db.table("upsert_me").insert({"id": 1, "name": "Example"}, pk="id")
result = CliRunner().invoke(
cli.cli,
["upsert", db_path, "upsert_me", "-", "--flatten", "--pk", "id", "--alter"],
@ -1424,7 +1424,7 @@ def test_create_table(args, schema):
)
assert result.exit_code == 0
db = Database("test.db")
assert schema == db["t"].schema
assert schema == db.table("t").schema
def test_create_table_foreign_key():
@ -1459,21 +1459,21 @@ def test_create_table_foreign_key():
' "id" INTEGER PRIMARY KEY,\n'
' "name" TEXT\n'
")"
) == db["authors"].schema
) == db.table("authors").schema
assert (
'CREATE TABLE "books" (\n'
' "id" INTEGER PRIMARY KEY,\n'
' "title" TEXT,\n'
' "author_id" INTEGER REFERENCES "authors"("id")\n'
")"
) == db["books"].schema
) == db.table("books").schema
def test_create_table_error_if_table_exists():
runner = CliRunner()
with runner.isolated_filesystem():
db = Database("test.db")
db["dogs"].insert({"name": "Cleo"})
db.table("dogs").insert({"name": "Cleo"})
result = runner.invoke(
cli.cli, ["create-table", "test.db", "dogs", "id", "integer"]
)
@ -1488,24 +1488,24 @@ def test_create_table_ignore():
runner = CliRunner()
with runner.isolated_filesystem():
db = Database("test.db")
db["dogs"].insert({"name": "Cleo"})
db.table("dogs").insert({"name": "Cleo"})
result = runner.invoke(
cli.cli, ["create-table", "test.db", "dogs", "id", "integer", "--ignore"]
)
assert result.exit_code == 0
assert 'CREATE TABLE "dogs" (\n "name" TEXT\n)' == db["dogs"].schema
assert 'CREATE TABLE "dogs" (\n "name" TEXT\n)' == db.table("dogs").schema
def test_create_table_replace():
runner = CliRunner()
with runner.isolated_filesystem():
db = Database("test.db")
db["dogs"].insert({"name": "Cleo"})
db.table("dogs").insert({"name": "Cleo"})
result = runner.invoke(
cli.cli, ["create-table", "test.db", "dogs", "id", "integer", "--replace"]
)
assert result.exit_code == 0
assert 'CREATE TABLE "dogs" (\n "id" INTEGER\n)' == db["dogs"].schema
assert 'CREATE TABLE "dogs" (\n "id" INTEGER\n)' == db.table("dogs").schema
def test_create_view():
@ -1517,7 +1517,8 @@ def test_create_view():
)
assert result.exit_code == 0
assert (
'CREATE VIEW "version" AS select sqlite_version()' == db["version"].schema
'CREATE VIEW "version" AS select sqlite_version()'
== db.view("version").schema
)
@ -1554,7 +1555,7 @@ def test_create_view_ignore():
assert result.exit_code == 0
assert (
'CREATE VIEW "version" AS select sqlite_version() + 1'
== db["version"].schema
== db.view("version").schema
)
@ -1575,7 +1576,8 @@ def test_create_view_replace():
)
assert result.exit_code == 0
assert (
'CREATE VIEW "version" AS select sqlite_version()' == db["version"].schema
'CREATE VIEW "version" AS select sqlite_version()'
== db.view("version").schema
)
@ -1583,7 +1585,7 @@ def test_drop_table():
runner = CliRunner()
with runner.isolated_filesystem():
db = Database("test.db")
db["t"].create({"pk": int}, pk="pk")
db.table("t").create({"pk": int}, pk="pk")
assert "t" in db.table_names()
result = runner.invoke(
cli.cli,
@ -1601,7 +1603,7 @@ def test_drop_table_error():
runner = CliRunner()
with runner.isolated_filesystem():
db = Database("test.db")
db["t"].create({"pk": int}, pk="pk")
db.table("t").create({"pk": int}, pk="pk")
result = runner.invoke(
cli.cli,
[
@ -1624,7 +1626,7 @@ def test_drop_table_on_view_errors():
runner = CliRunner()
with runner.isolated_filesystem():
db = Database("test.db")
db["t"].insert({"id": 1})
db.table("t").insert({"id": 1})
db.create_view("v", "select * from t")
result = runner.invoke(cli.cli, ["drop-table", "test.db", "v"])
assert result.exit_code == 1
@ -1660,7 +1662,7 @@ def test_drop_view_on_table_errors():
runner = CliRunner()
with runner.isolated_filesystem():
db = Database("test.db")
db["t"].insert({"id": 1})
db.table("t").insert({"id": 1})
result = runner.invoke(cli.cli, ["drop-view", "test.db", "t"])
assert result.exit_code == 1
assert 'Error: "t" is a table, not a view - use drop-table to drop it' == (
@ -1677,7 +1679,7 @@ def test_drop_view_error():
runner = CliRunner()
with runner.isolated_filesystem():
db = Database("test.db")
db["t"].create({"pk": int}, pk="pk")
db.table("t").create({"pk": int}, pk="pk")
result = runner.invoke(
cli.cli,
[
@ -1702,7 +1704,7 @@ def test_enable_wal():
with runner.isolated_filesystem():
for dbname in dbs:
db = Database(dbname)
db["t"].create({"pk": int}, pk="pk")
db.table("t").create({"pk": int}, pk="pk")
assert db.journal_mode == "delete"
result = runner.invoke(cli.cli, ["enable-wal"] + dbs, catch_exceptions=False)
assert result.exit_code == 0
@ -1717,7 +1719,7 @@ def test_disable_wal():
with runner.isolated_filesystem():
for dbname in dbs:
db = Database(dbname)
db["t"].create({"pk": int}, pk="pk")
db.table("t").create({"pk": int}, pk="pk")
db.enable_wal()
assert db.journal_mode == "wal"
result = runner.invoke(cli.cli, ["disable-wal"] + dbs)
@ -1740,7 +1742,7 @@ def test_disable_wal():
def test_query_update(db_path, args, expected):
db = Database(db_path)
with db.conn:
db["dogs"].insert_all(
db.table("dogs").insert_all(
[
{"id": 1, "age": 4, "name": "Cleo"},
]
@ -1756,11 +1758,13 @@ def test_query_update(db_path, args, expected):
def test_add_foreign_keys(db_path):
db = Database(db_path)
db["countries"].insert({"id": 7, "name": "Panama"}, pk="id")
db["authors"].insert({"id": 3, "name": "Matilda", "country_id": 7}, pk="id")
db["books"].insert({"id": 2, "title": "Wolf anatomy", "author_id": 3}, pk="id")
assert db["authors"].foreign_keys == []
assert db["books"].foreign_keys == []
db.table("countries").insert({"id": 7, "name": "Panama"}, pk="id")
db.table("authors").insert({"id": 3, "name": "Matilda", "country_id": 7}, pk="id")
db.table("books").insert(
{"id": 2, "title": "Wolf anatomy", "author_id": 3}, pk="id"
)
assert db.table("authors").foreign_keys == []
assert db.table("books").foreign_keys == []
result = CliRunner().invoke(
cli.cli,
[
@ -1777,7 +1781,7 @@ def test_add_foreign_keys(db_path):
],
)
assert result.exit_code == 0
assert db["authors"].foreign_keys == [
assert db.table("authors").foreign_keys == [
ForeignKey(
table="authors",
column="country_id",
@ -1785,7 +1789,7 @@ def test_add_foreign_keys(db_path):
other_column="id",
)
]
assert db["books"].foreign_keys == [
assert db.table("books").foreign_keys == [
ForeignKey(
table="books", column="author_id", other_table="authors", other_column="id"
)
@ -1909,7 +1913,7 @@ def test_add_foreign_keys(db_path):
def test_transform(db_path, args, expected_schema):
db = Database(db_path)
with db.conn:
db["dogs"].insert(
db.table("dogs").insert(
{"id": 1, "age": 4, "name": "Cleo"},
not_null={"age"},
defaults={"age": 1},
@ -1918,20 +1922,20 @@ def test_transform(db_path, args, expected_schema):
result = CliRunner().invoke(cli.cli, ["transform", db_path, "dogs"] + args)
print(result.output)
assert result.exit_code == 0
schema = db["dogs"].schema
schema = db.table("dogs").schema
assert schema == expected_schema
def test_transform_sql(db_path):
db = Database(db_path)
with db.conn:
db["dogs"].insert(
db.table("dogs").insert(
{"id": 1, "age": 4, "name": "Cleo"},
not_null={"age"},
defaults={"age": 1},
pk="id",
)
original_schema = db["dogs"].schema
original_schema = db.table("dogs").schema
result = CliRunner().invoke(
cli.cli, ["transform", db_path, "dogs", "--drop", "name", "--sql"]
@ -1942,7 +1946,7 @@ def test_transform_sql(db_path):
assert '"age" INTEGER NOT NULL DEFAULT' in result.output
assert 'DROP TABLE "dogs";' in result.output
assert 'ALTER TABLE "dogs_new_' in result.output
assert db["dogs"].schema == original_schema
assert db.table("dogs").schema == original_schema
@pytest.mark.parametrize(
@ -1958,12 +1962,12 @@ def test_transform_strict_option(db_path, initial_strict, args, expected_strict)
db = Database(db_path)
if not db.supports_strict:
pytest.skip("SQLite version does not support strict tables")
db["dogs"].create({"id": int}, strict=initial_strict)
db.table("dogs").create({"id": int}, strict=initial_strict)
result = CliRunner().invoke(cli.cli, ["transform", db_path, "dogs"] + args)
assert result.exit_code == 0, result.output
assert db["dogs"].strict is expected_strict
assert db.table("dogs").strict is expected_strict
@pytest.mark.parametrize(
@ -1977,20 +1981,20 @@ def test_transform_strict_option_sql(db_path, initial_strict, flag, sql_is_stric
db = Database(db_path)
if not db.supports_strict:
pytest.skip("SQLite version does not support strict tables")
db["dogs"].create({"id": int}, strict=initial_strict)
db.table("dogs").create({"id": int}, strict=initial_strict)
result = CliRunner().invoke(cli.cli, ["transform", db_path, "dogs", flag, "--sql"])
assert result.exit_code == 0, result.output
assert (") STRICT;" in result.output) is sql_is_strict
assert db["dogs"].strict is initial_strict
assert db.table("dogs").strict is initial_strict
def test_transform_strict_option_with_invalid_data(db_path):
db = Database(db_path)
if not db.supports_strict:
pytest.skip("SQLite version does not support strict tables")
dogs = db["dogs"]
dogs = db.table("dogs")
dogs.create({"id": int})
dogs.insert({"id": "not-an-integer"})
@ -2048,10 +2052,10 @@ def test_transform_add_or_drop_foreign_key(db_path, extra_args, expected_schema)
db = Database(db_path)
with db.conn:
# Create table with three foreign keys so we can drop two of them
db["continent"].insert({"id": 1, "name": "Europe"}, pk="id")
db["country"].insert({"id": 1, "name": "France"}, pk="id")
db["city"].insert({"id": 24, "name": "Paris"}, pk="id")
db["places"].insert(
db.table("continent").insert({"id": 1, "name": "Europe"}, pk="id")
db.table("country").insert({"id": 1, "name": "France"}, pk="id")
db.table("city").insert({"id": 24, "name": "Paris"}, pk="id")
db.table("places").insert(
{
"id": 32,
"name": "Caveau de la Huchette",
@ -2072,7 +2076,7 @@ def test_transform_add_or_drop_foreign_key(db_path, extra_args, expected_schema)
+ extra_args,
)
assert result.exit_code == 0
schema = db["places"].schema
schema = db.table("places").schema
assert schema == expected_schema
@ -2133,7 +2137,7 @@ _common_other_schema = (
def test_extract(db_path, args, expected_table_schema, expected_other_schema):
db = Database(db_path)
with db.conn:
db["trees"].insert(
db.table("trees").insert(
{"id": 1, "address": "4 Park Ave", "species": "Palm"},
pk="id",
)
@ -2142,7 +2146,7 @@ def test_extract(db_path, args, expected_table_schema, expected_other_schema):
)
print(result.output)
assert result.exit_code == 0
schema = db["trees"].schema
schema = db.table("trees").schema
assert schema == expected_table_schema
other_schema = next(
t for t in db.tables if t.name not in ("trees", "Gosh", "Gosh2")
@ -2190,7 +2194,7 @@ def test_insert_encoding(tmpdir):
)
assert good_result.exit_code == 0
db = Database(db_path)
assert list(db["places"].rows) == [
assert list(db.table("places").rows) == [
{
"date": "2020-01-01",
"name": "Barra da Lagoa",
@ -2226,7 +2230,7 @@ def test_insert_encoding(tmpdir):
def test_search(tmpdir, fts, extra_arg, expected):
db_path = str(tmpdir / "test.db")
db = Database(db_path)
db["articles"].insert_all(
db.table("articles").insert_all(
[
{"id": 1, "title": "Title the first"},
{"id": 2, "title": "Title the second"},
@ -2234,7 +2238,7 @@ def test_search(tmpdir, fts, extra_arg, expected):
],
pk="id",
)
db["articles"].enable_fts(["title"], fts_version=fts)
db.table("articles").enable_fts(["title"], fts_version=fts)
result = CliRunner().invoke(
cli.cli,
["search", db_path, "articles", "second"] + ([extra_arg] if extra_arg else []),
@ -2247,7 +2251,7 @@ def test_search(tmpdir, fts, extra_arg, expected):
def test_search_quote(tmpdir):
db_path = str(tmpdir / "test.db")
db = Database(db_path)
db["creatures"].insert({"name": "dog."}).enable_fts(["name"])
db.table("creatures").insert({"name": "dog."}).enable_fts(["name"])
# Without --quote should return an error
error_result = CliRunner().invoke(cli.cli, ["search", db_path, "creatures", 'dog"'])
assert error_result.exit_code == 1
@ -2355,11 +2359,11 @@ _TRIGGERS_EXPECTED = (
def test_triggers(tmpdir, extra_args, expected):
db_path = str(tmpdir / "test.db")
db = Database(db_path)
db["articles"].insert(
db.table("articles").insert(
{"id": 1, "title": "Title the first"},
pk="id",
)
db["counter"].insert({"count": 1})
db.table("counter").insert({"count": 1})
db.conn.execute(textwrap.dedent("""
CREATE TRIGGER blah AFTER INSERT ON articles
BEGIN
@ -2420,9 +2424,9 @@ def test_triggers(tmpdir, extra_args, expected):
def test_schema(tmpdir, options, expected):
db_path = str(tmpdir / "test.db")
db = Database(db_path)
db["dogs"].create({"id": int, "name": str})
db["chickens"].create({"id": int, "name": str, "breed": str})
db["chickens"].create_index(["breed"])
db.table("dogs").create({"id": int, "name": str})
db.table("chickens").create({"id": int, "name": str, "breed": str})
db.table("chickens").create_index(["breed"])
result = CliRunner().invoke(
cli.cli,
["schema", db_path] + options,
@ -2446,7 +2450,7 @@ def test_long_csv_column_value(tmpdir):
)
assert result.exit_code == 0
db = Database(db_path)
rows = list(db["bigtable"].rows)
rows = list(db.table("bigtable").rows)
assert len(rows) == 1
assert rows[0]["text"] == long_string
@ -2473,7 +2477,7 @@ def test_import_no_headers(tmpdir, args, tsv):
)
assert result.exit_code == 0, result.output
db = Database(db_path)
schema = db["creatures"].schema
schema = db.table("creatures").schema
assert schema == (
'CREATE TABLE "creatures" (\n'
' "untitled_1" TEXT,\n'
@ -2481,7 +2485,7 @@ def test_import_no_headers(tmpdir, args, tsv):
' "untitled_3" TEXT\n'
")"
)
rows = list(db["creatures"].rows)
rows = list(db.table("creatures").rows)
assert rows == [
{"untitled_1": "Cleo", "untitled_2": "Dog", "untitled_3": "5"},
{"untitled_1": "Tracy", "untitled_2": "Spider", "untitled_3": "7"},
@ -2493,10 +2497,10 @@ def test_attach(tmpdir):
bar_path = str(tmpdir / "bar.db")
db = Database(foo_path)
with db.conn:
db["foo"].insert({"id": 1, "text": "foo"})
db.table("foo").insert({"id": 1, "text": "foo"})
db2 = Database(bar_path)
with db2.conn:
db2["bar"].insert({"id": 1, "text": "bar"})
db2.table("bar").insert({"id": 1, "text": "bar"})
db.attach("bar", bar_path)
sql = "select * from foo union all select * from bar.bar"
result = CliRunner().invoke(
@ -2557,7 +2561,7 @@ def test_insert_detect_types(tmpdir):
)
assert result.exit_code == 0
db = Database(db_path)
assert list(db["creatures"].rows) == [
assert list(db.table("creatures").rows) == [
{"name": "Cleo", "age": 6, "weight": 45.5},
{"name": "Dori", "age": 1, "weight": 3.5},
]
@ -2589,7 +2593,7 @@ def test_upsert_detect_types(tmpdir):
)
assert result.exit_code == 0
db = Database(db_path)
assert list(db["creatures"].rows) == [
assert list(db.table("creatures").rows) == [
{"id": 1, "name": "Cleo", "age": 6, "weight": 45.5},
{"id": 2, "name": "Dori", "age": 1, "weight": 3.5},
]
@ -2608,7 +2612,7 @@ def test_csv_detect_types_creates_real_columns(tmpdir):
assert result.exit_code == 0
db = Database(db_path)
# Check that the schema uses REAL for the weight column
assert db["creatures"].schema == (
assert db.table("creatures").schema == (
'CREATE TABLE "creatures" (\n'
' "name" TEXT,\n'
' "age" INTEGER,\n'
@ -2630,11 +2634,11 @@ def test_insert_no_detect_types(tmpdir):
assert result.exit_code == 0
db = Database(db_path)
# All columns should be TEXT when --no-detect-types is used
assert list(db["creatures"].rows) == [
assert list(db.table("creatures").rows) == [
{"name": "Cleo", "age": "6", "weight": "45.5"},
{"name": "Dori", "age": "1", "weight": "3.5"},
]
assert db["creatures"].schema == (
assert db.table("creatures").schema == (
'CREATE TABLE "creatures" (\n'
' "name" TEXT,\n'
' "age" TEXT,\n'
@ -2665,11 +2669,11 @@ def test_upsert_no_detect_types(tmpdir):
assert result.exit_code == 0
db = Database(db_path)
# All columns should be TEXT when --no-detect-types is used
assert list(db["creatures"].rows) == [
assert list(db.table("creatures").rows) == [
{"id": "1", "name": "Cleo", "age": "6", "weight": "45.5"},
{"id": "2", "name": "Dori", "age": "1", "weight": "3.5"},
]
assert db["creatures"].schema == (
assert db.table("creatures").schema == (
'CREATE TABLE "creatures" (\n'
' "id" TEXT PRIMARY KEY,\n'
' "name" TEXT,\n'
@ -2751,20 +2755,20 @@ def test_create_database(tmpdir, enable_wal):
def test_analyze(tmpdir, options, expected):
db_path = str(tmpdir / "test.db")
db = Database(db_path)
db["one_index"].insert({"id": 1, "name": "Cleo"}, pk="id")
db["one_index"].create_index(["name"])
db["two_indexes"].insert({"id": 1, "name": "Cleo", "species": "dog"}, pk="id")
db["two_indexes"].create_index(["name"])
db["two_indexes"].create_index(["species"])
db.table("one_index").insert({"id": 1, "name": "Cleo"}, pk="id")
db.table("one_index").create_index(["name"])
db.table("two_indexes").insert({"id": 1, "name": "Cleo", "species": "dog"}, pk="id")
db.table("two_indexes").create_index(["name"])
db.table("two_indexes").create_index(["species"])
result = CliRunner().invoke(cli.cli, ["analyze", db_path] + options)
assert result.exit_code == 0
assert list(db["sqlite_stat1"].rows) == expected
assert list(db.table("sqlite_stat1").rows) == expected
def test_rename_table(tmpdir):
db_path = str(tmpdir / "test.db")
db = Database(db_path)
db["one"].insert({"id": 1, "name": "Cleo"}, pk="id")
db.table("one").insert({"id": 1, "name": "Cleo"}, pk="id")
# First try a non-existent table
result_error = CliRunner().invoke(
cli.cli,
@ -2782,7 +2786,7 @@ def test_rename_table(tmpdir):
catch_exceptions=False,
)
assert result_error2.exit_code == 0
previous_columns = db["one"].columns_dict
previous_columns = db.table("one").columns_dict
# Now try for a table that exists
result = CliRunner().invoke(
cli.cli,
@ -2790,13 +2794,13 @@ def test_rename_table(tmpdir):
catch_exceptions=False,
)
assert result.exit_code == 0
assert db["two"].columns_dict == previous_columns
assert db.table("two").columns_dict == previous_columns
def test_duplicate_table(tmpdir):
db_path = str(tmpdir / "test.db")
db = Database(db_path)
db["one"].insert({"id": 1, "name": "Cleo"}, pk="id")
db.table("one").insert({"id": 1, "name": "Cleo"}, pk="id")
# First try a non-existent table
result_error = CliRunner().invoke(
cli.cli,
@ -2819,8 +2823,8 @@ def test_duplicate_table(tmpdir):
catch_exceptions=False,
)
assert result.exit_code == 0
assert db["one"].columns_dict == db["two"].columns_dict
assert list(db["one"].rows) == list(db["two"].rows)
assert db.table("one").columns_dict == db.table("two").columns_dict
assert list(db.table("one").rows) == list(db.table("two").rows)
@pytest.mark.skipif(not _has_compiled_ext(), reason="Requires compiled ext.c")
@ -2863,9 +2867,9 @@ def test_create_table_strict(strict):
+ (["--strict"] if strict else []),
)
assert result.exit_code == 0
assert db["items"].strict == strict or not db.supports_strict
assert db.table("items").strict == strict or not db.supports_strict
# Should have a floating point column
assert db["items"].columns_dict == {"id": int, "w": float}
assert db.table("items").columns_dict == {"id": int, "w": float}
@pytest.mark.parametrize("method", ("insert", "upsert"))
@ -2880,12 +2884,12 @@ def test_insert_upsert_strict(tmpdir, method, strict):
)
assert result.exit_code == 0
db = Database(db_path)
assert db["items"].strict == strict or not db.supports_strict
assert db.table("items").strict == strict or not db.supports_strict
def test_extract_bad_column_clean_error(db_path):
db = Database(db_path)
db["trees"].insert({"id": 1, "species": "Palm"}, pk="id")
db.table("trees").insert({"id": 1, "species": "Palm"}, pk="id")
result = CliRunner().invoke(cli.cli, ["extract", db_path, "trees", "nope"])
assert result.exit_code == 1
assert result.exception is None or isinstance(result.exception, SystemExit)
@ -2894,7 +2898,7 @@ def test_extract_bad_column_clean_error(db_path):
def test_extract_view_clean_error(db_path):
db = Database(db_path)
db["trees"].insert({"id": 1, "species": "Palm"}, pk="id")
db.table("trees").insert({"id": 1, "species": "Palm"}, pk="id")
db.create_view("v", "select * from trees")
result = CliRunner().invoke(cli.cli, ["extract", db_path, "v", "species"])
assert result.exit_code == 1

View file

@ -13,7 +13,7 @@ from sqlite_utils import Database, cli
def test_db_and_path(tmpdir):
db_path = str(pathlib.Path(tmpdir) / "data.db")
db = Database(db_path)
db["example"].insert_all(
db.table("example").insert_all(
[
{"id": 1, "name": "One"},
{"id": 2, "name": "Two"},
@ -44,7 +44,7 @@ def test_cli_bulk(test_db_and_path):
{"id": 2, "name": "Two"},
{"id": 3, "name": "THREE"},
{"id": 4, "name": "FOUR"},
] == list(db["example"].rows)
] == list(db.table("example").rows)
def test_cli_bulk_multiple_functions(test_db_and_path):
@ -70,7 +70,7 @@ def test_cli_bulk_multiple_functions(test_db_and_path):
{"id": 2, "name": "Two"},
{"id": 3, "name": "THREE"},
{"id": 4, "name": "FOUR"},
] == list(db["example"].rows)
] == list(db.table("example").rows)
def test_cli_bulk_batch_size(test_db_and_path):
@ -95,13 +95,13 @@ def test_cli_bulk_batch_size(test_db_and_path):
proc.stdin.write(b'{"id": 3, "name": "Three"}\n\n')
proc.stdin.flush()
time.sleep(1)
assert db["example"].count == 2
assert db.table("example").count == 2
# Writing another should trigger a commit:
proc.stdin.write(b'{"id": 4, "name": "Four"}\n\n')
proc.stdin.flush()
time.sleep(1)
assert db["example"].count == 4
assert db.table("example").count == 4
proc.stdin.close()
proc.wait()

View file

@ -12,7 +12,7 @@ from sqlite_utils import cli
@pytest.fixture
def test_db_and_path(fresh_db_and_path):
db, db_path = fresh_db_and_path
db["example"].insert_all(
db.table("example").insert_all(
[
{"id": 1, "dt": "5th October 2019 12:04"},
{"id": 2, "dt": "6th October 2019 00:05:06"},
@ -47,12 +47,12 @@ def fresh_db_and_path(tmpdir):
)
def test_convert_code(fresh_db_and_path, code):
db, db_path = fresh_db_and_path
db["t"].insert({"text": "October"})
db.table("t").insert({"text": "October"})
result = CliRunner().invoke(
cli.cli, ["convert", db_path, "t", "text", code], catch_exceptions=False
)
assert result.exit_code == 0, result.output
value = next(iter(db["t"].rows))["text"]
value = next(iter(db.table("t").rows))["text"]
assert value == "Spooktober"
@ -65,7 +65,7 @@ def test_convert_code(fresh_db_and_path, code):
)
def test_convert_code_errors(fresh_db_and_path, bad_code):
db, db_path = fresh_db_and_path
db["t"].insert({"text": "October"})
db.table("t").insert({"text": "October"})
result = CliRunner().invoke(
cli.cli, ["convert", db_path, "t", "text", bad_code], catch_exceptions=False
)
@ -93,12 +93,12 @@ def test_convert_import(test_db_and_path):
{"id": 2, "dt": "6th OXXober 2019 00:05:06"},
{"id": 3, "dt": ""},
{"id": 4, "dt": None},
] == list(db["example"].rows)
] == list(db.table("example").rows)
def test_convert_import_nested(fresh_db_and_path):
db, db_path = fresh_db_and_path
db["example"].insert({"xml": '<item name="Cleo" />'})
db.table("example").insert({"xml": '<item name="Cleo" />'})
result = CliRunner().invoke(
cli.cli,
[
@ -114,7 +114,7 @@ def test_convert_import_nested(fresh_db_and_path):
assert result.exit_code == 0, result.output
assert [
{"xml": "Cleo"},
] == list(db["example"].rows)
] == list(db.table("example").rows)
def test_convert_dryrun(test_db_and_path):
@ -152,7 +152,7 @@ def test_convert_dryrun(test_db_and_path):
"Would affect 4 rows"
)
# But it should not have actually modified the table data
assert list(db["example"].rows) == [
assert list(db.table("example").rows) == [
{"id": 1, "dt": "5th October 2019 12:04"},
{"id": 2, "dt": "6th October 2019 00:05:06"},
{"id": 3, "dt": ""},
@ -269,7 +269,7 @@ def test_convert_output_column(test_db_and_path, drop):
if drop:
for row in expected:
del row["dt"]
assert list(db["example"].rows) == expected
assert list(db.table("example").rows) == expected
@pytest.mark.parametrize(
@ -352,7 +352,7 @@ def test_convert_output_error(test_db_and_path, options, expected_error):
@pytest.mark.parametrize("drop", (True, False))
def test_convert_multi(fresh_db_and_path, drop):
db, db_path = fresh_db_and_path
db["creatures"].insert_all(
db.table("creatures").insert_all(
[
{"id": 1, "name": "Simon"},
{"id": 2, "name": "Cleo"},
@ -378,12 +378,12 @@ def test_convert_multi(fresh_db_and_path, drop):
if drop:
for row in expected:
del row["name"]
assert list(db["creatures"].rows) == expected
assert list(db.table("creatures").rows) == expected
def test_convert_multi_complex_column_types(fresh_db_and_path):
db, db_path = fresh_db_and_path
db["rows"].insert_all(
db.table("rows").insert_all(
[
{"id": 1},
{"id": 2},
@ -412,7 +412,7 @@ def test_convert_multi_complex_column_types(fresh_db_and_path):
],
)
assert result.exit_code == 0, result.output
assert list(db["rows"].rows) == [
assert list(db.table("rows").rows) == [
{"id": 1, "is_str": "", "is_float": 1.2, "is_int": None, "is_bytes": None},
{"id": 2, "is_str": None, "is_float": 1.0, "is_int": 12, "is_bytes": None},
{
@ -424,7 +424,7 @@ def test_convert_multi_complex_column_types(fresh_db_and_path):
},
{"id": 4, "is_str": None, "is_float": None, "is_int": None, "is_bytes": None},
]
assert db["rows"].schema == (
assert db.table("rows").schema == (
'CREATE TABLE "rows" (\n'
' "id" INTEGER PRIMARY KEY\n'
', "is_str" TEXT, "is_float" REAL, "is_int" INTEGER, "is_bytes" BLOB)'
@ -435,7 +435,7 @@ def test_convert_multi_complex_column_types(fresh_db_and_path):
def test_recipe_jsonsplit(tmpdir, delimiter):
db_path = str(pathlib.Path(tmpdir) / "data.db")
db = sqlite_utils.Database(db_path)
db["example"].insert_all(
db.table("example").insert_all(
[
{"id": 1, "tags": (delimiter or ",").join(["foo", "bar"])},
{"id": 2, "tags": (delimiter or ",").join(["bar", "baz"])},
@ -448,7 +448,7 @@ def test_recipe_jsonsplit(tmpdir, delimiter):
args = ["convert", db_path, "example", "tags", code]
result = CliRunner().invoke(cli.cli, args)
assert result.exit_code == 0, result.output
assert list(db["example"].rows) == [
assert list(db.table("example").rows) == [
{"id": 1, "tags": '["foo", "bar"]'},
{"id": 2, "tags": '["bar", "baz"]'},
]
@ -464,7 +464,7 @@ def test_recipe_jsonsplit(tmpdir, delimiter):
)
def test_recipe_jsonsplit_type(fresh_db_and_path, type, expected_array):
db, db_path = fresh_db_and_path
db["example"].insert_all(
db.table("example").insert_all(
[
{"id": 1, "records": "1,2,3"},
],
@ -476,13 +476,13 @@ def test_recipe_jsonsplit_type(fresh_db_and_path, type, expected_array):
args = ["convert", db_path, "example", "records", code]
result = CliRunner().invoke(cli.cli, args)
assert result.exit_code == 0, result.output
assert json.loads(db["example"].get(1)["records"]) == expected_array
assert json.loads(db.table("example").get(1)["records"]) == expected_array
@pytest.mark.parametrize("drop", (True, False))
def test_recipe_jsonsplit_output(fresh_db_and_path, drop):
db, db_path = fresh_db_and_path
db["example"].insert_all(
db.table("example").insert_all(
[
{"id": 1, "records": "1,2,3"},
],
@ -501,7 +501,7 @@ def test_recipe_jsonsplit_output(fresh_db_and_path, drop):
}
if drop:
del expected["records"]
assert db["example"].get(1) == expected
assert db.table("example").get(1) == expected
def test_cannot_use_drop_without_multi_or_output(fresh_db_and_path):
@ -558,7 +558,7 @@ def test_convert_where(test_db_and_path):
],
)
assert result.exit_code == 0, result.output
assert list(db["example"].rows) == [
assert list(db.table("example").rows) == [
{"id": 1, "dt": "5th October 2019 12:04"},
{"id": 2, "dt": "6TH OCTOBER 2019 00:05:06"},
{"id": 3, "dt": ""},
@ -568,7 +568,7 @@ def test_convert_where(test_db_and_path):
def test_convert_where_multi(fresh_db_and_path):
db, db_path = fresh_db_and_path
db["names"].insert_all(
db.table("names").insert_all(
[{"id": 1, "name": "Cleo"}, {"id": 2, "name": "Bants"}], pk="id"
)
result = CliRunner().invoke(
@ -588,7 +588,7 @@ def test_convert_where_multi(fresh_db_and_path):
],
)
assert result.exit_code == 0, result.output
assert list(db["names"].rows) == [
assert list(db.table("names").rows) == [
{"id": 1, "name": "Cleo", "upper": None},
{"id": 2, "name": "Bants", "upper": "BANTS"},
]
@ -596,7 +596,7 @@ def test_convert_where_multi(fresh_db_and_path):
def test_convert_code_standard_input(fresh_db_and_path):
db, db_path = fresh_db_and_path
db["names"].insert_all([{"id": 1, "name": "Cleo"}], pk="id")
db.table("names").insert_all([{"id": 1, "name": "Cleo"}], pk="id")
result = CliRunner().invoke(
cli.cli,
[
@ -609,27 +609,27 @@ def test_convert_code_standard_input(fresh_db_and_path):
input="value.upper()",
)
assert result.exit_code == 0, result.output
assert list(db["names"].rows) == [
assert list(db.table("names").rows) == [
{"id": 1, "name": "CLEO"},
]
def test_convert_hyphen_workaround(fresh_db_and_path):
db, db_path = fresh_db_and_path
db["names"].insert_all([{"id": 1, "name": "Cleo"}], pk="id")
db.table("names").insert_all([{"id": 1, "name": "Cleo"}], pk="id")
result = CliRunner().invoke(
cli.cli,
["convert", db_path, "names", "name", '"-"'],
)
assert result.exit_code == 0, result.output
assert list(db["names"].rows) == [
assert list(db.table("names").rows) == [
{"id": 1, "name": "-"},
]
def test_convert_initialization_pattern(fresh_db_and_path):
db, db_path = fresh_db_and_path
db["names"].insert_all([{"id": 1, "name": "Cleo"}], pk="id")
db.table("names").insert_all([{"id": 1, "name": "Cleo"}], pk="id")
result = CliRunner().invoke(
cli.cli,
[
@ -642,7 +642,7 @@ def test_convert_initialization_pattern(fresh_db_and_path):
input="import random\nrandom.seed(1)\ndef convert(value): return random.randint(0, 100)",
)
assert result.exit_code == 0, result.output
assert list(db["names"].rows) == [
assert list(db.table("names").rows) == [
{"id": 1, "name": "17"},
]
@ -657,13 +657,13 @@ def test_convert_handles_falsey_values(fresh_db_and_path):
"x",
"-",
]
db["t"].insert_all([{"x": 0}, {"x": 1}])
assert db["t"].get(1)["x"] == 0
assert db["t"].get(2)["x"] == 1
db.table("t").insert_all([{"x": 0}, {"x": 1}])
assert db.table("t").get(1)["x"] == 0
assert db.table("t").get(2)["x"] == 1
result = CliRunner().invoke(cli.cli, args, input="value + 1")
assert result.exit_code == 0, result.output
assert db["t"].get(1)["x"] == 1
assert db["t"].get(2)["x"] == 2
assert db.table("t").get(1)["x"] == 1
assert db.table("t").get(2)["x"] == 2
@pytest.mark.parametrize(
@ -684,7 +684,7 @@ def test_convert_callable_reference(test_db_and_path, code):
cli.cli, ["convert", db_path, "example", "dt", code], catch_exceptions=False
)
assert result.exit_code == 0, result.output
rows = list(db["example"].rows)
rows = list(db.table("example").rows)
assert rows[0]["dt"] == "2019-10-05"
assert rows[1]["dt"] == "2019-10-06"
assert rows[2]["dt"] == ""
@ -694,7 +694,7 @@ def test_convert_callable_reference(test_db_and_path, code):
def test_convert_callable_reference_with_import(fresh_db_and_path):
"""Test callable reference from an imported module"""
db, db_path = fresh_db_and_path
db["example"].insert({"id": 1, "data": '{"name": "test"}'})
db.table("example").insert({"id": 1, "data": '{"name": "test"}'})
result = CliRunner().invoke(
cli.cli,
[
@ -710,5 +710,5 @@ def test_convert_callable_reference_with_import(fresh_db_and_path):
)
assert result.exit_code == 0, result.output
# json.loads returns a dict, which sqlite stores as JSON string
row = db["example"].get(1)
row = db.table("example").get(1)
assert row["data"] == '{"name": "test"}'

View file

@ -21,7 +21,7 @@ def test_insert_simple(tmpdir):
)
db = Database(db_path)
assert ["dogs"] == db.table_names()
assert [] == db["dogs"].indexes
assert [] == db.table("dogs").indexes
def test_insert_from_stdin(tmpdir):
@ -96,7 +96,7 @@ def test_insert_with_primary_keys(db_path, tmpdir, args, expected_pks):
Database(db_path).query("select * from dogs")
)
db = Database(db_path)
assert db["dogs"].pks == expected_pks
assert db.table("dogs").pks == expected_pks
def test_insert_multiple_with_primary_key(db_path, tmpdir):
@ -110,7 +110,7 @@ def test_insert_multiple_with_primary_key(db_path, tmpdir):
assert result.exit_code == 0
db = Database(db_path)
assert dogs == list(db.query("select * from dogs order by id"))
assert ["id"] == db["dogs"].pks
assert ["id"] == db.table("dogs").pks
def test_insert_multiple_with_compound_primary_key(db_path, tmpdir):
@ -127,7 +127,7 @@ def test_insert_multiple_with_compound_primary_key(db_path, tmpdir):
assert result.exit_code == 0
db = Database(db_path)
assert dogs == list(db.query("select * from dogs order by breed, id"))
assert {"breed", "id"} == set(db["dogs"].pks)
assert {"breed", "id"} == set(db.table("dogs").pks)
assert (
'CREATE TABLE "dogs" (\n'
' "breed" TEXT,\n'
@ -136,7 +136,7 @@ def test_insert_multiple_with_compound_primary_key(db_path, tmpdir):
' "age" INTEGER,\n'
' PRIMARY KEY ("id", "breed")\n'
")"
) == db["dogs"].schema
) == db.table("dogs").schema
def test_insert_not_null_default(db_path, tmpdir):
@ -160,7 +160,7 @@ def test_insert_not_null_default(db_path, tmpdir):
' "name" TEXT NOT NULL,\n'
" \"age\" INTEGER NOT NULL DEFAULT '1',\n"
" \"score\" INTEGER DEFAULT '5'\n)"
) == db["dogs"].schema
) == db.table("dogs").schema
def test_insert_binary_base64(db_path):
@ -191,7 +191,7 @@ def test_insert_newline_delimited(db_path):
def test_insert_ignore(db_path, tmpdir):
db = Database(db_path)
db["dogs"].insert({"id": 1, "name": "Cleo"}, pk="id")
db.table("dogs").insert({"id": 1, "name": "Cleo"}, pk="id")
json_path = str(tmpdir / "dogs.json")
with open(json_path, "w") as fp:
fp.write(json.dumps([{"id": 1, "name": "Bailey"}]))
@ -232,7 +232,7 @@ def test_insert_csv_tsv(content, options, db_path, tmpdir):
catch_exceptions=False,
)
assert result.exit_code == 0
assert [{"foo": "1", "bar": "2", "baz": "cat,dog"}] == list(db["data"].rows)
assert [{"foo": "1", "bar": "2", "baz": "cat,dog"}] == list(db.table("data").rows)
@pytest.mark.parametrize("empty_null", (True, False))
@ -248,7 +248,7 @@ def test_insert_csv_empty_null(db_path, empty_null):
)
assert result.exit_code == 0
db = Database(db_path)
assert [r for r in db["data"].rows] == [
assert [r for r in db.table("data").rows] == [
{"foo": "1", "bar": None if empty_null else "", "baz": "cat"}
]
@ -302,7 +302,7 @@ def test_insert_replace(db_path, tmpdir):
test_insert_multiple_with_primary_key(db_path, tmpdir)
json_path = str(tmpdir / "insert-replace.json")
db = Database(db_path)
assert db["dogs"].count == 20
assert db.table("dogs").count == 20
insert_replace_dogs = [
{"id": 1, "name": "Insert replaced 1", "age": 4},
{"id": 2, "name": "Insert replaced 2", "age": 4},
@ -314,7 +314,7 @@ def test_insert_replace(db_path, tmpdir):
cli.cli, ["insert", db_path, "dogs", json_path, "--pk", "id", "--replace"]
)
assert result.exit_code == 0, result.output
assert db["dogs"].count == 21
assert db.table("dogs").count == 21
assert (
list(db.query("select * from dogs where id in (1, 2, 21) order by id"))
== insert_replace_dogs
@ -377,7 +377,7 @@ def test_insert_alter(db_path, tmpdir):
assert result.exit_code == 0, result.output
# Soundness check the database itself
db = Database(db_path)
assert {"foo": str, "n": int, "baz": int} == db["from_json_nl"].columns_dict
assert {"foo": str, "n": int, "baz": int} == db.table("from_json_nl").columns_dict
assert [
{"foo": "bar", "n": 1, "baz": None},
{"foo": "baz", "n": 2, "baz": None},
@ -387,8 +387,8 @@ def test_insert_alter(db_path, tmpdir):
def test_insert_analyze(db_path):
db = Database(db_path)
db["rows"].insert({"foo": "x", "n": 3})
db["rows"].create_index(["n"])
db.table("rows").insert({"foo": "x", "n": 3})
db.table("rows").create_index(["n"])
assert "sqlite_stat1" not in db.table_names()
result = CliRunner().invoke(
cli.cli,
@ -583,7 +583,7 @@ def test_insert_streaming_batch_size_1(db_path):
def try_until(expected):
tries = 0
while True:
rows = list(Database(db_path)["rows"].rows)
rows = list(Database(db_path).table("rows").rows)
if rows == expected:
return
tries += 1
@ -615,13 +615,13 @@ def test_insert_csv_headers_only(tmpdir):
assert result.exit_code == 0
# Table should not exist since there were no data rows
db = Database(db_path)
assert not db["data"].exists()
assert not db.table("data").exists()
def test_insert_into_view_errors(tmpdir):
db_path = str(tmpdir / "test.db")
db = Database(db_path)
db["t"].insert({"id": 1})
db.table("t").insert({"id": 1})
db.create_view("v", "select * from t")
db.close()
result = CliRunner().invoke(
@ -637,7 +637,7 @@ def test_insert_csv_detect_types_leaves_existing_table_alone(db_path):
# table would rewrite its column types and corrupt data such as
# TEXT zip codes with leading zeros
db = Database(db_path)
db["places"].insert({"name": "Boston", "zip": "01234"})
db.table("places").insert({"name": "Boston", "zip": "01234"})
result = CliRunner().invoke(
cli.cli,
["insert", db_path, "places", "-", "--csv"],
@ -645,8 +645,8 @@ def test_insert_csv_detect_types_leaves_existing_table_alone(db_path):
input="name,zip\nSF,94107",
)
assert result.exit_code == 0, result.output
assert db["places"].columns_dict["zip"] is str
assert list(db["places"].rows) == [
assert db.table("places").columns_dict["zip"] is str
assert list(db.table("places").rows) == [
{"name": "Boston", "zip": "01234"},
{"name": "SF", "zip": "94107"},
]
@ -662,7 +662,7 @@ def test_insert_csv_detect_types_new_table(db_path):
)
assert result.exit_code == 0, result.output
db = Database(db_path)
assert db["data"].columns_dict == {"name": str, "age": int, "weight": float}
assert db.table("data").columns_dict == {"name": str, "age": int, "weight": float}
@pytest.mark.parametrize(
@ -708,13 +708,13 @@ def test_insert_upsert_csv_type_overrides_detected_types(
expected_columns = {"zipcode": str, "score": float}
if command == "upsert":
expected_columns = {"id": int, **expected_columns}
assert db["places"].columns_dict == expected_columns
assert list(db["places"].rows) == [expected_row]
assert db.table("places").columns_dict == expected_columns
assert list(db.table("places").rows) == [expected_row]
def test_upsert_csv_detect_types_leaves_existing_table_alone(db_path):
db = Database(db_path)
db["places"].insert({"id": 1, "name": "Boston", "zip": "01234"}, pk="id")
db.table("places").insert({"id": 1, "name": "Boston", "zip": "01234"}, pk="id")
result = CliRunner().invoke(
cli.cli,
["upsert", db_path, "places", "-", "--csv", "--pk", "id"],
@ -722,15 +722,15 @@ def test_upsert_csv_detect_types_leaves_existing_table_alone(db_path):
input="id,name,zip\n2,SF,94107",
)
assert result.exit_code == 0, result.output
assert db["places"].columns_dict["zip"] is str
assert db["places"].get(1)["zip"] == "01234"
assert db.table("places").columns_dict["zip"] is str
assert db.table("places").get(1)["zip"] == "01234"
def test_insert_invalid_pk_clean_error(db_path):
# An invalid --pk against an existing table should be a clean CLI
# error, not a raw InvalidColumns traceback
db = Database(db_path)
db["t"].insert({"a": 1})
db.table("t").insert({"a": 1})
result = CliRunner().invoke(
cli.cli,
["insert", db_path, "t", "-", "--pk", "badcol"],
@ -765,8 +765,8 @@ def test_insert_code(tmpdir, code):
)
assert result.exit_code == 0, result.output
db = Database(db_path)
assert db["creatures"].pks == ["id"]
assert list(db["creatures"].rows) == [
assert db.table("creatures").pks == ["id"]
assert list(db.table("creatures").rows) == [
{"id": 1, "name": "Cleo"},
{"id": 2, "name": "Suna"},
]
@ -782,7 +782,7 @@ def test_insert_code_from_file(tmpdir):
["insert", db_path, "creatures", "--code", code_path],
)
assert result.exit_code == 0, result.output
assert list(Database(db_path)["creatures"].rows) == [
assert list(Database(db_path).table("creatures").rows) == [
{"id": 1, "name": "Cleo"},
{"id": 2, "name": "Suna"},
]
@ -791,7 +791,7 @@ def test_insert_code_from_file(tmpdir):
def test_upsert_code(tmpdir):
db_path = str(tmpdir / "dogs.db")
db = Database(db_path)
db["creatures"].insert_all(
db.table("creatures").insert_all(
[{"id": 1, "name": "old"}, {"id": 2, "name": "Suna"}], pk="id"
)
result = CliRunner().invoke(
@ -799,7 +799,7 @@ def test_upsert_code(tmpdir):
["upsert", db_path, "creatures", "--code", CODE_ROWS_FUNCTION, "--pk", "id"],
)
assert result.exit_code == 0, result.output
assert list(db["creatures"].rows) == [
assert list(db.table("creatures").rows) == [
{"id": 1, "name": "Cleo"},
{"id": 2, "name": "Suna"},
]
@ -858,7 +858,9 @@ def test_insert_code_single_dict(tmpdir):
],
)
assert result.exit_code == 0, result.output
assert list(Database(db_path)["creatures"].rows) == [{"id": 1, "name": "Cleo"}]
assert list(Database(db_path).table("creatures").rows) == [
{"id": 1, "name": "Cleo"}
]
def test_insert_code_not_iterable(tmpdir):

View file

@ -228,7 +228,7 @@ def test_memory_save(tmpdir, extra_args):
)
assert result.exit_code == 0
db = Database(save_to)
assert list(db["stdin"].rows) == [
assert list(db.table("stdin").rows) == [
{"id": 1, "name": "Cleo"},
{"id": 2, "name": "Bants"},
]

View file

@ -13,11 +13,11 @@ m = Migrations("hello")
@m()
def foo(db):
db["foo"].insert({"hello": "world"})
db.table("foo").insert({"hello": "world"})
@m()
def bar(db):
db["bar"].insert({"hello": "world"})
db.table("bar").insert({"hello": "world"})
"""
@ -42,21 +42,21 @@ creatures = Migrations("creatures")
@creatures()
def create_table(db):
db["creatures"].insert({"name": "Cleo"})
db.table("creatures").insert({"name": "Cleo"})
@creatures()
def add_weight(db):
db["creature_weights"].insert({"weight": 4.2})
db.table("creature_weights").insert({"weight": 4.2})
sales = Migrations("sales")
@sales()
def create_table(db):
db["sales"].insert({"id": 1})
db.table("sales").insert({"id": 1})
@sales()
def add_weight(db):
db["sales_weights"].insert({"weight": 10})
db.table("sales_weights").insert({"weight": 10})
""",
"utf-8",
)
@ -99,10 +99,10 @@ def test_basic(two_migrations, arg):
assert " Pending:\n (none)" in list_output
db = sqlite_utils.Database(db_path)
assert db["foo"].exists()
assert db["bar"].exists()
assert db["_sqlite_migrations"].exists()
rows = list(db["_sqlite_migrations"].rows)
assert db.table("foo").exists()
assert db.table("bar").exists()
assert db.table("_sqlite_migrations").exists()
rows = list(db.table("_sqlite_migrations").rows)
assert len(rows) == 2
assert rows[0]["name"] == "foo"
assert rows[1]["name"] == "bar"
@ -113,13 +113,13 @@ def test_list_same_migration_names_in_different_sets(capsys):
@applied(name="foo")
def applied_foo(db):
db["applied"].insert({"hello": "world"})
db.table("applied").insert({"hello": "world"})
pending = sqlite_utils.Migrations("pending")
@pending(name="foo")
def pending_foo(db):
db["pending"].insert({"hello": "world"})
db.table("pending").insert({"hello": "world"})
db = sqlite_utils.Database(memory=True)
applied.apply(db)
@ -144,7 +144,7 @@ m = Migrations("hello")
@m()
def foo(db):
db["dogs"].insert({"id": 1, "name": "Cleo"})
db.table("dogs").insert({"id": 1, "name": "Cleo"})
""",
"utf-8",
)
@ -184,9 +184,9 @@ Schema after:
new_migration = """
@m()
def bar(db):
db["dogs"].add_column("age", int)
db["dogs"].add_column("weight", float)
db["dogs"].transform()
db.table("dogs").add_column("age", int)
db.table("dogs").add_column("weight", float)
db.table("dogs").transform()
"""
migrations_py.write_text(migrations_py.read_text("utf-8") + new_migration)
@ -224,8 +224,8 @@ def test_stop_before(two_migrations):
)
assert result.exit_code == 0
db = sqlite_utils.Database(db_path)
assert db["foo"].exists()
assert not db["bar"].exists()
assert db.table("foo").exists()
assert not db.table("bar").exists()
def test_stop_before_multiple_sets_unqualified(two_migrations):
@ -239,7 +239,7 @@ m = Migrations("hello2")
@m()
def foo(db):
db["foo"].insert({"hello": "world"})
db.table("foo").insert({"hello": "world"})
""",
"utf-8",
)
@ -257,7 +257,7 @@ def foo(db):
assert result.exit_code == 0, result.output
db = sqlite_utils.Database(db_path)
assert db.table_names() == ["_sqlite_migrations"]
assert list(db["_sqlite_migrations"].rows) == []
assert list(db.table("_sqlite_migrations").rows) == []
def test_stop_before_qualified_only_affects_named_set(two_sets_same_migration_name):
@ -275,10 +275,10 @@ def test_stop_before_qualified_only_affects_named_set(two_sets_same_migration_na
)
assert result.exit_code == 0, result.output
db = sqlite_utils.Database(db_path)
assert db["creatures"].exists()
assert not db["creature_weights"].exists()
assert db["sales"].exists()
assert db["sales_weights"].exists()
assert db.table("creatures").exists()
assert not db.table("creature_weights").exists()
assert db.table("sales").exists()
assert db.table("sales_weights").exists()
def test_stop_before_multiple_qualified(two_sets_same_migration_name):
@ -298,10 +298,10 @@ def test_stop_before_multiple_qualified(two_sets_same_migration_name):
)
assert result.exit_code == 0, result.output
db = sqlite_utils.Database(db_path)
assert db["creatures"].exists()
assert not db["creature_weights"].exists()
assert db["sales"].exists()
assert not db["sales_weights"].exists()
assert db.table("creatures").exists()
assert not db.table("creature_weights").exists()
assert db.table("sales").exists()
assert not db.table("sales_weights").exists()
LEGACY_MIGRATIONS = """
@ -331,7 +331,7 @@ class LegacyMigrations:
return fn
def ensure_migrations_table(self, db):
db[self.migrations_table].create(
db.table(self.migrations_table).create(
{"migration_set": str, "name": str, "applied_at": str},
pk=("migration_set", "name"),
if_not_exists=True,
@ -341,7 +341,7 @@ class LegacyMigrations:
self.ensure_migrations_table(db)
return [
_Applied(row["name"], row["applied_at"])
for row in db[self.migrations_table].rows_where(
for row in db.table(self.migrations_table).rows_where(
"migration_set = ?", [self.name]
)
]
@ -355,7 +355,7 @@ class LegacyMigrations:
if migration.name == stop_before:
return
migration.fn(db)
db[self.migrations_table].insert(
db.table(self.migrations_table).insert(
{
"migration_set": self.name,
"name": migration.name,
@ -369,11 +369,11 @@ legacy = LegacyMigrations("legacy_set")
@legacy
def first(db):
db["first"].insert({"hello": "world"})
db.table("first").insert({"hello": "world"})
@legacy
def second(db):
db["second"].insert({"hello": "world"})
db.table("second").insert({"hello": "world"})
"""
@ -446,11 +446,11 @@ def test_list_does_not_upgrade_legacy_migrations_table(two_migrations):
path, _ = two_migrations
db_path = str(path / "test.db")
db = sqlite_utils.Database(db_path)
db["_sqlite_migrations"].create(
db.table("_sqlite_migrations").create(
{"migration_set": str, "name": str, "applied_at": str},
pk=("migration_set", "name"),
)
db["_sqlite_migrations"].insert(
db.table("_sqlite_migrations").insert(
{"migration_set": "hello", "name": "foo", "applied_at": "x"}
)
db.close()
@ -462,7 +462,7 @@ def test_list_does_not_upgrade_legacy_migrations_table(two_migrations):
assert "foo - x" in result.output
# --list must not perform the one-way legacy schema upgrade
db2 = sqlite_utils.Database(db_path)
assert db2["_sqlite_migrations"].pks == ["migration_set", "name"]
assert db2.table("_sqlite_migrations").pks == ["migration_set", "name"]
db2.close()
@ -485,7 +485,7 @@ def test_stop_before_applied_migration_errors(two_migrations):
assert result.exit_code != 0
assert "already been applied" in result.output
db = sqlite_utils.Database(db_path)
assert not db["bar"].exists()
assert not db.table("bar").exists()
def test_list_with_legacy_class_is_read_only(tmpdir):
@ -496,7 +496,7 @@ def test_list_with_legacy_class_is_read_only(tmpdir):
(path / "migrations.py").write_text(LEGACY_MIGRATIONS, "utf-8")
db_path = str(path / "test.db")
db = sqlite_utils.Database(db_path)
db["existing"].insert({"id": 1})
db.table("existing").insert({"id": 1})
db.close()
result = CliRunner().invoke(
sqlite_utils.cli.cli, ["migrate", db_path, str(path), "--list"]

View file

@ -43,4 +43,4 @@ def test_column_affinity(column_def, expected_type):
@pytest.mark.parametrize("column_def,expected_type", EXAMPLES)
def test_columns_dict(fresh_db, column_def, expected_type):
fresh_db.execute(f"create table foo (col {column_def})")
assert {"col": expected_type} == fresh_db["foo"].columns_dict
assert {"col": expected_type} == fresh_db.table("foo").columns_dict

View file

@ -13,14 +13,14 @@ from sqlite_utils.db import ForeignKey
def test_insert_populates_last_pk_case_insensitively(fresh_db):
books = fresh_db["books"]
books = fresh_db.table("books")
books.create({"Id": int, "Title": str}, pk="Id")
books.insert({"Id": 1, "Title": "One"}, pk="id")
assert books.last_pk == 1
def test_insert_populates_last_pk_compound_pk_case_insensitively(fresh_db):
books = fresh_db["books"]
books = fresh_db.table("books")
books.create({"Author": str, "Position": int, "Title": str})
books.insert(
{"Author": "Sue", "Position": 1, "Title": "One"}, pk=("author", "position")
@ -31,7 +31,7 @@ def test_insert_populates_last_pk_compound_pk_case_insensitively(fresh_db):
@pytest.mark.parametrize("use_old_upsert", (False, True))
def test_upsert_pk_case_differs_from_schema(use_old_upsert):
db = Database(memory=True, use_old_upsert=use_old_upsert)
books = db["books"]
books = db.table("books")
books.create({"Id": int, "Title": str}, pk="Id")
books.insert({"Id": 1, "Title": "One"})
books.upsert({"id": 1, "title": "Won"}, pk="id")
@ -43,7 +43,7 @@ def test_upsert_pk_case_differs_from_schema(use_old_upsert):
def test_upsert_record_key_case_differs_from_pk(use_old_upsert):
# all_columns comes from the record keys, pk= from the caller
db = Database(memory=True, use_old_upsert=use_old_upsert)
books = db["books"]
books = db.table("books")
books.create({"Id": int, "Title": str}, pk="Id")
books.upsert({"ID": 1, "Title": "One"}, pk="id")
assert list(books.rows) == [{"Id": 1, "Title": "One"}]
@ -52,7 +52,7 @@ def test_upsert_record_key_case_differs_from_pk(use_old_upsert):
def test_upsert_inferred_pk_case_differs_from_record_keys(fresh_db):
# pk is inferred from the existing schema as "Id", records use "id"
books = fresh_db["books"]
books = fresh_db.table("books")
books.create({"Id": int, "Title": str}, pk="Id")
books.upsert({"id": 1, "title": "One"})
assert list(books.rows) == [{"Id": 1, "Title": "One"}]
@ -60,7 +60,7 @@ def test_upsert_inferred_pk_case_differs_from_record_keys(fresh_db):
def test_upsert_list_mode_pk_case_insensitive(fresh_db):
books = fresh_db["books"]
books = fresh_db.table("books")
books.create({"Id": int, "Title": str}, pk="Id")
books.upsert_all([["id", "title"], [1, "One"]], pk="Id")
assert list(books.rows) == [{"Id": 1, "Title": "One"}]
@ -68,84 +68,84 @@ def test_upsert_list_mode_pk_case_insensitive(fresh_db):
def test_lookup_pk_case_insensitive(fresh_db):
fresh_db["species"].create({"ID": int, "Name": str}, pk="ID")
fresh_db["species"].insert({"ID": 5, "Name": "Palm"})
fresh_db["species"].create_index(["Name"], unique=True)
assert fresh_db["species"].lookup({"Name": "Palm"}, pk="id") == 5
fresh_db.table("species").create({"ID": int, "Name": str}, pk="ID")
fresh_db.table("species").insert({"ID": 5, "Name": "Palm"})
fresh_db.table("species").create_index(["Name"], unique=True)
assert fresh_db.table("species").lookup({"Name": "Palm"}, pk="id") == 5
def test_lookup_does_not_create_redundant_index(fresh_db):
fresh_db["species"].create({"id": int, "Name": str}, pk="id")
fresh_db["species"].create_index(["Name"], unique=True)
fresh_db["species"].lookup({"name": "Palm"})
assert len(fresh_db["species"].indexes) == 1
fresh_db.table("species").create({"id": int, "Name": str}, pk="id")
fresh_db.table("species").create_index(["Name"], unique=True)
fresh_db.table("species").lookup({"name": "Palm"})
assert len(fresh_db.table("species").indexes) == 1
def test_create_table_transform_same_columns_different_case(fresh_db):
fresh_db["t"].create({"Name": str, "Age": int})
fresh_db["t"].insert({"Name": "Cleo", "Age": 5})
fresh_db.table("t").create({"Name": str, "Age": int})
fresh_db.table("t").insert({"Name": "Cleo", "Age": 5})
fresh_db.create_table("t", {"name": str, "age": int}, transform=True)
# Schema casing is preserved - SQLite considers these the same columns
assert fresh_db["t"].columns_dict == {"Name": str, "Age": int}
assert list(fresh_db["t"].rows) == [{"Name": "Cleo", "Age": 5}]
assert fresh_db.table("t").columns_dict == {"Name": str, "Age": int}
assert list(fresh_db.table("t").rows) == [{"Name": "Cleo", "Age": 5}]
def test_create_table_transform_case_insensitive_with_changes(fresh_db):
fresh_db["t"].create({"Name": str, "Age": int})
fresh_db.table("t").create({"Name": str, "Age": int})
fresh_db.create_table("t", {"name": str, "age": str, "size": int}, transform=True)
# age changed type, size added, Name untouched
assert fresh_db["t"].columns_dict == {"Name": str, "Age": str, "size": int}
assert fresh_db.table("t").columns_dict == {"Name": str, "Age": str, "size": int}
def test_transform_types_case_insensitive(fresh_db):
fresh_db["t"].create({"Name": str, "Age": str})
fresh_db["t"].transform(types={"age": int})
assert fresh_db["t"].columns_dict == {"Name": str, "Age": int}
fresh_db.table("t").create({"Name": str, "Age": str})
fresh_db.table("t").transform(types={"age": int})
assert fresh_db.table("t").columns_dict == {"Name": str, "Age": int}
def test_transform_rename_case_insensitive(fresh_db):
fresh_db["t"].create({"Name": str})
fresh_db["t"].transform(rename={"name": "title"})
assert fresh_db["t"].columns_dict == {"title": str}
fresh_db.table("t").create({"Name": str})
fresh_db.table("t").transform(rename={"name": "title"})
assert fresh_db.table("t").columns_dict == {"title": str}
def test_transform_drop_case_insensitive(fresh_db):
fresh_db["t"].create({"Name": str, "Age": int})
fresh_db["t"].transform(drop=["name"])
assert fresh_db["t"].columns_dict == {"Age": int}
fresh_db.table("t").create({"Name": str, "Age": int})
fresh_db.table("t").transform(drop=["name"])
assert fresh_db.table("t").columns_dict == {"Age": int}
def test_transform_not_null_and_defaults_case_insensitive(fresh_db):
fresh_db["t"].create({"Name": str, "Age": int})
fresh_db["t"].transform(not_null={"name"}, defaults={"age": 3})
columns = {c.name: c for c in fresh_db["t"].columns}
fresh_db.table("t").create({"Name": str, "Age": int})
fresh_db.table("t").transform(not_null={"name"}, defaults={"age": 3})
columns = {c.name: c for c in fresh_db.table("t").columns}
assert columns["Name"].notnull
assert fresh_db["t"].default_values == {"Age": 3}
assert fresh_db.table("t").default_values == {"Age": 3}
def test_transform_pk_case_insensitive(fresh_db):
fresh_db["t"].create({"Id": int, "Name": str})
fresh_db["t"].transform(pk="id")
assert fresh_db["t"].pks == ["Id"]
assert fresh_db["t"].columns_dict == {"Id": int, "Name": str}
fresh_db.table("t").create({"Id": int, "Name": str})
fresh_db.table("t").transform(pk="id")
assert fresh_db.table("t").pks == ["Id"]
assert fresh_db.table("t").columns_dict == {"Id": int, "Name": str}
def test_transform_drop_foreign_keys_case_insensitive(fresh_db):
fresh_db["parent"].create({"Id": int}, pk="Id")
fresh_db["child"].create(
fresh_db.table("parent").create({"Id": int}, pk="Id")
fresh_db.table("child").create(
{"id": int, "Parent_ID": int},
pk="id",
foreign_keys=[("Parent_ID", "parent", "Id")],
)
fresh_db["child"].transform(drop_foreign_keys=["parent_id"])
assert fresh_db["child"].foreign_keys == []
fresh_db.table("child").transform(drop_foreign_keys=["parent_id"])
assert fresh_db.table("child").foreign_keys == []
def test_add_foreign_key_case_insensitive(fresh_db):
fresh_db["parent"].create({"Id": int}, pk="Id")
fresh_db["child"].create({"id": int, "Parent_ID": int}, pk="id")
fresh_db["child"].add_foreign_key("parent_id", "parent", "id")
fks = fresh_db["child"].foreign_keys
fresh_db.table("parent").create({"Id": int}, pk="Id")
fresh_db.table("child").create({"id": int, "Parent_ID": int}, pk="id")
fresh_db.table("child").add_foreign_key("parent_id", "parent", "id")
fks = fresh_db.table("child").foreign_keys
assert len(fks) == 1
# The foreign key should use the schema casing of the columns
assert fks[0].column == "Parent_ID"
@ -153,79 +153,83 @@ def test_add_foreign_key_case_insensitive(fresh_db):
def test_add_foreign_keys_case_insensitive(fresh_db):
fresh_db["parent"].create({"Id": int}, pk="Id")
fresh_db["child"].create({"id": int, "Parent_ID": int}, pk="id")
fresh_db.table("parent").create({"Id": int}, pk="Id")
fresh_db.table("child").create({"id": int, "Parent_ID": int}, pk="id")
fresh_db.add_foreign_keys([("child", "parent_id", "parent", "id")])
fks = fresh_db["child"].foreign_keys
fks = fresh_db.table("child").foreign_keys
assert len(fks) == 1
assert fks[0].column == "Parent_ID"
assert fks[0].other_column == "Id"
def test_add_foreign_key_detects_existing_case_insensitively(fresh_db):
fresh_db["parent"].create({"Id": int}, pk="Id")
fresh_db["child"].create(
fresh_db.table("parent").create({"Id": int}, pk="Id")
fresh_db.table("child").create(
{"id": int, "Parent_ID": int},
pk="id",
foreign_keys=[("Parent_ID", "parent", "Id")],
)
# ignore=True should treat this as already existing, not add a duplicate
fresh_db["child"].add_foreign_key("parent_id", "parent", "id", ignore=True)
assert len(fresh_db["child"].foreign_keys) == 1
fresh_db.table("child").add_foreign_key("parent_id", "parent", "id", ignore=True)
assert len(fresh_db.table("child").foreign_keys) == 1
def test_add_column_fk_col_case_insensitive(fresh_db):
fresh_db["parent"].create({"Id": int}, pk="Id")
fresh_db["child"].create({"id": int}, pk="id")
fresh_db["child"].add_column("parent_id", int, fk="parent", fk_col="id")
fks = fresh_db["child"].foreign_keys
fresh_db.table("parent").create({"Id": int}, pk="Id")
fresh_db.table("child").create({"id": int}, pk="id")
fresh_db.table("child").add_column("parent_id", int, fk="parent", fk_col="id")
fks = fresh_db.table("child").foreign_keys
assert len(fks) == 1
assert fks[0].other_column == "Id"
def test_extract_case_insensitive(fresh_db):
fresh_db["trees"].insert({"id": 1, "Species": "Palm"}, pk="id")
fresh_db["trees"].extract("species")
assert fresh_db["trees"].columns_dict == {"id": int, "Species_id": int}
assert list(fresh_db["Species"].rows) == [{"id": 1, "Species": "Palm"}]
fresh_db.table("trees").insert({"id": 1, "Species": "Palm"}, pk="id")
fresh_db.table("trees").extract("species")
assert fresh_db.table("trees").columns_dict == {"id": int, "Species_id": int}
assert list(fresh_db.table("Species").rows) == [{"id": 1, "Species": "Palm"}]
def test_convert_multi_case_insensitive(fresh_db):
fresh_db["t"].insert({"id": 1, "Name": "Cleo"}, pk="id")
fresh_db["t"].convert("name", lambda v: {"upper": v.upper()}, multi=True)
assert list(fresh_db["t"].rows) == [{"id": 1, "Name": "Cleo", "upper": "CLEO"}]
fresh_db.table("t").insert({"id": 1, "Name": "Cleo"}, pk="id")
fresh_db.table("t").convert("name", lambda v: {"upper": v.upper()}, multi=True)
assert list(fresh_db.table("t").rows) == [
{"id": 1, "Name": "Cleo", "upper": "CLEO"}
]
def test_convert_output_case_insensitive(fresh_db):
fresh_db["t"].insert({"id": 1, "Name": "Cleo", "Upper": None}, pk="id")
fresh_db["t"].convert("name", lambda v: v.upper(), output="upper")
assert list(fresh_db["t"].rows) == [{"id": 1, "Name": "Cleo", "Upper": "CLEO"}]
fresh_db.table("t").insert({"id": 1, "Name": "Cleo", "Upper": None}, pk="id")
fresh_db.table("t").convert("name", lambda v: v.upper(), output="upper")
assert list(fresh_db.table("t").rows) == [
{"id": 1, "Name": "Cleo", "Upper": "CLEO"}
]
def test_create_table_sql_pk_case_insensitive(fresh_db):
fresh_db["t"].create({"Id": int, "Name": str}, pk="id")
fresh_db.table("t").create({"Id": int, "Name": str}, pk="id")
# Should not have created an extra lowercase "id" column
assert fresh_db["t"].columns_dict == {"Id": int, "Name": str}
assert fresh_db["t"].pks == ["Id"]
assert fresh_db.table("t").columns_dict == {"Id": int, "Name": str}
assert fresh_db.table("t").pks == ["Id"]
def test_create_table_not_null_and_defaults_case_insensitive(fresh_db):
fresh_db["t"].create(
fresh_db.table("t").create(
{"Name": str, "Age": int}, not_null={"name"}, defaults={"age": 1}
)
columns = {c.name: c for c in fresh_db["t"].columns}
columns = {c.name: c for c in fresh_db.table("t").columns}
assert columns["Name"].notnull
assert fresh_db["t"].default_values == {"Age": 1}
assert fresh_db.table("t").default_values == {"Age": 1}
def test_create_table_foreign_keys_case_insensitive(fresh_db):
fresh_db["parent"].create({"Id": int}, pk="Id")
fresh_db["child"].create(
fresh_db.table("parent").create({"Id": int}, pk="Id")
fresh_db.table("child").create(
{"id": int, "Parent_ID": int},
pk="id",
foreign_keys=[("parent_id", "parent", "id")],
)
fks = fresh_db["child"].foreign_keys
fks = fresh_db.table("child").foreign_keys
assert fks == [
ForeignKey(
table="child", column="Parent_ID", other_table="parent", other_column="Id"

View file

@ -20,8 +20,8 @@ def test_recursive_triggers_off():
def test_memory_name():
db1 = Database(memory_name="shared")
db2 = Database(memory_name="shared")
db1["dogs"].insert({"name": "Cleo"})
assert list(db2["dogs"].rows) == [{"name": "Cleo"}]
db1.table("dogs").insert({"name": "Cleo"})
assert list(db2.table("dogs").rows) == [{"name": "Cleo"}]
def test_sqlite_version():
@ -36,7 +36,7 @@ def test_sqlite_version():
def test_database_context_manager(tmpdir):
path = str(tmpdir / "test.db")
with Database(path) as db:
db["t"].insert({"id": 1})
db.table("t").insert({"id": 1})
# Raw writes commit automatically too
db.execute("insert into t (id) values (2)")
# An explicitly opened transaction left uncommitted on purpose:
@ -47,7 +47,7 @@ def test_database_context_manager(tmpdir):
db.execute("select 1")
# ... and the open explicit transaction was rolled back, not committed
db2 = Database(path)
assert [r["id"] for r in db2["t"].rows] == [1, 2]
assert [r["id"] for r in db2.table("t").rows] == [1, 2]
db2.close()
@ -86,8 +86,8 @@ def test_legacy_transaction_control_connection_is_accepted(tmpdir):
str(tmpdir / "test.db"), autocommit=sqlite3.LEGACY_TRANSACTION_CONTROL
)
db = Database(conn)
db["t"].insert({"id": 1}, pk="id")
assert [r["id"] for r in db["t"].rows] == [1]
db.table("t").insert({"id": 1}, pk="id")
assert [r["id"] for r in db.table("t").rows] == [1]
db.close()

View file

@ -1,17 +1,17 @@
def test_insert_conversion(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert({"foo": "bar"}, conversions={"foo": "upper(?)"})
assert [{"foo": "BAR"}] == list(table.rows)
def test_insert_all_conversion(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert_all([{"foo": "bar"}], conversions={"foo": "upper(?)"})
assert [{"foo": "BAR"}] == list(table.rows)
def test_upsert_conversion(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.upsert({"id": 1, "foo": "bar"}, pk="id", conversions={"foo": "upper(?)"})
assert [{"id": 1, "foo": "BAR"}] == list(table.rows)
table.upsert(
@ -21,7 +21,7 @@ def test_upsert_conversion(fresh_db):
def test_upsert_all_conversion(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.upsert_all(
[{"id": 1, "foo": "bar"}], pk="id", conversions={"foo": "upper(?)"}
)
@ -29,7 +29,7 @@ def test_upsert_all_conversion(fresh_db):
def test_update_conversion(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert({"id": 5, "foo": "bar"}, pk="id")
table.update(5, {"foo": "baz"}, conversions={"foo": "upper(?)"})
assert [{"id": 5, "foo": "BAZ"}] == list(table.rows)

View file

@ -27,7 +27,7 @@ from sqlite_utils.db import BadMultiValues
),
)
def test_convert(fresh_db, columns, fn, expected):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert({"title": "Mixed Case", "abstract": "Abstract"})
table.convert(columns, fn)
assert list(table.rows) == [expected]
@ -37,7 +37,7 @@ def test_convert(fresh_db, columns, fn, expected):
"where,where_args", (("id > 1", None), ("id > :id", {"id": 1}), ("id > ?", [1]))
)
def test_convert_where(fresh_db, where, where_args):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert_all(
[
{"id": 1, "title": "One"},
@ -53,7 +53,7 @@ def test_convert_where(fresh_db, where, where_args):
def test_convert_handles_falsey_values(fresh_db):
# Falsey values like 0 should be converted (issue #527)
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert_all([{"x": 0}, {"x": 1}])
assert table.get(1)["x"] == 0
assert table.get(2)["x"] == 1
@ -70,14 +70,14 @@ def test_convert_handles_falsey_values(fresh_db):
),
)
def test_convert_output(fresh_db, drop, expected):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert({"title": "Mixed Case"})
table.convert("title", lambda v: v.upper(), output="other", drop=drop)
assert list(table.rows) == [expected]
def test_convert_output_multiple_column_error(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
with pytest.raises(ValueError) as excinfo:
table.convert(["title", "other"], lambda v: v, output="out")
assert "output= can only be used with a single column" in str(excinfo.value)
@ -91,14 +91,14 @@ def test_convert_output_multiple_column_error(fresh_db):
),
)
def test_convert_output_type(fresh_db, type, expected):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert({"number": "123"})
table.convert("number", lambda v: v, output="other", output_type=type, drop=True)
assert list(table.rows) == [expected]
def test_convert_multi(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert({"title": "Mixed Case"})
table.convert(
"title",
@ -123,7 +123,7 @@ def test_convert_multi(fresh_db):
def test_convert_multi_where(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert_all(
[
{"id": 1, "title": "One"},
@ -145,14 +145,14 @@ def test_convert_multi_where(fresh_db):
def test_convert_multi_exception(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert({"title": "Mixed Case"})
with pytest.raises(BadMultiValues):
table.convert("title", lambda v: v.upper(), multi=True)
def test_convert_repeated(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
col = "num"
table.insert({col: 1})
table.convert(col, lambda x: x * 2)

File diff suppressed because it is too large Load diff

View file

@ -32,9 +32,9 @@ EXAMPLES = [
@pytest.mark.parametrize("column_def,initial_value,expected_value", EXAMPLES)
def test_quote_default_value(fresh_db, column_def, initial_value, expected_value):
fresh_db.execute(f"create table foo (col {column_def})")
assert initial_value == fresh_db["foo"].columns[0].default_value
assert initial_value == fresh_db.table("foo").columns[0].default_value
assert expected_value == fresh_db.quote_default_value(
fresh_db["foo"].columns[0].default_value
fresh_db.table("foo").columns[0].default_value
)
@ -48,7 +48,7 @@ def test_insert_empty_record_uses_default_values(fresh_db):
)
""")
table = fresh_db["has_defaults"]
table = fresh_db.table("has_defaults")
table.insert({})
rows = list(table.rows)

View file

@ -2,7 +2,7 @@ import sqlite_utils
def test_delete_rowid_table(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert({"foo": 1})
rowid = table.insert({"foo": 2}).last_pk
table.delete(rowid)
@ -10,7 +10,7 @@ def test_delete_rowid_table(fresh_db):
def test_delete_pk_table(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert({"id": 1}, pk="id")
table.insert({"id": 2}, pk="id")
table.delete(1)
@ -18,7 +18,7 @@ def test_delete_pk_table(fresh_db):
def test_delete_where(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
for i in range(1, 11):
table.insert({"id": i}, pk="id")
assert table.count == 10
@ -27,7 +27,7 @@ def test_delete_where(fresh_db):
def test_delete_where_all(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
for i in range(1, 11):
table.insert({"id": i}, pk="id")
assert table.count == 10
@ -38,27 +38,27 @@ def test_delete_where_all(fresh_db):
def test_delete_where_commits(tmpdir):
path = str(tmpdir / "test.db")
db = sqlite_utils.Database(path)
db["table"].insert_all([{"id": i} for i in range(5)], pk="id")
db["table"].delete_where("id > ?", [2])
db.table("table").insert_all([{"id": i} for i in range(5)], pk="id")
db.table("table").delete_where("id > ?", [2])
# The connection must not be left inside an open transaction,
# otherwise subsequent atomic() blocks never commit either
assert not db.conn.in_transaction
db["table"].insert({"id": 100})
db.table("table").insert({"id": 100})
db.close()
db2 = sqlite_utils.Database(path)
assert [r["id"] for r in db2["table"].rows] == [0, 1, 2, 100]
assert [r["id"] for r in db2.table("table").rows] == [0, 1, 2, 100]
db2.close()
def test_delete_where_analyze(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert_all(({"id": i, "i": i} for i in range(10)), pk="id")
table.create_index(["i"], analyze=True)
assert "sqlite_stat1" in fresh_db.table_names()
assert list(fresh_db["sqlite_stat1"].rows) == [
assert list(fresh_db.table("sqlite_stat1").rows) == [
{"tbl": "table", "idx": "idx_table_i", "stat": "10 1"}
]
table.delete_where("id > ?", [5], analyze=True)
assert list(fresh_db["sqlite_stat1"].rows) == [
assert list(fresh_db.table("sqlite_stat1").rows) == [
{"tbl": "table", "idx": "idx_table_i", "stat": "6 1"}
]

View file

@ -22,7 +22,7 @@ def test_duplicate(fresh_db):
"bool_col": True,
"datetime_col": str(dt),
}
table1 = fresh_db["table1"]
table1 = fresh_db.table("table1")
row_id = table1.insert(data).last_rowid
# Duplicate table:
table2 = table1.duplicate("table2")
@ -40,4 +40,4 @@ def test_duplicate(fresh_db):
def test_duplicate_fails_if_table_does_not_exist(fresh_db):
with pytest.raises(NoTable):
fresh_db["not_a_table"].duplicate("duplicated")
fresh_db.table("not_a_table").duplicate("duplicated")

View file

@ -5,7 +5,7 @@ from sqlite_utils import Database, cli
def test_enable_counts_specific_table(fresh_db):
foo = fresh_db["foo"]
foo = fresh_db.table("foo")
assert fresh_db.table_names() == []
for i in range(10):
foo.insert({"name": f"item {i}"})
@ -41,24 +41,24 @@ def test_enable_counts_specific_table(fresh_db):
),
}
assert fresh_db.table_names() == ["foo", "_counts"]
assert list(fresh_db["_counts"].rows) == [{"count": 10, "table": "foo"}]
assert list(fresh_db.table("_counts").rows) == [{"count": 10, "table": "foo"}]
# Add some items to test the triggers
for i in range(5):
foo.insert({"name": f"item {10 + i}"})
assert foo.count == 15
assert list(fresh_db["_counts"].rows) == [{"count": 15, "table": "foo"}]
assert list(fresh_db.table("_counts").rows) == [{"count": 15, "table": "foo"}]
# Delete some items
foo.delete_where("rowid < 7")
assert foo.count == 9
assert list(fresh_db["_counts"].rows) == [{"count": 9, "table": "foo"}]
assert list(fresh_db.table("_counts").rows) == [{"count": 9, "table": "foo"}]
foo.delete_where()
assert foo.count == 0
assert list(fresh_db["_counts"].rows) == [{"count": 0, "table": "foo"}]
assert list(fresh_db.table("_counts").rows) == [{"count": 0, "table": "foo"}]
def test_enable_counts_all_tables(fresh_db):
foo = fresh_db["foo"]
bar = fresh_db["bar"]
foo = fresh_db.table("foo")
bar = fresh_db.table("bar")
foo.insert({"name": "Cleo"})
bar.insert({"name": "Cleo"})
foo.enable_fts(["name"])
@ -73,7 +73,7 @@ def test_enable_counts_all_tables(fresh_db):
"foo_fts_config",
"_counts",
}
assert list(fresh_db["_counts"].rows) == [
assert list(fresh_db.table("_counts").rows) == [
{"count": 1, "table": "foo"},
{"count": 1, "table": "bar"},
{"count": 3, "table": "foo_fts_data"},
@ -87,10 +87,10 @@ def test_enable_counts_all_tables(fresh_db):
def counts_db_path(tmpdir):
path = str(tmpdir / "test.db")
db = Database(path)
db["foo"].insert({"name": "bar"})
db["bar"].insert({"name": "bar"})
db["bar"].insert({"name": "bar"})
db["baz"].insert({"name": "bar"})
db.table("foo").insert({"name": "bar"})
db.table("bar").insert({"name": "bar"})
db.table("bar").insert({"name": "bar"})
db.table("baz").insert({"name": "bar"})
return path
@ -163,25 +163,25 @@ def test_uses_counts_after_enable_counts(counts_db_path):
def test_reset_counts(counts_db_path):
db = Database(counts_db_path)
db["foo"].enable_counts()
db["bar"].enable_counts()
db.table("foo").enable_counts()
db.table("bar").enable_counts()
assert db.cached_counts() == {"foo": 1, "bar": 2}
# Corrupt the value
db["_counts"].update("foo", {"count": 3})
db.table("_counts").update("foo", {"count": 3})
assert db.cached_counts() == {"foo": 3, "bar": 2}
assert db["foo"].count == 3
assert db.table("foo").count == 3
# Reset them
db.reset_counts()
assert db.cached_counts() == {"foo": 1, "bar": 2}
assert db["foo"].count == 1
assert db.table("foo").count == 1
def test_reset_counts_cli(counts_db_path):
db = Database(counts_db_path)
db["foo"].enable_counts()
db["bar"].enable_counts()
db.table("foo").enable_counts()
db.table("bar").enable_counts()
assert db.cached_counts() == {"foo": 1, "bar": 2}
db["_counts"].update("foo", {"count": 3})
db.table("_counts").update("foo", {"count": 3})
result = CliRunner().invoke(cli.cli, ["reset-counts", counts_db_path])
assert result.exit_code == 0
assert db.cached_counts() == {"foo": 1, "bar": 2}

View file

@ -11,7 +11,7 @@ def test_extract_single_column(fresh_db, table, fk_column):
expected_table = table or "species"
expected_fk = fk_column or f"{expected_table}_id"
iter_species = itertools.cycle(["Palm", "Spruce", "Mangrove", "Oak"])
fresh_db["tree"].insert_all(
fresh_db.table("tree").insert_all(
(
{
"id": i,
@ -23,8 +23,8 @@ def test_extract_single_column(fresh_db, table, fk_column):
),
pk="id",
)
fresh_db["tree"].extract("species", table=table, fk_column=fk_column)
assert fresh_db["tree"].schema == (
fresh_db.table("tree").extract("species", table=table, fk_column=fk_column)
assert fresh_db.table("tree").schema == (
'CREATE TABLE "tree" (\n'
' "id" INTEGER PRIMARY KEY,\n'
' "name" TEXT,\n'
@ -32,18 +32,18 @@ def test_extract_single_column(fresh_db, table, fk_column):
+ ' "end" INTEGER\n'
+ ")"
)
assert fresh_db[expected_table].schema == (
assert fresh_db.table(expected_table).schema == (
f'CREATE TABLE "{expected_table}" (\n' + ' "id" INTEGER PRIMARY KEY,\n'
' "species" TEXT\n'
")"
)
assert list(fresh_db[expected_table].rows) == [
assert list(fresh_db.table(expected_table).rows) == [
{"id": 1, "species": "Palm"},
{"id": 2, "species": "Spruce"},
{"id": 3, "species": "Mangrove"},
{"id": 4, "species": "Oak"},
]
assert list(itertools.islice(fresh_db["tree"].rows, 0, 4)) == [
assert list(itertools.islice(fresh_db.table("tree").rows, 0, 4)) == [
{"id": 1, "name": "Tree 1", expected_fk: 1, "end": 1},
{"id": 2, "name": "Tree 2", expected_fk: 2, "end": 1},
{"id": 3, "name": "Tree 3", expected_fk: 3, "end": 1},
@ -54,7 +54,7 @@ def test_extract_single_column(fresh_db, table, fk_column):
def test_extract_multiple_columns_with_rename(fresh_db):
iter_common = itertools.cycle(["Palm", "Spruce", "Mangrove", "Oak"])
iter_latin = itertools.cycle(["Arecaceae", "Picea", "Rhizophora", "Quercus"])
fresh_db["tree"].insert_all(
fresh_db.table("tree").insert_all(
(
{
"id": i,
@ -67,30 +67,30 @@ def test_extract_multiple_columns_with_rename(fresh_db):
pk="id",
)
fresh_db["tree"].extract(
fresh_db.table("tree").extract(
["common_name", "latin_name"], rename={"common_name": "name"}
)
assert fresh_db["tree"].schema == (
assert fresh_db.table("tree").schema == (
'CREATE TABLE "tree" (\n'
' "id" INTEGER PRIMARY KEY,\n'
' "name" TEXT,\n'
' "common_name_latin_name_id" INTEGER REFERENCES "common_name_latin_name"("id")\n'
")"
)
assert fresh_db["common_name_latin_name"].schema == (
assert fresh_db.table("common_name_latin_name").schema == (
'CREATE TABLE "common_name_latin_name" (\n'
' "id" INTEGER PRIMARY KEY,\n'
' "name" TEXT,\n'
' "latin_name" TEXT\n'
")"
)
assert list(fresh_db["common_name_latin_name"].rows) == [
assert list(fresh_db.table("common_name_latin_name").rows) == [
{"name": "Palm", "id": 1, "latin_name": "Arecaceae"},
{"name": "Spruce", "id": 2, "latin_name": "Picea"},
{"name": "Mangrove", "id": 3, "latin_name": "Rhizophora"},
{"name": "Oak", "id": 4, "latin_name": "Quercus"},
]
assert list(itertools.islice(fresh_db["tree"].rows, 0, 4)) == [
assert list(itertools.islice(fresh_db.table("tree").rows, 0, 4)) == [
{"id": 1, "name": "Tree 1", "common_name_latin_name_id": 1},
{"id": 2, "name": "Tree 2", "common_name_latin_name_id": 2},
{"id": 3, "name": "Tree 3", "common_name_latin_name_id": 3},
@ -99,7 +99,7 @@ def test_extract_multiple_columns_with_rename(fresh_db):
def test_extract_invalid_columns(fresh_db):
fresh_db["tree"].insert(
fresh_db.table("tree").insert(
{
"id": 1,
"name": "Tree 1",
@ -109,19 +109,19 @@ def test_extract_invalid_columns(fresh_db):
pk="id",
)
with pytest.raises(InvalidColumns):
fresh_db["tree"].extract(["bad_column"])
fresh_db.table("tree").extract(["bad_column"])
def test_extract_rowid_table(fresh_db):
fresh_db["tree"].insert(
fresh_db.table("tree").insert(
{
"name": "Tree 1",
"common_name": "Palm",
"latin_name": "Arecaceae",
}
)
fresh_db["tree"].extract(["common_name", "latin_name"])
assert fresh_db["tree"].schema == (
fresh_db.table("tree").extract(["common_name", "latin_name"])
assert fresh_db.table("tree").schema == (
'CREATE TABLE "tree" (\n'
' "name" TEXT,\n'
' "common_name_latin_name_id" INTEGER REFERENCES "common_name_latin_name"("id")\n'
@ -139,68 +139,68 @@ def test_extract_rowid_table(fresh_db):
def test_reuse_lookup_table(fresh_db):
fresh_db["species"].insert({"id": 1, "name": "Wolf"}, pk="id")
fresh_db["sightings"].insert({"id": 10, "species": "Wolf"}, pk="id")
fresh_db["individuals"].insert(
fresh_db.table("species").insert({"id": 1, "name": "Wolf"}, pk="id")
fresh_db.table("sightings").insert({"id": 10, "species": "Wolf"}, pk="id")
fresh_db.table("individuals").insert(
{"id": 10, "name": "Terriana", "species": "Fox"}, pk="id"
)
fresh_db["sightings"].extract("species", rename={"species": "name"})
fresh_db["individuals"].extract("species", rename={"species": "name"})
assert fresh_db["sightings"].schema == (
fresh_db.table("sightings").extract("species", rename={"species": "name"})
fresh_db.table("individuals").extract("species", rename={"species": "name"})
assert fresh_db.table("sightings").schema == (
'CREATE TABLE "sightings" (\n'
' "id" INTEGER PRIMARY KEY,\n'
' "species_id" INTEGER REFERENCES "species"("id")\n'
")"
)
assert fresh_db["individuals"].schema == (
assert fresh_db.table("individuals").schema == (
'CREATE TABLE "individuals" (\n'
' "id" INTEGER PRIMARY KEY,\n'
' "name" TEXT,\n'
' "species_id" INTEGER REFERENCES "species"("id")\n'
")"
)
assert list(fresh_db["species"].rows) == [
assert list(fresh_db.table("species").rows) == [
{"id": 1, "name": "Wolf"},
{"id": 2, "name": "Fox"},
]
def test_extract_error_on_incompatible_existing_lookup_table(fresh_db):
fresh_db["species"].insert({"id": 1})
fresh_db["tree"].insert({"name": "Tree 1", "common_name": "Palm"})
fresh_db.table("species").insert({"id": 1})
fresh_db.table("tree").insert({"name": "Tree 1", "common_name": "Palm"})
with pytest.raises(InvalidColumns):
fresh_db["tree"].extract("common_name", table="species")
fresh_db.table("tree").extract("common_name", table="species")
# Try again with incompatible existing column type
fresh_db["species2"].insert({"id": 1, "common_name": 3.5})
fresh_db.table("species2").insert({"id": 1, "common_name": 3.5})
with pytest.raises(InvalidColumns):
fresh_db["tree"].extract("common_name", table="species2")
fresh_db.table("tree").extract("common_name", table="species2")
def test_extract_works_with_null_values(fresh_db):
fresh_db["listens"].insert_all(
fresh_db.table("listens").insert_all(
[
{"id": 1, "track_title": "foo", "album_title": "bar"},
{"id": 2, "track_title": "baz", "album_title": None},
],
pk="id",
)
fresh_db["listens"].extract(
fresh_db.table("listens").extract(
columns=["album_title"], table="albums", fk_column="album_id"
)
assert list(fresh_db["listens"].rows) == [
assert list(fresh_db.table("listens").rows) == [
{"id": 1, "track_title": "foo", "album_id": 1},
{"id": 2, "track_title": "baz", "album_id": None},
]
assert list(fresh_db["albums"].rows) == [
assert list(fresh_db.table("albums").rows) == [
{"id": 1, "album_title": "bar"},
]
def test_extract_null_values_single_column(fresh_db):
# https://github.com/simonw/sqlite-utils/issues/186
fresh_db["species"].insert({"id": 1, "species": "Wolf"}, pk="id")
fresh_db["individuals"].insert_all(
fresh_db.table("species").insert({"id": 1, "species": "Wolf"}, pk="id")
fresh_db.table("individuals").insert_all(
[
{"id": 10, "name": "Terriana", "species": "Fox"},
{"id": 11, "name": "Spenidorm", "species": None},
@ -210,13 +210,13 @@ def test_extract_null_values_single_column(fresh_db):
],
pk="id",
)
fresh_db["individuals"].extract("species")
fresh_db.table("individuals").extract("species")
# No null row should have been added to species
assert list(fresh_db["species"].rows) == [
assert list(fresh_db.table("species").rows) == [
{"id": 1, "species": "Wolf"},
{"id": 2, "species": "Fox"},
]
assert list(fresh_db["individuals"].rows) == [
assert list(fresh_db.table("individuals").rows) == [
{"id": 10, "name": "Terriana", "species_id": 2},
{"id": 11, "name": "Spenidorm", "species_id": None},
{"id": 12, "name": "Grantheim", "species_id": 1},
@ -228,7 +228,7 @@ def test_extract_null_values_single_column(fresh_db):
def test_extract_null_values_multiple_columns(fresh_db):
# A row should be extracted if at least one column is not null -
# only rows where ALL extracted columns are null are left alone
fresh_db["circulation"].insert_all(
fresh_db.table("circulation").insert_all(
[
{"id": 1, "title": "title one", "creator": "creator one", "year": 2018},
{"id": 2, "title": "title two", "creator": None, "year": 2019},
@ -237,14 +237,14 @@ def test_extract_null_values_multiple_columns(fresh_db):
],
pk="id",
)
fresh_db["circulation"].extract(
fresh_db.table("circulation").extract(
["title", "creator"], table="books", fk_column="book_id"
)
assert list(fresh_db["books"].rows) == [
assert list(fresh_db.table("books").rows) == [
{"id": 1, "title": "title one", "creator": "creator one"},
{"id": 2, "title": "title two", "creator": None},
]
assert list(fresh_db["circulation"].rows) == [
assert list(fresh_db.table("circulation").rows) == [
{"id": 1, "book_id": 1, "year": 2018},
{"id": 2, "book_id": 2, "year": 2019},
{"id": 3, "book_id": None, "year": 2020},
@ -255,20 +255,20 @@ def test_extract_null_values_multiple_columns(fresh_db):
def test_extract_null_values_existing_lookup_table_with_null_row(fresh_db):
# Even if the lookup table already contains an all-null row, rows where
# every extracted column is null should keep a null foreign key
fresh_db["species"].insert({"id": 1, "species": None}, pk="id")
fresh_db["individuals"].insert_all(
fresh_db.table("species").insert({"id": 1, "species": None}, pk="id")
fresh_db.table("individuals").insert_all(
[
{"id": 10, "name": "Terriana", "species": "Fox"},
{"id": 11, "name": "Spenidorm", "species": None},
],
pk="id",
)
fresh_db["individuals"].extract("species")
assert list(fresh_db["species"].rows) == [
fresh_db.table("individuals").extract("species")
assert list(fresh_db.table("species").rows) == [
{"id": 1, "species": None},
{"id": 2, "species": "Fox"},
]
assert list(fresh_db["individuals"].rows) == [
assert list(fresh_db.table("individuals").rows) == [
{"id": 10, "name": "Terriana", "species_id": 2},
{"id": 11, "name": "Spenidorm", "species_id": None},
]
@ -279,17 +279,19 @@ def test_extract_repeated_into_shared_lookup_with_nulls(fresh_db):
# cannot dedupe NULL-containing rows against the existing lookup
# table - extracting a second table into the same lookup previously
# inserted duplicate rows that nothing pointed to
fresh_db["t1"].insert_all(
fresh_db.table("t1").insert_all(
[
{"id": 1, "species": None, "common": "X"},
{"id": 2, "species": "Oak", "common": "Oak"},
],
pk="id",
)
fresh_db["t2"].insert_all([{"id": 1, "species": None, "common": "X"}], pk="id")
fresh_db["t1"].extract(["species", "common"], table="lk")
fresh_db["t2"].extract(["species", "common"], table="lk")
assert fresh_db["lk"].count == 2
fresh_db.table("t2").insert_all(
[{"id": 1, "species": None, "common": "X"}], pk="id"
)
fresh_db.table("t1").extract(["species", "common"], table="lk")
fresh_db.table("t2").extract(["species", "common"], table="lk")
assert fresh_db.table("lk").count == 2
# Both tables point at the same lookup row
t1_fk = fresh_db.execute("select lk_id from t1 where id = 1").fetchone()[0]
t2_fk = fresh_db.execute("select lk_id from t2 where id = 1").fetchone()[0]
@ -298,8 +300,8 @@ def test_extract_repeated_into_shared_lookup_with_nulls(fresh_db):
def test_extract_repeated_into_shared_lookup_no_nulls(fresh_db):
# Non-NULL rows were already deduped by the unique index - keep it so
fresh_db["t1"].insert_all([{"id": 1, "species": "Oak"}], pk="id")
fresh_db["t2"].insert_all([{"id": 1, "species": "Oak"}], pk="id")
fresh_db["t1"].extract(["species"], table="lk")
fresh_db["t2"].extract(["species"], table="lk")
assert fresh_db["lk"].count == 1
fresh_db.table("t1").insert_all([{"id": 1, "species": "Oak"}], pk="id")
fresh_db.table("t2").insert_all([{"id": 1, "species": "Oak"}], pk="id")
fresh_db.table("t1").extract(["species"], table="lk")
fresh_db.table("t2").extract(["species"], table="lk")
assert fresh_db.table("lk").count == 1

View file

@ -32,15 +32,15 @@ def test_extracts(fresh_db, kwargs, expected_table, use_table_factory):
assert {expected_table, "Trees"} == set(fresh_db.table_names())
assert (
f'CREATE TABLE "{expected_table}" (\n "id" INTEGER PRIMARY KEY,\n "value" TEXT\n)'
== fresh_db[expected_table].schema
== fresh_db.table(expected_table).schema
)
assert (
f'CREATE TABLE "Trees" (\n "id" INTEGER,\n "species_id" INTEGER REFERENCES "{expected_table}"("id")\n)'
== fresh_db["Trees"].schema
== fresh_db.table("Trees").schema
)
# Should have a foreign key reference
assert len(fresh_db["Trees"].foreign_keys) == 1
fk = fresh_db["Trees"].foreign_keys[0]
assert len(fresh_db.table("Trees").foreign_keys) == 1
fk = fresh_db.table("Trees").foreign_keys[0]
assert fk.table == "Trees"
assert fk.column == "species_id"
@ -54,22 +54,22 @@ def test_extracts(fresh_db, kwargs, expected_table, use_table_factory):
partial=0,
columns=["value"],
)
] == fresh_db[expected_table].indexes
] == fresh_db.table(expected_table).indexes
# Finally, check the rows
assert [{"id": 1, "value": "Oak"}, {"id": 2, "value": "Palm"}] == list(
fresh_db[expected_table].rows
fresh_db.table(expected_table).rows
)
assert [
{"id": 1, "species_id": 1},
{"id": 2, "species_id": 1},
{"id": 3, "species_id": 2},
] == list(fresh_db["Trees"].rows)
] == list(fresh_db.table("Trees").rows)
def test_extracts_null_values(fresh_db):
# https://github.com/simonw/sqlite-utils/issues/186
# Null values should stay null, not be extracted into the lookup table
fresh_db["Trees"].insert_all(
fresh_db.table("Trees").insert_all(
[
{"id": 1, "species_id": "Oak"},
{"id": 2, "species_id": None},
@ -78,11 +78,11 @@ def test_extracts_null_values(fresh_db):
],
extracts={"species_id": "Species"},
)
assert list(fresh_db["Species"].rows) == [
assert list(fresh_db.table("Species").rows) == [
{"id": 1, "value": "Oak"},
{"id": 2, "value": "Palm"},
]
assert list(fresh_db["Trees"].rows) == [
assert list(fresh_db.table("Trees").rows) == [
{"id": 1, "species_id": 1},
{"id": 2, "species_id": None},
{"id": 3, "species_id": 2},
@ -92,7 +92,7 @@ def test_extracts_null_values(fresh_db):
def test_extracts_null_values_list_mode(fresh_db):
# Same as test_extracts_null_values but for list-based records
fresh_db["Trees"].insert_all(
fresh_db.table("Trees").insert_all(
[
["id", "species_id"],
[1, "Oak"],
@ -102,11 +102,11 @@ def test_extracts_null_values_list_mode(fresh_db):
],
extracts={"species_id": "Species"},
)
assert list(fresh_db["Species"].rows) == [
assert list(fresh_db.table("Species").rows) == [
{"id": 1, "value": "Oak"},
{"id": 2, "value": "Palm"},
]
assert list(fresh_db["Trees"].rows) == [
assert list(fresh_db.table("Trees").rows) == [
{"id": 1, "species_id": 1},
{"id": 2, "species_id": None},
{"id": 3, "species_id": 2},

View file

@ -32,7 +32,7 @@ def compound_db():
def test_compound_foreign_key(compound_db):
fks = compound_db["courses"].foreign_keys
fks = compound_db.table("courses").foreign_keys
assert len(fks) == 1
fk = fks[0]
assert fk.is_compound is True
@ -46,10 +46,10 @@ def test_compound_foreign_key(compound_db):
def test_single_foreign_key_gets_columns_fields(fresh_db):
fresh_db["authors"].insert({"id": 1, "name": "Sally"}, pk="id")
fresh_db["books"].insert({"title": "Hedgehogs", "author_id": 1})
fresh_db["books"].add_foreign_key("author_id", "authors", "id")
fk = fresh_db["books"].foreign_keys[0]
fresh_db.table("authors").insert({"id": 1, "name": "Sally"}, pk="id")
fresh_db.table("books").insert({"title": "Hedgehogs", "author_id": 1})
fresh_db.table("books").add_foreign_key("author_id", "authors", "id")
fk = fresh_db.table("books").foreign_keys[0]
assert fk.is_compound is False
assert fk.column == "author_id"
assert fk.other_column == "id"
@ -60,10 +60,10 @@ def test_single_foreign_key_gets_columns_fields(fresh_db):
def test_foreign_key_no_longer_unpacks_as_tuple(fresh_db):
# Clean break in 4.0: ForeignKey is a dataclass, not a namedtuple, so the
# old tuple unpacking and indexing patterns now fail hard.
fresh_db["authors"].insert({"id": 1, "name": "Sally"}, pk="id")
fresh_db["books"].insert({"title": "Hedgehogs", "author_id": 1})
fresh_db["books"].add_foreign_key("author_id", "authors", "id")
fk = fresh_db["books"].foreign_keys[0]
fresh_db.table("authors").insert({"id": 1, "name": "Sally"}, pk="id")
fresh_db.table("books").insert({"title": "Hedgehogs", "author_id": 1})
fresh_db.table("books").add_foreign_key("author_id", "authors", "id")
fk = fresh_db.table("books").foreign_keys[0]
with pytest.raises(TypeError):
_table, _column, _other_table, _other_column = fk
with pytest.raises(TypeError):
@ -71,16 +71,18 @@ def test_foreign_key_no_longer_unpacks_as_tuple(fresh_db):
def test_foreign_keys_are_sortable(fresh_db):
fresh_db["authors"].insert({"id": 1, "name": "Sally"}, pk="id")
fresh_db["categories"].insert({"id": 1, "name": "Wildlife"}, pk="id")
fresh_db["books"].insert({"title": "Hedgehogs", "author_id": 1, "category_id": 1})
fresh_db.table("authors").insert({"id": 1, "name": "Sally"}, pk="id")
fresh_db.table("categories").insert({"id": 1, "name": "Wildlife"}, pk="id")
fresh_db.table("books").insert(
{"title": "Hedgehogs", "author_id": 1, "category_id": 1}
)
fresh_db.add_foreign_keys(
[
("books", "author_id", "authors", "id"),
("books", "category_id", "categories", "id"),
]
)
fks = sorted(fresh_db["books"].foreign_keys)
fks = sorted(fresh_db.table("books").foreign_keys)
assert fks[0].column == "author_id"
assert fks[1].column == "category_id"
@ -105,7 +107,7 @@ def test_mixed_compound_and_single_foreign_keys_are_sortable():
REFERENCES departments(campus_name, dept_code)
);
""")
fks = db["courses"].foreign_keys
fks = db.table("courses").foreign_keys
assert len(fks) == 2
assert {fk.is_compound for fk in fks} == {True, False}
fks_sorted = sorted(fks)
@ -163,8 +165,8 @@ def test_create_table_with_compound_foreign_key(departments_db, foreign_keys):
pk="course_code",
foreign_keys=foreign_keys,
)
assert departments_db["courses"].schema == EXPECTED_COURSES_SCHEMA
fks = departments_db["courses"].foreign_keys
assert departments_db.table("courses").schema == EXPECTED_COURSES_SCHEMA
fks = departments_db.table("courses").foreign_keys
assert len(fks) == 1
fk = fks[0]
assert fk.is_compound is True
@ -181,10 +183,10 @@ def test_create_table_compound_foreign_key_enforced(departments_db):
pk="course_code",
foreign_keys=[(("campus_name", "dept_code"), "departments")],
)
departments_db["departments"].insert(
departments_db.table("departments").insert(
{"campus_name": "Berkeley", "dept_code": "CS", "dept_name": "Computer Science"}
)
departments_db["courses"].insert(
departments_db.table("courses").insert(
{"course_code": "CS101", "campus_name": "Berkeley", "dept_code": "CS"}
)
with pytest.raises(sqlite3.IntegrityError):
@ -207,8 +209,8 @@ def test_create_table_compound_foreign_key_missing_other_column(departments_db):
def test_transform_preserves_compound_foreign_key(compound_db):
compound_db["courses"].transform(rename={"course_name": "title"})
fks = compound_db["courses"].foreign_keys
compound_db.table("courses").transform(rename={"course_name": "title"})
fks = compound_db.table("courses").foreign_keys
assert len(fks) == 1
fk = fks[0]
assert fk.is_compound is True
@ -218,8 +220,8 @@ def test_transform_preserves_compound_foreign_key(compound_db):
def test_transform_rename_member_column_updates_compound_foreign_key(compound_db):
compound_db["courses"].transform(rename={"campus_name": "campus"})
fks = compound_db["courses"].foreign_keys
compound_db.table("courses").transform(rename={"campus_name": "campus"})
fks = compound_db.table("courses").foreign_keys
assert len(fks) == 1
fk = fks[0]
assert fk.is_compound is True
@ -231,9 +233,9 @@ def test_transform_rename_member_column_updates_compound_foreign_key(compound_db
def test_transform_drop_member_column_drops_compound_foreign_key(compound_db):
# Matches single-column behavior: dropping the column silently
# drops the foreign key that used it
compound_db["courses"].transform(drop={"dept_code"})
assert compound_db["courses"].foreign_keys == []
assert "FOREIGN KEY" not in compound_db["courses"].schema
compound_db.table("courses").transform(drop={"dept_code"})
assert compound_db.table("courses").foreign_keys == []
assert "FOREIGN KEY" not in compound_db.table("courses").schema
@pytest.mark.parametrize(
@ -246,11 +248,11 @@ def test_transform_drop_member_column_drops_compound_foreign_key(compound_db):
),
)
def test_transform_drop_compound_foreign_key(compound_db, drop_foreign_keys):
compound_db["courses"].transform(drop_foreign_keys=drop_foreign_keys)
assert compound_db["courses"].foreign_keys == []
compound_db.table("courses").transform(drop_foreign_keys=drop_foreign_keys)
assert compound_db.table("courses").foreign_keys == []
# The columns themselves survive
assert {"campus_name", "dept_code"} <= set(
compound_db["courses"].columns_dict.keys()
compound_db.table("courses").columns_dict.keys()
)
@ -265,12 +267,12 @@ def courses_db(departments_db):
def test_add_compound_foreign_key(courses_db):
t = courses_db["courses"].add_foreign_key(
t = courses_db.table("courses").add_foreign_key(
("campus_name", "dept_code"), "departments", ("campus_name", "dept_code")
)
# Returns self
assert t.name == "courses"
fks = courses_db["courses"].foreign_keys
fks = courses_db.table("courses").foreign_keys
assert len(fks) == 1
fk = fks[0]
assert fk.is_compound is True
@ -281,27 +283,33 @@ def test_add_compound_foreign_key(courses_db):
def test_add_compound_foreign_key_guesses_other_columns(courses_db):
# Lists work here too, though tuples are the documented form
courses_db["courses"].add_foreign_key(["campus_name", "dept_code"], "departments")
fk = courses_db["courses"].foreign_keys[0]
courses_db.table("courses").add_foreign_key(
["campus_name", "dept_code"], "departments"
)
fk = courses_db.table("courses").foreign_keys[0]
assert fk.other_columns == ("campus_name", "dept_code")
def test_add_compound_foreign_key_error_if_already_exists(courses_db):
courses_db["courses"].add_foreign_key(("campus_name", "dept_code"), "departments")
courses_db.table("courses").add_foreign_key(
("campus_name", "dept_code"), "departments"
)
with pytest.raises(AlterError) as ex:
courses_db["courses"].add_foreign_key(
courses_db.table("courses").add_foreign_key(
("campus_name", "dept_code"), "departments"
)
assert "already exists" in ex.value.args[0]
# ignore=True should not raise
courses_db["courses"].add_foreign_key(
courses_db.table("courses").add_foreign_key(
("campus_name", "dept_code"), "departments", ignore=True
)
def test_add_compound_foreign_key_error_if_column_missing(courses_db):
with pytest.raises(AlterError):
courses_db["courses"].add_foreign_key(("campus_name", "nope"), "departments")
courses_db.table("courses").add_foreign_key(
("campus_name", "nope"), "departments"
)
def test_db_add_foreign_keys_compound(courses_db):
@ -315,14 +323,14 @@ def test_db_add_foreign_keys_compound(courses_db):
)
]
)
fk = courses_db["courses"].foreign_keys[0]
fk = courses_db.table("courses").foreign_keys[0]
assert fk.is_compound is True
assert fk.columns == ("campus_name", "dept_code")
def test_index_foreign_keys_compound_creates_composite_index(compound_db):
compound_db.index_foreign_keys()
index_columns = [i.columns for i in compound_db["courses"].indexes]
index_columns = [i.columns for i in compound_db.table("courses").indexes]
assert ["campus_name", "dept_code"] in index_columns
# No separate single-column indexes for the members
assert ["campus_name"] not in index_columns
@ -339,22 +347,22 @@ def test_foreign_key_captures_on_delete_and_on_update():
ON DELETE CASCADE ON UPDATE RESTRICT
);
""")
fk = db["books"].foreign_keys[0]
fk = db.table("books").foreign_keys[0]
assert fk.on_delete == "CASCADE"
assert fk.on_update == "RESTRICT"
def test_foreign_key_on_delete_defaults_to_no_action(fresh_db):
fresh_db["authors"].insert({"id": 1}, pk="id")
fresh_db["books"].insert({"id": 1, "author_id": 1}, pk="id")
fresh_db["books"].add_foreign_key("author_id", "authors", "id")
fk = fresh_db["books"].foreign_keys[0]
fresh_db.table("authors").insert({"id": 1}, pk="id")
fresh_db.table("books").insert({"id": 1, "author_id": 1}, pk="id")
fresh_db.table("books").add_foreign_key("author_id", "authors", "id")
fk = fresh_db.table("books").foreign_keys[0]
assert fk.on_delete == "NO ACTION"
assert fk.on_update == "NO ACTION"
def test_create_table_foreign_key_with_on_delete(fresh_db):
fresh_db["authors"].insert({"id": 1}, pk="id")
fresh_db.table("authors").insert({"id": 1}, pk="id")
fresh_db.create_table(
"books",
{"id": int, "author_id": int},
@ -369,8 +377,8 @@ def test_create_table_foreign_key_with_on_delete(fresh_db):
)
],
)
assert "ON DELETE CASCADE" in fresh_db["books"].schema
assert fresh_db["books"].foreign_keys[0].on_delete == "CASCADE"
assert "ON DELETE CASCADE" in fresh_db.table("books").schema
assert fresh_db.table("books").foreign_keys[0].on_delete == "CASCADE"
def test_transform_preserves_on_delete_cascade():
@ -383,11 +391,11 @@ def test_transform_preserves_on_delete_cascade():
author_id INTEGER REFERENCES authors(id) ON DELETE CASCADE
);
""")
db["books"].transform(rename={"title": "book_title"})
fk = db["books"].foreign_keys[0]
db.table("books").transform(rename={"title": "book_title"})
fk = db.table("books").foreign_keys[0]
assert fk.on_delete == "CASCADE"
assert fk.on_update == "NO ACTION"
assert "ON DELETE CASCADE" in db["books"].schema
assert "ON DELETE CASCADE" in db.table("books").schema
def test_transform_preserves_compound_foreign_key_on_delete():
@ -406,11 +414,11 @@ def test_transform_preserves_compound_foreign_key_on_delete():
REFERENCES departments(campus_name, dept_code) ON DELETE CASCADE
);
""")
db["courses"].transform(rename={"course_code": "code"})
fk = db["courses"].foreign_keys[0]
db.table("courses").transform(rename={"course_code": "code"})
fk = db.table("courses").foreign_keys[0]
assert fk.is_compound is True
assert fk.on_delete == "CASCADE"
assert "ON DELETE CASCADE" in db["courses"].schema
assert "ON DELETE CASCADE" in db.table("courses").schema
def test_implicit_primary_key_reference_is_resolved():
@ -424,7 +432,7 @@ def test_implicit_primary_key_reference_is_resolved():
author_id INTEGER REFERENCES authors
);
""")
fk = db["books"].foreign_keys[0]
fk = db.table("books").foreign_keys[0]
assert fk.is_compound is False
assert fk.other_column == "author_id"
assert fk.other_columns == ("author_id",)
@ -445,7 +453,7 @@ def test_implicit_compound_primary_key_reference_is_resolved():
FOREIGN KEY (campus_name, dept_code) REFERENCES departments
);
""")
fk = db["courses"].foreign_keys[0]
fk = db.table("courses").foreign_keys[0]
assert fk.is_compound is True
assert fk.other_columns == ("campus_name", "dept_code")
@ -470,14 +478,14 @@ def test_add_foreign_keys_preserves_actions(fresh_db):
# https://github.com/simonw/sqlite-utils/issues/594 review finding:
# ForeignKey objects passed to db.add_foreign_keys() were flattened
# to plain tuples, losing on_delete/on_update
fresh_db["authors"].insert({"id": 1}, pk="id")
fresh_db["books"].insert({"id": 1, "author_id": 1}, pk="id")
fresh_db.table("authors").insert({"id": 1}, pk="id")
fresh_db.table("books").insert({"id": 1, "author_id": 1}, pk="id")
fresh_db.add_foreign_keys(
[ForeignKey("books", "author_id", "authors", "id", on_delete="CASCADE")]
)
fk = fresh_db["books"].foreign_keys[0]
fk = fresh_db.table("books").foreign_keys[0]
assert fk.on_delete == "CASCADE"
assert "ON DELETE CASCADE" in fresh_db["books"].schema
assert "ON DELETE CASCADE" in fresh_db.table("books").schema
def test_add_foreign_keys_preserves_actions_compound(courses_db):
@ -495,36 +503,36 @@ def test_add_foreign_keys_preserves_actions_compound(courses_db):
)
]
)
fk = courses_db["courses"].foreign_keys[0]
fk = courses_db.table("courses").foreign_keys[0]
assert fk.is_compound is True
assert fk.on_delete == "CASCADE"
assert "ON DELETE CASCADE" in courses_db["courses"].schema
assert "ON DELETE CASCADE" in courses_db.table("courses").schema
def test_add_foreign_key_on_delete_on_update(fresh_db):
fresh_db["authors"].insert({"id": 1}, pk="id")
fresh_db["books"].insert({"id": 1, "author_id": 1}, pk="id")
fresh_db["books"].add_foreign_key(
fresh_db.table("authors").insert({"id": 1}, pk="id")
fresh_db.table("books").insert({"id": 1, "author_id": 1}, pk="id")
fresh_db.table("books").add_foreign_key(
"author_id", "authors", "id", on_delete="CASCADE", on_update="RESTRICT"
)
fk = fresh_db["books"].foreign_keys[0]
fk = fresh_db.table("books").foreign_keys[0]
assert fk.on_delete == "CASCADE"
assert fk.on_update == "RESTRICT"
assert "ON UPDATE RESTRICT ON DELETE CASCADE" in fresh_db["books"].schema
assert "ON UPDATE RESTRICT ON DELETE CASCADE" in fresh_db.table("books").schema
# The cascade should actually fire
fresh_db.execute("PRAGMA foreign_keys = ON")
fresh_db.execute("delete from authors where id = 1")
assert fresh_db["books"].count == 0
assert fresh_db.table("books").count == 0
def test_add_compound_foreign_key_on_delete(courses_db):
courses_db["courses"].add_foreign_key(
courses_db.table("courses").add_foreign_key(
("campus_name", "dept_code"), "departments", on_delete="SET NULL"
)
fk = courses_db["courses"].foreign_keys[0]
fk = courses_db.table("courses").foreign_keys[0]
assert fk.is_compound is True
assert fk.on_delete == "SET NULL"
assert "ON DELETE SET NULL" in courses_db["courses"].schema
assert "ON DELETE SET NULL" in courses_db.table("courses").schema
def test_implicit_compound_foreign_key_resolves_pk_declaration_order(fresh_db):
@ -536,7 +544,7 @@ def test_implicit_compound_foreign_key_resolves_pk_declaration_order(fresh_db):
fresh_db.execute(
"create table child (x text, y text, foreign key (x, y) references other)"
)
fk = fresh_db["child"].foreign_keys[0]
fk = fresh_db.table("child").foreign_keys[0]
assert fk.other_columns == ("a", "b")
@ -549,46 +557,46 @@ def test_transform_implicit_compound_foreign_key_stays_valid(fresh_db):
"create table child (x text, y text, foreign key (x, y) references other)"
)
fresh_db.execute("PRAGMA foreign_keys = ON")
fresh_db["other"].insert({"a": "A", "b": "B"})
fresh_db["child"].insert({"x": "A", "y": "B"})
fresh_db["child"].transform(types={"x": str})
assert fresh_db["child"].foreign_keys[0].other_columns == ("a", "b")
fresh_db.table("other").insert({"a": "A", "b": "B"})
fresh_db.table("child").insert({"x": "A", "y": "B"})
fresh_db.table("child").transform(types={"x": str})
assert fresh_db.table("child").foreign_keys[0].other_columns == ("a", "b")
# The constraint still points the right way around
fresh_db["child"].insert({"x": "A", "y": "B"})
fresh_db.table("child").insert({"x": "A", "y": "B"})
with pytest.raises(sqlite3.IntegrityError):
fresh_db["child"].insert({"x": "B", "y": "A"})
fresh_db.table("child").insert({"x": "B", "y": "A"})
def test_create_compound_foreign_key_guesses_pk_declaration_order(fresh_db):
fresh_db.execute("create table other (b text, a text, primary key (a, b))")
fresh_db["other"].insert({"a": "A", "b": "B"})
fresh_db["child"].create(
fresh_db.table("other").insert({"a": "A", "b": "B"})
fresh_db.table("child").create(
{"id": int, "x": str, "y": str},
pk="id",
foreign_keys=[(("x", "y"), "other")],
)
assert fresh_db["child"].foreign_keys[0].other_columns == ("a", "b")
assert fresh_db.table("child").foreign_keys[0].other_columns == ("a", "b")
fresh_db.execute("PRAGMA foreign_keys = ON")
fresh_db["child"].insert({"id": 1, "x": "A", "y": "B"})
fresh_db.table("child").insert({"id": 1, "x": "A", "y": "B"})
with pytest.raises(sqlite3.IntegrityError):
fresh_db["child"].insert({"id": 2, "x": "B", "y": "A"})
fresh_db.table("child").insert({"id": 2, "x": "B", "y": "A"})
def test_add_compound_foreign_key_guesses_pk_declaration_order(fresh_db):
fresh_db.execute("create table other (b text, a text, primary key (a, b))")
fresh_db["child"].insert({"id": 1, "x": "A", "y": "B"}, pk="id")
fresh_db["child"].add_foreign_key(("x", "y"), "other")
assert fresh_db["child"].foreign_keys[0].other_columns == ("a", "b")
fresh_db.table("child").insert({"id": 1, "x": "A", "y": "B"}, pk="id")
fresh_db.table("child").add_foreign_key(("x", "y"), "other")
assert fresh_db.table("child").foreign_keys[0].other_columns == ("a", "b")
def test_foreign_keys_are_hashable(fresh_db):
# set() over foreign_keys worked with the 3.x namedtuple and must
# keep working with the dataclass
fresh_db["p"].insert({"id": 1}, pk="id")
fresh_db["c"].insert(
fresh_db.table("p").insert({"id": 1}, pk="id")
fresh_db.table("c").insert(
{"id": 1, "pid": 1}, pk="id", foreign_keys=[("pid", "p", "id")]
)
fks = set(fresh_db["c"].foreign_keys)
fks = set(fresh_db.table("c").foreign_keys)
assert len(fks) == 1
assert ForeignKey("c", "pid", "p", "id") in fks
# Usable as dict keys too
@ -617,9 +625,9 @@ def test_create_table_mixed_foreign_keys_list(fresh_db):
# 3.x accepted a mix of ForeignKey objects, tuples and bare column
# strings in foreign_keys= (ForeignKey was a namedtuple, so it passed
# the tuple check) - keep accepting the mix
fresh_db["authors"].insert({"id": 1}, pk="id")
fresh_db["publishers"].insert({"id": 1}, pk="id")
fresh_db["books"].create(
fresh_db.table("authors").insert({"id": 1}, pk="id")
fresh_db.table("publishers").insert({"id": 1}, pk="id")
fresh_db.table("books").create(
{"id": int, "author_id": int, "publisher_id": int},
pk="id",
foreign_keys=[
@ -627,14 +635,14 @@ def test_create_table_mixed_foreign_keys_list(fresh_db):
("publisher_id", "publishers", "id"),
],
)
fks = {fk.column: fk.other_table for fk in fresh_db["books"].foreign_keys}
fks = {fk.column: fk.other_table for fk in fresh_db.table("books").foreign_keys}
assert fks == {"author_id": "authors", "publisher_id": "publishers"}
def test_create_table_mixed_foreign_keys_with_string(fresh_db):
fresh_db["authors"].insert({"id": 1}, pk="id")
fresh_db["publishers"].insert({"id": 1}, pk="id")
fresh_db["books"].create(
fresh_db.table("authors").insert({"id": 1}, pk="id")
fresh_db.table("publishers").insert({"id": 1}, pk="id")
fresh_db.table("books").create(
{"id": int, "author_id": int, "publisher_id": int},
pk="id",
foreign_keys=[
@ -642,15 +650,15 @@ def test_create_table_mixed_foreign_keys_with_string(fresh_db):
("publisher_id", "publishers", "id"),
],
)
fks = {fk.column: fk.other_table for fk in fresh_db["books"].foreign_keys}
fks = {fk.column: fk.other_table for fk in fresh_db.table("books").foreign_keys}
assert fks == {"author_id": "authors", "publisher_id": "publishers"}
def test_add_foreign_keys_existing_with_different_actions_errors(fresh_db):
# Requesting an existing foreign key with different ON DELETE/ON UPDATE
# actions was silently skipped, dropping the requested change
fresh_db["authors"].insert({"id": 1}, pk="id")
fresh_db["books"].insert(
fresh_db.table("authors").insert({"id": 1}, pk="id")
fresh_db.table("books").insert(
{"id": 1, "author_id": 1},
pk="id",
foreign_keys=[("author_id", "authors", "id")],
@ -660,19 +668,21 @@ def test_add_foreign_keys_existing_with_different_actions_errors(fresh_db):
[ForeignKey("books", "author_id", "authors", "id", on_delete="CASCADE")]
)
assert "ON DELETE" in str(ex.value)
assert fresh_db["books"].foreign_keys[0].on_delete == "NO ACTION"
assert fresh_db.table("books").foreign_keys[0].on_delete == "NO ACTION"
def test_add_foreign_keys_identical_existing_is_noop(fresh_db):
# An exact match, including actions, is silently skipped so repeated
# calls stay idempotent
fresh_db["authors"].insert({"id": 1}, pk="id")
fresh_db["books"].insert({"id": 1, "author_id": 1}, pk="id")
fresh_db["books"].add_foreign_key("author_id", "authors", "id", on_delete="CASCADE")
fresh_db.table("authors").insert({"id": 1}, pk="id")
fresh_db.table("books").insert({"id": 1, "author_id": 1}, pk="id")
fresh_db.table("books").add_foreign_key(
"author_id", "authors", "id", on_delete="CASCADE"
)
fresh_db.add_foreign_keys(
[ForeignKey("books", "author_id", "authors", "id", on_delete="CASCADE")]
)
fks = fresh_db["books"].foreign_keys
fks = fresh_db.table("books").foreign_keys
assert len(fks) == 1
assert fks[0].on_delete == "CASCADE"
@ -680,13 +690,13 @@ def test_add_foreign_keys_identical_existing_is_noop(fresh_db):
def test_add_foreign_keys_compound_column_count_mismatch_errors(fresh_db):
# Previously the extra other-column was silently discarded, creating
# a single-column foreign key to just ("id")
fresh_db["departments"].insert(
fresh_db.table("departments").insert(
{"campus": "north", "code": "cs"}, pk=("campus", "code")
)
fresh_db["courses"].insert({"id": 1, "campus": "north"}, pk="id")
fresh_db.table("courses").insert({"id": 1, "campus": "north"}, pk="id")
with pytest.raises(ValueError) as ex:
fresh_db.add_foreign_keys(
[("courses", ("campus",), "departments", ("campus", "code"))]
)
assert "same number of columns" in str(ex.value)
assert fresh_db["courses"].foreign_keys == []
assert fresh_db.table("courses").foreign_keys == []

View file

@ -20,7 +20,7 @@ search_records = [
def test_enable_fts(fresh_db):
table = fresh_db["searchable"]
table = fresh_db.table("searchable")
table.insert_all(search_records)
assert ["searchable"] == fresh_db.table_names()
table.enable_fts(["text", "country"], fts_version="FTS4")
@ -54,7 +54,7 @@ def test_enable_fts(fresh_db):
def test_enable_fts_escape_table_names(fresh_db):
# Table names with restricted chars are handled correctly.
# colons and dots are restricted characters for table names.
table = fresh_db["http://example.com"]
table = fresh_db.table("http://example.com")
table.insert_all(search_records)
assert ["http://example.com"] == fresh_db.table_names()
table.enable_fts(["text", "country"], fts_version="FTS4")
@ -87,7 +87,7 @@ def test_enable_fts_escape_table_names(fresh_db):
def test_search_duplicate_columns_are_deduped(fresh_db):
# https://github.com/simonw/sqlite-utils/issues/624
table = fresh_db["t"]
table = fresh_db.table("t")
table.insert_all(search_records)
table.enable_fts(["text", "country"], fts_version="FTS4")
rows = list(table.search("tanuki", columns=["text", "text"]))
@ -100,7 +100,7 @@ def test_search_duplicate_columns_are_deduped(fresh_db):
def test_search_limit_offset(fresh_db):
table = fresh_db["t"]
table = fresh_db.table("t")
table.insert_all(search_records)
table.enable_fts(["text", "country"], fts_version="FTS4")
assert len(list(table.search("are"))) == 2
@ -113,7 +113,7 @@ def test_search_limit_offset(fresh_db):
def test_search_offset_without_limit(fresh_db):
table = fresh_db["t"]
table = fresh_db.table("t")
table.insert_all(search_records)
table.enable_fts(["text", "country"], fts_version="FTS4")
assert [row["rowid"] for row in table.search("are", order_by="rowid")] == [1, 2]
@ -125,7 +125,7 @@ def test_search_offset_without_limit(fresh_db):
@pytest.mark.parametrize("fts_version", ("FTS4", "FTS5"))
def test_search_where(fresh_db, fts_version):
table = fresh_db["t"]
table = fresh_db.table("t")
table.insert_all(search_records)
table.enable_fts(["text", "country"], fts_version=fts_version)
results = list(
@ -142,7 +142,7 @@ def test_search_where(fresh_db, fts_version):
def test_search_where_args_disallows_query(fresh_db):
table = fresh_db["t"]
table = fresh_db.table("t")
with pytest.raises(ValueError) as ex:
list(
table.search(
@ -156,7 +156,7 @@ def test_search_where_args_disallows_query(fresh_db):
def test_search_include_rank(fresh_db):
table = fresh_db["t"]
table = fresh_db.table("t")
table.insert_all(search_records)
table.enable_fts(["text", "country"], fts_version="FTS5")
results = list(table.search("are", include_rank=True))
@ -182,7 +182,7 @@ def test_search_include_rank(fresh_db):
def test_enable_fts_table_names_containing_spaces(fresh_db):
table = fresh_db["test"]
table = fresh_db.table("test")
table.insert({"column with spaces": "in its name"})
table.enable_fts(["column with spaces"])
assert [
@ -196,7 +196,7 @@ def test_enable_fts_table_names_containing_spaces(fresh_db):
def test_populate_fts(fresh_db):
table = fresh_db["populatable"]
table = fresh_db.table("populatable")
table.insert(search_records[0])
table.enable_fts(["text", "country"], fts_version="FTS4")
assert [] == list(table.search("trash pandas"))
@ -217,7 +217,7 @@ def test_populate_fts(fresh_db):
def test_populate_fts_escape_table_names(fresh_db):
# Restricted characters such as colon and dots should be escaped.
table = fresh_db["http://example.com"]
table = fresh_db.table("http://example.com")
table.insert(search_records[0])
table.enable_fts(["text", "country"], fts_version="FTS4")
assert [] == list(table.search("trash pandas"))
@ -238,7 +238,7 @@ def test_populate_fts_escape_table_names(fresh_db):
@pytest.mark.parametrize("fts_version", ("4", "5"))
def test_fts_tokenize(fresh_db, fts_version):
table_name = f"searchable_{fts_version}"
table = fresh_db[table_name]
table = fresh_db.table(table_name)
table.insert_all(search_records)
# Test without porter stemming
table.enable_fts(
@ -266,7 +266,7 @@ def test_fts_tokenize(fresh_db, fts_version):
def test_fts_tokenize_escaped(fresh_db):
# A malicious tokenize value must not be able to break out of the
# string literal in the CREATE VIRTUAL TABLE statement.
table = fresh_db["searchable"]
table = fresh_db.table("searchable")
table.insert_all(search_records)
malicious = "porter'); CREATE TABLE injected(x); --"
with pytest.raises(Exception):
@ -278,7 +278,7 @@ def test_fts_tokenize_escaped(fresh_db):
def test_optimize_fts(fresh_db):
for fts_version in ("4", "5"):
table_name = f"searchable_{fts_version}"
table = fresh_db[table_name]
table = fresh_db.table(table_name)
table.insert_all(search_records)
table.enable_fts(["text", "country"], fts_version=f"FTS{fts_version}")
# You can call optimize successfully against the tables OR their _fts equivalents:
@ -288,11 +288,11 @@ def test_optimize_fts(fresh_db):
"searchable_4_fts",
"searchable_5_fts",
):
fresh_db[table_name].optimize()
fresh_db.table(table_name).optimize()
def test_enable_fts_with_triggers(fresh_db):
table = fresh_db["searchable"]
table = fresh_db.table("searchable")
table.insert(search_records[0])
table.enable_fts(["text", "country"], fts_version="FTS4", create_triggers=True)
rows1 = list(table.search("tanuki"))
@ -321,7 +321,7 @@ def test_enable_fts_with_triggers(fresh_db):
@pytest.mark.parametrize("create_triggers", [True, False])
def test_disable_fts(fresh_db, create_triggers):
table = fresh_db["searchable"]
table = fresh_db.table("searchable")
table.insert(search_records[0])
table.enable_fts(["text", "country"], create_triggers=create_triggers)
assert {
@ -354,7 +354,7 @@ def test_disable_fts(fresh_db, create_triggers):
def test_rebuild_fts(fresh_db):
table = fresh_db["searchable"]
table = fresh_db.table("searchable")
table.insert(search_records[0])
table.enable_fts(["text", "country"])
# Run a search
@ -380,7 +380,7 @@ def test_rebuild_fts(fresh_db):
def test_optimize_and_rebuild_fts_commit(tmpdir, method):
path = str(tmpdir / "test.db")
db = Database(path)
table = db["searchable"]
table = db.table("searchable")
table.insert(search_records[0])
table.enable_fts(["text", "country"])
getattr(table, method)()
@ -390,16 +390,16 @@ def test_optimize_and_rebuild_fts_commit(tmpdir, method):
table.insert(search_records[1])
db.close()
db2 = Database(path)
assert db2["searchable"].count == 2
assert db2.table("searchable").count == 2
db2.close()
@pytest.mark.parametrize("invalid_table", ["does_not_exist", "not_searchable"])
def test_rebuild_fts_invalid(fresh_db, invalid_table):
fresh_db["not_searchable"].insert({"foo": "bar"})
fresh_db.table("not_searchable").insert({"foo": "bar"})
# Raise OperationalError on invalid table
with pytest.raises(sqlite3.OperationalError):
fresh_db[invalid_table].rebuild_fts()
fresh_db.table(invalid_table).rebuild_fts()
@pytest.mark.parametrize("fts_version", ["FTS4", "FTS5"])
@ -408,15 +408,17 @@ def test_rebuild_removes_junk_docsize_rows(tmpdir, fts_version):
path = tmpdir / "test.db"
db = Database(str(path), recursive_triggers=False)
licenses = [{"key": "apache2", "name": "Apache 2"}, {"key": "bsd", "name": "BSD"}]
db["licenses"].insert_all(licenses, pk="key", replace=True)
db["licenses"].enable_fts(["name"], create_triggers=True, fts_version=fts_version)
assert db["licenses_fts_docsize"].count == 2
db.table("licenses").insert_all(licenses, pk="key", replace=True)
db.table("licenses").enable_fts(
["name"], create_triggers=True, fts_version=fts_version
)
assert db.table("licenses_fts_docsize").count == 2
# Bug: insert with replace increases the number of rows in _docsize:
db["licenses"].insert_all(licenses, pk="key", replace=True)
assert db["licenses_fts_docsize"].count == 4
db.table("licenses").insert_all(licenses, pk="key", replace=True)
assert db.table("licenses_fts_docsize").count == 4
# rebuild should fix this:
db["licenses_fts"].rebuild_fts()
assert db["licenses_fts_docsize"].count == 2
db.table("licenses_fts").rebuild_fts()
assert db.table("licenses_fts_docsize").count == 2
@pytest.mark.parametrize(
@ -430,7 +432,7 @@ def test_rebuild_removes_junk_docsize_rows(tmpdir, fts_version):
)
def test_enable_fts_replace(kwargs):
db = Database(memory=True)
db["books"].insert(
db.table("books").insert(
{
"id": 1,
"title": "Habits of Australian Marsupials",
@ -438,31 +440,31 @@ def test_enable_fts_replace(kwargs):
},
pk="id",
)
db["books"].enable_fts(["title", "author"])
assert not db["books"].triggers
assert db["books_fts"].columns_dict.keys() == {"title", "author"}
assert "FTS5" in db["books_fts"].schema
assert "porter" not in db["books_fts"].schema
db.table("books").enable_fts(["title", "author"])
assert not db.table("books").triggers
assert db.table("books_fts").columns_dict.keys() == {"title", "author"}
assert "FTS5" in db.table("books_fts").schema
assert "porter" not in db.table("books_fts").schema
# Now modify the FTS configuration
should_have_changed_columns = "columns" in kwargs
if "columns" not in kwargs:
kwargs["columns"] = ["title", "author"]
db["books"].enable_fts(**kwargs, replace=True)
db.table("books").enable_fts(**kwargs, replace=True)
# Check that the new configuration is correct
if should_have_changed_columns:
assert db["books_fts"].columns_dict.keys() == {"title"}
assert db.table("books_fts").columns_dict.keys() == {"title"}
if "create_triggers" in kwargs:
assert db["books"].triggers
assert db.table("books").triggers
if "fts_version" in kwargs:
assert "FTS4" in db["books_fts"].schema
assert "FTS4" in db.table("books_fts").schema
if "tokenize" in kwargs:
assert "porter" in db["books_fts"].schema
assert "porter" in db.table("books_fts").schema
def test_enable_fts_replace_does_nothing_if_args_the_same():
queries = []
db = Database(memory=True, tracer=lambda sql, params: queries.append((sql, params)))
db["books"].insert(
db.table("books").insert(
{
"id": 1,
"title": "Habits of Australian Marsupials",
@ -470,17 +472,19 @@ def test_enable_fts_replace_does_nothing_if_args_the_same():
},
pk="id",
)
db["books"].enable_fts(["title", "author"], create_triggers=True)
db.table("books").enable_fts(["title", "author"], create_triggers=True)
queries.clear()
# Running that again shouldn't run much SQL:
db["books"].enable_fts(["title", "author"], create_triggers=True, replace=True)
db.table("books").enable_fts(
["title", "author"], create_triggers=True, replace=True
)
# The only SQL that executed should be select statements
assert all(q[0].startswith("select ") for q in queries)
def test_enable_fts_replace_handles_legacy_bracket_quoted_content_table():
db = Database(memory=True)
db["books"].insert(
db.table("books").insert(
{
"id": 1,
"title": "Habits of Australian Marsupials",
@ -495,10 +499,10 @@ def test_enable_fts_replace_handles_legacy_bracket_quoted_content_table():
);
""")
db["books"].enable_fts(["title", "author"], replace=True)
db.table("books").enable_fts(["title", "author"], replace=True)
assert db["books_fts"].columns_dict.keys() == {"title", "author"}
assert 'content="books"' in db["books_fts"].schema
assert db.table("books_fts").columns_dict.keys() == {"title", "author"}
assert 'content="books"' in db.table("books_fts").schema
def test_view_has_no_enable_fts():
@ -506,7 +510,7 @@ def test_view_has_no_enable_fts():
db.create_view("hello", "select 1 + 1")
# Views deliberately do not have an enable_fts() method
with pytest.raises(AttributeError):
db["hello"].enable_fts() # type: ignore[union-attr]
db.view("hello").enable_fts() # type: ignore[union-attr]
@pytest.mark.parametrize(
@ -712,14 +716,14 @@ def test_view_has_no_enable_fts():
)
def test_search_sql(kwargs, fts, expected):
db = Database(memory=True)
db["books"].insert(
db.table("books").insert(
{
"title": "Habits of Australian Marsupials",
"author": "Marlee Hawkins",
}
)
db["books"].enable_fts(["title", "author"], fts_version=fts)
sql = db["books"].search_sql(**kwargs)
db.table("books").enable_fts(["title", "author"], fts_version=fts)
sql = db.table("books").search_sql(**kwargs)
assert sql == expected
@ -740,7 +744,7 @@ def test_search_sql(kwargs, fts, expected):
),
)
def test_quote_fts_query(fresh_db, input, expected):
table = fresh_db["searchable"]
table = fresh_db.table("searchable")
table.insert_all(search_records)
table.enable_fts(["text", "country"])
quoted = fresh_db.quote_fts(input)
@ -750,7 +754,7 @@ def test_quote_fts_query(fresh_db, input, expected):
def test_search_quote(fresh_db):
table = fresh_db["searchable"]
table = fresh_db.table("searchable")
table.insert_all(search_records)
table.enable_fts(["text", "country"])
query = "cat's"
@ -763,7 +767,7 @@ def test_search_quote(fresh_db):
def test_enable_fts_cli_on_view_errors(tmpdir):
db_path = str(tmpdir / "test.db")
db = Database(db_path)
db["t"].insert({"text": "hello"})
db.table("t").insert({"text": "hello"})
db.create_view("v", "select * from t")
db.close()
from click.testing import CliRunner

View file

@ -4,14 +4,14 @@ from sqlite_utils.db import NotFoundError
def test_get_rowid(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
cleo = {"name": "Cleo", "age": 4}
row_id = dogs.insert(cleo).last_rowid
assert cleo == dogs.get(row_id)
def test_get_primary_key(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
cleo = {"name": "Cleo", "age": 4, "id": 5}
last_pk = dogs.insert(cleo, pk="id").last_pk
assert 5 == last_pk
@ -23,10 +23,10 @@ def test_get_primary_key(fresh_db):
[(100, None), (None, None), ((1, 2), "Need 1 primary key value"), ("2", None)],
)
def test_get_not_found(argument, expected_msg, fresh_db):
fresh_db["dogs"].insert(
fresh_db.table("dogs").insert(
{"id": 1, "name": "Cleo", "age": 4, "is_good": True}, pk="id"
)
with pytest.raises(NotFoundError) as excinfo:
fresh_db["dogs"].get(argument)
fresh_db.table("dogs").get(argument)
if expected_msg is not None:
assert expected_msg == excinfo.value.args[0]

View file

@ -45,7 +45,7 @@ def test_add_geometry_column():
coord_dimension="XY",
)
assert db["geometry_columns"].get(["locations", "geometry"]) == {
assert db.table("geometry_columns").get(["locations", "geometry"]) == {
"f_table_name": "locations",
"f_geometry_column": "geometry",
"geometry_type": 1, # point
@ -133,7 +133,7 @@ def test_cli_add_geometry_column(tmpdir):
db = Database(str(db_path))
db.init_spatialite()
table = db["locations"].create({"name": str})
table = db.table("locations").create({"name": str})
result = CliRunner().invoke(
cli,
@ -149,7 +149,7 @@ def test_cli_add_geometry_column(tmpdir):
assert result.exit_code == 0
assert db["geometry_columns"].get(["locations", "geometry"]) == {
assert db.table("geometry_columns").get(["locations", "geometry"]) == {
"f_table_name": "locations",
"f_geometry_column": "geometry",
"geometry_type": 1, # point
@ -164,7 +164,7 @@ def test_cli_add_geometry_column_options(tmpdir):
db_path = tmpdir / "spatial.db"
db = Database(str(db_path))
db.init_spatialite()
table = db["locations"].create({"name": str})
table = db.table("locations").create({"name": str})
result = CliRunner().invoke(
cli,
@ -183,7 +183,7 @@ def test_cli_add_geometry_column_options(tmpdir):
assert result.exit_code == 0
assert db["geometry_columns"].get(["locations", "geometry"]) == {
assert db.table("geometry_columns").get(["locations", "geometry"]) == {
"f_table_name": "locations",
"f_geometry_column": "geometry",
"geometry_type": 3, # polygon
@ -202,7 +202,7 @@ def test_cli_add_geometry_column_invalid_type(tmpdir):
db = Database(str(db_path))
db.init_spatialite()
table = db["locations"].create({"name": str})
table = db.table("locations").create({"name": str})
result = CliRunner().invoke(
cli,
@ -225,7 +225,7 @@ def test_cli_create_spatial_index(tmpdir):
db = Database(str(db_path))
db.init_spatialite()
table = db["locations"].create({"name": str})
table = db.table("locations").create({"name": str})
table.add_geometry_column("geometry", "POINT")
result = CliRunner().invoke(

View file

@ -11,8 +11,8 @@ def test_roundtrip_integers(integer):
row = {
"integer": integer,
}
db["test"].insert(row)
assert list(db["test"].rows) == [row]
db.table("test").insert(row)
assert list(db.table("test").rows) == [row]
@given(st.text())
@ -21,8 +21,8 @@ def test_roundtrip_text(text):
row = {
"text": text,
}
db["test"].insert(row)
assert list(db["test"].rows) == [row]
db.table("test").insert(row)
assert list(db.table("test").rows) == [row]
@given(st.binary(max_size=1024 * 1024))
@ -31,8 +31,8 @@ def test_roundtrip_binary(binary):
row = {
"binary": binary,
}
db["test"].insert(row)
assert list(db["test"].rows) == [row]
db.table("test").insert(row)
assert list(db.table("test").rows) == [row]
@given(st.floats(allow_nan=False))
@ -41,5 +41,5 @@ def test_roundtrip_floats(floats):
row = {
"floats": floats,
}
db["test"].insert(row)
assert list(db["test"].rows) == [row]
db.table("test").insert(row)
assert list(db.table("test").rows) == [row]

View file

@ -57,7 +57,7 @@ def test_insert_files(silent, pk_args, expected_pks):
)
assert result.exit_code == 0, result.stdout
db = Database(db_path)
rows_by_path = {r["path"]: r for r in db["files"].rows}
rows_by_path = {r["path"]: r for r in db.table("files").rows}
one, two, three = (
rows_by_path["one.txt"],
rows_by_path["two.txt"],
@ -114,7 +114,7 @@ def test_insert_files(silent, pk_args, expected_pks):
for colname, expected_type in expected_types.items():
for row in (one, two, three):
assert isinstance(row[colname], expected_type)
assert set(db["files"].pks) == set(expected_pks)
assert set(db.table("files").pks) == set(expected_pks)
@pytest.mark.parametrize(
@ -144,7 +144,7 @@ def test_insert_files_stdin(use_text, encoding, input, expected):
)
assert result.exit_code == 0, result.stdout
db = Database(db_path)
row = next(iter(db["files"].rows))
row = next(iter(db.table("files").rows))
key = "content"
if use_text:
key = "content_text"

View file

@ -1,6 +1,6 @@
import pytest
from sqlite_utils.db import Check, Database, Index, View, XIndex, XIndexColumn
from sqlite_utils.db import Check, Database, Index, Table, View, XIndex, XIndexColumn
def _check_supports_strict():
@ -21,10 +21,10 @@ def test_view_names(fresh_db):
def test_table_names_fts4(existing_db):
existing_db["woo"].insert({"title": "Hello"}).enable_fts(
existing_db.table("woo").insert({"title": "Hello"}).enable_fts(
["title"], fts_version="FTS4"
)
existing_db["woo2"].insert({"title": "Hello"}).enable_fts(
existing_db.table("woo2").insert({"title": "Hello"}).enable_fts(
["title"], fts_version="FTS5"
)
assert ["woo_fts"] == existing_db.table_names(fts4=True)
@ -32,17 +32,17 @@ def test_table_names_fts4(existing_db):
def test_detect_fts(existing_db):
existing_db["woo"].insert({"title": "Hello"}).enable_fts(
existing_db.table("woo").insert({"title": "Hello"}).enable_fts(
["title"], fts_version="FTS4"
)
existing_db["woo2"].insert({"title": "Hello"}).enable_fts(
existing_db.table("woo2").insert({"title": "Hello"}).enable_fts(
["title"], fts_version="FTS5"
)
assert "woo_fts" == existing_db["woo"].detect_fts()
assert "woo_fts" == existing_db["woo_fts"].detect_fts()
assert "woo2_fts" == existing_db["woo2"].detect_fts()
assert "woo2_fts" == existing_db["woo2_fts"].detect_fts()
assert existing_db["foo"].detect_fts() is None
assert "woo_fts" == existing_db.table("woo").detect_fts()
assert "woo_fts" == existing_db.table("woo_fts").detect_fts()
assert "woo2_fts" == existing_db.table("woo2").detect_fts()
assert "woo2_fts" == existing_db.table("woo2_fts").detect_fts()
assert existing_db.table("foo").detect_fts() is None
@pytest.mark.parametrize("reverse_order", (True, False))
@ -52,14 +52,14 @@ def test_detect_fts_similar_tables(fresh_db, reverse_order):
if reverse_order:
table1, table2 = table2, table1
fresh_db[table1].insert({"title": "Hello"}).enable_fts(
fresh_db.table(table1).insert({"title": "Hello"}).enable_fts(
["title"], fts_version="FTS4"
)
fresh_db[table2].insert({"title": "Hello"}).enable_fts(
fresh_db.table(table2).insert({"title": "Hello"}).enable_fts(
["title"], fts_version="FTS4"
)
assert fresh_db[table1].detect_fts() == f"{table1}_fts"
assert fresh_db[table2].detect_fts() == f"{table2}_fts"
assert fresh_db.table(table1).detect_fts() == f"{table1}_fts"
assert fresh_db.table(table2).detect_fts() == f"{table2}_fts"
def test_tables(existing_db):
@ -77,26 +77,34 @@ def test_views(fresh_db):
assert view.columns_dict == {"1": str}
def test_getitem_returns_table_or_view(fresh_db):
fresh_db.table("items").insert({"id": 1}, pk="id")
fresh_db.create_view("item_ids", "select id from items")
assert isinstance(fresh_db["items"], Table)
assert isinstance(fresh_db["item_ids"], View)
def test_count(existing_db):
assert existing_db["foo"].count == 3
assert existing_db["foo"].count_where() == 3
assert existing_db["foo"].execute_count() == 3
assert existing_db.table("foo").count == 3
assert existing_db.table("foo").count_where() == 3
assert existing_db.table("foo").execute_count() == 3
def test_count_where(existing_db):
assert existing_db["foo"].count_where("text != ?", ["two"]) == 2
assert existing_db["foo"].count_where("text != :t", {"t": "two"}) == 2
assert existing_db.table("foo").count_where("text != ?", ["two"]) == 2
assert existing_db.table("foo").count_where("text != :t", {"t": "two"}) == 2
def test_columns(existing_db):
table = existing_db["foo"]
table = existing_db.table("foo")
assert [{"name": "text", "type": "TEXT"}] == [
{"name": col.name, "type": col.type} for col in table.columns
]
def test_table_schema(existing_db):
assert existing_db["foo"].schema == "CREATE TABLE foo (text TEXT)"
assert existing_db.table("foo").schema == "CREATE TABLE foo (text TEXT)"
def test_database_schema(existing_db):
@ -104,9 +112,9 @@ def test_database_schema(existing_db):
def test_table_repr(fresh_db):
table = fresh_db["dogs"].insert({"name": "Cleo", "age": 4})
table = fresh_db.table("dogs").insert({"name": "Cleo", "age": 4})
assert "<Table dogs (name, age)>" == repr(table)
assert "<Table cats (does not exist yet)>" == repr(fresh_db["cats"])
assert "<Table cats (does not exist yet)>" == repr(fresh_db.table("cats"))
def test_indexes(fresh_db):
@ -125,7 +133,7 @@ def test_indexes(fresh_db):
columns=["c2", "c3"],
),
Index(seq=1, name="Gosh_c1", unique=0, origin="c", partial=0, columns=["c1"]),
] == fresh_db["Gosh"].indexes
] == fresh_db.table("Gosh").indexes
def test_xindexes(fresh_db):
@ -134,7 +142,7 @@ def test_xindexes(fresh_db):
create index Gosh_c1 on Gosh(c1);
create index Gosh_c2c3 on Gosh(c2, c3 desc);
""")
assert fresh_db["Gosh"].xindexes == [
assert fresh_db.table("Gosh").xindexes == [
XIndex(
name="Gosh_c2c3",
columns=[
@ -166,15 +174,15 @@ def test_xindexes(fresh_db):
def test_guess_foreign_table(fresh_db, column, expected_table_guess):
fresh_db.create_table("authors", {"name": str})
fresh_db.create_table("genre", {"name": str})
assert expected_table_guess == fresh_db["books"].guess_foreign_table(column)
assert expected_table_guess == fresh_db.table("books").guess_foreign_table(column)
@pytest.mark.parametrize(
"pk,expected", ((None, ["rowid"]), ("id", ["id"]), (["id", "id2"], ["id", "id2"]))
)
def test_pks(fresh_db, pk, expected):
fresh_db["foo"].insert_all([{"id": 1, "id2": 2}], pk=pk)
assert expected == fresh_db["foo"].pks
fresh_db.table("foo").insert_all([{"id": 1, "id2": 2}], pk=pk)
assert expected == fresh_db.table("foo").pks
def test_checks(fresh_db):
@ -185,7 +193,7 @@ def test_checks(fresh_db):
CONSTRAINT within_maximum CHECK(score <= maximum)
)
""")
scores = fresh_db["scores"]
scores = fresh_db.table("scores")
expected_column = Check("score > 0", name="positive", column="score")
expected_table = Check("score <= maximum", name="within_maximum")
assert scores.checks == [expected_column, expected_table]
@ -195,26 +203,26 @@ def test_checks(fresh_db):
def test_checks_nonexistent_and_virtual_tables(fresh_db):
assert fresh_db["does_not_exist"].checks == []
fresh_db["searchable"].insert({"text": "hello"}).enable_fts(
assert fresh_db.table("does_not_exist").checks == []
fresh_db.table("searchable").insert({"text": "hello"}).enable_fts(
["text"], fts_version="FTS5"
)
assert fresh_db["searchable_fts"].checks == []
assert fresh_db.table("searchable_fts").checks == []
def test_triggers_and_triggers_dict(fresh_db):
assert [] == fresh_db.triggers
authors = fresh_db["authors"]
authors = fresh_db.table("authors")
authors.insert_all(
[
{"name": "Frank Herbert", "famous_works": "Dune"},
{"name": "Neal Stephenson", "famous_works": "Cryptonomicon"},
]
)
fresh_db["other"].insert({"foo": "bar"})
fresh_db.table("other").insert({"foo": "bar"})
assert authors.triggers == []
assert authors.triggers_dict == {}
assert fresh_db["other"].triggers == []
assert fresh_db.table("other").triggers == []
assert fresh_db.triggers_dict == {}
authors.enable_fts(
["name", "famous_works"], fts_version="FTS4", create_triggers=True
@ -226,7 +234,7 @@ def test_triggers_and_triggers_dict(fresh_db):
}
assert expected_triggers == {(t.name, t.table) for t in fresh_db.triggers}
assert expected_triggers == {
(t.name, t.table) for t in fresh_db["authors"].triggers
(t.name, t.table) for t in fresh_db.table("authors").triggers
}
expected_triggers = {
"authors_ai": (
@ -246,13 +254,13 @@ def test_triggers_and_triggers_dict(fresh_db):
),
}
assert authors.triggers_dict == expected_triggers
assert fresh_db["other"].triggers == []
assert fresh_db["other"].triggers_dict == {}
assert fresh_db.table("other").triggers == []
assert fresh_db.table("other").triggers_dict == {}
assert fresh_db.triggers_dict == expected_triggers
def test_has_counts_triggers(fresh_db):
authors = fresh_db["authors"]
authors = fresh_db.table("authors")
authors.insert({"name": "Frank Herbert"})
assert not authors.has_counts_triggers
authors.enable_counts()
@ -301,14 +309,14 @@ def test_has_counts_triggers(fresh_db):
)
def test_virtual_table_using(fresh_db, sql, expected_name, expected_using):
fresh_db.execute(sql)
assert fresh_db[expected_name].virtual_table_using == expected_using
assert fresh_db.table(expected_name).virtual_table_using == expected_using
def test_use_rowid(fresh_db):
fresh_db["rowid_table"].insert({"name": "Cleo"})
fresh_db["regular_table"].insert({"id": 1, "name": "Cleo"}, pk="id")
assert fresh_db["rowid_table"].use_rowid
assert not fresh_db["regular_table"].use_rowid
fresh_db.table("rowid_table").insert({"name": "Cleo"})
fresh_db.table("regular_table").insert({"id": 1, "name": "Cleo"}, pk="id")
assert fresh_db.table("rowid_table").use_rowid
assert not fresh_db.table("regular_table").use_rowid
@pytest.mark.skipif(
@ -327,7 +335,7 @@ def test_use_rowid(fresh_db):
)
def test_table_strict(fresh_db, create_table, expected_strict):
fresh_db.execute(create_table)
table = fresh_db["t"]
table = fresh_db.table("t")
assert table.strict == expected_strict
@ -343,10 +351,10 @@ def test_table_strict(fresh_db, create_table, expected_strict):
),
)
def test_table_default_values(fresh_db, value):
fresh_db["default_values"].insert(
fresh_db.table("default_values").insert(
{"nodefault": 1, "value": value}, defaults={"value": value}
)
default_values = fresh_db["default_values"].default_values
default_values = fresh_db.table("default_values").default_values
assert default_values == {"value": value}
@ -356,8 +364,8 @@ def test_table_default_values_escaped_quotes(fresh_db):
fresh_db.execute(
"create table t (id integer primary key, name text default 'O''Brien')"
)
assert "default 'O''Brien'" in fresh_db["t"].schema
assert fresh_db["t"].default_values == {"name": "O'Brien"}
assert "default 'O''Brien'" in fresh_db.table("t").schema
assert fresh_db.table("t").default_values == {"name": "O'Brien"}
def test_pks_use_primary_key_declaration_order(fresh_db):
@ -365,11 +373,11 @@ def test_pks_use_primary_key_declaration_order(fresh_db):
# pks must follow the declaration order, which is what SQLite uses to
# resolve implicit foreign key references and compound pk lookups
fresh_db.execute("create table t (b text, a text, primary key (a, b))")
assert fresh_db["t"].pks == ["a", "b"]
assert fresh_db.table("t").pks == ["a", "b"]
def test_transform_preserves_compound_pk_declaration_order(fresh_db):
fresh_db.execute("create table t (a text, b text, c text, primary key (b, a))")
fresh_db["t"].transform(drop={"c"})
assert fresh_db["t"].pks == ["b", "a"]
assert 'PRIMARY KEY ("b", "a")' in fresh_db["t"].schema
fresh_db.table("t").transform(drop={"c"})
assert fresh_db.table("t").pks == ["b", "a"]
assert 'PRIMARY KEY ("b", "a")' in fresh_db.table("t").schema

View file

@ -19,9 +19,9 @@ def test_insert_all_list_mode_basic():
yield [2, "Bob", 25]
yield [3, "Charlie", 35]
db["people"].insert_all(data_generator())
db.table("people").insert_all(data_generator())
rows = list(db["people"].rows)
rows = list(db.table("people").rows)
assert len(rows) == 3
assert rows[0] == {"id": 1, "name": "Alice", "age": 30}
assert rows[1] == {"id": 2, "name": "Bob", "age": 25}
@ -37,10 +37,10 @@ def test_insert_all_list_mode_with_pk():
yield [1, "Alice", 95]
yield [2, "Bob", 87]
db["scores"].insert_all(data_generator(), pk="id")
db.table("scores").insert_all(data_generator(), pk="id")
assert db["scores"].pks == ["id"]
rows = list(db["scores"].rows)
assert db.table("scores").pks == ["id"]
rows = list(db.table("scores").rows)
assert len(rows) == 2
@ -54,7 +54,7 @@ def test_upsert_all_list_mode():
yield [1, "Alice", 100]
yield [2, "Bob", 200]
db["data"].insert_all(initial_data(), pk="id")
db.table("data").insert_all(initial_data(), pk="id")
# Upsert with some updates and new records
def upsert_data():
@ -62,9 +62,9 @@ def test_upsert_all_list_mode():
yield [1, "Alice", 150] # Update existing
yield [3, "Charlie", 300] # Insert new
db["data"].upsert_all(upsert_data(), pk="id")
db.table("data").upsert_all(upsert_data(), pk="id")
rows = list(db["data"].rows_where(order_by="id"))
rows = list(db.table("data").rows_where(order_by="id"))
assert len(rows) == 3
assert rows[0] == {"id": 1, "name": "Alice", "value": 150}
assert rows[1] == {"id": 2, "name": "Bob", "value": 200}
@ -81,9 +81,9 @@ def test_list_mode_with_various_types():
yield [2, "Bob", 87.3, False]
yield [3, "Charlie", None, True]
db["mixed"].insert_all(data_generator())
db.table("mixed").insert_all(data_generator())
rows = list(db["mixed"].rows)
rows = list(db.table("mixed").rows)
assert len(rows) == 3
assert rows[0]["score"] == 95.5
assert rows[1]["active"] == 0 # SQLite stores boolean as int
@ -99,7 +99,7 @@ def test_list_mode_error_non_string_columns():
yield ["a", "b", "c"]
with pytest.raises(ValueError, match="must be a list of column name strings"):
db["bad"].insert_all(bad_data())
db.table("bad").insert_all(bad_data())
def test_list_mode_error_mixed_types():
@ -111,7 +111,7 @@ def test_list_mode_error_mixed_types():
yield {"id": 1, "name": "Alice"} # Should be a list, not dict
with pytest.raises(ValueError, match="must also be lists"):
db["bad"].insert_all(bad_data())
db.table("bad").insert_all(bad_data())
def test_list_mode_empty_after_headers():
@ -122,9 +122,9 @@ def test_list_mode_empty_after_headers():
yield ["id", "name", "age"]
# No data rows
result = db["people"].insert_all(data_generator())
result = db.table("people").insert_all(data_generator())
assert result is not None
assert not db["people"].exists()
assert not db.table("people").exists()
def test_list_mode_batch_processing():
@ -136,7 +136,7 @@ def test_list_mode_batch_processing():
for i in range(1000):
yield [i, f"value_{i}"]
db["large"].insert_all(large_data(), batch_size=100)
db.table("large").insert_all(large_data(), batch_size=100)
count = db.execute("SELECT COUNT(*) as c FROM large").fetchone()[0]
assert count == 1000
@ -152,9 +152,9 @@ def test_list_mode_shorter_rows():
yield [2, "Bob"] # Missing age and city
yield [3, "Charlie", 35] # Missing city
db["people"].insert_all(data_generator())
db.table("people").insert_all(data_generator())
rows = list(db["people"].rows_where(order_by="id"))
rows = list(db.table("people").rows_where(order_by="id"))
assert rows[0] == {"id": 1, "name": "Alice", "age": 30, "city": "NYC"}
assert rows[1] == {"id": 2, "name": "Bob", "age": None, "city": None}
assert rows[2] == {"id": 3, "name": "Charlie", "age": 35, "city": None}
@ -170,9 +170,9 @@ def test_backwards_compatibility_dict_mode():
{"id": 2, "name": "Bob", "age": 25},
]
db["people"].insert_all(data)
db.table("people").insert_all(data)
rows = list(db["people"].rows)
rows = list(db.table("people").rows)
assert len(rows) == 2
assert rows[0] == {"id": 1, "name": "Alice", "age": 30}
@ -189,9 +189,9 @@ def test_insert_all_tuple_mode_basic():
yield (2, "Bob", 25)
yield (3, "Charlie", 35)
db["people"].insert_all(data_generator())
db.table("people").insert_all(data_generator())
rows = list(db["people"].rows)
rows = list(db.table("people").rows)
assert len(rows) == 3
assert rows[0] == {"id": 1, "name": "Alice", "age": 30}
assert rows[1] == {"id": 2, "name": "Bob", "age": 25}
@ -211,9 +211,9 @@ def test_insert_all_mixed_list_tuple():
yield [3, "Charlie", 35]
yield (4, "Diana", 40)
db["people"].insert_all(data_generator())
db.table("people").insert_all(data_generator())
rows = list(db["people"].rows)
rows = list(db.table("people").rows)
assert len(rows) == 4
assert rows[0] == {"id": 1, "name": "Alice", "age": 30}
assert rows[1] == {"id": 2, "name": "Bob", "age": 25}
@ -231,7 +231,7 @@ def test_upsert_all_tuple_mode():
yield (1, "Alice", 100)
yield (2, "Bob", 200)
db["data"].insert_all(initial_data(), pk="id")
db.table("data").insert_all(initial_data(), pk="id")
# Upsert with tuples
def upsert_data():
@ -239,9 +239,9 @@ def test_upsert_all_tuple_mode():
yield (1, "Alice", 150) # Update existing
yield (3, "Charlie", 300) # Insert new
db["data"].upsert_all(upsert_data(), pk="id")
db.table("data").upsert_all(upsert_data(), pk="id")
rows = list(db["data"].rows_where(order_by="id"))
rows = list(db.table("data").rows_where(order_by="id"))
assert len(rows) == 3
assert rows[0] == {"id": 1, "name": "Alice", "value": 150}
assert rows[1] == {"id": 2, "name": "Bob", "value": 200}
@ -258,9 +258,9 @@ def test_tuple_mode_shorter_rows():
yield 2, "Bob" # Missing age and city
yield 3, "Charlie", 35 # Missing city
db["people"].insert_all(data_generator())
db.table("people").insert_all(data_generator())
rows = list(db["people"].rows_where(order_by="id"))
rows = list(db.table("people").rows_where(order_by="id"))
assert rows[0] == {"id": 1, "name": "Alice", "age": 30, "city": "NYC"}
assert rows[1] == {"id": 2, "name": "Bob", "age": None, "city": None}
assert rows[2] == {"id": 3, "name": "Charlie", "age": 35, "city": None}
@ -271,18 +271,18 @@ def test_list_mode_single_record_upsert_last_pk():
db = Database(memory=True)
# Create table first
db["data"].insert({"id": 1, "name": "Alice", "value": 100}, pk="id")
db.table("data").insert({"id": 1, "name": "Alice", "value": 100}, pk="id")
# Now upsert a single record using list mode
def upsert_data():
yield ["id", "name", "value"]
yield [1, "Alice", 150] # Update existing
table = db["data"]
table = db.table("data")
table.upsert_all(upsert_data(), pk="id")
# Verify the data was updated
rows = list(db["data"].rows)
rows = list(db.table("data").rows)
assert rows == [{"id": 1, "name": "Alice", "value": 150}]
# Verify last_pk is populated correctly

View file

@ -4,7 +4,7 @@ from sqlite_utils.db import Index
def test_lookup_new_table(fresh_db):
species = fresh_db["species"]
species = fresh_db.table("species")
palm_id = species.lookup({"name": "Palm"})
oak_id = species.lookup({"name": "Oak"})
cherry_id = species.lookup({"name": "Cherry"})
@ -26,7 +26,7 @@ def test_lookup_new_table(fresh_db):
def test_lookup_new_table_compound_key(fresh_db):
species = fresh_db["species"]
species = fresh_db.table("species")
palm_id = species.lookup({"name": "Palm", "type": "Tree"})
oak_id = species.lookup({"name": "Oak", "type": "Tree"})
assert palm_id == species.lookup({"name": "Palm", "type": "Tree"})
@ -70,7 +70,7 @@ def test_lookup_fails_if_constraint_cannot_be_added(fresh_db):
def test_lookup_with_extra_values(fresh_db):
species = fresh_db["species"]
species = fresh_db.table("species")
id = species.lookup({"name": "Palm", "type": "Tree"}, {"first_seen": "2020-01-01"})
assert species.get(id) == {
"id": 1,
@ -90,9 +90,9 @@ def test_lookup_with_extra_values(fresh_db):
def test_lookup_with_extra_insert_parameters(fresh_db):
other_table = fresh_db["other_table"]
other_table = fresh_db.table("other_table")
other_table.insert({"id": 1, "name": "Name"}, pk="id")
species = fresh_db["species"]
species = fresh_db.table("species")
id = species.lookup(
{"name": "Palm", "type": "Tree"},
{
@ -156,15 +156,15 @@ def test_lookup_with_extra_insert_parameters(fresh_db):
@pytest.mark.parametrize("strict", (False, True))
def test_lookup_new_table_strict(fresh_db, strict):
fresh_db["species"].lookup({"name": "Palm"}, strict=strict)
assert fresh_db["species"].strict == strict or not fresh_db.supports_strict
fresh_db.table("species").lookup({"name": "Palm"}, strict=strict)
assert fresh_db.table("species").strict == strict or not fresh_db.supports_strict
def test_lookup_null_value_idempotent(fresh_db):
# https://github.com/simonw/sqlite-utils/issues/186
# Repeated lookups of a null value should return the same row,
# not insert a duplicate row each time
species = fresh_db["species"]
species = fresh_db.table("species")
first_id = species.lookup({"name": None})
second_id = species.lookup({"name": None})
assert first_id == second_id
@ -172,7 +172,7 @@ def test_lookup_null_value_idempotent(fresh_db):
def test_lookup_compound_key_with_null_idempotent(fresh_db):
species = fresh_db["species"]
species = fresh_db.table("species")
palm_id = species.lookup({"name": "Palm", "type": None})
oak_id = species.lookup({"name": "Oak", "type": "Tree"})
assert palm_id == species.lookup({"name": "Palm", "type": None})

View file

@ -4,45 +4,45 @@ from sqlite_utils.db import ForeignKey, NoObviousTable
def test_insert_m2m_single(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo"}, pk="id").m2m(
"humans", {"id": 1, "name": "Natalie D"}, pk="id"
)
assert {"dogs_humans", "humans", "dogs"} == set(fresh_db.table_names())
humans = fresh_db["humans"]
dogs_humans = fresh_db["dogs_humans"]
humans = fresh_db.table("humans")
dogs_humans = fresh_db.table("dogs_humans")
assert [{"id": 1, "name": "Natalie D"}] == list(humans.rows)
assert [{"humans_id": 1, "dogs_id": 1}] == list(dogs_humans.rows)
def test_insert_m2m_alter(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo"}, pk="id").m2m(
"humans", {"id": 1, "name": "Natalie D"}, pk="id"
)
dogs.update(1).m2m(
"humans", {"id": 2, "name": "Simon W", "nerd": True}, pk="id", alter=True
)
assert list(fresh_db["humans"].rows) == [
assert list(fresh_db.table("humans").rows) == [
{"id": 1, "name": "Natalie D", "nerd": None},
{"id": 2, "name": "Simon W", "nerd": 1},
]
assert list(fresh_db["dogs_humans"].rows) == [
assert list(fresh_db.table("dogs_humans").rows) == [
{"humans_id": 1, "dogs_id": 1},
{"humans_id": 2, "dogs_id": 1},
]
def test_insert_m2m_list(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo"}, pk="id").m2m(
"humans",
[{"id": 1, "name": "Natalie D"}, {"id": 2, "name": "Simon W"}],
pk="id",
)
assert {"dogs", "humans", "dogs_humans"} == set(fresh_db.table_names())
humans = fresh_db["humans"]
dogs_humans = fresh_db["dogs_humans"]
humans = fresh_db.table("humans")
dogs_humans = fresh_db.table("dogs_humans")
assert [{"humans_id": 1, "dogs_id": 1}, {"humans_id": 2, "dogs_id": 1}] == list(
dogs_humans.rows
)
@ -68,7 +68,7 @@ def test_insert_m2m_iterable(fresh_db):
def iterable():
yield from iterable_records
platypuses = fresh_db["platypuses"]
platypuses = fresh_db.table("platypuses")
platypuses.insert({"id": 1, "name": "Perry"}, pk="id").m2m(
"humans",
iterable(),
@ -76,8 +76,8 @@ def test_insert_m2m_iterable(fresh_db):
)
assert {"platypuses", "humans", "humans_platypuses"} == set(fresh_db.table_names())
humans = fresh_db["humans"]
humans_platypuses = fresh_db["humans_platypuses"]
humans = fresh_db.table("humans")
humans_platypuses = fresh_db.table("humans_platypuses")
assert [
{"humans_id": 1, "platypuses_id": 1},
{"humans_id": 2, "platypuses_id": 1},
@ -111,14 +111,14 @@ def test_m2m_with_table_objects(fresh_db):
assert expected_tables == set(fresh_db.table_names())
assert dogs.count == 1
assert humans.count == 2
assert fresh_db["dogs_humans"].count == 2
assert fresh_db.table("dogs_humans").count == 2
def test_m2m_lookup(fresh_db):
people = fresh_db.table("people", pk="id")
people.insert({"name": "Wahyu"}).m2m("tags", lookup={"tag": "Coworker"})
people_tags = fresh_db["people_tags"]
tags = fresh_db["tags"]
people_tags = fresh_db.table("people_tags")
tags = fresh_db.table("tags")
assert people_tags.exists()
assert tags.exists()
assert [
@ -150,9 +150,9 @@ def test_m2m_explicit_table_name_argument(fresh_db):
people.insert({"name": "Wahyu"}).m2m(
"tags", lookup={"tag": "Coworker"}, m2m_table="tagged"
)
assert fresh_db["tags"].exists
assert fresh_db["tagged"].exists
assert not fresh_db["people_tags"].exists()
assert fresh_db.table("tags").exists
assert fresh_db.table("tagged").exists
assert not fresh_db.table("people_tags").exists()
def test_m2m_table_candidates(fresh_db):
@ -181,25 +181,25 @@ def test_uses_existing_m2m_table_if_exists(fresh_db):
# Code should look for an existing table with fks to both tables
# and use that if it exists.
people = fresh_db.create_table("people", {"id": int, "name": str}, pk="id")
fresh_db["tags"].lookup({"tag": "Coworker"})
fresh_db.table("tags").lookup({"tag": "Coworker"})
fresh_db.create_table(
"tagged",
{"people_id": int, "tags_id": int},
foreign_keys=["people_id", "tags_id"],
)
people.insert({"name": "Wahyu"}).m2m("tags", lookup={"tag": "Coworker"})
assert fresh_db["tags"].exists()
assert fresh_db["tagged"].exists()
assert not fresh_db["people_tags"].exists()
assert not fresh_db["tags_people"].exists()
assert [{"people_id": 1, "tags_id": 1}] == list(fresh_db["tagged"].rows)
assert fresh_db.table("tags").exists()
assert fresh_db.table("tagged").exists()
assert not fresh_db.table("people_tags").exists()
assert not fresh_db.table("tags_people").exists()
assert [{"people_id": 1, "tags_id": 1}] == list(fresh_db.table("tagged").rows)
def test_requires_explicit_m2m_table_if_multiple_options(fresh_db):
# If the code scans for m2m tables and finds more than one candidate
# it should require that the m2m_table=x argument is used
people = fresh_db.create_table("people", {"id": int, "name": str}, pk="id")
fresh_db["tags"].lookup({"tag": "Coworker"})
fresh_db.table("tags").lookup({"tag": "Coworker"})
fresh_db.create_table(
"tagged",
{"people_id": int, "tags_id": int},

View file

@ -10,11 +10,11 @@ def migrations():
@migrations()
def m001(db):
db["dogs"].insert({"name": "Cleo"})
db.table("dogs").insert({"name": "Cleo"})
@migrations()
def m002(db):
db["cats"].create({"name": str})
db.table("cats").create({"name": str})
db.execute("insert into dogs (name) values ('Pancakes')")
return migrations
@ -28,11 +28,11 @@ def migrations_not_ordered_alphabetically():
@migrations()
def m002(db):
db["dogs"].insert({"name": "Cleo"})
db.table("dogs").insert({"name": "Cleo"})
@migrations()
def m001(db):
db["cats"].create({"name": str})
db.table("cats").create({"name": str})
db.execute("insert into dogs (name) values ('Pancakes')")
return migrations
@ -44,7 +44,7 @@ def migrations2():
@migrations()
def m001(db):
db["dogs2"].insert({"name": "Cleo"})
db.table("dogs2").insert({"name": "Cleo"})
return migrations
@ -96,7 +96,7 @@ def test_applied_at_is_a_string(migrations):
def test_failing_migration_rolls_back(migrations):
@migrations()
def m003(db):
db["birds"].create({"name": str})
db.table("birds").create({"name": str})
db.execute("insert into dogs (name) values ('Dozer')")
raise ValueError("boom")
@ -105,7 +105,7 @@ def test_failing_migration_rolls_back(migrations):
migrations.apply(db)
# m001 and m002 committed before the failure and stay applied
assert set(db.table_names()) == {"_sqlite_migrations", "dogs", "cats"}
assert [r["name"] for r in db["dogs"].rows] == ["Cleo", "Pancakes"]
assert [r["name"] for r in db.table("dogs").rows] == ["Cleo", "Pancakes"]
assert [m.name for m in migrations.applied(db)] == ["m001", "m002"]
# Everything m003 did was rolled back and it is still pending
assert [m.name for m in migrations.pending(db)] == ["m003"]
@ -117,11 +117,11 @@ def test_rerun_after_failure_applies_each_migration_once():
@migrations()
def m001(db):
db["dogs"].insert({"name": "Cleo"})
db.table("dogs").insert({"name": "Cleo"})
@migrations()
def m002(db):
db["dogs"].insert({"name": "Pancakes"})
db.table("dogs").insert({"name": "Pancakes"})
if state["fail"]:
raise ValueError("boom")
@ -131,7 +131,7 @@ def test_rerun_after_failure_applies_each_migration_once():
state["fail"] = False
migrations.apply(db)
# m001 must not have been re-applied, m002 applied exactly once
assert [r["name"] for r in db["dogs"].rows] == ["Cleo", "Pancakes"]
assert [r["name"] for r in db.table("dogs").rows] == ["Cleo", "Pancakes"]
assert [m.name for m in migrations.applied(db)] == ["m001", "m002"]
@ -142,7 +142,7 @@ def test_non_transactional_migration_allows_vacuum(tmpdir):
@migrations()
def m001(db):
db["dogs"].insert({"name": "Cleo"})
db.table("dogs").insert({"name": "Cleo"})
@migrations(transactional=False)
def m002(db):
@ -185,11 +185,13 @@ def test_apply_composes_inside_outer_transaction(migrations):
)
def test_upgrades_sqlite_migrations(migrations, create_table, pk):
db = sqlite_utils.Database(memory=True)
db["_sqlite_migrations"].create(create_table, pk=pk)
db.table("_sqlite_migrations").create(create_table, pk=pk)
assert db.table_names() == ["_sqlite_migrations"]
assert db["_sqlite_migrations"].pks == ([pk] if isinstance(pk, str) else list(pk))
assert db.table("_sqlite_migrations").pks == (
[pk] if isinstance(pk, str) else list(pk)
)
migrations.apply(db)
assert db["_sqlite_migrations"].pks == ["id"]
assert db.table("_sqlite_migrations").pks == ["id"]
def test_pending_and_applied_are_read_only(migrations):
@ -227,7 +229,7 @@ def test_stop_before_applied_migration_errors(migrations):
assert "m001" in str(ex.value)
assert "already been applied" in str(ex.value)
# Nothing else was applied
assert not db["cats"].exists()
assert not db.table("cats").exists()
def test_stop_before_applied_migration_errors_before_any_apply(migrations):
@ -238,9 +240,9 @@ def test_stop_before_applied_migration_errors_before_any_apply(migrations):
@only_second()
def m002(db):
db["cats"].create({"name": str})
db.table("cats").create({"name": str})
only_second.apply(db) # m002 applied, m001 still pending
with pytest.raises(ValueError):
migrations.apply(db, stop_before="m002")
assert not db["dogs"].exists()
assert not db.table("dogs").exists()

View file

@ -112,7 +112,7 @@ def test_mutator_commits_by_default(tmp_path, mutate, expected_rows):
db = seed_database(path)
assert not db.conn.in_transaction
mutate(db["items"])
mutate(db.table("items"))
assert current_rows(db) == expected_rows
assert not db.conn.in_transaction
@ -127,7 +127,7 @@ def test_mutator_commits_with_outer_atomic(tmp_path, mutate, expected_rows):
with db.atomic():
assert db.conn.in_transaction
mutate(db["items"])
mutate(db.table("items"))
assert current_rows(db) == expected_rows
assert db.conn.in_transaction
@ -143,7 +143,7 @@ def test_mutator_rolls_back_outer_atomic(tmp_path, mutate, expected_rows):
db = seed_database(path)
with pytest.raises(RollbackTest), db.atomic():
mutate(db["items"])
mutate(db.table("items"))
assert current_rows(db) == expected_rows
assert db.conn.in_transaction
raise RollbackTest

View file

@ -6,7 +6,7 @@ from sqlite_utils.utils import sqlite3
def test_query(fresh_db):
fresh_db["dogs"].insert_all([{"name": "Cleo"}, {"name": "Pancakes"}])
fresh_db.table("dogs").insert_all([{"name": "Cleo"}, {"name": "Pancakes"}])
results = fresh_db.query("select * from dogs order by name desc")
assert isinstance(results, types.GeneratorType)
assert list(results) == [{"name": "Pancakes"}, {"name": "Cleo"}]
@ -20,13 +20,13 @@ def test_query_executes_eagerly(fresh_db):
def test_query_rejects_statements_that_return_no_rows(fresh_db):
fresh_db["dogs"].insert({"name": "Cleo"})
fresh_db.table("dogs").insert({"name": "Cleo"})
with pytest.raises(ValueError) as ex:
fresh_db.query("update dogs set name = 'Cleopaws'")
assert "execute()" in str(ex.value)
# The rejected update was rolled back, and no transaction is left open
assert not fresh_db.conn.in_transaction
assert [row["name"] for row in fresh_db["dogs"].rows] == ["Cleo"]
assert [row["name"] for row in fresh_db.table("dogs").rows] == ["Cleo"]
def test_query_rejected_ddl_is_rolled_back(fresh_db):
@ -37,7 +37,7 @@ def test_query_rejected_ddl_is_rolled_back(fresh_db):
def test_query_rejected_write_inside_transaction_is_rolled_back(fresh_db):
fresh_db["dogs"].insert({"name": "Cleo"})
fresh_db.table("dogs").insert({"name": "Cleo"})
fresh_db.begin()
fresh_db.execute("insert into dogs (name) values ('Pancakes')")
with pytest.raises(ValueError):
@ -45,7 +45,7 @@ def test_query_rejected_write_inside_transaction_is_rolled_back(fresh_db):
# The transaction is still open and the earlier insert is intact
assert fresh_db.conn.in_transaction
fresh_db.commit()
assert [row["name"] for row in fresh_db["dogs"].rows] == ["Cleo", "Pancakes"]
assert [row["name"] for row in fresh_db.table("dogs").rows] == ["Cleo", "Pancakes"]
@pytest.mark.parametrize(
@ -77,7 +77,7 @@ def test_query_comment_prefixed_commit_does_not_commit_transaction(fresh_db):
# A COMMIT hidden behind a leading comment must not slip past the
# keyword check - previously it committed the caller's open
# transaction before the ValueError was raised
fresh_db["dogs"].insert({"name": "Cleo"})
fresh_db.table("dogs").insert({"name": "Cleo"})
fresh_db.begin()
fresh_db.execute("insert into dogs (name) values ('Pancakes')")
with pytest.raises(ValueError):
@ -85,7 +85,7 @@ def test_query_comment_prefixed_commit_does_not_commit_transaction(fresh_db):
# The explicit transaction is still open and can still be rolled back
assert fresh_db.conn.in_transaction
fresh_db.rollback()
assert [row["name"] for row in fresh_db["dogs"].rows] == ["Cleo"]
assert [row["name"] for row in fresh_db.table("dogs").rows] == ["Cleo"]
@pytest.mark.parametrize("sql", ["; COMMIT", "\ufeffCOMMIT"])
@ -94,7 +94,7 @@ def test_query_prefixed_commit_does_not_commit_transaction(fresh_db, sql):
# real token, so the keyword scanner must skip them too - previously
# '; COMMIT' slipped past the check and committed the caller's open
# transaction before raising OperationalError
fresh_db["dogs"].insert({"name": "Cleo"})
fresh_db.table("dogs").insert({"name": "Cleo"})
fresh_db.begin()
fresh_db.execute("insert into dogs (name) values ('Pancakes')")
with pytest.raises(ValueError):
@ -102,7 +102,7 @@ def test_query_prefixed_commit_does_not_commit_transaction(fresh_db, sql):
# The explicit transaction is still open and can still be rolled back
assert fresh_db.conn.in_transaction
fresh_db.rollback()
assert [row["name"] for row in fresh_db["dogs"].rows] == ["Cleo"]
assert [row["name"] for row in fresh_db.table("dogs").rows] == ["Cleo"]
def test_query_error_leaves_no_transaction_open(fresh_db):
@ -190,12 +190,12 @@ def test_first_keyword(sql, expected):
reason="RETURNING requires SQLite 3.35.0 or higher",
)
def test_query_insert_returning(fresh_db):
fresh_db["dogs"].insert({"name": "Cleo"})
fresh_db.table("dogs").insert({"name": "Cleo"})
rows = list(
fresh_db.query("insert into dogs (name) values ('Pancakes') returning name")
)
assert rows == [{"name": "Pancakes"}]
assert fresh_db["dogs"].count == 2
assert fresh_db.table("dogs").count == 2
@pytest.mark.skipif(
@ -207,7 +207,7 @@ def test_query_insert_returning_commits_without_iteration(tmpdir):
path = str(tmpdir / "test.db")
db = Database(path)
db["dogs"].insert({"name": "Cleo"})
db.table("dogs").insert({"name": "Cleo"})
# Never iterate over the results
db.query("insert into dogs (name) values ('Pancakes') returning name")
assert not db.conn.in_transaction
@ -227,7 +227,7 @@ def test_query_insert_returning_partial_iteration_still_commits(tmpdir):
path = str(tmpdir / "test.db")
db = Database(path)
db["dogs"].insert({"name": "Cleo"})
db.table("dogs").insert({"name": "Cleo"})
row = next(
db.query(
"insert into dogs (name) values ('Pancakes'), ('Marnie') returning name"
@ -246,7 +246,7 @@ def test_query_insert_returning_partial_iteration_still_commits(tmpdir):
reason="RETURNING requires SQLite 3.35.0 or higher",
)
def test_query_insert_returning_respects_explicit_transaction(fresh_db):
fresh_db["dogs"].insert({"name": "Cleo"})
fresh_db.table("dogs").insert({"name": "Cleo"})
fresh_db.begin()
rows = list(
fresh_db.query("insert into dogs (name) values ('Pancakes') returning name")
@ -255,13 +255,13 @@ def test_query_insert_returning_respects_explicit_transaction(fresh_db):
# Still inside the explicit transaction - not committed
assert fresh_db.conn.in_transaction
fresh_db.rollback()
assert [row["name"] for row in fresh_db["dogs"].rows] == ["Cleo"]
assert [row["name"] for row in fresh_db.table("dogs").rows] == ["Cleo"]
def test_query_duplicate_column_names_are_deduped(fresh_db):
# https://github.com/simonw/sqlite-utils/issues/624
fresh_db["one"].insert({"id": 1, "value": "left"})
fresh_db["two"].insert({"id": 2, "value": "right"})
fresh_db.table("one").insert({"id": 1, "value": "left"})
fresh_db.table("two").insert({"id": 2, "value": "right"})
rows = list(
fresh_db.query("select one.id, two.id, one.value, two.value from one, two")
)
@ -277,7 +277,7 @@ def test_query_deduped_column_avoids_existing_names(fresh_db):
def test_execute_returning_dicts(fresh_db):
# Like db.query() but returns a list, included for backwards compatibility
# see https://github.com/simonw/sqlite-utils/issues/290
fresh_db["test"].insert({"id": 1, "bar": 2}, pk="id")
fresh_db.table("test").insert({"id": 1, "bar": 2}, pk="id")
assert fresh_db.execute_returning_dicts("select * from test") == [
{"id": 1, "bar": 2}
]

View file

@ -8,7 +8,7 @@ from sqlite_utils.utils import sqlite3
@pytest.fixture
def dates_db(fresh_db):
fresh_db["example"].insert_all(
fresh_db.table("example").insert_all(
[
{"id": 1, "dt": "5th October 2019 12:04"},
{"id": 2, "dt": "6th October 2019 00:05:06"},
@ -21,8 +21,8 @@ def dates_db(fresh_db):
def test_parsedate(dates_db):
dates_db["example"].convert("dt", recipes.parsedate)
assert list(dates_db["example"].rows) == [
dates_db.table("example").convert("dt", recipes.parsedate)
assert list(dates_db.table("example").rows) == [
{"id": 1, "dt": "2019-10-05"},
{"id": 2, "dt": "2019-10-06"},
{"id": 3, "dt": ""},
@ -31,8 +31,8 @@ def test_parsedate(dates_db):
def test_parsedatetime(dates_db):
dates_db["example"].convert("dt", recipes.parsedatetime)
assert list(dates_db["example"].rows) == [
dates_db.table("example").convert("dt", recipes.parsedatetime)
assert list(dates_db.table("example").rows) == [
{"id": 1, "dt": "2019-10-05T12:04:00"},
{"id": 2, "dt": "2019-10-06T00:05:06"},
{"id": 3, "dt": ""},
@ -50,16 +50,16 @@ def test_parsedatetime(dates_db):
),
)
def test_dayfirst_yearfirst(fresh_db, recipe, kwargs, expected):
fresh_db["example"].insert_all(
fresh_db.table("example").insert_all(
[
{"id": 1, "dt": "03/04/05"},
],
pk="id",
)
fresh_db["example"].convert(
fresh_db.table("example").convert(
"dt", lambda value: getattr(recipes, recipe)(value, **kwargs)
)
assert list(fresh_db["example"].rows) == [
assert list(fresh_db.table("example").rows) == [
{"id": 1, "dt": expected},
]
@ -68,7 +68,7 @@ def test_dayfirst_yearfirst(fresh_db, recipe, kwargs, expected):
@pytest.mark.parametrize("fn", ("parsedate", "parsedatetime"))
def test_dateparse_errors_raises(fresh_db, fn):
"""Test that invalid dates raise errors when errors=None"""
fresh_db["example"].insert_all(
fresh_db.table("example").insert_all(
[
{"id": 1, "dt": "invalid"},
],
@ -76,30 +76,32 @@ def test_dateparse_errors_raises(fresh_db, fn):
)
# Exception in SQLite callback surfaces as OperationalError
with pytest.raises(sqlite3.OperationalError):
fresh_db["example"].convert("dt", lambda value: getattr(recipes, fn)(value))
fresh_db.table("example").convert(
"dt", lambda value: getattr(recipes, fn)(value)
)
@pytest.mark.parametrize("fn", ("parsedate", "parsedatetime"))
@pytest.mark.parametrize("errors", (recipes.SET_NULL, recipes.IGNORE))
def test_dateparse_errors_handled(fresh_db, fn, errors):
"""Test error handling modes for invalid dates"""
fresh_db["example"].insert_all(
fresh_db.table("example").insert_all(
[
{"id": 1, "dt": "invalid"},
],
pk="id",
)
fresh_db["example"].convert(
fresh_db.table("example").convert(
"dt", lambda value: getattr(recipes, fn)(value, errors=errors)
)
rows = list(fresh_db["example"].rows)
rows = list(fresh_db.table("example").rows)
expected = [{"id": 1, "dt": None if errors is recipes.SET_NULL else "invalid"}]
assert rows == expected
@pytest.mark.parametrize("delimiter", [None, ";", "-"])
def test_jsonsplit(fresh_db, delimiter):
fresh_db["example"].insert_all(
fresh_db.table("example").insert_all(
[
{"id": 1, "tags": (delimiter or ",").join(["foo", "bar"])},
{"id": 2, "tags": (delimiter or ",").join(["bar", "baz"])},
@ -114,8 +116,8 @@ def test_jsonsplit(fresh_db, delimiter):
else:
fn = recipes.jsonsplit
fresh_db["example"].convert("tags", fn)
assert list(fresh_db["example"].rows) == [
fresh_db.table("example").convert("tags", fn)
assert list(fresh_db.table("example").rows) == [
{"id": 1, "tags": '["foo", "bar"]'},
{"id": 2, "tags": '["bar", "baz"]'},
]
@ -130,7 +132,7 @@ def test_jsonsplit(fresh_db, delimiter):
),
)
def test_jsonsplit_type(fresh_db, type, expected):
fresh_db["example"].insert_all(
fresh_db.table("example").insert_all(
[
{"id": 1, "records": "1,2,3"},
],
@ -144,5 +146,5 @@ def test_jsonsplit_type(fresh_db, type, expected):
else:
fn = recipes.jsonsplit
fresh_db["example"].convert("records", fn)
assert json.loads(fresh_db["example"].get(1)["records"]) == expected
fresh_db.table("example").convert("records", fn)
assert json.loads(fresh_db.table("example").get(1)["records"]) == expected

View file

@ -33,8 +33,8 @@ def test_recreate(tmp_path, use_path, create_file_first):
filepath = pathlib.Path(filepath)
if create_file_first:
db = Database(filepath)
db["t1"].insert({"foo": "bar"})
db.table("t1").insert({"foo": "bar"})
assert ["t1"] == db.table_names()
db.close()
Database(filepath, recreate=True)["t2"].insert({"foo": "bar"})
Database(filepath, recreate=True).table("t2").insert({"foo": "bar"})
assert ["t2"] == Database(filepath).table_names()

View file

@ -3,7 +3,7 @@ import pytest
def test_rows(existing_db):
assert [{"text": "one"}, {"text": "two"}, {"text": "three"}] == list(
existing_db["foo"].rows
existing_db.table("foo").rows
)
@ -18,7 +18,7 @@ def test_rows(existing_db):
],
)
def test_rows_where(where, where_args, expected_ids, fresh_db):
table = fresh_db["dogs"]
table = fresh_db.table("dogs")
table.insert_all(
[
{"id": 1, "name": "Cleo", "age": 4, "is_good": True},
@ -41,7 +41,7 @@ def test_rows_where(where, where_args, expected_ids, fresh_db):
],
)
def test_rows_where_order_by(where, order_by, expected_ids, fresh_db):
table = fresh_db["dogs"]
table = fresh_db.table("dogs")
table.insert_all(
[
{"id": 1, "name": "Cleo", "age": 4},
@ -65,7 +65,7 @@ def test_rows_where_order_by(where, order_by, expected_ids, fresh_db):
],
)
def test_rows_where_offset_limit(fresh_db, offset, limit, expected):
table = fresh_db["rows"]
table = fresh_db.table("rows")
table.insert_all([{"id": id} for id in range(1, 101)], pk="id")
assert table.count == 100
assert expected == [
@ -74,13 +74,13 @@ def test_rows_where_offset_limit(fresh_db, offset, limit, expected):
def test_pks_and_rows_where_offset_without_limit(fresh_db):
table = fresh_db["rows"]
table = fresh_db.table("rows")
table.insert_all([{"id": id} for id in range(1, 6)], pk="id")
assert [pk for pk, _ in table.pks_and_rows_where(offset=3, order_by="id")] == [4, 5]
def test_pks_and_rows_where_rowid(fresh_db):
table = fresh_db["rowid_table"]
table = fresh_db.table("rowid_table")
table.insert_all({"number": i + 10} for i in range(3))
pks_and_rows = list(table.pks_and_rows_where())
assert pks_and_rows == [
@ -91,7 +91,7 @@ def test_pks_and_rows_where_rowid(fresh_db):
def test_pks_and_rows_where_simple_pk(fresh_db):
table = fresh_db["simple_pk_table"]
table = fresh_db.table("simple_pk_table")
table.insert_all(({"id": i + 10} for i in range(3)), pk="id")
pks_and_rows = list(table.pks_and_rows_where())
assert pks_and_rows == [
@ -102,7 +102,7 @@ def test_pks_and_rows_where_simple_pk(fresh_db):
def test_pks_and_rows_where_compound_pk(fresh_db):
table = fresh_db["compound_pk_table"]
table = fresh_db.table("compound_pk_table")
table.insert_all(
({"type": "number", "number": i, "plusone": i + 1} for i in range(3)),
pk=("type", "number"),
@ -117,8 +117,8 @@ def test_pks_and_rows_where_compound_pk(fresh_db):
def test_rows_where_duplicate_select_columns_are_deduped(fresh_db):
# https://github.com/simonw/sqlite-utils/issues/624
fresh_db["t"].insert({"id": 1, "name": "Cleo"})
rows = list(fresh_db["t"].rows_where(select="id, id, name"))
fresh_db.table("t").insert({"id": 1, "name": "Cleo"})
rows = list(fresh_db.table("t").rows_where(select="id, id, name"))
assert rows == [{"id": 1, "id_2": 1, "name": "Cleo"}]
@ -130,10 +130,10 @@ def test_pks_and_rows_where_view(fresh_db):
# an AttributeError from View lacking Table-only properties
from sqlite_utils.utils import sqlite3
fresh_db["dogs"].insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db.table("dogs").insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db.create_view("dog_names", "select name from dogs")
try:
result = list(fresh_db["dog_names"].pks_and_rows_where())
result = list(fresh_db.view("dog_names").pks_and_rows_where())
except sqlite3.OperationalError:
pass # SQLite 3.36+: no such column: rowid
else:
@ -144,6 +144,6 @@ def test_pks_and_rows_where_view(fresh_db):
def test_pks_and_rows_where_compound_pk_declaration_order(fresh_db):
# Compound pks are returned in PRIMARY KEY declaration order
fresh_db.execute("create table t (b text, a text, primary key (a, b))")
fresh_db["t"].insert({"a": "A", "b": "B"})
pks_and_rows = list(fresh_db["t"].pks_and_rows_where())
fresh_db.table("t").insert({"a": "A", "b": "B"})
pks_and_rows = list(fresh_db.table("t").pks_and_rows_where())
assert pks_and_rows == [(("A", "B"), {"b": "B", "a": "A"})]

View file

@ -19,7 +19,7 @@ def test_sniff(tmpdir, filepath):
)
assert result.exit_code == 0, result.stdout
db = Database(db_path)
assert list(db["creatures"].rows) == [
assert list(db.table("creatures").rows) == [
{"id": "1", "species": "dog", "name": "Cleo", "age": "5"},
{"id": "2", "species": "dog", "name": "Pancakes", "age": "4"},
{"id": "3", "species": "cat", "name": "Mozie", "age": "8"},

View file

@ -128,7 +128,7 @@ def test_transform_sql_table_with_primary_key(
def tracer(sql, params):
return captured.append((sql, params))
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
if use_pragma_foreign_keys:
fresh_db.conn.execute("PRAGMA foreign_keys=ON")
dogs.insert({"id": 1, "name": "Cleo", "age": "5"}, pk="id")
@ -209,7 +209,7 @@ def test_transform_sql_table_with_no_primary_key(
def tracer(sql, params):
return captured.append((sql, params))
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
if use_pragma_foreign_keys:
fresh_db.conn.execute("PRAGMA foreign_keys=ON")
dogs.insert({"id": 1, "name": "Cleo", "age": "5"})
@ -229,7 +229,7 @@ def test_transform_sql_table_with_no_primary_key(
def test_transform_sql_with_no_primary_key_to_primary_key_of_id(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo", "age": "5"})
assert (
dogs.schema
@ -244,7 +244,7 @@ def test_transform_sql_with_no_primary_key_to_primary_key_of_id(fresh_db):
def test_transform_rename_pk(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo", "age": "5"}, pk="id")
dogs.transform(rename={"id": "pk"})
assert (
@ -265,7 +265,7 @@ def test_transform_preserves_keyword_literal_defaults(fresh_db):
" note TEXT DEFAULT NULL"
")"
)
table = fresh_db["t"]
table = fresh_db.table("t")
table.insert({"id": 1})
before = fresh_db.execute("SELECT is_active, flag, note FROM t").fetchone()
assert before == (1, 0, None)
@ -288,7 +288,7 @@ def test_transform_preserves_keyword_literal_defaults(fresh_db):
def test_transform_not_null(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo", "age": "5"}, pk="id")
dogs.transform(not_null={"name"})
assert (
@ -298,7 +298,7 @@ def test_transform_not_null(fresh_db):
def test_transform_remove_a_not_null(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo", "age": "5"}, not_null={"age"}, pk="id")
dogs.transform(not_null={"name": True, "age": False})
assert (
@ -309,7 +309,7 @@ def test_transform_remove_a_not_null(fresh_db):
@pytest.mark.parametrize("not_null", [{"age"}, {"age": True}])
def test_transform_add_not_null_with_rename(fresh_db, not_null):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo", "age": "5"}, pk="id")
dogs.transform(not_null=not_null, rename={"age": "dog_age"})
assert (
@ -319,7 +319,7 @@ def test_transform_add_not_null_with_rename(fresh_db, not_null):
def test_transform_defaults(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo", "age": 5}, pk="id")
dogs.transform(defaults={"age": 1})
assert (
@ -329,7 +329,7 @@ def test_transform_defaults(fresh_db):
def test_transform_defaults_and_rename_column(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo", "age": 5}, pk="id")
dogs.transform(rename={"age": "dog_age"}, defaults={"age": 1})
assert (
@ -339,7 +339,7 @@ def test_transform_defaults_and_rename_column(fresh_db):
def test_remove_defaults(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo", "age": 5}, defaults={"age": 1}, pk="id")
dogs.transform(defaults={"age": None})
assert (
@ -350,8 +350,8 @@ def test_remove_defaults(fresh_db):
@pytest.fixture
def authors_db(fresh_db):
books = fresh_db["books"]
authors = fresh_db["authors"]
books = fresh_db.table("books")
authors = fresh_db.table("authors")
authors.insert({"id": 5, "name": "Jane McGonical"}, pk="id")
books.insert(
{"id": 2, "title": "Reality is Broken", "author_id": 5},
@ -362,13 +362,13 @@ def authors_db(fresh_db):
def test_transform_foreign_keys_persist(authors_db):
assert authors_db["books"].foreign_keys == [
assert authors_db.table("books").foreign_keys == [
ForeignKey(
table="books", column="author_id", other_table="authors", other_column="id"
)
]
authors_db["books"].transform(rename={"title": "book_title"})
assert authors_db["books"].foreign_keys == [
authors_db.table("books").transform(rename={"title": "book_title"})
assert authors_db.table("books").foreign_keys == [
ForeignKey(
table="books", column="author_id", other_table="authors", other_column="id"
)
@ -381,8 +381,8 @@ def test_transform_foreign_keys_survive_renamed_column(
):
if use_pragma_foreign_keys:
authors_db.conn.execute("PRAGMA foreign_keys=ON")
authors_db["books"].transform(rename={"author_id": "author_id_2"})
assert authors_db["books"].foreign_keys == [
authors_db.table("books").transform(rename={"author_id": "author_id_2"})
assert authors_db.table("books").foreign_keys == [
ForeignKey(
table="books",
column="author_id_2",
@ -393,9 +393,9 @@ def test_transform_foreign_keys_survive_renamed_column(
def _add_country_city_continent(db):
db["country"].insert({"id": 1, "name": "France"}, pk="id")
db["continent"].insert({"id": 2, "name": "Europe"}, pk="id")
db["city"].insert({"id": 24, "name": "Paris"}, pk="id")
db.table("country").insert({"id": 1, "name": "France"}, pk="id")
db.table("continent").insert({"id": 2, "name": "Europe"}, pk="id")
db.table("city").insert({"id": 24, "name": "Paris"}, pk="id")
_CAVEAU = {
@ -413,11 +413,11 @@ def test_transform_drop_foreign_keys(fresh_db, use_pragma_foreign_keys):
fresh_db.conn.execute("PRAGMA foreign_keys=ON")
# Create table with three foreign keys so we can drop two of them
_add_country_city_continent(fresh_db)
fresh_db["places"].insert(
fresh_db.table("places").insert(
_CAVEAU,
foreign_keys=("country", "continent", "city"),
)
assert fresh_db["places"].foreign_keys == [
assert fresh_db.table("places").foreign_keys == [
ForeignKey(
table="places", column="city", other_table="city", other_column="id"
),
@ -432,9 +432,9 @@ def test_transform_drop_foreign_keys(fresh_db, use_pragma_foreign_keys):
),
]
# Drop two of those foreign keys
fresh_db["places"].transform(drop_foreign_keys=("country", "continent"))
fresh_db.table("places").transform(drop_foreign_keys=("country", "continent"))
# Should be only one foreign key now
assert fresh_db["places"].foreign_keys == [
assert fresh_db.table("places").foreign_keys == [
ForeignKey(table="places", column="city", other_table="city", other_column="id")
]
if use_pragma_foreign_keys:
@ -443,17 +443,17 @@ def test_transform_drop_foreign_keys(fresh_db, use_pragma_foreign_keys):
def test_transform_verify_foreign_keys(fresh_db):
fresh_db.conn.execute("PRAGMA foreign_keys=ON")
fresh_db["authors"].insert({"id": 3, "name": "Tina"}, pk="id")
fresh_db["books"].insert(
fresh_db.table("authors").insert({"id": 3, "name": "Tina"}, pk="id")
fresh_db.table("books").insert(
{"id": 1, "title": "Book", "author_id": 3}, pk="id", foreign_keys={"author_id"}
)
# Renaming the id column on authors should break everything
with pytest.raises(OperationalError) as e:
fresh_db["authors"].transform(rename={"id": "id2"})
fresh_db.table("authors").transform(rename={"id": "id2"})
assert e.value.args[0] == 'foreign key mismatch - "books" referencing "authors"'
# This should have rolled us back
assert (
fresh_db["authors"].schema
fresh_db.table("authors").schema
== 'CREATE TABLE "authors" (\n "id" INTEGER PRIMARY KEY,\n "name" TEXT\n)'
)
assert fresh_db.conn.execute("PRAGMA foreign_keys").fetchone()[0]
@ -476,20 +476,22 @@ def test_transform_on_delete_cascade_does_not_delete_records(
author_id INTEGER REFERENCES authors(id) ON DELETE CASCADE
);
""")
fresh_db["authors"].insert({"id": 1, "name": "Ursula K. Le Guin"})
fresh_db["books"].insert({"id": 1, "title": "The Dispossessed", "author_id": 1})
fresh_db.table("authors").insert({"id": 1, "name": "Ursula K. Le Guin"})
fresh_db.table("books").insert(
{"id": 1, "title": "The Dispossessed", "author_id": 1}
)
# Transform the table on the other end of the cascading foreign key
fresh_db["authors"].transform(rename={"name": "author_name"})
assert list(fresh_db["authors"].rows) == [
fresh_db.table("authors").transform(rename={"name": "author_name"})
assert list(fresh_db.table("authors").rows) == [
{"id": 1, "author_name": "Ursula K. Le Guin"}
]
assert list(fresh_db["books"].rows) == [
assert list(fresh_db.table("books").rows) == [
{"id": 1, "title": "The Dispossessed", "author_id": 1}
]
# Transforming the table with the cascading foreign key should not
# delete its records either
fresh_db["books"].transform(rename={"title": "book_title"})
assert list(fresh_db["books"].rows) == [
fresh_db.table("books").transform(rename={"title": "book_title"})
assert list(fresh_db.table("books").rows) == [
{"id": 1, "book_title": "The Dispossessed", "author_id": 1}
]
if use_pragma_foreign_keys:
@ -511,17 +513,19 @@ def test_transform_in_transaction_refuses_destructive_on_delete(fresh_db, on_del
author_id INTEGER REFERENCES authors(id) ON DELETE {on_delete}
);
""")
fresh_db["authors"].insert({"id": 1, "name": "Ursula K. Le Guin"})
fresh_db["books"].insert({"id": 1, "title": "The Dispossessed", "author_id": 1})
previous_schema = fresh_db["authors"].schema
fresh_db.table("authors").insert({"id": 1, "name": "Ursula K. Le Guin"})
fresh_db.table("books").insert(
{"id": 1, "title": "The Dispossessed", "author_id": 1}
)
previous_schema = fresh_db.table("authors").schema
with fresh_db.atomic(), pytest.raises(TransactionError) as excinfo:
fresh_db["authors"].transform(rename={"name": "author_name"})
fresh_db.table("authors").transform(rename={"name": "author_name"})
message = str(excinfo.value)
assert "books" in message
assert f"ON DELETE {on_delete.upper()}" in message
# Nothing should have changed
assert fresh_db["authors"].schema == previous_schema
assert list(fresh_db["books"].rows) == [
assert fresh_db.table("authors").schema == previous_schema
assert list(fresh_db.table("books").rows) == [
{"id": 1, "title": "The Dispossessed", "author_id": 1}
]
assert fresh_db.conn.execute("PRAGMA foreign_keys").fetchone()[0]
@ -538,16 +542,16 @@ def test_transform_in_transaction_refuses_self_referential_cascade(fresh_db):
parent_id INTEGER REFERENCES categories(id) ON DELETE CASCADE
);
""")
fresh_db["categories"].insert_all(
fresh_db.table("categories").insert_all(
[
{"id": 1, "name": "Fiction", "parent_id": None},
{"id": 2, "name": "Science Fiction", "parent_id": 1},
]
)
with fresh_db.atomic(), pytest.raises(TransactionError) as excinfo:
fresh_db["categories"].transform(rename={"name": "title"})
fresh_db.table("categories").transform(rename={"name": "title"})
assert "categories" in str(excinfo.value)
assert fresh_db["categories"].count == 2
assert fresh_db.table("categories").count == 2
def test_transform_in_transaction_allowed_with_no_action_foreign_key(fresh_db):
@ -562,14 +566,16 @@ def test_transform_in_transaction_allowed_with_no_action_foreign_key(fresh_db):
author_id INTEGER REFERENCES authors(id)
);
""")
fresh_db["authors"].insert({"id": 1, "name": "Ursula K. Le Guin"})
fresh_db["books"].insert({"id": 1, "title": "The Dispossessed", "author_id": 1})
fresh_db.table("authors").insert({"id": 1, "name": "Ursula K. Le Guin"})
fresh_db.table("books").insert(
{"id": 1, "title": "The Dispossessed", "author_id": 1}
)
with fresh_db.atomic():
fresh_db["authors"].transform(rename={"name": "author_name"})
assert list(fresh_db["authors"].rows) == [
fresh_db.table("authors").transform(rename={"name": "author_name"})
assert list(fresh_db.table("authors").rows) == [
{"id": 1, "author_name": "Ursula K. Le Guin"}
]
assert list(fresh_db["books"].rows) == [
assert list(fresh_db.table("books").rows) == [
{"id": 1, "title": "The Dispossessed", "author_id": 1}
]
assert fresh_db.conn.execute("PRAGMA foreign_keys").fetchone()[0]
@ -587,11 +593,13 @@ def test_transform_in_transaction_allowed_for_child_table(fresh_db):
author_id INTEGER REFERENCES authors(id) ON DELETE CASCADE
);
""")
fresh_db["authors"].insert({"id": 1, "name": "Ursula K. Le Guin"})
fresh_db["books"].insert({"id": 1, "title": "The Dispossessed", "author_id": 1})
fresh_db.table("authors").insert({"id": 1, "name": "Ursula K. Le Guin"})
fresh_db.table("books").insert(
{"id": 1, "title": "The Dispossessed", "author_id": 1}
)
with fresh_db.atomic():
fresh_db["books"].transform(rename={"title": "book_title"})
assert list(fresh_db["books"].rows) == [
fresh_db.table("books").transform(rename={"title": "book_title"})
assert list(fresh_db.table("books").rows) == [
{"id": 1, "book_title": "The Dispossessed", "author_id": 1}
]
@ -607,24 +615,28 @@ def test_transform_in_transaction_allowed_with_foreign_keys_off(fresh_db):
author_id INTEGER REFERENCES authors(id) ON DELETE CASCADE
);
""")
fresh_db["authors"].insert({"id": 1, "name": "Ursula K. Le Guin"})
fresh_db["books"].insert({"id": 1, "title": "The Dispossessed", "author_id": 1})
fresh_db.table("authors").insert({"id": 1, "name": "Ursula K. Le Guin"})
fresh_db.table("books").insert(
{"id": 1, "title": "The Dispossessed", "author_id": 1}
)
with fresh_db.atomic():
fresh_db["authors"].transform(rename={"name": "author_name"})
assert list(fresh_db["books"].rows) == [
fresh_db.table("authors").transform(rename={"name": "author_name"})
assert list(fresh_db.table("books").rows) == [
{"id": 1, "title": "The Dispossessed", "author_id": 1}
]
def test_transform_add_foreign_keys_from_scratch(fresh_db):
_add_country_city_continent(fresh_db)
fresh_db["places"].insert(_CAVEAU)
fresh_db.table("places").insert(_CAVEAU)
# Should have no foreign keys
assert fresh_db["places"].foreign_keys == []
assert fresh_db.table("places").foreign_keys == []
# Now add them using .transform()
fresh_db["places"].transform(add_foreign_keys=("country", "continent", "city"))
fresh_db.table("places").transform(
add_foreign_keys=("country", "continent", "city")
)
# Should now have all three:
assert fresh_db["places"].foreign_keys == [
assert fresh_db.table("places").foreign_keys == [
ForeignKey(
table="places", column="city", other_table="city", other_column="id"
),
@ -638,7 +650,7 @@ def test_transform_add_foreign_keys_from_scratch(fresh_db):
table="places", column="country", other_table="country", other_column="id"
),
]
assert fresh_db["places"].schema == (
assert fresh_db.table("places").schema == (
'CREATE TABLE "places" (\n'
' "id" INTEGER,\n'
' "name" TEXT,\n'
@ -662,18 +674,18 @@ def test_transform_add_foreign_keys_from_scratch(fresh_db):
)
def test_transform_add_foreign_keys_from_partial(fresh_db, add_foreign_keys):
_add_country_city_continent(fresh_db)
fresh_db["places"].insert(
fresh_db.table("places").insert(
_CAVEAU,
foreign_keys=("city",),
)
# Should have one foreign keys
assert fresh_db["places"].foreign_keys == [
assert fresh_db.table("places").foreign_keys == [
ForeignKey(table="places", column="city", other_table="city", other_column="id")
]
# Now add three more using .transform()
fresh_db["places"].transform(add_foreign_keys=add_foreign_keys)
fresh_db.table("places").transform(add_foreign_keys=add_foreign_keys)
# Should now have all three:
assert fresh_db["places"].foreign_keys == [
assert fresh_db.table("places").foreign_keys == [
ForeignKey(
table="places", column="city", other_table="city", other_column="id"
),
@ -702,14 +714,14 @@ def test_transform_add_foreign_keys_from_partial(fresh_db, add_foreign_keys):
)
def test_transform_replace_foreign_keys(fresh_db, foreign_keys):
_add_country_city_continent(fresh_db)
fresh_db["places"].insert(
fresh_db.table("places").insert(
_CAVEAU,
foreign_keys=("city",),
)
assert len(fresh_db["places"].foreign_keys) == 1
assert len(fresh_db.table("places").foreign_keys) == 1
# Replace with two different ones
fresh_db["places"].transform(foreign_keys=foreign_keys)
assert fresh_db["places"].schema == (
fresh_db.table("places").transform(foreign_keys=foreign_keys)
assert fresh_db.table("places").schema == (
'CREATE TABLE "places" (\n'
' "id" INTEGER,\n'
' "name" TEXT,\n'
@ -729,7 +741,7 @@ def test_transform_preserves_rowids(fresh_db, table_type):
pk = ("id", "name")
elif table_type == "rowid":
pk = None
fresh_db["places"].insert_all(
fresh_db.table("places").insert_all(
[
{"id": "1", "name": "Paris", "country": "France"},
{"id": "2", "name": "London", "country": "UK"},
@ -738,13 +750,13 @@ def test_transform_preserves_rowids(fresh_db, table_type):
pk=pk,
)
# Now delete and insert a row to mix up the `rowid` sequence
fresh_db["places"].delete_where("id = ?", ["2"])
fresh_db["places"].insert({"id": "4", "name": "London", "country": "UK"})
fresh_db.table("places").delete_where("id = ?", ["2"])
fresh_db.table("places").insert({"id": "4", "name": "London", "country": "UK"})
previous_rows = [
tuple(row) for row in fresh_db.execute("select rowid, id, name from places")
]
# Transform it
fresh_db["places"].transform(column_order=("country", "name"))
fresh_db.table("places").transform(column_order=("country", "name"))
# Should be the same
next_rows = [
tuple(row) for row in fresh_db.execute("select rowid, id, name from places")
@ -774,7 +786,7 @@ def test_transform_strict(fresh_db, initial_strict, transform_strict, expected_s
def test_transform_to_strict_with_invalid_data(fresh_db):
if not fresh_db.supports_strict:
pytest.skip("SQLite version does not support strict tables")
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.create({"id": int})
dogs.insert({"id": "not-an-integer"})
@ -801,7 +813,7 @@ def test_transform_strict_updates_default(fresh_db):
@pytest.mark.parametrize("method_name", ("transform", "transform_sql"))
def test_transform_to_strict_not_supported(fresh_db, method_name):
table = fresh_db["items"]
table = fresh_db.table("items")
table.create({"id": int})
fresh_db._supports_strict = False
@ -823,7 +835,7 @@ def test_transform_to_strict_not_supported(fresh_db, method_name):
def test_transform_indexes(fresh_db, indexes, transform_params):
# https://github.com/simonw/sqlite-utils/issues/633
# New table should have same indexes as old table after transformation
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo", "age": 5, "breed": "Labrador"}, pk="id")
for index in indexes:
@ -849,13 +861,13 @@ def test_transform_indexes(fresh_db, indexes, transform_params):
if "keep_table" in transform_params:
assert all(
index.origin == "pk"
for index in fresh_db[transform_params["keep_table"]].indexes
for index in fresh_db.table(transform_params["keep_table"]).indexes
)
def test_transform_retains_indexes_with_foreign_keys(fresh_db):
dogs = fresh_db["dogs"]
owners = fresh_db["owners"]
dogs = fresh_db.table("dogs")
owners = fresh_db.table("owners")
dogs.insert({"id": 1, "name": "Cleo", "owner_id": 1}, pk="id")
owners.insert({"id": 1, "name": "Alice"}, pk="id")
@ -890,7 +902,7 @@ def test_transform_retains_indexes_with_foreign_keys(fresh_db):
)
def test_transform_with_indexes_errors(fresh_db, transform_params):
# Should error with a compound (name, age) index if age is renamed or dropped
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo", "age": 5}, pk="id")
dogs.create_index(["name", "age"])
@ -906,7 +918,7 @@ def test_transform_with_indexes_errors(fresh_db, transform_params):
def test_transform_with_unique_constraint_implicit_index(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
# Create a table with a UNIQUE constraint on 'name', which creates an implicit index
fresh_db.execute("""
CREATE TABLE dogs (
@ -933,7 +945,7 @@ def test_transform_with_unique_constraint_implicit_index(fresh_db):
def test_transform_preserves_view(fresh_db):
# https://github.com/simonw/sqlite-utils/issues/831
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db.execute("create view dogs_view as select id, name from dogs")
view_sql_before = fresh_db.execute(
@ -958,8 +970,8 @@ def test_transform_preserves_view(fresh_db):
def test_transform_variants_preserve_view(fresh_db, transform_params):
# Covers retyping, changing primary key and foreign key modifications,
# with a view whose columns are untouched by the transform
fresh_db["other"].insert({"id": 1}, pk="id")
dogs = fresh_db["dogs"]
fresh_db.table("other").insert({"id": 1}, pk="id")
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo", "other_id": 1}, pk="id")
if "drop_foreign_keys" in transform_params:
dogs.transform(add_foreign_keys=[("other_id", "other", "id")])
@ -972,13 +984,13 @@ def test_transform_variants_preserve_view(fresh_db, transform_params):
"select sql from sqlite_master where name = 'dogs_view'"
).fetchone()[0]
assert view_sql_before == view_sql_after
assert list(fresh_db["dogs_view"].rows) == [{"id": 1, "name": "Cleo"}]
assert list(fresh_db.view("dogs_view").rows) == [{"id": 1, "name": "Cleo"}]
def test_transform_view_referencing_renamed_column(fresh_db):
# The view survives but querying it raises "no such column" - inherent
# to SQLite views, whose SQL is stored as text
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db.execute("create view dogs_view as select id, name from dogs")
dogs.transform(rename={"name": "title"})
@ -987,7 +999,7 @@ def test_transform_view_referencing_renamed_column(fresh_db):
def test_transform_view_on_view(fresh_db):
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db.execute("create view v1 as select id, name from dogs")
fresh_db.execute("create view v2 as select name from v1")
@ -999,13 +1011,13 @@ def test_transform_view_on_view(fresh_db):
"select sql from sqlite_master where type = 'view' order by name"
).fetchall()
assert sqls_before == sqls_after
assert list(fresh_db["v2"].rows) == [{"name": "Cleo"}]
assert list(fresh_db.view("v2").rows) == [{"name": "Cleo"}]
def test_transform_keep_table_does_not_repoint_view(fresh_db):
# Without legacy_alter_table the ALTER TABLE dogs RENAME TO dogs_backup
# step would rewrite the view to select from "dogs_backup"
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db.execute("create view dogs_view as select id, name from dogs")
dogs.transform(types={"name": str}, keep_table="dogs_backup")
@ -1015,7 +1027,7 @@ def test_transform_keep_table_does_not_repoint_view(fresh_db):
assert "dogs_backup" not in view_sql
# View reads from the live table, not the frozen backup
dogs.insert({"id": 2, "name": "Pancakes"})
assert list(fresh_db["dogs_view"].rows) == [
assert list(fresh_db.view("dogs_view").rows) == [
{"id": 1, "name": "Cleo"},
{"id": 2, "name": "Pancakes"},
]
@ -1024,7 +1036,7 @@ def test_transform_keep_table_does_not_repoint_view(fresh_db):
def test_transform_sql_standalone_statements_work_with_view(fresh_db):
# The documented "run these statements yourself" workflow should be
# standalone-correct, so the pragmas must come from transform_sql()
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db.execute("create view dogs_view as select id, name from dogs")
sqls = dogs.transform_sql(types={"name": str}, tmp_suffix="suffix")
@ -1033,12 +1045,12 @@ def test_transform_sql_standalone_statements_work_with_view(fresh_db):
assert sqls[-1] == "PRAGMA legacy_alter_table=OFF;"
for sql in sqls:
fresh_db.execute(sql)
assert list(fresh_db["dogs_view"].rows) == [{"id": 1, "name": "Cleo"}]
assert list(fresh_db.view("dogs_view").rows) == [{"id": 1, "name": "Cleo"}]
def test_transform_with_view_in_open_transaction(fresh_db):
fresh_db.conn.execute("PRAGMA foreign_keys=ON")
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo"}, pk="id")
fresh_db.execute("create view dogs_view as select id, name from dogs")
with fresh_db.conn:
@ -1054,7 +1066,7 @@ def test_transform_with_view_in_open_transaction(fresh_db):
def test_transform_restores_legacy_alter_table_setting(fresh_db):
if sqlite3.sqlite_version_info < (3, 25, 0):
pytest.skip("legacy_alter_table pragma requires SQLite 3.25 or higher")
dogs = fresh_db["dogs"]
dogs = fresh_db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo"}, pk="id")
# Default is OFF, reset to OFF afterwards
dogs.transform(types={"name": str})
@ -1075,7 +1087,7 @@ def test_transform_preserves_check_constraints(fresh_db):
CONSTRAINT nonzero_id CHECK(id != 0)
)
""")
scores = fresh_db["scores"]
scores = fresh_db.table("scores")
scores.insert({"id": 1, "score": 50})
scores.transform()
assert scores.checks == [
@ -1095,7 +1107,7 @@ def test_transform_preserves_check_ending_in_line_comment(fresh_db):
)
)
""")
inventory = fresh_db["inventory"]
inventory = fresh_db.table("inventory")
inventory.transform(types={"quantity": float})
assert inventory.checks == [Check("quantity >= 0 -- Quantity cannot be negative")]
with pytest.raises(sqlite3.IntegrityError, match="CHECK constraint failed"):
@ -1113,7 +1125,7 @@ def test_transform_preserves_comments_owned_by_columns(fresh_db):
age INTEGER -- May be NULL
)
""")
people = fresh_db["people"]
people = fresh_db.table("people")
people.insert({"id": 1, "name": "Cleo", "age": 5})
people.transform(
rename={"name": "display_name"},
@ -1143,8 +1155,8 @@ def test_transform_drops_comments_owned_by_dropped_column(fresh_db):
obsolete TEXT /* Drop this too */
)
""")
fresh_db["t"].transform(drop={"obsolete"})
schema = fresh_db["t"].schema
fresh_db.table("t").transform(drop={"obsolete"})
schema = fresh_db.table("t").schema
assert "Keep this explanation" in schema
assert "Drop this explanation" not in schema
assert "Drop this too" not in schema
@ -1159,7 +1171,7 @@ def test_transform_renames_columns_inside_check_constraints(fresh_db):
CONSTRAINT within_maximum CHECK(quantity <= maximum)
)
""")
inventory = fresh_db["inventory"]
inventory = fresh_db.table("inventory")
inventory.insert({"quantity": 2, "maximum": 3})
inventory.transform(rename={"quantity": "amount"})
assert inventory.checks == [
@ -1182,7 +1194,7 @@ def test_transform_check_rewrite_preserves_functions_and_quotes(fresh_db):
CHECK(length("old name") > 0 AND length != '')
)
""")
items = fresh_db["items"]
items = fresh_db.table("items")
items.insert({"length": "label", "old name": "hello"})
items.transform(rename={"length": "description", "old name": "new name"})
assert items.checks == [Check("length(\"new name\") > 0 AND description != ''")]
@ -1190,9 +1202,9 @@ def test_transform_check_rewrite_preserves_functions_and_quotes(fresh_db):
def test_transform_check_rewrite_quotes_keyword_column(fresh_db):
fresh_db.execute("CREATE TABLE t(old_name TEXT CHECK(old_name != ''))")
fresh_db["t"].insert({"old_name": "value"})
fresh_db["t"].transform(rename={"old_name": "select"})
assert fresh_db["t"].checks == [Check("\"select\" != ''", column="select")]
fresh_db.table("t").insert({"old_name": "value"})
fresh_db.table("t").transform(rename={"old_name": "select"})
assert fresh_db.table("t").checks == [Check("\"select\" != ''", column="select")]
def test_transform_check_rewrite_does_not_rename_collations_or_cast_types(fresh_db):
@ -1209,9 +1221,9 @@ def test_transform_check_rewrite_does_not_rename_collations_or_cast_types(fresh_
)
)
""")
fresh_db["t"].insert({"nocase": "n", "kind": "k", "other": "o"})
fresh_db["t"].transform(rename={"nocase": "label", "kind": "category"})
check = fresh_db["t"].checks[0].check
fresh_db.table("t").insert({"nocase": "n", "kind": "k", "other": "o"})
fresh_db.table("t").transform(rename={"nocase": "label", "kind": "category"})
check = fresh_db.table("t").checks[0].check
assert "COLLATE nocase" in check
assert "AS kind" in check
assert "AND label != ''" in check
@ -1226,9 +1238,9 @@ def test_transform_drops_check_owned_by_dropped_column(fresh_db):
CHECK(id > 0)
)
""")
fresh_db["t"].insert({"id": 1, "obsolete": 2})
fresh_db["t"].transform(drop={"obsolete"})
assert fresh_db["t"].checks == [Check("id > 0")]
fresh_db.table("t").insert({"id": 1, "obsolete": 2})
fresh_db.table("t").transform(drop={"obsolete"})
assert fresh_db.table("t").checks == [Check("id > 0")]
def test_transform_refuses_to_drop_column_used_by_remaining_check(fresh_db):
@ -1239,7 +1251,7 @@ def test_transform_refuses_to_drop_column_used_by_remaining_check(fresh_db):
CHECK(minimum <= maximum)
)
""")
ranges = fresh_db["ranges"]
ranges = fresh_db.table("ranges")
ranges.insert({"minimum": 1, "maximum": 2})
schema_before = ranges.schema
with pytest.raises(

View file

@ -7,14 +7,14 @@ from sqlite_utils.db import NotFoundError
def test_update_rowid_table(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
rowid = table.insert({"foo": "bar"}).last_pk
table.update(rowid, {"foo": "baz"})
assert [{"foo": "baz"}] == list(table.rows)
def test_update_pk_table(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
pk = table.insert({"foo": "bar", "id": 5}, pk="id").last_pk
assert 5 == pk
table.update(pk, {"foo": "baz"})
@ -22,7 +22,7 @@ def test_update_pk_table(fresh_db):
def test_update_compound_pk_table(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
pk = table.insert({"id1": 5, "id2": 3, "v": 1}, pk=("id1", "id2")).last_pk
assert (5, 3) == pk
table.update(pk, {"v": 2})
@ -42,14 +42,14 @@ def test_update_compound_pk_table(fresh_db):
),
)
def test_update_invalid_pk(fresh_db, pk, update_pk):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert({"id1": 5, "id2": 3, "v": 1}, pk=pk)
with pytest.raises(NotFoundError):
table.update(update_pk, {"v": 2})
def test_update_alter(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
rowid = table.insert({"foo": "bar"}).last_pk
table.update(rowid, {"new_col": 1.2}, alter=True)
assert [{"foo": "bar", "new_col": 1.2}] == list(table.rows)
@ -72,7 +72,7 @@ def test_update_alter(fresh_db):
def test_update_alter_with_special_column_characters(fresh_db):
# With double-quote escaping, columns with special characters are now valid
table = fresh_db["table"]
table = fresh_db.table("table")
rowid = table.insert({"foo": "bar"}).last_pk
table.update(rowid, {"new_col[abc]": 1.2}, alter=True)
assert list(table.rows) == [{"foo": "bar", "new_col[abc]": 1.2}]
@ -106,8 +106,8 @@ def test_update_with_no_values_sets_last_pk(fresh_db):
),
)
def test_update_dictionaries_and_lists_as_json(fresh_db, data_structure):
fresh_db["test"].insert({"id": 1, "data": ""}, pk="id")
fresh_db["test"].update(1, {"data": data_structure})
fresh_db.table("test").insert({"id": 1, "data": ""}, pk="id")
fresh_db.table("test").update(1, {"data": data_structure})
row = fresh_db.execute("select id, data from test").fetchone()
assert row[0] == 1
assert data_structure == json.loads(row[1])

View file

@ -7,7 +7,7 @@ from sqlite_utils.db import PrimaryKeyRequired
@pytest.mark.parametrize("use_old_upsert", (False, True))
def test_upsert(use_old_upsert):
db = Database(memory=True, use_old_upsert=use_old_upsert)
table = db["table"]
table = db.table("table")
table.insert({"id": 1, "name": "Cleo"}, pk="id")
table.upsert({"id": 1, "age": 5}, pk="id", alter=True)
assert list(table.rows) == [{"id": 1, "name": "Cleo", "age": 5}]
@ -15,7 +15,7 @@ def test_upsert(use_old_upsert):
def test_upsert_all(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.upsert_all([{"id": 1, "name": "Cleo"}, {"id": 2, "name": "Nixie"}], pk="id")
table.upsert_all([{"id": 1, "age": 5}, {"id": 2, "age": 5}], pk="id", alter=True)
assert list(table.rows) == [
@ -26,7 +26,7 @@ def test_upsert_all(fresh_db):
def test_upsert_all_single_column(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.upsert_all([{"name": "Cleo"}], pk="name")
assert list(table.rows) == [{"name": "Cleo"}]
assert table.pks == ["name"]
@ -34,16 +34,16 @@ def test_upsert_all_single_column(fresh_db):
def test_upsert_all_not_null(fresh_db):
# https://github.com/simonw/sqlite-utils/issues/538
fresh_db["comments"].upsert_all(
fresh_db.table("comments").upsert_all(
[{"id": 1, "name": "Cleo"}],
pk="id",
not_null=["name"],
)
assert list(fresh_db["comments"].rows) == [{"id": 1, "name": "Cleo"}]
assert list(fresh_db.table("comments").rows) == [{"id": 1, "name": "Cleo"}]
def test_upsert_error_if_no_pk(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
with pytest.raises(PrimaryKeyRequired):
table.upsert_all([{"id": 1, "name": "Cleo"}])
with pytest.raises(PrimaryKeyRequired):
@ -53,7 +53,7 @@ def test_upsert_error_if_no_pk(fresh_db):
@pytest.mark.parametrize("use_old_upsert", (False, True))
def test_upsert_empty_record_errors(use_old_upsert):
db = Database(memory=True, use_old_upsert=use_old_upsert)
table = db["table"]
table = db.table("table")
table.insert({"id": 1, "name": "Cleo"}, pk="id")
with pytest.raises(PrimaryKeyRequired):
table.upsert({}, pk="id")
@ -66,7 +66,7 @@ def test_upsert_empty_record_errors(use_old_upsert):
@pytest.mark.parametrize("use_old_upsert", (False, True))
def test_upsert_missing_pk_value_errors(use_old_upsert):
db = Database(memory=True, use_old_upsert=use_old_upsert)
table = db["table"]
table = db.table("table")
table.insert({"id": 1, "name": "Cleo"}, pk="id")
# Records that omit the pk column entirely
with pytest.raises(PrimaryKeyRequired):
@ -78,7 +78,7 @@ def test_upsert_missing_pk_value_errors(use_old_upsert):
def test_upsert_missing_compound_pk_value_errors(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.insert({"a": "x", "b": "y", "v": 1}, pk=("a", "b"))
# Missing one component of the detected compound primary key
with pytest.raises(PrimaryKeyRequired):
@ -105,7 +105,7 @@ def test_upsert_uses_compound_pk_from_existing_table(use_old_upsert):
primary key (Source, Object, Category)
)
""")
table = db["summary"]
table = db.table("summary")
table.upsert(
{
"Source": "Client A",
@ -134,7 +134,7 @@ def test_upsert_uses_compound_pk_from_existing_table(use_old_upsert):
def test_upsert_with_hash_id(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.upsert({"foo": "bar"}, hash_id="pk")
assert [{"pk": "a5e744d0164540d33b1d7ea616c28f2fa97e754a", "foo": "bar"}] == list(
table.rows
@ -144,7 +144,7 @@ def test_upsert_with_hash_id(fresh_db):
@pytest.mark.parametrize("hash_id", (None, "custom_id"))
def test_upsert_with_hash_id_columns(fresh_db, hash_id):
table = fresh_db["table"]
table = fresh_db.table("table")
table.upsert({"a": 1, "b": 2, "c": 3}, hash_id=hash_id, hash_id_columns=("a", "b"))
assert list(table.rows) == [
{
@ -167,7 +167,7 @@ def test_upsert_with_hash_id_columns(fresh_db, hash_id):
def test_upsert_compound_primary_key(fresh_db):
table = fresh_db["table"]
table = fresh_db.table("table")
table.upsert_all(
[
{"species": "dog", "id": 1, "name": "Cleo", "age": 4},

View file

@ -18,7 +18,7 @@ def test_enable_disable_wal(db_path_tmpdir):
assert "test.db-wal" not in [f.basename for f in tmpdir.listdir()]
db.enable_wal()
assert "wal" == db.journal_mode
db["test"].insert({"foo": "bar"})
db.table("test").insert({"foo": "bar"})
assert "test.db-wal" in [f.basename for f in tmpdir.listdir()]
db.disable_wal()
assert "delete" == db.journal_mode
@ -27,25 +27,25 @@ def test_enable_disable_wal(db_path_tmpdir):
def test_enable_wal_inside_transaction_raises(db_path_tmpdir):
db, _path, _tmpdir = db_path_tmpdir
db["test"].insert({"id": 1}, pk="id")
db.table("test").insert({"id": 1}, pk="id")
with pytest.raises(TransactionError), db.atomic():
db["test"].insert({"id": 2}, pk="id")
db.table("test").insert({"id": 2}, pk="id")
db.enable_wal()
# The atomic() block must have rolled back cleanly and the
# journal mode must be unchanged
assert db.journal_mode == "delete"
assert [r["id"] for r in db["test"].rows] == [1]
assert [r["id"] for r in db.table("test").rows] == [1]
def test_disable_wal_inside_transaction_raises(db_path_tmpdir):
db, _path, _tmpdir = db_path_tmpdir
db.enable_wal()
db["test"].insert({"id": 1}, pk="id")
db.table("test").insert({"id": 1}, pk="id")
with pytest.raises(TransactionError), db.atomic():
db["test"].insert({"id": 2}, pk="id")
db.table("test").insert({"id": 2}, pk="id")
db.disable_wal()
assert db.journal_mode == "wal"
assert [r["id"] for r in db["test"].rows] == [1]
assert [r["id"] for r in db.table("test").rows] == [1]
def test_ensure_autocommit_on(db_path_tmpdir):
@ -65,9 +65,9 @@ def test_enable_wal_noop_inside_transaction_is_allowed(db_path_tmpdir):
db, _path, _tmpdir = db_path_tmpdir
db.enable_wal()
with db.atomic():
db["test"].insert({"id": 1}, pk="id")
db.table("test").insert({"id": 1}, pk="id")
db.enable_wal()
assert [r["id"] for r in db["test"].rows] == [1]
assert [r["id"] for r in db.table("test").rows] == [1]
def test_ensure_autocommit_on_inside_transaction_raises(db_path_tmpdir):
@ -75,7 +75,7 @@ def test_ensure_autocommit_on_inside_transaction_raises(db_path_tmpdir):
# effect, silently breaking the caller's rollback guarantee - so
# entering autocommit mode with a transaction open is an error
db, _path, _tmpdir = db_path_tmpdir
db["test"].insert({"id": 1}, pk="id")
db.table("test").insert({"id": 1}, pk="id")
db.begin()
db.execute("insert into test (id) values (2)")
with pytest.raises(TransactionError), db.ensure_autocommit_on():
@ -83,4 +83,4 @@ def test_ensure_autocommit_on_inside_transaction_raises(db_path_tmpdir):
# The transaction is still open and can still be rolled back
assert db.conn.in_transaction
db.rollback()
assert [r["id"] for r in db["test"].rows] == [1]
assert [r["id"] for r in db.table("test").rows] == [1]