Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ The following types of tables aren't currently supported:
- `smallserial`
- Tables with triggers.
- Tables with invalid indexes (the user should drop or re-index them first).
- Tables with deferrable unique constraints.
- Referring foreign keys on a different schema than the original table.

## Required user permissions (or privileges)
Expand Down
2 changes: 2 additions & 0 deletions src/psycopack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from ._repack import (
BasePsycopackError,
CompositePrimaryKey,
DeferrableUniqueConstraint,
InheritedTable,
InvalidIndexes,
InvalidPrimaryKeyTypeForConversion,
Expand All @@ -32,6 +33,7 @@
"BackfillBatch",
"BasePsycopackError",
"CompositePrimaryKey",
"DeferrableUniqueConstraint",
"FailureDueToLockTimeout",
"InheritedTable",
"InvalidIndexes",
Expand Down
13 changes: 1 addition & 12 deletions src/psycopack/_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,23 +295,12 @@ def create_unique_constraint_using_idx(
table: str,
constraint: str,
index: str,
is_deferrable: bool,
is_deferred: bool,
) -> None:
add_constraint_sql = dedent("""
ALTER TABLE {schema}.{table}
ADD CONSTRAINT {constraint}
UNIQUE USING INDEX {index}
UNIQUE USING INDEX {index} NOT DEFERRABLE
""")
if is_deferrable:
add_constraint_sql += " DEFERRABLE"
else:
add_constraint_sql += " NOT DEFERRABLE"

if is_deferred:
add_constraint_sql += " INITIALLY DEFERRED"
else:
add_constraint_sql += " INITIALLY IMMEDIATE"

self.cur.execute(
psycopg.sql.SQL(add_constraint_sql)
Expand Down
21 changes: 19 additions & 2 deletions src/psycopack/_repack.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class ReferringForeignKeyInDifferentSchema(BasePsycopackError):
pass


class DeferrableUniqueConstraint(BasePsycopackError):
pass


class NoCreateAndUsagePrivilegeOnSchema(BasePsycopackError):
pass

Expand Down Expand Up @@ -333,6 +337,21 @@ def pre_validate(self) -> None:
f"schema: {fks_in_different_schema}."
)

deferrable_unique_constraints = [
c.name
for c in self.introspector.get_constraints(
table=self.table, types=["u"]
)
if c.is_deferrable
]
if deferrable_unique_constraints:
raise DeferrableUniqueConstraint(
f"Psycopack does not currently support tables with "
f"deferrable unique constraints. The table {self.table} "
f"has the following deferrable unique constraints: "
f"{deferrable_unique_constraints}."
)

def setup_repacking(self) -> None:
with (
self.tracker.track(_tracker.Stage.SETUP),
Expand Down Expand Up @@ -758,8 +777,6 @@ def _create_unique_constraints(self) -> None:
# From previous steps, the index name is the same as the
# constraint, not a typo!
index=constraint_name,
is_deferrable=cons.is_deferrable,
is_deferred=cons.is_deferred,
)

def _create_check_and_fk_constraints(self) -> None:
Expand Down
20 changes: 0 additions & 20 deletions tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ def create_table_for_repacking(
int_with_long_index_name INTEGER,
var_with_unique_idx VARCHAR(10),
var_with_unique_const VARCHAR(10) UNIQUE,
var_with_deferrable_const VARCHAR(10),
var_with_deferred_const VARCHAR(10),
valid_fk INTEGER REFERENCES {schema}.referred_table(id),
not_valid_fk INTEGER,
{table_name} INTEGER,
Expand Down Expand Up @@ -115,20 +113,6 @@ def create_table_for_repacking(
CHECK (int_with_not_valid_check >= 0) NOT VALID;
""")
)
cur.execute(
dedent(f"""
ALTER TABLE {schema}.{table_name} ADD CONSTRAINT non_deferrable_const
UNIQUE (var_with_deferrable_const)
DEFERRABLE;
""")
)
cur.execute(
dedent(f"""
ALTER TABLE {schema}.{table_name} ADD CONSTRAINT deferred_const
UNIQUE (var_with_deferred_const)
DEFERRABLE INITIALLY DEFERRED;
""")
)
# Constraint for a column that has the same name as the table.
cur.execute(
dedent(f"""
Expand Down Expand Up @@ -156,8 +140,6 @@ def create_table_for_repacking(
int_with_long_index_name,
var_with_unique_idx,
var_with_unique_const,
var_with_deferrable_const,
var_with_deferred_const,
valid_fk,
not_valid_fk,
{table_name},
Expand All @@ -173,8 +155,6 @@ def create_table_for_repacking(
(floor(random() * 10) + 1)::int,
substring(md5(random()::text), 1, 10),
substring(md5(random()::text), 1, 10),
substring(md5(random()::text), 1, 10),
substring(md5(random()::text), 1, 10),
(floor(random() * {referred_table_rows}) + 1)::int,
(floor(random() * {referred_table_rows}) + 1)::int,
(floor(random() * 10) + 1)::int,
Expand Down
36 changes: 32 additions & 4 deletions tests/test_repack.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from psycopack import (
BackfillBatch,
CompositePrimaryKey,
DeferrableUniqueConstraint,
FailureDueToLockTimeout,
InheritedTable,
InvalidIndexes,
Expand Down Expand Up @@ -1356,8 +1357,6 @@ def test_when_table_has_large_value_being_inserted(
int_with_long_index_name,
var_with_unique_idx,
var_with_unique_const,
var_with_deferrable_const,
var_with_deferred_const,
valid_fk,
not_valid_fk,
to_repack,
Expand All @@ -1372,8 +1371,6 @@ def test_when_table_has_large_value_being_inserted(
(floor(random() * 10) + 1)::int,
substring(md5(random()::text), 1, 10),
substring(md5(random()::text), 1, 10),
substring(md5(random()::text), 1, 10),
substring(md5(random()::text), 1, 10),
(floor(random() * 10) + 1)::int,
(floor(random() * 10) + 1)::int,
(floor(random() * 10) + 1)::int,
Expand Down Expand Up @@ -1756,6 +1753,37 @@ def test_with_fks_from_another_schema(connection: _psycopg.Connection) -> None:
repack.full()


def test_with_deferrable_unique_constraint(connection: _psycopg.Connection) -> None:
with _cur.get_cursor(connection, logged=True) as cur:
factories.create_table_for_repacking(
connection=connection,
cur=cur,
table_name="to_repack",
rows=100,
)
repack = Psycopack(
table="to_repack",
batch_size=1,
conn=connection,
cur=cur,
)
cur.execute(
"ALTER TABLE to_repack DROP CONSTRAINT to_repack_var_with_unique_const_key;"
)
cur.execute(
dedent("""
ALTER TABLE to_repack ADD CONSTRAINT to_repack_var_with_unique_const_key
UNIQUE (var_with_unique_const)
DEFERRABLE;
""")
)

with pytest.raises(
DeferrableUniqueConstraint, match="to_repack_var_with_unique_const_key"
):
repack.full()


def test_without_schema_privileges(connection: _psycopg.Connection) -> None:
schema = "sweet_schema"
with _cur.get_cursor(connection, logged=True) as cur:
Expand Down
Loading