diff --git a/Cargo.lock b/Cargo.lock index 6a52b62..fde8c80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,7 +211,7 @@ checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -277,7 +277,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -319,9 +319,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libloading" @@ -413,6 +413,7 @@ dependencies = [ "regex", "serde", "serde_json", + "stacker", "thiserror", ] @@ -497,6 +498,15 @@ dependencies = [ "prost", ] +[[package]] +name = "psm" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +dependencies = [ + "cc", +] + [[package]] name = "quote" version = "1.0.33" @@ -589,7 +599,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -635,6 +645,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +[[package]] +name = "stacker" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + [[package]] name = "syn" version = "2.0.31" @@ -656,7 +679,7 @@ dependencies = [ "fastrand", "redox_syscall 0.3.5", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -748,7 +771,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -757,13 +789,29 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -772,42 +820,90 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 1f535d5..7596fd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ itertools = "0.10.3" prost = { version = "0.13.5", features = ["no-recursion-limit"] } serde = { version = "1.0.139", features = ["derive"] } serde_json = "1.0.82" +stacker = "0.1" thiserror = "1.0.31" [build-dependencies] diff --git a/src/lib.rs b/src/lib.rs index 3c23ff8..2a12dfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,9 +63,9 @@ pub use node_mut::*; pub use node_ref::*; pub use parse_result::*; pub use query::*; -pub use raw_deparse::deparse_raw; +pub use raw_deparse::{deparse_raw, deparse_raw_with_stack}; pub use raw_fingerprint::fingerprint_raw; -pub use raw_parse::parse_raw; +pub use raw_parse::{parse_raw, parse_raw_with_stack}; pub use raw_scan::scan_raw; pub use summary::*; pub use summary_result::*; diff --git a/src/raw_deparse.rs b/src/raw_deparse.rs index bdad7ae..3c336b4 100644 --- a/src/raw_deparse.rs +++ b/src/raw_deparse.rs @@ -49,6 +49,27 @@ pub fn deparse_raw(protobuf: &protobuf::ParseResult) -> Result { } } +/// Deparses a protobuf ParseResult with a custom stack size. +/// +/// This function is useful for deparsing deeply nested queries that might overflow the stack. +/// It uses the `stacker` crate to grow the stack if needed. +/// +/// # Arguments +/// +/// * `protobuf` - The protobuf ParseResult to deparse +/// * `stack_size` - The stack size in bytes to ensure is available for deparsing +/// +/// # Example +/// +/// ```rust +/// let result = pg_query::parse("SELECT * FROM users").unwrap(); +/// let sql = pg_query::deparse_raw_with_stack(&result.protobuf, 8 * 1024 * 1024).unwrap(); +/// assert_eq!(sql, "SELECT * FROM users"); +/// ``` +pub fn deparse_raw_with_stack(protobuf: &protobuf::ParseResult, stack_size: usize) -> Result { + stacker::maybe_grow(32 * 1024, stack_size, || deparse_raw(protobuf)) +} + /// Allocates a C node of the given type. unsafe fn alloc_node(tag: bindings_raw::NodeTag) -> *mut T { bindings_raw::pg_query_alloc_node(std::mem::size_of::(), tag as i32) as *mut T diff --git a/src/raw_parse.rs b/src/raw_parse.rs index 5ecec1a..c29b7e3 100644 --- a/src/raw_parse.rs +++ b/src/raw_parse.rs @@ -41,6 +41,26 @@ pub fn parse_raw(statement: &str) -> Result { parse_result } +/// Parses a SQL statement with a custom stack size. +/// +/// This function is useful for parsing deeply nested queries that might overflow the stack. +/// It uses the `stacker` crate to grow the stack if needed. +/// +/// # Arguments +/// +/// * `statement` - The SQL statement to parse +/// * `stack_size` - The stack size in bytes to ensure is available for parsing +/// +/// # Example +/// +/// ```rust +/// let result = pg_query::parse_raw_with_stack("SELECT * FROM users", 8 * 1024 * 1024).unwrap(); +/// assert_eq!(result.tables(), vec!["users"]); +/// ``` +pub fn parse_raw_with_stack(statement: &str, stack_size: usize) -> Result { + stacker::maybe_grow(32 * 1024, stack_size, || parse_raw(statement)) +} + /// Converts a PostgreSQL List of RawStmt nodes to protobuf RawStmt vector. unsafe fn convert_list_to_raw_stmts(list: *mut bindings_raw::List) -> Vec { if list.is_null() { @@ -1918,10 +1938,8 @@ unsafe fn convert_discard_stmt(ds: &bindings_raw::DiscardStmt) -> protobuf::Disc } unsafe fn convert_coerce_to_domain(ctd: &bindings_raw::CoerceToDomain) -> protobuf::CoerceToDomain { - // xpr is an embedded Expr, convert it as a node pointer - let xpr_ptr = &ctd.xpr as *const bindings_raw::Expr as *mut bindings_raw::Node; protobuf::CoerceToDomain { - xpr: convert_node_boxed(xpr_ptr), + xpr: None, arg: convert_node_boxed(ctd.arg as *mut bindings_raw::Node), resulttype: ctd.resulttype, resulttypmod: ctd.resulttypmod, @@ -2150,9 +2168,8 @@ unsafe fn convert_bit_string(bs: &bindings_raw::BitString) -> protobuf::BitStrin } unsafe fn convert_boolean_test(bt: &bindings_raw::BooleanTest) -> protobuf::BooleanTest { - let xpr_ptr = &bt.xpr as *const bindings_raw::Expr as *mut bindings_raw::Node; protobuf::BooleanTest { - xpr: convert_node_boxed(xpr_ptr), + xpr: None, arg: convert_node_boxed(bt.arg as *mut bindings_raw::Node), booltesttype: bt.booltesttype as i32 + 1, location: bt.location, @@ -2751,20 +2768,12 @@ unsafe fn convert_stats_elem(se: &bindings_raw::StatsElem) -> protobuf::StatsEle } unsafe fn convert_sql_value_function(svf: &bindings_raw::SQLValueFunction) -> protobuf::SqlValueFunction { - let xpr_ptr = &svf.xpr as *const bindings_raw::Expr as *mut bindings_raw::Node; - protobuf::SqlValueFunction { - xpr: convert_node_boxed(xpr_ptr), - op: svf.op as i32 + 1, - r#type: svf.type_, - typmod: svf.typmod, - location: svf.location, - } + protobuf::SqlValueFunction { xpr: None, op: svf.op as i32 + 1, r#type: svf.type_, typmod: svf.typmod, location: svf.location } } unsafe fn convert_xml_expr(xe: &bindings_raw::XmlExpr) -> protobuf::XmlExpr { - let xpr_ptr = &xe.xpr as *const bindings_raw::Expr as *mut bindings_raw::Node; protobuf::XmlExpr { - xpr: convert_node_boxed(xpr_ptr), + xpr: None, op: xe.op as i32 + 1, name: convert_c_string(xe.name), named_args: convert_list_to_nodes(xe.named_args), @@ -2789,9 +2798,8 @@ unsafe fn convert_xml_serialize(xs: &bindings_raw::XmlSerialize) -> protobuf::Xm } unsafe fn convert_named_arg_expr(nae: &bindings_raw::NamedArgExpr) -> protobuf::NamedArgExpr { - let xpr_ptr = &nae.xpr as *const bindings_raw::Expr as *mut bindings_raw::Node; protobuf::NamedArgExpr { - xpr: convert_node_boxed(xpr_ptr), + xpr: None, arg: convert_node_boxed(nae.arg as *mut bindings_raw::Node), name: convert_c_string(nae.name), argnumber: nae.argnumber, @@ -2824,9 +2832,8 @@ unsafe fn convert_json_value_expr(jve: &bindings_raw::JsonValueExpr) -> protobuf } unsafe fn convert_json_constructor_expr(jce: &bindings_raw::JsonConstructorExpr) -> protobuf::JsonConstructorExpr { - let xpr_ptr = &jce.xpr as *const bindings_raw::Expr as *mut bindings_raw::Node; protobuf::JsonConstructorExpr { - xpr: convert_node_boxed(xpr_ptr), + xpr: None, r#type: jce.type_ as i32 + 1, args: convert_list_to_nodes(jce.args), func: convert_node_boxed(jce.func as *mut bindings_raw::Node), @@ -2853,9 +2860,8 @@ unsafe fn convert_json_behavior(jb: &bindings_raw::JsonBehavior) -> protobuf::Js } unsafe fn convert_json_expr(je: &bindings_raw::JsonExpr) -> protobuf::JsonExpr { - let xpr_ptr = &je.xpr as *const bindings_raw::Expr as *mut bindings_raw::Node; protobuf::JsonExpr { - xpr: convert_node_boxed(xpr_ptr), + xpr: None, op: je.op as i32 + 1, column_name: convert_c_string(je.column_name), formatted_expr: convert_node_boxed(je.formatted_expr as *mut bindings_raw::Node), diff --git a/tests/raw_parse/expressions.rs b/tests/raw_parse/expressions.rs index c108bbf..1a1e983 100644 --- a/tests/raw_parse/expressions.rs +++ b/tests/raw_parse/expressions.rs @@ -468,3 +468,27 @@ fn it_parses_params_in_insert() { assert_eq!(raw_result.protobuf, proto_result.protobuf); } + +// ============================================================================ +// SQL Value Function tests +// ============================================================================ + +/// Test CURRENT_TIMESTAMP (was causing infinite recursion) +#[test] +fn it_parses_current_timestamp() { + let query = "INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (6, 1, 37553, -2309, CURRENT_TIMESTAMP)"; + let raw_result = parse_raw(query).unwrap(); + let proto_result = parse(query).unwrap(); + + assert_eq!(raw_result.protobuf, proto_result.protobuf); +} + +/// Test other SQL value functions +#[test] +fn it_parses_sql_value_functions() { + let query = "SELECT CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, LOCALTIME, LOCALTIMESTAMP, CURRENT_USER, CURRENT_CATALOG, CURRENT_SCHEMA"; + let raw_result = parse_raw(query).unwrap(); + let proto_result = parse(query).unwrap(); + + assert_eq!(raw_result.protobuf, proto_result.protobuf); +}