From 265a44246f7d137802773c6439b5bbf87ed677ce Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Thu, 7 Mar 2024 11:04:08 -0800 Subject: [PATCH 1/7] Add errors from BaseTableExpr's to the evaluator --- partiql-catalog/src/lib.rs | 1 + partiql-eval/src/error.rs | 13 ++++++++++++- partiql-eval/src/eval/expr/base_table.rs | 8 ++++---- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/partiql-catalog/src/lib.rs b/partiql-catalog/src/lib.rs index 62f92a21..83479cb4 100644 --- a/partiql-catalog/src/lib.rs +++ b/partiql-catalog/src/lib.rs @@ -46,6 +46,7 @@ pub struct ObjectId { } pub type BaseTableExprResultError = Box; + pub type BaseTableExprResultValueIter<'a> = Box>>; pub type BaseTableExprResult<'a> = diff --git a/partiql-eval/src/error.rs b/partiql-eval/src/error.rs index 2bdde323..d845a893 100644 --- a/partiql-eval/src/error.rs +++ b/partiql-eval/src/error.rs @@ -1,6 +1,7 @@ use crate::eval::evaluable::Evaluable; use crate::eval::expr::EvalExpr; use crate::eval::EvalContext; +use partiql_catalog::BaseTableExprResultError; use partiql_value::{Tuple, Value}; use std::borrow::Cow; use thiserror::Error; @@ -30,7 +31,7 @@ pub struct EvalErr { } /// An error that can happen during evaluation. -#[derive(Error, Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Error, Debug)] #[non_exhaustive] pub enum EvaluationError { /// Internal error that was not due to user input or API violation. @@ -42,6 +43,16 @@ pub enum EvaluationError { /// Feature has not yet been implemented. #[error("Not yet implemented: {0}")] NotYetImplemented(String), + + /// Error originating in an extension + #[error("Base Table Expression Error: {0}")] + ExtensionBaseTableExprResultError(BaseTableExprResultError), +} + +impl From for EvaluationError { + fn from(e: BaseTableExprResultError) -> Self { + EvaluationError::ExtensionBaseTableExprResultError(e) + } } /// Used when an error occurs during the the logical to eval plan conversion. Allows the conversion diff --git a/partiql-eval/src/eval/expr/base_table.rs b/partiql-eval/src/eval/expr/base_table.rs index 7a9e955b..1d6effc9 100644 --- a/partiql-eval/src/eval/expr/base_table.rs +++ b/partiql-eval/src/eval/expr/base_table.rs @@ -37,14 +37,14 @@ impl EvalExpr for EvalFnBaseTableExpr { let bag: Result = it.collect(); match bag { Ok(b) => Value::from(b), - Err(_) => { - // TODO hook into pending eval errors + Err(err) => { + ctx.add_error(err.into()); Missing } } } - Err(_) => { - // TODO hook into pending eval errors + Err(err) => { + ctx.add_error(err.into()); Missing } }; From 037cb71836d4ff721b1e7b23f0c752b60ef95b21 Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Thu, 7 Mar 2024 11:08:03 -0800 Subject: [PATCH 2/7] Add changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c38c1bb..a6f01bbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Adds quotes to the attributes of PartiQL tuple's debug output so it can be read and transformed using Kotlin `partiql-cli` - [breaking] Changes the interface to `EvalPlan` to accept an `EvalContext` +- [breaking] Changes `EvaluationError` to not implement `Clone` ### Added - Add `partiql-extension-visualize` for visualizing AST and logical plan @@ -16,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed `ORDER BY`'s ability to see into projection aliases +- Fixed errors in `BaseTableExpr`s get added to the evaluation context ## [0.6.0] - 2023-10-31 ### Changed From 3f15890c880ed7166047775afc145144873f1f85 Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Thu, 7 Mar 2024 12:03:33 -0800 Subject: [PATCH 3/7] Rename error --- partiql-eval/src/error.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/partiql-eval/src/error.rs b/partiql-eval/src/error.rs index d845a893..2239d1ec 100644 --- a/partiql-eval/src/error.rs +++ b/partiql-eval/src/error.rs @@ -46,16 +46,16 @@ pub enum EvaluationError { /// Error originating in an extension #[error("Base Table Expression Error: {0}")] - ExtensionBaseTableExprResultError(BaseTableExprResultError), + ExtensioResultError(BaseTableExprResultError), } impl From for EvaluationError { fn from(e: BaseTableExprResultError) -> Self { - EvaluationError::ExtensionBaseTableExprResultError(e) + EvaluationError::ExtensioResultError(e) } } -/// Used when an error occurs during the the logical to eval plan conversion. Allows the conversion +/// Used when an error occurs during the logical to eval plan conversion. Allows the conversion /// to continue in order to report multiple errors. #[derive(Debug)] pub(crate) struct ErrorNode {} From 0ce6c8b1a6e381a6b982a7535b7f648d8c0788f6 Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Thu, 7 Mar 2024 12:04:18 -0800 Subject: [PATCH 4/7] Fix permissive mode erroring --- CHANGELOG.md | 2 ++ partiql-eval/src/eval/mod.rs | 23 +++++++++++++++-------- partiql-eval/src/plan.rs | 19 ++++++++++++------- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6f01bbc..4f2c51a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Adds quotes to the attributes of PartiQL tuple's debug output so it can be read and transformed using Kotlin `partiql-cli` - [breaking] Changes the interface to `EvalPlan` to accept an `EvalContext` - [breaking] Changes `EvaluationError` to not implement `Clone` +- [breaking] Changes the structure of `EvalPlan` ### Added - Add `partiql-extension-visualize` for visualizing AST and logical plan @@ -18,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed `ORDER BY`'s ability to see into projection aliases - Fixed errors in `BaseTableExpr`s get added to the evaluation context +- Fixed certain errors surfacing in Permissive evaluation mode, when they should only be present in Strict mode ## [0.6.0] - 2023-10-31 ### Changed diff --git a/partiql-eval/src/eval/mod.rs b/partiql-eval/src/eval/mod.rs index c12794c8..d5251daf 100644 --- a/partiql-eval/src/eval/mod.rs +++ b/partiql-eval/src/eval/mod.rs @@ -23,6 +23,7 @@ use petgraph::visit::EdgeRef; use unicase::UniCase; use crate::eval::evaluable::{EvalType, Evaluable}; +use crate::plan::EvaluationMode; pub(crate) mod eval_expr_wrapper; pub mod evaluable; @@ -31,11 +32,14 @@ pub mod expr; /// Represents a PartiQL evaluation query plan which is a plan that can be evaluated to produce /// a result. The plan uses a directed `petgraph::StableGraph`. #[derive(Debug)] -pub struct EvalPlan(pub StableGraph, u8, Directed>); +pub struct EvalPlan { + mode: EvaluationMode, + plan_graph: StableGraph, u8, Directed>, +} impl Default for EvalPlan { fn default() -> Self { - Self::new() + Self::new(EvaluationMode::Permissive, Default::default()) } } @@ -48,13 +52,16 @@ fn err_illegal_state(msg: impl AsRef) -> EvalErr { impl EvalPlan { /// Creates a new evaluation plan. - fn new() -> Self { - EvalPlan(StableGraph::, u8, Directed>::new()) + pub fn new( + mode: EvaluationMode, + plan_graph: StableGraph, u8, Directed>, + ) -> Self { + EvalPlan { mode, plan_graph } } #[inline] fn plan_graph(&mut self) -> &mut StableGraph, u8> { - &mut self.0 + &mut self.plan_graph } #[inline] @@ -73,7 +80,7 @@ impl EvalPlan { // that all v ∈ V \{v0} are reachable from v0. Note that this is the definition of trees // without the condition |E| = |V | − 1. Hence, all trees are DAGs. // Reference: https://link.springer.com/article/10.1007/s00450-009-0061-0 - let ops = toposort(&self.0, None).map_err(|e| EvalErr { + let ops = toposort(&self.plan_graph, None).map_err(|e| EvalErr { errors: vec![EvaluationError::InvalidEvaluationPlan(format!( "Malformed evaluation plan detected: {e:?}" ))], @@ -101,7 +108,7 @@ impl EvalPlan { result = Some(src.evaluate(ctx)); // return on first evaluation error - if ctx.has_errors() { + if ctx.has_errors() && self.mode == EvaluationMode::Strict { return Err(EvalErr { errors: ctx.errors(), }); @@ -127,7 +134,7 @@ impl EvalPlan { } pub fn to_dot_graph(&self) -> String { - format!("{:?}", Dot::with_config(&self.0, &[])) + format!("{:?}", Dot::with_config(&self.plan_graph, &[])) } } diff --git a/partiql-eval/src/plan.rs b/partiql-eval/src/plan.rs index e6fc7996..b269f0c5 100644 --- a/partiql-eval/src/plan.rs +++ b/partiql-eval/src/plan.rs @@ -49,6 +49,7 @@ macro_rules! correct_num_args_or_err { }; } +#[derive(Debug, Eq, PartialEq)] pub enum EvaluationMode { Strict, Permissive, @@ -129,22 +130,26 @@ impl<'c> EvaluatorPlanner<'c> { fn plan_eval(&mut self, lg: &LogicalPlan) -> EvalPlan { let flows = lg.flows(); - let mut graph: StableGraph<_, _> = Default::default(); + let mut plan_graph: StableGraph<_, _> = Default::default(); let mut seen = HashMap::new(); for (s, d, branch_num) in flows { let mut add_node = |op_id: &OpId| { let logical_op = lg.operator(*op_id).unwrap(); - *seen - .entry(*op_id) - .or_insert_with(|| graph.add_node(self.get_eval_node::<{ STRICT }>(logical_op))) + *seen.entry(*op_id).or_insert_with(|| { + plan_graph.add_node(self.get_eval_node::<{ STRICT }>(logical_op)) + }) }; let (s, d) = (add_node(s), add_node(d)); - graph.add_edge(s, d, *branch_num); + plan_graph.add_edge(s, d, *branch_num); } - - EvalPlan(graph) + let mode = if STRICT { + EvaluationMode::Strict + } else { + EvaluationMode::Permissive + }; + EvalPlan::new(mode, plan_graph) } fn get_eval_node(&mut self, be: &BindingsOp) -> Box { From 5bf0c5b510e7bfba9a3a90dd6634845abb61627b Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Thu, 7 Mar 2024 12:04:37 -0800 Subject: [PATCH 5/7] Add test for extension errors --- partiql/Cargo.toml | 2 + partiql/tests/extension_error.rs | 257 +++++++++++++++++++++++++++++++ partiql/tests/user_context.rs | 2 +- 3 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 partiql/tests/extension_error.rs diff --git a/partiql/Cargo.toml b/partiql/Cargo.toml index 9f7bb6de..68257272 100644 --- a/partiql/Cargo.toml +++ b/partiql/Cargo.toml @@ -39,6 +39,8 @@ itertools = "0.12" criterion = "0.5" rand = "0.8" +assert_matches = "1.5" + [[bench]] name = "bench_eval_multi_like" harness = false diff --git a/partiql/tests/extension_error.rs b/partiql/tests/extension_error.rs new file mode 100644 index 00000000..e3832bcf --- /dev/null +++ b/partiql/tests/extension_error.rs @@ -0,0 +1,257 @@ +use std::any::Any; +use std::borrow::Cow; +use std::cell::RefCell; + +use std::error::Error; + +use thiserror::Error; + +use partiql_catalog::call_defs::{CallDef, CallSpec, CallSpecArg}; +use partiql_catalog::context::{SessionContext, SystemContext}; +use partiql_catalog::{ + BaseTableExpr, BaseTableExprResult, BaseTableExprResultError, BaseTableFunctionInfo, Catalog, + Extension, PartiqlCatalog, TableFunction, +}; +use partiql_eval::env::basic::MapBindings; +use partiql_eval::error::{EvalErr, EvaluationError}; +use partiql_eval::eval::{BasicContext, Evaluated}; +use partiql_eval::plan::EvaluationMode; +use partiql_parser::{Parsed, ParserResult}; +use partiql_value::{bag, tuple, DateTime, Value}; + +use partiql_logical as logical; + +#[derive(Debug)] +pub struct UserCtxTestExtension {} + +impl partiql_catalog::Extension for UserCtxTestExtension { + fn name(&self) -> String { + "test_extension".into() + } + + fn load(&self, catalog: &mut dyn Catalog) -> Result<(), Box> { + match catalog + .add_table_function(TableFunction::new(Box::new(TestUserContextFunction::new()))) + { + Ok(_) => Ok(()), + Err(e) => Err(Box::new(e) as Box), + } + } +} + +#[derive(Debug)] +pub(crate) struct TestUserContextFunction { + call_def: CallDef, +} + +impl TestUserContextFunction { + pub fn new() -> Self { + TestUserContextFunction { + call_def: CallDef { + names: vec!["test_user_context"], + overloads: vec![CallSpec { + input: vec![CallSpecArg::Positional], + output: Box::new(|args| { + logical::ValueExpr::Call(logical::CallExpr { + name: logical::CallName::ByName("test_user_context".to_string()), + arguments: args, + }) + }), + }], + }, + } + } +} + +impl BaseTableFunctionInfo for TestUserContextFunction { + fn call_def(&self) -> &CallDef { + &self.call_def + } + + fn plan_eval(&self) -> Box { + Box::new(EvalTestCtxTable {}) + } +} + +#[derive(Error, Debug)] +#[non_exhaustive] +pub enum UserCtxError { + #[error("bad arguments")] + BadArgs, + #[error("runtime error")] + Runtime, +} + +#[derive(Debug)] +pub(crate) struct EvalTestCtxTable {} + +impl BaseTableExpr for EvalTestCtxTable { + fn evaluate<'c>( + &self, + args: &[Cow], + ctx: &'c dyn SessionContext<'c>, + ) -> BaseTableExprResult<'c> { + if let Some(arg1) = args.first() { + match arg1.as_ref() { + Value::String(name) => Ok(Box::new(TestDataGen {})), + _ => { + let error = UserCtxError::BadArgs; + Err(Box::new(error) as BaseTableExprResultError) + } + } + } else { + let error = UserCtxError::BadArgs; + Err(Box::new(error) as BaseTableExprResultError) + } + } +} + +struct TestDataGen {} + +impl Iterator for TestDataGen { + type Item = Result; + + fn next(&mut self) -> Option { + Some(Err(Box::new(UserCtxError::Runtime))) + } +} +#[track_caller] +#[inline] +pub(crate) fn parse(statement: &str) -> ParserResult { + partiql_parser::Parser::default().parse(statement) +} + +#[track_caller] +#[inline] +pub(crate) fn lower( + catalog: &dyn Catalog, + parsed: &Parsed, +) -> partiql_logical::LogicalPlan { + let planner = partiql_logical_planner::LogicalPlanner::new(catalog); + planner.lower(parsed).expect("lower") +} + +#[track_caller] +#[inline] +pub(crate) fn evaluate( + mode: EvaluationMode, + catalog: &dyn Catalog, + logical: partiql_logical::LogicalPlan, + bindings: MapBindings, + ctx_vals: &[(String, &(dyn Any))], +) -> Result { + let mut planner = partiql_eval::plan::EvaluatorPlanner::new(mode, catalog); + + let mut plan = planner.compile(&logical).expect("Expect no plan error"); + + let sys = SystemContext { + now: DateTime::from_system_now_utc(), + }; + let mut ctx = BasicContext::new(bindings, sys); + for (k, v) in ctx_vals { + ctx.user.insert(k.as_str().into(), *v); + } + + plan.execute_mut(&ctx) +} + +#[test] +fn test_context_bad_args_permissive() { + use assert_matches::assert_matches; + let query = "SELECT foo, bar from test_user_context(9) as data"; + + let mut catalog = PartiqlCatalog::default(); + let ext = UserCtxTestExtension {}; + ext.load(&mut catalog).expect("extension load to succeed"); + + let parsed = parse(query); + let lowered = lower(&catalog, &parsed.expect("parse")); + let bindings = Default::default(); + + let ctx: [(String, &dyn Any); 0] = []; + let out = evaluate( + EvaluationMode::Permissive, + &catalog, + lowered, + bindings, + &ctx, + ); + + assert!(out.is_ok()); + assert_eq!(out.unwrap().result, bag!(tuple!()).into()); +} +#[test] +fn test_context_bad_args_strict() { + use assert_matches::assert_matches; + let query = "SELECT foo, bar from test_user_context(9) as data"; + + let mut catalog = PartiqlCatalog::default(); + let ext = UserCtxTestExtension {}; + ext.load(&mut catalog).expect("extension load to succeed"); + + let parsed = parse(query); + let lowered = lower(&catalog, &parsed.expect("parse")); + let bindings = Default::default(); + + let ctx: [(String, &dyn Any); 0] = []; + let out = evaluate(EvaluationMode::Strict, &catalog, lowered, bindings, &ctx); + + assert!(out.is_err()); + let err = out.unwrap_err(); + assert_eq!(err.errors.len(), 1); + let err = &err.errors[0]; + assert_matches!(err, EvaluationError::ExtensioResultError(err) => { + assert_eq!(err.to_string(), "bad arguments") + }); +} + +#[test] +fn test_context_runtime_permissive() { + use assert_matches::assert_matches; + let query = "SELECT foo, bar from test_user_context('counter') as data"; + + let mut catalog = PartiqlCatalog::default(); + let ext = UserCtxTestExtension {}; + ext.load(&mut catalog).expect("extension load to succeed"); + + let parsed = parse(query); + let lowered = lower(&catalog, &parsed.expect("parse")); + let bindings = Default::default(); + + let ctx: [(String, &dyn Any); 0] = []; + let out = evaluate( + EvaluationMode::Permissive, + &catalog, + lowered, + bindings, + &ctx, + ); + + assert!(out.is_ok()); + assert_eq!(out.unwrap().result, bag!(tuple!()).into()); +} + +#[test] +fn test_context_runtime_strict() { + use assert_matches::assert_matches; + let query = "SELECT foo, bar from test_user_context('counter') as data"; + + let mut catalog = PartiqlCatalog::default(); + let ext = UserCtxTestExtension {}; + ext.load(&mut catalog).expect("extension load to succeed"); + + let parsed = parse(query); + let lowered = lower(&catalog, &parsed.expect("parse")); + let bindings = Default::default(); + + let ctx: [(String, &dyn Any); 0] = []; + let out = evaluate(EvaluationMode::Strict, &catalog, lowered, bindings, &ctx); + + assert!(out.is_err()); + let err = out.unwrap_err(); + assert_eq!(err.errors.len(), 1); + let err = &err.errors[0]; + assert_matches!(err, EvaluationError::ExtensioResultError(err) => { + assert_eq!(err.to_string(), "runtime error") + }); +} diff --git a/partiql/tests/user_context.rs b/partiql/tests/user_context.rs index bd35c776..59564c67 100644 --- a/partiql/tests/user_context.rs +++ b/partiql/tests/user_context.rs @@ -25,7 +25,7 @@ pub struct UserCtxTestExtension {} impl partiql_catalog::Extension for UserCtxTestExtension { fn name(&self) -> String { - "ion".into() + "test_extension".into() } fn load(&self, catalog: &mut dyn Catalog) -> Result<(), Box> { From d4906a43735e46d978e9ad88ca3bd398b1e1705a Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Thu, 7 Mar 2024 12:16:20 -0800 Subject: [PATCH 6/7] cleanup --- partiql/tests/extension_error.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/partiql/tests/extension_error.rs b/partiql/tests/extension_error.rs index e3832bcf..492f1600 100644 --- a/partiql/tests/extension_error.rs +++ b/partiql/tests/extension_error.rs @@ -1,6 +1,5 @@ use std::any::Any; use std::borrow::Cow; -use std::cell::RefCell; use std::error::Error; @@ -89,11 +88,11 @@ impl BaseTableExpr for EvalTestCtxTable { fn evaluate<'c>( &self, args: &[Cow], - ctx: &'c dyn SessionContext<'c>, + _ctx: &'c dyn SessionContext<'c>, ) -> BaseTableExprResult<'c> { if let Some(arg1) = args.first() { match arg1.as_ref() { - Value::String(name) => Ok(Box::new(TestDataGen {})), + Value::String(_name) => Ok(Box::new(TestDataGen {})), _ => { let error = UserCtxError::BadArgs; Err(Box::new(error) as BaseTableExprResultError) @@ -157,7 +156,6 @@ pub(crate) fn evaluate( #[test] fn test_context_bad_args_permissive() { - use assert_matches::assert_matches; let query = "SELECT foo, bar from test_user_context(9) as data"; let mut catalog = PartiqlCatalog::default(); @@ -207,7 +205,6 @@ fn test_context_bad_args_strict() { #[test] fn test_context_runtime_permissive() { - use assert_matches::assert_matches; let query = "SELECT foo, bar from test_user_context('counter') as data"; let mut catalog = PartiqlCatalog::default(); From 6dc377d5037ee4e123015876a75d0bf69a9f9dbd Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Thu, 7 Mar 2024 12:51:11 -0800 Subject: [PATCH 7/7] Add argument lifetime to `BaseTableExpr`'s `evaluate` --- CHANGELOG.md | 1 + .../src/lib.rs | 9 ++++--- partiql-catalog/src/lib.rs | 8 +++--- partiql/tests/extension_error.rs | 9 ++++--- partiql/tests/user_context.rs | 27 ++++++++++++------- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f2c51a8..58585ddd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [breaking] Changes the interface to `EvalPlan` to accept an `EvalContext` - [breaking] Changes `EvaluationError` to not implement `Clone` - [breaking] Changes the structure of `EvalPlan` +- [breaking] Changes the interface of `BaseTableExpr`'s `evaluate` to allow arguments to be held by the returned iterator ### Added - Add `partiql-extension-visualize` for visualizing AST and logical plan diff --git a/extension/partiql-extension-ion-functions/src/lib.rs b/extension/partiql-extension-ion-functions/src/lib.rs index 034e190d..f4df7911 100644 --- a/extension/partiql-extension-ion-functions/src/lib.rs +++ b/extension/partiql-extension-ion-functions/src/lib.rs @@ -99,11 +99,14 @@ impl BaseTableFunctionInfo for ReadIonFunction { pub(crate) struct EvalFnReadIon {} impl BaseTableExpr for EvalFnReadIon { - fn evaluate<'c>( + fn evaluate<'a, 'c>( &self, - args: &[Cow], + args: &'a [Cow], _ctx: &'c dyn SessionContext<'c>, - ) -> BaseTableExprResult<'c> { + ) -> BaseTableExprResult<'a> + where + 'c: 'a, + { if let Some(arg1) = args.first() { match arg1.as_ref() { Value::String(path) => parse_ion_file(path), diff --git a/partiql-catalog/src/lib.rs b/partiql-catalog/src/lib.rs index 83479cb4..8df8b9b1 100644 --- a/partiql-catalog/src/lib.rs +++ b/partiql-catalog/src/lib.rs @@ -53,11 +53,13 @@ pub type BaseTableExprResult<'a> = Result, BaseTableExprResultError>; pub trait BaseTableExpr: Debug { - fn evaluate<'c>( + fn evaluate<'a, 'c>( &self, - args: &[Cow], + args: &'a [Cow], ctx: &'c dyn SessionContext<'c>, - ) -> BaseTableExprResult<'c>; + ) -> BaseTableExprResult<'a> + where + 'c: 'a; } pub trait BaseTableFunctionInfo: Debug { diff --git a/partiql/tests/extension_error.rs b/partiql/tests/extension_error.rs index 492f1600..068a552e 100644 --- a/partiql/tests/extension_error.rs +++ b/partiql/tests/extension_error.rs @@ -85,11 +85,14 @@ pub enum UserCtxError { pub(crate) struct EvalTestCtxTable {} impl BaseTableExpr for EvalTestCtxTable { - fn evaluate<'c>( + fn evaluate<'a, 'c>( &self, - args: &[Cow], + args: &'a [Cow], _ctx: &'c dyn SessionContext<'c>, - ) -> BaseTableExprResult<'c> { + ) -> BaseTableExprResult<'a> + where + 'c: 'a, + { if let Some(arg1) = args.first() { match arg1.as_ref() { Value::String(_name) => Ok(Box::new(TestDataGen {})), diff --git a/partiql/tests/user_context.rs b/partiql/tests/user_context.rs index 59564c67..94d0f70a 100644 --- a/partiql/tests/user_context.rs +++ b/partiql/tests/user_context.rs @@ -83,14 +83,17 @@ pub enum UserCtxError { pub(crate) struct EvalTestCtxTable {} impl BaseTableExpr for EvalTestCtxTable { - fn evaluate<'c>( + fn evaluate<'a, 'c>( &self, - args: &[Cow], + args: &'a [Cow], ctx: &'c dyn SessionContext<'c>, - ) -> BaseTableExprResult<'c> { + ) -> BaseTableExprResult<'a> + where + 'c: 'a, + { if let Some(arg1) = args.first() { match arg1.as_ref() { - Value::String(name) => generated_data(name.to_string(), ctx), + Value::String(name) => generated_data(name.as_str(), ctx), _ => { let error = UserCtxError::Unknown; Err(Box::new(error) as BaseTableExprResultError) @@ -103,12 +106,15 @@ impl BaseTableExpr for EvalTestCtxTable { } } -struct TestDataGen<'a> { - ctx: &'a dyn SessionContext<'a>, - name: String, +struct TestDataGen<'a, 'c> +where + 'c: 'a, +{ + ctx: &'c dyn SessionContext<'c>, + name: &'a str, } -impl<'a> Iterator for TestDataGen<'a> { +impl<'a, 'c> Iterator for TestDataGen<'a, 'c> { type Item = Result; fn next(&mut self) -> Option { @@ -131,7 +137,10 @@ impl<'a> Iterator for TestDataGen<'a> { } } -fn generated_data<'a>(name: String, ctx: &'a dyn SessionContext<'a>) -> BaseTableExprResult<'a> { +fn generated_data<'a, 'c>(name: &'a str, ctx: &'c dyn SessionContext<'c>) -> BaseTableExprResult<'a> +where + 'c: 'a, +{ Ok(Box::new(TestDataGen { ctx, name })) }