/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.types.mcompat.mfuncs; import com.foundationdb.server.types.LazyList; import com.foundationdb.server.types.TClass; import com.foundationdb.server.types.TExecutionContext; import com.foundationdb.server.types.TInstance; import com.foundationdb.server.types.TScalar; import com.foundationdb.server.types.TOverloadResult; import com.foundationdb.server.types.mcompat.mtypes.MApproximateNumber; import com.foundationdb.server.types.mcompat.mtypes.MNumeric; import com.foundationdb.server.types.mcompat.mtypes.MString; import com.foundationdb.server.types.value.ValueSource; import com.foundationdb.server.types.value.ValueTarget; import com.foundationdb.server.types.texpressions.TInputSetBuilder; import com.foundationdb.server.types.texpressions.TScalarBase; public final class MField extends TScalarBase { public static final TScalar[] exactOverloads = new TScalar[] { // From the MySQL docs: // // If all arguments to FIELD() are strings, all arguments are compared as strings. // If all arguments are numbers, they are compared as numbers. // Otherwise, the arguments are compared as double. // // We can accomplish this by having three ScalarsGroups. The first consist of VARCHAR and CHAR. We want // more than one TScalar in this group, so that same-type-at is false and it's not automatically picked. // Only the various string types are strongly castable to either of these, so that takes care of the first // sentence in the FIELD spec (as quoted above). The second ScalarsGroup consists of exact numbers. We // provide these in four flavors: signed and unsigned of BIGINT and DECIMAL. This lets us cover all of the // exact precision types without expensive casts (casts between non-DECIMAL ints are cheap). Finally, // the DOUBLE overload is in a ScalarsGroup by itself, so that same-type-at is true and the scalar // is always picked. new MField(MString.VARCHAR, false, 1), new MField(MString.CHAR, false, 1), new MField(MNumeric.BIGINT, false, 2), new MField(MNumeric.BIGINT_UNSIGNED, false, 2), new MField(MNumeric.DECIMAL, false, 2), new MField(MNumeric.DECIMAL_UNSIGNED, false, 2), new MField(MApproximateNumber.DOUBLE, false, 3) }; @Override protected void buildInputSets(TInputSetBuilder builder) { builder.setExact(exact).vararg(targetClass, 0, 1); } @Override protected void doEvaluate(TExecutionContext context, LazyList<? extends ValueSource> inputs, ValueTarget output) { ValueSource needle = inputs.get(0); int result = 0; if (!needle.isNull()) { TInstance needleInstance = needle.getType(); for (int i = 1, size = inputs.size(); i < size; ++i) { ValueSource arg = inputs.get(i); if (!arg.isNull()) { TInstance argInstance = arg.getType(); int cmp = TClass.compare(needleInstance, needle, argInstance, arg); if (cmp == 0) { result = i; break; } } } } output.putInt32(result); } @Override public String displayName() { return "FIELD"; } @Override public TOverloadResult resultType() { return TOverloadResult.fixed(MNumeric.INT, 3); } @Override public int[] getPriorities() { return new int[] { priority }; } @Override protected boolean nullContaminates(int inputIndex) { return false; } private MField(TClass targetClass, boolean exact, int maxPriority) { this.targetClass = targetClass; this.exact = exact; this.priority = maxPriority; } private final TClass targetClass; private final int priority; private final boolean exact; }