diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index dd3d2dee121..202a67547d6 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: include: - - { name: x86_64 Linux, target: x86_64-unknown-linux-gnu, runner: bare-metal, container: 'debian:bookworm' } + - { name: x86_64 Linux, target: x86_64-unknown-linux-gnu, runner: ubuntu-latest } - { name: aarch64 Linux, target: aarch64-unknown-linux-gnu, runner: arm-runner } # Disabled because musl builds weren't working and we didn't want to investigate. See https://github.com/clockworklabs/SpacetimeDB/pull/2964. # - { name: x86_64 Linux musl, target: x86_64-unknown-linux-musl, runner: bare-metal, container: alpine } diff --git a/Cargo.lock b/Cargo.lock index ed929d1e017..256b886592c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,9 +63,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -186,6 +186,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ar_archive_writer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" +dependencies = [ + "object 0.32.2", +] + [[package]] name = "arbitrary" version = "1.4.2" @@ -249,7 +258,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -260,7 +269,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -443,7 +452,7 @@ name = "benchmarks-module" version = "0.1.0" dependencies = [ "anyhow", - "spacetimedb 1.6.0", + "spacetimedb 1.7.0", ] [[package]] @@ -505,7 +514,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -592,7 +601,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -680,7 +689,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -726,9 +735,9 @@ dependencies = [ [[package]] name = "calendrical_calculations" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c5d386a9f2c8b97e1a036420bcf937db4e5c9df33eb0232025008ced6104c0" +checksum = "3a0b39595c6ee54a8d0900204ba4c401d0ab4eb45adaf07178e8d017541529e7" dependencies = [ "core_maths", "displaydoc", @@ -783,9 +792,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.41" +version = "1.2.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" dependencies = [ "find-msvc-tools", "jobserver", @@ -921,9 +930,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.50" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive 4.5.49", @@ -935,14 +944,14 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2a2617956a06d4885b490697b5307ebb09fec10b088afc18c81762d848c2339" dependencies = [ - "clap 4.5.50", + "clap 4.5.51", ] [[package]] name = "clap_builder" -version = "4.5.50" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -973,7 +982,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1072,7 +1081,7 @@ dependencies = [ [[package]] name = "connect_disconnect_client" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "spacetimedb-sdk", @@ -1347,7 +1356,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.50", + "clap 4.5.51", "criterion-plot", "futures", "is-terminal", @@ -1549,7 +1558,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1560,7 +1569,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1614,9 +1623,9 @@ checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" [[package]] name = "deranged" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", "serde_core", @@ -1630,7 +1639,7 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1641,7 +1650,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1654,7 +1663,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1674,7 +1683,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", "unicode-xid", ] @@ -1716,7 +1725,7 @@ dependencies = [ "diplomat_core", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1736,7 +1745,7 @@ dependencies = [ "serde", "smallvec", "strck", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1755,16 +1764,7 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys 0.4.1", -] - -[[package]] -name = "dirs" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" -dependencies = [ - "dirs-sys 0.5.0", + "dirs-sys", ] [[package]] @@ -1775,22 +1775,10 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users 0.4.6", + "redox_users", "windows-sys 0.48.0", ] -[[package]] -name = "dirs-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" -dependencies = [ - "libc", - "option-ext", - "redox_users 0.5.2", - "windows-sys 0.61.2", -] - [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1798,7 +1786,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users 0.4.6", + "redox_users", "winapi", ] @@ -1810,7 +1798,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1903,6 +1891,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +[[package]] +name = "endian-type" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "869b0adbda23651a9c5c0c3d270aac9fcb52e8622a8f2b17e57802d7791962f2" + [[package]] name = "enum-as-inner" version = "0.6.1" @@ -1912,7 +1906,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1932,7 +1926,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1945,7 +1939,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1966,7 +1960,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -2143,9 +2137,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -2163,6 +2157,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -2299,7 +2299,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -2525,7 +2525,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", "rayon", "serde", ] @@ -2537,6 +2537,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" dependencies = [ "allocator-api2", + "equivalent", + "foldhash 0.2.0", ] [[package]] @@ -2616,11 +2618,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2844,7 +2846,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -2899,9 +2901,9 @@ dependencies = [ [[package]] name = "icu_calendar" -version = "2.0.5" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6c40ba6481ed7ddd358437af0f000eb9f661345845977d9d1b38e606374e1f" +checksum = "d6f0e52e009b6b16ba9c0693578796f2dd4aaa59a7f8f920423706714a89ac4e" dependencies = [ "calendrical_calculations", "displaydoc", @@ -2910,21 +2912,20 @@ dependencies = [ "icu_locale_core", "icu_provider", "tinystr", - "writeable", "zerovec", ] [[package]] name = "icu_calendar_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219c8639ab936713a87b571eed2bc2615aa9137e8af6eb221446ee5644acc18" +checksum = "527f04223b17edfe0bd43baf14a0cb1b017830db65f3950dc00224860a9a446d" [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -2935,11 +2936,10 @@ dependencies = [ [[package]] name = "icu_locale" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ae5921528335e91da1b6c695dbf1ec37df5ac13faa3f91e5640be93aa2fbefd" +checksum = "532b11722e350ab6bf916ba6eb0efe3ee54b932666afec989465f9243fe6dd60" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_locale_data", @@ -2951,12 +2951,13 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", + "serde", "tinystr", "writeable", "zerovec", @@ -2964,17 +2965,16 @@ dependencies = [ [[package]] name = "icu_locale_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fdef0c124749d06a743c69e938350816554eb63ac979166590e2b4ee4252765" +checksum = "f03e2fcaefecdf05619f3d6f91740e79ab969b4dd54f77cbf546b1d0d28e3147" [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -2985,42 +2985,40 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", + "serde", "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -3142,7 +3140,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96d2465363ed2d81857759fc864cf6bb7997f79327aec028d65bd7989393685" dependencies = [ "ahash 0.8.12", - "clap 4.5.50", + "clap 4.5.51", "crossbeam-channel", "crossbeam-utils", "dashmap 6.1.0", @@ -3238,20 +3236,20 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi 0.5.2", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -3306,12 +3304,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "ixdtf" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ef2d119044a672ceb96e59608dffe8677f29dc6ec48ed693a4b9ac84751e9b" -dependencies = [ - "displaydoc", -] +checksum = "84de9d95a6d2547d9b77ee3f25fa0ee32e3c3a6484d47a55adebc0439c077992" [[package]] name = "jemalloc_pprof" @@ -3370,9 +3365,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", @@ -3420,7 +3415,7 @@ name = "keynote-benchmarks" version = "0.1.0" dependencies = [ "log", - "spacetimedb 1.6.0", + "spacetimedb 1.7.0", ] [[package]] @@ -3445,9 +3440,9 @@ dependencies = [ [[package]] name = "lazy-regex" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" +checksum = "191898e17ddee19e60bccb3945aa02339e81edd4a8c50e21fd4d48cdecda7b29" dependencies = [ "lazy-regex-proc_macros", "once_cell", @@ -3456,14 +3451,14 @@ dependencies = [ [[package]] name = "lazy-regex-proc_macros" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" +checksum = "c35dc8b0da83d1a9507e12122c80dea71a9c7c613014347392483a83ea593e04" dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -3566,7 +3561,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d8de370f98a6cb8a4606618e53e802f93b094ddec0f96988eaec2c27e6e9ce7" dependencies = [ - "clap 4.5.50", + "clap 4.5.51", "termcolor", "threadpool", ] @@ -3603,9 +3598,9 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "lock_api" @@ -3732,9 +3727,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" dependencies = [ "libc", ] @@ -3800,7 +3795,7 @@ version = "0.0.0" dependencies = [ "anyhow", "log", - "spacetimedb 1.6.0", + "spacetimedb 1.7.0", ] [[package]] @@ -3837,7 +3832,7 @@ checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -4146,6 +4141,15 @@ dependencies = [ "objc2", ] +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "object" version = "0.36.7" @@ -4175,9 +4179,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "oorandom" @@ -4208,7 +4212,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -4219,9 +4223,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.3+3.5.4" +version = "300.5.4+3.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6bad8cd0233b63971e232cc9c5e83039375b8586d2312f31fda85db8f888c2" +checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" dependencies = [ "cc", ] @@ -4330,9 +4334,9 @@ dependencies = [ [[package]] name = "oxc-miette" -version = "2.5.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c42cefdcbebec6b0b72229ac0e02261a6770cb7ba39ccc5475a856164066db1" +checksum = "f02105a875f3751a0b44b4c822b01177728dd9049ae6fb419e9b04887d730ed1" dependencies = [ "cfg-if", "owo-colors 4.2.3", @@ -4345,13 +4349,13 @@ dependencies = [ [[package]] name = "oxc-miette-derive" -version = "2.5.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05bbaa5b6b98826bb62b164406f703bee72c5287af9986f9c863fa8ea992b476" +checksum = "003b4612827f6501183873fb0735da92157e3c7daa71c40921c7d2758fec2229" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -4395,7 +4399,7 @@ dependencies = [ "phf", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -4893,7 +4897,7 @@ name = "perf-test-module" version = "0.1.0" dependencies = [ "log", - "spacetimedb 1.6.0", + "spacetimedb 1.7.0", ] [[package]] @@ -4984,7 +4988,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5013,7 +5017,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5077,17 +5081,16 @@ dependencies = [ [[package]] name = "pnp" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a10a726fb86dab6571b148c0f52cf619a4aabf0ac4fcf578bd4cd2178fb0e6d0" +checksum = "7adbc1ab7344e1e77be663e91cb129e989e398c319df7a9b8dbda9dd6758df38" dependencies = [ "byteorder", "concurrent_lru", - "dirs 6.0.0", "fancy-regex", "miniz_oxide", "pathdiff", - "radix_trie", + "radix_trie 0.3.0", "rustc-hash", "serde", "serde_json", @@ -5121,7 +5124,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5160,11 +5163,12 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ - "serde", + "serde_core", + "writeable", "zerovec", ] @@ -5215,7 +5219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5253,9 +5257,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -5277,14 +5281,13 @@ dependencies = [ [[package]] name = "proptest" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb0be07becd10686a0bb407298fb425360a5c44a663774406340c59a22de4ce" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set", "bit-vec", "bitflags 2.10.0", - "lazy_static", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -5303,7 +5306,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5326,7 +5329,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5337,10 +5340,11 @@ checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "psm" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e66fcd288453b748497d8fb18bccc83a16b0518e3906d4b8df0a8d42d93dbb1c" +checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" dependencies = [ + "ar_archive_writer", "cc", ] @@ -5381,7 +5385,7 @@ checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5445,7 +5449,7 @@ name = "quickstart-chat-module" version = "0.1.0" dependencies = [ "log", - "spacetimedb 1.6.0", + "spacetimedb 1.7.0", ] [[package]] @@ -5475,7 +5479,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" dependencies = [ - "endian-type", + "endian-type 0.1.2", + "nibble_vec", +] + +[[package]] +name = "radix_trie" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b4431027dcd37fc2a73ef740b5f233aa805897935b8bce0195e41bbf9a3289a" +dependencies = [ + "endian-type 0.2.0", "nibble_vec", ] @@ -5642,17 +5656,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "redox_users" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror 2.0.17", -] - [[package]] name = "ref-cast" version = "1.0.25" @@ -5670,7 +5673,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5723,11 +5726,11 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "regress" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145bb27393fe455dd64d6cbc8d059adfa392590a45eadf079c01b11857e7b010" +checksum = "2057b2325e68a893284d1538021ab90279adac1139957ca2a74426c6f118fb48" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.16.0", "memchr", ] @@ -5916,7 +5919,7 @@ checksum = "bd83f5f173ff41e00337d97f6572e416d022ef8a19f371817259ae960324c482" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -6362,9 +6365,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.33" +version = "0.23.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "751e04a496ca00bb97a5e043158d23d66b5aabf2e1d5aa2a0aaebb1aafe6f82c" +checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7" dependencies = [ "once_cell", "rustls-pki-types", @@ -6384,18 +6387,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.7" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", @@ -6435,7 +6438,7 @@ dependencies = [ "log", "memchr", "nix 0.26.4", - "radix_trie", + "radix_trie 0.2.1", "scopeguard", "unicode-segmentation", "unicode-width 0.1.14", @@ -6535,12 +6538,12 @@ dependencies = [ "anyhow", "log", "paste", - "spacetimedb 1.6.0", + "spacetimedb 1.7.0", ] [[package]] name = "sdk-unreal-test-harness" -version = "1.6.0" +version = "1.7.0" dependencies = [ "serial_test", "spacetimedb-testing", @@ -6604,9 +6607,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" +checksum = "16c2f82143577edb4921b71ede051dac62ca3c16084e918bf7b40c96ae10eb33" [[package]] name = "semver" @@ -6651,7 +6654,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -6711,9 +6714,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.15.0" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5" +checksum = "aa66c845eee442168b2c8134fec70ac50dc20e760769c8ba0ad1319ca1959b04" dependencies = [ "base64 0.22.1", "chrono", @@ -6730,14 +6733,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.15.0" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27" +checksum = "b91a903660542fced4e99881aa481bdbaec1634568ee02e0b8bd57c64cb38955" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -6762,7 +6765,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -6852,9 +6855,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio 0.8.11", @@ -6974,68 +6977,68 @@ name = "spacetime-module" version = "0.1.0" dependencies = [ "log", - "spacetimedb 1.6.0", + "spacetimedb 1.7.0", ] [[package]] name = "spacetimedb" version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8679cf54a7a653e6bc612bef28c219f98b9274308d8aae1e86046ed685db48b1" dependencies = [ "bytemuck", "derive_more 0.99.20", "getrandom 0.2.16", - "insta", "log", "rand 0.8.5", "scoped-tls", - "serde_json", "spacetimedb-bindings-macro 1.6.0", "spacetimedb-bindings-sys 1.6.0", "spacetimedb-lib 1.6.0", "spacetimedb-primitives 1.6.0", - "trybuild", ] [[package]] name = "spacetimedb" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8679cf54a7a653e6bc612bef28c219f98b9274308d8aae1e86046ed685db48b1" +version = "1.7.0" dependencies = [ "bytemuck", "derive_more 0.99.20", "getrandom 0.2.16", + "insta", "log", "rand 0.8.5", "scoped-tls", - "spacetimedb-bindings-macro 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spacetimedb-bindings-sys 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spacetimedb-lib 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spacetimedb-primitives 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json", + "spacetimedb-bindings-macro 1.7.0", + "spacetimedb-bindings-sys 1.7.0", + "spacetimedb-lib 1.7.0", + "spacetimedb-primitives 1.7.0", + "trybuild", ] [[package]] name = "spacetimedb-auth" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "serde", "serde_json", "serde_with", "spacetimedb-jsonwebtoken", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", ] [[package]] name = "spacetimedb-bench" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "anymap", "byte-unit", - "clap 4.5.50", + "clap 4.5.51", "criterion", - "foldhash", + "foldhash 0.1.5", "futures", "iai-callgrind", "iai-callgrind-macros", @@ -7056,11 +7059,11 @@ dependencies = [ "spacetimedb-data-structures", "spacetimedb-datastore", "spacetimedb-execution", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-paths", - "spacetimedb-primitives 1.6.0", + "spacetimedb-primitives 1.7.0", "spacetimedb-query", - "spacetimedb-sats 1.6.0", + "spacetimedb-sats 1.7.0", "spacetimedb-schema", "spacetimedb-standalone", "spacetimedb-table", @@ -7076,60 +7079,60 @@ dependencies = [ [[package]] name = "spacetimedb-bindings-macro" version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a930242493f5c875ab96903eb40fb6a90c4d3ae99597fd51da569ff22769a03" dependencies = [ "heck 0.4.1", "humantime", "proc-macro2", "quote", "spacetimedb-primitives 1.6.0", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] name = "spacetimedb-bindings-macro" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a930242493f5c875ab96903eb40fb6a90c4d3ae99597fd51da569ff22769a03" +version = "1.7.0" dependencies = [ "heck 0.4.1", "humantime", "proc-macro2", "quote", - "spacetimedb-primitives 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 2.0.107", + "spacetimedb-primitives 1.7.0", + "syn 2.0.108", ] [[package]] name = "spacetimedb-bindings-sys" version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e565dfcdd2dc3f58e0178052ec1c8ce710b013482d4fa50b511ba786a2c3bc68" dependencies = [ "spacetimedb-primitives 1.6.0", ] [[package]] name = "spacetimedb-bindings-sys" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e565dfcdd2dc3f58e0178052ec1c8ce710b013482d4fa50b511ba786a2c3bc68" +version = "1.7.0" dependencies = [ - "spacetimedb-primitives 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spacetimedb-primitives 1.7.0", ] [[package]] name = "spacetimedb-cli" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "base64 0.21.7", "bytes", "cargo_metadata", "chrono", - "clap 4.5.50", + "clap 4.5.51", "clap-markdown", "colored", "convert_case 0.6.0", "dialoguer", - "dirs 5.0.1", + "dirs", "duct", "email_address", "flate2", @@ -7160,9 +7163,9 @@ dependencies = [ "spacetimedb-data-structures", "spacetimedb-fs-utils", "spacetimedb-jsonwebtoken", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-paths", - "spacetimedb-primitives 1.6.0", + "spacetimedb-primitives 1.7.0", "spacetimedb-schema", "syntect", "tabled", @@ -7186,7 +7189,7 @@ dependencies = [ [[package]] name = "spacetimedb-client-api" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "async-stream", @@ -7223,7 +7226,7 @@ dependencies = [ "spacetimedb-data-structures", "spacetimedb-datastore", "spacetimedb-jsonwebtoken", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-paths", "spacetimedb-schema", "tempfile", @@ -7240,7 +7243,7 @@ dependencies = [ [[package]] name = "spacetimedb-client-api-messages" -version = "1.6.0" +version = "1.7.0" dependencies = [ "bytes", "bytestring", @@ -7254,16 +7257,16 @@ dependencies = [ "serde_json", "serde_with", "smallvec", - "spacetimedb-lib 1.6.0", - "spacetimedb-primitives 1.6.0", - "spacetimedb-sats 1.6.0", + "spacetimedb-lib 1.7.0", + "spacetimedb-primitives 1.7.0", + "spacetimedb-sats 1.7.0", "strum", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-codegen" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -7272,15 +7275,15 @@ dependencies = [ "itertools 0.12.1", "regex", "spacetimedb-data-structures", - "spacetimedb-lib 1.6.0", - "spacetimedb-primitives 1.6.0", + "spacetimedb-lib 1.7.0", + "spacetimedb-primitives 1.7.0", "spacetimedb-schema", "spacetimedb-testing", ] [[package]] name = "spacetimedb-commitlog" -version = "1.6.0" +version = "1.7.0" dependencies = [ "async-stream", "bitflags 2.10.0", @@ -7300,8 +7303,8 @@ dependencies = [ "spacetimedb-commitlog", "spacetimedb-fs-utils", "spacetimedb-paths", - "spacetimedb-primitives 1.6.0", - "spacetimedb-sats 1.6.0", + "spacetimedb-primitives 1.7.0", + "spacetimedb-sats 1.7.0", "tempfile", "thiserror 1.0.69", "tokio", @@ -7312,7 +7315,7 @@ dependencies = [ [[package]] name = "spacetimedb-core" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "arrayvec", @@ -7332,7 +7335,7 @@ dependencies = [ "crossbeam-channel", "crossbeam-queue", "derive_more 0.99.20", - "dirs 5.0.1", + "dirs", "enum-as-inner", "enum-map", "env_logger 0.10.2", @@ -7392,14 +7395,14 @@ dependencies = [ "spacetimedb-fs-utils", "spacetimedb-jsonwebtoken", "spacetimedb-jwks", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-memory-usage", "spacetimedb-metrics", "spacetimedb-paths", "spacetimedb-physical-plan", - "spacetimedb-primitives 1.6.0", + "spacetimedb-primitives 1.7.0", "spacetimedb-query", - "spacetimedb-sats 1.6.0", + "spacetimedb-sats 1.7.0", "spacetimedb-schema", "spacetimedb-snapshot", "spacetimedb-subscription", @@ -7434,7 +7437,7 @@ dependencies = [ [[package]] name = "spacetimedb-data-structures" -version = "1.6.0" +version = "1.7.0" dependencies = [ "ahash 0.8.12", "crossbeam-queue", @@ -7448,7 +7451,7 @@ dependencies = [ [[package]] name = "spacetimedb-datastore" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "bytes", @@ -7468,11 +7471,11 @@ dependencies = [ "spacetimedb-data-structures", "spacetimedb-durability", "spacetimedb-execution", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-metrics", "spacetimedb-paths", - "spacetimedb-primitives 1.6.0", - "spacetimedb-sats 1.6.0", + "spacetimedb-primitives 1.7.0", + "spacetimedb-sats 1.7.0", "spacetimedb-schema", "spacetimedb-snapshot", "spacetimedb-table", @@ -7483,14 +7486,14 @@ dependencies = [ [[package]] name = "spacetimedb-durability" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "itertools 0.12.1", "log", "spacetimedb-commitlog", "spacetimedb-paths", - "spacetimedb-sats 1.6.0", + "spacetimedb-sats 1.7.0", "thiserror 1.0.69", "tokio", "tracing", @@ -7498,32 +7501,32 @@ dependencies = [ [[package]] name = "spacetimedb-execution" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "itertools 0.12.1", "spacetimedb-expr", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-physical-plan", - "spacetimedb-primitives 1.6.0", - "spacetimedb-sats 1.6.0", + "spacetimedb-primitives 1.7.0", + "spacetimedb-sats 1.7.0", "spacetimedb-sql-parser", "spacetimedb-table", ] [[package]] name = "spacetimedb-expr" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "bigdecimal", "derive_more 0.99.20", "ethnum", "pretty_assertions", - "spacetimedb 1.6.0", - "spacetimedb-lib 1.6.0", - "spacetimedb-primitives 1.6.0", - "spacetimedb-sats 1.6.0", + "spacetimedb 1.7.0", + "spacetimedb-lib 1.7.0", + "spacetimedb-primitives 1.7.0", + "spacetimedb-sats 1.7.0", "spacetimedb-schema", "spacetimedb-sql-parser", "thiserror 1.0.69", @@ -7531,7 +7534,7 @@ dependencies = [ [[package]] name = "spacetimedb-fs-utils" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "hex", @@ -7574,26 +7577,18 @@ dependencies = [ [[package]] name = "spacetimedb-lib" version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ba57c8f1983bb144ee7e1ff28aa5882ca437f67c14a14daad4bf61d31f7040d" dependencies = [ "anyhow", "bitflags 2.10.0", "blake3", - "bytes", "chrono", "derive_more 0.99.20", "enum-as-inner", - "enum-map", "hex", - "insta", "itertools 0.12.1", - "proptest", - "proptest-derive", - "ron", - "serde", - "serde_json", "spacetimedb-bindings-macro 1.6.0", - "spacetimedb-memory-usage", - "spacetimedb-metrics", "spacetimedb-primitives 1.6.0", "spacetimedb-sats 1.6.0", "thiserror 1.0.69", @@ -7601,27 +7596,35 @@ dependencies = [ [[package]] name = "spacetimedb-lib" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba57c8f1983bb144ee7e1ff28aa5882ca437f67c14a14daad4bf61d31f7040d" +version = "1.7.0" dependencies = [ "anyhow", "bitflags 2.10.0", "blake3", + "bytes", "chrono", "derive_more 0.99.20", "enum-as-inner", + "enum-map", "hex", + "insta", "itertools 0.12.1", - "spacetimedb-bindings-macro 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spacetimedb-primitives 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spacetimedb-sats 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proptest", + "proptest-derive", + "ron", + "serde", + "serde_json", + "spacetimedb-bindings-macro 1.7.0", + "spacetimedb-memory-usage", + "spacetimedb-metrics", + "spacetimedb-primitives 1.7.0", + "spacetimedb-sats 1.7.0", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-memory-usage" -version = "1.6.0" +version = "1.7.0" dependencies = [ "decorum", "ethnum", @@ -7631,7 +7634,7 @@ dependencies = [ [[package]] name = "spacetimedb-metrics" -version = "1.6.0" +version = "1.7.0" dependencies = [ "arrayvec", "itertools 0.12.1", @@ -7641,11 +7644,11 @@ dependencies = [ [[package]] name = "spacetimedb-paths" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "chrono", - "dirs 5.0.1", + "dirs", "fs2", "itoa", "junction", @@ -7657,7 +7660,7 @@ dependencies = [ [[package]] name = "spacetimedb-pg" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "async-trait", @@ -7668,22 +7671,22 @@ dependencies = [ "pgwire", "spacetimedb-client-api", "spacetimedb-client-api-messages", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "thiserror 1.0.69", "tokio", ] [[package]] name = "spacetimedb-physical-plan" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "derive_more 0.99.20", "either", "pretty_assertions", "spacetimedb-expr", - "spacetimedb-lib 1.6.0", - "spacetimedb-primitives 1.6.0", + "spacetimedb-lib 1.7.0", + "spacetimedb-primitives 1.7.0", "spacetimedb-schema", "spacetimedb-sql-parser", "spacetimedb-table", @@ -7692,31 +7695,30 @@ dependencies = [ [[package]] name = "spacetimedb-primitives" version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dcd64c6970ca59e7b71e51952cf1a71bae2652b1eb736c19a7e528f3874894a" dependencies = [ "bitflags 2.10.0", "either", - "enum-as-inner", "itertools 0.12.1", "nohash-hasher", - "proptest", - "spacetimedb-memory-usage", ] [[package]] name = "spacetimedb-primitives" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dcd64c6970ca59e7b71e51952cf1a71bae2652b1eb736c19a7e528f3874894a" +version = "1.7.0" dependencies = [ "bitflags 2.10.0", "either", "itertools 0.12.1", "nohash-hasher", + "proptest", + "spacetimedb-memory-usage", ] [[package]] name = "spacetimedb-query" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "itertools 0.12.1", @@ -7724,9 +7726,9 @@ dependencies = [ "spacetimedb-client-api-messages", "spacetimedb-execution", "spacetimedb-expr", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-physical-plan", - "spacetimedb-primitives 1.6.0", + "spacetimedb-primitives 1.7.0", "spacetimedb-sql-parser", "spacetimedb-table", ] @@ -7734,15 +7736,14 @@ dependencies = [ [[package]] name = "spacetimedb-sats" version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae61d8f88bda21f56c143bc9b4ddc20100ff08ae4fed0b9444509f7c3ca4339" dependencies = [ - "ahash 0.8.12", "anyhow", "arrayvec", "bitflags 2.10.0", - "blake3", "bytemuck", "bytes", - "bytestring", "chrono", "decorum", "derive_more 0.99.20", @@ -7750,32 +7751,26 @@ dependencies = [ "ethnum", "hex", "itertools 0.12.1", - "proptest", - "proptest-derive", - "rand 0.9.2", "second-stack", - "serde", - "serde_json", "sha3", "smallvec", "spacetimedb-bindings-macro 1.6.0", - "spacetimedb-memory-usage", - "spacetimedb-metrics", "spacetimedb-primitives 1.6.0", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-sats" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae61d8f88bda21f56c143bc9b4ddc20100ff08ae4fed0b9444509f7c3ca4339" +version = "1.7.0" dependencies = [ + "ahash 0.8.12", "anyhow", "arrayvec", "bitflags 2.10.0", + "blake3", "bytemuck", "bytes", + "bytestring", "chrono", "decorum", "derive_more 0.99.20", @@ -7783,17 +7778,24 @@ dependencies = [ "ethnum", "hex", "itertools 0.12.1", + "proptest", + "proptest-derive", + "rand 0.9.2", "second-stack", + "serde", + "serde_json", "sha3", "smallvec", - "spacetimedb-bindings-macro 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spacetimedb-primitives 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spacetimedb-bindings-macro 1.7.0", + "spacetimedb-memory-usage", + "spacetimedb-metrics", + "spacetimedb-primitives 1.7.0", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-schema" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "derive_more 0.99.20", @@ -7810,9 +7812,9 @@ dependencies = [ "serial_test", "smallvec", "spacetimedb-data-structures", - "spacetimedb-lib 1.6.0", - "spacetimedb-primitives 1.6.0", - "spacetimedb-sats 1.6.0", + "spacetimedb-lib 1.7.0", + "spacetimedb-primitives 1.7.0", + "spacetimedb-sats 1.7.0", "spacetimedb-sql-parser", "spacetimedb-testing", "termcolor", @@ -7823,7 +7825,7 @@ dependencies = [ [[package]] name = "spacetimedb-sdk" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anymap", "base64 0.21.7", @@ -7842,9 +7844,9 @@ dependencies = [ "rand 0.9.2", "spacetimedb-client-api-messages", "spacetimedb-data-structures", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-metrics", - "spacetimedb-sats 1.6.0", + "spacetimedb-sats 1.7.0", "spacetimedb-testing", "thiserror 1.0.69", "tokio", @@ -7853,7 +7855,7 @@ dependencies = [ [[package]] name = "spacetimedb-snapshot" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "blake3", @@ -7870,10 +7872,10 @@ dependencies = [ "spacetimedb-datastore", "spacetimedb-durability", "spacetimedb-fs-utils", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-paths", - "spacetimedb-primitives 1.6.0", - "spacetimedb-sats 1.6.0", + "spacetimedb-primitives 1.7.0", + "spacetimedb-sats 1.7.0", "spacetimedb-schema", "spacetimedb-table", "tempfile", @@ -7886,23 +7888,23 @@ dependencies = [ [[package]] name = "spacetimedb-sql-parser" -version = "1.6.0" +version = "1.7.0" dependencies = [ "derive_more 0.99.20", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "sqlparser", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-standalone" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "async-trait", "axum", - "clap 4.5.50", - "dirs 5.0.1", + "clap 4.5.51", + "dirs", "futures", "hostname", "http 1.3.1", @@ -7920,7 +7922,7 @@ dependencies = [ "spacetimedb-client-api-messages", "spacetimedb-core", "spacetimedb-datastore", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-paths", "spacetimedb-pg", "spacetimedb-schema", @@ -7937,20 +7939,20 @@ dependencies = [ [[package]] name = "spacetimedb-subscription" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "spacetimedb-execution", "spacetimedb-expr", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-physical-plan", - "spacetimedb-primitives 1.6.0", + "spacetimedb-primitives 1.7.0", "spacetimedb-query", ] [[package]] name = "spacetimedb-table" -version = "1.6.0" +version = "1.7.0" dependencies = [ "ahash 0.8.12", "blake3", @@ -7966,20 +7968,20 @@ dependencies = [ "rand 0.9.2", "smallvec", "spacetimedb-data-structures", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-memory-usage", - "spacetimedb-primitives 1.6.0", - "spacetimedb-sats 1.6.0", + "spacetimedb-primitives 1.7.0", + "spacetimedb-sats 1.7.0", "spacetimedb-schema", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-testing" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", - "clap 4.5.50", + "clap 4.5.51", "duct", "env_logger 0.10.2", "lazy_static", @@ -7992,7 +7994,7 @@ dependencies = [ "spacetimedb-client-api", "spacetimedb-core", "spacetimedb-data-structures", - "spacetimedb-lib 1.6.0", + "spacetimedb-lib 1.7.0", "spacetimedb-paths", "spacetimedb-schema", "spacetimedb-standalone", @@ -8003,11 +8005,11 @@ dependencies = [ [[package]] name = "spacetimedb-update" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "bytes", - "clap 4.5.50", + "clap 4.5.51", "dialoguer", "flate2", "http-body-util", @@ -8028,7 +8030,7 @@ dependencies = [ [[package]] name = "spacetimedb-vm" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "arrayvec", @@ -8038,9 +8040,9 @@ dependencies = [ "smallvec", "spacetimedb-data-structures", "spacetimedb-execution", - "spacetimedb-lib 1.6.0", - "spacetimedb-primitives 1.6.0", - "spacetimedb-sats 1.6.0", + "spacetimedb-lib 1.7.0", + "spacetimedb-primitives 1.7.0", + "spacetimedb-sats 1.7.0", "spacetimedb-schema", "spacetimedb-table", "tempfile", @@ -8119,12 +8121,12 @@ dependencies = [ [[package]] name = "sqltest" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "async-trait", "chrono", - "clap 4.5.50", + "clap 4.5.51", "console", "derive_more 0.99.20", "fs-err", @@ -8138,8 +8140,8 @@ dependencies = [ "rust_decimal", "spacetimedb-core", "spacetimedb-datastore", - "spacetimedb-lib 1.6.0", - "spacetimedb-sats 1.6.0", + "spacetimedb-lib 1.7.0", + "spacetimedb-sats 1.7.0", "spacetimedb-vm", "sqllogictest", "sqllogictest-engines", @@ -8250,7 +8252,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -8271,9 +8273,12 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sugar_path" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8230d5b8a65a6d4d4a7e5ee8dbdd9312ba447a8b8329689a390a0945d69b57ce" +checksum = "48abcb2199ce37819c20dc7a72dc09e3263a00e598ff5089fe5fda92e0f63c37" +dependencies = [ + "smallvec", +] [[package]] name = "syn" @@ -8288,9 +8293,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.107" +version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", @@ -8332,7 +8337,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -8447,9 +8452,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "target-triple" -version = "0.1.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790" +checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" [[package]] name = "tempdir" @@ -8513,7 +8518,7 @@ dependencies = [ [[package]] name = "test-client" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "env_logger 0.10.2", @@ -8525,7 +8530,7 @@ dependencies = [ [[package]] name = "test-counter" -version = "1.6.0" +version = "1.7.0" dependencies = [ "anyhow", "spacetimedb-data-structures", @@ -8574,7 +8579,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -8585,7 +8590,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -8682,11 +8687,12 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", + "serde_core", "zerovec", ] @@ -8740,7 +8746,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -9036,7 +9042,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -9163,9 +9169,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.112" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d66678374d835fe847e0dc8348fde2ceb5be4a7ec204437d8367f0d8df266a5" +checksum = "559b6a626c0815c942ac98d434746138b4f89ddd6a1b8cbb168c6845fb3376c5" dependencies = [ "glob", "serde", @@ -9194,7 +9200,7 @@ checksum = "ee6ff59666c9cbaec3533964505d39154dc4e0a56151fdea30a09ed0301f62e2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", "termcolor", ] @@ -9244,7 +9250,7 @@ version = "0.1.0" dependencies = [ "anyhow", "log", - "spacetimedb 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spacetimedb 1.6.0", ] [[package]] @@ -9253,14 +9259,14 @@ version = "0.1.0" dependencies = [ "anyhow", "log", - "spacetimedb 1.6.0", + "spacetimedb 1.7.0", ] [[package]] name = "tzif" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e762ac355f0c204d09ae644b3d59423d5ddfc5603997d60c8c56f24e429a9d" +checksum = "a0376dfa52cce372f3b095010fd064fb850a5d8fbfd5be8b0ffa3d64eeab5a5d" dependencies = [ "combine", ] @@ -9285,9 +9291,9 @@ checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-linebreak" @@ -9297,18 +9303,18 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" dependencies = [ "tinyvec", ] [[package]] name = "unicode-properties" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" [[package]] name = "unicode-segmentation" @@ -9352,7 +9358,7 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", - "clap 4.5.50", + "clap 4.5.51", "duct", "regex", "semver", @@ -9534,9 +9540,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", @@ -9545,25 +9551,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.107", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.54" +version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", @@ -9574,9 +9566,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9584,22 +9576,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.107", - "wasm-bindgen-backend", + "syn 2.0.108", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] @@ -9647,7 +9639,7 @@ checksum = "3961bf864c790b5a06939f8f36d2a1a6be5bf0f926ddc25fb159b1766f2874db" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", "synstructure 0.13.2", "thiserror 1.0.69", ] @@ -9761,7 +9753,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", "wasmtime-component-util", "wasmtime-wit-bindgen", "wit-parser", @@ -9878,7 +9870,7 @@ checksum = "6879a8e168aef3fe07335343b7fbede12fa494215e83322e173d4018e124a846" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -9895,9 +9887,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.81" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", @@ -10094,7 +10086,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -10105,7 +10097,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -10553,9 +10545,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wyz" @@ -10635,11 +10627,10 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -10647,13 +10638,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", "synstructure 0.13.2", ] @@ -10674,7 +10665,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -10694,7 +10685,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", "synstructure 0.13.2", ] @@ -10715,14 +10706,14 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -10731,10 +10722,11 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ + "serde", "yoke", "zerofrom", "zerovec-derive", @@ -10742,13 +10734,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -10783,9 +10775,9 @@ dependencies = [ [[package]] name = "zopfli" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" dependencies = [ "bumpalo", "crc32fast", diff --git a/Cargo.toml b/Cargo.toml index 5bb1d180aed..d1dc2344c4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,44 +89,44 @@ inherits = "release" debug = true [workspace.package] -version = "1.6.0" +version = "1.7.0" edition = "2021" # update rust-toolchain.toml too! rust-version = "1.90.0" [workspace.dependencies] -spacetimedb = { path = "crates/bindings", version = "=1.6.0" } -spacetimedb-auth = { path = "crates/auth", version = "=1.6.0" } -spacetimedb-bindings-macro = { path = "crates/bindings-macro", version = "=1.6.0" } -spacetimedb-bindings-sys = { path = "crates/bindings-sys", version = "=1.6.0" } -spacetimedb-cli = { path = "crates/cli", version = "=1.6.0" } -spacetimedb-client-api = { path = "crates/client-api", version = "=1.6.0" } -spacetimedb-client-api-messages = { path = "crates/client-api-messages", version = "=1.6.0" } -spacetimedb-codegen = { path = "crates/codegen", version = "=1.6.0" } -spacetimedb-commitlog = { path = "crates/commitlog", version = "=1.6.0" } -spacetimedb-core = { path = "crates/core", version = "=1.6.0" } -spacetimedb-data-structures = { path = "crates/data-structures", version = "=1.6.0" } -spacetimedb-datastore = { path = "crates/datastore", version = "=1.6.0" } -spacetimedb-durability = { path = "crates/durability", version = "=1.6.0" } -spacetimedb-execution = { path = "crates/execution", version = "=1.6.0" } -spacetimedb-expr = { path = "crates/expr", version = "=1.6.0" } -spacetimedb-lib = { path = "crates/lib", default-features = false, version = "=1.6.0" } -spacetimedb-memory-usage = { path = "crates/memory-usage", version = "=1.6.0", default-features = false } -spacetimedb-metrics = { path = "crates/metrics", version = "=1.6.0" } -spacetimedb-paths = { path = "crates/paths", version = "=1.6.0" } -spacetimedb-pg = { path = "crates/pg", version = "=1.6.0" } -spacetimedb-physical-plan = { path = "crates/physical-plan", version = "=1.6.0" } -spacetimedb-primitives = { path = "crates/primitives", version = "=1.6.0" } -spacetimedb-query = { path = "crates/query", version = "=1.6.0" } -spacetimedb-sats = { path = "crates/sats", version = "=1.6.0" } -spacetimedb-schema = { path = "crates/schema", version = "=1.6.0" } -spacetimedb-standalone = { path = "crates/standalone", version = "=1.6.0" } -spacetimedb-sql-parser = { path = "crates/sql-parser", version = "=1.6.0" } -spacetimedb-table = { path = "crates/table", version = "=1.6.0" } -spacetimedb-vm = { path = "crates/vm", version = "=1.6.0" } -spacetimedb-fs-utils = { path = "crates/fs-utils", version = "=1.6.0" } -spacetimedb-snapshot = { path = "crates/snapshot", version = "=1.6.0" } -spacetimedb-subscription = { path = "crates/subscription", version = "=1.6.0" } +spacetimedb = { path = "crates/bindings", version = "=1.7.0" } +spacetimedb-auth = { path = "crates/auth", version = "=1.7.0" } +spacetimedb-bindings-macro = { path = "crates/bindings-macro", version = "=1.7.0" } +spacetimedb-bindings-sys = { path = "crates/bindings-sys", version = "=1.7.0" } +spacetimedb-cli = { path = "crates/cli", version = "=1.7.0" } +spacetimedb-client-api = { path = "crates/client-api", version = "=1.7.0" } +spacetimedb-client-api-messages = { path = "crates/client-api-messages", version = "=1.7.0" } +spacetimedb-codegen = { path = "crates/codegen", version = "=1.7.0" } +spacetimedb-commitlog = { path = "crates/commitlog", version = "=1.7.0" } +spacetimedb-core = { path = "crates/core", version = "=1.7.0" } +spacetimedb-data-structures = { path = "crates/data-structures", version = "=1.7.0" } +spacetimedb-datastore = { path = "crates/datastore", version = "=1.7.0" } +spacetimedb-durability = { path = "crates/durability", version = "=1.7.0" } +spacetimedb-execution = { path = "crates/execution", version = "=1.7.0" } +spacetimedb-expr = { path = "crates/expr", version = "=1.7.0" } +spacetimedb-lib = { path = "crates/lib", default-features = false, version = "=1.7.0" } +spacetimedb-memory-usage = { path = "crates/memory-usage", version = "=1.7.0", default-features = false } +spacetimedb-metrics = { path = "crates/metrics", version = "=1.7.0" } +spacetimedb-paths = { path = "crates/paths", version = "=1.7.0" } +spacetimedb-pg = { path = "crates/pg", version = "=1.7.0" } +spacetimedb-physical-plan = { path = "crates/physical-plan", version = "=1.7.0" } +spacetimedb-primitives = { path = "crates/primitives", version = "=1.7.0" } +spacetimedb-query = { path = "crates/query", version = "=1.7.0" } +spacetimedb-sats = { path = "crates/sats", version = "=1.7.0" } +spacetimedb-schema = { path = "crates/schema", version = "=1.7.0" } +spacetimedb-standalone = { path = "crates/standalone", version = "=1.7.0" } +spacetimedb-sql-parser = { path = "crates/sql-parser", version = "=1.7.0" } +spacetimedb-table = { path = "crates/table", version = "=1.7.0" } +spacetimedb-vm = { path = "crates/vm", version = "=1.7.0" } +spacetimedb-fs-utils = { path = "crates/fs-utils", version = "=1.7.0" } +spacetimedb-snapshot = { path = "crates/snapshot", version = "=1.7.0" } +spacetimedb-subscription = { path = "crates/subscription", version = "=1.7.0" } # Prevent `ahash` from pulling in `getrandom` by disabling default features. # Modules use `getrandom02` and we need to prevent an incompatible version diff --git a/LICENSE.txt b/LICENSE.txt index 8471788dae9..40f9d2c9477 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -5,7 +5,7 @@ Business Source License 1.1 Parameters Licensor: Clockwork Laboratories, Inc. -Licensed Work: SpacetimeDB 1.6.0 +Licensed Work: SpacetimeDB 1.7.0 The Licensed Work is (c) 2023 Clockwork Laboratories, Inc. @@ -21,7 +21,7 @@ Additional Use Grant: You may make use of the Licensed Work provided your Licensed Work by creating tables whose schemas are controlled by such third parties. -Change Date: 2030-10-15 +Change Date: 2030-10-31 Change License: GNU Affero General Public License v3.0 with a linking exception diff --git a/crates/bindings-csharp/BSATN.Codegen/BSATN.Codegen.csproj b/crates/bindings-csharp/BSATN.Codegen/BSATN.Codegen.csproj index d7c8b7aeeec..5784a4da6be 100644 --- a/crates/bindings-csharp/BSATN.Codegen/BSATN.Codegen.csproj +++ b/crates/bindings-csharp/BSATN.Codegen/BSATN.Codegen.csproj @@ -2,7 +2,7 @@ SpacetimeDB.BSATN.Codegen - 1.6.0 + 1.7.0 SpacetimeDB BSATN Codegen The SpacetimeDB BSATN Codegen implements the Roslyn incremental generators for BSATN serialization/deserialization in C#. diff --git a/crates/bindings-csharp/BSATN.Runtime/BSATN.Runtime.csproj b/crates/bindings-csharp/BSATN.Runtime/BSATN.Runtime.csproj index c4c991b73c3..97f12a6bfef 100644 --- a/crates/bindings-csharp/BSATN.Runtime/BSATN.Runtime.csproj +++ b/crates/bindings-csharp/BSATN.Runtime/BSATN.Runtime.csproj @@ -2,7 +2,7 @@ SpacetimeDB.BSATN.Runtime - 1.6.0 + 1.7.0 SpacetimeDB BSATN Runtime The SpacetimeDB BSATN Runtime implements APIs for BSATN serialization/deserialization in C#. true diff --git a/crates/bindings-csharp/Codegen/Codegen.csproj b/crates/bindings-csharp/Codegen/Codegen.csproj index 33a6772f134..aedc4de1c9b 100644 --- a/crates/bindings-csharp/Codegen/Codegen.csproj +++ b/crates/bindings-csharp/Codegen/Codegen.csproj @@ -2,7 +2,7 @@ SpacetimeDB.Codegen - 1.6.0 + 1.7.0 SpacetimeDB Module Codegen The SpacetimeDB Codegen implements the Roslyn incremental generators for writing SpacetimeDB modules in C#. diff --git a/crates/bindings-csharp/Runtime/Internal/Autogen/RawMiscModuleExportV9.g.cs b/crates/bindings-csharp/Runtime/Internal/Autogen/RawMiscModuleExportV9.g.cs index ca42e53f391..b2887f2b49e 100644 --- a/crates/bindings-csharp/Runtime/Internal/Autogen/RawMiscModuleExportV9.g.cs +++ b/crates/bindings-csharp/Runtime/Internal/Autogen/RawMiscModuleExportV9.g.cs @@ -10,7 +10,6 @@ namespace SpacetimeDB.Internal [SpacetimeDB.Type] public partial record RawMiscModuleExportV9 : SpacetimeDB.TaggedEnum<( RawColumnDefaultValueV9 ColumnDefaultValue, - RawProcedureDefV9 Procedure, - RawViewDefV9 View + SpacetimeDB.Unit _Reserved )>; } diff --git a/crates/bindings-csharp/Runtime/Internal/Autogen/RawProcedureDefV9.g.cs b/crates/bindings-csharp/Runtime/Internal/Autogen/RawProcedureDefV9.g.cs deleted file mode 100644 index b286db08c11..00000000000 --- a/crates/bindings-csharp/Runtime/Internal/Autogen/RawProcedureDefV9.g.cs +++ /dev/null @@ -1,41 +0,0 @@ -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. - -#nullable enable - -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; - -namespace SpacetimeDB.Internal -{ - [SpacetimeDB.Type] - [DataContract] - public sealed partial class RawProcedureDefV9 - { - [DataMember(Name = "name")] - public string Name; - [DataMember(Name = "params")] - public List Params; - [DataMember(Name = "return_type")] - public SpacetimeDB.BSATN.AlgebraicType ReturnType; - - public RawProcedureDefV9( - string Name, - List Params, - SpacetimeDB.BSATN.AlgebraicType ReturnType - ) - { - this.Name = Name; - this.Params = Params; - this.ReturnType = ReturnType; - } - - public RawProcedureDefV9() - { - this.Name = ""; - this.Params = new(); - this.ReturnType = null!; - } - } -} diff --git a/crates/bindings-csharp/Runtime/Internal/Autogen/RawViewDefV9.g.cs b/crates/bindings-csharp/Runtime/Internal/Autogen/RawViewDefV9.g.cs deleted file mode 100644 index e7fb7c3f5ad..00000000000 --- a/crates/bindings-csharp/Runtime/Internal/Autogen/RawViewDefV9.g.cs +++ /dev/null @@ -1,49 +0,0 @@ -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. - -#nullable enable - -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; - -namespace SpacetimeDB.Internal -{ - [SpacetimeDB.Type] - [DataContract] - public sealed partial class RawViewDefV9 - { - [DataMember(Name = "name")] - public string Name; - [DataMember(Name = "is_public")] - public bool IsPublic; - [DataMember(Name = "is_anonymous")] - public bool IsAnonymous; - [DataMember(Name = "params")] - public List Params; - [DataMember(Name = "return_type")] - public SpacetimeDB.BSATN.AlgebraicType ReturnType; - - public RawViewDefV9( - string Name, - bool IsPublic, - bool IsAnonymous, - List Params, - SpacetimeDB.BSATN.AlgebraicType ReturnType - ) - { - this.Name = Name; - this.IsPublic = IsPublic; - this.IsAnonymous = IsAnonymous; - this.Params = Params; - this.ReturnType = ReturnType; - } - - public RawViewDefV9() - { - this.Name = ""; - this.Params = new(); - this.ReturnType = null!; - } - } -} diff --git a/crates/bindings-csharp/Runtime/Runtime.csproj b/crates/bindings-csharp/Runtime/Runtime.csproj index 00a5bc43391..5e68bff2729 100644 --- a/crates/bindings-csharp/Runtime/Runtime.csproj +++ b/crates/bindings-csharp/Runtime/Runtime.csproj @@ -2,7 +2,7 @@ SpacetimeDB.Runtime - 1.6.0 + 1.7.0 SpacetimeDB Module Runtime The SpacetimeDB Runtime implements the database runtime bindings for writing SpacetimeDB modules in C#. diff --git a/crates/bindings-macro/src/lib.rs b/crates/bindings-macro/src/lib.rs index c82ac7429df..cd85b80ef65 100644 --- a/crates/bindings-macro/src/lib.rs +++ b/crates/bindings-macro/src/lib.rs @@ -8,12 +8,10 @@ // // (private documentation for the macro authors is totally fine here and you SHOULD write that!) -mod procedure; mod reducer; mod sats; mod table; mod util; -mod view; use proc_macro::TokenStream as StdTokenStream; use proc_macro2::TokenStream; @@ -106,14 +104,6 @@ mod sym { } } -#[proc_macro_attribute] -pub fn procedure(args: StdTokenStream, item: StdTokenStream) -> StdTokenStream { - cvt_attr::(args, item, quote!(), |args, original_function| { - let args = procedure::ProcedureArgs::parse(args)?; - procedure::procedure_impl(args, original_function) - }) -} - #[proc_macro_attribute] pub fn reducer(args: StdTokenStream, item: StdTokenStream) -> StdTokenStream { cvt_attr::(args, item, quote!(), |args, original_function| { @@ -122,14 +112,6 @@ pub fn reducer(args: StdTokenStream, item: StdTokenStream) -> StdTokenStream { }) } -#[proc_macro_attribute] -pub fn view(args: StdTokenStream, item: StdTokenStream) -> StdTokenStream { - cvt_attr::(args, item, quote!(), |args, original_function| { - let args = view::ViewArgs::parse(args, &original_function.sig.ident)?; - view::view_impl(args, original_function) - }) -} - /// It turns out to be shockingly difficult to construct an [`Attribute`]. /// That type is not [`Parse`], instead having two distinct methods /// for parsing "inner" vs "outer" attributes. diff --git a/crates/bindings-macro/src/procedure.rs b/crates/bindings-macro/src/procedure.rs deleted file mode 100644 index 0f4013d96ae..00000000000 --- a/crates/bindings-macro/src/procedure.rs +++ /dev/null @@ -1,130 +0,0 @@ -use crate::reducer::{assert_only_lifetime_generics, extract_typed_args}; -use crate::sym; -use crate::util::{check_duplicate, ident_to_litstr, match_meta}; -use proc_macro2::TokenStream; -use quote::quote; -use syn::parse::Parser as _; -use syn::{ItemFn, LitStr}; - -#[derive(Default)] -pub(crate) struct ProcedureArgs { - /// For consistency with reducers: allow specifying a different export name than the Rust function name. - name: Option, -} - -impl ProcedureArgs { - pub(crate) fn parse(input: TokenStream) -> syn::Result { - let mut args = Self::default(); - syn::meta::parser(|meta| { - match_meta!(match meta { - sym::name => { - check_duplicate(&args.name, &meta)?; - args.name = Some(meta.value()?.parse()?); - } - }); - Ok(()) - }) - .parse2(input)?; - Ok(args) - } -} - -pub(crate) fn procedure_impl(args: ProcedureArgs, original_function: &ItemFn) -> syn::Result { - let func_name = &original_function.sig.ident; - let vis = &original_function.vis; - - let procedure_name = args.name.unwrap_or_else(|| ident_to_litstr(func_name)); - - assert_only_lifetime_generics(original_function, "procedures")?; - - let typed_args = extract_typed_args(original_function)?; - - // TODO: Require that procedures be `async` functions syntactically, - // and use `futures_util::FutureExt::now_or_never` to poll them. - // if !&original_function.sig.asyncness.is_some() { - // return Err(syn::Error::new_spanned( - // original_function.sig.clone(), - // "procedures must be `async`", - // )); - // }; - - // Extract all function parameter names. - let opt_arg_names = typed_args.iter().map(|arg| { - if let syn::Pat::Ident(i) = &*arg.pat { - let name = i.ident.to_string(); - quote!(Some(#name)) - } else { - quote!(None) - } - }); - - let arg_tys = typed_args.iter().map(|arg| arg.ty.as_ref()).collect::>(); - let first_arg_ty = arg_tys.first().into_iter(); - let rest_arg_tys = arg_tys.iter().skip(1); - - // Extract the return type. - let ret_ty_for_assert = match &original_function.sig.output { - syn::ReturnType::Default => None, - syn::ReturnType::Type(_, t) => Some(&**t), - } - .into_iter(); - - let ret_ty_for_info = match &original_function.sig.output { - syn::ReturnType::Default => quote!(()), - syn::ReturnType::Type(_, t) => quote!(#t), - }; - - let register_describer_symbol = format!("__preinit__20_register_describer_{}", procedure_name.value()); - - let lifetime_params = &original_function.sig.generics; - let lifetime_where_clause = &lifetime_params.where_clause; - - let generated_describe_function = quote! { - #[export_name = #register_describer_symbol] - pub extern "C" fn __register_describer() { - spacetimedb::rt::register_procedure::<_, _, #func_name>(#func_name) - } - }; - - Ok(quote! { - const _: () = { - #generated_describe_function - }; - #[allow(non_camel_case_types)] - #vis struct #func_name { _never: ::core::convert::Infallible } - const _: () = { - fn _assert_args #lifetime_params () #lifetime_where_clause { - #(let _ = <#first_arg_ty as spacetimedb::rt::ProcedureContextArg>::_ITEM;)* - #(let _ = <#rest_arg_tys as spacetimedb::rt::ProcedureArg>::_ITEM;)* - #(let _ = <#ret_ty_for_assert as spacetimedb::rt::IntoProcedureResult>::to_result;)* - } - }; - impl #func_name { - fn invoke(__ctx: spacetimedb::ProcedureContext, __args: &[u8]) -> spacetimedb::ProcedureResult { - spacetimedb::rt::invoke_procedure(#func_name, __ctx, __args) - } - } - #[automatically_derived] - impl spacetimedb::rt::FnInfo for #func_name { - /// The type of this function. - type Invoke = spacetimedb::rt::ProcedureFn; - - /// The function kind, which will cause scheduled tables to accept procedures. - type FnKind = spacetimedb::rt::FnKindProcedure<#ret_ty_for_info>; - - /// The name of this function - const NAME: &'static str = #procedure_name; - - /// The parameter names of this function - const ARG_NAMES: &'static [Option<&'static str>] = &[#(#opt_arg_names),*]; - - /// The pointer for invoking this function - const INVOKE: spacetimedb::rt::ProcedureFn = #func_name::invoke; - - /// The return type of this function - fn return_type(ts: &mut impl spacetimedb::sats::typespace::TypespaceBuilder) -> Option { - Some(<#ret_ty_for_info as spacetimedb::SpacetimeType>::make_type(ts)) - } - } - }) -} diff --git a/crates/bindings-macro/src/reducer.rs b/crates/bindings-macro/src/reducer.rs index 5adf4075284..ff98cc6250b 100644 --- a/crates/bindings-macro/src/reducer.rs +++ b/crates/bindings-macro/src/reducer.rs @@ -4,7 +4,7 @@ use proc_macro2::{Span, TokenStream}; use quote::{quote, quote_spanned}; use syn::parse::Parser as _; use syn::spanned::Spanned; -use syn::{FnArg, Ident, ItemFn, LitStr, PatType}; +use syn::{FnArg, Ident, ItemFn, LitStr}; #[derive(Default)] pub(crate) struct ReducerArgs { @@ -59,29 +59,25 @@ impl ReducerArgs { } } -pub(crate) fn assert_only_lifetime_generics(original_function: &ItemFn, function_kind_plural: &str) -> syn::Result<()> { +pub(crate) fn reducer_impl(args: ReducerArgs, original_function: &ItemFn) -> syn::Result { + let func_name = &original_function.sig.ident; + let vis = &original_function.vis; + + let reducer_name = args.name.unwrap_or_else(|| ident_to_litstr(func_name)); + for param in &original_function.sig.generics.params { let err = |msg| syn::Error::new_spanned(param, msg); match param { syn::GenericParam::Lifetime(_) => {} - syn::GenericParam::Type(_) => { - return Err(err(format!( - "type parameters are not allowed on {function_kind_plural}" - ))) - } - syn::GenericParam::Const(_) => { - return Err(err(format!( - "const parameters are not allowed on {function_kind_plural}" - ))) - } + syn::GenericParam::Type(_) => return Err(err("type parameters are not allowed on reducers")), + syn::GenericParam::Const(_) => return Err(err("const parameters are not allowed on reducers")), } } - Ok(()) -} -/// Extract all function parameters, except for `self` ones that aren't allowed. -pub(crate) fn extract_typed_args(original_function: &ItemFn) -> syn::Result> { - original_function + let lifecycle = args.lifecycle.iter().filter_map(|lc| lc.to_lifecycle_value()); + + // Extract all function parameters, except for `self` ones that aren't allowed. + let typed_args = original_function .sig .inputs .iter() @@ -89,20 +85,7 @@ pub(crate) fn extract_typed_args(original_function: &ItemFn) -> syn::Result Ok(arg), _ => Err(syn::Error::new_spanned(arg, "expected typed argument")), }) - .collect() -} - -pub(crate) fn reducer_impl(args: ReducerArgs, original_function: &ItemFn) -> syn::Result { - let func_name = &original_function.sig.ident; - let vis = &original_function.vis; - - let reducer_name = args.name.unwrap_or_else(|| ident_to_litstr(func_name)); - - assert_only_lifetime_generics(original_function, "reducers")?; - - let lifecycle = args.lifecycle.iter().filter_map(|lc| lc.to_lifecycle_value()); - - let typed_args = extract_typed_args(original_function)?; + .collect::>>()?; // Extract all function parameter names. let opt_arg_names = typed_args.iter().map(|arg| { @@ -156,14 +139,11 @@ pub(crate) fn reducer_impl(args: ReducerArgs, original_function: &ItemFn) -> syn } } #[automatically_derived] - impl spacetimedb::rt::FnInfo for #func_name { - type Invoke = spacetimedb::rt::ReducerFn; - /// The function kind, which will cause scheduled tables to accept reducers. - type FnKind = spacetimedb::rt::FnKindReducer; + impl spacetimedb::rt::ReducerInfo for #func_name { const NAME: &'static str = #reducer_name; #(const LIFECYCLE: Option = Some(#lifecycle);)* const ARG_NAMES: &'static [Option<&'static str>] = &[#(#opt_arg_names),*]; - const INVOKE: Self::Invoke = #func_name::invoke; + const INVOKE: spacetimedb::rt::ReducerFn = #func_name::invoke; } }) } diff --git a/crates/bindings-macro/src/table.rs b/crates/bindings-macro/src/table.rs index 2748f195e64..4e2b079992f 100644 --- a/crates/bindings-macro/src/table.rs +++ b/crates/bindings-macro/src/table.rs @@ -40,7 +40,7 @@ impl TableAccess { struct ScheduledArg { span: Span, - reducer_or_procedure: Path, + reducer: Path, at: Option, } @@ -113,7 +113,7 @@ impl TableArgs { impl ScheduledArg { fn parse_meta(meta: ParseNestedMeta) -> syn::Result { let span = meta.path.span(); - let mut reducer_or_procedure = None; + let mut reducer = None; let mut at = None; meta.parse_nested_meta(|meta| { @@ -126,26 +126,16 @@ impl ScheduledArg { } }) } else { - check_duplicate_msg( - &reducer_or_procedure, - &meta, - "can only specify one scheduled reducer or procedure", - )?; - reducer_or_procedure = Some(meta.path); + check_duplicate_msg(&reducer, &meta, "can only specify one scheduled reducer")?; + reducer = Some(meta.path); } Ok(()) })?; - let reducer_or_procedure = reducer_or_procedure.ok_or_else(|| { - meta.error( - "must specify scheduled reducer or procedure associated with the table: scheduled(function_name)", - ) + let reducer = reducer.ok_or_else(|| { + meta.error("must specify scheduled reducer associated with the table: scheduled(reducer_name)") })?; - Ok(Self { - span, - reducer_or_procedure, - at, - }) + Ok(Self { span, reducer, at }) } } @@ -828,20 +818,17 @@ pub(crate) fn table_impl(mut args: TableArgs, item: &syn::DeriveInput) -> syn::R ) })?; - let reducer_or_procedure = &sched.reducer_or_procedure; + let reducer = &sched.reducer; let scheduled_at_id = scheduled_at_column.index; let desc = quote!(spacetimedb::table::ScheduleDesc { - reducer_or_procedure_name: <#reducer_or_procedure as spacetimedb::rt::FnInfo>::NAME, + reducer_name: <#reducer as spacetimedb::rt::ReducerInfo>::NAME, scheduled_at_column: #scheduled_at_id, }); let primary_key_ty = primary_key_column.ty; let scheduled_at_ty = scheduled_at_column.ty; let typecheck = quote! { - spacetimedb::rt::scheduled_typecheck::< - #original_struct_ident, - <#reducer_or_procedure as spacetimedb::rt::FnInfo>::FnKind, - >(#reducer_or_procedure); + spacetimedb::rt::scheduled_reducer_typecheck::<#original_struct_ident>(#reducer); spacetimedb::rt::assert_scheduled_table_primary_key::<#primary_key_ty>(); let _ = |x: #scheduled_at_ty| { let _: spacetimedb::ScheduleAt = x; }; }; diff --git a/crates/bindings-macro/src/view.rs b/crates/bindings-macro/src/view.rs deleted file mode 100644 index 8575a226e06..00000000000 --- a/crates/bindings-macro/src/view.rs +++ /dev/null @@ -1,182 +0,0 @@ -use heck::ToSnakeCase; -use proc_macro2::{Ident, Span, TokenStream}; -use quote::quote; -use syn::ext::IdentExt; -use syn::parse::Parser; -use syn::{FnArg, ItemFn}; - -use crate::sym; -use crate::util::{check_duplicate_msg, match_meta}; - -pub(crate) struct ViewArgs { - name: Ident, - #[allow(unused)] - public: bool, -} - -impl ViewArgs { - /// Parse `#[view(name = ..., public)]` where both `name` and `public` are required. - pub(crate) fn parse(input: TokenStream, func_ident: &Ident) -> syn::Result { - let mut name = None; - let mut public = None; - syn::meta::parser(|meta| { - match_meta!(match meta { - sym::name => { - check_duplicate_msg(&name, &meta, "`name` already specified")?; - name = Some(meta.value()?.parse()?); - } - sym::public => { - check_duplicate_msg(&public, &meta, "`public` already specified")?; - public = Some(()); - } - }); - Ok(()) - }) - .parse2(input)?; - let name = name.ok_or_else(|| { - let view = func_ident.to_string().to_snake_case(); - syn::Error::new( - Span::call_site(), - format_args!("must specify view name, e.g. `#[spacetimedb::view(name = {view})]"), - ) - })?; - let () = public - .ok_or_else(|| syn::Error::new(Span::call_site(), "views must be `public`, e.g. `#[view(public)]`"))?; - Ok(Self { name, public: true }) - } -} - -pub(crate) fn view_impl(args: ViewArgs, original_function: &ItemFn) -> syn::Result { - let vis = &original_function.vis; - let func_name = &original_function.sig.ident; - let view_ident = args.name; - let view_name = view_ident.unraw().to_string(); - - for param in &original_function.sig.generics.params { - let err = |msg| syn::Error::new_spanned(param, msg); - match param { - syn::GenericParam::Lifetime(_) => {} - syn::GenericParam::Type(_) => return Err(err("type parameters are not allowed on views")), - syn::GenericParam::Const(_) => return Err(err("const parameters are not allowed on views")), - } - } - - // Extract parameters - let typed_args = original_function - .sig - .inputs - .iter() - .map(|arg| match arg { - FnArg::Typed(arg) => Ok(arg), - FnArg::Receiver(_) => Err(syn::Error::new_spanned( - arg, - "The `self` parameter is not allowed in views", - )), - }) - .collect::>>()?; - - // Extract parameter names - let opt_arg_names = typed_args.iter().map(|arg| { - if let syn::Pat::Ident(i) = &*arg.pat { - let name = i.ident.to_string(); - quote!(Some(#name)) - } else { - quote!(None) - } - }); - - let arg_tys = typed_args.iter().map(|arg| arg.ty.as_ref()).collect::>(); - - // Extract the context type and the rest of the parameter types - let [ctx_ty, arg_tys @ ..] = &arg_tys[..] else { - return Err(syn::Error::new_spanned( - &original_function.sig, - "Views must always have a context parameter: `&ViewContext` or `&AnonymousViewContext`", - )); - }; - - // Extract the context type - let ctx_ty = match ctx_ty { - syn::Type::Reference(ctx_ty) => ctx_ty.elem.as_ref(), - _ => { - return Err(syn::Error::new_spanned( - ctx_ty, - "The first parameter of a view must be a context parameter: `&ViewContext` or `&AnonymousViewContext`; passed by reference", - )); - } - }; - - // Views must return a result - let ret_ty = match &original_function.sig.output { - syn::ReturnType::Type(_, t) => t.as_ref(), - syn::ReturnType::Default => { - return Err(syn::Error::new_spanned( - &original_function.sig, - "views must return `Vec` or `Option` where `T` is a `SpacetimeType`", - )); - } - }; - - let register_describer_symbol = format!("__preinit__20_register_describer_{}", view_name); - - let lt_params = &original_function.sig.generics; - let lt_where_clause = <_params.where_clause; - - let generated_describe_function = quote! { - #[export_name = #register_describer_symbol] - pub extern "C" fn __register_describer() { - spacetimedb::rt::ViewRegistrar::<#ctx_ty>::register::<_, #func_name, _, _>(#func_name) - } - }; - - Ok(quote! { - const _: () = { #generated_describe_function }; - - #[allow(non_camel_case_types)] - #vis struct #func_name { _never: ::core::convert::Infallible } - - const _: () = { - fn _assert_args #lt_params () #lt_where_clause { - let _ = <#ctx_ty as spacetimedb::rt::ViewContextArg>::_ITEM; - let _ = <#ret_ty as spacetimedb::rt::ViewReturn>::_ITEM; - } - }; - - const _: () = { - fn _assert_args #lt_params () #lt_where_clause { - #(let _ = <#arg_tys as spacetimedb::rt::ViewArg>::_ITEM;)* - } - }; - - impl #func_name { - fn invoke(__ctx: #ctx_ty, __args: &[u8]) -> Vec { - spacetimedb::rt::ViewDispatcher::<#ctx_ty>::invoke::<_, _, _>(#func_name, __ctx, __args) - } - } - - #[automatically_derived] - impl spacetimedb::rt::FnInfo for #func_name { - /// The type of this function - type Invoke = as spacetimedb::rt::ViewKindTrait>::InvokeFn; - - /// The function kind, which will cause scheduled tables to reject views. - type FnKind = spacetimedb::rt::FnKindView; - - /// The name of this function - const NAME: &'static str = #view_name; - - /// The parameter names of this function - const ARG_NAMES: &'static [Option<&'static str>] = &[#(#opt_arg_names),*]; - - /// The pointer for invoking this function - const INVOKE: Self::Invoke = #func_name::invoke; - - /// The return type of this function - fn return_type( - ts: &mut impl spacetimedb::sats::typespace::TypespaceBuilder - ) -> Option { - Some(<#ret_ty as spacetimedb::SpacetimeType>::make_type(ts)) - } - } - }) -} diff --git a/crates/bindings-sys/src/lib.rs b/crates/bindings-sys/src/lib.rs index 0e1c786eb39..52c3b47f38e 100644 --- a/crates/bindings-sys/src/lib.rs +++ b/crates/bindings-sys/src/lib.rs @@ -645,26 +645,6 @@ pub mod raw { pub fn get_jwt(connection_id_ptr: *const u8, bytes_source_id: *mut BytesSource) -> u16; } - #[link(wasm_import_module = "spacetime_10.3")] - extern "C" { - /// Suspends execution of this WASM instance until approximately `wake_at_micros_since_unix_epoch`. - /// - /// Returns immediately if `wake_at_micros_since_unix_epoch` is in the past. - /// - /// Upon resuming, returns the current timestamp as microseconds since the Unix epoch. - /// - /// Not particularly useful, except for testing SpacetimeDB internals related to suspending procedure execution. - /// # Traps - /// - /// Traps if: - /// - /// - The calling WASM instance is holding open a transaction. - /// - The calling WASM instance is not executing a procedure. - // TODO(procedure-sleep-until): remove this - #[cfg(feature = "unstable")] - pub fn procedure_sleep_until(wake_at_micros_since_unix_epoch: i64) -> i64; - } - /// What strategy does the database index use? /// /// See also: @@ -1238,14 +1218,3 @@ impl Drop for RowIter { } } } - -pub mod procedure { - //! Side-effecting or asynchronous operations which only procedures are allowed to perform. - #[inline] - #[cfg(feature = "unstable")] - pub fn sleep_until(wake_at_timestamp: i64) -> i64 { - // Safety: Just calling an `extern "C"` function. - // Nothing weird happening here. - unsafe { super::raw::procedure_sleep_until(wake_at_timestamp) } - } -} diff --git a/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/index.ts b/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/index.ts index f30b1c1253b..89a6af89230 100644 --- a/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/index.ts +++ b/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/index.ts @@ -89,7 +89,7 @@ const REMOTE_MODULE = { }, }, versionInfo: { - cliVersion: '1.6.0', + cliVersion: '1.7.0', }, // Constructors which are used by the DbConnectionImpl to // extract type information from the generated RemoteModule. diff --git a/crates/bindings-typescript/package.json b/crates/bindings-typescript/package.json index b1dd6daf6a9..74217fac8e8 100644 --- a/crates/bindings-typescript/package.json +++ b/crates/bindings-typescript/package.json @@ -1,6 +1,6 @@ { "name": "spacetimedb", - "version": "1.6.2", + "version": "1.7.0", "description": "API and ABI bindings for the SpacetimeDB TypeScript module library", "homepage": "https://github.com/clockworklabs/SpacetimeDB#readme", "bugs": { diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 03f210f177f..4c557ba4240 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -48,8 +48,6 @@ pub use table::{ pub type ReducerResult = core::result::Result<(), Box>; -pub type ProcedureResult = Vec; - pub use spacetimedb_bindings_macro::duration; /// Generates code for registering a row-level security rule. @@ -543,7 +541,6 @@ pub use spacetimedb_bindings_macro::table; /// If an error occurs in the disconnect reducer, /// the client is still recorded as disconnected. /// -// TODO(docs): Move these docs to be on `table`, rather than `reducer`. This will reduce duplication with procedure docs. /// # Scheduled reducers /// /// In addition to life cycle annotations, reducers can be made **scheduled**. @@ -669,204 +666,6 @@ pub use spacetimedb_bindings_macro::table; #[doc(inline)] pub use spacetimedb_bindings_macro::reducer; -/// Marks a function as a SpacetimeDB procedure. -/// -/// A procedure is a function that runs within the database and can be invoked remotely by [clients], -/// but unlike a [`reducer`], a procedure is not automatically transactional. -/// This allows procedures to perform certain side-effecting operations, -/// but also means that module developers must be more careful not to corrupt the database state -/// when execution aborts or operations fail. -/// -/// When in doubt, prefer writing [`reducer`]s unless you need to perform an operation only available to procedures. -/// -/// The first argument of a procedure is always `&mut ProcedureContext`. -/// The [`ProcedureContext`] exposes information about the caller and allows side-effecting operations. -/// -/// After this, a procedure can take any number of arguments. -/// These arguments must implement the [`SpacetimeType`], [`Serialize`], and [`Deserialize`] traits. -/// All of these traits can be derived at once by marking a type with `#[derive(SpacetimeType)]`. -/// -/// A procedure may return any type that implements [`SpacetimeType`], [`Serialize`] and [`Deserialize`]. -/// Unlike [reducer]s, SpacetimeDB does not assign any special semantics to [`Result`] return values. -/// -/// If a procedure returns successfully (as opposed to panicking), its return value will be sent to the calling client. -/// If a procedure panics, its panic message will be sent to the calling client instead. -/// Procedure arguments and return values are not otherwise broadcast to clients. -/// -/// ```no_run -/// # use spacetimedb::{procedure, SpacetimeType, ProcedureContext, Timestamp}; -/// #[procedure] -/// fn return_value(ctx: &mut ProcedureContext, arg: MyArgument) -> MyReturnValue { -/// MyReturnValue { -/// a: format!("Hello, {}", ctx.sender), -/// b: ctx.timestamp, -/// } -/// } -/// -/// #[derive(SpacetimeType)] -/// struct MyArgument { -/// val: u32, -/// } -/// -/// #[derive(SpacetimeType)] -/// struct MyReturnValue { -/// a: String, -/// b: Timestamp, -/// } -/// ``` -/// -/// # Blocking operations -/// -/// Procedures are allowed to perform certain operations which take time. -/// During the execution of these operations, the procedure's execution will be suspended, -/// allowing other database operations to run in parallel. -/// -/// Procedures must not hold open a transaction while performing a blocking operation. -// TODO(procedure-http): add example with an HTTP request. -// TODO(procedure-transaction): document obtaining and using a transaction within a procedure. -/// -/// # Scheduled procedures -// TODO(docs): after moving scheduled reducer docs into table secion, link there. -/// -/// Like [reducer]s, procedures can be made **scheduled**. -/// This allows calling procedures at a particular time, or in a loop. -/// It also allows reducers to enqueue procedure runs. -/// -/// Scheduled procedures are called on a best-effort basis and may be slightly delayed in their execution -/// when a database is under heavy load. -/// -/// [clients]: https://spacetimedb.com/docs/#client -// TODO(procedure-async): update docs and examples with `async`-ness. -#[doc(inline)] -pub use spacetimedb_bindings_macro::procedure; - -/// Marks a function as a spacetimedb view. -/// -/// A view is a function with read-only access to the database. -/// -/// The first argument of a view is always a [`&ViewContext`] or [`&AnonymousViewContext`]. -/// The former can only read from the database whereas latter can also access info about the caller. -/// -/// After this, a view can take any number of arguments just like reducers. -/// These arguments must implement the [`SpacetimeType`], [`Serialize`], and [`Deserialize`] traits. -/// All of these traits can be derived at once by marking a type with `#[derive(SpacetimeType)]`. -/// -/// Views return `Vec` or `Option` where `T` is a `SpacetimeType`. -/// -/// ```no_run -/// # mod demo { -/// use spacetimedb::{view, table, AnonymousViewContext, SpacetimeType, ViewContext}; -/// use spacetimedb_lib::Identity; -/// -/// #[table(name = player)] -/// struct Player { -/// #[auto_inc] -/// #[primary_key] -/// id: u64, -/// -/// #[unique] -/// identity: Identity, -/// -/// #[index(btree)] -/// level: u32, -/// } -/// -/// impl Player { -/// fn merge(self, location: Location) -> PlayerAndLocation { -/// PlayerAndLocation { -/// player_id: self.id, -/// level: self.level, -/// x: location.x, -/// y: location.y, -/// } -/// } -/// } -/// -/// #[derive(SpacetimeType)] -/// struct PlayerId { -/// id: u64, -/// } -/// -/// #[table(name = location, index(name = coordinates, btree(columns = [x, y])))] -/// struct Location { -/// #[unique] -/// player_id: u64, -/// x: u64, -/// y: u64, -/// } -/// -/// #[derive(SpacetimeType)] -/// struct PlayerAndLocation { -/// player_id: u64, -/// level: u32, -/// x: u64, -/// y: u64, -/// } -/// -/// // A view that selects at most one row from a table -/// #[view(name = my_player, public)] -/// fn my_player(ctx: &ViewContext) -> Option { -/// ctx.db.player().identity().find(ctx.sender) -/// } -/// -/// // An example of column projection -/// #[view(name = my_player_id, public)] -/// fn my_player_id(ctx: &ViewContext) -> Option { -/// ctx.db.player().identity().find(ctx.sender).map(|Player { id, .. }| PlayerId { id }) -/// } -/// -/// // An example of a parameterized view -/// #[view(name = players_at_level, public)] -/// fn players_at_level(ctx: &AnonymousViewContext, level: u32) -> Vec { -/// ctx.db.player().level().filter(level).collect() -/// } -/// -/// // An example that is analogous to a semijoin in sql -/// #[view(name = players_at_coordinates, public)] -/// fn players_at_coordinates(ctx: &AnonymousViewContext, x: u64, y: u64) -> Vec { -/// ctx -/// .db -/// .location() -/// .coordinates() -/// .filter((x, y)) -/// .filter_map(|location| ctx.db.player().id().find(location.player_id)) -/// .collect() -/// } -/// -/// // An example of a join that combines fields from two different tables -/// #[view(name = players_with_coordinates, public)] -/// fn players_with_coordinates(ctx: &AnonymousViewContext, x: u64, y: u64) -> Vec { -/// ctx -/// .db -/// .location() -/// .coordinates() -/// .filter((x, y)) -/// .filter_map(|location| ctx -/// .db -/// .player() -/// .id() -/// .find(location.player_id) -/// .map(|player| player.merge(location)) -/// ) -/// .collect() -/// } -/// # } -/// ``` -/// -/// Just like reducers, views are limited in their ability to interact with the outside world. -/// They have no access to any network or filesystem interfaces. -/// Calling methods from [`std::io`], [`std::net`], or [`std::fs`] will result in runtime errors. -/// -/// Views are callable by reducers and other views simply by passing their `ViewContext`.. -/// This is a regular function call. -/// The callee will run within the caller's transaction. -/// -/// -/// [`&ViewContext`]: `ViewContext` -/// [`&AnonymousViewContext`]: `AnonymousViewContext` -#[doc(inline)] -pub use spacetimedb_bindings_macro::view; - /// One of two possible types that can be passed as the first argument to a `#[view]`. /// The other is [`ViewContext`]. /// Use this type if the view does not depend on the caller's identity. @@ -879,6 +678,7 @@ pub struct AnonymousViewContext { /// Use this type if the view depends on the caller's identity. pub struct ViewContext { pub sender: Identity, + pub connection_id: Option, pub db: LocalReadOnly, } @@ -906,8 +706,11 @@ pub struct ReducerContext { /// The `ConnectionId` of the client that invoked the reducer. /// - /// Will be `None` for certain reducers invoked automatically by the host, - /// including `init` and scheduled reducers. + /// `None` if no `ConnectionId` was supplied to the `/database/call` HTTP endpoint, + /// or via the CLI's `spacetime call` subcommand. + /// + /// For automatic reducers, i.e. `init`, `client_connected`, `client_disconnected`, and scheduled reducers, + /// this will be the module's `ConnectionId`. pub connection_id: Option, /// Allows accessing the local database attached to a module. @@ -1012,76 +815,12 @@ impl ReducerContext { pub fn as_read_only(&self) -> ViewContext { ViewContext { sender: self.sender, + connection_id: self.connection_id, db: LocalReadOnly {}, } } } -/// The context that any procedure is provided with. -/// -/// Each procedure must accept `&mut ProcedureContext` as its first argument. -/// -/// Includes information about the client calling the procedure and the time of invocation, -/// and exposes methods for running transactions and performing side-effecting operations. -pub struct ProcedureContext { - /// The `Identity` of the client that invoked the procedure. - pub sender: Identity, - - /// The time at which the procedure was started. - pub timestamp: Timestamp, - - /// The `ConnectionId` of the client that invoked the procedure. - /// - /// Will be `None` for certain scheduled procedures. - pub connection_id: Option, - // TODO: Add rng? - // Complex and requires design because we may want procedure RNG to behave differently from reducer RNG, - // as it could actually be seeded by OS randomness rather than a deterministic source. -} - -impl ProcedureContext { - /// Read the current module's [`Identity`]. - pub fn identity(&self) -> Identity { - // Hypothetically, we *could* read the module identity out of the system tables. - // However, this would be: - // - Onerous, because we have no tooling to inspect the system tables from module code. - // - Slow (at least relatively), - // because it would involve multiple host calls which hit the datastore, - // as compared to a single host call which does not. - // As such, we've just defined a host call - // which reads the module identity out of the `InstanceEnv`. - Identity::from_byte_array(spacetimedb_bindings_sys::identity()) - } - - /// Suspend execution until approximately `Timestamp`. - /// - /// This will update `self.timestamp` to the new time after execution resumes. - /// - /// Actual time suspended may not be exactly equal to `duration`. - /// Callers should read `self.timestamp` after resuming to determine the new time. - /// - /// ```no_run - /// # use std::time::Duration; - /// # use spacetimedb::{procedure, ProcedureContext}; - /// # #[procedure] - /// # fn sleep_one_second(ctx: &mut ProcedureContext) { - /// let prev_time = ctx.timestamp; - /// let target = prev_time + Duration::from_secs(1); - /// ctx.sleep_until(target); - /// let new_time = ctx.timestamp; - /// let actual_delta = new_time.duration_since(prev_time).unwrap(); - /// log::info!("Slept from {prev_time} to {new_time}, a total of {actual_delta:?}"); - /// # } - /// ``` - // TODO(procedure-sleep-until): remove this method - #[cfg(feature = "unstable")] - pub fn sleep_until(&mut self, timestamp: Timestamp) { - let new_time = sys::procedure::sleep_until(timestamp.to_micros_since_unix_epoch()); - let new_time = Timestamp::from_micros_since_unix_epoch(new_time); - self.timestamp = new_time; - } -} - /// A handle on a database with a particular table schema. pub trait DbContext { /// A view into the tables of a database. @@ -1108,10 +847,6 @@ impl DbContext for ReducerContext { } } -// `ProcedureContext` is *not* a `DbContext`. We will add a `TxContext` -// which can be obtained from `ProcedureContext::start_tx`, -// and that will be a `DbContext`. - /// Allows accessing the local database attached to the module. /// /// This slightly strange type appears to have no methods, but that is misleading. diff --git a/crates/bindings/src/rt.rs b/crates/bindings/src/rt.rs index 8e159ce716c..be513ea52f4 100644 --- a/crates/bindings/src/rt.rs +++ b/crates/bindings/src/rt.rs @@ -1,40 +1,20 @@ #![deny(unsafe_op_in_unsafe_fn)] use crate::table::IndexAlgo; -use crate::{ - sys, AnonymousViewContext, IterBuf, LocalReadOnly, ProcedureContext, ProcedureResult, ReducerContext, - ReducerResult, SpacetimeType, Table, ViewContext, -}; +use crate::{sys, IterBuf, ReducerContext, ReducerResult, SpacetimeType, Table}; pub use spacetimedb_lib::db::raw_def::v9::Lifecycle as LifecycleReducer; use spacetimedb_lib::db::raw_def::v9::{RawIndexAlgorithm, RawModuleDefV9Builder, TableType}; use spacetimedb_lib::de::{self, Deserialize, Error as _, SeqProductAccess}; use spacetimedb_lib::sats::typespace::TypespaceBuilder; use spacetimedb_lib::sats::{impl_deserialize, impl_serialize, ProductTypeElement}; use spacetimedb_lib::ser::{Serialize, SerializeSeqProduct}; -use spacetimedb_lib::{bsatn, AlgebraicType, ConnectionId, Identity, ProductType, RawModuleDef, Timestamp}; +use spacetimedb_lib::{bsatn, ConnectionId, Identity, ProductType, RawModuleDef, Timestamp}; use spacetimedb_primitives::*; -use std::convert::Infallible; use std::fmt; use std::marker::PhantomData; use std::sync::{Mutex, OnceLock}; use sys::raw::{BytesSink, BytesSource}; -pub trait IntoVec { - fn into_vec(self) -> Vec; -} - -impl IntoVec for Vec { - fn into_vec(self) -> Vec { - self - } -} - -impl IntoVec for Option { - fn into_vec(self) -> Vec { - self.into_iter().collect() - } -} - /// The `sender` invokes `reducer` at `timestamp` and provides it with the given `args`. /// /// Returns an invalid buffer on success @@ -49,22 +29,6 @@ pub fn invoke_reducer<'a, A: Args<'a>>( reducer.invoke(&ctx, args) } - -pub fn invoke_procedure<'a, A: Args<'a>, Ret: IntoProcedureResult>( - procedure: impl Procedure<'a, A, Ret>, - mut ctx: ProcedureContext, - args: &'a [u8], -) -> ProcedureResult { - // Deserialize the arguments from a bsatn encoding. - let SerDeArgs(args) = bsatn::from_slice(args).expect("unable to decode args"); - - // TODO(procedure-async): get a future out of `procedure.invoke` and call `FutureExt::now_or_never` on it? - // Or maybe do that within the `Procedure::invoke` method? - let res = procedure.invoke(&mut ctx, args); - - res.to_result() -} - /// A trait for types representing the *execution logic* of a reducer. #[diagnostic::on_unimplemented( message = "invalid reducer signature", @@ -79,101 +43,22 @@ pub trait Reducer<'de, A: Args<'de>> { fn invoke(&self, ctx: &ReducerContext, args: A) -> ReducerResult; } -/// Invoke a caller-specific view. -/// Returns a BSATN encoded `Vec` of rows. -pub fn invoke_view<'a, A: Args<'a>, T: SpacetimeType + Serialize>( - view: impl View<'a, A, T>, - ctx: ViewContext, - args: &'a [u8], -) -> Vec { - // Deserialize the arguments from a bsatn encoding. - let SerDeArgs(args) = bsatn::from_slice(args).expect("unable to decode args"); - let rows: Vec = view.invoke(&ctx, args); - let mut buf = IterBuf::take(); - buf.serialize_into(&rows).expect("unable to encode rows"); - std::mem::take(&mut *buf) -} -/// A trait for types representing the execution logic of a caller-specific view. -#[diagnostic::on_unimplemented( - message = "invalid view signature", - label = "this view signature is not valid", - note = "", - note = "view signatures must match:", - note = " `Fn(&ViewContext, [T1, ...]) -> Vec | Option`", - note = "where each `Ti` implements `SpacetimeType`.", - note = "" -)] -pub trait View<'de, A: Args<'de>, T: SpacetimeType + Serialize> { - fn invoke(&self, ctx: &ViewContext, args: A) -> Vec; -} - -/// Invoke an anonymous view. -/// Returns a BSATN encoded `Vec` of rows. -pub fn invoke_anonymous_view<'a, A: Args<'a>, T: SpacetimeType + Serialize>( - view: impl AnonymousView<'a, A, T>, - ctx: AnonymousViewContext, - args: &'a [u8], -) -> Vec { - // Deserialize the arguments from a bsatn encoding. - let SerDeArgs(args) = bsatn::from_slice(args).expect("unable to decode args"); - let rows: Vec = view.invoke(&ctx, args); - let mut buf = IterBuf::take(); - buf.serialize_into(&rows).expect("unable to encode rows"); - std::mem::take(&mut *buf) -} -/// A trait for types representing the execution logic of an anonymous view. -#[diagnostic::on_unimplemented( - message = "invalid anonymous view signature", - label = "this view signature is not valid", - note = "", - note = "anonymous view signatures must match:", - note = " `Fn(&AnonymousViewContext, [T1, ...]) -> Vec | Option`", - note = "where each `Ti` implements `SpacetimeType`.", - note = "" -)] -pub trait AnonymousView<'de, A: Args<'de>, T: SpacetimeType + Serialize> { - fn invoke(&self, ctx: &AnonymousViewContext, args: A) -> Vec; -} - -/// A trait for types that can *describe* a callable function such as a reducer or view. -pub trait FnInfo { - /// The type of function to invoke. - type Invoke; - - /// One of [`FnKindReducer`], [`FnKindProcedure`] or [`FnKindView`]. - /// - /// Used as a type argument to [`ExportFunctionForScheduledTable`] and [`scheduled_typecheck`]. - /// See for details on this technique. - type FnKind; - - /// The name of the function. +/// A trait for types that can *describe* a reducer. +pub trait ReducerInfo { + /// The name of the reducer. const NAME: &'static str; - /// The lifecycle of the function, if there is one. + /// The lifecycle of the reducer, if there is one. const LIFECYCLE: Option = None; - /// A description of the parameter names of the function. + /// A description of the parameter names of the reducer. const ARG_NAMES: &'static [Option<&'static str>]; - /// The function to invoke. - const INVOKE: Self::Invoke; - - /// The return type of this function. - /// Currently only implemented for views. - fn return_type(_ts: &mut impl TypespaceBuilder) -> Option { - None - } + /// The function to call to invoke the reducer. + const INVOKE: ReducerFn; } -pub trait Procedure<'de, A: Args<'de>, Ret: IntoProcedureResult> { - fn invoke(&self, ctx: &mut ProcedureContext, args: A) -> Ret; -} - -/// A trait of types representing the arguments of a reducer, procedure or view. -/// -/// This does not include the context first argument, -/// only the client-provided args. -/// As such, the same trait can be used for all sorts of exported functions. +/// A trait of types representing the arguments of a reducer. pub trait Args<'de>: Sized { /// How many arguments does the reducer accept? const LEN: usize; @@ -184,8 +69,8 @@ pub trait Args<'de>: Sized { /// Serialize the arguments in `self` into the sequence `prod` according to the type `S`. fn serialize_seq_product(&self, prod: &mut S) -> Result<(), S::Error>; - /// Returns the schema of the args for this function provided a `typespace`. - fn schema(typespace: &mut impl TypespaceBuilder) -> ProductType; + /// Returns the schema for this reducer provided a `typespace`. + fn schema(typespace: &mut impl TypespaceBuilder) -> ProductType; } /// A trait of types representing the result of executing a reducer. @@ -211,18 +96,6 @@ impl IntoReducerResult for Result<(), E> { } } -#[diagnostic::on_unimplemented( - message = "The procedure return type `{Self}` does not implement `SpacetimeType`", - note = "if you own the type, try adding `#[derive(SpacetimeType)]` to its definition" -)] -pub trait IntoProcedureResult: SpacetimeType + Serialize { - #[inline] - fn to_result(&self) -> ProcedureResult { - bsatn::to_vec(&self).expect("Failed to serialize procedure result") - } -} -impl IntoProcedureResult for T {} - #[diagnostic::on_unimplemented( message = "the first argument of a reducer must be `&ReducerContext`", label = "first argument must be `&ReducerContext`" @@ -246,191 +119,22 @@ pub trait ReducerArg { } impl ReducerArg for T {} -#[diagnostic::on_unimplemented( - message = "the first argument of a procedure must be `&mut ProcedureContext`", - label = "first argument must be `&mut ProcedureContext`" -)] -pub trait ProcedureContextArg { - // a little hack used in the macro to make error messages nicer. it generates ::_ITEM - #[doc(hidden)] - const _ITEM: () = (); -} -impl ProcedureContextArg for &mut ProcedureContext {} - -/// A trait of types that can be an argument of a procedure. -#[diagnostic::on_unimplemented( - message = "the procedure argument `{Self}` does not implement `SpacetimeType`", - note = "if you own the type, try adding `#[derive(SpacetimeType)]` to its definition" -)] -pub trait ProcedureArg { - // a little hack used in the macro to make error messages nicer. it generates ::_ITEM - #[doc(hidden)] - const _ITEM: () = (); -} -impl ProcedureArg for T {} - -#[diagnostic::on_unimplemented( - message = "The first parameter of a `#[view]` must be `&ViewContext` or `&AnonymousViewContext`" -)] -pub trait ViewContextArg { - #[doc(hidden)] - const _ITEM: () = (); -} -impl ViewContextArg for ViewContext {} -impl ViewContextArg for AnonymousViewContext {} - -/// A trait of types that can be an argument of a view. -#[diagnostic::on_unimplemented( - message = "the view argument `{Self}` does not implement `SpacetimeType`", - note = "if you own the type, try adding `#[derive(SpacetimeType)]` to its definition" -)] -pub trait ViewArg { - #[doc(hidden)] - const _ITEM: () = (); -} -impl ViewArg for T {} - -/// A trait of types that can be the return type of a view. -#[diagnostic::on_unimplemented(message = "Views must return `Vec` or `Option` where `T` is a `SpacetimeType`")] -pub trait ViewReturn { - #[doc(hidden)] - const _ITEM: () = (); -} -impl ViewReturn for Vec {} -impl ViewReturn for Option {} - -/// Map the correct dispatcher based on the `Ctx` type -pub struct ViewKind { - _marker: PhantomData, -} - -pub trait ViewKindTrait { - type InvokeFn; -} - -impl ViewKindTrait for ViewKind { - type InvokeFn = ViewFn; -} - -impl ViewKindTrait for ViewKind { - type InvokeFn = AnonymousFn; -} - -/// Invoke the correct dispatcher based on the `Ctx` type -pub struct ViewDispatcher { - _marker: PhantomData, -} - -impl ViewDispatcher { - #[inline] - pub fn invoke<'a, A, T, V>(view: V, ctx: ViewContext, args: &'a [u8]) -> Vec - where - A: Args<'a>, - T: SpacetimeType + Serialize, - V: View<'a, A, T>, - { - invoke_view(view, ctx, args) - } -} - -impl ViewDispatcher { - #[inline] - pub fn invoke<'a, A, T, V>(view: V, ctx: AnonymousViewContext, args: &'a [u8]) -> Vec - where - A: Args<'a>, - T: SpacetimeType + Serialize, - V: AnonymousView<'a, A, T>, - { - invoke_anonymous_view(view, ctx, args) - } -} - -/// Register the correct dispatcher based on the `Ctx` type -pub struct ViewRegistrar { - _marker: PhantomData, -} - -impl ViewRegistrar { - #[inline] - pub fn register<'a, A, I, T, V>(view: V) - where - A: Args<'a>, - T: SpacetimeType + Serialize, - I: FnInfo, - V: View<'a, A, T>, - { - register_view::(view) - } -} - -impl ViewRegistrar { - #[inline] - pub fn register<'a, A, I, T, V>(view: V) - where - A: Args<'a>, - T: SpacetimeType + Serialize, - I: FnInfo, - V: AnonymousView<'a, A, T>, - { - register_anonymous_view::(view) - } -} - /// Assert that a reducer type-checks with a given type. -pub const fn scheduled_typecheck<'de, Row, FnKind>(_x: impl ExportFunctionForScheduledTable<'de, Row, FnKind>) +pub const fn scheduled_reducer_typecheck<'de, Row>(_x: impl ReducerForScheduledTable<'de, Row>) where Row: SpacetimeType + Serialize + Deserialize<'de>, { core::mem::forget(_x); } -/// Tacit marker argument to [`ExportFunctionForScheduledTable`] for reducers. -pub struct FnKindReducer { - _never: Infallible, -} - -/// Tacit marker argument to [`ExportFunctionForScheduledTable`] for procedures. -/// -/// Holds the procedure's return type in order to avoid an error due to an unconstrained type argument. -pub struct FnKindProcedure { - _never: Infallible, - _ret_ty: PhantomData Ret>, -} - -/// Tacit marker argument to [`ExportFunctionForScheduledTable`] for views. -/// -/// Because views are never scheduled, we don't need to distinguish between anonymous or sender-identity views, -/// or to include their return type. -pub struct FnKindView { - _never: Infallible, -} - -/// Trait bound for [`scheduled_typecheck`], which the [`crate::table`] macro generates to typecheck scheduled functions. -/// -/// The `FnKind` parameter here is a coherence-defeating marker, which Will Crichton calls a "tacit parameter." -/// See for details on this technique. -/// It will be one of [`FnKindReducer`] or [`FnKindProcedure`] in modules that compile successfully. -/// It may be [`FnKindView`], but that will always fail to typecheck, as views cannot be used as scheduled functions. #[diagnostic::on_unimplemented( - message = "invalid signature for scheduled table reducer or procedure", - note = "views cannot be scheduled", - note = "the scheduled function must take `{TableRow}` as its sole argument", - note = "e.g: `fn scheduled_reducer(ctx: &ReducerContext, arg: {TableRow})`", - // TODO(procedure-async): amend this to `async fn` once procedures are `async`-ified - note = "or `fn scheduled_procedure(ctx: &mut ProcedureContext, arg: {TableRow})`", + message = "invalid signature for scheduled table reducer", + note = "the scheduled reducer must take `{TableRow}` as its sole argument", + note = "e.g: `fn scheduled_reducer(ctx: &ReducerContext, arg: {TableRow})`" )] -pub trait ExportFunctionForScheduledTable<'de, TableRow, FnKind> {} -impl<'de, TableRow: SpacetimeType + Serialize + Deserialize<'de>, F: Reducer<'de, (TableRow,)>> - ExportFunctionForScheduledTable<'de, TableRow, FnKindReducer> for F -{ -} - -impl< - 'de, - TableRow: SpacetimeType + Serialize + Deserialize<'de>, - Ret: SpacetimeType + Serialize + Deserialize<'de>, - F: Procedure<'de, (TableRow,), Ret>, - > ExportFunctionForScheduledTable<'de, TableRow, FnKindProcedure> for F +pub trait ReducerForScheduledTable<'de, TableRow> {} +impl<'de, TableRow: SpacetimeType + Serialize + Deserialize<'de>, R: Reducer<'de, (TableRow,)>> + ReducerForScheduledTable<'de, TableRow> for R { } @@ -502,15 +206,15 @@ impl<'de, A: Args<'de>> de::ProductVisitor<'de> for ArgsVisitor { } } -macro_rules! impl_reducer_procedure_view { +macro_rules! impl_reducer { ($($T1:ident $(, $T:ident)*)?) => { - impl_reducer_procedure_view!(@impl $($T1 $(, $T)*)?); - $(impl_reducer_procedure_view!($($T),*);)? + impl_reducer!(@impl $($T1 $(, $T)*)?); + $(impl_reducer!($($T),*);)? }; (@impl $($T:ident),*) => { // Implement `Args` for the tuple type `($($T,)*)`. impl<'de, $($T: SpacetimeType + Deserialize<'de> + Serialize),*> Args<'de> for ($($T,)*) { - const LEN: usize = impl_reducer_procedure_view!(@count $($T)*); + const LEN: usize = impl_reducer!(@count $($T)*); #[allow(non_snake_case)] #[allow(unused)] fn visit_seq_product>(mut prod: Acc) -> Result { @@ -535,7 +239,7 @@ macro_rules! impl_reducer_procedure_view { #[inline] #[allow(non_snake_case, irrefutable_let_patterns)] - fn schema(_typespace: &mut impl TypespaceBuilder) -> ProductType { + fn schema(_typespace: &mut impl TypespaceBuilder) -> ProductType { // Extract the names of the arguments. let [.., $($T),*] = Info::ARG_NAMES else { panic!() }; ProductType::new(vec![ @@ -547,7 +251,7 @@ macro_rules! impl_reducer_procedure_view { } } - // Implement `Reducer<..., ContextArg>` for the tuple type `($($T,)*)`. + // Implement `Reducer<..., ContextArg>` for the tuple type `($($T,)*)`. impl<'de, Func, Ret, $($T: SpacetimeType + Deserialize<'de> + Serialize),*> Reducer<'de, ($($T,)*)> for Func where Func: Fn(&ReducerContext, $($T),*) -> Ret, @@ -560,60 +264,15 @@ macro_rules! impl_reducer_procedure_view { } } - impl<'de, Func, Ret, $($T: SpacetimeType + Deserialize<'de> + Serialize),*> Procedure<'de, ($($T,)*), Ret> for Func - where - Func: Fn(&mut ProcedureContext, $($T),*) -> Ret, - Ret: IntoProcedureResult, - { - #[allow(non_snake_case)] - fn invoke(&self, ctx: &mut ProcedureContext, args: ($($T,)*)) -> Ret { - let ($($T,)*) = args; - self(ctx, $($T),*) - } - } - - // Implement `View<..., ViewContext>` for the tuple type `($($T,)*)`. - impl<'de, Func, Elem, Retn, $($T),*> - View<'de, ($($T,)*), Elem> for Func - where - $($T: SpacetimeType + Deserialize<'de> + Serialize,)* - Func: Fn(&ViewContext, $($T),*) -> Retn, - Retn: IntoVec, - Elem: SpacetimeType + Serialize, - { - #[allow(non_snake_case)] - fn invoke(&self, ctx: &ViewContext, args: ($($T,)*)) -> Vec { - let ($($T,)*) = args; - self(ctx, $($T),*).into_vec() - } - } - - // Implement `View<..., AnonymousViewContext>` for the tuple type `($($T,)*)`. - impl<'de, Func, Elem, Retn, $($T),*> - AnonymousView<'de, ($($T,)*), Elem> for Func - where - $($T: SpacetimeType + Deserialize<'de> + Serialize,)* - Func: Fn(&AnonymousViewContext, $($T),*) -> Retn, - Retn: IntoVec, - Elem: SpacetimeType + Serialize, - { - #[allow(non_snake_case)] - fn invoke(&self, ctx: &AnonymousViewContext, args: ($($T,)*)) -> Vec { - let ($($T,)*) = args; - self(ctx, $($T),*).into_vec() - } - } }; // Counts the number of elements in the tuple. (@count $($T:ident)*) => { - 0 $(+ impl_reducer_procedure_view!(@drop $T 1))* + 0 $(+ impl_reducer!(@drop $T 1))* }; (@drop $a:tt $b:tt) => { $b }; } -impl_reducer_procedure_view!( - A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE, AF -); +impl_reducer!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE, AF); /// Provides deserialization and serialization for any type `A: Args`. struct SerDeArgs(A); @@ -680,7 +339,7 @@ pub fn register_table() { table = table.with_column_sequence(col); } if let Some(schedule) = T::SCHEDULE { - table = table.with_schedule(schedule.reducer_or_procedure_name, schedule.scheduled_at_column); + table = table.with_schedule(schedule.reducer_name, schedule.scheduled_at_column); } for col in T::get_default_col_values().iter_mut() { @@ -703,7 +362,7 @@ impl From> for RawIndexAlgorithm { } /// Registers a describer for the reducer `I` with arguments `A`. -pub fn register_reducer<'a, A: Args<'a>, I: FnInfo>(_: impl Reducer<'a, A>) { +pub fn register_reducer<'a, A: Args<'a>, I: ReducerInfo>(_: impl Reducer<'a, A>) { register_describer(|module| { let params = A::schema::(&mut module.inner); module.inner.add_reducer(I::NAME, params, I::LIFECYCLE); @@ -711,50 +370,6 @@ pub fn register_reducer<'a, A: Args<'a>, I: FnInfo>(_: impl }) } -pub fn register_procedure<'a, A, Ret, I>(_: impl Procedure<'a, A, Ret>) -where - A: Args<'a>, - Ret: SpacetimeType + Serialize, - I: FnInfo, -{ - register_describer(|module| { - let params = A::schema::(&mut module.inner); - let ret_ty = ::make_type(&mut module.inner); - module.inner.add_procedure(I::NAME, params, ret_ty); - module.procedures.push(I::INVOKE); - }) -} - -/// Registers a describer for the view `I` with arguments `A` and return type `Vec`. -pub fn register_view<'a, A, I, T>(_: impl View<'a, A, T>) -where - A: Args<'a>, - I: FnInfo, - T: SpacetimeType + Serialize, -{ - register_describer(|module| { - let params = A::schema::(&mut module.inner); - let return_type = I::return_type(&mut module.inner).unwrap(); - module.inner.add_view(I::NAME, true, false, params, return_type); - module.views.push(I::INVOKE); - }) -} - -/// Registers a describer for the anonymous view `I` with arguments `A` and return type `Vec`. -pub fn register_anonymous_view<'a, A, I, T>(_: impl AnonymousView<'a, A, T>) -where - A: Args<'a>, - I: FnInfo, - T: SpacetimeType + Serialize, -{ - register_describer(|module| { - let params = A::schema::(&mut module.inner); - let return_type = I::return_type(&mut module.inner).unwrap(); - module.inner.add_view(I::NAME, true, true, params, return_type); - module.views_anon.push(I::INVOKE); - }) -} - /// Registers a row-level security policy. pub fn register_row_level_security(sql: &'static str) { register_describer(|module| { @@ -764,17 +379,11 @@ pub fn register_row_level_security(sql: &'static str) { /// A builder for a module. #[derive(Default)] -pub struct ModuleBuilder { +struct ModuleBuilder { /// The module definition. inner: RawModuleDefV9Builder, /// The reducers of the module. reducers: Vec, - /// The procedures of the module. - procedures: Vec, - /// The client specific views of the module. - views: Vec, - /// The anonymous views of the module. - views_anon: Vec, } // Not actually a mutex; because WASM is single-threaded this basically just turns into a refcell. @@ -785,17 +394,6 @@ static DESCRIBERS: Mutex>> = Mutex::new(Vec::new()); pub type ReducerFn = fn(ReducerContext, &[u8]) -> ReducerResult; static REDUCERS: OnceLock> = OnceLock::new(); -pub type ProcedureFn = fn(ProcedureContext, &[u8]) -> ProcedureResult; -static PROCEDURES: OnceLock> = OnceLock::new(); - -/// A view function takes in `(ViewContext, Args)` and returns a Vec of bytes. -pub type ViewFn = fn(ViewContext, &[u8]) -> Vec; -static VIEWS: OnceLock> = OnceLock::new(); - -/// An anonymous view function takes in `(AnonymousViewContext, Args)` and returns a Vec of bytes. -pub type AnonymousFn = fn(AnonymousViewContext, &[u8]) -> Vec; -static ANONYMOUS_VIEWS: OnceLock> = OnceLock::new(); - /// Called by the host when the module is initialized /// to describe the module into a serialized form that is returned. /// @@ -824,11 +422,8 @@ extern "C" fn __describe_module__(description: BytesSink) { let module_def = RawModuleDef::V9(module_def); let bytes = bsatn::to_vec(&module_def).expect("unable to serialize typespace"); - // Write the sets of reducers, procedures and views. + // Write the set of reducers. REDUCERS.set(module.reducers).ok().unwrap(); - PROCEDURES.set(module.procedures).ok().unwrap(); - VIEWS.set(module.views).ok().unwrap(); - ANONYMOUS_VIEWS.set(module.views_anon).ok().unwrap(); // Write the bsatn data into the sink. write_to_sink(description, &bytes); @@ -880,11 +475,16 @@ extern "C" fn __call_reducer__( error: BytesSink, ) -> i16 { // Piece together `sender_i` into an `Identity`. - let sender = reconstruct_sender_identity(sender_0, sender_1, sender_2, sender_3); + let sender = [sender_0, sender_1, sender_2, sender_3]; + let sender: [u8; 32] = bytemuck::must_cast(sender); + let sender = Identity::from_byte_array(sender); // The LITTLE-ENDIAN constructor. // Piece together `conn_id_i` into a `ConnectionId`. // The all-zeros `ConnectionId` (`ConnectionId::ZERO`) is interpreted as `None`. - let conn_id = reconstruct_connection_id(conn_id_0, conn_id_1); + let conn_id = [conn_id_0, conn_id_1]; + let conn_id: [u8; 16] = bytemuck::must_cast(conn_id); + let conn_id = ConnectionId::from_le_byte_array(conn_id); // The LITTLE-ENDIAN constructor. + let conn_id = (conn_id != ConnectionId::ZERO).then_some(conn_id); // Assemble the `ReducerContext`. let timestamp = Timestamp::from_micros_since_unix_epoch(timestamp as i64); @@ -895,170 +495,15 @@ extern "C" fn __call_reducer__( // Dispatch to it with the arguments read. let res = with_read_args(args, |args| reducers[id](ctx, args)); // Convert any error message to an error code and writes to the `error` sink. - convert_err_to_errno(res, error) -} - -/// Reconstruct the `sender_i` args to [`__call_reducer__`] and [`__call_procedure__`] into an [`Identity`]. -fn reconstruct_sender_identity(sender_0: u64, sender_1: u64, sender_2: u64, sender_3: u64) -> Identity { - let sender = [sender_0, sender_1, sender_2, sender_3]; - let sender: [u8; 32] = bytemuck::must_cast(sender); - Identity::from_byte_array(sender) // The LITTLE-ENDIAN constructor. -} - -/// Reconstruct the `conn_id_i` args to [`__call_reducer__`] and [`__call_procedure__`] into a [`ConnectionId`]. -/// -/// The all-zeros `ConnectionId` (`ConnectionId::ZERO`) is interpreted as `None`. -fn reconstruct_connection_id(conn_id_0: u64, conn_id_1: u64) -> Option { - // Piece together `conn_id_i` into a `ConnectionId`. - // The all-zeros `ConnectionId` (`ConnectionId::ZERO`) is interpreted as `None`. - let conn_id = [conn_id_0, conn_id_1]; - let conn_id: [u8; 16] = bytemuck::must_cast(conn_id); - let conn_id = ConnectionId::from_le_byte_array(conn_id); // The LITTLE-ENDIAN constructor. - (conn_id != ConnectionId::ZERO).then_some(conn_id) -} - -/// If `res` is `Err`, write the message to `out` and return non-zero. -/// If `res` is `Ok`, return zero. -/// -/// Called by [`__call_reducer__`] and [`__call_procedure__`] -/// to convert the user-returned `Result` into a low-level errno return. -fn convert_err_to_errno(res: Result<(), Box>, out: BytesSink) -> i16 { match res { Ok(()) => 0, Err(msg) => { - write_to_sink(out, msg.as_bytes()); + write_to_sink(error, msg.as_bytes()); errno::HOST_CALL_FAILURE.get() as i16 } } } -/// Called by the host to execute a procedure -/// when the `sender` calls the procedure identified by `id` at `timestamp` with `args`. -/// -/// The `sender_{0-3}` are the pieces of a `[u8; 32]` (`u256`) representing the sender's `Identity`. -/// They are encoded as follows (assuming `identity.to_byte_array(): [u8; 32]`): -/// - `sender_0` contains bytes `[0 ..8 ]`. -/// - `sender_1` contains bytes `[8 ..16]`. -/// - `sender_2` contains bytes `[16..24]`. -/// - `sender_3` contains bytes `[24..32]`. -/// -/// Note that `to_byte_array` uses LITTLE-ENDIAN order! This matches most host systems. -/// -/// The `conn_id_{0-1}` are the pieces of a `[u8; 16]` (`u128`) representing the callers's [`ConnectionId`]. -/// They are encoded as follows (assuming `conn_id.as_le_byte_array(): [u8; 16]`): -/// - `conn_id_0` contains bytes `[0 ..8 ]`. -/// - `conn_id_1` contains bytes `[8 ..16]`. -/// -/// Again, note that `to_byte_array` uses LITTLE-ENDIAN order! This matches most host systems. -/// -/// The `args` is a `BytesSource`, registered on the host side, -/// which can be read with `bytes_source_read`. -/// The contents of the buffer are the BSATN-encoding of the arguments to the reducer. -/// In the case of empty arguments, `args` will be 0, that is, invalid. -/// -/// The `result_sink` is a `BytesSink`, registered on the host side, -/// which can be written to with `bytes_sink_write`. -/// Procedures are expected to always write to this sink -/// the BSATN-serialized bytes of a value of the procedure's return type. -/// -/// Procedures always return the error 0. All other return values are reserved. -#[no_mangle] -extern "C" fn __call_procedure__( - id: usize, - sender_0: u64, - sender_1: u64, - sender_2: u64, - sender_3: u64, - conn_id_0: u64, - conn_id_1: u64, - timestamp: u64, - args: BytesSource, - result_sink: BytesSink, -) -> i16 { - // Piece together `sender_i` into an `Identity`. - let sender = reconstruct_sender_identity(sender_0, sender_1, sender_2, sender_3); - - // Piece together `conn_id_i` into a `ConnectionId`. - let conn_id = reconstruct_connection_id(conn_id_0, conn_id_1); - - let timestamp = Timestamp::from_micros_since_unix_epoch(timestamp as i64); - - // Assemble the `ProcedureContext`. - let ctx = ProcedureContext { - connection_id: conn_id, - sender, - timestamp, - }; - - // Grab the list of procedures, which is populated by the preinit functions. - let procedures = PROCEDURES.get().unwrap(); - - // Deserialize the args and pass them to the actual procedure. - let res = with_read_args(args, |args| procedures[id](ctx, args)); - - // Write the result bytes to the `result_sink`. - write_to_sink(result_sink, &res); - - // Return 0 for no error. Procedures always either trap or return 0. - 0 -} - -/// Called by the host to execute an anonymous view. -/// -/// The `args` is a `BytesSource`, registered on the host side, -/// which can be read with `bytes_source_read`. -/// The contents of the buffer are the BSATN-encoding of the arguments to the view. -/// In the case of empty arguments, `args` will be 0, that is, invalid. -/// -/// The output of the view is written to a `BytesSink`, -/// registered on the host side, with `bytes_sink_write`. -#[no_mangle] -extern "C" fn __call_view_anon__(id: usize, args: BytesSource, sink: BytesSink) -> i16 { - let views = ANONYMOUS_VIEWS.get().unwrap(); - write_to_sink( - sink, - &with_read_args(args, |args| { - views[id](AnonymousViewContext { db: LocalReadOnly {} }, args) - }), - ); - 0 -} - -/// Called by the host to execute a view when the `sender` calls the view identified by `id` with `args`. -/// See [`__call_reducer__`] for more commentary on the arguments. -/// -/// The `args` is a `BytesSource`, registered on the host side, -/// which can be read with `bytes_source_read`. -/// The contents of the buffer are the BSATN-encoding of the arguments to the view. -/// In the case of empty arguments, `args` will be 0, that is, invalid. -/// -/// The output of the view is written to a `BytesSink`, -/// registered on the host side, with `bytes_sink_write`. -#[no_mangle] -extern "C" fn __call_view__( - id: usize, - sender_0: u64, - sender_1: u64, - sender_2: u64, - sender_3: u64, - args: BytesSource, - sink: BytesSink, -) -> i16 { - // Piece together `sender_i` into an `Identity`. - let sender = [sender_0, sender_1, sender_2, sender_3]; - let sender: [u8; 32] = bytemuck::must_cast(sender); - let sender = Identity::from_byte_array(sender); // The LITTLE-ENDIAN constructor. - - let views = VIEWS.get().unwrap(); - let db = LocalReadOnly {}; - - write_to_sink( - sink, - &with_read_args(args, |args| views[id](ViewContext { sender, db }, args)), - ); - 0 -} - /// Run `logic` with `args` read from the host into a `&[u8]`. fn with_read_args(args: BytesSource, logic: impl FnOnce(&[u8]) -> R) -> R { if args == BytesSource::INVALID { @@ -1179,7 +624,7 @@ macro_rules! __make_register_reftype { #[cfg(feature = "unstable")] #[doc(hidden)] -pub fn volatile_nonatomic_schedule_immediate<'de, A: Args<'de>, R: Reducer<'de, A>, R2: FnInfo>( +pub fn volatile_nonatomic_schedule_immediate<'de, A: Args<'de>, R: Reducer<'de, A>, R2: ReducerInfo>( _reducer: R, args: A, ) { diff --git a/crates/bindings/src/table.rs b/crates/bindings/src/table.rs index 0dd7a6edc17..0ca8e174989 100644 --- a/crates/bindings/src/table.rs +++ b/crates/bindings/src/table.rs @@ -149,7 +149,7 @@ pub enum IndexAlgo<'a> { } pub struct ScheduleDesc<'a> { - pub reducer_or_procedure_name: &'a str, + pub reducer_name: &'a str, pub scheduled_at_column: u16, } diff --git a/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap b/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap index a0b8fc583e8..6d29d31a881 100644 --- a/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap +++ b/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap @@ -42,11 +42,6 @@ spacetimedb │ ├── spacetimedb_primitives │ │ ├── bitflags │ │ ├── either -│ │ ├── enum_as_inner -│ │ │ ├── heck -│ │ │ ├── proc_macro2 (*) -│ │ │ ├── quote (*) -│ │ │ └── syn (*) │ │ ├── itertools │ │ │ └── either │ │ └── nohash_hasher @@ -55,7 +50,6 @@ spacetimedb │ └── spacetimedb_primitives │ ├── bitflags │ ├── either -│ ├── enum_as_inner (*) │ ├── itertools │ │ └── either │ └── nohash_hasher @@ -76,7 +70,11 @@ spacetimedb │ │ [build-dependencies] │ │ └── autocfg │ ├── derive_more (*) -│ ├── enum_as_inner (*) +│ ├── enum_as_inner +│ │ ├── heck +│ │ ├── proc_macro2 (*) +│ │ ├── quote (*) +│ │ └── syn (*) │ ├── hex │ ├── itertools (*) │ ├── spacetimedb_bindings_macro (*) diff --git a/crates/bindings/tests/ui/reducers.stderr b/crates/bindings/tests/ui/reducers.stderr index 3c1ea8ae015..14814c9e055 100644 --- a/crates/bindings/tests/ui/reducers.stderr +++ b/crates/bindings/tests/ui/reducers.stderr @@ -37,8 +37,8 @@ error[E0277]: invalid reducer signature note: required by a bound in `register_reducer` --> src/rt.rs | - | pub fn register_reducer<'a, A: Args<'a>, I: FnInfo>(_: impl Reducer<'a, A>) { - | ^^^^^^^^^^^^^^ required by this bound in `register_reducer` + | pub fn register_reducer<'a, A: Args<'a>, I: ReducerInfo>(_: impl Reducer<'a, A>) { + | ^^^^^^^^^^^^^^ required by this bound in `register_reducer` error[E0277]: the reducer argument `Test` does not implement `SpacetimeType` --> tests/ui/reducers.rs:6:40 @@ -98,8 +98,8 @@ error[E0277]: invalid reducer signature note: required by a bound in `register_reducer` --> src/rt.rs | - | pub fn register_reducer<'a, A: Args<'a>, I: FnInfo>(_: impl Reducer<'a, A>) { - | ^^^^^^^^^^^^^^ required by this bound in `register_reducer` + | pub fn register_reducer<'a, A: Args<'a>, I: ReducerInfo>(_: impl Reducer<'a, A>) { + | ^^^^^^^^^^^^^^ required by this bound in `register_reducer` error[E0277]: `Test` is not a valid reducer return type --> tests/ui/reducers.rs:9:46 @@ -151,8 +151,8 @@ error[E0277]: invalid reducer signature note: required by a bound in `register_reducer` --> src/rt.rs | - | pub fn register_reducer<'a, A: Args<'a>, I: FnInfo>(_: impl Reducer<'a, A>) { - | ^^^^^^^^^^^^^^ required by this bound in `register_reducer` + | pub fn register_reducer<'a, A: Args<'a>, I: ReducerInfo>(_: impl Reducer<'a, A>) { + | ^^^^^^^^^^^^^^ required by this bound in `register_reducer` error[E0277]: the first argument of a reducer must be `&ReducerContext` --> tests/ui/reducers.rs:23:20 @@ -202,8 +202,8 @@ error[E0277]: invalid reducer signature note: required by a bound in `register_reducer` --> src/rt.rs | - | pub fn register_reducer<'a, A: Args<'a>, I: FnInfo>(_: impl Reducer<'a, A>) { - | ^^^^^^^^^^^^^^ required by this bound in `register_reducer` + | pub fn register_reducer<'a, A: Args<'a>, I: ReducerInfo>(_: impl Reducer<'a, A>) { + | ^^^^^^^^^^^^^^ required by this bound in `register_reducer` error[E0277]: the first argument of a reducer must be `&ReducerContext` --> tests/ui/reducers.rs:26:21 @@ -249,9 +249,9 @@ error[E0593]: function is expected to take 2 arguments, but it takes 3 arguments | ----------------------------------------------------------------- takes 3 arguments | = note: required for `for<'a> fn(&'a ReducerContext, u8, u8) {scheduled_table_reducer}` to implement `Reducer<'_, (ScheduledTable,)>` - = note: required for `for<'a> fn(&'a ReducerContext, u8, u8) {scheduled_table_reducer}` to implement `ExportFunctionForScheduledTable<'_, ScheduledTable, FnKindReducer>` -note: required by a bound in `scheduled_typecheck` + = note: required for `for<'a> fn(&'a ReducerContext, u8, u8) {scheduled_table_reducer}` to implement `ReducerForScheduledTable<'_, ScheduledTable>` +note: required by a bound in `scheduled_reducer_typecheck` --> src/rt.rs | - | pub const fn scheduled_typecheck<'de, Row, FnKind>(_x: impl ExportFunctionForScheduledTable<'de, Row, FnKind>) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `scheduled_typecheck` + | pub const fn scheduled_reducer_typecheck<'de, Row>(_x: impl ReducerForScheduledTable<'de, Row>) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `scheduled_reducer_typecheck` diff --git a/crates/bindings/tests/ui/views.rs b/crates/bindings/tests/ui/views.rs index 56c29161bea..131a0cf2f47 100644 --- a/crates/bindings/tests/ui/views.rs +++ b/crates/bindings/tests/ui/views.rs @@ -1,4 +1,4 @@ -use spacetimedb::{reducer, table, view, AnonymousViewContext, Identity, ReducerContext, ViewContext}; +use spacetimedb::{reducer, table, ReducerContext}; #[table(name = test)] struct Test { @@ -64,95 +64,4 @@ fn read_only_btree_index_no_delete(ctx: &ReducerContext) { read_only.db.test().x().delete(0u32..); } -#[table(name = player)] -struct Player { - #[unique] - identity: Identity, -} - -struct NotSpacetimeType {} - -/// Private views not allowed; must be `#[view(public, ...)]` -#[view(name = view_def_no_public)] -fn view_def_no_public(_: &ViewContext) -> Vec { - vec![] -} - -/// Duplicate `public` -#[view(name = view_def_dup_public, public, public)] -fn view_def_dup_public() -> Vec { - vec![] -} - -/// Duplicate `name` -#[view(name = view_def_dup_name, name = view_def_dup_name, public)] -fn view_def_dup_name() -> Vec { - vec![] -} - -/// Unsupported attribute arg -#[view(name = view_def_unsupported_arg, public, anonymous)] -fn view_def_unsupported_arg() -> Vec { - vec![] -} - -/// A `ViewContext` is required -#[view(name = view_def_no_context, public)] -fn view_def_no_context() -> Vec { - vec![] -} - -/// A `ViewContext` is required -#[view(name = view_def_wrong_context, public)] -fn view_def_wrong_context(_: &ReducerContext) -> Vec { - vec![] -} - -/// Must pass the `ViewContext` by ref -#[view(name = view_def_pass_context_by_value, public)] -fn view_def_pass_context_by_value(_: ViewContext) -> Vec { - vec![] -} - -/// The view context must be the first parameter -#[view(name = view_def_wrong_context_position, public)] -fn view_def_wrong_context_position(_: &u32, _: &ViewContext) -> Vec { - vec![] -} - -/// Must return `Vec` or `Option` where `T` is a SpacetimeType -#[view(name = view_def_no_return, public)] -fn view_def_no_return(_: &ViewContext) {} - -/// Must return `Vec` or `Option` where `T` is a SpacetimeType -#[view(name = view_def_wrong_return, public)] -fn view_def_wrong_return(_: &ViewContext) -> Player { - Player { - identity: Identity::ZERO, - } -} - -/// Must return `Vec` or `Option` where `T` is a SpacetimeType -#[view(name = view_def_returns_not_a_spacetime_type, public)] -fn view_def_returns_not_a_spacetime_type(_: &AnonymousViewContext) -> Option { - None -} - -/// Cannot use a view as a scheduled function -#[spacetimedb::table(name = scheduled_table, scheduled(scheduled_table_view))] -struct ScheduledTable { - #[primary_key] - #[auto_inc] - scheduled_id: u64, - scheduled_at: spacetimedb::ScheduleAt, - x: u8, - y: u8, -} - -/// Cannot use a view as a scheduled function -#[view(name = scheduled_table_view, public)] -fn scheduled_table_view(_: &ViewContext, _args: ScheduledTable) -> Vec { - vec![] -} - fn main() {} diff --git a/crates/bindings/tests/ui/views.stderr b/crates/bindings/tests/ui/views.stderr index 1693aaa1e71..95108add08c 100644 --- a/crates/bindings/tests/ui/views.stderr +++ b/crates/bindings/tests/ui/views.stderr @@ -1,85 +1,3 @@ -error: views must be `public`, e.g. `#[view(public)]` - --> tests/ui/views.rs:76:1 - | -76 | #[view(name = view_def_no_public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: `public` already specified - --> tests/ui/views.rs:82:44 - | -82 | #[view(name = view_def_dup_public, public, public)] - | ^^^^^^ - -error: `name` already specified - --> tests/ui/views.rs:88:34 - | -88 | #[view(name = view_def_dup_name, name = view_def_dup_name, public)] - | ^^^^ - -error: expected `name` or `public` - --> tests/ui/views.rs:94:49 - | -94 | #[view(name = view_def_unsupported_arg, public, anonymous)] - | ^^^^^^^^^ - -error: Views must always have a context parameter: `&ViewContext` or `&AnonymousViewContext` - --> tests/ui/views.rs:101:1 - | -101 | fn view_def_no_context() -> Vec { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: The first parameter of a view must be a context parameter: `&ViewContext` or `&AnonymousViewContext`; passed by reference - --> tests/ui/views.rs:113:38 - | -113 | fn view_def_pass_context_by_value(_: ViewContext) -> Vec { - | ^^^^^^^^^^^ - -error: views must return `Vec` or `Option` where `T` is a `SpacetimeType` - --> tests/ui/views.rs:125:1 - | -125 | fn view_def_no_return(_: &ViewContext) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: the trait bound `ViewKind: ViewKindTrait` is not satisfied - --> tests/ui/views.rs:106:1 - | -106 | #[view(name = view_def_wrong_context, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ViewKindTrait` is not implemented for `ViewKind` - | - = help: the following other types implement trait `ViewKindTrait`: - ViewKind - ViewKind - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0276]: impl has stricter requirements than trait - --> tests/ui/views.rs:106:1 - | -106 | #[view(name = view_def_wrong_context, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `ViewKind: ViewKindTrait` - | - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `ViewKind: ViewKindTrait` is not satisfied - --> tests/ui/views.rs:118:1 - | -118 | #[view(name = view_def_wrong_context_position, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ViewKindTrait` is not implemented for `ViewKind` - | - = help: the following other types implement trait `ViewKindTrait`: - ViewKind - ViewKind - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0276]: impl has stricter requirements than trait - --> tests/ui/views.rs:118:1 - | -118 | #[view(name = view_def_wrong_context_position, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `ViewKind: ViewKindTrait` - | - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0599]: no method named `iter` found for reference `&test__ViewHandle` in the current scope --> tests/ui/views.rs:15:34 | @@ -170,309 +88,3 @@ error[E0599]: no method named `delete` found for struct `RangedIndexReadOnly` in | 64 | read_only.db.test().x().delete(0u32..); | ^^^^^^ method not found in `RangedIndexReadOnly` - -error[E0599]: no function or associated item named `register` found for struct `ViewRegistrar` in the current scope - --> tests/ui/views.rs:106:1 - | -106 | #[view(name = view_def_wrong_context, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item not found in `ViewRegistrar` - | - = note: the function or associated item was found for - - `ViewRegistrar` - - `ViewRegistrar` - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: The first parameter of a `#[view]` must be `&ViewContext` or `&AnonymousViewContext` - --> tests/ui/views.rs:107:31 - | -107 | fn view_def_wrong_context(_: &ReducerContext) -> Vec { - | ^^^^^^^^^^^^^^ the trait `ViewContextArg` is not implemented for `ReducerContext` - | - = help: the following other types implement trait `ViewContextArg`: - AnonymousViewContext - ViewContext - -error[E0599]: no function or associated item named `invoke` found for struct `ViewDispatcher` in the current scope - --> tests/ui/views.rs:106:1 - | -106 | #[view(name = view_def_wrong_context, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item not found in `ViewDispatcher` - | - = note: the function or associated item was found for - - `ViewDispatcher` - - `ViewDispatcher` - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0599]: no function or associated item named `register` found for struct `ViewRegistrar` in the current scope - --> tests/ui/views.rs:118:1 - | -118 | #[view(name = view_def_wrong_context_position, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item not found in `ViewRegistrar` - | - = note: the function or associated item was found for - - `ViewRegistrar` - - `ViewRegistrar` - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: The first parameter of a `#[view]` must be `&ViewContext` or `&AnonymousViewContext` - --> tests/ui/views.rs:119:40 - | -119 | fn view_def_wrong_context_position(_: &u32, _: &ViewContext) -> Vec { - | ^^^ the trait `ViewContextArg` is not implemented for `u32` - | - = help: the following other types implement trait `ViewContextArg`: - AnonymousViewContext - ViewContext - -error[E0277]: the view argument `&ViewContext` does not implement `SpacetimeType` - --> tests/ui/views.rs:119:48 - | -119 | fn view_def_wrong_context_position(_: &u32, _: &ViewContext) -> Vec { - | ^^^^^^^^^^^^ the trait `SpacetimeType` is not implemented for `ViewContext` - | - = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition - = help: the following other types implement trait `SpacetimeType`: - &T - () - AlgebraicType - AlgebraicTypeRef - Arc - ArrayType - Box - ColId - and $N others - = note: required for `&ViewContext` to implement `SpacetimeType` - = note: required for `&ViewContext` to implement `ViewArg` - -error[E0599]: no function or associated item named `invoke` found for struct `ViewDispatcher` in the current scope - --> tests/ui/views.rs:118:1 - | -118 | #[view(name = view_def_wrong_context_position, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item not found in `ViewDispatcher` - | - = note: the function or associated item was found for - - `ViewDispatcher` - - `ViewDispatcher` - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: invalid view signature - --> tests/ui/views.rs:128:1 - | -128 | #[view(name = view_def_wrong_return, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this view signature is not valid - | - = help: the trait `spacetimedb::rt::View<'_, _, _>` is not implemented for fn item `for<'a> fn(&'a ViewContext) -> Player {view_def_wrong_return}` - = note: - = note: view signatures must match: - = note: `Fn(&ViewContext, [T1, ...]) -> Vec | Option` - = note: where each `Ti` implements `SpacetimeType`. - = note: -note: required by a bound in `ViewRegistrar::::register` - --> src/rt.rs - | - | pub fn register<'a, A, I, T, V>(view: V) - | -------- required by a bound in this associated function -... - | V: View<'a, A, T>, - | ^^^^^^^^^^^^^^ required by this bound in `ViewRegistrar::::register` - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: Views must return `Vec` or `Option` where `T` is a `SpacetimeType` - --> tests/ui/views.rs:129:46 - | -129 | fn view_def_wrong_return(_: &ViewContext) -> Player { - | ^^^^^^ the trait `ViewReturn` is not implemented for `Player` - | - = help: the following other types implement trait `ViewReturn`: - Option - Vec - -error[E0277]: invalid view signature - --> tests/ui/views.rs:128:1 - | -128 | #[view(name = view_def_wrong_return, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this view signature is not valid - | - = help: the trait `spacetimedb::rt::View<'_, _, _>` is not implemented for fn item `for<'a> fn(&'a ViewContext) -> Player {view_def_wrong_return}` - = note: - = note: view signatures must match: - = note: `Fn(&ViewContext, [T1, ...]) -> Vec | Option` - = note: where each `Ti` implements `SpacetimeType`. - = note: -note: required by a bound in `ViewDispatcher::::invoke` - --> src/rt.rs - | - | pub fn invoke<'a, A, T, V>(view: V, ctx: ViewContext, args: &'a [u8]) -> Vec - | ------ required by a bound in this associated function -... - | V: View<'a, A, T>, - | ^^^^^^^^^^^^^^ required by this bound in `ViewDispatcher::::invoke` - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `NotSpacetimeType: SpacetimeType` is not satisfied - --> tests/ui/views.rs:136:1 - | -136 | #[view(name = view_def_returns_not_a_spacetime_type, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `SpacetimeType` is not implemented for `NotSpacetimeType` - | - = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition - = help: the following other types implement trait `SpacetimeType`: - &T - () - AlgebraicType - AlgebraicTypeRef - Arc - ArrayType - Box - ColId - and $N others - = note: required for `for<'a> fn(&'a AnonymousViewContext) -> Option {view_def_returns_not_a_spacetime_type}` to implement `AnonymousView<'_, (), NotSpacetimeType>` -note: required by a bound in `ViewRegistrar::::register` - --> src/rt.rs - | - | pub fn register<'a, A, I, T, V>(view: V) - | -------- required by a bound in this associated function -... - | V: AnonymousView<'a, A, T>, - | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ViewRegistrar::::register` - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `NotSpacetimeType: Serialize` is not satisfied - --> tests/ui/views.rs:136:1 - | -136 | #[view(name = view_def_returns_not_a_spacetime_type, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Serialize` is not implemented for `NotSpacetimeType` - | - = help: the following other types implement trait `Serialize`: - &T - () - (T0, T1) - (T0, T1, T2) - (T0, T1, T2, T3) - (T0, T1, T2, T3, T4) - (T0, T1, T2, T3, T4, T5) - (T0, T1, T2, T3, T4, T5, T6) - and $N others - = note: required for `for<'a> fn(&'a AnonymousViewContext) -> Option {view_def_returns_not_a_spacetime_type}` to implement `AnonymousView<'_, (), NotSpacetimeType>` -note: required by a bound in `ViewRegistrar::::register` - --> src/rt.rs - | - | pub fn register<'a, A, I, T, V>(view: V) - | -------- required by a bound in this associated function -... - | V: AnonymousView<'a, A, T>, - | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ViewRegistrar::::register` - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `NotSpacetimeType: SpacetimeType` is not satisfied - --> tests/ui/views.rs:137:71 - | -137 | fn view_def_returns_not_a_spacetime_type(_: &AnonymousViewContext) -> Option { - | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `SpacetimeType` is not implemented for `NotSpacetimeType` - | - = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition - = help: the following other types implement trait `SpacetimeType`: - &T - () - AlgebraicType - AlgebraicTypeRef - Arc - ArrayType - Box - ColId - and $N others - = note: required for `Option` to implement `ViewReturn` - -error[E0277]: the trait bound `NotSpacetimeType: SpacetimeType` is not satisfied - --> tests/ui/views.rs:136:1 - | -136 | #[view(name = view_def_returns_not_a_spacetime_type, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `SpacetimeType` is not implemented for `NotSpacetimeType` - | - = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition - = help: the following other types implement trait `SpacetimeType`: - &T - () - AlgebraicType - AlgebraicTypeRef - Arc - ArrayType - Box - ColId - and $N others - = note: required for `for<'a> fn(&'a AnonymousViewContext) -> Option {view_def_returns_not_a_spacetime_type}` to implement `AnonymousView<'_, (), NotSpacetimeType>` -note: required by a bound in `ViewDispatcher::::invoke` - --> src/rt.rs - | - | pub fn invoke<'a, A, T, V>(view: V, ctx: AnonymousViewContext, args: &'a [u8]) -> Vec - | ------ required by a bound in this associated function -... - | V: AnonymousView<'a, A, T>, - | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ViewDispatcher::::invoke` - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `NotSpacetimeType: Serialize` is not satisfied - --> tests/ui/views.rs:136:1 - | -136 | #[view(name = view_def_returns_not_a_spacetime_type, public)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Serialize` is not implemented for `NotSpacetimeType` - | - = help: the following other types implement trait `Serialize`: - &T - () - (T0, T1) - (T0, T1, T2) - (T0, T1, T2, T3) - (T0, T1, T2, T3, T4) - (T0, T1, T2, T3, T4, T5) - (T0, T1, T2, T3, T4, T5, T6) - and $N others - = note: required for `for<'a> fn(&'a AnonymousViewContext) -> Option {view_def_returns_not_a_spacetime_type}` to implement `AnonymousView<'_, (), NotSpacetimeType>` -note: required by a bound in `ViewDispatcher::::invoke` - --> src/rt.rs - | - | pub fn invoke<'a, A, T, V>(view: V, ctx: AnonymousViewContext, args: &'a [u8]) -> Vec - | ------ required by a bound in this associated function -... - | V: AnonymousView<'a, A, T>, - | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ViewDispatcher::::invoke` - = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `NotSpacetimeType: SpacetimeType` is not satisfied - --> tests/ui/views.rs:137:71 - | -137 | fn view_def_returns_not_a_spacetime_type(_: &AnonymousViewContext) -> Option { - | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `SpacetimeType` is not implemented for `NotSpacetimeType` - | - = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition - = help: the following other types implement trait `SpacetimeType`: - &T - () - AlgebraicType - AlgebraicTypeRef - Arc - ArrayType - Box - ColId - and $N others - = note: required for `Option` to implement `SpacetimeType` - -error[E0277]: invalid signature for scheduled table reducer or procedure - --> tests/ui/views.rs:142:56 - | -142 | #[spacetimedb::table(name = scheduled_table, scheduled(scheduled_table_view))] - | -------------------------------------------------------^^^^^^^^^^^^^^^^^^^^--- - | | | - | | unsatisfied trait bound - | required by a bound introduced by this call - | - = help: the trait `ExportFunctionForScheduledTable<'_, ScheduledTable, FnKindView>` is not implemented for fn item `for<'a> fn(&'a ViewContext, ScheduledTable) -> Vec {scheduled_table_view}` - = note: views cannot be scheduled - = note: the scheduled function must take `ScheduledTable` as its sole argument - = note: e.g: `fn scheduled_reducer(ctx: &ReducerContext, arg: ScheduledTable)` - = note: or `fn scheduled_procedure(ctx: &mut ProcedureContext, arg: ScheduledTable)` -note: required by a bound in `scheduled_typecheck` - --> src/rt.rs - | - | pub const fn scheduled_typecheck<'de, Row, FnKind>(_x: impl ExportFunctionForScheduledTable<'de, Row, FnKind>) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `scheduled_typecheck` diff --git a/crates/cli/src/subcommands/project/typescript/package._json b/crates/cli/src/subcommands/project/typescript/package._json index 47be347a920..9af76d05094 100644 --- a/crates/cli/src/subcommands/project/typescript/package._json +++ b/crates/cli/src/subcommands/project/typescript/package._json @@ -10,6 +10,6 @@ "author": "", "license": "ISC", "dependencies": { - "spacetimedb": "1.6.*" + "spacetimedb": "1.7.*" } } \ No newline at end of file diff --git a/crates/cli/templates/basic-c-sharp/server/StdbModule.csproj b/crates/cli/templates/basic-c-sharp/server/StdbModule.csproj index 4a504b099c8..6121a5ae7f9 100644 --- a/crates/cli/templates/basic-c-sharp/server/StdbModule.csproj +++ b/crates/cli/templates/basic-c-sharp/server/StdbModule.csproj @@ -8,7 +8,7 @@ - + diff --git a/crates/cli/templates/basic-rust/client/Cargo.toml b/crates/cli/templates/basic-rust/client/Cargo.toml index 35168a60fd8..56befed5f8a 100644 --- a/crates/cli/templates/basic-rust/client/Cargo.toml +++ b/crates/cli/templates/basic-rust/client/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -spacetimedb-sdk = "1.6.*" +spacetimedb-sdk = "1.7.*" diff --git a/crates/cli/templates/basic-rust/server/Cargo.toml b/crates/cli/templates/basic-rust/server/Cargo.toml index dfbc3a9823e..417cc88be6c 100644 --- a/crates/cli/templates/basic-rust/server/Cargo.toml +++ b/crates/cli/templates/basic-rust/server/Cargo.toml @@ -9,5 +9,5 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -spacetimedb = "1.6.*" +spacetimedb = "1.7.*" log = "0.4" diff --git a/crates/client-api-messages/src/websocket.rs b/crates/client-api-messages/src/websocket.rs index 4620f508809..cf24e6e5e87 100644 --- a/crates/client-api-messages/src/websocket.rs +++ b/crates/client-api-messages/src/websocket.rs @@ -105,8 +105,6 @@ pub enum ClientMessage { /// Remove a subscription to a SQL query that was added with SubscribeSingle. Unsubscribe(Unsubscribe), UnsubscribeMulti(UnsubscribeMulti), - /// Request a procedure run. - CallProcedure(CallProcedure), } impl ClientMessage { @@ -129,17 +127,6 @@ impl ClientMessage { ClientMessage::Subscribe(x) => ClientMessage::Subscribe(x), ClientMessage::SubscribeMulti(x) => ClientMessage::SubscribeMulti(x), ClientMessage::UnsubscribeMulti(x) => ClientMessage::UnsubscribeMulti(x), - ClientMessage::CallProcedure(CallProcedure { - procedure, - args, - request_id, - flags, - }) => ClientMessage::CallProcedure(CallProcedure { - procedure, - args: f(args), - request_id, - flags, - }), } } } @@ -305,40 +292,6 @@ pub struct OneOffQuery { pub query_string: Box, } -#[derive(SpacetimeType)] -#[sats(crate = spacetimedb_lib)] -/// Request a procedure run. -/// -/// Parametric over the argument type to enable [`ClientMessage::map_args`]. -pub struct CallProcedure { - /// The name of the procedure to call. - pub procedure: Box, - /// The arguments to the procedure. - /// - /// In the wire format, this will be a [`Bytes`], BSATN or JSON encoded according to the reducer's argument schema - /// and the enclosing message format. - pub args: Args, - /// An identifier for a client request. - /// - /// The server will include the same ID in the response [`ProcedureResult`]. - pub request_id: u32, - /// Reserved space for future extensions. - pub flags: CallProcedureFlags, -} - -#[derive(Clone, Copy, Default, PartialEq, Eq)] -pub enum CallProcedureFlags { - #[default] - Default, -} - -impl_st!([] CallProcedureFlags, AlgebraicType::U8); -impl_serialize!([] CallProcedureFlags, (self, ser) => ser.serialize_u8(*self as u8)); -impl_deserialize!([] CallProcedureFlags, de => match de.deserialize_u8()? { - 0 => Ok(Self::Default), - x => Err(D::Error::custom(format_args!("invalid call procedure flag {x}"))), -}); - /// The tag recognized by the host and SDKs to mean no compression of a [`ServerMessage`]. pub const SERVER_MSG_COMPRESSION_TAG_NONE: u8 = 0; @@ -373,8 +326,6 @@ pub enum ServerMessage { SubscribeMultiApplied(SubscribeMultiApplied), /// Sent in response to an `UnsubscribeMulti` message. This contains the matching rows. UnsubscribeMultiApplied(UnsubscribeMultiApplied), - /// Sent in response to a [`CallProcedure`] message. This contains the return value. - ProcedureResult(ProcedureResult), } /// The matching rows of a subscription query. @@ -754,48 +705,6 @@ pub struct OneOffTable { pub rows: F::List, } -/// The result of running a procedure, -/// including the return value of the procedure on success. -/// -/// Sent in response to a [`CallProcedure`] message. -#[derive(SpacetimeType, Debug)] -#[sats(crate = spacetimedb_lib)] -pub struct ProcedureResult { - /// The status of the procedure run. - /// - /// Contains the return value if successful, or the error message if not. - pub status: ProcedureStatus, - /// The time when the reducer started. - /// - /// Note that [`Timestamp`] serializes as `i64` nanoseconds since the Unix epoch. - pub timestamp: Timestamp, - /// The time the procedure took to run. - pub total_host_execution_duration: TimeDuration, - /// The same same client-provided identifier as in the original [`ProcedureCall`] request. - /// - /// Clients use this to correlate the response with the original request. - pub request_id: u32, -} - -/// The status of a procedure call, -/// including the return value on success. -#[derive(SpacetimeType, Debug)] -#[sats(crate = spacetimedb_lib)] -pub enum ProcedureStatus { - /// The procedure ran and returned the enclosed value. - /// - /// All user error handling happens within here; - /// the returned value may be a `Result` or `Option`, - /// or any other type to which the user may ascribe arbitrary meaning. - Returned(F::Single), - /// The reducer was interrupted due to insufficient energy/funds. - /// - /// The procedure may have performed some observable side effects before being interrupted. - OutOfEnergy, - /// The call failed in the host, e.g. due to a type error or unknown procedure name. - InternalError(String), -} - /// Used whenever different formats need to coexist. #[derive(Debug, Clone)] pub enum FormatSwitch { diff --git a/crates/client-api/src/routes/database.rs b/crates/client-api/src/routes/database.rs index 6fecde08bbd..70a2473ec69 100644 --- a/crates/client-api/src/routes/database.rs +++ b/crates/client-api/src/routes/database.rs @@ -8,7 +8,7 @@ use crate::auth::{ }; use crate::routes::subscribe::generate_random_connection_id; pub use crate::util::{ByteStringBody, NameOrIdentity}; -use crate::{log_and_500, ControlStateDelegate, DatabaseDef, Host, NodeDelegate}; +use crate::{log_and_500, ControlStateDelegate, DatabaseDef, NodeDelegate}; use axum::body::{Body, Bytes}; use axum::extract::{Path, Query, State}; use axum::response::{ErrorResponse, IntoResponse}; @@ -20,10 +20,10 @@ use http::StatusCode; use serde::Deserialize; use spacetimedb::database_logger::DatabaseLogger; use spacetimedb::host::module_host::ClientConnectedError; +use spacetimedb::host::ReducerCallError; +use spacetimedb::host::ReducerOutcome; use spacetimedb::host::UpdateDatabaseResult; -use spacetimedb::host::{FunctionArgs, MigratePlanResult}; -use spacetimedb::host::{ModuleHost, ReducerOutcome}; -use spacetimedb::host::{ProcedureCallError, ReducerCallError}; +use spacetimedb::host::{MigratePlanResult, ReducerArgs}; use spacetimedb::identity::Identity; use spacetimedb::messages::control_db::{Database, HostType}; use spacetimedb_client_api_messages::name::{ @@ -31,7 +31,7 @@ use spacetimedb_client_api_messages::name::{ }; use spacetimedb_lib::db::raw_def::v9::RawModuleDefV9; use spacetimedb_lib::identity::AuthCtx; -use spacetimedb_lib::{sats, AlgebraicValue, ProductValue, Timestamp}; +use spacetimedb_lib::{sats, ProductValue, Timestamp}; use spacetimedb_schema::auto_migrate::{ MigrationPolicy as SchemaMigrationPolicy, MigrationToken, PrettyPrintStyle as AutoMigratePrettyPrintStyle, }; @@ -56,23 +56,62 @@ pub async fn call( TypedHeader(content_type): TypedHeader, ByteStringBody(body): ByteStringBody, ) -> axum::response::Result { - assert_content_type_json(content_type)?; - + if content_type != headers::ContentType::json() { + return Err(axum::extract::rejection::MissingJsonContentType::default().into()); + } let caller_identity = auth.claims.identity; - let args = FunctionArgs::Json(body); + let args = ReducerArgs::Json(body); + + let db_identity = name_or_identity.resolve(&worker_ctx).await?; + let database = worker_ctx_find_database(&worker_ctx, &db_identity) + .await? + .ok_or_else(|| { + log::error!("Could not find database: {}", db_identity.to_hex()); + NO_SUCH_DATABASE + })?; + let identity = database.owner_identity; + + let leader = worker_ctx + .leader(database.id) + .await + .map_err(log_and_500)? + .ok_or(StatusCode::NOT_FOUND)?; + let module = leader.module().await.map_err(log_and_500)?; // HTTP callers always need a connection ID to provide to connect/disconnect, // so generate one. let connection_id = generate_random_connection_id(); - let (module, Database { owner_identity, .. }) = find_module_and_database(&worker_ctx, name_or_identity).await?; - - module - .call_identity_connected(auth.into(), connection_id) - .await - .map_err(client_connected_error_to_response)?; + match module.call_identity_connected(auth.into(), connection_id).await { + // If `call_identity_connected` returns `Err(Rejected)`, then the `client_connected` reducer errored, + // meaning the connection was refused. Return 403 forbidden. + Err(ClientConnectedError::Rejected(msg)) => return Err((StatusCode::FORBIDDEN, msg).into()), + // If `call_identity_connected` returns `Err(OutOfEnergy)`, + // then, well, the database is out of energy. + // Return 503 service unavailable. + Err(err @ ClientConnectedError::OutOfEnergy) => { + return Err((StatusCode::SERVICE_UNAVAILABLE, err.to_string()).into()) + } + // If `call_identity_connected` returns `Err(ReducerCall)`, + // something went wrong while invoking the `client_connected` reducer. + // I (pgoldman 2025-03-27) am not really sure how this would happen, + // but we returned 404 not found in this case prior to my editing this code, + // so I guess let's keep doing that. + Err(ClientConnectedError::ReducerCall(e)) => { + return Err((StatusCode::NOT_FOUND, format!("{:#}", anyhow::anyhow!(e))).into()) + } + // If `call_identity_connected` returns `Err(DBError)`, + // then the module didn't define `client_connected`, + // but something went wrong when we tried to insert into `st_client`. + // That's weird and scary, so return 500 internal error. + Err(e @ ClientConnectedError::DBError(_)) => { + return Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into()) + } + // If `call_identity_connected` returns `Ok`, then we can actually call the reducer we want. + Ok(()) => (), + } let result = match module .call_reducer(caller_identity, Some(connection_id), None, None, None, &reducer, args) .await @@ -100,14 +139,17 @@ pub async fn call( } }; - module - .call_identity_disconnected(caller_identity, connection_id) - .await - .map_err(client_disconnected_error_to_response)?; + if let Err(e) = module.call_identity_disconnected(caller_identity, connection_id).await { + // If `call_identity_disconnected` errors, something is very wrong: + // it means we tried to delete the `st_client` row but failed. + // Note that `call_identity_disconnected` swallows errors from the `client_disconnected` reducer. + // Slap a 500 on it and pray. + return Err((StatusCode::INTERNAL_SERVER_ERROR, format!("{:#}", anyhow::anyhow!(e))).into()); + } match result { Ok(result) => { - let (status, body) = reducer_outcome_response(&owner_identity, &reducer, result.outcome); + let (status, body) = reducer_outcome_response(&identity, &reducer, result.outcome); Ok(( status, TypedHeader(SpacetimeEnergyUsed(result.energy_used)), @@ -119,15 +161,7 @@ pub async fn call( } } -fn assert_content_type_json(content_type: headers::ContentType) -> axum::response::Result<()> { - if content_type != headers::ContentType::json() { - Err(axum::extract::rejection::MissingJsonContentType::default().into()) - } else { - Ok(()) - } -} - -fn reducer_outcome_response(owner_identity: &Identity, reducer: &str, outcome: ReducerOutcome) -> (StatusCode, String) { +fn reducer_outcome_response(identity: &Identity, reducer: &str, outcome: ReducerOutcome) -> (StatusCode, String) { match outcome { ReducerOutcome::Committed => (StatusCode::OK, "".to_owned()), ReducerOutcome::Failed(errmsg) => { @@ -135,7 +169,7 @@ fn reducer_outcome_response(owner_identity: &Identity, reducer: &str, outcome: R (StatusCode::from_u16(530).unwrap(), errmsg) } ReducerOutcome::BudgetExceeded => { - log::warn!("Node's energy budget exceeded for identity: {owner_identity} while executing {reducer}"); + log::warn!("Node's energy budget exceeded for identity: {identity} while executing {reducer}"); ( StatusCode::PAYMENT_REQUIRED, "Module energy budget exhausted.".to_owned(), @@ -144,69 +178,6 @@ fn reducer_outcome_response(owner_identity: &Identity, reducer: &str, outcome: R } } -fn client_connected_error_to_response(err: ClientConnectedError) -> ErrorResponse { - match err { - // If `call_identity_connected` returns `Err(Rejected)`, then the `client_connected` reducer errored, - // meaning the connection was refused. Return 403 forbidden. - ClientConnectedError::Rejected(msg) => (StatusCode::FORBIDDEN, msg).into(), - // If `call_identity_connected` returns `Err(OutOfEnergy)`, - // then, well, the database is out of energy. - // Return 503 service unavailable. - ClientConnectedError::OutOfEnergy => (StatusCode::SERVICE_UNAVAILABLE, err.to_string()).into(), - // If `call_identity_connected` returns `Err(ReducerCall)`, - // something went wrong while invoking the `client_connected` reducer. - // I (pgoldman 2025-03-27) am not really sure how this would happen, - // but we returned 404 not found in this case prior to my editing this code, - // so I guess let's keep doing that. - ClientConnectedError::ReducerCall(e) => (StatusCode::NOT_FOUND, format!("{:#}", anyhow::anyhow!(e))).into(), - // If `call_identity_connected` returns `Err(DBError)`, - // then the module didn't define `client_connected`, - // but something went wrong when we tried to insert into `st_client`. - // That's weird and scary, so return 500 internal error. - ClientConnectedError::DBError(_) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into(), - } -} - -/// If `call_identity_disconnected` errors, something is very wrong: -/// it means we tried to delete the `st_client` row but failed. -/// -/// Note that `call_identity_disconnected` swallows errors from the `client_disconnected` reducer. -/// Slap a 500 on it and pray. -fn client_disconnected_error_to_response(err: ReducerCallError) -> ErrorResponse { - (StatusCode::INTERNAL_SERVER_ERROR, format!("{:#}", anyhow::anyhow!(err))).into() -} - -async fn find_leader_and_database( - worker_ctx: &S, - name_or_identity: NameOrIdentity, -) -> axum::response::Result<(Host, Database)> { - let db_identity = name_or_identity.resolve(worker_ctx).await?; - let database = worker_ctx_find_database(worker_ctx, &db_identity) - .await? - .ok_or_else(|| { - log::error!("Could not find database: {}", db_identity.to_hex()); - NO_SUCH_DATABASE - })?; - - let leader = worker_ctx - .leader(database.id) - .await - .map_err(log_and_500)? - .ok_or(StatusCode::NOT_FOUND)?; - - Ok((leader, database)) -} - -async fn find_module_and_database( - worker_ctx: &S, - name_or_identity: NameOrIdentity, -) -> axum::response::Result<(ModuleHost, Database)> { - let (leader, database) = find_leader_and_database(worker_ctx, name_or_identity).await?; - let module = leader.module().await.map_err(log_and_500)?; - - Ok((module, database)) -} - #[derive(Debug, derive_more::From)] pub enum DBCallErr { HandlerError(ErrorResponse), @@ -214,93 +185,6 @@ pub enum DBCallErr { InstanceNotScheduled, } -#[derive(Deserialize)] -pub struct ProcedureParams { - name_or_identity: NameOrIdentity, - procedure: String, -} - -async fn procedure( - State(worker_ctx): State, - Extension(auth): Extension, - Path(ProcedureParams { - name_or_identity, - procedure, - }): Path, - TypedHeader(content_type): TypedHeader, - ByteStringBody(body): ByteStringBody, -) -> axum::response::Result { - assert_content_type_json(content_type)?; - - let caller_identity = auth.claims.identity; - - let args = FunctionArgs::Json(body); - - let (module, _) = find_module_and_database(&worker_ctx, name_or_identity).await?; - - // HTTP callers always need a connection ID to provide to connect/disconnect, - // so generate one. - let connection_id = generate_random_connection_id(); - - // Call the database's `client_connected` reducer, if any. - // If it fails or rejects the connection, bail. - module - .call_identity_connected(auth.into(), connection_id) - .await - .map_err(client_connected_error_to_response)?; - - let result = match module - .call_procedure(caller_identity, Some(connection_id), None, &procedure, args) - .await - { - Ok(res) => Ok(res), - Err(e) => { - let status_code = match e { - ProcedureCallError::Args(_) => { - log::debug!("Attempt to call reducer with invalid arguments"); - StatusCode::BAD_REQUEST - } - ProcedureCallError::NoSuchModule(_) => StatusCode::NOT_FOUND, - ProcedureCallError::NoSuchProcedure => { - log::debug!("Attempt to call non-existent procedure {procedure}"); - StatusCode::NOT_FOUND - } - ProcedureCallError::OutOfEnergy => StatusCode::PAYMENT_REQUIRED, - ProcedureCallError::InternalError(_) => StatusCode::INTERNAL_SERVER_ERROR, - }; - log::error!("Error while invoking procedure {e:#}"); - Err((status_code, format!("{:#}", anyhow::anyhow!(e)))) - } - }; - - module - .call_identity_disconnected(caller_identity, connection_id) - .await - .map_err(client_disconnected_error_to_response)?; - - match result { - Ok(result) => { - // Procedures don't assign a special meaning to error returns, unlike reducers, - // as there's no transaction for them to automatically abort. - // Instead, we just pass on their return value with the OK status so long as we successfully invoked the procedure. - let (status, body) = procedure_outcome_response(result.return_val); - Ok(( - status, - TypedHeader(SpacetimeExecutionDurationMicros(result.execution_duration)), - body, - )) - } - Err(e) => Err((e.0, e.1).into()), - } -} - -fn procedure_outcome_response(return_val: AlgebraicValue) -> (StatusCode, axum::response::Response) { - ( - StatusCode::OK, - axum::Json(sats::serde::SerdeWrapper(return_val)).into_response(), - ) -} - #[derive(Deserialize)] pub struct SchemaParams { name_or_identity: NameOrIdentity, @@ -325,7 +209,17 @@ pub async fn schema( where S: ControlStateDelegate + NodeDelegate, { - let (module, _) = find_module_and_database(&worker_ctx, name_or_identity).await?; + let db_identity = name_or_identity.resolve(&worker_ctx).await?; + let database = worker_ctx_find_database(&worker_ctx, &db_identity) + .await? + .ok_or(NO_SUCH_DATABASE)?; + + let leader = worker_ctx + .leader(database.id) + .await + .map_err(log_and_500)? + .ok_or(StatusCode::NOT_FOUND)?; + let module = leader.module().await.map_err(log_and_500)?; let module_def = &module.info.module_def; let response_json = match version { @@ -513,11 +407,20 @@ where // Anyone is authorized to execute SQL queries. The SQL engine will determine // which queries this identity is allowed to execute against the database. - let (host, database) = find_leader_and_database(&worker_ctx, name_or_identity).await?; + let db_identity = name_or_identity.resolve(&worker_ctx).await?; + let database = worker_ctx_find_database(&worker_ctx, &db_identity) + .await? + .ok_or(NO_SUCH_DATABASE)?; let auth = AuthCtx::new(database.owner_identity, caller_identity); log::debug!("auth: {auth:?}"); + let host = worker_ctx + .leader(database.id) + .await + .map_err(log_and_500)? + .ok_or(StatusCode::NOT_FOUND)?; + host.exec_sql(auth, database, confirmed, sql).await } @@ -1037,8 +940,6 @@ pub struct DatabaseRoutes { pub subscribe_get: MethodRouter, /// POST: /database/:name_or_identity/call/:reducer pub call_reducer_post: MethodRouter, - /// POST: /database/:name_or_identity/procedure/:reducer - pub call_procedure_post: MethodRouter, /// GET: /database/:name_or_identity/schema pub schema_get: MethodRouter, /// GET: /database/:name_or_identity/logs @@ -1068,7 +969,6 @@ where identity_get: get(get_identity::), subscribe_get: get(handle_websocket::), call_reducer_post: post(call::), - call_procedure_post: post(procedure::), schema_get: get(schema::), logs_get: get(logs::), sql_post: post(sql::), @@ -1093,7 +993,6 @@ where .route("/identity", self.identity_get) .route("/subscribe", self.subscribe_get) .route("/call/:reducer", self.call_reducer_post) - .route("/procedure/:procedure", self.call_procedure_post) .route("/schema", self.schema_get) .route("/logs", self.logs_get) .route("/sql", self.sql_post) diff --git a/crates/core/src/client/client_connection.rs b/crates/core/src/client/client_connection.rs index 7f3b3b24003..85d43482d16 100644 --- a/crates/core/src/client/client_connection.rs +++ b/crates/core/src/client/client_connection.rs @@ -7,14 +7,13 @@ use std::sync::Arc; use std::task::{Context, Poll}; use std::time::{Instant, SystemTime}; -use super::messages::{OneOffQueryResponseMessage, ProcedureResultMessage, SerializableMessage}; +use super::messages::{OneOffQueryResponseMessage, SerializableMessage}; use super::{message_handlers, ClientActorId, MessageHandleError}; use crate::db::relational_db::RelationalDB; use crate::error::DBError; use crate::host::module_host::ClientConnectedError; -use crate::host::{FunctionArgs, ModuleHost, NoSuchModule, ReducerCallError, ReducerCallResult}; +use crate::host::{ModuleHost, NoSuchModule, ReducerArgs, ReducerCallError, ReducerCallResult}; use crate::messages::websocket::Subscribe; -use crate::subscription::module_subscription_manager::BroadcastError; use crate::util::asyncify; use crate::util::prometheus_handle::IntGaugeExt; use crate::worker_metrics::WORKER_METRICS; @@ -810,7 +809,7 @@ impl ClientConnection { pub async fn call_reducer( &self, reducer: &str, - args: FunctionArgs, + args: ReducerArgs, request_id: RequestId, timer: Instant, flags: CallReducerFlags, @@ -835,29 +834,6 @@ impl ClientConnection { .await } - pub async fn call_procedure( - &self, - procedure: &str, - args: FunctionArgs, - request_id: RequestId, - timer: Instant, - ) -> Result<(), BroadcastError> { - let res = self - .module() - .call_procedure( - self.id.identity, - Some(self.id.connection_id), - Some(timer), - procedure, - args, - ) - .await; - - self.module() - .subscriptions() - .send_procedure_message(self.sender(), ProcedureResultMessage::from_result(&res, request_id)) - } - pub async fn subscribe_single( &self, subscription: SubscribeSingle, diff --git a/crates/core/src/client/message_handlers.rs b/crates/core/src/client/message_handlers.rs index 313b87d0281..e2077d948e4 100644 --- a/crates/core/src/client/message_handlers.rs +++ b/crates/core/src/client/message_handlers.rs @@ -2,11 +2,10 @@ use super::messages::{SubscriptionUpdateMessage, SwitchedServerMessage, ToProtoc use super::{ClientConnection, DataMessage, Protocol}; use crate::energy::EnergyQuanta; use crate::host::module_host::{EventStatus, ModuleEvent, ModuleFunctionCall}; -use crate::host::{FunctionArgs, ReducerId}; +use crate::host::{ReducerArgs, ReducerId}; use crate::identity::Identity; use crate::messages::websocket::{CallReducer, ClientMessage, OneOffQuery}; use crate::worker_metrics::WORKER_METRICS; -use spacetimedb_client_api_messages::websocket::CallProcedure; use spacetimedb_datastore::execution_context::WorkloadType; use spacetimedb_lib::de::serde::DeserializeWrapper; use spacetimedb_lib::identity::RequestId; @@ -37,14 +36,14 @@ pub async fn handle(client: &ClientConnection, message: DataMessage, timer: Inst let DeserializeWrapper(message) = serde_json::from_str::>>>(&text)?; message.map_args(|s| { - FunctionArgs::Json(match s { + ReducerArgs::Json(match s { Cow::Borrowed(s) => text.slice_ref(s), Cow::Owned(string) => string.into(), }) }) } DataMessage::Binary(message_buf) => bsatn::from_slice::>(&message_buf)? - .map_args(|b| FunctionArgs::Bsatn(message_buf.slice_ref(b))), + .map_args(|b| ReducerArgs::Bsatn(message_buf.slice_ref(b))), }; let module = client.module(); @@ -130,27 +129,9 @@ pub async fn handle(client: &ClientConnection, message: DataMessage, timer: Inst .observe(timer.elapsed().as_secs_f64()); res.map_err(|err| (None, None, err)) } - ClientMessage::CallProcedure(CallProcedure { - ref procedure, - args, - request_id, - flags: _, - }) => { - let res = client.call_procedure(procedure, args, request_id, timer).await; - WORKER_METRICS - .request_round_trip - .with_label_values(&WorkloadType::Procedure, &database_identity, procedure) - .observe(timer.elapsed().as_secs_f64()); - if let Err(e) = res { - log::warn!("Procedure call failed: {e:#}"); - } - // `ClientConnection::call_procedure` handles sending the error message to the client if the call fails, - // so we don't need to return an `Err` here. - Ok(()) - } }; - res.map_err(|(reducer_name, reducer_id, err)| MessageExecutionError { - reducer: reducer_name.cloned(), + res.map_err(|(reducer, reducer_id, err)| MessageExecutionError { + reducer: reducer.cloned(), reducer_id, caller_identity: client.id.identity, caller_connection_id: Some(client.id.connection_id), diff --git a/crates/core/src/client/messages.rs b/crates/core/src/client/messages.rs index eb2ace727e5..67f3b90397b 100644 --- a/crates/core/src/client/messages.rs +++ b/crates/core/src/client/messages.rs @@ -1,6 +1,6 @@ use super::{ClientConfig, DataMessage, Protocol}; -use crate::host::module_host::{EventStatus, ModuleEvent, ProcedureCallError}; -use crate::host::{ArgsTuple, ProcedureCallResult}; +use crate::host::module_host::{EventStatus, ModuleEvent}; +use crate::host::ArgsTuple; use crate::messages::websocket as ws; use crate::subscription::websocket_building::{brotli_compress, decide_compression, gzip_compress}; use bytes::{BufMut, Bytes, BytesMut}; @@ -13,7 +13,7 @@ use spacetimedb_client_api_messages::websocket::{ use spacetimedb_datastore::execution_context::WorkloadType; use spacetimedb_lib::identity::RequestId; use spacetimedb_lib::ser::serde::SerializeWrapper; -use spacetimedb_lib::{AlgebraicValue, ConnectionId, TimeDuration, Timestamp}; +use spacetimedb_lib::{ConnectionId, TimeDuration}; use spacetimedb_primitives::TableId; use spacetimedb_sats::bsatn; use std::sync::Arc; @@ -167,7 +167,6 @@ pub enum SerializableMessage { Subscribe(SubscriptionUpdateMessage), Subscription(SubscriptionMessage), TxUpdate(TransactionUpdateMessage), - ProcedureResult(ProcedureResultMessage), } impl SerializableMessage { @@ -178,7 +177,7 @@ impl SerializableMessage { Self::Subscribe(msg) => Some(msg.num_rows()), Self::Subscription(msg) => Some(msg.num_rows()), Self::TxUpdate(msg) => Some(msg.num_rows()), - Self::Identity(_) | Self::ProcedureResult(_) => None, + Self::Identity(_) => None, } } @@ -195,7 +194,6 @@ impl SerializableMessage { }, Self::TxUpdate(_) => Some(WorkloadType::Update), Self::Identity(_) => None, - Self::ProcedureResult(_) => Some(WorkloadType::Procedure), } } } @@ -210,7 +208,6 @@ impl ToProtocol for SerializableMessage { SerializableMessage::Subscribe(msg) => msg.to_protocol(protocol), SerializableMessage::TxUpdate(msg) => msg.to_protocol(protocol), SerializableMessage::Subscription(msg) => msg.to_protocol(protocol), - SerializableMessage::ProcedureResult(msg) => msg.to_protocol(protocol), } } } @@ -587,98 +584,3 @@ fn convert(msg: OneOffQueryResponseMessage) -> ws::Server total_host_execution_duration: msg.total_host_execution_duration, }) } - -/// Result of a procedure run. -#[derive(Debug)] -pub enum ProcedureStatus { - /// The procedure ran to completion and returned this value. - Returned(AlgebraicValue), - /// The procedure was terminated due to running out of energy. - OutOfEnergy, - /// The procedure failed to run to completion. This string describes the failure. - InternalError(String), -} - -/// Will be sent to the caller of a procedure after that procedure finishes running. -#[derive(Debug)] -pub struct ProcedureResultMessage { - status: ProcedureStatus, - timestamp: Timestamp, - total_host_execution_duration: TimeDuration, - request_id: u32, -} - -impl ProcedureResultMessage { - pub fn from_result(res: &Result, request_id: RequestId) -> Self { - let (status, timestamp, execution_duration) = match res { - Ok(ProcedureCallResult { - return_val, - execution_duration, - start_timestamp, - }) => ( - ProcedureStatus::Returned(return_val.clone()), - *start_timestamp, - TimeDuration::from(*execution_duration), - ), - Err(err) => ( - match err { - ProcedureCallError::OutOfEnergy => ProcedureStatus::OutOfEnergy, - _ => ProcedureStatus::InternalError(format!("{err}")), - }, - Timestamp::UNIX_EPOCH, - TimeDuration::ZERO, - ), - }; - - ProcedureResultMessage { - status, - timestamp, - total_host_execution_duration: execution_duration, - request_id, - } - } -} - -impl ToProtocol for ProcedureResultMessage { - type Encoded = SwitchedServerMessage; - - fn to_protocol(self, protocol: Protocol) -> Self::Encoded { - fn convert( - msg: ProcedureResultMessage, - serialize_value: impl Fn(AlgebraicValue) -> F::Single, - ) -> ws::ServerMessage { - let ProcedureResultMessage { - status, - timestamp, - total_host_execution_duration, - request_id, - } = msg; - let status = match status { - ProcedureStatus::InternalError(msg) => ws::ProcedureStatus::InternalError(msg), - ProcedureStatus::OutOfEnergy => ws::ProcedureStatus::OutOfEnergy, - ProcedureStatus::Returned(val) => ws::ProcedureStatus::Returned(serialize_value(val)), - }; - ws::ServerMessage::ProcedureResult(ws::ProcedureResult { - status, - timestamp, - total_host_execution_duration, - request_id, - }) - } - - // Note that procedure returns are sent only to the caller, not broadcast to all subscribers, - // so we don't have to bother with memoizing the serialization the way we do for reducer args. - match protocol { - Protocol::Binary => FormatSwitch::Bsatn(convert(self, |val| { - bsatn::to_vec(&val) - .expect("Procedure return value failed to serialize to BSATN") - .into() - })), - Protocol::Text => FormatSwitch::Json(convert(self, |val| { - serde_json::to_string(&SerializeWrapper(val)) - .expect("Procedure return value failed to serialize to JSON") - .into() - })), - } - } -} diff --git a/crates/core/src/db/relational_db.rs b/crates/core/src/db/relational_db.rs index 4b7a533c008..c7b05a7bbfb 100644 --- a/crates/core/src/db/relational_db.rs +++ b/crates/core/src/db/relational_db.rs @@ -42,7 +42,7 @@ use spacetimedb_primitives::*; use spacetimedb_sats::algebraic_type::fmt::fmt_algebraic_type; use spacetimedb_sats::memory_usage::MemoryUsage; use spacetimedb_sats::{AlgebraicType, AlgebraicValue, ProductType, ProductValue}; -use spacetimedb_schema::def::{ModuleDef, TableDef, ViewDef}; +use spacetimedb_schema::def::{ModuleDef, TableDef}; use spacetimedb_schema::schema::{ ColumnSchema, IndexSchema, RowLevelSecuritySchema, Schema, SequenceSchema, TableSchema, }; @@ -57,7 +57,7 @@ use std::collections::HashSet; use std::fmt; use std::fs::File; use std::io; -use std::ops::{Bound, RangeBounds}; +use std::ops::RangeBounds; use std::path::Path; use std::sync::Arc; use tokio::sync::watch; @@ -1055,32 +1055,6 @@ impl RelationalDB { Ok(self.inner.create_table_mut_tx(tx, schema)?) } - pub fn drop_table(&self, tx: &mut MutTx, table_id: TableId) -> Result<(), DBError> { - let table_name = self - .table_name_from_id_mut(tx, table_id)? - .map(|name| name.to_string()) - .unwrap_or_default(); - Ok(self.inner.drop_table_mut_tx(tx, table_id).map(|_| { - DB_METRICS - .rdb_num_table_rows - .with_label_values(&self.database_identity, &table_id.into(), &table_name) - .set(0) - })?) - } - - pub fn create_view( - &self, - tx: &mut MutTx, - module_def: &ModuleDef, - view_def: &ViewDef, - ) -> Result<(ViewId, TableId), DBError> { - Ok(tx.create_view(module_def, view_def)?) - } - - pub fn drop_view(&self, tx: &mut MutTx, view_id: ViewId) -> Result<(), DBError> { - Ok(tx.drop_view(view_id)?) - } - pub fn create_table_for_test_with_the_works( &self, name: &str, @@ -1158,6 +1132,19 @@ impl RelationalDB { self.create_table_for_test_with_the_works(name, schema, &indexes[..], &[], StAccess::Public) } + pub fn drop_table(&self, tx: &mut MutTx, table_id: TableId) -> Result<(), DBError> { + let table_name = self + .table_name_from_id_mut(tx, table_id)? + .map(|name| name.to_string()) + .unwrap_or_default(); + Ok(self.inner.drop_table_mut_tx(tx, table_id).map(|_| { + DB_METRICS + .rdb_num_table_rows + .with_label_values(&self.database_identity, &table_id.into(), &table_name) + .set(0) + })?) + } + /// Rename a table. /// /// Sets the name of the table to `new_name` regardless of the previous value. This is a @@ -1168,10 +1155,6 @@ impl RelationalDB { Ok(self.inner.rename_table_mut_tx(tx, table_id, new_name)?) } - pub fn view_id_from_name_mut(&self, tx: &MutTx, view_name: &str) -> Result, DBError> { - Ok(self.inner.view_id_from_name_mut_tx(tx, view_name)?) - } - pub fn table_id_from_name_mut(&self, tx: &MutTx, table_name: &str) -> Result, DBError> { Ok(self.inner.table_id_from_name_mut_tx(tx, table_name)?) } @@ -1353,15 +1336,7 @@ impl RelationalDB { prefix_elems: ColId, rstart: &[u8], rend: &[u8], - ) -> Result< - ( - TableId, - Bound, - Bound, - impl Iterator>, - ), - DBError, - > { + ) -> Result<(TableId, impl Iterator>), DBError> { Ok(tx.index_scan_range(index_id, prefix, prefix_elems, rstart, rend)?) } diff --git a/crates/core/src/db/update.rs b/crates/core/src/db/update.rs index d4d3b6648c3..9edd7cc0244 100644 --- a/crates/core/src/db/update.rs +++ b/crates/core/src/db/update.rs @@ -7,7 +7,7 @@ use spacetimedb_lib::identity::AuthCtx; use spacetimedb_lib::AlgebraicValue; use spacetimedb_primitives::{ColSet, TableId}; use spacetimedb_schema::auto_migrate::{AutoMigratePlan, ManualMigratePlan, MigratePlan}; -use spacetimedb_schema::def::{TableDef, ViewDef}; +use spacetimedb_schema::def::TableDef; use spacetimedb_schema::schema::{column_schemas_from_defs, IndexSchema, Schema, SequenceSchema, TableSchema}; /// The logger used for by [`update_database`] and friends. @@ -137,17 +137,6 @@ fn auto_migrate_database( stdb.create_table(tx, table_schema)?; } - spacetimedb_schema::auto_migrate::AutoMigrateStep::AddView(view_name) => { - let view_def: &ViewDef = plan.new.expect_lookup(view_name); - stdb.create_view(tx, plan.new, view_def)?; - } - spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveView(view_name) => { - let view_id = stdb.view_id_from_name_mut(tx, view_name)?.unwrap(); - stdb.drop_view(tx, view_id)?; - } - spacetimedb_schema::auto_migrate::AutoMigrateStep::UpdateView(_) => { - unimplemented!("Recompute view and update its backing table") - } spacetimedb_schema::auto_migrate::AutoMigrateStep::AddIndex(index_name) => { let table_def = plan.new.stored_in_table_def(index_name).unwrap(); let index_def = table_def.indexes.get(index_name).unwrap(); diff --git a/crates/core/src/host/host_controller.rs b/crates/core/src/host/host_controller.rs index f81e5d6b018..cc266b30351 100644 --- a/crates/core/src/host/host_controller.rs +++ b/crates/core/src/host/host_controller.rs @@ -29,7 +29,7 @@ use spacetimedb_datastore::db_metrics::data_size::DATA_SIZE_METRICS; use spacetimedb_datastore::db_metrics::DB_METRICS; use spacetimedb_datastore::traits::Program; use spacetimedb_durability::{self as durability}; -use spacetimedb_lib::{hash_bytes, AlgebraicValue, Identity, Timestamp}; +use spacetimedb_lib::{hash_bytes, Identity}; use spacetimedb_paths::server::{ReplicaDir, ServerDataDir}; use spacetimedb_paths::FromPathUnchecked; use spacetimedb_sats::hash::Hash; @@ -170,13 +170,6 @@ impl From<&EventStatus> for ReducerOutcome { } } -#[derive(Clone, Debug)] -pub struct ProcedureCallResult { - pub return_val: AlgebraicValue, - pub execution_duration: Duration, - pub start_timestamp: Timestamp, -} - impl HostController { pub fn new( data_dir: Arc, diff --git a/crates/core/src/host/instance_env.rs b/crates/core/src/host/instance_env.rs index 76b7c6d8882..6825f1a50de 100644 --- a/crates/core/src/host/instance_env.rs +++ b/crates/core/src/host/instance_env.rs @@ -11,7 +11,7 @@ use smallvec::SmallVec; use spacetimedb_datastore::locking_tx_datastore::state_view::StateView; use spacetimedb_datastore::locking_tx_datastore::MutTxId; use spacetimedb_lib::{ConnectionId, Identity, Timestamp}; -use spacetimedb_primitives::{ColId, ColList, IndexId, TableId, ViewId}; +use spacetimedb_primitives::{ColId, ColList, IndexId, TableId}; use spacetimedb_sats::{ bsatn::{self, ToBsatn}, buffer::{CountWriter, TeeWriter}, @@ -31,7 +31,6 @@ pub struct InstanceEnv { pub tx: TxSlot, /// The timestamp the current reducer began running. pub start_time: Timestamp, - pub view_id: Option, } #[derive(Clone, Default)] @@ -173,7 +172,6 @@ impl InstanceEnv { scheduler, tx: TxSlot::default(), start_time: Timestamp::now(), - view_id: None, } } @@ -182,15 +180,9 @@ impl InstanceEnv { &self.replica_ctx.database.database_identity } - /// Signal to this `InstanceEnv` that a reducer or procedure call is beginning. - pub fn start_funcall(&mut self, ts: Timestamp) { + /// Signal to this `InstanceEnv` that a reducer call is beginning. + pub fn start_reducer(&mut self, ts: Timestamp) { self.start_time = ts; - self.view_id = None; - } - - /// Signal to this `InstanceEnv` that a we're going to execute a view and compute its read set. - pub fn start_view(&mut self, view_id: ViewId) { - self.view_id = Some(view_id); } fn get_tx(&self) -> Result + '_, GetTxError> { @@ -383,7 +375,7 @@ impl InstanceEnv { let tx = &mut *self.tx.get()?; // Find all rows in the table to delete. - let (table_id, _, _, iter) = stdb.index_scan_range(tx, index_id, prefix, prefix_elems, rstart, rend)?; + let (table_id, iter) = stdb.index_scan_range(tx, index_id, prefix, prefix_elems, rstart, rend)?; // Re. `SmallVec`, `delete_by_field` only cares about 1 element, so optimize for that. let rows_to_delete = iter.map(|row_ref| row_ref.pointer()).collect::>(); @@ -468,11 +460,7 @@ impl InstanceEnv { let tx = &mut *self.get_tx()?; // Query the row count for id. - stdb.table_row_count_mut(tx, table_id) - .ok_or(NodesError::TableNotFound) - .inspect(|_| { - tx.record_table_scan(self.view_id, table_id); - }) + stdb.table_row_count_mut(tx, table_id).ok_or(NodesError::TableNotFound) } #[tracing::instrument(level = "trace", skip_all)] @@ -496,8 +484,6 @@ impl InstanceEnv { &mut bytes_scanned, ); - tx.record_table_scan(self.view_id, table_id); - tx.metrics.rows_scanned += rows_scanned; tx.metrics.bytes_scanned += bytes_scanned; @@ -522,13 +508,11 @@ impl InstanceEnv { let mut bytes_scanned = 0; // Open index iterator - let (table_id, lower, upper, iter) = stdb.index_scan_range(tx, index_id, prefix, prefix_elems, rstart, rend)?; + let (_, iter) = stdb.index_scan_range(tx, index_id, prefix, prefix_elems, rstart, rend)?; // Scan the index and serialize rows to bsatn let chunks = ChunkedWriter::collect_iter(pool, iter, &mut rows_scanned, &mut bytes_scanned); - tx.record_index_scan(self.view_id, table_id, index_id, lower, upper); - tx.metrics.index_seeks += 1; tx.metrics.rows_scanned += rows_scanned; tx.metrics.bytes_scanned += bytes_scanned; @@ -664,7 +648,6 @@ mod test { scheduler, tx: TxSlot::default(), start_time: Timestamp::now(), - view_id: None, }, runtime, )) diff --git a/crates/core/src/host/mod.rs b/crates/core/src/host/mod.rs index 8cbcab7b926..935de8c7e6b 100644 --- a/crates/core/src/host/mod.rs +++ b/crates/core/src/host/mod.rs @@ -5,9 +5,10 @@ use derive_more::Display; use enum_map::Enum; use once_cell::sync::OnceCell; use spacetimedb_lib::bsatn; -use spacetimedb_lib::de::{serde::SeedWrapper, DeserializeSeed}; +use spacetimedb_lib::de::serde::SeedWrapper; +use spacetimedb_lib::de::DeserializeSeed; use spacetimedb_lib::ProductValue; -use spacetimedb_schema::def::deserialize::{ArgsSeed, FunctionDef}; +use spacetimedb_schema::def::deserialize::ReducerArgsDeserializeSeed; mod disk_storage; mod host_controller; @@ -24,43 +25,43 @@ mod wasm_common; pub use disk_storage::DiskStorage; pub use host_controller::{ - extract_schema, ExternalDurability, ExternalStorage, HostController, MigratePlanResult, ProcedureCallResult, - ProgramStorage, ReducerCallResult, ReducerOutcome, + extract_schema, ExternalDurability, ExternalStorage, HostController, MigratePlanResult, ProgramStorage, + ReducerCallResult, ReducerOutcome, }; -pub use module_host::{ModuleHost, NoSuchModule, ProcedureCallError, ReducerCallError, UpdateDatabaseResult}; +pub use module_host::{ModuleHost, NoSuchModule, ReducerCallError, UpdateDatabaseResult}; pub use scheduler::Scheduler; -/// Encoded arguments to a database function. -/// -/// A database function is either a reducer or a procedure. #[derive(Debug)] -pub enum FunctionArgs { +pub enum ReducerArgs { Json(ByteString), Bsatn(Bytes), Nullary, } -impl FunctionArgs { - fn into_tuple(self, seed: ArgsSeed<'_, Def>) -> Result { - self._into_tuple(seed).map_err(|err| InvalidFunctionArguments { +impl ReducerArgs { + fn into_tuple(self, seed: ReducerArgsDeserializeSeed) -> Result { + self._into_tuple(seed).map_err(|err| InvalidReducerArguments { err, - function_name: seed.name().into(), + reducer: (*seed.reducer_def().name).into(), }) } - fn _into_tuple(self, seed: ArgsSeed<'_, Def>) -> anyhow::Result { + fn _into_tuple(self, seed: ReducerArgsDeserializeSeed) -> anyhow::Result { Ok(match self { - FunctionArgs::Json(json) => ArgsTuple { + ReducerArgs::Json(json) => ArgsTuple { tuple: from_json_seed(&json, SeedWrapper(seed))?, bsatn: OnceCell::new(), json: OnceCell::with_value(json), }, - FunctionArgs::Bsatn(bytes) => ArgsTuple { + ReducerArgs::Bsatn(bytes) => ArgsTuple { tuple: seed.deserialize(bsatn::Deserializer::new(&mut &bytes[..]))?, bsatn: OnceCell::with_value(bytes), json: OnceCell::new(), }, - FunctionArgs::Nullary => { - anyhow::ensure!(seed.params().elements.is_empty(), "failed to typecheck args"); + ReducerArgs::Nullary => { + anyhow::ensure!( + seed.reducer_def().params.elements.is_empty(), + "failed to typecheck args" + ); ArgsTuple::nullary() } }) @@ -105,33 +106,14 @@ impl Default for ArgsTuple { // TODO(noa): replace imports from this module with imports straight from primitives. pub use spacetimedb_primitives::ReducerId; -/// Inner error type for [`InvalidReducerArguments`] and [`InvalidProcedureArguments`]. #[derive(thiserror::Error, Debug)] -#[error("invalid arguments for function {function_name}: {err}")] -pub struct InvalidFunctionArguments { +#[error("invalid arguments for reducer {reducer}: {err}")] +pub struct InvalidReducerArguments { #[source] err: anyhow::Error, - function_name: Box, + reducer: Box, } -/// Newtype over [`InvalidFunctionArguments`] which renders with the word "reducer". -#[derive(thiserror::Error, Debug)] -#[error("invalid arguments for reducer {}: {}", .0.function_name, .0.err)] -pub struct InvalidReducerArguments( - #[from] - #[source] - InvalidFunctionArguments, -); - -/// Newtype over [`InvalidFunctionArguments`] which renders with the word "procedure". -#[derive(thiserror::Error, Debug)] -#[error("invalid arguments for procedure {}: {}", .0.function_name, .0.err)] -pub struct InvalidProcedureArguments( - #[from] - #[source] - InvalidFunctionArguments, -); - fn from_json_seed<'de, T: serde::de::DeserializeSeed<'de>>(s: &'de str, seed: T) -> anyhow::Result { let mut de = serde_json::Deserializer::from_str(s); let mut track = serde_path_to_error::Track::new(); @@ -167,6 +149,4 @@ pub enum AbiCall { GetJwt, VolatileNonatomicScheduleImmediate, - - ProcedureSleepUntil, } diff --git a/crates/core/src/host/module_host.rs b/crates/core/src/host/module_host.rs index 1fde862906f..a296f48d508 100644 --- a/crates/core/src/host/module_host.rs +++ b/crates/core/src/host/module_host.rs @@ -1,7 +1,4 @@ -use super::{ - ArgsTuple, FunctionArgs, InvalidProcedureArguments, InvalidReducerArguments, ProcedureCallResult, - ReducerCallResult, ReducerId, ReducerOutcome, Scheduler, -}; +use super::{ArgsTuple, InvalidReducerArguments, ReducerArgs, ReducerCallResult, ReducerId, ReducerOutcome, Scheduler}; use crate::client::messages::{OneOffQueryResponseMessage, SerializableMessage}; use crate::client::{ClientActorId, ClientConnectionSender}; use crate::database_logger::{LogLevel, Record}; @@ -10,7 +7,6 @@ use crate::energy::EnergyQuanta; use crate::error::DBError; use crate::estimation::estimate_rows_scanned; use crate::hash::Hash; -use crate::host::InvalidFunctionArguments; use crate::identity::Identity; use crate::messages::control_db::{Database, HostType}; use crate::module_host_context::ModuleCreationContext; @@ -47,17 +43,16 @@ use spacetimedb_lib::identity::{AuthCtx, RequestId}; use spacetimedb_lib::metrics::ExecutionMetrics; use spacetimedb_lib::ConnectionId; use spacetimedb_lib::Timestamp; -use spacetimedb_primitives::{ProcedureId, TableId}; +use spacetimedb_primitives::TableId; use spacetimedb_query::compile_subscription; use spacetimedb_sats::ProductValue; use spacetimedb_schema::auto_migrate::{AutoMigrateError, MigrationPolicy}; -use spacetimedb_schema::def::deserialize::ArgsSeed; -use spacetimedb_schema::def::{ModuleDef, ProcedureDef, ReducerDef, TableDef, ViewDef}; +use spacetimedb_schema::def::deserialize::ReducerArgsDeserializeSeed; +use spacetimedb_schema::def::{ModuleDef, ReducerDef, TableDef}; use spacetimedb_schema::schema::{Schema, TableSchema}; use spacetimedb_vm::relation::RelValue; use std::collections::VecDeque; use std::fmt; -use std::future::Future; use std::sync::atomic::AtomicBool; use std::sync::{Arc, Weak}; use std::time::{Duration, Instant}; @@ -402,13 +397,6 @@ impl Instance { Instance::Js(inst) => inst.call_reducer(tx, params), } } - - async fn call_procedure(&mut self, params: CallProcedureParams) -> Result { - match self { - Instance::Wasm(inst) => inst.call_procedure(params).await, - Instance::Js(inst) => inst.call_procedure(params).await, - } - } } /// Creates the table for `table_def` in `stdb`. @@ -424,18 +412,6 @@ pub fn create_table_from_def( Ok(()) } -/// Creates the table for `view_def` in `stdb`. -pub fn create_table_from_view_def( - stdb: &RelationalDB, - tx: &mut MutTxId, - module_def: &ModuleDef, - view_def: &ViewDef, -) -> anyhow::Result<()> { - stdb.create_view(tx, module_def, view_def) - .with_context(|| format!("failed to create table for view {}", &view_def.name))?; - Ok(()) -} - /// If the module instance's replica_ctx is uninitialized, initialize it. fn init_database( replica_ctx: &ReplicaContext, @@ -459,15 +435,6 @@ fn init_database( logger.info(&format!("Creating table `{}`", &def.name)); create_table_from_def(stdb, tx, module_def, def)?; } - - let mut view_defs: Vec<_> = module_def.views().collect(); - view_defs.sort_by(|a, b| a.name.cmp(&b.name)); - - for def in view_defs { - logger.info(&format!("Creating table for view `{}`", &def.name)); - create_table_from_view_def(stdb, tx, module_def, def)?; - } - // Insert the late-bound row-level security expressions. for rls in module_def.row_level_security() { logger.info(&format!("Creating row level security `{}`", rls.sql)); @@ -528,15 +495,6 @@ pub struct CallReducerParams { pub args: ArgsTuple, } -pub struct CallProcedureParams { - pub timestamp: Timestamp, - pub caller_identity: Identity, - pub caller_connection_id: ConnectionId, - pub timer: Option, - pub procedure_id: ProcedureId, - pub args: ArgsTuple, -} - /// Holds a [`Module`] and a set of [`Instance`]s from it, /// and allocates the [`Instance`]s to be used for function calls. /// @@ -690,20 +648,6 @@ pub enum ReducerCallError { LifecycleReducer(Lifecycle), } -#[derive(thiserror::Error, Debug)] -pub enum ProcedureCallError { - #[error(transparent)] - Args(#[from] InvalidProcedureArguments), - #[error(transparent)] - NoSuchModule(#[from] NoSuchModule), - #[error("No such procedure")] - NoSuchProcedure, - #[error("Procedure terminated due to insufficient budget")] - OutOfEnergy, - #[error("The module instance encountered a fatal error: {0}")] - InternalError(String), -} - #[derive(thiserror::Error, Debug)] pub enum InitDatabaseError { #[error(transparent)] @@ -826,37 +770,6 @@ impl ModuleHost { }) } - async fn call_async_with_instance(&self, label: &str, f: Fun) -> Result - where - Fun: (FnOnce(Instance) -> Fut) + Send + 'static, - Fut: Future + Send + 'static, - R: Send + 'static, - { - self.guard_closed()?; - let timer_guard = self.start_call_timer(label); - - scopeguard::defer_on_unwind!({ - log::warn!("procedure {label} panicked"); - (self.on_panic)(); - }); - - // TODO: should we be calling and/or `await`-ing `get_instance` within the below `run_job`? - // Unclear how much overhead this call can have. - let instance = self.instance_manager.lock().await.get_instance().await; - - let (res, instance) = self - .executor - .run_job(async move { - drop(timer_guard); - f(instance).await - }) - .await; - - self.instance_manager.lock().await.return_instance(instance); - - Ok(res) - } - /// Run a function on the JobThread for this module which has access to the module instance. async fn call(&self, label: &str, f: F) -> Result where @@ -971,7 +884,7 @@ impl ModuleHost { None, reducer_id, reducer_def, - FunctionArgs::Nullary, + ReducerArgs::Nullary, inst, )?; @@ -1066,10 +979,10 @@ impl ModuleHost { log::error!( "`call_identity_disconnected`: fallback transaction to delete from `st_client` failed: {err}" ); - InvalidReducerArguments(InvalidFunctionArguments { + InvalidReducerArguments { err: err.into(), - function_name: reducer_name.into(), - }) + reducer: reducer_name.into(), + } .into() }) }; @@ -1098,7 +1011,7 @@ impl ModuleHost { None, reducer_id, reducer_def, - FunctionArgs::Nullary, + ReducerArgs::Nullary, inst, ); @@ -1184,10 +1097,10 @@ impl ModuleHost { timer: Option, reducer_id: ReducerId, reducer_def: &ReducerDef, - args: FunctionArgs, + args: ReducerArgs, ) -> Result { - let reducer_seed = ArgsSeed(self.info.module_def.typespace().with_type(reducer_def)); - let args = args.into_tuple(reducer_seed).map_err(InvalidReducerArguments)?; + let reducer_seed = ReducerArgsDeserializeSeed(self.info.module_def.typespace().with_type(reducer_def)); + let args = args.into_tuple(reducer_seed)?; let caller_connection_id = caller_connection_id.unwrap_or(ConnectionId::ZERO); Ok(self @@ -1218,11 +1131,11 @@ impl ModuleHost { timer: Option, reducer_id: ReducerId, reducer_def: &ReducerDef, - args: FunctionArgs, + args: ReducerArgs, module_instance: &mut Instance, ) -> Result { - let reducer_seed = ArgsSeed(self.info.module_def.typespace().with_type(reducer_def)); - let args = args.into_tuple(reducer_seed).map_err(InvalidReducerArguments)?; + let reducer_seed = ReducerArgsDeserializeSeed(self.info.module_def.typespace().with_type(reducer_def)); + let args = args.into_tuple(reducer_seed)?; let caller_connection_id = caller_connection_id.unwrap_or(ConnectionId::ZERO); Ok(module_instance.call_reducer( @@ -1248,7 +1161,7 @@ impl ModuleHost { request_id: Option, timer: Option, reducer_name: &str, - args: FunctionArgs, + args: ReducerArgs, ) -> Result { let res = async { let (reducer_id, reducer_def) = self @@ -1274,8 +1187,13 @@ impl ModuleHost { .await; let log_message = match &res { - Err(ReducerCallError::NoSuchReducer) => Some(no_such_function_log_message("reducer", reducer_name)), - Err(ReducerCallError::Args(_)) => Some(args_error_log_message("reducer", reducer_name)), + Err(ReducerCallError::NoSuchReducer) => Some(format!( + "External attempt to call nonexistent reducer \"{reducer_name}\" failed. Have you run `spacetime generate` recently?" + )), + Err(ReducerCallError::Args(_)) => Some(format!( + "External attempt to call reducer \"{reducer_name}\" failed, invalid arguments.\n\ + This is likely due to a mismatched client schema, have you run `spacetime generate` recently?", + )), _ => None, }; if let Some(log_message) = log_message { @@ -1285,74 +1203,6 @@ impl ModuleHost { res } - pub async fn call_procedure( - &self, - caller_identity: Identity, - caller_connection_id: Option, - timer: Option, - procedure_name: &str, - args: FunctionArgs, - ) -> Result { - let res = async { - let (procedure_id, procedure_def) = self - .info - .module_def - .procedure_full(procedure_name) - .ok_or(ProcedureCallError::NoSuchProcedure)?; - self.call_procedure_inner( - caller_identity, - caller_connection_id, - timer, - procedure_id, - procedure_def, - args, - ) - .await - } - .await; - - let log_message = match &res { - Err(ProcedureCallError::NoSuchProcedure) => Some(no_such_function_log_message("procedure", procedure_name)), - Err(ProcedureCallError::Args(_)) => Some(args_error_log_message("procedure", procedure_name)), - _ => None, - }; - - if let Some(log_message) = log_message { - self.inject_logs(LogLevel::Error, procedure_name, &log_message) - } - - res - } - - async fn call_procedure_inner( - &self, - caller_identity: Identity, - caller_connection_id: Option, - timer: Option, - procedure_id: ProcedureId, - procedure_def: &ProcedureDef, - args: FunctionArgs, - ) -> Result { - let procedure_seed = ArgsSeed(self.info.module_def.typespace().with_type(procedure_def)); - let args = args.into_tuple(procedure_seed).map_err(InvalidProcedureArguments)?; - let caller_connection_id = caller_connection_id.unwrap_or(ConnectionId::ZERO); - - self.call_async_with_instance(&procedure_def.name, async move |mut inst| { - let res = inst - .call_procedure(CallProcedureParams { - timestamp: Timestamp::now(), - caller_identity, - caller_connection_id, - timer, - procedure_id, - args, - }) - .await; - (res, inst) - }) - .await? - } - // Scheduled reducers require a different function here to call their reducer // because their reducer arguments are stored in the database and need to be fetched // within the same transaction as the reducer call. @@ -1390,12 +1240,10 @@ impl ModuleHost { Ok(inst.call_reducer(Some(tx), params)) } Ok(None) => Err(ReducerCallError::ScheduleReducerNotFound), - Err(err) => Err(ReducerCallError::Args(InvalidReducerArguments( - InvalidFunctionArguments { - err, - function_name: REDUCER.into(), - }, - ))), + Err(err) => Err(ReducerCallError::Args(InvalidReducerArguments { + err, + reducer: REDUCER.into(), + })), } }) .await? @@ -1438,11 +1286,11 @@ impl ModuleHost { self.module.scheduler().closed().await; } - pub fn inject_logs(&self, log_level: LogLevel, function_name: &str, message: &str) { + pub fn inject_logs(&self, log_level: LogLevel, reducer_name: &str, message: &str) { self.replica_ctx().logger.write( log_level, &Record { - function: Some(function_name), + function: Some(reducer_name), ..Record::injected(message) }, &(), @@ -1618,14 +1466,3 @@ impl WeakModuleHost { }) } } - -fn no_such_function_log_message(function_kind: &str, function_name: &str) -> String { - format!("External attempt to call nonexistent {function_kind} \"{function_name}\" failed. Have you run `spacetime generate` recently?") -} - -fn args_error_log_message(function_kind: &str, function_name: &str) -> String { - format!( - "External attempt to call {function_kind} \"{function_name}\" failed, invalid arguments.\n\ - This is likely due to a mismatched client schema, have you run `spacetime generate` recently?" - ) -} diff --git a/crates/core/src/host/scheduler.rs b/crates/core/src/host/scheduler.rs index cc6ee632154..620c8f059dc 100644 --- a/crates/core/src/host/scheduler.rs +++ b/crates/core/src/host/scheduler.rs @@ -21,7 +21,7 @@ use super::module_host::ModuleEvent; use super::module_host::ModuleFunctionCall; use super::module_host::{CallReducerParams, WeakModuleHost}; use super::module_host::{DatabaseUpdate, EventStatus}; -use super::{FunctionArgs, ModuleHost, ReducerCallError}; +use super::{ModuleHost, ReducerArgs, ReducerCallError}; use spacetimedb_datastore::execution_context::Workload; use spacetimedb_datastore::locking_tx_datastore::MutTxId; use spacetimedb_datastore::system_tables::{StFields, StScheduledFields, ST_SCHEDULED_ID}; @@ -60,7 +60,7 @@ enum SchedulerMessage { }, ScheduleImmediate { reducer_name: String, - args: FunctionArgs, + args: ReducerArgs, }, } @@ -242,7 +242,7 @@ impl Scheduler { Ok(()) } - pub fn volatile_nonatomic_schedule_immediate(&self, reducer_name: String, args: FunctionArgs) { + pub fn volatile_nonatomic_schedule_immediate(&self, reducer_name: String, args: ReducerArgs) { let _ = self.tx.send(MsgOrExit::Msg(SchedulerMessage::ScheduleImmediate { reducer_name, args, @@ -267,7 +267,7 @@ struct SchedulerActor { enum QueueItem { Id { id: ScheduledReducerId, at: Timestamp }, - VolatileNonatomicImmediate { reducer_name: String, args: FunctionArgs }, + VolatileNonatomicImmediate { reducer_name: String, args: ReducerArgs }, } #[cfg(target_pointer_width = "64")] @@ -349,7 +349,7 @@ impl SchedulerActor { .reducer_arg_deserialize_seed(&reducer[..]) .ok_or_else(|| anyhow!("Reducer not found: {reducer}"))?; - let reducer_args = FunctionArgs::Bsatn(bsatn_args.into()).into_tuple(reducer_seed)?; + let reducer_args = ReducerArgs::Bsatn(bsatn_args.into()).into_tuple(reducer_seed)?; // the timestamp we tell the reducer it's running at will be // at least the timestamp it was scheduled to run at. diff --git a/crates/core/src/host/v8/mod.rs b/crates/core/src/host/v8/mod.rs index 73b1ad9d76a..b0e31fd31f1 100644 --- a/crates/core/src/host/v8/mod.rs +++ b/crates/core/src/host/v8/mod.rs @@ -7,7 +7,7 @@ use self::ser::serialize_to_js; use self::string::{str_from_ident, IntoJsString}; use self::syscall::{call_call_reducer, call_describe_module, call_reducer_fun, resolve_sys_module, FnRet}; use super::module_common::{build_common_module_from_raw, run_describer, ModuleCommon}; -use super::module_host::{CallProcedureParams, CallReducerParams, Module, ModuleInfo, ModuleRuntime}; +use super::module_host::{CallReducerParams, Module, ModuleInfo, ModuleRuntime}; use super::UpdateDatabaseResult; use crate::host::instance_env::{ChunkPool, InstanceEnv}; use crate::host::module_host::Instance; @@ -200,7 +200,7 @@ impl JsInstanceEnv { fn start_reducer(&mut self, name: &str, ts: Timestamp) { self.reducer_start = Instant::now(); name.clone_into(&mut self.reducer_name); - self.instance_env.start_funcall(ts); + self.instance_env.start_reducer(ts); } /// Returns the name of the most recent reducer to be run in this environment. @@ -295,13 +295,6 @@ impl JsInstance { response } - - pub async fn call_procedure( - &mut self, - _params: CallProcedureParams, - ) -> Result { - todo!("JS/TS module procedure support") - } } /// A request for the worker in [`spawn_instance_worker`]. diff --git a/crates/core/src/host/v8/syscall.rs b/crates/core/src/host/v8/syscall.rs index 98eacd38b43..dcf1ba2beed 100644 --- a/crates/core/src/host/v8/syscall.rs +++ b/crates/core/src/host/v8/syscall.rs @@ -1158,7 +1158,7 @@ fn volatile_nonatomic_schedule_immediate<'scope>( get_env(scope)? .instance_env .scheduler - .volatile_nonatomic_schedule_immediate(name, crate::host::FunctionArgs::Bsatn(args.into())); + .volatile_nonatomic_schedule_immediate(name, crate::host::ReducerArgs::Bsatn(args.into())); Ok(()) } diff --git a/crates/core/src/host/wasm_common.rs b/crates/core/src/host/wasm_common.rs index 0fbdec0d17b..b44976e53c3 100644 --- a/crates/core/src/host/wasm_common.rs +++ b/crates/core/src/host/wasm_common.rs @@ -14,8 +14,6 @@ use spacetimedb_table::table::UniqueConstraintViolation; pub const CALL_REDUCER_DUNDER: &str = "__call_reducer__"; -pub const CALL_PROCEDURE_DUNDER: &str = "__call_procedure__"; - pub const DESCRIBE_MODULE_DUNDER: &str = "__describe_module__"; /// functions with this prefix run prior to __setup__, initializing global variables and the like @@ -386,8 +384,8 @@ pub struct AbiRuntimeError { } macro_rules! abi_funcs { - ($link_sync:ident, $link_async:ident) => { - $link_sync! { + ($mac:ident) => { + $mac! { "spacetime_10.0"::table_id_from_name, "spacetime_10.0"::datastore_table_row_count, "spacetime_10.0"::datastore_table_scan_bsatn, @@ -415,10 +413,6 @@ macro_rules! abi_funcs { "spacetime_10.2"::get_jwt, } - - $link_async! { - "spacetime_10.3"::procedure_sleep_until, - } }; } pub(crate) use abi_funcs; diff --git a/crates/core/src/host/wasm_common/module_host_actor.rs b/crates/core/src/host/wasm_common/module_host_actor.rs index 6bf94421f2a..2cdf70335cd 100644 --- a/crates/core/src/host/wasm_common/module_host_actor.rs +++ b/crates/core/src/host/wasm_common/module_host_actor.rs @@ -1,10 +1,6 @@ -use bytes::Bytes; use prometheus::{Histogram, IntCounter, IntGauge}; use spacetimedb_lib::db::raw_def::v9::Lifecycle; -use spacetimedb_lib::de::DeserializeSeed; -use spacetimedb_primitives::ProcedureId; use spacetimedb_schema::auto_migrate::{MigratePlan, MigrationPolicy, MigrationPolicyError}; -use std::future::Future; use std::sync::Arc; use std::time::Duration; use tracing::span::EnteredSpan; @@ -16,12 +12,9 @@ use crate::energy::{EnergyMonitor, ReducerBudget, ReducerFingerprint}; use crate::host::instance_env::InstanceEnv; use crate::host::module_common::{build_common_module_from_raw, ModuleCommon}; use crate::host::module_host::{ - CallProcedureParams, CallReducerParams, DatabaseUpdate, EventStatus, ModuleEvent, ModuleFunctionCall, ModuleInfo, -}; -use crate::host::{ - ArgsTuple, ProcedureCallError, ProcedureCallResult, ReducerCallResult, ReducerId, ReducerOutcome, Scheduler, - UpdateDatabaseResult, + CallReducerParams, DatabaseUpdate, EventStatus, ModuleEvent, ModuleFunctionCall, ModuleInfo, }; +use crate::host::{ArgsTuple, ReducerCallResult, ReducerId, ReducerOutcome, Scheduler, UpdateDatabaseResult}; use crate::identity::Identity; use crate::messages::control_db::HostType; use crate::module_host_context::ModuleCreationContextLimited; @@ -55,7 +48,6 @@ pub trait WasmInstancePre: Send + Sync + 'static { fn instantiate(&self, env: InstanceEnv, func_names: &FuncNames) -> Result; } -#[async_trait::async_trait] pub trait WasmInstance: Send + Sync + 'static { fn extract_descriptions(&mut self) -> Result, DescribeError>; @@ -64,8 +56,6 @@ pub trait WasmInstance: Send + Sync + 'static { fn call_reducer(&mut self, op: ReducerOp<'_>, budget: ReducerBudget) -> ExecuteResult; fn log_traceback(func_type: &str, func: &str, trap: &anyhow::Error); - - async fn call_procedure(&mut self, op: ProcedureOp, budget: ReducerBudget) -> ProcedureExecuteResult; } pub struct EnergyStats { @@ -74,11 +64,6 @@ pub struct EnergyStats { } impl EnergyStats { - pub const ZERO: Self = Self { - budget: ReducerBudget::ZERO, - remaining: ReducerBudget::ZERO, - }; - /// Returns the used energy amount. fn used(&self) -> ReducerBudget { (self.budget.get() - self.remaining.get()).into() @@ -90,17 +75,6 @@ pub struct ExecutionTimings { pub wasm_instance_env_call_times: CallTimes, } -impl ExecutionTimings { - /// Not a `const` because there doesn't seem to be any way to `const` construct an `enum_map::EnumMap`, - /// which `CallTimes` uses. - pub fn zero() -> Self { - Self { - total_duration: Duration::ZERO, - wasm_instance_env_call_times: CallTimes::new(), - } - } -} - /// The result that `__call_reducer__` produces during normal non-trap execution. pub type ReducerResult = Result<(), Box>; @@ -111,15 +85,6 @@ pub struct ExecuteResult { pub call_result: anyhow::Result, } -pub struct ProcedureExecuteResult { - #[allow(unused)] - pub energy: EnergyStats, - #[allow(unused)] - pub timings: ExecutionTimings, - pub memory_allocation: usize, - pub call_result: anyhow::Result, -} - pub struct WasmModuleHostActor { module: T::InstancePre, common: ModuleCommon, @@ -273,24 +238,6 @@ impl WasmModuleInstance { pub fn call_reducer(&mut self, tx: Option, params: CallReducerParams) -> ReducerCallResult { crate::callgrind_flag::invoke_allowing_callgrind(|| self.call_reducer_with_tx(tx, params)) } - - pub async fn call_procedure( - &mut self, - params: CallProcedureParams, - ) -> Result { - let res = self - .common - .call_procedure( - params, - |ty, fun, err| T::log_traceback(ty, fun, err), - |op, budget| self.instance.call_procedure(op, budget), - ) - .await; - if res.is_err() { - self.trapped = true; - } - res - } } impl WasmModuleInstance { @@ -393,101 +340,6 @@ impl InstanceCommon { } } - async fn call_procedure>( - &mut self, - params: CallProcedureParams, - log_traceback: impl FnOnce(&str, &str, &anyhow::Error), - vm_call_procedure: impl FnOnce(ProcedureOp, ReducerBudget) -> F, - ) -> Result { - let CallProcedureParams { - timestamp, - caller_identity, - caller_connection_id, - timer, - procedure_id, - args, - } = params; - - // We've already validated by this point that the procedure exists, - // so it's fine to use the panicking `procedure_by_id`. - let procedure_def = self.info.module_def.procedure_by_id(procedure_id); - let procedure_name: &str = &procedure_def.name; - - // TODO(observability): Add tracing spans, energy, metrics? - // These will require further thinking once we implement procedure suspend/resume, - // and so are not worth doing yet. - - let op = ProcedureOp { - id: procedure_id, - name: procedure_name.into(), - caller_identity, - caller_connection_id, - timestamp, - arg_bytes: args.get_bsatn().clone(), - }; - - let energy_fingerprint = ReducerFingerprint { - module_hash: self.info.module_hash, - module_identity: self.info.owner_identity, - caller_identity, - reducer_name: &procedure_def.name, - }; - - // TODO(procedure-energy): replace with call to separate function `procedure_budget`. - let budget = self.energy_monitor.reducer_budget(&energy_fingerprint); - - let result = vm_call_procedure(op, budget).await; - - let ProcedureExecuteResult { - memory_allocation, - call_result, - // TODO(procedure-energy): Do something with timing and energy. - .. - } = result; - - // TODO(shub): deduplicate with reducer and view logic. - if self.allocated_memory != memory_allocation { - self.metric_wasm_memory_bytes.set(memory_allocation as i64); - self.allocated_memory = memory_allocation; - } - - match call_result { - Err(err) => { - log_traceback("procedure", &procedure_def.name, &err); - - WORKER_METRICS - .wasm_instance_errors - .with_label_values( - &caller_identity, - &self.info.module_hash, - &caller_connection_id, - procedure_name, - ) - .inc(); - - // TODO(procedure-energy): - // if energy.remaining.get() == 0 { - // return Err(ProcedureCallError::OutOfEnergy); - // } else - { - Err(ProcedureCallError::InternalError(format!("{err}"))) - } - } - Ok(return_val) => { - let return_type = &procedure_def.return_type; - let seed = spacetimedb_sats::WithTypespace::new(self.info.module_def.typespace(), return_type); - let return_val = seed - .deserialize(bsatn::Deserializer::new(&mut &return_val[..])) - .map_err(|err| ProcedureCallError::InternalError(format!("{err}")))?; - Ok(ProcedureCallResult { - return_val, - execution_duration: timer.map(|timer| timer.elapsed()).unwrap_or_default(), - start_timestamp: timestamp, - }) - } - } - } - /// Execute a reducer. /// /// If `Some` [`MutTxId`] is supplied, the reducer is called within the @@ -837,14 +689,3 @@ impl From> for execution_context::ReducerContext { } } } - -/// Describes a procedure call in a cheaply shareable way. -#[derive(Clone, Debug)] -pub struct ProcedureOp { - pub id: ProcedureId, - pub name: Box, - pub caller_identity: Identity, - pub caller_connection_id: ConnectionId, - pub timestamp: Timestamp, - pub arg_bytes: Bytes, -} diff --git a/crates/core/src/host/wasmtime/wasm_instance_env.rs b/crates/core/src/host/wasmtime/wasm_instance_env.rs index 7aac129306d..b17fcf6fffb 100644 --- a/crates/core/src/host/wasmtime/wasm_instance_env.rs +++ b/crates/core/src/host/wasmtime/wasm_instance_env.rs @@ -11,7 +11,6 @@ use anyhow::Context as _; use spacetimedb_data_structures::map::IntMap; use spacetimedb_lib::{ConnectionId, Timestamp}; use spacetimedb_primitives::{errno, ColId}; -use std::future::Future; use std::num::NonZeroU32; use std::time::Instant; use wasmtime::{AsContext, Caller, StoreContextMut}; @@ -103,8 +102,8 @@ pub(super) struct WasmInstanceEnv { /// Track time spent in module-defined spans. timing_spans: TimingSpanSet, - /// The point in time the last, or current, reducer or procedure call started at. - funcall_start: Instant, + /// The point in time the last reducer call started at. + reducer_start: Instant, /// Track time spent in all wasm instance env calls (aka syscall time). /// @@ -112,8 +111,8 @@ pub(super) struct WasmInstanceEnv { /// to this tracker. call_times: CallTimes, - /// The name of the last, including current, reducer or procedure to be executed by this environment. - funcall_name: String, + /// The last, including current, reducer to be executed by this environment. + reducer_name: String, /// A pool of unused allocated chunks that can be reused. // TODO(Centril): consider using this pool for `console_timer_start` and `bytes_sink_write`. @@ -130,7 +129,7 @@ type RtResult = anyhow::Result; impl WasmInstanceEnv { /// Create a new `WasmEnstanceEnv` from the given `InstanceEnv`. pub fn new(instance_env: InstanceEnv) -> Self { - let funcall_start = Instant::now(); + let reducer_start = Instant::now(); Self { instance_env, mem: None, @@ -139,9 +138,9 @@ impl WasmInstanceEnv { standard_bytes_sink: None, iters: Default::default(), timing_spans: Default::default(), - funcall_start, + reducer_start, call_times: CallTimes::new(), - funcall_name: String::from(""), + reducer_name: String::from(""), chunk_pool: <_>::default(), } } @@ -220,54 +219,48 @@ impl WasmInstanceEnv { self.standard_bytes_sink.take().unwrap_or_default() } - /// Signal to this `WasmInstanceEnv` that a reducer or procedure call is beginning. + /// Signal to this `WasmInstanceEnv` that a reducer call is beginning. /// - /// Returns the handle used by reducers and procedures to read from `args` - /// as well as the handle used to write the reducer error message or procedure return value. - pub fn start_funcall(&mut self, name: &str, args: bytes::Bytes, ts: Timestamp) -> (BytesSourceId, u32) { - // Create the output sink. - // Reducers which fail will write their error message here. - // Procedures will write their result here. + /// Returns the handle used by reducers to read from `args` + /// as well as the handle used to write the error message, if any. + pub fn start_reducer(&mut self, name: &str, args: bytes::Bytes, ts: Timestamp) -> (BytesSourceId, u32) { let errors = self.setup_standard_bytes_sink(); let args = self.create_bytes_source(args).unwrap(); - self.funcall_start = Instant::now(); - name.clone_into(&mut self.funcall_name); - self.instance_env.start_funcall(ts); + self.reducer_start = Instant::now(); + name.clone_into(&mut self.reducer_name); + self.instance_env.start_reducer(ts); (args, errors) } - /// Returns the name of the most recent reducer or procedure to be run in this environment. - pub fn funcall_name(&self) -> &str { - &self.funcall_name + /// Returns the name of the most recent reducer to be run in this environment. + pub fn reducer_name(&self) -> &str { + &self.reducer_name } - /// Returns the name of the most recent reducer or procedure to be run in this environment, - /// or `None` if no reducer or procedure is actively being invoked. + /// Returns the name of the most recent reducer to be run in this environment, + /// or `None` if no reducer is actively being invoked. fn log_record_function(&self) -> Option<&str> { - let function = self.funcall_name(); + let function = self.reducer_name(); (!function.is_empty()).then_some(function) } - /// Returns the start time of the most recent reducer or procedure to be run in this environment. - pub fn funcall_start(&self) -> Instant { - self.funcall_start + /// Returns the name of the most recent reducer to be run in this environment. + pub fn reducer_start(&self) -> Instant { + self.reducer_start } - /// Signal to this `WasmInstanceEnv` that a reducer or procedure call is over. - /// - /// Returns time measurements which can be recorded as metrics, - /// and the errors written by the WASM code to the standard error sink. - /// - /// This resets the call times and clears the arguments source and error sink. - pub fn finish_funcall(&mut self) -> (ExecutionTimings, Vec) { + /// Signal to this `WasmInstanceEnv` that a reducer call is over. + /// This resets all of the state associated to a single reducer call, + /// and returns instrumentation records. + pub fn finish_reducer(&mut self) -> (ExecutionTimings, Vec) { // For the moment, // we only explicitly clear the source/sink buffers and the "syscall" times. // TODO: should we be clearing `iters` and/or `timing_spans`? - let total_duration = self.funcall_start.elapsed(); + let total_duration = self.reducer_start.elapsed(); // Taking the call times record also resets timings to 0s for the next call. let wasm_instance_env_call_times = self.call_times.take(); @@ -1013,7 +1006,7 @@ impl WasmInstanceEnv { let args = mem.deref_slice(args, args_len)?; env.instance_env.scheduler.volatile_nonatomic_schedule_immediate( name.to_owned(), - crate::host::FunctionArgs::Bsatn(args.to_vec().into()), + crate::host::ReducerArgs::Bsatn(args.to_vec().into()), ); Ok(()) @@ -1374,53 +1367,6 @@ impl WasmInstanceEnv { Ok(()) }) } - - /// Suspends execution of this WASM instance until approximately `wake_at_micros_since_unix_epoch`. - /// - /// Returns immediately if `wake_at_micros_since_unix_epoch` is in the past. - /// - /// Upon resuming, returns the current timestamp as microseconds since the Unix epoch. - /// - /// Not particularly useful, except for testing SpacetimeDB internals related to suspending procedure execution. - /// - /// In our public module-facing interfaces, this function is marked as unstable. - /// - /// # Traps - /// - /// Traps if: - /// - /// - The calling WASM instance is holding open a transaction. - /// - The calling WASM instance is not executing a procedure. - // TODO(procedure-sleep-until): remove this - pub fn procedure_sleep_until<'caller>( - mut caller: Caller<'caller, Self>, - (wake_at_micros_since_unix_epoch,): (i64,), - ) -> Box + Send + 'caller> { - Box::new(async move { - use std::time::SystemTime; - let span_start = span::CallSpanStart::new(AbiCall::ProcedureSleepUntil); - - let get_current_time = || Timestamp::now().to_micros_since_unix_epoch(); - - if wake_at_micros_since_unix_epoch < 0 { - return get_current_time(); - } - - let wake_at = Timestamp::from_micros_since_unix_epoch(wake_at_micros_since_unix_epoch); - let Ok(duration) = SystemTime::from(wake_at).duration_since(SystemTime::now()) else { - return get_current_time(); - }; - - tokio::time::sleep(duration).await; - - let res = get_current_time(); - - let span = span_start.end(); - span::record_span(&mut caller.data_mut().call_times, span); - - res - }) - } } impl BacktraceProvider for wasmtime::StoreContext<'_, T> { diff --git a/crates/core/src/host/wasmtime/wasmtime_module.rs b/crates/core/src/host/wasmtime/wasmtime_module.rs index 7cae069db93..eaf5b682e29 100644 --- a/crates/core/src/host/wasmtime/wasmtime_module.rs +++ b/crates/core/src/host/wasmtime/wasmtime_module.rs @@ -9,7 +9,6 @@ use crate::host::wasm_common::module_host_actor::{DescribeError, InitializationE use crate::host::wasm_common::*; use crate::util::string_from_utf8_lossy_owned; use futures_util::FutureExt; -use spacetimedb_lib::{ConnectionId, Identity}; use spacetimedb_primitives::errno::HOST_CALL_FAILURE; use wasmtime::{ AsContext, AsContextMut, ExternType, Instance, InstancePre, Linker, Store, TypedFunc, WasmBacktrace, WasmParams, @@ -40,7 +39,7 @@ impl WasmtimeModule { WasmtimeModule { module } } - pub const IMPLEMENTED_ABI: abi::VersionTuple = abi::VersionTuple::new(10, 3); + pub const IMPLEMENTED_ABI: abi::VersionTuple = abi::VersionTuple::new(10, 2); pub(super) fn link_imports(linker: &mut Linker) -> anyhow::Result<()> { const { assert!(WasmtimeModule::IMPLEMENTED_ABI.major == spacetimedb_lib::MODULE_ABI_MAJOR_VERSION) }; @@ -50,13 +49,7 @@ impl WasmtimeModule { linker$(.func_wrap($module, stringify!($func), WasmInstanceEnv::$func)?)*; } } - macro_rules! link_async_functions { - ($($module:literal :: $func:ident,)*) => { - #[allow(deprecated)] - linker$(.func_wrap_async($module, stringify!($func), WasmInstanceEnv::$func)?)*; - } - } - abi_funcs!(link_functions, link_async_functions); + abi_funcs!(link_functions); Ok(()) } } @@ -133,11 +126,9 @@ impl module_host_actor::WasmInstancePre for WasmtimeModule { store.epoch_deadline_callback(|store| { let env = store.data(); let database = env.instance_env().replica_ctx.database_identity; - let funcall = env.funcall_name(); - let dur = env.funcall_start().elapsed(); - // TODO(procedure-timing): This measurement is not super meaningful for procedures, - // which may (will) suspend execution and therefore may not have been continuously running since `env.funcall_start`. - tracing::warn!(funcall, ?database, "Wasm has been running for {dur:?}"); + let reducer = env.reducer_name(); + let dur = env.reducer_start().elapsed(); + tracing::warn!(reducer, ?database, "Wasm has been running for {dur:?}"); Ok(wasmtime::UpdateDeadline::Continue(EPOCH_TICKS_PER_SECOND)) }); @@ -171,78 +162,22 @@ impl module_host_actor::WasmInstancePre for WasmtimeModule { .get_typed_func(&mut store, CALL_REDUCER_DUNDER) .expect("no call_reducer"); - let call_procedure = get_call_procedure(&mut store, &instance); - Ok(WasmtimeInstance { store, instance, call_reducer, - call_procedure, }) } } -/// Look up the `instance`'s export named by [`CALL_PROCEDURE_DUNDER`]. -/// -/// Return `None` if the `instance` has no such export. -/// Modules from before the introduction of procedures will not have a `__call_procedure__` export, -/// which is fine because they also won't define any procedures. -/// -/// Panics if the `instance` has an export at the expected name, -/// but it is not a function or is a function of an inappropriate type. -/// For new modules, this will be caught during publish. -/// Old modules from before the introduction of procedures might have an export at that name, -/// but it follows the double-underscore pattern of reserved names, -/// so we're fine to break those modules. -fn get_call_procedure(store: &mut Store, instance: &Instance) -> Option { - // Wasmtime uses `anyhow` for error reporting, vexing library users the world over. - // This means we can't distinguish between the failure modes of `Instance::get_typed_func`. - // Instead, we type out the body of that method ourselves, - // but with error handling appropriate to our needs. - let export = instance.get_export(store.as_context_mut(), CALL_PROCEDURE_DUNDER)?; - - Some( - export - .into_func() - .unwrap_or_else(|| panic!("{CALL_PROCEDURE_DUNDER} export is not a function")) - .typed(store) - .unwrap_or_else(|err| panic!("{CALL_PROCEDURE_DUNDER} export is a function with incorrect type: {err}")), - ) -} - -type CallReducerType = TypedFunc< - ( - // Reducer ID, - u32, - // Sender `Identity` - u64, - u64, - u64, - u64, - // Sender `ConnectionId`, or 0 for none. - u64, - u64, - // Start timestamp. - u64, - // Args byte source. - u32, - // Errors byte sink. - u32, - ), - // Errno. - i32, ->; -// `__call_procedure__` takes the same arguments as `__call_reducer__`. -type CallProcedureType = CallReducerType; +type CallReducerType = TypedFunc<(u32, u64, u64, u64, u64, u64, u64, u64, u32, u32), i32>; pub struct WasmtimeInstance { store: Store, instance: Instance, call_reducer: CallReducerType, - call_procedure: Option, } -#[async_trait::async_trait] impl module_host_actor::WasmInstance for WasmtimeInstance { fn extract_descriptions(&mut self) -> Result, DescribeError> { let describer_func_name = DESCRIBE_MODULE_DUNDER; @@ -271,17 +206,18 @@ impl module_host_actor::WasmInstance for WasmtimeInstance { #[tracing::instrument(level = "trace", skip_all)] fn call_reducer(&mut self, op: ReducerOp<'_>, budget: ReducerBudget) -> module_host_actor::ExecuteResult { let store = &mut self.store; - - prepare_store_for_call(store, budget); + // Set the fuel budget in WASM. + set_store_fuel(store, budget.into()); + store.set_epoch_deadline(EPOCH_TICKS_PER_SECOND); // Prepare sender identity and connection ID, as LITTLE-ENDIAN byte arrays. - let [sender_0, sender_1, sender_2, sender_3] = prepare_identity_for_call(*op.caller_identity); - let [conn_id_0, conn_id_1] = prepare_connection_id_for_call(*op.caller_connection_id); + let [sender_0, sender_1, sender_2, sender_3] = bytemuck::must_cast(op.caller_identity.to_byte_array()); + let [conn_id_0, conn_id_1] = bytemuck::must_cast(op.caller_connection_id.as_le_byte_array()); // Prepare arguments to the reducer + the error sink & start timings. let args_bytes = op.args.get_bsatn().clone(); - let (args_source, errors_sink) = store.data_mut().start_funcall(op.name, args_bytes, op.timestamp); + let (args_source, errors_sink) = store.data_mut().start_reducer(op.name, args_bytes, op.timestamp); let call_result = call_sync_typed_func( &self.call_reducer, @@ -303,7 +239,7 @@ impl module_host_actor::WasmInstance for WasmtimeInstance { // Signal that this reducer call is finished. This gets us the timings // associated to our reducer call, and clears all of the instance state // associated to the call. - let (timings, error) = store.data_mut().finish_funcall(); + let (timings, error) = store.data_mut().finish_reducer(); let call_result = call_result.map(|code| handle_error_sink_code(code, error)); @@ -324,77 +260,6 @@ impl module_host_actor::WasmInstance for WasmtimeInstance { fn log_traceback(func_type: &str, func: &str, trap: &anyhow::Error) { log_traceback(func_type, func, trap) } - - #[tracing::instrument(level = "trace", skip_all)] - async fn call_procedure( - &mut self, - op: module_host_actor::ProcedureOp, - budget: ReducerBudget, - ) -> module_host_actor::ProcedureExecuteResult { - let store = &mut self.store; - prepare_store_for_call(store, budget); - - // Prepare sender identity and connection ID, as LITTLE-ENDIAN byte arrays. - let [sender_0, sender_1, sender_2, sender_3] = prepare_identity_for_call(op.caller_identity); - let [conn_id_0, conn_id_1] = prepare_connection_id_for_call(op.caller_connection_id); - - // Prepare arguments to the reducer + the error sink & start timings. - let (args_source, result_sink) = store.data_mut().start_funcall(&op.name, op.arg_bytes, op.timestamp); - - let Some(call_procedure) = self.call_procedure.as_ref() else { - return module_host_actor::ProcedureExecuteResult { - energy: module_host_actor::EnergyStats::ZERO, - timings: module_host_actor::ExecutionTimings::zero(), - memory_allocation: get_memory_size(store), - call_result: Err(anyhow::anyhow!( - "Module defines procedure {} but does not export `{}`", - op.name, - CALL_PROCEDURE_DUNDER, - )), - }; - }; - let call_result = call_procedure - .call_async( - &mut *store, - ( - op.id.0, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - op.timestamp.to_micros_since_unix_epoch() as u64, - args_source.0, - result_sink, - ), - ) - .await; - - // Close the timing span for this procedure and get the BSATN bytes of its result. - let (timings, result_bytes) = store.data_mut().finish_funcall(); - - let call_result = call_result.and_then(|code| { - (code == 0).then_some(result_bytes.into()).ok_or_else(|| { - anyhow::anyhow!( - "{CALL_PROCEDURE_DUNDER} returned unexpected code {code}. Procedures should return code 0 or trap." - ) - }) - }); - - let remaining_fuel = get_store_fuel(store); - let remaining = ReducerBudget::from(remaining_fuel); - - let energy = module_host_actor::EnergyStats { budget, remaining }; - let memory_allocation = get_memory_size(store); - - module_host_actor::ProcedureExecuteResult { - energy, - timings, - memory_allocation, - call_result, - } - } } fn set_store_fuel(store: &mut impl AsContextMut, fuel: WasmtimeFuel) { @@ -405,48 +270,6 @@ fn get_store_fuel(store: &impl AsContext) -> WasmtimeFuel { WasmtimeFuel(store.as_context().get_fuel().unwrap()) } -fn prepare_store_for_call(store: &mut Store, budget: ReducerBudget) { - // note that ReducerBudget being a u64 is load-bearing here - although we convert budget right back into - // EnergyQuanta at the end of this function, from_energy_quanta clamps it to a u64 range. - // otherwise, we'd return something like `used: i128::MAX - u64::MAX`, which is inaccurate. - set_store_fuel(store, budget.into()); - - // We enable epoch interruption only to log on long-running WASM functions. - // Our epoch interrupt callback logs and then immediately resumes execution. - store.set_epoch_deadline(EPOCH_TICKS_PER_SECOND); -} - -/// Convert `caller_identity` to the format used by `__call_reducer__` and `__call_procedure__`, -/// i.e. an array of 4 `u64`s. -/// -/// Callers should destructure this like: -/// ```ignore -/// # let identity = Identity::ZERO; -/// let [sender_0, sender_1, sender_2, sender_3] = prepare_identity_for_call(identity); -/// ``` -fn prepare_identity_for_call(caller_identity: Identity) -> [u64; 4] { - // Encode this as a LITTLE-ENDIAN byte array - bytemuck::must_cast(caller_identity.to_byte_array()) -} - -/// Convert `caller_connection_id` to the format used by `__call_reducer` and `__call_procedure__`, -/// i.e. an array of 2 `u64`s. -/// -/// Callers should destructure this like: -/// ```ignore -/// # let connection_id = ConnectionId::ZERO; -/// let [conn_id_0, conn_id_1] = prepare_connection_id_for_call(connection_id); -/// ``` -/// -fn prepare_connection_id_for_call(caller_connection_id: ConnectionId) -> [u64; 2] { - // Encode this as a LITTLE-ENDIAN byte array - bytemuck::must_cast(caller_connection_id.as_le_byte_array()) -} - -fn get_memory_size(store: &Store) -> usize { - store.data().get_mem().memory.data_size(store) -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/core/src/subscription/module_subscription_actor.rs b/crates/core/src/subscription/module_subscription_actor.rs index 1c1f492b285..41eb62e60ba 100644 --- a/crates/core/src/subscription/module_subscription_actor.rs +++ b/crates/core/src/subscription/module_subscription_actor.rs @@ -7,8 +7,8 @@ use super::query::compile_query_with_hashes; use super::tx::DeltaTx; use super::{collect_table_update, TableUpdateType}; use crate::client::messages::{ - ProcedureResultMessage, SerializableMessage, SubscriptionData, SubscriptionError, SubscriptionMessage, - SubscriptionResult, SubscriptionRows, SubscriptionUpdateMessage, TransactionUpdateMessage, + SerializableMessage, SubscriptionData, SubscriptionError, SubscriptionMessage, SubscriptionResult, + SubscriptionRows, SubscriptionUpdateMessage, TransactionUpdateMessage, }; use crate::client::{ClientActorId, ClientConnectionSender, Protocol}; use crate::db::relational_db::{MutTx, RelationalDB, Tx}; @@ -647,22 +647,6 @@ impl ModuleSubscriptions { .send_client_message(recipient, Some(tx_offset), message) } - /// Like [`Self::send_client_message`], - /// but doesn't require a `TxId` because procedures don't hold a transaction open. - pub fn send_procedure_message( - &self, - recipient: Arc, - message: ProcedureResultMessage, - ) -> Result<(), BroadcastError> { - self.broadcast_queue.send_client_message( - recipient, - // TODO(procedure-tx): We'll need some mechanism for procedures to report their last-referenced TxOffset, - // and to pass it here. - // This is currently moot, as procedures have no way to open a transaction yet. - None, message, - ) - } - #[tracing::instrument(level = "trace", skip_all)] pub fn add_multi_subscription( &self, diff --git a/crates/core/src/util/jobs.rs b/crates/core/src/util/jobs.rs index 059e2486278..d97acd652b7 100644 --- a/crates/core/src/util/jobs.rs +++ b/crates/core/src/util/jobs.rs @@ -71,9 +71,6 @@ impl CoreInfo { // However, `max_blocking_threads` will panic if passed 0, so we set a limit of 1 // and use `on_thread_start` to log an error when spawning a blocking task. .max_blocking_threads(1) - // Enable the timer system so that `procedure_sleep_until` can work. - // TODO(procedure-sleep): Remove this. - .enable_time() .on_thread_start({ use std::sync::atomic::{AtomicBool, Ordering}; let already_spawned_worker = AtomicBool::new(false); diff --git a/crates/datastore/src/execution_context.rs b/crates/datastore/src/execution_context.rs index ef72cbbdf25..f4fbcaf84ae 100644 --- a/crates/datastore/src/execution_context.rs +++ b/crates/datastore/src/execution_context.rs @@ -131,7 +131,6 @@ pub enum WorkloadType { Unsubscribe, Update, Internal, - Procedure, } impl Default for WorkloadType { diff --git a/crates/datastore/src/locking_tx_datastore/committed_state.rs b/crates/datastore/src/locking_tx_datastore/committed_state.rs index 24df23ae55f..a57aaeef2e4 100644 --- a/crates/datastore/src/locking_tx_datastore/committed_state.rs +++ b/crates/datastore/src/locking_tx_datastore/committed_state.rs @@ -6,34 +6,28 @@ use super::{ tx_state::{IndexIdMap, PendingSchemaChange, TxState}, IterByColEqTx, }; +use crate::system_tables::{ST_CONNECTION_CREDENTIALS_ID, ST_CONNECTION_CREDENTIALS_IDX}; use crate::{ db_metrics::DB_METRICS, error::{DatastoreError, IndexError, TableError}, execution_context::ExecutionContext, - locking_tx_datastore::{mut_tx::ViewReadSets, state_view::iter_st_column_for_table}, + locking_tx_datastore::state_view::iter_st_column_for_table, system_tables::{ system_tables, StColumnRow, StConstraintData, StConstraintRow, StIndexRow, StSequenceRow, StTableFields, StTableRow, SystemTable, ST_CLIENT_ID, ST_CLIENT_IDX, ST_COLUMN_ID, ST_COLUMN_IDX, ST_COLUMN_NAME, ST_CONSTRAINT_ID, ST_CONSTRAINT_IDX, ST_CONSTRAINT_NAME, ST_INDEX_ID, ST_INDEX_IDX, ST_INDEX_NAME, ST_MODULE_ID, ST_MODULE_IDX, ST_ROW_LEVEL_SECURITY_ID, ST_ROW_LEVEL_SECURITY_IDX, ST_SCHEDULED_ID, ST_SCHEDULED_IDX, ST_SEQUENCE_ID, ST_SEQUENCE_IDX, ST_SEQUENCE_NAME, ST_TABLE_ID, ST_TABLE_IDX, ST_VAR_ID, - ST_VAR_IDX, ST_VIEW_ARG_ID, ST_VIEW_ARG_IDX, + ST_VAR_IDX, }, traits::TxData, }; -use crate::{ - locking_tx_datastore::mut_tx::ReadSet, - system_tables::{ - ST_CONNECTION_CREDENTIALS_ID, ST_CONNECTION_CREDENTIALS_IDX, ST_VIEW_CLIENT_ID, ST_VIEW_CLIENT_IDX, - ST_VIEW_COLUMN_ID, ST_VIEW_COLUMN_IDX, ST_VIEW_ID, ST_VIEW_IDX, ST_VIEW_PARAM_ID, ST_VIEW_PARAM_IDX, - }, -}; use anyhow::anyhow; use core::{convert::Infallible, ops::RangeBounds}; -use spacetimedb_data_structures::map::{HashMap, HashSet, IntMap, IntSet}; +use spacetimedb_data_structures::map::{HashSet, IntMap, IntSet}; use spacetimedb_durability::TxOffset; use spacetimedb_lib::{db::auth::StTableType, Identity}; -use spacetimedb_primitives::{ColId, ColList, ColSet, IndexId, TableId, ViewId}; +use spacetimedb_primitives::{ColId, ColList, ColSet, IndexId, TableId}; use spacetimedb_sats::{algebraic_value::de::ValueDeserializer, memory_usage::MemoryUsage, Deserialize}; use spacetimedb_sats::{AlgebraicValue, ProductValue}; use spacetimedb_schema::{ @@ -50,40 +44,6 @@ use std::collections::BTreeMap; use std::sync::Arc; use thin_vec::ThinVec; -type IndexKeyReadSet = HashMap>; -type IndexColReadSet = HashMap; - -#[derive(Default)] -struct CommittedReadSets { - tables: IntMap>, - index_keys: IntMap, -} - -impl MemoryUsage for CommittedReadSets { - fn heap_usage(&self) -> usize { - self.tables.heap_usage() + self.index_keys.heap_usage() - } -} - -impl CommittedReadSets { - /// Record in the [`CommittedState`] that this view scans this table - fn view_scans_table(&mut self, view_id: ViewId, table_id: TableId) { - self.tables.entry(table_id).or_default().insert(view_id); - } - - /// Record in the [`CommittedState`] that this view reads this index `key` for these table `cols` - fn view_reads_index_key(&mut self, view_id: ViewId, table_id: TableId, cols: ColList, key: &AlgebraicValue) { - self.index_keys - .entry(table_id) - .or_default() - .entry(cols) - .or_default() - .entry(key.clone()) - .or_default() - .insert(view_id); - } -} - /// Contains the live, in-memory snapshot of a database. This structure /// is exposed in order to support tools wanting to process the commit /// logs directly. For normal usage, see the RelationalDB struct instead. @@ -109,11 +69,6 @@ pub struct CommittedState { /// We should split `CommittedState` into two types /// where one, e.g., `ReplayCommittedState`, has this field. table_dropped: IntSet, - /// We track the read sets for each view in the committed state. - /// We check each reducer's write set against these read sets. - /// Any overlap will trigger a re-evaluation of the affected view, - /// and its read set will be updated accordingly. - read_sets: CommittedReadSets, } impl MemoryUsage for CommittedState { @@ -125,7 +80,6 @@ impl MemoryUsage for CommittedState { index_id_map, page_pool: _, table_dropped, - read_sets, } = self; // NOTE(centril): We do not want to include the heap usage of `page_pool` as it's a shared resource. next_tx_offset.heap_usage() @@ -133,7 +87,6 @@ impl MemoryUsage for CommittedState { + blob_store.heap_usage() + index_id_map.heap_usage() + table_dropped.heap_usage() - + read_sets.heap_usage() } } @@ -196,7 +149,6 @@ impl CommittedState { blob_store: <_>::default(), index_id_map: <_>::default(), table_dropped: <_>::default(), - read_sets: <_>::default(), page_pool, } } @@ -301,12 +253,6 @@ impl CommittedState { schemas[ST_CONNECTION_CREDENTIALS_IDX].clone(), ); - self.create_table(ST_VIEW_ID, schemas[ST_VIEW_IDX].clone()); - self.create_table(ST_VIEW_PARAM_ID, schemas[ST_VIEW_PARAM_IDX].clone()); - self.create_table(ST_VIEW_COLUMN_ID, schemas[ST_VIEW_COLUMN_IDX].clone()); - self.create_table(ST_VIEW_CLIENT_ID, schemas[ST_VIEW_CLIENT_IDX].clone()); - self.create_table(ST_VIEW_ARG_ID, schemas[ST_VIEW_ARG_IDX].clone()); - // Insert the sequences into `st_sequences` let (st_sequences, blob_store, pool) = self.get_table_and_blob_store_or_create(ST_SEQUENCE_ID, &schemas[ST_SEQUENCE_IDX]); @@ -661,13 +607,10 @@ impl CommittedState { tx_data.has_rows_or_connect_disconnect(ctx.reducer_context()) } - pub(super) fn merge(&mut self, tx_state: TxState, read_sets: ViewReadSets, ctx: &ExecutionContext) -> TxData { + pub(super) fn merge(&mut self, tx_state: TxState, ctx: &ExecutionContext) -> TxData { let mut tx_data = TxData::default(); let mut truncates = IntSet::default(); - // Merge read sets from the `MutTxId` into the `CommittedState` - self.merge_read_sets(read_sets); - // First, apply deletes. This will free up space in the committed tables. self.merge_apply_deletes( &mut tx_data, @@ -698,26 +641,6 @@ impl CommittedState { tx_data } - fn merge_read_set(&mut self, view_id: ViewId, read_set: ReadSet) { - for table_id in read_set.tables_scanned() { - self.read_sets.view_scans_table(view_id, *table_id); - } - for (table_id, index_id, key) in read_set.index_keys_scanned() { - if let Some(cols) = self - .get_schema(*table_id) - .map(|table_schema| table_schema.col_list_for_index_id(*index_id)) - { - self.read_sets.view_reads_index_key(view_id, *table_id, cols, key); - } - } - } - - fn merge_read_sets(&mut self, read_sets: ViewReadSets) { - for (view_id, read_set) in read_sets { - self.merge_read_set(view_id, read_set); - } - } - fn merge_apply_deletes( &mut self, tx_data: &mut TxData, diff --git a/crates/datastore/src/locking_tx_datastore/datastore.rs b/crates/datastore/src/locking_tx_datastore/datastore.rs index 570eb2d0023..01c0f72b8b8 100644 --- a/crates/datastore/src/locking_tx_datastore/datastore.rs +++ b/crates/datastore/src/locking_tx_datastore/datastore.rs @@ -35,7 +35,7 @@ use spacetimedb_durability::TxOffset; use spacetimedb_lib::{db::auth::StAccess, metrics::ExecutionMetrics}; use spacetimedb_lib::{ConnectionId, Identity}; use spacetimedb_paths::server::SnapshotDirPath; -use spacetimedb_primitives::{ColList, ConstraintId, IndexId, SequenceId, TableId, ViewId}; +use spacetimedb_primitives::{ColList, ConstraintId, IndexId, SequenceId, TableId}; use spacetimedb_sats::{ algebraic_value::de::ValueDeserializer, bsatn, buffer::BufReader, AlgebraicValue, ProductValue, }; @@ -512,10 +512,6 @@ impl MutTxDatastore for Locking { tx.rename_table(table_id, new_name) } - fn view_id_from_name_mut_tx(&self, tx: &Self::MutTx, view_name: &str) -> Result> { - tx.view_id_from_name(view_name) - } - fn table_id_from_name_mut_tx(&self, tx: &Self::MutTx, table_name: &str) -> Result> { tx.table_id_from_name(table_name) } @@ -926,7 +922,6 @@ impl MutTx for Locking { sequence_state_lock, tx_state: TxState::default(), lock_wait_time, - read_sets: <_>::default(), timer, ctx, metrics, @@ -1252,13 +1247,11 @@ mod tests { use crate::system_tables::{ system_tables, StColumnRow, StConnectionCredentialsFields, StConstraintData, StConstraintFields, StConstraintRow, StIndexAlgorithm, StIndexFields, StIndexRow, StRowLevelSecurityFields, StScheduledFields, - StSequenceFields, StSequenceRow, StTableRow, StVarFields, StViewArgFields, StViewFields, ST_CLIENT_NAME, - ST_COLUMN_ID, ST_COLUMN_NAME, ST_CONNECTION_CREDENTIALS_ID, ST_CONNECTION_CREDENTIALS_NAME, ST_CONSTRAINT_ID, - ST_CONSTRAINT_NAME, ST_INDEX_ID, ST_INDEX_NAME, ST_MODULE_NAME, ST_RESERVED_SEQUENCE_RANGE, - ST_ROW_LEVEL_SECURITY_ID, ST_ROW_LEVEL_SECURITY_NAME, ST_SCHEDULED_ID, ST_SCHEDULED_NAME, ST_SEQUENCE_ID, - ST_SEQUENCE_NAME, ST_TABLE_NAME, ST_VAR_ID, ST_VAR_NAME, ST_VIEW_ARG_ID, ST_VIEW_ARG_NAME, ST_VIEW_CLIENT_ID, - ST_VIEW_CLIENT_NAME, ST_VIEW_COLUMN_ID, ST_VIEW_COLUMN_NAME, ST_VIEW_ID, ST_VIEW_NAME, ST_VIEW_PARAM_ID, - ST_VIEW_PARAM_NAME, + StSequenceFields, StSequenceRow, StTableRow, StVarFields, ST_CLIENT_NAME, ST_COLUMN_ID, ST_COLUMN_NAME, + ST_CONNECTION_CREDENTIALS_ID, ST_CONNECTION_CREDENTIALS_NAME, ST_CONSTRAINT_ID, ST_CONSTRAINT_NAME, + ST_INDEX_ID, ST_INDEX_NAME, ST_MODULE_NAME, ST_RESERVED_SEQUENCE_RANGE, ST_ROW_LEVEL_SECURITY_ID, + ST_ROW_LEVEL_SECURITY_NAME, ST_SCHEDULED_ID, ST_SCHEDULED_NAME, ST_SEQUENCE_ID, ST_SEQUENCE_NAME, + ST_TABLE_NAME, ST_VAR_ID, ST_VAR_NAME, }; use crate::traits::{IsolationLevel, MutTx}; use crate::Result; @@ -1272,7 +1265,7 @@ mod tests { use spacetimedb_lib::error::ResultTest; use spacetimedb_lib::st_var::StVarValue; use spacetimedb_lib::{resolved_type_via_v9, ScheduleAt, TimeDuration}; - use spacetimedb_primitives::{col_list, ColId, ScheduleId, ViewId}; + use spacetimedb_primitives::{col_list, ColId, ScheduleId}; use spacetimedb_sats::algebraic_value::ser::value_serialize; use spacetimedb_sats::bsatn::ToBsatn; use spacetimedb_sats::layout::RowTypeLayout; @@ -1711,12 +1704,6 @@ mod tests { TableRow { id: ST_SCHEDULED_ID.into(), name: ST_SCHEDULED_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StScheduledFields::ScheduleId.into()) }, TableRow { id: ST_ROW_LEVEL_SECURITY_ID.into(), name: ST_ROW_LEVEL_SECURITY_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StRowLevelSecurityFields::Sql.into()) }, TableRow { id: ST_CONNECTION_CREDENTIALS_ID.into(), name: ST_CONNECTION_CREDENTIALS_NAME, ty: StTableType::System, access: StAccess::Private, primary_key: Some(StConnectionCredentialsFields::ConnectionId.into()) }, - TableRow { id: ST_VIEW_ID.into(), name: ST_VIEW_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StViewFields::ViewId.into()) }, - TableRow { id: ST_VIEW_PARAM_ID.into(), name: ST_VIEW_PARAM_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: None }, - TableRow { id: ST_VIEW_COLUMN_ID.into(), name: ST_VIEW_COLUMN_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: None }, - TableRow { id: ST_VIEW_CLIENT_ID.into(), name: ST_VIEW_CLIENT_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: None }, - TableRow { id: ST_VIEW_ARG_ID.into(), name: ST_VIEW_ARG_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StViewArgFields::Id.into()) }, - ])); #[rustfmt::skip] assert_eq!(query.scan_st_columns()?, map_array([ @@ -1775,30 +1762,6 @@ mod tests { ColRow { table: ST_CONNECTION_CREDENTIALS_ID.into(), pos: 0, name: "connection_id", ty: AlgebraicType::U128 }, ColRow { table: ST_CONNECTION_CREDENTIALS_ID.into(), pos: 1, name: "jwt_payload", ty: AlgebraicType::String }, - - ColRow { table: ST_VIEW_ID.into(), pos: 0, name: "view_id", ty: ViewId::get_type() }, - ColRow { table: ST_VIEW_ID.into(), pos: 1, name: "view_name", ty: AlgebraicType::String }, - ColRow { table: ST_VIEW_ID.into(), pos: 2, name: "table_id", ty: AlgebraicType::option(TableId::get_type()) }, - ColRow { table: ST_VIEW_ID.into(), pos: 3, name: "is_public", ty: AlgebraicType::Bool }, - ColRow { table: ST_VIEW_ID.into(), pos: 4, name: "is_anonymous", ty: AlgebraicType::Bool }, - - ColRow { table: ST_VIEW_PARAM_ID.into(), pos: 0, name: "view_id", ty: ViewId::get_type() }, - ColRow { table: ST_VIEW_PARAM_ID.into(), pos: 1, name: "param_pos", ty: ColId::get_type() }, - ColRow { table: ST_VIEW_PARAM_ID.into(), pos: 2, name: "param_name", ty: AlgebraicType::String }, - ColRow { table: ST_VIEW_PARAM_ID.into(), pos: 3, name: "param_type", ty: AlgebraicType::bytes() }, - - ColRow { table: ST_VIEW_COLUMN_ID.into(), pos: 0, name: "view_id", ty: ViewId::get_type() }, - ColRow { table: ST_VIEW_COLUMN_ID.into(), pos: 1, name: "col_pos", ty: ColId::get_type() }, - ColRow { table: ST_VIEW_COLUMN_ID.into(), pos: 2, name: "col_name", ty: AlgebraicType::String }, - ColRow { table: ST_VIEW_COLUMN_ID.into(), pos: 3, name: "col_type", ty: AlgebraicType::bytes() }, - - ColRow { table: ST_VIEW_CLIENT_ID.into(), pos: 0, name: "view_id", ty: ViewId::get_type() }, - ColRow { table: ST_VIEW_CLIENT_ID.into(), pos: 1, name: "arg_id", ty: AlgebraicType::U64 }, - ColRow { table: ST_VIEW_CLIENT_ID.into(), pos: 2, name: "identity", ty: AlgebraicType::U256 }, - ColRow { table: ST_VIEW_CLIENT_ID.into(), pos: 3, name: "connection_id", ty: AlgebraicType::U128 }, - - ColRow { table: ST_VIEW_ARG_ID.into(), pos: 0, name: "id", ty: AlgebraicType::U64 }, - ColRow { table: ST_VIEW_ARG_ID.into(), pos: 1, name: "bytes", ty: AlgebraicType::bytes() }, ])); #[rustfmt::skip] assert_eq!(query.scan_st_indexes()?, map_array([ @@ -1815,14 +1778,6 @@ mod tests { IndexRow { id: 11, table: ST_ROW_LEVEL_SECURITY_ID.into(), col: col(0), name: "st_row_level_security_table_id_idx_btree", }, IndexRow { id: 12, table: ST_ROW_LEVEL_SECURITY_ID.into(), col: col(1), name: "st_row_level_security_sql_idx_btree", }, IndexRow { id: 13, table: ST_CONNECTION_CREDENTIALS_ID.into(), col: col(0), name: "st_connection_credentials_connection_id_idx_btree", }, - IndexRow { id: 14, table: ST_VIEW_ID.into(), col: col(0), name: "st_view_view_id_idx_btree", }, - IndexRow { id: 15, table: ST_VIEW_ID.into(), col: col(1), name: "st_view_view_name_idx_btree", }, - IndexRow { id: 16, table: ST_VIEW_PARAM_ID.into(), col: col_list![0, 1], name: "st_view_param_view_id_param_pos_idx_btree", }, - IndexRow { id: 17, table: ST_VIEW_COLUMN_ID.into(), col: col_list![0, 1], name: "st_view_column_view_id_col_pos_idx_btree", }, - IndexRow { id: 18, table: ST_VIEW_CLIENT_ID.into(), col: col_list![0, 1], name: "st_view_client_view_id_arg_id_idx_btree", }, - IndexRow { id: 19, table: ST_VIEW_CLIENT_ID.into(), col: col_list![2, 3], name: "st_view_client_identity_connection_id_idx_btree", }, - IndexRow { id: 20, table: ST_VIEW_ARG_ID.into(), col: col(0), name: "st_view_arg_id_idx_btree", }, - IndexRow { id: 21, table: ST_VIEW_ARG_ID.into(), col: col(1), name: "st_view_arg_bytes_idx_btree", }, ])); let start = ST_RESERVED_SEQUENCE_RANGE as i128 + 1; #[rustfmt::skip] @@ -1833,8 +1788,6 @@ mod tests { SequenceRow { id: 2, table: ST_INDEX_ID.into(), col_pos: 0, name: "st_index_index_id_seq", start }, SequenceRow { id: 3, table: ST_CONSTRAINT_ID.into(), col_pos: 0, name: "st_constraint_constraint_id_seq", start }, SequenceRow { id: 4, table: ST_SCHEDULED_ID.into(), col_pos: 0, name: "st_scheduled_schedule_id_seq", start }, - SequenceRow { id: 6, table: ST_VIEW_ID.into(), col_pos: 0, name: "st_view_view_id_seq", start }, - SequenceRow { id: 7, table: ST_VIEW_ARG_ID.into(), col_pos: 0, name: "st_view_arg_id_seq", start }, ], |row| StSequenceRow { allocated: start - 1, @@ -1855,13 +1808,7 @@ mod tests { ConstraintRow { constraint_id: 10, table_id: ST_SCHEDULED_ID.into(), unique_columns: col(1), constraint_name: "st_scheduled_table_id_key", }, ConstraintRow { constraint_id: 11, table_id: ST_ROW_LEVEL_SECURITY_ID.into(), unique_columns: col(1), constraint_name: "st_row_level_security_sql_key", }, ConstraintRow { constraint_id: 12, table_id: ST_CONNECTION_CREDENTIALS_ID.into(), unique_columns: col(0), constraint_name: "st_connection_credentials_connection_id_key", }, - ConstraintRow { constraint_id: 13, table_id: ST_VIEW_ID.into(), unique_columns: col(0), constraint_name: "st_view_view_id_key", }, - ConstraintRow { constraint_id: 14, table_id: ST_VIEW_ID.into(), unique_columns: col(1), constraint_name: "st_view_view_name_key", }, - ConstraintRow { constraint_id: 15, table_id: ST_VIEW_PARAM_ID.into(), unique_columns: col_list![0, 1], constraint_name: "st_view_param_view_id_param_pos_key", }, - ConstraintRow { constraint_id: 16, table_id: ST_VIEW_COLUMN_ID.into(), unique_columns: col_list![0, 1], constraint_name: "st_view_column_view_id_col_pos_key", }, - ConstraintRow { constraint_id: 17, table_id: ST_VIEW_ARG_ID.into(), unique_columns: col(0), constraint_name: "st_view_arg_id_key", }, - ConstraintRow { constraint_id: 18, table_id: ST_VIEW_ARG_ID.into(), unique_columns: col(1), constraint_name: "st_view_arg_bytes_key", }, - ])); + ])); // Verify we get back the tables correctly with the proper ids... let cols = query.scan_st_columns()?; @@ -2277,14 +2224,6 @@ mod tests { IndexRow { id: 11, table: ST_ROW_LEVEL_SECURITY_ID.into(), col: col(0), name: "st_row_level_security_table_id_idx_btree", }, IndexRow { id: 12, table: ST_ROW_LEVEL_SECURITY_ID.into(), col: col(1), name: "st_row_level_security_sql_idx_btree", }, IndexRow { id: 13, table: ST_CONNECTION_CREDENTIALS_ID.into(), col: col(0), name: "st_connection_credentials_connection_id_idx_btree", }, - IndexRow { id: 14, table: ST_VIEW_ID.into(), col: col(0), name: "st_view_view_id_idx_btree", }, - IndexRow { id: 15, table: ST_VIEW_ID.into(), col: col(1), name: "st_view_view_name_idx_btree", }, - IndexRow { id: 16, table: ST_VIEW_PARAM_ID.into(), col: col_list![0, 1], name: "st_view_param_view_id_param_pos_idx_btree", }, - IndexRow { id: 17, table: ST_VIEW_COLUMN_ID.into(), col: col_list![0, 1], name: "st_view_column_view_id_col_pos_idx_btree", }, - IndexRow { id: 18, table: ST_VIEW_CLIENT_ID.into(), col: col_list![0, 1], name: "st_view_client_view_id_arg_id_idx_btree", }, - IndexRow { id: 19, table: ST_VIEW_CLIENT_ID.into(), col: col_list![2, 3], name: "st_view_client_identity_connection_id_idx_btree", }, - IndexRow { id: 20, table: ST_VIEW_ARG_ID.into(), col: col(0), name: "st_view_arg_id_idx_btree", }, - IndexRow { id: 21, table: ST_VIEW_ARG_ID.into(), col: col(1), name: "st_view_arg_bytes_idx_btree", }, IndexRow { id: seq_start, table: FIRST_NON_SYSTEM_ID, col: col(0), name: "Foo_id_idx_btree", }, IndexRow { id: seq_start + 1, table: FIRST_NON_SYSTEM_ID, col: col(1), name: "Foo_name_idx_btree", }, IndexRow { id: seq_start + 2, table: FIRST_NON_SYSTEM_ID, col: col(2), name: "Foo_age_idx_btree", }, @@ -3375,7 +3314,7 @@ mod tests { table_id: TableId::SENTINEL, schedule_id: ScheduleId::SENTINEL, schedule_name: "schedule".into(), - function_name: "reducer".into(), + reducer_name: "reducer".into(), at_column: 1.into(), }; let sum_ty = AlgebraicType::sum([("foo", AlgebraicType::Bool), ("bar", AlgebraicType::U16)]); diff --git a/crates/datastore/src/locking_tx_datastore/mut_tx.rs b/crates/datastore/src/locking_tx_datastore/mut_tx.rs index 98ae9b93a19..d449717d03e 100644 --- a/crates/datastore/src/locking_tx_datastore/mut_tx.rs +++ b/crates/datastore/src/locking_tx_datastore/mut_tx.rs @@ -8,10 +8,11 @@ use super::{ tx_state::{IndexIdMap, PendingSchemaChange, TxState, TxTableForInsertion}, SharedMutexGuard, SharedWriteGuard, }; +use crate::execution_context::ExecutionContext; +use crate::execution_context::Workload; use crate::system_tables::{ - system_tables, ConnectionIdViaU128, StConnectionCredentialsFields, StConnectionCredentialsRow, StViewColumnFields, - StViewFields, StViewParamFields, StViewParamRow, ST_CONNECTION_CREDENTIALS_ID, ST_VIEW_COLUMN_ID, ST_VIEW_ID, - ST_VIEW_PARAM_ID, + system_tables, ConnectionIdViaU128, StConnectionCredentialsFields, StConnectionCredentialsRow, + ST_CONNECTION_CREDENTIALS_ID, }; use crate::traits::{InsertFlags, RowTypeForTable, TxData, UpdateFlags}; use crate::{ @@ -24,13 +25,10 @@ use crate::{ ST_SEQUENCE_ID, ST_TABLE_ID, }, }; -use crate::{execution_context::ExecutionContext, system_tables::StViewColumnRow}; -use crate::{execution_context::Workload, system_tables::StViewRow}; use core::ops::RangeBounds; use core::{cell::RefCell, mem}; use core::{iter, ops::Bound}; use smallvec::SmallVec; -use spacetimedb_data_structures::map::{IntMap, IntSet}; use spacetimedb_durability::TxOffset; use spacetimedb_execution::{dml::MutDatastore, Datastore, DeltaStore, Row}; use spacetimedb_lib::{db::raw_def::v9::RawSql, metrics::ExecutionMetrics}; @@ -39,7 +37,7 @@ use spacetimedb_lib::{ ConnectionId, Identity, }; use spacetimedb_primitives::{ - col_list, ColId, ColList, ColSet, ConstraintId, IndexId, ScheduleId, SequenceId, TableId, ViewId, + col_list, ColId, ColList, ColSet, ConstraintId, IndexId, ScheduleId, SequenceId, TableId, }; use spacetimedb_sats::{ bsatn::{self, to_writer, DecodeError, Deserializer}, @@ -47,9 +45,8 @@ use spacetimedb_sats::{ ser::Serialize, AlgebraicType, AlgebraicValue, ProductType, ProductValue, WithTypespace, }; -use spacetimedb_schema::{ - def::{ModuleDef, ViewColumnDef, ViewDef}, - schema::{ColumnSchema, ConstraintSchema, IndexSchema, RowLevelSecuritySchema, SequenceSchema, TableSchema}, +use spacetimedb_schema::schema::{ + ColumnSchema, ConstraintSchema, IndexSchema, RowLevelSecuritySchema, SequenceSchema, TableSchema, }; use spacetimedb_table::{ blob_store::BlobStore, @@ -69,55 +66,6 @@ use std::{ type DecodeResult = core::result::Result; -/// Views track their read sets and update the [`CommittedState`] with them. -/// The [`CommittedState`] maintains these read sets in order to determine when to re-evaluate a view. -#[derive(Default)] -pub struct ReadSet { - table_scans: IntSet, - index_keys: IntMap>, -} - -impl ReadSet { - /// Enumerate the tables that are scanned and tracked by this read set - pub fn tables_scanned(&self) -> impl Iterator + '_ { - self.table_scans.iter() - } - - /// Enumerate the single index keys that are tracked by this read set - pub fn index_keys_scanned(&self) -> impl Iterator + '_ { - self.index_keys - .iter() - .flat_map(|(table_id, keys)| keys.iter().map(move |(index_id, key)| (table_id, index_id, key))) - } - - /// Track a table scan in this read set - fn insert_table_scan(&mut self, table_id: TableId) { - self.table_scans.insert(table_id); - } - - /// Track an index scan in this read set. - /// If we only read a single index key we record the key. - /// If we read a range, we treat it as though we scanned the entire table. - fn insert_index_scan( - &mut self, - table_id: TableId, - index_id: IndexId, - lower: Bound, - upper: Bound, - ) { - match (lower, upper) { - (Bound::Included(lower), Bound::Included(upper)) if lower == upper => { - self.index_keys.entry(table_id).or_default().insert(index_id, lower); - } - _ => { - self.table_scans.insert(table_id); - } - } - } -} - -pub type ViewReadSets = IntMap; - /// Represents a Mutable transaction. Holds locks for its duration /// /// The initialization of this struct is sensitive because improper @@ -128,7 +76,6 @@ pub struct MutTxId { pub(super) committed_state_write_lock: SharedWriteGuard, pub(super) sequence_state_lock: SharedMutexGuard, pub(super) lock_wait_time: Duration, - pub(super) read_sets: ViewReadSets, // TODO(cloutiertyler): The below were made `pub` for the datastore split. We should // make these private again. pub timer: Instant, @@ -136,33 +83,7 @@ pub struct MutTxId { pub metrics: ExecutionMetrics, } -static_assert_size!(MutTxId, 432); - -impl MutTxId { - /// Record that a view performs a table scan in this transaction's read set - pub fn record_table_scan(&mut self, view_id: Option, table_id: TableId) { - if let Some(view_id) = view_id { - self.read_sets.entry(view_id).or_default().insert_table_scan(table_id) - } - } - - /// Record that a view performs an index scan in this transaction's read set - pub fn record_index_scan( - &mut self, - view_id: Option, - table_id: TableId, - index_id: IndexId, - lower: Bound, - upper: Bound, - ) { - if let Some(view_id) = view_id { - self.read_sets - .entry(view_id) - .or_default() - .insert_index_scan(table_id, index_id, lower, upper) - } - } -} +static_assert_size!(MutTxId, 400); impl Datastore for MutTxId { type TableIter<'a> @@ -293,53 +214,6 @@ impl MutTxId { Ok(()) } - /// Create a backing table for a view and update the system tables. - /// - /// Requires: - /// - Everything [`Self::create_table`] requires. - /// - /// Ensures: - /// - Everything [`Self::create_table`] ensures. - /// - The returned [`ViewId`] is unique and not [`ViewId::SENTINEL`]. - /// - All view metadata maintained by the datastore is created atomically - pub fn create_view(&mut self, module_def: &ModuleDef, view_def: &ViewDef) -> Result<(ViewId, TableId)> { - let table_schema = TableSchema::from_view_def(module_def, view_def); - let table_id = self.create_table(table_schema)?; - - let ViewDef { - name, - is_anonymous, - is_public, - params, - return_columns, - .. - } = view_def; - - let view_id = self.insert_into_st_view(name.clone().into(), table_id, *is_public, *is_anonymous)?; - self.insert_into_st_view_param(view_id, params)?; - self.insert_into_st_view_column(view_id, return_columns)?; - Ok((view_id, table_id)) - } - - /// Drop the backing table of a view and update the system tables. - pub fn drop_view(&mut self, view_id: ViewId) -> Result<()> { - // Drop the view's metadata - self.drop_st_view(view_id)?; - self.drop_st_view_param(view_id)?; - self.drop_st_view_column(view_id)?; - - // Drop the view's backing table if materialized - if let StViewRow { - table_id: Some(table_id), - .. - } = self.lookup_st_view(view_id)? - { - return self.drop_table(table_id); - }; - - Ok(()) - } - /// Create a table. /// /// Requires: @@ -401,7 +275,7 @@ impl MutTxId { table_id: schedule.table_id, schedule_id: schedule.schedule_id, schedule_name: schedule.schedule_name, - reducer_name: schedule.function_name, + reducer_name: schedule.reducer_name, at_column: schedule.at_column, }; let id = self @@ -444,75 +318,6 @@ impl MutTxId { }) } - fn lookup_st_view(&self, view_id: ViewId) -> Result { - let row = self - .iter_by_col_eq(ST_VIEW_ID, StViewFields::ViewId, &view_id.into())? - .next() - .ok_or_else(|| TableError::IdNotFound(SystemTable::st_view, view_id.into()))?; - - StViewRow::try_from(row) - } - - /// Insert a row into `st_view`, auto-increments and returns the [`ViewId`]. - fn insert_into_st_view( - &mut self, - view_name: Box, - table_id: TableId, - is_public: bool, - is_anonymous: bool, - ) -> Result { - Ok(self - .insert_via_serialize_bsatn( - ST_VIEW_ID, - &StViewRow { - view_id: ViewId::SENTINEL, - view_name, - table_id: Some(table_id), - is_public, - is_anonymous, - }, - )? - .1 - .collapse() - .read_col(StViewFields::ViewId)?) - } - - /// For each parameter of a view, insert a row into `st_view_param`. - /// This does not include the context parameter. - fn insert_into_st_view_param(&mut self, view_id: ViewId, params: &ProductType) -> Result<()> { - for (i, field) in params.elements.iter().enumerate() { - self.insert_via_serialize_bsatn( - ST_VIEW_PARAM_ID, - &StViewParamRow { - view_id, - param_pos: i.into(), - param_name: field - .name - .clone() - .unwrap_or_else(|| format!("param_{i}").into_boxed_str()), - param_type: field.algebraic_type.clone().into(), - }, - )?; - } - Ok(()) - } - - /// For each column or field returned in a view, insert a row into `st_view_column`. - fn insert_into_st_view_column(&mut self, view_id: ViewId, columns: &[ViewColumnDef]) -> Result<()> { - for def in columns { - self.insert_via_serialize_bsatn( - ST_VIEW_COLUMN_ID, - &StViewColumnRow { - view_id, - col_pos: def.col_id, - col_name: def.name.clone().into(), - col_type: def.ty.clone().into(), - }, - )?; - } - Ok(()) - } - fn create_table_internal(&mut self, schema: Arc) { // Construct the in memory tables. let table_id = schema.table_id; @@ -558,26 +363,6 @@ impl MutTxId { self.delete_col_eq(ST_COLUMN_ID, StColumnFields::TableId.col_id(), &table_id.into()) } - /// Drops the row in `st_table` for this `table_id` - fn drop_st_table(&mut self, table_id: TableId) -> Result<()> { - self.delete_col_eq(ST_TABLE_ID, StTableFields::TableId.col_id(), &table_id.into()) - } - - /// Drops the row in `st_view` for this `view_id` - fn drop_st_view(&mut self, view_id: ViewId) -> Result<()> { - self.delete_col_eq(ST_VIEW_ID, StViewFields::ViewId.col_id(), &view_id.into()) - } - - /// Drops the rows in `st_view_param` for this `view_id` - fn drop_st_view_param(&mut self, view_id: ViewId) -> Result<()> { - self.delete_col_eq(ST_VIEW_PARAM_ID, StViewParamFields::ViewId.col_id(), &view_id.into()) - } - - /// Drops the rows in `st_view_column` for this `view_id` - fn drop_st_view_column(&mut self, view_id: ViewId) -> Result<()> { - self.delete_col_eq(ST_VIEW_COLUMN_ID, StViewColumnFields::ViewId.col_id(), &view_id.into()) - } - pub fn drop_table(&mut self, table_id: TableId) -> Result<()> { self.clear_table(table_id)?; @@ -596,7 +381,7 @@ impl MutTxId { } // Drop the table and their columns - self.drop_st_table(table_id)?; + self.delete_col_eq(ST_TABLE_ID, StTableFields::TableId.col_id(), &table_id.into())?; self.drop_st_column(table_id)?; if let Some(schedule) = &schema.schedule { @@ -645,14 +430,6 @@ impl MutTxId { Ok(ret) } - pub fn view_id_from_name(&self, view_name: &str) -> Result> { - let view_name = &view_name.into(); - let row = self - .iter_by_col_eq(ST_VIEW_ID, StViewFields::ViewName, view_name)? - .next(); - Ok(row.map(|row| row.read_col(StViewFields::ViewId).unwrap())) - } - pub fn table_id_from_name(&self, table_name: &str) -> Result> { let table_name = &table_name.into(); let row = self @@ -1027,12 +804,7 @@ impl MutTxId { prefix_elems: ColId, rstart: &[u8], rend: &[u8], - ) -> Result<( - TableId, - Bound, - Bound, - IndexScanRanged<'a>, - )> { + ) -> Result<(TableId, IndexScanRanged<'a>)> { // Extract the table id, and commit/tx indices. let (table_id, commit_index, tx_index) = self .get_table_and_index(index_id) @@ -1051,12 +823,9 @@ impl MutTxId { let tx_iter = tx_index.map(|i| i.seek_range(&bounds)); let commit_iter = commit_index.seek_range(&bounds); - let (lower, upper) = bounds; - let dt = self.tx_state.get_delete_table(table_id); let iter = combine_range_index_iters(dt, tx_iter, commit_iter); - - Ok((table_id, lower, upper, iter)) + Ok((table_id, iter)) } /// Translate `index_id` to the table id, and commit/tx indices. @@ -1260,19 +1029,14 @@ impl MutTxId { /// - The sequence metadata is inserted into the system tables (and other data structures reflecting them). /// - The returned ID is unique and not `SequenceId::SENTINEL`. pub fn create_sequence(&mut self, seq: SequenceSchema) -> Result { + if seq.sequence_id != SequenceId::SENTINEL { + return Err(anyhow::anyhow!("`sequence_id` must be `SequenceId::SENTINEL` in `{:#?}`", seq).into()); + } if seq.table_id == TableId::SENTINEL { return Err(anyhow::anyhow!("`table_id` must not be `TableId::SENTINEL` in `{seq:#?}`").into()); } let table_id = seq.table_id; - let matching_system_table_schema = system_tables().iter().find(|s| s.table_id == table_id).cloned(); - - if seq.sequence_id != SequenceId::SENTINEL && matching_system_table_schema.is_none() { - return Err(anyhow::anyhow!("`sequence_id` must be `SequenceId::SENTINEL` in `{:#?}`", seq).into()); - } - - let sequence_id = seq.sequence_id; - log::trace!( "SEQUENCE CREATING: {} for table: {} and col: {}", seq.sequence_name, @@ -1284,7 +1048,7 @@ impl MutTxId { // NOTE: Because st_sequences has a unique index on sequence_name, this will // fail if the table already exists. let mut sequence_row = StSequenceRow { - sequence_id, + sequence_id: SequenceId::SENTINEL, sequence_name: seq.sequence_name, table_id, col_pos: seq.col_pos, @@ -1571,9 +1335,7 @@ impl MutTxId { /// - `String`, the name of the reducer which ran during this transaction. pub(super) fn commit(mut self) -> (TxOffset, TxData, TxMetrics, String) { let tx_offset = self.committed_state_write_lock.next_tx_offset; - let tx_data = self - .committed_state_write_lock - .merge(self.tx_state, self.read_sets, &self.ctx); + let tx_data = self.committed_state_write_lock.merge(self.tx_state, &self.ctx); // Compute and keep enough info that we can // record metrics after the transaction has ended @@ -1615,9 +1377,7 @@ impl MutTxId { /// - [`TxMetrics`], various measurements of the work performed by this transaction. /// - [`TxId`], a read-only transaction with a shared lock on the committed state. pub fn commit_downgrade(mut self, workload: Workload) -> (TxData, TxMetrics, TxId) { - let tx_data = self - .committed_state_write_lock - .merge(self.tx_state, self.read_sets, &self.ctx); + let tx_data = self.committed_state_write_lock.merge(self.tx_state, &self.ctx); // Compute and keep enough info that we can // record metrics after the transaction has ended diff --git a/crates/datastore/src/system_tables.rs b/crates/datastore/src/system_tables.rs index 503ef4d9576..da97e186e4f 100644 --- a/crates/datastore/src/system_tables.rs +++ b/crates/datastore/src/system_tables.rs @@ -65,17 +65,6 @@ pub const ST_ROW_LEVEL_SECURITY_ID: TableId = TableId(10); /// The static ID of the table that stores the credentials for each connection. pub const ST_CONNECTION_CREDENTIALS_ID: TableId = TableId(11); -/// The static ID of the table that tracks views -pub const ST_VIEW_ID: TableId = TableId(12); -/// The static ID of the table that tracks view parameters -pub const ST_VIEW_PARAM_ID: TableId = TableId(13); -/// The static ID of the table that tracks view columns -pub const ST_VIEW_COLUMN_ID: TableId = TableId(14); -/// The static ID of the table that tracks the clients subscribed to each view -pub const ST_VIEW_CLIENT_ID: TableId = TableId(15); -/// The static ID of the table that tracks view arguments -pub const ST_VIEW_ARG_ID: TableId = TableId(16); - pub(crate) const ST_CONNECTION_CREDENTIALS_NAME: &str = "st_connection_credentials"; pub const ST_TABLE_NAME: &str = "st_table"; pub const ST_COLUMN_NAME: &str = "st_column"; @@ -87,11 +76,6 @@ pub(crate) const ST_CLIENT_NAME: &str = "st_client"; pub(crate) const ST_SCHEDULED_NAME: &str = "st_scheduled"; pub(crate) const ST_VAR_NAME: &str = "st_var"; pub(crate) const ST_ROW_LEVEL_SECURITY_NAME: &str = "st_row_level_security"; -pub(crate) const ST_VIEW_NAME: &str = "st_view"; -pub(crate) const ST_VIEW_PARAM_NAME: &str = "st_view_param"; -pub(crate) const ST_VIEW_COLUMN_NAME: &str = "st_view_column"; -pub(crate) const ST_VIEW_CLIENT_NAME: &str = "st_view_client"; -pub(crate) const ST_VIEW_ARG_NAME: &str = "st_view_arg"; /// Reserved range of sequence values used for system tables. /// /// Ids for user-created tables will start at `ST_RESERVED_SEQUENCE_RANGE`. @@ -113,7 +97,6 @@ pub const ST_RESERVED_SEQUENCE_RANGE: u32 = 4096; #[derive(Debug, Display)] pub enum SystemTable { st_table, - st_view, st_column, st_sequence, st_index, @@ -121,7 +104,7 @@ pub enum SystemTable { st_row_level_security, } -pub fn system_tables() -> [TableSchema; 16] { +pub fn system_tables() -> [TableSchema; 11] { [ // The order should match the `id` of the system table, that start with [ST_TABLE_IDX]. st_table_schema(), @@ -135,11 +118,6 @@ pub fn system_tables() -> [TableSchema; 16] { st_row_level_security_schema(), st_sequence_schema(), st_connection_credential_schema(), - st_view_schema(), - st_view_param_schema(), - st_view_column_schema(), - st_view_client_schema(), - st_view_arg_schema(), ] } @@ -179,11 +157,6 @@ pub(crate) const ST_SCHEDULED_IDX: usize = 7; pub(crate) const ST_ROW_LEVEL_SECURITY_IDX: usize = 8; pub(crate) const ST_SEQUENCE_IDX: usize = 9; pub(crate) const ST_CONNECTION_CREDENTIALS_IDX: usize = 10; -pub(crate) const ST_VIEW_IDX: usize = 11; -pub(crate) const ST_VIEW_PARAM_IDX: usize = 12; -pub(crate) const ST_VIEW_COLUMN_IDX: usize = 13; -pub(crate) const ST_VIEW_CLIENT_IDX: usize = 14; -pub(crate) const ST_VIEW_ARG_IDX: usize = 15; macro_rules! st_fields_enum { ($(#[$attr:meta])* enum $ty_name:ident { $($name:expr, $var:ident = $discr:expr,)* }) => { @@ -228,14 +201,6 @@ st_fields_enum!(enum StTableFields { "table_primary_key", PrimaryKey = 4, }); // WARNING: For a stable schema, don't change the field names and discriminants. -st_fields_enum!(enum StViewFields { - "view_id", ViewId = 0, - "view_name", ViewName = 1, - "table_id", TableId = 2, - "is_public", IsPublic = 3, - "is_anonymous", IsAnonymous = 4, -}); -// WARNING: For a stable schema, don't change the field names and discriminants. st_fields_enum!(enum StColumnFields { "table_id", TableId = 0, "col_pos", ColPos = 1, @@ -243,32 +208,6 @@ st_fields_enum!(enum StColumnFields { "col_type", ColType = 3, }); // WARNING: For a stable schema, don't change the field names and discriminants. -st_fields_enum!(enum StViewColumnFields { - "view_id", ViewId = 0, - "col_pos", ColPos = 1, - "col_name", ColName = 2, - "col_type", ColType = 3, -}); -// WARNING: For a stable schema, don't change the field names and discriminants. -st_fields_enum!(enum StViewClientFields { - "view_id", ViewId = 0, - "arg_id", ArgId = 1, - "identity", Identity = 2, - "connection_id", ConnectionId = 3, -}); -// WARNING: For a stable schema, don't change the field names and discriminants. -st_fields_enum!(enum StViewArgFields { - "id", Id = 0, - "bytes", Bytes = 1, -}); -// WARNING: For a stable schema, don't change the field names and discriminants. -st_fields_enum!(enum StViewParamFields { - "view_id", ViewId = 0, - "param_pos", ParamPos = 1, - "param_name", ParamName = 2, - "param_type", ParamType = 3, -}); -// WARNING: For a stable schema, don't change the field names and discriminants. st_fields_enum!(enum StIndexFields { "index_id", IndexId = 0, "table_id", TableId = 1, @@ -365,15 +304,6 @@ fn system_module_def() -> ModuleDef { .with_unique_constraint(StTableFields::TableName) .with_index_no_accessor_name(btree(StTableFields::TableName)); - let st_view_type = builder.add_type::(); - builder - .build_table(ST_VIEW_NAME, *st_view_type.as_ref().expect("should be ref")) - .with_type(TableType::System) - .with_auto_inc_primary_key(StViewFields::ViewId) - .with_index_no_accessor_name(btree(StViewFields::ViewId)) - .with_unique_constraint(StViewFields::ViewName) - .with_index_no_accessor_name(btree(StViewFields::ViewName)); - let st_raw_column_type = builder.add_type::(); let st_col_row_unique_cols = [StColumnFields::TableId.col_id(), StColumnFields::ColPos.col_id()]; builder @@ -382,41 +312,6 @@ fn system_module_def() -> ModuleDef { .with_unique_constraint(st_col_row_unique_cols) .with_index_no_accessor_name(btree(st_col_row_unique_cols)); - let st_view_col_type = builder.add_type::(); - let st_view_col_unique_cols = [StViewColumnFields::ViewId.col_id(), StViewColumnFields::ColPos.col_id()]; - builder - .build_table(ST_VIEW_COLUMN_NAME, *st_view_col_type.as_ref().expect("should be ref")) - .with_type(TableType::System) - .with_unique_constraint(st_view_col_unique_cols) - .with_index_no_accessor_name(btree(st_view_col_unique_cols)); - - let st_view_param_type = builder.add_type::(); - let st_view_param_unique_cols = [StViewParamFields::ViewId.col_id(), StViewParamFields::ParamPos.col_id()]; - builder - .build_table(ST_VIEW_PARAM_NAME, *st_view_param_type.as_ref().expect("should be ref")) - .with_type(TableType::System) - .with_unique_constraint(st_view_param_unique_cols) - .with_index_no_accessor_name(btree(st_view_param_unique_cols)); - - let st_view_client_type = builder.add_type::(); - builder - .build_table( - ST_VIEW_CLIENT_NAME, - *st_view_client_type.as_ref().expect("should be ref"), - ) - .with_type(TableType::System) - .with_index_no_accessor_name(btree([StViewClientFields::ViewId, StViewClientFields::ArgId])) - .with_index_no_accessor_name(btree([StViewClientFields::Identity, StViewClientFields::ConnectionId])); - - let st_view_arg_type = builder.add_type::(); - builder - .build_table(ST_VIEW_ARG_NAME, *st_view_arg_type.as_ref().expect("should be ref")) - .with_type(TableType::System) - .with_auto_inc_primary_key(StViewArgFields::Id) - .with_index_no_accessor_name(btree(StViewArgFields::Id)) - .with_unique_constraint(StViewArgFields::Bytes) - .with_index_no_accessor_name(btree(StViewArgFields::Bytes)); - let st_index_type = builder.add_type::(); builder .build_table(ST_INDEX_NAME, *st_index_type.as_ref().expect("should be ref")) @@ -513,11 +408,6 @@ fn system_module_def() -> ModuleDef { validate_system_table::(&result, ST_VAR_NAME); validate_system_table::(&result, ST_SCHEDULED_NAME); validate_system_table::(&result, ST_CONNECTION_CREDENTIALS_NAME); - validate_system_table::(&result, ST_VIEW_NAME); - validate_system_table::(&result, ST_VIEW_PARAM_NAME); - validate_system_table::(&result, ST_VIEW_COLUMN_NAME); - validate_system_table::(&result, ST_VIEW_CLIENT_NAME); - validate_system_table::(&result, ST_VIEW_ARG_NAME); result } @@ -553,12 +443,6 @@ lazy_static::lazy_static! { m.insert("st_scheduled_table_id_key", ConstraintId(10)); m.insert("st_row_level_security_sql_key", ConstraintId(11)); m.insert("st_connection_credentials_connection_id_key", ConstraintId(12)); - m.insert("st_view_view_id_key", ConstraintId(13)); - m.insert("st_view_view_name_key", ConstraintId(14)); - m.insert("st_view_param_view_id_param_pos_key", ConstraintId(15)); - m.insert("st_view_column_view_id_col_pos_key", ConstraintId(16)); - m.insert("st_view_arg_id_key", ConstraintId(17)); - m.insert("st_view_arg_bytes_key", ConstraintId(18)); m }; } @@ -581,14 +465,6 @@ lazy_static::lazy_static! { m.insert("st_row_level_security_table_id_idx_btree", IndexId(11)); m.insert("st_row_level_security_sql_idx_btree", IndexId(12)); m.insert("st_connection_credentials_connection_id_idx_btree", IndexId(13)); - m.insert("st_view_view_id_idx_btree", IndexId(14)); - m.insert("st_view_view_name_idx_btree", IndexId(15)); - m.insert("st_view_param_view_id_param_pos_idx_btree", IndexId(16)); - m.insert("st_view_column_view_id_col_pos_idx_btree", IndexId(17)); - m.insert("st_view_client_view_id_arg_id_idx_btree", IndexId(18)); - m.insert("st_view_client_identity_connection_id_idx_btree", IndexId(19)); - m.insert("st_view_arg_id_idx_btree", IndexId(20)); - m.insert("st_view_arg_bytes_idx_btree", IndexId(21)); m }; } @@ -603,8 +479,6 @@ lazy_static::lazy_static! { m.insert("st_constraint_constraint_id_seq", SequenceId(3)); m.insert("st_scheduled_schedule_id_seq", SequenceId(4)); m.insert("st_sequence_sequence_id_seq", SequenceId(5)); - m.insert("st_view_view_id_seq", SequenceId(6)); - m.insert("st_view_arg_id_seq", SequenceId(7)); m }; } @@ -705,26 +579,6 @@ pub fn st_var_schema() -> TableSchema { st_schema(ST_VAR_NAME, ST_VAR_ID) } -pub fn st_view_schema() -> TableSchema { - st_schema(ST_VIEW_NAME, ST_VIEW_ID) -} - -pub fn st_view_param_schema() -> TableSchema { - st_schema(ST_VIEW_PARAM_NAME, ST_VIEW_PARAM_ID) -} - -pub fn st_view_column_schema() -> TableSchema { - st_schema(ST_VIEW_COLUMN_NAME, ST_VIEW_COLUMN_ID) -} - -pub fn st_view_client_schema() -> TableSchema { - st_schema(ST_VIEW_CLIENT_NAME, ST_VIEW_CLIENT_ID) -} - -pub fn st_view_arg_schema() -> TableSchema { - st_schema(ST_VIEW_ARG_NAME, ST_VIEW_ARG_ID) -} - /// If `table_id` refers to a known system table, return its schema. /// /// Used when restoring from a snapshot; system tables are reinstantiated with this schema, @@ -744,11 +598,6 @@ pub(crate) fn system_table_schema(table_id: TableId) -> Option { ST_CONNECTION_CREDENTIALS_ID => Some(st_connection_credential_schema()), ST_VAR_ID => Some(st_var_schema()), ST_SCHEDULED_ID => Some(st_scheduled_schema()), - ST_VIEW_ID => Some(st_view_schema()), - ST_VIEW_PARAM_ID => Some(st_view_param_schema()), - ST_VIEW_COLUMN_ID => Some(st_view_column_schema()), - ST_VIEW_CLIENT_ID => Some(st_view_client_schema()), - ST_VIEW_ARG_ID => Some(st_view_arg_schema()), _ => None, } } @@ -784,39 +633,6 @@ impl From for ProductValue { } } -/// System Table [ST_VIEW_NAME] -/// -/// | view_id | view_name | table_id | is_public | is_anonymous | -/// |---------|-----------|----------|-----------|--------------| -/// | 1 | "player" | 4 | true | true | -#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)] -#[sats(crate = spacetimedb_lib)] -pub struct StViewRow { - /// An auto-inc id for each view - pub view_id: ViewId, - /// The name of the view function as defined in the module - pub view_name: Box, - /// The [`TableId`] for this view if materialized. - /// Currently all views are materialized and therefore are assigned a [`TableId`] by default. - pub table_id: Option, - /// Is this a public or a private view? - /// Currently only public views are supported. - /// Private views may be supported in the future. - pub is_public: bool, - /// Is this view anonymous? - /// An anonymous view does not know who called it. - /// Specifically, it is a view that has an `AnonymousViewContext` as its first argument. - /// This type does not have access to the [`Identity`] of the caller. - pub is_anonymous: bool, -} - -impl TryFrom> for StViewRow { - type Error = DatastoreError; - fn try_from(row: RowRef<'_>) -> Result { - read_via_bsatn(row) - } -} - /// A wrapper around `AlgebraicType` that acts like `AlgegbraicType::bytes()` for serialization purposes. #[derive(Debug, Clone, PartialEq, Eq)] pub struct AlgebraicTypeViaBytes(pub AlgebraicType); @@ -893,62 +709,6 @@ impl From for StColumnRow { } } -/// System Table [ST_VIEW_COLUMN_NAME] -/// -/// | view_id | col_pos | col_name | col_type | -/// |---------|---------|----------|--------------------| -/// | 1 | 0 | "x" | AlgebraicType::U32 | -#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)] -#[sats(crate = spacetimedb_lib)] -pub struct StViewColumnRow { - /// A foreign key referencing [`ST_VIEW_NAME`]. - pub view_id: ViewId, - pub col_pos: ColId, - pub col_name: Box, - pub col_type: AlgebraicTypeViaBytes, -} - -/// System Table [ST_VIEW_PARAM_NAME] -/// -/// | view_id | param_pos | param_name | param_type | -/// |---------|-----------|------------|-----------------------| -/// | 1 | 0 | "y" | AlgebraicType::U32 | -#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)] -#[sats(crate = spacetimedb_lib)] -pub struct StViewParamRow { - /// A foreign key referencing [`ST_VIEW_NAME`]. - pub view_id: ViewId, - pub param_pos: ColId, - pub param_name: Box, - pub param_type: AlgebraicTypeViaBytes, -} - -/// System table [ST_VIEW_CLIENT_NAME] -/// -/// | view_id | arg_id | identity | connection_id | -/// |---------|--------|----------|---------------| -/// | 1 | 2 | 0x... | 0x... | -#[derive(Debug, Clone, Eq, PartialEq, SpacetimeType)] -#[sats(crate = spacetimedb_lib)] -pub struct StViewClientRow { - pub view_id: ViewId, - pub arg_id: u64, - pub identity: IdentityViaU256, - pub connection_id: ConnectionIdViaU128, -} - -/// System table [ST_VIEW_ARG_NAME] -/// -/// | id | bytes | -/// |----|---------| -/// | 1 | | -#[derive(Debug, Clone, Eq, PartialEq, SpacetimeType)] -#[sats(crate = spacetimedb_lib)] -pub struct StViewArgRow { - pub id: u64, - pub bytes: Box<[u8]>, -} - /// System Table [ST_INDEX_NAME] /// /// | index_id | table_id | index_name | index_algorithm | @@ -1472,11 +1232,6 @@ impl TryFrom> for StVarRow { pub struct StScheduledRow { pub(crate) schedule_id: ScheduleId, pub(crate) table_id: TableId, - /// The name of the reducer or procedure which will run when this table's rows reach their execution time. - /// - /// Note that, despite the column name, this may refer to either a reducer or a procedure. - /// We cannot change the schema of existing system tables, - /// so we are unable to rename this column. pub(crate) reducer_name: Box, pub(crate) schedule_name: Box, pub(crate) at_column: ColId, @@ -1499,7 +1254,7 @@ impl From for ScheduleSchema { fn from(row: StScheduledRow) -> Self { Self { table_id: row.table_id, - function_name: row.reducer_name, + reducer_name: row.reducer_name, schedule_id: row.schedule_id, schedule_name: row.schedule_name, at_column: row.at_column, diff --git a/crates/datastore/src/traits.rs b/crates/datastore/src/traits.rs index b04b2444863..d6fc69d57fb 100644 --- a/crates/datastore/src/traits.rs +++ b/crates/datastore/src/traits.rs @@ -509,7 +509,6 @@ pub trait MutTxDatastore: TxDatastore + MutTx { fn schema_for_table_mut_tx(&self, tx: &Self::MutTx, table_id: TableId) -> Result>; fn drop_table_mut_tx(&self, tx: &mut Self::MutTx, table_id: TableId) -> Result<()>; fn rename_table_mut_tx(&self, tx: &mut Self::MutTx, table_id: TableId, new_name: &str) -> Result<()>; - fn view_id_from_name_mut_tx(&self, tx: &Self::MutTx, view_name: &str) -> Result>; fn table_id_from_name_mut_tx(&self, tx: &Self::MutTx, table_name: &str) -> Result>; fn table_id_exists_mut_tx(&self, tx: &Self::MutTx, table_id: &TableId) -> bool; fn table_name_from_id_mut_tx<'a>(&'a self, tx: &'a Self::MutTx, table_id: TableId) -> Result>>; diff --git a/crates/lib/src/db/raw_def/v9.rs b/crates/lib/src/db/raw_def/v9.rs index d094c1a9a84..948597c9680 100644 --- a/crates/lib/src/db/raw_def/v9.rs +++ b/crates/lib/src/db/raw_def/v9.rs @@ -82,16 +82,9 @@ pub struct RawModuleDefV9 { pub types: Vec, /// Miscellaneous additional module exports. - /// - /// The enum [`RawMiscModuleExportV9`] can have new variants added - /// without breaking existing compiled modules. - /// As such, this acts as a sort of dumping ground for any exports added after we defined `RawModuleDefV9`. - /// - /// If/when we define `RawModuleDefV10`, these should be moved out of `misc_exports` and into their own fields, - /// and the new `misc_exports` should once again be initially empty. pub misc_exports: Vec, - /// Row level security definitions. + /// Low level security definitions. /// /// Each definition must have a unique name. pub row_level_security: Vec, @@ -301,7 +294,7 @@ pub fn direct(col: impl Into) -> RawIndexAlgorithm { RawIndexAlgorithm::Direct { column: col.into() } } -/// Marks a table as a timer table for a scheduled reducer or procedure. +/// Marks a table as a timer table for a scheduled reducer. /// /// The table must have columns: /// - `scheduled_id` of type `u64`. @@ -314,9 +307,7 @@ pub struct RawScheduleDefV9 { /// Even though there is ABSOLUTELY NO REASON TO. pub name: Option>, - /// The name of the reducer or procedure to call. - /// - /// Despite the field name here, this may be either a reducer or a procedure. + /// The name of the reducer to call. pub reducer_name: RawIdentifier, /// The column of the `scheduled_at` field of this scheduled table. @@ -367,20 +358,12 @@ pub struct RawRowLevelSecurityDefV9 { } /// A miscellaneous module export. -/// -/// All of the variants here were added after the format of [`RawModuleDefV9`] was already stabilized. -/// If/when we define `RawModuleDefV10`, these should allbe moved out of `misc_exports` and into their own fields. #[derive(Debug, Clone, SpacetimeType)] #[sats(crate = crate)] -#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord, derive_more::From))] +#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))] #[non_exhaustive] pub enum RawMiscModuleExportV9 { - /// A default value for a column added during a supervised automigration. ColumnDefaultValue(RawColumnDefaultValueV9), - /// A procedure definition. - Procedure(RawProcedureDefV9), - /// A view definition. - View(RawViewDefV9), } /// Marks a particular table's column as having a particular default. @@ -446,41 +429,6 @@ impl fmt::Debug for RawScopedTypeNameV9 { } } -/// A view definition. -#[derive(Debug, Clone, SpacetimeType)] -#[sats(crate = crate)] -#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))] -pub struct RawViewDefV9 { - /// The name of the view function as defined in the module - pub name: RawIdentifier, - - /// Is this a public or a private view? - /// Currently only public views are supported. - /// Private views may be supported in the future. - pub is_public: bool, - - /// Is this view anonymous? - /// An anonymous view does not know who called it. - /// Specifically, it is a view that has an `AnonymousViewContext` as its first argument. - /// This type does not have access to the `Identity` of the caller. - pub is_anonymous: bool, - - /// The types and optional names of the parameters, in order. - /// This `ProductType` need not be registered in the typespace. - pub params: ProductType, - - /// The return type of the view. - /// Either `T`, `Option`, or `Vec` where `T` is a `SpacetimeType`. - /// - /// More strictly `T` must be a SATS `ProductType`, - /// however this will be validated by the server on publish. - /// - /// This is the single source of truth for the views's columns. - /// All elements of the inner `ProductType` must have names. - /// This again will be validated by the server on publish. - pub return_type: AlgebraicType, -} - /// A reducer definition. #[derive(Debug, Clone, SpacetimeType)] #[sats(crate = crate)] @@ -511,27 +459,6 @@ pub enum Lifecycle { OnDisconnect, } -/// A procedure definition. -/// -/// Will be wrapped in [`RawMiscModuleExportV9`] and included in the [`RawModuleDefV9`]'s `misc_exports` vec. -#[derive(Debug, Clone, SpacetimeType)] -#[sats(crate = crate)] -#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))] -pub struct RawProcedureDefV9 { - /// The name of the procedure. - pub name: RawIdentifier, - - /// The types and optional names of the parameters, in order. - /// This `ProductType` need not be registered in the typespace. - pub params: ProductType, - - /// The type of the return value. - /// - /// If this is a user-defined product or sum type, - /// it should be registered in the typespace and indirected through an [`AlgebraicType::Ref`]. - pub return_type: AlgebraicType, -} - /// A builder for a [`RawModuleDefV9`]. #[derive(Default)] pub struct RawModuleDefV9Builder { @@ -704,48 +631,6 @@ impl RawModuleDefV9Builder { }); } - /// Add a procedure to the in-progress module. - /// - /// Accepts a `ProductType` of arguments. - /// The arguments `ProductType` need not be registered in the typespace. - /// - /// Also accepts an `AlgebraicType` return type. - /// If this is a user-defined product or sum type, - /// it should be registered in the typespace and indirected through an `AlgebraicType::Ref`. - /// - /// The `&mut ProcedureContext` first argument to the procedure should not be included in the `params`. - pub fn add_procedure( - &mut self, - name: impl Into, - params: spacetimedb_sats::ProductType, - return_type: spacetimedb_sats::AlgebraicType, - ) { - self.module - .misc_exports - .push(RawMiscModuleExportV9::Procedure(RawProcedureDefV9 { - name: name.into(), - params, - return_type, - })) - } - - pub fn add_view( - &mut self, - name: impl Into, - is_public: bool, - is_anonymous: bool, - params: ProductType, - return_type: AlgebraicType, - ) { - self.module.misc_exports.push(RawMiscModuleExportV9::View(RawViewDefV9 { - name: name.into(), - is_public, - is_anonymous, - params, - return_type, - })); - } - /// Add a row-level security policy to the module. /// /// The `sql` expression should be a valid SQL expression that will be used to filter rows. @@ -911,16 +796,13 @@ impl RawTableDefBuilder<'_> { /// Adds a schedule definition to the table. /// - /// The `function_name` should name a reducer or procedure - /// which accepts one argument, a row of this table. - /// /// The table must have the appropriate columns for a scheduled table. pub fn with_schedule( mut self, - function_name: impl Into, + reducer_name: impl Into, scheduled_at_column: impl Into, ) -> Self { - let reducer_name = function_name.into(); + let reducer_name = reducer_name.into(); let scheduled_at_column = scheduled_at_column.into(); self.table.schedule = Some(RawScheduleDefV9 { name: None, diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 2715ccb9b6b..8da5f76e113 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -12,7 +12,6 @@ memory-usage = ["dep:spacetimedb-memory-usage"] [dependencies] bitflags.workspace = true either.workspace = true -enum-as-inner.workspace = true nohash-hasher.workspace = true itertools.workspace = true spacetimedb-memory-usage = { workspace = true, optional = true, default-features = false } diff --git a/crates/primitives/src/ids.rs b/crates/primitives/src/ids.rs index 5aadfb8455b..fc9a9b69e61 100644 --- a/crates/primitives/src/ids.rs +++ b/crates/primitives/src/ids.rs @@ -2,8 +2,6 @@ use core::fmt; -use enum_as_inner::EnumAsInner; - macro_rules! system_id { ($(#[$($doc_comment:tt)*])* pub struct $name:ident(pub $backing_ty:ty);) => { @@ -78,12 +76,6 @@ system_id! { } auto_inc_system_id!(TableId); -system_id! { - /// An identifier for a view, unique within a database. - pub struct ViewId(pub u32); -} -auto_inc_system_id!(ViewId); - system_id! { /// An identifier for a sequence, unique within a database. pub struct SequenceId(pub u32); @@ -124,18 +116,3 @@ system_id! { // This is never stored in a system table, but is useful to have defined here. pub struct ReducerId(pub u32); } - -system_id! { - /// The index of a procedure as defined in a module's procedure list. - // This is never stored in a system table, but is useful to have defined here. - pub struct ProcedureId(pub u32); -} - -/// An id for a function exported from a module, which may be a reducer or a procedure. -// This is never stored in a system table, -// but is useful to have defined here to provide a shared language for downstream crates. -#[derive(Clone, Copy, Debug, EnumAsInner)] -pub enum FunctionId { - Reducer(ReducerId), - Procedure(ProcedureId), -} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 715fc77cec0..7ae37765514 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -7,9 +7,7 @@ mod ids; pub use attr::{AttributeKind, ColumnAttribute, ConstraintKind, Constraints}; pub use col_list::{ColList, ColOrCols, ColSet}; -pub use ids::{ - ColId, ConstraintId, FunctionId, IndexId, ProcedureId, ReducerId, ScheduleId, SequenceId, TableId, ViewId, -}; +pub use ids::{ColId, ConstraintId, IndexId, ReducerId, ScheduleId, SequenceId, TableId}; /// The minimum size of a chunk yielded by a wasm abi RowIter. pub const ROW_ITER_CHUNK_SIZE: usize = 32 * 1024; diff --git a/crates/sats/src/algebraic_type.rs b/crates/sats/src/algebraic_type.rs index e42ba015639..c4a45fe1d3b 100644 --- a/crates/sats/src/algebraic_type.rs +++ b/crates/sats/src/algebraic_type.rs @@ -202,11 +202,6 @@ impl AlgebraicType { matches!(self, Self::Sum(p) if p.is_empty()) } - /// Returns whether this type is an option type. - pub fn is_option(&self) -> bool { - matches!(self, Self::Sum(p) if p.is_option()) - } - /// If this type is the standard option type, returns the type of the `some` variant. /// Otherwise, returns `None`. pub fn as_option(&self) -> Option<&AlgebraicType> { diff --git a/crates/sats/src/convert.rs b/crates/sats/src/convert.rs index 1729402cdc3..dfd4a8dbe7e 100644 --- a/crates/sats/src/convert.rs +++ b/crates/sats/src/convert.rs @@ -1,7 +1,7 @@ use crate::sum_value::SumTag; use crate::{i256, u256}; use crate::{AlgebraicType, AlgebraicValue, ProductType, ProductValue}; -use spacetimedb_primitives::{ColId, ConstraintId, IndexId, ScheduleId, SequenceId, TableId, ViewId}; +use spacetimedb_primitives::{ColId, ConstraintId, IndexId, ScheduleId, SequenceId, TableId}; impl crate::Value for AlgebraicValue { type Type = AlgebraicType; @@ -64,7 +64,6 @@ macro_rules! system_id { }; } system_id!(TableId); -system_id!(ViewId); system_id!(ColId); system_id!(SequenceId); system_id!(IndexId); diff --git a/crates/sats/src/de/impls.rs b/crates/sats/src/de/impls.rs index cf1090253af..0e50206703f 100644 --- a/crates/sats/src/de/impls.rs +++ b/crates/sats/src/de/impls.rs @@ -742,7 +742,6 @@ impl FieldNameVisitor<'_> for TupleNameVisitor<'_> { } impl_deserialize!([] spacetimedb_primitives::TableId, de => u32::deserialize(de).map(Self)); -impl_deserialize!([] spacetimedb_primitives::ViewId, de => u32::deserialize(de).map(Self)); impl_deserialize!([] spacetimedb_primitives::SequenceId, de => u32::deserialize(de).map(Self)); impl_deserialize!([] spacetimedb_primitives::IndexId, de => u32::deserialize(de).map(Self)); impl_deserialize!([] spacetimedb_primitives::ConstraintId, de => u32::deserialize(de).map(Self)); diff --git a/crates/sats/src/ser/impls.rs b/crates/sats/src/ser/impls.rs index dc737c5b50c..15b2bc67638 100644 --- a/crates/sats/src/ser/impls.rs +++ b/crates/sats/src/ser/impls.rs @@ -258,7 +258,6 @@ impl_serialize!([] ValueWithType<'_, ArrayValue>, (self, ser) => { }); impl_serialize!([] spacetimedb_primitives::TableId, (self, ser) => ser.serialize_u32(self.0)); -impl_serialize!([] spacetimedb_primitives::ViewId, (self, ser) => ser.serialize_u32(self.0)); impl_serialize!([] spacetimedb_primitives::SequenceId, (self, ser) => ser.serialize_u32(self.0)); impl_serialize!([] spacetimedb_primitives::IndexId, (self, ser) => ser.serialize_u32(self.0)); impl_serialize!([] spacetimedb_primitives::ConstraintId, (self, ser) => ser.serialize_u32(self.0)); diff --git a/crates/sats/src/typespace.rs b/crates/sats/src/typespace.rs index 36058c79195..46aeb3ddaa5 100644 --- a/crates/sats/src/typespace.rs +++ b/crates/sats/src/typespace.rs @@ -412,7 +412,6 @@ impl_st!([T] Option, ts => AlgebraicType::option(T::make_type(ts))); impl_st!([] spacetimedb_primitives::ColId, AlgebraicType::U16); impl_st!([] spacetimedb_primitives::TableId, AlgebraicType::U32); -impl_st!([] spacetimedb_primitives::ViewId, AlgebraicType::U32); impl_st!([] spacetimedb_primitives::IndexId, AlgebraicType::U32); impl_st!([] spacetimedb_primitives::SequenceId, AlgebraicType::U32); impl_st!([] spacetimedb_primitives::ConstraintId, AlgebraicType::U32); diff --git a/crates/schema/src/auto_migrate.rs b/crates/schema/src/auto_migrate.rs index b6e179ef205..7a0f665fe7b 100644 --- a/crates/schema/src/auto_migrate.rs +++ b/crates/schema/src/auto_migrate.rs @@ -202,16 +202,6 @@ pub struct AutoMigratePlan<'def> { pub steps: Vec>, } -impl AutoMigratePlan<'_> { - fn any_step(&self, f: impl Fn(&AutoMigrateStep) -> bool) -> bool { - self.steps.iter().any(f) - } - - fn disconnects_all_users(&self) -> bool { - self.any_step(|step| matches!(step, AutoMigrateStep::DisconnectAllUsers)) - } -} - /// Checks that must be performed before performing an automatic migration. /// These checks can access table contents and other database state. #[derive(PartialEq, Eq, Debug, PartialOrd, Ord)] @@ -250,8 +240,6 @@ pub enum AutoMigrateStep<'def> { RemoveSequence(::Key<'def>), /// Remove a schedule annotation from a table. RemoveSchedule(::Key<'def>), - /// Remove a view and corresponding view table - RemoveView(::Key<'def>), /// Remove a row-level security query. RemoveRowLevelSecurity(::Key<'def>), @@ -280,17 +268,12 @@ pub enum AutoMigrateStep<'def> { AddSequence(::Key<'def>), /// Add a schedule annotation to a table. AddSchedule(::Key<'def>), - /// Add a view and corresponding view table - AddView(::Key<'def>), /// Add a row-level security query. AddRowLevelSecurity(::Key<'def>), /// Change the access of a table. ChangeAccess(::Key<'def>), - /// Recompute a view, update its backing table, and push updates to clients - UpdateView(::Key<'def>), - /// Disconnect all users connected to the module. DisconnectAllUsers, } @@ -445,7 +428,6 @@ pub fn ponder_auto_migrate<'def>(old: &'def ModuleDef, new: &'def ModuleDef) -> prechecks: Vec::new(), }; - let views_ok = auto_migrate_views(&mut plan); let tables_ok = auto_migrate_tables(&mut plan); // Our diffing algorithm will detect added constraints / indexes / sequences in new tables, we use this to filter those out. @@ -464,8 +446,7 @@ pub fn ponder_auto_migrate<'def>(old: &'def ModuleDef, new: &'def ModuleDef) -> // have already been reflected in the database state. let rls_ok = auto_migrate_row_level_security(&mut plan); - let ((), (), (), (), (), ()) = - (views_ok, tables_ok, indexes_ok, sequences_ok, constraints_ok, rls_ok).combine_errors()?; + let ((), (), (), (), ()) = (tables_ok, indexes_ok, sequences_ok, constraints_ok, rls_ok).combine_errors()?; plan.steps.sort(); plan.prechecks.sort(); @@ -508,116 +489,6 @@ fn diff<'def, T: ModuleDefLookup, I: Iterator>( })) } -fn auto_migrate_views(plan: &mut AutoMigratePlan<'_>) -> Result<()> { - diff(plan.old, plan.new, ModuleDef::views) - .map(|table_diff| -> Result<()> { - match table_diff { - Diff::Add { new } => { - plan.steps.push(AutoMigrateStep::AddView(new.key())); - Ok(()) - } - // From the user's perspective, views do not have persistent state. - // Hence removal does not require a manual migration - just disconnecting clients. - Diff::Remove { old } if plan.disconnects_all_users() => { - plan.steps.push(AutoMigrateStep::RemoveView(old.key())); - Ok(()) - } - Diff::Remove { old } => { - plan.steps.push(AutoMigrateStep::RemoveView(old.key())); - plan.steps.push(AutoMigrateStep::DisconnectAllUsers); - Ok(()) - } - Diff::MaybeChange { old, new } => auto_migrate_view(plan, old, new), - } - }) - .collect_all_errors() -} - -fn auto_migrate_view<'def>(plan: &mut AutoMigratePlan<'def>, old: &'def ViewDef, new: &'def ViewDef) -> Result<()> { - let key = old.key(); - - if old.is_public != new.is_public { - plan.steps.push(AutoMigrateStep::ChangeAccess(key)); - } - - // We can always auto-migrate a view because we can always re-compute it. - // However certain things require us to disconnect clients: - // 1. If we add or remove a column or parameter - // 2. If we change the order of the columns or parameters - // 3. If we change the types of the columns or parameters - // 4. If we change the context parameter - let Any(incompatible_return_type) = diff(plan.old, plan.new, |def| { - def.lookup_expect::(key).return_columns.iter() - }) - .map(|col_diff| { - match col_diff { - // We must disconnect clients if we add or remove a parameter or column - Diff::Add { .. } | Diff::Remove { .. } => Any(true), - Diff::MaybeChange { old, new } => { - if old.col_id != new.col_id { - return Any(true); - }; - - ensure_old_ty_upgradable_to_new( - false, - &|| old.view_name.clone(), - &|| old.name.clone(), - &WithTypespace::new(plan.old.typespace(), &old.ty) - .resolve_refs() - .expect("valid ViewDefs must have valid type refs"), - &WithTypespace::new(plan.new.typespace(), &new.ty) - .resolve_refs() - .expect("valid ViewDefs must have valid type refs"), - ) - .unwrap_or(Any(true)) - } - } - }) - .collect(); - - let Any(incompatible_param_types) = diff(plan.old, plan.new, |def| { - def.lookup_expect::(key).param_columns.iter() - }) - .map(|col_diff| { - match col_diff { - // We must disconnect clients if we add or remove a parameter or column - Diff::Add { .. } | Diff::Remove { .. } => Any(true), - Diff::MaybeChange { old, new } => { - if old.col_id != new.col_id { - return Any(true); - }; - - ensure_old_ty_upgradable_to_new( - false, - &|| old.view_name.clone(), - &|| old.name.clone(), - &WithTypespace::new(plan.old.typespace(), &old.ty) - .resolve_refs() - .expect("valid ViewDefs must have valid type refs"), - &WithTypespace::new(plan.new.typespace(), &new.ty) - .resolve_refs() - .expect("valid ViewDefs must have valid type refs"), - ) - .unwrap_or(Any(true)) - } - } - }) - .collect(); - - if old.is_anonymous != new.is_anonymous || incompatible_return_type || incompatible_param_types { - plan.steps.push(AutoMigrateStep::AddView(new.key())); - plan.steps.push(AutoMigrateStep::RemoveView(old.key())); - - if !plan.disconnects_all_users() { - plan.steps.push(AutoMigrateStep::DisconnectAllUsers); - } - } else { - plan.steps.push(AutoMigrateStep::UpdateView(old.key())); - } - - Ok(()) -} - fn auto_migrate_tables(plan: &mut AutoMigratePlan<'_>) -> Result<()> { diff(plan.old, plan.new, ModuleDef::tables) .map(|table_diff| -> Result<()> { @@ -692,13 +563,7 @@ fn auto_migrate_table<'def>(plan: &mut AutoMigratePlan<'def>, old: &'def TableDe let new_ty = WithTypespace::new(plan.new.typespace(), &new.ty) .resolve_refs() .expect("valid TableDef must have valid type refs"); - let types_ok = ensure_old_ty_upgradable_to_new( - false, - &|| old.table_name.clone(), - &|| old.name.clone(), - &old_ty, - &new_ty, - ); + let types_ok = ensure_old_ty_upgradable_to_new(false, old, &old_ty, &new_ty); // Note that the diff algorithm relies on `ModuleDefLookup` for `ColumnDef`, // which looks up columns by NAME, NOT position: precisely to allow this step to work! @@ -729,7 +594,11 @@ fn auto_migrate_table<'def>(plan: &mut AutoMigratePlan<'def>, old: &'def TableDe // If we're adding a column, we'll rewrite the whole table. // That makes any `ChangeColumns` moot, so we can skip it. if columns_added { - if !plan.disconnects_all_users() { + if !plan + .steps + .iter() + .any(|step| matches!(step, AutoMigrateStep::DisconnectAllUsers)) + { plan.steps.push(AutoMigrateStep::DisconnectAllUsers); } plan.steps.push(AutoMigrateStep::AddColumns(key)); @@ -779,21 +648,19 @@ impl + Default, M2: BitOr + Default> FromIte fn ensure_old_ty_upgradable_to_new( within: bool, - old_container_name: &impl Fn() -> Identifier, - old_column_name: &impl Fn() -> Identifier, + old: &ColumnDef, old_ty: &AlgebraicType, new_ty: &AlgebraicType, ) -> Result { use AutoMigrateError::*; // Ensures an `old_ty` within `old` is upgradable to `new_ty`. - let ensure = - |(old_ty, new_ty)| ensure_old_ty_upgradable_to_new(true, old_container_name, old_column_name, old_ty, new_ty); + let ensure = |(old_ty, new_ty)| ensure_old_ty_upgradable_to_new(true, old, old_ty, new_ty); // Returns a `ChangeColumnTypeParts` error using the current `old_ty` and `new_ty`. let parts_for_error = || ChangeColumnTypeParts { - table: old_container_name(), - column: old_column_name(), + table: old.table_name.clone(), + column: old.name.clone(), type1: old_ty.clone().into(), type2: new_ty.clone().into(), }; @@ -896,13 +763,9 @@ fn ensure_old_ty_upgradable_to_new( } // For arrays, we need to check each field's upgradability due to sums. - (AlgebraicType::Array(old_ty), AlgebraicType::Array(new_ty)) => ensure_old_ty_upgradable_to_new( - true, - old_container_name, - old_column_name, - &old_ty.elem_ty, - &new_ty.elem_ty, - ), + (AlgebraicType::Array(old_ty), AlgebraicType::Array(new_ty)) => { + ensure_old_ty_upgradable_to_new(true, old, &old_ty.elem_ty, &new_ty.elem_ty) + } // We only have the simple cases left, and there, no change is good change. (old_ty, new_ty) if old_ty == new_ty => Ok(Any(false)), @@ -1041,15 +904,6 @@ mod tests { use v9::{RawModuleDefV9Builder, TableAccess}; use validate::tests::expect_identifier; - fn create_module_def(build_module: impl Fn(&mut RawModuleDefV9Builder)) -> ModuleDef { - let mut builder = RawModuleDefV9Builder::new(); - build_module(&mut builder); - builder - .finish() - .try_into() - .expect("new_def should be a valid database definition") - } - fn initial_module_def() -> ModuleDef { let mut builder = RawModuleDefV9Builder::new(); let schedule_at = builder.add_type::(); @@ -1105,17 +959,6 @@ mod tests { None, ); - // Add a view and add its return type to the typespace - let view_return_ty = AlgebraicType::product([("a", AlgebraicType::U64), ("b", AlgebraicType::U64)]); - let view_return_ty_ref = builder.add_algebraic_type([], "my_view_return", view_return_ty, true); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32), ("y", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(view_return_ty_ref)), - ); - builder .build_table_with_new_type( "Inspections", @@ -1201,17 +1044,6 @@ mod tests { None, ); - // Add a view and add its return type to the typespace - let view_return_ty = AlgebraicType::product([("a", AlgebraicType::U64)]); - let view_return_ty_ref = builder.add_algebraic_type([], "my_view_return", view_return_ty, true); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(view_return_ty_ref)), - ); - let new_inspections_type = builder .build_table_with_new_type( "Inspections", @@ -1261,7 +1093,6 @@ mod tests { let bananas = expect_identifier("Bananas"); let deliveries = expect_identifier("Deliveries"); let oranges = expect_identifier("Oranges"); - let my_view = expect_identifier("my_view"); let bananas_sequence = "Bananas_id_seq"; let apples_unique_constraint = "Apples_id_key"; @@ -1343,9 +1174,6 @@ mod tests { assert!(steps.contains(&AutoMigrateStep::AddColumns(&bananas)), "{steps:?}"); // Column is changed but it will not reflect in steps due to `AutoMigrateStep::AddColumns` assert!(!steps.contains(&AutoMigrateStep::ChangeColumns(&bananas)), "{steps:?}"); - - assert!(steps.contains(&AutoMigrateStep::RemoveView(&my_view)), "{steps:?}"); - assert!(steps.contains(&AutoMigrateStep::AddView(&my_view)), "{steps:?}"); } #[test] @@ -1752,497 +1580,4 @@ mod tests { .expect("should pretty print") ); } - - #[test] - fn add_view() { - let old_def = create_module_def(|_| {}); - let new_def = create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::array(AlgebraicType::Ref(return_type_ref)), - ); - }); - - let my_view = expect_identifier("my_view"); - - let plan = ponder_auto_migrate(&old_def, &new_def).expect("auto migration should succeed"); - let steps = &plan.steps[..]; - - assert!(!plan.disconnects_all_users(), "{plan:#?}"); - assert!(steps.contains(&AutoMigrateStep::AddView(&my_view)), "{steps:?}"); - assert!(!steps.contains(&AutoMigrateStep::RemoveView(&my_view)), "{steps:?}"); - } - - #[test] - fn remove_view() { - let old_def = create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::array(AlgebraicType::Ref(return_type_ref)), - ); - }); - let new_def = create_module_def(|_| {}); - - let my_view = expect_identifier("my_view"); - - let plan = ponder_auto_migrate(&old_def, &new_def).expect("auto migration should succeed"); - let steps = &plan.steps[..]; - - assert!(plan.disconnects_all_users(), "{plan:#?}"); - assert!(steps.contains(&AutoMigrateStep::RemoveView(&my_view)), "{steps:?}"); - assert!(!steps.contains(&AutoMigrateStep::AddView(&my_view)), "{steps:?}"); - } - - #[test] - fn migrate_view_recompute() { - struct TestCase { - desc: &'static str, - old_def: ModuleDef, - new_def: ModuleDef, - } - - for TestCase { - desc: name, - old_def, - new_def, - } in [ - TestCase { - desc: "Return `Vec` instead of `Option`", - old_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - new_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::array(AlgebraicType::Ref(return_type_ref)), - ); - }), - }, - TestCase { - desc: "No change; recompute view", - old_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - new_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - }, - ] { - let my_view = expect_identifier("my_view"); - - let plan = ponder_auto_migrate(&old_def, &new_def).expect("auto migration should succeed"); - let steps = &plan.steps[..]; - - assert!(!plan.disconnects_all_users(), "{name}, plan: {plan:#?}"); - - assert!( - steps.contains(&AutoMigrateStep::UpdateView(&my_view)), - "{name}, steps: {steps:?}" - ); - assert!( - !steps.contains(&AutoMigrateStep::AddView(&my_view)), - "{name}, steps: {steps:?}" - ); - assert!( - !steps.contains(&AutoMigrateStep::RemoveView(&my_view)), - "{name}, steps: {steps:?}" - ); - } - } - - #[test] - fn migrate_view_disconnect_clients() { - struct TestCase { - desc: &'static str, - old_def: ModuleDef, - new_def: ModuleDef, - } - - for TestCase { - desc: name, - old_def, - new_def, - } in [ - TestCase { - desc: "Change context parameter", - old_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - new_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - false, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - }, - TestCase { - desc: "Add parameter", - old_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - new_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32), ("y", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - }, - TestCase { - desc: "Remove parameter", - old_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32), ("y", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - new_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - }, - TestCase { - desc: "Reorder parameters", - old_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32), ("y", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - new_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("y", AlgebraicType::U32), ("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - }, - TestCase { - desc: "Change parameter type", - old_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - new_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::String)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - }, - TestCase { - desc: "Add column", - old_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - new_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64), ("b", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - }, - TestCase { - desc: "Remove column", - old_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64), ("b", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - new_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - }, - TestCase { - desc: "Reorder columns", - old_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64), ("b", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - new_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("b", AlgebraicType::U64), ("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - }, - TestCase { - desc: "Change column type", - old_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::U64)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - new_def: create_module_def(|builder| { - let return_type_ref = builder.add_algebraic_type( - [], - "my_view_return_type", - AlgebraicType::product([("a", AlgebraicType::String)]), - true, - ); - builder.add_view( - "my_view", - true, - true, - ProductType::from([("x", AlgebraicType::U32)]), - AlgebraicType::option(AlgebraicType::Ref(return_type_ref)), - ); - }), - }, - ] { - let my_view = expect_identifier("my_view"); - - let plan = ponder_auto_migrate(&old_def, &new_def).expect("auto migration should succeed"); - let steps = &plan.steps[..]; - - assert!(plan.disconnects_all_users(), "{name}, plan: {plan:?}"); - - assert!( - steps.contains(&AutoMigrateStep::AddView(&my_view)), - "{name}, steps: {steps:?}" - ); - assert!( - steps.contains(&AutoMigrateStep::RemoveView(&my_view)), - "{name}, steps: {steps:?}" - ); - assert!( - !steps.contains(&AutoMigrateStep::UpdateView(&my_view)), - "{name}, steps: {steps:?}" - ); - } - } } diff --git a/crates/schema/src/auto_migrate/formatter.rs b/crates/schema/src/auto_migrate/formatter.rs index 06c82920c00..54b4ed87ce3 100644 --- a/crates/schema/src/auto_migrate/formatter.rs +++ b/crates/schema/src/auto_migrate/formatter.rs @@ -5,7 +5,7 @@ use std::io; use super::{AutoMigratePlan, IndexAlgorithm, ModuleDefLookup, TableDef}; use crate::{ auto_migrate::AutoMigrateStep, - def::{ConstraintData, FunctionKind, ModuleDef, ScheduleDef, ViewDef}, + def::{ConstraintData, ModuleDef, ScheduleDef}, identifier::Identifier, }; use itertools::Itertools; @@ -32,18 +32,6 @@ fn format_step( plan: &super::AutoMigratePlan, ) -> Result<(), FormattingErrors> { match step { - AutoMigrateStep::AddView(view) => { - let view_info = extract_view_info(*view, plan.new)?; - f.format_view(&view_info, Action::Created) - } - AutoMigrateStep::RemoveView(view) => { - let view_info = extract_view_info(*view, plan.old)?; - f.format_view(&view_info, Action::Removed) - } - // This means the body of the view may have been updated. - // So we must recompute it and send any updates to clients. - // No need to include this step in the formatted plan. - AutoMigrateStep::UpdateView(_) => Ok(()), AutoMigrateStep::AddTable(t) => { let table_info = extract_table_info(*t, plan)?; f.format_add_table(&table_info) @@ -110,8 +98,6 @@ fn format_step( pub enum FormattingErrors { #[error("Table not found: {table}")] TableNotFound { table: Box }, - #[error("View not found: {view}")] - ViewNotFound { view: Box }, #[error("Index not found")] IndexNotFound, #[error("Constraint not found")] @@ -142,7 +128,6 @@ pub enum Action { pub trait MigrationFormatter { fn format_header(&mut self) -> io::Result<()>; fn format_add_table(&mut self, table_info: &TableInfo) -> io::Result<()>; - fn format_view(&mut self, view_info: &ViewInfo, action: Action) -> io::Result<()>; fn format_index(&mut self, index_info: &IndexInfo, action: Action) -> io::Result<()>; fn format_constraint(&mut self, constraint_info: &ConstraintInfo, action: Action) -> io::Result<()>; fn format_sequence(&mut self, sequence_info: &SequenceInfo, action: Action) -> io::Result<()>; @@ -166,26 +151,6 @@ pub struct TableInfo { pub schedule: Option, } -#[derive(Debug, Clone, PartialEq)] -pub struct ViewInfo { - pub name: String, - pub params: Vec, - pub columns: Vec, - pub is_anonymous: bool, -} - -#[derive(Debug, Clone, PartialEq)] -pub struct ViewColumnInfo { - pub name: Identifier, - pub type_name: AlgebraicType, -} - -#[derive(Debug, Clone, PartialEq)] -pub struct ViewParamInfo { - pub name: Identifier, - pub type_name: AlgebraicType, -} - #[derive(Debug, Clone, PartialEq)] pub struct ColumnInfo { pub name: Identifier, @@ -223,8 +188,7 @@ pub struct AccessChangeInfo { #[derive(Debug, Clone, PartialEq)] pub struct ScheduleInfo { pub table_name: String, - pub function_name: Identifier, - pub function_kind: FunctionKind, + pub reducer_name: Identifier, } #[derive(Debug, Clone, PartialEq)] @@ -350,8 +314,7 @@ fn extract_table_info( let schedule = table_def.schedule.as_ref().map(|schedule| ScheduleInfo { table_name: table_def.name.to_string().clone(), - function_name: schedule.function_name.clone(), - function_kind: schedule.function_kind, + reducer_name: schedule.reducer_name.clone(), }); Ok(TableInfo { @@ -366,53 +329,6 @@ fn extract_table_info( }) } -fn extract_view_info( - view: ::Key<'_>, - module_def: &ModuleDef, -) -> Result { - let view_def = module_def.view(view).ok_or_else(|| FormattingErrors::ViewNotFound { - view: view.to_string().into(), - })?; - - let name = view_def.name.to_string(); - let is_anonymous = view_def.is_anonymous; - - let params = view_def - .param_columns - .iter() - .map(|column| { - let type_name = WithTypespace::new(module_def.typespace(), &column.ty) - .resolve_refs() - .map_err(|_| FormattingErrors::TypeResolution)?; - Ok(ViewParamInfo { - name: column.name.clone(), - type_name, - }) - }) - .collect::, FormattingErrors>>()?; - - let columns = view_def - .return_columns - .iter() - .map(|column| { - let type_name = WithTypespace::new(module_def.typespace(), &column.ty) - .resolve_refs() - .map_err(|_| FormattingErrors::TypeResolution)?; - Ok(ViewColumnInfo { - name: column.name.clone(), - type_name, - }) - }) - .collect::, FormattingErrors>>()?; - - Ok(ViewInfo { - name, - params, - columns, - is_anonymous, - }) -} - fn extract_index_info( index: ::Key<'_>, module_def: &ModuleDef, @@ -522,8 +438,7 @@ fn extract_schedule_info( Ok(ScheduleInfo { table_name: schedule_def.name.to_string().clone(), - function_name: schedule_def.function_name.clone(), - function_kind: schedule_def.function_kind, + reducer_name: schedule_def.reducer_name.clone(), }) } diff --git a/crates/schema/src/auto_migrate/termcolor_formatter.rs b/crates/schema/src/auto_migrate/termcolor_formatter.rs index 2bd3fb2cfc1..85648e3c244 100644 --- a/crates/schema/src/auto_migrate/termcolor_formatter.rs +++ b/crates/schema/src/auto_migrate/termcolor_formatter.rs @@ -5,8 +5,6 @@ use spacetimedb_lib::{db::raw_def::v9::TableAccess, AlgebraicType}; use spacetimedb_sats::algebraic_type::fmt::fmt_algebraic_type; use termcolor::{Buffer, Color, ColorChoice, ColorSpec, WriteColor}; -use crate::auto_migrate::formatter::ViewInfo; - use super::formatter::{ AccessChangeInfo, Action, ColumnChange, ColumnChanges, ConstraintInfo, IndexInfo, MigrationFormatter, NewColumns, RlsInfo, ScheduleInfo, SequenceInfo, TableInfo, @@ -221,49 +219,7 @@ impl MigrationFormatter for TermColorFormatter { if let Some(s) = &table.schedule { self.write_colored_line("Schedule:", Some(self.colors.section_header), true)?; self.indent(); - self.write_bullet(&format!("Calls {}: {}", s.function_kind, s.function_name))?; - self.dedent(); - } - - self.dedent(); - self.write_line("") - } - - fn format_view(&mut self, view: &ViewInfo, action: Action) -> io::Result<()> { - self.write_indent()?; - self.buffer.write_all("▸ ".to_string().as_bytes())?; - self.write_action_prefix(&action)?; - self.buffer.write_all(if view.is_anonymous { - b" anonymous view: " - } else { - b" view: " - })?; - self.write_colored(&view.name, Some(self.colors.table_name), true)?; - self.buffer.write_all(b"\n")?; - - self.indent(); - - if !view.params.is_empty() { - self.write_colored_line("Parameters:", Some(self.colors.section_header), true)?; - self.indent(); - for col in &view.params { - self.write_indent()?; - self.buffer.write_all(format!("• {}: ", col.name).as_bytes())?; - self.write_type_name(&col.type_name)?; - self.buffer.write_all(b"\n")?; - } - self.dedent(); - } - - if !view.columns.is_empty() { - self.write_colored_line("Columns:", Some(self.colors.section_header), true)?; - self.indent(); - for col in &view.columns { - self.write_indent()?; - self.buffer.write_all(format!("• {}: ", col.name).as_bytes())?; - self.write_type_name(&col.type_name)?; - self.buffer.write_all(b"\n")?; - } + self.write_bullet(&format!("Calls reducer: {}", s.reducer_name))?; self.dedent(); } @@ -320,7 +276,7 @@ impl MigrationFormatter for TermColorFormatter { self.buffer.write_all(b" schedule for table ")?; self.write_colored(&s.table_name, Some(self.colors.table_name), true)?; self.buffer - .write_all(format!(" calling {} {}\n", s.function_kind, s.function_name).as_bytes()) + .write_all(format!(" calling reducer {}\n", s.reducer_name).as_bytes()) } fn format_rls(&mut self, r: &RlsInfo, action: Action) -> io::Result<()> { diff --git a/crates/schema/src/def.rs b/crates/schema/src/def.rs index 146782fb019..0e5cea3c82a 100644 --- a/crates/schema/src/def.rs +++ b/crates/schema/src/def.rs @@ -23,7 +23,7 @@ use crate::error::{IdentifierError, ValidationErrors}; use crate::identifier::Identifier; use crate::schema::{Schema, TableSchema}; use crate::type_for_generate::{AlgebraicTypeUse, ProductTypeDef, TypespaceForGenerate}; -use deserialize::ArgsSeed; +use deserialize::ReducerArgsDeserializeSeed; use enum_map::EnumMap; use hashbrown::Equivalent; use indexmap::IndexMap; @@ -33,12 +33,12 @@ use spacetimedb_data_structures::map::HashMap; use spacetimedb_lib::db::raw_def; use spacetimedb_lib::db::raw_def::v9::{ Lifecycle, RawColumnDefaultValueV9, RawConstraintDataV9, RawConstraintDefV9, RawIdentifier, RawIndexAlgorithm, - RawIndexDefV9, RawMiscModuleExportV9, RawModuleDefV9, RawProcedureDefV9, RawReducerDefV9, RawRowLevelSecurityDefV9, - RawScheduleDefV9, RawScopedTypeNameV9, RawSequenceDefV9, RawSql, RawTableDefV9, RawTypeDefV9, - RawUniqueConstraintDataV9, RawViewDefV9, TableAccess, TableType, + RawIndexDefV9, RawMiscModuleExportV9, RawModuleDefV9, RawReducerDefV9, RawRowLevelSecurityDefV9, RawScheduleDefV9, + RawScopedTypeNameV9, RawSequenceDefV9, RawSql, RawTableDefV9, RawTypeDefV9, RawUniqueConstraintDataV9, TableAccess, + TableType, }; use spacetimedb_lib::{ProductType, RawModuleDef}; -use spacetimedb_primitives::{ColId, ColList, ColOrCols, ColSet, ProcedureId, ReducerId, TableId}; +use spacetimedb_primitives::{ColId, ColList, ColOrCols, ColSet, ReducerId, TableId}; use spacetimedb_sats::{AlgebraicType, AlgebraicValue}; use spacetimedb_sats::{AlgebraicTypeRef, Typespace}; @@ -103,18 +103,6 @@ pub struct ModuleDef { /// and must be preserved for future calls to `__call_reducer__`. reducers: IndexMap, - /// The procedures of the module definition. - /// - /// Like `reducers`, this uses [`IndexMap`] to preserve order - /// so that `__call_procedure__` receives stable integer IDs. - procedures: IndexMap, - - /// The views of the module definition. - /// - /// Like `reducers`, this uses [`IndexMap`] to preserve order - /// so that `__call_view__` receives stable integer IDs. - views: IndexMap, - /// A map from lifecycle reducer kind to reducer id. lifecycle_reducers: EnumMap>, @@ -173,16 +161,6 @@ impl ModuleDef { self.reducers.values() } - /// The procedures of the module definition. - pub fn procedures(&self) -> impl Iterator { - self.procedures.values() - } - - /// The views of the module definition. - pub fn views(&self) -> impl Iterator { - self.views.values() - } - /// The type definitions of the module definition. pub fn types(&self) -> impl Iterator { self.types.values() @@ -240,12 +218,6 @@ impl ModuleDef { self.tables.get(name) } - /// Convenience method to look up a view, possibly by a string. - pub fn view>(&self, name: &K) -> Option<&ViewDef> { - // If the string IS a valid identifier, we can just look it up. - self.views.get(name) - } - /// Convenience method to look up a reducer, possibly by a string. pub fn reducer>(&self, name: &K) -> Option<&ReducerDef> { // If the string IS a valid identifier, we can just look it up. @@ -271,25 +243,6 @@ impl ModuleDef { self.reducers.get_index(id.idx()).map(|(_, def)| def) } - /// Convenience method to look up a procedure, possibly by a string, returning its id as well. - pub fn procedure_full>( - &self, - name: &K, - ) -> Option<(ProcedureId, &ProcedureDef)> { - // If the string IS a valid identifier, we can just look it up. - self.procedures.get_full(name).map(|(idx, _, def)| (idx.into(), def)) - } - - /// Look up a procuedure by its id, panicking if it doesn't exist. - pub fn procedure_by_id(&self, id: ProcedureId) -> &ProcedureDef { - &self.procedures[id.idx()] - } - - /// Look up a procuedure by its id, returning `None` if it doesn't exist. - pub fn get_procedure_by_id(&self, id: ProcedureId) -> Option<&ProcedureDef> { - self.procedures.get_index(id.idx()).map(|(_, def)| def) - } - /// Looks up a lifecycle reducer defined in the module. pub fn lifecycle_reducer(&self, lifecycle: Lifecycle) -> Option<(ReducerId, &ReducerDef)> { self.lifecycle_reducers[lifecycle].map(|i| (i, &self.reducers[i.idx()])) @@ -300,9 +253,9 @@ impl ModuleDef { pub fn reducer_arg_deserialize_seed>( &self, name: &K, - ) -> Option<(ReducerId, ArgsSeed<'_, ReducerDef>)> { + ) -> Option<(ReducerId, ReducerArgsDeserializeSeed<'_>)> { let (id, reducer) = self.reducer_full(name)?; - Some((id, ArgsSeed(self.typespace.with_type(reducer)))) + Some((id, ReducerArgsDeserializeSeed(self.typespace.with_type(reducer)))) } /// Look up the name corresponding to an `AlgebraicTypeRef`. @@ -381,7 +334,6 @@ impl From for RawModuleDefV9 { fn from(val: ModuleDef) -> Self { let ModuleDef { tables, - views, reducers, lifecycle_reducers: _, types, @@ -390,19 +342,13 @@ impl From for RawModuleDefV9 { typespace_for_generate: _, refmap: _, row_level_security_raw, - procedures, } = val; RawModuleDefV9 { tables: to_raw(tables), reducers: reducers.into_iter().map(|(_, def)| def.into()).collect(), types: to_raw(types), - // TODO: Do we need to include default values here? - misc_exports: procedures - .into_iter() - .map(|(_, def)| def.into()) - .chain(views.into_iter().map(|(_, def)| def.into())) - .collect(), + misc_exports: vec![], typespace, row_level_security: row_level_security_raw.into_iter().map(|(_, def)| def).collect(), } @@ -719,67 +665,6 @@ pub struct ColumnDef { pub default_value: Option, } -/// A struct representing a validated view column -#[derive(Debug, Clone, Eq, PartialEq)] -#[non_exhaustive] -pub struct ViewColumnDef { - /// The name of the column. - pub name: Identifier, - - /// The position of this column in the view's return type. - pub col_id: ColId, - - /// The type of this column. - pub ty: AlgebraicType, - - /// The type of the column, formatted for client code generation. - pub ty_for_generate: AlgebraicTypeUse, - - /// The view this def is stored in. - pub view_name: Identifier, -} - -impl From for ViewColumnDef { - fn from( - ColumnDef { - name, - col_id, - ty, - ty_for_generate, - table_name: view_name, - .. - }: ColumnDef, - ) -> Self { - Self { - name, - col_id, - ty, - ty_for_generate, - view_name, - } - } -} - -/// A struct representing a validated view parameter -#[derive(Debug, Clone, Eq, PartialEq)] -#[non_exhaustive] -pub struct ViewParamDef { - /// The name of the parameter. - pub name: Identifier, - - /// The position of this parameter in the view's parameter list. - pub col_id: ColId, - - /// The type of this parameter. - pub ty: AlgebraicType, - - /// The type of the parameter, formatted for client code generation. - pub ty_for_generate: AlgebraicTypeUse, - - /// The view this def is stored in. - pub view_name: Identifier, -} - /// A constraint definition attached to a table. #[derive(Debug, Clone, Eq, PartialEq)] pub struct ConstraintDef { @@ -860,30 +745,7 @@ impl From for RawRowLevelSecurityDefV9 { } } -#[derive(Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd)] -pub enum FunctionKind { - /// Functions which have not yet been determined to be reducers or procedures. - /// - /// Used as a placeholder during module validation, - /// when pre-processing [`ScheduleDef`]s prior to validating their scheduled functions. - /// Will never appear in a fully-validated [`ModuleDef`], - /// and should not be placed in errors either. - Unknown, - Reducer, - Procedure, -} - -impl fmt::Display for FunctionKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(match self { - FunctionKind::Unknown => "exported function", - FunctionKind::Reducer => "reducer", - FunctionKind::Procedure => "procedure", - }) - } -} - -/// Marks a table as a timer table for a scheduled reducer or procedure. +/// Marks a table as a timer table for a scheduled reducer. #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub struct ScheduleDef { @@ -900,18 +762,16 @@ pub struct ScheduleDef { /// Must be named `scheduled_id` and be of type `u64`. pub id_column: ColId, - /// The name of the reducer or procedure to call. - pub function_name: Identifier, - - /// Whether the `function_name` refers to a reducer or a procedure. - pub function_kind: FunctionKind, + /// The name of the reducer to call. Not yet an `Identifier` because + /// reducer names are not currently validated. + pub reducer_name: Identifier, } impl From for RawScheduleDefV9 { fn from(val: ScheduleDef) -> Self { RawScheduleDefV9 { name: Some(val.name), - reducer_name: val.function_name.into(), + reducer_name: val.reducer_name.into(), scheduled_at_column: val.at_column, } } @@ -1038,103 +898,11 @@ impl From for RawScopedTypeNameV9 { } } -/// A view exported by the module. -#[derive(Debug, Clone, Eq, PartialEq)] -#[non_exhaustive] -pub struct ViewDef { - /// The name of the view. This must be unique within the module. - pub name: Identifier, - - /// Is this a public or a private view? - /// Currently only public views are supported. - /// Private views may be supported in the future. - pub is_public: bool, - - /// Is this view anonymous? - /// An anonymous view does not know who called it. - /// Specifically, it is a view that has an `AnonymousViewContext` as its first argument. - /// This type does not have access to the `Identity` of the caller. - pub is_anonymous: bool, - - /// The parameters of the view. - /// - /// This `ProductType` need not be registered in the module's `Typespace`. - pub params: ProductType, - - /// The parameters of the view, formatted for client codegen. - /// - /// This `ProductType` need not be registered in the module's `TypespaceForGenerate`. - pub params_for_generate: ProductTypeDef, - - /// The return type of the view. - /// Either `T`, `Option`, or `Vec` where `T` is a [`ProductType`]. - /// - /// Here `Option` refers to [`AlgebraicType::option()`] and `Vec` refers to [`AlgebraicType::array()`]. - /// - /// `T` defines the columns of the view. - /// `T` will be registered in the module's `Typespace`. - pub return_type: AlgebraicType, - - /// The return type of the view, formatted for client codegen. - pub return_type_for_generate: AlgebraicTypeUse, - - /// The return columns of this view. - /// The same information is stored in `return_type`. - /// This is just a more convenient-to-access format. - pub return_columns: Vec, - - /// The columns that track the arguments of this view. - /// The same information is stored in `params`. - /// This is just a more convenient-to-access format. - pub param_columns: Vec, -} - -impl ViewDef { - /// Get a column by the column's name. - pub fn get_column_by_name(&self, name: &Identifier) -> Option<&ViewColumnDef> { - self.return_columns.iter().find(|c| &c.name == name) - } - - /// Get a parameter by the parameter's name. - pub fn get_param_by_name(&self, name: &Identifier) -> Option<&ViewParamDef> { - self.param_columns.iter().find(|c| &c.name == name) - } -} - -impl From for RawViewDefV9 { - fn from(val: ViewDef) -> Self { - let ViewDef { - name, - is_anonymous, - is_public, - params, - params_for_generate: _, - return_type, - return_type_for_generate: _, - return_columns: _, - param_columns: _, - } = val; - RawViewDefV9 { - name: name.into(), - is_anonymous, - is_public, - params, - return_type, - } - } -} - -impl From for RawMiscModuleExportV9 { - fn from(def: ViewDef) -> Self { - Self::View(def.into()) - } -} - /// A reducer exported by the module. #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub struct ReducerDef { - /// The name of the reducer. This must be unique within the module's set of reducers and procedures. + /// The name of the reducer. This must be unique within the module. pub name: Identifier, /// The parameters of the reducer. @@ -1161,53 +929,6 @@ impl From for RawReducerDefV9 { } } -#[derive(Debug, Clone, Eq, PartialEq)] -#[non_exhaustive] -pub struct ProcedureDef { - /// The name of the procedure. - /// - /// This must be unique within the module's set of reducers and procedures. - pub name: Identifier, - - /// The parameters of the procedure. - /// - /// This `ProductType` need not be registered in the module's `Typespace`. - pub params: ProductType, - - /// The parameters of the procedure, formatted for client codegen. - /// - /// This `ProductType` need not be registered in the module's `TypespaceForGenerate`. - pub params_for_generate: ProductTypeDef, - - /// The return type of the procedure. - /// - /// If this is a non-special compound type, it should be registered in the module's `Typespace` - /// and indirected through an [`AlgebraicType::Ref`]. - pub return_type: AlgebraicType, - - /// The return type of the procedure. - /// - /// If this is a non-special compound type, it should be registered in the module's `TypespaceForGenerate` - /// and indirected through an [`AlgebraicTypeUse::Ref`]. - pub return_type_for_generate: AlgebraicTypeUse, -} - -impl From for RawProcedureDefV9 { - fn from(val: ProcedureDef) -> Self { - RawProcedureDefV9 { - name: val.name.into(), - params: val.params, - return_type: val.return_type, - } - } -} - -impl From for RawMiscModuleExportV9 { - fn from(def: ProcedureDef) -> Self { - Self::Procedure(def.into()) - } -} - impl ModuleDefLookup for TableDef { type Key<'a> = &'a Identifier; @@ -1262,40 +983,6 @@ impl ModuleDefLookup for ColumnDef { } } -impl ModuleDefLookup for ViewColumnDef { - // We don't use `ColId` here because we want this to be portable - // across migrations. - type Key<'a> = (&'a Identifier, &'a Identifier); - - fn key(&self) -> Self::Key<'_> { - (&self.view_name, &self.name) - } - - fn lookup<'a>(module_def: &'a ModuleDef, (view_name, name): Self::Key<'_>) -> Option<&'a Self> { - module_def - .views - .get(view_name) - .and_then(|view| view.get_column_by_name(name)) - } -} - -impl ModuleDefLookup for ViewParamDef { - // We don't use `ColId` here because we want this to be portable - // across migrations. - type Key<'a> = (&'a Identifier, &'a Identifier); - - fn key(&self) -> Self::Key<'_> { - (&self.view_name, &self.name) - } - - fn lookup<'a>(module_def: &'a ModuleDef, (view_name, name): Self::Key<'_>) -> Option<&'a Self> { - module_def - .views - .get(view_name) - .and_then(|view| view.get_param_by_name(name)) - } -} - impl ModuleDefLookup for ConstraintDef { type Key<'a> = &'a str; @@ -1361,18 +1048,6 @@ impl ModuleDefLookup for ReducerDef { } } -impl ModuleDefLookup for ViewDef { - type Key<'a> = &'a Identifier; - - fn key(&self) -> Self::Key<'_> { - &self.name - } - - fn lookup<'a>(view_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> { - view_def.views.get(key) - } -} - fn to_raw(data: HashMap) -> Vec where Def: ModuleDefLookup + Into, diff --git a/crates/schema/src/def/deserialize.rs b/crates/schema/src/def/deserialize.rs index 09b78f3d2d4..a5a14240584 100644 --- a/crates/schema/src/def/deserialize.rs +++ b/crates/schema/src/def/deserialize.rs @@ -1,60 +1,21 @@ //! Helpers to allow deserializing data using a ReducerDef. -use crate::def::{ProcedureDef, ReducerDef}; -use spacetimedb_lib::{ - sats::{self, de, impl_serialize, ser, ProductValue}, - ProductType, -}; +use crate::def::ReducerDef; +use spacetimedb_lib::sats::{self, de, ProductValue}; -/// Wrapper around a function def that allows deserializing to a [`ProductValue`] at the type of the def's parameter [`ProductType`]. -/// -/// Sensible instantiations for `Def` are [`ProcedureDef`] and [`ReducerDef`]. -pub struct ArgsSeed<'a, Def>(pub sats::WithTypespace<'a, Def>); +/// Wrapper around a `ReducerDef` that allows deserializing to a `ProductValue` at the type +/// of the reducer's parameter `ProductType`. +#[derive(Clone, Copy)] +pub struct ReducerArgsDeserializeSeed<'a>(pub sats::WithTypespace<'a, ReducerDef>); -// Manual impls of traits rather than derives, -// 'cause derives are always constrained on all type parameters, -// even though `ArgsSeed: Copy` in our case. -impl Clone for ArgsSeed<'_, Def> { - fn clone(&self) -> Self { - *self +impl<'a> ReducerArgsDeserializeSeed<'a> { + /// Get the reducer def of this seed. + pub fn reducer_def(&self) -> &'a ReducerDef { + self.0.ty() } } -impl Copy for ArgsSeed<'_, Def> {} -pub trait FunctionDef { - fn params(&self) -> &ProductType; - fn name(&self) -> &str; -} - -impl FunctionDef for ReducerDef { - fn params(&self) -> &ProductType { - &self.params - } - fn name(&self) -> &str { - &self.name - } -} - -impl FunctionDef for ProcedureDef { - fn params(&self) -> &ProductType { - &self.params - } - fn name(&self) -> &str { - &self.name - } -} - -impl ArgsSeed<'_, Def> { - pub fn name(&self) -> &str { - self.0.ty().name() - } - - pub fn params(&self) -> &ProductType { - self.0.ty().params() - } -} - -impl<'de, Def: FunctionDef> de::DeserializeSeed<'de> for ArgsSeed<'_, Def> { +impl<'de> de::DeserializeSeed<'de> for ReducerArgsDeserializeSeed<'_> { type Output = ProductValue; fn deserialize>(self, deserializer: D) -> Result { @@ -62,40 +23,24 @@ impl<'de, Def: FunctionDef> de::DeserializeSeed<'de> for ArgsSeed<'_, Def> { } } -impl<'de, Def: FunctionDef> de::ProductVisitor<'de> for ArgsSeed<'_, Def> { +impl<'de> de::ProductVisitor<'de> for ReducerArgsDeserializeSeed<'_> { type Output = ProductValue; fn product_name(&self) -> Option<&str> { - Some(self.0.ty().name()) + Some(&self.0.ty().name) } - fn product_len(&self) -> usize { - self.0.ty().params().elements.len() + self.0.ty().params.elements.len() } - fn product_kind(&self) -> de::ProductKind { de::ProductKind::ReducerArgs } fn visit_seq_product>(self, tup: A) -> Result { - de::visit_seq_product(self.0.map(|r| &*r.params().elements), &self, tup) + de::visit_seq_product(self.0.map(|r| &*r.params.elements), &self, tup) } fn visit_named_product>(self, tup: A) -> Result { - de::visit_named_product(self.0.map(|r| &*r.params().elements), &self, tup) + de::visit_named_product(self.0.map(|r| &*r.params.elements), &self, tup) } } - -pub struct ReducerArgsWithSchema<'a> { - value: &'a ProductValue, - ty: sats::WithTypespace<'a, ReducerDef>, -} -impl_serialize!([] ReducerArgsWithSchema<'_>, (self, ser) => { - use itertools::Itertools; - use ser::SerializeSeqProduct; - let mut seq = ser.serialize_seq_product(self.value.elements.len())?; - for (value, elem) in self.value.elements.iter().zip_eq(&*self.ty.ty().params.elements) { - seq.serialize_element(&self.ty.with(&elem.algebraic_type).with_value(value))?; - } - seq.end() -}); diff --git a/crates/schema/src/def/validate/v8.rs b/crates/schema/src/def/validate/v8.rs index 347e1f47e07..89a853a995e 100644 --- a/crates/schema/src/def/validate/v8.rs +++ b/crates/schema/src/def/validate/v8.rs @@ -57,8 +57,6 @@ fn upgrade_module(def: RawModuleDefV8, extra_errors: &mut Vec) tables, reducers, types, - // V8 module defs don't have procedures or column default values, - // which are all we use the `misc_exports` for at this time (pgoldman 2025-10-09). misc_exports: Default::default(), row_level_security: vec![], // v8 doesn't have row-level security } @@ -528,7 +526,7 @@ mod tests { assert_eq!(delivery_def.columns[2].ty, AlgebraicType::U64); assert_eq!(delivery_def.schedule.as_ref().unwrap().at_column, 1.into()); assert_eq!( - &delivery_def.schedule.as_ref().unwrap().function_name[..], + &delivery_def.schedule.as_ref().unwrap().reducer_name[..], "check_deliveries" ); assert_eq!(delivery_def.primary_key, Some(ColId(2))); diff --git a/crates/schema/src/def/validate/v9.rs b/crates/schema/src/def/validate/v9.rs index 3f811dca6ed..6adbbf01b9a 100644 --- a/crates/schema/src/def/validate/v9.rs +++ b/crates/schema/src/def/validate/v9.rs @@ -5,11 +5,9 @@ use crate::{def::validate::Result, error::TypeLocation}; use spacetimedb_data_structures::error_stream::{CollectAllErrors, CombineErrors}; use spacetimedb_data_structures::map::HashSet; use spacetimedb_lib::db::default_element_ordering::{product_type_has_default_ordering, sum_type_has_default_ordering}; -use spacetimedb_lib::db::raw_def::v9::RawViewDefV9; use spacetimedb_lib::ProductType; use spacetimedb_primitives::col_list; use spacetimedb_sats::{bsatn::de::Deserializer, de::DeserializeSeed, WithTypespace}; -use std::borrow::Cow; /// Validate a `RawModuleDefV9` and convert it into a `ModuleDef`, /// or return a stream of errors if the definition is invalid. @@ -51,53 +49,6 @@ pub fn validate(def: RawModuleDefV9) -> Result { .validate_reducer_def(reducer, ReducerId(idx as u32)) .map(|reducer_def| (reducer_def.name.clone(), reducer_def)) }) - // Collect into a `Vec` first to preserve duplicate names. - // Later on, in `check_function_names_are_unique`, we'll transform this into an `IndexMap`. - .collect_all_errors::>(); - - let (procedures, misc_exports) = - misc_exports - .into_iter() - .partition::, _>(|misc_export| { - matches!(misc_export, RawMiscModuleExportV9::Procedure(_)) - }); - - let (views, misc_exports) = misc_exports - .into_iter() - .partition::, _>(|misc_export| { - matches!(misc_export, RawMiscModuleExportV9::View(_)) - }); - - let procedures = procedures - .into_iter() - .map(|procedure| { - let RawMiscModuleExportV9::Procedure(procedure) = procedure else { - unreachable!("Already partitioned procedures separate from other `RawMiscModuleExportV9` variants"); - }; - procedure - }) - .map(|procedure| { - validator - .validate_procedure_def(procedure) - .map(|procedure_def| (procedure_def.name.clone(), procedure_def)) - }) - // Collect into a `Vec` first to preserve duplicate names. - // Later on, in `check_function_names_are_unique`, we'll transform this into an `IndexMap`. - .collect_all_errors::>(); - - let views = views - .into_iter() - .map(|view| { - let RawMiscModuleExportV9::View(view) = view else { - unreachable!("Already partitioned views separate from other `RawMiscModuleExportV9` variants"); - }; - view - }) - .map(|view| { - validator - .validate_view_def(view) - .map(|view_def| (view_def.name.clone(), view_def)) - }) .collect_all_errors(); let tables = tables @@ -125,16 +76,14 @@ pub fn validate(def: RawModuleDefV9) -> Result { }) .collect_all_errors::>(); - let tables_types_reducers_procedures_views = (tables, types, reducers, procedures, views) + let tables_types_reducers = (tables, types, reducers) .combine_errors() - .and_then(|(mut tables, types, reducers, procedures, views)| { - let ((reducers, procedures, views), ()) = ( - check_function_names_are_unique(reducers, procedures, views), - check_non_procedure_misc_exports(misc_exports, &validator, &mut tables), - ) - .combine_errors()?; - check_scheduled_functions_exist(&mut tables, &reducers, &procedures)?; - Ok((tables, types, reducers, procedures, views)) + .and_then(|(mut tables, types, reducers)| { + let sched_exists = check_scheduled_reducers_exist(&tables, &reducers); + let default_values_work = proccess_misc_exports(misc_exports, &validator, &mut tables); + (sched_exists, default_values_work).combine_errors()?; + + Ok((tables, types, reducers)) }); let ModuleValidator { @@ -144,15 +93,13 @@ pub fn validate(def: RawModuleDefV9) -> Result { .. } = validator; - let (tables, types, reducers, procedures, views) = - (tables_types_reducers_procedures_views).map_err(|errors| errors.sort_deduplicate())?; + let (tables, types, reducers) = (tables_types_reducers).map_err(|errors| errors.sort_deduplicate())?; let typespace_for_generate = typespace_for_generate.finish(); Ok(ModuleDef { tables, reducers, - views, types, typespace, typespace_for_generate, @@ -160,7 +107,6 @@ pub fn validate(def: RawModuleDefV9) -> Result { refmap, row_level_security_raw, lifecycle_reducers, - procedures, }) } @@ -340,19 +286,26 @@ impl ModuleValidator<'_> { }) } - fn params_for_generate<'a>( - &mut self, - params: &'a ProductType, - make_type_location: impl Fn(usize, Option>) -> TypeLocation<'a>, - ) -> Result> { - params + /// Validate a reducer definition. + fn validate_reducer_def(&mut self, reducer_def: RawReducerDefV9, reducer_id: ReducerId) -> Result { + let RawReducerDefV9 { + name, + params, + lifecycle, + } = reducer_def; + + let params_for_generate: Result<_> = params .elements .iter() .enumerate() .map(|(position, param)| { // Note: this does not allocate, since `TypeLocation` is defined using `Cow`. // We only allocate if an error is returned. - let location = make_type_location(position, param.name().map(Into::into)); + let location = TypeLocation::ReducerArg { + reducer_name: (&*name).into(), + position, + arg_name: param.name().map(Into::into), + }; let param_name = param .name() .ok_or_else(|| { @@ -366,27 +319,10 @@ impl ModuleValidator<'_> { let ty_use = self.validate_for_type_use(&location, ¶m.algebraic_type); (param_name, ty_use).combine_errors() }) - .collect_all_errors() - } - - /// Validate a reducer definition. - fn validate_reducer_def(&mut self, reducer_def: RawReducerDefV9, reducer_id: ReducerId) -> Result { - let RawReducerDefV9 { - name, - params, - lifecycle, - } = reducer_def; - - let params_for_generate: Result<_> = - self.params_for_generate(¶ms, |position, arg_name| TypeLocation::ReducerArg { - reducer_name: (&*name).into(), - position, - arg_name, - }); + .collect_all_errors(); - // Reducers share the "function namespace" with procedures. - // Uniqueness is validated in a later pass, in `check_function_names_are_unique`. - let name = identifier(name.clone()); + // reducers don't live in the global namespace. + let name = identifier(name); let lifecycle = lifecycle .map(|lifecycle| match &mut self.lifecycle_reducers[lifecycle] { @@ -397,7 +333,9 @@ impl ModuleValidator<'_> { Some(_) => Err(ValidationError::DuplicateLifecycle { lifecycle }.into()), }) .transpose(); + let (name, params_for_generate, lifecycle) = (name, params_for_generate, lifecycle).combine_errors()?; + Ok(ReducerDef { name, params: params.clone(), @@ -409,149 +347,6 @@ impl ModuleValidator<'_> { }) } - fn validate_procedure_def(&mut self, procedure_def: RawProcedureDefV9) -> Result { - let RawProcedureDefV9 { - name, - params, - return_type, - } = procedure_def; - - let params_for_generate = self.params_for_generate(¶ms, |position, arg_name| TypeLocation::ProcedureArg { - procedure_name: Cow::Borrowed(&name), - position, - arg_name, - }); - - let return_type_for_generate = self.validate_for_type_use( - &TypeLocation::ProcedureReturn { - procedure_name: Cow::Borrowed(&name), - }, - &return_type, - ); - - // Procedures share the "function namespace" with reducers. - // Uniqueness is validated in a later pass, in `check_function_names_are_unique`. - let name = identifier(name); - - let (name, params_for_generate, return_type_for_generate) = - (name, params_for_generate, return_type_for_generate).combine_errors()?; - - Ok(ProcedureDef { - name, - params, - params_for_generate: ProductTypeDef { - elements: params_for_generate, - recursive: false, // A ProductTypeDef not stored in a Typespace cannot be recursive. - }, - return_type, - return_type_for_generate, - }) - } - - /// Validate a view definition. - fn validate_view_def(&mut self, view_def: RawViewDefV9) -> Result { - let RawViewDefV9 { - name, - is_public, - is_anonymous, - params, - return_type, - } = view_def; - - let invalid_return_type = || { - ValidationErrors::from(ValidationError::InvalidViewReturnType { - view: name.clone(), - ty: return_type.clone().into(), - }) - }; - - // The possible return types of a view are `Vec` or `Option`, - // where `T` is a `ProductType` in the `Typespace`. - // Here we extract the inner product type ref `T`. - // We exit early for errors since this breaks all the other checks. - let product_type_ref = return_type - .as_option() - .and_then(AlgebraicType::as_ref) - .or_else(|| { - return_type - .as_array() - .map(|array_type| array_type.elem_ty.as_ref()) - .and_then(AlgebraicType::as_ref) - }) - .cloned() - .ok_or_else(invalid_return_type)?; - - let product_type = self - .typespace - .get(product_type_ref) - .and_then(AlgebraicType::as_product) - .ok_or_else(|| { - ValidationErrors::from(ValidationError::InvalidProductTypeRef { - table: name.clone(), - ref_: product_type_ref, - }) - })?; - - let params_for_generate = self.params_for_generate(¶ms, |position, arg_name| TypeLocation::ViewArg { - view_name: Cow::Borrowed(&name), - position, - arg_name, - })?; - - let return_type_for_generate = self.validate_for_type_use( - &TypeLocation::ViewReturn { - view_name: Cow::Borrowed(&name), - }, - &return_type, - ); - - let mut view_in_progress = ViewValidator::new( - name.clone(), - product_type_ref, - product_type, - ¶ms, - ¶ms_for_generate, - self, - ); - - // Views have the same interface as tables and therefore must be registered in the global namespace. - // - // Note, views also share the "function namespace" with reducers and procedures. - // While this isn't strictly necessary because reducers and views have different calling contexts, - // we may want to support calling views in the same context as reducers in the future (e.g. `spacetime call`). - // Hence we validate uniqueness among reducer, procedure, and view names in a later pass. - // See `check_function_names_are_unique`. - let name = view_in_progress.add_to_global_namespace(name).and_then(identifier); - - let n = product_type.elements.len(); - let return_columns = (0..n) - .map(|id| view_in_progress.validate_view_column_def(id.into())) - .collect_all_errors(); - - let n = params.elements.len(); - let param_columns = (0..n) - .map(|id| view_in_progress.validate_param_column_def(id.into())) - .collect_all_errors(); - - let (name, return_type_for_generate, return_columns, param_columns) = - (name, return_type_for_generate, return_columns, param_columns).combine_errors()?; - - Ok(ViewDef { - name, - is_anonymous, - is_public, - params, - params_for_generate: ProductTypeDef { - elements: params_for_generate, - recursive: false, // A `ProductTypeDef` not stored in a `Typespace` cannot be recursive. - }, - return_type, - return_type_for_generate, - return_columns, - param_columns, - }) - } - fn validate_column_default_value( &self, tables: &HashMap, @@ -689,86 +484,6 @@ impl ModuleValidator<'_> { } } -/// A partially validated view. -/// -/// This is just a small wrapper around [`TableValidator`] so that we can: -/// 1. Validate column defs -/// 2. Insert view names into the global namespace. -struct ViewValidator<'a, 'b> { - inner: TableValidator<'a, 'b>, - params: &'a ProductType, - params_for_generate: &'a [(Identifier, AlgebraicTypeUse)], -} - -impl<'a, 'b> ViewValidator<'a, 'b> { - fn new( - raw_name: Box, - product_type_ref: AlgebraicTypeRef, - product_type: &'a ProductType, - params: &'a ProductType, - params_for_generate: &'a [(Identifier, AlgebraicTypeUse)], - module_validator: &'a mut ModuleValidator<'b>, - ) -> Self { - Self { - inner: TableValidator { - raw_name, - product_type_ref, - product_type, - module_validator, - has_sequence: Default::default(), - }, - params, - params_for_generate, - } - } - - fn validate_param_column_def(&mut self, col_id: ColId) -> Result { - let column = &self - .params - .elements - .get(col_id.idx()) - .expect("enumerate is generating an out-of-range index..."); - - let (_, ty_for_generate) = self - .params_for_generate - .get(col_id.idx()) - .expect("enumerate is generating an out-of-range index..."); - - let name: Result = identifier( - column - .name() - .map(|name| name.into()) - .unwrap_or_else(|| format!("param_{}", col_id).into_boxed_str()), - ); - - // This error will be created multiple times if the view name is invalid, - // but we sort and deduplicate the error stream afterwards, - // so it isn't a huge deal. - // - // This is necessary because we require `ErrorStream` to be nonempty. - // We need to put something in there if the view name is invalid. - let view_name = identifier(self.inner.raw_name.clone()); - - let (name, view_name) = (name, view_name).combine_errors()?; - - Ok(ViewParamDef { - name, - ty: column.algebraic_type.clone(), - ty_for_generate: ty_for_generate.clone(), - col_id, - view_name, - }) - } - - fn validate_view_column_def(&mut self, col_id: ColId) -> Result { - self.inner.validate_column_def(col_id).map(ViewColumnDef::from) - } - - fn add_to_global_namespace(&mut self, name: Box) -> Result> { - self.inner.add_to_global_namespace(name) - } -} - /// A partially validated table. struct TableValidator<'a, 'b> { module_validator: &'a mut ModuleValidator<'b>, @@ -1002,8 +717,7 @@ impl TableValidator<'_, '_> { /// Validate a schedule definition. fn validate_schedule_def(&mut self, schedule: RawScheduleDefV9, primary_key: Option) -> Result { let RawScheduleDefV9 { - // Despite the field name, a `RawScheduleDefV9` may refer to either a reducer or a function. - reducer_name: function_name, + reducer_name, scheduled_at_column, name, } = schedule; @@ -1035,20 +749,15 @@ impl TableValidator<'_, '_> { }); let name = self.add_to_global_namespace(name); - let function_name = identifier(function_name); + let reducer_name = identifier(reducer_name); - let (name, (at_column, id_column), function_name) = (name, at_id, function_name).combine_errors()?; + let (name, (at_column, id_column), reducer_name) = (name, at_id, reducer_name).combine_errors()?; Ok(ScheduleDef { name, at_column, id_column, - function_name, - - // Fill this in as a placeholder now. - // It will be populated with the correct `FunctionKind` later, - // in `check_scheduled_functions_exist`. - function_kind: FunctionKind::Unknown, + reducer_name, }) } @@ -1188,44 +897,32 @@ fn identifier(name: Box) -> Result { Identifier::new(name).map_err(|error| ValidationError::IdentifierError { error }.into()) } -/// Check that every [`ScheduleDef`]'s `function_name` refers to a real reducer or procedure -/// and that the function's arguments are appropriate for the table, -/// then record the scheduled function's [`FunctionKind`] in the [`ScheduleDef`]. -fn check_scheduled_functions_exist( - tables: &mut IdentifierMap, +fn check_scheduled_reducers_exist( + tables: &IdentifierMap, reducers: &IndexMap, - procedures: &IndexMap, ) -> Result<()> { - let validate_params = - |params_from_function: &ProductType, table_row_type_ref: AlgebraicTypeRef, function_name: &str| { - if params_from_function.elements.len() == 1 - && params_from_function.elements[0].algebraic_type == table_row_type_ref.into() - { - Ok(()) - } else { - Err(ValidationError::IncorrectScheduledFunctionParams { - function_name: function_name.into(), - function_kind: FunctionKind::Reducer, - expected: AlgebraicType::product([AlgebraicType::Ref(table_row_type_ref)]).into(), - actual: params_from_function.clone().into(), - }) - } - }; - tables - .values_mut() + .values() .map(|table| -> Result<()> { - if let Some(schedule) = &mut table.schedule { - if let Some(reducer) = reducers.get(&schedule.function_name) { - schedule.function_kind = FunctionKind::Reducer; - validate_params(&reducer.params, table.product_type_ref, &reducer.name).map_err(Into::into) - } else if let Some(procedure) = procedures.get(&schedule.function_name) { - schedule.function_kind = FunctionKind::Procedure; - validate_params(&procedure.params, table.product_type_ref, &procedure.name).map_err(Into::into) + if let Some(schedule) = &table.schedule { + let reducer = reducers.get(&schedule.reducer_name); + if let Some(reducer) = reducer { + if reducer.params.elements.len() == 1 + && reducer.params.elements[0].algebraic_type == table.product_type_ref.into() + { + Ok(()) + } else { + Err(ValidationError::IncorrectScheduledReducerParams { + reducer: (&*schedule.reducer_name).into(), + expected: AlgebraicType::product([AlgebraicType::Ref(table.product_type_ref)]).into(), + actual: reducer.params.clone().into(), + } + .into()) + } } else { - Err(ValidationError::MissingScheduledFunction { + Err(ValidationError::MissingScheduledReducer { schedule: schedule.name.clone(), - function: schedule.function_name.clone(), + reducer: schedule.reducer_name.clone(), } .into()) } @@ -1236,55 +933,7 @@ fn check_scheduled_functions_exist( .collect_all_errors() } -/// Check that all function (reducer, procedure, or view) names are unique, -/// then re-organize the reducers and procedures into [`IndexMap`]s -/// for storage in the [`ModuleDef`]. -#[allow(clippy::type_complexity)] -fn check_function_names_are_unique( - reducers: Vec<(Identifier, ReducerDef)>, - procedures: Vec<(Identifier, ProcedureDef)>, - views: Vec<(Identifier, ViewDef)>, -) -> Result<( - IndexMap, - IndexMap, - IndexMap, -)> { - let mut errors = vec![]; - - let mut reducers_map = IndexMap::with_capacity(reducers.len()); - - for (name, def) in reducers { - if reducers_map.contains_key(&name) { - errors.push(ValidationError::DuplicateFunctionName { name }); - } else { - reducers_map.insert(name, def); - } - } - - let mut procedures_map = IndexMap::with_capacity(procedures.len()); - - for (name, def) in procedures { - if reducers_map.contains_key(&name) || procedures_map.contains_key(&name) { - errors.push(ValidationError::DuplicateFunctionName { name }); - } else { - procedures_map.insert(name, def); - } - } - - let mut views_map = IndexMap::with_capacity(views.len()); - - for (name, def) in views { - if reducers_map.contains_key(&name) || procedures_map.contains_key(&name) || views_map.contains_key(&name) { - errors.push(ValidationError::DuplicateFunctionName { name }); - } else { - views_map.insert(name, def); - } - } - - ErrorStream::add_extra_errors(Ok((reducers_map, procedures_map, views_map)), errors) -} - -fn check_non_procedure_misc_exports( +fn proccess_misc_exports( misc_exports: Vec, validator: &ModuleValidator, tables: &mut IdentifierMap, @@ -1293,9 +942,6 @@ fn check_non_procedure_misc_exports( .into_iter() .map(|export| match export { RawMiscModuleExportV9::ColumnDefaultValue(cdv) => process_column_default_value(&cdv, validator, tables), - RawMiscModuleExportV9::Procedure(_proc) => { - unreachable!("Procedure defs should already have been sorted out of `misc_exports`") - } _ => unimplemented!("unknown misc export"), }) .collect_all_errors::<()>() @@ -1347,8 +993,7 @@ mod tests { }; use crate::def::{validate::Result, ModuleDef}; use crate::def::{ - BTreeAlgorithm, ConstraintData, ConstraintDef, DirectAlgorithm, FunctionKind, IndexDef, SequenceDef, - UniqueConstraintData, + BTreeAlgorithm, ConstraintData, ConstraintDef, DirectAlgorithm, IndexDef, SequenceDef, UniqueConstraintData, }; use crate::error::*; use crate::type_for_generate::ClientCodegenError; @@ -1565,13 +1210,9 @@ mod tests { assert_eq!(delivery_def.columns[2].ty, AlgebraicType::U64); assert_eq!(delivery_def.schedule.as_ref().unwrap().at_column, 1.into()); assert_eq!( - &delivery_def.schedule.as_ref().unwrap().function_name[..], + &delivery_def.schedule.as_ref().unwrap().reducer_name[..], "check_deliveries" ); - assert_eq!( - delivery_def.schedule.as_ref().unwrap().function_kind, - FunctionKind::Reducer - ); assert_eq!(delivery_def.primary_key, Some(ColId(2))); assert_eq!(def.typespace.get(product_type_ref), Some(&product_type)); @@ -2019,9 +1660,9 @@ mod tests { .finish(); let result: Result = builder.finish().try_into(); - expect_error_matching!(result, ValidationError::MissingScheduledFunction { schedule, function } => { + expect_error_matching!(result, ValidationError::MissingScheduledReducer { schedule, reducer } => { &schedule[..] == "Deliveries_sched" && - function == &expect_identifier("check_deliveries") + reducer == &expect_identifier("check_deliveries") }); } @@ -2047,11 +1688,10 @@ mod tests { builder.add_reducer("check_deliveries", ProductType::from([("a", AlgebraicType::U64)]), None); let result: Result = builder.finish().try_into(); - expect_error_matching!(result, ValidationError::IncorrectScheduledFunctionParams { function_name, function_kind, expected, actual } => { - &function_name[..] == "check_deliveries" && - *function_kind == FunctionKind::Reducer && - expected.0 == AlgebraicType::product([AlgebraicType::Ref(deliveries_product_type)]) && - actual.0 == ProductType::from([("a", AlgebraicType::U64)]).into() + expect_error_matching!(result, ValidationError::IncorrectScheduledReducerParams { reducer, expected, actual } => { + &reducer[..] == "check_deliveries" && + expected.0 == AlgebraicType::product([AlgebraicType::Ref(deliveries_product_type)]) && + actual.0 == ProductType::from([("a", AlgebraicType::U64)]).into() }); } @@ -2097,46 +1737,4 @@ mod tests { assert!(def.lookup::("wacky.index()").is_some()); assert!(def.lookup::("wacky.sequence()").is_some()); } - - #[test] - fn duplicate_reducer_names() { - let mut builder = RawModuleDefV9Builder::new(); - - builder.add_reducer("foo", [("i", AlgebraicType::I32)].into(), None); - builder.add_reducer("foo", [("name", AlgebraicType::String)].into(), None); - - let result: Result = builder.finish().try_into(); - - expect_error_matching!(result, ValidationError::DuplicateFunctionName { name } => { - &name[..] == "foo" - }); - } - - #[test] - fn duplicate_procedure_names() { - let mut builder = RawModuleDefV9Builder::new(); - - builder.add_procedure("foo", [("i", AlgebraicType::I32)].into(), AlgebraicType::unit()); - builder.add_procedure("foo", [("name", AlgebraicType::String)].into(), AlgebraicType::unit()); - - let result: Result = builder.finish().try_into(); - - expect_error_matching!(result, ValidationError::DuplicateFunctionName { name } => { - &name[..] == "foo" - }); - } - - #[test] - fn duplicate_procedure_and_reducer_name() { - let mut builder = RawModuleDefV9Builder::new(); - - builder.add_reducer("foo", [("i", AlgebraicType::I32)].into(), None); - builder.add_procedure("foo", [("i", AlgebraicType::I32)].into(), AlgebraicType::unit()); - - let result: Result = builder.finish().try_into(); - - expect_error_matching!(result, ValidationError::DuplicateFunctionName { name } => { - &name[..] == "foo" - }); - } } diff --git a/crates/schema/src/error.rs b/crates/schema/src/error.rs index c67456991a9..e7526ad095d 100644 --- a/crates/schema/src/error.rs +++ b/crates/schema/src/error.rs @@ -7,7 +7,7 @@ use spacetimedb_sats::{bsatn::DecodeError, AlgebraicType, AlgebraicTypeRef}; use std::borrow::Cow; use std::fmt; -use crate::def::{FunctionKind, ScopedTypeName}; +use crate::def::ScopedTypeName; use crate::identifier::Identifier; use crate::type_for_generate::ClientCodegenError; @@ -82,11 +82,6 @@ pub enum ValidationError { start: Option, max_value: Option, }, - #[error("View {view} has invalid return type {ty}")] - InvalidViewReturnType { - view: RawIdentifier, - ty: PrettyAlgebraicType, - }, #[error("Table {table} has invalid product_type_ref {ref_}")] InvalidProductTypeRef { table: RawIdentifier, @@ -113,12 +108,11 @@ pub enum ValidationError { MissingPrimaryKeyUniqueConstraint { column: RawColumnName }, #[error("Table {table} should have a type definition for its product_type_element, but does not")] TableTypeNameMismatch { table: Identifier }, - #[error("Schedule {schedule} refers to a scheduled reducer or procedure {function} that does not exist")] - MissingScheduledFunction { schedule: Box, function: Identifier }, - #[error("Scheduled {function_kind} {function_name} expected to have type {expected}, but has type {actual}")] - IncorrectScheduledFunctionParams { - function_name: RawIdentifier, - function_kind: FunctionKind, + #[error("Schedule {schedule} refers to a scheduled reducer {reducer} that does not exist")] + MissingScheduledReducer { schedule: Box, reducer: Identifier }, + #[error("Scheduled reducer {reducer} expected to have type {expected}, but has type {actual}")] + IncorrectScheduledReducerParams { + reducer: RawIdentifier, expected: PrettyAlgebraicType, actual: PrettyAlgebraicType, }, @@ -136,8 +130,6 @@ pub enum ValidationError { MultipleColumnDefaultValues { table: RawIdentifier, col_id: ColId }, #[error("Table {table} not found")] TableNotFound { table: RawIdentifier }, - #[error("Name {name} is used for multiple reducers, procedures and/or views")] - DuplicateFunctionName { name: Identifier }, } /// A wrapper around an `AlgebraicType` that implements `fmt::Display`. @@ -181,22 +173,6 @@ pub enum TypeLocation<'a> { position: usize, arg_name: Option>, }, - /// A procedure argument. - ProcedureArg { - procedure_name: Cow<'a, str>, - position: usize, - arg_name: Option>, - }, - /// A view argument. - ViewArg { - view_name: Cow<'a, str>, - position: usize, - arg_name: Option>, - }, - /// A procedure return type. - ProcedureReturn { procedure_name: Cow<'a, str> }, - /// A view return type. - ViewReturn { view_name: Cow<'a, str> }, /// A type in the typespace. InTypespace { /// The reference to the type within the typespace. @@ -217,30 +193,6 @@ impl TypeLocation<'_> { position, arg_name: arg_name.map(|s| s.to_string().into()), }, - TypeLocation::ProcedureArg { - procedure_name, - position, - arg_name, - } => TypeLocation::ProcedureArg { - procedure_name: procedure_name.to_string().into(), - position, - arg_name: arg_name.map(|s| s.to_string().into()), - }, - TypeLocation::ViewArg { - view_name, - position, - arg_name, - } => TypeLocation::ViewArg { - view_name: view_name.to_string().into(), - position, - arg_name: arg_name.map(|s| s.to_string().into()), - }, - Self::ProcedureReturn { procedure_name } => TypeLocation::ProcedureReturn { - procedure_name: procedure_name.to_string().into(), - }, - Self::ViewReturn { view_name } => TypeLocation::ViewReturn { - view_name: view_name.to_string().into(), - }, // needed to convince rustc this is allowed. TypeLocation::InTypespace { ref_ } => TypeLocation::InTypespace { ref_ }, } @@ -261,34 +213,6 @@ impl fmt::Display for TypeLocation<'_> { } Ok(()) } - TypeLocation::ProcedureArg { - procedure_name, - position, - arg_name, - } => { - write!(f, "procedure `{procedure_name}` argument {position}")?; - if let Some(arg_name) = arg_name { - write!(f, " (`{arg_name}`)")?; - } - Ok(()) - } - TypeLocation::ViewArg { - view_name, - position, - arg_name, - } => { - write!(f, "view `{view_name}` argument {position}")?; - if let Some(arg_name) = arg_name { - write!(f, " (`{arg_name}`)")?; - } - Ok(()) - } - TypeLocation::ProcedureReturn { procedure_name } => { - write!(f, "procedure `{procedure_name}` return value") - } - TypeLocation::ViewReturn { view_name } => { - write!(f, "view `{view_name}` return value") - } TypeLocation::InTypespace { ref_ } => { write!(f, "typespace ref `{ref_}`") } diff --git a/crates/schema/src/schema.rs b/crates/schema/src/schema.rs index 86c748237c8..f9d104795f2 100644 --- a/crates/schema/src/schema.rs +++ b/crates/schema/src/schema.rs @@ -21,7 +21,7 @@ use std::sync::Arc; use crate::def::{ ColumnDef, ConstraintData, ConstraintDef, IndexAlgorithm, IndexDef, ModuleDef, ModuleDefLookup, ScheduleDef, - SequenceDef, TableDef, UniqueConstraintData, ViewColumnDef, ViewDef, + SequenceDef, TableDef, UniqueConstraintData, }; use crate::identifier::Identifier; @@ -587,106 +587,6 @@ pub fn column_schemas_from_defs(module_def: &ModuleDef, columns: &[ColumnDef], t .collect() } -impl TableSchema { - /// Every view is materialized by default. For example: - /// ```rust,ignore - /// #[table] - /// pub struct MyTable { - /// a: u32, - /// b: u32, - /// } - /// - /// #[view(name = my_view, public)] - /// fn my_view(ctx: &ViewContext, x: u32, y: u32) -> Vec { ... } - /// - /// #[view(name = my_anonymous_view, public)] - /// fn my_anonymous_view(ctx: &AnonymousViewContext, x: u32, y: u32) -> Vec { ... } - /// ``` - /// - /// The above views are materialized with the following schema: - /// - /// my_view: - /// - /// | sender | arg_id | a | b | - /// |----------------|--------|-----|-----| - /// | (some = 0x...) | u64 | u32 | u32 | - /// - /// my_anonymous_view: - /// - /// | sender | arg_id | a | b | - /// |-------------|--------|-----|-----| - /// | (none = ()) | u64 | u32 | u32 | - /// - /// Note, `arg_id` is a foreign key into `st_view_arg`. - pub fn from_view_def(module_def: &ModuleDef, view_def: &ViewDef) -> Self { - module_def.expect_contains(view_def); - - let ViewDef { - name, - is_public, - return_columns, - .. - } = view_def; - - let n = return_columns.len() + 2; - let mut columns = Vec::with_capacity(n); - - let sender_col_name = "sender"; - let arg_id_col_name = "arg_id"; - - columns.push(ColumnSchema { - table_id: TableId::SENTINEL, - col_pos: ColId(0), - col_name: sender_col_name.into(), - col_type: AlgebraicType::option(AlgebraicType::identity()), - }); - - columns.push(ColumnSchema { - table_id: TableId::SENTINEL, - col_pos: ColId(1), - col_name: arg_id_col_name.into(), - col_type: AlgebraicType::U64, - }); - - columns.extend( - return_columns - .iter() - .map(|def| ColumnSchema::from_view_column_def(module_def, def)) - .enumerate() - .map(|(i, schema)| (ColId::from(i + 2), schema)) - .map(|(col_pos, schema)| ColumnSchema { col_pos, ..schema }), - ); - - let index_name = format!("{}_{}_{}_idx_btree", name, sender_col_name, arg_id_col_name); - - let indexes = vec![IndexSchema { - index_id: IndexId::SENTINEL, - table_id: TableId::SENTINEL, - index_name: index_name.into_boxed_str(), - index_algorithm: IndexAlgorithm::BTree(col_list![0, 1].into()), - }]; - - let table_access = if *is_public { - StAccess::Public - } else { - StAccess::Private - }; - - TableSchema::new( - TableId::SENTINEL, - (*name).clone().into(), - columns, - indexes, - vec![], - vec![], - StTableType::User, - table_access, - None, - None, - ) - } -} - impl Schema for TableSchema { type Def = TableDef; type Id = TableId; @@ -879,18 +779,6 @@ impl ColumnSchema { col_type: ty, } } - - fn from_view_column_def(module_def: &ModuleDef, def: &ViewColumnDef) -> Self { - let col_type = WithTypespace::new(module_def.typespace(), &def.ty) - .resolve_refs() - .expect("validated module should have all types resolve"); - ColumnSchema { - table_id: TableId::SENTINEL, - col_pos: def.col_id, - col_name: (*def.name).into(), - col_type, - } - } } impl Schema for ColumnSchema { @@ -1034,20 +922,20 @@ pub struct ScheduleSchema { /// The name of the schedule. pub schedule_name: Box, - /// The name of the reducer or procedure to call. - pub function_name: Box, + /// The name of the reducer to call. + pub reducer_name: Box, /// The column containing the `ScheduleAt` enum. pub at_column: ColId, } impl ScheduleSchema { - pub fn for_test(name: impl Into>, function: impl Into>, at: impl Into) -> Self { + pub fn for_test(name: impl Into>, reducer: impl Into>, at: impl Into) -> Self { Self { table_id: TableId::SENTINEL, schedule_id: ScheduleId::SENTINEL, schedule_name: name.into(), - function_name: function.into(), + reducer_name: reducer.into(), at_column: at.into(), } } @@ -1067,7 +955,7 @@ impl Schema for ScheduleSchema { table_id: parent_id, schedule_id: id, schedule_name: (*def.name).into(), - function_name: (*def.function_name).into(), + reducer_name: (*def.reducer_name).into(), at_column: def.at_column, // Ignore def.at_column and id_column. Those are recovered at runtime. } @@ -1076,9 +964,9 @@ impl Schema for ScheduleSchema { fn check_compatible(&self, _module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> { ensure_eq!(&self.schedule_name[..], &def.name[..], "Schedule name mismatch"); ensure_eq!( - &self.function_name[..], - &def.function_name[..], - "Schedule function name mismatch" + &self.reducer_name[..], + &def.reducer_name[..], + "Schedule reducer name mismatch" ); Ok(()) } diff --git a/crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__empty_to_populated_migration.snap b/crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__empty_to_populated_migration.snap index 51487bc74fb..564c1bfb0e2 100644 --- a/crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__empty_to_populated_migration.snap +++ b/crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__empty_to_populated_migration.snap @@ -51,13 +51,5 @@ expression: "plan.pretty_print(PrettyPrintStyle::AnsiColor).expect(\"should pret Auto-increment constraints: • Inspections_scheduled_id_seq on scheduled_id -▸ ▸ Created anonymous view: my_view - Parameters: - • x: U32 - • y: U32 - Columns: - • a: U64 - • b: U64 - ▸ Created row level security policy: `SELECT * FROM Apples` diff --git a/crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__updated pretty print no color.snap b/crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__updated pretty print no color.snap index bb6cbc98ef0..c317d1216fc 100644 --- a/crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__updated pretty print no color.snap +++ b/crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__updated pretty print no color.snap @@ -1,6 +1,6 @@ --- source: crates/schema/src/auto_migrate.rs -expression: "plan.pretty_print(PrettyPrintStyle::NoColor).expect(\"should pretty print\")" +expression: "plan.pretty_print(true).expect(\"should pretty print\")" --- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Database Migration Plan @@ -10,14 +10,6 @@ Database Migration Plan ▸ Removed unique constraint Apples_id_key on [id] of table Apples ▸ Removed auto-increment constraint Apples_id_seq on column id of table Apples ▸ Removed schedule for table Deliveries_sched calling reducer check_deliveries -▸ ▸ Removed anonymous view: my_view - Parameters: - • x: U32 - • y: U32 - Columns: - • a: U64 - • b: U64 - ▸ Removed row level security policy: `SELECT * FROM Apples` ▸ Changed columns for table Apples @@ -39,12 +31,6 @@ Database Migration Plan ▸ Created index Apples_id_count_idx_btree on [id, count] of table Apples ▸ Created auto-increment constraint Bananas_id_seq on column id of table Bananas ▸ Created schedule for table Inspections_sched calling reducer perform_inspection -▸ ▸ Created anonymous view: my_view - Parameters: - • x: U32 - Columns: - • a: U64 - ▸ Created row level security policy: `SELECT * FROM Bananas` ▸ Changed access for table Bananas (public → private) diff --git a/crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__updated pretty print.snap b/crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__updated pretty print.snap index b9a9b7827bd..b6281e07036 100644 --- a/crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__updated pretty print.snap +++ b/crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__updated pretty print.snap @@ -10,14 +10,6 @@ expression: "plan.pretty_print(PrettyPrintStyle::AnsiColor).expect(\"should pret ▸ Removed unique constraint Apples_id_key on [id] of table Apples ▸ Removed auto-increment constraint Apples_id_seq on column id of table Apples ▸ Removed schedule for table Deliveries_sched calling reducer check_deliveries -▸ ▸ Removed anonymous view: my_view - Parameters: - • x: U32 - • y: U32 - Columns: - • a: U64 - • b: U64 - ▸ Removed row level security policy: `SELECT * FROM Apples` ▸ Changed columns for table Apples @@ -39,12 +31,6 @@ expression: "plan.pretty_print(PrettyPrintStyle::AnsiColor).expect(\"should pret ▸ Created index Apples_id_count_idx_btree on [id, count] of table Apples ▸ Created auto-increment constraint Bananas_id_seq on column id of table Bananas ▸ Created schedule for table Inspections_sched calling reducer perform_inspection -▸ ▸ Created anonymous view: my_view - Parameters: - • x: U32 - Columns: - • a: U64 - ▸ Created row level security policy: `SELECT * FROM Bananas` ▸ Changed access for table Bananas (public → private) diff --git a/crates/schema/src/type_for_generate.rs b/crates/schema/src/type_for_generate.rs index df2eff0446c..4b1e83be69b 100644 --- a/crates/schema/src/type_for_generate.rs +++ b/crates/schema/src/type_for_generate.rs @@ -146,7 +146,7 @@ impl Index<&'_ AlgebraicTypeRef> for TypespaceForGenerate { } /// An algebraic type definition. -#[derive(Debug, Clone, PartialEq, Eq, EnumAsInner)] +#[derive(Debug, Clone, EnumAsInner)] pub enum AlgebraicTypeDef { /// A product type declaration. Product(ProductTypeDef), @@ -237,7 +237,7 @@ pub struct SumTypeDef { } /// A sum type, all of whose variants contain (). -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone)] pub struct PlainEnumTypeDef { pub variants: Box<[Identifier]>, } diff --git a/crates/table/src/read_column.rs b/crates/table/src/read_column.rs index be58029f714..123b751f5a3 100644 --- a/crates/table/src/read_column.rs +++ b/crates/table/src/read_column.rs @@ -326,7 +326,6 @@ macro_rules! impl_read_column_via_from { impl_read_column_via_from! { u16 => spacetimedb_primitives::ColId; - u32 => spacetimedb_primitives::ViewId; u32 => spacetimedb_primitives::TableId; u32 => spacetimedb_primitives::IndexId; u32 => spacetimedb_primitives::ConstraintId; diff --git a/crates/testing/src/modules.rs b/crates/testing/src/modules.rs index 7dbcd442b06..5a883c429a4 100644 --- a/crates/testing/src/modules.rs +++ b/crates/testing/src/modules.rs @@ -19,7 +19,7 @@ use tokio::runtime::{Builder, Runtime}; use spacetimedb::client::{ClientActorId, ClientConfig, ClientConnection, DataMessage}; use spacetimedb::database_logger::DatabaseLogger; use spacetimedb::db::{Config, Storage}; -use spacetimedb::host::FunctionArgs; +use spacetimedb::host::ReducerArgs; use spacetimedb::messages::websocket::CallReducerFlags; use spacetimedb_client_api::{ControlStateReadAccess, ControlStateWriteAccess, DatabaseDef, NodeDelegate}; use spacetimedb_lib::{bsatn, sats}; @@ -55,7 +55,7 @@ pub struct ModuleHandle { } impl ModuleHandle { - async fn call_reducer(&self, reducer: &str, args: FunctionArgs) -> anyhow::Result<()> { + async fn call_reducer(&self, reducer: &str, args: ReducerArgs) -> anyhow::Result<()> { let result = self .client .call_reducer(reducer, args, 0, Instant::now(), CallReducerFlags::FullUpdate) @@ -72,12 +72,12 @@ impl ModuleHandle { pub async fn call_reducer_json(&self, reducer: &str, args: &sats::ProductValue) -> anyhow::Result<()> { let args = serde_json::to_string(&args).unwrap(); - self.call_reducer(reducer, FunctionArgs::Json(args.into())).await + self.call_reducer(reducer, ReducerArgs::Json(args.into())).await } pub async fn call_reducer_binary(&self, reducer: &str, args: &sats::ProductValue) -> anyhow::Result<()> { let args = bsatn::to_vec(&args).unwrap(); - self.call_reducer(reducer, FunctionArgs::Bsatn(args.into())).await + self.call_reducer(reducer, ReducerArgs::Bsatn(args.into())).await } pub async fn send(&self, message: impl Into) -> anyhow::Result<()> { diff --git a/crates/testing/tests/standalone_integration_test.rs b/crates/testing/tests/standalone_integration_test.rs index 7f19bd185ce..2df76d7d628 100644 --- a/crates/testing/tests/standalone_integration_test.rs +++ b/crates/testing/tests/standalone_integration_test.rs @@ -130,50 +130,6 @@ fn test_calling_a_reducer_with_private_table() { ); } -fn test_calling_a_procedure_in_module(module_name: &'static str) { - init(); - - CompiledModule::compile(module_name, CompilationMode::Debug).with_module_async( - DEFAULT_CONFIG, - |module| async move { - let json = r#" -{ - "CallProcedure": { - "procedure": "sleep_one_second", - "args": "[]", - "request_id": 0, - "flags": 0 - } -}"# - .to_string(); - module.send(json).await.unwrap(); - - // It sleeps one second, but we'll wait two just to be safe. - tokio::time::sleep(std::time::Duration::from_secs(2)).await; - - let logs = read_logs(&module).await; - let logs = logs - .into_iter() - // Filter out log lines from the `repeating_test` reducer, - // which runs frequently enough to appear in our logs after we've slept a second. - .filter(|line| !line.starts_with("Timestamp: Timestamp { __timestamp_micros_since_unix_epoch__: ")) - .collect::>(); - let [log_sleep] = &logs[..] else { - panic!("Expected a single log message but found {logs:#?}"); - }; - - assert!(log_sleep.starts_with("Slept from ")); - assert!(log_sleep.contains("a total of")); - }, - ) -} - -#[test] -#[serial] -fn test_calling_a_procedure() { - test_calling_a_procedure_in_module("module-test"); -} - /// Invoke the `module-test` module, /// use `caller` to invoke its `test` reducer, /// and assert that its logs look right. diff --git a/demo/Blackholio/server-csharp/StdbModule.csproj b/demo/Blackholio/server-csharp/StdbModule.csproj index 60c71d74247..eea7fcc05cb 100644 --- a/demo/Blackholio/server-csharp/StdbModule.csproj +++ b/demo/Blackholio/server-csharp/StdbModule.csproj @@ -13,7 +13,7 @@ - + diff --git a/licenses/BSL.txt b/licenses/BSL.txt index 8058a776b49..758ef4e8948 100644 --- a/licenses/BSL.txt +++ b/licenses/BSL.txt @@ -5,7 +5,7 @@ Business Source License 1.1 Parameters Licensor: Clockwork Laboratories, Inc. -Licensed Work: SpacetimeDB 1.6.0 +Licensed Work: SpacetimeDB 1.7.0 The Licensed Work is (c) 2023 Clockwork Laboratories, Inc. @@ -21,7 +21,7 @@ Additional Use Grant: You may make use of the Licensed Work provided your Licensed Work by creating tables whose schemas are controlled by such third parties. -Change Date: 2030-10-15 +Change Date: 2030-10-31 Change License: GNU Affero General Public License v3.0 with a linking exception diff --git a/modules/module-test/src/lib.rs b/modules/module-test/src/lib.rs index 831b50b117a..7986af92841 100644 --- a/modules/module-test/src/lib.rs +++ b/modules/module-test/src/lib.rs @@ -1,12 +1,10 @@ #![allow(clippy::disallowed_names)] -use std::time::Duration; - +use spacetimedb::log; use spacetimedb::spacetimedb_lib::db::raw_def::v9::TableAccess; use spacetimedb::spacetimedb_lib::{self, bsatn}; use spacetimedb::{ duration, table, ConnectionId, Deserialize, Identity, ReducerContext, SpacetimeType, Table, Timestamp, }; -use spacetimedb::{log, ProcedureContext}; pub type TestAlias = TestA; @@ -439,20 +437,3 @@ fn assert_caller_identity_is_module_identity(ctx: &ReducerContext) { log::info!("Called by the owner {owner}"); } } - -#[spacetimedb::procedure] -fn sleep_one_second(ctx: &mut ProcedureContext) { - let prev_time = ctx.timestamp; - let target = prev_time + Duration::from_secs(1); - ctx.sleep_until(target); - let new_time = ctx.timestamp; - let actual_delta = new_time.duration_since(prev_time).unwrap(); - log::info!("Slept from {prev_time} to {new_time}, a total of {actual_delta:?}"); -} - -#[spacetimedb::procedure] -fn return_value(_ctx: &mut ProcedureContext, foo: u64) -> Baz { - Baz { - field: format!("{foo}"), - } -} diff --git a/sdks/csharp/SpacetimeDB.ClientSDK.csproj b/sdks/csharp/SpacetimeDB.ClientSDK.csproj index b7f81755e20..f1f24620b2f 100644 --- a/sdks/csharp/SpacetimeDB.ClientSDK.csproj +++ b/sdks/csharp/SpacetimeDB.ClientSDK.csproj @@ -16,8 +16,8 @@ logo.png README.md https://github.com/clockworklabs/com.clockworklabs.spacetimedbsdk - 1.6.0 - 1.6.0 + 1.7.0 + 1.7.0 $(DefaultItemExcludes);*~/** packages @@ -25,7 +25,7 @@ - + diff --git a/sdks/csharp/examples~/quickstart-chat/server/StdbModule.csproj b/sdks/csharp/examples~/quickstart-chat/server/StdbModule.csproj index 12710df0560..beac8e96f64 100644 --- a/sdks/csharp/examples~/quickstart-chat/server/StdbModule.csproj +++ b/sdks/csharp/examples~/quickstart-chat/server/StdbModule.csproj @@ -14,7 +14,7 @@ - + diff --git a/sdks/csharp/examples~/regression-tests/server/StdbModule.csproj b/sdks/csharp/examples~/regression-tests/server/StdbModule.csproj index 4a504b099c8..6121a5ae7f9 100644 --- a/sdks/csharp/examples~/regression-tests/server/StdbModule.csproj +++ b/sdks/csharp/examples~/regression-tests/server/StdbModule.csproj @@ -8,7 +8,7 @@ - + diff --git a/sdks/csharp/package.json b/sdks/csharp/package.json index 3219e255131..68dfccff873 100644 --- a/sdks/csharp/package.json +++ b/sdks/csharp/package.json @@ -1,7 +1,7 @@ { "name": "com.clockworklabs.spacetimedbsdk", "displayName": "SpacetimeDB SDK", - "version": "1.6.0", + "version": "1.7.0", "description": "The SpacetimeDB Client SDK is a software development kit (SDK) designed to interact with and manipulate SpacetimeDB modules..", "keywords": [], "author": { diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0.meta b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0.meta rename to sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers.meta b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/analyzers.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers.meta rename to sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/analyzers.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet.meta b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/analyzers/dotnet.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet.meta rename to sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/analyzers/dotnet.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs.meta b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/analyzers/dotnet/cs.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs.meta rename to sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/analyzers/dotnet/cs.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll similarity index 98% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll rename to sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll index 1db1c1f2115..d74a8cb7231 100755 Binary files a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll and b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll differ diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta rename to sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib.meta b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/lib.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib.meta rename to sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/lib.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1.meta b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/lib/netstandard2.1.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1.meta rename to sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/lib/netstandard2.1.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll similarity index 97% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll rename to sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll index b66c06e7b8a..6fc14119dbf 100755 Binary files a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll and b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll differ diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll.meta b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll.meta rename to sdks/csharp/packages/spacetimedb.bsatn.runtime/1.7.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll.meta diff --git a/sdks/rust/src/db_connection.rs b/sdks/rust/src/db_connection.rs index b7f507f263b..aa5c3f6fb27 100644 --- a/sdks/rust/src/db_connection.rs +++ b/sdks/rust/src/db_connection.rs @@ -1184,8 +1184,7 @@ async fn parse_loop( error: e.error.to_string(), }, ws::ServerMessage::SubscribeApplied(_) => unreachable!("Rust client SDK never sends `SubscribeSingle`, but received a `SubscribeApplied` from the host... huh?"), - ws::ServerMessage::UnsubscribeApplied(_) => unreachable!("Rust client SDK never sends `UnsubscribeSingle`, but received a `UnsubscribeApplied` from the host... huh?"), - ws::ServerMessage::ProcedureResult(_) => todo!("Rust client SDK procedure support"), + ws::ServerMessage::UnsubscribeApplied(_) => unreachable!("Rust client SDK never sends `UnsubscribeSingle`, but received a `UnsubscribeApplied` from the host... huh?") }) .expect("Failed to send ParsedMessage to main thread"); } diff --git a/sdks/rust/tests/connect_disconnect_client/src/module_bindings/mod.rs b/sdks/rust/tests/connect_disconnect_client/src/module_bindings/mod.rs index 4c763beb353..77cb1bb77fd 100644 --- a/sdks/rust/tests/connect_disconnect_client/src/module_bindings/mod.rs +++ b/sdks/rust/tests/connect_disconnect_client/src/module_bindings/mod.rs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.6.0 (commit a952ba57372dfbee37da9d079a3f86704ca35611). +// This was generated using spacetimedb cli version 1.6.0 (commit 11eb6b1cc9098d6b3727cef255b0c6b3dbf1df97). #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; diff --git a/sdks/rust/tests/test-client/src/module_bindings/mod.rs b/sdks/rust/tests/test-client/src/module_bindings/mod.rs index 234638a8f28..f7eb3a169d9 100644 --- a/sdks/rust/tests/test-client/src/module_bindings/mod.rs +++ b/sdks/rust/tests/test-client/src/module_bindings/mod.rs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.6.0 (commit a952ba57372dfbee37da9d079a3f86704ca35611). +// This was generated using spacetimedb cli version 1.6.0 (commit 11eb6b1cc9098d6b3727cef255b0c6b3dbf1df97). #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; diff --git a/smoketests/tests/quickstart.py b/smoketests/tests/quickstart.py index fdcf3b7a4e5..0d729f2f2d2 100644 --- a/smoketests/tests/quickstart.py +++ b/smoketests/tests/quickstart.py @@ -301,6 +301,19 @@ def sdk_setup(self, path: Path): source_dir=(STDB_DIR / "crates/bindings-csharp/BSATN.Runtime").absolute(), build_subdir="bin/Release" ) + # This one is only needed because the regression-tests subdir uses it + override_nuget_package( + project_dir=STDB_DIR/"sdks/csharp", + package="SpacetimeDB.Runtime", + source_dir=(STDB_DIR / "crates/bindings-csharp/Runtime").absolute(), + build_subdir="bin/Release" + ) + override_nuget_package( + project_dir=path, + package="SpacetimeDB.BSATN.Runtime", + source_dir=(STDB_DIR / "crates/bindings-csharp/BSATN.Runtime").absolute(), + build_subdir="bin/Release" + ) override_nuget_package( project_dir=path, package="SpacetimeDB.ClientSDK", @@ -316,6 +329,12 @@ def server_postprocess(self, server_path: Path): source_dir=(STDB_DIR / "crates/bindings-csharp/Runtime").absolute(), build_subdir="bin/Release" ) + override_nuget_package( + project_dir=server_path, + package="SpacetimeDB.BSATN.Runtime", + source_dir=(STDB_DIR / "crates/bindings-csharp/BSATN.Runtime").absolute(), + build_subdir="bin/Release" + ) def test_quickstart(self): """Run the C# quickstart guides for server and client.""" diff --git a/smoketests/tests/views.py b/smoketests/tests/views.py deleted file mode 100644 index 49e97f27906..00000000000 --- a/smoketests/tests/views.py +++ /dev/null @@ -1,103 +0,0 @@ -from .. import Smoketest, random_string - - -class Views(Smoketest): - MODULE_CODE = """ -use spacetimedb::ViewContext; - -#[derive(Copy, Clone)] -#[spacetimedb::table(name = player_state)] -pub struct PlayerState { - #[primary_key] - id: u64, - #[index(btree)] - level: u64, -} - -#[spacetimedb::view(name = player, public)] -pub fn player(ctx: &ViewContext, id: u64) -> Option { - ctx.db.player_state().id().find(id) -} -""" - - def assertSql(self, sql, expected): - self.maxDiff = None - sql_out = self.spacetime("sql", self.database_identity, sql) - sql_out = "\n".join([line.rstrip() for line in sql_out.splitlines()]) - expected = "\n".join([line.rstrip() for line in expected.splitlines()]) - self.assertMultiLineEqual(sql_out, expected) - - def test_st_view_tables(self): - """This test asserts that views populate the st_view_* system tables""" - - self.assertSql("SELECT * FROM st_view", """\ - view_id | view_name | table_id | is_public | is_anonymous ----------+-----------+---------------+-----------+-------------- - 4096 | "player" | (some = 4097) | true | false -""") - - self.assertSql("SELECT * FROM st_view_param", """\ - view_id | param_pos | param_name | param_type ----------+-----------+------------+------------ - 4096 | 0 | "id" | 0x0d -""") - - self.assertSql("SELECT * FROM st_view_column", """\ - view_id | col_pos | col_name | col_type ----------+---------+----------+---------- - 4096 | 0 | "id" | 0x0d - 4096 | 1 | "level" | 0x0d -""") - -class FailPublish(Smoketest): - AUTOPUBLISH = False - - MODULE_CODE_BROKEN_NAMESPACE = """ -use spacetimedb::ViewContext; - -#[spacetimedb::table(name = person, public)] -pub struct Person { - name: String, -} - -#[spacetimedb::view(name = person, public)] -pub fn person(ctx: &ViewContext) -> Option { - None -} -""" - - MODULE_CODE_BROKEN_RETURN_TYPE = """ -use spacetimedb::{SpacetimeType, ViewContext}; - -#[derive(SpacetimeType)] -pub enum ABC { - A, - B, - C, -} - -#[spacetimedb::view(name = person, public)] -pub fn person(ctx: &ViewContext) -> Option { - None -} -""" - - def test_fail_publish_namespace_collision(self): - """Publishing a module should fail if a table and view have the same name""" - - name = random_string() - - self.write_module_code(self.MODULE_CODE_BROKEN_NAMESPACE) - - with self.assertRaises(Exception): - self.publish_module(name) - - def test_fail_publish_wrong_return_type(self): - """Publishing a module should fail if the inner return type is not a product type""" - - name = random_string() - - self.write_module_code(self.MODULE_CODE_BROKEN_RETURN_TYPE) - - with self.assertRaises(Exception): - self.publish_module(name)