Skip to content

Conversation

@dcapwell
Copy link
Contributor

No description provided.

@dcapwell
Copy link
Contributor Author

I used AI to help write these docs, and had it create test cases for each example to make sure it actually works. While doing this it found a lot of nice UX or issues; below is the state that it created

Accord Future Syntax and Semantic Features

This document tracks CQL transaction syntax and semantic features that are documented or desired but not yet implemented in the current version of Cassandra. These features may be added in future releases.

Arithmetic Expressions with Row References

Issue: Row Reference Arithmetic in UPDATE SET Clauses

Status: Not implemented
Parser Error: no viable alternative at input '-'

Description:
Using arithmetic expressions with row references (LET variable fields) in UPDATE SET clauses is not currently supported by the CQL parser.

Example of Unsupported Syntax:

BEGIN TRANSACTION
  LET user_data = (SELECT balance FROM users WHERE id = ?);
  UPDATE users SET balance = user_data.balance - 50 WHERE id = ?;
COMMIT TRANSACTION

Error Message:

line 1:X no viable alternative at input '-' (...SET balance = [user_data].balance...)

Current Workaround:
Use column self-reference instead of row reference arithmetic:

BEGIN TRANSACTION
  LET user_data = (SELECT balance FROM users WHERE id = ?);
  IF user_data.balance > 50 THEN
    UPDATE users SET balance = balance - 50 WHERE id = ?;
  END IF
COMMIT TRANSACTION

Impact:

  • Cannot compute new values based on LET variable fields in UPDATE SET clauses
  • Must use column self-reference (e.g., balance = balance - 50) instead
  • Row references can still be used in IF conditions for validation

Comparing Two Row References

Issue: Comparison Between Two LET Variables

Status: Not implemented
Parser Error: IllegalArgumentException

Description:
Comparing values from two different LET variables in an IF condition is not currently supported.

Example of Unsupported Syntax:

BEGIN TRANSACTION
  LET account = (SELECT balance FROM accounts WHERE id = ?);
  LET limit = (SELECT max_balance FROM limits WHERE type = 'standard');
  
  IF account.balance < limit.max_balance THEN
    UPDATE accounts SET balance = balance + 10 WHERE id = ?;
  END IF
COMMIT TRANSACTION

Current Workaround:
Read the limit in the application layer and pass it as a parameter:

-- Application code:
-- double maxBalance = 1000.00; // Retrieved from limits table

BEGIN TRANSACTION
  LET account = (SELECT balance FROM accounts WHERE id = ?);
  
  IF account.balance < ? THEN -- Pass maxBalance
    UPDATE accounts SET balance = balance + 10 WHERE id = ?;
  END IF
COMMIT TRANSACTION

Impact:

  • Cannot perform cross-table validations entirely within the database if they involve comparing values between tables
  • Requires application-side logic for such comparisons

Range Updates and Deletes

Issue: Updates or Deletes without Full Primary Key

Status: Not implemented
Error: InvalidRequestException: Some clustering keys are missing

Description:
Performing UPDATE or DELETE operations that match multiple rows (range operations) by specifying only the partition key (or a partial primary key) is not currently supported in transactions. You must specify the full primary key for every modification.

Example of Unsupported Syntax:

BEGIN TRANSACTION
  -- Attempting to update all sessions for a user
  UPDATE user_sessions 
  SET status = 'invalidated' 
  WHERE user_id = ?; -- Missing session_id (clustering key)
COMMIT TRANSACTION

Current Workaround:
Perform a read query to identify the specific rows to modify, then issue individual modification statements for each row within the transaction (or pass the IDs as parameters).

-- Step 1: Query active sessions (outside transaction or in previous step)
-- SELECT session_id FROM user_sessions WHERE user_id = ?;

-- Step 2: Transaction with specific updates
BEGIN TRANSACTION
  UPDATE users SET status = 'deactivated' WHERE id = ?;
  
  -- Update known sessions individually
  UPDATE user_sessions SET status = 'invalidated' WHERE user_id = ? AND session_id = ?;
  UPDATE user_sessions SET status = 'invalidated' WHERE user_id = ? AND session_id = ?;
COMMIT TRANSACTION

Impact:

  • Cannot atomically "delete all" or "update all" children records without knowing their IDs
  • Requires application to track or query IDs before modifying

Arithmetic Expressions with Row References in SELECT

Issue: Row Reference Arithmetic in SELECT Return Values

Status: Not implemented
Parser Error: no viable alternative at input '-'

Description:
Computing arithmetic expressions with row references in SELECT statements within transactions is not supported.

Example of Unsupported Syntax:

BEGIN TRANSACTION
  LET user_data = (SELECT balance FROM users WHERE id = ?);
  UPDATE users SET balance = balance - 50 WHERE id = ?;
  SELECT user_data.balance, user_data.balance - 50;
COMMIT TRANSACTION

Error Message:

line 1:X no viable alternative at input '-' (...SELECT user_data.balance, user_data.balance...)

Current Workaround:
Return only the row reference fields without arithmetic:

BEGIN TRANSACTION
  LET user_data = (SELECT balance FROM users WHERE id = ?);
  UPDATE users SET balance = balance - 50 WHERE id = ?;
  SELECT user_data.balance;
COMMIT TRANSACTION

Impact:

  • Cannot return computed values based on LET variables
  • Application must perform calculations on returned values
  • Row reference fields can be returned individually

SELECT with Row Data References Positioning

Issue: SELECT with Row References Must Precede Modifications

Status: Grammar limitation
Parser Error: no viable alternative at input 'SELECT'

Description:
The CQL grammar requires that SELECT rowDataReferences (selecting only row reference fields without FROM clause) must appear before modification statements in a transaction. After UPDATE/INSERT/DELETE, only full SELECT statements with FROM clauses are allowed.

Example of Unsupported Syntax:

BEGIN TRANSACTION
  LET user_data = (SELECT balance FROM users WHERE id = ?);
  UPDATE users SET balance = balance - 50 WHERE id = ?;
  SELECT user_data.balance;  -- Error: SELECT with row references after UPDATE
COMMIT TRANSACTION

Error Message:

line 1:X no viable alternative at input 'SELECT' (... WHERE id = ?; [SELECT]...)

Current Workaround:
Place SELECT statements before modifications, or query outside the transaction:

-- Option 1: SELECT before UPDATE
BEGIN TRANSACTION
  LET user_data = (SELECT balance FROM users WHERE id = ?);
  SELECT user_data.balance;  -- SELECT before UPDATE
  UPDATE users SET balance = balance - 50 WHERE id = ?;
COMMIT TRANSACTION

-- Option 2: Query after transaction commits
BEGIN TRANSACTION
  UPDATE users SET balance = balance - 50 WHERE id = ?;
COMMIT TRANSACTION
-- Then query outside transaction:
SELECT balance FROM users WHERE id = ?;

Grammar Structure:
According to Parser.g, the transaction structure is:

BEGIN TRANSACTION
  [LET statements]
  [SELECT selectStatement | SELECT rowDataReferences]  <- Must be before modifications
  [IF condition THEN]
    [modifications]
  [END IF]
COMMIT TRANSACTION

The grammar does NOT allow SELECT statements after modification statements, even if they are full SELECT statements with FROM clauses. The SELECT position is fixed in the grammar structure.

Impact:

  • ALL SELECT statements must appear before any UPDATE/INSERT/DELETE
  • Cannot SELECT updated values within the same transaction
  • To see results after modifications, query outside the transaction
  • Row reference-only SELECTs must also precede modifications

Computed LET Assignments

Issue: LET Assignments with Computed Values

Status: Not implemented
Parser Error: Varies based on expression type

Description:
LET statements can only assign the result of a SELECT query. They cannot assign computed values, arithmetic expressions, or CASE expressions.

Example of Unsupported Syntax:

BEGIN TRANSACTION
  LET user_data = (SELECT balance FROM users WHERE id = ?);
  LET new_balance = user_data.balance - 50;  -- Error: computed assignment
  LET discount_rate = CASE user_data.tier
                      WHEN 'gold' THEN 0.10
                      ELSE 0.0 END;  -- Error: CASE expression

  UPDATE users SET balance = new_balance WHERE id = ?;
COMMIT TRANSACTION

Current Workaround:
Perform computations directly in UPDATE statements or in the application layer:

BEGIN TRANSACTION
  LET user_data = (SELECT balance, tier FROM users WHERE id = ?);

  -- Compute in the UPDATE statement instead
  IF user_data.balance >= 50 THEN
    UPDATE users SET balance = balance - 50 WHERE id = ?;
  END IF
COMMIT TRANSACTION

Impact:

  • Cannot store intermediate computed values in LET variables
  • All computations must happen in UPDATE/INSERT SET clauses
  • Complex calculations must be done in application code
  • No support for CASE expressions in LET assignments

SELECT After Modification Statements

Issue: SELECT Must Precede All Modifications

Status: Grammar restriction
Parser Error: no viable alternative at input 'SELECT' (when after modifications)

Description:
The CQL transaction grammar strictly orders statements: LET, then SELECT, then modifications. SELECT statements (including those returning computation results) cannot appear after any UPDATE, INSERT, or DELETE.

Example of Unsupported Syntax:

BEGIN TRANSACTION
  LET user_data = (SELECT balance FROM users WHERE id = ?);

  UPDATE users SET balance = balance - 50 WHERE id = ?;

  -- Error: SELECT after UPDATE not allowed
  SELECT user_data.balance, 'Update successful' as message;
COMMIT TRANSACTION

Current Workaround:
Either move SELECT before modifications or query outside the transaction:

-- Option 1: SELECT before modifications
BEGIN TRANSACTION
  LET user_data = (SELECT balance FROM users WHERE id = ?);
  SELECT user_data.balance;  -- Before UPDATE

  UPDATE users SET balance = balance - 50 WHERE id = ?;
COMMIT TRANSACTION

-- Option 2: Query after transaction
BEGIN TRANSACTION
  UPDATE users SET balance = balance - 50 WHERE id = ?;
COMMIT TRANSACTION
-- Then query outside:
SELECT balance FROM users WHERE id = ?;

Impact:

  • Cannot return confirmation messages with results from modifications
  • Cannot SELECT updated values within the transaction
  • Return values must be queried separately or selected before modifications
  • Limits ability to return operation status

IN and OR Operators in IF Conditions

Issue: IN and OR Not Supported in Transaction Conditions

Status: Not implemented
Parser Error: no viable alternative at input 'IN' or no viable alternative at input 'OR'

Description:
The IN operator and OR logical operator are not currently supported in transaction IF conditions. Only the AND logical operator is available for combining conditions.

Example of Unsupported Syntax (IN):

BEGIN TRANSACTION
  LET order = (SELECT status FROM orders WHERE order_id = ?);

  IF order.status IN ('PENDING', 'PAID') THEN
    UPDATE orders SET status = 'CANCELLED' WHERE order_id = ?;
  END IF
COMMIT TRANSACTION

Error Message:

line 1:X no viable alternative at input 'IN' (...IF order.status [IN]...)

Example of Unsupported Syntax (OR):

BEGIN TRANSACTION
  LET order = (SELECT status FROM orders WHERE order_id = ?);

  IF order.status = 'PENDING' OR order.status = 'PAID' THEN
    UPDATE orders SET status = 'CANCELLED' WHERE order_id = ?;
  END IF
COMMIT TRANSACTION

Error Message:

line 1:X no viable alternative at input 'OR' (...= 'PENDING' [OR]...)

Current Workaround:
Use numeric status codes and range comparisons to check multiple valid states:

-- Schema uses integer status codes: 10=PENDING, 20=PAID, 30=SHIPPED, etc.
BEGIN TRANSACTION
  LET order = (SELECT status_code FROM orders WHERE order_id = ?);

  -- Use range comparison instead of IN/OR
  -- status_code <= 20 means PENDING or PAID
  IF order.status_code <= 20 THEN
    UPDATE orders SET status_code = 99 WHERE order_id = ?;  -- 99=CANCELLED
  END IF
COMMIT TRANSACTION

Impact:

  • Cannot use IN (value1, value2, ...) syntax to check membership in a set
  • Cannot use OR to combine multiple conditions
  • Only AND is supported for combining conditions
  • Workaround requires careful design of numeric status codes to enable range comparisons

Grammar Reference:
The txnConditions rule in Parser.g only supports AND:

txnConditions returns [List<ConditionStatement.Raw> conditions]
    : txnColumnCondition[conditions] ( K_AND txnColumnCondition[conditions] )*
    ;

Code Reference:
ConditionStatement.java contains a TODO for future support:

// TODO: Support for IN, CONTAINS, CONTAINS KEY

Summary of Limitations

Row Reference Arithmetic Limitations

What Works:

  • Row references in IF conditions: IF user_data.balance > 100 THEN
  • Row references in comparisons: IF user_data.balance >= order_total THEN
  • Returning row reference fields: SELECT user_data.balance
  • Column self-reference arithmetic: SET balance = balance - 50

What Doesn't Work:

  • Arithmetic in UPDATE SET: SET balance = user_data.balance - 50
  • Arithmetic in SELECT: SELECT user_data.balance - 50
  • Computed LET values: LET new_balance = user_data.balance - 50

Parser Status:
The CQL parser (Parser.g) does not currently support arithmetic expressions involving row references outside of conditional expressions.


Future Enhancements

These features may be added in future Cassandra versions:

  1. Computed LET Assignments:

    LET new_balance = user_data.balance - 50;
    UPDATE users SET balance = new_balance WHERE id = ?;
  2. Arithmetic Expressions in SET Clauses:

    UPDATE users SET balance = user_data.balance - 50 WHERE id = ?;
  3. Computed SELECT Returns:

    SELECT user_data.balance, user_data.balance - 50 AS new_balance;

Testing Notes

All examples in the Cassandra documentation (doc/modules/cassandra/pages/developing/cql/transactions*.adoc) should only show syntax that currently works. Examples using unsupported syntax have been removed and documented here for future reference.

Test classes validating documentation examples:

  • BasicTransactionExamplesTest - Basic transaction patterns
  • BankingExamplesTest - Banking and financial examples
  • EcommerceExamplesTest - E-commerce examples
  • UserManagementExamplesTest - User management examples
  • InOperatorTest - Validates IN and OR operators are not supported in IF conditions
  • Other example tests in test/distributed/org/apache/cassandra/distributed/test/accord/example/

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