github.com/greenplum-db/gpbackup@v0.0.0-20240517212602-89daab1885b3/backup/predata_types_test.go (about)

     1  package backup_test
     2  
     3  import (
     4  	"database/sql"
     5  	"fmt"
     6  
     7  	"github.com/greenplum-db/gpbackup/backup"
     8  	"github.com/greenplum-db/gpbackup/testutils"
     9  	"github.com/greenplum-db/gpbackup/toc"
    10  
    11  	. "github.com/onsi/ginkgo/v2"
    12  )
    13  
    14  var _ = Describe("backup/predata_types tests", func() {
    15  	emptyMetadata := backup.ObjectMetadata{}
    16  	emptyMetadataMap := backup.MetadataMap{}
    17  	typeMetadata := testutils.DefaultMetadata(toc.OBJ_TYPE, false, true, true, true)
    18  	typeMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_TYPE, false, true, true, true)
    19  
    20  	BeforeEach(func() {
    21  		tocfile, backupfile = testutils.InitializeTestTOC(buffer, "predata")
    22  	})
    23  	Describe("PrintCreateEnumTypeStatements", func() {
    24  		enumOne := backup.EnumType{Oid: 1, Schema: "public", Name: "enum_type", EnumLabels: "'bar',\n\t'baz',\n\t'foo'"}
    25  		enumTwo := backup.EnumType{Oid: 1, Schema: "public", Name: "enum_type", EnumLabels: "'bar',\n\t'baz',\n\t'foo'"}
    26  
    27  		It("prints an enum type with multiple attributes", func() {
    28  			backup.PrintCreateEnumTypeStatements(backupfile, tocfile, []backup.EnumType{enumOne}, emptyMetadataMap)
    29  			testutils.ExpectEntry(tocfile.PredataEntries, 0, "public", "", "enum_type", toc.OBJ_TYPE)
    30  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TYPE public.enum_type AS ENUM (
    31  	'bar',
    32  	'baz',
    33  	'foo'
    34  );`)
    35  		})
    36  		It("prints an enum type with comment, security label, and owner", func() {
    37  			backup.PrintCreateEnumTypeStatements(backupfile, tocfile, []backup.EnumType{enumTwo}, typeMetadataMap)
    38  			expectedStatements := []string{`CREATE TYPE public.enum_type AS ENUM (
    39  	'bar',
    40  	'baz',
    41  	'foo'
    42  );`,
    43  				"COMMENT ON TYPE public.enum_type IS 'This is a type comment.';",
    44  				"ALTER TYPE public.enum_type OWNER TO testrole;",
    45  				"SECURITY LABEL FOR dummy ON TYPE public.enum_type IS 'unclassified';"}
    46  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, expectedStatements...)
    47  		})
    48  	})
    49  	Describe("PrintCreateCompositeTypeStatement", func() {
    50  		var compType backup.CompositeType
    51  		var oneAtt, oneAttWithCollation, twoAtts, attWithComment []backup.Attribute
    52  		BeforeEach(func() {
    53  			compType = backup.CompositeType{Oid: 1, Schema: "public", Name: "composite_type"}
    54  			oneAtt = []backup.Attribute{{Name: "foo", Type: "integer"}}
    55  			oneAttWithCollation = []backup.Attribute{{Name: "foo", Type: "integer", Collation: "public.some_coll"}}
    56  			twoAtts = []backup.Attribute{{Name: "foo", Type: "integer"}, {Name: "bar", Type: "text"}}
    57  			attWithComment = []backup.Attribute{{Name: "foo", Type: "integer", Comment: "'attribute comment'"}}
    58  		})
    59  
    60  		It("prints a composite type with one attribute", func() {
    61  			compType.Attributes = oneAtt
    62  			backup.PrintCreateCompositeTypeStatement(backupfile, tocfile, compType, emptyMetadata)
    63  			testutils.ExpectEntry(tocfile.PredataEntries, 0, "public", "", "composite_type", toc.OBJ_TYPE)
    64  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TYPE public.composite_type AS (
    65  	foo integer
    66  );`)
    67  		})
    68  		It("prints a composite type with one attribute with a collation", func() {
    69  			compType.Attributes = oneAttWithCollation
    70  			backup.PrintCreateCompositeTypeStatement(backupfile, tocfile, compType, emptyMetadata)
    71  			testutils.ExpectEntry(tocfile.PredataEntries, 0, "public", "", "composite_type", toc.OBJ_TYPE)
    72  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TYPE public.composite_type AS (
    73  	foo integer COLLATE public.some_coll
    74  );`)
    75  		})
    76  		It("prints a composite type with multiple attributes", func() {
    77  			compType.Attributes = twoAtts
    78  			backup.PrintCreateCompositeTypeStatement(backupfile, tocfile, compType, emptyMetadata)
    79  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TYPE public.composite_type AS (
    80  	foo integer,
    81  	bar text
    82  );`)
    83  		})
    84  		It("prints a composite type with comment, security label, and owner", func() {
    85  			compType.Attributes = twoAtts
    86  			backup.PrintCreateCompositeTypeStatement(backupfile, tocfile, compType, typeMetadata)
    87  			expectedEntries := []string{`CREATE TYPE public.composite_type AS (
    88  	foo integer,
    89  	bar text
    90  );`,
    91  				"COMMENT ON TYPE public.composite_type IS 'This is a type comment.';",
    92  				"ALTER TYPE public.composite_type OWNER TO testrole;",
    93  				"SECURITY LABEL FOR dummy ON TYPE public.composite_type IS 'unclassified';"}
    94  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, expectedEntries...)
    95  		})
    96  		It("prints a composite type with attribute comment", func() {
    97  			compType.Attributes = attWithComment
    98  			backup.PrintCreateCompositeTypeStatement(backupfile, tocfile, compType, emptyMetadata)
    99  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TYPE public.composite_type AS (
   100  	foo integer
   101  );`, "COMMENT ON COLUMN public.composite_type.foo IS 'attribute comment';")
   102  		})
   103  	})
   104  	Describe("PrintCreateBaseTypeStatement", func() {
   105  		baseSimple := backup.BaseType{Oid: 1, Schema: "public", Name: "base_type", Input: "input_fn", Output: "output_fn", Receive: "", Send: "", ModIn: "", ModOut: "", InternalLength: -1, IsPassedByValue: false, Alignment: "c", Storage: "p", DefaultVal: "", Element: "", Category: "U", Preferred: false, Delimiter: ""}
   106  		basePartial := backup.BaseType{Oid: 1, Schema: "public", Name: "base_type", Input: "input_fn", Output: "output_fn", Receive: "receive_fn", Send: "send_fn", ModIn: "modin_fn", ModOut: "modout_fn", InternalLength: -1, IsPassedByValue: false, Alignment: "c", Storage: "p", DefaultVal: "42", Element: "int4", Category: "U", Delimiter: ","}
   107  		baseFull := backup.BaseType{Oid: 1, Schema: "public", Name: "base_type", Input: "input_fn", Output: "output_fn", Receive: "receive_fn", Send: "send_fn", ModIn: "modin_fn", ModOut: "modout_fn", InternalLength: 16, IsPassedByValue: true, Alignment: "s", Storage: "e", DefaultVal: "42", Element: "int4", Category: "N", Preferred: true, Delimiter: ",", StorageOptions: "compresstype=zlib, compresslevel=1, blocksize=32768", Collatable: true}
   108  		basePermOne := backup.BaseType{Oid: 1, Schema: "public", Name: "base_type", Input: "input_fn", Output: "output_fn", Receive: "", Send: "", ModIn: "", ModOut: "", InternalLength: -1, IsPassedByValue: false, Alignment: "d", Storage: "m", DefaultVal: "", Element: "", Category: "U", Delimiter: ""}
   109  		basePermTwo := backup.BaseType{Oid: 1, Schema: "public", Name: "base_type", Input: "input_fn", Output: "output_fn", Receive: "", Send: "", ModIn: "", ModOut: "", InternalLength: -1, IsPassedByValue: false, Alignment: "i", Storage: "x", DefaultVal: "", Element: "", Category: "U", Delimiter: ""}
   110  
   111  		It("prints a base type with no optional arguments", func() {
   112  			backup.PrintCreateBaseTypeStatement(backupfile, tocfile, baseSimple, emptyMetadata)
   113  			testutils.ExpectEntry(tocfile.PredataEntries, 0, "public", "", "base_type", toc.OBJ_TYPE)
   114  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TYPE public.base_type (
   115  	INPUT = input_fn,
   116  	OUTPUT = output_fn
   117  );`)
   118  		})
   119  		It("prints a base type where all optional arguments have default values where possible", func() {
   120  			backup.PrintCreateBaseTypeStatement(backupfile, tocfile, basePartial, emptyMetadata)
   121  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, fmt.Sprintf(`CREATE TYPE public.base_type (
   122  	INPUT = input_fn,
   123  	OUTPUT = output_fn,
   124  	RECEIVE = receive_fn,
   125  	SEND = send_fn,
   126  	TYPMOD_IN = modin_fn,
   127  	TYPMOD_OUT = modout_fn,
   128  	DEFAULT = '42',
   129  	ELEMENT = int4,
   130  	DELIMITER = ','
   131  );`))
   132  		})
   133  		It("prints a base type with all optional arguments provided", func() {
   134  			backup.PrintCreateBaseTypeStatement(backupfile, tocfile, baseFull, emptyMetadata)
   135  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, fmt.Sprintf(`CREATE TYPE public.base_type (
   136  	INPUT = input_fn,
   137  	OUTPUT = output_fn,
   138  	RECEIVE = receive_fn,
   139  	SEND = send_fn,
   140  	TYPMOD_IN = modin_fn,
   141  	TYPMOD_OUT = modout_fn,
   142  	INTERNALLENGTH = 16,
   143  	PASSEDBYVALUE,
   144  	ALIGNMENT = int2,
   145  	STORAGE = external,
   146  	DEFAULT = '42',
   147  	ELEMENT = int4,
   148  	DELIMITER = ',',
   149  	CATEGORY = 'N',
   150  	PREFERRED = true,
   151  	COLLATABLE = true
   152  );
   153  
   154  ALTER TYPE public.base_type
   155  	SET DEFAULT ENCODING (compresstype=zlib, compresslevel=1, blocksize=32768);`))
   156  		})
   157  		It("prints a base type with double alignment and main storage", func() {
   158  			backup.PrintCreateBaseTypeStatement(backupfile, tocfile, basePermOne, emptyMetadata)
   159  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TYPE public.base_type (
   160  	INPUT = input_fn,
   161  	OUTPUT = output_fn,
   162  	ALIGNMENT = double,
   163  	STORAGE = main
   164  );`)
   165  		})
   166  		It("prints a base type with int4 alignment and external storage", func() {
   167  			backup.PrintCreateBaseTypeStatement(backupfile, tocfile, basePermTwo, emptyMetadata)
   168  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TYPE public.base_type (
   169  	INPUT = input_fn,
   170  	OUTPUT = output_fn,
   171  	ALIGNMENT = int4,
   172  	STORAGE = extended
   173  );`)
   174  		})
   175  		It("prints a base type with comment, security label, and owner", func() {
   176  			backup.PrintCreateBaseTypeStatement(backupfile, tocfile, baseSimple, typeMetadata)
   177  			expectedEntries := []string{`CREATE TYPE public.base_type (
   178  	INPUT = input_fn,
   179  	OUTPUT = output_fn
   180  );`,
   181  				"COMMENT ON TYPE public.base_type IS 'This is a type comment.';",
   182  				"ALTER TYPE public.base_type OWNER TO testrole;",
   183  				"SECURITY LABEL FOR dummy ON TYPE public.base_type IS 'unclassified';"}
   184  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, expectedEntries...)
   185  		})
   186  	})
   187  	Describe("PrintCreateShellTypeStatements", func() {
   188  		shellOne := backup.ShellType{Oid: 1, Schema: "public", Name: "shell_type1"}
   189  		baseOne := backup.BaseType{Oid: 1, Schema: "public", Name: "base_type1", Input: "input_fn", Output: "output_fn", Receive: "", Send: "", ModIn: "", ModOut: "", InternalLength: -1, IsPassedByValue: false, Alignment: "c", Storage: "p", DefaultVal: "", Element: "", Category: "U", Delimiter: ""}
   190  		baseTwo := backup.BaseType{Oid: 1, Schema: "public", Name: "base_type2", Input: "input_fn", Output: "output_fn", Receive: "", Send: "", ModIn: "", ModOut: "", InternalLength: -1, IsPassedByValue: false, Alignment: "c", Storage: "p", DefaultVal: "", Element: "", Category: "U", Delimiter: ""}
   191  		rangeOne := backup.RangeType{Oid: 1, Schema: "public", Name: "range_type1"}
   192  		It("prints shell type for a shell type", func() {
   193  			backup.PrintCreateShellTypeStatements(backupfile, tocfile, []backup.ShellType{shellOne}, []backup.BaseType{}, []backup.RangeType{})
   194  			testutils.ExpectEntry(tocfile.PredataEntries, 0, "public", "", "shell_type1", toc.OBJ_TYPE)
   195  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, "CREATE TYPE public.shell_type1;")
   196  		})
   197  		It("prints shell type for a base type", func() {
   198  			backup.PrintCreateShellTypeStatements(backupfile, tocfile, []backup.ShellType{}, []backup.BaseType{baseOne, baseTwo}, []backup.RangeType{})
   199  			testutils.ExpectEntry(tocfile.PredataEntries, 0, "public", "", "base_type1", toc.OBJ_TYPE)
   200  			testutils.ExpectEntry(tocfile.PredataEntries, 1, "public", "", "base_type2", toc.OBJ_TYPE)
   201  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, "CREATE TYPE public.base_type1;", "CREATE TYPE public.base_type2;")
   202  		})
   203  		It("prints shell type for a range type", func() {
   204  			backup.PrintCreateShellTypeStatements(backupfile, tocfile, []backup.ShellType{}, []backup.BaseType{}, []backup.RangeType{rangeOne})
   205  			testutils.ExpectEntry(tocfile.PredataEntries, 0, "public", "", "range_type1", toc.OBJ_TYPE)
   206  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, "CREATE TYPE public.range_type1;")
   207  		})
   208  	})
   209  	Describe("PrintCreateDomainStatement", func() {
   210  		emptyConstraint := make([]backup.Constraint, 0)
   211  		checkConstraint := []backup.Constraint{{Name: "domain1_check", Def: sql.NullString{String: "CHECK (VALUE > 2)", Valid: true}, OwningObject: "public.domain1"}}
   212  		domainOne := backup.Domain{Oid: 1, Schema: "public", Name: "domain1", DefaultVal: "4", BaseType: "numeric", NotNull: true, Collation: "public.mycollation"}
   213  		domainTwo := backup.Domain{Oid: 1, Schema: "public", Name: "domain2", DefaultVal: "", BaseType: "varchar", NotNull: false, Collation: ""}
   214  		It("prints a domain with a constraint", func() {
   215  			backup.PrintCreateDomainStatement(backupfile, tocfile, domainOne, emptyMetadata, checkConstraint)
   216  			testutils.ExpectEntry(tocfile.PredataEntries, 0, "public", "", "domain1", toc.OBJ_DOMAIN)
   217  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE DOMAIN public.domain1 AS numeric DEFAULT 4 COLLATE public.mycollation NOT NULL
   218  	CONSTRAINT domain1_check CHECK (VALUE > 2);`)
   219  		})
   220  		It("prints a domain without constraint", func() {
   221  			backup.PrintCreateDomainStatement(backupfile, tocfile, domainOne, emptyMetadata, emptyConstraint)
   222  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE DOMAIN public.domain1 AS numeric DEFAULT 4 COLLATE public.mycollation NOT NULL;`)
   223  		})
   224  		It("prints a domain without constraint with comment, security label, and owner", func() {
   225  			domainMetadata := testutils.DefaultMetadata(toc.OBJ_DOMAIN, false, true, true, true)
   226  			backup.PrintCreateDomainStatement(backupfile, tocfile, domainTwo, domainMetadata, emptyConstraint)
   227  			expectedEntries := []string{"CREATE DOMAIN public.domain2 AS varchar;",
   228  				"COMMENT ON DOMAIN public.domain2 IS 'This is a domain comment.';",
   229  				"ALTER DOMAIN public.domain2 OWNER TO testrole;",
   230  				"SECURITY LABEL FOR dummy ON DOMAIN public.domain2 IS 'unclassified';"}
   231  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, expectedEntries...)
   232  		})
   233  	})
   234  	Describe("PrintCreateRangeTypeStatement", func() {
   235  		basicRangeType := backup.RangeType{
   236  			Name:    "rangetype",
   237  			Schema:  "public",
   238  			SubType: "test_subtype_schema.test_subtype",
   239  		}
   240  		complexRangeType := backup.RangeType{
   241  			Name: "rangetype", Schema: "public",
   242  			SubType:        "test_subtype_schema.test_subtype",
   243  			SubTypeOpClass: "opclass_schema.test_opclass",
   244  			Collation:      "collation_schema.test_collation",
   245  			Canonical:      "canonical_schema.test_canonical",
   246  			SubTypeDiff:    "diff_schema.test_diff",
   247  		}
   248  		It("prints a basic range type", func() {
   249  			backup.PrintCreateRangeTypeStatement(backupfile, tocfile, basicRangeType, emptyMetadata)
   250  			testutils.ExpectEntry(tocfile.PredataEntries, 0, "public", "", "rangetype", toc.OBJ_TYPE)
   251  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TYPE public.rangetype AS RANGE (
   252  	SUBTYPE = test_subtype_schema.test_subtype
   253  );`)
   254  		})
   255  		It("prints a complex range type", func() {
   256  			backup.PrintCreateRangeTypeStatement(backupfile, tocfile, complexRangeType, emptyMetadata)
   257  			testutils.ExpectEntry(tocfile.PredataEntries, 0, "public", "", "rangetype", toc.OBJ_TYPE)
   258  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TYPE public.rangetype AS RANGE (
   259  	SUBTYPE = test_subtype_schema.test_subtype,
   260  	SUBTYPE_OPCLASS = opclass_schema.test_opclass,
   261  	COLLATION = collation_schema.test_collation,
   262  	CANONICAL = canonical_schema.test_canonical,
   263  	SUBTYPE_DIFF = diff_schema.test_diff
   264  );`)
   265  		})
   266  		It("prints a range type with an owner, security label, and a comment", func() {
   267  			backup.PrintCreateRangeTypeStatement(backupfile, tocfile, basicRangeType, typeMetadata)
   268  			expectedStatements := []string{`CREATE TYPE public.rangetype AS RANGE (
   269  	SUBTYPE = test_subtype_schema.test_subtype
   270  );`,
   271  				"COMMENT ON TYPE public.rangetype IS 'This is a type comment.';",
   272  				"ALTER TYPE public.rangetype OWNER TO testrole;",
   273  				"SECURITY LABEL FOR dummy ON TYPE public.rangetype IS 'unclassified';"}
   274  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, expectedStatements...)
   275  		})
   276  	})
   277  	Describe("PrintCreateCollationStatement", func() {
   278  		It("prints a create collation statement", func() {
   279  			collation := backup.Collation{Oid: 1, Name: "collation1", Collate: "collate1", Ctype: "ctype1", Schema: "schema1"}
   280  			backup.PrintCreateCollationStatements(backupfile, tocfile, []backup.Collation{collation}, emptyMetadataMap)
   281  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE COLLATION schema1.collation1 (LC_COLLATE = 'collate1', LC_CTYPE = 'ctype1');`)
   282  		})
   283  		It("prints a create collation statement with owner and comment", func() {
   284  			collation := backup.Collation{Oid: 1, Name: "collation1", Collate: "collate1", Ctype: "ctype1", Schema: "schema1"}
   285  			collationMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_COLLATION, false, true, true, false)
   286  			backup.PrintCreateCollationStatements(backupfile, tocfile, []backup.Collation{collation}, collationMetadataMap)
   287  			expectedStatements := []string{
   288  				"CREATE COLLATION schema1.collation1 (LC_COLLATE = 'collate1', LC_CTYPE = 'ctype1');",
   289  				"COMMENT ON COLLATION schema1.collation1 IS 'This is a collation comment.';",
   290  				"ALTER COLLATION schema1.collation1 OWNER TO testrole;"}
   291  			testutils.AssertBufferContents(tocfile.PredataEntries, buffer, expectedStatements...)
   292  		})
   293  	})
   294  })