Skip to content
This repository has been archived by the owner on Oct 25, 2024. It is now read-only.

Commit

Permalink
renaming, documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
lostman committed Nov 9, 2023
1 parent bd54ce0 commit 0ae23a6
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 87 deletions.
193 changes: 109 additions & 84 deletions packages/fuel-indexer-plugin/src/find.rs
Original file line number Diff line number Diff line change
@@ -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<T> {
constraint: Constraint<T>,
/// Represents `WHERE filter ORDER BY ASC | DSC` part of the SQL statement.
pub struct QueryFragment<T> {
constraint: Filter<T>,
order_by: Option<sql::OrderByExpr>,
}

impl<T> std::fmt::Display for Query<T> {
/// Convert `QueryFragment` to `String`. `SELECT * from table_name` is lated
/// added by the Fuel indexer to generate the entire query.
impl<T> std::fmt::Display for QueryFragment<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.constraint)?;
if let Some(ref order_by) = self.order_by {
Expand All @@ -16,60 +19,65 @@ impl<T> std::fmt::Display for Query<T> {
}
}

impl<T> From<Constraint<T>> for Query<T> {
fn from(constraint: Constraint<T>) -> Self {
Query {
/// Automatic lifting of `Filter` into `QueryFragment` leaving `ORDER BY`
/// unspecified.
impl<T> From<Filter<T>> for QueryFragment<T> {
fn from(constraint: Filter<T>) -> Self {
QueryFragment {
constraint,
order_by: None,
}
}
}

pub struct Constraint<T> {
/// 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<T> {
constraint: sql::Expr,
phantom: std::marker::PhantomData<T>,
}

impl<T> std::fmt::Display for Constraint<T> {
impl<T> std::fmt::Display for Filter<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.constraint)
}
}

impl<T> Constraint<T> {
impl<T> Filter<T> {
fn new(constraint: sql::Expr) -> Self {
Self {
constraint,
phantom: std::marker::PhantomData,
}
}

pub fn and(self, right: Constraint<T>) -> Constraint<T> {
pub fn and(self, right: Filter<T>) -> Filter<T> {
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<T>) -> Constraint<T> {
pub fn or(self, right: Filter<T>) -> Filter<T> {
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<F>(self, f: Field<T, F>) -> Query<T> {
Query {
pub fn order_by_asc<F>(self, f: Field<T, F>) -> QueryFragment<T> {
QueryFragment {
constraint: self,
order_by: Some(sql::OrderByExpr {
expr: sql::Expr::Identifier(sql::Ident::new(f.field)),
Expand All @@ -79,8 +87,8 @@ impl<T> Constraint<T> {
}
}

pub fn order_by_desc<F>(self, f: Field<T, F>) -> Query<T> {
Query {
pub fn order_by_desc<F>(self, f: Field<T, F>) -> QueryFragment<T> {
QueryFragment {
constraint: self,
order_by: Some(sql::OrderByExpr {
expr: sql::Expr::Identifier(sql::Ident::new(f.field)),
Expand All @@ -91,20 +99,9 @@ impl<T> Constraint<T> {
}
}

pub struct Field<T, F> {
field: String,
phantom: std::marker::PhantomData<(T, F)>,
}

impl<T, F> Field<T, F> {
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,
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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<T>`, e.g., `Filter<OrderId>`.
pub struct Field<T, F> {
field: String,
phantom: std::marker::PhantomData<(T, F)>,
}

impl<T, F> Field<T, F> {
pub fn new(field: String) -> Self {
Field {
field,
phantom: std::marker::PhantomData,
}
}
}

impl<T, F: ToSQLValue> Field<T, F> {
pub fn eq(self, val: F) -> Constraint<T> {
self.constraint(sql::BinaryOperator::Eq, val)
pub fn eq(self, val: F) -> Filter<T> {
self.filter(sql::BinaryOperator::Eq, val)
}

pub fn ne(self, val: F) -> Constraint<T> {
self.constraint(sql::BinaryOperator::NotEq, val)
pub fn ne(self, val: F) -> Filter<T> {
self.filter(sql::BinaryOperator::NotEq, val)
}

pub fn gt(self, val: F) -> Constraint<T> {
self.constraint(sql::BinaryOperator::Gt, val)
pub fn gt(self, val: F) -> Filter<T> {
self.filter(sql::BinaryOperator::Gt, val)
}

pub fn ge(self, val: F) -> Constraint<T> {
self.constraint(sql::BinaryOperator::GtEq, val)
pub fn ge(self, val: F) -> Filter<T> {
self.filter(sql::BinaryOperator::GtEq, val)
}

pub fn lt(self, val: F) -> Constraint<T> {
self.constraint(sql::BinaryOperator::Lt, val)
pub fn lt(self, val: F) -> Filter<T> {
self.filter(sql::BinaryOperator::Lt, val)
}

pub fn le(self, val: F) -> Constraint<T> {
self.constraint(sql::BinaryOperator::LtEq, val)
pub fn le(self, val: F) -> Filter<T> {
self.filter(sql::BinaryOperator::LtEq, val)
}

fn constraint(self, op: sql::BinaryOperator, val: F) -> Constraint<T> {
fn filter(self, op: sql::BinaryOperator, val: F) -> Filter<T> {
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<F> }`
/// which requires additional logic for dealing with NULL values. Like `Field<T, F>`,
/// it is used to build a type-safe `Filter<T>`.
pub struct OptionField<T, F> {
field: String,
phantom: std::marker::PhantomData<(T, F)>,
Expand All @@ -222,49 +247,49 @@ impl<T, F> OptionField<T, F> {
}

impl<T, F: ToSQLValue> OptionField<T, F> {
pub fn eq(self, val: F) -> Constraint<T> {
self.constraint(sql::BinaryOperator::Eq, val)
pub fn eq(self, val: F) -> Filter<T> {
self.filter(sql::BinaryOperator::Eq, val)
}

pub fn ne(self, val: F) -> Constraint<T> {
self.constraint(sql::BinaryOperator::NotEq, val)
pub fn ne(self, val: F) -> Filter<T> {
self.filter(sql::BinaryOperator::NotEq, val)
}

pub fn gt(self, val: F) -> Constraint<T> {
self.constraint(sql::BinaryOperator::Gt, val)
pub fn gt(self, val: F) -> Filter<T> {
self.filter(sql::BinaryOperator::Gt, val)
}

pub fn ge(self, val: F) -> Constraint<T> {
self.constraint(sql::BinaryOperator::GtEq, val)
pub fn ge(self, val: F) -> Filter<T> {
self.filter(sql::BinaryOperator::GtEq, val)
}

pub fn lt(self, val: F) -> Constraint<T> {
self.constraint(sql::BinaryOperator::Lt, val)
pub fn lt(self, val: F) -> Filter<T> {
self.filter(sql::BinaryOperator::Lt, val)
}

pub fn le(self, val: F) -> Constraint<T> {
self.constraint(sql::BinaryOperator::LtEq, val)
pub fn le(self, val: F) -> Filter<T> {
self.filter(sql::BinaryOperator::LtEq, val)
}

pub fn is_null(self) -> Constraint<T> {
Constraint::new(sql::Expr::IsNull(Box::new(sql::Expr::Identifier(
pub fn is_null(self) -> Filter<T> {
Filter::new(sql::Expr::IsNull(Box::new(sql::Expr::Identifier(
sql::Ident::new(self.field),
))))
}

pub fn is_not_null(self) -> Constraint<T> {
Constraint::new(sql::Expr::IsNotNull(Box::new(sql::Expr::Identifier(
pub fn is_not_null(self) -> Filter<T> {
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<T> {
fn filter(self, op: sql::BinaryOperator, val: F) -> Filter<T> {
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)
}
}
}
6 changes: 3 additions & 3 deletions packages/fuel-indexer-plugin/src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T, WasmIndexerError>`. `wasmer` unwraps the `Result` and uses the
Expand Down Expand Up @@ -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<Query<Self>>) -> Option<Self> {
let query: Query<Self> = query.into();
fn find(query: impl Into<QueryFragment<Self>>) -> Option<Self> {
let query: QueryFragment<Self> = query.into();
unsafe {
let buff = bincode::serialize(&query.to_string()).unwrap();
let mut bufflen = (buff.len() as u32).to_le_bytes();
Expand Down

0 comments on commit 0ae23a6

Please sign in to comment.