mirror of https://github.com/apache/cassandra
Merge branch 'cassandra-4.1' into trunk
This commit is contained in:
commit
235d2df0ee
|
|
@ -158,6 +158,7 @@ Merged from 3.11:
|
|||
* Creating of a keyspace on insufficient number of replicas should filter out gosspping-only members (CASSANDRA-17759)
|
||||
* Suppress CVE-2022-25857 and other snakeyaml CVEs (CASSANDRA-17907)
|
||||
Merged from 3.0:
|
||||
* Fix incorrect resource name in LIST PERMISSION output (CASSANDRA-17848)
|
||||
* Suppress CVE-2022-41854 and similar (CASSANDRA-18083)
|
||||
* Fix running Ant rat targets without git (CASSANDRA-17974)
|
||||
* Harden JMX by resolving beanshooter issues (CASSANDRA-17921)
|
||||
|
|
|
|||
|
|
@ -171,15 +171,23 @@ public class FunctionResource implements IResource
|
|||
|
||||
/**
|
||||
* Parses a resource name into a FunctionResource instance.
|
||||
* A valid resource name for function should be in the follow format:
|
||||
* functions/KEYSPACE/FUNCTION_NAME[FUNCTION_ARGS]
|
||||
* Note that
|
||||
* 1. FUNCTION_NAME could contain any character due to the use of quoted text in CQL.
|
||||
* 2. FUNCTION_ARGS could be empty. If it is not empty, it is expressed in this format:
|
||||
* FUNCTION_ARG1^FUNCTION_ARG2... where ^ is the delimiter for arguments
|
||||
*
|
||||
* @param name Name of the function resource.
|
||||
* @return FunctionResource instance matching the name.
|
||||
*/
|
||||
public static FunctionResource fromName(String name)
|
||||
{
|
||||
String[] parts = StringUtils.split(name, '/');
|
||||
// Split the name into at most 3 parts.
|
||||
// The last part is the function name + args list, the name might contains '/'
|
||||
String[] parts = StringUtils.split(name, "/", 3);
|
||||
|
||||
if (!parts[0].equals(ROOT_NAME) || parts.length > 3)
|
||||
if (!parts[0].equals(ROOT_NAME))
|
||||
throw new IllegalArgumentException(String.format("%s is not a valid function resource name", name));
|
||||
|
||||
if (parts.length == 1)
|
||||
|
|
@ -190,8 +198,17 @@ public class FunctionResource implements IResource
|
|||
|
||||
if (!name.matches("^.+\\[.*\\]$"))
|
||||
throw new IllegalArgumentException(String.format("%s is not a valid function resource name. It must end with \"[]\"", name));
|
||||
String[] nameAndArgs = StringUtils.split(parts[2], "[|]");
|
||||
return function(parts[1], nameAndArgs[0], nameAndArgs.length > 1 ? argsListFromString(nameAndArgs[1]) : Collections.emptyList());
|
||||
|
||||
String function = parts[2];
|
||||
// The name must end with '[...]' block
|
||||
int lastStartingBracketIndex = function.lastIndexOf('[');
|
||||
String functionName = StringUtils.substring(function, 0, lastStartingBracketIndex);
|
||||
String functionArgs = StringUtils.substring(function,
|
||||
// excludes the wrapping brackets [ ]
|
||||
lastStartingBracketIndex + 1,
|
||||
function.length() - 1);
|
||||
|
||||
return function(parts[1], functionName, functionArgs.isEmpty() ? Collections.emptyList() : argsListFromString(functionArgs));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
*/
|
||||
package org.apache.cassandra.cql3.functions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import org.apache.cassandra.cql3.CqlBuilder;
|
||||
|
|
@ -24,6 +29,9 @@ import org.apache.cassandra.schema.SchemaConstants;
|
|||
|
||||
public final class FunctionName
|
||||
{
|
||||
private static final Set<Character> DISALLOWED_CHARACTERS = Collections.unmodifiableSet(
|
||||
new HashSet<>(Arrays.asList('/', '[', ']')));
|
||||
|
||||
// We special case the token function because that's the only function which name is a reserved keyword
|
||||
private static final FunctionName TOKEN_FUNCTION_NAME = FunctionName.nativeFunction("token");
|
||||
|
||||
|
|
@ -35,6 +43,24 @@ public final class FunctionName
|
|||
return new FunctionName(SchemaConstants.SYSTEM_KEYSPACE_NAME, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the function name, e.g. contains no disallowed characters
|
||||
* @param name
|
||||
* @return true if name is valid; otherwise, false
|
||||
*/
|
||||
public static boolean isNameValid(String name)
|
||||
{
|
||||
for (int i = 0; i < name.length(); i++)
|
||||
{
|
||||
char c = name.charAt(i);
|
||||
if (DISALLOWED_CHARACTERS.contains(c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public FunctionName(String keyspace, String name)
|
||||
{
|
||||
assert name != null : "Name parameter must not be null";
|
||||
|
|
|
|||
|
|
@ -96,6 +96,9 @@ public final class CreateAggregateStatement extends AlterSchemaStatement
|
|||
if (ifNotExists && orReplace)
|
||||
throw ire("Cannot use both 'OR REPLACE' and 'IF NOT EXISTS' directives");
|
||||
|
||||
if (!FunctionName.isNameValid(aggregateName))
|
||||
throw ire("Aggregate name '%s' is invalid", aggregateName);
|
||||
|
||||
rawArgumentTypes.stream()
|
||||
.filter(raw -> !raw.isTuple() && raw.isFrozen())
|
||||
.findFirst()
|
||||
|
|
|
|||
|
|
@ -89,6 +89,9 @@ public final class CreateFunctionStatement extends AlterSchemaStatement
|
|||
|
||||
UDFunction.assertUdfsEnabled(language);
|
||||
|
||||
if (!FunctionName.isNameValid(functionName))
|
||||
throw ire("Function name '%s' is invalid", functionName);
|
||||
|
||||
if (new HashSet<>(argumentNames).size() != argumentNames.size())
|
||||
throw ire("Duplicate argument names for given function %s with argument names %s", functionName, argumentNames);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
package org.apache.cassandra.auth;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
|
@ -37,7 +39,7 @@ public class FunctionResourceTest
|
|||
private static final String varType = "org.apache.cassandra.db.marshal.UTF8Type";
|
||||
|
||||
@Test
|
||||
public void testFunction() throws Exception
|
||||
public void testFunction()
|
||||
{
|
||||
FunctionResource expected = FunctionResource.root();
|
||||
FunctionResource actual = FunctionResource.fromName(func);
|
||||
|
|
@ -46,7 +48,7 @@ public class FunctionResourceTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionKeyspace() throws Exception
|
||||
public void testFunctionKeyspace()
|
||||
{
|
||||
FunctionResource expected = FunctionResource.keyspace(ks);
|
||||
FunctionResource actual = FunctionResource.fromName(String.format("%s/%s", func, ks));
|
||||
|
|
@ -55,7 +57,7 @@ public class FunctionResourceTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionWithSingleInputParameter() throws Exception
|
||||
public void testFunctionWithSingleInputParameter()
|
||||
{
|
||||
List<AbstractType<?>> argTypes = new ArrayList<>();
|
||||
argTypes.add(TypeParser.parse(varType));
|
||||
|
|
@ -66,7 +68,7 @@ public class FunctionResourceTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionWithMultipleInputParameter() throws Exception
|
||||
public void testFunctionWithMultipleInputParameter()
|
||||
{
|
||||
List<AbstractType<?>> argTypes = new ArrayList<>();
|
||||
argTypes.add(TypeParser.parse(varType));
|
||||
|
|
@ -78,7 +80,7 @@ public class FunctionResourceTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionWithoutInputParameter() throws Exception
|
||||
public void testFunctionWithoutInputParameter()
|
||||
{
|
||||
List<AbstractType<?>> argTypes = new ArrayList<>();
|
||||
FunctionResource expected = FunctionResource.function(ks, name, argTypes);
|
||||
|
|
@ -105,12 +107,21 @@ public class FunctionResourceTest
|
|||
@Test
|
||||
public void testFunctionWithInvalidInput()
|
||||
{
|
||||
String expected = String.format("%s/%s/%s[%s]/test is not a valid function resource name", func, ks, name, varType);
|
||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> FunctionResource.fromName(String.format("%s/%s/%s[%s]/test",
|
||||
func,
|
||||
ks,
|
||||
name,
|
||||
varType)))
|
||||
String invalidInput = String.format("%s/%s/%s[%s]/test", func, ks, name, varType);
|
||||
String expected = String.format("%s is not a valid function resource name. It must end with \"[]\"", invalidInput);
|
||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> FunctionResource.fromName(invalidInput))
|
||||
.withMessage(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionWithSpecialCharacter()
|
||||
{
|
||||
for (String funcName : Arrays.asList("my/fancy/func", "my_other[fancy]func"))
|
||||
{
|
||||
String input = String.format("%s/%s/%s[%s]", func, ks, funcName, varType);
|
||||
FunctionResource actual = FunctionResource.fromName(input);
|
||||
FunctionResource expected = FunctionResource.function(ks, funcName, Collections.singletonList(TypeParser.parse(varType)));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.apache.cassandra.cql3.validation.entities;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -26,7 +27,8 @@ import com.google.common.reflect.TypeToken;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.datastax.driver.core.*;
|
||||
import com.datastax.driver.core.TypeTokens;
|
||||
import com.datastax.driver.core.UDTValue;
|
||||
import com.datastax.driver.core.exceptions.InvalidQueryException;
|
||||
import org.apache.cassandra.cql3.CQLTester;
|
||||
import org.apache.cassandra.cql3.QueryProcessor;
|
||||
|
|
@ -46,6 +48,8 @@ import org.apache.cassandra.transport.ProtocolVersion;
|
|||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
public class UFTest extends CQLTester
|
||||
{
|
||||
@Test
|
||||
|
|
@ -1007,4 +1011,21 @@ public class UFTest extends CQLTester
|
|||
assertRows(execute("SELECT " + fNameICC + "(empty_int) FROM %s"), row(0));
|
||||
assertRows(execute("SELECT " + fNameICN + "(empty_int) FROM %s"), row(new Object[]{ null }));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectInvalidFunctionNamesOnCreation()
|
||||
{
|
||||
for (String funcName : Arrays.asList("my/fancy/func", "my_other[fancy]func"))
|
||||
{
|
||||
assertThatThrownBy(() -> {
|
||||
createFunctionOverload(String.format("%s.\"%s\"", KEYSPACE_PER_TEST, funcName), "int",
|
||||
"CREATE OR REPLACE FUNCTION %s(val int) " +
|
||||
"RETURNS NULL ON NULL INPUT " +
|
||||
"RETURNS int " +
|
||||
"LANGUAGE JAVA\n" +
|
||||
"AS 'return val;'");
|
||||
}).hasRootCauseInstanceOf(InvalidRequestException.class)
|
||||
.hasRootCauseMessage("Function name '%s' is invalid", funcName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ import org.apache.cassandra.transport.ProtocolVersion;
|
|||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
|
||||
import static ch.qos.logback.core.CoreConstants.RECONFIGURE_ON_CHANGE_TASK;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
|
@ -2232,4 +2233,20 @@ public class AggregationTest extends CQLTester
|
|||
assertRows(execute("select sum(v1), sum(v2), sum(v3) from %s;"),
|
||||
row((float) 15.3, 15.3, BigDecimal.valueOf(15.3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectInvalidAggregateNamesOnCreation()
|
||||
{
|
||||
for (String funcName : Arrays.asList("my/fancy/aggregate", "my_other[fancy]aggregate"))
|
||||
{
|
||||
assertThatThrownBy(() -> {
|
||||
createAggregateOverload(String.format("%s.\"%s\"", KEYSPACE_PER_TEST, funcName), "int",
|
||||
" CREATE AGGREGATE IF NOT EXISTS %s(text, text)\n" +
|
||||
" SFUNC func\n" +
|
||||
" STYPE map<text,bigint>\n" +
|
||||
" INITCOND { };");
|
||||
}).hasRootCauseInstanceOf(InvalidRequestException.class)
|
||||
.hasRootCauseMessage("Aggregate name '%s' is invalid", funcName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue