| 15 | } |
| 16 | |
| 17 | func (v *inVisitor) Visit(node ast.Node) astutils.Visitor { |
| 18 | if v.err != nil { |
| 19 | return nil |
| 20 | } |
| 21 | |
| 22 | in, ok := node.(*ast.In) |
| 23 | if !ok { |
| 24 | return v |
| 25 | } |
| 26 | |
| 27 | // Validate that sqlc.slice in an IN statement is the only arg, eg: |
| 28 | // id IN (sqlc.slice("ids")) -- GOOD |
| 29 | // id in (0, 1, sqlc.slice("ids")) -- BAD |
| 30 | |
| 31 | if len(in.List) <= 1 { |
| 32 | return v |
| 33 | } |
| 34 | |
| 35 | for _, n := range in.List { |
| 36 | call, ok := n.(*ast.FuncCall) |
| 37 | if !ok { |
| 38 | continue |
| 39 | } |
| 40 | fn := call.Func |
| 41 | if fn == nil { |
| 42 | continue |
| 43 | } |
| 44 | |
| 45 | if fn.Schema == "sqlc" && fn.Name == "slice" { |
| 46 | var inExpr, sliceArg string |
| 47 | |
| 48 | // determine inExpr |
| 49 | switch n := in.Expr.(type) { |
| 50 | case *ast.ColumnRef: |
| 51 | inExpr = n.Name |
| 52 | default: |
| 53 | inExpr = "..." |
| 54 | } |
| 55 | |
| 56 | // determine sliceArg |
| 57 | if len(call.Args.Items) == 1 { |
| 58 | switch n := call.Args.Items[0].(type) { |
| 59 | case *ast.A_Const: |
| 60 | if str, ok := n.Val.(*ast.String); ok { |
| 61 | sliceArg = "\"" + str.Str + "\"" |
| 62 | } else { |
| 63 | sliceArg = "?" |
| 64 | } |
| 65 | case *ast.ColumnRef: |
| 66 | sliceArg = n.Name |
| 67 | default: |
| 68 | // impossible, validate.FuncCall should have caught this |
| 69 | sliceArg = "..." |
| 70 | } |
| 71 | } |
| 72 | v.err = &sqlerr.Error{ |
| 73 | Message: fmt.Sprintf("expected '%s IN' expr to consist only of sqlc.slice(%s); eg ", inExpr, sliceArg), |
| 74 | Location: call.Pos(), |