blob: b71c6077ada071cb7c4dbca433e8b5c50dbe8113 [file] [log] [blame]
package integration
import (
"database/sql"
"fmt"
"github.com/greenplum-db/gp-common-go-libs/structmatcher"
"github.com/greenplum-db/gp-common-go-libs/testhelper"
"github.com/greenplum-db/gpbackup/backup"
"github.com/greenplum-db/gpbackup/options"
"github.com/greenplum-db/gpbackup/testutils"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("backup integration tests", func() {
Describe("GetFunctions", func() {
var prokindValue string
var plannerSupportValue string
var proparallelValue string
BeforeEach(func() {
testutils.SkipIfBefore5(connectionPool)
if true {
prokindValue = "f"
plannerSupportValue = "-"
proparallelValue = "u"
} else {
prokindValue = ""
plannerSupportValue = ""
proparallelValue = ""
}
})
It("returns a slice of functions", func() {
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.add(integer, integer) RETURNS integer
AS 'SELECT $1 + $2'
LANGUAGE SQL`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(integer, integer)")
testhelper.AssertQueryRuns(connectionPool, `
CREATE FUNCTION public.append(integer, integer) RETURNS SETOF record
AS 'SELECT ($1, $2)'
LANGUAGE SQL
SECURITY DEFINER
STRICT
STABLE
COST 200
ROWS 200
SET search_path = pg_temp
MODIFIES SQL DATA
`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.append(integer, integer)")
testhelper.AssertQueryRuns(connectionPool, "COMMENT ON FUNCTION public.append(integer, integer) IS 'this is a function comment'")
results := backup.GetFunctions(connectionPool)
addFunction := backup.Function{
Schema: "public", Name: "add", Kind: prokindValue, ReturnsSet: false, FunctionBody: "SELECT $1 + $2",
BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
IdentArgs: sql.NullString{String: "integer, integer", Valid: true},
ResultType: sql.NullString{String: "integer", Valid: true},
Volatility: "v", IsStrict: false, IsSecurityDefiner: false, PlannerSupport: plannerSupportValue, Config: "", Cost: 100, NumRows: 0,
DataAccess: "c", Language: "sql", ExecLocation: "a", Parallel: proparallelValue}
appendFunction := backup.Function{
Schema: "public", Name: "append", Kind: prokindValue, ReturnsSet: true, FunctionBody: "SELECT ($1, $2)",
BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
IdentArgs: sql.NullString{String: "integer, integer", Valid: true},
ResultType: sql.NullString{String: "SETOF record", Valid: true},
Volatility: "s", IsStrict: true, IsSecurityDefiner: true, PlannerSupport: plannerSupportValue, Config: `SET search_path TO 'pg_temp'`, Cost: 200,
NumRows: 200, DataAccess: "m", Language: "sql", ExecLocation: "a", Parallel: proparallelValue}
Expect(results).To(HaveLen(2))
structmatcher.ExpectStructsToMatchExcluding(&results[0], &addFunction, "Oid")
structmatcher.ExpectStructsToMatchExcluding(&results[1], &appendFunction, "Oid")
})
It("returns a slice of functions in a specific schema", func() {
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.add(integer, integer) RETURNS integer
AS 'SELECT $1 + $2'
LANGUAGE SQL`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(integer, integer)")
testhelper.AssertQueryRuns(connectionPool, "CREATE SCHEMA testschema")
defer testhelper.AssertQueryRuns(connectionPool, "DROP SCHEMA testschema")
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION testschema.add(integer, integer) RETURNS integer
AS 'SELECT $1 + $2'
LANGUAGE SQL`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION testschema.add(integer, integer)")
addFunction := backup.Function{
Schema: "testschema", Name: "add", Kind: prokindValue, ReturnsSet: false, FunctionBody: "SELECT $1 + $2",
BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
IdentArgs: sql.NullString{String: "integer, integer", Valid: true},
ResultType: sql.NullString{String: "integer", Valid: true},
Volatility: "v", IsStrict: false, IsSecurityDefiner: false, PlannerSupport: plannerSupportValue, Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
Language: "sql", ExecLocation: "a", Parallel: proparallelValue}
_ = backupCmdFlags.Set(options.INCLUDE_SCHEMA, "testschema")
results := backup.GetFunctions(connectionPool)
Expect(results).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&results[0], &addFunction, "Oid")
})
It("returns a window function", func() {
testutils.SkipIfBefore6(connectionPool)
if true {
// GPDB7 only allows set-returning functions to execute on coordinator
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.add(integer, integer) RETURNS SETOF integer
AS 'SELECT $1 + $2'
LANGUAGE SQL WINDOW`)
} else {
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.add(integer, integer) RETURNS integer
AS 'SELECT $1 + $2'
LANGUAGE SQL WINDOW`)
}
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(integer, integer)")
results := backup.GetFunctions(connectionPool)
var windowFunction backup.Function
if true {
windowFunction = backup.Function{
Schema: "public", Name: "add", ReturnsSet: true, FunctionBody: "SELECT $1 + $2",
BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
IdentArgs: sql.NullString{String: "integer, integer", Valid: true},
ResultType: sql.NullString{String: "SETOF integer", Valid: true},
Volatility: "v", IsStrict: false, IsSecurityDefiner: false, PlannerSupport: plannerSupportValue, Config: "", Cost: 100, NumRows: 1000, DataAccess: "c",
Language: "sql", Kind: "w", ExecLocation: "a", Parallel: proparallelValue, IsWindow: true}
} else {
windowFunction = backup.Function{
Schema: "public", Name: "add", ReturnsSet: false, FunctionBody: "SELECT $1 + $2",
BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
IdentArgs: sql.NullString{String: "integer, integer", Valid: true},
ResultType: sql.NullString{String: "integer", Valid: true},
Volatility: "v", IsStrict: false, IsSecurityDefiner: false,
PlannerSupport: plannerSupportValue, Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
Language: "sql", IsWindow: true, ExecLocation: "a", Parallel: proparallelValue}
}
Expect(results).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&results[0], &windowFunction, "Oid")
})
It("returns a function to execute on coordinator and all segments", func() {
testutils.SkipIfBefore6(connectionPool)
if false {
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.srf_on_coordinator(integer, integer) RETURNS integer
AS 'SELECT $1 + $2'
LANGUAGE SQL WINDOW
EXECUTE ON MASTER;`)
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.srf_on_all_segments(integer, integer) RETURNS integer
AS 'SELECT $1 + $2'
LANGUAGE SQL WINDOW
EXECUTE ON ALL SEGMENTS;`)
} else {
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.srf_on_coordinator(integer, integer) RETURNS SETOF integer
AS 'SELECT $1 + $2'
LANGUAGE SQL WINDOW
EXECUTE ON COORDINATOR;`)
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.srf_on_all_segments(integer, integer) RETURNS SETOF integer
AS 'SELECT $1 + $2'
LANGUAGE SQL WINDOW
EXECUTE ON ALL SEGMENTS;`)
}
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.srf_on_coordinator(integer, integer)")
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.srf_on_all_segments(integer, integer)")
results := backup.GetFunctions(connectionPool)
var prokindValue string
if true {
prokindValue = "w"
} else {
prokindValue = ""
}
srfOnCoordinatorFunction := backup.Function{
Schema: "public", Name: "srf_on_coordinator", Kind: prokindValue, ReturnsSet: false, FunctionBody: "SELECT $1 + $2",
BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
IdentArgs: sql.NullString{String: "integer, integer", Valid: true},
ResultType: sql.NullString{String: "integer", Valid: true},
Volatility: "v", IsStrict: false, IsSecurityDefiner: false,
PlannerSupport: plannerSupportValue, Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
Language: "sql", IsWindow: true, ExecLocation: "m", Parallel: proparallelValue}
if true {
srfOnCoordinatorFunction.ExecLocation = "c"
// GPDB7 only allows set-returning functions to execute on coordinator
srfOnCoordinatorFunction.ReturnsSet = true
srfOnCoordinatorFunction.NumRows = 1000
srfOnCoordinatorFunction.ResultType = sql.NullString{String: "SETOF integer", Valid: true}
}
srfOnAllSegmentsFunction := backup.Function{
Schema: "public", Name: "srf_on_all_segments", Kind: prokindValue, ReturnsSet: false, FunctionBody: "SELECT $1 + $2",
BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
IdentArgs: sql.NullString{String: "integer, integer", Valid: true},
ResultType: sql.NullString{String: "integer", Valid: true},
Volatility: "v", IsStrict: false, IsSecurityDefiner: false,
PlannerSupport: plannerSupportValue, Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
Language: "sql", IsWindow: true, ExecLocation: "s", Parallel: proparallelValue}
if true {
// GPDB7 only allows set-returning functions to execute on all segments
srfOnAllSegmentsFunction.ReturnsSet = true
srfOnAllSegmentsFunction.NumRows = 1000
srfOnAllSegmentsFunction.ResultType = sql.NullString{String: "SETOF integer", Valid: true}
}
Expect(results).To(HaveLen(2))
structmatcher.ExpectStructsToMatchExcluding(&results[0], &srfOnAllSegmentsFunction, "Oid")
structmatcher.ExpectStructsToMatchExcluding(&results[1], &srfOnCoordinatorFunction, "Oid")
})
It("returns a function to execute on initplan", func() {
if false {
Skip("Test only applicable to GPDB6.5 and above")
}
if true {
// GPDB7 only allows set-returning functions to execute on coordinator
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.srf_on_initplan(integer, integer) RETURNS SETOF integer
AS 'SELECT $1 + $2'
LANGUAGE SQL WINDOW
EXECUTE ON INITPLAN;`)
} else {
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.srf_on_initplan(integer, integer) RETURNS integer
AS 'SELECT $1 + $2'
LANGUAGE SQL WINDOW
EXECUTE ON INITPLAN;`)
}
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.srf_on_initplan(integer, integer)")
results := backup.GetFunctions(connectionPool)
var srfOnInitplan backup.Function
if true {
// GPDB7 only allows set-returning functions to execute on coordinator
srfOnInitplan = backup.Function{
Schema: "public", Name: "srf_on_initplan", ReturnsSet: true, FunctionBody: "SELECT $1 + $2",
BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
IdentArgs: sql.NullString{String: "integer, integer", Valid: true},
ResultType: sql.NullString{String: "SETOF integer", Valid: true},
Volatility: "v", IsStrict: false, IsSecurityDefiner: false, Config: "", Cost: 100, NumRows: 1000, DataAccess: "c",
PlannerSupport: plannerSupportValue, Language: "sql", IsWindow: true, ExecLocation: "i",
Parallel: proparallelValue, Kind: "w"}
} else {
srfOnInitplan = backup.Function{
Schema: "public", Name: "srf_on_initplan", ReturnsSet: false, FunctionBody: "SELECT $1 + $2",
BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
IdentArgs: sql.NullString{String: "integer, integer", Valid: true},
ResultType: sql.NullString{String: "integer", Valid: true},
Volatility: "v", IsStrict: false, IsSecurityDefiner: false, Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
PlannerSupport: plannerSupportValue, Language: "sql", IsWindow: true, ExecLocation: "i",
Parallel: proparallelValue}
}
Expect(results).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&results[0], &srfOnInitplan, "Oid")
})
It("returns a function with LEAKPROOF", func() {
testutils.SkipIfBefore6(connectionPool)
testhelper.AssertQueryRuns(connectionPool, `
CREATE FUNCTION public.append(integer, integer) RETURNS SETOF record
AS 'SELECT ($1, $2)'
LANGUAGE SQL
SECURITY DEFINER
STRICT
LEAKPROOF
STABLE
COST 200
ROWS 200
SET search_path = pg_temp
MODIFIES SQL DATA
`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.append(integer, integer)")
results := backup.GetFunctions(connectionPool)
appendFunction := backup.Function{
Schema: "public", Name: "append", Kind: prokindValue, ReturnsSet: true, FunctionBody: "SELECT ($1, $2)",
BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
IdentArgs: sql.NullString{String: "integer, integer", Valid: true},
ResultType: sql.NullString{String: "SETOF record", Valid: true},
Volatility: "s", IsStrict: true, IsLeakProof: true, IsSecurityDefiner: true,
PlannerSupport: plannerSupportValue, Config: `SET search_path TO 'pg_temp'`, Cost: 200,
NumRows: 200, DataAccess: "m", Language: "sql", ExecLocation: "a", Parallel: proparallelValue}
Expect(results).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&results[0], &appendFunction, "Oid")
})
It("does not return range type constructor functions", func() {
testutils.SkipIfBefore6(connectionPool)
testhelper.AssertQueryRuns(connectionPool, "CREATE TYPE public.textrange AS RANGE (SUBTYPE = pg_catalog.text)")
defer testhelper.AssertQueryRuns(connectionPool, "DROP TYPE public.textrange")
results := backup.GetFunctions(connectionPool)
Expect(results).To(HaveLen(0))
})
It("returns a function that has quotes in Config", func() {
testhelper.AssertQueryRuns(connectionPool, `
CREATE FUNCTION public.myfunc(integer) RETURNS text
LANGUAGE plpgsql NO SQL
SET work_mem TO '1MB'
AS $_$
begin
set work_mem = '2MB';
perform 1/$1;
return current_setting('work_mem');
end $_$;
`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.myfunc(integer)")
results := backup.GetFunctions(connectionPool)
appendFunction := backup.Function{
Schema: "public", Name: "myfunc", Kind: prokindValue, ReturnsSet: false, FunctionBody: `
begin
set work_mem = '2MB';
perform 1/$1;
return current_setting('work_mem');
end `,
BinaryPath: "", Arguments: sql.NullString{String: "integer", Valid: true},
IdentArgs: sql.NullString{String: "integer", Valid: true},
ResultType: sql.NullString{String: "text", Valid: true},
Volatility: "v", IsStrict: false, IsLeakProof: false, IsSecurityDefiner: false,
PlannerSupport: plannerSupportValue, Config: "SET work_mem TO '1MB'", Cost: 100,
NumRows: 0, DataAccess: "n", Language: "plpgsql", ExecLocation: "a", Parallel: proparallelValue}
Expect(results).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&results[0], &appendFunction, "Oid")
})
It("returns a function that sets a GUC with a string array value with quoted items", func() {
testhelper.AssertQueryRuns(connectionPool, `CREATE SCHEMA "abc""def"`)
testhelper.AssertQueryRuns(connectionPool, `
CREATE FUNCTION public.myfunc(integer) RETURNS text
LANGUAGE plpgsql NO SQL
SET search_path TO "$user", public, "abc""def"
AS $_$
begin
set work_mem = '2MB';
perform 1/$1;
return current_setting('work_mem');
end $_$;
`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.myfunc(integer)")
defer testhelper.AssertQueryRuns(connectionPool, `DROP SCHEMA "abc""def"`)
results := backup.GetFunctions(connectionPool)
appendFunction := backup.Function{
Schema: "public", Name: "myfunc", Kind: prokindValue, ReturnsSet: false, FunctionBody: `
begin
set work_mem = '2MB';
perform 1/$1;
return current_setting('work_mem');
end `,
BinaryPath: "", Arguments: sql.NullString{String: "integer", Valid: true},
IdentArgs: sql.NullString{String: "integer", Valid: true},
ResultType: sql.NullString{String: "text", Valid: true},
Volatility: "v", IsStrict: false, IsLeakProof: false, IsSecurityDefiner: false,
PlannerSupport: plannerSupportValue, Config: `SET search_path TO '$user', 'public', 'abc"def'`, Cost: 100,
NumRows: 0, DataAccess: "n", Language: "plpgsql", ExecLocation: "a", Parallel: proparallelValue}
Expect(results).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&results[0], &appendFunction, "Oid")
})
It("includes stored procedures in the result", func() {
testutils.SkipIfBefore7(connectionPool)
testhelper.AssertQueryRuns(connectionPool, `CREATE TABLE public.tbl (n int);`)
defer testhelper.AssertQueryRuns(connectionPool, `DROP TABLE public.tbl;`)
testhelper.AssertQueryRuns(connectionPool, `
CREATE PROCEDURE
public.insert_data(a integer, b integer) LANGUAGE SQL AS $$
INSERT INTO public.tbl VALUES (a);
INSERT INTO public.tbl VALUES (b);
$$;`)
defer testhelper.AssertQueryRuns(connectionPool, `DROP PROCEDURE public.insert_data(a integer, b integer);`)
testhelper.AssertQueryRuns(connectionPool, `
CREATE PROCEDURE
public.insert_more_data(a integer, b integer) LANGUAGE SQL AS $$
INSERT INTO public.tbl VALUES (a);
INSERT INTO public.tbl VALUES (b);
$$;`)
defer testhelper.AssertQueryRuns(connectionPool, `DROP PROCEDURE public.insert_more_data(a integer, b integer);`)
results := backup.GetFunctions(connectionPool)
firstProcedure := backup.Function{
Schema: "public", Name: "insert_data", Kind: "p", ReturnsSet: false, FunctionBody: `
INSERT INTO public.tbl VALUES (a);
INSERT INTO public.tbl VALUES (b);
`,
BinaryPath: "", Arguments: sql.NullString{String: "a integer, b integer", Valid: true},
IdentArgs: sql.NullString{String: "a integer, b integer", Valid: true},
ResultType: sql.NullString{String: "", Valid: false},
Volatility: "v", IsSecurityDefiner: false, PlannerSupport: plannerSupportValue,
Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
Language: "sql", ExecLocation: "a", Parallel: proparallelValue}
secondProcedure := backup.Function{
Schema: "public", Name: "insert_more_data", Kind: "p", ReturnsSet: false, FunctionBody: `
INSERT INTO public.tbl VALUES (a);
INSERT INTO public.tbl VALUES (b);
`,
BinaryPath: "", Arguments: sql.NullString{String: "a integer, b integer", Valid: true},
IdentArgs: sql.NullString{String: "a integer, b integer", Valid: true},
ResultType: sql.NullString{String: "", Valid: false},
Volatility: "v", IsSecurityDefiner: false, PlannerSupport: plannerSupportValue,
Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
Language: "sql", ExecLocation: "a", Parallel: proparallelValue}
Expect(results).To(HaveLen(2))
structmatcher.ExpectStructsToMatchExcluding(&results[0], &firstProcedure, "Oid")
structmatcher.ExpectStructsToMatchExcluding(&results[1], &secondProcedure, "Oid")
})
})
Describe("GetFunctions4", func() {
BeforeEach(func() {
testutils.SkipIfNot4(connectionPool)
})
It("returns a slice of functions", func() {
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.add(numeric, integer) RETURNS numeric
AS 'SELECT $1 + $2'
LANGUAGE SQL`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(numeric, integer)")
testhelper.AssertQueryRuns(connectionPool, `
CREATE FUNCTION public.append(float, integer) RETURNS SETOF record
AS 'SELECT ($1, $2)'
LANGUAGE SQL
SECURITY DEFINER
STRICT
STABLE
`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.append(float, integer)")
testhelper.AssertQueryRuns(connectionPool, "COMMENT ON FUNCTION public.append(float, integer) IS 'this is a function comment'")
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public."specChar"(t text, "precision" double precision) RETURNS double precision AS $$BEGIN RETURN precision + 1; END;$$ LANGUAGE PLPGSQL;`)
defer testhelper.AssertQueryRuns(connectionPool, `DROP FUNCTION public."specChar"(text, double precision)`)
results := backup.GetFunctions4(connectionPool)
addFunction := backup.Function{
Schema: "public", Name: "add", ReturnsSet: false, FunctionBody: "SELECT $1 + $2", BinaryPath: "",
Volatility: "v", IsStrict: false, IsSecurityDefiner: false, NumRows: 0, Language: "sql", ExecLocation: "a"}
appendFunction := backup.Function{
Schema: "public", Name: "append", ReturnsSet: true, FunctionBody: "SELECT ($1, $2)", BinaryPath: "",
Volatility: "s", IsStrict: true, IsSecurityDefiner: true, Language: "sql", ExecLocation: "a"}
specCharFunction := backup.Function{
Schema: "public", Name: `"specChar"`, ReturnsSet: false, FunctionBody: "BEGIN RETURN precision + 1; END;", BinaryPath: "",
Volatility: "v", IsStrict: false, IsSecurityDefiner: false, NumRows: 0, Language: "plpgsql", ExecLocation: "a"}
Expect(results).To(HaveLen(3))
structmatcher.ExpectStructsToMatchExcluding(&results[0], &addFunction, "Oid")
structmatcher.ExpectStructsToMatchExcluding(&results[1], &appendFunction, "Oid")
structmatcher.ExpectStructsToMatchExcluding(&results[2], &specCharFunction, "Oid")
})
It("returns a slice of functions in a specific schema", func() {
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.add(numeric, integer) RETURNS numeric
AS 'SELECT $1 + $2'
LANGUAGE SQL`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(numeric, integer)")
testhelper.AssertQueryRuns(connectionPool, "CREATE SCHEMA testschema")
defer testhelper.AssertQueryRuns(connectionPool, "DROP SCHEMA testschema")
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION testschema.add(float, integer) RETURNS float
AS 'SELECT $1 + $2'
LANGUAGE SQL`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION testschema.add(float, integer)")
addFunction := backup.Function{
Schema: "testschema", Name: "add", ReturnsSet: false, FunctionBody: "SELECT $1 + $2", BinaryPath: "",
Volatility: "v", IsStrict: false, IsSecurityDefiner: false, Language: "sql", ExecLocation: "a"}
_ = backupCmdFlags.Set(options.INCLUDE_SCHEMA, "testschema")
results := backup.GetFunctions4(connectionPool)
Expect(results).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&results[0], &addFunction, "Oid")
})
})
Describe("GetAggregates", func() {
BeforeEach(func() {
testhelper.AssertQueryRuns(connectionPool, `
CREATE FUNCTION public.mysfunc_accum(numeric, numeric, numeric)
RETURNS numeric
AS 'select $1 + $2 + $3'
LANGUAGE SQL
IMMUTABLE;
`)
testhelper.AssertQueryRuns(connectionPool, `
CREATE FUNCTION public.mypre_accum(numeric, numeric)
RETURNS numeric
AS 'select $1 + $2'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
`)
})
AfterEach(func() {
testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.mysfunc_accum(numeric, numeric, numeric)")
testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.mypre_accum(numeric, numeric)")
})
It("creates an aggregate with sortop in pg_catalog", func() {
testhelper.AssertQueryRuns(connectionPool, `
CREATE FUNCTION public.ascii_larger(prev character, curr character) RETURNS character
AS $$
begin if (prev ~>~ curr) then return prev; end if; return curr; end; $$
LANGUAGE plpgsql IMMUTABLE NO SQL;`)
transitionOid := testutils.OidFromObjectName(connectionPool, "public", "ascii_larger", backup.TYPE_FUNCTION)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.ascii_larger(character, character)")
testhelper.AssertQueryRuns(connectionPool, `
CREATE AGGREGATE public.ascii_max(character) (
SFUNC = public.ascii_larger,
STYPE = character,
SORTOP = ~>~ );`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP AGGREGATE public.ascii_max(character)")
resultAggregates := backup.GetAggregates(connectionPool)
aggregateDef := backup.Aggregate{
Schema: "public", Name: "ascii_max", Arguments: sql.NullString{String: "character", Valid: true},
IdentArgs: sql.NullString{String: "character", Valid: true},
TransitionFunction: transitionOid, FinalFunction: 0, SortOperator: "~>~", SortOperatorSchema: "pg_catalog", TransitionDataType: "character",
InitialValue: "", InitValIsNull: true, MInitValIsNull: true, IsOrdered: false,
}
if true {
aggregateDef.Kind = "n"
aggregateDef.Finalmodify = "r"
aggregateDef.Mfinalmodify = "r"
aggregateDef.Parallel = "u"
}
Expect(resultAggregates).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&resultAggregates[0], &aggregateDef, "Oid")
})
It("returns a slice of aggregates", func() {
testhelper.AssertQueryRuns(connectionPool, `
CREATE AGGREGATE public.agg_prefunc(numeric, numeric) (
SFUNC = public.mysfunc_accum,
STYPE = numeric,
PREFUNC = public.mypre_accum,
INITCOND = 0 );
`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP AGGREGATE public.agg_prefunc(numeric, numeric)")
transitionOid := testutils.OidFromObjectName(connectionPool, "public", "mysfunc_accum", backup.TYPE_FUNCTION)
prelimOid := testutils.OidFromObjectName(connectionPool, "public", "mypre_accum", backup.TYPE_FUNCTION)
result := backup.GetAggregates(connectionPool)
aggregateDef := backup.Aggregate{
Schema: "public", Name: "agg_prefunc", Arguments: sql.NullString{String: "numeric, numeric", Valid: true},
IdentArgs: sql.NullString{String: "numeric, numeric", Valid: true}, TransitionFunction: transitionOid, PreliminaryFunction: prelimOid,
FinalFunction: 0, SortOperator: "", TransitionDataType: "numeric", InitialValue: "0", MInitValIsNull: true, IsOrdered: false,
}
if true {
aggregateDef.PreliminaryFunction = 0
aggregateDef.CombineFunction = prelimOid
}
if true {
aggregateDef.Kind = "n"
aggregateDef.Finalmodify = "r"
aggregateDef.Mfinalmodify = "r"
aggregateDef.Parallel = "u"
}
Expect(result).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&result[0], &aggregateDef, "Oid")
})
It("returns a slice of aggregates in a specific schema", func() {
testhelper.AssertQueryRuns(connectionPool, `
CREATE AGGREGATE public.agg_prefunc(numeric, numeric) (
SFUNC = public.mysfunc_accum,
STYPE = numeric,
PREFUNC = public.mypre_accum,
INITCOND = 0 );
`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP AGGREGATE public.agg_prefunc(numeric, numeric)")
testhelper.AssertQueryRuns(connectionPool, "CREATE SCHEMA testschema")
defer testhelper.AssertQueryRuns(connectionPool, "DROP SCHEMA testschema")
testhelper.AssertQueryRuns(connectionPool, `
CREATE AGGREGATE testschema.agg_prefunc(numeric, numeric) (
SFUNC = public.mysfunc_accum,
STYPE = numeric,
PREFUNC = public.mypre_accum,
INITCOND = 0 );
`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP AGGREGATE testschema.agg_prefunc(numeric, numeric)")
transitionOid := testutils.OidFromObjectName(connectionPool, "public", "mysfunc_accum", backup.TYPE_FUNCTION)
prelimOid := testutils.OidFromObjectName(connectionPool, "public", "mypre_accum", backup.TYPE_FUNCTION)
aggregateDef := backup.Aggregate{
Schema: "testschema", Name: "agg_prefunc", Arguments: sql.NullString{String: "numeric, numeric", Valid: true},
IdentArgs: sql.NullString{String: "numeric, numeric", Valid: true}, TransitionFunction: transitionOid, PreliminaryFunction: prelimOid,
FinalFunction: 0, SortOperator: "", TransitionDataType: "numeric", InitialValue: "0", MInitValIsNull: true, IsOrdered: false,
}
if true {
aggregateDef.PreliminaryFunction = 0
aggregateDef.CombineFunction = prelimOid
}
if true {
aggregateDef.Kind = "n"
aggregateDef.Finalmodify = "r"
aggregateDef.Mfinalmodify = "r"
aggregateDef.Parallel = "u"
}
_ = backupCmdFlags.Set(options.INCLUDE_SCHEMA, "testschema")
result := backup.GetAggregates(connectionPool)
Expect(result).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&result[0], &aggregateDef, "Oid")
})
It("returns a slice for a hypothetical ordered-set aggregate", func() {
testutils.SkipIfBefore6(connectionPool)
testhelper.AssertQueryRuns(connectionPool, `
CREATE AGGREGATE public.agg_hypo_ord (VARIADIC "any" ORDER BY VARIADIC "any")
(
SFUNC = pg_catalog.ordered_set_transition_multi,
STYPE = internal,
FINALFUNC = pg_catalog.rank_final,
FINALFUNC_EXTRA,
HYPOTHETICAL
);`)
defer testhelper.AssertQueryRuns(connectionPool, `DROP AGGREGATE public.agg_hypo_ord(VARIADIC "any" ORDER BY VARIADIC "any")`)
transitionOid := testutils.OidFromObjectName(connectionPool, "pg_catalog", "ordered_set_transition_multi", backup.TYPE_FUNCTION)
finalOid := testutils.OidFromObjectName(connectionPool, "pg_catalog", "rank_final", backup.TYPE_FUNCTION)
result := backup.GetAggregates(connectionPool)
aggregateDef := backup.Aggregate{
Schema: "public", Name: "agg_hypo_ord", Arguments: sql.NullString{String: `VARIADIC "any" ORDER BY VARIADIC "any"`, Valid: true},
IdentArgs: sql.NullString{String: `VARIADIC "any" ORDER BY VARIADIC "any"`, Valid: true}, TransitionFunction: transitionOid,
FinalFunction: finalOid, TransitionDataType: "internal", InitValIsNull: true, MInitValIsNull: true, FinalFuncExtra: true, Hypothetical: true,
}
if true {
aggregateDef.Hypothetical = false
aggregateDef.Kind = "h"
aggregateDef.Finalmodify = "w"
aggregateDef.Mfinalmodify = "w"
aggregateDef.Parallel = "u"
}
Expect(result).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&result[0], &aggregateDef, "Oid")
})
It("returns a slice of aggregates with a combine function and transition data size", func() {
testutils.SkipIfBefore6(connectionPool)
testhelper.AssertQueryRuns(connectionPool, `
CREATE AGGREGATE public.agg_combinefunc(numeric, numeric) (
SFUNC = public.mysfunc_accum,
STYPE = numeric,
SSPACE = 1000,
COMBINEFUNC = public.mypre_accum,
INITCOND = 0 );
`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP AGGREGATE public.agg_combinefunc(numeric, numeric)")
transitionOid := testutils.OidFromObjectName(connectionPool, "public", "mysfunc_accum", backup.TYPE_FUNCTION)
combineOid := testutils.OidFromObjectName(connectionPool, "public", "mypre_accum", backup.TYPE_FUNCTION)
result := backup.GetAggregates(connectionPool)
aggregateDef := backup.Aggregate{
Schema: "public", Name: "agg_combinefunc", Arguments: sql.NullString{String: "numeric, numeric", Valid: true},
IdentArgs: sql.NullString{String: "numeric, numeric", Valid: true}, TransitionFunction: transitionOid, CombineFunction: combineOid,
FinalFunction: 0, SortOperator: "", TransitionDataType: "numeric", TransitionDataSize: 1000,
InitialValue: "0", MInitValIsNull: true, IsOrdered: false,
}
if true {
aggregateDef.Kind = "n"
aggregateDef.Finalmodify = "r"
aggregateDef.Mfinalmodify = "r"
aggregateDef.Parallel = "u"
}
Expect(result).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&result[0], &aggregateDef, "Oid")
})
It("returns a slice of aggregates with serial/deserial functions", func() {
testutils.SkipIfBefore6(connectionPool)
testhelper.AssertQueryRuns(connectionPool, `
CREATE AGGREGATE public.myavg (numeric) (
stype = internal,
sfunc = numeric_avg_accum,
finalfunc = numeric_avg,
serialfunc = numeric_avg_serialize,
deserialfunc = numeric_avg_deserialize);
`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP AGGREGATE public.myavg(numeric)")
serialOid := testutils.OidFromObjectName(connectionPool, "pg_catalog", "numeric_avg_serialize", backup.TYPE_FUNCTION)
deserialOid := testutils.OidFromObjectName(connectionPool, "pg_catalog", "numeric_avg_deserialize", backup.TYPE_FUNCTION)
result := backup.GetAggregates(connectionPool)
aggregateDef := backup.Aggregate{
Schema: "public", Name: "myavg", Arguments: sql.NullString{String: "numeric", Valid: true},
IdentArgs: sql.NullString{String: "numeric", Valid: true}, SerialFunction: serialOid, DeserialFunction: deserialOid,
FinalFunction: 0, SortOperator: "", TransitionDataType: "internal",
IsOrdered: false, InitValIsNull: true, MInitValIsNull: true,
}
if true {
aggregateDef.Kind = "n"
aggregateDef.Finalmodify = "r"
aggregateDef.Mfinalmodify = "r"
aggregateDef.Parallel = "u"
}
Expect(result).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&result[0], &aggregateDef, "Oid", "TransitionFunction", "FinalFunction")
})
It("returns a slice of aggregates with moving attributes", func() {
testutils.SkipIfBefore6(connectionPool)
testhelper.AssertQueryRuns(connectionPool, `
CREATE AGGREGATE public.moving_agg(numeric,numeric) (
SFUNC = public.mysfunc_accum,
STYPE = numeric,
MSFUNC = public.mysfunc_accum,
MINVFUNC = public.mysfunc_accum,
MSTYPE = numeric,
MSSPACE = 100,
MFINALFUNC = public.mysfunc_accum,
MFINALFUNC_EXTRA,
MINITCOND = 0
);
`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP AGGREGATE public.moving_agg(numeric, numeric)")
sfuncOid := testutils.OidFromObjectName(connectionPool, "public", "mysfunc_accum", backup.TYPE_FUNCTION)
result := backup.GetAggregates(connectionPool)
aggregateDef := backup.Aggregate{
Schema: "public", Name: "moving_agg", Arguments: sql.NullString{String: "numeric, numeric", Valid: true},
IdentArgs: sql.NullString{String: "numeric, numeric", Valid: true}, TransitionFunction: sfuncOid, TransitionDataType: "numeric",
InitValIsNull: true, MTransitionFunction: sfuncOid, MInverseTransitionFunction: sfuncOid,
MTransitionDataType: "numeric", MTransitionDataSize: 100, MFinalFunction: sfuncOid,
MFinalFuncExtra: true, MInitialValue: "0", MInitValIsNull: false,
}
if true {
aggregateDef.Kind = "n"
aggregateDef.Finalmodify = "r"
aggregateDef.Mfinalmodify = "r"
aggregateDef.Parallel = "u"
}
Expect(result).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&result[0], &aggregateDef, "Oid")
})
})
Describe("GetFunctionOidToInfoMap", func() {
It("returns map containing function information", func() {
result := backup.GetFunctionOidToInfoMap(connectionPool)
initialLength := len(result)
testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION public.add(integer, integer) RETURNS integer
AS 'SELECT $1 + $2'
LANGUAGE SQL`)
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(integer, integer)")
result = backup.GetFunctionOidToInfoMap(connectionPool)
oid := testutils.OidFromObjectName(connectionPool, "public", "add", backup.TYPE_FUNCTION)
Expect(result).To(HaveLen(initialLength + 1))
Expect(result[oid].QualifiedName).To(Equal("public.add"))
Expect(result[oid].Arguments.String).To(Equal("integer, integer"))
Expect(result[oid].IsInternal).To(BeFalse())
})
It("returns a map containing an internal function", func() {
result := backup.GetFunctionOidToInfoMap(connectionPool)
oid := testutils.OidFromObjectName(connectionPool, "pg_catalog", "boolin", backup.TYPE_FUNCTION)
Expect(result[oid].QualifiedName).To(Equal("pg_catalog.boolin"))
Expect(result[oid].IsInternal).To(BeTrue())
})
})
Describe("GetCasts", func() {
It("returns a slice for a basic cast with a function in 4.3", func() {
testutils.SkipIfNot4(connectionPool)
testhelper.AssertQueryRuns(connectionPool, "CREATE FUNCTION public.casttotext(bool) RETURNS pg_catalog.text STRICT IMMUTABLE LANGUAGE PLPGSQL AS $$ BEGIN IF $1 IS TRUE THEN RETURN 'true'; ELSE RETURN 'false'; END IF; END; $$;")
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.casttotext(bool)")
testhelper.AssertQueryRuns(connectionPool, "CREATE CAST (bool AS text) WITH FUNCTION public.casttotext(bool) AS ASSIGNMENT")
defer testhelper.AssertQueryRuns(connectionPool, "DROP CAST (bool AS text)")
results := backup.GetCasts(connectionPool)
castDef := backup.Cast{Oid: 0, SourceTypeFQN: "pg_catalog.bool", TargetTypeFQN: "pg_catalog.text", FunctionSchema: "public", FunctionName: "casttotext", FunctionArgs: "boolean", CastContext: "a", CastMethod: "f"}
Expect(results).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&castDef, &results[0], "Oid", "FunctionOid")
})
It("returns a slice for a basic cast with a function in 5 and 6", func() {
testutils.SkipIfBefore5(connectionPool)
testhelper.AssertQueryRuns(connectionPool, "CREATE FUNCTION public.casttoint(text) RETURNS integer STRICT IMMUTABLE LANGUAGE SQL AS 'SELECT cast($1 as integer);'")
defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.casttoint(text)")
testhelper.AssertQueryRuns(connectionPool, "CREATE CAST (text AS integer) WITH FUNCTION public.casttoint(text) AS ASSIGNMENT")
defer testhelper.AssertQueryRuns(connectionPool, "DROP CAST (text AS int4)")
results := backup.GetCasts(connectionPool)
castDef := backup.Cast{Oid: 0, SourceTypeFQN: "pg_catalog.text", TargetTypeFQN: "pg_catalog.int4", FunctionSchema: "public", FunctionName: "casttoint", FunctionArgs: "text", CastContext: "a", CastMethod: "f"}
Expect(results).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&castDef, &results[0], "Oid")
})
It("returns a slice for a basic cast without a function", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE FUNCTION public.cast_in(cstring) RETURNS public.casttesttype AS $$textin$$ LANGUAGE internal STRICT NO SQL")
testhelper.AssertQueryRuns(connectionPool, "CREATE FUNCTION public.cast_out(public.casttesttype) RETURNS cstring AS $$textout$$ LANGUAGE internal STRICT NO SQL")
testhelper.AssertQueryRuns(connectionPool, "CREATE TYPE public.casttesttype (INTERNALLENGTH = variable, INPUT = public.cast_in, OUTPUT = public.cast_out)")
defer testhelper.AssertQueryRuns(connectionPool, "DROP TYPE public.casttesttype CASCADE")
testhelper.AssertQueryRuns(connectionPool, "CREATE CAST (text AS public.casttesttype) WITHOUT FUNCTION AS IMPLICIT")
defer testhelper.AssertQueryRuns(connectionPool, "DROP CAST (text AS public.casttesttype)")
results := backup.GetCasts(connectionPool)
castDef := backup.Cast{Oid: 0, SourceTypeFQN: "pg_catalog.text", TargetTypeFQN: "public.casttesttype", FunctionSchema: "", FunctionName: "", FunctionArgs: "", CastContext: "i", CastMethod: "b"}
Expect(results).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&castDef, &results[0], "Oid")
})
It("returns a slice of casts with the source and target types in a different schema", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE SCHEMA testschema1")
defer testhelper.AssertQueryRuns(connectionPool, "DROP SCHEMA testschema1")
testhelper.AssertQueryRuns(connectionPool, "CREATE FUNCTION cast_in(cstring) RETURNS testschema1.casttesttype AS $$textin$$ LANGUAGE internal STRICT NO SQL")
testhelper.AssertQueryRuns(connectionPool, "CREATE FUNCTION cast_out(testschema1.casttesttype) RETURNS cstring AS $$textout$$ LANGUAGE internal STRICT NO SQL")
testhelper.AssertQueryRuns(connectionPool, "CREATE TYPE testschema1.casttesttype (INTERNALLENGTH = variable, INPUT = cast_in, OUTPUT = cast_out)")
defer testhelper.AssertQueryRuns(connectionPool, "DROP TYPE testschema1.casttesttype CASCADE")
testhelper.AssertQueryRuns(connectionPool, "CREATE CAST (text AS testschema1.casttesttype) WITHOUT FUNCTION AS IMPLICIT")
defer testhelper.AssertQueryRuns(connectionPool, "DROP CAST (text AS testschema1.casttesttype)")
testhelper.AssertQueryRuns(connectionPool, "CREATE CAST (testschema1.casttesttype AS text) WITHOUT FUNCTION AS IMPLICIT")
defer testhelper.AssertQueryRuns(connectionPool, "DROP CAST (testschema1.casttesttype AS text)")
results := backup.GetCasts(connectionPool)
castDefTarget := backup.Cast{Oid: 0, SourceTypeFQN: "pg_catalog.text", TargetTypeFQN: "testschema1.casttesttype", FunctionSchema: "", FunctionName: "", FunctionArgs: "", CastContext: "i", CastMethod: "b"}
castDefSource := backup.Cast{Oid: 0, SourceTypeFQN: "testschema1.casttesttype", TargetTypeFQN: "pg_catalog.text", FunctionSchema: "", FunctionName: "", FunctionArgs: "", CastContext: "i", CastMethod: "b"}
Expect(results).To(HaveLen(2))
structmatcher.ExpectStructsToMatchExcluding(&castDefTarget, &results[0], "Oid")
structmatcher.ExpectStructsToMatchExcluding(&castDefSource, &results[1], "Oid")
})
It("returns a slice for an inout cast", func() {
testutils.SkipIfBefore6(connectionPool)
testhelper.AssertQueryRuns(connectionPool, "CREATE TYPE public.custom_numeric AS (i numeric)")
defer testhelper.AssertQueryRuns(connectionPool, "DROP TYPE public.custom_numeric")
testhelper.AssertQueryRuns(connectionPool, "CREATE CAST (varchar AS public.custom_numeric) WITH INOUT")
defer testhelper.AssertQueryRuns(connectionPool, "DROP CAST (varchar AS public.custom_numeric)")
results := backup.GetCasts(connectionPool)
castDef := backup.Cast{Oid: 0, SourceTypeFQN: `pg_catalog."varchar"`, TargetTypeFQN: "public.custom_numeric", FunctionSchema: "", FunctionName: "", FunctionArgs: "", CastContext: "e", CastMethod: "i"}
Expect(results).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&castDef, &results[0], "Oid")
})
})
Describe("GetExtensions", func() {
It("returns a slice of extension", func() {
testutils.SkipIfBefore5(connectionPool)
testhelper.AssertQueryRuns(connectionPool, "CREATE EXTENSION plperl")
defer testhelper.AssertQueryRuns(connectionPool, "DROP EXTENSION plperl")
results := backup.GetExtensions(connectionPool)
Expect(results).To(HaveLen(1))
plperlDef := backup.Extension{Oid: 0, Name: "plperl", Schema: "pg_catalog"}
structmatcher.ExpectStructsToMatchExcluding(&plperlDef, &results[0], "Oid")
})
})
Describe("GetProceduralLanguages", func() {
It("returns a slice of procedural languages", func() {
plpythonString := "plpython"
if true {
plpythonString = "plpython3"
}
testhelper.AssertQueryRuns(connectionPool, fmt.Sprintf("CREATE LANGUAGE %su", plpythonString))
defer testhelper.AssertQueryRuns(connectionPool, fmt.Sprintf("DROP LANGUAGE %su", plpythonString))
pythonHandlerOid := testutils.OidFromObjectName(connectionPool, "pg_catalog", fmt.Sprintf("%s_call_handler", plpythonString), backup.TYPE_FUNCTION)
expectedPlpythonInfo := backup.ProceduralLanguage{Oid: 1, Name: fmt.Sprintf("%su", plpythonString), Owner: "testrole", IsPl: true, PlTrusted: false, Handler: pythonHandlerOid, Inline: 0, Validator: 0}
if true {
pythonInlineOid := testutils.OidFromObjectName(connectionPool, "pg_catalog", fmt.Sprintf("%s_inline_handler", plpythonString), backup.TYPE_FUNCTION)
expectedPlpythonInfo.Inline = pythonInlineOid
}
if true {
expectedPlpythonInfo.Validator = testutils.OidFromObjectName(connectionPool, "pg_catalog", fmt.Sprintf("%s_validator", plpythonString), backup.TYPE_FUNCTION)
}
resultProcLangs := backup.GetProceduralLanguages(connectionPool)
Expect(resultProcLangs).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&expectedPlpythonInfo, &resultProcLangs[0], "Oid", "Owner")
})
})
Describe("GetConversions", func() {
It("returns a slice of conversions", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE CONVERSION public.testconv FOR 'LATIN1' TO 'MULE_INTERNAL' FROM latin1_to_mic")
defer testhelper.AssertQueryRuns(connectionPool, "DROP CONVERSION public.testconv")
expectedConversion := backup.Conversion{Oid: 0, Schema: "public", Name: "testconv", ForEncoding: "LATIN1", ToEncoding: "MULE_INTERNAL", ConversionFunction: "pg_catalog.latin1_to_mic", IsDefault: false}
resultConversions := backup.GetConversions(connectionPool)
Expect(resultConversions).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&expectedConversion, &resultConversions[0], "Oid")
})
It("returns a slice of conversions in a specific schema", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE CONVERSION public.testconv FOR 'LATIN1' TO 'MULE_INTERNAL' FROM latin1_to_mic")
defer testhelper.AssertQueryRuns(connectionPool, "DROP CONVERSION public.testconv")
testhelper.AssertQueryRuns(connectionPool, "CREATE SCHEMA testschema")
defer testhelper.AssertQueryRuns(connectionPool, "DROP SCHEMA testschema")
testhelper.AssertQueryRuns(connectionPool, "CREATE CONVERSION testschema.testconv FOR 'LATIN1' TO 'MULE_INTERNAL' FROM latin1_to_mic")
defer testhelper.AssertQueryRuns(connectionPool, "DROP CONVERSION testschema.testconv")
expectedConversion := backup.Conversion{Oid: 0, Schema: "testschema", Name: "testconv", ForEncoding: "LATIN1", ToEncoding: "MULE_INTERNAL", ConversionFunction: "pg_catalog.latin1_to_mic", IsDefault: false}
_ = backupCmdFlags.Set(options.INCLUDE_SCHEMA, "testschema")
resultConversions := backup.GetConversions(connectionPool)
Expect(resultConversions).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&expectedConversion, &resultConversions[0], "Oid")
})
})
Describe("GetTransforms", func() {
BeforeEach(func() {
testutils.SkipIfBefore7(connectionPool)
})
It("returns a slice of transfroms", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE TRANSFORM FOR pg_catalog.int4 LANGUAGE c (FROM SQL WITH FUNCTION numeric_support(internal), TO SQL WITH FUNCTION int4recv(internal));")
defer testhelper.AssertQueryRuns(connectionPool, "DROP TRANSFORM FOR int4 LANGUAGE c")
fromSQLFuncOid := testutils.OidFromObjectName(connectionPool, "pg_catalog", "numeric_support", backup.TYPE_FUNCTION)
toSQLFuncOid := testutils.OidFromObjectName(connectionPool, "pg_catalog", "int4recv", backup.TYPE_FUNCTION)
expectedTransforms := backup.Transform{TypeNamespace: "pg_catalog", TypeName: "int4", LanguageName: "c", FromSQLFunc: fromSQLFuncOid, ToSQLFunc: toSQLFuncOid}
resultTransforms := backup.GetTransforms(connectionPool)
Expect(resultTransforms).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&expectedTransforms, &resultTransforms[0], "Oid")
})
})
Describe("GetForeignDataWrappers", func() {
BeforeEach(func() {
testutils.SkipIfBefore6(connectionPool)
})
It("returns a slice of foreign data wrappers", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE FOREIGN DATA WRAPPER foreigndatawrapper")
defer testhelper.AssertQueryRuns(connectionPool, "DROP FOREIGN DATA WRAPPER foreigndatawrapper")
expectedForeignDataWrapper := backup.ForeignDataWrapper{Oid: 0, Name: "foreigndatawrapper"}
resultForeignDataWrapper := backup.GetForeignDataWrappers(connectionPool)
Expect(resultForeignDataWrapper).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&expectedForeignDataWrapper, &resultForeignDataWrapper[0], "Oid")
})
It("returns a slice of foreign data wrappers with a validator", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE FOREIGN DATA WRAPPER foreigndatawrapper VALIDATOR postgresql_fdw_validator")
defer testhelper.AssertQueryRuns(connectionPool, "DROP FOREIGN DATA WRAPPER foreigndatawrapper")
validatorOid := testutils.OidFromObjectName(connectionPool, "pg_catalog", "postgresql_fdw_validator", backup.TYPE_FUNCTION)
expectedForeignDataWrapper := backup.ForeignDataWrapper{Oid: 0, Name: "foreigndatawrapper", Validator: validatorOid}
resultForeignDataWrapper := backup.GetForeignDataWrappers(connectionPool)
Expect(resultForeignDataWrapper).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&expectedForeignDataWrapper, &resultForeignDataWrapper[0], "Oid")
})
It("returns a slice of foreign data wrappers with options", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE FOREIGN DATA WRAPPER foreigndatawrapper OPTIONS (dbname 'testdb', debug 'true')")
defer testhelper.AssertQueryRuns(connectionPool, "DROP FOREIGN DATA WRAPPER foreigndatawrapper")
expectedForeignDataWrapper := backup.ForeignDataWrapper{Oid: 0, Name: "foreigndatawrapper", Options: "dbname 'testdb', debug 'true'"}
resultForeignDataWrappers := backup.GetForeignDataWrappers(connectionPool)
Expect(resultForeignDataWrappers).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&expectedForeignDataWrapper, &resultForeignDataWrappers[0], "Oid")
})
})
Describe("GetForeignServers", func() {
BeforeEach(func() {
testutils.SkipIfBefore6(connectionPool)
testhelper.AssertQueryRuns(connectionPool, "CREATE FOREIGN DATA WRAPPER foreigndatawrapper")
})
AfterEach(func() {
testhelper.AssertQueryRuns(connectionPool, "DROP FOREIGN DATA WRAPPER foreigndatawrapper CASCADE")
})
It("returns a slice of foreign servers", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE SERVER foreignserver FOREIGN DATA WRAPPER foreigndatawrapper")
expectedServer := backup.ForeignServer{Oid: 1, Name: "foreignserver", ForeignDataWrapper: "foreigndatawrapper"}
resultServers := backup.GetForeignServers(connectionPool)
Expect(resultServers).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&expectedServer, &resultServers[0], "Oid")
})
It("returns a slice of foreign servers with a type and version", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE SERVER foreignserver TYPE 'mytype' VERSION 'myversion' FOREIGN DATA WRAPPER foreigndatawrapper")
expectedServer := backup.ForeignServer{Oid: 1, Name: "foreignserver", Type: "mytype", Version: "myversion", ForeignDataWrapper: "foreigndatawrapper"}
resultServers := backup.GetForeignServers(connectionPool)
Expect(resultServers).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&expectedServer, &resultServers[0], "Oid")
})
It("returns a slice of foreign servers with options", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE SERVER foreignserver FOREIGN DATA WRAPPER foreigndatawrapper OPTIONS (dbname 'testdb', host 'localhost')")
expectedServer := backup.ForeignServer{Oid: 1, Name: "foreignserver", ForeignDataWrapper: "foreigndatawrapper", Options: "dbname 'testdb', host 'localhost'"}
resultServers := backup.GetForeignServers(connectionPool)
Expect(resultServers).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&expectedServer, &resultServers[0], "Oid")
})
})
Describe("GetUserMappings", func() {
BeforeEach(func() {
testutils.SkipIfBefore6(connectionPool)
testhelper.AssertQueryRuns(connectionPool, "CREATE FOREIGN DATA WRAPPER foreigndatawrapper")
testhelper.AssertQueryRuns(connectionPool, "CREATE SERVER foreignserver FOREIGN DATA WRAPPER foreigndatawrapper")
})
AfterEach(func() {
testhelper.AssertQueryRuns(connectionPool, "DROP FOREIGN DATA WRAPPER foreigndatawrapper CASCADE")
})
It("returns a slice of user mappings", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE USER MAPPING FOR testrole SERVER foreignserver")
expectedMapping := backup.UserMapping{Oid: 1, User: "testrole", Server: "foreignserver"}
resultMappings := backup.GetUserMappings(connectionPool)
Expect(resultMappings).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&expectedMapping, &resultMappings[0], "Oid")
})
It("returns a slice of user mappings with options", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE USER MAPPING FOR public SERVER foreignserver OPTIONS (dbname 'testdb', host 'localhost')")
expectedMapping := backup.UserMapping{Oid: 1, User: "public", Server: "foreignserver", Options: "dbname 'testdb', host 'localhost'"}
resultMappings := backup.GetUserMappings(connectionPool)
Expect(resultMappings).To(HaveLen(1))
structmatcher.ExpectStructsToMatchExcluding(&expectedMapping, &resultMappings[0], "Oid")
})
It("returns a slice of user mappings in sorted order", func() {
testhelper.AssertQueryRuns(connectionPool, "CREATE USER MAPPING FOR testrole SERVER foreignserver")
testhelper.AssertQueryRuns(connectionPool, "CREATE USER MAPPING FOR anothertestrole SERVER foreignserver")
expectedMapping := []backup.UserMapping{
{Oid: 1, User: "anothertestrole", Server: "foreignserver"},
{Oid: 1, User: "testrole", Server: "foreignserver"},
}
resultMappings := backup.GetUserMappings(connectionPool)
Expect(resultMappings).To(HaveLen(2))
for idx := range expectedMapping {
structmatcher.ExpectStructsToMatchExcluding(&expectedMapping[idx], &resultMappings[idx], "Oid")
}
})
})
})