WithChildren implements the sql.Expression interface.
(ctx *sql.Context, children ...sql.Expression)
| 177 | |
| 178 | // WithChildren implements the sql.Expression interface. |
| 179 | func (it *InTuple) WithChildren(ctx *sql.Context, children ...sql.Expression) (sql.Expression, error) { |
| 180 | if len(children) != 2 { |
| 181 | return nil, sql.ErrInvalidChildrenNumber.New(it, len(children), 2) |
| 182 | } |
| 183 | rightTuple, ok := children[1].(expression.Tuple) |
| 184 | if !ok { |
| 185 | return nil, errors.Errorf("%T: expected right child to be `%T` but has type `%T`", it, expression.Tuple{}, children[1]) |
| 186 | } |
| 187 | if len(rightTuple) == 0 { |
| 188 | return nil, errors.Errorf("IN must contain at least 1 expression") |
| 189 | } |
| 190 | // We'll only resolve the comparison functions once we have all Doltgres types. |
| 191 | // We may see GMS types during some analyzer steps, so we should wait until those are done. |
| 192 | if leftType, ok := children[0].Type(ctx).(*pgtypes.DoltgresType); ok { |
| 193 | // Rather than finding and resolving a comparison function every time we call Eval, we resolve them once and |
| 194 | // reuse the functions. We also want to avoid re-assigning the parameters of the comparison functions since that |
| 195 | // will also cause the functions to resolve again. To do this, we store expressions within our struct that the |
| 196 | // functions reference, so we can freely switch the values within the literals without changing anything |
| 197 | // regarding the comparison functions. This is usually unsafe, but since we're verifying the types returned by |
| 198 | // the parameters, and assigning the values to our own literals, we do not have to worry. This offers a |
| 199 | // significant speedup as function resolution is very expensive, so we want to do it as few times as possible |
| 200 | // (preferably once). |
| 201 | staticLiteral := expression.NewLiteral(nil, leftType) |
| 202 | arrayLiterals := make([]*expression.Literal, len(rightTuple)) |
| 203 | // Each expression may be a different type (which is valid), so we need a comparison function for each expression. |
| 204 | compFuncs := make([]framework.Function, len(rightTuple)) |
| 205 | allValidChildren := true |
| 206 | for i, rightExpr := range rightTuple { |
| 207 | rightType, ok := rightExpr.Type(ctx).(*pgtypes.DoltgresType) |
| 208 | if !ok { |
| 209 | allValidChildren = false |
| 210 | break |
| 211 | } |
| 212 | arrayLiterals[i] = expression.NewLiteral(nil, rightType) |
| 213 | compFunc := framework.GetBinaryFunction(framework.Operator_BinaryEqual).Compile(ctx, "internal_in_comparison", staticLiteral, arrayLiterals[i]) |
| 214 | if compFunc == nil { |
| 215 | return nil, errors.Errorf("operator does not exist: %s = %s", leftType.String(), rightType.String()) |
| 216 | } |
| 217 | cid := compFunc.Type(ctx).(*pgtypes.DoltgresType).ID |
| 218 | if cid != pgtypes.Bool.ID { |
| 219 | // Prepared statement binding values will need explicit casting to appropriate type |
| 220 | ec := NewAssignmentCast(arrayLiterals[i], pgtypes.Unknown, staticLiteral.Type(ctx).(*pgtypes.DoltgresType)) |
| 221 | compFunc = framework.GetBinaryFunction(framework.Operator_BinaryEqual).Compile(ctx, "internal_in_comparison", staticLiteral, ec) |
| 222 | if compFunc == nil || compFunc.StashedError() != nil { |
| 223 | return nil, errors.Errorf("operator does not exist: %s = %s", leftType.String(), rightType.String()) |
| 224 | } |
| 225 | cid = compFunc.Type(ctx).(*pgtypes.DoltgresType).ID |
| 226 | if cid != pgtypes.Bool.ID { |
| 227 | // This should never happen, but this is just to be safe |
| 228 | return nil, errors.Errorf("%T: found equality comparison that does not return a bool", it) |
| 229 | } |
| 230 | } |
| 231 | compFuncs[i] = compFunc |
| 232 | } |
| 233 | if allValidChildren { |
| 234 | return &InTuple{ |
| 235 | leftExpr: children[0], |
| 236 | rightExpr: rightTuple, |
no test coverage detected