| package backup |
| |
| /* |
| * This file contains structs and functions related to executing specific |
| * queries to gather metadata for the objects handled in predata_general.go. |
| */ |
| |
| import ( |
| "fmt" |
| |
| "github.com/apache/cloudberry-backup/toc" |
| "github.com/apache/cloudberry-backup/utils" |
| "github.com/apache/cloudberry-go-libs/dbconn" |
| "github.com/apache/cloudberry-go-libs/gplog" |
| ) |
| |
| type Operator struct { |
| Oid uint32 |
| Schema string |
| Name string |
| Procedure string |
| LeftArgType string |
| RightArgType string |
| CommutatorOp string |
| NegatorOp string |
| RestrictFunction string |
| JoinFunction string |
| CanHash bool |
| CanMerge bool |
| } |
| |
| func (o Operator) GetMetadataEntry() (string, toc.MetadataEntry) { |
| return "predata", |
| toc.MetadataEntry{ |
| Schema: o.Schema, |
| Name: o.Name, |
| ObjectType: toc.OBJ_OPERATOR, |
| ReferenceObject: "", |
| StartByte: 0, |
| EndByte: 0, |
| } |
| } |
| |
| func (o Operator) GetUniqueID() UniqueID { |
| return UniqueID{ClassID: PG_OPERATOR_OID, Oid: o.Oid} |
| } |
| |
| func (o Operator) FQN() string { |
| leftArg := "NONE" |
| rightArg := "NONE" |
| if o.LeftArgType != "-" { |
| leftArg = o.LeftArgType |
| } |
| if o.RightArgType != "-" { |
| rightArg = o.RightArgType |
| } |
| return fmt.Sprintf("%s.%s (%s, %s)", o.Schema, o.Name, leftArg, rightArg) |
| } |
| |
| func GetOperators(connectionPool *dbconn.DBConn) []Operator { |
| results := make([]Operator, 0) |
| query := fmt.Sprintf(` |
| SELECT o.oid AS oid, |
| quote_ident(n.nspname) AS schema, |
| oprname AS name, |
| oprcode::regproc AS procedure, |
| oprleft::regtype AS leftargtype, |
| oprright::regtype AS rightargtype, |
| oprcom::regoper AS commutatorop, |
| oprnegate::regoper AS negatorop, |
| oprrest AS restrictfunction, |
| oprjoin AS joinfunction, |
| oprcanmerge AS canmerge, |
| oprcanhash AS canhash |
| FROM pg_operator o |
| JOIN pg_namespace n on n.oid = o.oprnamespace |
| WHERE %s AND oprcode != 0 |
| AND %s`, SchemaFilterClause("n"), ExtensionFilterClause("o")) |
| |
| err := connectionPool.Select(&results, query) |
| gplog.FatalOnError(err) |
| return results |
| } |
| |
| type OperatorFamily struct { |
| Oid uint32 |
| Schema string |
| Name string |
| IndexMethod string |
| } |
| |
| func (opf OperatorFamily) GetMetadataEntry() (string, toc.MetadataEntry) { |
| return "predata", |
| toc.MetadataEntry{ |
| Schema: opf.Schema, |
| Name: opf.Name, |
| ObjectType: toc.OBJ_OPERATOR_FAMILY, |
| ReferenceObject: "", |
| StartByte: 0, |
| EndByte: 0, |
| } |
| } |
| |
| func (opf OperatorFamily) GetUniqueID() UniqueID { |
| return UniqueID{ClassID: PG_OPFAMILY_OID, Oid: opf.Oid} |
| } |
| |
| func (opf OperatorFamily) FQN() string { |
| return fmt.Sprintf("%s USING %s", utils.MakeFQN(opf.Schema, opf.Name), opf.IndexMethod) |
| } |
| |
| func GetOperatorFamilies(connectionPool *dbconn.DBConn) []OperatorFamily { |
| results := make([]OperatorFamily, 0) |
| query := fmt.Sprintf(` |
| SELECT o.oid AS oid, |
| quote_ident(n.nspname) AS schema, |
| quote_ident(opfname) AS name, |
| (SELECT quote_ident(amname) FROM pg_am WHERE oid = opfmethod) AS indexMethod |
| FROM pg_opfamily o |
| JOIN pg_namespace n on n.oid = o.opfnamespace |
| WHERE %s |
| AND %s`, |
| SchemaFilterClause("n"), ExtensionFilterClause("o")) |
| err := connectionPool.Select(&results, query) |
| gplog.FatalOnError(err) |
| return results |
| } |
| |
| type OperatorClass struct { |
| Oid uint32 |
| Schema string |
| Name string |
| FamilySchema string |
| FamilyName string |
| IndexMethod string |
| Type string |
| Default bool |
| StorageType string |
| Operators []OperatorClassOperator |
| Functions []OperatorClassFunction |
| } |
| |
| func (opc OperatorClass) GetMetadataEntry() (string, toc.MetadataEntry) { |
| return "predata", |
| toc.MetadataEntry{ |
| Schema: opc.Schema, |
| Name: opc.Name, |
| ObjectType: toc.OBJ_OPERATOR_CLASS, |
| ReferenceObject: "", |
| StartByte: 0, |
| EndByte: 0, |
| } |
| } |
| |
| func (opc OperatorClass) GetUniqueID() UniqueID { |
| return UniqueID{ClassID: PG_OPCLASS_OID, Oid: opc.Oid} |
| } |
| |
| func (opc OperatorClass) FQN() string { |
| return fmt.Sprintf("%s USING %s", utils.MakeFQN(opc.Schema, opc.Name), opc.IndexMethod) |
| } |
| |
| func GetOperatorClasses(connectionPool *dbconn.DBConn) []OperatorClass { |
| results := make([]OperatorClass, 0) |
| query := fmt.Sprintf(` |
| SELECT c.oid AS oid, |
| quote_ident(cls_ns.nspname) AS schema, |
| quote_ident(opcname) AS name, |
| quote_ident(fam_ns.nspname) AS familyschema, |
| quote_ident(opfname) AS familyname, |
| (SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS indexmethod, |
| opcintype::pg_catalog.regtype AS type, |
| opcdefault AS default, |
| /* |
| * GPDB7 (PG12) and CBDB (PG14) have different behaviors when casting |
| * opckeytype to a regtype when the STORAGE type is the same as the |
| * FOR TYPE and the opckeytype is not 0. |
| * |
| * In GPDB7, the expression opckeytype::regtype evaluates to "-" |
| * in this case. |
| * |
| * In CBDB, the same expression evaluates to the type name (e.g., "integer"). |
| * |
| * This CASE statement explicitly checks if the types are the same to |
| * normalize this behavior, ensuring the output is always "-" when the |
| * storage type is the same as the input type. |
| */ |
| CASE WHEN opcintype = opckeytype THEN '-' ELSE opckeytype::pg_catalog.regtype END AS storagetype |
| FROM pg_catalog.pg_opclass c |
| LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily |
| JOIN pg_catalog.pg_namespace cls_ns ON cls_ns.oid = opcnamespace |
| JOIN pg_catalog.pg_namespace fam_ns ON fam_ns.oid = opfnamespace |
| WHERE %s |
| AND %s`, |
| SchemaFilterClause("cls_ns"), ExtensionFilterClause("c")) |
| |
| err := connectionPool.Select(&results, query) |
| gplog.FatalOnError(err) |
| |
| operators := GetOperatorClassOperators(connectionPool) |
| for i := range results { |
| results[i].Operators = operators[results[i].Oid] |
| } |
| functions := GetOperatorClassFunctions(connectionPool) |
| for i := range results { |
| results[i].Functions = functions[results[i].Oid] |
| } |
| |
| return results |
| } |
| |
| type OperatorClassOperator struct { |
| ClassOid uint32 |
| StrategyNumber int |
| Operator string |
| Recheck bool |
| OrderByFamily string |
| } |
| |
| func GetOperatorClassOperators(connectionPool *dbconn.DBConn) map[uint32][]OperatorClassOperator { |
| results := make([]OperatorClassOperator, 0) |
| version5Query := ` |
| SELECT refobjid AS classoid, |
| amopstrategy AS strategynumber, |
| amopopr::pg_catalog.regoperator AS operator, |
| amopreqcheck AS recheck |
| FROM pg_catalog.pg_amop ao |
| JOIN pg_catalog.pg_depend d ON d.objid = ao.oid |
| WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass |
| AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass |
| ORDER BY amopstrategy` |
| |
| atLeast6Query := ` |
| SELECT refobjid AS classoid, |
| amopstrategy AS strategynumber, |
| amopopr::pg_catalog.regoperator AS operator, |
| coalesce(quote_ident(ns.nspname) || '.' || quote_ident(opf.opfname), '') AS orderbyfamily |
| FROM pg_catalog.pg_amop ao |
| JOIN pg_catalog.pg_depend d ON d.objid = ao.oid |
| LEFT JOIN pg_opfamily opf ON opf.oid = ao.amopsortfamily |
| LEFT JOIN pg_namespace ns ON ns.oid = opf.opfnamespace |
| WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass |
| AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass |
| ORDER BY amopstrategy` |
| |
| cloudberryQuery := ` |
| SELECT |
| opc.oid AS classoid, |
| amop.amopstrategy AS strategynumber, |
| amop.amopopr::pg_catalog.regoperator AS operator, |
| coalesce(quote_ident(sort_ns.nspname) || '.' || quote_ident(sort_opf.opfname), '') AS orderbyfamily |
| FROM |
| pg_catalog.pg_opclass AS opc |
| JOIN |
| pg_catalog.pg_amop AS amop ON opc.opcfamily = amop.amopfamily AND amop.amoplefttype = opc.opcintype |
| LEFT JOIN |
| pg_catalog.pg_opfamily AS sort_opf ON sort_opf.oid = amop.amopsortfamily |
| LEFT JOIN |
| pg_catalog.pg_namespace AS sort_ns ON sort_ns.oid = sort_opf.opfnamespace |
| ORDER BY |
| classoid, strategynumber` |
| |
| query := "" |
| if connectionPool.Version.IsCBDB() { |
| query = cloudberryQuery |
| } else if connectionPool.Version.IsGPDB() && connectionPool.Version.Is("5") { |
| query = version5Query |
| } else { |
| query = atLeast6Query |
| } |
| |
| err := connectionPool.Select(&results, query) |
| gplog.FatalOnError(err) |
| |
| operators := make(map[uint32][]OperatorClassOperator) |
| for _, result := range results { |
| operators[result.ClassOid] = append(operators[result.ClassOid], result) |
| } |
| return operators |
| } |
| |
| type OperatorClassFunction struct { |
| ClassOid uint32 |
| SupportNumber int |
| FunctionName string |
| LeftType string `db:"amproclefttype"` |
| RightType string `db:"amprocrighttype"` |
| } |
| |
| func GetOperatorClassFunctions(connectionPool *dbconn.DBConn) map[uint32][]OperatorClassFunction { |
| results := make([]OperatorClassFunction, 0) |
| query := ` |
| SELECT refobjid AS classoid, |
| amprocnum AS supportnumber, |
| amproclefttype::regtype, |
| amprocrighttype::regtype, |
| amproc::regprocedure::text AS functionname |
| FROM pg_catalog.pg_amproc ap |
| JOIN pg_catalog.pg_depend d ON d.objid = ap.oid |
| WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass |
| AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass |
| ORDER BY amprocnum` |
| |
| err := connectionPool.Select(&results, query) |
| gplog.FatalOnError(err) |
| |
| functions := make(map[uint32][]OperatorClassFunction) |
| for _, result := range results { |
| functions[result.ClassOid] = append(functions[result.ClassOid], result) |
| } |
| return functions |
| } |