You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudberry.apache.org by dj...@apache.org on 2026/10/23 02:54:42 UTC
(cloudberry-backup) 01/03: Adapt tests to the PG16 kernel of Cloudberry main
This is an automated email from the ASF dual-hosted git repository.
tuhaihe pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudberry-backup.git
commit ee59c2cdae4e8943250a3730259593d50da48198
Author: Dianjin Wang <wa...@gmail.com>
AuthorDate: Tue Oct 20 17:08:53 2026 +0800
Adapt tests to the PG16 kernel of Cloudberry main
Cloudberry main (the future 3.x) is now on the PG16 kernel. Running the
suites against it gives 8 integration failures and 1 end_to_end
failure. Both suites pass on REL_2_STABLE. All of the failures come
from PG16 behavior changes, not from gpbackup bugs:
- A superuser that grants a membership without GRANTED BY is now
recorded as the bootstrap superuser, not as itself.
- A role named in GRANTED BY must hold ADMIN OPTION on the granted
role, even when it is a superuser.
- A role that granted a membership can no longer be dropped while
that membership exists, so the "dropped granter" case cannot be
set up at all.
- pg_get_viewdef() no longer qualifies the columns of a single-table
query.
Add testutils.IsCBDB3OrLater() and use it to set up and check these
cases per version. It compares SemVer.Major directly because a main
build reports "3.0.0-devel", which sorts before "3.0.0" under semver,
so Version.AtLeast("3") returns false.
The membership tests now also match on the member, since granting
ADMIN OPTION to testrole first adds a second row for usergroup.
Tested on a demo cluster on Cloudberry main (PG 16.9) and on
REL_2_STABLE (PG 14.10): integration and end_to_end pass on both.
See: https://github.com/apache/cloudberry-backup/issues/103
Assisted-by: Claude Code
---
end_to_end/end_to_end_suite_test.go | 10 ++++++++--
integration/metadata_globals_create_test.go | 6 ++++--
integration/metadata_globals_queries_test.go | 26 ++++++++++++++++++++++----
integration/postdata_create_test.go | 7 ++++++-
testutils/functions.go | 20 ++++++++++++++++++++
5 files changed, 60 insertions(+), 9 deletions(-)
diff --git a/end_to_end/end_to_end_suite_test.go b/end_to_end/end_to_end_suite_test.go
index 585c2057..468ee686 100644
--- a/end_to_end/end_to_end_suite_test.go
+++ b/end_to_end/end_to_end_suite_test.go
@@ -2158,8 +2158,14 @@ LANGUAGE plpgsql NO SQL;`)
Expect(contents).To(ContainSubstring("CREATE VIEW public.key_dependent_view AS \nSELECT\n\tNULL::integer AS key,\n\tNULL::character varying(20) COLLATE pg_catalog.\"C\" AS data;"))
Expect(contents).To(ContainSubstring("CREATE VIEW public.key_dependent_view_no_cols AS \nSELECT;"))
Expect(contents).To(ContainSubstring("ALTER TABLE ONLY public.view_base_table ADD CONSTRAINT view_base_table_pkey PRIMARY KEY (key);"))
- Expect(contents).To(ContainSubstring("CREATE OR REPLACE VIEW public.key_dependent_view AS SELECT view_base_table.key,\n (view_base_table.data COLLATE \"C\") AS data\n FROM public.view_base_table\n GROUP BY view_base_table.key;"))
- Expect(contents).To(ContainSubstring("CREATE OR REPLACE VIEW public.key_dependent_view_no_cols AS SELECT\n FROM public.view_base_table\n GROUP BY view_base_table.key\n HAVING (length((view_base_table.data)::text) > 0);"))
+ if testutils.IsCBDB3OrLater(backupConn) {
+ // PG16 no longer qualifies columns of a single-table query.
+ Expect(contents).To(ContainSubstring("CREATE OR REPLACE VIEW public.key_dependent_view AS SELECT key,\n (data COLLATE \"C\") AS data\n FROM public.view_base_table\n GROUP BY key;"))
+ Expect(contents).To(ContainSubstring("CREATE OR REPLACE VIEW public.key_dependent_view_no_cols AS SELECT\n FROM public.view_base_table\n GROUP BY key\n HAVING (length((data)::text) > 0);"))
+ } else {
+ Expect(contents).To(ContainSubstring("CREATE OR REPLACE VIEW public.key_dependent_view AS SELECT view_base_table.key,\n (view_base_table.data COLLATE \"C\") AS data\n FROM public.view_base_table\n GROUP BY view_base_table.key;"))
+ Expect(contents).To(ContainSubstring("CREATE OR REPLACE VIEW public.key_dependent_view_no_cols AS SELECT\n FROM public.view_base_table\n GROUP BY view_base_table.key\n HAVING (length((view_base_table.data)::text) > 0);"))
+ }
gprestore(gprestorePath, restoreHelperPath, timestamp,
"--redirect-db", "restoredb",
diff --git a/integration/metadata_globals_create_test.go b/integration/metadata_globals_create_test.go
index 2063c3a1..15551390 100644
--- a/integration/metadata_globals_create_test.go
+++ b/integration/metadata_globals_create_test.go
@@ -416,6 +416,7 @@ var _ = Describe("backup integration create statement tests", func() {
defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE testuser`)
})
It("grants a role without ADMIN OPTION", func() {
+ testutils.GrantAdminToGrantorIfRequired(connectionPool, "usergroup", "testrole")
numRoleMembers := len(backup.GetRoleMembers(connectionPool))
expectedRoleMember := backup.RoleMember{Role: "usergroup", Member: "testuser", Grantor: "testrole", IsAdmin: false}
backup.PrintRoleMembershipStatements(backupfile, tocfile, []backup.RoleMember{expectedRoleMember})
@@ -425,7 +426,7 @@ var _ = Describe("backup integration create statement tests", func() {
resultRoleMembers := backup.GetRoleMembers(connectionPool)
Expect(resultRoleMembers).To(HaveLen(numRoleMembers + 1))
for _, roleMember := range resultRoleMembers {
- if roleMember.Role == "usergroup" {
+ if roleMember.Role == "usergroup" && roleMember.Member == "testuser" {
structmatcher.ExpectStructsToMatch(&expectedRoleMember, &roleMember)
return
}
@@ -433,6 +434,7 @@ var _ = Describe("backup integration create statement tests", func() {
Fail("Role 'testuser' is not a member of role 'usergroup'")
})
It("grants a role WITH ADMIN OPTION", func() {
+ testutils.GrantAdminToGrantorIfRequired(connectionPool, "usergroup", "testrole")
numRoleMembers := len(backup.GetRoleMembers(connectionPool))
expectedRoleMember := backup.RoleMember{Role: "usergroup", Member: "testuser", Grantor: "testrole", IsAdmin: true}
backup.PrintRoleMembershipStatements(backupfile, tocfile, []backup.RoleMember{expectedRoleMember})
@@ -442,7 +444,7 @@ var _ = Describe("backup integration create statement tests", func() {
resultRoleMembers := backup.GetRoleMembers(connectionPool)
Expect(resultRoleMembers).To(HaveLen(numRoleMembers + 1))
for _, roleMember := range resultRoleMembers {
- if roleMember.Role == "usergroup" {
+ if roleMember.Role == "usergroup" && roleMember.Member == "testuser" {
structmatcher.ExpectStructsToMatch(&expectedRoleMember, &roleMember)
return
}
diff --git a/integration/metadata_globals_queries_test.go b/integration/metadata_globals_queries_test.go
index d6182e69..650fef14 100644
--- a/integration/metadata_globals_queries_test.go
+++ b/integration/metadata_globals_queries_test.go
@@ -506,6 +506,17 @@ CREATEEXTTABLE (protocol='gphdfs', type='writable')`
})
})
Describe("GetRoleMembers", func() {
+ /*
+ * Since PG16 (Cloudberry 3.x) a superuser that grants a membership
+ * without GRANTED BY is recorded as the bootstrap superuser rather
+ * than as itself.
+ */
+ implicitGrantor := func(currentRole string) string {
+ if testutils.IsCBDB3OrLater(connectionPool) {
+ return testutils.GetUserByID(connectionPool, 10)
+ }
+ return currentRole
+ }
BeforeEach(func() {
testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE usergroup`)
testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE testuser`)
@@ -516,7 +527,7 @@ CREATEEXTTABLE (protocol='gphdfs', type='writable')`
})
It("returns a role without ADMIN OPTION", func() {
testhelper.AssertQueryRuns(connectionPool, "GRANT usergroup TO testuser")
- expectedRoleMember := backup.RoleMember{Role: "usergroup", Member: "testuser", Grantor: "testrole", IsAdmin: false}
+ expectedRoleMember := backup.RoleMember{Role: "usergroup", Member: "testuser", Grantor: implicitGrantor("testrole"), IsAdmin: false}
roleMembers := backup.GetRoleMembers(connectionPool)
@@ -529,13 +540,14 @@ CREATEEXTTABLE (protocol='gphdfs', type='writable')`
Fail("Role 'testuser' is not a member of role 'usergroup'")
})
It("returns a role WITH ADMIN OPTION", func() {
+ testutils.GrantAdminToGrantorIfRequired(connectionPool, "usergroup", "testrole")
testhelper.AssertQueryRuns(connectionPool, "GRANT usergroup TO testuser WITH ADMIN OPTION GRANTED BY testrole")
expectedRoleMember := backup.RoleMember{Role: "usergroup", Member: "testuser", Grantor: "testrole", IsAdmin: true}
roleMembers := backup.GetRoleMembers(connectionPool)
for _, roleMember := range roleMembers {
- if roleMember.Role == "usergroup" {
+ if roleMember.Role == "usergroup" && roleMember.Member == "testuser" {
structmatcher.ExpectStructsToMatch(&expectedRoleMember, &roleMember)
return
}
@@ -552,7 +564,7 @@ CREATEEXTTABLE (protocol='gphdfs', type='writable')`
testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE "1testuser"`)
defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE "1testuser"`)
testhelper.AssertQueryRuns(connectionPool, `GRANT "1usergroup" TO "1testuser"`)
- expectedRoleMember := backup.RoleMember{Role: `"1usergroup"`, Member: `"1testuser"`, Grantor: `"1testrole"`, IsAdmin: false}
+ expectedRoleMember := backup.RoleMember{Role: `"1usergroup"`, Member: `"1testuser"`, Grantor: implicitGrantor(`"1testrole"`), IsAdmin: false}
roleMembers := backup.GetRoleMembers(connectionPool)
@@ -565,6 +577,12 @@ CREATEEXTTABLE (protocol='gphdfs', type='writable')`
Fail(`Role "1testuser" is not a member of role "1usergroup"`)
})
It("handles dropped granter", func() {
+ if testutils.IsCBDB3OrLater(connectionPool) {
+ // Since PG16 a role that granted a membership cannot be
+ // dropped while that membership exists, so a membership
+ // with a dropped grantor can no longer be produced.
+ Skip("Test not applicable to Cloudberry 3.x and later")
+ }
testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE testdropgranter_role`)
defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE testdropgranter_role`)
testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE testdropgranter_member`)
@@ -583,7 +601,7 @@ CREATEEXTTABLE (protocol='gphdfs', type='writable')`
testhelper.AssertQueryRuns(connectionPool, "CREATE CAST (oid AS text) WITH FUNCTION pg_catalog.text(oid) AS IMPLICIT;")
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION pg_catalog.text(oid) CASCADE;")
testhelper.AssertQueryRuns(connectionPool, "GRANT usergroup TO testuser")
- expectedRoleMember := backup.RoleMember{Role: "usergroup", Member: "testuser", Grantor: "testrole", IsAdmin: false}
+ expectedRoleMember := backup.RoleMember{Role: "usergroup", Member: "testuser", Grantor: implicitGrantor("testrole"), IsAdmin: false}
roleMembers := backup.GetRoleMembers(connectionPool)
diff --git a/integration/postdata_create_test.go b/integration/postdata_create_test.go
index aba3cfb1..1e2ba5a9 100644
--- a/integration/postdata_create_test.go
+++ b/integration/postdata_create_test.go
@@ -402,11 +402,16 @@ AS $$ BEGIN RAISE EXCEPTION 'exception'; END; $$;`)
DROP TABLE public.view_base_table CASCADE;
`)
+ viewDef := " SELECT view_base_table.key,\n (view_base_table.data COLLATE \"C\") AS data\n FROM public.view_base_table\n GROUP BY view_base_table.key;"
+ if testutils.IsCBDB3OrLater(connectionPool) {
+ // PG16 no longer qualifies columns of a single-table query.
+ viewDef = " SELECT key,\n (data COLLATE \"C\") AS data\n FROM public.view_base_table\n GROUP BY key;"
+ }
view1 := backup.View{
Schema: "public",
Name: "key_dependent_view",
Definition: sql.NullString{
- String: " SELECT view_base_table.key,\n (view_base_table.data COLLATE \"C\") AS data\n FROM public.view_base_table\n GROUP BY view_base_table.key;",
+ String: viewDef,
Valid: true,
},
ColumnDefs: []backup.ColumnDefinition{
diff --git a/testutils/functions.go b/testutils/functions.go
index da86eb0d..7245ce94 100644
--- a/testutils/functions.go
+++ b/testutils/functions.go
@@ -559,6 +559,26 @@ func SkipIfBefore7(connectionPool *dbconn.DBConn) {
}
}
+/*
+ * Cloudberry 3.x is built on the PostgreSQL 16 kernel. Compare the major
+ * version directly: a development build reports a version such as
+ * "3.0.0-devel", which sorts before "3.0.0" under semver and so fails
+ * Version.AtLeast("3").
+ */
+func IsCBDB3OrLater(connectionPool *dbconn.DBConn) bool {
+ return connectionPool.Version.IsCBDB() && connectionPool.Version.SemVer.Major >= 3
+}
+
+/*
+ * Since PG16 (Cloudberry 3.x) the role named in GRANTED BY must hold ADMIN
+ * OPTION on the granted role, even when that role is a superuser.
+ */
+func GrantAdminToGrantorIfRequired(connectionPool *dbconn.DBConn, role string, grantor string) {
+ if IsCBDB3OrLater(connectionPool) {
+ testhelper.AssertQueryRuns(connectionPool, fmt.Sprintf("GRANT %s TO %s WITH ADMIN OPTION", role, grantor))
+ }
+}
+
func InitializeTestTOC(buffer io.Writer, which string) (*toc.TOC, *utils.FileWithByteCount) {
tocfile := &toc.TOC{}
tocfile.InitializeMetadataEntryMap()
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cloudberry.apache.org
For additional commands, e-mail: commits-help@cloudberry.apache.org