blob: dbff267cfd3c236f9bd7935c6479759db7c7a313 [file]
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;`)
})
})
})