github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/testutils/sqlutils/name_resolution_testutils.go (about) 1 // Copyright 2018 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 sqlutils 12 13 import ( 14 "context" 15 "fmt" 16 "testing" 17 18 "github.com/cockroachdb/cockroach/pkg/sql/parser" 19 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 20 "github.com/cockroachdb/cockroach/pkg/testutils" 21 ) 22 23 // ColumnItemResolverTester is an interface that should be implemented by any 24 // struct that also implements tree.ColumnItemResolver. It is used to test that 25 // the implementation of tree.ColumnItemResolver is correct. 26 type ColumnItemResolverTester interface { 27 // GetColumnItemResolver returns the tree.ColumnItemResolver. Since any 28 // struct implementing ColumnItemResolverTester should also implement 29 // tree.ColumnItemResolver, this is basically an identity function. 30 GetColumnItemResolver() tree.ColumnItemResolver 31 32 // AddTable adds a table with the given column names to the 33 // tree.ColumnItemResolver. 34 AddTable(tabName tree.TableName, colNames []tree.Name) 35 36 // ResolveQualifiedStarTestResults returns the results of running 37 // RunResolveQualifiedStarTest on the tree.ColumnItemResolver. 38 ResolveQualifiedStarTestResults( 39 srcName *tree.TableName, srcMeta tree.ColumnSourceMeta, 40 ) (string, string, error) 41 42 // ResolveColumnItemTestResults returns the results of running 43 // RunResolveColumnItemTest on the tree.ColumnItemResolver. 44 ResolveColumnItemTestResults(colRes tree.ColumnResolutionResult) (string, error) 45 } 46 47 func initColumnItemResolverTester(t *testing.T, ct ColumnItemResolverTester) { 48 ct.AddTable(tree.MakeTableNameWithSchema("", "crdb_internal", "tables"), []tree.Name{"table_name"}) 49 ct.AddTable(tree.MakeTableName("db1", "foo"), []tree.Name{"x"}) 50 ct.AddTable(tree.MakeTableName("db2", "foo"), []tree.Name{"x"}) 51 ct.AddTable(tree.MakeUnqualifiedTableName("bar"), []tree.Name{"x"}) 52 ct.AddTable(tree.MakeTableName("db1", "kv"), []tree.Name{"k", "v"}) 53 } 54 55 // RunResolveQualifiedStarTest tests that the given ColumnItemResolverTester 56 // correctly resolves names of the form "<tableName>.*". 57 func RunResolveQualifiedStarTest(t *testing.T, ct ColumnItemResolverTester) { 58 testCases := []struct { 59 in string 60 tnout string 61 csout string 62 err string 63 }{ 64 {`a.*`, ``, ``, `no data source matches pattern: a.*`}, 65 {`foo.*`, ``, ``, `ambiguous source name: "foo"`}, 66 {`db1.public.foo.*`, `db1.public.foo`, `x`, ``}, 67 {`db1.foo.*`, `db1.public.foo`, `x`, ``}, 68 {`dbx.foo.*`, ``, ``, `no data source matches pattern: dbx.foo.*`}, 69 {`kv.*`, `db1.public.kv`, `k, v`, ``}, 70 } 71 72 initColumnItemResolverTester(t, ct) 73 resolver := ct.GetColumnItemResolver() 74 for _, tc := range testCases { 75 t.Run(tc.in, func(t *testing.T) { 76 tnout, csout, err := func() (string, string, error) { 77 stmt, err := parser.ParseOne(fmt.Sprintf("SELECT %s", tc.in)) 78 if err != nil { 79 return "", "", err 80 } 81 v := stmt.AST.(*tree.Select).Select.(*tree.SelectClause).Exprs[0].Expr.(tree.VarName) 82 c, err := v.NormalizeVarName() 83 if err != nil { 84 return "", "", err 85 } 86 acs, ok := c.(*tree.AllColumnsSelector) 87 if !ok { 88 return "", "", fmt.Errorf("var name %s (%T) did not resolve to AllColumnsSelector, found %T instead", 89 v, v, c) 90 } 91 tn, res, err := acs.Resolve(context.Background(), resolver) 92 if err != nil { 93 return "", "", err 94 } 95 return ct.ResolveQualifiedStarTestResults(tn, res) 96 }() 97 if !testutils.IsError(err, tc.err) { 98 t.Fatalf("%s: expected %s, but found %v", tc.in, tc.err, err) 99 } 100 if tc.err != "" { 101 return 102 } 103 104 if tc.tnout != tnout { 105 t.Fatalf("%s: expected tn %s, but found %s", tc.in, tc.tnout, tnout) 106 } 107 if tc.csout != csout { 108 t.Fatalf("%s: expected cs %s, but found %s", tc.in, tc.csout, csout) 109 } 110 }) 111 } 112 } 113 114 // RunResolveColumnItemTest tests that the given ColumnItemResolverTester 115 // correctly resolves column names. 116 func RunResolveColumnItemTest(t *testing.T, ct ColumnItemResolverTester) { 117 testCases := []struct { 118 in string 119 out string 120 err string 121 }{ 122 {`a`, ``, `column "a" does not exist`}, 123 {`x`, ``, `column reference "x" is ambiguous \(candidates: db1.public.foo.x, db2.public.foo.x, bar.x\)`}, 124 {`k`, `db1.public.kv.k`, ``}, 125 {`v`, `db1.public.kv.v`, ``}, 126 {`table_name`, `"".crdb_internal.tables.table_name`, ``}, 127 128 {`blix.x`, ``, `no data source matches prefix: blix`}, 129 {`"".x`, ``, `invalid column name: ""\.x`}, 130 {`foo.x`, ``, `ambiguous source name`}, 131 {`kv.k`, `db1.public.kv.k`, ``}, 132 {`bar.x`, `bar.x`, ``}, 133 {`tables.table_name`, `"".crdb_internal.tables.table_name`, ``}, 134 135 {`a.b.x`, ``, `no data source matches prefix: a\.b`}, 136 {`crdb_internal.tables.table_name`, `"".crdb_internal.tables.table_name`, ``}, 137 {`public.foo.x`, ``, `ambiguous source name`}, 138 {`public.kv.k`, `db1.public.kv.k`, ``}, 139 140 // CockroachDB extension: d.t.x -> d.public.t.x 141 {`db1.foo.x`, `db1.public.foo.x`, ``}, 142 {`db2.foo.x`, `db2.public.foo.x`, ``}, 143 144 {`a.b.c.x`, ``, `no data source matches prefix: a\.b\.c`}, 145 {`"".crdb_internal.tables.table_name`, `"".crdb_internal.tables.table_name`, ``}, 146 {`db1.public.foo.x`, `db1.public.foo.x`, ``}, 147 {`db2.public.foo.x`, `db2.public.foo.x`, ``}, 148 {`db1.public.kv.v`, `db1.public.kv.v`, ``}, 149 } 150 151 initColumnItemResolverTester(t, ct) 152 resolver := ct.GetColumnItemResolver() 153 for _, tc := range testCases { 154 t.Run(tc.in, func(t *testing.T) { 155 out, err := func() (string, error) { 156 stmt, err := parser.ParseOne(fmt.Sprintf("SELECT %s", tc.in)) 157 if err != nil { 158 return "", err 159 } 160 v := stmt.AST.(*tree.Select).Select.(*tree.SelectClause).Exprs[0].Expr.(tree.VarName) 161 c, err := v.NormalizeVarName() 162 if err != nil { 163 return "", err 164 } 165 ci, ok := c.(*tree.ColumnItem) 166 if !ok { 167 return "", fmt.Errorf("var name %s (%T) did not resolve to ColumnItem, found %T instead", 168 v, v, c) 169 } 170 res, err := ci.Resolve(context.Background(), resolver) 171 if err != nil { 172 return "", err 173 } 174 return ct.ResolveColumnItemTestResults(res) 175 }() 176 if !testutils.IsError(err, tc.err) { 177 t.Fatalf("%s: expected %s, but found %v", tc.in, tc.err, err) 178 } 179 if tc.err != "" { 180 return 181 } 182 183 if tc.out != out { 184 t.Fatalf("%s: expected %s, but found %s", tc.in, tc.out, out) 185 } 186 }) 187 } 188 }