From 0ae23a6abcddcab5cd4f155d0456f1f98bc856e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Wo=C5=9B?= Date: Thu, 9 Nov 2023 20:21:11 +0000 Subject: [PATCH] renaming, documentation --- packages/fuel-indexer-plugin/src/find.rs | 193 +++++++++++++---------- packages/fuel-indexer-plugin/src/wasm.rs | 6 +- 2 files changed, 112 insertions(+), 87 deletions(-) diff --git a/packages/fuel-indexer-plugin/src/find.rs b/packages/fuel-indexer-plugin/src/find.rs index 90cbef235..38491d39a 100644 --- a/packages/fuel-indexer-plugin/src/find.rs +++ b/packages/fuel-indexer-plugin/src/find.rs @@ -1,12 +1,15 @@ -use fuel_indexer_types::scalar::{Boolean, Bytes, B256, UID}; +use fuel_indexer_types::scalar::{Boolean, UID}; use sqlparser::ast as sql; -pub struct Query { - constraint: Constraint, +/// Represents `WHERE filter ORDER BY ASC | DSC` part of the SQL statement. +pub struct QueryFragment { + constraint: Filter, order_by: Option, } -impl std::fmt::Display for Query { +/// Convert `QueryFragment` to `String`. `SELECT * from table_name` is lated +/// added by the Fuel indexer to generate the entire query. +impl std::fmt::Display for QueryFragment { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.constraint)?; if let Some(ref order_by) = self.order_by { @@ -16,27 +19,32 @@ impl std::fmt::Display for Query { } } -impl From> for Query { - fn from(constraint: Constraint) -> Self { - Query { +/// Automatic lifting of `Filter` into `QueryFragment` leaving `ORDER BY` +/// unspecified. +impl From> for QueryFragment { + fn from(constraint: Filter) -> Self { + QueryFragment { constraint, order_by: None, } } } -pub struct Constraint { +/// Represents a WHERE clause of the SQL statement. Multiple `Filter`s can be +/// joined with `and` and `or` and also ordered, at which point they become +/// `QueryFragment`s. +pub struct Filter { constraint: sql::Expr, phantom: std::marker::PhantomData, } -impl std::fmt::Display for Constraint { +impl std::fmt::Display for Filter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.constraint) } } -impl Constraint { +impl Filter { fn new(constraint: sql::Expr) -> Self { Self { constraint, @@ -44,32 +52,32 @@ impl Constraint { } } - pub fn and(self, right: Constraint) -> Constraint { + pub fn and(self, right: Filter) -> Filter { let constraint = sql::Expr::BinaryOp { left: Box::new(self.constraint), op: sql::BinaryOperator::And, right: Box::new(right.constraint), }; - Constraint { + Filter { constraint, phantom: std::marker::PhantomData, } } - pub fn or(self, right: Constraint) -> Constraint { + pub fn or(self, right: Filter) -> Filter { let constraint = sql::Expr::BinaryOp { left: Box::new(self.constraint), op: sql::BinaryOperator::Or, right: Box::new(right.constraint), }; - Constraint { + Filter { constraint, phantom: std::marker::PhantomData, } } - pub fn order_by_asc(self, f: Field) -> Query { - Query { + pub fn order_by_asc(self, f: Field) -> QueryFragment { + QueryFragment { constraint: self, order_by: Some(sql::OrderByExpr { expr: sql::Expr::Identifier(sql::Ident::new(f.field)), @@ -79,8 +87,8 @@ impl Constraint { } } - pub fn order_by_desc(self, f: Field) -> Query { - Query { + pub fn order_by_desc(self, f: Field) -> QueryFragment { + QueryFragment { constraint: self, order_by: Some(sql::OrderByExpr { expr: sql::Expr::Identifier(sql::Ident::new(f.field)), @@ -91,20 +99,9 @@ impl Constraint { } } -pub struct Field { - field: String, - phantom: std::marker::PhantomData<(T, F)>, -} - -impl Field { - pub fn new(field: String) -> Self { - Field { - field, - phantom: std::marker::PhantomData, - } - } -} - +/// A trait used to convert a value of scalar type into `sqlparser::ast::Value`. +/// That is, for injecting a value into the `sqlparser`'s representation which +/// we then use to generate a `QueryFragment`. pub trait ToSQLValue where Self: Sized, @@ -118,25 +115,6 @@ impl ToSQLValue for String { } } -impl ToSQLValue for B256 { - fn to_sql_value(self) -> sql::Value { - unsafe { - sql::Value::SingleQuotedByteStringLiteral( - std::str::from_utf8_unchecked(&self).to_string(), - ) - } - } -} - -impl ToSQLValue for Bytes { - fn to_sql_value(self) -> sql::Value { - unsafe { - sql::Value::SingleQuotedByteStringLiteral( - std::str::from_utf8_unchecked(&self).to_string(), - ) - } - } -} impl ToSQLValue for Boolean { fn to_sql_value(self) -> sql::Value { @@ -150,6 +128,32 @@ impl ToSQLValue for UID { } } +macro_rules! impl_bytes_to_sql_value { + ($T:ident) => { + impl ToSQLValue for fuel_indexer_types::scalar::$T { + fn to_sql_value(self) -> sql::Value { + unsafe { + sql::Value::SingleQuotedByteStringLiteral( + std::str::from_utf8_unchecked(self.as_ref()).to_string(), + ) + } + } + } + }; +} + +impl_bytes_to_sql_value!(B256); +impl_bytes_to_sql_value!(Bytes32); +impl_bytes_to_sql_value!(Bytes8); +impl_bytes_to_sql_value!(Bytes4); +impl_bytes_to_sql_value!(Bytes); +impl_bytes_to_sql_value!(AssetId); +impl_bytes_to_sql_value!(Address); +impl_bytes_to_sql_value!(ContractId); +impl_bytes_to_sql_value!(MessageId); +impl_bytes_to_sql_value!(Nonce); +impl_bytes_to_sql_value!(Salt); + macro_rules! impl_number_to_sql_value { ($T:ident) => { impl ToSQLValue for fuel_indexer_types::scalar::$T { @@ -172,41 +176,62 @@ impl_number_to_sql_value!(U32); impl_number_to_sql_value!(I8); impl_number_to_sql_value!(U8); +impl_number_to_sql_value!(BlockHeight); + +/// Captures the information necessary to represent `struct T { field: F }`. +/// It is then used to build a type-safe `Filter`, e.g., `Filter`. +pub struct Field { + field: String, + phantom: std::marker::PhantomData<(T, F)>, +} + +impl Field { + pub fn new(field: String) -> Self { + Field { + field, + phantom: std::marker::PhantomData, + } + } +} + impl Field { - pub fn eq(self, val: F) -> Constraint { - self.constraint(sql::BinaryOperator::Eq, val) + pub fn eq(self, val: F) -> Filter { + self.filter(sql::BinaryOperator::Eq, val) } - pub fn ne(self, val: F) -> Constraint { - self.constraint(sql::BinaryOperator::NotEq, val) + pub fn ne(self, val: F) -> Filter { + self.filter(sql::BinaryOperator::NotEq, val) } - pub fn gt(self, val: F) -> Constraint { - self.constraint(sql::BinaryOperator::Gt, val) + pub fn gt(self, val: F) -> Filter { + self.filter(sql::BinaryOperator::Gt, val) } - pub fn ge(self, val: F) -> Constraint { - self.constraint(sql::BinaryOperator::GtEq, val) + pub fn ge(self, val: F) -> Filter { + self.filter(sql::BinaryOperator::GtEq, val) } - pub fn lt(self, val: F) -> Constraint { - self.constraint(sql::BinaryOperator::Lt, val) + pub fn lt(self, val: F) -> Filter { + self.filter(sql::BinaryOperator::Lt, val) } - pub fn le(self, val: F) -> Constraint { - self.constraint(sql::BinaryOperator::LtEq, val) + pub fn le(self, val: F) -> Filter { + self.filter(sql::BinaryOperator::LtEq, val) } - fn constraint(self, op: sql::BinaryOperator, val: F) -> Constraint { + fn filter(self, op: sql::BinaryOperator, val: F) -> Filter { let expr = sql::Expr::BinaryOp { left: Box::new(sql::Expr::Identifier(sql::Ident::new(self.field.clone()))), op, right: Box::new(sql::Expr::Value(val.to_sql_value())), }; - Constraint::new(expr) + Filter::new(expr) } } +/// Captures the information necessary to represent `struct T { field: Option }` +/// which requires additional logic for dealing with NULL values. Like `Field`, +/// it is used to build a type-safe `Filter`. pub struct OptionField { field: String, phantom: std::marker::PhantomData<(T, F)>, @@ -222,49 +247,49 @@ impl OptionField { } impl OptionField { - pub fn eq(self, val: F) -> Constraint { - self.constraint(sql::BinaryOperator::Eq, val) + pub fn eq(self, val: F) -> Filter { + self.filter(sql::BinaryOperator::Eq, val) } - pub fn ne(self, val: F) -> Constraint { - self.constraint(sql::BinaryOperator::NotEq, val) + pub fn ne(self, val: F) -> Filter { + self.filter(sql::BinaryOperator::NotEq, val) } - pub fn gt(self, val: F) -> Constraint { - self.constraint(sql::BinaryOperator::Gt, val) + pub fn gt(self, val: F) -> Filter { + self.filter(sql::BinaryOperator::Gt, val) } - pub fn ge(self, val: F) -> Constraint { - self.constraint(sql::BinaryOperator::GtEq, val) + pub fn ge(self, val: F) -> Filter { + self.filter(sql::BinaryOperator::GtEq, val) } - pub fn lt(self, val: F) -> Constraint { - self.constraint(sql::BinaryOperator::Lt, val) + pub fn lt(self, val: F) -> Filter { + self.filter(sql::BinaryOperator::Lt, val) } - pub fn le(self, val: F) -> Constraint { - self.constraint(sql::BinaryOperator::LtEq, val) + pub fn le(self, val: F) -> Filter { + self.filter(sql::BinaryOperator::LtEq, val) } - pub fn is_null(self) -> Constraint { - Constraint::new(sql::Expr::IsNull(Box::new(sql::Expr::Identifier( + pub fn is_null(self) -> Filter { + Filter::new(sql::Expr::IsNull(Box::new(sql::Expr::Identifier( sql::Ident::new(self.field), )))) } - pub fn is_not_null(self) -> Constraint { - Constraint::new(sql::Expr::IsNotNull(Box::new(sql::Expr::Identifier( + pub fn is_not_null(self) -> Filter { + Filter::new(sql::Expr::IsNotNull(Box::new(sql::Expr::Identifier( sql::Ident::new(self.field), )))) } // Helper function that unwraps the Option converting None to NULL. - fn constraint(self, op: sql::BinaryOperator, val: F) -> Constraint { + fn filter(self, op: sql::BinaryOperator, val: F) -> Filter { let expr = sql::Expr::BinaryOp { left: Box::new(sql::Expr::Identifier(sql::Ident::new(self.field))), op, right: Box::new(sql::Expr::Value(val.to_sql_value())), }; - Constraint::new(expr) + Filter::new(expr) } -} +} \ No newline at end of file diff --git a/packages/fuel-indexer-plugin/src/wasm.rs b/packages/fuel-indexer-plugin/src/wasm.rs index 9d77f721a..bbf0399c5 100644 --- a/packages/fuel-indexer-plugin/src/wasm.rs +++ b/packages/fuel-indexer-plugin/src/wasm.rs @@ -17,7 +17,7 @@ pub use hex::FromHex; pub use sha2::{Digest, Sha256}; pub use std::collections::{HashMap, HashSet}; -pub use crate::find::{Constraint, Field, OptionField, Query}; +pub use crate::find::{Filter, Field, OptionField, QueryFragment}; // These are instantiated with functions which return // `Result`. `wasmer` unwraps the `Result` and uses the @@ -128,8 +128,8 @@ pub trait Entity<'a>: Sized + PartialEq + Eq + std::fmt::Debug { } /// Finds the first entity that satisfies the given constraints. - fn find(query: impl Into>) -> Option { - let query: Query = query.into(); + fn find(query: impl Into>) -> Option { + let query: QueryFragment = query.into(); unsafe { let buff = bincode::serialize(&query.to_string()).unwrap(); let mut bufflen = (buff.len() as u32).to_le_bytes();