diff --git a/crates/proof-of-sql-parser/src/error.rs b/crates/proof-of-sql-parser/src/error.rs index e412df51a..32f2e3017 100644 --- a/crates/proof-of-sql-parser/src/error.rs +++ b/crates/proof-of-sql-parser/src/error.rs @@ -2,6 +2,7 @@ use alloc::string::String; use snafu::Snafu; /// Errors encountered during the parsing process +#[allow(clippy::module_name_repetitions)] #[derive(Debug, Snafu, Eq, PartialEq)] pub enum ParseError { #[snafu(display("Unable to parse query"))] @@ -17,13 +18,13 @@ pub enum ParseError { error: String, }, #[snafu(display("Unable to parse resource_id"))] - /// Can not parse the resource_id + /// Can not parse the `resource_id` ResourceIdParseError { /// The underlying error error: String, }, } -/// General parsing error that may occur, for example if the provided schema/object_name strings +/// General parsing error that may occur, for example if the provided `schema`/`object_name` strings /// aren't valid postgres-style identifiers (excluding dollar signs). pub type ParseResult = Result; diff --git a/crates/proof-of-sql-parser/src/identifier.rs b/crates/proof-of-sql-parser/src/identifier.rs index a5069aa28..3248bcd14 100644 --- a/crates/proof-of-sql-parser/src/identifier.rs +++ b/crates/proof-of-sql-parser/src/identifier.rs @@ -12,9 +12,9 @@ pub struct Identifier { impl Identifier { /// Constructor for [Identifier] /// - /// Note: this constructor should be private within the proof_of_sql_parser crate. + /// Note: this constructor should be private within the `proof_of_sql_parser` crate. /// This is necessary to guarantee that no one outside the crate - /// can create Names, thus securing that ResourceIds and Identifiers + /// can create Names, thus securing that [`ResourceId`]s and [`Identifier`]s /// are always valid postgresql identifiers. pub(crate) fn new>(string: S) -> Self { Self { @@ -22,18 +22,25 @@ impl Identifier { } } - /// An alias for [Identifier::from_str], provided for convenience. + /// An alias for [`Identifier::from_str`], provided for convenience. + /// + /// # Errors + /// Returns a `ParseResult::Err` if the input string does not meet the requirements for a valid identifier. + /// This may include errors such as invalid characters or incorrect formatting based on the specific rules + /// that `Identifier::from_str` enforces. pub fn try_new>(string: S) -> ParseResult { Self::from_str(string.as_ref()) } /// The name of this [Identifier] /// It already implements [Deref] to [str], so this method is not necessary for most use cases. + #[must_use] pub fn name(&self) -> &str { self.name.as_str() } - /// An alias for [Identifier::name], provided for convenience. + /// An alias for [`Identifier::name`], provided for convenience. + #[must_use] pub fn as_str(&self) -> &str { self.name() } @@ -46,7 +53,7 @@ impl FromStr for Identifier { let name = IdentifierParser::new() .parse(string) .map_err(|e| ParseError::IdentifierParseError{ error: - format!("failed to parse identifier, (you may have used a reserved keyword as an ID, i.e. 'timestamp') {:?}", e)})?; + format!("failed to parse identifier, (you may have used a reserved keyword as an ID, i.e. 'timestamp') {e:?}")})?; Ok(Identifier::new(name)) } diff --git a/crates/proof-of-sql-parser/src/intermediate_ast.rs b/crates/proof-of-sql-parser/src/intermediate_ast.rs index 2095301a5..70bec1122 100644 --- a/crates/proof-of-sql-parser/src/intermediate_ast.rs +++ b/crates/proof-of-sql-parser/src/intermediate_ast.rs @@ -13,7 +13,7 @@ use core::{ }; use serde::{Deserialize, Serialize}; -/// Representation of a SetExpression, a collection of rows, each having one or more columns. +/// Representation of a `SetExpression`, a collection of rows, each having one or more columns. #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub enum SetExpression { /// Query result as `SetExpression` @@ -50,6 +50,7 @@ pub struct AliasedResultExpr { impl AliasedResultExpr { /// Create a new `AliasedResultExpr` + #[must_use] pub fn new(expr: Expression, alias: Identifier) -> Self { Self { expr: Box::new(expr), @@ -59,6 +60,7 @@ impl AliasedResultExpr { /// Try to get the identifier of the expression if it is a column /// Otherwise return None + #[must_use] pub fn try_as_identifier(&self) -> Option<&Identifier> { match self.expr.as_ref() { Expression::Column(column) => Some(column), @@ -185,7 +187,8 @@ pub enum Expression { } impl Expression { - /// Create a new SUM() + /// Create a new `SUM()` + #[must_use] pub fn sum(self) -> Box { Box::new(Expression::Aggregation { op: AggregationOperator::Sum, @@ -193,7 +196,8 @@ impl Expression { }) } - /// Create a new MAX() + /// Create a new `MAX()` + #[must_use] pub fn max(self) -> Box { Box::new(Expression::Aggregation { op: AggregationOperator::Max, @@ -201,7 +205,8 @@ impl Expression { }) } - /// Create a new MIN() + /// Create a new `MIN()` + #[must_use] pub fn min(self) -> Box { Box::new(Expression::Aggregation { op: AggregationOperator::Min, @@ -209,7 +214,8 @@ impl Expression { }) } - /// Create a new COUNT() + /// Create a new `COUNT()` + #[must_use] pub fn count(self) -> Box { Box::new(Expression::Aggregation { op: AggregationOperator::Count, @@ -217,7 +223,8 @@ impl Expression { }) } - /// Create a new FIRST() + /// Create a new `FIRST()` + #[must_use] pub fn first(self) -> Box { Box::new(Expression::Aggregation { op: AggregationOperator::First, @@ -225,6 +232,7 @@ impl Expression { }) } /// Create an `AliasedResultExpr` from an `Expression` using the provided alias. + #[must_use] pub fn alias(self, alias: &str) -> AliasedResultExpr { AliasedResultExpr { expr: Box::new(self), @@ -277,7 +285,7 @@ impl core::ops::Sub> for Box { } } -/// OrderBy +/// `OrderBy` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct OrderBy { /// which column to order by @@ -286,7 +294,7 @@ pub struct OrderBy { pub direction: OrderByDirection, } -/// OrderByDirection values +/// `OrderByDirection` values #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)] pub enum OrderByDirection { /// Ascending @@ -310,7 +318,7 @@ impl Display for OrderByDirection { pub struct Slice { /// number of rows to return /// - /// if u64::MAX, specify all rows + /// if `u64::MAX`, specify all rows pub number_rows: u64, /// number of rows to skip @@ -349,7 +357,7 @@ macro_rules! impl_int_to_literal { ($tt:ty) => { impl From<$tt> for Literal { fn from(val: $tt) -> Self { - Literal::BigInt(val as i64) + Literal::BigInt(i64::from(val)) } } }; diff --git a/crates/proof-of-sql-parser/src/intermediate_decimal.rs b/crates/proof-of-sql-parser/src/intermediate_decimal.rs index 9ebaeb04e..1cfa7e1c9 100644 --- a/crates/proof-of-sql-parser/src/intermediate_decimal.rs +++ b/crates/proof-of-sql-parser/src/intermediate_decimal.rs @@ -13,6 +13,7 @@ use serde::{Deserialize, Serialize}; use snafu::Snafu; /// Errors related to the processing of decimal values in proof-of-sql +#[allow(clippy::module_name_repetitions)] #[derive(Snafu, Debug, PartialEq)] pub enum IntermediateDecimalError { /// Represents an error encountered during the parsing of a decimal string. @@ -47,22 +48,34 @@ pub struct IntermediateDecimal { impl IntermediateDecimal { /// Get the integer part of the fixed-point representation of this intermediate decimal. + #[must_use] pub fn value(&self) -> BigDecimal { self.value.clone() } /// Get the precision of the fixed-point representation of this intermediate decimal. + #[must_use] pub fn precision(&self) -> u8 { - self.value.digits() as u8 + match u8::try_from(self.value.digits()) { + Ok(v) => v, + Err(_) => u8::MAX, // Returning u8::MAX on truncation + } } /// Get the scale of the fixed-point representation of this intermediate decimal. + #[must_use] pub fn scale(&self) -> i8 { - self.value.fractional_digit_count() as i8 + match i8::try_from(self.value.fractional_digit_count()) { + Ok(v) => v, + Err(_) => i8::MAX, // Returning i8::MAX on truncation + } } /// Attempts to convert the decimal to `BigInt` while adjusting it to the specified precision and scale. /// Returns an error if the conversion cannot be performed due to precision or scale constraints. + /// + /// # Errors + /// Returns an `IntermediateDecimalError::LossyCast` error if the number of digits in the scaled decimal exceeds the specified precision. pub fn try_into_bigint_with_precision_and_scale( &self, precision: u8, diff --git a/crates/proof-of-sql-parser/src/lib.rs b/crates/proof-of-sql-parser/src/lib.rs index 6702f5479..461eed134 100644 --- a/crates/proof-of-sql-parser/src/lib.rs +++ b/crates/proof-of-sql-parser/src/lib.rs @@ -1,5 +1,6 @@ #![doc = include_str!("../README.md")] #![no_std] +#![allow(clippy::missing_panics_doc)] // Fixed in Issue #163 extern crate alloc; /// Module for handling an intermediate decimal type received from the lexer. @@ -34,9 +35,9 @@ pub mod resource_id; pub use resource_id::ResourceId; // lalrpop-generated code is not clippy-compliant -lalrpop_mod!(#[allow(clippy::all, missing_docs, clippy::missing_docs_in_private_items)] pub sql); +lalrpop_mod!(#[allow(clippy::all, missing_docs, clippy::missing_docs_in_private_items, clippy::pedantic)] pub sql); -/// Implement Deserialize through FromStr to avoid invalid identifiers. +/// Implement [`Deserialize`] through [`FromStr`] to avoid invalid identifiers. #[macro_export] macro_rules! impl_serde_from_str { ($type:ty) => { diff --git a/crates/proof-of-sql-parser/src/posql_time/error.rs b/crates/proof-of-sql-parser/src/posql_time/error.rs index 9e23cd019..6897b3e1d 100644 --- a/crates/proof-of-sql-parser/src/posql_time/error.rs +++ b/crates/proof-of-sql-parser/src/posql_time/error.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; use snafu::Snafu; /// Errors related to time operations, including timezone and timestamp conversions.s +#[allow(clippy::module_name_repetitions)] #[derive(Snafu, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum PoSQLTimestampError { /// Error when the timezone string provided cannot be parsed into a valid timezone. @@ -44,7 +45,7 @@ pub enum PoSQLTimestampError { error: String, }, - /// Represents a failure to parse a provided time unit precision value, PoSQL supports + /// Represents a failure to parse a provided time unit precision value, `PoSQL` supports /// Seconds, Milliseconds, Microseconds, and Nanoseconds #[snafu(display("Timestamp parsing error: {error}"))] UnsupportedPrecision { diff --git a/crates/proof-of-sql-parser/src/posql_time/timestamp.rs b/crates/proof-of-sql-parser/src/posql_time/timestamp.rs index 87e7b59da..f036bac14 100644 --- a/crates/proof-of-sql-parser/src/posql_time/timestamp.rs +++ b/crates/proof-of-sql-parser/src/posql_time/timestamp.rs @@ -5,6 +5,7 @@ use core::hash::Hash; use serde::{Deserialize, Serialize}; /// Represents a fully parsed timestamp with detailed time unit and timezone information +#[allow(clippy::module_name_repetitions)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct PoSQLTimestamp { /// The datetime representation in UTC. @@ -19,21 +20,24 @@ pub struct PoSQLTimestamp { impl PoSQLTimestamp { /// Returns the combined date and time with time zone. + #[must_use] pub fn timestamp(&self) -> DateTime { self.timestamp } - /// Returns the [PoSQLTimeUnit] for this timestamp + /// Returns the [`PoSQLTimeUnit`] for this timestamp + #[must_use] pub fn timeunit(&self) -> PoSQLTimeUnit { self.timeunit } - /// Returns the [PoSQLTimeZone] for this timestamp + /// Returns the [`PoSQLTimeZone`] for this timestamp + #[must_use] pub fn timezone(&self) -> PoSQLTimeZone { self.timezone } - /// Attempts to parse a timestamp string into an [PoSQLTimestamp] structure. + /// Attempts to parse a timestamp string into an [`PoSQLTimestamp`] structure. /// This function supports two primary formats: /// /// 1. **RFC 3339 Parsing**: @@ -45,6 +49,13 @@ impl PoSQLTimestamp { /// - The `from_offset` method is used to determine whether the timezone should be represented /// as `Utc` or `FixedOffset`. This function simplifies the decision based on the offset value. /// + /// # Errors + /// This function returns a `PoSQLTimestampError` in the following cases: + /// + /// - **Parsing Error**: Returns `PoSQLTimestampError::ParsingError` if the input string does not conform + /// to the RFC 3339 format or if the timestamp cannot be parsed due to invalid formatting. + /// This error includes the original parsing error message for further details. + /// /// # Examples /// ``` /// use chrono::{DateTime, Utc}; @@ -94,6 +105,17 @@ impl PoSQLTimestamp { /// - Since Unix epoch timestamps don't inherently carry timezone information, /// any Unix time parsed directly from an integer is assumed to be in UTC. /// + /// # Errors + /// This function returns a `PoSQLTimestampError` in the following cases: + /// + /// - **Ambiguous Time**: Returns `PoSQLTimestampError::Ambiguous` if the provided epoch time + /// corresponds to a time that is ambiguous (e.g., during a daylight saving time change where + /// the local time could correspond to two different UTC times). + /// + /// - **Non-Existent Local Time**: Returns `PoSQLTimestampError::LocalTimeDoesNotExist` if the + /// provided epoch time corresponds to a time that does not exist in the local time zone (e.g., + /// during a daylight saving time change where a certain local time is skipped). + /// /// # Examples /// ``` /// use chrono::{DateTime, Utc}; @@ -112,7 +134,7 @@ impl PoSQLTimestamp { timezone: PoSQLTimeZone::Utc, }), LocalResult::Ambiguous(earliest, latest) => Err(PoSQLTimestampError::Ambiguous{ error: - format!("The local time is ambiguous because there is a fold in the local time: earliest: {} latest: {} ", earliest, latest), + format!("The local time is ambiguous because there is a fold in the local time: earliest: {earliest} latest: {latest} "), }), LocalResult::None => Err(PoSQLTimestampError::LocalTimeDoesNotExist), } diff --git a/crates/proof-of-sql-parser/src/posql_time/timezone.rs b/crates/proof-of-sql-parser/src/posql_time/timezone.rs index b7b9cee20..c4f775afd 100644 --- a/crates/proof-of-sql-parser/src/posql_time/timezone.rs +++ b/crates/proof-of-sql-parser/src/posql_time/timezone.rs @@ -8,12 +8,13 @@ use serde::{Deserialize, Serialize}; pub enum PoSQLTimeZone { /// Default variant for UTC timezone Utc, - /// TImezone offset in seconds + /// `TImezone` offset in seconds FixedOffset(i32), } impl PoSQLTimeZone { /// Parse a timezone from a count of seconds + #[must_use] pub fn from_offset(offset: i32) -> Self { if offset == 0 { PoSQLTimeZone::Utc @@ -67,7 +68,7 @@ impl fmt::Display for PoSQLTimeZone { if seconds < 0 { write!(f, "-{:02}:{:02}", hours.abs(), minutes) } else { - write!(f, "+{:02}:{:02}", hours, minutes) + write!(f, "+{hours:02}:{minutes:02}") } } } diff --git a/crates/proof-of-sql-parser/src/posql_time/unit.rs b/crates/proof-of-sql-parser/src/posql_time/unit.rs index 97b1d87e1..480080b76 100644 --- a/crates/proof-of-sql-parser/src/posql_time/unit.rs +++ b/crates/proof-of-sql-parser/src/posql_time/unit.rs @@ -3,6 +3,7 @@ use core::fmt; use serde::{Deserialize, Serialize}; /// An intermediate type representing the time units from a parsed query +#[allow(clippy::module_name_repetitions)] #[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize, PartialEq, Eq)] pub enum PoSQLTimeUnit { /// Represents seconds with precision 0: ex "2024-06-20 12:34:56" diff --git a/crates/proof-of-sql-parser/src/resource_id.rs b/crates/proof-of-sql-parser/src/resource_id.rs index 3c4348cc2..fa1e453ca 100644 --- a/crates/proof-of-sql-parser/src/resource_id.rs +++ b/crates/proof-of-sql-parser/src/resource_id.rs @@ -17,7 +17,8 @@ pub struct ResourceId { } impl ResourceId { - /// Constructor for [ResourceId]s. + /// Constructor for [`ResourceId`]s. + #[must_use] pub fn new(schema: Identifier, object_name: Identifier) -> Self { Self { schema, @@ -25,10 +26,10 @@ impl ResourceId { } } - /// Constructor for [ResourceId]s. + /// Constructor for [`ResourceId`]s. /// /// # Errors - /// Fails if the provided schema/object_name strings aren't valid postgres-style + /// Fails if the provided `schema/object_name` strings aren't valid postgres-style /// identifiers (excluding dollar signs). /// These identifiers are defined here: /// . @@ -42,27 +43,30 @@ impl ResourceId { }) } - /// The schema identifier of this [ResourceId]. + /// The schema identifier of this [`ResourceId`]. + #[must_use] pub fn schema(&self) -> Identifier { self.schema } - /// The object_name identifier of this [ResourceId]. + /// The `object_name` identifier of this [`ResourceId`]. + #[must_use] pub fn object_name(&self) -> Identifier { self.object_name } - /// Conversion to string in the format used in KeyDB. + /// Conversion to string in the format used in `KeyDB`. /// /// Space and time APIs accept a `.` separator in resource ids. - /// However, when a resource id is stored in KeyDB, or used as a key, a `:` separator is used. - /// This method differs from [ResourceId::to_string] by using the latter format. + /// However, when a resource id is stored in `KeyDB`, or used as a key, a `:` separator is used. + /// This method differs from [`ResourceId::to_string`] by using the latter format. /// /// Furthermore, while space and time APIs accept lowercase resource identifiers, /// all resource identifiers are stored internally in uppercase. /// This method performs that transformation as well. /// For more information, see /// . + #[must_use] pub fn storage_format(&self) -> String { let ResourceId { schema, @@ -93,7 +97,7 @@ impl FromStr for ResourceId { fn from_str(string: &str) -> ParseResult { let (schema, object_name) = ResourceIdParser::new().parse(string).map_err(|e| { ParseError::ResourceIdParseError { - error: format!("{:?}", e), + error: format!("{e:?}"), } })?; diff --git a/crates/proof-of-sql-parser/src/select_statement.rs b/crates/proof-of-sql-parser/src/select_statement.rs index 283e0820f..cd97c4ce8 100644 --- a/crates/proof-of-sql-parser/src/select_statement.rs +++ b/crates/proof-of-sql-parser/src/select_statement.rs @@ -1,7 +1,7 @@ use super::intermediate_ast::{OrderBy, SetExpression, Slice, TableExpression}; use crate::{sql::SelectStatementParser, Identifier, ParseError, ParseResult, ResourceId}; use alloc::{boxed::Box, string::ToString, vec::Vec}; -use core::{fmt, ops::Deref, str::FromStr}; +use core::{fmt, str::FromStr}; use serde::{Deserialize, Serialize}; /// Representation of a select statement, that is, the only type of queries allowed. @@ -29,11 +29,11 @@ impl fmt::Debug for SelectStatement { } impl SelectStatement { - /// This function returns the referenced tables in the provided intermediate_ast + /// This function returns the referenced tables in the provided `intermediate_ast` /// /// Note that we provide a `default_schema` in case the table expression /// does not have any associated schema. This `default_schema` is - /// used to construct the resource_id, as we cannot have this field empty. + /// used to construct the `resource_id`, as we cannot have this field empty. /// In case the table expression already has an associated schema, /// then it's used instead of `default_schema`. Although the DQL endpoint /// would require both to be equal, we have chosen to not fail here @@ -42,6 +42,7 @@ impl SelectStatement { /// /// Return: /// - The vector with all tables referenced by the intermediate ast, encoded as resource ids. + #[must_use] pub fn get_table_references(&self, default_schema: Identifier) -> Vec { let set_expression: &SetExpression = &(self.expr); @@ -74,15 +75,15 @@ fn convert_table_expr_to_resource_id_vector( ) -> Vec { let mut tables = Vec::new(); - for table_expression in table_expressions.iter() { - let table_ref: &TableExpression = table_expression.deref(); + for table_expression in table_expressions { + let table_ref: &TableExpression = table_expression; match table_ref { TableExpression::Named { table, schema } => { - let schema = schema - .as_ref() - .map(|schema| schema.as_str()) - .unwrap_or_else(|| default_schema.name()); + let schema = schema.as_ref().map_or_else( + || default_schema.name(), + super::identifier::Identifier::as_str, + ); tables.push(ResourceId::try_new(schema, table.as_str()).unwrap()); } diff --git a/crates/proof-of-sql-parser/src/utility.rs b/crates/proof-of-sql-parser/src/utility.rs index 87d3d99ba..d6b0d8957 100644 --- a/crates/proof-of-sql-parser/src/utility.rs +++ b/crates/proof-of-sql-parser/src/utility.rs @@ -1,12 +1,20 @@ -use crate::{intermediate_ast::*, Identifier, SelectStatement}; +use crate::{ + intermediate_ast::{ + AggregationOperator, AliasedResultExpr, BinaryOperator, Expression, Literal, OrderBy, + OrderByDirection, SelectResultExpr, SetExpression, Slice, TableExpression, UnaryOperator, + }, + Identifier, SelectStatement, +}; use alloc::{boxed::Box, vec, vec::Vec}; /// Construct an identifier from a str +#[must_use] pub fn ident(name: &str) -> Identifier { name.parse().unwrap() } /// Construct a new boxed `Expression` A == B +#[must_use] pub fn equal(left: Box, right: Box) -> Box { Box::new(Expression::Binary { op: BinaryOperator::Equal, @@ -16,6 +24,7 @@ pub fn equal(left: Box, right: Box) -> Box { } /// Construct a new boxed `Expression` A >= B +#[must_use] pub fn ge(left: Box, right: Box) -> Box { Box::new(Expression::Binary { op: BinaryOperator::GreaterThanOrEqual, @@ -25,6 +34,7 @@ pub fn ge(left: Box, right: Box) -> Box { } /// Construct a new boxed `Expression` A <= B +#[must_use] pub fn le(left: Box, right: Box) -> Box { Box::new(Expression::Binary { op: BinaryOperator::LessThanOrEqual, @@ -34,6 +44,7 @@ pub fn le(left: Box, right: Box) -> Box { } /// Construct a new boxed `Expression` NOT P +#[must_use] pub fn not(expr: Box) -> Box { Box::new(Expression::Unary { op: UnaryOperator::Not, @@ -42,6 +53,7 @@ pub fn not(expr: Box) -> Box { } /// Construct a new boxed `Expression` P AND Q +#[must_use] pub fn and(left: Box, right: Box) -> Box { Box::new(Expression::Binary { op: BinaryOperator::And, @@ -51,6 +63,7 @@ pub fn and(left: Box, right: Box) -> Box { } /// Construct a new boxed `Expression` P OR Q +#[must_use] pub fn or(left: Box, right: Box) -> Box { Box::new(Expression::Binary { op: BinaryOperator::Or, @@ -60,6 +73,7 @@ pub fn or(left: Box, right: Box) -> Box { } /// Construct a new boxed `Expression` A + B +#[must_use] pub fn add(left: Box, right: Box) -> Box { Box::new(Expression::Binary { op: BinaryOperator::Add, @@ -69,6 +83,7 @@ pub fn add(left: Box, right: Box) -> Box { } /// Construct a new boxed `Expression` A - B +#[must_use] pub fn sub(left: Box, right: Box) -> Box { Box::new(Expression::Binary { op: BinaryOperator::Subtract, @@ -78,6 +93,7 @@ pub fn sub(left: Box, right: Box) -> Box { } /// Construct a new boxed `Expression` A * B +#[must_use] pub fn mul(left: Box, right: Box) -> Box { Box::new(Expression::Binary { op: BinaryOperator::Multiply, @@ -87,6 +103,7 @@ pub fn mul(left: Box, right: Box) -> Box { } /// Construct a new boxed `Expression` A / B +#[must_use] pub fn div(left: Box, right: Box) -> Box { Box::new(Expression::Binary { op: BinaryOperator::Division, @@ -98,6 +115,7 @@ pub fn div(left: Box, right: Box) -> Box { /// Get table from schema and name. /// /// If the schema is `None`, the table is assumed to be in the default schema. +#[must_use] pub fn tab(schema: Option<&str>, name: &str) -> Box { Box::new(TableExpression::Named { table: name.parse().unwrap(), @@ -106,6 +124,7 @@ pub fn tab(schema: Option<&str>, name: &str) -> Box { } /// Get column from name +#[must_use] pub fn col(name: &str) -> Box { Box::new(Expression::Column(name.parse().unwrap())) } @@ -116,6 +135,7 @@ pub fn lit>(literal: L) -> Box { } /// Compute the sum of an expression +#[must_use] pub fn sum(expr: Box) -> Box { Box::new(Expression::Aggregation { op: AggregationOperator::Sum, @@ -124,6 +144,7 @@ pub fn sum(expr: Box) -> Box { } /// Compute the minimum of an expression +#[must_use] pub fn min(expr: Box) -> Box { Box::new(Expression::Aggregation { op: AggregationOperator::Min, @@ -132,6 +153,7 @@ pub fn min(expr: Box) -> Box { } /// Compute the maximum of an expression +#[must_use] pub fn max(expr: Box) -> Box { Box::new(Expression::Aggregation { op: AggregationOperator::Max, @@ -140,6 +162,7 @@ pub fn max(expr: Box) -> Box { } /// Count the amount of non-null entries of expression +#[must_use] pub fn count(expr: Box) -> Box { Box::new(Expression::Aggregation { op: AggregationOperator::Count, @@ -148,11 +171,13 @@ pub fn count(expr: Box) -> Box { } /// Count the rows +#[must_use] pub fn count_all() -> Box { count(Box::new(Expression::Wildcard)) } /// An expression with an alias i.e. EXPR AS ALIAS +#[must_use] pub fn aliased_expr(expr: Box, alias: &str) -> AliasedResultExpr { AliasedResultExpr { expr, @@ -161,11 +186,13 @@ pub fn aliased_expr(expr: Box, alias: &str) -> AliasedResultExpr { } /// Select all columns from a table i.e. SELECT * +#[must_use] pub fn col_res_all() -> SelectResultExpr { SelectResultExpr::ALL } /// Select one column from a table and give it an alias i.e. SELECT COL AS ALIAS +#[must_use] pub fn col_res(col_val: Box, alias: &str) -> SelectResultExpr { SelectResultExpr::AliasedResultExpr(AliasedResultExpr { expr: col_val, @@ -174,11 +201,13 @@ pub fn col_res(col_val: Box, alias: &str) -> SelectResultExpr { } /// Select multiple columns from a table i.e. SELECT COL1, COL2, ... +#[must_use] pub fn cols_res(names: &[&str]) -> Vec { names.iter().map(|name| col_res(col(name), name)).collect() } /// Compute the minimum of an expression and give it an alias i.e. SELECT MIN(EXPR) AS ALIAS +#[must_use] pub fn min_res(expr: Box, alias: &str) -> SelectResultExpr { SelectResultExpr::AliasedResultExpr(AliasedResultExpr { expr: min(expr), @@ -187,6 +216,7 @@ pub fn min_res(expr: Box, alias: &str) -> SelectResultExpr { } /// Compute the maximum of an expression and give it an alias i.e. SELECT MAX(EXPR) AS ALIAS +#[must_use] pub fn max_res(expr: Box, alias: &str) -> SelectResultExpr { SelectResultExpr::AliasedResultExpr(AliasedResultExpr { expr: max(expr), @@ -195,6 +225,7 @@ pub fn max_res(expr: Box, alias: &str) -> SelectResultExpr { } /// Compute the sum of an expression and give it an alias i.e. SELECT SUM(EXPR) AS ALIAS +#[must_use] pub fn sum_res(expr: Box, alias: &str) -> SelectResultExpr { SelectResultExpr::AliasedResultExpr(AliasedResultExpr { expr: sum(expr), @@ -203,6 +234,7 @@ pub fn sum_res(expr: Box, alias: &str) -> SelectResultExpr { } /// Count the amount of non-null entries of expression and give it an alias i.e. SELECT COUNT(EXPR) AS ALIAS +#[must_use] pub fn count_res(expr: Box, alias: &str) -> SelectResultExpr { SelectResultExpr::AliasedResultExpr(AliasedResultExpr { expr: count(expr), @@ -211,6 +243,7 @@ pub fn count_res(expr: Box, alias: &str) -> SelectResultExpr { } /// Count rows and give the result an alias i.e. SELECT COUNT(*) AS ALIAS +#[must_use] pub fn count_all_res(alias: &str) -> SelectResultExpr { SelectResultExpr::AliasedResultExpr(AliasedResultExpr { expr: Expression::Aggregation { @@ -223,6 +256,7 @@ pub fn count_all_res(alias: &str) -> SelectResultExpr { } /// Generate a `SetExpression` of the kind SELECT COL1, COL2, ... FROM TAB WHERE EXPR GROUP BY ... +#[must_use] pub fn query( result_exprs: Vec, tab: Box, @@ -240,6 +274,7 @@ pub fn query( /// Generate a `SetExpression` of the kind SELECT COL1, COL2, ... FROM TAB GROUP BY ... /// /// Note that there is no WHERE clause. +#[must_use] pub fn query_all( result_exprs: Vec, tab: Box, @@ -256,6 +291,7 @@ pub fn query_all( /// Generate a query of the kind SELECT ... ORDER BY ... [LIMIT ... OFFSET ...] /// /// Note that `expr` is a boxed `SetExpression` +#[must_use] pub fn select( expr: Box, order_by: Vec, @@ -269,6 +305,7 @@ pub fn select( } /// Order by one column i.e. ORDER BY ID [ASC|DESC] +#[must_use] pub fn order(id: &str, direction: OrderByDirection) -> Vec { vec![OrderBy { expr: id.parse().unwrap(), @@ -277,6 +314,7 @@ pub fn order(id: &str, direction: OrderByDirection) -> Vec { } /// Order by multiple columns i.e. ORDER BY ID0 [ASC|DESC], ID1 [ASC|DESC], ... +#[must_use] pub fn orders(ids: &[&str], directions: &[OrderByDirection]) -> Vec { ids.iter() .zip(directions.iter()) @@ -288,6 +326,7 @@ pub fn orders(ids: &[&str], directions: &[OrderByDirection]) -> Vec { } /// Slice a query result using `LIMIT` and `OFFSET` clauses i.e. LIMIT N OFFSET M +#[must_use] pub fn slice(number_rows: u64, offset_value: i64) -> Option { Some(Slice { number_rows, @@ -296,6 +335,7 @@ pub fn slice(number_rows: u64, offset_value: i64) -> Option { } /// Group by clause with multiple columns i.e. GROUP BY ID0, ID1, ... +#[must_use] pub fn group_by(ids: &[&str]) -> Vec { ids.iter().map(|id| id.parse().unwrap()).collect() }