github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/operators_test.go (about) 1 // Copyright 2020 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package tree 12 13 import ( 14 "encoding/csv" 15 "io" 16 "os" 17 "path/filepath" 18 "strconv" 19 "testing" 20 21 "github.com/cockroachdb/cockroach/pkg/sql/types" 22 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 23 "github.com/lib/pq/oid" 24 "github.com/stretchr/testify/require" 25 ) 26 27 // TestOperatorVolatilityMatchesPostgres checks that our defined operators match 28 // Postgres' operators for Volatility. 29 // 30 // Dump command below: 31 // COPY ( 32 // SELECT o.oprname, o.oprleft, o.oprright, o.oprresult, p.provolatile, p.proleakproof 33 // FROM pg_operator AS o JOIN pg_proc AS p ON (o.oprcode = p.oid) 34 // ORDER BY o.oprname, o.oprleft, o.oprright, o.oprresult 35 // ) TO STDOUT WITH CSV DELIMITER '|' HEADER; 36 func TestOperatorVolatilityMatchesPostgres(t *testing.T) { 37 defer leaktest.AfterTest(t)() 38 csvPath := filepath.Join("testdata", "pg_operator_provolatile_dump.csv") 39 f, err := os.Open(csvPath) 40 require.NoError(t, err) 41 42 defer f.Close() 43 44 reader := csv.NewReader(f) 45 reader.Comma = '|' 46 47 // Read header row 48 _, err = reader.Read() 49 require.NoError(t, err) 50 51 type pgOp struct { 52 name string 53 leftType oid.Oid 54 rightType oid.Oid 55 volatility Volatility 56 } 57 var pgOps []pgOp 58 for { 59 line, err := reader.Read() 60 if err == io.EOF { 61 break 62 } 63 // Columns are: 64 // 0 1 2 3 4 5 65 // oprname oprleft oprright oprresult provolatile proleakproof 66 require.NoError(t, err) 67 require.Len(t, line, 6) 68 69 name := line[0] 70 71 leftOid, err := strconv.Atoi(line[1]) 72 require.NoError(t, err) 73 74 rightOid, err := strconv.Atoi(line[2]) 75 require.NoError(t, err) 76 77 provolatile := line[4] 78 require.Len(t, provolatile, 1) 79 proleakproof := line[5] 80 require.Len(t, proleakproof, 1) 81 82 v, err := VolatilityFromPostgres(provolatile, proleakproof[0] == 't') 83 require.NoError(t, err) 84 pgOps = append(pgOps, pgOp{ 85 name: name, 86 leftType: oid.Oid(leftOid), 87 rightType: oid.Oid(rightOid), 88 volatility: v, 89 }) 90 } 91 92 check := func(name string, leftType, rightType *types.T, volatility Volatility) { 93 t.Helper() 94 if volatility == 0 { 95 t.Errorf("operator %s(%v,%v) has no volatility set", name, leftType, rightType) 96 return 97 } 98 99 pgName := name 100 // Postgres doesn't have separate operators for IS (NOT) DISTINCT FROM; remap 101 // to equality. 102 switch name { 103 case IsDistinctFrom.String(), IsNotDistinctFrom.String(): 104 pgName = EQ.String() 105 } 106 107 var leftOid oid.Oid 108 if leftType != nil { 109 leftOid = leftType.Oid() 110 } 111 rightOid := rightType.Oid() 112 for _, o := range pgOps { 113 if o.name == pgName && o.leftType == leftOid && o.rightType == rightOid { 114 if o.volatility != volatility { 115 t.Errorf( 116 "operator %s(%v,%v) has volatility %s, corresponding pg operator has %s", 117 name, leftType, rightType, volatility, o.volatility, 118 ) 119 } 120 return 121 } 122 } 123 if testing.Verbose() { 124 t.Logf("operator %s(%v,%v) %d %d with volatility %s has no corresponding pg operator", 125 name, leftType, rightType, leftOid, rightOid, volatility, 126 ) 127 } 128 } 129 130 // Check unary ops. We don't just go through the map so we process them in 131 // an orderly fashion. 132 for op := UnaryOperator(0); op < NumUnaryOperators; op++ { 133 for _, impl := range UnaryOps[op] { 134 o := impl.(*UnaryOp) 135 check(op.String(), nil /* leftType */, o.Typ, o.Volatility) 136 } 137 } 138 139 // Check comparison ops. 140 for op := ComparisonOperator(0); op < NumComparisonOperators; op++ { 141 for _, impl := range CmpOps[op] { 142 o := impl.(*CmpOp) 143 check(op.String(), o.LeftType, o.RightType, o.Volatility) 144 } 145 } 146 147 // Check binary ops. 148 for op := BinaryOperator(0); op < NumBinaryOperators; op++ { 149 for _, impl := range BinOps[op] { 150 o := impl.(*BinOp) 151 check(op.String(), o.LeftType, o.RightType, o.Volatility) 152 } 153 } 154 }