From b596c8d35852e9bfedb2d0b3a3619bae784f4cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Uzarski?= Date: Tue, 11 Mar 2025 16:21:17 +0100 Subject: [PATCH 1/2] metadata: implement cass_table_meta_column --- scylla-rust-wrapper/src/metadata.rs | 69 +++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/scylla-rust-wrapper/src/metadata.rs b/scylla-rust-wrapper/src/metadata.rs index 6ee0708e..56999e0a 100644 --- a/scylla-rust-wrapper/src/metadata.rs +++ b/scylla-rust-wrapper/src/metadata.rs @@ -32,6 +32,8 @@ pub struct CassTableMeta { pub columns_metadata: HashMap, pub partition_keys: Vec, pub clustering_keys: Vec, + /// Non-key columns sorted alphabetically by name. + pub non_key_sorted_columns: Vec, pub views: HashMap>, } @@ -81,11 +83,25 @@ pub fn create_table_metadata(table_name: &str, table_metadata: &Table) -> CassTa columns_metadata.insert(column_name.clone(), cass_column_meta); }); + let mut non_key_sorted_columns = columns_metadata + .iter() + .filter(|(_, column)| { + !matches!( + column.column_kind, + CassColumnType::CASS_COLUMN_TYPE_PARTITION_KEY + | CassColumnType::CASS_COLUMN_TYPE_CLUSTERING_KEY, + ) + }) + .map(|(name, _column)| name.to_owned()) + .collect::>(); + non_key_sorted_columns.sort_unstable(); + CassTableMeta { name: table_name.to_owned(), columns_metadata, partition_keys: table_metadata.partition_key.clone(), clustering_keys: table_metadata.clustering_key.clone(), + non_key_sorted_columns, views: HashMap::new(), } } @@ -209,6 +225,59 @@ pub unsafe extern "C" fn cass_table_meta_column_count(table_meta: *const CassTab table_meta.columns_metadata.len() as size_t } +#[no_mangle] +pub unsafe extern "C" fn cass_table_meta_column( + table_meta: *const CassTableMeta, + index: size_t, +) -> *const CassColumnMeta { + // The order of columns in cpp-driver (and in DESCRIBE TABLE in cqlsh): + // 1. partition keys sorted by position <- this is guaranteed by rust-driver. + // Table::partition_keys is a Vector of pk names, sorted by position. + // 2. clustering keys sorted by position <- this is guaranteed by rust-driver (same reasoning as above). + // 3. remaining columns in alphabetical order <- this is something we need to guarantee. + // + // Example: + // CREATE TABLE t + // ( + // i int, f int, g int STATIC, b int, c int STATIC, a int, d int, j int, h int, + // PRIMARY KEY( (d, a, j), h, i ) + // ); + // + // The order should be: d, a, j, h, i, b, c, f, g + // First pks by position: d, a, j + // Then cks by position: h, i + // Then remaining columns alphabetically: b, c, f, g + + let table_meta = RefFFI::as_ref(table_meta); + let index = index as usize; + + // Check if the index lands in partition keys. If so, simply return the corresponding column. + if let Some(pk_name) = table_meta.partition_keys.get(index) { + // unwrap: partition key must exist in columns_metadata. This is ensured by rust-driver. + return RefFFI::as_ptr(table_meta.columns_metadata.get(pk_name).unwrap()); + } + + // Update the index to search in clustering keys + let index = index - table_meta.partition_keys.len(); + + // Check if the index lands in clustering keys. If so, simply return the corresponding column. + if let Some(ck_name) = table_meta.clustering_keys.get(index) { + // unwrap: clustering key must exist in columns_metadata. This is ensured by rust-driver. + return RefFFI::as_ptr(table_meta.columns_metadata.get(ck_name).unwrap()); + } + + // Update the index to search in remaining columns + let index = index - table_meta.clustering_keys.len(); + + table_meta + .non_key_sorted_columns + .get(index) + .map_or(std::ptr::null(), |column_name| { + // unwrap: We guarantee that column_name exists in columns_metadata. See `create_table_metadata`. + RefFFI::as_ptr(table_meta.columns_metadata.get(column_name).unwrap()) + }) +} + #[no_mangle] pub unsafe extern "C" fn cass_table_meta_partition_key( table_meta: *const CassTableMeta, From d65f06be11d8cf7ba973fee6e11dc498d0aeec41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Uzarski?= Date: Tue, 11 Mar 2025 17:23:36 +0100 Subject: [PATCH 2/2] it: implement integration test to verify metadata column order --- .../tests/test_schema_metadata.cpp | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/src/integration/tests/test_schema_metadata.cpp b/tests/src/integration/tests/test_schema_metadata.cpp index 0a380e19..0ed3ae95 100644 --- a/tests/src/integration/tests/test_schema_metadata.cpp +++ b/tests/src/integration/tests/test_schema_metadata.cpp @@ -254,6 +254,53 @@ CASSANDRA_INTEGRATION_TEST_F(SchemaMetadataTest, KeyspaceMetadata) { cass_schema_meta_free(schema_meta); } +CASSANDRA_INTEGRATION_TEST_F(SchemaMetadataTest, TableMetadataColumnOrder) { + // The order should be: d, a, j, h, i, b, c, f, g + // First pks by position: d, a, j + // Then cks by position: h, i + // Then remaining columns alphabetically: b, c, f, g + + session_.execute(format_string("CREATE TABLE %s " + "(i int, f int, g int STATIC, b int, c int STATIC, a int, d int, j int, h int, " + "PRIMARY KEY( (d, a, j), h, i ) )", "column_order_test")); + + const CassSchemaMeta* schema_meta = session_.schema_meta(); + + const CassKeyspaceMeta* keyspace_meta = cass_schema_meta_keyspace_by_name(schema_meta, keyspace_name_.c_str()); + ASSERT_TRUE(keyspace_meta); + + const CassTableMeta* table_meta = cass_keyspace_meta_table_by_name(keyspace_meta, "column_order_test"); + ASSERT_TRUE(table_meta); + + ASSERT_EQ(cass_table_meta_column_count(table_meta), 9u); + + ASSERT_EQ(cass_table_meta_partition_key_count(table_meta), 3u); + ASSERT_EQ(cass_table_meta_clustering_key_count(table_meta), 2u); + + auto check_column = [&](size_t index, const char* name) { + const CassColumnMeta* column_meta; + const char* column_meta_name; + size_t column_meta_name_length; + + column_meta = cass_table_meta_column(table_meta, index); + ASSERT_TRUE(column_meta); + cass_column_meta_name(column_meta, &column_meta_name, &column_meta_name_length); + ASSERT_EQ(std::string(column_meta_name, column_meta_name_length), name); + }; + + check_column(0, "d"); + check_column(1, "a"); + check_column(2, "j"); + check_column(3, "h"); + check_column(4, "i"); + check_column(5, "b"); + check_column(6, "c"); + check_column(7, "f"); + check_column(8, "g"); + + cass_schema_meta_free(schema_meta); +} + CASSANDRA_INTEGRATION_TEST_F(SchemaMetadataTest, MetadataIterator) { const CassSchemaMeta* schema_meta = session_.schema_meta();