diff --git a/Cargo.lock b/Cargo.lock index 55a96a2c33..2ace44167e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -98,7 +98,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -211,7 +211,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -604,7 +604,7 @@ dependencies = [ "arrow-schema", "chrono", "half", - "indexmap 2.2.5", + "indexmap 2.14.0", "lexical-core", "num", "serde", @@ -689,7 +689,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -719,7 +719,7 @@ checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -741,7 +741,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -752,7 +752,7 @@ checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -974,7 +974,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -1085,7 +1085,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", "syn_derive", ] @@ -1120,7 +1120,7 @@ dependencies = [ "base64 0.13.1", "bitvec", "hex", - "indexmap 2.2.5", + "indexmap 2.14.0", "js-sys", "once_cell", "rand", @@ -1397,7 +1397,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -1484,7 +1484,7 @@ checksum = "7c64043d6c7b7a4c58e39e7efccfdea7b93d885a795d0c054a69dbbf4dd52686" dependencies = [ "strum", "strum_macros", - "unicode-width", + "unicode-width 0.1.11", ] [[package]] @@ -1496,7 +1496,7 @@ dependencies = [ "encode_unicode 0.3.6", "lazy_static", "libc", - "unicode-width", + "unicode-width 0.1.11", "windows-sys 0.52.0", ] @@ -1543,7 +1543,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4be93df536dfbcbd39ff7c129635da089901116b88bfc29ec1acb9b56f8ff35" dependencies = [ - "unicode-width", + "unicode-width 0.1.11", "vte", ] @@ -1843,13 +1843,13 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] name = "daggy" version = "0.8.0" -source = "git+https://github.com/getdozer/daggy?branch=feat/try_map#4ff8ebbdba979ecd7f638b6636c33ec9c0d27ccc" +source = "git+https://github.com/getdozer/daggy?branch=feat%2Ftry_map#4ff8ebbdba979ecd7f638b6636c33ec9c0d27ccc" dependencies = [ "petgraph 0.6.3", "serde", @@ -1900,7 +1900,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -1922,7 +1922,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core 0.20.8", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -1995,7 +1995,7 @@ dependencies = [ "glob", "half", "hashbrown 0.14.3", - "indexmap 2.2.5", + "indexmap 2.14.0", "itertools 0.12.1", "log", "num_cpus", @@ -2110,7 +2110,7 @@ dependencies = [ "half", "hashbrown 0.14.3", "hex", - "indexmap 2.2.5", + "indexmap 2.14.0", "itertools 0.12.1", "log", "md-5", @@ -2143,7 +2143,7 @@ dependencies = [ "futures", "half", "hashbrown 0.14.3", - "indexmap 2.2.5", + "indexmap 2.14.0", "itertools 0.12.1", "log", "once_cell", @@ -2235,7 +2235,7 @@ dependencies = [ "fix-hidden-lifetime-bug", "futures", "hashbrown 0.14.3", - "indexmap 2.2.5", + "indexmap 2.14.0", "itertools 0.12.1", "lazy_static", "libc", @@ -2333,7 +2333,7 @@ checksum = "6cf517bddfd22d79d0f284500318e3f9aea193536c2b61cbf6ce7b50a85f1b6a" dependencies = [ "anyhow", "deno_media_type", - "indexmap 2.2.5", + "indexmap 2.14.0", "log", "once_cell", "parking_lot", @@ -2511,7 +2511,7 @@ dependencies = [ "quote", "strum", "strum_macros", - "syn 2.0.53", + "syn 2.0.117", "thiserror", ] @@ -2681,7 +2681,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -2759,7 +2759,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -3069,6 +3069,8 @@ dependencies = [ "proptest", "sqlparser 0.35.0", "tokio", + "wasmi", + "wat", ] [[package]] @@ -3134,7 +3136,7 @@ dependencies = [ "chrono", "geo", "ijson", - "indexmap 2.2.5", + "indexmap 2.14.0", "indicatif", "log", "ordered-float 3.9.2", @@ -3312,7 +3314,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -3324,7 +3326,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -3590,6 +3592,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -3622,7 +3630,7 @@ checksum = "3a0b11eeb173ce52f84ebd943d42e58813a2ebb78a6a3ff0a243b71c5199cd7b" dependencies = [ "proc-macro2", "swc_macros_common", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -3650,7 +3658,7 @@ checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e" dependencies = [ "frunk_proc_macro_helpers", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -3662,7 +3670,7 @@ dependencies = [ "frunk_core", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -3674,7 +3682,7 @@ dependencies = [ "frunk_core", "frunk_proc_macro_helpers", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -3767,7 +3775,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -3954,7 +3962,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.2.5", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", @@ -3973,7 +3981,7 @@ dependencies = [ "futures-sink", "futures-util", "http 1.1.0", - "indexmap 2.2.5", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", @@ -4042,6 +4050,21 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "hashlink" version = "0.8.4" @@ -4285,7 +4308,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.6", "tokio", "tower-service", "tracing", @@ -4526,13 +4549,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.17.1", "serde", + "serde_core", ] [[package]] @@ -4545,7 +4569,7 @@ dependencies = [ "instant", "number_prefix", "portable-atomic", - "unicode-width", + "unicode-width 0.1.11", ] [[package]] @@ -4636,7 +4660,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -4764,7 +4788,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee7893dab2e44ae5f9d0173f26ff4aa327c10b01b06a72b52dd9405b628640d" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.14.0", ] [[package]] @@ -4808,6 +4832,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "lexical-core" version = "0.8.5" @@ -4924,9 +4954,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" @@ -5050,7 +5080,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.6.29", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -5324,7 +5354,7 @@ dependencies = [ "proc-macro-error 1.0.4", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", "termcolor", "thiserror", ] @@ -5775,7 +5805,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -6185,7 +6215,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -6202,7 +6232,7 @@ dependencies = [ [[package]] name = "petgraph" version = "0.6.3" -source = "git+https://github.com/getdozer/petgraph?branch=feat/try_map#2422a3a21d92e7a6446f0d0f172c8aa24258846e" +source = "git+https://github.com/getdozer/petgraph?branch=feat%2Ftry_map#2422a3a21d92e7a6446f0d0f172c8aa24258846e" dependencies = [ "fixedbitset", "indexmap 1.9.3", @@ -6217,7 +6247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.2.5", + "indexmap 2.14.0", ] [[package]] @@ -6260,7 +6290,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -6289,7 +6319,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -6373,7 +6403,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -6458,7 +6488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -6472,7 +6502,7 @@ dependencies = [ "is-terminal", "lazy_static", "term", - "unicode-width", + "unicode-width 0.1.11", ] [[package]] @@ -6589,7 +6619,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", "proc-macro2", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -6601,14 +6631,14 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -6687,7 +6717,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.53", + "syn 2.0.117", "tempfile", "which 4.4.2", ] @@ -6702,7 +6732,7 @@ dependencies = [ "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -7502,7 +7532,7 @@ dependencies = [ "nix", "radix_trie", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.11", "utf8parse", "winapi", ] @@ -7515,7 +7545,7 @@ checksum = "e5af959c8bf6af1aff6d2b463a57f71aae53d1332da58419e30ad8dc7011d951" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -7711,10 +7741,11 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] @@ -7737,15 +7768,24 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -7765,7 +7805,7 @@ version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.14.0", "itoa", "ryu", "serde", @@ -7825,7 +7865,7 @@ version = "0.9.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0623d197252096520c6f2a5e1171ee436e5af99a5d7caa2891e55e61950e6d9" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.14.0", "itoa", "ryu", "serde", @@ -7879,7 +7919,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -8197,7 +8237,7 @@ checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -8225,6 +8265,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string-interner" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23de088478b31c349c9ba67816fa55d9355232d63c3afea8bf513e31f0f1d2c0" +dependencies = [ + "hashbrown 0.15.5", + "serde", +] + [[package]] name = "string_enum" version = "0.4.2" @@ -8234,7 +8284,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -8279,7 +8329,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -8299,7 +8349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca1318e5d6716d6541696727c88d9b8dfc8cfe6afd6908e186546fd4af7f5b98" dependencies = [ "memchr", - "unicode-width", + "unicode-width 0.1.11", ] [[package]] @@ -8342,7 +8392,7 @@ dependencies = [ "swc_eq_ignore_macros", "swc_visit", "tracing", - "unicode-width", + "unicode-width 0.1.11", "url", ] @@ -8352,7 +8402,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112884e66b60e614c0f416138b91b8b82b7fea6ed0ecc5e26bad4726c57a6c99" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.14.0", "serde", "serde_json", "swc_config_macro", @@ -8367,7 +8417,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -8416,7 +8466,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -8462,7 +8512,7 @@ checksum = "6d4ab26ec124b03e47f54d4daade8e9a9dcd66d3a4ca3cd47045f138d267a60e" dependencies = [ "better_scoped_tls", "bitflags 2.5.0", - "indexmap 2.2.5", + "indexmap 2.14.0", "once_cell", "phf", "rustc-hash", @@ -8500,7 +8550,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -8531,7 +8581,7 @@ checksum = "9918e22caf1ea4a71085f5d818d6c0bf5c19d669cfb9d38f9fdc3da0496abdc7" dependencies = [ "base64 0.21.7", "dashmap 5.5.3", - "indexmap 2.2.5", + "indexmap 2.14.0", "once_cell", "serde", "sha-1 0.10.0", @@ -8570,7 +8620,7 @@ version = "0.125.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cead1083e46b0f072a82938f16d366014468f7510350957765bb4d013496890" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.14.0", "num_cpus", "once_cell", "rustc-hash", @@ -8604,7 +8654,7 @@ checksum = "695a1d8b461033d32429b5befbf0ad4d7a2c4d6ba9cd5ba4e0645c615839e8e4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -8615,7 +8665,7 @@ checksum = "50176cfc1cbc8bb22f41c6fe9d1ec53fbe057001219b5954961b8ad0f336fce9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -8639,7 +8689,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -8655,9 +8705,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.53" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -8684,7 +8734,7 @@ dependencies = [ "proc-macro-error 1.0.4", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -8780,7 +8830,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -8916,7 +8966,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -9038,7 +9088,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.14.0", "toml_datetime", "winnow", ] @@ -9049,7 +9099,7 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.14.0", "toml_datetime", "winnow", ] @@ -9060,7 +9110,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.14.0", "toml_datetime", "winnow", ] @@ -9133,7 +9183,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -9251,7 +9301,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -9454,7 +9504,7 @@ checksum = "f03ca4cb38206e2bef0700092660bb74d696f808514dae47fa1467cbfe26e96e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -9582,6 +9632,12 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unindent" version = "0.1.11" @@ -9825,7 +9881,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -9859,7 +9915,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9870,6 +9926,16 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-encoder" +version = "0.248.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac92cf547bc18d27ecc521015c08c353b4f18b84ab388bb6d1b6b682c620d9b6" +dependencies = [ + "leb128fmt", + "wasmparser 0.248.0", +] + [[package]] name = "wasm-streams" version = "0.3.0" @@ -9883,6 +9949,90 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasmi" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22bf475363d09d960b48275c4ea9403051add498a9d80c64dbc91edabab9d1d0" +dependencies = [ + "spin 0.9.8", + "wasmi_collections", + "wasmi_core", + "wasmi_ir", + "wasmparser 0.228.0", + "wat", +] + +[[package]] +name = "wasmi_collections" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85851acbdffd675a9b699b3590406a1d37fc1e1fd073743c7c9cf47c59caacba" +dependencies = [ + "string-interner", +] + +[[package]] +name = "wasmi_core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef64cf60195d1f937dbaed592a5afce3e6d86868fb8070c5255bc41539d68f9d" +dependencies = [ + "libm", +] + +[[package]] +name = "wasmi_ir" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcb572ce4400e06b5475819f3d6b9048513efbca785f0b9ef3a41747f944fd8" +dependencies = [ + "wasmi_core", +] + +[[package]] +name = "wasmparser" +version = "0.228.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4abf1132c1fdf747d56bbc1bb52152400c70f336870f968b85e89ea422198ae3" +dependencies = [ + "bitflags 2.5.0", + "indexmap 2.14.0", +] + +[[package]] +name = "wasmparser" +version = "0.248.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4439c5eee9df71ee0c6efb37f63b1fcb1fec38f85f5142c54e7ed05d33091a" +dependencies = [ + "bitflags 2.5.0", + "indexmap 2.14.0", + "semver 1.0.22", +] + +[[package]] +name = "wast" +version = "248.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acc54622ed5a5cddafcdf152043f9d4aed54d4a653d686b7dfe874809fca99d7" +dependencies = [ + "bumpalo", + "leb128fmt", + "memchr", + "unicode-width 0.2.2", + "wasm-encoder", +] + +[[package]] +name = "wat" +version = "1.248.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75cd9e510603909748e6ebab89f27cd04472c1d9d85a3c88a7a6fc51a1a7934" +dependencies = [ + "wast", +] + [[package]] name = "web-sys" version = "0.3.69" @@ -10376,7 +10526,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] @@ -10396,7 +10546,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.117", ] [[package]] diff --git a/dozer-cli/Cargo.toml b/dozer-cli/Cargo.toml index cb0519af7e..c0f79bfcab 100644 --- a/dozer-cli/Cargo.toml +++ b/dozer-cli/Cargo.toml @@ -64,4 +64,5 @@ mongodb = ["dozer-ingestion/mongodb"] onnx = ["dozer-sql/onnx"] tokio-console = ["dozer-tracing/tokio-console"] javascript = ["dozer-ingestion/javascript", "dozer-sql/javascript"] +wasm = ["dozer-sql/wasm"] datafusion = ["dozer-ingestion/datafusion"] diff --git a/dozer-sql/Cargo.toml b/dozer-sql/Cargo.toml index 28bb354f41..32d2bf25ab 100644 --- a/dozer-sql/Cargo.toml +++ b/dozer-sql/Cargo.toml @@ -28,3 +28,4 @@ proptest = "1.3.1" python = ["dozer-sql-expression/python"] onnx = ["dozer-sql-expression/onnx"] javascript = ["dozer-sql-expression/javascript"] +wasm = ["dozer-sql-expression/wasm"] diff --git a/dozer-sql/expression/Cargo.toml b/dozer-sql/expression/Cargo.toml index 3423d7904a..8371fb063e 100644 --- a/dozer-sql/expression/Cargo.toml +++ b/dozer-sql/expression/Cargo.toml @@ -22,12 +22,15 @@ async-recursion = "1.0.5" dozer-deno = { path = "../../dozer-deno", optional = true } deno_core = { workspace = true, optional = true } +wasmi = { version = "1.0.9", optional = true } [dev-dependencies] proptest = "1.2.0" +wat = "1.248.0" [features] bigdecimal = ["dep:bigdecimal", "sqlparser/bigdecimal"] python = ["dozer-types/python-auto-initialize"] onnx = ["dep:ort", "dep:ndarray", "dep:half"] javascript = ["dep:dozer-deno", "dep:deno_core"] +wasm = ["dep:wasmi"] diff --git a/dozer-sql/expression/src/builder.rs b/dozer-sql/expression/src/builder.rs index cefcda0039..6190561970 100644 --- a/dozer-sql/expression/src/builder.rs +++ b/dozer-sql/expression/src/builder.rs @@ -573,6 +573,26 @@ impl ExpressionBuilder { Err(Error::JavaScriptNotEnabled) } } + + UdfType::Wasm(config) => { + #[cfg(feature = "wasm")] + { + self.parse_wasm_udf( + function_name.clone(), + config, + sql_function, + schema, + udfs, + ) + .await + } + + #[cfg(not(feature = "wasm"))] + { + let _ = config; + Err(Error::WasmNotEnabled) + } + } }; } @@ -995,6 +1015,34 @@ impl ExpressionBuilder { Ok(Expression::JavaScriptUdf(udf)) } + #[cfg(feature = "wasm")] + async fn parse_wasm_udf( + &mut self, + name: String, + config: &dozer_types::models::udf_config::WasmConfig, + function: &Function, + schema: &Schema, + udfs: &[UdfConfig], + ) -> Result { + let mut args = vec![]; + for argument in &function.args { + let arg = self + .parse_sql_function_arg(false, argument, schema, udfs) + .await?; + args.push(arg); + } + + let udf = crate::wasm::Udf::new( + name, + config.module.clone(), + config.function.clone(), + FieldType::try_from(config.return_type.as_str()) + .map_err(|_| Error::InvalidWasmReturnType(config.return_type.clone()))?, + args, + )?; + Ok(Expression::WasmUdf(udf)) + } + async fn parse_sql_in_list_operator( &mut self, parse_aggregations: bool, @@ -1062,3 +1110,118 @@ pub fn extend_schema_source_def(schema: &Schema, name: &NameOrAlias) -> Schema { output_schema } + +#[cfg(test)] +mod wasm_builder_tests { + use super::*; + #[cfg(feature = "wasm")] + use dozer_types::types::{Field, Record}; + use dozer_types::{ + models::udf_config::{UdfConfig, UdfType, WasmConfig}, + types::FieldDefinition, + }; + use sqlparser::{ + ast::{SelectItem, SetExpr, Statement}, + dialect::GenericDialect, + parser::Parser, + }; + + fn first_projection_expr(sql: &str) -> Expr { + let dialect = GenericDialect {}; + let ast = Parser::parse_sql(&dialect, sql).unwrap(); + let Statement::Query(query) = ast.into_iter().next().unwrap() else { + panic!("expected query"); + }; + let SetExpr::Select(select) = *query.body else { + panic!("expected select"); + }; + match select.projection.into_iter().next().unwrap() { + SelectItem::UnnamedExpr(expr) => expr, + _ => panic!("expected unnamed expression"), + } + } + + fn schema(field_type: FieldType) -> Schema { + let mut schema = Schema::new(); + schema.field( + FieldDefinition::new( + "value".to_string(), + field_type, + false, + SourceDefinition::Dynamic, + ), + false, + ); + schema + } + + fn wasm_udf(module: String) -> Vec { + vec![UdfConfig { + name: "add_one".to_string(), + config: UdfType::Wasm(WasmConfig { + module, + function: None, + return_type: "int".to_string(), + }), + }] + } + + #[cfg(feature = "wasm")] + fn write_wasm_module() -> String { + let wasm = wat::parse_str( + r#" + (module + (func (export "add_one") (param i64) (result i64) + local.get 0 + i64.const 1 + i64.add)) + "#, + ) + .unwrap(); + let path = std::env::temp_dir().join(format!( + "dozer-expression-builder-wasm-{}.wasm", + std::process::id() + )); + std::fs::write(&path, wasm).unwrap(); + path.to_string_lossy().to_string() + } + + #[cfg(feature = "wasm")] + #[test] + fn builds_and_evaluates_wasm_udf() { + let sql_expr = first_projection_expr("SELECT add_one(value) FROM t"); + let schema = schema(FieldType::Int); + let runtime = Arc::new(Runtime::new().unwrap()); + let mut builder = ExpressionBuilder::new(schema.fields.len(), runtime.clone()); + let mut expression = runtime + .block_on(builder.build(false, &sql_expr, &schema, &wasm_udf(write_wasm_module()))) + .unwrap(); + let record = Record::new(vec![Field::Int(41)]); + + assert_eq!( + expression.evaluate(&record, &schema).unwrap(), + Field::Int(42) + ); + assert_eq!( + expression.get_type(&schema).unwrap().return_type, + FieldType::Int + ); + } + + #[cfg(not(feature = "wasm"))] + #[test] + fn wasm_udf_requires_feature() { + let sql_expr = first_projection_expr("SELECT add_one(value) FROM t"); + let schema = schema(FieldType::Int); + let runtime = Arc::new(Runtime::new().unwrap()); + let mut builder = ExpressionBuilder::new(schema.fields.len(), runtime.clone()); + let result = runtime.block_on(builder.build( + false, + &sql_expr, + &schema, + &wasm_udf("./missing.wasm".to_string()), + )); + + assert!(matches!(result, Err(Error::WasmNotEnabled))); + } +} diff --git a/dozer-sql/expression/src/error.rs b/dozer-sql/expression/src/error.rs index 7078229713..1bdf645eb3 100644 --- a/dozer-sql/expression/src/error.rs +++ b/dozer-sql/expression/src/error.rs @@ -109,6 +109,15 @@ pub enum Error { #[error("JavaScript UDF error: {0}")] JavaScript(#[from] crate::javascript::Error), + #[error("WASM UDF is not enabled")] + WasmNotEnabled, + #[error("Invalid WASM UDF return type: {0}")] + InvalidWasmReturnType(String), + + #[cfg(feature = "wasm")] + #[error("WASM UDF error: {0}")] + Wasm(#[from] crate::wasm::Error), + // Legacy error types. #[error("Sql error: {0}")] SqlError(#[source] OperationError), diff --git a/dozer-sql/expression/src/execution.rs b/dozer-sql/expression/src/execution.rs index 1c2891b234..044ead0d4c 100644 --- a/dozer-sql/expression/src/execution.rs +++ b/dozer-sql/expression/src/execution.rs @@ -105,6 +105,8 @@ pub enum Expression { }, #[cfg(feature = "javascript")] JavaScriptUdf(crate::javascript::Udf), + #[cfg(feature = "wasm")] + WasmUdf(crate::wasm::Udf), } impl Expression { @@ -285,6 +287,8 @@ impl Expression { } #[cfg(feature = "javascript")] Expression::JavaScriptUdf(udf) => udf.to_string(schema), + #[cfg(feature = "wasm")] + Expression::WasmUdf(udf) => udf.to_string(schema), Expression::IsNull { arg } => arg.to_string(schema) + " IS NULL ", Expression::IsNotNull { arg } => arg.to_string(schema) + " IS NOT NULL ", } @@ -378,6 +382,8 @@ impl Expression { Expression::IsNotNull { arg } => evaluate_is_not_null(schema, arg, record), #[cfg(feature = "javascript")] Expression::JavaScriptUdf(udf) => udf.evaluate(record, schema), + #[cfg(feature = "wasm")] + Expression::WasmUdf(udf) => udf.evaluate(record, schema), } } @@ -487,6 +493,8 @@ impl Expression { )), #[cfg(feature = "javascript")] Expression::JavaScriptUdf(udf) => Ok(udf.get_type()), + #[cfg(feature = "wasm")] + Expression::WasmUdf(udf) => Ok(udf.get_type()), Expression::IsNull { arg: _ } => Ok(ExpressionType::new( FieldType::Boolean, false, diff --git a/dozer-sql/expression/src/lib.rs b/dozer-sql/expression/src/lib.rs index c94463f9d9..da24bae06e 100644 --- a/dozer-sql/expression/src/lib.rs +++ b/dozer-sql/expression/src/lib.rs @@ -23,6 +23,8 @@ mod javascript; mod onnx; #[cfg(feature = "python")] mod python_udf; +#[cfg(feature = "wasm")] +mod wasm; pub use num_traits; pub use sqlparser; diff --git a/dozer-sql/expression/src/wasm.rs b/dozer-sql/expression/src/wasm.rs new file mode 100644 index 0000000000..75a1c9a79f --- /dev/null +++ b/dozer-sql/expression/src/wasm.rs @@ -0,0 +1,442 @@ +use std::{convert::TryFrom, fs, sync::Arc}; + +use dozer_types::{ + ordered_float::OrderedFloat, + thiserror, + types::{Field, FieldType, Record, Schema, SourceDefinition}, +}; +use wasmi::{Engine, Extern, Linker, Module, Store, Val, ValType, F64}; + +use crate::execution::{Expression, ExpressionType}; + +#[derive(Debug, Clone)] +pub struct Udf { + udf_name: String, + function_name: String, + return_type: FieldType, + args: Vec, + engine: Engine, + module: Arc, +} + +impl PartialEq for Udf { + fn eq(&self, other: &Self) -> bool { + self.udf_name == other.udf_name + && self.function_name == other.function_name + && self.return_type == other.return_type + && self.args == other.args + } +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("failed to read WASM module {path}: {source}")] + ReadModule { + path: String, + #[source] + source: std::io::Error, + }, + #[error("failed to load WASM module: {0}")] + LoadModule(String), + #[error("failed to instantiate WASM module: {0}")] + Instantiate(String), + #[error("WASM function {0} was not found")] + MissingFunction(String), + #[error("WASM export {0} is not a function")] + ExportNotFunction(String), + #[error("WASM function {function_name} expected {expected} arguments, got {actual}")] + InvalidArgumentCount { + function_name: String, + expected: usize, + actual: usize, + }, + #[error("Unsupported WASM argument type for function {function_name}: {field_type}")] + UnsupportedArgumentType { + function_name: String, + field_type: FieldType, + }, + #[error("Unsupported WASM return type for function {function_name}: {field_type}")] + UnsupportedReturnType { + function_name: String, + field_type: FieldType, + }, + #[error("Invalid null argument for WASM function {function_name}")] + NullArgument { function_name: String }, + #[error("WASM uint argument {value} is out of i64 ABI range for function {function_name}")] + UIntArgumentOutOfRange { function_name: String, value: u64 }, + #[error("WASM uint return value {value} is out of u64 ABI range for function {function_name}")] + UIntReturnOutOfRange { function_name: String, value: i64 }, + #[error( + "WASM function {function_name} expected {expected:?}, got {actual:?} at argument {index}" + )] + InvalidArgumentType { + function_name: String, + index: usize, + expected: ValType, + actual: ValType, + }, + #[error("WASM function {function_name} expected return {expected:?}, got {actual:?}")] + InvalidReturnType { + function_name: String, + expected: ValType, + actual: ValType, + }, + #[error("WASM function {function_name} expected one return value, got {actual}")] + InvalidReturnCount { + function_name: String, + actual: usize, + }, + #[error("failed to call WASM function {function_name}: {source}")] + Call { + function_name: String, + source: wasmi::Error, + }, +} + +impl Udf { + pub fn new( + udf_name: String, + module_path: String, + function_name: Option, + return_type: FieldType, + args: Vec, + ) -> Result { + let engine = Engine::default(); + let module_bytes = fs::read(&module_path).map_err(|source| Error::ReadModule { + path: module_path, + source, + })?; + let module = Module::new(&engine, &module_bytes[..]) + .map_err(|error| Error::LoadModule(error.to_string()))?; + + let function_name = function_name.unwrap_or_else(|| udf_name.clone()); + field_type_to_wasm_type(&function_name, return_type)?; + Ok(Self { + udf_name, + function_name, + return_type, + args, + engine, + module: Arc::new(module), + }) + } + + pub fn get_type(&self) -> ExpressionType { + ExpressionType { + return_type: self.return_type, + nullable: false, + source: SourceDefinition::Dynamic, + is_primary_key: false, + } + } + + pub fn evaluate( + &mut self, + record: &Record, + schema: &Schema, + ) -> Result { + let mut store = Store::new(&self.engine, ()); + let linker = Linker::new(&self.engine); + let instance = linker + .instantiate_and_start(&mut store, &self.module) + .map_err(|error| Error::Instantiate(error.to_string()))?; + let export = instance + .get_export(&store, &self.function_name) + .ok_or_else(|| Error::MissingFunction(self.function_name.clone()))?; + let Extern::Func(func) = export else { + return Err(Error::ExportNotFunction(self.function_name.clone()).into()); + }; + + let func_type = func.ty(&store); + let param_types = func_type.params().to_vec(); + if param_types.len() != self.args.len() { + return Err(Error::InvalidArgumentCount { + function_name: self.function_name.clone(), + expected: param_types.len(), + actual: self.args.len(), + } + .into()); + } + + let mut params = Vec::with_capacity(self.args.len()); + for (index, arg) in self.args.iter_mut().enumerate() { + let field = arg.evaluate(record, schema)?; + let value = field_to_wasm_value(&self.function_name, field)?; + if value.ty() != param_types[index] { + return Err(Error::InvalidArgumentType { + function_name: self.function_name.clone(), + index, + expected: param_types[index], + actual: value.ty(), + } + .into()); + } + params.push(value); + } + + let return_types = func_type.results().to_vec(); + if return_types.len() != 1 { + return Err(Error::InvalidReturnCount { + function_name: self.function_name.clone(), + actual: return_types.len(), + } + .into()); + } + + let expected_return_type = field_type_to_wasm_type(&self.function_name, self.return_type)?; + if return_types[0] != expected_return_type { + return Err(Error::InvalidReturnType { + function_name: self.function_name.clone(), + expected: expected_return_type, + actual: return_types[0], + } + .into()); + } + + let mut results = vec![Val::default(expected_return_type)]; + func.call(&mut store, ¶ms, &mut results) + .map_err(|source| Error::Call { + function_name: self.function_name.clone(), + source, + })?; + + wasm_value_to_field(&self.function_name, self.return_type, results.remove(0)) + .map_err(Into::into) + } + + pub fn to_string(&self, schema: &Schema) -> String { + format!( + "{}({})", + self.udf_name, + self.args + .iter() + .map(|expr| expr.to_string(schema)) + .collect::>() + .join(",") + ) + } +} + +fn field_to_wasm_value(function_name: &str, field: Field) -> Result { + match field { + Field::Int(value) => Ok(Val::I64(value)), + Field::UInt(value) => Ok(Val::I64(i64::try_from(value).map_err(|_| { + Error::UIntArgumentOutOfRange { + function_name: function_name.to_string(), + value, + } + })?)), + Field::Float(value) => Ok(Val::F64(F64::from(value.0))), + Field::Boolean(value) => Ok(Val::I32(i32::from(value))), + Field::Null => Err(Error::NullArgument { + function_name: function_name.to_string(), + }), + field => Err(Error::UnsupportedArgumentType { + function_name: function_name.to_string(), + field_type: field.ty().unwrap_or(FieldType::String), + }), + } +} + +fn field_type_to_wasm_type(function_name: &str, field_type: FieldType) -> Result { + match field_type { + FieldType::Int | FieldType::UInt => Ok(ValType::I64), + FieldType::Float => Ok(ValType::F64), + FieldType::Boolean => Ok(ValType::I32), + field_type => Err(Error::UnsupportedReturnType { + function_name: function_name.to_string(), + field_type, + }), + } +} + +fn wasm_value_to_field( + function_name: &str, + return_type: FieldType, + value: Val, +) -> Result { + match (return_type, value) { + (FieldType::Int, Val::I64(value)) => Ok(Field::Int(value)), + (FieldType::UInt, Val::I64(value)) => { + Ok(Field::UInt(u64::try_from(value).map_err(|_| { + Error::UIntReturnOutOfRange { + function_name: function_name.to_string(), + value, + } + })?)) + } + (FieldType::Float, Val::F64(value)) => Ok(Field::Float(OrderedFloat(value.to_float()))), + (FieldType::Boolean, Val::I32(value)) => Ok(Field::Boolean(value != 0)), + (return_type, value) => Err(Error::InvalidReturnType { + function_name: function_name.to_string(), + expected: field_type_to_wasm_type(function_name, return_type)?, + actual: value.ty(), + }), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use dozer_types::types::{FieldDefinition, Schema}; + + fn write_wasm_module(name: &str, wat_source: &str) -> String { + let wasm = wat::parse_str(wat_source).unwrap(); + let path = + std::env::temp_dir().join(format!("dozer-wasm-udf-{name}-{}.wasm", std::process::id())); + std::fs::write(&path, wasm).unwrap(); + path.to_string_lossy().to_string() + } + + fn schema(field_type: FieldType) -> Schema { + let mut schema = Schema::new(); + schema.field( + FieldDefinition::new( + "value".to_string(), + field_type, + false, + SourceDefinition::Dynamic, + ), + false, + ); + schema + } + + #[test] + fn evaluates_i64_udf() { + let module = write_wasm_module( + "add-one", + r#" + (module + (func (export "add_one") (param i64) (result i64) + local.get 0 + i64.const 1 + i64.add)) + "#, + ); + let mut udf = Udf::new( + "add_one".to_string(), + module, + None, + FieldType::Int, + vec![Expression::Column { index: 0 }], + ) + .unwrap(); + let schema = schema(FieldType::Int); + let record = Record::new(vec![Field::Int(41)]); + + assert_eq!(udf.evaluate(&record, &schema).unwrap(), Field::Int(42)); + } + + #[test] + fn evaluates_boolean_udf() { + let module = write_wasm_module( + "not", + r#" + (module + (func (export "not_value") (param i32) (result i32) + local.get 0 + i32.eqz)) + "#, + ); + let mut udf = Udf::new( + "not_value".to_string(), + module, + None, + FieldType::Boolean, + vec![Expression::Column { index: 0 }], + ) + .unwrap(); + let schema = schema(FieldType::Boolean); + let record = Record::new(vec![Field::Boolean(true)]); + + assert_eq!( + udf.evaluate(&record, &schema).unwrap(), + Field::Boolean(false) + ); + } + + #[test] + fn rejects_uint_argument_out_of_i64_range() { + let module = write_wasm_module( + "identity-uint", + r#" + (module + (func (export "identity") (param i64) (result i64) + local.get 0)) + "#, + ); + let mut udf = Udf::new( + "identity".to_string(), + module, + None, + FieldType::UInt, + vec![Expression::Column { index: 0 }], + ) + .unwrap(); + let schema = schema(FieldType::UInt); + let record = Record::new(vec![Field::UInt(i64::MAX as u64 + 1)]); + let error = udf.evaluate(&record, &schema).unwrap_err(); + + assert!(matches!( + error, + crate::error::Error::Wasm(Error::UIntArgumentOutOfRange { .. }) + )); + } + + #[test] + fn rejects_negative_uint_return_value() { + let module = write_wasm_module( + "negative-uint", + r#" + (module + (func (export "negative") (result i64) + i64.const -1)) + "#, + ); + let mut udf = Udf::new( + "negative".to_string(), + module, + None, + FieldType::UInt, + vec![], + ) + .unwrap(); + let schema = schema(FieldType::UInt); + let record = Record::new(vec![]); + let error = udf.evaluate(&record, &schema).unwrap_err(); + + assert!(matches!( + error, + crate::error::Error::Wasm(Error::UIntReturnOutOfRange { .. }) + )); + } + + #[test] + fn rejects_unsupported_return_type() { + let module = write_wasm_module( + "string-return", + r#" + (module + (func (export "return_string") (result i32) + i32.const 0)) + "#, + ); + let error = Udf::new( + "return_string".to_string(), + module, + None, + FieldType::String, + vec![], + ) + .unwrap_err(); + + assert!(matches!( + error, + Error::UnsupportedReturnType { + field_type: FieldType::String, + .. + } + )); + } +} diff --git a/dozer-tests/Cargo.toml b/dozer-tests/Cargo.toml index d37ff2face..5d00ea96cb 100644 --- a/dozer-tests/Cargo.toml +++ b/dozer-tests/Cargo.toml @@ -43,3 +43,4 @@ libtest-mimic = "0.6.1" [features] python = ["dozer-sql/python"] +wasm = ["dozer-sql/wasm"] diff --git a/dozer-types/src/models/udf_config.rs b/dozer-types/src/models/udf_config.rs index 512fba3a57..46b94b30d3 100644 --- a/dozer-types/src/models/udf_config.rs +++ b/dozer-types/src/models/udf_config.rs @@ -1,7 +1,6 @@ use schemars::JsonSchema; use crate::serde::{Deserialize, Serialize}; - #[derive(Debug, Serialize, Deserialize, JsonSchema, Eq, PartialEq, Clone)] #[serde(deny_unknown_fields)] pub struct UdfConfig { @@ -16,6 +15,7 @@ pub struct UdfConfig { pub enum UdfType { Onnx(OnnxConfig), JavaScript(JavaScriptConfig), + Wasm(WasmConfig), } #[derive(Debug, Serialize, Deserialize, JsonSchema, Eq, PartialEq, Clone)] @@ -31,3 +31,14 @@ pub struct JavaScriptConfig { /// path to the module file pub module: String, } + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct WasmConfig { + /// path to the WebAssembly module file + pub module: String, + /// exported function name. Defaults to the UDF name when omitted. + pub function: Option, + /// return type of the exported function + pub return_type: String, +} diff --git a/dozer-types/src/tests/udf_yaml_deserialize.rs b/dozer-types/src/tests/udf_yaml_deserialize.rs index 6cec087a5b..6ef4bc2ef7 100644 --- a/dozer-types/src/tests/udf_yaml_deserialize.rs +++ b/dozer-types/src/tests/udf_yaml_deserialize.rs @@ -1,4 +1,4 @@ -use crate::models::udf_config::{OnnxConfig, UdfConfig, UdfType}; +use crate::models::udf_config::{OnnxConfig, UdfConfig, UdfType, WasmConfig}; #[test] fn standard() { @@ -17,3 +17,46 @@ fn standard() { let expected = udf_conf; assert_eq!(expected, deserializer_result); } + +#[test] +fn wasm() { + let udf_config = r#" + name: is_fraudulent + config: !Wasm + module: ./models/fraud.wasm + function: score + return_type: boolean + "#; + let deserializer_result = serde_yaml::from_str::(udf_config).unwrap(); + let udf_conf = UdfConfig { + config: UdfType::Wasm(WasmConfig { + module: "./models/fraud.wasm".to_string(), + function: Some("score".to_string()), + return_type: "boolean".to_string(), + }), + name: "is_fraudulent".to_string(), + }; + let expected = udf_conf; + assert_eq!(expected, deserializer_result); +} + +#[test] +fn wasm_defaults_function_name() { + let udf_config = r#" + name: score + config: !Wasm + module: ./models/score.wasm + return_type: int + "#; + let deserializer_result = serde_yaml::from_str::(udf_config).unwrap(); + let udf_conf = UdfConfig { + config: UdfType::Wasm(WasmConfig { + module: "./models/score.wasm".to_string(), + function: None, + return_type: "int".to_string(), + }), + name: "score".to_string(), + }; + let expected = udf_conf; + assert_eq!(expected, deserializer_result); +}