diff --git a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java index 0a112eb59f..ade69dd79b 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java @@ -25,7 +25,6 @@ import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.apache.cassandra.config.Schema; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.schema.Functions; @@ -57,7 +56,8 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction this.initcond = initcond; } - public static UDAggregate create(FunctionName name, + public static UDAggregate create(Functions functions, + FunctionName name, List> argTypes, AbstractType returnType, FunctionName stateFunc, @@ -73,8 +73,8 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction return new UDAggregate(name, argTypes, returnType, - resolveScalar(name, stateFunc, stateTypes), - finalFunc != null ? resolveScalar(name, finalFunc, finalTypes) : null, + resolveScalar(functions, name, stateFunc, stateTypes), + finalFunc != null ? resolveScalar(functions, name, finalFunc, finalTypes) : null, initcond); } @@ -194,9 +194,9 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction }; } - private static ScalarFunction resolveScalar(FunctionName aName, FunctionName fName, List> argTypes) throws InvalidRequestException + private static ScalarFunction resolveScalar(Functions functions, FunctionName aName, FunctionName fName, List> argTypes) throws InvalidRequestException { - Optional fun = Schema.instance.findFunction(fName, argTypes); + Optional fun = functions.find(fName, argTypes); if (!fun.isPresent()) throw new InvalidRequestException(String.format("Referenced state function '%s %s' for aggregate '%s' does not exist", fName, diff --git a/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java b/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java index f23ec0b2f6..39066da120 100644 --- a/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java +++ b/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java @@ -163,7 +163,9 @@ public final class LegacySchemaMigrator Collection tables = readTables(keyspaceName); Collection types = readTypes(keyspaceName); Collection functions = readFunctions(keyspaceName); - Collection aggregates = readAggregates(keyspaceName); + Functions.Builder functionsBuilder = Functions.builder(); + functions.forEach(udf -> functionsBuilder.add(udf.metadata)); + Collection aggregates = readAggregates(functionsBuilder.build(), keyspaceName); return new Keyspace(timestamp, keyspaceName, params, tables, types, functions, aggregates); } @@ -811,7 +813,7 @@ public final class LegacySchemaMigrator * Reading UDAs */ - private static Collection readAggregates(String keyspaceName) + private static Collection readAggregates(Functions functions, String keyspaceName) { String query = format("SELECT aggregate_name, signature FROM %s.%s WHERE keyspace_name = ?", SystemKeyspace.NAME, @@ -820,14 +822,14 @@ public final class LegacySchemaMigrator query(query, keyspaceName).forEach(row -> aggregateSignatures.put(row.getString("aggregate_name"), row.getList("signature", UTF8Type.instance))); Collection aggregates = new ArrayList<>(); - aggregateSignatures.entries().forEach(pair -> aggregates.add(readAggregate(keyspaceName, pair.getKey(), pair.getValue()))); + aggregateSignatures.entries().forEach(pair -> aggregates.add(readAggregate(functions, keyspaceName, pair.getKey(), pair.getValue()))); return aggregates; } - private static Aggregate readAggregate(String keyspaceName, String aggregateName, List signature) + private static Aggregate readAggregate(Functions functions, String keyspaceName, String aggregateName, List signature) { long timestamp = readAggregateTimestamp(keyspaceName, aggregateName, signature); - UDAggregate metadata = readAggregateMetadata(keyspaceName, aggregateName, signature); + UDAggregate metadata = readAggregateMetadata(functions, keyspaceName, aggregateName, signature); return new Aggregate(timestamp, metadata); } @@ -841,9 +843,9 @@ public final class LegacySchemaMigrator return query(query, keyspaceName, aggregateName, signature).one().getLong("timestamp"); } - private static UDAggregate readAggregateMetadata(String keyspaceName, String functionName, List signature) + private static UDAggregate readAggregateMetadata(Functions functions, String keyspaceName, String functionName, List signature) { - String query = format("SELECT * FROM %s.%s WHERE keyspace_name = ? AND function_name = ? AND signature = ?", + String query = format("SELECT * FROM %s.%s WHERE keyspace_name = ? AND aggregate_name = ? AND signature = ?", SystemKeyspace.NAME, SystemKeyspace.LEGACY_AGGREGATES); UntypedResultSet.Row row = query(query, keyspaceName, functionName, signature).one(); @@ -863,13 +865,13 @@ public final class LegacySchemaMigrator AbstractType returnType = parseType(row.getString("return_type")); FunctionName stateFunc = new FunctionName(keyspaceName, row.getString("state_func")); + AbstractType stateType = parseType(row.getString("state_type")); FunctionName finalFunc = row.has("final_func") ? new FunctionName(keyspaceName, row.getString("final_func")) : null; - AbstractType stateType = row.has("state_type") ? parseType(row.getString("state_type")) : null; ByteBuffer initcond = row.has("initcond") ? row.getBytes("initcond") : null; try { - return UDAggregate.create(name, argTypes, returnType, stateFunc, finalFunc, stateType, initcond); + return UDAggregate.create(functions, name, argTypes, returnType, stateFunc, finalFunc, stateType, initcond); } catch (InvalidRequestException reason) { diff --git a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java index f0bdd142a5..ca91b7b997 100644 --- a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java +++ b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java @@ -776,8 +776,8 @@ public final class SchemaKeyspace Types types = createTypesFromPartition(serializedTypes); Collection udfs = createFunctionsFromFunctionsPartition(serializedFunctions); - Collection udas = createAggregatesFromAggregatesPartition(serializedAggregates); - Functions functions = org.apache.cassandra.schema.Functions.builder().add(udfs).add(udas).build(); + Functions functions = org.apache.cassandra.schema.Functions.builder().add(udfs).build(); + functions = createAggregatesFromAggregatesPartition(functions, serializedAggregates); return KeyspaceMetadata.create(name, params, tables, views, types, functions); } @@ -1635,16 +1635,20 @@ public final class SchemaKeyspace .build(); } - private static Collection createAggregatesFromAggregatesPartition(RowIterator partition) + private static Functions createAggregatesFromAggregatesPartition(Functions functions, RowIterator partition) { - List aggregates = new ArrayList<>(); String query = String.format("SELECT * FROM %s.%s", NAME, AGGREGATES); for (UntypedResultSet.Row row : QueryProcessor.resultify(query, partition)) - aggregates.add(createAggregateFromAggregateRow(row)); - return aggregates; + functions = functions.with(createAggregateFromAggregateRow(functions, row)); + return functions; } private static UDAggregate createAggregateFromAggregateRow(UntypedResultSet.Row row) + { + return createAggregateFromAggregateRow(Schema.instance.getKSMetaData(row.getString("keyspace_name")).functions, row); + } + + private static UDAggregate createAggregateFromAggregateRow(Functions functions, UntypedResultSet.Row row) { String ksName = row.getString("keyspace_name"); String functionName = row.getString("aggregate_name"); @@ -1673,7 +1677,7 @@ public final class SchemaKeyspace try { - return UDAggregate.create(name, argTypes, returnType, stateFunc, finalFunc, stateType, initcond); + return UDAggregate.create(functions, name, argTypes, returnType, stateFunc, finalFunc, stateType, initcond); } catch (InvalidRequestException reason) { diff --git a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java index 7124e40670..d069d56716 100644 --- a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java +++ b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java @@ -251,7 +251,9 @@ public class LegacySchemaMigratorTest keyspaces.add(keyspaceWithTriggers()); keyspaces.add(keyspaceWithUDTs()); keyspaces.add(keyspaceWithUDFs()); + keyspaces.add(keyspaceWithUDFsAndUDTs()); keyspaces.add(keyspaceWithUDAs()); + keyspaces.add(keyspaceWithUDAsAndUDTs()); return keyspaces; } @@ -326,7 +328,6 @@ public class LegacySchemaMigratorTest { String keyspace = KEYSPACE_PREFIX + "UDFs"; - UDFunction udf1 = UDFunction.create(new FunctionName(keyspace, "udf"), ImmutableList.of(new ColumnIdentifier("col1", false), new ColumnIdentifier("col2", false)), ImmutableList.of(BytesType.instance, Int32Type.instance), @@ -360,17 +361,173 @@ public class LegacySchemaMigratorTest Functions.of(udf1, udf2, udf3)); } - // TODO: add representative UDAs set private static KeyspaceMetadata keyspaceWithUDAs() { String keyspace = KEYSPACE_PREFIX + "UDAs"; + UDFunction udf1 = UDFunction.create(new FunctionName(keyspace, "udf1"), + ImmutableList.of(new ColumnIdentifier("col1", false), new ColumnIdentifier("col2", false)), + ImmutableList.of(Int32Type.instance, Int32Type.instance), + Int32Type.instance, + false, + "java", + "return 42;"); + + UDFunction udf2 = UDFunction.create(new FunctionName(keyspace, "udf2"), + ImmutableList.of(new ColumnIdentifier("col1", false), new ColumnIdentifier("col2", false)), + ImmutableList.of(LongType.instance, Int32Type.instance), + LongType.instance, + false, + "java", + "return 42L;"); + + UDFunction udf3 = UDFunction.create(new FunctionName(keyspace, "udf3"), + ImmutableList.of(new ColumnIdentifier("col1", false)), + ImmutableList.of(LongType.instance), + DoubleType.instance, + false, + "java", + "return 42d;"); + + Functions udfs = Functions.builder().add(udf1).add(udf2).add(udf3).build(); + + UDAggregate uda1 = UDAggregate.create(udfs, new FunctionName(keyspace, "uda1"), + ImmutableList.of(udf1.argTypes().get(1)), + udf1.returnType(), + udf1.name(), + null, + udf1.argTypes().get(0), + null + ); + + UDAggregate uda2 = UDAggregate.create(udfs, new FunctionName(keyspace, "uda2"), + ImmutableList.of(udf2.argTypes().get(1)), + udf3.returnType(), + udf2.name(), + udf3.name(), + udf2.argTypes().get(0), + LongType.instance.decompose(0L) + ); + return KeyspaceMetadata.create(keyspace, KeyspaceParams.simple(1), Tables.none(), Views.none(), Types.none(), - Functions.of()); + Functions.of(udf1, udf2, udf3, uda1, uda2)); + } + + private static KeyspaceMetadata keyspaceWithUDFsAndUDTs() + { + String keyspace = KEYSPACE_PREFIX + "UDFUDTs"; + + UserType udt1 = new UserType(keyspace, + bytes("udt1"), + new ArrayList() {{ add(bytes("col1")); add(bytes("col2")); }}, + new ArrayList>() {{ add(UTF8Type.instance); add(Int32Type.instance); }}); + + UserType udt2 = new UserType(keyspace, + bytes("udt2"), + new ArrayList() {{ add(bytes("col1")); add(bytes("col2")); }}, + new ArrayList>() {{ add(ListType.getInstance(udt1, false)); add(Int32Type.instance); }}); + + UDFunction udf1 = UDFunction.create(new FunctionName(keyspace, "udf"), + ImmutableList.of(new ColumnIdentifier("col1", false), new ColumnIdentifier("col2", false)), + ImmutableList.of(udt1, udt2), + LongType.instance, + false, + "java", + "return 42L;"); + + // an overload with the same name, not a typo + UDFunction udf2 = UDFunction.create(new FunctionName(keyspace, "udf"), + ImmutableList.of(new ColumnIdentifier("col3", false), new ColumnIdentifier("col4", false)), + ImmutableList.of(AsciiType.instance, LongType.instance), + Int32Type.instance, + true, + "java", + "return 42;"); + + UDFunction udf3 = UDFunction.create(new FunctionName(keyspace, "udf3"), + ImmutableList.of(new ColumnIdentifier("col4", false)), + ImmutableList.of(new TupleType(Arrays.asList(udt1, udt2))), + BooleanType.instance, + false, + "java", + "return true;"); + + return KeyspaceMetadata.create(keyspace, + KeyspaceParams.simple(1), + Tables.none(), + Views.none(), + Types.of(udt1, udt2), + Functions.of(udf1, udf2, udf3)); + } + + private static KeyspaceMetadata keyspaceWithUDAsAndUDTs() + { + String keyspace = KEYSPACE_PREFIX + "UDAUDTs"; + + UserType udt1 = new UserType(keyspace, + bytes("udt1"), + new ArrayList() {{ add(bytes("col1")); add(bytes("col2")); }}, + new ArrayList>() {{ add(UTF8Type.instance); add(Int32Type.instance); }}); + + UserType udt2 = new UserType(keyspace, + bytes("udt2"), + new ArrayList() {{ add(bytes("col1")); add(bytes("col2")); }}, + new ArrayList>() {{ add(ListType.getInstance(udt1, false)); add(Int32Type.instance); }}); + + UDFunction udf1 = UDFunction.create(new FunctionName(keyspace, "udf1"), + ImmutableList.of(new ColumnIdentifier("col1", false), new ColumnIdentifier("col2", false)), + ImmutableList.of(udt1, udt2), + udt1, + false, + "java", + "return null;"); + + UDFunction udf2 = UDFunction.create(new FunctionName(keyspace, "udf2"), + ImmutableList.of(new ColumnIdentifier("col1", false), new ColumnIdentifier("col2", false)), + ImmutableList.of(udt2, udt1), + udt2, + false, + "java", + "return null;"); + + UDFunction udf3 = UDFunction.create(new FunctionName(keyspace, "udf3"), + ImmutableList.of(new ColumnIdentifier("col1", false)), + ImmutableList.of(udt2), + DoubleType.instance, + false, + "java", + "return 42d;"); + + Functions udfs = Functions.builder().add(udf1).add(udf2).add(udf3).build(); + + UDAggregate uda1 = UDAggregate.create(udfs, new FunctionName(keyspace, "uda1"), + ImmutableList.of(udf1.argTypes().get(1)), + udf1.returnType(), + udf1.name(), + null, + udf1.argTypes().get(0), + null + ); + + UDAggregate uda2 = UDAggregate.create(udfs, new FunctionName(keyspace, "uda2"), + ImmutableList.of(udf2.argTypes().get(1)), + udf3.returnType(), + udf2.name(), + udf3.name(), + udf2.argTypes().get(0), + LongType.instance.decompose(0L) + ); + + return KeyspaceMetadata.create(keyspace, + KeyspaceParams.simple(1), + Tables.none(), + Views.none(), + Types.of(udt1, udt2), + Functions.of(udf1, udf2, udf3, uda1, uda2)); } /*