blob: cffb9b0e74aeb97f805453ace715f63fe2cd2ade [file]
package backup_test
import (
"database/sql"
"fmt"
"github.com/apache/cloudberry-backup/backup"
"github.com/apache/cloudberry-backup/testutils"
"github.com/apache/cloudberry-backup/toc"
. "github.com/onsi/ginkgo/v2"
)
var _ = Describe("backup/postdata tests", func() {
var emptyMetadataMap = backup.MetadataMap{}
BeforeEach(func() {
tocfile, backupfile = testutils.InitializeTestTOC(buffer, "postdata")
})
Context("PrintCreateIndexStatements", func() {
var index backup.IndexDefinition
BeforeEach(func() {
index = backup.IndexDefinition{Oid: 1, Name: "testindex", OwningSchema: "public", OwningTable: "testtable", Def: sql.NullString{String: "CREATE INDEX testindex ON public.testtable USING btree(i)", Valid: true}}
})
It("can print a basic index", func() {
indexes := []backup.IndexDefinition{index}
backup.PrintCreateIndexStatements(backupfile, tocfile, indexes, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "public", "public.testtable", "testindex", toc.OBJ_INDEX)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, `CREATE INDEX testindex ON public.testtable USING btree(i);`)
})
It("can print an index used for clustering", func() {
index.IsClustered = true
indexes := []backup.IndexDefinition{index}
backup.PrintCreateIndexStatements(backupfile, tocfile, indexes, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "public", "public.testtable", "testindex", toc.OBJ_INDEX)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, "CREATE INDEX testindex ON public.testtable USING btree(i);",
"ALTER TABLE public.testtable CLUSTER ON testindex;")
})
It("can print an index with a tablespace", func() {
index.Tablespace = "test_tablespace"
indexes := []backup.IndexDefinition{index}
backup.PrintCreateIndexStatements(backupfile, tocfile, indexes, emptyMetadataMap)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, "CREATE INDEX testindex ON public.testtable USING btree(i);",
"ALTER INDEX public.testindex SET TABLESPACE test_tablespace;")
})
It("can print an index with a comment", func() {
indexes := []backup.IndexDefinition{index}
indexMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_INDEX, false, false, true, false)
backup.PrintCreateIndexStatements(backupfile, tocfile, indexes, indexMetadataMap)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, "CREATE INDEX testindex ON public.testtable USING btree(i);",
"COMMENT ON INDEX public.testindex IS 'This is an index comment.';")
})
It("can print an index that is a replica identity", func() {
index.IsReplicaIdentity = true
indexes := []backup.IndexDefinition{index}
backup.PrintCreateIndexStatements(backupfile, tocfile, indexes, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "public", "public.testtable", "testindex", toc.OBJ_INDEX)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer,
"CREATE INDEX testindex ON public.testtable USING btree(i);",
"ALTER TABLE public.testtable REPLICA IDENTITY USING INDEX testindex;",
)
})
It("can print an index of a partition that is attached to a parent partition index", func() {
testutils.SkipIfBefore7(connectionPool)
parentIndex := backup.IndexDefinition{Oid: 2, Name: "parenttestindex", OwningSchema: "public", OwningTable: "parenttesttable", Def: sql.NullString{String: "CREATE INDEX parenttestindex ON public.parenttesttable USING btree(i)", Valid: true}}
index.ParentIndexFQN = "public.parenttestindex"
indexes := []backup.IndexDefinition{parentIndex, index}
backup.PrintCreateIndexStatements(backupfile, tocfile, indexes, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "public", "public.parenttesttable", "parenttestindex", toc.OBJ_INDEX)
testutils.ExpectEntry(tocfile.PostdataEntries, 1, "public", "public.testtable", "testindex", toc.OBJ_INDEX)
testutils.ExpectEntry(tocfile.PostdataEntries, 2, "public", "public.testindex", "testindex", "INDEX METADATA")
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer,
"CREATE INDEX parenttestindex ON public.parenttesttable USING btree(i);",
"CREATE INDEX testindex ON public.testtable USING btree(i);",
"ALTER INDEX public.parenttestindex ATTACH PARTITION public.testindex;",
)
})
})
Context("PrintCreateRuleStatements", func() {
rule := backup.RuleDefinition{Oid: 1, Name: "testrule", OwningSchema: "public", OwningTable: "testtable", Def: sql.NullString{String: "CREATE RULE update_notify AS ON UPDATE TO testtable DO NOTIFY testtable;", Valid: true}}
It("can print a basic rule", func() {
rules := []backup.RuleDefinition{rule}
emptyMetadataMap := backup.MetadataMap{}
backup.PrintCreateRuleStatements(backupfile, tocfile, rules, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "public", "public.testtable", "testrule", toc.OBJ_RULE)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, "CREATE RULE update_notify AS ON UPDATE TO testtable DO NOTIFY testtable;")
})
It("can print a rule with a comment", func() {
rules := []backup.RuleDefinition{rule}
ruleMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_RULE, false, false, true, false)
backup.PrintCreateRuleStatements(backupfile, tocfile, rules, ruleMetadataMap)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, "CREATE RULE update_notify AS ON UPDATE TO testtable DO NOTIFY testtable;",
"COMMENT ON RULE testrule ON public.testtable IS 'This is a rule comment.';")
})
})
Context("PrintCreateTriggerStatements", func() {
trigger := backup.TriggerDefinition{Oid: 1, Name: "testtrigger", OwningSchema: "public", OwningTable: "testtable", Def: sql.NullString{String: "CREATE TRIGGER sync_testtable AFTER INSERT OR DELETE OR UPDATE ON testtable FOR EACH STATEMENT EXECUTE PROCEDURE flatfile_update_trigger()", Valid: true}}
It("can print a basic trigger", func() {
triggers := []backup.TriggerDefinition{trigger}
emptyMetadataMap := backup.MetadataMap{}
backup.PrintCreateTriggerStatements(backupfile, tocfile, triggers, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "public", "public.testtable", "testtrigger", toc.OBJ_TRIGGER)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, "CREATE TRIGGER sync_testtable AFTER INSERT OR DELETE OR UPDATE ON testtable FOR EACH STATEMENT EXECUTE PROCEDURE flatfile_update_trigger();")
})
It("can print a trigger with a comment", func() {
triggers := []backup.TriggerDefinition{trigger}
triggerMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_TRIGGER, false, false, true, false)
backup.PrintCreateTriggerStatements(backupfile, tocfile, triggers, triggerMetadataMap)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, "CREATE TRIGGER sync_testtable AFTER INSERT OR DELETE OR UPDATE ON testtable FOR EACH STATEMENT EXECUTE PROCEDURE flatfile_update_trigger();",
"COMMENT ON TRIGGER testtrigger ON public.testtable IS 'This is a trigger comment.';")
})
})
Context("PrintCreateEventTriggerStatements", func() {
var evTrigExecReplacement string
BeforeEach(func() {
if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("7")) || connectionPool.Version.IsCBDB() {
evTrigExecReplacement = toc.OBJ_FUNCTION
} else {
evTrigExecReplacement = toc.OBJ_PROCEDURE
}
})
It("can print a basic event trigger", func() {
eventTrigger := backup.EventTrigger{Oid: 1, Name: "testeventtrigger", Event: "ddl_command_start", FunctionName: "abort_any_command", Enabled: "O"}
eventTriggers := []backup.EventTrigger{eventTrigger}
emptyMetadataMap := backup.MetadataMap{}
backup.PrintCreateEventTriggerStatements(backupfile, tocfile, eventTriggers, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "", "", "testeventtrigger", toc.OBJ_EVENT_TRIGGER)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, fmt.Sprintf(`CREATE EVENT TRIGGER testeventtrigger
ON ddl_command_start
EXECUTE %s abort_any_command();`, evTrigExecReplacement))
})
It("can print a basic event trigger with a comment", func() {
eventTrigger := backup.EventTrigger{Oid: 1, Name: "testeventtrigger", Event: "ddl_command_start", FunctionName: "abort_any_command", Enabled: "O"}
eventTriggers := []backup.EventTrigger{eventTrigger}
eventTriggerMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_EVENT_TRIGGER, false, false, true, false)
backup.PrintCreateEventTriggerStatements(backupfile, tocfile, eventTriggers, eventTriggerMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "", "", "testeventtrigger", toc.OBJ_EVENT_TRIGGER)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, fmt.Sprintf(`CREATE EVENT TRIGGER testeventtrigger
ON ddl_command_start
EXECUTE %s abort_any_command();`, evTrigExecReplacement), `COMMENT ON EVENT TRIGGER testeventtrigger IS 'This is an event trigger comment.';`)
})
It("can print a basic event trigger with an owner", func() {
eventTrigger := backup.EventTrigger{Oid: 1, Name: "testeventtrigger", Event: "ddl_command_start", FunctionName: "abort_any_command", Enabled: "O"}
eventTriggers := []backup.EventTrigger{eventTrigger}
eventTriggerMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_EVENT_TRIGGER, false, true, false, false)
backup.PrintCreateEventTriggerStatements(backupfile, tocfile, eventTriggers, eventTriggerMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "", "", "testeventtrigger", toc.OBJ_EVENT_TRIGGER)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, fmt.Sprintf(`CREATE EVENT TRIGGER testeventtrigger
ON ddl_command_start
EXECUTE %s abort_any_command();`, evTrigExecReplacement), `ALTER EVENT TRIGGER testeventtrigger OWNER TO testrole;`)
})
It("can print a basic event trigger with a security label", func() {
eventTrigger := backup.EventTrigger{Oid: 1, Name: "testeventtrigger", Event: "ddl_command_start", FunctionName: "abort_any_command", Enabled: "O"}
eventTriggers := []backup.EventTrigger{eventTrigger}
eventTriggerMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_EVENT_TRIGGER, false, false, false, true)
backup.PrintCreateEventTriggerStatements(backupfile, tocfile, eventTriggers, eventTriggerMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "", "", "testeventtrigger", toc.OBJ_EVENT_TRIGGER)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, fmt.Sprintf(`CREATE EVENT TRIGGER testeventtrigger
ON ddl_command_start
EXECUTE %s abort_any_command();`, evTrigExecReplacement), `SECURITY LABEL FOR dummy ON EVENT TRIGGER testeventtrigger IS 'unclassified';`)
})
It("can print an event trigger with filter variables", func() {
eventTrigger := backup.EventTrigger{Oid: 1, Name: "testeventtrigger", Event: "ddl_command_start", FunctionName: "abort_any_command", Enabled: "O", EventTags: `'DROP FUNCTION','DROP TABLE'`}
eventTriggers := []backup.EventTrigger{eventTrigger}
emptyMetadataMap := backup.MetadataMap{}
backup.PrintCreateEventTriggerStatements(backupfile, tocfile, eventTriggers, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "", "", "testeventtrigger", toc.OBJ_EVENT_TRIGGER)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, fmt.Sprintf(`CREATE EVENT TRIGGER testeventtrigger
ON ddl_command_start
WHEN TAG IN ('DROP FUNCTION','DROP TABLE')
EXECUTE %s abort_any_command();`, evTrigExecReplacement))
})
It("can print an event trigger with filter variables with enable option DISABLE", func() {
eventTrigger := backup.EventTrigger{Oid: 1, Name: "testeventtrigger", Event: "ddl_command_start", FunctionName: "abort_any_command", Enabled: "D"}
eventTriggers := []backup.EventTrigger{eventTrigger}
emptyMetadataMap := backup.MetadataMap{}
backup.PrintCreateEventTriggerStatements(backupfile, tocfile, eventTriggers, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "", "", "testeventtrigger", toc.OBJ_EVENT_TRIGGER)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, fmt.Sprintf(`CREATE EVENT TRIGGER testeventtrigger
ON ddl_command_start
EXECUTE %s abort_any_command();`, evTrigExecReplacement), `ALTER EVENT TRIGGER testeventtrigger DISABLE;`)
})
It("can print an event trigger with filter variables with enable option ENABLE", func() {
eventTrigger := backup.EventTrigger{Oid: 1, Name: "testeventtrigger", Event: "ddl_command_start", FunctionName: "abort_any_command", Enabled: ""}
eventTriggers := []backup.EventTrigger{eventTrigger}
emptyMetadataMap := backup.MetadataMap{}
backup.PrintCreateEventTriggerStatements(backupfile, tocfile, eventTriggers, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "", "", "testeventtrigger", toc.OBJ_EVENT_TRIGGER)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, fmt.Sprintf(`CREATE EVENT TRIGGER testeventtrigger
ON ddl_command_start
EXECUTE %s abort_any_command();`, evTrigExecReplacement), `ALTER EVENT TRIGGER testeventtrigger ENABLE;`)
})
It("can print an event trigger with filter variables with enable option ENABLE REPLICA", func() {
eventTrigger := backup.EventTrigger{Oid: 1, Name: "testeventtrigger", Event: "ddl_command_start", FunctionName: "abort_any_command", Enabled: "R"}
eventTriggers := []backup.EventTrigger{eventTrigger}
emptyMetadataMap := backup.MetadataMap{}
backup.PrintCreateEventTriggerStatements(backupfile, tocfile, eventTriggers, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "", "", "testeventtrigger", toc.OBJ_EVENT_TRIGGER)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, fmt.Sprintf(`CREATE EVENT TRIGGER testeventtrigger
ON ddl_command_start
EXECUTE %s abort_any_command();`, evTrigExecReplacement), `ALTER EVENT TRIGGER testeventtrigger ENABLE REPLICA;`)
})
It("can print an event trigger with filter variables with enable option ENABLE ALWAYS", func() {
eventTrigger := backup.EventTrigger{Oid: 1, Name: "testeventtrigger", Event: "ddl_command_start", FunctionName: "abort_any_command", Enabled: "A"}
eventTriggers := []backup.EventTrigger{eventTrigger}
emptyMetadataMap := backup.MetadataMap{}
backup.PrintCreateEventTriggerStatements(backupfile, tocfile, eventTriggers, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "", "", "testeventtrigger", toc.OBJ_EVENT_TRIGGER)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, fmt.Sprintf(`CREATE EVENT TRIGGER testeventtrigger
ON ddl_command_start
EXECUTE %s abort_any_command();`, evTrigExecReplacement), `ALTER EVENT TRIGGER testeventtrigger ENABLE ALWAYS;`)
})
})
Context("PrintCreateExtendedStatistics", func() {
statExt := backup.StatisticExt{Oid: 1, Name: "test_stat", Namespace: "public", Owner: "username", TableSchema: "myschema", TableName: "mytable", Definition: "CREATE STATISTICS public.test_stat (dependencies) ON a, b FROM myschema.mytable"}
It("can print an extended statistics", func() {
stats := []backup.StatisticExt{statExt}
emptyMetadataMap := backup.MetadataMap{}
backup.PrintCreateExtendedStatistics(backupfile, tocfile, stats, emptyMetadataMap)
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "public", "myschema.mytable", "test_stat", toc.OBJ_STATISTICS)
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, `CREATE STATISTICS public.test_stat (dependencies) ON a, b FROM myschema.mytable;`)
})
})
Describe("PrintCreatePostdataViewStatements", func() {
It("prints all postdata views", func() {
view1 := backup.View{
Schema: "schema1",
Name: "view1",
Definition: sql.NullString{String: "SELECT 1", Valid: true},
}
view2 := backup.View{
Schema: "schema1",
Name: "view2",
Definition: sql.NullString{String: "SELECT 2", Valid: true},
Options: " WITH (check_option = cascaded, security_barrier)",
}
backup.PrintCreatePostdataViewStatements(backupfile, tocfile, []backup.View{view1, view2})
testutils.ExpectEntry(tocfile.PostdataEntries, 0, "schema1", "", "view1", toc.OBJ_VIEW)
testutils.ExpectEntry(tocfile.PostdataEntries, 1, "schema1", "", "view2", toc.OBJ_VIEW)
expected1 := "CREATE OR REPLACE VIEW schema1.view1 AS SELECT 1"
expected2 := "CREATE OR REPLACE VIEW schema1.view2 WITH (check_option = cascaded, security_barrier) AS SELECT 2"
testutils.AssertBufferContents(tocfile.PostdataEntries, buffer, expected1, expected2)
})
})
})