mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-08-13 12:54:12 +02:00
Use db.table() and db.view() in tests, closes #838
This commit is contained in:
parent
43d5d3331f
commit
38fe466700
43 changed files with 1321 additions and 1254 deletions
|
|
@ -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"},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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"}'
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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 == []
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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"})]
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue