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 })