Eval implements the sql.Expression interface.
(ctx *sql.Context, row sql.Row)
| 61 | |
| 62 | // Eval implements the sql.Expression interface. |
| 63 | func (in *InSubquery) Eval(ctx *sql.Context, row sql.Row) (any, error) { |
| 64 | if len(in.compFuncs) == 0 { |
| 65 | return nil, errors.Errorf("%T: cannot Eval as it has not been fully resolved", in) |
| 66 | } |
| 67 | |
| 68 | left, err := in.leftExpr.Eval(ctx, row) |
| 69 | if err != nil { |
| 70 | return nil, err |
| 71 | } |
| 72 | |
| 73 | // The NULL handling for IN expressions is tricky. According to |
| 74 | // https://www.postgresql.org/docs/16/functions-comparisons.html#FUNCTIONS-COMPARISONS-IN-SCALAR: |
| 75 | // To comply with the SQL standard, IN() returns NULL not only if the expression on the left hand side is NULL, but |
| 76 | // also if no match is found in the list and one of the expressions in the list is NULL. |
| 77 | leftNull := left == nil |
| 78 | |
| 79 | if types.NumColumns(in.Left().Type(ctx)) != types.NumColumns(in.Right().Type(ctx)) { |
| 80 | return nil, sql.ErrInvalidOperandColumns.New(types.NumColumns(in.Left().Type(ctx)), types.NumColumns(in.Right().Type(ctx))) |
| 81 | } |
| 82 | |
| 83 | right := in.rightExpr |
| 84 | |
| 85 | // TODO: does this work for all pg values? |
| 86 | values, err := right.HashMultiple(ctx, row) |
| 87 | if err != nil { |
| 88 | return nil, err |
| 89 | } |
| 90 | |
| 91 | // NULL IN (list) returns NULL. NULL IN (empty list) returns 0 |
| 92 | if leftNull { |
| 93 | if values.Size() == 0 { |
| 94 | return false, nil |
| 95 | } |
| 96 | return nil, nil |
| 97 | } |
| 98 | |
| 99 | // TODO: it might be possible for the left value to hash to a different value than the right even though they pass |
| 100 | // an equality check. We need to perform a type conversion here to catch this case. |
| 101 | key, err := hash.HashOf(ctx, nil, sql.NewRow(left)) |
| 102 | if err != nil { |
| 103 | return nil, err |
| 104 | } |
| 105 | |
| 106 | // If the hashed values don't contain the left value hash, we know it's not there. |
| 107 | // If we do find the hash of the left value, we still need to check for equality, |
| 108 | // since non-equal values could have the same hash in some cases. |
| 109 | val, notFoundErr := values.Get(key) |
| 110 | if notFoundErr != nil { |
| 111 | if _, nilValNotFoundErr := values.Get(nilKey); nilValNotFoundErr == nil { |
| 112 | return nil, nil |
| 113 | } |
| 114 | return false, nil |
| 115 | } |
| 116 | |
| 117 | var r sql.Row |
| 118 | rowVal, ok := val.([]any) |
| 119 | if !ok { |
| 120 | r = sql.Row{val} |