blob: 8fb040697411db3c756ef6addca5ad786070a262 [file]
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 (
"database/sql"
"fmt"
"regexp"
"strings"
"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 Function struct {
Oid uint32
Schema string
Name string
ReturnsSet bool `db:"proretset"`
FunctionBody string
BinaryPath string
Arguments sql.NullString
IdentArgs sql.NullString
ResultType sql.NullString
Volatility string `db:"provolatile"`
IsStrict bool `db:"proisstrict"`
IsLeakProof bool `db:"proleakproof"`
IsSecurityDefiner bool `db:"prosecdef"`
Config string `db:"proconfig"`
Cost float32 `db:"procost"`
NumRows float32 `db:"prorows"`
DataAccess string `db:"prodataaccess"`
Language string
Kind string `db:"prokind"` // GPDB 7+
PlannerSupport string `db:"prosupport"` // GPDB 7+
IsWindow bool `db:"proiswindow"` // before 7
ExecLocation string `db:"proexeclocation"`
Parallel string `db:"proparallel"` // GPDB 7+
TransformTypes string // GPDB 7+
}
func (f Function) GetMetadataEntry() (string, toc.MetadataEntry) {
nameWithArgs := fmt.Sprintf("%s(%s)", f.Name, f.IdentArgs.String)
return "predata",
toc.MetadataEntry{
Schema: f.Schema,
Name: nameWithArgs,
ObjectType: toc.OBJ_FUNCTION,
ReferenceObject: "",
StartByte: 0,
EndByte: 0,
}
}
func (f Function) GetUniqueID() UniqueID {
return UniqueID{ClassID: PG_PROC_OID, Oid: f.Oid}
}
func (f Function) FQN() string {
/*
* We need to include arguments to differentiate functions with the same name
*/
return fmt.Sprintf("%s.%s(%s)", f.Schema, f.Name, f.IdentArgs.String)
}
func GetFunctions(connectionPool *dbconn.DBConn) []Function {
excludeImplicitFunctionsClause := ""
if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("6")) || connectionPool.Version.IsCBDB() {
// This excludes implicitly created functions. Currently this is only range type functions
excludeImplicitFunctionsClause = `
AND NOT EXISTS (
SELECT 1 FROM pg_depend
WHERE classid = 'pg_proc'::regclass::oid
AND objid = p.oid AND deptype = 'i')`
}
locationAtts := ""
if connectionPool.Version.IsGPDB() && connectionPool.Version.Before("6") {
locationAtts = "'a' AS proexeclocation,"
} else if connectionPool.Version.IsGPDB() && connectionPool.Version.Is("6") {
locationAtts = "proiswindow,proexeclocation,proleakproof,"
} else {
locationAtts = "proexeclocation,proleakproof,proparallel,"
}
before7Query := fmt.Sprintf(`
SELECT p.oid,
quote_ident(nspname) AS schema,
quote_ident(proname) AS name,
proretset,
coalesce(prosrc, '') AS functionbody,
coalesce(probin, '') AS binarypath,
pg_catalog.pg_get_function_arguments(p.oid) AS arguments,
pg_catalog.pg_get_function_identity_arguments(p.oid) AS identargs,
pg_catalog.pg_get_function_result(p.oid) AS resulttype,
provolatile,
proisstrict,
prosecdef,
%s
coalesce(array_to_string(ARRAY(SELECT 'SET ' || option_name || ' TO ' || option_value
FROM pg_options_to_table(proconfig)), ' '), '') AS proconfig,
procost,
prorows,
prodataaccess,
l.lanname AS language
FROM pg_proc p
JOIN pg_catalog.pg_language l ON p.prolang = l.oid
LEFT JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE %s
AND proisagg = 'f'
AND %s%s
ORDER BY nspname, proname, identargs`, locationAtts,
SchemaFilterClause("n"),
ExtensionFilterClause("p"),
excludeImplicitFunctionsClause)
atLeast7Query := fmt.Sprintf(`
SELECT p.oid,
quote_ident(nspname) AS schema,
quote_ident(p.proname) AS name,
proretset,
coalesce(prosrc, '') AS functionbody,
coalesce(probin, '') AS binarypath,
pg_catalog.pg_get_function_arguments(p.oid) AS arguments,
pg_catalog.pg_get_function_identity_arguments(p.oid) AS identargs,
pg_catalog.pg_get_function_result(p.oid) AS resulttype,
provolatile,
proisstrict,
prosecdef,
%s
coalesce(array_to_string(ARRAY(SELECT 'SET ' || option_name || ' TO ' || option_value
FROM pg_options_to_table(proconfig)), ' '), '') AS proconfig,
procost,
prorows,
prokind,
prosupport,
l.lanname AS language,
coalesce(array_to_string(ARRAY(SELECT 'FOR TYPE ' || nm.nspname || '.' || typ.typname
from
unnest(p.protrftypes) as trf_unnest
left join pg_type typ
on trf_unnest = typ.oid
left join pg_namespace nm
on typ.typnamespace = nm.oid
), ', '), '') AS transformtypes
FROM pg_proc p
JOIN pg_catalog.pg_language l ON p.prolang = l.oid
LEFT JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE %s
AND prokind <> 'a'
AND %s%s
ORDER BY nspname, proname, identargs`, locationAtts,
SchemaFilterClause("n"),
ExtensionFilterClause("p"),
excludeImplicitFunctionsClause)
query := ""
if connectionPool.Version.IsGPDB() && connectionPool.Version.Before("7") {
query = before7Query
} else {
query = atLeast7Query
}
results := make([]Function, 0)
err := connectionPool.Select(&results, query)
gplog.FatalOnError(err)
err = PostProcessFunctionConfigs(results)
gplog.FatalOnError(err)
// Process kind value for GPDB7+ window functions, to ensure window
// attribute is correctly set.
// Remove all functions that have NULL arguments, NULL identity
// arguments, or NULL result type. This can happen if the query
// above is run and a concurrent function drop happens just
// before the pg_get_function_* functions execute.
verifiedResults := make([]Function, 0)
for _, result := range results {
if ((connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("7")) || connectionPool.Version.IsCBDB()) && result.Kind == "w" {
result.IsWindow = true
}
if result.Arguments.Valid && result.IdentArgs.Valid && result.ResultType.Valid {
verifiedResults = append(verifiedResults, result)
} else if ((connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("7")) || connectionPool.Version.IsCBDB()) &&
result.Kind == "p" && !result.ResultType.Valid { // GPDB7+ stored procedure
verifiedResults = append(verifiedResults, result)
} else {
gplog.Warn("Function '%s.%s' not backed up, most likely dropped after gpbackup had begun.", result.Schema, result.Name)
}
}
return verifiedResults
}
func PostProcessFunctionConfigs(allFunctions []Function) error {
setToNameValuePattern := regexp.MustCompile(`^SET (.*) TO (.*)$`)
for i, function := range allFunctions {
if function.Config == "" {
continue
}
captures := setToNameValuePattern.FindStringSubmatch(function.Config)
if len(captures) != 3 {
return fmt.Errorf("Function config does not match syntax expectations. Function was: %v", function)
}
gucName := strings.ToLower(captures[1])
gucValue := captures[2]
quotedValue := QuoteGUCValue(gucName, gucValue)
// write to struct by referencing the slice rather than the readonly 'function' copy
allFunctions[i].Config = fmt.Sprintf(`SET %s TO %s`, gucName, quotedValue)
}
return nil
}
func QuoteGUCValue(name, value string) string {
/*
* GUC tools have one way to stuff many strings into a single string, with it own
* system of quotation. SQL quotation is different, so we have to unwind the
* GUC quoting system and use the SQL system.
* We are modeling this function after SplitGUCList in psql/dumputils.c
*/
var result string
if name == "temp_tablespaces" ||
name == "session_preload_libraries" ||
name == "shared_preload_libraries" ||
name == "local_preload_libraries" ||
name == "search_path" {
strSplit := strings.Split(value, ",")
for i, item := range strSplit {
item = strings.Trim(item, " ")
item = UnescapeDoubleQuote(item)
item = `'` + item + `'`
strSplit[i] = item
}
result = strings.Join(strSplit, ", ")
} else {
result = `'` + value + `'`
}
return result
}
func UnescapeDoubleQuote(value string) string {
result := value
if len(value) > 1 && value[0] == '"' && value[len(value)-1] == '"' {
result = value[1 : len(value)-1]
result = strings.Replace(result, `""`, `"`, -1)
}
return result
}
func GetFunctionReturnTypes(connectionPool *dbconn.DBConn) map[uint32]Function {
query := fmt.Sprintf(`
SELECT p.oid,
proretset,
CASE WHEN proretset = 't' THEN 'SETOF ' || format_type(prorettype, NULL)
ELSE format_type(prorettype, NULL) END AS resulttype
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE %s`, SchemaFilterClause("n"))
results := make([]Function, 0)
err := connectionPool.Select(&results, query)
gplog.FatalOnError(err)
returnMap := make(map[uint32]Function)
for _, result := range results {
returnMap[result.Oid] = result
}
return returnMap
}
type Aggregate struct {
Oid uint32
Schema string
Name string
Arguments sql.NullString
IdentArgs sql.NullString
TransitionFunction uint32 `db:"aggtransfn"`
PreliminaryFunction uint32 `db:"aggprelimfn"`
CombineFunction uint32 `db:"aggcombinefn"`
SerialFunction uint32 `db:"aggserialfn"`
DeserialFunction uint32 `db:"aggdeserialfn"`
FinalFunction uint32 `db:"aggfinalfn"`
FinalFuncExtra bool
SortOperator string
SortOperatorSchema string
Hypothetical bool // GPDB < 7
Kind string // GPDB7+
TransitionDataType string
TransitionDataSize int `db:"aggtransspace"`
InitialValue string
InitValIsNull bool
IsOrdered bool `db:"aggordered"`
MTransitionFunction uint32 `db:"aggmtransfn"`
MInverseTransitionFunction uint32 `db:"aggminvtransfn"`
MTransitionDataType string
MTransitionDataSize int `db:"aggmtransspace"`
MFinalFunction uint32 `db:"aggmfinalfn"`
MFinalFuncExtra bool
MInitialValue string
MInitValIsNull bool
Finalmodify string // GPDB7+
Mfinalmodify string // GPDB7+
Parallel string // GPDB7+
}
func (a Aggregate) GetMetadataEntry() (string, toc.MetadataEntry) {
identArgumentsStr := "*"
if a.IdentArgs.String != "" {
identArgumentsStr = a.IdentArgs.String
}
aggWithArgs := fmt.Sprintf("%s(%s)", a.Name, identArgumentsStr)
return "predata",
toc.MetadataEntry{
Schema: a.Schema,
Name: aggWithArgs,
ObjectType: toc.OBJ_AGGREGATE,
ReferenceObject: "",
StartByte: 0,
EndByte: 0,
}
}
func (a Aggregate) GetUniqueID() UniqueID {
return UniqueID{ClassID: PG_AGGREGATE_OID, Oid: a.Oid}
}
func (a Aggregate) FQN() string {
identArgumentsStr := "*"
if a.IdentArgs.String != "" {
identArgumentsStr = a.IdentArgs.String
}
return fmt.Sprintf("%s.%s(%s)", a.Schema, a.Name, identArgumentsStr)
}
func GetAggregates(connectionPool *dbconn.DBConn) []Aggregate {
version5query := fmt.Sprintf(`
SELECT p.oid,
quote_ident(n.nspname) AS schema,
p.proname AS name,
pg_catalog.pg_get_function_arguments(p.oid) AS arguments,
pg_catalog.pg_get_function_identity_arguments(p.oid) AS identargs,
a.aggtransfn::regproc::oid,
a.aggprelimfn::regproc::oid,
a.aggfinalfn::regproc::oid,
coalesce(o.oprname, '') AS sortoperator,
coalesce(quote_ident(opn.nspname), '') AS sortoperatorschema,
format_type(a.aggtranstype, NULL) as transitiondatatype,
coalesce(a.agginitval, '') AS initialvalue,
(a.agginitval IS NULL) AS initvalisnull,
true AS minitvalisnull,
a.aggordered
FROM pg_aggregate a
LEFT JOIN pg_proc p ON a.aggfnoid = p.oid
LEFT JOIN pg_namespace n ON p.pronamespace = n.oid
LEFT JOIN pg_operator o ON a.aggsortop = o.oid
LEFT JOIN pg_namespace opn ON o.oprnamespace = opn.oid
WHERE %s
AND %s`,
SchemaFilterClause("n"), ExtensionFilterClause("p"))
version6query := fmt.Sprintf(`
SELECT p.oid,
quote_ident(n.nspname) AS schema,
p.proname AS name,
pg_catalog.pg_get_function_arguments(p.oid) AS arguments,
pg_catalog.pg_get_function_identity_arguments(p.oid) AS identargs,
a.aggtransfn::regproc::oid,
a.aggcombinefn::regproc::oid,
a.aggserialfn::regproc::oid,
a.aggdeserialfn::regproc::oid,
a.aggfinalfn::regproc::oid,
a.aggfinalextra AS finalfuncextra,
coalesce(o.oprname, '') AS sortoperator,
coalesce(quote_ident(opn.nspname), '') AS sortoperatorschema,
(a.aggkind = 'h') AS hypothetical,
format_type(a.aggtranstype, NULL) as transitiondatatype,
aggtransspace,
coalesce(a.agginitval, '') AS initialvalue,
(a.agginitval IS NULL) AS initvalisnull,
a.aggmtransfn::regproc::oid,
a.aggminvtransfn::regproc::oid,
a.aggmfinalfn::regproc::oid,
a.aggmfinalextra AS mfinalfuncextra,
format_type(a.aggmtranstype, NULL) as mtransitiondatatype,
aggmtransspace,
(a.aggminitval IS NULL) AS minitvalisnull,
coalesce(a.aggminitval, '') AS minitialvalue
FROM pg_aggregate a
LEFT JOIN pg_proc p ON a.aggfnoid = p.oid
LEFT JOIN pg_namespace n ON p.pronamespace = n.oid
LEFT JOIN pg_operator o ON a.aggsortop = o.oid
LEFT JOIN pg_namespace opn ON o.oprnamespace = opn.oid
WHERE %s
AND %s`,
SchemaFilterClause("n"), ExtensionFilterClause("p"))
atLeast7Query := fmt.Sprintf(`
SELECT p.oid,
quote_ident(n.nspname) AS schema,
p.proname AS name,
p.proparallel as parallel,
pg_catalog.pg_get_function_arguments(p.oid) AS arguments,
pg_catalog.pg_get_function_identity_arguments(p.oid) AS identargs,
a.aggtransfn::regproc::oid,
a.aggcombinefn::regproc::oid,
a.aggserialfn::regproc::oid,
a.aggdeserialfn::regproc::oid,
a.aggfinalfn::regproc::oid,
a.aggfinalextra AS finalfuncextra,
coalesce(o.oprname, '') AS sortoperator,
coalesce(quote_ident(opn.nspname), '') AS sortoperatorschema,
aggkind AS kind,
format_type(a.aggtranstype, NULL) as transitiondatatype,
aggtransspace,
coalesce(a.agginitval, '') AS initialvalue,
(a.agginitval IS NULL) AS initvalisnull,
a.aggmtransfn::regproc::oid,
a.aggminvtransfn::regproc::oid,
a.aggmfinalfn::regproc::oid,
a.aggmfinalextra AS mfinalfuncextra,
format_type(a.aggmtranstype, NULL) as mtransitiondatatype,
aggmtransspace,
(a.aggminitval IS NULL) AS minitvalisnull,
coalesce(a.aggminitval, '') AS minitialvalue,
a.aggfinalmodify AS finalmodify,
a.aggmfinalmodify AS mfinalmodify
FROM pg_aggregate a
LEFT JOIN pg_proc p ON a.aggfnoid = p.oid
LEFT JOIN pg_namespace n ON p.pronamespace = n.oid
LEFT JOIN pg_operator o ON a.aggsortop = o.oid
LEFT JOIN pg_namespace opn ON o.oprnamespace = opn.oid
WHERE %s
AND %s`,
SchemaFilterClause("n"), ExtensionFilterClause("p"))
aggregates := make([]Aggregate, 0)
query := ""
if connectionPool.Version.IsGPDB() && connectionPool.Version.Is("5") {
query = version5query
} else if connectionPool.Version.IsGPDB() && connectionPool.Version.Is("6") {
query = version6query
} else {
query = atLeast7Query
}
err := connectionPool.Select(&aggregates, query)
gplog.FatalOnError(err)
for i := range aggregates {
if aggregates[i].MTransitionDataType == "-" {
aggregates[i].MTransitionDataType = ""
}
}
// Remove all aggregates that have NULL arguments or NULL
// identity arguments. This can happen if the query above
// is run and a concurrent aggregate drop happens before
// the pg_get_function_* functions execute.
verifiedAggregates := make([]Aggregate, 0)
for _, aggregate := range aggregates {
if aggregate.Arguments.Valid && aggregate.IdentArgs.Valid {
verifiedAggregates = append(verifiedAggregates, aggregate)
} else {
gplog.Warn("Aggregate '%s.%s' not backed up, most likely dropped after gpbackup had begun.", aggregate.Schema, aggregate.Name)
}
}
return verifiedAggregates
}
type FunctionInfo struct {
Oid uint32
Name string
Schema string
QualifiedName string
Arguments sql.NullString
IdentArgs sql.NullString
IsInternal bool
}
func (info FunctionInfo) FQN() string {
return fmt.Sprintf("%s(%s)", info.QualifiedName, info.IdentArgs.String)
}
func (info FunctionInfo) GetMetadataEntry() (string, toc.MetadataEntry) {
nameWithArgs := fmt.Sprintf("%s(%s)", info.Name, info.IdentArgs.String)
return "predata",
toc.MetadataEntry{
Schema: info.Schema,
Name: nameWithArgs,
ObjectType: toc.OBJ_FUNCTION,
ReferenceObject: "",
StartByte: 0,
EndByte: 0,
}
}
func GetFunctionOidToInfoMap(connectionPool *dbconn.DBConn) map[uint32]FunctionInfo {
query := `
SELECT p.oid,
quote_ident(n.nspname) AS schema,
quote_ident(p.proname) AS name,
pg_catalog.pg_get_function_arguments(p.oid) AS arguments,
pg_catalog.pg_get_function_identity_arguments(p.oid) AS identargs
FROM pg_proc p
LEFT JOIN pg_namespace n ON p.pronamespace = n.oid`
results := make([]FunctionInfo, 0)
funcMap := make(map[uint32]FunctionInfo)
err := connectionPool.Select(&results, query)
gplog.FatalOnError(err)
for _, funcInfo := range results {
if !funcInfo.Arguments.Valid || !funcInfo.IdentArgs.Valid {
gplog.Warn("Function '%s.%s' not backed up, most likely dropped after gpbackup had begun.", funcInfo.Schema, funcInfo.Name)
continue
}
if funcInfo.Schema == "pg_catalog" {
funcInfo.IsInternal = true
}
funcInfo.QualifiedName = utils.MakeFQN(funcInfo.Schema, funcInfo.Name)
funcMap[funcInfo.Oid] = funcInfo
}
return funcMap
}
type Cast struct {
Oid uint32
SourceTypeFQN string
TargetTypeFQN string
FunctionSchema string
FunctionName string
FunctionArgs string
CastContext string
CastMethod string
}
func (c Cast) GetMetadataEntry() (string, toc.MetadataEntry) {
castStr := fmt.Sprintf("(%s AS %s)", c.SourceTypeFQN, c.TargetTypeFQN)
filterSchema := "pg_catalog"
if c.CastMethod == "f" {
filterSchema = c.FunctionSchema // Use the function's schema to allow restore filtering
}
return "predata",
toc.MetadataEntry{
Schema: filterSchema,
Name: castStr,
ObjectType: toc.OBJ_CAST,
ReferenceObject: "",
StartByte: 0,
EndByte: 0,
}
}
func (c Cast) GetUniqueID() UniqueID {
return UniqueID{ClassID: PG_CAST_OID, Oid: c.Oid}
}
func (c Cast) FQN() string {
return fmt.Sprintf("(%s AS %s)", c.SourceTypeFQN, c.TargetTypeFQN)
}
func GetCasts(connectionPool *dbconn.DBConn) []Cast {
/* This query retrieves all casts where either the source type, the target
* type, or the cast function is user-defined.
*/
methodStr := ""
if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("6")) || connectionPool.Version.IsCBDB() {
methodStr = "castmethod,"
} else {
methodStr = "CASE WHEN c.castfunc = 0 THEN 'b' ELSE 'f' END AS castmethod,"
}
query := fmt.Sprintf(`
SELECT
c.oid,
quote_ident(sn.nspname) || '.' || quote_ident(st.typname) AS sourcetypefqn,
quote_ident(tn.nspname) || '.' || quote_ident(tt.typname) AS targettypefqn,
coalesce(quote_ident(n.nspname), '') AS functionschema,
coalesce(quote_ident(p.proname), '') AS functionname,
coalesce(pg_get_function_arguments(p.oid), '') AS functionargs,
%s
c.castcontext
FROM pg_cast c
JOIN pg_type st ON c.castsource = st.oid
JOIN pg_type tt ON c.casttarget = tt.oid
JOIN pg_namespace sn ON st.typnamespace = sn.oid
JOIN pg_namespace tn ON tt.typnamespace = tn.oid
LEFT JOIN pg_proc p ON c.castfunc = p.oid
LEFT JOIN pg_description d ON c.oid = d.objoid
LEFT JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE ((%s) OR (%s) OR (%s))
AND %s
ORDER BY 1, 2`, methodStr,
SchemaFilterClause("sn"), SchemaFilterClause("tn"),
SchemaFilterClause("n"), ExtensionFilterClause("c"))
casts := make([]Cast, 0)
err := connectionPool.Select(&casts, query)
gplog.FatalOnError(err)
return casts
}
type Extension struct {
Oid uint32
Name string
Schema string
}
func (e Extension) GetMetadataEntry() (string, toc.MetadataEntry) {
return "predata",
toc.MetadataEntry{
Schema: "",
Name: e.Name,
ObjectType: toc.OBJ_EXTENSION,
ReferenceObject: "",
StartByte: 0,
EndByte: 0,
}
}
func (e Extension) GetUniqueID() UniqueID {
return UniqueID{ClassID: PG_EXTENSION_OID, Oid: e.Oid}
}
func (e Extension) FQN() string {
return e.Name
}
func GetExtensions(connectionPool *dbconn.DBConn) []Extension {
results := make([]Extension, 0)
query := fmt.Sprintf(`
SELECT e.oid,
quote_ident(extname) AS name,
quote_ident(n.nspname) AS schema
FROM pg_extension e
JOIN pg_namespace n ON e.extnamespace = n.oid
WHERE e.oid >= %d`, FIRST_NORMAL_OBJECT_ID)
err := connectionPool.Select(&results, query)
gplog.FatalOnError(err)
return results
}
type ProceduralLanguage struct {
Oid uint32
Name string
Owner string
IsPl bool `db:"lanispl"`
PlTrusted bool `db:"lanpltrusted"`
Handler uint32 `db:"lanplcallfoid"`
Inline uint32 `db:"laninline"`
Validator uint32 `db:"lanvalidator"`
}
func (pl ProceduralLanguage) GetMetadataEntry() (string, toc.MetadataEntry) {
return "predata",
toc.MetadataEntry{
Schema: "",
Name: pl.Name,
ObjectType: toc.OBJ_LANGUAGE,
ReferenceObject: "",
StartByte: 0,
EndByte: 0,
}
}
func (pl ProceduralLanguage) GetUniqueID() UniqueID {
return UniqueID{ClassID: PG_LANGUAGE_OID, Oid: pl.Oid}
}
func (pl ProceduralLanguage) FQN() string {
return pl.Name
}
func GetProceduralLanguages(connectionPool *dbconn.DBConn) []ProceduralLanguage {
results := make([]ProceduralLanguage, 0)
query := fmt.Sprintf(`
SELECT oid,
quote_ident(l.lanname) AS name,
pg_get_userbyid(l.lanowner) AS owner,
l.lanispl,
l.lanpltrusted,
l.lanplcallfoid::regprocedure::oid,
l.laninline::regprocedure::oid,
l.lanvalidator::regprocedure::oid
FROM pg_language l
WHERE l.lanispl='t'
AND l.lanname != 'plpgsql'
AND %s`, ExtensionFilterClause("l"))
err := connectionPool.Select(&results, query)
gplog.FatalOnError(err)
return results
}
type Transform struct {
Oid uint32
TypeNamespace string `db:"typnamespace"`
TypeName string `db:"typname"`
LanguageName string `db:"lanname"`
FromSQLFunc uint32 `db:"trffromsql"`
ToSQLFunc uint32 `db:"trftosql"`
}
func (trf Transform) GetMetadataEntry() (string, toc.MetadataEntry) {
return "predata",
toc.MetadataEntry{
Schema: "",
Name: "",
ObjectType: toc.OBJ_TRANSFORM,
ReferenceObject: "",
StartByte: 0,
EndByte: 0,
}
}
func (trf Transform) GetUniqueID() UniqueID {
return UniqueID{ClassID: PG_TRANSFORM_OID, Oid: trf.Oid}
}
func (trf Transform) FQN() string {
return fmt.Sprintf("FOR %s.%s LANGUAGE %s", trf.TypeNamespace, trf.TypeName, trf.LanguageName)
}
func GetTransforms(connectionPool *dbconn.DBConn) []Transform {
results := make([]Transform, 0)
query := `
SELECT trf.oid,
quote_ident(ns.nspname) AS typnamespace,
quote_ident(tp.typname) AS typname,
l.lanname,
trf.trffromsql::oid,
trf.trftosql::oid
FROM pg_transform trf
JOIN pg_type tp ON trf.trftype=tp.oid
JOIN pg_namespace ns ON tp.typnamespace = ns.oid
JOIN pg_language l ON trf.trflang=l.oid;`
err := connectionPool.Select(&results, query)
gplog.FatalOnError(err)
return results
}
type Conversion struct {
Oid uint32
Schema string
Name string
ForEncoding string
ToEncoding string
ConversionFunction string
IsDefault bool `db:"condefault"`
}
func (c Conversion) GetMetadataEntry() (string, toc.MetadataEntry) {
return "predata",
toc.MetadataEntry{
Schema: c.Schema,
Name: c.Name,
ObjectType: toc.OBJ_CONVERSION,
ReferenceObject: "",
StartByte: 0,
EndByte: 0,
}
}
func (c Conversion) GetUniqueID() UniqueID {
return UniqueID{ClassID: PG_CONVERSION_OID, Oid: c.Oid}
}
func (c Conversion) FQN() string {
return utils.MakeFQN(c.Schema, c.Name)
}
func GetConversions(connectionPool *dbconn.DBConn) []Conversion {
results := make([]Conversion, 0)
query := fmt.Sprintf(`
SELECT c.oid,
quote_ident(n.nspname) AS schema,
quote_ident(c.conname) AS name,
pg_encoding_to_char(c.conforencoding) AS forencoding,
pg_encoding_to_char(c.contoencoding) AS toencoding,
quote_ident(fn.nspname) || '.' || quote_ident(p.proname) AS conversionfunction,
c.condefault
FROM pg_conversion c
JOIN pg_namespace n ON c.connamespace = n.oid
JOIN pg_proc p ON c.conproc = p.oid
JOIN pg_namespace fn ON p.pronamespace = fn.oid
WHERE %s
AND %s
ORDER BY n.nspname, c.conname`, SchemaFilterClause("n"), ExtensionFilterClause("c"))
err := connectionPool.Select(&results, query)
gplog.FatalOnError(err)
return results
}
type ForeignDataWrapper struct {
Oid uint32
Name string
Handler uint32
Validator uint32
Options string
}
func (fdw ForeignDataWrapper) GetMetadataEntry() (string, toc.MetadataEntry) {
return "predata",
toc.MetadataEntry{
Schema: "",
Name: fdw.Name,
ObjectType: toc.OBJ_FOREIGN_DATA_WRAPPER,
ReferenceObject: "",
StartByte: 0,
EndByte: 0,
}
}
func (fdw ForeignDataWrapper) GetUniqueID() UniqueID {
return UniqueID{ClassID: PG_FOREIGN_DATA_WRAPPER_OID, Oid: fdw.Oid}
}
func (fdw ForeignDataWrapper) FQN() string {
return fdw.Name
}
func GetForeignDataWrappers(connectionPool *dbconn.DBConn) []ForeignDataWrapper {
results := make([]ForeignDataWrapper, 0)
query := fmt.Sprintf(`
SELECT oid,
quote_ident(fdwname) AS name,
fdwvalidator AS validator,
fdwhandler AS handler,
array_to_string(ARRAY(
SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value)
FROM pg_options_to_table(fdwoptions) ORDER BY option_name), ', ') AS options
FROM pg_foreign_data_wrapper
WHERE oid >= %d AND %s`, FIRST_NORMAL_OBJECT_ID, ExtensionFilterClause(""))
err := connectionPool.Select(&results, query)
gplog.FatalOnError(err)
return results
}
type ForeignServer struct {
Oid uint32
Name string
Type string
Version string
ForeignDataWrapper string
Options string
}
func (fs ForeignServer) GetMetadataEntry() (string, toc.MetadataEntry) {
return "predata",
toc.MetadataEntry{
Schema: "",
Name: fs.Name,
ObjectType: toc.OBJ_FOREIGN_SERVER,
ReferenceObject: "",
StartByte: 0,
EndByte: 0,
}
}
func (fs ForeignServer) GetUniqueID() UniqueID {
return UniqueID{ClassID: PG_FOREIGN_SERVER_OID, Oid: fs.Oid}
}
func (fs ForeignServer) FQN() string {
return fs.Name
}
func GetForeignServers(connectionPool *dbconn.DBConn) []ForeignServer {
results := make([]ForeignServer, 0)
query := fmt.Sprintf(`
SELECT fs.oid,
quote_ident(fs.srvname) AS name,
coalesce(fs.srvtype, '') AS type,
coalesce(fs.srvversion, '') AS version,
quote_ident(fdw.fdwname) AS foreigndatawrapper,
array_to_string(ARRAY(
SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value)
FROM pg_options_to_table(fs.srvoptions) ORDER BY option_name), ', ') AS options
FROM pg_foreign_server fs
LEFT JOIN pg_foreign_data_wrapper fdw ON fdw.oid = srvfdw
WHERE fs.oid >= %d AND %s`, FIRST_NORMAL_OBJECT_ID, ExtensionFilterClause("fs"))
err := connectionPool.Select(&results, query)
gplog.FatalOnError(err)
return results
}
type UserMapping struct {
Oid uint32
User string
Server string
Options string
}
func (um UserMapping) GetMetadataEntry() (string, toc.MetadataEntry) {
return "predata",
toc.MetadataEntry{
Schema: "",
Name: um.FQN(),
ObjectType: toc.OBJ_USER_MAPPING,
ReferenceObject: "",
StartByte: 0,
EndByte: 0,
}
}
func (um UserMapping) GetUniqueID() UniqueID {
return UniqueID{ClassID: PG_USER_MAPPING_OID, Oid: um.Oid}
}
func (um UserMapping) FQN() string {
// User mappings don't have a unique name, so we construct an arbitrary identifier
return fmt.Sprintf("%s ON %s", um.User, um.Server)
}
func GetUserMappings(connectionPool *dbconn.DBConn) []UserMapping {
query := `
SELECT um.umid AS oid,
quote_ident(um.usename) AS user,
quote_ident(um.srvname) AS server,
array_to_string(ARRAY(
SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value)
FROM pg_options_to_table(um.umoptions) ORDER BY option_name), ', ') AS options
FROM pg_user_mappings um
WHERE um.umid NOT IN (select objid from pg_depend where deptype = 'e')
ORDER by um.usename`
results := make([]UserMapping, 0)
err := connectionPool.Select(&results, query)
gplog.FatalOnError(err)
return results
}
type StatisticExt struct {
Oid uint32
Name string
Namespace string // namespace that statistics object belongs to
Owner string
TableSchema string // schema that table covered by statistics belongs to
TableName string // table covered by statistics
Definition string
}
func (se StatisticExt) GetMetadataEntry() (string, toc.MetadataEntry) {
return "postdata",
toc.MetadataEntry{
Schema: se.Namespace,
Name: se.Name,
ObjectType: toc.OBJ_STATISTICS,
ReferenceObject: utils.MakeFQN(se.TableSchema, se.TableName),
StartByte: 0,
EndByte: 0,
}
}
func (se StatisticExt) GetUniqueID() UniqueID {
return UniqueID{ClassID: PG_STATISTIC_EXT_OID, Oid: se.Oid}
}
func (se StatisticExt) FQN() string {
return fmt.Sprintf("%s.%s", se.Namespace, se.Name)
}
func GetExtendedStatistics(connectionPool *dbconn.DBConn) []StatisticExt {
results := make([]StatisticExt, 0)
query := `
SELECT se.oid,
stxname AS name,
regexp_replace(pg_catalog.pg_get_statisticsobjdef(se.oid), '(.* FROM ).*', '\1' || quote_ident(c.relnamespace::regnamespace::text) || '.' || quote_ident(c.relname)) AS definition,
quote_ident(se.stxnamespace::regnamespace::text) AS namespace,
se.stxowner::regrole AS owner,
quote_ident(c.relnamespace::regnamespace::text) AS tableschema,
quote_ident(c.relname) AS tablename
FROM pg_catalog.pg_statistic_ext se
JOIN pg_catalog.pg_class c ON se.stxrelid = c.oid;`
err := connectionPool.Select(&results, query)
gplog.FatalOnError(err)
return results
}