diff --git a/Cargo.lock b/Cargo.lock index bbf3465637b..bdf7921f0f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4473,9 +4473,9 @@ checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" [[package]] name = "sqlparser" -version = "0.45.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7bbffee862a796d67959a89859d6b1046bb5016d63e23835ad0da182777bbe0" +checksum = "11a81a8cad9befe4cf1b9d2d4b9c6841c76f0882a3fec00d95133953c13b3d3d" dependencies = [ "log", ] diff --git a/Cargo.toml b/Cargo.toml index 33c717979e7..e786b797d4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ serde_derive = "1.0.125" serde_json = { version = "1.0", features = ["arbitrary_precision"] } serde_regex = "1.1.0" serde_yaml = "0.9.21" +sqlparser = "0.46.0" syn = { version = "2.0.60", features = ["full"] } tonic = { version = "0.8.3", features = ["tls-roots", "gzip"] } tonic-build = { version = "0.8.4", features = ["prost"] } diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 183ecce05df..1ca0d3aa79f 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -48,6 +48,7 @@ slog = { version = "2.7.0", features = [ "release_max_level_trace", "max_level_trace", ] } +sqlparser = { workspace = true } # TODO: This should be reverted to the latest version once it's published # stable-hash_legacy = { version = "0.3.3", package = "stable-hash" } # stable-hash = { version = "0.4.2" } @@ -90,7 +91,6 @@ web3 = { git = "https://github.com/graphprotocol/rust-web3", branch = "graph-pat "arbitrary_precision", ] } serde_plain = "1.0.2" -sqlparser = "0.45.0" csv = "1.3.0" object_store = { version = "0.9.1", features = ["gcp"] } diff --git a/graph/src/schema/input/sqlexpr.rs b/graph/src/schema/input/sqlexpr.rs index c8cd7c7396a..9b65469558b 100644 --- a/graph/src/schema/input/sqlexpr.rs +++ b/graph/src/schema/input/sqlexpr.rs @@ -132,8 +132,17 @@ impl<'a> VisitExpr<'a> { Cast { expr, data_type: _, + kind, format: _, - } => self.visit_expr(expr), + } => match kind { + // Cast: `CAST( as )` + // DoubleColon: `::` + p::CastKind::Cast | p::CastKind::DoubleColon => self.visit_expr(expr), + // These two are not Postgres syntax + p::CastKind::TryCast | p::CastKind::SafeCast => { + self.nope(&format!("non-standard cast '{:?}'", kind)) + } + }, Nested(expr) | IsFalse(expr) | IsNotFalse(expr) | IsTrue(expr) | IsNotTrue(expr) | IsNull(expr) | IsNotNull(expr) => self.visit_expr(expr), IsDistinctFrom(expr1, expr2) | IsNotDistinctFrom(expr1, expr2) => { @@ -157,8 +166,6 @@ impl<'a> VisitExpr<'a> { AnyOp { .. } => self.nope("AnyOp"), AllOp { .. } => self.nope("AllOp"), Convert { .. } => self.nope("Convert"), - TryCast { .. } => self.nope("TryCast"), - SafeCast { .. } => self.nope("SafeCast"), AtTimeZone { .. } => self.nope("AtTimeZone"), Extract { .. } => self.nope("Extract"), Ceil { .. } => self.nope("Ceil"), @@ -171,12 +178,8 @@ impl<'a> VisitExpr<'a> { IntroducedString { .. } => self.nope("IntroducedString"), TypedString { .. } => self.nope("TypedString"), MapAccess { .. } => self.nope("MapAccess"), - AggregateExpressionWithFilter { .. } => self.nope("AggregateExpressionWithFilter"), Exists { .. } => self.nope("Exists"), Subquery(_) => self.nope("Subquery"), - ArraySubquery(_) => self.nope("ArraySubquery"), - ListAgg(_) => self.nope("ListAgg"), - ArrayAgg(_) => self.nope("ArrayAgg"), GroupingSets(_) => self.nope("GroupingSets"), Cube(_) => self.nope("Cube"), Rollup(_) => self.nope("Rollup"), @@ -191,6 +194,7 @@ impl<'a> VisitExpr<'a> { QualifiedWildcard(_) => self.nope("QualifiedWildcard"), Dictionary(_) => self.nope("Dictionary"), OuterJoin(_) => self.nope("OuterJoin"), + Prior(_) => self.nope("Prior"), } } @@ -201,12 +205,14 @@ impl<'a> VisitExpr<'a> { filter, null_treatment, over, - distinct: _, - special: _, - order_by, + within_group, } = func; - if filter.is_some() || null_treatment.is_some() || over.is_some() || !order_by.is_empty() { + if filter.is_some() + || null_treatment.is_some() + || over.is_some() + || !within_group.is_empty() + { return self.illegal_function(format!("call to {name} uses an illegal feature")); } @@ -217,22 +223,45 @@ impl<'a> VisitExpr<'a> { )); } self.visitor.visit_func_name(&mut idents[0])?; - for arg in pargs { - use p::FunctionArg::*; - match arg { - Named { .. } => { - return self.illegal_function(format!("call to {name} uses a named argument")); + match pargs { + p::FunctionArguments::None => { /* nothing to do */ } + p::FunctionArguments::Subquery(_) => { + return self.illegal_function(format!("call to {name} uses a subquery argument")) + } + p::FunctionArguments::List(pargs) => { + let p::FunctionArgumentList { + duplicate_treatment, + args, + clauses, + } = pargs; + if duplicate_treatment.is_some() { + return self + .illegal_function(format!("call to {name} uses a duplicate treatment")); + } + if !clauses.is_empty() { + return self.illegal_function(format!("call to {name} uses a clause")); } - Unnamed(arg) => match arg { - p::FunctionArgExpr::Expr(expr) => { - self.visit_expr(expr)?; - } - p::FunctionArgExpr::QualifiedWildcard(_) | p::FunctionArgExpr::Wildcard => { - return self - .illegal_function(format!("call to {name} uses a wildcard argument")); - } - }, - }; + for arg in args { + use p::FunctionArg::*; + match arg { + Named { .. } => { + return self + .illegal_function(format!("call to {name} uses a named argument")); + } + Unnamed(arg) => match arg { + p::FunctionArgExpr::Expr(expr) => { + self.visit_expr(expr)?; + } + p::FunctionArgExpr::QualifiedWildcard(_) + | p::FunctionArgExpr::Wildcard => { + return self.illegal_function(format!( + "call to {name} uses a wildcard argument" + )); + } + }, + }; + } + } } Ok(()) } @@ -263,9 +292,19 @@ impl<'a> VisitExpr<'a> { | PGNotLikeMatch | PGNotILikeMatch | PGStartsWith - | PGCustomBinaryOperator(_) => { - self.not_supported(format!("binary operator {op} is not supported")) - } + | PGCustomBinaryOperator(_) + | Arrow + | LongArrow + | HashArrow + | HashLongArrow + | AtAt + | AtArrow + | ArrowAt + | HashMinus + | AtQuestion + | Question + | QuestionAnd + | QuestionPipe => self.not_supported(format!("binary operator {op} is not supported")), } } diff --git a/runtime/test/src/test.rs b/runtime/test/src/test.rs index f25000ffae7..37951e076eb 100644 --- a/runtime/test/src/test.rs +++ b/runtime/test/src/test.rs @@ -142,6 +142,7 @@ pub async fn test_module_latest(subgraph_id: &str, wasm_file: &str) -> WasmInsta pub trait WasmInstanceExt { fn invoke_export0_void(&mut self, f: &str) -> Result<(), Error>; fn invoke_export1_val_void(&mut self, f: &str, v: V) -> Result<(), Error>; + #[allow(dead_code)] fn invoke_export0(&mut self, f: &str) -> AscPtr; fn invoke_export1(&mut self, f: &str, arg: &T) -> AscPtr where diff --git a/store/postgres/src/jsonb.rs b/store/postgres/src/jsonb.rs deleted file mode 100644 index ecece51f3a2..00000000000 --- a/store/postgres/src/jsonb.rs +++ /dev/null @@ -1,27 +0,0 @@ -use diesel::expression::{AsExpression, Expression}; -use diesel::helper_types::AsExprOf; -use diesel::sql_types::Jsonb; - -mod operators { - use diesel::sql_types::Jsonb; - - // restrict to backend: Pg - infix_operator!(JsonbMerge, " || ", Jsonb, backend: diesel::pg::Pg); -} - -// This is currently unused, but allowing JSONB merging in the database -// is generally useful. We'll leave it here until we can merge it upstream -// See https://github.com/diesel-rs/diesel/issues/2036 -#[allow(dead_code)] -pub type JsonbMerge = operators::JsonbMerge>; - -pub trait PgJsonbExpressionMethods: Expression + Sized { - fn merge>(self, other: T) -> JsonbMerge { - JsonbMerge::::new(self, other.as_expression()) - } -} - -impl> PgJsonbExpressionMethods for T where - T: Expression -{ -} diff --git a/store/postgres/src/lib.rs b/store/postgres/src/lib.rs index 8e3cece0cc7..dc1177c7ba3 100644 --- a/store/postgres/src/lib.rs +++ b/store/postgres/src/lib.rs @@ -26,7 +26,6 @@ mod dynds; mod fork; mod functions; mod jobs; -mod jsonb; mod notification_listener; mod primary; pub mod query_store; diff --git a/store/postgres/src/relational_queries.rs b/store/postgres/src/relational_queries.rs index b23805ec392..8f09df120c2 100644 --- a/store/postgres/src/relational_queries.rs +++ b/store/postgres/src/relational_queries.rs @@ -109,9 +109,6 @@ macro_rules! constraint_violation { /// trait on a given column means "send these values to the database in a form /// that can later be used for comparisons with that column" trait ForeignKeyClauses { - /// The type of the column - fn column_type(&self) -> &ColumnType; - /// The name of the column fn name(&self) -> &str; @@ -167,10 +164,6 @@ impl PushBindParam for IdList { } impl ForeignKeyClauses for Column { - fn column_type(&self) -> &ColumnType { - &self.column_type - } - fn name(&self) -> &str { self.name.as_str() } diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index 26e640dec3b..1087ecf43cf 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -176,24 +176,6 @@ impl TestCase { } } -#[derive(Debug)] -struct Output { - stdout: Option, - stderr: Option, -} - -impl std::fmt::Display for Output { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(ref stdout) = self.stdout { - write!(f, "{}", stdout)?; - } - if let Some(ref stderr) = self.stderr { - write!(f, "{}", stderr)? - } - Ok(()) - } -} - /// Run the given `query` against the `subgraph` and check that the result /// has no errors and that the `data` portion of the response matches the /// `exp` value.