diff --git a/swh/provenance/__init__.py b/swh/provenance/__init__.py --- a/swh/provenance/__init__.py +++ b/swh/provenance/__init__.py @@ -1,4 +1,5 @@ from typing import TYPE_CHECKING +import warnings from .postgresql.db_utils import connect @@ -24,17 +25,14 @@ def get_provenance(cls: str, **kwargs) -> "ProvenanceInterface": if cls == "local": conn = connect(kwargs["db"]) - if kwargs.get("with_path", True): - from swh.provenance.postgresql.provenancedb_with_path import ( - ProvenanceWithPathDB, + if not kwargs.get("with_path", True): + warnings.warn( + "The ProvenanceDB without path support has been removed. " + "Falling back to ProvenanceDB with path.", + category=DeprecationWarning, ) + from swh.provenance.postgresql.provenancedb import ProvenanceDB - return ProvenanceWithPathDB(conn) - else: - from swh.provenance.postgresql.provenancedb_without_path import ( - ProvenanceWithoutPathDB, - ) - - return ProvenanceWithoutPathDB(conn) + return ProvenanceDB(conn) else: raise NotImplementedError diff --git a/swh/provenance/postgresql/provenancedb_base.py b/swh/provenance/postgresql/provenancedb.py rename from swh/provenance/postgresql/provenancedb_base.py rename to swh/provenance/postgresql/provenancedb.py --- a/swh/provenance/postgresql/provenancedb_base.py +++ b/swh/provenance/postgresql/provenancedb.py @@ -1,7 +1,8 @@ from datetime import datetime import itertools import logging -from typing import Dict, Iterable, List, Optional, Set, Tuple +import os +from typing import Dict, Generator, Iterable, List, Optional, Set, Tuple import psycopg2 import psycopg2.extras @@ -12,6 +13,10 @@ from ..revision import RevisionEntry +def normalize(path: bytes) -> bytes: + return path[2:] if path.startswith(bytes("." + os.path.sep, "utf-8")) else path + + class Cache(TypedDict): data: Dict[bytes, datetime] added: Set[bytes] @@ -22,9 +27,11 @@ content: Cache directory: Cache revision: Cache + # below are insertion caches only content_early_in_rev: Set[Tuple[bytes, bytes, bytes]] content_in_dir: Set[Tuple[bytes, bytes, bytes]] directory_in_rev: Set[Tuple[bytes, bytes, bytes]] + # these two are for the origin layer revision_before_rev: List[Tuple[bytes, bytes]] revision_in_org: List[Tuple[bytes, bytes]] @@ -42,7 +49,7 @@ ) -class ProvenanceDBBase: +class ProvenanceDB: raise_on_commit: bool = False def __init__(self, conn: psycopg2.extensions.connection): @@ -307,3 +314,142 @@ (revision.id,), ) return self.cursor.fetchone() is not None + + def content_add_to_directory( + self, directory: DirectoryEntry, blob: FileEntry, prefix: bytes + ): + self.cache["content_in_dir"].add( + (blob.id, directory.id, normalize(os.path.join(prefix, blob.name))) + ) + + def content_add_to_revision( + self, revision: RevisionEntry, blob: FileEntry, prefix: bytes + ): + self.cache["content_early_in_rev"].add( + (blob.id, revision.id, normalize(os.path.join(prefix, blob.name))) + ) + + def directory_add_to_revision( + self, revision: RevisionEntry, directory: DirectoryEntry, path: bytes + ): + self.cache["directory_in_rev"].add((directory.id, revision.id, normalize(path))) + + def content_find_first( + self, blobid: bytes + ) -> Optional[Tuple[bytes, bytes, datetime, bytes]]: + self.cursor.execute( + """ + SELECT C.sha1 AS blob, + R.sha1 AS rev, + R.date AS date, + L.path AS path + FROM content AS C + INNER JOIN content_early_in_rev AS CR ON (CR.blob = C.id) + INNER JOIN location as L ON (CR.loc = L.id) + INNER JOIN revision as R ON (CR.rev = R.id) + WHERE C.sha1=%s + ORDER BY date, rev, path ASC LIMIT 1 + """, + (blobid,), + ) + return self.cursor.fetchone() + + def content_find_all( + self, blobid: bytes, limit: Optional[int] = None + ) -> Generator[Tuple[bytes, bytes, datetime, bytes], None, None]: + early_cut = f"LIMIT {limit}" if limit is not None else "" + self.cursor.execute( + f""" + (SELECT C.sha1 AS blob, + R.sha1 AS rev, + R.date AS date, + L.path AS path + FROM content AS C + INNER JOIN content_early_in_rev AS CR ON (CR.blob = C.id) + INNER JOIN location AS L ON (CR.loc = L.id) + INNER JOIN revision AS R ON (CR.rev = R.id) + WHERE C.sha1=%s) + UNION + (SELECT C.sha1 AS blob, + R.sha1 AS rev, + R.date AS date, + CASE DL.path + WHEN '' THEN CL.path + WHEN '.' THEN CL.path + ELSE (DL.path || '/' || CL.path)::unix_path + END AS path + FROM content AS C + INNER JOIN content_in_dir AS CD ON (C.id = CD.blob) + INNER JOIN directory_in_rev AS DR ON (CD.dir = DR.dir) + INNER JOIN revision AS R ON (DR.rev = R.id) + INNER JOIN location AS CL ON (CD.loc = CL.id) + INNER JOIN location AS DL ON (DR.loc = DL.id) + WHERE C.sha1=%s) + ORDER BY date, rev, path {early_cut} + """, + (blobid, blobid), + ) + # TODO: use POSTGRESQL EXPLAIN looking for query optimizations. + yield from self.cursor.fetchall() + + def insert_location(self, src0_table, src1_table, dst_table): + """Insert location entries in `dst_table` from the insert_cache + + Also insert missing location entries in the 'location' table. + """ + # TODO: find a better way of doing this; might be doable in a coupls of + # SQL queries (one to insert missing entries in the location' table, + # one to insert entries in the dst_table) + + # Resolve src0 ids + src0_sha1s = tuple(set(sha1 for (sha1, _, _) in self.cache[dst_table])) + fmt = ",".join(["%s"] * len(src0_sha1s)) + self.cursor.execute( + f"""SELECT sha1, id FROM {src0_table} WHERE sha1 IN ({fmt})""", + src0_sha1s, + ) + src0_values = dict(self.cursor.fetchall()) + + # Resolve src1 ids + src1_sha1s = tuple(set(sha1 for (_, sha1, _) in self.cache[dst_table])) + fmt = ",".join(["%s"] * len(src1_sha1s)) + self.cursor.execute( + f"""SELECT sha1, id FROM {src1_table} WHERE sha1 IN ({fmt})""", + src1_sha1s, + ) + src1_values = dict(self.cursor.fetchall()) + + # insert missing locations + locations = tuple(set((loc,) for (_, _, loc) in self.cache[dst_table])) + psycopg2.extras.execute_values( + self.cursor, + """ + LOCK TABLE ONLY location; + INSERT INTO location(path) VALUES %s + ON CONFLICT (path) DO NOTHING + """, + locations, + ) + # fetch location ids + fmt = ",".join(["%s"] * len(locations)) + self.cursor.execute( + f"SELECT path, id FROM location WHERE path IN ({fmt})", + locations, + ) + loc_ids = dict(self.cursor.fetchall()) + + # Insert values in dst_table + rows = [ + (src0_values[sha1_src], src1_values[sha1_dst], loc_ids[loc]) + for (sha1_src, sha1_dst, loc) in self.cache[dst_table] + ] + psycopg2.extras.execute_values( + self.cursor, + f""" + LOCK TABLE ONLY {dst_table}; + INSERT INTO {dst_table} VALUES %s + ON CONFLICT DO NOTHING + """, + rows, + ) + self.cache[dst_table].clear() diff --git a/swh/provenance/postgresql/provenancedb_with_path.py b/swh/provenance/postgresql/provenancedb_with_path.py deleted file mode 100644 --- a/swh/provenance/postgresql/provenancedb_with_path.py +++ /dev/null @@ -1,155 +0,0 @@ -from datetime import datetime -import os -from typing import Generator, Optional, Tuple - -import psycopg2 -import psycopg2.extras - -from ..model import DirectoryEntry, FileEntry -from ..revision import RevisionEntry -from .provenancedb_base import ProvenanceDBBase - - -def normalize(path: bytes) -> bytes: - return path[2:] if path.startswith(bytes("." + os.path.sep, "utf-8")) else path - - -class ProvenanceWithPathDB(ProvenanceDBBase): - def content_add_to_directory( - self, directory: DirectoryEntry, blob: FileEntry, prefix: bytes - ): - self.cache["content_in_dir"].add( - (blob.id, directory.id, normalize(os.path.join(prefix, blob.name))) - ) - - def content_add_to_revision( - self, revision: RevisionEntry, blob: FileEntry, prefix: bytes - ): - self.cache["content_early_in_rev"].add( - (blob.id, revision.id, normalize(os.path.join(prefix, blob.name))) - ) - - def directory_add_to_revision( - self, revision: RevisionEntry, directory: DirectoryEntry, path: bytes - ): - self.cache["directory_in_rev"].add((directory.id, revision.id, normalize(path))) - - def content_find_first( - self, blobid: bytes - ) -> Optional[Tuple[bytes, bytes, datetime, bytes]]: - self.cursor.execute( - """ - SELECT C.sha1 AS blob, - R.sha1 AS rev, - R.date AS date, - L.path AS path - FROM content AS C - INNER JOIN content_early_in_rev AS CR ON (CR.blob = C.id) - INNER JOIN location as L ON (CR.loc = L.id) - INNER JOIN revision as R ON (CR.rev = R.id) - WHERE C.sha1=%s - ORDER BY date, rev, path ASC LIMIT 1 - """, - (blobid,), - ) - return self.cursor.fetchone() - - def content_find_all( - self, blobid: bytes, limit: Optional[int] = None - ) -> Generator[Tuple[bytes, bytes, datetime, bytes], None, None]: - early_cut = f"LIMIT {limit}" if limit is not None else "" - self.cursor.execute( - f""" - (SELECT C.sha1 AS blob, - R.sha1 AS rev, - R.date AS date, - L.path AS path - FROM content AS C - INNER JOIN content_early_in_rev AS CR ON (CR.blob = C.id) - INNER JOIN location AS L ON (CR.loc = L.id) - INNER JOIN revision AS R ON (CR.rev = R.id) - WHERE C.sha1=%s) - UNION - (SELECT C.sha1 AS blob, - R.sha1 AS rev, - R.date AS date, - CASE DL.path - WHEN '' THEN CL.path - WHEN '.' THEN CL.path - ELSE (DL.path || '/' || CL.path)::unix_path - END AS path - FROM content AS C - INNER JOIN content_in_dir AS CD ON (C.id = CD.blob) - INNER JOIN directory_in_rev AS DR ON (CD.dir = DR.dir) - INNER JOIN revision AS R ON (DR.rev = R.id) - INNER JOIN location AS CL ON (CD.loc = CL.id) - INNER JOIN location AS DL ON (DR.loc = DL.id) - WHERE C.sha1=%s) - ORDER BY date, rev, path {early_cut} - """, - (blobid, blobid), - ) - # TODO: use POSTGRESQL EXPLAIN looking for query optimizations. - yield from self.cursor.fetchall() - - def insert_location(self, src0_table, src1_table, dst_table): - """Insert location entries in `dst_table` from the insert_cache - - Also insert missing location entries in the 'location' table. - """ - # TODO: find a better way of doing this; might be doable in a coupls of - # SQL queries (one to insert missing entries in the location' table, - # one to insert entries in the dst_table) - - # Resolve src0 ids - src0_sha1s = tuple(set(sha1 for (sha1, _, _) in self.cache[dst_table])) - fmt = ",".join(["%s"] * len(src0_sha1s)) - self.cursor.execute( - f"""SELECT sha1, id FROM {src0_table} WHERE sha1 IN ({fmt})""", - src0_sha1s, - ) - src0_values = dict(self.cursor.fetchall()) - - # Resolve src1 ids - src1_sha1s = tuple(set(sha1 for (_, sha1, _) in self.cache[dst_table])) - fmt = ",".join(["%s"] * len(src1_sha1s)) - self.cursor.execute( - f"""SELECT sha1, id FROM {src1_table} WHERE sha1 IN ({fmt})""", - src1_sha1s, - ) - src1_values = dict(self.cursor.fetchall()) - - # insert missing locations - locations = tuple(set((loc,) for (_, _, loc) in self.cache[dst_table])) - psycopg2.extras.execute_values( - self.cursor, - """ - LOCK TABLE ONLY location; - INSERT INTO location(path) VALUES %s - ON CONFLICT (path) DO NOTHING - """, - locations, - ) - # fetch location ids - fmt = ",".join(["%s"] * len(locations)) - self.cursor.execute( - f"SELECT path, id FROM location WHERE path IN ({fmt})", - locations, - ) - loc_ids = dict(self.cursor.fetchall()) - - # Insert values in dst_table - rows = [ - (src0_values[sha1_src], src1_values[sha1_dst], loc_ids[loc]) - for (sha1_src, sha1_dst, loc) in self.cache[dst_table] - ] - psycopg2.extras.execute_values( - self.cursor, - f""" - LOCK TABLE ONLY {dst_table}; - INSERT INTO {dst_table} VALUES %s - ON CONFLICT DO NOTHING - """, - rows, - ) - self.cache[dst_table].clear() diff --git a/swh/provenance/postgresql/provenancedb_without_path.py b/swh/provenance/postgresql/provenancedb_without_path.py deleted file mode 100644 --- a/swh/provenance/postgresql/provenancedb_without_path.py +++ /dev/null @@ -1,140 +0,0 @@ -from datetime import datetime -import itertools -import operator -from typing import Generator, Optional, Tuple - -import psycopg2 -import psycopg2.extras - -from ..model import DirectoryEntry, FileEntry -from ..revision import RevisionEntry -from .provenancedb_base import ProvenanceDBBase - -######################################################################################## -######################################################################################## -######################################################################################## - - -class ProvenanceWithoutPathDB(ProvenanceDBBase): - def content_add_to_directory( - self, directory: DirectoryEntry, blob: FileEntry, prefix: bytes - ): - self.cache["content_in_dir"].add((blob.id, directory.id, b"")) - - def content_add_to_revision( - self, revision: RevisionEntry, blob: FileEntry, prefix: bytes - ): - self.cache["content_early_in_rev"].add((blob.id, revision.id, b"")) - - def content_find_first( - self, blobid: bytes - ) -> Optional[Tuple[bytes, bytes, datetime, bytes]]: - self.cursor.execute( - """ - SELECT revision.sha1 AS rev, - revision.date AS date - FROM (SELECT content_early_in_rev.rev - FROM content_early_in_rev - JOIN content - ON content.id=content_early_in_rev.blob - WHERE content.sha1=%s - ) AS content_in_rev - JOIN revision - ON revision.id=content_in_rev.rev - ORDER BY date, rev ASC LIMIT 1 - """, - (blobid,), - ) - row = self.cursor.fetchone() - if row is not None: - # TODO: query revision from the archive and look for blobid into a - # recursive directory_ls of the revision's root. - return blobid, row[0], row[1], b"" - return None - - def content_find_all( - self, blobid: bytes, limit: Optional[int] = None - ) -> Generator[Tuple[bytes, bytes, datetime, bytes], None, None]: - early_cut = f"LIMIT {limit}" if limit is not None else "" - self.cursor.execute( - f""" - (SELECT revision.sha1 AS rev, - revision.date AS date - FROM (SELECT content_early_in_rev.rev - FROM content_early_in_rev - JOIN content - ON content.id=content_early_in_rev.blob - WHERE content.sha1=%s - ) AS content_in_rev - JOIN revision - ON revision.id=content_in_rev.rev - ) - UNION - (SELECT revision.sha1 AS rev, - revision.date AS date - FROM (SELECT directory_in_rev.rev - FROM (SELECT content_in_dir.dir - FROM content_in_dir - JOIN content - ON content_in_dir.blob=content.id - WHERE content.sha1=%s - ) AS content_dir - JOIN directory_in_rev - ON directory_in_rev.dir=content_dir.dir - ) AS content_in_rev - JOIN revision - ON revision.id=content_in_rev.rev - ) - ORDER BY date, rev {early_cut} - """, - (blobid, blobid), - ) - # TODO: use POSTGRESQL EXPLAIN looking for query optimizations. - for row in self.cursor.fetchall(): - # TODO: query revision from the archive and look for blobid into a - # recursive directory_ls of the revision's root. - yield blobid, row[0], row[1], b"" - - def directory_add_to_revision( - self, revision: RevisionEntry, directory: DirectoryEntry, path: bytes - ): - self.cache["directory_in_rev"].add((directory.id, revision.id, b"")) - - def insert_location(self, src0_table, src1_table, dst_table): - # Resolve src0 ids - src0_values = dict().fromkeys( - map(operator.itemgetter(0), self.insert_cache[dst_table]) - ) - values = ", ".join(itertools.repeat("%s", len(src0_values))) - self.cursor.execute( - f"""SELECT sha1, id FROM {src0_table} WHERE sha1 IN ({values})""", - tuple(src0_values), - ) - src0_values = dict(self.cursor.fetchall()) - - # Resolve src1 ids - src1_values = dict().fromkeys( - map(operator.itemgetter(1), self.insert_cache[dst_table]) - ) - values = ", ".join(itertools.repeat("%s", len(src1_values))) - self.cursor.execute( - f"""SELECT sha1, id FROM {src1_table} WHERE sha1 IN ({values})""", - tuple(src1_values), - ) - src1_values = dict(self.cursor.fetchall()) - - # Insert values in dst_table - rows = map( - lambda row: (src0_values[row[0]], src1_values[row[1]]), - self.insert_cache[dst_table], - ) - psycopg2.extras.execute_values( - self.cursor, - f""" - LOCK TABLE ONLY {dst_table}; - INSERT INTO {dst_table} VALUES %s - ON CONFLICT DO NOTHING - """, - rows, - ) - self.insert_cache[dst_table].clear() diff --git a/swh/provenance/sql/15-flavor.sql b/swh/provenance/sql/15-flavor.sql deleted file mode 100644 --- a/swh/provenance/sql/15-flavor.sql +++ /dev/null @@ -1,21 +0,0 @@ --- database flavor -create type database_flavor as enum ( - 'with-path', - 'without-path' -); -comment on type database_flavor is 'Flavor of the current database'; - -create table dbflavor ( - flavor database_flavor, - single_row char(1) primary key default 'x', - check (single_row = 'x') -); -comment on table dbflavor is 'Database flavor storage'; -comment on column dbflavor.flavor is 'Database flavor currently deployed'; -comment on column dbflavor.single_row is 'Bogus column to force the table to have a single row'; - -create or replace function swh_get_dbflavor() returns database_flavor language sql stable as $$ - select coalesce((select flavor from dbflavor), 'with-path'); -$$; - -comment on function swh_get_dbflavor is 'Get the flavor of the database currently deployed'; diff --git a/swh/provenance/sql/30-schema.sql b/swh/provenance/sql/30-schema.sql --- a/swh/provenance/sql/30-schema.sql +++ b/swh/provenance/sql/30-schema.sql @@ -1,6 +1,4 @@ -- psql variables to get the current database flavor -select swh_get_dbflavor() = 'with-path' as dbflavor_with_path \gset - create table dbversion ( @@ -37,38 +35,28 @@ create table content_early_in_rev ( blob bigint not null, -- internal identifier of the content blob - rev bigint not null -- internal identifier of the revision where the blob appears for the first time -\if :dbflavor_with_path - , + rev bigint not null, -- internal identifier of the revision where the blob appears for the first time loc bigint not null -- location of the content relative to the revision root directory -\endif -- foreign key (blob) references content (id), -- foreign key (rev) references revision (id), -- foreign key (loc) references location (id) ); comment on column content_early_in_rev.blob is 'Content internal identifier'; comment on column content_early_in_rev.rev is 'Revision internal identifier'; -\if :dbflavor_with_path comment on column content_early_in_rev.loc is 'Location of content in revision'; -\endif create table content_in_dir ( blob bigint not null, -- internal identifier of the content blob - dir bigint not null -- internal identifier of the directory containing the blob -\if :dbflavor_with_path - , + dir bigint not null, -- internal identifier of the directory containing the blob loc bigint not null -- location of the content relative to its parent directory in the isochrone frontier -\endif -- foreign key (blob) references content (id), -- foreign key (dir) references directory (id), -- foreign key (loc) references location (id) ); comment on column content_in_dir.blob is 'Content internal identifier'; comment on column content_in_dir.dir is 'Directory internal identifier'; -\if :dbflavor_with_path comment on column content_in_dir.loc is 'Location of content in directory'; -\endif create table directory ( @@ -83,20 +71,15 @@ create table directory_in_rev ( dir bigint not null, -- internal identifier of the directory appearing in the revision - rev bigint not null -- internal identifier of the revision containing the directory -\if :dbflavor_with_path - , + rev bigint not null, -- internal identifier of the revision containing the directory loc bigint not null -- location of the directory relative to the revision root directory -\endif -- foreign key (dir) references directory (id), -- foreign key (rev) references revision (id), -- foreign key (loc) references location (id) ); comment on column directory_in_rev.dir is 'Directory internal identifier'; comment on column directory_in_rev.rev is 'Revision internal identifier'; -\if :dbflavor_with_path comment on column directory_in_rev.loc is 'Location of directory in revision'; -\endif create table origin ( @@ -141,7 +124,6 @@ comment on column revision_in_org.rev is 'Revision internal identifier'; comment on column revision_in_org.org is 'Origin internal identifier'; -\if :dbflavor_with_path create table location ( id bigserial primary key, -- internal identifier of the location @@ -149,4 +131,3 @@ ); comment on column location.id is 'Location internal identifier'; comment on column location.path is 'Path to the location'; -\endif diff --git a/swh/provenance/sql/60-indexes.sql b/swh/provenance/sql/60-indexes.sql --- a/swh/provenance/sql/60-indexes.sql +++ b/swh/provenance/sql/60-indexes.sql @@ -1,12 +1,5 @@ -- psql variables to get the current database flavor -select swh_get_dbflavor() = 'with-path' as dbflavor_with_path \gset -\if :dbflavor_with_path alter table content_early_in_rev add primary key (blob, rev, loc); alter table content_in_dir add primary key (blob, dir, loc); alter table directory_in_rev add primary key (dir, rev, loc); -\else -alter table content_early_in_rev add primary key (blob, rev); -alter table content_in_dir add primary key (blob, dir); -alter table directory_in_rev add primary key (dir, rev); -\endif diff --git a/swh/provenance/tests/conftest.py b/swh/provenance/tests/conftest.py --- a/swh/provenance/tests/conftest.py +++ b/swh/provenance/tests/conftest.py @@ -36,9 +36,7 @@ @pytest.fixture def provenance(provenance_db): """return a working and initialized provenance db""" - from swh.provenance.postgresql.provenancedb_with_path import ( - ProvenanceWithPathDB as ProvenanceDB, - ) + from swh.provenance.postgresql.provenancedb import ProvenanceDB BaseDb.adapt_conn(provenance_db) prov = ProvenanceDB(provenance_db) diff --git a/swh/provenance/tests/test_cli.py b/swh/provenance/tests/test_cli.py --- a/swh/provenance/tests/test_cli.py +++ b/swh/provenance/tests/test_cli.py @@ -5,7 +5,6 @@ from click.testing import CliRunner import psycopg2 -import pytest import yaml from swh.core.cli import swh as swhmain @@ -46,13 +45,13 @@ TABLES = { - "dbflavor", "dbversion", "content", "content_early_in_rev", "content_in_dir", "directory", "directory_in_rev", + "location", "origin", "revision", "revision_before_rev", @@ -60,17 +59,12 @@ } -@pytest.mark.parametrize( - "flavor, dbtables", (("with-path", TABLES | {"location"}), ("without-path", TABLES)) -) -def test_cli_db_create_and_init_db_with_flavor( - monkeypatch, postgresql, flavor, dbtables -): +def test_cli_db_create_and_init_db_with_flavor(monkeypatch, postgresql): """Test that 'swh db init provenance' works with flavors for both with-path and without-path flavors""" - dbname = f"{flavor}-db" + dbname = "provenance-db" # DB creation using 'swh db create' db_params = postgresql.get_dsn_parameters() @@ -81,34 +75,17 @@ assert result.exit_code == 0, result.output # DB init using 'swh db init' - result = CliRunner().invoke( - swhmain, ["db", "init", "-d", dbname, "--flavor", flavor, "provenance"] - ) + result = CliRunner().invoke(swhmain, ["db", "init", "-d", dbname, "provenance"]) assert result.exit_code == 0, result.output - assert f"(flavor {flavor})" in result.output db_params["dbname"] = dbname cnx = psycopg2.connect(**db_params) # check the DB looks OK (check for db_flavor and expected tables) with cnx.cursor() as cur: - cur.execute("select swh_get_dbflavor()") - assert cur.fetchone() == (flavor,) - cur.execute( "select table_name from information_schema.tables " "where table_schema = 'public' " f"and table_catalog = '{dbname}'" ) tables = set(x for (x,) in cur.fetchall()) - assert tables == dbtables - - -def test_cli_init_db_default_flavor(provenance_db): - "Test that 'swh db init provenance' defaults to a with-path flavored DB" - dbname = provenance_db.dsn - result = CliRunner().invoke(swhmain, ["db", "init", "-d", dbname, "provenance"]) - assert result.exit_code == 0, result.output - - with provenance_db.cursor() as cur: - cur.execute("select swh_get_dbflavor()") - assert cur.fetchone() == ("with-path",) + assert tables == TABLES