| package backup_test |
| |
| import ( |
| "database/sql" |
| |
| "github.com/apache/cloudberry-backup/backup" |
| "github.com/apache/cloudberry-backup/testutils" |
| "github.com/apache/cloudberry-backup/toc" |
| "github.com/apache/cloudberry-go-libs/testhelper" |
| |
| . "github.com/onsi/ginkgo/v2" |
| ) |
| |
| var _ = Describe("backup/predata_relations tests", func() { |
| rowOne := backup.ColumnDefinition{Oid: 0, Num: 1, Name: "i", Type: "integer", StatTarget: -1} |
| rowTwo := backup.ColumnDefinition{Oid: 1, Num: 2, Name: "j", Type: "character varying(20)", StatTarget: -1} |
| |
| noMetadata := backup.ObjectMetadata{} |
| |
| var testTable backup.Table |
| BeforeEach(func() { |
| tocfile, backupfile = testutils.InitializeTestTOC(buffer, "predata") |
| extTableEmpty := backup.ExternalTableDefinition{Oid: 0, Type: -2, Protocol: -2, Location: sql.NullString{String: "", Valid: false}, ExecLocation: "ALL_SEGMENTS", FormatType: "t", FormatOpts: "", Command: "", RejectLimit: 0, RejectLimitType: "", ErrTableName: "", ErrTableSchema: "", Encoding: "UTF-8", Writable: false, URIs: nil} |
| testTable = backup.Table{ |
| Relation: backup.Relation{Schema: "public", Name: "tablename"}, |
| TableDefinition: backup.TableDefinition{DistPolicy: backup.DistPolicy{Policy: "DISTRIBUTED RANDOMLY"}, PartDef: "", PartTemplateDef: "", StorageOpts: "", ExtTableDef: extTableEmpty}, |
| } |
| }) |
| Describe("PrintCreateTableStatement", func() { |
| It("calls PrintRegularTableCreateStatement for a regular table", func() { |
| tableMetadata := backup.ObjectMetadata{Owner: "testrole"} |
| |
| testTable.IsExternal = false |
| backup.PrintCreateTableStatement(backupfile, tocfile, testTable, tableMetadata) |
| testutils.ExpectEntry(tocfile.PredataEntries, 0, "public", "", "tablename", toc.OBJ_TABLE) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| ) DISTRIBUTED RANDOMLY;`, "ALTER TABLE public.tablename OWNER TO testrole;") |
| }) |
| It("calls PrintExternalTableCreateStatement for an external table", func() { |
| testTable.IsExternal = true |
| backup.PrintCreateTableStatement(backupfile, tocfile, testTable, noMetadata) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE READABLE EXTERNAL WEB TABLE public.tablename ( |
| ) |
| FORMAT 'TEXT' |
| ENCODING 'UTF-8';`) |
| }) |
| }) |
| Describe("PrintRegularTableCreateStatement", func() { |
| rowOneEncoding := backup.ColumnDefinition{Oid: 0, Num: 1, Name: "i", Type: "integer", Encoding: "compresstype=none,blocksize=32768,compresslevel=0", StatTarget: -1} |
| rowTwoEncoding := backup.ColumnDefinition{Oid: 0, Num: 2, Name: "j", Type: "character varying(20)", Encoding: "compresstype=zlib,blocksize=65536,compresslevel=1", StatTarget: -1} |
| rowNotNull := backup.ColumnDefinition{Oid: 0, Num: 2, Name: "j", NotNull: true, Type: "character varying(20)", StatTarget: -1} |
| rowEncodingNotNull := backup.ColumnDefinition{Oid: 0, Num: 2, Name: "j", NotNull: true, Type: "character varying(20)", Encoding: "compresstype=zlib,blocksize=65536,compresslevel=1", StatTarget: -1} |
| rowOneDef := backup.ColumnDefinition{Oid: 0, Num: 1, Name: "i", HasDefault: true, Type: "integer", StatTarget: -1, DefaultVal: "42"} |
| rowTwoDef := backup.ColumnDefinition{Oid: 0, Num: 2, Name: "j", HasDefault: true, Type: "character varying(20)", StatTarget: -1, DefaultVal: "'bar'::text"} |
| rowTwoEncodingDef := backup.ColumnDefinition{Oid: 0, Num: 2, Name: "j", HasDefault: true, Type: "character varying(20)", Encoding: "compresstype=zlib,blocksize=65536,compresslevel=1", StatTarget: -1, DefaultVal: "'bar'::text"} |
| rowNotNullDef := backup.ColumnDefinition{Oid: 0, Num: 2, Name: "j", NotNull: true, HasDefault: true, Type: "character varying(20)", StatTarget: -1, DefaultVal: "'bar'::text"} |
| rowEncodingNotNullDef := backup.ColumnDefinition{Oid: 0, Num: 2, Name: "j", NotNull: true, HasDefault: true, Type: "character varying(20)", Encoding: "compresstype=zlib,blocksize=65536,compresslevel=1", StatTarget: -1, DefaultVal: "'bar'::text"} |
| rowStats := backup.ColumnDefinition{Oid: 0, Num: 1, Name: "i", Type: "integer", StatTarget: 3} |
| colOptions := backup.ColumnDefinition{Oid: 0, Num: 1, Name: "i", Type: "integer", Options: "n_distinct=1", StatTarget: -1} |
| colStorageType := backup.ColumnDefinition{Oid: 0, Num: 1, Name: "i", Type: "integer", StatTarget: -1, StorageType: "PLAIN"} |
| colWithCollation := backup.ColumnDefinition{Oid: 0, Num: 1, Name: "c", Type: "character (8)", StatTarget: -1, Collation: "public.some_coll"} |
| rowTwoGenerated := backup.ColumnDefinition{Oid: 0, Num: 2, Name: "j", HasDefault: true, Type: "integer", StatTarget: -1, DefaultVal: "(i * 2)", AttGenerated: "STORED"} |
| rowTwoGeneratedInherited := backup.ColumnDefinition{Oid: 0, Num: 2, Name: "j", HasDefault: true, Type: "integer", StatTarget: -1, DefaultVal: "(i * 2)", AttGenerated: "STORED", IsInherited: true} |
| |
| Context("No special table attributes", func() { |
| It("prints a CREATE TABLE OF type block with one attribute", func() { |
| col := []backup.ColumnDefinition{rowOne} |
| testTable.ColumnDefs = col |
| testTable.TableType = "public.some_type" |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename OF public.some_type ( |
| i WITH OPTIONS |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| |
| It("prints a CREATE TABLE block with one attribute", func() { |
| col := []backup.ColumnDefinition{rowOne} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE block with one line per attribute", func() { |
| col := []backup.ColumnDefinition{rowOne, rowTwo} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE block with no attributes", func() { |
| testTable.ColumnDefs = []backup.ColumnDefinition{} |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| }) |
| Context("One special table attribute", func() { |
| It("prints a CREATE TABLE block where one line has the given ENCODING and the other has the default ENCODING", func() { |
| col := []backup.ColumnDefinition{rowOneEncoding, rowTwoEncoding} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer ENCODING (compresstype=none,blocksize=32768,compresslevel=0), |
| j character varying(20) ENCODING (compresstype=zlib,blocksize=65536,compresslevel=1) |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE block where one line contains NOT NULL", func() { |
| col := []backup.ColumnDefinition{rowOne, rowNotNull} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) NOT NULL |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE OF type block where one line contains NOT NULL", func() { |
| col := []backup.ColumnDefinition{rowOne, rowNotNull} |
| testTable.TableType = "public.some_type" |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename OF public.some_type ( |
| i WITH OPTIONS, |
| j WITH OPTIONS NOT NULL |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE block where one line contains DEFAULT", func() { |
| col := []backup.ColumnDefinition{rowOneDef, rowTwo} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer DEFAULT 42, |
| j character varying(20) |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE block where both lines contain DEFAULT", func() { |
| col := []backup.ColumnDefinition{rowOneDef, rowTwoDef} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer DEFAULT 42, |
| j character varying(20) DEFAULT 'bar'::text |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE block where one line contains COLLATE", func() { |
| col := []backup.ColumnDefinition{rowOne, colWithCollation} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| c character (8) COLLATE public.some_coll |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE block followed by an ALTER COLUMN ... SET STATISTICS statement", func() { |
| col := []backup.ColumnDefinition{rowStats} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer |
| ) DISTRIBUTED RANDOMLY; |
| |
| ALTER TABLE ONLY public.tablename ALTER COLUMN i SET STATISTICS 3;`) |
| }) |
| It("prints a CREATE TABLE block followed by an ALTER COLUMN ... SET STORAGE statement", func() { |
| col := []backup.ColumnDefinition{colStorageType} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer |
| ) DISTRIBUTED RANDOMLY; |
| |
| ALTER TABLE ONLY public.tablename ALTER COLUMN i SET STORAGE PLAIN;`) |
| }) |
| It("prints a CREATE TABLE block followed by an ALTER COLUMN ... SET ... statement", func() { |
| col := []backup.ColumnDefinition{colOptions} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer |
| ) DISTRIBUTED RANDOMLY; |
| |
| ALTER TABLE ONLY public.tablename ALTER COLUMN i SET (n_distinct=1);`) |
| }) |
| It("prints a CREATE TABLE block with one line with regular attribute and a line with generated attribute", func() { |
| col := []backup.ColumnDefinition{rowOne, rowTwoGenerated} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j integer GENERATED ALWAYS AS (i * 2) STORED |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE block that omits the generated attribute if it inherits from another table", func() { |
| col := []backup.ColumnDefinition{rowOne, rowTwoGeneratedInherited} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j integer |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| }) |
| Context("Multiple special table attributes on one column", func() { |
| It("prints a CREATE TABLE block where one line contains both NOT NULL and ENCODING", func() { |
| col := []backup.ColumnDefinition{rowOneEncoding, rowEncodingNotNull} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer ENCODING (compresstype=none,blocksize=32768,compresslevel=0), |
| j character varying(20) NOT NULL ENCODING (compresstype=zlib,blocksize=65536,compresslevel=1) |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE block where one line contains both DEFAULT and NOT NULL", func() { |
| col := []backup.ColumnDefinition{rowOne, rowNotNullDef} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) DEFAULT 'bar'::text NOT NULL |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE block where one line contains both DEFAULT and ENCODING", func() { |
| col := []backup.ColumnDefinition{rowOneEncoding, rowTwoEncodingDef} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer ENCODING (compresstype=none,blocksize=32768,compresslevel=0), |
| j character varying(20) DEFAULT 'bar'::text ENCODING (compresstype=zlib,blocksize=65536,compresslevel=1) |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE block where one line contains all three of DEFAULT, NOT NULL, and ENCODING", func() { |
| col := []backup.ColumnDefinition{rowOneEncoding, rowEncodingNotNullDef} |
| testTable.ColumnDefs = col |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer ENCODING (compresstype=none,blocksize=32768,compresslevel=0), |
| j character varying(20) DEFAULT 'bar'::text NOT NULL ENCODING (compresstype=zlib,blocksize=65536,compresslevel=1) |
| ) DISTRIBUTED RANDOMLY;`) |
| }) |
| }) |
| Context("Table qualities (distribution keys and storage options)", func() { |
| distSingle := backup.DistPolicy{Policy: "DISTRIBUTED BY (i)"} |
| distComposite := backup.DistPolicy{Policy: "DISTRIBUTED BY (i, j)"} |
| |
| aoOpts := "appendonly=true" |
| coOpts := "appendonly=true, orientation=column" |
| heapFillOpts := "fillfactor=42" |
| coManyOpts := "appendonly=true, orientation=column, fillfactor=42, compresstype=zlib, blocksize=32768, compresslevel=1" |
| |
| col := []backup.ColumnDefinition{rowOne, rowTwo} |
| BeforeEach(func() { |
| testTable.ColumnDefs = col |
| }) |
| It("has a single-column distribution key", func() { |
| testTable.DistPolicy = distSingle |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) DISTRIBUTED BY (i);`) |
| }) |
| It("has a multiple-column composite distribution key", func() { |
| testTable.DistPolicy = distComposite |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) DISTRIBUTED BY (i, j);`) |
| }) |
| It("is an append-optimized table", func() { |
| testTable.StorageOpts = aoOpts |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (appendonly=true) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("is an append-optimized table with a single-column distribution key", func() { |
| testTable.DistPolicy = distSingle |
| testTable.StorageOpts = aoOpts |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (appendonly=true) DISTRIBUTED BY (i);`) |
| }) |
| It("is an append-optimized table with a two-column composite distribution key", func() { |
| testTable.DistPolicy = distComposite |
| testTable.StorageOpts = aoOpts |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (appendonly=true) DISTRIBUTED BY (i, j);`) |
| }) |
| It("is an append-optimized column-oriented table", func() { |
| testTable.StorageOpts = coOpts |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (appendonly=true, orientation=column) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("is an append-optimized column-oriented table with a single-column distribution key", func() { |
| testTable.DistPolicy = distSingle |
| testTable.StorageOpts = coOpts |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (appendonly=true, orientation=column) DISTRIBUTED BY (i);`) |
| }) |
| It("is an append-optimized column-oriented table with a two-column composite distribution key", func() { |
| testTable.DistPolicy = distComposite |
| testTable.StorageOpts = coOpts |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (appendonly=true, orientation=column) DISTRIBUTED BY (i, j);`) |
| }) |
| It("is a heap table with a fill factor", func() { |
| testTable.StorageOpts = heapFillOpts |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (fillfactor=42) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("is a heap table with a fill factor and a single-column distribution key", func() { |
| testTable.DistPolicy = distSingle |
| testTable.StorageOpts = heapFillOpts |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (fillfactor=42) DISTRIBUTED BY (i);`) |
| }) |
| It("is a heap table with a fill factor and a multiple-column composite distribution key", func() { |
| testTable.DistPolicy = distComposite |
| testTable.StorageOpts = heapFillOpts |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (fillfactor=42) DISTRIBUTED BY (i, j);`) |
| }) |
| It("is an append-optimized column-oriented table with complex storage options", func() { |
| testTable.StorageOpts = coManyOpts |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (appendonly=true, orientation=column, fillfactor=42, compresstype=zlib, blocksize=32768, compresslevel=1) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("is an append-optimized column-oriented table with complex storage options and a single-column distribution key", func() { |
| testTable.DistPolicy = distSingle |
| testTable.StorageOpts = coManyOpts |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (appendonly=true, orientation=column, fillfactor=42, compresstype=zlib, blocksize=32768, compresslevel=1) DISTRIBUTED BY (i);`) |
| }) |
| It("is an append-optimized column-oriented table with complex storage options and a two-column composite distribution key", func() { |
| testTable.DistPolicy = distComposite |
| testTable.StorageOpts = coManyOpts |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (appendonly=true, orientation=column, fillfactor=42, compresstype=zlib, blocksize=32768, compresslevel=1) DISTRIBUTED BY (i, j);`) |
| }) |
| It("is a GPDB 7+ root partition", func() { |
| testutils.SkipIfBefore7(connectionPool) |
| testTable.PartitionKeyDef = "RANGE (b)" |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) PARTITION BY RANGE (b) DISTRIBUTED RANDOMLY;`) |
| }) |
| }) |
| Context("Table partitioning", func() { |
| col := []backup.ColumnDefinition{rowOne, rowTwo} |
| BeforeEach(func() { |
| partDef := `PARTITION BY LIST(gender) |
| ( |
| PARTITION girls VALUES('F') WITH (tablename='rank_1_prt_girls', appendonly=false ), |
| PARTITION boys VALUES('M') WITH (tablename='rank_1_prt_boys', appendonly=false ), |
| DEFAULT PARTITION other WITH (tablename='rank_1_prt_other', appendonly=false ) |
| ) |
| ` |
| testTable.PartDef = partDef |
| testTable.ColumnDefs = col |
| }) |
| It("is a partition table with no table attributes", func() { |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) DISTRIBUTED RANDOMLY PARTITION BY LIST(gender) |
| ( |
| PARTITION girls VALUES('F') WITH (tablename='rank_1_prt_girls', appendonly=false ), |
| PARTITION boys VALUES('M') WITH (tablename='rank_1_prt_boys', appendonly=false ), |
| DEFAULT PARTITION other WITH (tablename='rank_1_prt_other', appendonly=false ) |
| );`) |
| }) |
| It("is a partition table with table attributes", func() { |
| testTable.StorageOpts = "appendonly=true, orientation=column" |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) WITH (appendonly=true, orientation=column) DISTRIBUTED RANDOMLY PARTITION BY LIST(gender) |
| ( |
| PARTITION girls VALUES('F') WITH (tablename='rank_1_prt_girls', appendonly=false ), |
| PARTITION boys VALUES('M') WITH (tablename='rank_1_prt_boys', appendonly=false ), |
| DEFAULT PARTITION other WITH (tablename='rank_1_prt_other', appendonly=false ) |
| );`) |
| }) |
| It("is a partition table with subpartitions and table attributes", func() { |
| partTemplateDef := `ALTER TABLE tablename |
| SET SUBPARTITION TEMPLATE |
| ( |
| SUBPARTITION usa VALUES('usa') WITH (tablename='tablename'), |
| SUBPARTITION asia VALUES('asia') WITH (tablename='tablename'), |
| SUBPARTITION europe VALUES('europe') WITH (tablename='tablename'), |
| DEFAULT SUBPARTITION other_regions WITH (tablename='tablename') |
| ) |
| ` |
| testTable.PartTemplateDef = partTemplateDef |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) DISTRIBUTED RANDOMLY PARTITION BY LIST(gender) |
| ( |
| PARTITION girls VALUES('F') WITH (tablename='rank_1_prt_girls', appendonly=false ), |
| PARTITION boys VALUES('M') WITH (tablename='rank_1_prt_boys', appendonly=false ), |
| DEFAULT PARTITION other WITH (tablename='rank_1_prt_other', appendonly=false ) |
| ); |
| ALTER TABLE tablename |
| SET SUBPARTITION TEMPLATE |
| ( |
| SUBPARTITION usa VALUES('usa') WITH (tablename='tablename'), |
| SUBPARTITION asia VALUES('asia') WITH (tablename='tablename'), |
| SUBPARTITION europe VALUES('europe') WITH (tablename='tablename'), |
| DEFAULT SUBPARTITION other_regions WITH (tablename='tablename') |
| );`) |
| }) |
| }) |
| Context("Tablespaces", func() { |
| It("prints a CREATE TABLE block with a TABLESPACE clause", func() { |
| testTable.TablespaceName = "test_tablespace" |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| ) TABLESPACE test_tablespace DISTRIBUTED RANDOMLY;`) |
| }) |
| }) |
| Context("Inheritance", func() { |
| It("prints a CREATE TABLE block with a single-inheritance INHERITS clause", func() { |
| col := []backup.ColumnDefinition{rowOne} |
| testTable.ColumnDefs = col |
| testTable.Inherits = []string{"public.parent"} |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer |
| ) INHERITS (public.parent) DISTRIBUTED RANDOMLY;`) |
| }) |
| It("prints a CREATE TABLE block with a multiple-inheritance INHERITS clause", func() { |
| col := []backup.ColumnDefinition{rowOne, rowTwo} |
| testTable.ColumnDefs = col |
| testTable.Inherits = []string{"public.parent_one", "public.parent_two"} |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) INHERITS (public.parent_one, public.parent_two) DISTRIBUTED RANDOMLY;`) |
| }) |
| }) |
| Context("Foreign Table", func() { |
| BeforeEach(func() { |
| testTable.DistPolicy = backup.DistPolicy{} |
| }) |
| It("prints a CREATE TABLE block without options", func() { |
| col := []backup.ColumnDefinition{rowOne, rowTwo} |
| testTable.ColumnDefs = col |
| foreignTable := backup.ForeignTableDefinition{Options: "", Server: "fs"} |
| testTable.ForeignDef = foreignTable |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE FOREIGN TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) SERVER fs ;`) |
| }) |
| It("prints a CREATE TABLE block with options", func() { |
| col := []backup.ColumnDefinition{rowOne, rowTwo} |
| testTable.ColumnDefs = col |
| foreignTable := backup.ForeignTableDefinition{Options: "delimiter=',' quote='\"'", Server: "fs"} |
| testTable.ForeignDef = foreignTable |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE FOREIGN TABLE public.tablename ( |
| i integer, |
| j character varying(20) |
| ) SERVER fs OPTIONS (delimiter=',' quote='"') ;`) |
| }) |
| It("prints a CREATE TABLE block with foreign data options on attributes", func() { |
| rowWithFdwOptions := backup.ColumnDefinition{Oid: 0, Num: 1, Name: "i", Type: "integer", StatTarget: -1, FdwOptions: "option1 'val1', option2 'val2'"} |
| col := []backup.ColumnDefinition{rowWithFdwOptions, rowTwo} |
| testTable.ColumnDefs = col |
| foreignTable := backup.ForeignTableDefinition{Server: "fs"} |
| testTable.ForeignDef = foreignTable |
| backup.PrintRegularTableCreateStatement(backupfile, tocfile, testTable) |
| testutils.AssertBufferContents(tocfile.PredataEntries, buffer, `CREATE FOREIGN TABLE public.tablename ( |
| i integer OPTIONS (option1 'val1', option2 'val2'), |
| j character varying(20) |
| ) SERVER fs ;`) |
| }) |
| }) |
| }) |
| Describe("PrintPostCreateTableStatements", func() { |
| rowCommentOne := backup.ColumnDefinition{Oid: 0, Num: 1, Name: "i", Type: "integer", StatTarget: -1, Comment: "This is a column comment."} |
| rowCommentTwo := backup.ColumnDefinition{Oid: 0, Num: 2, Name: "j", Type: "integer", StatTarget: -1, Comment: "This is another column comment."} |
| |
| It("does not print default replica identity statement", func() { |
| testTable.ReplicaIdentity = "d" |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, noMetadata, []uint32{0, 0}) |
| testhelper.NotExpectRegexp(buffer, `REPLICA IDENTITY`) |
| }) |
| It("does not print index replica identity statement", func() { |
| testTable.ReplicaIdentity = "i" |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, noMetadata, []uint32{0, 0}) |
| testhelper.NotExpectRegexp(buffer, `REPLICA IDENTITY`) |
| }) |
| It("does not print null replica identity statement", func() { |
| testTable.ReplicaIdentity = "" |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, noMetadata, []uint32{0, 0}) |
| testhelper.NotExpectRegexp(buffer, `REPLICA IDENTITY`) |
| }) |
| It("prints replica identity full", func() { |
| testTable.ReplicaIdentity = "f" |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, noMetadata, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, `ALTER TABLE public.tablename REPLICA IDENTITY FULL;`) |
| }) |
| It("prints replica identity nothing", func() { |
| testTable.ReplicaIdentity = "n" |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, noMetadata, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, `ALTER TABLE public.tablename REPLICA IDENTITY NOTHING;`) |
| }) |
| It("prints a block with a table comment", func() { |
| col := []backup.ColumnDefinition{rowOne} |
| testTable.ColumnDefs = col |
| tableMetadata := testutils.DefaultMetadata(toc.OBJ_TABLE, false, false, true, false) |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, tableMetadata, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, ` |
| |
| COMMENT ON TABLE public.tablename IS 'This is a table comment.';`) |
| }) |
| It("prints a block with a single column comment", func() { |
| col := []backup.ColumnDefinition{rowCommentOne} |
| testTable.ColumnDefs = col |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, noMetadata, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, ` |
| |
| COMMENT ON COLUMN public.tablename.i IS 'This is a column comment.';`) |
| }) |
| It("prints a block with a single column comment containing special characters", func() { |
| rowCommentSpecialCharacters := backup.ColumnDefinition{Oid: 0, Num: 1, Name: "i", Type: "integer", StatTarget: -1, Comment: `This is a ta'ble 1+=;,./\>,<@\\n^comment.`} |
| |
| col := []backup.ColumnDefinition{rowCommentSpecialCharacters} |
| testTable.ColumnDefs = col |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, noMetadata, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, ` |
| |
| COMMENT ON COLUMN public.tablename.i IS 'This is a ta''ble 1+=;,./\>,<@\\n^comment.';`) |
| }) |
| It("prints a block with multiple column comments", func() { |
| col := []backup.ColumnDefinition{rowCommentOne, rowCommentTwo} |
| testTable.ColumnDefs = col |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, noMetadata, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, ` |
| |
| COMMENT ON COLUMN public.tablename.i IS 'This is a column comment.'; |
| |
| |
| COMMENT ON COLUMN public.tablename.j IS 'This is another column comment.';`) |
| }) |
| It("prints an ALTER TABLE ... OWNER TO statement to set the table owner", func() { |
| col := []backup.ColumnDefinition{rowOne} |
| testTable.ColumnDefs = col |
| tableMetadata := testutils.DefaultMetadata(toc.OBJ_TABLE, false, true, false, false) |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, tableMetadata, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, ` |
| |
| ALTER TABLE public.tablename OWNER TO testrole;`) |
| }) |
| It("prints a SECURITY LABEL statement for the table", func() { |
| col := []backup.ColumnDefinition{rowOne} |
| testTable.ColumnDefs = col |
| tableMetadata := testutils.DefaultMetadata(toc.OBJ_TABLE, false, false, false, true) |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, tableMetadata, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, ` |
| |
| SECURITY LABEL FOR dummy ON TABLE public.tablename IS 'unclassified';`) |
| }) |
| It("does not print an ALTER TABLE... REPLICA IDENTITY for foreign tables", func() { |
| testTable.ForeignDef = backup.ForeignTableDefinition{Options: "", Server: "fs"} |
| testTable.ReplicaIdentity = "n" |
| tableMetadata := testutils.DefaultMetadata(toc.OBJ_FOREIGN_TABLE, true, true, true, true) |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, tableMetadata, []uint32{0, 0}) |
| testhelper.NotExpectRegexp(buffer, `REPLICA IDENTITY`) |
| }) |
| It("prints owner, comment, security label, and ACL statements for foreign table", func() { |
| col := []backup.ColumnDefinition{rowOne} |
| testTable.ColumnDefs = col |
| testTable.ForeignDef = backup.ForeignTableDefinition{Options: "", Server: "fs"} |
| tableMetadata := testutils.DefaultMetadata(toc.OBJ_FOREIGN_TABLE, true, true, true, true) |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, tableMetadata, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, ` |
| |
| COMMENT ON FOREIGN TABLE public.tablename IS 'This is a foreign table comment.'; |
| |
| |
| ALTER FOREIGN TABLE public.tablename OWNER TO testrole; |
| |
| |
| REVOKE ALL ON TABLE public.tablename FROM PUBLIC; |
| REVOKE ALL ON TABLE public.tablename FROM testrole; |
| GRANT ALL ON TABLE public.tablename TO testrole; |
| |
| |
| SECURITY LABEL FOR dummy ON FOREIGN TABLE public.tablename IS 'unclassified';`) |
| }) |
| It("prints both an ALTER TABLE ... OWNER TO statement and comments", func() { |
| col := []backup.ColumnDefinition{rowCommentOne, rowCommentTwo} |
| testTable.ColumnDefs = col |
| tableMetadata := backup.ObjectMetadata{Owner: "testrole", Comment: "This is a table comment."} |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, tableMetadata, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, ` |
| |
| COMMENT ON TABLE public.tablename IS 'This is a table comment.'; |
| |
| |
| ALTER TABLE public.tablename OWNER TO testrole; |
| |
| |
| COMMENT ON COLUMN public.tablename.i IS 'This is a column comment.'; |
| |
| |
| COMMENT ON COLUMN public.tablename.j IS 'This is another column comment.';`) |
| }) |
| It("prints a GRANT statement on a table column", func() { |
| privilegesColumnOne := backup.ColumnDefinition{Oid: 0, Num: 1, Name: "i", Type: "integer", StatTarget: -1, Privileges: sql.NullString{String: "testrole=r/testrole", Valid: true}} |
| privilegesColumnTwo := backup.ColumnDefinition{Oid: 1, Num: 2, Name: "j", Type: "character varying(20)", StatTarget: -1, Privileges: sql.NullString{String: "testrole2=arwx/testrole2", Valid: true}} |
| col := []backup.ColumnDefinition{privilegesColumnOne, privilegesColumnTwo} |
| testTable.ColumnDefs = col |
| tableMetadata := backup.ObjectMetadata{Owner: "testrole"} |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, tableMetadata, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, ` |
| |
| ALTER TABLE public.tablename OWNER TO testrole; |
| |
| |
| REVOKE ALL (i) ON TABLE public.tablename FROM PUBLIC; |
| REVOKE ALL (i) ON TABLE public.tablename FROM testrole; |
| GRANT SELECT (i) ON TABLE public.tablename TO testrole; |
| |
| |
| REVOKE ALL (j) ON TABLE public.tablename FROM PUBLIC; |
| REVOKE ALL (j) ON TABLE public.tablename FROM testrole; |
| GRANT ALL (j) ON TABLE public.tablename TO testrole2;`) |
| }) |
| It("prints a security group statement on a table column", func() { |
| privilegesColumnOne := backup.ColumnDefinition{Oid: 0, Num: 1, Name: "i", Type: "integer", StatTarget: -1, SecurityLabelProvider: "dummy", SecurityLabel: "unclassified"} |
| privilegesColumnTwo := backup.ColumnDefinition{Oid: 1, Num: 2, Name: "j", Type: "character varying(20)", StatTarget: -1, SecurityLabelProvider: "dummy", SecurityLabel: "unclassified"} |
| col := []backup.ColumnDefinition{privilegesColumnOne, privilegesColumnTwo} |
| testTable.ColumnDefs = col |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, backup.ObjectMetadata{}, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, ` |
| |
| SECURITY LABEL FOR dummy ON COLUMN public.tablename.i IS 'unclassified'; |
| |
| |
| SECURITY LABEL FOR dummy ON COLUMN public.tablename.j IS 'unclassified';`) |
| }) |
| It("prints altered schemas of child partitions different from the root partition", func() { |
| testTable.PartitionAlteredSchemas = []backup.AlteredPartitionRelation{ |
| {OldSchema: "schema1", NewSchema: "schema2", Name: "table1"}, |
| {OldSchema: "schema2", NewSchema: "schema1", Name: "table2"}, |
| } |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, backup.ObjectMetadata{}, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, ` |
| |
| ALTER TABLE schema1.table1 SET SCHEMA schema2; |
| |
| |
| ALTER TABLE schema2.table2 SET SCHEMA schema1;`) |
| }) |
| It("prints force row security", func() { |
| testutils.SkipIfBefore7(connectionPool) |
| testTable.ForceRowSecurity = true |
| backup.PrintPostCreateTableStatements(backupfile, tocfile, testTable, backup.ObjectMetadata{}, []uint32{0, 0}) |
| testhelper.ExpectRegexp(buffer, `ALTER TABLE ONLY public.tablename FORCE ROW LEVEL SECURITY;`) |
| }) |
| }) |
| }) |