github.com/tuhaihe/gpbackup@v1.0.3/backup/queries_functions_test.go (about)

     1  package backup_test
     2  
     3  import (
     4  	"database/sql"
     5  	"database/sql/driver"
     6  
     7  	"github.com/DATA-DOG/go-sqlmock"
     8  	"github.com/tuhaihe/gp-common-go-libs/structmatcher"
     9  	"github.com/tuhaihe/gpbackup/backup"
    10  
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  )
    14  
    15  var _ = Describe("backup/queries_acl tests", func() {
    16  	Describe("PostProcessFunctionConfigs", func() {
    17  		It("returns correct value for search_path", func() {
    18  			allFunctions := []backup.Function{
    19  				{Config: "SET SEARCH_PATH TO bar"},
    20  			}
    21  			err := backup.PostProcessFunctionConfigs(allFunctions)
    22  			Expect(err).ToNot(HaveOccurred())
    23  			Expect(allFunctions[0].Config).To(Equal(`SET search_path TO 'bar'`))
    24  		})
    25  		It("returns error when function config does not parse", func() {
    26  			allFunctions := []backup.Function{
    27  				{Config: "SET foo blah blah blah"},
    28  			}
    29  			err := backup.PostProcessFunctionConfigs(allFunctions)
    30  			Expect(err).To(HaveOccurred())
    31  		})
    32  		// known bug https://www.pivotaltracker.com/story/show/164575992
    33  		PIt("returns correct value for multiple GUCs in one function", func() {
    34  			allFunctions := []backup.Function{
    35  				// not clear how the native pg_proc.proconfig field will translate into our Config attribute: assuming we get 2 separate strings
    36  				{Config: `SET search_path TO bar, blah
    37  SET BAZ TO abc`},
    38  			}
    39  			err := backup.PostProcessFunctionConfigs(allFunctions)
    40  			Expect(err).ToNot(HaveOccurred())
    41  			// expecting separate lines stored in the Config attribute;
    42  			// this may not be the perfect solution, TBD: may want to have it become a slice of strings
    43  			Expect(allFunctions[0].Config).To(Equal(`SET search_path TO 'bar', blah
    44  SET baz to abc`))
    45  		})
    46  	})
    47  	Describe("QuoteGUCValue", func() {
    48  		It("returns correct value for a name/value pair", func() {
    49  			result := backup.QuoteGUCValue("foo", `bar`)
    50  			Expect(result).To(Equal(`'bar'`))
    51  		})
    52  		It("returns correct value for SEARCH_PATH", func() {
    53  			result := backup.QuoteGUCValue("search_path", `"$user",public`)
    54  			Expect(result).To(Equal(`'$user', 'public'`))
    55  		})
    56  		It("returns correct value for temp_tablespaces", func() {
    57  			result := backup.QuoteGUCValue("temp_tablespaces", `"tables""pace1%",     tablespace2`)
    58  			Expect(result).To(Equal(`'tables"pace1%', 'tablespace2'`))
    59  		})
    60  	})
    61  	Describe("UnescapeDoubleQuote", func() {
    62  		It("removes outside quotes", func() {
    63  			result := backup.UnescapeDoubleQuote(`"foo"`)
    64  			Expect(result).To(Equal(`foo`))
    65  		})
    66  		It("does nothing if string has no quotes surrounding it", func() {
    67  			result := backup.UnescapeDoubleQuote(`foo`)
    68  			Expect(result).To(Equal(`foo`))
    69  		})
    70  		It("removes outside quotes and unescapes embedded quote", func() {
    71  			result := backup.UnescapeDoubleQuote(`"foo"""`)
    72  			Expect(result).To(Equal(`foo"`))
    73  		})
    74  		It("removes outside quotes and unescapes multiple embedded quotes", func() {
    75  			result := backup.UnescapeDoubleQuote(`"""foo"""`)
    76  			Expect(result).To(Equal(`"foo"`))
    77  		})
    78  	})
    79  	Describe("GetFunctions", func() {
    80  		It("GetFunctions properly handles NULL function arguments, NULL function identity arguments, or NULL function result types", func() {
    81  			if false {
    82  				Skip("Test does not apply for GPDB versions before 5")
    83  			}
    84  
    85  			header := []string{"oid", "schema", "name", "proretset", "functionbody", "binarypath", "arguments", "identargs", "resulttype",
    86  				"provolatile", "proisstrict", "prosecdef", "proconfig", "procost", "prorows", "prodataaccess", "language"}
    87  			rowGood := []driver.Value{"1", "mock_schema", "mock_table", false, "mock_funcbody", "mock_path",
    88  				sql.NullString{String: "mock_args", Valid: true}, sql.NullString{String: "mock_identargs", Valid: true},
    89  				sql.NullString{String: "mock_resulttype", Valid: true}, "mock_volatility", false, false, "", 0, 0,
    90  				"mock_dataaccess", "mock_language"}
    91  			rowNullArg := []driver.Value{"2", "mock_schema2", "mock_table2", false, "mock_funcbody2", "mock_path2", nil,
    92  				sql.NullString{String: "mock_identargs2", Valid: true}, sql.NullString{String: "mock_resulttype2", Valid: true}, "mock_volatility2",
    93  				false, false, "", 0, 0, "mock_dataaccess2", "mock_language2"}
    94  			rowNullIdentArg := []driver.Value{"3", "mock_schema3", "mock_table3", false, "mock_funcbody3", "mock_path3",
    95  				sql.NullString{String: "mock_args3", Valid: true}, nil, sql.NullString{String: "mock_resulttype3", Valid: true}, "mock_volatility3",
    96  				false, false, "", 0, 0, "mock_dataaccess3", "mock_language3"}
    97  			rowNullResultType := []driver.Value{"4", "mock_schema4", "mock_table4", false, "mock_funcbody4", "mock_path4",
    98  				sql.NullString{String: "mock_args4", Valid: true}, sql.NullString{String: "mock_identargs4", Valid: true}, nil, "mock_volatility4",
    99  				false, false, "", 0, 0, "mock_dataaccess4", "mock_language4"}
   100  			fakeRows := sqlmock.NewRows(header).AddRow(rowGood...).AddRow(rowNullArg...).AddRow(rowNullIdentArg...).AddRow(rowNullResultType...)
   101  			mock.ExpectQuery(`SELECT (.*)`).WillReturnRows(fakeRows)
   102  			result := backup.GetFunctions(connectionPool)
   103  
   104  			// Expect the GetFunctions function to return only the 1st row since all other rows have invalid NULL strings
   105  			expectedResult := []backup.Function{{Oid: 1, Schema: "mock_schema", Name: "mock_table", ReturnsSet: false, FunctionBody: "mock_funcbody",
   106  				BinaryPath: "mock_path", Arguments: sql.NullString{String: "mock_args", Valid: true},
   107  				IdentArgs: sql.NullString{String: "mock_identargs", Valid: true}, ResultType: sql.NullString{String: "mock_resulttype", Valid: true},
   108  				Volatility: "mock_volatility", IsStrict: false, IsSecurityDefiner: false, Config: "", Cost: 0,
   109  				NumRows: 0, DataAccess: "mock_dataaccess", Language: "mock_language"}}
   110  
   111  			Expect(result).To(HaveLen(1))
   112  			structmatcher.ExpectStructsToMatch(&expectedResult[0], &result[0])
   113  		})
   114  	})
   115  	Describe("GetAggregates", func() {
   116  		It("GetAggregates properly handles NULL aggregate arguments or NULL aggregate identity arguments", func() {
   117  			if false {
   118  				Skip("Test does not apply for GPDB versions before 5")
   119  			}
   120  
   121  			header := []string{"oid", "schema", "name", "arguments", "identargs", "aggtransfn", "aggprelimfn", "aggfinalfn", "sortoperator",
   122  				"sortoperatorschema", "transitiondatatype", "initialvalue", "initvalisnull", "minitvalisnull", "aggordered"}
   123  			rowGood := []driver.Value{"1", "mock_schema", "mock_table", sql.NullString{String: "mock_args", Valid: true},
   124  				sql.NullString{String: "mock_identargs", Valid: true}, 0, 0, 0, "mock_operator", "mock_operatorschema",
   125  				"mock_transdatatype", "mock_initvalue", false, false, false}
   126  			rowNullArg := []driver.Value{"2", "mock_schema2", "mock_table2", nil, sql.NullString{String: "mock_identargs2", Valid: true}, 0, 0, 0,
   127  				"mock_operator2", "mock_operatorschema2", "mock_transdatatype2", "mock_initvalue2", false, false, false}
   128  			rowNullIdentArg := []driver.Value{"3", "mock_schema3", "mock_table3", sql.NullString{String: "mock_args3", Valid: true}, nil, 0, 0, 0,
   129  				"mock_operator3", "mock_operatorschema3", "mock_transdatatype3", "mock_initvalue3", false, false, false}
   130  			fakeRows := sqlmock.NewRows(header).AddRow(rowGood...).AddRow(rowNullArg...).AddRow(rowNullIdentArg...)
   131  			mock.ExpectQuery(`SELECT (.*)`).WillReturnRows(fakeRows)
   132  			result := backup.GetAggregates(connectionPool)
   133  
   134  			// Expect the GetAggregates function to return only the 1st row since all other rows have invalid NULL strings
   135  			expectedResult := []backup.Aggregate{{Oid: 1, Schema: "mock_schema", Name: "mock_table",
   136  				Arguments: sql.NullString{String: "mock_args", Valid: true}, IdentArgs: sql.NullString{String: "mock_identargs", Valid: true},
   137  				TransitionFunction: 0, PreliminaryFunction: 0, FinalFunction: 0, SortOperator: "mock_operator",
   138  				SortOperatorSchema: "mock_operatorschema", TransitionDataType: "mock_transdatatype", InitialValue: "mock_initvalue",
   139  				InitValIsNull: false, MInitValIsNull: false, IsOrdered: false}}
   140  			Expect(result).To(HaveLen(1))
   141  			structmatcher.ExpectStructsToMatch(&expectedResult[0], &result[0])
   142  		})
   143  	})
   144  })