All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.questdb.griffin.FunctionFactoryDescriptor Maven / Gradle / Ivy

/*******************************************************************************
 *     ___                  _   ____  ____
 *    / _ \ _   _  ___  ___| |_|  _ \| __ )
 *   | | | | | | |/ _ \/ __| __| | | |  _ \
 *   | |_| | |_| |  __/\__ \ |_| |_| | |_) |
 *    \__\_\\__,_|\___||___/\__|____/|____/
 *
 *  Copyright (c) 2014-2019 Appsicle
 *  Copyright (c) 2019-2020 QuestDB
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/

package io.questdb.griffin;

import io.questdb.cairo.ColumnType;
import io.questdb.std.Misc;
import io.questdb.std.str.StringSink;

public class FunctionFactoryDescriptor {
    private static final int ARRAY_MASK = 1 << 31;
    private static final int CONST_MASK = 1 << 30;
    private static final int TYPE_MASK = ~(ARRAY_MASK | CONST_MASK);
    private final FunctionFactory factory;
    private final long[] argTypes;
    private final int sigArgCount;
    private final int openBraceIndex;

    public FunctionFactoryDescriptor(FunctionFactory factory) throws SqlException {
        this.factory = factory;

        final String sig = factory.getSignature();
        this.openBraceIndex = validateSignatureAndGetNameSeparator(sig);
        // validate data types
        int typeCount = 0;
        for (
                int i = openBraceIndex + 1, n = sig.length() - 1;
                i < n; typeCount++
        ) {
            char cc = sig.charAt(i);
            if (FunctionFactoryDescriptor.getArgType(cc) == -1) {
                throw SqlException.position(0).put("illegal argument type: ").put('`').put(cc).put('`');
            }
            // check if this is an array
            i++;
            if (i < n && sig.charAt(i) == '[') {
                i++;
                if (i >= n || sig.charAt(i) != ']') {
                    throw SqlException.position(0).put("invalid array declaration");
                }
                i++;
            }
        }

        // second loop, less paranoid
        long[] types = new long[typeCount % 2 == 0 ? typeCount / 2 : typeCount / 2 + 1];
        for (int i = openBraceIndex + 1, n = sig.length() - 1, typeIndex = 0; i < n; ) {
            final char c = sig.charAt(i);
            int type = FunctionFactoryDescriptor.getArgType(c);
            final int arrayIndex = typeIndex / 2;
            final int arrayValueOffset = (typeIndex % 2) * 32;
            // check if this is an array
            i++;

            // array bit
            if (i < n && sig.charAt(i) == '[') {
                type |= (1 << 31);
                i += 2;
            }
            // constant bit
            if ((c | 32) == c) {
                type |= (1 << 30);
            }
            types[arrayIndex] |= (toUnsignedLong(type) << (32 - arrayValueOffset));
            typeIndex++;
        }
        this.argTypes = types;
        this.sigArgCount = typeCount;
    }

    private static long toUnsignedLong(int type) {
        return ((long) type) & 0xffffffffL;
    }

    public static int getArgType(char c) {
        int sigArgType;
        switch (c | 32) {
            case 'd':
                sigArgType = ColumnType.DOUBLE;
                break;
            case 'b':
                sigArgType = ColumnType.BYTE;
                break;
            case 'e':
                sigArgType = ColumnType.SHORT;
                break;
            case 'a':
                sigArgType = ColumnType.CHAR;
                break;
            case 'f':
                sigArgType = ColumnType.FLOAT;
                break;
            case 'i':
                sigArgType = ColumnType.INT;
                break;
            case 'l':
                sigArgType = ColumnType.LONG;
                break;
            case 's':
                sigArgType = ColumnType.STRING;
                break;
            case 't':
                sigArgType = ColumnType.BOOLEAN;
                break;
            case 'k':
                sigArgType = ColumnType.SYMBOL;
                break;
            case 'm':
                sigArgType = ColumnType.DATE;
                break;
            case 'n':
                sigArgType = ColumnType.TIMESTAMP;
                break;
            case 'u':
                sigArgType = ColumnType.BINARY;
                break;
            case 'v':
                sigArgType = ColumnType.VAR_ARG;
                break;
            case 'c':
                sigArgType = ColumnType.CURSOR;
                break;
            case 'r':
                sigArgType = ColumnType.RECORD;
                break;
            case 'h':
                sigArgType = ColumnType.LONG256;
                break;
            default:
                sigArgType = -1;
                break;
        }
        return sigArgType;
    }

    public static boolean isArray(int mask) {
        return (mask & ARRAY_MASK) != 0;
    }

    public static boolean isConstant(int mask) {
        return (mask & CONST_MASK) != 0;
    }

    public static int toType(int mask) {
        return mask & TYPE_MASK;
    }

    public static int validateSignatureAndGetNameSeparator(String sig) throws SqlException {
        if (sig == null) {
            throw SqlException.$(0, "NULL signature");
        }

        int openBraceIndex = sig.indexOf('(');
        if (openBraceIndex == -1) {
            throw SqlException.$(0, "open brace expected");
        }

        if (openBraceIndex == 0) {
            throw SqlException.$(0, "empty function name");
        }

        if (sig.charAt(sig.length() - 1) != ')') {
            throw SqlException.$(0, "close brace expected");
        }

        int c = sig.charAt(0);
        if (c >= '0' && c <= '9') {
            throw SqlException.$(0, "name must not start with digit");
        }

        for (int i = 0; i < openBraceIndex; i++) {
            char cc = sig.charAt(i);
            if (FunctionFactoryCache.invalidFunctionNameChars.contains(cc)) {
                throw SqlException.position(0).put("invalid character: ").put(cc);
            }
        }

        if (FunctionFactoryCache.invalidFunctionNames.keyIndex(sig, 0, openBraceIndex) < 0) {
            throw SqlException.position(0).put("invalid function name character: ").put(sig);
        }
        return openBraceIndex;
    }

    public static String replaceSignatureNameAndSwapArgs(String name, String signature) throws SqlException {
        int openBraceIndex = validateSignatureAndGetNameSeparator(signature);
        StringSink signatureBuilder = Misc.getThreadLocalBuilder();
        signatureBuilder.put(name);
        signatureBuilder.put('(');
        for (int i = signature.length() - 2; i > openBraceIndex; i--) {
            char curr = signature.charAt(i);
            if (curr == '[') {
                signatureBuilder.put("[]");
            } else if (curr != ']') {
                signatureBuilder.put(curr);
            }
        }
        signatureBuilder.put(')');
        return signatureBuilder.toString();
    }

    public static String replaceSignatureName(String name, String signature) throws SqlException {
        int openBraceIndex = validateSignatureAndGetNameSeparator(signature);
        StringSink signatureBuilder = Misc.getThreadLocalBuilder();
        signatureBuilder.put(name);
        signatureBuilder.put(signature, openBraceIndex, signature.length());
        return signatureBuilder.toString();
    }

    public int getArgTypeMask(int index) {
        int arrayIndex = index / 2;
        long mask = argTypes[arrayIndex];
        return (int) ((mask >>> (32 - (index % 2) * 32)));
    }

    public long[] getArgTypes() {
        return argTypes;
    }

    public FunctionFactory getFactory() {
        return factory;
    }

    public String getName() {
        return factory.getSignature().substring(0, openBraceIndex);
    }

    public int getSigArgCount() {
        return sigArgCount;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy