blob: 9f6324ef99412f7525e69588f5647ddea64e579c [file]
package backup
/*
* This file contains structs and functions related to backing up function
* metadata, and metadata closely related to functions such as aggregates
* and casts, that needs to be restored before data is restored.
*/
import (
"fmt"
"github.com/apache/cloudberry-backup/toc"
"github.com/apache/cloudberry-backup/utils"
"github.com/apache/cloudberry-go-libs/gplog"
)
func PrintCreateFunctionStatement(metadataFile *utils.FileWithByteCount, objToc *toc.TOC, funcDef Function, funcMetadata ObjectMetadata) {
start := metadataFile.ByteCount
funcFQN := utils.MakeFQN(funcDef.Schema, funcDef.Name)
if ((connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("7")) || connectionPool.Version.IsCBDB()) && funcDef.Kind == "p" {
metadataFile.MustPrintf("\n\nCREATE PROCEDURE %s(%s) AS", funcFQN, funcDef.Arguments.String)
} else {
metadataFile.MustPrintf("\n\nCREATE FUNCTION %s(%s) RETURNS %s AS", funcFQN, funcDef.Arguments.String, funcDef.ResultType.String)
}
PrintFunctionBodyOrPath(metadataFile, funcDef)
metadataFile.MustPrintf("LANGUAGE %s", funcDef.Language)
PrintFunctionModifiers(metadataFile, funcDef)
metadataFile.MustPrintln(";")
section, entry := funcDef.GetMetadataEntry()
tier := globalTierMap[funcDef.GetUniqueID()]
objToc.AddMetadataEntry(section, entry, start, metadataFile.ByteCount, tier)
PrintObjectMetadata(metadataFile, objToc, funcMetadata, funcDef, "", tier)
}
/*
* This function either prints a path to an executable function (for C and
* internal functions) or a function definition (for functions in other languages).
*/
func PrintFunctionBodyOrPath(metadataFile *utils.FileWithByteCount, funcDef Function) {
/*
* pg_proc.probin uses either NULL (in this case an empty string) or "-"
* to signify an unused path, for historical reasons. See dumpFunc in
* pg_dump.c for details.
*/
if funcDef.BinaryPath != "" && funcDef.BinaryPath != "-" {
metadataFile.MustPrintf("\n'%s', '%s'\n", funcDef.BinaryPath, funcDef.FunctionBody)
} else {
metadataFile.MustPrintf("\n%s\n", utils.DollarQuoteString(funcDef.FunctionBody))
}
}
func PrintFunctionModifiers(metadataFile *utils.FileWithByteCount, funcDef Function) {
// DataAccess removed from the catalog in GP7
if connectionPool.Version.IsGPDB() && connectionPool.Version.Before("7") {
switch funcDef.DataAccess {
case "c":
metadataFile.MustPrintf(" CONTAINS SQL")
case "m":
metadataFile.MustPrintf(" MODIFIES SQL DATA")
case "n":
metadataFile.MustPrintf(" NO SQL")
case "r":
metadataFile.MustPrintf(" READS SQL DATA")
}
}
switch funcDef.Volatility {
case "i":
metadataFile.MustPrintf(" IMMUTABLE")
case "s":
metadataFile.MustPrintf(" STABLE")
case "v": // Default case, don't print anything else
}
switch funcDef.ExecLocation {
case "m":
metadataFile.MustPrintf(" EXECUTE ON MASTER")
case "c":
metadataFile.MustPrintf(" EXECUTE ON COORDINATOR")
case "s":
metadataFile.MustPrintf(" EXECUTE ON ALL SEGMENTS")
case "i":
metadataFile.MustPrintf(" EXECUTE ON INITPLAN")
case "a": // Default case, don't print anything else
}
if funcDef.IsWindow || funcDef.Kind == "w" {
metadataFile.MustPrintf(" WINDOW")
}
if funcDef.IsStrict {
metadataFile.MustPrintf(" STRICT")
}
if funcDef.IsLeakProof {
metadataFile.MustPrintf(" LEAKPROOF")
}
if funcDef.IsSecurityDefiner {
metadataFile.MustPrintf(" SECURITY DEFINER")
}
if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("7")) || connectionPool.Version.IsCBDB() {
if funcDef.TransformTypes != "" {
metadataFile.MustPrintf("\nTRANSFORM %s\n", funcDef.TransformTypes)
}
if funcDef.PlannerSupport != "-" {
metadataFile.MustPrintf("\nSUPPORT %s", funcDef.PlannerSupport)
}
}
// Default cost is 1 for C and internal functions or 100 for functions in other languages
isInternalOrC := funcDef.Language == "c" || funcDef.Language == "internal"
if !((!isInternalOrC && funcDef.Cost == 100) || (isInternalOrC && funcDef.Cost == 1) || funcDef.Cost == 0) {
metadataFile.MustPrintf("\nCOST %v", funcDef.Cost)
}
if funcDef.ReturnsSet && funcDef.NumRows != 0 && funcDef.NumRows != 1000 {
metadataFile.MustPrintf("\nROWS %v", funcDef.NumRows)
}
if funcDef.Config != "" {
metadataFile.MustPrintf("\n%s", funcDef.Config)
}
// Stored procedures do not permit parallelism declarations
if ((connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("7")) || connectionPool.Version.IsCBDB()) && funcDef.Kind != "p" {
switch funcDef.Parallel {
case "u":
metadataFile.MustPrintf(" PARALLEL UNSAFE")
case "s":
metadataFile.MustPrintf(" PARALLEL SAFE")
case "r":
metadataFile.MustPrintf(" PARALLEL RESTRICTED")
default:
gplog.Fatal(fmt.Errorf("unrecognized proparallel value for function %s", funcDef.FQN()), "")
}
}
}
func PrintCreateAggregateStatement(metadataFile *utils.FileWithByteCount, objToc *toc.TOC, aggDef Aggregate, funcInfoMap map[uint32]FunctionInfo, aggMetadata ObjectMetadata) {
start := metadataFile.ByteCount
orderedStr := ""
if aggDef.IsOrdered {
orderedStr = "ORDERED "
}
argumentsStr := "*"
if aggDef.Arguments.String != "" {
argumentsStr = aggDef.Arguments.String
}
metadataFile.MustPrintf("\n\nCREATE %sAGGREGATE %s.%s(%s) (\n", orderedStr, aggDef.Schema, aggDef.Name, argumentsStr)
metadataFile.MustPrintf("\tSFUNC = %s,\n", funcInfoMap[aggDef.TransitionFunction].QualifiedName)
metadataFile.MustPrintf("\tSTYPE = %s", aggDef.TransitionDataType)
if aggDef.TransitionDataSize != 0 {
metadataFile.MustPrintf(",\n\tSSPACE = %d", aggDef.TransitionDataSize)
}
if aggDef.PreliminaryFunction != 0 {
metadataFile.MustPrintf(",\n\tPREFUNC = %s", funcInfoMap[aggDef.PreliminaryFunction].QualifiedName)
}
if aggDef.CombineFunction != 0 {
metadataFile.MustPrintf(",\n\tCOMBINEFUNC = %s", funcInfoMap[aggDef.CombineFunction].QualifiedName)
}
if aggDef.SerialFunction != 0 {
metadataFile.MustPrintf(",\n\tSERIALFUNC = %s", funcInfoMap[aggDef.SerialFunction].QualifiedName)
}
if aggDef.DeserialFunction != 0 {
metadataFile.MustPrintf(",\n\tDESERIALFUNC = %s", funcInfoMap[aggDef.DeserialFunction].QualifiedName)
}
if aggDef.FinalFunction != 0 {
metadataFile.MustPrintf(",\n\tFINALFUNC = %s", funcInfoMap[aggDef.FinalFunction].QualifiedName)
}
if aggDef.FinalFuncExtra {
metadataFile.MustPrintf(",\n\tFINALFUNC_EXTRA")
}
if !aggDef.InitValIsNull {
metadataFile.MustPrintf(",\n\tINITCOND = '%s'", aggDef.InitialValue)
}
if aggDef.SortOperator != "" {
metadataFile.MustPrintf(",\n\tSORTOP = %s.\"%s\"", aggDef.SortOperatorSchema, aggDef.SortOperator)
}
if connectionPool.Version.IsGPDB() && connectionPool.Version.Before("7") {
if aggDef.Hypothetical {
metadataFile.MustPrintf(",\n\tHYPOTHETICAL")
}
} else {
if aggDef.Kind == "h" {
metadataFile.MustPrintf(",\n\tHYPOTHETICAL")
}
}
if aggDef.MTransitionFunction != 0 {
metadataFile.MustPrintf(",\n\tMSFUNC = %s", funcInfoMap[aggDef.MTransitionFunction].QualifiedName)
}
if aggDef.MInverseTransitionFunction != 0 {
metadataFile.MustPrintf(",\n\tMINVFUNC = %s", funcInfoMap[aggDef.MInverseTransitionFunction].QualifiedName)
}
if aggDef.MTransitionDataType != "" {
metadataFile.MustPrintf(",\n\tMSTYPE = %s", aggDef.MTransitionDataType)
}
if aggDef.MTransitionDataSize != 0 {
metadataFile.MustPrintf(",\n\tMSSPACE = %d", aggDef.MTransitionDataSize)
}
if aggDef.MFinalFunction != 0 {
metadataFile.MustPrintf(",\n\tMFINALFUNC = %s", funcInfoMap[aggDef.MFinalFunction].QualifiedName)
}
if aggDef.MFinalFuncExtra {
metadataFile.MustPrintf(",\n\tMFINALFUNC_EXTRA")
}
if !aggDef.MInitValIsNull {
metadataFile.MustPrintf(",\n\tMINITCOND = '%s'", aggDef.MInitialValue)
}
if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("7")) || connectionPool.Version.IsCBDB() {
var defaultFinalModify string
if aggDef.Kind == "o" {
defaultFinalModify = "w"
} else {
defaultFinalModify = "r"
}
if aggDef.Finalmodify == "" {
aggDef.Finalmodify = defaultFinalModify
}
if aggDef.Mfinalmodify == "" {
aggDef.Mfinalmodify = defaultFinalModify
}
if aggDef.Finalmodify != defaultFinalModify {
if aggDef.Finalmodify == "r" {
metadataFile.MustPrintf(",\n\tFINALFUNC_MODIFY = READ_ONLY")
} else if aggDef.Finalmodify == "s" {
metadataFile.MustPrintf(",\n\tFINALFUNC_MODIFY = SHAREABLE")
} else if aggDef.Finalmodify == "w" {
metadataFile.MustPrintf(",\n\tFINALFUNC_MODIFY = READ_WRITE")
} else {
gplog.Fatal(fmt.Errorf("invalid aggfinalmodify value: expected 'r', 's' or 'w', got '%s'", aggDef.Finalmodify), "")
}
}
if aggDef.Mfinalmodify != defaultFinalModify {
if aggDef.Mfinalmodify == "r" {
metadataFile.MustPrintf(",\n\tMFINALFUNC_MODIFY = READ_ONLY")
} else if aggDef.Mfinalmodify == "s" {
metadataFile.MustPrintf(",\n\tMFINALFUNC_MODIFY = SHAREABLE")
} else if aggDef.Mfinalmodify == "w" {
metadataFile.MustPrintf(",\n\tMFINALFUNC_MODIFY = READ_WRITE")
} else {
gplog.Fatal(fmt.Errorf("invalid aggmfinalmodify value: expected 'r', 's' or 'w', got '%s'", aggDef.Mfinalmodify), "")
}
}
}
if aggDef.Parallel != "" {
switch aggDef.Parallel {
case "u":
metadataFile.MustPrintf(",\n\tPARALLEL = UNSAFE")
case "s":
metadataFile.MustPrintf(",\n\tPARALLEL = SAFE")
case "r":
metadataFile.MustPrintf(",\n\tPARALLEL = RESTRICTED")
default:
gplog.Fatal(fmt.Errorf("unrecognized proparallel value for function %s", aggDef.Parallel), "")
}
}
metadataFile.MustPrintln("\n);")
section, entry := aggDef.GetMetadataEntry()
tier := globalTierMap[aggDef.GetUniqueID()]
objToc.AddMetadataEntry(section, entry, start, metadataFile.ByteCount, tier)
PrintObjectMetadata(metadataFile, objToc, aggMetadata, aggDef, "", tier)
}
func PrintCreateCastStatement(metadataFile *utils.FileWithByteCount, objToc *toc.TOC, castDef Cast, castMetadata ObjectMetadata) {
start := metadataFile.ByteCount
metadataFile.MustPrintf("\n\nCREATE CAST %s\n", castDef.FQN())
switch castDef.CastMethod {
case "i":
metadataFile.MustPrintf("\tWITH INOUT")
case "b":
metadataFile.MustPrintf("\tWITHOUT FUNCTION")
case "f":
funcFQN := utils.MakeFQN(castDef.FunctionSchema, castDef.FunctionName)
metadataFile.MustPrintf("\tWITH FUNCTION %s(%s)", funcFQN, castDef.FunctionArgs)
}
switch castDef.CastContext {
case "a":
metadataFile.MustPrintf("\nAS ASSIGNMENT")
case "i":
metadataFile.MustPrintf("\nAS IMPLICIT")
case "e": // Default case, don't print anything else
}
metadataFile.MustPrintf(";")
section, entry := castDef.GetMetadataEntry()
tier := globalTierMap[castDef.GetUniqueID()]
objToc.AddMetadataEntry(section, entry, start, metadataFile.ByteCount, tier)
PrintObjectMetadata(metadataFile, objToc, castMetadata, castDef, "", tier)
}
func PrintCreateExtensionStatements(metadataFile *utils.FileWithByteCount, objToc *toc.TOC, extensionDefs []Extension, extensionMetadata MetadataMap) {
for _, extensionDef := range extensionDefs {
start := metadataFile.ByteCount
if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("7")) || connectionPool.Version.IsCBDB() {
// changes to gp_toolkit in gpdb7 require explicilty creating the schema before the extension
metadataFile.MustPrintf(
"\n\nCREATE SCHEMA IF NOT EXISTS %[1]s;\nSET search_path=%[1]s,pg_catalog;\nCREATE EXTENSION IF NOT EXISTS %[2]s WITH SCHEMA %[1]s;\nSET search_path=pg_catalog;\n",
extensionDef.Schema, extensionDef.Name)
} else {
metadataFile.MustPrintf(
"\n\nSET search_path=%[1]s,pg_catalog;\nCREATE EXTENSION IF NOT EXISTS %[2]s WITH SCHEMA %[1]s;\nSET search_path=pg_catalog;\n",
extensionDef.Schema, extensionDef.Name)
}
section, entry := extensionDef.GetMetadataEntry()
tier := globalTierMap[extensionDef.GetUniqueID()]
objToc.AddMetadataEntry(section, entry, start, metadataFile.ByteCount, tier)
PrintObjectMetadata(metadataFile, objToc, extensionMetadata[extensionDef.GetUniqueID()], extensionDef, "", tier)
}
}
/*
* This function separates out functions related to procedural languages from
* any other functions, so that language-related functions can be backed up before
* the languages themselves and we can avoid sorting languages and functions
* together to resolve dependencies.
*/
func ExtractLanguageFunctions(funcDefs []Function, procLangs []ProceduralLanguage) ([]Function, []Function) {
isLangFuncMap := make(map[uint32]bool)
for _, procLang := range procLangs {
for _, funcDef := range funcDefs {
isLangFuncMap[funcDef.Oid] = isLangFuncMap[funcDef.Oid] ||
funcDef.Oid == procLang.Handler ||
funcDef.Oid == procLang.Inline ||
funcDef.Oid == procLang.Validator
}
}
langFuncs := make([]Function, 0)
otherFuncs := make([]Function, 0)
for _, funcDef := range funcDefs {
if isLangFuncMap[funcDef.Oid] {
langFuncs = append(langFuncs, funcDef)
} else {
otherFuncs = append(otherFuncs, funcDef)
}
}
return langFuncs, otherFuncs
}
func PrintCreateLanguageStatements(metadataFile *utils.FileWithByteCount, objToc *toc.TOC, procLangs []ProceduralLanguage,
funcInfoMap map[uint32]FunctionInfo, procLangMetadata MetadataMap) {
for _, procLang := range procLangs {
start := metadataFile.ByteCount
metadataFile.MustPrintf("\n\nCREATE ")
if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("6")) || connectionPool.Version.IsCBDB() {
metadataFile.MustPrintf("OR REPLACE ")
}
if procLang.PlTrusted {
metadataFile.MustPrintf("TRUSTED ")
}
metadataFile.MustPrintf("PROCEDURAL LANGUAGE %s", procLang.Name)
paramsStr := ""
alterStr := ""
/*
* If the handler, validator, and inline functions are in pg_pltemplate, we can
* back up a CREATE LANGUAGE command without specifying them individually.
*
* The schema of the handler function should match the schema of the language itself, but
* the inline and validator functions can be in a different schema and must be schema-qualified.
*/
if procLang.Handler != 0 {
handlerInfo := funcInfoMap[procLang.Handler]
paramsStr += fmt.Sprintf(" HANDLER %s", handlerInfo.QualifiedName)
alterStr += fmt.Sprintf("\nALTER FUNCTION %s(%s) OWNER TO %s;", handlerInfo.QualifiedName, handlerInfo.Arguments.String, procLang.Owner)
}
if procLang.Inline != 0 {
inlineInfo := funcInfoMap[procLang.Inline]
paramsStr += fmt.Sprintf(" INLINE %s", inlineInfo.QualifiedName)
alterStr += fmt.Sprintf("\nALTER FUNCTION %s(%s) OWNER TO %s;", inlineInfo.QualifiedName, inlineInfo.Arguments.String, procLang.Owner)
}
if procLang.Validator != 0 {
validatorInfo := funcInfoMap[procLang.Validator]
paramsStr += fmt.Sprintf(" VALIDATOR %s", validatorInfo.QualifiedName)
alterStr += fmt.Sprintf("\nALTER FUNCTION %s(%s) OWNER TO %s;", validatorInfo.QualifiedName, validatorInfo.Arguments.String, procLang.Owner)
}
metadataFile.MustPrintf("%s;", paramsStr)
section, entry := procLang.GetMetadataEntry()
tier := globalTierMap[procLang.GetUniqueID()]
objToc.AddMetadataEntry(section, entry, start, metadataFile.ByteCount, tier)
start = metadataFile.ByteCount
metadataFile.MustPrint(alterStr)
objToc.AddMetadataEntry(section, entry, start, metadataFile.ByteCount, tier)
PrintObjectMetadata(metadataFile, objToc, procLangMetadata[procLang.GetUniqueID()], procLang, "", tier)
}
}
func PrintCreateTransformStatement(metadataFile *utils.FileWithByteCount, objToc *toc.TOC, transform Transform, funcInfoMap map[uint32]FunctionInfo, transformMetadata ObjectMetadata) {
fromSQLFunc, fromSQLIsDefined := funcInfoMap[transform.FromSQLFunc]
toSQLFunc, toSQLIsDefined := funcInfoMap[transform.ToSQLFunc]
TypeFQN := fmt.Sprintf("%s.%s", transform.TypeNamespace, transform.TypeName)
if !fromSQLIsDefined && !toSQLIsDefined {
gplog.Warn("Skipping invalid transform object for type %s and language %s; At least one of FROM and TO functions should be specified.", TypeFQN, transform.LanguageName)
return
}
start := metadataFile.ByteCount
statement := fmt.Sprintf("\n\nCREATE TRANSFORM FOR %s LANGUAGE %s (", TypeFQN, transform.LanguageName)
if fromSQLIsDefined {
statement += fmt.Sprintf("FROM SQL WITH FUNCTION %s", fromSQLFunc.FQN())
} else {
gplog.Warn("No FROM function found for transform object with type %s and language %s\n", TypeFQN, transform.LanguageName)
}
if toSQLIsDefined {
if fromSQLIsDefined {
statement += ", "
}
statement += fmt.Sprintf("TO SQL WITH FUNCTION %s", toSQLFunc.FQN())
} else {
gplog.Warn("No TO function found for transform object with type %s and language %s\n", TypeFQN, transform.LanguageName)
}
statement += ");"
metadataFile.MustPrintf("%s", statement)
section, entry := transform.GetMetadataEntry()
tier := globalTierMap[transform.GetUniqueID()]
objToc.AddMetadataEntry(section, entry, start, metadataFile.ByteCount, tier)
PrintObjectMetadata(metadataFile, objToc, transformMetadata, transform, "", tier)
}
func PrintCreateConversionStatements(metadataFile *utils.FileWithByteCount, objToc *toc.TOC, conversions []Conversion, conversionMetadata MetadataMap) {
for _, conversion := range conversions {
start := metadataFile.ByteCount
convFQN := utils.MakeFQN(conversion.Schema, conversion.Name)
defaultStr := ""
if conversion.IsDefault {
defaultStr = " DEFAULT"
}
metadataFile.MustPrintf("\n\nCREATE%s CONVERSION %s FOR '%s' TO '%s' FROM %s;",
defaultStr, convFQN, conversion.ForEncoding, conversion.ToEncoding, conversion.ConversionFunction)
section, entry := conversion.GetMetadataEntry()
tier := globalTierMap[conversion.GetUniqueID()]
objToc.AddMetadataEntry(section, entry, start, metadataFile.ByteCount, tier)
PrintObjectMetadata(metadataFile, objToc, conversionMetadata[conversion.GetUniqueID()], conversion, "", tier)
}
}
func PrintCreateForeignDataWrapperStatement(metadataFile *utils.FileWithByteCount, objToc *toc.TOC,
fdw ForeignDataWrapper, funcInfoMap map[uint32]FunctionInfo, fdwMetadata ObjectMetadata) {
start := metadataFile.ByteCount
metadataFile.MustPrintf("\n\nCREATE FOREIGN DATA WRAPPER %s", fdw.Name)
if fdw.Handler != 0 {
metadataFile.MustPrintf("\n\tHANDLER %s", funcInfoMap[fdw.Handler].QualifiedName)
}
if fdw.Validator != 0 {
metadataFile.MustPrintf("\n\tVALIDATOR %s", funcInfoMap[fdw.Validator].QualifiedName)
}
if fdw.Options != "" {
metadataFile.MustPrintf("\n\tOPTIONS (%s)", fdw.Options)
}
metadataFile.MustPrintf(";")
section, entry := fdw.GetMetadataEntry()
tier := globalTierMap[fdw.GetUniqueID()]
objToc.AddMetadataEntry(section, entry, start, metadataFile.ByteCount, tier)
PrintObjectMetadata(metadataFile, objToc, fdwMetadata, fdw, "", tier)
}
func PrintCreateServerStatement(metadataFile *utils.FileWithByteCount, objToc *toc.TOC, server ForeignServer, serverMetadata ObjectMetadata) {
start := metadataFile.ByteCount
metadataFile.MustPrintf("\n\nCREATE SERVER %s", server.Name)
if server.Type != "" {
metadataFile.MustPrintf("\n\tTYPE '%s'", server.Type)
}
if server.Version != "" {
metadataFile.MustPrintf("\n\tVERSION '%s'", server.Version)
}
metadataFile.MustPrintf("\n\tFOREIGN DATA WRAPPER %s", server.ForeignDataWrapper)
if server.Options != "" {
metadataFile.MustPrintf("\n\tOPTIONS (%s)", server.Options)
}
metadataFile.MustPrintf(";")
//NOTE: We must specify SERVER when creating and dropping, but FOREIGN SERVER when granting and revoking
section, entry := server.GetMetadataEntry()
tier := globalTierMap[server.GetUniqueID()]
objToc.AddMetadataEntry(section, entry, start, metadataFile.ByteCount, tier)
PrintObjectMetadata(metadataFile, objToc, serverMetadata, server, "", tier)
}
func PrintCreateUserMappingStatement(metadataFile *utils.FileWithByteCount, objToc *toc.TOC, mapping UserMapping) {
start := metadataFile.ByteCount
metadataFile.MustPrintf("\n\nCREATE USER MAPPING FOR %s\n\tSERVER %s", mapping.User, mapping.Server)
if mapping.Options != "" {
metadataFile.MustPrintf("\n\tOPTIONS (%s)", mapping.Options)
}
metadataFile.MustPrintf(";")
section, entry := mapping.GetMetadataEntry()
tier := globalTierMap[mapping.GetUniqueID()]
objToc.AddMetadataEntry(section, entry, start, metadataFile.ByteCount, tier)
}