diff --git a/CHANGES.txt b/CHANGES.txt index 7db90c01a9..793c1ab1fd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 4.2 + * Remove Scripted UDFs internals; hooks to be added later in CASSANDRA-17281 (CASSANDRA-18252) * Update JNA to 5.13.0 (CASSANDRA-18050) * Make virtual tables decide if they implicitly enable ALLOW FILTERING (CASSANDRA-18238) * Add row, tombstone, and sstable count to nodetool profileload (CASSANDRA-18022) diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml index 3bc759461a..5a5ddd28f9 100644 --- a/conf/cassandra.yaml +++ b/conf/cassandra.yaml @@ -1455,12 +1455,6 @@ trace_type_repair_ttl: 7d # As of Cassandra 3.0 there is a sandbox in place that should prevent execution of evil code. user_defined_functions_enabled: false -# Enables scripted UDFs (JavaScript UDFs). -# Java UDFs are always enabled, if user_defined_functions_enabled is true. -# Enable this option to be able to use UDFs with "language javascript" or any custom JSR-223 provider. -# This option has no effect, if user_defined_functions_enabled is false. -scripted_user_defined_functions_enabled: false - # Enables encrypting data at-rest (on disk). Different key providers can be plugged in, but the default reads from # a JCE-style keystore. A single keystore can hold multiple keys, but the one referenced by # the "key_alias" is the only key that will be used for encrypt opertaions; previously used keys diff --git a/doc/modules/cassandra/pages/cql/functions.adoc b/doc/modules/cassandra/pages/cql/functions.adoc index a0c9d39f88..4a660e2f28 100644 --- a/doc/modules/cassandra/pages/cql/functions.adoc +++ b/doc/modules/cassandra/pages/cql/functions.adoc @@ -351,8 +351,7 @@ Examples: ==== User-defined functions User-defined functions (UDFs) execute user-provided code in Cassandra. -By default, Cassandra supports defining functions in _Java_ and _JavaScript_. -Support for other JSR 223 compliant scripting languages, such as Python, Ruby, and Scala, is possible by adding a JAR to the classpath. +By default, Cassandra supports defining functions in _Java_. UDFs are part of the Cassandra schema, and are automatically propagated to all nodes in the cluster. UDFs can be _overloaded_, so that multiple UDFs with different argument types can have the same function name. @@ -361,8 +360,8 @@ UDFs can be _overloaded_, so that multiple UDFs with different argument types ca [NOTE] .Note ==== -_JavaScript_ user-defined functions have been deprecated. They are planned for removal -in the next major release. +_JavaScript_ user-defined functions have been deprecated in Cassandra 4.1. In preparation for Cassandra 5.0, their removal is +already in progress. For more information - CASSANDRA-17281, CASSANDRA-18252. ==== For example: diff --git a/pylib/cassandra-cqlsh-tests.sh b/pylib/cassandra-cqlsh-tests.sh index ad6ae75368..4db07cf61b 100755 --- a/pylib/cassandra-cqlsh-tests.sh +++ b/pylib/cassandra-cqlsh-tests.sh @@ -97,7 +97,6 @@ fi ccm remove test || true # in case an old ccm cluster is left behind ccm create test -n 1 --install-dir=${CASSANDRA_DIR} ccm updateconf "user_defined_functions_enabled: true" -ccm updateconf "scripted_user_defined_functions_enabled: true" version_from_build=$(ccm node1 versionfrombuild) export pre_or_post_cdc=$(python -c """from distutils.version import LooseVersion diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java index 3aff5fe956..4082d8eb41 100644 --- a/src/java/org/apache/cassandra/config/Config.java +++ b/src/java/org/apache/cassandra/config/Config.java @@ -564,6 +564,8 @@ public class Config @Replaces(oldName = "enable_user_defined_functions", converter = Converters.IDENTITY, deprecated = true) public boolean user_defined_functions_enabled = false; + + @Deprecated @Replaces(oldName = "enable_scripted_user_defined_functions", converter = Converters.IDENTITY, deprecated = true) public boolean scripted_user_defined_functions_enabled = false; diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java index 1cf36a064b..3e1630c561 100644 --- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java +++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java @@ -814,7 +814,8 @@ public class DatabaseDescriptor logger.warn("Allowing java.lang.System.* access in UDFs is dangerous and not recommended. Set allow_extra_insecure_udfs: false to disable."); if(conf.scripted_user_defined_functions_enabled) - logger.warn("JavaScript user-defined functions have been deprecated. You can still use them but the plan is to remove them in the next major version. For more information - CASSANDRA-17280"); + throw new ConfigurationException("JavaScript user-defined functions were removed in CASSANDRA-18252. " + + "Hooks are planned to be introduced as part of CASSANDRA-17280"); if (conf.commitlog_segment_size.toMebibytes() == 0) throw new ConfigurationException("commitlog_segment_size must be positive, but was " @@ -3579,11 +3580,6 @@ public class DatabaseDescriptor return conf.scripted_user_defined_functions_enabled; } - public static void enableScriptedUserDefinedFunctions(boolean enableScriptedUserDefinedFunctions) - { - conf.scripted_user_defined_functions_enabled = enableScriptedUserDefinedFunctions; - } - public static boolean enableUserDefinedFunctionsThreads() { return conf.user_defined_functions_threads_enabled; diff --git a/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java b/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java deleted file mode 100644 index e42fbe9c42..0000000000 --- a/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.cassandra.cql3.functions; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.net.*; -import java.nio.ByteBuffer; -import java.security.*; -import java.security.cert.Certificate; -import java.util.*; -import java.util.concurrent.ExecutorService; -import javax.script.*; - -import jdk.nashorn.api.scripting.AbstractJSObject; -import jdk.nashorn.api.scripting.ClassFilter; -import jdk.nashorn.api.scripting.NashornScriptEngine; -import jdk.nashorn.api.scripting.NashornScriptEngineFactory; -import org.apache.cassandra.concurrent.NamedThreadFactory; -import org.apache.cassandra.cql3.ColumnIdentifier; -import org.apache.cassandra.db.marshal.AbstractType; -import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.security.SecurityThreadGroup; -import org.apache.cassandra.security.ThreadAwareSecurityManager; -import org.apache.cassandra.transport.ProtocolVersion; - -final class ScriptBasedUDFunction extends UDFunction -{ - private static final ProtectionDomain protectionDomain; - private static final AccessControlContext accessControlContext; - - // - // For scripted UDFs we have to rely on the security mechanisms of the scripting engine and - // SecurityManager - especially SecurityManager.checkPackageAccess(). Unlike Java-UDFs, strict checking - // of class access via the UDF class loader is not possible, since e.g. Nashorn builds its own class loader - // (jdk.nashorn.internal.runtime.ScriptLoader / jdk.nashorn.internal.runtime.NashornLoader) configured with - // a system class loader. - // - private static final String[] allowedPackagesArray = - { - // following required by jdk.nashorn.internal.objects.Global.initJavaAccess() - "", - "com", - "edu", - "java", - "javax", - "javafx", - "org", - // following required by Nashorn runtime - "java.lang", - "java.lang.invoke", - "java.lang.reflect", - "java.nio.charset", - "java.util", - "java.util.concurrent", - "javax.script", - "sun.reflect", - "jdk.internal.org.objectweb.asm.commons", - "jdk.nashorn.internal.runtime", - "jdk.nashorn.internal.runtime.linker", - // Nashorn / Java 11 - "java.lang.ref", - "java.io", - "java.util.function", - "jdk.dynalink.linker", - "jdk.internal.org.objectweb.asm", - "jdk.internal.reflect", - "jdk.nashorn.internal.scripts", - // following required by Java Driver - "java.math", - "java.nio", - "java.text", - "com.google.common.base", - "com.google.common.collect", - "com.google.common.reflect", - // following required by UDF - "org.apache.cassandra.cql3.functions.types", - "org.apache.cassandra.cql3.functions.types.exceptions", - "org.apache.cassandra.cql3.functions.types.utils" - }; - - // use a JVM standard ExecutorService as ExecutorPlus references internal - // classes, which triggers AccessControlException from the UDF sandbox - private static final UDFExecutorService executor = - new UDFExecutorService(new NamedThreadFactory("UserDefinedScriptFunctions", - Thread.MIN_PRIORITY, - udfClassLoader, - new SecurityThreadGroup("UserDefinedScriptFunctions", - Collections.unmodifiableSet(new HashSet<>(Arrays.asList(allowedPackagesArray))), - UDFunction::initializeThread)), - "userscripts"); - - private static final ClassFilter classFilter = clsName -> secureResource(clsName.replace('.', '/') + ".class"); - - private static final NashornScriptEngine scriptEngine; - - - static - { - ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); - ScriptEngine engine = scriptEngineManager.getEngineByName("nashorn"); - NashornScriptEngineFactory factory = engine != null ? (NashornScriptEngineFactory) engine.getFactory() : null; - scriptEngine = factory != null ? (NashornScriptEngine) factory.getScriptEngine(new String[]{}, udfClassLoader, classFilter) : null; - - try - { - protectionDomain = new ProtectionDomain(new CodeSource(new URL("udf", "localhost", 0, "/script", new URLStreamHandler() - { - protected URLConnection openConnection(URL u) - { - return null; - } - }), (Certificate[]) null), ThreadAwareSecurityManager.noPermissions); - } - catch (MalformedURLException e) - { - throw new RuntimeException(e); - } - accessControlContext = new AccessControlContext(new ProtectionDomain[]{ protectionDomain }); - } - - private final CompiledScript script; - private final Object udfContextBinding; - - ScriptBasedUDFunction(FunctionName name, - List argNames, - List> argTypes, - AbstractType returnType, - boolean calledOnNullInput, - String language, - String body) - { - super(name, argNames, argTypes, returnType, calledOnNullInput, language, body); - - if (!"JavaScript".equalsIgnoreCase(language) || scriptEngine == null) - throw new InvalidRequestException(String.format("Invalid language '%s' for function '%s'", language, name)); - - // execute compilation with no-permissions to prevent evil code e.g. via "static code blocks" / "class initialization" - try - { - this.script = AccessController.doPrivileged((PrivilegedExceptionAction) () -> scriptEngine.compile(body), - accessControlContext); - } - catch (PrivilegedActionException x) - { - Throwable e = x.getCause(); - logger.info("Failed to compile function '{}' for language {}: ", name, language, e); - throw new InvalidRequestException( - String.format("Failed to compile function '%s' for language %s: %s", name, language, e)); - } - - // It's not always possible to simply pass a plain Java object as a binding to Nashorn and - // let the script execute methods on it. - udfContextBinding = new UDFContextWrapper(); - } - - protected ExecutorService executor() - { - return executor; - } - - public ByteBuffer executeUserDefined(ProtocolVersion protocolVersion, List parameters) - { - Object[] params = new Object[argTypes.size()]; - for (int i = 0; i < params.length; i++) - params[i] = compose(protocolVersion, i, parameters.get(i)); - - Object result = executeScriptInternal(params); - - return decompose(protocolVersion, result); - } - - /** - * Like {@link UDFunction#executeUserDefined(ProtocolVersion, List)} but the first parameter is already in non-serialized form. - * Remaining parameters (2nd paramters and all others) are in {@code parameters}. - * This is used to prevent superfluous (de)serialization of the state of aggregates. - * Means: scalar functions of aggregates are called using this variant. - */ - protected Object executeAggregateUserDefined(ProtocolVersion protocolVersion, Object firstParam, List parameters) - { - Object[] params = new Object[argTypes.size()]; - params[0] = firstParam; - for (int i = 1; i < params.length; i++) - params[i] = compose(protocolVersion, i, parameters.get(i - 1)); - - return executeScriptInternal(params); - } - - private Object executeScriptInternal(Object[] params) - { - ScriptContext scriptContext = new SimpleScriptContext(); - scriptContext.setAttribute("javax.script.filename", this.name.toString(), ScriptContext.ENGINE_SCOPE); - Bindings bindings = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE); - for (int i = 0; i < params.length; i++) - bindings.put(argNames.get(i).toString(), params[i]); - bindings.put("udfContext", udfContextBinding); - - Object result; - try - { - // How to prevent Class.forName() _without_ "help" from the script engine ? - // NOTE: Nashorn enforces a special permission to allow class-loading, which is not granted - so it's fine. - - result = script.eval(scriptContext); - } - catch (ScriptException e) - { - throw new RuntimeException(e); - } - if (result == null) - return null; - - Class javaReturnType = UDHelper.asJavaClass(returnCodec); - Class resultType = result.getClass(); - if (!javaReturnType.isAssignableFrom(resultType)) - { - if (result instanceof Number) - { - Number rNumber = (Number) result; - if (javaReturnType == Integer.class) - result = rNumber.intValue(); - else if (javaReturnType == Long.class) - result = rNumber.longValue(); - else if (javaReturnType == Short.class) - result = rNumber.shortValue(); - else if (javaReturnType == Byte.class) - result = rNumber.byteValue(); - else if (javaReturnType == Float.class) - result = rNumber.floatValue(); - else if (javaReturnType == Double.class) - result = rNumber.doubleValue(); - else if (javaReturnType == BigInteger.class) - { - if (javaReturnType == Integer.class) - result = rNumber.intValue(); - else if (javaReturnType == Short.class) - result = rNumber.shortValue(); - else if (javaReturnType == Byte.class) - result = rNumber.byteValue(); - else if (javaReturnType == Long.class) - result = rNumber.longValue(); - else if (javaReturnType == Float.class) - result = rNumber.floatValue(); - else if (javaReturnType == Double.class) - result = rNumber.doubleValue(); - else if (javaReturnType == BigInteger.class) - { - if (rNumber instanceof BigDecimal) - result = ((BigDecimal) rNumber).toBigInteger(); - else if (rNumber instanceof Double || rNumber instanceof Float) - result = new BigDecimal(rNumber.toString()).toBigInteger(); - else - result = BigInteger.valueOf(rNumber.longValue()); - } - else if (javaReturnType == BigDecimal.class) - // String c'tor of BigDecimal is more accurate than valueOf(double) - result = new BigDecimal(rNumber.toString()); - } - else if (javaReturnType == BigDecimal.class) - // String c'tor of BigDecimal is more accurate than valueOf(double) - result = new BigDecimal(rNumber.toString()); - } - } - - return result; - } - - private final class UDFContextWrapper extends AbstractJSObject - { - private final AbstractJSObject fRetUDT; - private final AbstractJSObject fArgUDT; - private final AbstractJSObject fRetTup; - private final AbstractJSObject fArgTup; - - UDFContextWrapper() - { - fRetUDT = new AbstractJSObject() - { - public Object call(Object thiz, Object... args) - { - return udfContext.newReturnUDTValue(); - } - }; - fArgUDT = new AbstractJSObject() - { - public Object call(Object thiz, Object... args) - { - if (args[0] instanceof String) - return udfContext.newArgUDTValue((String) args[0]); - if (args[0] instanceof Number) - return udfContext.newArgUDTValue(((Number) args[0]).intValue()); - return super.call(thiz, args); - } - }; - fRetTup = new AbstractJSObject() - { - public Object call(Object thiz, Object... args) - { - return udfContext.newReturnTupleValue(); - } - }; - fArgTup = new AbstractJSObject() - { - public Object call(Object thiz, Object... args) - { - if (args[0] instanceof String) - return udfContext.newArgTupleValue((String) args[0]); - if (args[0] instanceof Number) - return udfContext.newArgTupleValue(((Number) args[0]).intValue()); - return super.call(thiz, args); - } - }; - } - - public Object getMember(String name) - { - switch(name) - { - case "newReturnUDTValue": - return fRetUDT; - case "newArgUDTValue": - return fArgUDT; - case "newReturnTupleValue": - return fRetTup; - case "newArgTupleValue": - return fArgTup; - } - return super.getMember(name); - } - } -} diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java index 52cd1fc187..58454a49ac 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java @@ -266,13 +266,7 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction { assertUdfsEnabled(language); - switch (language) - { - case "java": - return new JavaBasedUDFunction(name, argNames, argTypes, returnType, calledOnNullInput, body); - default: - return new ScriptBasedUDFunction(name, argNames, argTypes, returnType, calledOnNullInput, language, body); - } + return new JavaBasedUDFunction(name, argNames, argTypes, returnType, calledOnNullInput, body); } /** @@ -448,8 +442,8 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction { if (!DatabaseDescriptor.enableUserDefinedFunctions()) throw new InvalidRequestException("User-defined functions are disabled in cassandra.yaml - set user_defined_functions_enabled=true to enable"); - if (!"java".equalsIgnoreCase(language) && !DatabaseDescriptor.enableScriptedUserDefinedFunctions()) - throw new InvalidRequestException("Scripted user-defined functions are disabled in cassandra.yaml - set scripted_user_defined_functions_enabled=true to enable if you are aware of the security risks"); + if (!"java".equalsIgnoreCase(language)) + throw new InvalidRequestException("Currently only Java UDFs are available in Cassandra. For more information - CASSANDRA-18252 and CASSANDRA-17281"); } static void initializeThread() @@ -637,8 +631,7 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction } /** - * Used by UDF implementations (both Java code generated by {@link JavaBasedUDFunction} - * and script executor {@link ScriptBasedUDFunction}) to convert the C* + * Used by UDF implementations (both Java code generated by {@link JavaBasedUDFunction}) to convert the C* * serialized representation to the Java object representation. * * @param protocolVersion the native protocol version used for serialization @@ -655,8 +648,7 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction } /** - * Used by UDF implementations (both Java code generated by {@link JavaBasedUDFunction} - * and script executor {@link ScriptBasedUDFunction}) to convert the Java + * Used by UDF implementations (both Java code generated by {@link JavaBasedUDFunction}) to convert the Java * object representation for the return value to the C* serialized representation. * * @param protocolVersion the native protocol version used for serialization diff --git a/test/conf/cassandra-murmur.yaml b/test/conf/cassandra-murmur.yaml index c0c2ae71f6..385ed4632e 100644 --- a/test/conf/cassandra-murmur.yaml +++ b/test/conf/cassandra-murmur.yaml @@ -39,6 +39,6 @@ compaction_throughput: 0MiB/s row_cache_class_name: org.apache.cassandra.cache.OHCProvider row_cache_size: 16MiB user_defined_functions_enabled: true -scripted_user_defined_functions_enabled: true +scripted_user_defined_functions_enabled: false sasi_indexes_enabled: true materialized_views_enabled: true diff --git a/test/conf/cassandra-old.yaml b/test/conf/cassandra-old.yaml index 86983acc9e..10981c22cb 100644 --- a/test/conf/cassandra-old.yaml +++ b/test/conf/cassandra-old.yaml @@ -43,7 +43,7 @@ compaction_throughput_mb_per_sec: 64 row_cache_class_name: org.apache.cassandra.cache.OHCProvider row_cache_size_in_mb: 16 enable_user_defined_functions: true -enable_scripted_user_defined_functions: true +enable_scripted_user_defined_functions: false prepared_statements_cache_size_mb: corrupted_tombstone_strategy: exception stream_entire_sstables: true diff --git a/test/conf/cassandra-pem-jks-sslcontextfactory.yaml b/test/conf/cassandra-pem-jks-sslcontextfactory.yaml index 1f10e6c203..ad9d998551 100644 --- a/test/conf/cassandra-pem-jks-sslcontextfactory.yaml +++ b/test/conf/cassandra-pem-jks-sslcontextfactory.yaml @@ -140,7 +140,7 @@ compaction_throughput: 0MiB/s row_cache_class_name: org.apache.cassandra.cache.OHCProvider row_cache_size: 16MiB user_defined_functions_enabled: true -scripted_user_defined_functions_enabled: true +scripted_user_defined_functions_enabled: false prepared_statements_cache_size: 1MiB corrupted_tombstone_strategy: exception stream_entire_sstables: true diff --git a/test/conf/cassandra-pem-sslcontextfactory-invalidconfiguration.yaml b/test/conf/cassandra-pem-sslcontextfactory-invalidconfiguration.yaml index 8c7d910f49..15aaae0148 100644 --- a/test/conf/cassandra-pem-sslcontextfactory-invalidconfiguration.yaml +++ b/test/conf/cassandra-pem-sslcontextfactory-invalidconfiguration.yaml @@ -137,7 +137,7 @@ compaction_throughput: 0MiB/s row_cache_class_name: org.apache.cassandra.cache.OHCProvider row_cache_size: 16MiB user_defined_functions_enabled: true -scripted_user_defined_functions_enabled: true +scripted_user_defined_functions_enabled: false prepared_statements_cache_size: 1MiB corrupted_tombstone_strategy: exception stream_entire_sstables: true diff --git a/test/conf/cassandra-pem-sslcontextfactory.yaml b/test/conf/cassandra-pem-sslcontextfactory.yaml index 26d0f1f998..36daecd117 100644 --- a/test/conf/cassandra-pem-sslcontextfactory.yaml +++ b/test/conf/cassandra-pem-sslcontextfactory.yaml @@ -141,7 +141,7 @@ compaction_throughput: 0MiB/s row_cache_class_name: org.apache.cassandra.cache.OHCProvider row_cache_size: 16MiB user_defined_functions_enabled: true -scripted_user_defined_functions_enabled: true +scripted_user_defined_functions_enabled: false prepared_statements_cache_size: 1MiB corrupted_tombstone_strategy: exception stream_entire_sstables: true diff --git a/test/conf/cassandra-seeds.yaml b/test/conf/cassandra-seeds.yaml index 1c38f8e1ad..2a206f11d7 100644 --- a/test/conf/cassandra-seeds.yaml +++ b/test/conf/cassandra-seeds.yaml @@ -40,4 +40,4 @@ compaction_throughput: 0MiB/s row_cache_class_name: org.apache.cassandra.cache.OHCProvider row_cache_size: 16MiB user_defined_functions_enabled: true -scripted_user_defined_functions_enabled: true +scripted_user_defined_functions_enabled: false diff --git a/test/conf/cassandra-sslcontextfactory-invalidconfiguration.yaml b/test/conf/cassandra-sslcontextfactory-invalidconfiguration.yaml index d3970cbf98..6e6b21f9b3 100644 --- a/test/conf/cassandra-sslcontextfactory-invalidconfiguration.yaml +++ b/test/conf/cassandra-sslcontextfactory-invalidconfiguration.yaml @@ -72,7 +72,7 @@ compaction_throughput: 0MiB/s row_cache_class_name: org.apache.cassandra.cache.OHCProvider row_cache_size: 16MiB user_defined_functions_enabled: true -scripted_user_defined_functions_enabled: true +scripted_user_defined_functions_enabled: false prepared_statements_cache_size: 1MiB corrupted_tombstone_strategy: exception stream_entire_sstables: true diff --git a/test/conf/cassandra-sslcontextfactory.yaml b/test/conf/cassandra-sslcontextfactory.yaml index fde4bfdc6d..add166658a 100644 --- a/test/conf/cassandra-sslcontextfactory.yaml +++ b/test/conf/cassandra-sslcontextfactory.yaml @@ -75,7 +75,7 @@ compaction_throughput: 0MiB/s row_cache_class_name: org.apache.cassandra.cache.OHCProvider row_cache_size: 16MiB user_defined_functions_enabled: true -scripted_user_defined_functions_enabled: true +scripted_user_defined_functions_enabled: false prepared_statements_cache_size: 1MiB corrupted_tombstone_strategy: exception stream_entire_sstables: true diff --git a/test/conf/cassandra.yaml b/test/conf/cassandra.yaml index 3329ed9f36..f98207879c 100644 --- a/test/conf/cassandra.yaml +++ b/test/conf/cassandra.yaml @@ -43,7 +43,7 @@ compaction_throughput: 0MiB/s row_cache_class_name: org.apache.cassandra.cache.OHCProvider row_cache_size: 16MiB user_defined_functions_enabled: true -scripted_user_defined_functions_enabled: true +scripted_user_defined_functions_enabled: false prepared_statements_cache_size: 1MiB corrupted_tombstone_strategy: exception stream_entire_sstables: true diff --git a/test/unit/org/apache/cassandra/audit/AuditLoggerTest.java b/test/unit/org/apache/cassandra/audit/AuditLoggerTest.java index b4ae02b6da..94bff48f5e 100644 --- a/test/unit/org/apache/cassandra/audit/AuditLoggerTest.java +++ b/test/unit/org/apache/cassandra/audit/AuditLoggerTest.java @@ -495,7 +495,7 @@ public class AuditLoggerTest extends CQLTester { String tblName = createTableName(); - String cql = "CREATE FUNCTION IF NOT EXISTS " + KEYSPACE + "." + tblName + " (column TEXT,num int) RETURNS NULL ON NULL INPUT RETURNS text LANGUAGE javascript AS $$ column.substring(0,num) $$"; + String cql = "CREATE FUNCTION IF NOT EXISTS " + KEYSPACE + "." + tblName + " (column TEXT,num int) RETURNS NULL ON NULL INPUT RETURNS text LANGUAGE java AS $$ return column.substring(0,num); $$"; executeAndAssert(cql, AuditLogEntryType.CREATE_FUNCTION); cql = "DROP FUNCTION " + KEYSPACE + "." + tblName; diff --git a/test/unit/org/apache/cassandra/config/LoadOldYAMLBackwardCompatibilityTest.java b/test/unit/org/apache/cassandra/config/LoadOldYAMLBackwardCompatibilityTest.java index 0e028cb161..e0ce161fed 100644 --- a/test/unit/org/apache/cassandra/config/LoadOldYAMLBackwardCompatibilityTest.java +++ b/test/unit/org/apache/cassandra/config/LoadOldYAMLBackwardCompatibilityTest.java @@ -105,7 +105,7 @@ public class LoadOldYAMLBackwardCompatibilityTest assertEquals(new DurationSpec.IntSecondsBound(604800), config.trace_type_repair_ttl); assertNull(config.prepared_statements_cache_size); assertTrue(config.user_defined_functions_enabled); - assertTrue(config.scripted_user_defined_functions_enabled); + assertFalse(config.scripted_user_defined_functions_enabled); assertTrue(config.materialized_views_enabled); assertFalse(config.transient_replication_enabled); assertTrue(config.sasi_indexes_enabled); diff --git a/test/unit/org/apache/cassandra/cql3/selection/SelectionColumnMappingTest.java b/test/unit/org/apache/cassandra/cql3/selection/SelectionColumnMappingTest.java index d6de5ffc2b..659f34a737 100644 --- a/test/unit/org/apache/cassandra/cql3/selection/SelectionColumnMappingTest.java +++ b/test/unit/org/apache/cassandra/cql3/selection/SelectionColumnMappingTest.java @@ -534,8 +534,8 @@ public class SelectionColumnMappingTest extends CQLTester " CREATE FUNCTION %s (a int, b int)" + " RETURNS NULL ON NULL INPUT" + " RETURNS int" + - " LANGUAGE javascript" + - " AS 'a + b'")).name; + " LANGUAGE java" + + " AS ' return a + b;'")).name; String aFunc = createAggregate(KEYSPACE, "int, int", " CREATE AGGREGATE %s (int)" + @@ -547,15 +547,15 @@ public class SelectionColumnMappingTest extends CQLTester " CREATE FUNCTION %s (a int)" + " RETURNS NULL ON NULL INPUT" + " RETURNS int" + - " LANGUAGE javascript" + - " AS 'a+1'"); + " LANGUAGE java" + + " AS ' return a + 1;'"); String sqFunc = createFunction(KEYSPACE, "int", " CREATE FUNCTION %s (a int)" + " RETURNS NULL ON NULL INPUT" + " RETURNS int" + - " LANGUAGE javascript" + - " AS 'a*a'"); + " LANGUAGE java" + + " AS ' return a * a;'"); ColumnMetadata v1 = columnDefinition("v1"); SelectionColumns expected = SelectionColumnMapping.newMapping() diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UFPureScriptTupleCollectionTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UFPureScriptTupleCollectionTest.java deleted file mode 100644 index a1801b3f5c..0000000000 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/UFPureScriptTupleCollectionTest.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.cassandra.cql3.validation.entities; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; - -import org.junit.Test; - -import com.datastax.driver.core.DataType; -import com.datastax.driver.core.TupleType; -import com.datastax.driver.core.TupleValue; -import org.apache.cassandra.cql3.CQLTester; -import org.apache.cassandra.transport.ProtocolVersion; - -public class UFPureScriptTupleCollectionTest extends CQLTester -{ - // Just JavaScript UDFs to check how UDF - especially security/class-loading/sandboxing stuff - - // behaves, if no Java UDF has been executed before. - - // Do not add any other test here! - // See CASSANDRA-10141 - - @Test - public void testJavascriptTupleTypeCollection() throws Throwable - { - String tupleTypeDef = "tuple, set, map>"; - createTable("CREATE TABLE %s (key int primary key, tup frozen<" + tupleTypeDef + ">)"); - - String fTup1 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef, - "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS tuple, set, map> " + - "LANGUAGE javascript\n" + - "AS $$" + - " tup;$$;"); - String fTup2 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef, - "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE javascript\n" + - "AS $$" + - " tup.getDouble(0);$$;"); - String fTup3 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef, - "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS list " + - "LANGUAGE javascript\n" + - "AS $$" + - " tup.getList(1, java.lang.Double.class);$$;"); - String fTup4 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef, - "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS set " + - "LANGUAGE javascript\n" + - "AS $$" + - " tup.getSet(2, java.lang.String.class);$$;"); - String fTup5 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef, - "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS map " + - "LANGUAGE javascript\n" + - "AS $$" + - " tup.getMap(3, java.lang.Integer.class, java.lang.Boolean.class);$$;"); - - List list = Arrays.asList(1d, 2d, 3d); - Set set = new TreeSet<>(Arrays.asList("one", "three", "two")); - Map map = new TreeMap<>(); - map.put(1, true); - map.put(2, false); - map.put(3, true); - - Object t = tuple(1d, list, set, map); - - execute("INSERT INTO %s (key, tup) VALUES (1, ?)", t); - - assertRows(execute("SELECT " + fTup1 + "(tup) FROM %s WHERE key = 1"), - row(t)); - assertRows(execute("SELECT " + fTup2 + "(tup) FROM %s WHERE key = 1"), - row(1d)); - assertRows(execute("SELECT " + fTup3 + "(tup) FROM %s WHERE key = 1"), - row(list)); - assertRows(execute("SELECT " + fTup4 + "(tup) FROM %s WHERE key = 1"), - row(set)); - assertRows(execute("SELECT " + fTup5 + "(tup) FROM %s WHERE key = 1"), - row(map)); - - // same test - but via native protocol - // we use protocol V3 here to encode the expected version because the server - // always serializes Collections using V3 - see CollectionSerializer's - // serialize and deserialize methods. - TupleType tType = tupleTypeOf(ProtocolVersion.V3, - DataType.cdouble(), - DataType.list(DataType.cdouble()), - DataType.set(DataType.text()), - DataType.map(DataType.cint(), - DataType.cboolean())); - TupleValue tup = tType.newValue(1d, list, set, map); - for (ProtocolVersion version : PROTOCOL_VERSIONS) - { - assertRowsNet(version, - executeNet(version, "SELECT " + fTup1 + "(tup) FROM %s WHERE key = 1"), - row(tup)); - assertRowsNet(version, - executeNet(version, "SELECT " + fTup2 + "(tup) FROM %s WHERE key = 1"), - row(1d)); - assertRowsNet(version, - executeNet(version, "SELECT " + fTup3 + "(tup) FROM %s WHERE key = 1"), - row(list)); - assertRowsNet(version, - executeNet(version, "SELECT " + fTup4 + "(tup) FROM %s WHERE key = 1"), - row(set)); - assertRowsNet(version, - executeNet(version, "SELECT " + fTup5 + "(tup) FROM %s WHERE key = 1"), - row(map)); - } - } -} diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UFScriptTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UFScriptTest.java deleted file mode 100644 index fb33e6fabd..0000000000 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/UFScriptTest.java +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.cassandra.cql3.validation.entities; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.UUID; - -import org.junit.Assert; -import org.junit.Test; - -import org.apache.cassandra.config.DatabaseDescriptor; -import org.apache.cassandra.cql3.CQLTester; -import org.apache.cassandra.cql3.UntypedResultSet; -import org.apache.cassandra.cql3.functions.FunctionName; -import org.apache.cassandra.exceptions.FunctionExecutionException; -import org.apache.cassandra.transport.ProtocolVersion; -import org.apache.cassandra.utils.TimeUUID; - -import static org.apache.cassandra.utils.TimeUUID.Generator.nextTimeUUID; - -public class UFScriptTest extends CQLTester -{ - // Just JavaScript UDFs to check how UDF - especially security/class-loading/sandboxing stuff - - // behaves, if no Java UDF has been executed before. - - // Do not add any other test here - especially none using Java UDFs - - @Test - public void testJavascriptSimpleCollections() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, lst list, st set, mp map)"); - - String fName1 = createFunction(KEYSPACE_PER_TEST, "list", - "CREATE FUNCTION %s( lst list ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS list " + - "LANGUAGE javascript\n" + - "AS 'lst;';"); - String fName2 = createFunction(KEYSPACE_PER_TEST, "set", - "CREATE FUNCTION %s( st set ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS set " + - "LANGUAGE javascript\n" + - "AS 'st;';"); - String fName3 = createFunction(KEYSPACE_PER_TEST, "map", - "CREATE FUNCTION %s( mp map ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS map " + - "LANGUAGE javascript\n" + - "AS 'mp;';"); - - List list = Arrays.asList(1d, 2d, 3d); - Set set = new TreeSet<>(Arrays.asList("one", "three", "two")); - Map map = new TreeMap<>(); - map.put(1, true); - map.put(2, false); - map.put(3, true); - - execute("INSERT INTO %s (key, lst, st, mp) VALUES (1, ?, ?, ?)", list, set, map); - - assertRows(execute("SELECT lst, st, mp FROM %s WHERE key = 1"), - row(list, set, map)); - - assertRows(execute("SELECT " + fName1 + "(lst), " + fName2 + "(st), " + fName3 + "(mp) FROM %s WHERE key = 1"), - row(list, set, map)); - - for (ProtocolVersion version : PROTOCOL_VERSIONS) - assertRowsNet(version, - executeNet(version, "SELECT " + fName1 + "(lst), " + fName2 + "(st), " + fName3 + "(mp) FROM %s WHERE key = 1"), - row(list, set, map)); - } - - @Test - public void testJavascriptTupleType() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, tup frozen>)"); - - String fName = createFunction(KEYSPACE_PER_TEST, "tuple", - "CREATE FUNCTION %s( tup tuple ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS tuple " + - "LANGUAGE javascript\n" + - "AS $$tup;$$;"); - - Object t = tuple(1d, "foo", 2, true); - - execute("INSERT INTO %s (key, tup) VALUES (1, ?)", t); - - assertRows(execute("SELECT tup FROM %s WHERE key = 1"), - row(t)); - - assertRows(execute("SELECT " + fName + "(tup) FROM %s WHERE key = 1"), - row(t)); - } - - @Test - public void testJavascriptUserType() throws Throwable - { - String type = createType("CREATE TYPE %s (txt text, i int)"); - - createTable("CREATE TABLE %s (key int primary key, udt frozen<" + type + ">)"); - - String fUdt1 = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS " + type + ' ' + - "LANGUAGE javascript\n" + - "AS $$" + - " udt;$$;"); - String fUdt2 = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE javascript\n" + - "AS $$" + - " udt.getString(\"txt\");$$;"); - String fUdt3 = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE javascript\n" + - "AS $$" + - " udt.getInt(\"i\");$$;"); - - execute("INSERT INTO %s (key, udt) VALUES (1, {txt: 'one', i:1})"); - - UntypedResultSet rows = execute("SELECT " + fUdt1 + "(udt) FROM %s WHERE key = 1"); - Assert.assertEquals(1, rows.size()); - assertRows(execute("SELECT " + fUdt2 + "(udt) FROM %s WHERE key = 1"), - row("one")); - assertRows(execute("SELECT " + fUdt3 + "(udt) FROM %s WHERE key = 1"), - row(1)); - } - - @Test - public void testJavascriptUTCollections() throws Throwable - { - String type = createType("CREATE TYPE %s (txt text, i int)"); - - createTable(String.format("CREATE TABLE %%s " + - "(key int primary key, lst list>, st set>, mp map>)", - type, type, type)); - - String fName = createFunction(KEYSPACE, "list>", - "CREATE FUNCTION %s( lst list> ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE javascript\n" + - "AS $$" + - " lst.get(1).getString(\"txt\");$$;"); - createFunctionOverload(fName, "set>", - "CREATE FUNCTION %s( st set> ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE javascript\n" + - "AS $$" + - " st.iterator().next().getString(\"txt\");$$;"); - createFunctionOverload(fName, "map>", - "CREATE FUNCTION %s( mp map> ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE javascript\n" + - "AS $$" + - " mp.get(java.lang.Integer.valueOf(3)).getString(\"txt\");$$;"); - - execute("INSERT INTO %s (key, lst, st, mp) values (1, " + - // list> - "[ {txt: 'one', i:1}, {txt: 'three', i:1}, {txt: 'one', i:1} ] , " + - // set> - "{ {txt: 'one', i:1}, {txt: 'three', i:3}, {txt: 'two', i:2} }, " + - // map> - "{ 1: {txt: 'one', i:1}, 2: {txt: 'one', i:3}, 3: {txt: 'two', i:2} })"); - - assertRows(execute("SELECT " + fName + "(lst) FROM %s WHERE key = 1"), - row("three")); - assertRows(execute("SELECT " + fName + "(st) FROM %s WHERE key = 1"), - row("one")); - assertRows(execute("SELECT " + fName + "(mp) FROM %s WHERE key = 1"), - row("two")); - - String cqlSelect = "SELECT " + fName + "(lst), " + fName + "(st), " + fName + "(mp) FROM %s WHERE key = 1"; - assertRows(execute(cqlSelect), - row("three", "one", "two")); - - // same test - but via native protocol - for (ProtocolVersion version : PROTOCOL_VERSIONS) - assertRowsNet(version, - executeNet(version, cqlSelect), - row("three", "one", "two")); - } - - @Test - public void testJavascriptFunction() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - - String functionBody = '\n' + - " Math.sin(val);\n"; - - String fName = createFunction(KEYSPACE, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE javascript\n" + - "AS '" + functionBody + "';"); - - FunctionName fNameName = parseFunctionName(fName); - - assertRows(execute("SELECT language, body FROM system_schema.functions WHERE keyspace_name=? AND function_name=?", - fNameName.keyspace, fNameName.name), - row("javascript", functionBody)); - - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d); - assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), - row(1, 1d, Math.sin(1d)), - row(2, 2d, Math.sin(2d)), - row(3, 3d, Math.sin(3d)) - ); - } - - @Test - public void testJavascriptBadReturnType() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - - String fName = createFunction(KEYSPACE, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE javascript\n" + - "AS '\"string\";';"); - - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); - // throws IRE with ClassCastException - assertInvalidMessage("Invalid value for CQL type double", "SELECT key, val, " + fName + "(val) FROM %s"); - } - - @Test - public void testJavascriptThrow() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - - String fName = createFunction(KEYSPACE, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE javascript\n" + - "AS 'throw \"fool\";';"); - - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); - // throws IRE with ScriptException - assertInvalidThrowMessage("fool", FunctionExecutionException.class, - "SELECT key, val, " + fName + "(val) FROM %s"); - } - - @Test - public void testScriptReturnTypeCasting() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); - - Object[][] variations = { - new Object[] { "true", "boolean", true }, - new Object[] { "false", "boolean", false }, - new Object[] { "100", "tinyint", (byte)100 }, - new Object[] { "100.", "tinyint", (byte)100 }, - new Object[] { "100", "smallint", (short)100 }, - new Object[] { "100.", "smallint", (short)100 }, - new Object[] { "100", "int", 100 }, - new Object[] { "100.", "int", 100 }, - new Object[] { "100", "double", 100d }, - new Object[] { "100.", "double", 100d }, - new Object[] { "100", "bigint", 100L }, - new Object[] { "100.", "bigint", 100L }, - new Object[] { "100", "varint", BigInteger.valueOf(100L) }, - new Object[] { "100.", "varint", BigInteger.valueOf(100L) }, - new Object[] { "parseInt(\"100\");", "decimal", BigDecimal.valueOf(100d) }, - new Object[] { "100.", "decimal", BigDecimal.valueOf(100d) }, - }; - - for (Object[] variation : variations) - { - Object functionBody = variation[0]; - Object returnType = variation[1]; - Object expectedResult = variation[2]; - - String fName = createFunction(KEYSPACE, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS " +returnType + ' ' + - "LANGUAGE javascript " + - "AS '" + functionBody + ";';"); - assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), - row(1, 1d, expectedResult)); - } - } - - @Test - public void testScriptParamReturnTypes() throws Throwable - { - UUID ruuid = UUID.randomUUID(); - TimeUUID tuuid = nextTimeUUID(); - - createTable("CREATE TABLE %s (key int primary key, " + - "tival tinyint, sival smallint, ival int, lval bigint, fval float, dval double, vval varint, ddval decimal, " + - "timval time, dtval date, tsval timestamp, uval uuid, tuval timeuuid)"); - execute("INSERT INTO %s (key, tival, sival, ival, lval, fval, dval, vval, ddval, timval, dtval, tsval, uval, tuval) VALUES " + - "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", 1, - (byte)1, (short)1, 1, 1L, 1f, 1d, BigInteger.valueOf(1L), BigDecimal.valueOf(1d), 1L, Integer.MAX_VALUE, new Date(1), ruuid, tuuid); - - Object[][] variations = { - new Object[] { "tinyint", "tival", (byte)1, (byte)2 }, - new Object[] { "smallint", "sival", (short)1, (short)2 }, - new Object[] { "int", "ival", 1, 2 }, - new Object[] { "bigint", "lval", 1L, 2L }, - new Object[] { "float", "fval", 1f, 2f }, - new Object[] { "double", "dval", 1d, 2d }, - new Object[] { "varint", "vval", BigInteger.valueOf(1L), BigInteger.valueOf(2L) }, - new Object[] { "decimal", "ddval", BigDecimal.valueOf(1d), BigDecimal.valueOf(2d) }, - new Object[] { "time", "timval", 1L, 2L }, - }; - - for (Object[] variation : variations) - { - Object type = variation[0]; - Object col = variation[1]; - Object expected1 = variation[2]; - Object expected2 = variation[3]; - String fName = createFunction(KEYSPACE, type.toString(), - "CREATE OR REPLACE FUNCTION %s(val " + type + ") " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS " + type + ' ' + - "LANGUAGE javascript " + - "AS 'val+1;';"); - assertRows(execute("SELECT key, " + col + ", " + fName + '(' + col + ") FROM %s"), - row(1, expected1, expected2)); - } - - variations = new Object[][] { - new Object[] { "timestamp","tsval", new Date(1), new Date(1) }, - new Object[] { "uuid", "uval", ruuid, ruuid }, - new Object[] { "timeuuid", "tuval", tuuid, tuuid }, - new Object[] { "date", "dtval", Integer.MAX_VALUE, Integer.MAX_VALUE }, - }; - - for (Object[] variation : variations) - { - Object type = variation[0]; - Object col = variation[1]; - Object expected1 = variation[2]; - Object expected2 = variation[3]; - String fName = createFunction(KEYSPACE, type.toString(), - "CREATE OR REPLACE FUNCTION %s(val " + type + ") " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS " + type + ' ' + - "LANGUAGE javascript " + - "AS 'val;';"); - assertRows(execute("SELECT key, " + col + ", " + fName + '(' + col + ") FROM %s"), - row(1, expected1, expected2)); - } - } - - @Test - public void testJavascriptDisabled() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - - DatabaseDescriptor.enableScriptedUserDefinedFunctions(false); - try - { - assertInvalid("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".assertNotEnabled(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE javascript\n" + - "AS 'Math.sin(val);';"); - } - finally - { - DatabaseDescriptor.enableScriptedUserDefinedFunctions(true); - } - } - - @Test - public void testJavascriptCompileFailure() throws Throwable - { - assertInvalidMessage("Failed to compile function 'cql_test_keyspace.scrinv'", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".scrinv(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE javascript\n" + - "AS 'foo bar';"); - } - - @Test - public void testScriptInvalidLanguage() throws Throwable - { - assertInvalidMessage("Invalid language 'artificial_intelligence' for function 'cql_test_keyspace.scrinv'", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".scrinv(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE artificial_intelligence\n" + - "AS 'question for 42?';"); - } -} diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UFSecurityTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UFSecurityTest.java index 7422db4351..96a0cbc329 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/UFSecurityTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UFSecurityTest.java @@ -130,74 +130,6 @@ public class UFSecurityTest extends CQLTester "LANGUAGE JAVA\n" + "AS '" + typeAndSource[1] + "';"); } - - // JavaScript UDFs - - try - { - String fName = createFunction(KEYSPACE_PER_TEST, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE javascript\n" + - "AS 'org.apache.cassandra.service.StorageService.instance.isShutdown(); 0;';"); - execute("SELECT " + fName + "(dval) FROM %s WHERE key=1"); - Assert.fail("Javascript security check failed"); - } - catch (FunctionExecutionException e) - { - assertAccessControlException("", e); - } - - String[] javascript = - { - "java.lang.management.ManagmentFactory.getThreadMXBean(); 0;", - "new java.io.FileInputStream(\"/tmp/foo\"); 0;", - "new java.io.FileOutputStream(\"/tmp/foo\"); 0;", - "java.nio.file.FileSystems.getDefault().createFileExclusively(\"./foo_bar_baz\"); 0;", - "java.nio.channels.FileChannel.open(java.nio.file.FileSystems.getDefault().getPath(\"/etc/passwd\")); 0;", - "java.nio.channels.SocketChannel.open(); 0;", - "new java.net.ServerSocket().bind(null); 0;", - "var thread = new java.lang.Thread(); thread.start(); 0;", - "java.lang.System.getProperty(\"foo.bar.baz\"); 0;", - "java.lang.Runtime.getRuntime().exec(\"/tmp/foo\"); 0;", - "java.lang.Runtime.getRuntime().loadLibrary(\"foobar\"); 0;", - "java.lang.Runtime.getRuntime().loadLibrary(\"foobar\"); 0;", - // TODO these (ugly) calls are still possible - these can consume CPU (as one could do with an evil loop, too) -// "java.lang.Runtime.getRuntime().traceMethodCalls(true); 0;", -// "java.lang.Runtime.getRuntime().gc(); 0;", -// "java.lang.Runtime.getRuntime(); 0;", - }; - - for (String script : javascript) - { - try - { - String fName = createFunction(KEYSPACE_PER_TEST, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE javascript\n" + - "AS '" + script + "';"); - execute("SELECT " + fName + "(dval) FROM %s WHERE key=1"); - Assert.fail("Javascript security check failed: " + script); - } - catch (FunctionExecutionException e) - { - assertAccessControlException(script, e); - } - } - - String script = "java.lang.Class.forName(\"java.lang.System\"); 0;"; - String fName = createFunction(KEYSPACE_PER_TEST, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE javascript\n" + - "AS '" + script + "';"); - assertInvalidThrowMessage("Java reflection not supported when class filter is present", - FunctionExecutionException.class, - "SELECT " + fName + "(dval) FROM %s WHERE key=1"); } private static void assertAccessControlException(String script, FunctionExecutionException e) @@ -250,16 +182,6 @@ public class UFSecurityTest extends CQLTester "AS 'long t=System.currentTimeMillis()+500; while (t>System.currentTimeMillis()) { }; return 0d;';"); assertInvalidMessage("ran longer than 250ms", "SELECT " + fName + "(dval) FROM %s WHERE key=1"); - // Javascript UDF - - fName = createFunction(KEYSPACE_PER_TEST, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE JAVASCRIPT\n" + - "AS 'var t=java.lang.System.currentTimeMillis()+500; while (t>java.lang.System.currentTimeMillis()) { }; 0;';"); - assertInvalidMessage("ran longer than 250ms", "SELECT " + fName + "(dval) FROM %s WHERE key=1"); - return; } catch (Error | RuntimeException e) diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java index a092c04e97..bd1383eb11 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java @@ -87,17 +87,17 @@ public class UFTest extends CQLTester } @Test - public void testSchemaChange() throws Throwable + public void testSchemaChange() { String f = createFunctionName(KEYSPACE); String functionName = shortFunctionName(f); registerFunction(f, "double, double"); - assertSchemaChange("CREATE OR REPLACE FUNCTION " + f + "(state double, val double) " + - "RETURNS NULL ON NULL INPUT " + + assertSchemaChange("CREATE OR REPLACE FUNCTION " + f + "(state double, val double)" + + "CALLED ON NULL INPUT " + "RETURNS double " + - "LANGUAGE javascript " + - "AS '\"string\";';", + "LANGUAGE java " + + "AS ' return Double.valueOf(Math.max(state.doubleValue(), val.doubleValue())); ';", Change.CREATED, Target.FUNCTION, KEYSPACE, functionName, @@ -108,22 +108,22 @@ public class UFTest extends CQLTester assertSchemaChange("CREATE OR REPLACE FUNCTION " + f + "(state int, val int) " + "RETURNS NULL ON NULL INPUT " + "RETURNS int " + - "LANGUAGE javascript " + - "AS '\"string\";';", + "LANGUAGE java " + + "AS ' return Integer.valueOf(Math.max(state, val));';", Change.CREATED, Target.FUNCTION, KEYSPACE, functionName, "int", "int"); - assertSchemaChange("CREATE OR REPLACE FUNCTION " + f + "(state int, val int) " + + /*assertSchemaChange("CREATE OR REPLACE FUNCTION " + f + "(state int, val int) " + "RETURNS NULL ON NULL INPUT " + "RETURNS int " + - "LANGUAGE javascript " + - "AS '\"string1\";';", + "LANGUAGE java " + + "AS ' return Integer.valueOf(Math.max(state, val));';", Change.UPDATED, Target.FUNCTION, KEYSPACE, functionName, - "int", "int"); + "int", "int");*/ assertSchemaChange("DROP FUNCTION " + f + "(double, double)", Change.DROPPED, Target.FUNCTION, @@ -137,8 +137,8 @@ public class UFTest extends CQLTester assertSchemaChange("CREATE OR REPLACE FUNCTION " + fl + "(state list>, val double) " + "RETURNS NULL ON NULL INPUT " + "RETURNS double " + - "LANGUAGE javascript " + - "AS '\"string\";';", + "LANGUAGE java " + + "AS ' return val;';", Change.CREATED, Target.FUNCTION, KEYSPACE, shortFunctionName(fl), "list>", "double"); @@ -333,8 +333,8 @@ public class UFTest extends CQLTester "CREATE FUNCTION %s ( input double ) " + "CALLED ON NULL INPUT " + "RETURNS double " + - "LANGUAGE javascript " + - "AS 'input'"); + "LANGUAGE java " + + "AS 'return Double.valueOf(Math.log(input.doubleValue()));'"); Assert.assertEquals(1, Schema.instance.getUserFunctions(parseFunctionName(function)).size()); List prepared = new ArrayList<>(); @@ -793,11 +793,10 @@ public class UFTest extends CQLTester public void testDuplicateArgNames() throws Throwable { assertInvalidMessage("Duplicate argument names for given function", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".scrinv(val double, val text) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE javascript\n" + - "AS '\"foo bar\";';"); + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".scrinv(input double, input int) " + + "CALLED ON NULL INPUT " + + "RETURNS double " + + "LANGUAGE java AS 'return Math.max(input, input)';"); } @Test diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java index bfcf38e153..b078694dd6 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java @@ -512,16 +512,16 @@ public class AggregationTest extends CQLTester "CREATE OR REPLACE FUNCTION %s(state double, val double) " + "RETURNS NULL ON NULL INPUT " + "RETURNS double " + - "LANGUAGE javascript " + - "AS '\"string\";';"); + "LANGUAGE java " + + "AS ' return state;';"); createFunctionOverload(f, "double, double", "CREATE OR REPLACE FUNCTION %s(state int, val int) " + "RETURNS NULL ON NULL INPUT " + "RETURNS int " + - "LANGUAGE javascript " + - "AS '\"string\";';"); + "LANGUAGE java " + + "AS 'return state;';"); String a = createAggregateName(KEYSPACE); String aggregateName = shortFunctionName(a); @@ -564,8 +564,8 @@ public class AggregationTest extends CQLTester "CREATE OR REPLACE FUNCTION %s(state double, val list>) " + "RETURNS NULL ON NULL INPUT " + "RETURNS double " + - "LANGUAGE javascript " + - "AS '\"string\";';"); + "LANGUAGE java " + + "AS ' return state;';"); String a1 = createAggregateName(KEYSPACE); registerAggregate(a1, "list>"); @@ -587,16 +587,16 @@ public class AggregationTest extends CQLTester "CREATE OR REPLACE FUNCTION %s(state double, val double) " + "RETURNS NULL ON NULL INPUT " + "RETURNS double " + - "LANGUAGE javascript " + - "AS '\"string\";';"); + "LANGUAGE java " + + "AS 'return state;';"); createFunctionOverload(f, "double, double", "CREATE OR REPLACE FUNCTION %s(state int, val int) " + "RETURNS NULL ON NULL INPUT " + "RETURNS int " + - "LANGUAGE javascript " + - "AS '\"string\";';"); + "LANGUAGE java " + + "AS ' return state;';"); // DROP AGGREGATE must not succeed against a scalar assertInvalidMessage("matches multiple function definitions", "DROP AGGREGATE " + f); @@ -639,8 +639,8 @@ public class AggregationTest extends CQLTester "CREATE OR REPLACE FUNCTION %s(state double, val double) " + "RETURNS NULL ON NULL INPUT " + "RETURNS double " + - "LANGUAGE javascript " + - "AS '\"string\";';"); + "LANGUAGE java " + + "AS ' return state;';"); String a = createAggregate(KEYSPACE, "double", @@ -1138,81 +1138,6 @@ public class AggregationTest extends CQLTester } - @Test - public void testJavascriptAggregate() throws Throwable - { - createTable("CREATE TABLE %s (a int primary key, b int)"); - execute("INSERT INTO %s (a, b) VALUES (1, 1)"); - execute("INSERT INTO %s (a, b) VALUES (2, 2)"); - execute("INSERT INTO %s (a, b) VALUES (3, 3)"); - - String fState = createFunction(KEYSPACE, - "int, int", - "CREATE FUNCTION %s(a int, b int) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE javascript " + - "AS 'a + b;'"); - - String fFinal = createFunction(KEYSPACE, - "int", - "CREATE FUNCTION %s(a int) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE javascript " + - "AS '\"\"+a'"); - - String a = createFunction(KEYSPACE, - "int", - "CREATE AGGREGATE %s(int) " + - "SFUNC " + shortFunctionName(fState) + " " + - "STYPE int " + - "FINALFUNC " + shortFunctionName(fFinal) + " " + - "INITCOND 42"); - - // 42 + 1 + 2 + 3 = 48 - assertRows(execute("SELECT " + a + "(b) FROM %s"), row("48")); - - execute("DROP AGGREGATE " + a + "(int)"); - - execute("DROP FUNCTION " + fFinal + "(int)"); - execute("DROP FUNCTION " + fState + "(int, int)"); - - assertInvalidMessage("Unknown function", "SELECT " + a + "(b) FROM %s"); - } - - @Test - public void testJavascriptAggregateSimple() throws Throwable - { - createTable("CREATE TABLE %s (a int primary key, b int)"); - execute("INSERT INTO %s (a, b) VALUES (1, 1)"); - execute("INSERT INTO %s (a, b) VALUES (2, 2)"); - execute("INSERT INTO %s (a, b) VALUES (3, 3)"); - - String fState = createFunction(KEYSPACE, - "int, int", - "CREATE FUNCTION %s(a int, b int) " + - "CALLED ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE javascript " + - "AS 'a + b;'"); - - String a = createAggregate(KEYSPACE, - "int, int", - "CREATE AGGREGATE %s(int) " + - "SFUNC " + shortFunctionName(fState) + " " + - "STYPE int "); - - // 1 + 2 + 3 = 6 - assertRows(execute("SELECT " + a + "(b) FROM %s"), row(6)); - - execute("DROP AGGREGATE " + a + "(int)"); - - execute("DROP FUNCTION " + fState + "(int, int)"); - - assertInvalidMessage("Unknown function", "SELECT " + a + "(b) FROM %s"); - } - @Test public void testFunctionDropPreparedStatement() throws Throwable { @@ -1228,8 +1153,8 @@ public class AggregationTest extends CQLTester "CREATE FUNCTION %s(a int, b int) " + "CALLED ON NULL INPUT " + "RETURNS int " + - "LANGUAGE javascript " + - "AS 'a + b;'"); + "LANGUAGE java " + + "AS ' return a + b;'"); String a = createAggregate(otherKS, "int", @@ -1271,8 +1196,8 @@ public class AggregationTest extends CQLTester "CREATE FUNCTION %s(a int, b int) " + "CALLED ON NULL INPUT " + "RETURNS int " + - "LANGUAGE javascript " + - "AS 'a + b;'"); + "LANGUAGE java " + + "AS ' return a + b;'"); String a = createAggregate(KEYSPACE, "int",