github.com/tuhaihe/gpbackup@v1.0.3/integration/predata_functions_create_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"database/sql"
     5  	"fmt"
     6  
     7  	"github.com/tuhaihe/gp-common-go-libs/structmatcher"
     8  	"github.com/tuhaihe/gp-common-go-libs/testhelper"
     9  	"github.com/tuhaihe/gpbackup/backup"
    10  	"github.com/tuhaihe/gpbackup/testutils"
    11  
    12  	. "github.com/onsi/ginkgo/v2"
    13  	. "github.com/onsi/gomega"
    14  )
    15  
    16  var _ = Describe("backup integration create statement tests", func() {
    17  	BeforeEach(func() {
    18  		tocfile, backupfile = testutils.InitializeTestTOC(buffer, "predata")
    19  	})
    20  	Describe("PrintCreateFunctionStatement", func() {
    21  		Context("Tests for GPDB 4.3", func() {
    22  			BeforeEach(func() {
    23  				testutils.SkipIfNot4(connectionPool)
    24  			})
    25  			funcMetadata := backup.ObjectMetadata{}
    26  			It("creates a function with a simple return type", func() {
    27  				addFunction := backup.Function{
    28  					Schema: "public", Name: "add", ReturnsSet: false, FunctionBody: "SELECT $1 + $2",
    29  					BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
    30  					IdentArgs:  sql.NullString{String: "integer, integer", Valid: true},
    31  					ResultType: sql.NullString{String: "integer", Valid: true},
    32  					Volatility: "v", IsStrict: false, IsSecurityDefiner: false, Config: "", NumRows: 0, Language: "sql", ExecLocation: "a",
    33  				}
    34  
    35  				metadata := testutils.DefaultMetadata("FUNCTION", true, true, true, false)
    36  				backup.PrintCreateFunctionStatement(backupfile, tocfile, addFunction, metadata)
    37  
    38  				testhelper.AssertQueryRuns(connectionPool, buffer.String())
    39  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(integer, integer)")
    40  
    41  				resultFunctions := backup.GetFunctionsAllVersions(connectionPool)
    42  
    43  				Expect(resultFunctions).To(HaveLen(1))
    44  				structmatcher.ExpectStructsToMatchExcluding(&addFunction, &resultFunctions[0], "Oid")
    45  			})
    46  			It("creates a function that returns a set", func() {
    47  				appendFunction := backup.Function{
    48  					Schema: "public", Name: "append", ReturnsSet: true, FunctionBody: "SELECT ($1, $2)",
    49  					BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
    50  					IdentArgs:  sql.NullString{String: "integer, integer", Valid: true},
    51  					ResultType: sql.NullString{String: "SETOF record", Valid: true},
    52  					Volatility: "s", IsStrict: true, IsSecurityDefiner: true, Language: "sql", ExecLocation: "a",
    53  				}
    54  
    55  				backup.PrintCreateFunctionStatement(backupfile, tocfile, appendFunction, funcMetadata)
    56  
    57  				testhelper.AssertQueryRuns(connectionPool, buffer.String())
    58  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.append(integer, integer)")
    59  
    60  				resultFunctions := backup.GetFunctionsAllVersions(connectionPool)
    61  
    62  				Expect(resultFunctions).To(HaveLen(1))
    63  				structmatcher.ExpectStructsToMatchExcluding(&appendFunction, &resultFunctions[0], "Oid")
    64  			})
    65  			It("creates a function that returns a table", func() {
    66  				dupFunction := backup.Function{
    67  					Schema: "public", Name: "dup", ReturnsSet: true, FunctionBody: "SELECT $1, CAST($1 AS text) || ' is text'",
    68  					BinaryPath: "", Arguments: sql.NullString{String: "integer", Valid: true},
    69  					IdentArgs:  sql.NullString{String: "integer", Valid: true},
    70  					ResultType: sql.NullString{String: "TABLE(f1 integer, f2 text)", Valid: true},
    71  					Volatility: "v", IsStrict: false, IsSecurityDefiner: false, Language: "sql", ExecLocation: "a",
    72  				}
    73  
    74  				backup.PrintCreateFunctionStatement(backupfile, tocfile, dupFunction, funcMetadata)
    75  
    76  				testhelper.AssertQueryRuns(connectionPool, buffer.String())
    77  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.dup(integer)")
    78  
    79  				resultFunctions := backup.GetFunctionsAllVersions(connectionPool)
    80  
    81  				Expect(resultFunctions).To(HaveLen(1))
    82  				structmatcher.ExpectStructsToMatchExcluding(&dupFunction, &resultFunctions[0], "Oid")
    83  			})
    84  		})
    85  		Context("Tests for GPDB 5 and above", func() {
    86  			BeforeEach(func() {
    87  				testutils.SkipIfBefore5(connectionPool)
    88  			})
    89  			funcMetadata := backup.ObjectMetadata{}
    90  			It("creates a function with a simple return type", func() {
    91  				addFunction := backup.Function{
    92  					Schema: "public", Name: "add", ReturnsSet: false, FunctionBody: "SELECT $1 + $2",
    93  					BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
    94  					IdentArgs:  sql.NullString{String: "integer, integer", Valid: true},
    95  					ResultType: sql.NullString{String: "integer", Valid: true},
    96  					Volatility: "v", IsStrict: false, IsSecurityDefiner: false, Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
    97  					Language: "sql", ExecLocation: "a",
    98  				}
    99  				if true {
   100  					addFunction.PlannerSupport = "-"
   101  					addFunction.Kind = "f"
   102  					addFunction.Parallel = "u"
   103  				}
   104  
   105  				metadata := testutils.DefaultMetadata("FUNCTION", true, true, true, includeSecurityLabels)
   106  				backup.PrintCreateFunctionStatement(backupfile, tocfile, addFunction, metadata)
   107  
   108  				testhelper.AssertQueryRuns(connectionPool, buffer.String())
   109  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(integer, integer)")
   110  
   111  				resultFunctions := backup.GetFunctionsAllVersions(connectionPool)
   112  
   113  				Expect(resultFunctions).To(HaveLen(1))
   114  
   115  				structmatcher.ExpectStructsToMatchExcluding(&addFunction, &resultFunctions[0], "Oid")
   116  			})
   117  			It("creates a function that returns a set", func() {
   118  				appendFunction := backup.Function{
   119  					Schema: "public", Name: "append", ReturnsSet: true, FunctionBody: "SELECT ($1, $2)",
   120  					BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
   121  					IdentArgs:  sql.NullString{String: "integer, integer", Valid: true},
   122  					ResultType: sql.NullString{String: "SETOF record", Valid: true},
   123  					Volatility: "s", IsStrict: true, IsSecurityDefiner: true, Config: "SET search_path TO 'pg_temp'", Cost: 200,
   124  					NumRows: 200, DataAccess: "m", Language: "sql", ExecLocation: "a",
   125  				}
   126  				if true {
   127  					appendFunction.PlannerSupport = "-"
   128  					appendFunction.Kind = "f"
   129  					appendFunction.Parallel = "u"
   130  				}
   131  
   132  				backup.PrintCreateFunctionStatement(backupfile, tocfile, appendFunction, funcMetadata)
   133  
   134  				testhelper.AssertQueryRuns(connectionPool, buffer.String())
   135  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.append(integer, integer)")
   136  
   137  				resultFunctions := backup.GetFunctionsAllVersions(connectionPool)
   138  
   139  				Expect(resultFunctions).To(HaveLen(1))
   140  
   141  				structmatcher.ExpectStructsToMatchExcluding(&appendFunction, &resultFunctions[0], "Oid")
   142  			})
   143  			It("creates a function that returns a table", func() {
   144  				dupFunction := backup.Function{
   145  					Schema: "public", Name: "dup", ReturnsSet: true, FunctionBody: "SELECT $1, CAST($1 AS text) || ' is text'",
   146  					BinaryPath: "", Arguments: sql.NullString{String: "integer", Valid: true},
   147  					IdentArgs:  sql.NullString{String: "integer", Valid: true},
   148  					ResultType: sql.NullString{String: "TABLE(f1 integer, f2 text)", Valid: true},
   149  					Volatility: "v", IsStrict: false, IsSecurityDefiner: false, Config: "", Cost: 100, NumRows: 1000, DataAccess: "c",
   150  					Language: "sql", ExecLocation: "a",
   151  				}
   152  				if true {
   153  					dupFunction.PlannerSupport = "-"
   154  					dupFunction.Kind = "f"
   155  					dupFunction.Parallel = "u"
   156  				}
   157  
   158  				backup.PrintCreateFunctionStatement(backupfile, tocfile, dupFunction, funcMetadata)
   159  
   160  				testhelper.AssertQueryRuns(connectionPool, buffer.String())
   161  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.dup(integer)")
   162  
   163  				resultFunctions := backup.GetFunctionsAllVersions(connectionPool)
   164  
   165  				Expect(resultFunctions).To(HaveLen(1))
   166  				structmatcher.ExpectStructsToMatchExcluding(&dupFunction, &resultFunctions[0], "Oid")
   167  			})
   168  		})
   169  		Context("Tests for GPDB 6", func() {
   170  			BeforeEach(func() {
   171  				testutils.SkipIfBefore6(connectionPool)
   172  			})
   173  			funcMetadata := backup.ObjectMetadata{}
   174  			It("creates a window function to execute on coordinator", func() {
   175  				windowFunction := backup.Function{
   176  					Schema: "public", Name: "add", ReturnsSet: false, FunctionBody: "SELECT $1 + $2",
   177  					BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
   178  					IdentArgs:  sql.NullString{String: "integer, integer", Valid: true},
   179  					ResultType: sql.NullString{String: "integer", Valid: true},
   180  					Volatility: "v", IsStrict: false, IsSecurityDefiner: false, Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
   181  					Language: "sql", ExecLocation: "m", IsWindow: true}
   182  				if true {
   183  					windowFunction.PlannerSupport = "-"
   184  					windowFunction.Kind = "w"
   185  					windowFunction.Parallel = "u"
   186  					windowFunction.ExecLocation = "c"
   187  
   188  					// GPDB7 only allows set-returning functions to execute on coordinator
   189  					windowFunction.ReturnsSet = true
   190  					windowFunction.NumRows = 1000
   191  					windowFunction.ResultType = sql.NullString{String: "SETOF integer", Valid: true}
   192  				}
   193  
   194  				backup.PrintCreateFunctionStatement(backupfile, tocfile, windowFunction, funcMetadata)
   195  
   196  				testhelper.AssertQueryRuns(connectionPool, buffer.String())
   197  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(integer, integer)")
   198  
   199  				resultFunctions := backup.GetFunctionsAllVersions(connectionPool)
   200  
   201  				Expect(resultFunctions).To(HaveLen(1))
   202  				structmatcher.ExpectStructsToMatchExcluding(&windowFunction, &resultFunctions[0], "Oid")
   203  			})
   204  			It("creates a function to execute on segments", func() {
   205  				segmentFunction := backup.Function{
   206  					Schema: "public", Name: "add", ReturnsSet: false, FunctionBody: "SELECT $1 + $2",
   207  					BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
   208  					IdentArgs:  sql.NullString{String: "integer, integer", Valid: true},
   209  					ResultType: sql.NullString{String: "integer", Valid: true},
   210  					Volatility: "v", IsStrict: false, IsSecurityDefiner: false, Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
   211  					Language: "sql", IsWindow: false, ExecLocation: "s",
   212  				}
   213  				if true {
   214  					segmentFunction.PlannerSupport = "-"
   215  					segmentFunction.Kind = "f"
   216  					segmentFunction.Parallel = "u"
   217  
   218  					// GPDB7 only allows set-returning functions to execute on coordinator
   219  					segmentFunction.ReturnsSet = true
   220  					segmentFunction.NumRows = 1000
   221  					segmentFunction.ResultType = sql.NullString{String: "SETOF integer", Valid: true}
   222  				}
   223  
   224  				backup.PrintCreateFunctionStatement(backupfile, tocfile, segmentFunction, funcMetadata)
   225  
   226  				testhelper.AssertQueryRuns(connectionPool, buffer.String())
   227  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(integer, integer)")
   228  
   229  				resultFunctions := backup.GetFunctionsAllVersions(connectionPool)
   230  
   231  				Expect(resultFunctions).To(HaveLen(1))
   232  				structmatcher.ExpectStructsToMatchExcluding(&segmentFunction, &resultFunctions[0], "Oid")
   233  			})
   234  			It("creates a function with LEAKPROOF", func() {
   235  				leakProofFunction := backup.Function{
   236  					Schema: "public", Name: "add", ReturnsSet: false, FunctionBody: "SELECT $1 + $2",
   237  					BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
   238  					IdentArgs:  sql.NullString{String: "integer, integer", Valid: true},
   239  					ResultType: sql.NullString{String: "integer", Valid: true},
   240  					Volatility: "v", IsStrict: false, IsLeakProof: true, IsSecurityDefiner: false, Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
   241  					Language: "sql", IsWindow: false, ExecLocation: "a",
   242  				}
   243  				if true {
   244  					leakProofFunction.PlannerSupport = "-"
   245  					leakProofFunction.Kind = "f"
   246  					leakProofFunction.Parallel = "u"
   247  				}
   248  
   249  				backup.PrintCreateFunctionStatement(backupfile, tocfile, leakProofFunction, funcMetadata)
   250  
   251  				testhelper.AssertQueryRuns(connectionPool, buffer.String())
   252  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(integer, integer)")
   253  
   254  				resultFunctions := backup.GetFunctionsAllVersions(connectionPool)
   255  
   256  				Expect(resultFunctions).To(HaveLen(1))
   257  				structmatcher.ExpectStructsToMatchExcluding(&leakProofFunction, &resultFunctions[0], "Oid")
   258  			})
   259  		})
   260  		Context("Tests for GPDB 7", func() {
   261  			BeforeEach(func() {
   262  				testutils.SkipIfBefore7(connectionPool)
   263  			})
   264  			funcMetadata := backup.ObjectMetadata{}
   265  			It("creates a function with PARALLEL RESTRICTED", func() {
   266  				ParallelFunction := backup.Function{
   267  					Schema: "public", Name: "add", ReturnsSet: false, FunctionBody: "SELECT $1 + $2",
   268  					BinaryPath: "", Arguments: sql.NullString{String: "integer, integer", Valid: true},
   269  					IdentArgs:  sql.NullString{String: "integer, integer", Valid: true},
   270  					ResultType: sql.NullString{String: "integer", Valid: true},
   271  					Volatility: "v", IsStrict: false, IsLeakProof: false, IsSecurityDefiner: false, Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
   272  					Language: "sql", IsWindow: false, ExecLocation: "a", PlannerSupport: "-", Kind: "f", Parallel: "r",
   273  				}
   274  
   275  				backup.PrintCreateFunctionStatement(backupfile, tocfile, ParallelFunction, funcMetadata)
   276  
   277  				testhelper.AssertQueryRuns(connectionPool, buffer.String())
   278  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(integer, integer)")
   279  
   280  				resultFunctions := backup.GetFunctionsAllVersions(connectionPool)
   281  
   282  				Expect(resultFunctions).To(HaveLen(1))
   283  				structmatcher.ExpectStructsToMatchExcluding(&ParallelFunction, &resultFunctions[0], "Oid")
   284  			})
   285  			// This test is pended because getting the DLL for hstore_plperl compiled and working in our CI is proving very difficult.
   286  			// TODO: get this working in CI, and un-pend it then.
   287  			PIt("creates a function with TRANSFORM FOR TYPE", func() {
   288  				TransformFunction := backup.Function{
   289  					Schema: "public", Name: "add", ReturnsSet: false, FunctionBody: "SELECT $1 + 1",
   290  					BinaryPath: "", Arguments: sql.NullString{String: "hstore", Valid: true},
   291  					IdentArgs:  sql.NullString{String: "hstore", Valid: true},
   292  					ResultType: sql.NullString{String: "integer", Valid: true},
   293  					Volatility: "v", IsStrict: false, IsLeakProof: false, IsSecurityDefiner: false, Config: "", Cost: 100, NumRows: 0, DataAccess: "c",
   294  					Language: "plperl", IsWindow: false, ExecLocation: "a", PlannerSupport: "-", Kind: "f", Parallel: "u",
   295  					TransformTypes: "FOR TYPE pg_catalog.hstore",
   296  				}
   297  				backup.PrintCreateFunctionStatement(backupfile, tocfile, TransformFunction, funcMetadata)
   298  
   299  				// set up types and transforms needed
   300  				testhelper.AssertQueryRuns(connectionPool, "CREATE EXTENSION hstore;")
   301  				defer testhelper.AssertQueryRuns(connectionPool, "DROP EXTENSION hstore CASCADE;")
   302  
   303  				testhelper.AssertQueryRuns(connectionPool, "CREATE EXTENSION plperl;")
   304  				defer testhelper.AssertQueryRuns(connectionPool, "DROP EXTENSION plperl CASCADE;")
   305  
   306  				testhelper.AssertQueryRuns(connectionPool, `CREATE FUNCTION hstore_to_plperl(val internal) RETURNS internal 
   307  						AS '$libdir/hstore_plperl.so'  LANGUAGE C STRICT IMMUTABLE;`)
   308  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION hstore_to_plperl CASCADE;")
   309  
   310  				testhelper.AssertQueryRuns(connectionPool, "CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal))")
   311  
   312  				// create and assess function
   313  				testhelper.AssertQueryRuns(connectionPool, buffer.String())
   314  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.add(hstore)")
   315  
   316  				resultFunctions := backup.GetFunctionsAllVersions(connectionPool)
   317  
   318  				Expect(resultFunctions).To(HaveLen(1))
   319  				structmatcher.ExpectStructsToMatchExcluding(&TransformFunction, &resultFunctions[0], "Oid")
   320  			})
   321  		})
   322  	})
   323  	Describe("PrintCreateAggregateStatement", func() {
   324  		emptyMetadata := backup.ObjectMetadata{}
   325  		basicAggregateDef := backup.Aggregate{}
   326  		funcInfoMap := map[uint32]backup.FunctionInfo{
   327  			1: {QualifiedName: "public.mysfunc_accum", Arguments: sql.NullString{String: "numeric, numeric, numeric", Valid: true}},
   328  			2: {QualifiedName: "public.mypre_accum", Arguments: sql.NullString{String: "numeric, numeric", Valid: true}},
   329  			3: {QualifiedName: "pg_catalog.ordered_set_transition_multi", Arguments: sql.NullString{String: `internal, VARIADIC "any"`, Valid: true}},
   330  			4: {QualifiedName: "pg_catalog.rank_final", Arguments: sql.NullString{String: `internal, VARIADIC "any"`, Valid: true}},
   331  			5: {QualifiedName: "pg_catalog.numeric_avg", Arguments: sql.NullString{String: "internal", Valid: true}},
   332  			6: {QualifiedName: "pg_catalog.numeric_avg_serialize", Arguments: sql.NullString{String: "internal", Valid: true}},
   333  			7: {QualifiedName: "pg_catalog.numeric_avg_deserialize", Arguments: sql.NullString{String: "bytea, internal", Valid: true}},
   334  			8: {QualifiedName: "pg_catalog.numeric_avg_accum", Arguments: sql.NullString{String: "numeric, numeric", Valid: true}},
   335  			9: {QualifiedName: "pg_catalog.power", Arguments: sql.NullString{String: "numeric, numeric", Valid: true}},
   336  		}
   337  		BeforeEach(func() {
   338  			//Run queries to set up the database state so we can successfully create an aggregate
   339  			testhelper.AssertQueryRuns(connectionPool, `
   340  			CREATE FUNCTION public.mysfunc_accum(numeric, numeric, numeric)
   341  			   RETURNS numeric
   342  			   AS 'select $1 + $2 + $3'
   343  			   LANGUAGE SQL
   344  			   IMMUTABLE;
   345  			`)
   346  			testhelper.AssertQueryRuns(connectionPool, `
   347  			CREATE FUNCTION public.mypre_accum(numeric, numeric)
   348  			   RETURNS numeric
   349  			   AS 'select $1 + $2'
   350  			   LANGUAGE SQL
   351  			   IMMUTABLE
   352  			   RETURNS NULL ON NULL INPUT;
   353  			`)
   354  
   355  			basicAggregateDef = backup.Aggregate{
   356  				Oid: 1, Schema: "public", Name: "agg_prefunc", Arguments: sql.NullString{String: "numeric, numeric", Valid: true},
   357  				IdentArgs: sql.NullString{String: "numeric, numeric", Valid: true}, TransitionFunction: 1, PreliminaryFunction: 2,
   358  				TransitionDataType: "numeric", InitialValue: "0", MInitValIsNull: true,
   359  			}
   360  			if true {
   361  				basicAggregateDef.Kind = "n"
   362  				basicAggregateDef.Finalmodify = "r"
   363  				basicAggregateDef.Mfinalmodify = "r"
   364  				basicAggregateDef.Parallel = "u"
   365  			}
   366  		})
   367  		AfterEach(func() {
   368  			testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.mysfunc_accum(numeric, numeric, numeric)")
   369  			testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.mypre_accum(numeric, numeric)")
   370  		})
   371  		It("creates a basic aggregate", func() {
   372  			backup.PrintCreateAggregateStatement(backupfile, tocfile, basicAggregateDef, funcInfoMap, emptyMetadata)
   373  
   374  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   375  			defer testhelper.AssertQueryRuns(connectionPool, "DROP AGGREGATE public.agg_prefunc(numeric, numeric)")
   376  
   377  			resultAggregates := backup.GetAggregates(connectionPool)
   378  			Expect(resultAggregates).To(HaveLen(1))
   379  			structmatcher.ExpectStructsToMatchExcluding(&basicAggregateDef, &resultAggregates[0], "Oid", "TransitionFunction", "PreliminaryFunction", "CombineFunction")
   380  		})
   381  		It("creates an aggregate with an owner, security label, and a comment", func() {
   382  			aggMetadata := testutils.DefaultMetadata("AGGREGATE", false, true, true, includeSecurityLabels)
   383  			backup.PrintCreateAggregateStatement(backupfile, tocfile, basicAggregateDef, funcInfoMap, aggMetadata)
   384  
   385  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   386  			defer testhelper.AssertQueryRuns(connectionPool, "DROP AGGREGATE public.agg_prefunc(numeric, numeric)")
   387  
   388  			resultAggregates := backup.GetAggregates(connectionPool)
   389  			Expect(resultAggregates).To(HaveLen(1))
   390  			resultMetadataMap := backup.GetMetadataForObjectType(connectionPool, backup.TYPE_AGGREGATE)
   391  			resultMetadata := resultMetadataMap[resultAggregates[0].GetUniqueID()]
   392  			structmatcher.ExpectStructsToMatchExcluding(&basicAggregateDef, &resultAggregates[0], "Oid", "TransitionFunction", "PreliminaryFunction", "CombineFunction")
   393  			structmatcher.ExpectStructsToMatch(&aggMetadata, &resultMetadata)
   394  		})
   395  		It("creates a hypothetical ordered-set aggregate", func() {
   396  			testutils.SkipIfBefore6(connectionPool)
   397  			complexAggregateDef := backup.Aggregate{
   398  				Schema: "public", Name: "agg_hypo_ord", Arguments: sql.NullString{String: `VARIADIC "any" ORDER BY VARIADIC "any"`, Valid: true},
   399  				IdentArgs: sql.NullString{String: `VARIADIC "any" ORDER BY VARIADIC "any"`, Valid: true}, TransitionFunction: 3, FinalFunction: 4,
   400  				TransitionDataType: "internal", InitValIsNull: true, FinalFuncExtra: true, Hypothetical: true, MInitValIsNull: true,
   401  			}
   402  			if true {
   403  				complexAggregateDef.Hypothetical = false
   404  				complexAggregateDef.Kind = "h"
   405  				complexAggregateDef.Finalmodify = "w"
   406  				complexAggregateDef.Mfinalmodify = "w"
   407  				complexAggregateDef.Parallel = "u"
   408  			}
   409  
   410  			backup.PrintCreateAggregateStatement(backupfile, tocfile, complexAggregateDef, funcInfoMap, emptyMetadata)
   411  
   412  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   413  			defer testhelper.AssertQueryRuns(connectionPool, `DROP AGGREGATE public.agg_hypo_ord(VARIADIC "any" ORDER BY VARIADIC "any")`)
   414  			resultAggregates := backup.GetAggregates(connectionPool)
   415  
   416  			Expect(resultAggregates).To(HaveLen(1))
   417  			structmatcher.ExpectStructsToMatchExcluding(&complexAggregateDef, &resultAggregates[0], "Oid", "TransitionFunction", "FinalFunction")
   418  		})
   419  		It("creates an aggregate with a sort operator", func() {
   420  			aggregateDef := backup.Aggregate{
   421  				Schema: "public", Name: "agg_sort", Arguments: sql.NullString{String: "numeric", Valid: true},
   422  				IdentArgs: sql.NullString{String: "numeric", Valid: true}, TransitionFunction: 9, FinalFunction: 0,
   423  				SortOperator: "+", SortOperatorSchema: "pg_catalog", TransitionDataType: "numeric",
   424  				InitialValue: "0", IsOrdered: false, MInitValIsNull: true,
   425  			}
   426  			if true {
   427  				aggregateDef.Kind = "n"
   428  				aggregateDef.Finalmodify = "r"
   429  				aggregateDef.Mfinalmodify = "r"
   430  				aggregateDef.Parallel = "u"
   431  			}
   432  
   433  			backup.PrintCreateAggregateStatement(backupfile, tocfile, aggregateDef, funcInfoMap, emptyMetadata)
   434  
   435  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   436  			defer testhelper.AssertQueryRuns(connectionPool, `DROP AGGREGATE public.agg_sort(numeric)`)
   437  			resultAggregates := backup.GetAggregates(connectionPool)
   438  
   439  			Expect(resultAggregates).To(HaveLen(1))
   440  			structmatcher.ExpectStructsToMatchExcluding(&aggregateDef, &resultAggregates[0], "Oid", "TransitionFunction", "FinalFunction", "CombineFunction")
   441  		})
   442  		It("creates an aggregate with combine function and transition data size", func() {
   443  			testutils.SkipIfBefore6(connectionPool)
   444  			aggregateDef := backup.Aggregate{
   445  				Schema: "public", Name: "agg_6_features", Arguments: sql.NullString{String: "numeric, numeric", Valid: true},
   446  				IdentArgs: sql.NullString{String: "numeric, numeric", Valid: true}, TransitionFunction: 1, CombineFunction: 2,
   447  				FinalFunction: 0, SortOperator: "", TransitionDataType: "numeric", TransitionDataSize: 1000,
   448  				InitialValue: "0", IsOrdered: false, MInitValIsNull: true,
   449  			}
   450  			if true {
   451  				aggregateDef.Kind = "n"
   452  				aggregateDef.Finalmodify = "r"
   453  				aggregateDef.Mfinalmodify = "r"
   454  				aggregateDef.Parallel = "u"
   455  			}
   456  
   457  			backup.PrintCreateAggregateStatement(backupfile, tocfile, aggregateDef, funcInfoMap, emptyMetadata)
   458  
   459  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   460  			defer testhelper.AssertQueryRuns(connectionPool, `DROP AGGREGATE public.agg_6_features(numeric, numeric)`)
   461  			resultAggregates := backup.GetAggregates(connectionPool)
   462  
   463  			Expect(resultAggregates).To(HaveLen(1))
   464  			structmatcher.ExpectStructsToMatchExcluding(&aggregateDef, &resultAggregates[0], "Oid", "TransitionFunction", "FinalFunction", "CombineFunction")
   465  		})
   466  		It("creates an aggregate with serial/deserial functions", func() {
   467  			testutils.SkipIfBefore6(connectionPool)
   468  			aggregateDef := backup.Aggregate{
   469  				Schema: "public", Name: "myavg", Arguments: sql.NullString{String: "numeric", Valid: true},
   470  				IdentArgs: sql.NullString{String: "numeric", Valid: true}, TransitionFunction: 8,
   471  				FinalFunction: 5, SerialFunction: 6, DeserialFunction: 7, TransitionDataType: "internal",
   472  				IsOrdered: false, InitValIsNull: true, MInitValIsNull: true,
   473  			}
   474  			if true {
   475  				aggregateDef.Kind = "n"
   476  				aggregateDef.Finalmodify = "r"
   477  				aggregateDef.Mfinalmodify = "r"
   478  				aggregateDef.Parallel = "u"
   479  			}
   480  
   481  			backup.PrintCreateAggregateStatement(backupfile, tocfile, aggregateDef, funcInfoMap, emptyMetadata)
   482  
   483  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   484  			defer testhelper.AssertQueryRuns(connectionPool, `DROP AGGREGATE public.myavg(numeric)`)
   485  			resultAggregates := backup.GetAggregates(connectionPool)
   486  
   487  			Expect(resultAggregates).To(HaveLen(1))
   488  			structmatcher.ExpectStructsToMatchExcluding(&aggregateDef, &resultAggregates[0], "Oid", "TransitionFunction", "FinalFunction", "SerialFunction", "DeserialFunction")
   489  		})
   490  		It("creates an aggregate with moving attributes", func() {
   491  			testutils.SkipIfBefore6(connectionPool)
   492  			aggregateDef := backup.Aggregate{
   493  				Schema: "public", Name: "moving_agg", Arguments: sql.NullString{String: "numeric, numeric", Valid: true},
   494  				IdentArgs: sql.NullString{String: "numeric, numeric", Valid: true}, TransitionFunction: 1, TransitionDataType: "numeric",
   495  				InitValIsNull: true, MTransitionFunction: 1, MInverseTransitionFunction: 1,
   496  				MTransitionDataType: "numeric", MTransitionDataSize: 100, MFinalFunction: 1,
   497  				MFinalFuncExtra: true, MInitialValue: "0", MInitValIsNull: false,
   498  			}
   499  			if true {
   500  				aggregateDef.Kind = "n"
   501  				aggregateDef.Finalmodify = "r"
   502  				aggregateDef.Mfinalmodify = "r"
   503  				aggregateDef.Parallel = "u"
   504  			}
   505  
   506  			backup.PrintCreateAggregateStatement(backupfile, tocfile, aggregateDef, funcInfoMap, emptyMetadata)
   507  
   508  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   509  			defer testhelper.AssertQueryRuns(connectionPool, `DROP AGGREGATE public.moving_agg(numeric, numeric)`)
   510  			resultAggregates := backup.GetAggregates(connectionPool)
   511  
   512  			Expect(resultAggregates).To(HaveLen(1))
   513  			structmatcher.ExpectStructsToMatchExcluding(&aggregateDef, &resultAggregates[0], "Oid", "TransitionFunction", "MTransitionFunction", "MInverseTransitionFunction", "MFinalFunction")
   514  		})
   515  		// TODO: test for aggregate with aggfinalmodify/aggmfinalmodify (no table, just couple examples to check on query is correct)
   516  	})
   517  	Describe("PrintCreateCastStatement", func() {
   518  		var (
   519  			castMetadata backup.ObjectMetadata
   520  		)
   521  		BeforeEach(func() {
   522  			castMetadata = backup.ObjectMetadata{}
   523  		})
   524  		It("prints a basic cast with a function", func() {
   525  			castDef := backup.Cast{Oid: 0, SourceTypeFQN: "pg_catalog.money", TargetTypeFQN: "pg_catalog.text", FunctionSchema: "public", FunctionName: "money_to_text", FunctionArgs: "money", CastContext: "a", CastMethod: "f"}
   526  
   527  			testhelper.AssertQueryRuns(connectionPool, "CREATE FUNCTION public.money_to_text(money) RETURNS TEXT AS $$ SELECT textin(cash_out($1)) $$ LANGUAGE SQL;")
   528  			defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.money_to_text(money)")
   529  
   530  			backup.PrintCreateCastStatement(backupfile, tocfile, castDef, castMetadata)
   531  			defer testhelper.AssertQueryRuns(connectionPool, "DROP CAST (money AS text)")
   532  
   533  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   534  
   535  			resultCasts := backup.GetCasts(connectionPool)
   536  			Expect(resultCasts).To(HaveLen(1))
   537  			structmatcher.ExpectStructsToMatchExcluding(&castDef, &resultCasts[0], "Oid", "FunctionOid")
   538  		})
   539  		It("prints a basic cast without a function", func() {
   540  			castDef := backup.Cast{Oid: 0, SourceTypeFQN: "pg_catalog.text", TargetTypeFQN: "public.casttesttype", FunctionSchema: "", FunctionName: "", FunctionArgs: "", CastContext: "i", CastMethod: "b"}
   541  
   542  			testhelper.AssertQueryRuns(connectionPool, "CREATE FUNCTION public.cast_in(cstring) RETURNS public.casttesttype AS $$textin$$ LANGUAGE internal STRICT NO SQL")
   543  			testhelper.AssertQueryRuns(connectionPool, "CREATE FUNCTION public.cast_out(public.casttesttype) RETURNS cstring AS $$textout$$ LANGUAGE internal STRICT NO SQL")
   544  			testhelper.AssertQueryRuns(connectionPool, "CREATE TYPE public.casttesttype (INTERNALLENGTH = variable, INPUT = public.cast_in, OUTPUT = public.cast_out)")
   545  			defer testhelper.AssertQueryRuns(connectionPool, "DROP TYPE public.casttesttype CASCADE")
   546  
   547  			backup.PrintCreateCastStatement(backupfile, tocfile, castDef, castMetadata)
   548  			defer testhelper.AssertQueryRuns(connectionPool, "DROP CAST (text AS public.casttesttype)")
   549  
   550  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   551  
   552  			resultCasts := backup.GetCasts(connectionPool)
   553  			Expect(resultCasts).To(HaveLen(1))
   554  			structmatcher.ExpectStructsToMatchExcluding(&castDef, &resultCasts[0], "Oid")
   555  		})
   556  		It("prints a cast with a comment", func() {
   557  			castDef := backup.Cast{Oid: 1, SourceTypeFQN: "pg_catalog.money", TargetTypeFQN: "pg_catalog.text", FunctionSchema: "public", FunctionName: "money_to_text", FunctionArgs: "money", CastContext: "a", CastMethod: "f"}
   558  			castMetadata = testutils.DefaultMetadata("CAST", false, false, true, false)
   559  
   560  			testhelper.AssertQueryRuns(connectionPool, "CREATE FUNCTION public.money_to_text(money) RETURNS TEXT AS $$ SELECT textin(cash_out($1)) $$ LANGUAGE SQL;")
   561  			defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION public.money_to_text(money)")
   562  
   563  			backup.PrintCreateCastStatement(backupfile, tocfile, castDef, castMetadata)
   564  			defer testhelper.AssertQueryRuns(connectionPool, "DROP CAST (money AS text)")
   565  
   566  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   567  
   568  			resultCasts := backup.GetCasts(connectionPool)
   569  			Expect(resultCasts).To(HaveLen(1))
   570  			resultMetadataMap := backup.GetCommentsForObjectType(connectionPool, backup.TYPE_CAST)
   571  			resultMetadata := resultMetadataMap[resultCasts[0].GetUniqueID()]
   572  			structmatcher.ExpectStructsToMatchExcluding(&castDef, &resultCasts[0], "Oid", "FunctionOid")
   573  			structmatcher.ExpectStructsToMatchExcluding(&resultMetadata, &castMetadata, "Oid")
   574  		})
   575  		It("prints an inout cast ", func() {
   576  			testutils.SkipIfBefore6(connectionPool)
   577  			castDef := backup.Cast{Oid: 0, SourceTypeFQN: `pg_catalog."varchar"`, TargetTypeFQN: "public.custom_numeric", FunctionSchema: "", FunctionName: "", FunctionArgs: "", CastContext: "a", CastMethod: "i"}
   578  			testhelper.AssertQueryRuns(connectionPool, "CREATE TYPE public.custom_numeric AS (i numeric)")
   579  			defer testhelper.AssertQueryRuns(connectionPool, "DROP TYPE public.custom_numeric")
   580  
   581  			backup.PrintCreateCastStatement(backupfile, tocfile, castDef, castMetadata)
   582  			defer testhelper.AssertQueryRuns(connectionPool, "DROP CAST (varchar AS public.custom_numeric)")
   583  
   584  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   585  
   586  			resultCasts := backup.GetCasts(connectionPool)
   587  			Expect(resultCasts).To(HaveLen(1))
   588  			structmatcher.ExpectStructsToMatchExcluding(&castDef, &resultCasts[0], "Oid")
   589  		})
   590  	})
   591  	Describe("PrintCreateLanguageStatements", func() {
   592  		It("creates procedural languages", func() {
   593  			plpythonString := "plpython"
   594  			if true {
   595  				plpythonString = "plpython3"
   596  			}
   597  
   598  			funcInfoMap := map[uint32]backup.FunctionInfo{
   599  				1: {QualifiedName: fmt.Sprintf("pg_catalog.%s_call_handler", plpythonString), Arguments: sql.NullString{String: "", Valid: true}, IsInternal: true},
   600  				2: {QualifiedName: fmt.Sprintf("pg_catalog.%s_inline_handler", plpythonString), Arguments: sql.NullString{String: "internal", Valid: true}, IsInternal: true},
   601  			}
   602  			langOwner := ""
   603  			var langMetadata backup.ObjectMetadata
   604  			if false {
   605  				langOwner = testutils.GetUserByID(connectionPool, 10)
   606  				langMetadata = backup.ObjectMetadata{ObjectType: "LANGUAGE", Privileges: []backup.ACL{}, Owner: langOwner, Comment: "This is a language comment"}
   607  			} else {
   608  				langOwner = "testrole"
   609  				langMetadata = testutils.DefaultMetadata("LANGUAGE", false, true, true, includeSecurityLabels)
   610  			}
   611  			plpythonInfo := backup.ProceduralLanguage{Oid: 1, Name: fmt.Sprintf("%su", plpythonString), Owner: langOwner, IsPl: true, PlTrusted: false, Handler: 1, Inline: 2}
   612  
   613  			langMetadataMap := map[backup.UniqueID]backup.ObjectMetadata{plpythonInfo.GetUniqueID(): langMetadata}
   614  			if false {
   615  				plpythonInfo.Inline = 0
   616  			}
   617  			procLangs := []backup.ProceduralLanguage{plpythonInfo}
   618  
   619  			backup.PrintCreateLanguageStatements(backupfile, tocfile, procLangs, funcInfoMap, langMetadataMap)
   620  
   621  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   622  			defer testhelper.AssertQueryRuns(connectionPool, fmt.Sprintf("DROP LANGUAGE %su", plpythonString))
   623  
   624  			resultProcLangs := backup.GetProceduralLanguages(connectionPool)
   625  			resultMetadataMap := backup.GetMetadataForObjectType(connectionPool, backup.TYPE_PROCLANGUAGE)
   626  
   627  			plpythonInfo.Oid = testutils.OidFromObjectName(connectionPool, "", fmt.Sprintf("%su", plpythonString), backup.TYPE_PROCLANGUAGE)
   628  			Expect(resultProcLangs).To(HaveLen(1))
   629  			resultMetadata := resultMetadataMap[plpythonInfo.GetUniqueID()]
   630  			structmatcher.ExpectStructsToMatchIncluding(&plpythonInfo, &resultProcLangs[0], "Name", "IsPl", "PlTrusted")
   631  			structmatcher.ExpectStructsToMatch(&langMetadata, &resultMetadata)
   632  		})
   633  	})
   634  	Describe("PrintCreateExtensions", func() {
   635  		It("creates extensions", func() {
   636  			testutils.SkipIfBefore5(connectionPool)
   637  			plperlExtension := backup.Extension{Oid: 1, Name: "plperl", Schema: "pg_catalog"}
   638  			extensions := []backup.Extension{plperlExtension}
   639  			extensionMetadataMap := testutils.DefaultMetadataMap("EXTENSION", false, false, true, false)
   640  			extensionMetadata := extensionMetadataMap[plperlExtension.GetUniqueID()]
   641  			backup.PrintCreateExtensionStatements(backupfile, tocfile, extensions, extensionMetadataMap)
   642  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   643  			defer testhelper.AssertQueryRuns(connectionPool, "DROP EXTENSION plperl; SET search_path=pg_catalog")
   644  			resultExtensions := backup.GetExtensions(connectionPool)
   645  			resultMetadataMap := backup.GetCommentsForObjectType(connectionPool, backup.TYPE_EXTENSION)
   646  			plperlExtension.Oid = testutils.OidFromObjectName(connectionPool, "", "plperl", backup.TYPE_EXTENSION)
   647  			Expect(resultExtensions).To(HaveLen(1))
   648  			plperlMetadata := resultMetadataMap[plperlExtension.GetUniqueID()]
   649  			structmatcher.ExpectStructsToMatch(&plperlExtension, &resultExtensions[0])
   650  			structmatcher.ExpectStructsToMatch(&extensionMetadata, &plperlMetadata)
   651  		})
   652  	})
   653  	Describe("PrintCreateTransformStatements", func() {
   654  		var fromSQLFuncOid uint32
   655  		var toSQLFuncOid uint32
   656  		var funcInfoMap map[uint32]backup.FunctionInfo
   657  
   658  		BeforeEach(func() {
   659  			testutils.SkipIfBefore7(connectionPool)
   660  			fromSQLFuncOid = testutils.OidFromObjectName(connectionPool, "pg_catalog", "numeric_support", backup.TYPE_FUNCTION)
   661  			toSQLFuncOid = testutils.OidFromObjectName(connectionPool, "pg_catalog", "int2recv", backup.TYPE_FUNCTION)
   662  
   663  			funcInfoMap = map[uint32]backup.FunctionInfo{
   664  				fromSQLFuncOid: {QualifiedName: "numeric_support", IdentArgs: sql.NullString{String: "internal", Valid: true}},
   665  				toSQLFuncOid:   {QualifiedName: "int2recv", IdentArgs: sql.NullString{String: "internal", Valid: true}},
   666  			}
   667  		})
   668  
   669  		DescribeTable("creates transforms", func(fromSql func() uint32, toSql func() uint32) {
   670  			transform := backup.Transform{Oid: 1, TypeNamespace: "pg_catalog", TypeName: "int2", LanguageName: "c", FromSQLFunc: fromSql(), ToSQLFunc: toSql()}
   671  			transMetadata := testutils.DefaultMetadata("TRANSFORM", false, false, false, false)
   672  			backup.PrintCreateTransformStatement(backupfile, tocfile, transform, funcInfoMap, transMetadata)
   673  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   674  			defer testhelper.AssertQueryRuns(connectionPool, "DROP TRANSFORM FOR int2 LANGUAGE c")
   675  
   676  			resultTransforms := backup.GetTransforms(connectionPool)
   677  			Expect(resultTransforms).To(HaveLen(1))
   678  			structmatcher.ExpectStructsToMatchExcluding(&transform, &resultTransforms[0], "Oid")
   679  		},
   680  			Entry("both functions are specified", func() uint32 { return fromSQLFuncOid }, func() uint32 { return toSQLFuncOid }),
   681  			Entry("only fromSQL function is specified", func() uint32 { return fromSQLFuncOid }, func() uint32 { return uint32(0) }),
   682  			Entry("only toSql function is specified", func() uint32 { return uint32(0) }, func() uint32 { return toSQLFuncOid }),
   683  		)
   684  	})
   685  	Describe("PrintCreateConversionStatements", func() {
   686  		It("creates conversions", func() {
   687  			convOne := backup.Conversion{Oid: 1, Schema: "public", Name: "conv_one", ForEncoding: "LATIN1", ToEncoding: "MULE_INTERNAL", ConversionFunction: "pg_catalog.latin1_to_mic", IsDefault: false}
   688  			convTwo := backup.Conversion{Oid: 0, Schema: "public", Name: "conv_two", ForEncoding: "LATIN1", ToEncoding: "MULE_INTERNAL", ConversionFunction: "pg_catalog.latin1_to_mic", IsDefault: true}
   689  			conversions := []backup.Conversion{convOne, convTwo}
   690  			convMetadataMap := testutils.DefaultMetadataMap("CONVERSION", false, true, true, false)
   691  			convMetadata := convMetadataMap[convOne.GetUniqueID()]
   692  
   693  			backup.PrintCreateConversionStatements(backupfile, tocfile, conversions, convMetadataMap)
   694  
   695  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   696  			defer testhelper.AssertQueryRuns(connectionPool, "DROP CONVERSION public.conv_one")
   697  			defer testhelper.AssertQueryRuns(connectionPool, "DROP CONVERSION public.conv_two")
   698  
   699  			resultConversions := backup.GetConversions(connectionPool)
   700  			resultMetadataMap := backup.GetMetadataForObjectType(connectionPool, backup.TYPE_CONVERSION)
   701  
   702  			convOne.Oid = testutils.OidFromObjectName(connectionPool, "public", "conv_one", backup.TYPE_CONVERSION)
   703  			convTwo.Oid = testutils.OidFromObjectName(connectionPool, "public", "conv_two", backup.TYPE_CONVERSION)
   704  			Expect(resultConversions).To(HaveLen(2))
   705  			resultMetadata := resultMetadataMap[convOne.GetUniqueID()]
   706  			structmatcher.ExpectStructsToMatch(&convOne, &resultConversions[0])
   707  			structmatcher.ExpectStructsToMatch(&convTwo, &resultConversions[1])
   708  			structmatcher.ExpectStructsToMatch(&convMetadata, &resultMetadata)
   709  		})
   710  	})
   711  	Describe("PrintCreateForeignDataWrapperStatement", func() {
   712  		emptyMetadata := backup.ObjectMetadata{}
   713  		funcInfoMap := map[uint32]backup.FunctionInfo{
   714  			1: {QualifiedName: "pg_catalog.postgresql_fdw_validator", Arguments: sql.NullString{String: "", Valid: true}, IsInternal: true},
   715  		}
   716  		It("creates foreign data wrappers with a validator and options", func() {
   717  			testutils.SkipIfBefore6(connectionPool)
   718  			foreignDataWrapperValidator := backup.ForeignDataWrapper{Name: "foreigndata1", Validator: 1}
   719  			foreignDataWrapperOptions := backup.ForeignDataWrapper{Name: "foreigndata2", Options: "dbname 'testdb'"}
   720  
   721  			backup.PrintCreateForeignDataWrapperStatement(backupfile, tocfile, foreignDataWrapperValidator, funcInfoMap, emptyMetadata)
   722  			backup.PrintCreateForeignDataWrapperStatement(backupfile, tocfile, foreignDataWrapperOptions, funcInfoMap, emptyMetadata)
   723  
   724  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   725  			defer testhelper.AssertQueryRuns(connectionPool, "DROP FOREIGN DATA WRAPPER foreigndata1")
   726  			defer testhelper.AssertQueryRuns(connectionPool, "DROP FOREIGN DATA WRAPPER foreigndata2")
   727  
   728  			resultWrappers := backup.GetForeignDataWrappers(connectionPool)
   729  
   730  			Expect(resultWrappers).To(HaveLen(2))
   731  			structmatcher.ExpectStructsToMatchExcluding(&foreignDataWrapperValidator, &resultWrappers[0], "Oid", "Validator")
   732  			structmatcher.ExpectStructsToMatchExcluding(&foreignDataWrapperOptions, &resultWrappers[1], "Oid", "Validator")
   733  		})
   734  	})
   735  	Describe("PrintCreateServerStatement", func() {
   736  		emptyMetadata := backup.ObjectMetadata{}
   737  		It("creates a foreign server with all options", func() {
   738  			testutils.SkipIfBefore6(connectionPool)
   739  			foreignServer := backup.ForeignServer{Name: "foreignserver", Type: "mytype", Version: "myversion", ForeignDataWrapper: "foreigndatawrapper", Options: "dbname 'testdb', host 'localhost'"}
   740  
   741  			backup.PrintCreateServerStatement(backupfile, tocfile, foreignServer, emptyMetadata)
   742  
   743  			testhelper.AssertQueryRuns(connectionPool, "CREATE FOREIGN DATA WRAPPER foreigndatawrapper")
   744  			defer testhelper.AssertQueryRuns(connectionPool, "DROP FOREIGN DATA WRAPPER foreigndatawrapper CASCADE")
   745  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   746  
   747  			resultServers := backup.GetForeignServers(connectionPool)
   748  
   749  			Expect(resultServers).To(HaveLen(1))
   750  			structmatcher.ExpectStructsToMatchExcluding(&foreignServer, &resultServers[0], "Oid")
   751  		})
   752  	})
   753  	Describe("PrintCreateUserMappingStatement", func() {
   754  		It("creates a user mapping for a specific user with all options", func() {
   755  			testutils.SkipIfBefore6(connectionPool)
   756  			testhelper.AssertQueryRuns(connectionPool, "CREATE FOREIGN DATA WRAPPER foreigndatawrapper")
   757  			defer testhelper.AssertQueryRuns(connectionPool, "DROP FOREIGN DATA WRAPPER foreigndatawrapper CASCADE")
   758  			testhelper.AssertQueryRuns(connectionPool, "CREATE SERVER server FOREIGN DATA WRAPPER foreigndatawrapper")
   759  			userMapping := backup.UserMapping{User: "testrole", Server: "server", Options: "dbname 'testdb', host 'localhost'"}
   760  
   761  			backup.PrintCreateUserMappingStatement(backupfile, tocfile, userMapping)
   762  
   763  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   764  
   765  			resultMappings := backup.GetUserMappings(connectionPool)
   766  
   767  			Expect(resultMappings).To(HaveLen(1))
   768  			structmatcher.ExpectStructsToMatchExcluding(&userMapping, &resultMappings[0], "Oid")
   769  		})
   770  		It("creates a user mapping for public", func() {
   771  			testutils.SkipIfBefore6(connectionPool)
   772  			testhelper.AssertQueryRuns(connectionPool, "CREATE FOREIGN DATA WRAPPER foreigndatawrapper")
   773  			defer testhelper.AssertQueryRuns(connectionPool, "DROP FOREIGN DATA WRAPPER foreigndatawrapper CASCADE")
   774  			testhelper.AssertQueryRuns(connectionPool, "CREATE SERVER server FOREIGN DATA WRAPPER foreigndatawrapper")
   775  			userMapping := backup.UserMapping{User: "public", Server: "server"}
   776  
   777  			backup.PrintCreateUserMappingStatement(backupfile, tocfile, userMapping)
   778  
   779  			testhelper.AssertQueryRuns(connectionPool, buffer.String())
   780  
   781  			resultMappings := backup.GetUserMappings(connectionPool)
   782  
   783  			Expect(resultMappings).To(HaveLen(1))
   784  			structmatcher.ExpectStructsToMatchExcluding(&userMapping, &resultMappings[0], "Oid")
   785  		})
   786  	})
   787  })