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
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ sql-traits = { git = "https://github.com/connetto-rs/sql-traits", branch = "main
git2 = { version = "0.21", optional = true }
tempfile = { version = "3.27", optional = true }
thiserror = { version = "2.0", default-features = false }
# Grows the stack on demand in the translator's recursion, as `sqlparser`'s
# `recursive-protection` does in its parser.
recursive = { version = "0.1", optional = true }
# Optional Arbitrary impls on the public option types so fuzz harnesses
# can drive `Pg2SqliteOptions` (and its dependent enums) from libfuzzer
# input bytes. Gated behind the `arbitrary` feature so production
Expand Down Expand Up @@ -56,7 +59,7 @@ optional = true
[features]
# Default feature: pulls in everything that depends on the host environment
# (filesystem-backed loaders, git-backed loaders, std-bound error variants,
# and the `recursive-protection` stack guard from sqlparser). Drop this with
# and the stack growth in sqlparser's parser and this crate's translator). Drop this with
# `--no-default-features` to build for `wasm32-unknown-unknown` or other
# no_std targets. The `[patch.crates-io]` block below tracks apache main,
# which makes `sqlparser/visitor` no_std-compatible.
Expand All @@ -65,6 +68,7 @@ std = [
"sqlparser/std",
"sqlparser/recursive-protection",
"sql-traits/std",
"dep:recursive",
"thiserror/std",
"git",
]
Expand Down
1 change: 1 addition & 0 deletions src/history/effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ enum RowWrite<'a> {
}

/// What `statement` writes, or `None` when it writes no rows.
#[cfg_attr(feature = "std", recursive::recursive)]
fn row_write(statement: &Statement) -> Option<RowWrite<'_>> {
match statement {
Statement::Insert(insert) => {
Expand Down
1 change: 1 addition & 0 deletions src/history/placement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ fn place_statement(
})
}

#[cfg_attr(feature = "std", recursive::recursive)]
pub(super) fn primary_write_target(statement: &Statement) -> Option<&ObjectName> {
fn factor(factor: &TableFactor) -> Option<&ObjectName> {
match factor {
Expand Down
3 changes: 3 additions & 0 deletions src/history/rebind/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ impl Planner<'_> {
})
}

#[cfg_attr(feature = "std", recursive::recursive)]
pub(super) fn set_expr(
&mut self,
scope: &ColumnScope<'_, '_, ParserDB>,
Expand Down Expand Up @@ -229,6 +230,7 @@ impl Planner<'_> {
Ok(())
}

#[cfg_attr(feature = "std", recursive::recursive)]
pub(super) fn factor<'s, 'q, 'd>(
&mut self,
scope: &ColumnScope<'_, '_, ParserDB>,
Expand Down Expand Up @@ -266,6 +268,7 @@ impl Planner<'_> {

/// Plans one expression, returning the previous name of the column it is
/// when it is a bare reference to a renamed file column.
#[cfg_attr(feature = "std", recursive::recursive)]
pub(super) fn expr<'s, 'q, 'd>(
&mut self,
scope: &ColumnScope<'_, '_, ParserDB>,
Expand Down
1 change: 1 addition & 0 deletions src/history/rebind/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ impl Planner<'_> {
self.list_as(factor, listed, true)
}

#[cfg_attr(feature = "std", recursive::recursive)]
fn list_as(
&self,
factor: &TableFactor,
Expand Down
1 change: 1 addition & 0 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub(crate) mod session_variable;
pub(crate) mod shared_helpers;
pub(crate) mod sqlite_functions;
pub(crate) mod sqlite_keywords;
pub(crate) mod stack_headroom;
pub(crate) mod temporal_arithmetic;
pub(crate) mod temporal_literals;
pub(crate) mod timezone;
Expand Down
1 change: 1 addition & 0 deletions src/impls/datetime_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ fn take_last_argument(expr: &mut Expr) -> Option<Expr> {
/// Whether `expr` is a timestamp one of SQLite's date functions computed,
/// which is text without an offset.
#[must_use]
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn is_offset_less_timestamp_call(expr: &Expr) -> bool {
let Expr::Function(function) = peel_nested(expr) else { return false };
let named = |name| super::function_helpers::is_function_named(function, name);
Expand Down
1 change: 1 addition & 0 deletions src/impls/function_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub(crate) fn integer_literal(value: i64) -> Expr {
/// a narrower type convert at the call site, so the range a caller accepts is
/// stated where that caller decides it rather than here.
#[must_use]
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn integer_literal_value(expr: &Expr) -> Option<i64> {
match expr {
Expr::Value(ValueWithSpan { value: Value::Number(digits, _), .. }) => digits.parse().ok(),
Expand Down
1 change: 1 addition & 0 deletions src/impls/returning_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ impl<'a> Scope<'a> {
}
}

#[cfg_attr(feature = "std", recursive::recursive)]
fn rewrite_expr(&self, expr: &Expr) -> Result<Expr, Error> {
match expr {
Expr::CompoundIdentifier(parts) if parts.len() >= 2 => {
Expand Down
5 changes: 5 additions & 0 deletions src/impls/reverse_translator_impls/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ fn reverse_numeric_rules_may_apply(op: &BinaryOperator, left: &Expr, right: &Exp
) {
return false;
}
#[cfg_attr(feature = "std", recursive::recursive)]
fn addressable(expr: &Expr) -> bool {
match expr {
Expr::Identifier(_)
Expand All @@ -178,6 +179,7 @@ fn reverse_numeric_rules_may_apply(op: &BinaryOperator, left: &Expr, right: &Exp
_ => false,
}
}
#[cfg_attr(feature = "std", recursive::recursive)]
fn numeric_candidate(expr: &Expr) -> bool {
match expr {
Expr::Value(ValueWithSpan { value: Value::Number(_, _), .. }) => true,
Expand Down Expand Up @@ -210,6 +212,7 @@ fn reverse_unscale_or_translate(

/// Rescales integer literals inside an already-translated scale-preserving
/// call.
#[cfg_attr(feature = "std", recursive::recursive)]
fn reverse_scale_call_arguments(call: Expr, scale: u32) -> Result<Expr, Error> {
let Expr::Function(mut function) = call else { return Ok(call) };
if !is_scale_preserving_call(&function) {
Expand All @@ -235,6 +238,7 @@ fn reverse_scale_call_arguments(call: Expr, scale: u32) -> Result<Expr, Error> {
}

/// True when `expr` is definitely non-text, so `||` would fail in PostgreSQL.
#[cfg_attr(feature = "std", recursive::recursive)]
fn is_definitely_non_text(
expr: &Expr,
schema: &ParserDB,
Expand Down Expand Up @@ -382,6 +386,7 @@ impl ReverseTranslator for Expr {
type Schema = ParserDB;
type PostgresEntry = Self;

#[cfg_attr(feature = "std", recursive::recursive)]
fn reverse_translate(
&self,
schema: &Self::Schema,
Expand Down
1 change: 1 addition & 0 deletions src/impls/reverse_translator_impls/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ impl ReverseTranslator for Query {
type Schema = ParserDB;
type PostgresEntry = Query;

#[cfg_attr(feature = "std", recursive::recursive)]
fn reverse_translate(
&self,
schema: &Self::Schema,
Expand Down
1 change: 1 addition & 0 deletions src/impls/reverse_translator_impls/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ impl ReverseTranslator for Statement {
schema: &Self::Schema,
options: &crate::options::TranslationContext<'_>,
) -> Result<Self::PostgresEntry, Error> {
crate::impls::stack_headroom::ensure();
ensure_usable_rls_table_suffix(options)?;
let mut translated = match self {
Statement::Insert(insert) => {
Expand Down
1 change: 1 addition & 0 deletions src/impls/session_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ pub(crate) fn same_postgres_type(left: &DataType, right: &DataType) -> bool {
/// change nothing a membership test reads, since `current_setting` already
/// answers text.
#[must_use]
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn setting_operand(expr: &Expr) -> &Expr {
match expr {
Expr::Nested(inner)
Expand Down
13 changes: 13 additions & 0 deletions src/impls/shared_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ pub(crate) fn match_partial_not_supported_error() -> Error {
///
/// The qualifier of a compound name is dropped, since it may be an alias rather
/// than a table.
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn referenced_column_name(expr: &Expr) -> Option<&str> {
match expr {
Expr::Identifier(ident) => Some(ident.value.as_str()),
Expand Down Expand Up @@ -264,6 +265,7 @@ impl Visitor for FunctionColumnCollector {

/// Returns every column name `expr` mentions.
#[must_use]
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn extract_columns_from_expr(expr: &Expr) -> ColumnReferences {
match expr {
Expr::Identifier(ident) => ColumnReferences::Complete(vec![ident.value.clone()]),
Expand Down Expand Up @@ -452,6 +454,7 @@ enum DefinitionValue<T> {
Opaque,
}

#[cfg_attr(feature = "std", recursive::recursive)]
fn evaluate_definition<T: PartialEq>(
definition: &ColumnDefinition<'_, '_, '_, ParserDB>,
schema: &ParserDB,
Expand Down Expand Up @@ -538,6 +541,7 @@ pub(crate) fn query_output_matches(
Ok(false)
}

#[cfg_attr(feature = "std", recursive::recursive)]
fn definition_matches(
definition: &ColumnDefinition<'_, '_, '_, ParserDB>,
schema: &ParserDB,
Expand Down Expand Up @@ -661,6 +665,7 @@ fn has_nested_join(entry: &sqlparser::ast::TableWithJoins) -> bool {
}

/// Appends `entry` and everything a nested join inside it hides.
#[cfg_attr(feature = "std", recursive::recursive)]
fn flatten_relations(
entry: &sqlparser::ast::TableWithJoins,
out: &mut Vec<sqlparser::ast::TableWithJoins>,
Expand Down Expand Up @@ -704,6 +709,7 @@ pub(crate) fn relations_scope_query(
}

/// Unwraps parentheses, which carry no meaning for resolution.
#[cfg_attr(feature = "std", recursive::recursive)]
fn strip_nesting(expr: &Expr) -> &Expr {
match expr {
Expr::Nested(inner) => strip_nesting(inner),
Expand All @@ -720,6 +726,7 @@ fn unresolved_reference(reference: &Expr, reason: &str) -> crate::errors::Error

/// True when the column `expr` names is declared with a type `predicate`
/// accepts.
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn declared_type_matches(
expr: &Expr,
schema: &ParserDB,
Expand All @@ -739,6 +746,7 @@ pub(crate) fn declared_type_matches(
}

/// True when `expr` is a whole number by construction, so its scale is 0.
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn is_integral_expression(
expr: &Expr,
schema: &ParserDB,
Expand Down Expand Up @@ -833,6 +841,7 @@ pub(crate) fn is_scale_preserving_call(function: &Function) -> bool {
})
}

#[cfg_attr(feature = "std", recursive::recursive)]
fn numeric_precision_and_scale_of(
expr: &Expr,
schema: &ParserDB,
Expand Down Expand Up @@ -1912,6 +1921,7 @@ pub(crate) fn scale_literal_for_column(
}

/// True when `expr` is a number literal with a decimal point.
#[cfg_attr(feature = "std", recursive::recursive)]
fn is_decimal_number_literal(expr: &Expr) -> bool {
match expr {
Expr::Value(ValueWithSpan { value: Value::Number(digits, _), .. }) => digits.contains('.'),
Expand Down Expand Up @@ -3624,6 +3634,7 @@ fn translate_select_with_from<D: TranslationDirection>(
/// becomes a select over it as a derived table, so `(SELECT a FROM t ORDER BY
/// a LIMIT 1) UNION (SELECT b FROM t ORDER BY b DESC LIMIT 1)` keeps
/// answering 1, 30.
#[cfg_attr(feature = "std", recursive::recursive)]
fn unparenthesize_compound_operand(operand: sqlparser::ast::SetExpr) -> sqlparser::ast::SetExpr {
use sqlparser::ast::SetExpr;

Expand Down Expand Up @@ -3655,6 +3666,7 @@ fn unparenthesize_compound_operand(operand: sqlparser::ast::SetExpr) -> sqlparse
}

/// Shared `SetExpr` translation. Forward errors on `Table` and `Merge`.
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn translate_set_expr_shared<D: TranslationDirection>(
set_expr: &sqlparser::ast::SetExpr,
schema: &ParserDB,
Expand Down Expand Up @@ -4103,6 +4115,7 @@ fn subquery_is_trivially_uncorrelated(query: &Query) -> bool {
}

#[allow(clippy::too_many_lines)]
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn translate_table_factor<D: TranslationDirection>(
table_factor: &TableFactor,
schema: &ParserDB,
Expand Down
15 changes: 15 additions & 0 deletions src/impls/stack_headroom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//! The stack headroom the translator's `#[recursive]` functions need.

/// The most one step of a translator recursion cycle uses, which a debug build
/// takes up to about 140 KiB of.
#[cfg(feature = "std")]
const HEADROOM: usize = 256 * 1024;

/// Raises `recursive`'s process-wide minimum to [`HEADROOM`], never lowering
/// it.
pub(crate) fn ensure() {
#[cfg(feature = "std")]
if recursive::get_minimum_stack_size() < HEADROOM {
recursive::set_minimum_stack_size(HEADROOM);
}
}
2 changes: 2 additions & 0 deletions src/impls/temporal_arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub(crate) enum TemporalKind {

/// Resolve the temporal type of `expr` from its own spelling or from the
/// declared type of the column it names.
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn temporal_kind(
expr: &Expr,
schema: &ParserDB,
Expand Down Expand Up @@ -159,6 +160,7 @@ fn binary_result_kind(
})
}

#[cfg_attr(feature = "std", recursive::recursive)]
fn is_interval(expr: &Expr) -> bool {
match expr {
Expr::Interval(_) => true,
Expand Down
1 change: 1 addition & 0 deletions src/impls/timezone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ pub(crate) enum TimestampAwareness {
///
/// Both directions need this and must agree: the forward direction negates an
/// aware operand's offset, so the reverse direction has to negate it back.
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn timestamp_awareness(
expr: &Expr,
schema: &ParserDB,
Expand Down
2 changes: 2 additions & 0 deletions src/impls/translator_impls/create_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ fn lower_argument(function: &Function) -> Option<&Expr> {
plain_argument_expr(argument)
}

#[cfg_attr(feature = "std", recursive::recursive)]
fn is_reproducible_fts_document(expr: &Expr) -> bool {
match expr {
Expr::Identifier(_) | Expr::CompoundIdentifier(_) => true,
Expand All @@ -142,6 +143,7 @@ fn is_reproducible_fts_document(expr: &Expr) -> bool {
}

/// Returns the columns in a `to_tsvector` document that FTS5 can reproduce.
#[cfg_attr(feature = "std", recursive::recursive)]
fn analyze_fts_expression(expr: &Expr) -> Option<Vec<String>> {
match expr {
Expr::Function(function) => {
Expand Down
2 changes: 2 additions & 0 deletions src/impls/translator_impls/create_trigger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ use crate::{
/// `recursive_triggers = ON` (measured: `too many levels of trigger
/// recursion`). References to columns assigned LATER stay raw, because reading
/// the pre-update row is exactly what PostgreSQL does for them.
#[cfg_attr(feature = "std", recursive::recursive)]
fn compose_assignment_refs(expr: &Expr, assigned: &[(String, Expr)]) -> Expr {
let earlier = |name: &str| {
assigned
Expand Down Expand Up @@ -221,6 +222,7 @@ fn route_trigger_table_factor(
Ok(())
}

#[cfg_attr(feature = "std", recursive::recursive)]
fn route_trigger_query_writes(
query: &mut Query,
schema: &ParserDB,
Expand Down
4 changes: 4 additions & 0 deletions src/impls/translator_impls/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ fn translate_extract(
/// Under the JSON array representation an array column is TEXT holding a JSON
/// array, so nothing about the value distinguishes it from a string and the
/// declared type is the only evidence there is.
#[cfg_attr(feature = "std", recursive::recursive)]
fn is_array_expression(
expr: &Expr,
schema: &ParserDB,
Expand Down Expand Up @@ -396,6 +397,7 @@ fn translate_boolean_cast(
}

/// The word PostgreSQL renders a boolean literal as, when `expr` is one.
#[cfg_attr(feature = "std", recursive::recursive)]
fn boolean_literal_word(expr: &Expr) -> Option<&'static str> {
match expr {
Expr::Nested(inner) => boolean_literal_word(inner),
Expand All @@ -413,6 +415,7 @@ fn boolean_literal_word(expr: &Expr) -> Option<&'static str> {
/// and nothing downstream can tell it from a count. Anything this does not
/// recognise is left alone, which keeps a value that merely happens to be 1
/// from being rendered as a word.
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn is_boolean_expression(
expr: &Expr,
schema: &ParserDB,
Expand Down Expand Up @@ -3362,6 +3365,7 @@ impl crate::traits::translator::TranslatorWithContext for Expr {
type SQLiteEntry = Expr;

#[allow(clippy::too_many_lines)]
#[cfg_attr(feature = "std", recursive::recursive)]
fn translate_with_warnings(
&self,
schema: &sql_traits::structs::ParserDB,
Expand Down
2 changes: 2 additions & 0 deletions src/impls/translator_impls/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,7 @@ fn declares_function_by_option(name: &str, options: &Pg2SqliteOptions) -> bool {
/// It cannot see through a bare column reference, so an array or `json` COLUMN
/// takes the `json_quote` path and is quoted into a string, which is wrong and
/// needs the column's declared type to fix. Tracked as R89.
#[cfg_attr(feature = "std", recursive::recursive)]
fn is_already_json(expr: &Expr) -> bool {
const JSON_VALUED: [&str; 10] = [
"json",
Expand Down Expand Up @@ -1347,6 +1348,7 @@ fn resolves_to_non_textual_column(
/// thing that separates a document from its own text. An unqualified name is
/// accepted only when every column with that name in the schema agrees, since
/// guessing between the two is wrong half the time in either direction.
#[cfg_attr(feature = "std", recursive::recursive)]
pub(crate) fn carries_json(
expr: &Expr,
schema: &ParserDB,
Expand Down
Loading
Loading