@Override
    public void createFunction(SqlInvokedFunction function, boolean replace)
    {
        checkCatalog(function);
        checkFunctionLanguageSupported(function);
        checkArgument(!function.hasVersion(), "function '%s' is already versioned", function);

        QualifiedObjectName functionName = function.getFunctionId().getFunctionName();
        checkFieldLength("Catalog name", functionName.getCatalogName(), MAX_CATALOG_NAME_LENGTH);
        checkFieldLength("Schema name", functionName.getSchemaName(), MAX_SCHEMA_NAME_LENGTH);
        if (!functionNamespaceDao.functionNamespaceExists(functionName.getCatalogName(), functionName.getSchemaName())) {
            throw new PrestoException(NOT_FOUND, format("Function namespace not found: %s", functionName.getCatalogSchemaName()));
        }
        checkFieldLength("Function name", functionName.getObjectName(), MAX_FUNCTION_NAME_LENGTH);

        if (function.getParameters().size() > MAX_PARAMETER_COUNT) {
            throw new PrestoException(GENERIC_USER_ERROR, format("Function has more than %s parameters: %s", MAX_PARAMETER_COUNT, function.getParameters().size()));
        }
        for (Parameter parameter : function.getParameters()) {
            checkFieldLength("Parameter name", parameter.getName(), MAX_PARAMETER_NAME_LENGTH);
        }

        checkFieldLength(
                "Parameter type list",
                function.getFunctionId().getArgumentTypes().stream()
                        .map(TypeSignature::toString)
                        .collect(joining(",")),
                MAX_PARAMETER_TYPES_LENGTH);
        checkFieldLength("Return type", function.getSignature().getReturnType().toString(), MAX_RETURN_TYPE_LENGTH);

        jdbi.useTransaction(handle -> {
            FunctionNamespaceDao transactionDao = handle.attach(functionNamespaceDaoClass);
            Optional<SqlInvokedFunctionRecord> latestVersion = transactionDao.getLatestRecordForUpdate(hash(function.getFunctionId()), function.getFunctionId());
            if (!replace && latestVersion.isPresent() && !latestVersion.get().isDeleted()) {
                throw new PrestoException(ALREADY_EXISTS, "Function already exists: " + function.getFunctionId());
            }

            if (!latestVersion.isPresent() || !latestVersion.get().getFunction().hasSameDefinitionAs(function)) {
                long newVersion = latestVersion.map(SqlInvokedFunctionRecord::getFunction).map(MySqlFunctionNamespaceManager::getLongVersion).orElse(0L) + 1;
                insertSqlInvokedFunction(transactionDao, function, newVersion);
            }
            else if (latestVersion.get().isDeleted()) {
                SqlInvokedFunction latest = latestVersion.get().getFunction();
                checkState(latest.hasVersion(), "Function version missing: %s", latest.getFunctionId());
                transactionDao.setDeletionStatus(hash(latest.getFunctionId()), latest.getFunctionId(), getLongVersion(latest), false);
            }
        });
        refreshFunctionsCache(functionName);
    }
@Test
    public void testCreateFunctionFailedDuplicate()
    {
        createFunction(FUNCTION_POWER_TOWER_DOUBLE, true);
        assertPrestoException(() -> createFunction(FUNCTION_POWER_TOWER_DOUBLE, false), ALREADY_EXISTS, ".*Function already exists: unittest\\.memory\\.power_tower\\(double\\)");
        assertPrestoException(() -> createFunction(FUNCTION_POWER_TOWER_DOUBLE_UPDATED, false), ALREADY_EXISTS, ".*Function already exists: unittest\\.memory\\.power_tower\\(double\\)");
    }