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
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,13 @@ func (s *SnapshotGenerator) parseDump(d []byte) *dump {
case strings.HasPrefix(line, "ALTER TABLE") && isClusterOnAlterTable(line):
indicesAndConstraints.WriteString(line)
indicesAndConstraints.WriteString("\n\n")
case isAttachPartitionIndexStatement(line):
// ATTACH PARTITION references the parent index, which is created in
// the indices and constraints section. Restored in place it runs
// before that index exists, fails with "relation does not exist",
// and is never retried, leaving the parent index invalid.
indicesAndConstraints.WriteString(line)
indicesAndConstraints.WriteString("\n\n")
case strings.HasPrefix(line, "ALTER TABLE") && strings.Contains(line, "REPLICA IDENTITY"):
// REPLICA IDENTITY lines should be in the indicesAndConstraints section
// since they reference constraints/indices that are also there
Expand Down Expand Up @@ -1024,6 +1031,14 @@ func isIndexStatement(line string) bool {
strings.HasPrefix(line, "CREATE UNIQUE INDEX")
}

// isAttachPartitionIndexStatement reports whether the line attaches a partition
// index to its parent. pg_dump emits these for partitioned tables, after the
// CREATE INDEX statements they depend on.
func isAttachPartitionIndexStatement(line string) bool {
return strings.HasPrefix(line, "ALTER INDEX") &&
strings.Contains(line, "ATTACH PARTITION")
}

func materializedViewName(line string) (string, bool) {
if !strings.HasPrefix(line, "CREATE MATERIALIZED VIEW ") {
return "", false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1764,6 +1764,44 @@ ALTER TABLE public.example_table CLUSTER ON example_table_created_at_idx;
strings.Index(string(dump.indicesAndConstraints), "CLUSTER ON example_table_created_at_idx"))
}

func TestSnapshotGenerator_parseDumpMovesAttachPartitionToConstraints(t *testing.T) {
t.Parallel()

dumpBytes := []byte(`CREATE TABLE public.events (
event_id bigint NOT NULL,
partition_id integer NOT NULL
)
PARTITION BY LIST (partition_id);

CREATE TABLE public.events_0 (
event_id bigint NOT NULL,
partition_id integer NOT NULL
);

ALTER TABLE ONLY public.events ATTACH PARTITION public.events_0 FOR VALUES IN (0);

CREATE INDEX events_partition_id_idx ON ONLY public.events USING btree (partition_id);

CREATE INDEX events_0_partition_id_idx ON public.events_0 USING btree (partition_id);

ALTER INDEX public.events_partition_id_idx ATTACH PARTITION public.events_0_partition_id_idx;
`)

dump := (&SnapshotGenerator{}).parseDump(dumpBytes)

// the table attachment stays with the tables, the index attachment does not
require.Contains(t, string(dump.filtered), "ALTER TABLE ONLY public.events ATTACH PARTITION public.events_0")
require.NotContains(t, string(dump.filtered), "ALTER INDEX public.events_partition_id_idx ATTACH PARTITION")

require.Contains(t, string(dump.indicesAndConstraints), "ALTER INDEX public.events_partition_id_idx ATTACH PARTITION public.events_0_partition_id_idx;")

// and it has to land after the parent index it references, or it fails the
// same way it did when it was restored in place
require.Less(t,
strings.Index(string(dump.indicesAndConstraints), "CREATE INDEX events_partition_id_idx"),
strings.Index(string(dump.indicesAndConstraints), "ALTER INDEX public.events_partition_id_idx ATTACH PARTITION"))
}

func TestSnapshotGenerator_parseDumpMovesMaterializedViewIndexesToViews(t *testing.T) {
t.Parallel()

Expand Down