From 06c1951c0cab0d8187053fc8555540ffb1c3ce5f Mon Sep 17 00:00:00 2001 From: Stefan Miklosovic Date: Wed, 13 Aug 2025 10:32:48 +0200 Subject: [PATCH] poc patch --- .../cassandra/db/virtual/SimpleDataSet.java | 20 ++++++- .../cassandra/db/virtual/VirtualKeyspace.java | 8 ++- .../db/virtual/VirtualSchemaKeyspace.java | 52 ++++++++++++++++++- .../db/virtual/VirtualTypesDefinitions.java | 43 +++++++++++++++ .../cassandra/schema/KeyspaceMetadata.java | 7 ++- .../cassandra/service/CassandraDaemon.java | 13 +++++ 6 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 src/java/org/apache/cassandra/db/virtual/VirtualTypesDefinitions.java diff --git a/src/java/org/apache/cassandra/db/virtual/SimpleDataSet.java b/src/java/org/apache/cassandra/db/virtual/SimpleDataSet.java index 6f3052d8be..cf3c42f3a7 100644 --- a/src/java/org/apache/cassandra/db/virtual/SimpleDataSet.java +++ b/src/java/org/apache/cassandra/db/virtual/SimpleDataSet.java @@ -21,6 +21,7 @@ import java.nio.ByteBuffer; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.TreeMap; @@ -35,9 +36,11 @@ import org.apache.cassandra.db.filter.ClusteringIndexFilter; import org.apache.cassandra.db.filter.ColumnFilter; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.CompositeType; +import org.apache.cassandra.db.marshal.UserType; import org.apache.cassandra.db.rows.AbstractUnfilteredRowIterator; import org.apache.cassandra.db.rows.BTreeRow; import org.apache.cassandra.db.rows.BufferCell; +import org.apache.cassandra.db.rows.CellPath; import org.apache.cassandra.db.rows.EncodingStats; import org.apache.cassandra.db.rows.Rows; import org.apache.cassandra.db.rows.Unfiltered; @@ -200,7 +203,22 @@ public class SimpleDataSet extends AbstractVirtualTable.AbstractDataSet { Object value = values.get(c); if (null != value) - builder.addCell(BufferCell.live(c, now, decompose(c.type, value))); + { + if (c.isComplex() && c.type.isUDT()) + { + UserType userType = (UserType) c.type; + ByteBuffer decompose = decompose(c.type, value); + List unpack = userType.unpack(decompose); + for (int i = 0; i < unpack.size(); i++) + { + CellPath cellPath = userType.cellPathForField(userType.fieldName(i)); + builder.addCell(BufferCell.live(c, now, unpack.get(i), cellPath)); + } + } + else + builder.addCell(BufferCell.live(c, now, decompose(c.type, value))); + } + } catch (Exception e) { diff --git a/src/java/org/apache/cassandra/db/virtual/VirtualKeyspace.java b/src/java/org/apache/cassandra/db/virtual/VirtualKeyspace.java index a0585a2694..6029130a73 100644 --- a/src/java/org/apache/cassandra/db/virtual/VirtualKeyspace.java +++ b/src/java/org/apache/cassandra/db/virtual/VirtualKeyspace.java @@ -28,6 +28,7 @@ import com.google.common.collect.Iterables; import org.apache.cassandra.schema.KeyspaceMetadata; import org.apache.cassandra.schema.Tables; +import org.apache.cassandra.schema.Types; public class VirtualKeyspace { @@ -37,6 +38,11 @@ public class VirtualKeyspace private final ImmutableCollection tables; public VirtualKeyspace(String name, Collection tables) + { + this(name, tables, Types.none()); + } + + public VirtualKeyspace(String name, Collection tables, Types types) { this.name = name; this.tables = ImmutableList.copyOf(tables); @@ -50,7 +56,7 @@ public class VirtualKeyspace if (!duplicates.isEmpty()) throw new IllegalArgumentException(String.format("Duplicate table names in virtual keyspace %s: %s", name, duplicates)); - this.metadata = KeyspaceMetadata.virtual(name, Tables.of(Iterables.transform(tables, VirtualTable::metadata))); + this.metadata = KeyspaceMetadata.virtual(name, Tables.of(Iterables.transform(tables, VirtualTable::metadata)), types); } public String name() diff --git a/src/java/org/apache/cassandra/db/virtual/VirtualSchemaKeyspace.java b/src/java/org/apache/cassandra/db/virtual/VirtualSchemaKeyspace.java index 43e373d6e3..db6f8e3d06 100644 --- a/src/java/org/apache/cassandra/db/virtual/VirtualSchemaKeyspace.java +++ b/src/java/org/apache/cassandra/db/virtual/VirtualSchemaKeyspace.java @@ -17,11 +17,17 @@ */ package org.apache.cassandra.db.virtual; -import com.google.common.collect.ImmutableList; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.FieldIdentifier; +import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.BytesType; import org.apache.cassandra.db.marshal.Int32Type; +import org.apache.cassandra.db.marshal.ListType; import org.apache.cassandra.db.marshal.UTF8Type; +import org.apache.cassandra.db.marshal.UserType; import org.apache.cassandra.dht.LocalPartitioner; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.KeyspaceMetadata; @@ -37,7 +43,10 @@ public final class VirtualSchemaKeyspace extends VirtualKeyspace private VirtualSchemaKeyspace() { - super(VIRTUAL_SCHEMA, ImmutableList.of(new VirtualKeyspaces(VIRTUAL_SCHEMA), new VirtualTables(VIRTUAL_SCHEMA), new VirtualColumns(VIRTUAL_SCHEMA))); + super(VIRTUAL_SCHEMA, List.of(new VirtualKeyspaces(VIRTUAL_SCHEMA), + new VirtualTables(VIRTUAL_SCHEMA), + new VirtualColumns(VIRTUAL_SCHEMA), + new VirtualTypes(VIRTUAL_SCHEMA))); } private static final class VirtualKeyspaces extends AbstractVirtualTable @@ -149,4 +158,43 @@ public final class VirtualSchemaKeyspace extends VirtualKeyspace return result; } } + + private static final class VirtualTypes extends AbstractVirtualTable + { + private static final String KEYSPACE_NAME = "keyspace_name"; + private static final String TYPE_NAME = "type_name"; + private static final String FIELD_NAMES = "field_names"; + private static final String FIELD_TYPES = "field_types"; + + private VirtualTypes(String keyspace) + { + super(TableMetadata.builder(keyspace, "types") + .comment("virtual types definitions") + .kind(TableMetadata.Kind.VIRTUAL) + .partitioner(new LocalPartitioner(UTF8Type.instance)) + .addPartitionKeyColumn(KEYSPACE_NAME, UTF8Type.instance) + .addClusteringColumn(TYPE_NAME, UTF8Type.instance) + .addRegularColumn(FIELD_NAMES, ListType.getInstance(UTF8Type.instance,false).freeze()) + .addRegularColumn(FIELD_TYPES, ListType.getInstance(UTF8Type.instance,false).freeze()) + .build()); + } + + @Override + public DataSet data() + { + SimpleDataSet result = new SimpleDataSet(metadata); + + for (KeyspaceMetadata keyspace : VirtualKeyspaceRegistry.instance.virtualKeyspacesMetadata()) + { + for (UserType type : keyspace.types) + { + result.row(keyspace.name, type.getNameAsString()) + .column(FIELD_NAMES, type.fieldNames().stream().map(FieldIdentifier::toString).collect(Collectors.toList())) + .column(FIELD_TYPES, type.fieldTypes().stream().map(AbstractType::asCQL3Type).map(CQL3Type::toString).collect(Collectors.toList())); + } + } + + return result; + } + } } diff --git a/src/java/org/apache/cassandra/db/virtual/VirtualTypesDefinitions.java b/src/java/org/apache/cassandra/db/virtual/VirtualTypesDefinitions.java new file mode 100644 index 0000000000..034c185ea3 --- /dev/null +++ b/src/java/org/apache/cassandra/db/virtual/VirtualTypesDefinitions.java @@ -0,0 +1,43 @@ +/* + * 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.db.virtual; + +import java.util.List; + +import org.apache.cassandra.cql3.FieldIdentifier; +import org.apache.cassandra.db.marshal.LongType; +import org.apache.cassandra.db.marshal.UTF8Type; +import org.apache.cassandra.db.marshal.UserType; + +public final class VirtualTypesDefinitions +{ + public static UserType getGuardrailsThresholdSettings(String keyspace) + { + return new UserType(keyspace, + UTF8Type.instance.decompose("settings"), + List.of(FieldIdentifier.forQuoted("warn"), + FieldIdentifier.forQuoted("fail")), + List.of(LongType.instance, LongType.instance), + true); + } + + private VirtualTypesDefinitions() + { + } +} diff --git a/src/java/org/apache/cassandra/schema/KeyspaceMetadata.java b/src/java/org/apache/cassandra/schema/KeyspaceMetadata.java index 16a5c1b67a..d0ae1bc992 100644 --- a/src/java/org/apache/cassandra/schema/KeyspaceMetadata.java +++ b/src/java/org/apache/cassandra/schema/KeyspaceMetadata.java @@ -111,7 +111,12 @@ public final class KeyspaceMetadata implements SchemaElement public static KeyspaceMetadata virtual(String name, Tables tables) { - return new KeyspaceMetadata(name, Kind.VIRTUAL, KeyspaceParams.local(), tables, Views.none(), Types.none(), UserFunctions.none()); + return virtual(name, tables, Types.none()); + } + + public static KeyspaceMetadata virtual(String name, Tables tables, Types userTypes) + { + return new KeyspaceMetadata(name, Kind.VIRTUAL, KeyspaceParams.local(), tables, Views.none(), userTypes, UserFunctions.none()); } public KeyspaceMetadata withSwapped(KeyspaceParams params) diff --git a/src/java/org/apache/cassandra/service/CassandraDaemon.java b/src/java/org/apache/cassandra/service/CassandraDaemon.java index be8bd058b9..06d54ad916 100644 --- a/src/java/org/apache/cassandra/service/CassandraDaemon.java +++ b/src/java/org/apache/cassandra/service/CassandraDaemon.java @@ -562,6 +562,19 @@ public class CassandraDaemon // before that virtual table was instantiated. // In general, there is no need to do same treatment for slow queries as by the time queries are processed // the logging framework if fully setup already but for the sake of it and to be sure, just do it as well. + +// +// UserType guardrailsThresholdSettings = VirtualTypesDefinitions.getGuardrailsThresholdSettings(VIRTUAL_GUARDRAILS); +// +// VirtualKeyspace guardrailsKeyspace = new VirtualKeyspace(VIRTUAL_GUARDRAILS, +// List.of(new GuardrailValuesTable(), +// new GuardrailEnableFlagsTable(), +// new GuardrailThresholdsTable(guardrailsThresholdSettings)), +// Types.of(guardrailsThresholdSettings)); +// VirtualKeyspaceRegistry.instance.register(guardrailsKeyspace); + + // flush log messages to system_views.system_logs virtual table as there were messages already logged + // before that virtual table was instantiated LoggingSupportFactory.getLoggingSupport() .getAppender(VirtualTableAppender.class, VirtualTableAppender.APPENDER_NAME) .ifPresent(appender -> appender.flushBuffer(LogMessagesTable.class, LogMessagesTable.TABLE_NAME));