Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CASSANDRA-20155: To improve accord interoperability test coverage, nehttps://github.com/dcapwell/cassandra/pull/new/CASSANDRA-20155 #3753

Open
wants to merge 3 commits into
base: cep-15-accord
Choose a base branch
from

Conversation

dcapwell
Copy link
Contributor

No description provided.

checkFalse(isSelectingMultipleClusterings(assignment.select, options), INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE, "LET assignment", assignment.select.source);
if (assignment.select.getRestrictions().keyIsInRelation())
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when LIMIT ? we are not able to validate, so need to rerun the validation here

if (prepared.getRestrictions().keyIsInRelation())
checkTrue(prepared.getLimit(null) == DataLimits.NO_LIMIT, NO_PARTITION_IN_CLAUSE_WITH_LIMIT, "SELECT", prepared.source);
checkTrue(prepared.isLimitMarker() || prepared.getLimit(null) == DataLimits.NO_LIMIT, NO_PARTITION_IN_CLAUSE_WITH_LIMIT, "SELECT", prepared.source);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when LIMIT ? this would cause a NPE as options is null... we only don't NPE when the value is a literal...

@@ -75,7 +75,7 @@ private static Query read(String ks, String table, List<ByteBuffer> keys)
assert !keys.isEmpty();
Txn.Builder builder = new Txn.Builder();
for (int i = 0; i < keys.size(); i++)
builder.addLet("row" + i, new Select.Builder().withWildcard().withTable(ks, table).withColumnEquals("pk", keys.get(i)));
builder.addLet("row" + i, new Select.Builder().withWildcard().withTable(ks, table).value("pk", keys.get(i)));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in order to make select/update/delete (places that support WHERE clause) consistent all WHERE based methods are now an interface and each builder implements... this is why this method got moved.

value might not be the best name, open to feedback to help readability... maybe equals?

/**
* Should the history show the CQL? By default, this is off as its very verbose, but when debugging this can be helpful.
*/
private static final boolean HISTORY_SHOWS_CQL = false;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the changes to this class are not needed for this patch, nor for the model... but they are here for the following reasons

  1. this class is here to test topology changes, not be the fuzz test for accord... so by simplifying what we can do, the types, and the values... it makes debugging less crazy (I hate looking at blobs...)
  2. when you do hit a CQL issue (going to fix in trunk; delete with CAS against static fails) debugging can be annoying, so this flag lets you see the actual CQL run

@@ -60,9 +59,9 @@ public static CompiledStatement select(Operations.SelectRow select, SchemaSpec s
for (int i = 0; i < schema.clusteringKeys.size(); i++)
{
ColumnSpec<?> column = schema.clusteringKeys.get(i);
builder.withWhere(Reference.of(new Symbol(column.name, column.type.asServerType())),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reference as the default here was a dumb idea... I really wanted to make sure that txn references wouldn't break but this caused big usability issues... it also is really annoying to handle in the model as it adds far more states than Symbol does...

You can still use a Reference and this is tested with Txn... but removed the pattern for simplicity

@@ -75,7 +75,7 @@ private static Query read(String ks, String table, List<ByteBuffer> keys)
assert !keys.isEmpty();
Txn.Builder builder = new Txn.Builder();
for (int i = 0; i < keys.size(); i++)
builder.addLet("row" + i, new Select.Builder().withWildcard().withTable(ks, table).withColumnEquals("pk", keys.get(i)));
builder.addLet("row" + i, new Select.Builder().wildcard().table(ks, table).value("pk", keys.get(i)));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this case wildcard isn't needed, but it does protect the builder from something adding selections later on... because we not holding the reference after this line we really could remove, but left it for now.

@@ -233,7 +233,7 @@ private static Where.Inequalities toInequalities(Relations.RelationKind kind)
private static CompiledStatement toCompiled(Select select)
{
// Select does not add ';' by default, but CompiledStatement expects this
String cql = select.toCQL().replace("\n", " ") + ";";
String cql = select.toCQL(CQLFormatter.None.instance) + ';';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for context Alex had a patch to add this behavior into Select and there was some back and forth about that... so he dropped the logic and used this replace... since this patch adds formatters we can do the same without needing the replace now.

// select * from table where <primaryKeys>
return new Select(Collections.emptyList(),
Optional.of(new TableReference(Optional.of(mutation.table.keyspace), mutation.table.name)),
where(mutation.primaryKeys()),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supporting mutation.primaryKeys() is actually annoying as a mutation can impact multiple rows... This is also trying to hack together a bad model, so with the next patch adding a model it makes more sense to drop this logic in favor of the pending model.

import static accord.utils.Property.qt;
import static java.lang.String.format;

public class RandomSchemaV2Test extends CQLTester
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test will be replaced by SingleNodeTableWalkerTest in the follow up patch. CQLTester queries the same as NODE_LOCAL, and some CQL operations act differently there than when coordinated... this issue impacts accord's BEGIN TRANSACTION which is why we keep disabling CQL features... I tried porting the model here and was bitten by this... so it really is better to avoid this pattern of test in CQLTester unless we do coordinated... I went down a rabbit hole of allowing coordinated execution, but that then overlaps with networking... it was honestly hard to define how this class adds value when SingleNodeTableWalkerTest lands as it has a larger search space, validation, etc.

@Override
public Expression visit(Visitor v)
{
var u = v.visit(this);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the visit pattern is the same for each type, so documenting here as this is the first to show up in the diff

  1. the visitor could replace the full object, this is checked by calling the visitor directly v.visit(this)
  2. when working with sub elements we don't know if the element has sub-elements, so need to call e.visit(v) so it can recursively do its work.

@@ -47,7 +47,7 @@ public AbstractType<?> type()

public ByteBuffer encode()
{
return ((AbstractType) type).decompose(value);
return value instanceof ByteBuffer ? (ByteBuffer) value : ((AbstractType) type).decompose(value);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the next patch adds a model, and to keep things simple all values are ByteBuffer right away, and not the java objects AbstractType expects, but that broke the ast package's Value types... so needed to explicitly handle in both types

Comment on lines +35 to +36
private static final Symbol pk = new Symbol("pk", Int32Type.instance);
private static final Symbol ck = new Symbol("ck", Int32Type.instance);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are dead code... I just wanted to be complete...

Mutation.InsertBuilder builder = Mutation.insert(TBL1);
builder.value("pk", 0);
builder.value("v1", 0);
builder.ttl(new Literal(42, Int32Type.instance));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by default Bind is used when you use raw values. To make sure this test uses a Literal it needs to be explicit.

return Collections.singletonList(this);
}

class Where implements Conditional
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved this class from its own file to here, so all impl are in one place... its a pattern that I started doing with this package so migrated to keep consistent... many elements are small so their own files seems wasteful


class Where implements Conditional
{
public enum Inequality
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed to be singular...

sb.append(", ");
}
sb.setLength(sb.length() - 2); // ", "
sb.append(')');
}

@Override
public Stream<? extends Element> stream()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug found in testing... forgot to define the stream so the sub elements that contained bind would break... this method is used to find what bind values to send to the driver

sb.append(" IS ").append(kind.cql);
}
}

class Builder
class And implements Conditional
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved from its own class to here to be consistent with the other types in this package

Comment on lines 293 to 302
interface EqBuilder<T extends EqBuilder<T>>
{
T value(Symbol symbol, Expression e);
default T value(String symbol, int e)
{
return value(new Symbol(symbol, Int32Type.instance), Bind.of(e));
}
}

interface ConditionalBuilder<T extends ConditionalBuilder<T>> extends EqBuilder<T>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than have each builder copy/paste and let them get out of sync, made these interfaces to define what you can do with a Conditional, now all builders are consistent.

@@ -55,6 +61,10 @@ default UnquotedSymbol longName()
return name();
}
boolean supported(TableMetadata table, ColumnMetadata column);
default EnumSet<QueryType> supportedQueries(AbstractType<?> type)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is needed by the model to know what operations could be done by the index. maintaining this is actually kind of a burden, so would be good to figure out how to ask the real impl....

@Override
public EnumSet<QueryType> supportedQueries(AbstractType<?> type)
{
type = type.unwrap();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symbol.type is never reversed, you need Symbol.rawType() to get the reversed type... but since this works with types directly we have to be defensive off the bat...


default void toCQL(StringBuilder sb)
{
toCQL(sb, 0);
toCQL(sb, CQLFormatter.None.instance);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before we would pretty print by default, but to avoid the extra allocation switched to None... I kinda feel anyone who cares should always define just to make sure this default can change over time.

Comment on lines +113 to +120
public static FunctionCall tokenByColumns(List<Symbol> columns)
{
return new FunctionCall("token", columns, BytesType.instance);
}

public static FunctionCall tokenByValue(List<? extends Value> values)
{
return new FunctionCall("token", values, BytesType.instance);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dead code in this patch, but used in the follow up model patch. This is here for the following

WHERE token(pk) = token(?)

@@ -18,29 +18,35 @@

package org.apache.cassandra.cql3.ast;

import java.util.stream.Stream;
Copy link
Contributor Author

@dcapwell dcapwell Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a bad GH diff... AND was not renamed to StandardVisitors... they are unrelated, this is a 100% new file

import org.apache.cassandra.schema.ColumnMetadata;

public class Symbol implements ReferenceExpression, Comparable<Symbol>
{
public final String symbol;
private final AbstractType<?> type;
public final boolean reversed;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having reverse type has only been a problem. Caleb and I talked about doing this for ColumnMetadata and did it here... has made life so much easier... there is very little code that cares about ordering, those code paths just call .rawType()....


public String detailedName()
{
return symbol + " " + type.asCQL3Type() + (reversed ? " (reversed)" : "");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this has been nice in the test in the next patch... below is a sample of the sai reverse bug

		266: SELECT * FROM "arak0ffcoYDbQhwmjr1uXjr".tbl WHERE ck0 <= 0; -- ck0 varint (reversed), indexed with SAI
...
Caused by: java.lang.AssertionError: Missing rows:
pk0                     | pk1                  | ck0        | ck1                                | v0                        
-3.793674428756257E-259 | '11:03:35.709809507' | -153195755 | 0x00000000000013009e00000000000000 | '2027-09-06T19:24:27.334Z'

Expected:
pk0                     | pk1                  | ck0        | ck1                                | v0                        
-3.793674428756257E-259 | '11:03:35.709809507' | 0          | 0x0000000000001000bd00000000000000 | '2053-01-08T04:12:35.057Z'
-3.793674428756257E-259 | '11:03:35.709809507' | -153195755 | 0x00000000000013009e00000000000000 | '2027-09-06T19:24:27.334Z'

case UPDATE:
mutation = Mutation.update(tbl5())
.value(new Symbol("k", Int32Type.instance), Bind.of(1))
.set(new Symbol("v", Int32Type.instance), Bind.of(2))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one line is different... and has to be different as UPDATE has both SET and WHERE blocks... you must be explicit.

Comment on lines +326 to +329
private final LinkedHashSet<Symbol> allColumns;
private final LinkedHashSet<Symbol> partitionColumns, clusteringColumns;
private final LinkedHashSet<Symbol> primaryColumns;
private final LinkedHashSet<Symbol> regularColumns, staticColumns, regularAndStaticColumns;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hate showing the impl but I need a deterministic set and insertion order... in the follow up patch I am adding a new one, but to keep things simple this uses std java here

…ed to extend the harry model domain to handle more possible CQL states
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant