fix(migration): normalize PocketBase index definitions
This commit is contained in:
parent
d12ca92468
commit
bee2b3dd17
1 changed files with 33 additions and 2 deletions
|
|
@ -57,7 +57,18 @@ def index_name(statement):
|
|||
match = re.search(r"\bINDEX\s+`?([^`\s]+)`?", statement, re.IGNORECASE)
|
||||
if not match:
|
||||
raise ValueError(f"not a CREATE INDEX statement: {statement!r}")
|
||||
return match.group(1)
|
||||
return match.group(1).strip().lower()
|
||||
|
||||
|
||||
def normalise_index_sql(statement):
|
||||
"""Compare index SQL by meaning, not PocketBase's formatting choices."""
|
||||
value = statement.strip().rstrip(";")
|
||||
value = re.sub(r"`([^`]*)`", lambda match: f"`{match.group(1).lower()}`", value)
|
||||
value = re.sub(r"\s+", " ", value).strip().lower()
|
||||
# SQLite/PocketBase may insert or remove spaces around these tokens while
|
||||
# preserving the same columns, uniqueness and WHERE predicate.
|
||||
value = re.sub(r"\s*([(),=<>!])\s*", r"\1", value)
|
||||
return value
|
||||
|
||||
|
||||
def merge_indexes(existing, wanted):
|
||||
|
|
@ -72,7 +83,7 @@ def merge_indexes(existing, wanted):
|
|||
positions[name] = len(merged)
|
||||
merged.append(statement)
|
||||
changes.append(("add", statement))
|
||||
elif merged[position] != statement:
|
||||
elif normalise_index_sql(merged[position]) != normalise_index_sql(statement):
|
||||
merged[position] = statement
|
||||
changes.append(("replace", statement))
|
||||
return merged, changes
|
||||
|
|
@ -177,6 +188,26 @@ def self_test():
|
|||
assert candidate_indexes == [candidate_index]
|
||||
assert candidate_changes == [("add", candidate_index)]
|
||||
assert merge_indexes(candidate_indexes, [candidate_index])[1] == []
|
||||
# PocketBase returns the same three partial indexes uppercased and with
|
||||
# multiline/variable whitespace. Those rows must be idempotent.
|
||||
semantic_wanted = [
|
||||
"CREATE UNIQUE INDEX `idx_dn_batch_notes_candidate_key` ON `dn_batch_notes` (`batch`, `candidate_key`) WHERE `candidate_key` IS NOT NULL AND `candidate_key` != ''",
|
||||
"CREATE UNIQUE INDEX `idx_delivery_notes_batch_note` ON `delivery_notes` (`batch_note`) WHERE `batch_note` IS NOT NULL AND `batch_note` != ''",
|
||||
"CREATE UNIQUE INDEX `idx_dn_batches_capture_session_id` ON `dn_batches` (`capture_session_id`) WHERE `capture_session_id` != ''",
|
||||
]
|
||||
pocketbase_stored = [
|
||||
"CREATE UNIQUE INDEX `IDX_DN_BATCH_NOTES_CANDIDATE_KEY`\nON `DN_BATCH_NOTES` (`BATCH`, `CANDIDATE_KEY`)\nWHERE `CANDIDATE_KEY` IS NOT NULL AND `CANDIDATE_KEY` != ''",
|
||||
"CREATE UNIQUE INDEX `IDX_DELIVERY_NOTES_BATCH_NOTE` ON `DELIVERY_NOTES`\n( `BATCH_NOTE` ) WHERE `BATCH_NOTE` IS NOT NULL\nAND `BATCH_NOTE` != ''",
|
||||
"CREATE UNIQUE INDEX `IDX_DN_BATCHES_CAPTURE_SESSION_ID` ON `DN_BATCHES` (`CAPTURE_SESSION_ID`)\nWHERE `CAPTURE_SESSION_ID` != ''",
|
||||
]
|
||||
assert merge_indexes(pocketbase_stored, semantic_wanted)[1] == []
|
||||
near_misses = [
|
||||
semantic_wanted[0].replace("candidate_key`)", "other_key`)") ,
|
||||
semantic_wanted[1].replace("CREATE UNIQUE", "CREATE"),
|
||||
semantic_wanted[2].replace(" != ''", " IS NOT NULL"),
|
||||
]
|
||||
for stored, near_miss in zip(pocketbase_stored, near_misses):
|
||||
assert merge_indexes([stored], [near_miss])[1] == [("replace", near_miss)]
|
||||
print("self-test: index replacement and idempotence passed")
|
||||
return 0
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue