github.com/cloudberrydb/gpbackup@v1.0.3-0.20240118031043-5410fd45eed6/integration/predata_externals_create_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"database/sql"
     5  	"os"
     6  
     7  	"github.com/cloudberrydb/gp-common-go-libs/structmatcher"
     8  	"github.com/cloudberrydb/gp-common-go-libs/testhelper"
     9  	"github.com/cloudberrydb/gpbackup/backup"
    10  	"github.com/cloudberrydb/gpbackup/testutils"
    11  
    12  	. "github.com/onsi/ginkgo/v2"
    13  	. "github.com/onsi/gomega"
    14  )
    15  
    16  var _ = Describe("backup integration create statement tests", func() {
    17  	BeforeEach(func() {
    18  		tocfile, backupfile = testutils.InitializeTestTOC(buffer, "predata")
    19  	})
    20  	Describe("PrintExternalTableCreateStatement", func() {
    21  		var (
    22  			extTable  backup.ExternalTableDefinition
    23  			testTable backup.Table
    24  		)
    25  		BeforeEach(func() {
    26  			extTable = backup.ExternalTableDefinition{Oid: 0, Type: 0, Protocol: backup.FILE, Location: sql.NullString{String: "file://tmp/ext_table_file", Valid: true},
    27  				ExecLocation: "ALL_SEGMENTS", FormatType: "t", FormatOpts: "delimiter '	' null '\\N' escape '\\'",
    28  				Command: "", RejectLimit: 0, RejectLimitType: "", ErrTableName: "", ErrTableSchema: "", Encoding: "UTF8",
    29  				Writable: false, URIs: []string{"file://tmp/ext_table_file"}}
    30  			testTable = backup.Table{
    31  				Relation:        backup.Relation{Schema: "public", Name: "testtable"},
    32  				TableDefinition: backup.TableDefinition{IsExternal: true},
    33  			}
    34  			_, _ = os.Create("/tmp/ext_table_file")
    35  		})
    36  		AfterEach(func() {
    37  			_ = os.Remove("/tmp/ext_table_file")
    38  			testhelper.AssertQueryRuns(connectionPool, "DROP EXTERNAL TABLE public.testtable")
    39  			testhelper.AssertQueryRuns(connectionPool, "DROP TABLE IF EXISTS public.err_table")
    40  			testhelper.AssertQueryRuns(connectionPool, `DROP TABLE IF EXISTS public."err_table%percent"`)
    41  		})
    42  		It("creates a READABLE EXTERNAL table", func() {
    43  			extTable.Type = backup.READABLE
    44  			extTable.Writable = false
    45  			extTable.LogErrors = true
    46  			extTable.RejectLimit = 2
    47  			extTable.RejectLimitType = "r"
    48  			testTable.ExtTableDef = extTable
    49  
    50  			backup.PrintExternalTableCreateStatement(backupfile, tocfile, testTable)
    51  
    52  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
    53  
    54  			oid := testutils.OidFromObjectName(connectionPool, "public", "testtable", backup.TYPE_RELATION)
    55  			resultTableDefs := backup.GetExternalTableDefinitions(connectionPool)
    56  			resultTableDef := resultTableDefs[oid]
    57  			resultTableDef.Type, resultTableDef.Protocol = backup.DetermineExternalTableCharacteristics(resultTableDef)
    58  
    59  			structmatcher.ExpectStructsToMatchExcluding(&extTable, &resultTableDef, "Oid")
    60  		})
    61  		It("creates a READABLE WEB EXTERNAL table with an EXECUTE statement containing special characters", func() {
    62  			extTable.Type = backup.READABLE_WEB
    63  			extTable.Writable = false
    64  			extTable.Location = sql.NullString{String: "", Valid: true}
    65  			extTable.Protocol = backup.HTTP
    66  			extTable.URIs = nil
    67  			extTable.Command = `bash % someone's \.custom_script.sh`
    68  			testTable.ExtTableDef = extTable
    69  			if true {
    70  				// The query for GPDB 7+ will have a NULL value instead of ""
    71  				extTable.Location.Valid = false
    72  			}
    73  
    74  			backup.PrintExternalTableCreateStatement(backupfile, tocfile, testTable)
    75  
    76  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
    77  
    78  			oid := testutils.OidFromObjectName(connectionPool, "public", "testtable", backup.TYPE_RELATION)
    79  			resultTableDefs := backup.GetExternalTableDefinitions(connectionPool)
    80  			resultTableDef := resultTableDefs[oid]
    81  			resultTableDef.Type, resultTableDef.Protocol = backup.DetermineExternalTableCharacteristics(resultTableDef)
    82  
    83  			structmatcher.ExpectStructsToMatchExcluding(&extTable, &resultTableDef, "Oid")
    84  		})
    85  		It("creates a READABLE EXTERNAL table with CSV FORMAT options", func() {
    86  			extTable.Type = backup.READABLE
    87  			extTable.Writable = false
    88  			extTable.FormatType = "c"
    89  			extTable.FormatOpts = `delimiter '|' null '' escape ''' quote ''' force not null i`
    90  			testTable.ExtTableDef = extTable
    91  
    92  			backup.PrintExternalTableCreateStatement(backupfile, tocfile, testTable)
    93  
    94  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
    95  
    96  			oid := testutils.OidFromObjectName(connectionPool, "public", "testtable", backup.TYPE_RELATION)
    97  			resultTableDefs := backup.GetExternalTableDefinitions(connectionPool)
    98  			resultTableDef := resultTableDefs[oid]
    99  			resultTableDef.Type, resultTableDef.Protocol = backup.DetermineExternalTableCharacteristics(resultTableDef)
   100  
   101  			structmatcher.ExpectStructsToMatchExcluding(&extTable, &resultTableDef, "Oid")
   102  		})
   103  		It("creates a READABLE EXTERNAL table with CUSTOM formatter", func() {
   104  			extTable.Type = backup.READABLE
   105  			extTable.Writable = false
   106  			extTable.FormatType = "b"
   107  			extTable.FormatOpts = "formatter 'fixedwidth_out' i '20' "
   108  			if true {
   109  				extTable.FormatOpts = "formatter 'fixedwidth_out'i '20'"
   110  			}
   111  			testTable.ExtTableDef = extTable
   112  
   113  			backup.PrintExternalTableCreateStatement(backupfile, tocfile, testTable)
   114  
   115  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   116  
   117  			oid := testutils.OidFromObjectName(connectionPool, "public", "testtable", backup.TYPE_RELATION)
   118  			resultTableDefs := backup.GetExternalTableDefinitions(connectionPool)
   119  			resultTableDef := resultTableDefs[oid]
   120  			resultTableDef.Type, resultTableDef.Protocol = backup.DetermineExternalTableCharacteristics(resultTableDef)
   121  
   122  			structmatcher.ExpectStructsToMatchExcluding(&extTable, &resultTableDef, "Oid")
   123  		})
   124  		It("creates a READABLE EXTERNAL table with LOG ERRORS INTO", func() {
   125  			testutils.SkipIfNot4(connectionPool)
   126  			extTable.Type = backup.READABLE
   127  			extTable.Writable = false
   128  			extTable.ErrTableName = `"err_table%percent"`
   129  			extTable.ErrTableSchema = "public"
   130  			extTable.RejectLimit = 2
   131  			extTable.RejectLimitType = "r"
   132  			testTable.ExtTableDef = extTable
   133  
   134  			backup.PrintExternalTableCreateStatement(backupfile, tocfile, testTable)
   135  
   136  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   137  
   138  			oid := testutils.OidFromObjectName(connectionPool, "public", "testtable", backup.TYPE_RELATION)
   139  			resultTableDefs := backup.GetExternalTableDefinitions(connectionPool)
   140  			resultTableDef := resultTableDefs[oid]
   141  			resultTableDef.Type, resultTableDef.Protocol = backup.DetermineExternalTableCharacteristics(resultTableDef)
   142  
   143  			structmatcher.ExpectStructsToMatchExcluding(&extTable, &resultTableDef, "Oid")
   144  		})
   145  		It("creates a READABLE EXTERNAL table with FORMAT delimiter", func() {
   146  			extTable.Type = backup.READABLE
   147  			extTable.Writable = false
   148  			extTable.FormatOpts = "delimiter '%' null '' escape 'OFF'"
   149  			extTable.FormatType = "t"
   150  			testTable.ExtTableDef = extTable
   151  
   152  			backup.PrintExternalTableCreateStatement(backupfile, tocfile, testTable)
   153  
   154  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   155  
   156  			oid := testutils.OidFromObjectName(connectionPool, "public", "testtable", backup.TYPE_RELATION)
   157  			resultTableDefs := backup.GetExternalTableDefinitions(connectionPool)
   158  			resultTableDef := resultTableDefs[oid]
   159  			resultTableDef.Type, resultTableDef.Protocol = backup.DetermineExternalTableCharacteristics(resultTableDef)
   160  
   161  			structmatcher.ExpectStructsToMatchExcluding(&extTable, &resultTableDef, "Oid")
   162  		})
   163  		It("creates a WRITABLE EXTERNAL table", func() {
   164  			extTable.Type = backup.WRITABLE
   165  			extTable.Writable = true
   166  			extTable.Location = sql.NullString{String: "gpfdist://outputhost:8081/data1.out", Valid: true}
   167  			extTable.URIs = []string{"gpfdist://outputhost:8081/data1.out"}
   168  			extTable.Protocol = backup.GPFDIST
   169  			testTable.ExtTableDef = extTable
   170  
   171  			backup.PrintExternalTableCreateStatement(backupfile, tocfile, testTable)
   172  
   173  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   174  
   175  			oid := testutils.OidFromObjectName(connectionPool, "public", "testtable", backup.TYPE_RELATION)
   176  			resultTableDefs := backup.GetExternalTableDefinitions(connectionPool)
   177  			resultTableDef := resultTableDefs[oid]
   178  			resultTableDef.Type, resultTableDef.Protocol = backup.DetermineExternalTableCharacteristics(resultTableDef)
   179  
   180  			structmatcher.ExpectStructsToMatchExcluding(&extTable, &resultTableDef, "Oid")
   181  		})
   182  	})
   183  	Describe("PrintCreateExternalProtocolStatement", func() {
   184  		protocolReadOnly := backup.ExternalProtocol{Oid: 1, Name: "s3_read", Owner: "testrole", Trusted: true, ReadFunction: 2, WriteFunction: 0, Validator: 0}
   185  		protocolWriteOnly := backup.ExternalProtocol{Oid: 1, Name: "s3_write", Owner: "testrole", Trusted: false, ReadFunction: 0, WriteFunction: 1, Validator: 0}
   186  		protocolReadWrite := backup.ExternalProtocol{Oid: 1, Name: "s3_read_write", Owner: "testrole", Trusted: false, ReadFunction: 2, WriteFunction: 1, Validator: 0}
   187  		emptyMetadata := backup.ObjectMetadata{}
   188  		funcInfoMap := map[uint32]backup.FunctionInfo{
   189  			1: {QualifiedName: "public.write_to_s3", Arguments: sql.NullString{String: "", Valid: true}, IsInternal: false},
   190  			2: {QualifiedName: "public.read_from_s3", Arguments: sql.NullString{String: "", Valid: true}, IsInternal: false},
   191  		}
   192  
   193  		It("creates a trusted protocol with a read function, privileges, and an owner", func() {
   194  			protoMetadata := backup.ObjectMetadata{Privileges: []backup.ACL{{Grantee: "testrole", Select: true, Insert: true}}, Owner: "testrole"}
   195  
   196  			backup.PrintCreateExternalProtocolStatement(backupfile, tocfile, protocolReadOnly, funcInfoMap, protoMetadata)
   197  
   198  			testhelper.AssertQueryRuns(connectionPool, "CREATE OR REPLACE FUNCTION public.read_from_s3() RETURNS integer AS '$libdir/gps3ext.so', 's3_import' LANGUAGE C STABLE;")
   199  			defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.read_from_s3()")
   200  
   201  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   202  			defer testhelper.AssertQueryRuns(connectionPool, "DROP PROTOCOL s3_read")
   203  
   204  			resultExternalProtocols := backup.GetExternalProtocols(connectionPool)
   205  
   206  			Expect(resultExternalProtocols).To(HaveLen(1))
   207  			structmatcher.ExpectStructsToMatchExcluding(&protocolReadOnly, &resultExternalProtocols[0], "Oid", "ReadFunction", "FuncMap")
   208  		})
   209  		It("creates a protocol with a write function", func() {
   210  			backup.PrintCreateExternalProtocolStatement(backupfile, tocfile, protocolWriteOnly, funcInfoMap, emptyMetadata)
   211  
   212  			testhelper.AssertQueryRuns(connectionPool, "CREATE OR REPLACE FUNCTION public.write_to_s3() RETURNS integer AS '$libdir/gps3ext.so', 's3_export' LANGUAGE C STABLE;")
   213  			defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.write_to_s3()")
   214  
   215  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   216  			defer testhelper.AssertQueryRuns(connectionPool, "DROP PROTOCOL s3_write")
   217  
   218  			resultExternalProtocols := backup.GetExternalProtocols(connectionPool)
   219  
   220  			Expect(resultExternalProtocols).To(HaveLen(1))
   221  			structmatcher.ExpectStructsToMatchExcluding(&protocolWriteOnly, &resultExternalProtocols[0], "Oid", "WriteFunction", "FuncMap")
   222  		})
   223  		It("creates a protocol with a read and write function", func() {
   224  			backup.PrintCreateExternalProtocolStatement(backupfile, tocfile, protocolReadWrite, funcInfoMap, emptyMetadata)
   225  
   226  			testhelper.AssertQueryRuns(connectionPool, "CREATE OR REPLACE FUNCTION public.read_from_s3() RETURNS integer AS '$libdir/gps3ext.so', 's3_import' LANGUAGE C STABLE;")
   227  			defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.read_from_s3()")
   228  
   229  			testhelper.AssertQueryRuns(connectionPool, "CREATE OR REPLACE FUNCTION public.write_to_s3() RETURNS integer AS '$libdir/gps3ext.so', 's3_export' LANGUAGE C STABLE;")
   230  			defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.write_to_s3()")
   231  
   232  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   233  			defer testhelper.AssertQueryRuns(connectionPool, "DROP PROTOCOL s3_read_write")
   234  
   235  			resultExternalProtocols := backup.GetExternalProtocols(connectionPool)
   236  
   237  			Expect(resultExternalProtocols).To(HaveLen(1))
   238  			structmatcher.ExpectStructsToMatchExcluding(&protocolReadWrite, &resultExternalProtocols[0], "Oid", "ReadFunction", "WriteFunction", "FuncMap")
   239  		})
   240  	})
   241  	Describe("PrintExchangeExternalPartitionStatements", func() {
   242  		tables := []backup.Table{
   243  			{Relation: backup.Relation{Oid: 1, Schema: "public", Name: "part_tbl_ext_part_"}},
   244  			{Relation: backup.Relation{Oid: 2, Schema: "public", Name: "part_tbl"}},
   245  		}
   246  		emptyPartInfoMap := make(map[uint32]backup.PartitionInfo)
   247  		BeforeEach(func() {
   248  			// For GPDB 7+, external partitions will have their own ATTACH PARTITION DDL commands.
   249  			if true {
   250  				Skip("Test is not applicable to GPDB 7+")
   251  			}
   252  		})
   253  		AfterEach(func() {
   254  			testhelper.AssertQueryRuns(connectionPool, "DROP TABLE public.part_tbl")
   255  		})
   256  		It("writes an alter statement for a named list partition", func() {
   257  			externalPartition := backup.PartitionInfo{
   258  				PartitionRuleOid:       1,
   259  				PartitionParentRuleOid: 0,
   260  				ParentRelationOid:      2,
   261  				ParentSchema:           "public",
   262  				ParentRelationName:     "part_tbl",
   263  				RelationOid:            1,
   264  				PartitionName:          "girls",
   265  				PartitionRank:          0,
   266  				IsExternal:             true,
   267  			}
   268  			testhelper.AssertQueryRuns(connectionPool, `
   269  CREATE TABLE public.part_tbl (id int, gender char(1))
   270  DISTRIBUTED BY (id)
   271  PARTITION BY LIST (gender)
   272  ( PARTITION girls VALUES ('F'),
   273    PARTITION boys VALUES ('M'),
   274    DEFAULT PARTITION other );`)
   275  			testhelper.AssertQueryRuns(connectionPool, `
   276  CREATE EXTERNAL WEB TABLE public.part_tbl_ext_part_ (like public.part_tbl_1_prt_girls)
   277  EXECUTE 'echo -e "2\n1"' on host
   278  FORMAT 'csv';`)
   279  			externalPartitions := []backup.PartitionInfo{externalPartition}
   280  
   281  			backup.PrintExchangeExternalPartitionStatements(backupfile, tocfile, externalPartitions, emptyPartInfoMap, tables)
   282  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   283  
   284  			resultExtPartitions, resultPartInfoMap := backup.GetExternalPartitionInfo(connectionPool)
   285  			Expect(resultExtPartitions).To(HaveLen(1))
   286  			Expect(resultPartInfoMap).To(HaveLen(3))
   287  			structmatcher.ExpectStructsToMatchExcluding(&externalPartition, &resultExtPartitions[0], "PartitionRuleOid", "RelationOid", "ParentRelationOid")
   288  		})
   289  		It("writes an alter statement for an unnamed range partition", func() {
   290  			externalPartition := backup.PartitionInfo{
   291  				PartitionRuleOid:       1,
   292  				PartitionParentRuleOid: 0,
   293  				ParentRelationOid:      2,
   294  				ParentSchema:           "public",
   295  				ParentRelationName:     "part_tbl",
   296  				RelationOid:            1,
   297  				PartitionName:          "",
   298  				PartitionRank:          1,
   299  				IsExternal:             true,
   300  			}
   301  			testhelper.AssertQueryRuns(connectionPool, `
   302  CREATE TABLE public.part_tbl (a int)
   303  DISTRIBUTED BY (a)
   304  PARTITION BY RANGE (a)
   305  (start(1) end(3) every(1));`)
   306  			testhelper.AssertQueryRuns(connectionPool, `
   307  CREATE EXTERNAL WEB TABLE public.part_tbl_ext_part_ (like public.part_tbl_1_prt_1)
   308  EXECUTE 'echo -e "2\n1"' on host
   309  FORMAT 'csv';`)
   310  			externalPartitions := []backup.PartitionInfo{externalPartition}
   311  
   312  			backup.PrintExchangeExternalPartitionStatements(backupfile, tocfile, externalPartitions, emptyPartInfoMap, tables)
   313  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   314  
   315  			resultExtPartitions, resultPartInfoMap := backup.GetExternalPartitionInfo(connectionPool)
   316  			Expect(resultExtPartitions).To(HaveLen(1))
   317  			Expect(resultPartInfoMap).To(HaveLen(2))
   318  			structmatcher.ExpectStructsToMatchExcluding(&externalPartition, &resultExtPartitions[0], "PartitionRuleOid", "RelationOid", "ParentRelationOid")
   319  		})
   320  		It("writes an alter statement for a two level partition", func() {
   321  			testutils.SkipIfBefore5(connectionPool)
   322  			externalPartition := backup.PartitionInfo{
   323  				PartitionRuleOid:       10,
   324  				PartitionParentRuleOid: 11,
   325  				ParentRelationOid:      2,
   326  				ParentSchema:           "public",
   327  				ParentRelationName:     "part_tbl",
   328  				RelationOid:            1,
   329  				PartitionName:          "apj",
   330  				PartitionRank:          0,
   331  				IsExternal:             true,
   332  			}
   333  			externalPartitionParent := backup.PartitionInfo{
   334  				PartitionRuleOid:       11,
   335  				PartitionParentRuleOid: 0,
   336  				ParentRelationOid:      2,
   337  				ParentSchema:           "public",
   338  				ParentRelationName:     "part_tbl",
   339  				RelationOid:            0,
   340  				PartitionName:          "Dec16",
   341  				PartitionRank:          0,
   342  				IsExternal:             false,
   343  			}
   344  			testhelper.AssertQueryRuns(connectionPool, `
   345  CREATE TABLE public.part_tbl (a int,b date,c text,d int)
   346  DISTRIBUTED BY (a)
   347  PARTITION BY RANGE (b)
   348  SUBPARTITION BY LIST (c)
   349  SUBPARTITION TEMPLATE
   350  (SUBPARTITION usa values ('usa'),
   351  SUBPARTITION apj values ('apj'),
   352  SUBPARTITION eur values ('eur'))
   353  ( PARTITION Sep16 START (date '2016-09-01') INCLUSIVE ,
   354    PARTITION Oct16 START (date '2016-10-01') INCLUSIVE ,
   355    PARTITION Nov16 START (date '2016-11-01') INCLUSIVE ,
   356    PARTITION Dec16 START (date '2016-12-01') INCLUSIVE
   357                    END (date '2017-01-01') EXCLUSIVE);
   358  `)
   359  
   360  			testhelper.AssertQueryRuns(connectionPool, `CREATE EXTERNAL TABLE public.part_tbl_ext_part_ (a int,b date,c text,d int) LOCATION ('gpfdist://127.0.0.1/apj') FORMAT 'text';`)
   361  			partInfoMap := map[uint32]backup.PartitionInfo{externalPartitionParent.PartitionRuleOid: externalPartitionParent}
   362  			externalPartitions := []backup.PartitionInfo{externalPartition}
   363  
   364  			backup.PrintExchangeExternalPartitionStatements(backupfile, tocfile, externalPartitions, partInfoMap, tables)
   365  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   366  
   367  			resultExtPartitions, _ := backup.GetExternalPartitionInfo(connectionPool)
   368  			externalPartition.RelationOid = testutils.OidFromObjectName(connectionPool, "public", "part_tbl_1_prt_dec16_2_prt_apj", backup.TYPE_RELATION)
   369  			Expect(resultExtPartitions).To(HaveLen(1))
   370  			structmatcher.ExpectStructsToMatchExcluding(&externalPartition, &resultExtPartitions[0], "PartitionRuleOid", "PartitionParentRuleOid", "ParentRelationOid")
   371  		})
   372  		It("writes an alter statement for a three level partition", func() {
   373  			testutils.SkipIfBefore5(connectionPool)
   374  			externalPartition := backup.PartitionInfo{
   375  				PartitionRuleOid:       10,
   376  				PartitionParentRuleOid: 11,
   377  				ParentRelationOid:      2,
   378  				ParentSchema:           "public",
   379  				ParentRelationName:     "part_tbl",
   380  				RelationOid:            1,
   381  				PartitionName:          "europe",
   382  				PartitionRank:          0,
   383  				IsExternal:             true,
   384  			}
   385  			externalPartitionParent1 := backup.PartitionInfo{
   386  				PartitionRuleOid:       11,
   387  				PartitionParentRuleOid: 12,
   388  				ParentRelationOid:      2,
   389  				ParentSchema:           "public",
   390  				ParentRelationName:     "part_tbl",
   391  				RelationOid:            0,
   392  				PartitionName:          "",
   393  				PartitionRank:          1,
   394  				IsExternal:             false,
   395  			}
   396  			externalPartitionParent2 := backup.PartitionInfo{
   397  				PartitionRuleOid:       12,
   398  				PartitionParentRuleOid: 0,
   399  				ParentRelationOid:      2,
   400  				ParentSchema:           "public",
   401  				ParentRelationName:     "part_tbl",
   402  				RelationOid:            0,
   403  				PartitionName:          "",
   404  				PartitionRank:          3,
   405  				IsExternal:             false,
   406  			}
   407  			testhelper.AssertQueryRuns(connectionPool, `
   408  CREATE TABLE public.part_tbl (id int, year int, month int, day int, region text)
   409  DISTRIBUTED BY (id)
   410  PARTITION BY RANGE (year)
   411      SUBPARTITION BY RANGE (month)
   412         SUBPARTITION TEMPLATE (
   413          START (1) END (4) EVERY (1) )
   414             SUBPARTITION BY LIST (region)
   415               SUBPARTITION TEMPLATE (
   416                 SUBPARTITION usa VALUES ('usa'),
   417                 SUBPARTITION europe VALUES ('europe'),
   418                 SUBPARTITION asia VALUES ('asia')
   419  		)
   420  ( START (2002) END (2005) EVERY (1));
   421  `)
   422  
   423  			testhelper.AssertQueryRuns(connectionPool, `CREATE EXTERNAL TABLE public.part_tbl_ext_part_ (like public.part_tbl_1_prt_3_2_prt_1_3_prt_europe) LOCATION ('gpfdist://127.0.0.1/apj') FORMAT 'text';`)
   424  			partInfoMap := map[uint32]backup.PartitionInfo{externalPartitionParent1.PartitionRuleOid: externalPartitionParent1, externalPartitionParent2.PartitionRuleOid: externalPartitionParent2}
   425  			externalPartitions := []backup.PartitionInfo{externalPartition}
   426  
   427  			backup.PrintExchangeExternalPartitionStatements(backupfile, tocfile, externalPartitions, partInfoMap, tables)
   428  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   429  
   430  			resultExtPartitions, _ := backup.GetExternalPartitionInfo(connectionPool)
   431  			externalPartition.RelationOid = testutils.OidFromObjectName(connectionPool, "public", "part_tbl_1_prt_3_2_prt_1_3_prt_europe", backup.TYPE_RELATION)
   432  			Expect(resultExtPartitions).To(HaveLen(1))
   433  			structmatcher.ExpectStructsToMatchExcluding(&externalPartition, &resultExtPartitions[0], "PartitionRuleOid", "PartitionParentRuleOid", "ParentRelationOid")
   434  		})
   435  	})
   436  })