Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ exclude = [
]

[workspace.package]
version = "0.13.2"
version = "0.13.3"
edition = "2021"
license = "MIT"
authors = ["polyglot contributors"]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ If you want to disable `stacker` for a native Rust build, turn off default featu

```toml
[dependencies]
polyglot-sql = { version = "0.13.2", default-features = false, features = ["all-dialects", "transpile"] }
polyglot-sql = { version = "0.13.3", default-features = false, features = ["all-dialects", "transpile"] }
```

That can reduce overhead slightly on trusted inputs, but you lose the default stack-growth protection for deeply nested SQL.
Expand Down
2 changes: 1 addition & 1 deletion crates/polyglot-sql-function-catalogs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ With `polyglot-sql` compile-time wiring (recommended):

```toml
[dependencies]
polyglot-sql = { version = "0.13.2", features = ["function-catalog-clickhouse"] }
polyglot-sql = { version = "0.13.3", features = ["function-catalog-clickhouse"] }
```

Then run schema validation with type checks:
Expand Down
4 changes: 2 additions & 2 deletions crates/polyglot-sql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,14 @@ function-catalog-all-dialects = [

[dependencies]
regex = { version = "1", optional = true }
polyglot-sql-ast-derive = { path = "../polyglot-sql-ast-derive", version = "0.13.2" }
polyglot-sql-ast-derive = { path = "../polyglot-sql-ast-derive", version = "0.13.3" }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
unicode-segmentation = { workspace = true }
stacker = { version = "0.1", optional = true }
ts-rs = { version = "12.0", features = ["serde-compat"], optional = true }
polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.13.2", optional = true, default-features = false }
polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.13.3", optional = true, default-features = false }

[dev-dependencies]
pretty_assertions = "1.4"
Expand Down
8 changes: 4 additions & 4 deletions crates/polyglot-sql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ By default, `polyglot-sql` enables the full public API. Parser-only consumers ca
disable default features and opt into only the dialect parsers they need:

```toml
polyglot-sql = { version = "0.13.2", default-features = false }
polyglot-sql = { version = "0.13.3", default-features = false }
```

```toml
polyglot-sql = {
version = "0.13.2",
version = "0.13.3",
default-features = false,
features = ["dialect-clickhouse"],
}
Expand All @@ -44,14 +44,14 @@ Examples:
```toml
# Parse and generate SQL for one dialect.
polyglot-sql = {
version = "0.13.2",
version = "0.13.3",
default-features = false,
features = ["generate", "dialect-clickhouse"],
}

# Cross-dialect transpilation.
polyglot-sql = {
version = "0.13.2",
version = "0.13.3",
default-features = false,
features = ["transpile", "dialect-clickhouse", "dialect-postgresql"],
}
Expand Down
2 changes: 2 additions & 0 deletions crates/polyglot-sql/src/ast_mutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,15 @@ pub(crate) fn binary(kind: BinaryKind, left: Expression, right: Expression) -> E
BinaryKind::Div => Expression::Div(Box::new(operation)),
BinaryKind::Mod => Expression::Mod(Box::new(operation)),
BinaryKind::Like => Expression::Like(Box::new(LikeOp {
negated: false,
left: operation.left,
right: operation.right,
escape: None,
quantifier: None,
inferred_type: None,
})),
BinaryKind::ILike => Expression::ILike(Box::new(LikeOp {
negated: false,
left: operation.left,
right: operation.right,
escape: None,
Expand Down
4 changes: 3 additions & 1 deletion crates/polyglot-sql/src/dialects/athena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,10 @@ impl DialectImpl for AthenaDialect {
// ILike -> LOWER() LIKE LOWER() (Trino doesn't support ILIKE)
Expression::ILike(op) => {
let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left.clone())));
let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right.clone())));
let lower_right =
super::lower_like_pattern(op.right.clone(), op.quantifier.is_some());
Ok(Expression::Like(Box::new(LikeOp {
negated: op.negated,
left: lower_left,
right: lower_right,
escape: op.escape,
Expand Down
3 changes: 2 additions & 1 deletion crates/polyglot-sql/src/dialects/bigquery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,9 @@ impl DialectImpl for BigQueryDialect {
// ILIKE -> LOWER() LIKE LOWER() in BigQuery (no ILIKE support)
Expression::ILike(op) => {
let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
let lower_right = super::lower_like_pattern(op.right, op.quantifier.is_some());
Ok(Expression::Like(Box::new(LikeOp {
negated: op.negated,
left: lower_left,
right: lower_right,
escape: op.escape,
Expand Down
2 changes: 0 additions & 2 deletions crates/polyglot-sql/src/dialects/clickhouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ impl DialectImpl for ClickHouseDialect {
| Expression::IsTrue(_)
| Expression::IsFalse(_)
| Expression::IsJson(_)
| Expression::Like(_)
| Expression::ILike(_)
| Expression::SimilarTo(_)
| Expression::Glob(_)
| Expression::RegexpLike(_)
Expand Down
3 changes: 2 additions & 1 deletion crates/polyglot-sql/src/dialects/hive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ impl DialectImpl for HiveDialect {
// ILIKE -> LOWER() LIKE LOWER() in Hive (no ILIKE support)
Expression::ILike(op) => {
let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
let lower_right = super::lower_like_pattern(op.right, op.quantifier.is_some());
Ok(Expression::Like(Box::new(LikeOp {
negated: op.negated,
left: lower_left,
right: lower_right,
escape: op.escape,
Expand Down
39 changes: 39 additions & 0 deletions crates/polyglot-sql/src/dialects/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,45 @@ fn is_default_presto_date_format(fmt: &str) -> bool {
fmt == "%Y-%m-%d" || fmt == "%F"
}

/// Lower each literal quantified pattern, not its containing tuple/array.
#[cfg(feature = "transpile")]
pub(crate) fn lower_like_pattern(pattern: Expression, quantified: bool) -> Expression {
if quantified {
match pattern {
Expression::Paren(mut paren) => {
paren.this = lower_like_pattern(paren.this, true);
return Expression::Paren(paren);
}
Expression::Tuple(mut tuple) => {
tuple.expressions = tuple
.expressions
.into_iter()
.map(|value| lower_like_pattern(value, false))
.collect();
return Expression::Tuple(tuple);
}
Expression::Array(mut array) => {
array.expressions = array
.expressions
.into_iter()
.map(|value| lower_like_pattern(value, false))
.collect();
return Expression::Array(array);
}
Expression::ArrayFunc(mut array) => {
array.expressions = array
.expressions
.into_iter()
.map(|value| lower_like_pattern(value, false))
.collect();
return Expression::ArrayFunc(array);
}
other => return lower_like_pattern(other, false),
}
}
Expression::Lower(Box::new(crate::expressions::UnaryFunc::new(pattern)))
}

/// Applies a transform function bottom-up through an entire expression tree.
///
/// The public entrypoint uses an explicit task stack for the recursion-heavy shapes
Expand Down
3 changes: 2 additions & 1 deletion crates/polyglot-sql/src/dialects/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,9 @@ impl DialectImpl for MySQLDialect {
Expression::ILike(op) => {
// Transform ILIKE to: LOWER(left) LIKE LOWER(right)
let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
let lower_right = super::lower_like_pattern(op.right, op.quantifier.is_some());
Ok(Expression::Like(Box::new(LikeOp {
negated: op.negated,
left: lower_left,
right: lower_right,
escape: op.escape,
Expand Down
25 changes: 19 additions & 6 deletions crates/polyglot-sql/src/dialects/normalization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,18 @@ pub(super) fn normalize(

if matches!(source, DialectType::Generic) && matches!(target, DialectType::Drill) {
if let Expression::ILike(ref like) = e {
return Ok(Expression::Function(Box::new(Function::new(
if like.quantifier.is_some() {
return Ok(e);
}
let function = Expression::Function(Box::new(Function::new(
"ILIKE".to_string(),
vec![like.left.clone(), like.right.clone()],
))));
)));
return Ok(if like.negated {
Expression::Not(Box::new(crate::expressions::UnaryOp::new(function)))
} else {
function
});
}
}

Expand Down Expand Up @@ -3977,11 +3985,16 @@ pub(super) fn normalize(
"LOWER".to_string(),
vec![like.left],
)));
let lower_right = Expression::Function(Box::new(Function::new(
"LOWER".to_string(),
vec![like.right],
)));
let lower_right = if like.quantifier.is_some() {
super::lower_like_pattern(like.right, true)
} else {
Expression::Function(Box::new(Function::new(
"LOWER".to_string(),
vec![like.right],
)))
};
return Ok(Expression::Like(Box::new(crate::expressions::LikeOp {
negated: like.negated,
left: lower_left,
right: lower_right,
escape: like.escape,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ pub(super) fn rewrite(
Action::SimilarToToTsqlLike => match e {
Expression::SimilarTo(f) => {
let like = Expression::Like(Box::new(LikeOp {
negated: false,
left: f.this,
right: f.pattern,
escape: f.escape,
Expand Down
2 changes: 2 additions & 0 deletions crates/polyglot-sql/src/dialects/normalization/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3080,6 +3080,7 @@ pub(super) fn rewrite(
None
};
Ok(Expression::Like(Box::new(crate::expressions::LikeOp {
negated: false,
left: this,
right: pattern,
escape,
Expand All @@ -3097,6 +3098,7 @@ pub(super) fn rewrite(
None
};
Ok(Expression::ILike(Box::new(crate::expressions::LikeOp {
negated: false,
left: this,
right: pattern,
escape,
Expand Down
3 changes: 2 additions & 1 deletion crates/polyglot-sql/src/dialects/oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,9 @@ impl DialectImpl for OracleDialect {
// ILIKE -> LOWER() LIKE LOWER() in Oracle (no ILIKE support)
Expression::ILike(op) => {
let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
let lower_right = super::lower_like_pattern(op.right, op.quantifier.is_some());
Ok(Expression::Like(Box::new(LikeOp {
negated: op.negated,
left: lower_left,
right: lower_right,
escape: op.escape,
Expand Down
4 changes: 3 additions & 1 deletion crates/polyglot-sql/src/dialects/presto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ impl DialectImpl for PrestoDialect {
// ILike -> LOWER() LIKE LOWER() (Presto doesn't support ILIKE)
Expression::ILike(op) => {
let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left.clone())));
let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right.clone())));
let lower_right =
super::lower_like_pattern(op.right.clone(), op.quantifier.is_some());
Ok(Expression::Like(Box::new(LikeOp {
negated: op.negated,
left: lower_left,
right: lower_right,
escape: op.escape,
Expand Down
2 changes: 2 additions & 0 deletions crates/polyglot-sql/src/dialects/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2432,6 +2432,7 @@ impl SnowflakeDialect {
let right = args.next().unwrap();
let escape = args.next();
Ok(Expression::Like(Box::new(crate::expressions::LikeOp {
negated: false,
left,
right,
escape,
Expand All @@ -2447,6 +2448,7 @@ impl SnowflakeDialect {
let right = args.next().unwrap();
let escape = args.next();
Ok(Expression::ILike(Box::new(crate::expressions::LikeOp {
negated: false,
left,
right,
escape,
Expand Down
5 changes: 4 additions & 1 deletion crates/polyglot-sql/src/dialects/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,10 @@ impl DialectImpl for SQLiteDialect {
// ILike -> LOWER() LIKE LOWER() (SQLite doesn't support ILIKE)
Expression::ILike(op) => {
let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left.clone())));
let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right.clone())));
let lower_right =
super::lower_like_pattern(op.right.clone(), op.quantifier.is_some());
Ok(Expression::Like(Box::new(LikeOp {
negated: op.negated,
left: lower_left,
right: lower_right,
escape: op.escape,
Expand Down Expand Up @@ -375,6 +377,7 @@ impl SQLiteDialect {
let escape = args.remove(0);
Ok(Expression::Like(Box::new(LikeOp {
left: string,
negated: false,
right: pattern,
escape: Some(escape),
quantifier: None,
Expand Down
4 changes: 3 additions & 1 deletion crates/polyglot-sql/src/dialects/trino.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ impl DialectImpl for TrinoDialect {
// ILike -> LOWER() LIKE LOWER() (Trino doesn't support ILIKE)
Expression::ILike(op) => {
let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left.clone())));
let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right.clone())));
let lower_right =
super::lower_like_pattern(op.right.clone(), op.quantifier.is_some());
Ok(Expression::Like(Box::new(LikeOp {
negated: op.negated,
left: lower_left,
right: lower_right,
escape: op.escape,
Expand Down
3 changes: 2 additions & 1 deletion crates/polyglot-sql/src/dialects/tsql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,9 @@ impl DialectImpl for TSQLDialect {
// SQL Server is case-insensitive by default based on collation
// But for explicit case-insensitive matching, use LOWER
let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
let lower_right = super::lower_like_pattern(op.right, op.quantifier.is_some());
Ok(Expression::Like(Box::new(LikeOp {
negated: op.negated,
left: lower_left,
right: lower_right,
escape: op.escape,
Expand Down
Loading
Loading