Merge '[Backport 2026.1] cql3/statements/describe_statement: hide paxos state tables ' from Scylladb[bot]

Paxos state tables are internal tables fully managed by Scylla
and they shouldn't be exposed to the user nor they shouldn't be backed up.

This commit hides those kind of tables from all listings and if such table
is directly described with `DESC ks."tbl$paxos"`, the description is generated
withing a comment and a note for the user is added.

Fixes https://github.com/scylladb/scylladb/issues/28183

LWT on tablets and paxos state tables are present in 2025.4, so the patch should be backported to this version.

- (cherry picked from commit f89a8c4ec4)

- (cherry picked from commit 9baaddb613)

Parent PR: #28230

Closes scylladb/scylladb#28508

* github.com:scylladb/scylladb:
  test/cqlpy: add reproducer for hidden Paxos table being shown by DESC
  cql3/statements/describe_statement: hide paxos state tables
This commit is contained in:
Botond Dénes
2026-02-18 12:41:08 +02:00
2 changed files with 80 additions and 1 deletions

View File

@@ -23,6 +23,7 @@
#include "index/vector_index.hh"
#include "schema/schema.hh"
#include "service/client_state.hh"
#include "service/paxos/paxos_state.hh"
#include "types/types.hh"
#include "cql3/query_processor.hh"
#include "cql3/cql_statement.hh"
@@ -329,6 +330,19 @@ future<std::vector<description>> table(const data_dictionary::database& db, cons
"*/",
*table_desc.create_statement);
table_desc.create_statement = std::move(os).to_managed_string();
} else if (service::paxos::paxos_store::try_get_base_table(name)) {
// Paxos state table is internally managed by Scylla and it shouldn't be exposed to the user.
// The table is allowed to be described as a comment to ease administrative work but it's hidden from all listings.
fragmented_ostringstream os{};
fmt::format_to(os.to_iter(),
"/* Do NOT execute this statement! It's only for informational purposes.\n"
" A paxos state table is created automatically when enabling LWT on a base table.\n"
"\n{}\n"
"*/",
*table_desc.create_statement);
table_desc.create_statement = std::move(os).to_managed_string();
}
result.push_back(std::move(table_desc));
@@ -364,7 +378,7 @@ future<std::vector<description>> table(const data_dictionary::database& db, cons
future<std::vector<description>> tables(const data_dictionary::database& db, const lw_shared_ptr<keyspace_metadata>& ks, std::optional<bool> with_internals = std::nullopt) {
auto& replica_db = db.real_database();
auto tables = ks->tables() | std::views::filter([&replica_db] (const schema_ptr& s) {
return !cdc::is_log_for_some_table(replica_db, s->ks_name(), s->cf_name());
return !cdc::is_log_for_some_table(replica_db, s->ks_name(), s->cf_name()) && !service::paxos::paxos_store::try_get_base_table(s->cf_name());
}) | std::ranges::to<std::vector<schema_ptr>>();
std::ranges::sort(tables, std::ranges::less(), std::mem_fn(&schema::cf_name));

View File

@@ -34,6 +34,12 @@ CDC_LOG_TABLE_DESC_PREFIX =
" enabled option or creating the vector index on the base table's vector column.\n" \
"\n"
CDC_LOG_TABLE_DESC_SUFFIX = "\n*/"
# The prefix of the create statement returned by `DESC TABLE "tbl$paxos"` and corresponding to a Paxos state table.
PAXOS_STATE_TABLE_DESC_PREFIX = \
"/* Do NOT execute this statement! It's only for informational purposes.\n" \
" A paxos state table is created automatically when enabling LWT on a base table.\n" \
"\n"
PAXOS_STATE_TABLE_DESC_SUFFIX = "\n*/"
def filter_non_default_user(desc_result_iter: Iterable[DescRowType]) -> Iterable[DescRowType]:
return filter(lambda result: result.name != DEFAULT_SUPERUSER, desc_result_iter)
@@ -3376,3 +3382,62 @@ def test_desc_table_tombstone_gc(cql, test_keyspace, scylla_only):
# ignore spaces in comparison, as different versions of Scylla
# add spaces in different places
assert with_clause.replace(' ','') in desc.create_statement.replace(' ','')
# Ever since tablets were introduced to Scylla, LWT writes its Paxos log not
# in a central system table, but in a new table named "...$paxos" in the same
# keyspace.
# Similary as for CDC's internal tables, we expect these internal Paxos tables
# (which have names that are not valid CQL) to be hidden from various DESCRIBE commands.
# The only difference is that when describing a CDC base table,
# an ALTER statement of the log table is added to the description,
# but this is not the case for Paxos tables.
# This test checks that these internal tables aren't listed by DESCRIBE commands.
# Reproduces issue #28183
def test_hide_paxos_table(cql, test_keyspace):
with new_test_table(cql, test_keyspace, "p int primary key, x int") as table:
# The extra "...$paxos" table only appears after a real LWT write is
# performed. So let's do an LWT operation that will cause it to be
# created.
cql.execute(f'INSERT INTO {table}(p,x) values (1,2) IF NOT EXISTS')
# DESC TABLES
# Look at the tables in test_keyspace, check that the test table is
# in this list, but its long and unique name (by unique_table_name())
# isn't a proper substring of any other table's name.
tables = [r.name for r in cql.execute('DESC TABLES') if r.keyspace_name == test_keyspace]
_, table_name = table.split('.')
assert table_name in tables
for listed_name in tables:
if table_name != listed_name:
assert table_name not in listed_name
# DESC SCHEMA
tables = [r.name for r in cql.execute('DESC SCHEMA') if r.keyspace_name == test_keyspace]
assert table_name in tables
for listed_name in tables:
if table_name != listed_name:
assert table_name not in listed_name
# DESC KEYSPACE of the test keyspace
# Again, the test table should be in the list, but no other table
# that contains that name.
tables = [r.name for r in cql.execute(f'DESC KEYSPACE {test_keyspace}')]
assert table_name in tables
for listed_name in tables:
if table_name != listed_name:
assert table_name not in listed_name
# It is allowed to directly describe a Paxos state table with `DESC ks."tbl$paxos"`
# but it should contain only commented-out CQL statements, so executing them is a no-op.
def test_paxos_table_described_in_comment(scylla_only, cql, test_keyspace):
paxos_table_desc = ""
with new_test_table(cql, test_keyspace, "p int primary key, x int") as table:
# The extra "...$paxos" table only appears after a real LWT write is
# performed. So let's do an LWT operation that will cause it to be
# created.
cql.execute(f'INSERT INTO {table}(p,x) values (1,2) IF NOT EXISTS')
paxos_table_desc = cql.execute(f'DESC TABLE {test_keyspace}."{table.split('.')[1]}$paxos"').one().create_statement
assert paxos_table_desc.startswith(PAXOS_STATE_TABLE_DESC_PREFIX)
assert paxos_table_desc.endswith(PAXOS_STATE_TABLE_DESC_SUFFIX)
assert f'CREATE TABLE {test_keyspace}."{table.split('.')[1]}$paxos"' in paxos_table_desc