Skip to content

Commit

Permalink
cql3: SELECT CAST column names should match Cassandra's
Browse files Browse the repository at this point in the history
When doing a SELECT CAST(b AS int), Cassandra returns a column named
cast(b as int). Currently, Scylla uses a different name -
system.castasint(b). For Cassandra compatibility, we should switch to
the same name.

fixes #14508

Closes #14800
  • Loading branch information
alexander-tur authored and avikivity committed Sep 26, 2023
1 parent f42be12 commit 024ba84
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 5 deletions.
2 changes: 1 addition & 1 deletion cql3/assignment_testable.hh
Expand Up @@ -32,7 +32,7 @@ public:
}

static bool is_exact_match(test_result tr) {
return tr != test_result::EXACT_MATCH;
return tr == test_result::EXACT_MATCH;
}

/**
Expand Down
11 changes: 9 additions & 2 deletions cql3/expr/expression.cc
Expand Up @@ -1136,8 +1136,15 @@ std::ostream& operator<<(std::ostream& os, const expression::printer& pr) {
} else if (!pr.for_metadata) {
fmt::print(os, "{}({})", fn->name(), fmt::join(fc.args | transformed(to_printer), ", "));
} else {
auto args = boost::copy_range<std::vector<sstring>>(fc.args | transformed(to_printer) | transformed(fmt::to_string<expression::printer>));
fmt::print(os, "{}", fn->column_name(args));
const std::string_view fn_name = fn->name().name;
if (fn->name().keyspace == "system" && fn_name.starts_with("castas")) {
auto cast_type = fn_name.substr(6);
fmt::print(os, "cast({} as {})", fmt::join(fc.args | transformed(to_printer) | transformed(fmt::to_string<expression::printer>), ", "),
cast_type);
} else {
auto args = boost::copy_range<std::vector<sstring>>(fc.args | transformed(to_printer) | transformed(fmt::to_string<expression::printer>));
fmt::print(os, "{}", fn->column_name(args));
}
}
},
}, fc.func);
Expand Down
5 changes: 5 additions & 0 deletions cql3/expr/prepare_expr.cc
Expand Up @@ -835,6 +835,11 @@ sql_cast_prepare_expression(const cast& c, data_dictionary::database db, const s
throw exceptions::invalid_request_exception(fmt::format("Could not infer type of cast argument {}", c.arg));
}

// cast to the same type should be ommited
if (cast_type == type_of(*prepared_arg)) {
return prepared_arg;
}

// This will throw if a cast is impossible
auto fun = functions::get_castas_fctn_as_cql3_function(cast_type, type_of(*prepared_arg));

Expand Down
2 changes: 0 additions & 2 deletions test/cql-pytest/cassandra_tests/functions/cast_fcts_test.py
Expand Up @@ -15,8 +15,6 @@ def testInvalidQueries(cql, test_keyspace):
with create_table(cql, test_keyspace, "(a int primary key, b text, c double)") as table:
assertInvalidMessage(cql, table, "cannot be cast to", "SELECT CAST(a AS boolean) FROM %s")

# Marked cassandra_bug because of CASSANDRA-18647 (see comment below)
@pytest.mark.xfail(reason="issue #14508")
def testNumericCastsInSelectionClause(cql, test_keyspace, cassandra_bug):
with create_table(cql, test_keyspace, "(a tinyint primary key, b smallint, c int, d bigint, e float, f double, g decimal, h varint, i int)") as table:
execute(cql, table, "INSERT INTO %s (a, b, c, d, e, f, g, h) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
Expand Down
15 changes: 15 additions & 0 deletions test/cql-pytest/test_cast_data.py
Expand Up @@ -100,6 +100,21 @@ def test_cast_from_large_varint_to_varchar(cql, table1, cassandra_bug):
cql.execute(f'INSERT INTO {table1} (p, cVarint) VALUES ({p}, {v})')
assert [(str(v),)] == list(cql.execute(f"SELECT CAST(cVarint AS varchar) FROM {table1} WHERE p={p}"))

# In test_cast_column_names we are checking that columns are named in Cassandra style
def test_cast_column_names(cql, table1):
p = unique_key_int()
v = 123
for t in ['int', 'double', 'text', 'tinyint', 'smallint']:
cql.execute(f'INSERT INTO {table1} (p, cVarint) VALUES ({p}, {v})')
assert cql.execute(f"SELECT CAST(cVarint as {t}) FROM {table1} WHERE p={p}").one()._fields[0] == f"cast_cvarint_as_{t}"

#In test_ignore_cast_to_the_same_type we are checking that query with casting of column to the same type is optimized
def test_ignore_cast_to_the_same_type(cql, table1):
p = unique_key_int()
v = 456
cql.execute(f'INSERT INTO {table1} (p, cVarint) VALUES ({p}, {v})')
assert cql.execute(f"SELECT CAST(p as int) FROM {table1} WHERE p={p}").one()._fields[0] == "p"

# Test casting a counter to various other types. Reproduces #14501.
def test_cast_from_counter(cql, table2):
p = unique_key_int()
Expand Down

0 comments on commit 024ba84

Please sign in to comment.