Skip to content

Commit ff7bf49

Browse files
authored
Merge pull request scylladb#1249 from muzarski/refactor-column-specs
Refactor `ColumnSpecs` and adjust `PreparedStatement` API
2 parents dbca381 + e3425e1 commit ff7bf49

File tree

5 files changed

+87
-96
lines changed

5 files changed

+87
-96
lines changed

scylla/src/client/pager.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ impl QueryPager {
646646
pub fn type_check<'frame, 'metadata, RowT: DeserializeRow<'frame, 'metadata>>(
647647
&self,
648648
) -> Result<(), TypeCheckError> {
649-
RowT::type_check(self.column_specs().inner())
649+
RowT::type_check(self.column_specs().as_slice())
650650
}
651651

652652
/// Casts the iterator to a given row type, enabling [Stream]'ed operations
@@ -955,7 +955,7 @@ impl QueryPager {
955955

956956
/// Returns specification of row columns
957957
#[inline]
958-
pub fn column_specs(&self) -> ColumnSpecs<'_> {
958+
pub fn column_specs(&self) -> ColumnSpecs<'_, '_> {
959959
ColumnSpecs::new(self.current_page.metadata().col_specs())
960960
}
961961

scylla/src/response/query_result.rs

Lines changed: 22 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -8,87 +8,23 @@ use scylla_cql::deserialize::row::DeserializeRow;
88
use scylla_cql::deserialize::{DeserializationError, TypeCheckError};
99
use scylla_cql::frame::frame_errors::ResultMetadataAndRowsCountParseError;
1010
use scylla_cql::frame::response::result::{
11-
ColumnSpec, ColumnType, DeserializedMetadataAndRawRows, RawMetadataAndRawRows, TableSpec,
11+
ColumnSpec, DeserializedMetadataAndRawRows, RawMetadataAndRawRows,
1212
};
1313

14-
/// A view over specification of a table in the database.
15-
#[derive(Debug, Clone, Copy)]
16-
#[cfg_attr(test, derive(PartialEq, Eq))]
17-
pub struct TableSpecView<'res> {
18-
table_name: &'res str,
19-
ks_name: &'res str,
20-
}
21-
22-
impl<'res> TableSpecView<'res> {
23-
pub(crate) fn new_from_table_spec(spec: &'res TableSpec) -> Self {
24-
Self {
25-
table_name: spec.table_name(),
26-
ks_name: spec.ks_name(),
27-
}
28-
}
29-
30-
/// The name of the table.
31-
#[inline]
32-
pub fn table_name(&self) -> &'res str {
33-
self.table_name
34-
}
35-
36-
/// The name of the keyspace the table resides in.
37-
#[inline]
38-
pub fn ks_name(&self) -> &'res str {
39-
self.ks_name
40-
}
41-
}
42-
43-
/// A view over specification of a column returned by the database.
44-
#[derive(Debug, Clone, Copy)]
45-
#[cfg_attr(test, derive(PartialEq, Eq))]
46-
pub struct ColumnSpecView<'res> {
47-
table_spec: TableSpecView<'res>,
48-
name: &'res str,
49-
typ: &'res ColumnType<'res>,
50-
}
51-
52-
impl<'res> ColumnSpecView<'res> {
53-
pub(crate) fn new_from_column_spec(spec: &'res ColumnSpec) -> Self {
54-
Self {
55-
table_spec: TableSpecView::new_from_table_spec(spec.table_spec()),
56-
name: spec.name(),
57-
typ: spec.typ(),
58-
}
59-
}
60-
61-
/// Returns a view over specification of the table the column is part of.
62-
#[inline]
63-
pub fn table_spec(&self) -> TableSpecView<'res> {
64-
self.table_spec
65-
}
66-
67-
/// The column's name.
68-
#[inline]
69-
pub fn name(&self) -> &'res str {
70-
self.name
71-
}
72-
73-
/// The column's CQL type.
74-
#[inline]
75-
pub fn typ(&self) -> &'res ColumnType {
76-
self.typ
77-
}
78-
}
79-
8014
/// A view over specification of columns returned by the database.
8115
#[derive(Debug, Clone, Copy)]
82-
pub struct ColumnSpecs<'res> {
83-
specs: &'res [ColumnSpec<'res>],
16+
pub struct ColumnSpecs<'slice, 'spec> {
17+
specs: &'slice [ColumnSpec<'spec>],
8418
}
8519

86-
impl<'res> ColumnSpecs<'res> {
87-
pub(crate) fn new(specs: &'res [ColumnSpec<'res>]) -> Self {
20+
impl<'slice, 'spec> ColumnSpecs<'slice, 'spec> {
21+
/// Creates new [`ColumnSpecs`] wrapper from a slice.
22+
pub fn new(specs: &'slice [ColumnSpec<'spec>]) -> Self {
8823
Self { specs }
8924
}
9025

91-
pub(crate) fn inner(&self) -> &'res [ColumnSpec<'res>] {
26+
/// Returns a slice of col specs encompassed by this struct.
27+
pub fn as_slice(&self) -> &'slice [ColumnSpec<'spec>] {
9228
self.specs
9329
}
9430

@@ -101,25 +37,24 @@ impl<'res> ColumnSpecs<'res> {
10137

10238
/// Returns specification of k-th column returned from the database.
10339
#[inline]
104-
pub fn get_by_index(&self, k: usize) -> Option<ColumnSpecView<'res>> {
105-
self.specs.get(k).map(ColumnSpecView::new_from_column_spec)
40+
pub fn get_by_index(&self, k: usize) -> Option<&'slice ColumnSpec<'spec>> {
41+
self.specs.get(k)
10642
}
10743

10844
/// Returns specification of the column with given name returned from the database.
10945
#[inline]
110-
pub fn get_by_name(&self, name: &str) -> Option<(usize, ColumnSpecView<'res>)> {
46+
pub fn get_by_name(&self, name: &str) -> Option<(usize, &'slice ColumnSpec<'spec>)> {
11147
self.specs
11248
.iter()
11349
.enumerate()
11450
.find(|(_idx, spec)| spec.name() == name)
115-
.map(|(idx, spec)| (idx, ColumnSpecView::new_from_column_spec(spec)))
11651
}
11752

11853
/// Returns iterator over specification of columns returned from the database,
11954
/// ordered by column order in the response.
12055
#[inline]
121-
pub fn iter(&self) -> impl Iterator<Item = ColumnSpecView<'res>> {
122-
self.specs.iter().map(ColumnSpecView::new_from_column_spec)
56+
pub fn iter(&self) -> impl Iterator<Item = &'slice ColumnSpec<'spec>> {
57+
self.specs.iter()
12358
}
12459
}
12560

@@ -316,7 +251,7 @@ impl QueryRowsResult {
316251

317252
/// Returns column specifications.
318253
#[inline]
319-
pub fn column_specs(&self) -> ColumnSpecs {
254+
pub fn column_specs(&self) -> ColumnSpecs<'_, '_> {
320255
ColumnSpecs::new(self.raw_rows_with_metadata.metadata().col_specs())
321256
}
322257

@@ -487,8 +422,8 @@ mod tests {
487422
use assert_matches::assert_matches;
488423
use bytes::{Bytes, BytesMut};
489424
use itertools::Itertools as _;
490-
use scylla_cql::frame::response::result::NativeType;
491-
use scylla_cql::frame::response::result::ResultMetadata;
425+
use scylla_cql::frame::response::result::{ColumnType, ResultMetadata};
426+
use scylla_cql::frame::response::result::{NativeType, TableSpec};
492427
use scylla_cql::frame::types;
493428

494429
use super::*;
@@ -589,9 +524,7 @@ mod tests {
589524
// By index
590525
{
591526
for (i, expected_col_spec) in column_spec_infinite_iter().enumerate().take(n) {
592-
let expected_view =
593-
ColumnSpecView::new_from_column_spec(&expected_col_spec);
594-
assert_eq!(column_specs.get_by_index(i), Some(expected_view));
527+
assert_eq!(column_specs.get_by_index(i), Some(&expected_col_spec));
595528
}
596529

597530
assert_matches!(column_specs.get_by_index(n), None);
@@ -602,9 +535,10 @@ mod tests {
602535
for (idx, expected_col_spec) in column_spec_infinite_iter().enumerate().take(n)
603536
{
604537
let name = expected_col_spec.name();
605-
let expected_view =
606-
ColumnSpecView::new_from_column_spec(&expected_col_spec);
607-
assert_eq!(column_specs.get_by_name(name), Some((idx, expected_view)));
538+
assert_eq!(
539+
column_specs.get_by_name(name),
540+
Some((idx, &expected_col_spec))
541+
);
608542
}
609543

610544
assert_matches!(column_specs.get_by_name("ala ma kota"), None);
@@ -615,9 +549,7 @@ mod tests {
615549
for (got_view, expected_col_spec) in
616550
column_specs.iter().zip(column_spec_infinite_iter())
617551
{
618-
let expected_view =
619-
ColumnSpecView::new_from_column_spec(&expected_col_spec);
620-
assert_eq!(got_view, expected_view);
552+
assert_eq!(got_view, &expected_col_spec);
621553
}
622554
}
623555
}

scylla/src/statement/prepared_statement.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::frame::response::result::PreparedMetadata;
1919
use crate::frame::types::{Consistency, SerialConsistency};
2020
use crate::observability::history::HistoryListener;
2121
use crate::policies::retry::RetryPolicy;
22+
use crate::response::query_result::ColumnSpecs;
2223
use crate::routing::partitioner::{Partitioner, PartitionerHasher, PartitionerName};
2324
use crate::routing::Token;
2425

@@ -398,8 +399,8 @@ impl PreparedStatement {
398399
}
399400

400401
/// Access column specifications of the bind variables of this statement
401-
pub fn get_variable_col_specs(&self) -> &[ColumnSpec<'static>] {
402-
&self.shared.metadata.col_specs
402+
pub fn get_variable_col_specs(&self) -> ColumnSpecs<'_, 'static> {
403+
ColumnSpecs::new(&self.shared.metadata.col_specs)
403404
}
404405

405406
/// Access info about partition key indexes of the bind variables of this statement
@@ -413,8 +414,8 @@ impl PreparedStatement {
413414
}
414415

415416
/// Access column specifications of the result set returned after the execution of this statement
416-
pub fn get_result_set_col_specs(&self) -> &[ColumnSpec<'static>] {
417-
self.shared.result_metadata.col_specs()
417+
pub fn get_result_set_col_specs(&self) -> ColumnSpecs<'_, 'static> {
418+
ColumnSpecs::new(self.shared.result_metadata.col_specs())
418419
}
419420

420421
/// Get the name of the partitioner used for this statement.

scylla/tests/integration/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ mod shards;
1717
mod silent_prepare_batch;
1818
mod silent_prepare_query;
1919
mod skip_metadata_optimization;
20+
mod statement;
2021
mod tablets;
2122
pub(crate) mod utils;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use scylla::cluster::metadata::{ColumnType, NativeType};
2+
use scylla::frame::response::result::{ColumnSpec, TableSpec};
3+
4+
use crate::utils::{create_new_session_builder, setup_tracing, unique_keyspace_name, PerformDDL};
5+
6+
#[tokio::test]
7+
async fn test_prepared_statement_col_specs() {
8+
setup_tracing();
9+
let session = create_new_session_builder().build().await.unwrap();
10+
11+
let ks = unique_keyspace_name();
12+
session
13+
.ddl(format!(
14+
"CREATE KEYSPACE IF NOT EXISTS {} WITH REPLICATION =
15+
{{'class' : 'NetworkTopologyStrategy', 'replication_factor' : 3}}",
16+
ks
17+
))
18+
.await
19+
.unwrap();
20+
session.use_keyspace(&ks, false).await.unwrap();
21+
22+
session
23+
.ddl(
24+
"CREATE TABLE t (k1 int, k2 varint, c1 timestamp,
25+
a tinyint, b text, c smallint, PRIMARY KEY ((k1, k2), c1))",
26+
)
27+
.await
28+
.unwrap();
29+
30+
let spec = |name: &'static str, typ: ColumnType<'static>| -> ColumnSpec<'_> {
31+
ColumnSpec::borrowed(name, typ, TableSpec::borrowed(&ks, "t"))
32+
};
33+
34+
let prepared = session
35+
.prepare("SELECT * FROM t WHERE k1 = ? AND k2 = ? AND c1 > ?")
36+
.await
37+
.unwrap();
38+
39+
let variable_col_specs = prepared.get_variable_col_specs().as_slice();
40+
let expected_variable_col_specs = &[
41+
spec("k1", ColumnType::Native(NativeType::Int)),
42+
spec("k2", ColumnType::Native(NativeType::Varint)),
43+
spec("c1", ColumnType::Native(NativeType::Timestamp)),
44+
];
45+
assert_eq!(variable_col_specs, expected_variable_col_specs);
46+
47+
let result_set_col_specs = prepared.get_result_set_col_specs().as_slice();
48+
let expected_result_set_col_specs = &[
49+
spec("k1", ColumnType::Native(NativeType::Int)),
50+
spec("k2", ColumnType::Native(NativeType::Varint)),
51+
spec("c1", ColumnType::Native(NativeType::Timestamp)),
52+
spec("a", ColumnType::Native(NativeType::TinyInt)),
53+
spec("b", ColumnType::Native(NativeType::Text)),
54+
spec("c", ColumnType::Native(NativeType::SmallInt)),
55+
];
56+
assert_eq!(result_set_col_specs, expected_result_set_col_specs);
57+
}

0 commit comments

Comments
 (0)