diff --git a/collector/compile-benchmarks/README.md b/collector/compile-benchmarks/README.md index 552694ea1..3a560acb8 100644 --- a/collector/compile-benchmarks/README.md +++ b/collector/compile-benchmarks/README.md @@ -25,10 +25,8 @@ They mostly consist of real-world crates. - **cranelift-codegen-0.82.1**: The largest crate from a code generator. Used by wasmtime. Stresses obligation processing. - **cranelift-codegen-0.119.0**: The largest crate from a code generator. Used by wasmtime. Stresses obligation processing. -- **diesel-1.4.8**: A type safe SQL query builder. Utilizes the type system to - ensure a lot of invariants. Stresses anything related to resolving - trait bounds, by having a lot of trait impls for a large number of different - types. +- **diesel-1.4.8**: A type-safe SQL query builder. Utilizes the type system to ensure a lot of invariants. Stresses anything related to resolving trait bounds, by having a lot of trait impls for a large number of different types. +- **diesel-2.2:10**: A type-safe SQL query builder. Utilizes the type system to ensure a lot of invariants. Stresses anything related to resolving trait bounds, by having a lot of trait impls for a large number of different types. - **exa-0.10.1**: An `ls` replacement. A widely-used utility, and a binary crate. - **helloworld**: A trivial program. Gives a lower bound on compile time. diff --git a/collector/compile-benchmarks/REUSE.toml b/collector/compile-benchmarks/REUSE.toml index bc9879f55..4b883d42b 100644 --- a/collector/compile-benchmarks/REUSE.toml +++ b/collector/compile-benchmarks/REUSE.toml @@ -72,6 +72,11 @@ path = "diesel-1.4.8/**" SPDX-FileCopyrightText = "diesel contributors" SPDX-License-Identifier = "MIT OR Apache-2.0" +[[annotations]] +path = "diesel-2.2.10/**" +SPDX-FileCopyrightText = "diesel contributors" +SPDX-License-Identifier = "MIT OR Apache-2.0" + [[annotations]] path = "encoding/**" SPDX-FileCopyrightText = "encoding contributors" diff --git a/collector/compile-benchmarks/diesel-2.2.10/.cargo_vcs_info.json b/collector/compile-benchmarks/diesel-2.2.10/.cargo_vcs_info.json new file mode 100644 index 000000000..bd48b978d --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "2a6752b2b91c55b9968b7b8e3df44d5ff7eca166" + }, + "path_in_vcs": "diesel" +} \ No newline at end of file diff --git a/collector/compile-benchmarks/diesel-2.2.10/0-println.patch b/collector/compile-benchmarks/diesel-2.2.10/0-println.patch new file mode 100644 index 000000000..1e577c05e --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/0-println.patch @@ -0,0 +1,12 @@ +diff --git a/src/query_dsl/mod.rs b/src/query_dsl/mod.rs +index 8be915e7..6da19ecc 100644 +--- a/src/query_dsl/mod.rs ++++ b/src/query_dsl/mod.rs +@@ -119,6 +119,7 @@ pub trait QueryDsl: Sized { + where + Self: methods::DistinctDsl, + { ++ println!("testing"); + methods::DistinctDsl::distinct(self) + } + diff --git a/collector/compile-benchmarks/diesel-2.2.10/Cargo.lock b/collector/compile-benchmarks/diesel-2.2.10/Cargo.lock new file mode 100644 index 000000000..e724ba638 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/Cargo.lock @@ -0,0 +1,1337 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bigdecimal" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a22f228ab7a1b23027ccc6c350b72868017af7ea8356fbdf19f8d991c690013" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-link", +] + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "diesel" +version = "2.2.10" +dependencies = [ + "bigdecimal", + "bitflags", + "byteorder", + "cfg-if", + "chrono", + "diesel_derives", + "dotenvy", + "ipnet", + "ipnetwork", + "itoa", + "libc", + "libsqlite3-sys", + "mysqlclient-src", + "mysqlclient-sys", + "num-bigint", + "num-integer", + "num-traits", + "percent-encoding", + "pq-src", + "pq-sys", + "quickcheck", + "r2d2", + "serde_json", + "tempfile", + "time", + "url", + "uuid", +] + +[[package]] +name = "diesel_derives" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68d4216021b3ea446fd2047f5c8f8fe6e98af34508a254a01e4d6bc1e844f84d" +dependencies = [ + "diesel_table_macro_syntax", + "dsl_auto_type", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" +dependencies = [ + "syn", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "dsl_auto_type" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ae9aca7527f85f26dd76483eb38533fd84bd571065da1739656ef71c5ff5b" +dependencies = [ + "darling", + "either", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "errno" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "ipnetwork" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf370abdafd54d13e54a620e8c3e1145f28e46cc9d704bc6d94414559df41763" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libm" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d154aedcb0b7a1e91a3fddbe2a8350d3da76ac9d0220ae20da5c7aa8269612" + +[[package]] +name = "libsqlite3-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947e6816f7825b2b45027c2c32e7085da9934defa535de4a6a46b10a4d5257fa" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mysqlclient-src" +version = "0.1.3+9.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebac03ab8be41015fec2d2435929e4b19cd6133a624ac777409993f238768fd" +dependencies = [ + "cmake", + "link-cplusplus", + "openssl-src", + "openssl-sys", +] + +[[package]] +name = "mysqlclient-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe45ac04fb301fa824ce6a3a0ef0171b52e92c6d25973c085cece9d88727bd7" +dependencies = [ + "mysqlclient-src", + "pkg-config", + "semver", + "vcpkg", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl-src" +version = "300.5.0+3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "pq-src" +version = "0.3.6+libpq-17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5b04f06b634725218f5f2d0423e889c82f07890dbb859e6cbfbc7e08d49bde" +dependencies = [ + "cc", + "openssl-sys", +] + +[[package]] +name = "pq-sys" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c852911b98f5981956037b2ca976660612e548986c30af075e753107bc3400" +dependencies = [ + "libc", + "pq-src", + "vcpkg", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "env_logger", + "log", + "rand", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "r2d2" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "redox_syscall" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustix" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +dependencies = [ + "fastrand", + "getrandom 0.3.2", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/collector/compile-benchmarks/diesel-2.2.10/Cargo.toml b/collector/compile-benchmarks/diesel-2.2.10/Cargo.toml new file mode 100644 index 000000000..d71437285 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/Cargo.toml @@ -0,0 +1,274 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.78.0" +name = "diesel" +version = "2.2.10" +build = false +include = [ + "src/**/*.rs", + "tests/**/*.rs", + "LICENSE-*", + "README.md", + "src/sqlite/connection/diesel_manage_updated_at.sql", + "src/migration/setup_migration_table.sql", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A safe, extensible ORM and Query Builder for PostgreSQL, SQLite, and MySQL" +homepage = "https://diesel.rs" +documentation = "https://docs.rs/diesel/" +readme = "README.md" +keywords = [ + "orm", + "database", + "sql", +] +categories = ["database"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/diesel-rs/diesel" + +[package.metadata.docs.rs] +features = [ + "postgres", + "mysql", + "sqlite", + "extras", +] +no-default-features = true +rustc-args = [ + "--cfg", + "docsrs", +] +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[features] +128-column-tables = [ + "64-column-tables", + "diesel_derives/128-column-tables", +] +32-column-tables = ["diesel_derives/32-column-tables"] +64-column-tables = [ + "32-column-tables", + "diesel_derives/64-column-tables", +] +__with_asan_tests = [ + "libsqlite3-sys?/bundled", + "libsqlite3-sys?/with-asan", + "pq-sys?/bundled", + "pq-src?/with-asan", + "mysqlclient-sys?/bundled", + "mysqlclient-src?/with-asan", +] +chrono = [ + "diesel_derives/chrono", + "dep:chrono", +] +default = [ + "with-deprecated", + "32-column-tables", +] +extras = [ + "chrono", + "time", + "serde_json", + "uuid", + "network-address", + "numeric", + "r2d2", +] +huge-tables = ["64-column-tables"] +i-implement-a-third-party-backend-and-opt-into-breaking-changes = [] +ipnet-address = [ + "dep:ipnet", + "dep:libc", +] +large-tables = ["32-column-tables"] +mysql = [ + "dep:mysqlclient-sys", + "dep:url", + "dep:percent-encoding", + "dep:bitflags", + "mysql_backend", +] +mysql_backend = [ + "diesel_derives/mysql", + "dep:byteorder", +] +network-address = [ + "dep:ipnetwork", + "dep:libc", +] +numeric = [ + "dep:num-bigint", + "dep:bigdecimal", + "dep:num-traits", + "dep:num-integer", +] +postgres = [ + "dep:pq-sys", + "postgres_backend", +] +postgres_backend = [ + "diesel_derives/postgres", + "dep:bitflags", + "dep:byteorder", + "dep:itoa", +] +r2d2 = [ + "diesel_derives/r2d2", + "dep:r2d2", +] +returning_clauses_for_sqlite_3_35 = [] +serde_json = ["dep:serde_json"] +sqlite = [ + "dep:libsqlite3-sys", + "diesel_derives/sqlite", + "time?/formatting", + "time?/parsing", +] +time = [ + "diesel_derives/time", + "dep:time", +] +unstable = ["diesel_derives/nightly"] +uuid = ["dep:uuid"] +with-deprecated = ["diesel_derives/with-deprecated"] +without-deprecated = ["diesel_derives/without-deprecated"] + +[lib] +name = "diesel" +path = "src/lib.rs" + +[dependencies.bigdecimal] +version = ">=0.0.13, < 0.5.0" +optional = true + +[dependencies.bitflags] +version = "2.0.0" +optional = true + +[dependencies.byteorder] +version = "1.0" +optional = true + +[dependencies.chrono] +version = "0.4.20" +features = [ + "clock", + "std", +] +optional = true +default-features = false + +[dependencies.diesel_derives] +version = "~2.2.0" + +[dependencies.ipnet] +version = "2.5.0" +optional = true + +[dependencies.ipnetwork] +version = ">=0.12.2, <0.22.0" +optional = true + +[dependencies.itoa] +version = "1.0.0" +optional = true + +[dependencies.libc] +version = "0.2.0" +optional = true + +[dependencies.libsqlite3-sys] +version = ">=0.17.2, <0.34.0" +features = ["bundled_bindings"] +optional = true + +[dependencies.mysqlclient-src] +version = "0.1.0" +optional = true + +[dependencies.mysqlclient-sys] +version = ">=0.2.5, <0.5.0" +optional = true + +[dependencies.num-bigint] +version = ">=0.2.0, <0.5.0" +optional = true + +[dependencies.num-integer] +version = "0.1.39" +optional = true + +[dependencies.num-traits] +version = "0.2.0" +optional = true + +[dependencies.percent-encoding] +version = "2.1.0" +optional = true + +[dependencies.pq-src] +version = "0.3" +optional = true + +[dependencies.pq-sys] +version = ">=0.4.0, <0.8.0" +optional = true + +[dependencies.quickcheck] +version = "1.0.3" +optional = true + +[dependencies.r2d2] +version = ">= 0.8.2, < 0.9.0" +optional = true + +[dependencies.serde_json] +version = ">=0.8.0, <2.0" +optional = true + +[dependencies.time] +version = "0.3.9" +features = ["macros"] +optional = true + +[dependencies.url] +version = "2.1.0" +optional = true + +[dependencies.uuid] +version = ">=0.7.0, <2.0.0" +optional = true + +[dev-dependencies.cfg-if] +version = "1" + +[dev-dependencies.dotenvy] +version = "0.15" + +[dev-dependencies.ipnetwork] +version = ">=0.12.2, <0.22.0" + +[dev-dependencies.quickcheck] +version = "1.0.3" + +[dev-dependencies.tempfile] +version = "3.10.1" diff --git a/collector/compile-benchmarks/diesel-2.2.10/Cargo.toml.orig b/collector/compile-benchmarks/diesel-2.2.10/Cargo.toml.orig new file mode 100644 index 000000000..48e9d8a94 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/Cargo.toml.orig @@ -0,0 +1,98 @@ +[package] +name = "diesel" +version = "2.2.10" +license = "MIT OR Apache-2.0" +description = "A safe, extensible ORM and Query Builder for PostgreSQL, SQLite, and MySQL" +readme = "README.md" +documentation = "https://docs.rs/diesel/" +homepage = "https://diesel.rs" +repository = "https://github.com/diesel-rs/diesel" +keywords = ["orm", "database", "sql"] +categories = ["database"] +edition = "2021" +rust-version.workspace = true +include = [ + "src/**/*.rs", + "tests/**/*.rs", + "LICENSE-*", + "README.md", + "src/sqlite/connection/diesel_manage_updated_at.sql", + "src/migration/setup_migration_table.sql", +] + +[dependencies] +byteorder = { version = "1.0", optional = true } +chrono = { version = "0.4.20", optional = true, default-features = false, features = ["clock", "std"] } +libc = { version = "0.2.0", optional = true } +libsqlite3-sys = { version = ">=0.17.2, <0.34.0", optional = true, features = ["bundled_bindings"] } +mysqlclient-sys = { version = ">=0.2.5, <0.5.0", optional = true } +mysqlclient-src = { version = "0.1.0", optional = true } +pq-sys = { version = ">=0.4.0, <0.8.0", optional = true } +pq-src = { version = "0.3", optional = true } +quickcheck = { version = "1.0.3", optional = true } +serde_json = { version = ">=0.8.0, <2.0", optional = true } +url = { version = "2.1.0", optional = true } +percent-encoding = { version = "2.1.0", optional = true } +uuid = { version = ">=0.7.0, <2.0.0", optional = true } +ipnetwork = { version = ">=0.12.2, <0.22.0", optional = true } +ipnet = { version = "2.5.0", optional = true } +num-bigint = { version = ">=0.2.0, <0.5.0", optional = true } +num-traits = { version = "0.2.0", optional = true } +num-integer = { version = "0.1.39", optional = true } +bigdecimal = { version = ">=0.0.13, < 0.5.0", optional = true } +bitflags = { version = "2.0.0", optional = true } +r2d2 = { version = ">= 0.8.2, < 0.9.0", optional = true } +itoa = { version = "1.0.0", optional = true } +time = { version = "0.3.9", optional = true, features = ["macros"] } + +[dependencies.diesel_derives] +version = "~2.2.0" +path = "../diesel_derives" + +[dev-dependencies] +cfg-if = "1" +dotenvy = "0.15" +ipnetwork = ">=0.12.2, <0.22.0" +quickcheck = "1.0.3" +tempfile = "3.10.1" + +[features] +default = ["with-deprecated", "32-column-tables"] +extras = ["chrono", "time", "serde_json", "uuid", "network-address", "numeric", "r2d2"] +unstable = ["diesel_derives/nightly"] +large-tables = ["32-column-tables"] +huge-tables = ["64-column-tables"] +32-column-tables = ["diesel_derives/32-column-tables"] +64-column-tables = ["32-column-tables", "diesel_derives/64-column-tables"] +128-column-tables = ["64-column-tables", "diesel_derives/128-column-tables"] +postgres = ["dep:pq-sys", "postgres_backend"] +sqlite = ["dep:libsqlite3-sys", "diesel_derives/sqlite", "time?/formatting", "time?/parsing"] +mysql = ["dep:mysqlclient-sys", "dep:url", "dep:percent-encoding", "dep:bitflags", "mysql_backend"] +without-deprecated = ["diesel_derives/without-deprecated"] +with-deprecated = ["diesel_derives/with-deprecated"] +network-address = ["dep:ipnetwork", "dep:libc"] +ipnet-address = ["dep:ipnet", "dep:libc"] +numeric = ["dep:num-bigint", "dep:bigdecimal", "dep:num-traits", "dep:num-integer"] +postgres_backend = ["diesel_derives/postgres", "dep:bitflags", "dep:byteorder", "dep:itoa"] +mysql_backend = ["diesel_derives/mysql", "dep:byteorder"] +returning_clauses_for_sqlite_3_35 = [] +i-implement-a-third-party-backend-and-opt-into-breaking-changes = [] +r2d2 = ["diesel_derives/r2d2", "dep:r2d2"] +chrono = ["diesel_derives/chrono", "dep:chrono"] +time = ["diesel_derives/time", "dep:time"] +uuid = ["dep:uuid"] +serde_json = ["dep:serde_json"] +__with_asan_tests = [ + "libsqlite3-sys?/bundled", + "libsqlite3-sys?/with-asan", + "pq-sys?/bundled", + "pq-src?/with-asan", + "mysqlclient-sys?/bundled", + "mysqlclient-src?/with-asan", +] + +[package.metadata.docs.rs] +features = ["postgres", "mysql", "sqlite", "extras"] +no-default-features = true +rustc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/collector/compile-benchmarks/diesel-2.2.10/LICENSE-APACHE b/collector/compile-benchmarks/diesel-2.2.10/LICENSE-APACHE new file mode 100644 index 000000000..28c74952e --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/LICENSE-APACHE @@ -0,0 +1,190 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015-2021 Sean Griffin, 2018-2021 Diesel Core Team + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/collector/compile-benchmarks/diesel-2.2.10/LICENSE-MIT b/collector/compile-benchmarks/diesel-2.2.10/LICENSE-MIT new file mode 100644 index 000000000..b8643b354 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +2015-2021 Sean Griffin, 2018-2021 Diesel Core Team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/collector/compile-benchmarks/diesel-2.2.10/README.md b/collector/compile-benchmarks/diesel-2.2.10/README.md new file mode 100644 index 000000000..64c5d7538 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/README.md @@ -0,0 +1,265 @@ +[![diesel logo](https://diesel.rs/assets/images/diesel_logo_stacked_black.png)](https://diesel.rs) + +# A safe, extensible ORM and Query Builder for Rust + +[![Build Status](https://github.com/diesel-rs/diesel/workflows/CI%20Tests/badge.svg)](https://github.com/diesel-rs/diesel/actions?query=workflow%3A%22CI+Tests%22+branch%3Amaster) +[![Crates.io](https://img.shields.io/crates/v/diesel.svg)](https://crates.io/crates/diesel) + +API Documentation: [latest release](https://docs.rs/diesel) – [master branch](https://docs.diesel.rs/master/diesel/index.html) + +[Homepage](https://diesel.rs) + +Diesel gets rid of the boilerplate for database interaction and eliminates +runtime errors without sacrificing performance. It takes full advantage of +Rust's type system to create a low overhead query builder that "feels like +Rust." + +Supported databases: +1. [PostgreSQL](https://docs.diesel.rs/master/diesel/pg/index.html) +2. [MySQL](https://docs.diesel.rs/master/diesel/mysql/index.html) +3. [SQLite](https://docs.diesel.rs/master/diesel/sqlite/index.html) + +You can configure the database backend in `Cargo.toml`: + +```toml +[dependencies] +diesel = { version = "", features = [""] } +``` + +## Getting Started + +Find our extensive Getting Started tutorial at +[https://diesel.rs/guides/getting-started](https://diesel.rs/guides/getting-started). +Guides on more specific features are coming soon. + +## Getting help + +If you run into problems, Diesel has a very active Gitter room. +You can come ask for help at in our [GitHub Discussions](https://github.com/diesel-rs/diesel/discussions) forum. +This is also the right place to propose new features or show your applications. + +## Usage + +### Simple queries + +Simple queries are a complete breeze. Loading all users from a database: + +```rust +users::table.load(&mut connection) +``` + +Executed SQL: + +```sql +SELECT * FROM users; +``` + +Loading all the posts for a user: + +``` rust +Post::belonging_to(user).load(&mut connection) +``` + +Executed SQL: + +```sql +SELECT * FROM posts WHERE user_id = 1; +``` + +### Complex queries + +Diesel's powerful query builder helps you construct queries as simple or complex as +you need, at zero cost. + +```rust +let versions = Version::belonging_to(krate) + .select(id) + .order(num.desc()) + .limit(5); +let downloads = version_downloads + .filter(date.gt(now - 90.days())) + .filter(version_id.eq_any(versions)) + .order(date) + .load::(&mut conn)?; +``` + +Executed SQL: + +```sql +SELECT version_downloads.* + WHERE date > (NOW() - '90 days') + AND version_id = ANY( + SELECT id FROM versions + WHERE crate_id = 1 + ORDER BY num DESC + LIMIT 5 + ) + ORDER BY date +``` + +### Less boilerplate + +Diesel codegen generates boilerplate for you. It lets you focus on your business logic, not mapping to and from SQL rows. + +That means you can write this: + +```rust +#[derive(Queryable, Selectable)] +#[diesel(table_name = downloads)] +pub struct Download { + id: i32, + version_id: i32, + downloads: i32, + counted: i32, + date: SystemTime, +} +``` + +Instead of this without Diesel: + +```rust +pub struct Download { + id: i32, + version_id: i32, + downloads: i32, + counted: i32, + date: SystemTime, +} + +impl Download { + fn from_row(row: &Row) -> Download { + Download { + id: row.get("id"), + version_id: row.get("version_id"), + downloads: row.get("downloads"), + counted: row.get("counted"), + date: row.get("date"), + } + } +} +``` + +### Inserting data + +It's not just about reading data. Diesel makes it easy to use structs for new records. + +```rust +#[derive(Insertable)] +#[diesel(table_name = users)] +struct NewUser<'a> { + name: &'a str, + hair_color: Option<&'a str>, +} + +let new_users = vec![ + NewUser { name: "Sean", hair_color: Some("Black") }, + NewUser { name: "Gordon", hair_color: None }, +]; + +insert_into(users) + .values(&new_users) + .execute(&mut connection); +``` + +Executed SQL: + +```sql +INSERT INTO users (name, hair_color) VALUES + ('Sean', 'Black'), + ('Gordon', DEFAULT) +``` + +If you need data from the rows you inserted, just change `execute` to `get_result` or `get_results`. Diesel will take care of the rest. + +```rust +let new_users = vec![ + NewUser { name: "Sean", hair_color: Some("Black") }, + NewUser { name: "Gordon", hair_color: None }, +]; + +let inserted_users = insert_into(users) + .values(&new_users) + .get_results::(&mut connection); +``` + +Executed SQL: + +```sql +INSERT INTO users (name, hair_color) VALUES + ('Sean', 'Black'), + ('Gordon', DEFAULT) + RETURNING * +``` + +### Updating data + +Diesel's codegen can generate several ways to update a row, letting you encapsulate your logic in the way that makes sense for your app. + +Modifying a struct: + +```rust +post.published = true; +post.save_changes(&mut connection); +``` + +One-off batch changes: + +```rust +update(users.filter(email.like("%@spammer.com"))) + .set(banned.eq(true)) + .execute(&mut connection) +``` + +Using a struct for encapsulation: + +```rust +update(Settings::belonging_to(current_user)) + .set(&settings_form) + .execute(&mut connection) +``` + +### Raw SQL + +There will always be certain queries that are just easier to write as raw SQL, or can't be expressed with the query builder. Even in these cases, Diesel provides an easy to use API for writing raw SQL. + +```rust +#[derive(QueryableByName)] +#[diesel(table_name = users)] +struct User { + id: i32, + name: String, + organization_id: i32, +} + +// Using `include_str!` allows us to keep the SQL in a +// separate file, where our editor can give us SQL specific +// syntax highlighting. +sql_query(include_str!("complex_users_by_organization.sql")) + .bind::(organization_id) + .bind::(offset) + .bind::(limit) + .load::(&mut conn)?; +``` + +## Code of conduct + +Anyone who interacts with Diesel in any space, including but not limited to +this GitHub repository, must follow our [code of conduct](https://github.com/diesel-rs/diesel/blob/master/code_of_conduct.md). + +## License + +Licensed under either of these: + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + https://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or + https://opensource.org/licenses/MIT) + +### Contributing + +Before contributing, please read the [contributors guide](https://github.com/diesel-rs/diesel/blob/master/CONTRIBUTING.md) +for useful information about setting up Diesel locally, coding style and common abbreviations. + +Unless you explicitly state otherwise, any contribution you intentionally submit +for inclusion in the work, as defined in the Apache-2.0 license, shall be +dual-licensed as above, without any additional terms or conditions. diff --git a/collector/compile-benchmarks/diesel-2.2.10/perf-config.json b/collector/compile-benchmarks/diesel-2.2.10/perf-config.json new file mode 100644 index 000000000..710581fa8 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/perf-config.json @@ -0,0 +1,4 @@ +{ + "artifact": "library", + "category": "primary" +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/associations/belongs_to.rs b/collector/compile-benchmarks/diesel-2.2.10/src/associations/belongs_to.rs new file mode 100644 index 000000000..cf188fdd5 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/associations/belongs_to.rs @@ -0,0 +1,200 @@ +use super::HasTable; +use crate::dsl::{Eq, EqAny, Filter, FindBy}; +use crate::expression::array_comparison::AsInExpression; +use crate::expression::AsExpression; +use crate::prelude::*; +use crate::query_dsl::methods::FilterDsl; +use crate::sql_types::SqlType; + +use std::borrow::Borrow; +use std::hash::Hash; + +/// Indicates that a type belongs to `Parent` +/// +/// Specifically, this means that this struct has fields +/// which correspond to the primary key of `Parent`. +/// This implies that a foreign key relationship exists on the tables. +/// +/// This trait is not capable of supporting composite foreign keys +pub trait BelongsTo { + /// The foreign key of this struct + type ForeignKey: Hash + ::std::cmp::Eq; + /// The database column representing the foreign key + /// of the table this struct represents + type ForeignKeyColumn: Column; + + /// Returns the foreign key for `self` + fn foreign_key(&self) -> Option<&Self::ForeignKey>; + /// Returns the foreign key column of this struct's table + fn foreign_key_column() -> Self::ForeignKeyColumn; +} + +/// The `grouped_by` function groups records by their parent. +/// +/// `grouped_by` is called on a `Vec` with a `&[Parent]`. +/// The return value will be `Vec>` indexed to match their parent. +/// Or to put it another way, the returned data can be passed to `zip`, +/// and it will be combined with its parent. +/// This function does not generate a `GROUP BY` SQL statement, +/// as it operates on data structures already loaded from the database +/// +/// **Child** refers to the "many" part of a "one to many" relationship. It "belongs to" its parent +/// **Parent** refers to the "one" part of a "one to many" relationship and can "have many" children. +/// The child always has a foreign key, which refers to its parent's primary key. +/// In the following relationship, User has many Posts, +/// so User is the parent and Posts are children. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::{posts, users}; +/// # +/// # #[derive(Identifiable, Queryable, PartialEq, Debug)] +/// # pub struct User { +/// # id: i32, +/// # name: String, +/// # } +/// # +/// # #[derive(Debug, PartialEq)] +/// # #[derive(Identifiable, Queryable, Associations)] +/// # #[diesel(belongs_to(User))] +/// # pub struct Post { +/// # id: i32, +/// # user_id: i32, +/// # title: String, +/// # } +/// # +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = &mut establish_connection(); +/// let users = users::table.load::(connection)?; +/// let posts = Post::belonging_to(&users) +/// .load::(connection)? +/// .grouped_by(&users); +/// let data = users.into_iter().zip(posts).collect::>(); +/// +/// let expected_data = vec![ +/// ( +/// User { id: 1, name: "Sean".into() }, +/// vec![ +/// Post { id: 1, user_id: 1, title: "My first post".into() }, +/// Post { id: 2, user_id: 1, title: "About Rust".into() }, +/// ], +/// ), +/// ( +/// User { id: 2, name: "Tess".into() }, +/// vec![ +/// Post { id: 3, user_id: 2, title: "My first post too".into() }, +/// ], +/// ), +/// ]; +/// +/// assert_eq!(expected_data, data); +/// # Ok(()) +/// # } +/// ``` +/// +/// See [the module documentation] for more examples +/// +/// [the module documentation]: super +pub trait GroupedBy<'a, Parent>: IntoIterator + Sized { + /// See the trait documentation. + fn grouped_by(self, parents: &'a [Parent]) -> Vec>; +} + +type Id = ::Id; + +impl<'a, Parent: 'a, Child, Iter> GroupedBy<'a, Parent> for Iter +where + Iter: IntoIterator, + Child: BelongsTo, + &'a Parent: Identifiable, + Id<&'a Parent>: Borrow, +{ + fn grouped_by(self, parents: &'a [Parent]) -> Vec> { + use std::collections::HashMap; + + let id_indices: HashMap<_, _> = parents + .iter() + .enumerate() + .map(|(i, u)| (u.id(), i)) + .collect(); + let mut result = parents.iter().map(|_| Vec::new()).collect::>(); + for child in self { + if let Some(index) = child.foreign_key().map(|i| id_indices[i]) { + result[index].push(child); + } + } + result + } +} + +impl<'a, Parent, Child> BelongingToDsl<&'a Parent> for Child +where + &'a Parent: Identifiable, + Child: HasTable + BelongsTo, + Id<&'a Parent>: AsExpression<::SqlType>, + Child::Table: FilterDsl>>, + Child::ForeignKeyColumn: ExpressionMethods, + ::SqlType: SqlType, +{ + type Output = FindBy>; + + fn belonging_to(parent: &'a Parent) -> Self::Output { + FilterDsl::filter(Child::table(), Child::foreign_key_column().eq(parent.id())) + } +} + +impl<'a, Parent, Child> BelongingToDsl<&'a [Parent]> for Child +where + &'a Parent: Identifiable, + Child: HasTable + BelongsTo, + Vec>: AsInExpression<::SqlType>, + ::Table: FilterDsl>>>, + Child::ForeignKeyColumn: ExpressionMethods, + ::SqlType: SqlType, +{ + type Output = Filter>>>; + + fn belonging_to(parents: &'a [Parent]) -> Self::Output { + let ids = parents.iter().map(Identifiable::id).collect::>(); + FilterDsl::filter(Child::table(), Child::foreign_key_column().eq_any(ids)) + } +} + +impl<'a, Parent, Child> BelongingToDsl<(&'a [Parent], &'a [Parent])> for Child +where + &'a Parent: Identifiable, + Child: HasTable + BelongsTo, + Vec>: AsInExpression<::SqlType>, + ::Table: FilterDsl>>>, + Child::ForeignKeyColumn: ExpressionMethods, + ::SqlType: SqlType, +{ + type Output = Filter>>>; + + fn belonging_to(parents: (&'a [Parent], &'a [Parent])) -> Self::Output { + let ids = parents + .0 + .iter() + .chain(parents.1.iter()) + .map(Identifiable::id) + .collect::>(); + FilterDsl::filter(Child::table(), Child::foreign_key_column().eq_any(ids)) + } +} + +impl<'a, Parent, Child> BelongingToDsl<&'a Vec> for Child +where + Child: BelongingToDsl<&'a [Parent]>, +{ + type Output = Child::Output; + + fn belonging_to(parents: &'a Vec) -> Self::Output { + Self::belonging_to(&**parents) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/associations/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/associations/mod.rs new file mode 100644 index 000000000..7e2b65d95 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/associations/mod.rs @@ -0,0 +1,412 @@ +//! Traits related to relationships between multiple tables. +//! +//! Associations in Diesel are always child-to-parent. +//! You can declare an association between two records with `#[diesel(belongs_to)]`. +//! Unlike other ORMs, Diesel has no concept of `has many` +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! use schema::{posts, users}; +//! +//! #[derive(Identifiable, Queryable, PartialEq, Debug)] +//! #[diesel(table_name = users)] +//! pub struct User { +//! id: i32, +//! name: String, +//! } +//! +//! #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] +//! #[diesel(belongs_to(User))] +//! #[diesel(table_name = posts)] +//! pub struct Post { +//! id: i32, +//! user_id: i32, +//! title: String, +//! } +//! +//! # fn main() { +//! # run_test().unwrap(); +//! # } +//! # +//! # fn run_test() -> QueryResult<()> { +//! # let connection = &mut establish_connection(); +//! # use self::users::dsl::*; +//! let user = users.find(2).get_result::(connection)?; +//! let users_post = Post::belonging_to(&user) +//! .first(connection)?; +//! let expected = Post { id: 3, user_id: 2, title: "My first post too".into() }; +//! assert_eq!(expected, users_post); +//! # Ok(()) +//! # } +//! ``` +//! +//! Note that in addition to the `#[diesel(belongs_to)]` annotation, we also need to +//! `#[derive(Associations)]` +//! +//! `#[diesel(belongs_to)]` is given the name of the struct that represents the parent. +//! Both the parent and child must implement [`Identifiable`]. +//! The struct given to `#[diesel(belongs_to)]` must be in scope, +//! so you will need `use some_module::User` if `User` is defined in another module. +//! +//! If the parent record is generic over lifetimes, they can be written as `'_`. +//! You will also need to wrap the type in quotes until +//! `unrestricted_attribute_tokens` is stable. +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::{posts, users}; +//! # use std::borrow::Cow; +//! # +//! #[derive(Identifiable)] +//! #[diesel(table_name = users)] +//! pub struct User<'a> { +//! id: i32, +//! name: Cow<'a, str>, +//! } +//! +//! #[derive(Associations)] +//! #[diesel(belongs_to(User<'_>))] +//! #[diesel(table_name = posts)] +//! pub struct Post { +//! id: i32, +//! user_id: i32, +//! title: String, +//! } +//! # +//! # fn main() {} +//! ``` +//! +//! +//! By default, Diesel assumes that your foreign keys will follow the convention `table_name_id`. +//! If your foreign key has a different name, +//! you can provide the `foreign_key` argument to `#[diesel(belongs_to)]`. +//! For example, `#[diesel(belongs_to(Foo, foreign_key = mykey))]`. +//! +//! Associated data is typically loaded in multiple queries (one query per table). +//! This is usually more efficient than using a join, +//! especially if 3 or more tables are involved. +//! For most datasets, +//! using a join to load in a single query transmits so much duplicate data +//! that it costs more time than the extra round trip would have. +//! +//! You can load the children for one or more parents using +//! [`belonging_to`] +//! +//! [`belonging_to`]: crate::query_dsl::BelongingToDsl::belonging_to +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::users; +//! # use schema::posts; +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable)] +//! # pub struct User { +//! # id: i32, +//! # name: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)] +//! # #[diesel(belongs_to(User))] +//! # pub struct Post { +//! # id: i32, +//! # user_id: i32, +//! # title: String, +//! # } +//! # +//! # fn main() { +//! # use self::users::dsl::*; +//! # let connection = &mut establish_connection(); +//! # +//! let user = users.find(1).first::(connection).expect("Error loading user"); +//! let post_list = Post::belonging_to(&user) +//! .load::(connection) +//! .expect("Error loading posts"); +//! let expected = vec![ +//! Post { id: 1, user_id: 1, title: "My first post".to_string() }, +//! Post { id: 2, user_id: 1, title: "About Rust".to_string() }, +//! ]; +//! +//! assert_eq!(post_list, expected); +//! # } +//! ``` +//! +//! If you're coming from other ORMs, you'll notice that this design is quite different from most. +//! There you would have an instance method on the parent, or have the children stored somewhere on +//! the posts. This design leads to many problems, including [N+1 query +//! bugs][load-your-entire-database-into-memory-lol], and runtime errors when accessing an +//! association that isn't there. +//! +//! [load-your-entire-database-into-memory-lol]: https://stackoverflow.com/q/97197/1254484 +//! +//! In Diesel, data and its associations are considered to be separate. If you want to pass around +//! a user and all of its posts, that type is `(User, Vec)`. +//! +//! Next lets look at how to load the children for more than one parent record. +//! [`belonging_to`] can be used to load the data, but we'll also need to group it +//! with its parents. For this we use an additional method [`grouped_by`]. +//! +//! [`grouped_by`]: GroupedBy::grouped_by +//! [`belonging_to`]: crate::query_dsl::BelongingToDsl::belonging_to +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::{posts, users}; +//! # +//! # #[derive(Identifiable, Queryable)] +//! # pub struct User { +//! # id: i32, +//! # name: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq)] +//! # #[derive(Identifiable, Queryable, Associations)] +//! # #[diesel(belongs_to(User))] +//! # pub struct Post { +//! # id: i32, +//! # user_id: i32, +//! # title: String, +//! # } +//! # +//! # fn main() { +//! # run_test(); +//! # } +//! # +//! # fn run_test() -> QueryResult<()> { +//! # let connection = &mut establish_connection(); +//! # use self::users::dsl::*; +//! # use self::posts::dsl::{posts, title}; +//! let sean = users.filter(name.eq("Sean")).first::(connection)?; +//! let tess = users.filter(name.eq("Tess")).first::(connection)?; +//! +//! let seans_posts = Post::belonging_to(&sean) +//! .select(title) +//! .load::(connection)?; +//! assert_eq!(vec!["My first post", "About Rust"], seans_posts); +//! +//! // A vec or slice can be passed as well +//! let more_posts = Post::belonging_to(&vec![sean, tess]) +//! .select(title) +//! .load::(connection)?; +//! assert_eq!(vec!["My first post", "About Rust", "My first post too"], more_posts); +//! # Ok(()) +//! # } +//! ``` +//! +//! Typically you will want to group up the children with their parents. +//! In other ORMs, this is often called a `has_many` relationship. +//! Diesel provides support for doing this grouping, once the data has been +//! loaded. +//! +//! [`grouped_by`] is called on a `Vec` with a `&[Parent]`. +//! The return value will be `Vec>` indexed to match their parent. +//! Or to put it another way, the returned data can be passed to `zip`, +//! and it will be combined with its parent. +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::{posts, users}; +//! # +//! # #[derive(Identifiable, Queryable, PartialEq, Debug)] +//! # pub struct User { +//! # id: i32, +//! # name: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq)] +//! # #[derive(Identifiable, Queryable, Associations)] +//! # #[diesel(belongs_to(User))] +//! # pub struct Post { +//! # id: i32, +//! # user_id: i32, +//! # title: String, +//! # } +//! # +//! # fn main() { +//! # run_test(); +//! # } +//! # +//! # fn run_test() -> QueryResult<()> { +//! # let connection = &mut establish_connection(); +//! let users = users::table.load::(connection)?; +//! let posts = Post::belonging_to(&users) +//! .load::(connection)? +//! .grouped_by(&users); +//! let data = users.into_iter().zip(posts).collect::>(); +//! +//! let expected_data = vec![ +//! ( +//! User { id: 1, name: "Sean".into() }, +//! vec![ +//! Post { id: 1, user_id: 1, title: "My first post".into() }, +//! Post { id: 2, user_id: 1, title: "About Rust".into() }, +//! ], +//! ), +//! ( +//! User { id: 2, name: "Tess".into() }, +//! vec![ +//! Post { id: 3, user_id: 2, title: "My first post too".into() }, +//! ], +//! ), +//! ]; +//! +//! assert_eq!(expected_data, data); +//! # Ok(()) +//! # } +//! ``` +//! +//! [`grouped_by`] can be called multiple times +//! if you have multiple children or grandchildren. +//! +//! For example, this code will load some users, +//! all of their posts, +//! and all of the comments on those posts. +//! Explicit type annotations have been added +//! to make each line a bit more clear. +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::{users, posts, comments}; +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable)] +//! # pub struct User { +//! # id: i32, +//! # name: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)] +//! # #[diesel(belongs_to(User))] +//! # pub struct Post { +//! # id: i32, +//! # user_id: i32, +//! # title: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)] +//! # #[diesel(belongs_to(Post))] +//! # pub struct Comment { +//! # id: i32, +//! # post_id: i32, +//! # body: String, +//! # } +//! # +//! # fn main() { +//! # let connection = &mut establish_connection(); +//! # +//! let users: Vec = users::table.load::(connection) +//! .expect("error loading users"); +//! let posts: Vec = Post::belonging_to(&users) +//! .load::(connection) +//! .expect("error loading posts"); +//! let comments: Vec = Comment::belonging_to(&posts) +//! .load::(connection) +//! .expect("Error loading comments"); +//! let grouped_comments: Vec> = comments.grouped_by(&posts); +//! let posts_and_comments: Vec)>> = posts +//! .into_iter() +//! .zip(grouped_comments) +//! .grouped_by(&users); +//! let result: Vec<(User, Vec<(Post, Vec)>)> = users +//! .into_iter() +//! .zip(posts_and_comments) +//! .collect(); +//! let expected = vec![ +//! ( +//! User { id: 1, name: "Sean".to_string() }, +//! vec![ +//! ( +//! Post { id: 1, user_id: 1, title: "My first post".to_string() }, +//! vec![ Comment { id: 1, post_id: 1, body: "Great post".to_string() } ] +//! ), +//! ( +//! Post { id: 2, user_id: 1, title: "About Rust".to_string() }, +//! vec![ +//! Comment { id: 2, post_id: 2, body: "Yay! I am learning Rust".to_string() } +//! ] +//! +//! ) +//! ] +//! ), +//! ( +//! User { id: 2, name: "Tess".to_string() }, +//! vec![ +//! ( +//! Post { id: 3, user_id: 2, title: "My first post too".to_string() }, +//! vec![ Comment { id: 3, post_id: 3, body: "I enjoyed your post".to_string() } ] +//! ) +//! ] +//! ) +//! ]; +//! +//! assert_eq!(result, expected); +//! # } +//! ``` +//! +//! And that's it. +//! It may seem odd to have load, group, and zip be explicit separate steps +//! if you are coming from another ORM. +//! However, the goal is to provide simple building blocks which can +//! be used to construct the complex behavior applications need. +mod belongs_to; + +use std::hash::Hash; + +use crate::query_source::Table; + +pub use self::belongs_to::{BelongsTo, GroupedBy}; + +#[doc(inline)] +pub use diesel_derives::Associations; + +/// This trait indicates that a struct is associated with a single database table. +/// +/// This trait is implemented by structs which implement `Identifiable`, +/// as well as database tables themselves. +pub trait HasTable { + /// The table this type is associated with. + type Table: Table; + + /// Returns the table this type is associated with. + fn table() -> Self::Table; +} + +impl HasTable for &T { + type Table = T::Table; + + fn table() -> Self::Table { + T::table() + } +} + +/// This trait indicates that a struct represents a single row in a database table. +/// +/// This must be implemented to use associations. +/// Additionally, implementing this trait allows you to pass your struct to `update` +/// (`update(&your_struct)` is equivalent to +/// `update(YourStruct::table().find(&your_struct.primary_key())`). +/// +/// This trait is usually implemented on a reference to a struct, +/// not on the struct itself. It can be [derived](derive@Identifiable). +/// +pub trait Identifiable: HasTable { + /// The type of this struct's identifier. + /// + /// For single-field primary keys, this is typically `&'a i32`, or `&'a String` + /// For composite primary keys, this is typically `(&'a i32, &'a i32)` + /// or `(&'a String, &'a String)`, etc. + type Id: Hash + Eq; + + /// Returns the identifier for this record. + /// + /// This takes `self` by value, not reference. + /// This is because composite primary keys + /// are typically stored as multiple fields. + /// We could not return `&(String, String)` if each string is a separate field. + /// + /// Because of Rust's rules about specifying lifetimes, + /// this means that `Identifiable` is usually implemented on references + /// so that we have a lifetime to use for `Id`. + fn id(self) -> Self::Id; +} + +#[doc(inline)] +pub use diesel_derives::Identifiable; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/backend.rs b/collector/compile-benchmarks/diesel-2.2.10/src/backend.rs new file mode 100644 index 000000000..3847453f0 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/backend.rs @@ -0,0 +1,579 @@ +//! Types which represent various database backends + +use crate::query_builder::QueryBuilder; +use crate::sql_types::{self, HasSqlType, TypeMetadata}; + +#[cfg_attr( + not(any( + feature = "postgres_backend", + feature = "mysql_backend", + feature = "sqlite", + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )), + allow(unused_imports) +)] +#[doc(inline)] +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +pub(crate) use self::private::{DieselReserveSpecialization, TrustedBackend}; + +/// A database backend +/// +/// This trait represents the concept of a backend (e.g. "MySQL" vs "SQLite"). +/// It is separate from a [`Connection`](crate::connection::Connection) +/// to that backend. +/// One backend may have multiple concrete connection implementations. +/// +/// # Implementing a custom backend +/// +/// Implementing a custom backend requires enabling the +/// `i-implement-a-third-party-backend-and-opt-into-breaking-changes` crate feature +/// to get access to all necessary type and trait implementations. +/// +/// Implementations of this trait should not assume details about how the +/// connection is implemented. +/// For example, the `Pg` backend does not assume that `libpq` is being used. +/// Implementations of this trait can and should care about details of the wire +/// protocol used to communicate with the database. +/// +/// Implementing support for a new backend is a complex topic and depends on the +/// details how the newly implemented backend may communicate with diesel. As of this, +/// we cannot provide concrete examples here and only present a general outline of +/// the required steps. Existing backend implementations provide a good starting point +/// to see how certain things are solved for other backend implementations. +/// +/// Types implementing `Backend` should generally be zero sized structs. +/// +/// To implement the `Backend` trait, you need to: +/// +/// * Specify how a query should be build from string parts by providing a [`QueryBuilder`] +/// matching your backend +/// * Specify the bind value format used by your database connection library by providing +/// a [`BindCollector`](crate::query_builder::bind_collector::BindCollector) matching your backend +/// * Specify how values are received from the database by providing a corresponding raw value +/// definition +/// * Control sql dialect specific parts of diesels query dsl implementation by providing a +/// matching [`SqlDialect`] implementation +/// * Implement [`TypeMetadata`] to specify how your backend identifies types +/// * Specify support for common datatypes by implementing [`HasSqlType`] for the following sql types: +/// + [`SmallInt`](sql_types::SmallInt) +/// + [`Integer`](sql_types::Integer) +/// + [`BigInt`](sql_types::BigInt) +/// + [`Float`](sql_types::Float) +/// + [`Double`](sql_types::Double) +/// + [`Text`](sql_types::Text) +/// + [`Binary`](sql_types::Binary) +/// + [`Date`](sql_types::Date) +/// + [`Time`](sql_types::Time) +/// + [`Timestamp`](sql_types::Timestamp) +/// +/// Additionally to the listed required trait bounds you may want to implement +#[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "[`DieselReserveSpecialization`]" +)] +#[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "`DieselReserveSpecialization`" +)] +/// to opt in existing wild card [`QueryFragment`] impls for large parts of the dsl. +/// +/// [`QueryFragment`]: crate::query_builder::QueryFragment +pub trait Backend +where + Self: Sized + SqlDialect + TypeMetadata, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, +{ + /// The concrete [`QueryBuilder`] implementation for this backend. + type QueryBuilder: QueryBuilder; + + /// The actual type given to [`FromSql`], with lifetimes applied. This type + /// should not be used directly. + /// + /// [`FromSql`]: crate::deserialize::FromSql + type RawValue<'a>; + + /// The concrete [`BindCollector`](crate::query_builder::bind_collector::BindCollector) + /// implementation for this backend. + /// + /// Most backends should use [`RawBytesBindCollector`]. + /// + /// [`RawBytesBindCollector`]: crate::query_builder::bind_collector::RawBytesBindCollector + type BindCollector<'a>: crate::query_builder::bind_collector::BindCollector<'a, Self> + 'a; +} + +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[deprecated(note = "Use `Backend::RawValue` directly")] +pub type RawValue<'a, DB> = ::RawValue<'a>; + +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[deprecated(note = "Use `Backend::BindCollector` directly")] +pub type BindCollector<'a, DB> = ::BindCollector<'a>; + +/// This trait provides various options to configure the +/// generated SQL for a specific backend. +/// +/// Accessing anything from this trait is considered to be part of the +/// public API. Implementing this trait is not considered to be part of +/// diesel's public API, as future versions of diesel may add additional +/// associated constants here. +/// +/// Each associated type is used to configure the behaviour +/// of one or more [`QueryFragment`](crate::query_builder::QueryFragment) +/// implementations by providing +/// a custom `QueryFragment` implementation +/// to specialize on generic `QueryFragment` implementations. +/// +#[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See the [`sql_dialect`] module for options provided by diesel out of the box." +)] +pub trait SqlDialect: self::private::TrustedBackend { + /// Configures how this backend supports `RETURNING` clauses + /// + /// This allows backends to opt in `RETURNING` clause support and to + /// provide a custom [`QueryFragment`](crate::query_builder::QueryFragment) + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "implementation for [`ReturningClause`](crate::query_builder::ReturningClause)" + )] + #[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "implementation for `ReturningClause`" + )] + /// + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See [`sql_dialect::returning_clause`] for provided default implementations" + )] + type ReturningClause; + /// Configures how this backend supports `ON CONFLICT` clauses + /// + /// This allows backends to opt in `ON CONFLICT` clause support + /// + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See [`sql_dialect::on_conflict_clause`] for provided default implementations" + )] + type OnConflictClause; + /// Configures how this backend handles the bare `DEFAULT` keyword for + /// inserting the default value in a `INSERT INTO` `VALUES` clause + /// + /// This allows backends to opt in support for `DEFAULT` value expressions + /// for insert statements + /// + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See [`sql_dialect::default_keyword_for_insert`] for provided default implementations" + )] + type InsertWithDefaultKeyword; + /// Configures how this backend handles Batch insert statements + /// + /// This allows backends to provide a custom [`QueryFragment`](crate::query_builder::QueryFragment) + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "implementation for [`BatchInsert`](crate::query_builder::BatchInsert)" + )] + #[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "implementation for `BatchInsert`" + )] + /// + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See [`sql_dialect::batch_insert_support`] for provided default implementations" + )] + type BatchInsertSupport; + /// Configures how this backend handles the Concat clauses in + /// select statements. + /// + /// This allows backends to provide a custom [`QueryFragment`](crate::query_builder::QueryFragment) + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "implementation for [`Concat`](crate::expression::Concat)" + )] + #[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "implementation for `Concat`" + )] + /// + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See [`sql_dialect::concat_clause`] for provided default implementations" + )] + type ConcatClause; + /// Configures how this backend handles the `DEFAULT VALUES` clause for + /// insert statements. + /// + /// This allows backends to provide a custom [`QueryFragment`](crate::query_builder::QueryFragment) + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "implementation for [`DefaultValues`](crate::query_builder::DefaultValues)" + )] + #[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "implementation for `DefaultValues`" + )] + /// + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See [`sql_dialect::default_value_clause`] for provided default implementations" + )] + type DefaultValueClauseForInsert; + /// Configures how this backend handles empty `FROM` clauses for select statements. + /// + /// This allows backends to provide a custom [`QueryFragment`](crate::query_builder::QueryFragment) + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "implementation for [`NoFromClause`](crate::query_builder::NoFromClause)" + )] + #[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "implementation for `NoFromClause`" + )] + /// + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See [`sql_dialect::from_clause_syntax`] for provided default implementations" + )] + type EmptyFromClauseSyntax; + /// Configures how this backend handles `EXISTS()` expressions. + /// + /// This allows backends to provide a custom [`QueryFragment`](crate::query_builder::QueryFragment) + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "implementation for [`Exists`](crate::expression::exists::Exists)" + )] + #[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "implementation for `Exists`" + )] + /// + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See [`sql_dialect::exists_syntax`] for provided default implementations" + )] + type ExistsSyntax; + + /// Configures how this backend handles `IN()` and `NOT IN()` expressions. + /// + /// This allows backends to provide custom [`QueryFragment`](crate::query_builder::QueryFragment) + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "implementations for [`In`](crate::expression::array_comparison::In), + [`NotIn`](crate::expression::array_comparison::NotIn) and + [`Many`](crate::expression::array_comparison::Many)" + )] + #[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "implementations for `In`, `NotIn` and `Many`" + )] + /// + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See [`sql_dialect::array_comparison`] for provided default implementations" + )] + type ArrayComparison; + + /// Configures how this backend structures `SELECT` queries + /// + /// This allows backends to provide custom [`QueryFragment`](crate::query_builder::QueryFragment) + /// implementations for + #[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "`SelectStatement` and `BoxedSelectStatement`" + )] + #[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "[`SelectStatement`](crate::query_builder::SelectStatement) and + [`BoxedSelectStatement`](crate::query_builder::BoxedSelectStatement)" + )] + /// + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See [`sql_dialect::select_statement_syntax`] for provided default implementations" + )] + type SelectStatementSyntax; + + /// Configures how this backend structures `SELECT` queries + /// + /// This allows backends to provide custom [`QueryFragment`](crate::query_builder::QueryFragment) + /// implementations for [`Alias`](crate::query_source::Alias) + /// + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See [`sql_dialect::alias_syntax`] for provided default implementations" + )] + type AliasSyntax; +} + +/// This module contains all options provided by diesel to configure the [`SqlDialect`] trait. +// This module is only public behind the unstable feature flag, as we may want to change SqlDialect +// implementations of existing backends because of: +// * The backend gained support for previously unsupported SQL operations +// * The backend fixed/introduced a bug that requires special handling +// * We got some edge case wrong with sharing the implementation between backends +// +// By not exposing these types publicly we are able to change the exact definitions later on +// as users cannot write trait bounds that ensure that a specific type is used in place of +// an existing associated type. +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +pub(crate) mod sql_dialect { + #![cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + // Otherwise there are false positives + // because the lint seems to believe that these pub statements + // are not required, but they are required through the various backend impls + allow(unreachable_pub) + )] + #[cfg(doc)] + use super::SqlDialect; + + /// This module contains all diesel provided reusable options to + /// configure [`SqlDialect::OnConflictClause`] + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub mod on_conflict_clause { + /// A marker trait indicating if a `ON CONFLICT` clause is supported or not + /// + /// If you use a custom type to specify specialized support for `ON CONFLICT` clauses + /// implementing this trait opts into reusing diesels existing `ON CONFLICT` + /// `QueryFragment` implementations + pub trait SupportsOnConflictClause {} + + /// A marker trait indicating if a `ON CONFLICT (...) DO UPDATE ... [WHERE ...]` clause is supported or not + pub trait SupportsOnConflictClauseWhere {} + + /// A marker trait indicating whether the on conflict clause implementation + /// is mostly like postgresql + pub trait PgLikeOnConflictClause: SupportsOnConflictClause {} + + /// This marker type indicates that `ON CONFLICT` clauses are not supported for this backend + #[derive(Debug, Copy, Clone)] + pub struct DoesNotSupportOnConflictClause; + } + + /// This module contains all reusable options to configure + /// [`SqlDialect::ReturningClause`] + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub mod returning_clause { + /// A marker trait indicating if a `RETURNING` clause is supported or not + /// + /// If you use a custom type to specify specialized support for `RETURNING` clauses + /// implementing this trait opts in supporting `RETURNING` clause syntax + pub trait SupportsReturningClause {} + + /// Indicates that a backend provides support for `RETURNING` clauses + /// using the postgresql `RETURNING` syntax + #[derive(Debug, Copy, Clone)] + pub struct PgLikeReturningClause; + + /// Indicates that a backend does not support `RETURNING` clauses + #[derive(Debug, Copy, Clone)] + pub struct DoesNotSupportReturningClause; + + impl SupportsReturningClause for PgLikeReturningClause {} + } + + /// This module contains all reusable options to configure + /// [`SqlDialect::InsertWithDefaultKeyword`] + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub mod default_keyword_for_insert { + /// A marker trait indicating if a `DEFAULT` like expression + /// is supported as part of `INSERT INTO` clauses to indicate + /// that a default value should be inserted at a specific position + /// + /// If you use a custom type to specify specialized support for `DEFAULT` + /// expressions implementing this trait opts in support for `DEFAULT` + /// value expressions for inserts. Otherwise diesel will emulate this + /// behaviour. + pub trait SupportsDefaultKeyword {} + + /// Indicates that a backend support `DEFAULT` value expressions + /// for `INSERT INTO` statements based on the ISO SQL standard + #[derive(Debug, Copy, Clone)] + pub struct IsoSqlDefaultKeyword; + + /// Indicates that a backend does not support `DEFAULT` value + /// expressions for `INSERT INTO` statements + #[derive(Debug, Copy, Clone)] + pub struct DoesNotSupportDefaultKeyword; + + impl SupportsDefaultKeyword for IsoSqlDefaultKeyword {} + } + + /// This module contains all reusable options to configure + /// [`SqlDialect::BatchInsertSupport`] + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub mod batch_insert_support { + /// A marker trait indicating if batch insert statements + /// are supported for this backend or not + pub trait SupportsBatchInsert {} + + /// Indicates that this backend does not support batch + /// insert statements. + /// In this case diesel will emulate batch insert support + /// by inserting each row on its own + #[derive(Debug, Copy, Clone)] + pub struct DoesNotSupportBatchInsert; + + /// Indicates that this backend supports postgres style + /// batch insert statements to insert multiple rows using one + /// insert statement + #[derive(Debug, Copy, Clone)] + pub struct PostgresLikeBatchInsertSupport; + + impl SupportsBatchInsert for PostgresLikeBatchInsertSupport {} + } + /// This module contains all reusable options to configure + /// [`SqlDialect::ConcatClause`] + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub mod concat_clause { + + /// Indicates that this backend uses the + /// `||` operator to select a concatenation + /// of two variables or strings + #[derive(Debug, Clone, Copy)] + pub struct ConcatWithPipesClause; + } + + /// This module contains all reusable options to configure + /// [`SqlDialect::DefaultValueClauseForInsert`] + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub mod default_value_clause { + + /// Indicates that this backend uses the + /// `DEFAULT VALUES` syntax to specify + /// that a row consisting only of default + /// values should be inserted + #[derive(Debug, Clone, Copy)] + pub struct AnsiDefaultValueClause; + } + + /// This module contains all reusable options to configure + /// [`SqlDialect::EmptyFromClauseSyntax`] + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub(crate) mod from_clause_syntax { + + /// Indicates that this backend skips + /// the `FROM` clause in `SELECT` statements + /// if no table/view is queried + #[derive(Debug, Copy, Clone)] + pub struct AnsiSqlFromClauseSyntax; + } + + /// This module contains all reusable options to configure + /// [`SqlDialect::ExistsSyntax`] + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub mod exists_syntax { + + /// Indicates that this backend + /// treats `EXIST()` as function + /// like expression + #[derive(Debug, Copy, Clone)] + pub struct AnsiSqlExistsSyntax; + } + + /// This module contains all reusable options to configure + /// [`SqlDialect::ArrayComparison`] + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub mod array_comparison { + + /// Indicates that this backend requires a single bind + /// per array element in `IN()` and `NOT IN()` expression + #[derive(Debug, Copy, Clone)] + pub struct AnsiSqlArrayComparison; + } + + /// This module contains all reusable options to configure + /// [`SqlDialect::SelectStatementSyntax`] + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub mod select_statement_syntax { + /// Indicates that this backend uses the default + /// ANSI select statement structure + #[derive(Debug, Copy, Clone)] + pub struct AnsiSqlSelectStatement; + } + + /// This module contains all reusable options to configure + /// [`SqlDialect::AliasSyntax`] + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub mod alias_syntax { + /// Indicates that this backend uses `table AS alias` for + /// defining table aliases + #[derive(Debug, Copy, Clone)] + pub struct AsAliasSyntax; + } +} + +// These traits are not part of the public API +// because we want to replace them by with an associated type +// in the child trait later if GAT's are finally stable +pub(crate) mod private { + + /// This is a marker trait which indicates that + /// diesel may specialize a certain [`QueryFragment`] + /// impl in a later version. If you as a user encounter, where rustc + /// suggests adding this a bound to a type implementing `Backend` + /// consider adding the following bound instead + /// `YourQueryType: QueryFragment` (the concrete bound + /// is likely mentioned by rustc as part of a `note: …`) + /// + /// For any user implementing a custom backend: You likely want to implement + /// this trait for your custom backend type to opt in the existing [`QueryFragment`] impls in diesel. + /// As indicated by the `i-implement-a-third-party-backend-and-opt-into-breaking-changes` feature + /// diesel reserves the right to specialize any generic [`QueryFragment`](crate::query_builder::QueryFragment) + /// impl via [`SqlDialect`](super::SqlDialect) in a later minor version release + /// + /// [`QueryFragment`]: crate::query_builder::QueryFragment + #[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) + )] + pub trait DieselReserveSpecialization {} + + /// This trait just indicates that none implements + /// [`SqlDialect`](super::SqlDialect) without enabling the + /// `i-implement-a-third-party-backend-and-opt-into-breaking-changes` + /// feature flag. + #[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) + )] + pub trait TrustedBackend {} +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/connection/instrumentation.rs b/collector/compile-benchmarks/diesel-2.2.10/src/connection/instrumentation.rs new file mode 100644 index 000000000..5f33d78ac --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/connection/instrumentation.rs @@ -0,0 +1,318 @@ +use std::fmt::Debug; +use std::fmt::Display; +use std::num::NonZeroU32; +use std::ops::DerefMut; + +static GLOBAL_INSTRUMENTATION: std::sync::RwLock Option>> = + std::sync::RwLock::new(|| None); + +/// A helper trait for opaque query representations +/// which allows to get a `Display` and `Debug` +/// representation of the underlying type without +/// exposing type specific details +pub trait DebugQuery: Debug + Display {} + +impl DebugQuery for crate::query_builder::DebugQuery<'_, T, DB> where Self: Debug + Display {} + +/// A helper type that allows printing out str slices +/// +/// This type is necessary because it's not possible +/// to cast from a reference of a unsized type like `&str` +/// to a reference of a trait object even if that +/// type implements all necessary traits +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +pub(crate) struct StrQueryHelper<'query> { + s: &'query str, +} + +impl<'query> StrQueryHelper<'query> { + /// Construct a new `StrQueryHelper` + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + #[cfg(any( + feature = "postgres", + feature = "sqlite", + feature = "mysql", + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + ))] + pub(crate) fn new(s: &'query str) -> Self { + Self { s } + } +} + +impl Debug for StrQueryHelper<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Debug::fmt(self.s, f) + } +} + +impl Display for StrQueryHelper<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.s, f) + } +} + +impl DebugQuery for StrQueryHelper<'_> {} + +/// This enum describes possible connection events +/// that can be handled by an [`Instrumentation`] implementation +/// +/// Some fields might contain sensitive information, like login +/// details for the database. +/// +/// Diesel does not guarantee that future versions will +/// emit the same events in the same order or timing. +/// In addition the output of the [`Debug`] and [`Display`] +/// implementation of the enum itself and any of its fields +/// is not guarantee to be stable. +// +// This type is carefully designed +// to avoid any potential overhead by +// taking references for all things +// and by not performing any additional +// work until required. +// In addition it's carefully designed +// not to be dependent on the actual backend +// type, as that makes it easier to reuse +// `Instrumentation` implementations in +// a different context +#[derive(Debug)] +#[non_exhaustive] +pub enum InstrumentationEvent<'a> { + /// An event emitted by before starting + /// establishing a new connection + #[non_exhaustive] + StartEstablishConnection { + /// The database url the connection + /// tries to connect to + /// + /// This might contain sensitive information + /// like the database password + url: &'a str, + }, + /// An event emitted after establishing a + /// new connection + #[non_exhaustive] + FinishEstablishConnection { + /// The database url the connection + /// tries is connected to + /// + /// This might contain sensitive information + /// like the database password + url: &'a str, + /// An optional error if the connection failed + error: Option<&'a crate::result::ConnectionError>, + }, + /// An event that is emitted before executing + /// a query + #[non_exhaustive] + StartQuery { + /// A opaque representation of the query + /// + /// This type implements [`Debug`] and [`Display`], + /// but should be considered otherwise as opaque. + /// + /// The exact output of the [`Debug`] and [`Display`] + /// implementation is not considered as part of the + /// stable API. + query: &'a dyn DebugQuery, + }, + /// An event that is emitted when a query + /// is cached in the connection internal + /// prepared statement cache + #[non_exhaustive] + CacheQuery { + /// SQL string of the cached query + sql: &'a str, + }, + /// An event that is emitted after executing + /// a query + #[non_exhaustive] + FinishQuery { + /// A opaque representation of the query + /// + /// This type implements [`Debug`] and [`Display`], + /// but should be considered otherwise as opaque. + /// + /// The exact output of the [`Debug`] and [`Display`] + /// implementation is not considered as part of the + /// stable API. + query: &'a dyn DebugQuery, + /// An optional error if the connection failed + error: Option<&'a crate::result::Error>, + }, + /// An event that is emitted while + /// starting a new transaction + #[non_exhaustive] + BeginTransaction { + /// Transaction level of the newly started + /// transaction + depth: NonZeroU32, + }, + /// An event that is emitted while + /// committing a transaction + #[non_exhaustive] + CommitTransaction { + /// Transaction level of the to be committed + /// transaction + depth: NonZeroU32, + }, + /// An event that is emitted while + /// rolling back a transaction + #[non_exhaustive] + RollbackTransaction { + /// Transaction level of the to be rolled + /// back transaction + depth: NonZeroU32, + }, +} + +// these constructors exist to +// keep `#[non_exhaustive]` on all the variants +// and to gate the constructors on the unstable feature +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +impl<'a> InstrumentationEvent<'a> { + /// Create a new `InstrumentationEvent::StartEstablishConnection` event + #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] + pub fn start_establish_connection(url: &'a str) -> Self { + Self::StartEstablishConnection { url } + } + + /// Create a new `InstrumentationEvent::FinishEstablishConnection` event + #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] + pub fn finish_establish_connection( + url: &'a str, + error: Option<&'a crate::result::ConnectionError>, + ) -> Self { + Self::FinishEstablishConnection { url, error } + } + + /// Create a new `InstrumentationEvent::StartQuery` event + #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] + pub fn start_query(query: &'a dyn DebugQuery) -> Self { + Self::StartQuery { query } + } + + /// Create a new `InstrumentationEvent::CacheQuery` event + #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] + pub fn cache_query(sql: &'a str) -> Self { + Self::CacheQuery { sql } + } + + /// Create a new `InstrumentationEvent::FinishQuery` event + #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] + pub fn finish_query( + query: &'a dyn DebugQuery, + error: Option<&'a crate::result::Error>, + ) -> Self { + Self::FinishQuery { query, error } + } + + /// Create a new `InstrumentationEvent::BeginTransaction` event + #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] + pub fn begin_transaction(depth: NonZeroU32) -> Self { + Self::BeginTransaction { depth } + } + + /// Create a new `InstrumentationEvent::RollbackTransaction` event + #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] + pub fn rollback_transaction(depth: NonZeroU32) -> Self { + Self::RollbackTransaction { depth } + } + + /// Create a new `InstrumentationEvent::CommitTransaction` event + #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] + pub fn commit_transaction(depth: NonZeroU32) -> Self { + Self::CommitTransaction { depth } + } +} + +/// A type that provides an connection `Instrumentation` +/// +/// This trait is the basic building block for logging or +/// otherwise instrumenting diesel connection types. It +/// acts as callback that receives information about certain +/// important connection states +/// +/// For simple usages this trait is implemented for closures +/// accepting a [`InstrumentationEvent`] as argument. +/// +/// More complex usages and integrations with frameworks like +/// `tracing` and `log` are supposed to be part of their own +/// crates. +pub trait Instrumentation: Send + 'static { + /// The function that is invoked for each event + fn on_connection_event(&mut self, event: InstrumentationEvent<'_>); +} + +/// Get an instance of the default [`Instrumentation`] +/// +/// This function is mostly useful for crates implementing +/// their own connection types +pub fn get_default_instrumentation() -> Option> { + match GLOBAL_INSTRUMENTATION.read() { + Ok(f) => (*f)(), + Err(_) => None, + } +} + +/// Set a custom constructor for the default [`Instrumentation`] +/// used by new connections +/// +/// ```rust +/// use diesel::connection::{set_default_instrumentation, Instrumentation, InstrumentationEvent}; +/// +/// // a simple logger that prints all events to stdout +/// fn simple_logger() -> Option> { +/// // we need the explicit argument type there due +/// // to bugs in rustc +/// Some(Box::new(|event: InstrumentationEvent<'_>| { +/// println!("{event:?}") +/// })) +/// } +/// +/// set_default_instrumentation(simple_logger); +/// ``` +pub fn set_default_instrumentation( + default: fn() -> Option>, +) -> crate::QueryResult<()> { + match GLOBAL_INSTRUMENTATION.write() { + Ok(mut l) => { + *l = default; + Ok(()) + } + Err(e) => Err(crate::result::Error::DatabaseError( + crate::result::DatabaseErrorKind::Unknown, + Box::new(e.to_string()), + )), + } +} + +impl Instrumentation for F +where + F: FnMut(InstrumentationEvent<'_>) + Send + 'static, +{ + fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) { + (self)(event) + } +} + +impl Instrumentation for Box { + fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) { + self.deref_mut().on_connection_event(event) + } +} + +impl Instrumentation for Option +where + T: Instrumentation, +{ + fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) { + if let Some(i) = self { + i.on_connection_event(event) + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/connection/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/connection/mod.rs new file mode 100644 index 000000000..32efcbe47 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/connection/mod.rs @@ -0,0 +1,596 @@ +//! Types related to database connections + +pub(crate) mod instrumentation; +#[cfg(all( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + any(feature = "sqlite", feature = "postgres", feature = "mysql") +))] +pub(crate) mod statement_cache; +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +pub mod statement_cache; +mod transaction_manager; + +use crate::backend::Backend; +use crate::expression::QueryMetadata; +use crate::query_builder::{Query, QueryFragment, QueryId}; +use crate::result::*; +use crate::sql_types::TypeMetadata; +use std::fmt::Debug; + +#[doc(inline)] +pub use self::instrumentation::{ + get_default_instrumentation, set_default_instrumentation, DebugQuery, Instrumentation, + InstrumentationEvent, +}; +#[doc(inline)] +pub use self::transaction_manager::{ + AnsiTransactionManager, InTransactionStatus, TransactionDepthChange, TransactionManager, + TransactionManagerStatus, ValidTransactionManagerStatus, +}; + +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +pub(crate) use self::private::ConnectionSealed; + +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +pub use self::private::MultiConnectionHelper; + +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +pub use self::instrumentation::StrQueryHelper; + +#[cfg(all( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + any(feature = "sqlite", feature = "postgres", feature = "mysql") +))] +pub(crate) use self::private::MultiConnectionHelper; + +/// Perform simple operations on a backend. +/// +/// You should likely use [`Connection`] instead. +pub trait SimpleConnection { + /// Execute multiple SQL statements within the same string. + /// + /// This function is used to execute migrations, + /// which may contain more than one SQL statement. + fn batch_execute(&mut self, query: &str) -> QueryResult<()>; +} + +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[deprecated(note = "Directly use `LoadConnection::Cursor` instead")] +pub type LoadRowIter<'conn, 'query, C, DB, B = DefaultLoadingMode> = + >::Cursor<'conn, 'query>; + +/// A connection to a database +/// +/// This trait represents a database connection. It can be used to query the database through +/// the query dsl provided by diesel, custom extensions or raw sql queries. +/// +/// # Implementing a custom connection +/// +/// There are several reasons why you would want to implement a custom connection implementation: +/// +/// * To wrap an existing connection for instrumentation purposes +/// * To use a different underlying library to provide a connection implementation +/// for already existing backends. +/// * To add support for an unsupported database system +/// +/// Implementing a `Connection` in a third party crate requires +/// enabling the +/// `i-implement-a-third-party-backend-and-opt-into-breaking-changes` +/// crate feature which grants access to some of diesel's implementation details. +/// +/// +/// ## Wrapping an existing connection impl +/// +/// Wrapping an existing connection allows you to customize the implementation to +/// add additional functionality, like for example instrumentation. For this use case +/// you only need to implement `Connection`, [`LoadConnection`] and all super traits. +/// You should forward any method call to the wrapped connection type. +/// It is **important** to also forward any method where diesel provides a +/// default implementation, as the wrapped connection implementation may +/// contain a customized implementation. +/// +/// To allow the integration of your new connection type with other diesel features +#[cfg_attr( + feature = "r2d2", + doc = "it may be useful to also implement [`R2D2Connection`](crate::r2d2::R2D2Connection)" +)] +#[cfg_attr( + not(feature = "r2d2"), + doc = "it may be useful to also implement `R2D2Connection`" +)] +/// and [`MigrationConnection`](crate::migration::MigrationConnection). +/// +/// ## Provide a new connection implementation for an existing backend +/// +/// Implementing a new connection based on an existing backend can enable the usage of +/// other methods to connect to the database. One example here would be to replace +/// the official diesel provided connection implementations with an implementation +/// based on a pure rust connection crate. +/// +/// **It's important to use prepared statements to implement the following methods:** +/// * [`LoadConnection::load`] +/// * [`Connection::execute_returning_count`] +/// +/// For performance reasons it may also be meaningful to cache already prepared statements. +#[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "See [`StatementCache`](self::statement_cache::StatementCache)" +)] +#[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "See `StatementCache`" +)] +/// for a helper type to implement prepared statement caching. +#[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "The [statement_cache](self::statement_cache)" +)] +#[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "The statement_cache" +)] +/// module documentation contains details about efficient prepared statement caching +/// based on diesels query builder. +/// +/// It is required to implement at least the following parts: +/// +/// * A row type that describes how to receive values form a database row. +/// This type needs to implement [`Row`](crate::row::Row) +/// * A field type that describes a database field value. +/// This type needs to implement [`Field`](crate::row::Field) +/// * A connection type that wraps the connection + +/// the necessary state management. +/// * Maybe a [`TransactionManager`] implementation matching +/// the interface provided by the database connection crate. +/// Otherwise the implementation used by the corresponding +/// `Connection` in diesel can be reused. +/// +/// To allow the integration of your new connection type with other diesel features +#[cfg_attr( + feature = "r2d2", + doc = "it may be useful to also implement [`R2D2Connection`](crate::r2d2::R2D2Connection)" +)] +#[cfg_attr( + not(feature = "r2d2"), + doc = "it may be useful to also implement `R2D2Connection`" +)] +/// and [`MigrationConnection`](crate::migration::MigrationConnection). +/// +/// The exact implementation of the `Connection` trait depends on the interface provided +/// by the connection crate/library. A struct implementing `Connection` should +#[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + doc = "likely contain a [`StatementCache`](self::statement_cache::StatementCache)" +)] +#[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc = "likely contain a `StatementCache`" +)] +/// to cache prepared statements efficiently. +/// +/// As implementations differ significantly between the supported backends +/// we cannot give a one for all description here. Generally it's likely a +/// good idea to follow the implementation of the corresponding connection +/// in diesel at a high level to gain some idea how to implement your +/// custom implementation. +/// +/// ## Implement support for an unsupported database system +/// +/// Additionally to anything mentioned in the previous section the following steps are required: +/// +/// * Implement a custom backend type. See the documentation of [`Backend`] for details +/// * Implement appropriate [`FromSql`](crate::deserialize::FromSql)/ +/// [`ToSql`](crate::serialize::ToSql) conversions. +/// At least the following impls should be considered: +/// * `i16`: `FromSql` +/// * `i32`: `FromSql` +/// * `i64`: `FromSql` +/// * `f32`: `FromSql` +/// * `f64`: `FromSql` +/// * `bool`: `FromSql` +/// * `String`: `FromSql` +/// * `Vec`: `FromSql` +/// * `i16`: `ToSql` +/// * `i32`: `ToSql` +/// * `i64`: `ToSql` +/// * `f32`: `ToSql` +/// * `f64`: `ToSql` +/// * `bool`: `ToSql` +/// * `String`: `ToSql` +/// * `Vec`: `ToSql` +/// * Maybe a [`TransactionManager`] implementation matching +/// the interface provided by the database connection crate. +/// Otherwise the implementation used by the corresponding +/// `Connection` in diesel can be reused. +/// +/// As these implementations will vary depending on the backend being used, +/// we cannot give concrete examples here. We recommend looking at our existing +/// implementations to see how you can implement your own connection. +pub trait Connection: SimpleConnection + Sized + Send +where + // This trait bound is there so that implementing a new connection is + // gated behind the `i-implement-a-third-party-backend-and-opt-into-breaking-changes` + // feature flag + Self: ConnectionSealed, +{ + /// The backend this type connects to + type Backend: Backend; + + /// The transaction manager implementation used by this connection + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + type TransactionManager: TransactionManager; + + /// Establishes a new connection to the database + /// + /// The argument to this method and the method's behavior varies by backend. + /// See the documentation for that backend's connection class + /// for details about what it accepts and how it behaves. + fn establish(database_url: &str) -> ConnectionResult; + + /// Executes the given function inside of a database transaction + /// + /// This function executes the provided closure `f` inside a database + /// transaction. If there is already an open transaction for the current + /// connection savepoints will be used instead. The connection is committed if + /// the closure returns `Ok(_)`, it will be rolled back if it returns `Err(_)`. + /// For both cases the original result value will be returned from this function. + /// + /// If the transaction fails to commit due to a `SerializationFailure` or a + /// `ReadOnlyTransaction` a rollback will be attempted. + /// If the rollback fails, the error will be returned in a + /// [`Error::RollbackErrorOnCommit`], + /// from which you will be able to extract both the original commit error and + /// the rollback error. + /// In addition, the connection will be considered broken + /// as it contains a uncommitted unabortable open transaction. Any further + /// interaction with the transaction system will result in an returned error + /// in this case. + /// + /// If the closure returns an `Err(_)` and the rollback fails the function + /// will return that rollback error directly, and the transaction manager will + /// be marked as broken as it contains a uncommitted unabortable open transaction. + /// + /// If a nested transaction fails to release the corresponding savepoint + /// the error will be returned directly. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// use diesel::result::Error; + /// + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = &mut establish_connection(); + /// conn.transaction::<_, Error, _>(|conn| { + /// diesel::insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(conn)?; + /// + /// let all_names = users.select(name).load::(conn)?; + /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names); + /// + /// Ok(()) + /// })?; + /// + /// conn.transaction::<(), _, _>(|conn| { + /// diesel::insert_into(users) + /// .values(name.eq("Pascal")) + /// .execute(conn)?; + /// + /// let all_names = users.select(name).load::(conn)?; + /// assert_eq!(vec!["Sean", "Tess", "Ruby", "Pascal"], all_names); + /// + /// // If we want to roll back the transaction, but don't have an + /// // actual error to return, we can return `RollbackTransaction`. + /// Err(Error::RollbackTransaction) + /// }); + /// + /// let all_names = users.select(name).load::(conn)?; + /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names); + /// # Ok(()) + /// # } + /// ``` + fn transaction(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + E: From, + { + Self::TransactionManager::transaction(self, f) + } + + /// Creates a transaction that will never be committed. This is useful for + /// tests. Panics if called while inside of a transaction or + /// if called with a connection containing a broken transaction + fn begin_test_transaction(&mut self) -> QueryResult<()> { + match Self::TransactionManager::transaction_manager_status_mut(self) { + TransactionManagerStatus::Valid(valid_status) => { + assert_eq!(None, valid_status.transaction_depth()) + } + TransactionManagerStatus::InError => panic!("Transaction manager in error"), + }; + Self::TransactionManager::begin_transaction(self)?; + // set the test transaction flag + // to prevent that this connection gets dropped in connection pools + // Tests commonly set the poolsize to 1 and use `begin_test_transaction` + // to prevent modifications to the schema + Self::TransactionManager::transaction_manager_status_mut(self).set_test_transaction_flag(); + Ok(()) + } + + /// Executes the given function inside a transaction, but does not commit + /// it. Panics if the given function returns an error. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// use diesel::result::Error; + /// + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = &mut establish_connection(); + /// conn.test_transaction::<_, Error, _>(|conn| { + /// diesel::insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(conn)?; + /// + /// let all_names = users.select(name).load::(conn)?; + /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names); + /// + /// Ok(()) + /// }); + /// + /// // Even though we returned `Ok`, the transaction wasn't committed. + /// let all_names = users.select(name).load::(conn)?; + /// assert_eq!(vec!["Sean", "Tess"], all_names); + /// # Ok(()) + /// # } + /// ``` + fn test_transaction(&mut self, f: F) -> T + where + F: FnOnce(&mut Self) -> Result, + E: Debug, + { + let mut user_result = None; + let _ = self.transaction::<(), _, _>(|conn| { + user_result = f(conn).ok(); + Err(Error::RollbackTransaction) + }); + user_result.expect("Transaction did not succeed") + } + + /// Execute a single SQL statements given by a query and return + /// number of affected rows + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + fn execute_returning_count(&mut self, source: &T) -> QueryResult + where + T: QueryFragment + QueryId; + + /// Get access to the current transaction state of this connection + /// + /// This function should be used from [`TransactionManager`] to access + /// internally required state. + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + fn transaction_state( + &mut self, + ) -> &mut >::TransactionStateData; + + /// Get the instrumentation instance stored in this connection + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + fn instrumentation(&mut self) -> &mut dyn Instrumentation; + + /// Set a specific [`Instrumentation`] implementation for this connection + fn set_instrumentation(&mut self, instrumentation: impl Instrumentation); +} + +/// The specific part of a [`Connection`] which actually loads data from the database +/// +/// This is a separate trait to allow connection implementations to specify +/// different loading modes via the generic parameter. +pub trait LoadConnection: Connection { + /// The cursor type returned by [`LoadConnection::load`] + /// + /// Users should handle this as opaque type that implements [`Iterator`] + type Cursor<'conn, 'query>: Iterator< + Item = QueryResult<>::Row<'conn, 'query>>, + > + where + Self: 'conn; + + /// The row type used as [`Iterator::Item`] for the iterator implementation + /// of [`LoadConnection::Cursor`] + type Row<'conn, 'query>: crate::row::Row<'conn, Self::Backend> + where + Self: 'conn; + + /// Executes a given query and returns any requested values + /// + /// This function executes a given query and returns the + /// query result as given by the database. **Normal users + /// should not use this function**. Use + /// [`QueryDsl::load`](crate::QueryDsl) instead. + /// + /// This function is useful for people trying to build an alternative + /// dsl on top of diesel. It returns an [`impl Iterator>`](Iterator). + /// This type can be used to iterate over all rows returned by the database. + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + fn load<'conn, 'query, T>( + &'conn mut self, + source: T, + ) -> QueryResult> + where + T: Query + QueryFragment + QueryId + 'query, + Self::Backend: QueryMetadata; +} + +/// Describes a connection with an underlying [`crate::sql_types::TypeMetadata::MetadataLookup`] +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +pub trait WithMetadataLookup: Connection { + /// Retrieves the underlying metadata lookup + fn metadata_lookup(&mut self) -> &mut ::MetadataLookup; +} + +/// A variant of the [`Connection`](trait.Connection.html) trait that is +/// usable with dynamic dispatch +/// +/// If you are looking for a way to use pass database connections +/// for different database backends around in your application +/// this trait won't help you much. Normally you should only +/// need to use this trait if you are interacting with a connection +/// passed to a [`Migration`](../migration/trait.Migration.html) +pub trait BoxableConnection: SimpleConnection + std::any::Any { + /// Maps the current connection to `std::any::Any` + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + fn as_any(&self) -> &dyn std::any::Any; + + #[doc(hidden)] + fn as_any_mut(&mut self) -> &mut dyn std::any::Any; +} + +impl BoxableConnection for C +where + C: Connection + std::any::Any, +{ + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } +} + +/// The default loading mode provided by a [`Connection`]. +/// +/// Checkout the documentation of concrete connection types for details about +/// supported loading modes. +/// +/// All types implementing [`Connection`] should provide at least +/// a single [`LoadConnection`](self::LoadConnection) +/// implementation. +#[derive(Debug, Copy, Clone)] +pub struct DefaultLoadingMode; + +impl dyn BoxableConnection { + /// Downcast the current connection to a specific connection + /// type. + /// + /// This will return `None` if the underlying + /// connection does not match the corresponding + /// type, otherwise a reference to the underlying connection is returned + pub fn downcast_ref(&self) -> Option<&T> + where + T: Connection + 'static, + { + self.as_any().downcast_ref::() + } + + /// Downcast the current connection to a specific mutable connection + /// type. + /// + /// This will return `None` if the underlying + /// connection does not match the corresponding + /// type, otherwise a mutable reference to the underlying connection is returned + pub fn downcast_mut(&mut self) -> Option<&mut T> + where + T: Connection + 'static, + { + self.as_any_mut().downcast_mut::() + } + + /// Check if the current connection is + /// a specific connection type + pub fn is(&self) -> bool + where + T: Connection + 'static, + { + self.as_any().is::() + } +} + +// These traits are considered private for different reasons: +// +// `ConnectionSealed` to control who can implement `Connection`, +// so that we can later change the `Connection` trait +// +// `MultiConnectionHelper` is a workaround needed for the +// `MultiConnection` derive. We might stabilize this trait with +// the corresponding derive +// +// `ConnectionHelperType` as a workaround for the `LoadRowIter` +// type def. That trait should not be used by any user outside of diesel, +// it purely exists for backward compatibility reasons. +pub(crate) mod private { + + /// This trait restricts who can implement `Connection` + #[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) + )] + pub trait ConnectionSealed {} + + /// This trait provides helper methods to convert a database lookup type + /// to/from an `std::any::Any` reference. This is used internally by the `#[derive(MultiConnection)]` + /// implementation + #[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) + )] + pub trait MultiConnectionHelper: super::Connection { + /// Convert the lookup type to any + fn to_any<'a>( + lookup: &mut ::MetadataLookup, + ) -> &mut (dyn std::any::Any + 'a); + + /// Get the lookup type from any + fn from_any( + lookup: &mut dyn std::any::Any, + ) -> Option<&mut ::MetadataLookup>; + } + + // These impls are only there for backward compatibility reasons + // Remove them on the next breaking release + #[allow(unreachable_pub)] // must be pub for the type def using this trait + #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] + pub trait ConnectionHelperType: super::LoadConnection { + type Cursor<'conn, 'query> + where + Self: 'conn; + } + #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] + impl ConnectionHelperType for T + where + T: super::LoadConnection, + { + type Cursor<'conn, 'query> + = >::Cursor<'conn, 'query> + where + T: 'conn; + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/connection/statement_cache.rs b/collector/compile-benchmarks/diesel-2.2.10/src/connection/statement_cache.rs new file mode 100644 index 000000000..c88d6fcc3 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/connection/statement_cache.rs @@ -0,0 +1,390 @@ +//! Helper types for prepared statement caching +//! +//! A primer on prepared statement caching in Diesel +//! ------------------------------------------------ +//! +//! Diesel uses prepared statements for virtually all queries. This is most +//! visible in our lack of any sort of "quoting" API. Values must always be +//! transmitted as bind parameters, we do not support direct interpolation. The +//! only method in the public API that doesn't require the use of prepared +//! statements is [`SimpleConnection::batch_execute`](super::SimpleConnection::batch_execute). +//! +//! In order to avoid the cost of re-parsing and planning subsequent queries, +//! Diesel caches the prepared statement whenever possible. Queries will fall +//! into one of three buckets: +//! +//! - Unsafe to cache +//! - Cached by SQL +//! - Cached by type +//! +//! A query is considered unsafe to cache if it represents a potentially +//! unbounded number of queries. This is communicated to the connection through +//! [`QueryFragment::is_safe_to_cache_prepared`]. While this is done as a full AST +//! pass, after monomorphisation and inlining this will usually be optimized to +//! a constant. Only boxed queries will need to do actual work to answer this +//! question. +//! +//! The majority of AST nodes are safe to cache if their components are safe to +//! cache. There are at least 4 cases where a query is unsafe to cache: +//! +//! - queries containing `IN` with bind parameters +//! - This requires 1 bind parameter per value, and is therefore unbounded +//! - `IN` with subselects are cached (assuming the subselect is safe to +//! cache) +//! - `IN` statements for postgresql are cached as they use `= ANY($1)` instead +//! which does not cause an unbound number of binds +//! - `INSERT` statements with a variable number of rows +//! - The SQL varies based on the number of rows being inserted. +//! - `UPDATE` statements +//! - Technically it's bounded on "number of optional values being passed to +//! `SET` factorial" but that's still quite high, and not worth caching +//! for the same reason as single row inserts +//! - `SqlLiteral` nodes +//! - We have no way of knowing whether the SQL was generated dynamically or +//! not, so we must assume that it's unbounded +//! +//! For queries which are unsafe to cache, the statement cache will never insert +//! them. They will be prepared and immediately released after use (or in the +//! case of PG they will use the unnamed prepared statement). +//! +//! For statements which are able to be cached, we then have to determine what +//! to use as the cache key. The standard method that virtually all ORMs or +//! database access layers use in the wild is to store the statements in a +//! hash map, using the SQL as the key. +//! +//! However, the majority of queries using Diesel that are safe to cache as +//! prepared statements will be uniquely identified by their type. For these +//! queries, we can bypass the query builder entirely. Since our AST is +//! generally optimized away by the compiler, for these queries the cost of +//! fetching a prepared statement from the cache is the cost of [`HashMap::get`], where the key we're fetching by is a compile time constant. For +//! these types, the AST pass to gather the bind parameters will also be +//! optimized to accessing each parameter individually. +//! +//! Determining if a query can be cached by type is the responsibility of the +//! [`QueryId`] trait. This trait is quite similar to `Any`, but with a few +//! differences: +//! +//! - No `'static` bound +//! - Something being a reference never changes the SQL that is generated, +//! so `&T` has the same query id as `T`. +//! - `Option` instead of `TypeId` +//! - We need to be able to constrain on this trait being implemented, but +//! not all types will actually have a static query id. Hopefully once +//! specialization is stable we can remove the `QueryId` bound and +//! specialize on it instead (or provide a blanket impl for all `T`) +//! - Implementors give a more broad type than `Self` +//! - This really only affects bind parameters. There are 6 different Rust +//! types which can be used for a parameter of type `timestamp`. The same +//! statement can be used regardless of the Rust type, so [`Bound`](crate::expression::bound::Bound) +//! defines its [`QueryId`] as [`Bound`](crate::expression::bound::Bound). +//! +//! A type returning `Some(id)` or `None` for its query ID is based on whether +//! the SQL it generates can change without the type changing. At the moment, +//! the only type which is safe to cache as a prepared statement but does not +//! have a static query ID is something which has been boxed. +//! +//! One potential optimization that we don't perform is storing the queries +//! which are cached by type ID in a separate map. Since a type ID is a u64, +//! this would allow us to use a specialized map which knows that there will +//! never be hashing collisions (also known as a perfect hashing function), +//! which would mean lookups are always constant time. However, this would save +//! nanoseconds on an operation that will take microseconds or even +//! milliseconds. + +use std::any::TypeId; +use std::borrow::Cow; +use std::collections::HashMap; +use std::hash::Hash; +use std::ops::{Deref, DerefMut}; + +use crate::backend::Backend; +use crate::connection::InstrumentationEvent; +use crate::query_builder::*; +use crate::result::QueryResult; + +use super::Instrumentation; + +/// A prepared statement cache +#[allow(missing_debug_implementations, unreachable_pub)] +#[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) +)] +pub struct StatementCache { + pub(crate) cache: HashMap, Statement>, +} + +/// A helper type that indicates if a certain query +/// is cached inside of the prepared statement cache or not +/// +/// This information can be used by the connection implementation +/// to signal this fact to the database while actually +/// preparing the statement +#[derive(Debug, Clone, Copy)] +#[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) +)] +#[allow(unreachable_pub)] +pub enum PrepareForCache { + /// The statement will be cached + Yes, + /// The statement won't be cached + No, +} + +#[allow( + clippy::len_without_is_empty, + clippy::new_without_default, + unreachable_pub +)] +impl StatementCache +where + DB: Backend, + DB::TypeMetadata: Clone, + DB::QueryBuilder: Default, + StatementCacheKey: Hash + Eq, +{ + /// Create a new prepared statement cache + #[allow(unreachable_pub)] + pub fn new() -> Self { + StatementCache { + cache: HashMap::new(), + } + } + + /// Get the current length of the statement cache + #[allow(unreachable_pub)] + #[cfg(any( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + feature = "postgres", + all(feature = "sqlite", test) + ))] + #[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) + )] + pub fn len(&self) -> usize { + self.cache.len() + } + + /// Prepare a query as prepared statement + /// + /// This functions returns a prepared statement corresponding to the + /// query passed as `source` with the bind values passed as `bind_types`. + /// If the query is already cached inside this prepared statement cache + /// the cached prepared statement will be returned, otherwise `prepare_fn` + /// will be called to create a new prepared statement for this query source. + /// The first parameter of the callback contains the query string, the second + /// parameter indicates if the constructed prepared statement will be cached or not. + /// See the [module](self) documentation for details + /// about which statements are cached and which are not cached. + #[allow(unreachable_pub)] + pub fn cached_statement( + &mut self, + source: &T, + backend: &DB, + bind_types: &[DB::TypeMetadata], + mut prepare_fn: F, + instrumentation: &mut dyn Instrumentation, + ) -> QueryResult> + where + T: QueryFragment + QueryId, + F: FnMut(&str, PrepareForCache) -> QueryResult, + { + self.cached_statement_non_generic( + T::query_id(), + source, + backend, + bind_types, + &mut prepare_fn, + instrumentation, + ) + } + + /// Reduce the amount of monomorphized code by factoring this via dynamic dispatch + fn cached_statement_non_generic( + &mut self, + maybe_type_id: Option, + source: &dyn QueryFragmentForCachedStatement, + backend: &DB, + bind_types: &[DB::TypeMetadata], + prepare_fn: &mut dyn FnMut(&str, PrepareForCache) -> QueryResult, + instrumentation: &mut dyn Instrumentation, + ) -> QueryResult> { + use std::collections::hash_map::Entry::{Occupied, Vacant}; + + let cache_key = StatementCacheKey::for_source(maybe_type_id, source, bind_types, backend)?; + + if !source.is_safe_to_cache_prepared(backend)? { + let sql = cache_key.sql(source, backend)?; + return prepare_fn(&sql, PrepareForCache::No).map(MaybeCached::CannotCache); + } + + let cached_result = match self.cache.entry(cache_key) { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => { + let statement = { + let sql = entry.key().sql(source, backend)?; + instrumentation + .on_connection_event(InstrumentationEvent::CacheQuery { sql: &sql }); + prepare_fn(&sql, PrepareForCache::Yes) + }; + + entry.insert(statement?) + } + }; + + Ok(MaybeCached::Cached(cached_result)) + } +} + +/// Implemented for all `QueryFragment`s, dedicated to dynamic dispatch within the context of +/// `statement_cache` +/// +/// We want the generated code to be as small as possible, so for each query passed to +/// [`StatementCache::cached_statement`] the generated assembly will just call a non generic +/// version with dynamic dispatch pointing to the VTABLE of this minimal trait +/// +/// This preserves the opportunity for the compiler to entirely optimize the `construct_sql` +/// function as a function that simply returns a constant `String`. +#[allow(unreachable_pub)] +#[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) +)] +pub trait QueryFragmentForCachedStatement { + /// Convert the query fragment into a SQL string for the given backend + fn construct_sql(&self, backend: &DB) -> QueryResult; + /// Check whether it's safe to cache the query + fn is_safe_to_cache_prepared(&self, backend: &DB) -> QueryResult; +} +impl QueryFragmentForCachedStatement for T +where + DB: Backend, + DB::QueryBuilder: Default, + T: QueryFragment, +{ + fn construct_sql(&self, backend: &DB) -> QueryResult { + let mut query_builder = DB::QueryBuilder::default(); + self.to_sql(&mut query_builder, backend)?; + Ok(query_builder.finish()) + } + + fn is_safe_to_cache_prepared(&self, backend: &DB) -> QueryResult { + >::is_safe_to_cache_prepared(self, backend) + } +} + +/// Wraps a possibly cached prepared statement +/// +/// Essentially a customized version of [`Cow`] +/// that does not depend on [`ToOwned`] +#[allow(missing_debug_implementations, unreachable_pub)] +#[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) +)] +#[non_exhaustive] +pub enum MaybeCached<'a, T: 'a> { + /// Contains a not cached prepared statement + CannotCache(T), + /// Contains a reference cached prepared statement + Cached(&'a mut T), +} + +impl Deref for MaybeCached<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match *self { + MaybeCached::CannotCache(ref x) => x, + MaybeCached::Cached(ref x) => x, + } + } +} + +impl DerefMut for MaybeCached<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + match *self { + MaybeCached::CannotCache(ref mut x) => x, + MaybeCached::Cached(ref mut x) => x, + } + } +} + +/// The lookup key used by [`StatementCache`] internally +/// +/// This can contain either a at compile time known type id +/// (representing a statically known query) or a at runtime +/// calculated query string + parameter types (for queries +/// that may change depending on their parameters) +#[allow(missing_debug_implementations, unreachable_pub)] +#[derive(Hash, PartialEq, Eq)] +#[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) +)] +pub enum StatementCacheKey { + /// Represents a at compile time known query + /// + /// Calculated via [`QueryId::QueryId`] + Type(TypeId), + /// Represents a dynamically constructed query + /// + /// This variant is used if [`QueryId::HAS_STATIC_QUERY_ID`] + /// is `false` and [`AstPass::unsafe_to_cache_prepared`] is not + /// called for a given query. + Sql { + /// contains the sql query string + sql: String, + /// contains the types of any bind parameter passed to the query + bind_types: Vec, + }, +} + +impl StatementCacheKey +where + DB: Backend, + DB::QueryBuilder: Default, + DB::TypeMetadata: Clone, +{ + /// Create a new statement cache key for the given query source + // Note: Intentionally monomorphic over source. + #[allow(unreachable_pub)] + pub fn for_source( + maybe_type_id: Option, + source: &dyn QueryFragmentForCachedStatement, + bind_types: &[DB::TypeMetadata], + backend: &DB, + ) -> QueryResult { + match maybe_type_id { + Some(id) => Ok(StatementCacheKey::Type(id)), + None => { + let sql = source.construct_sql(backend)?; + Ok(StatementCacheKey::Sql { + sql, + bind_types: bind_types.into(), + }) + } + } + } + + /// Get the sql for a given query source based + /// + /// This is an optimization that may skip constructing the query string + /// twice if it's already part of the current cache key + // Note: Intentionally monomorphic over source. + #[allow(unreachable_pub)] + pub fn sql( + &self, + source: &dyn QueryFragmentForCachedStatement, + backend: &DB, + ) -> QueryResult> { + match *self { + StatementCacheKey::Type(_) => source.construct_sql(backend).map(Cow::Owned), + StatementCacheKey::Sql { ref sql, .. } => Ok(Cow::Borrowed(sql)), + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/connection/transaction_manager.rs b/collector/compile-benchmarks/diesel-2.2.10/src/connection/transaction_manager.rs new file mode 100644 index 000000000..3426d65e7 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/connection/transaction_manager.rs @@ -0,0 +1,1252 @@ +use crate::connection::Connection; +use crate::result::{Error, QueryResult}; +use std::borrow::Cow; +use std::num::NonZeroU32; + +/// Manages the internal transaction state for a connection. +/// +/// You will not need to interact with this trait, unless you are writing an +/// implementation of [`Connection`]. +pub trait TransactionManager { + /// Data stored as part of the connection implementation + /// to track the current transaction state of a connection + type TransactionStateData; + + /// Begin a new transaction or savepoint + /// + /// If the transaction depth is greater than 0, + /// this should create a savepoint instead. + /// This function is expected to increment the transaction depth by 1. + fn begin_transaction(conn: &mut Conn) -> QueryResult<()>; + + /// Rollback the inner-most transaction or savepoint + /// + /// If the transaction depth is greater than 1, + /// this should rollback to the most recent savepoint. + /// This function is expected to decrement the transaction depth by 1. + fn rollback_transaction(conn: &mut Conn) -> QueryResult<()>; + + /// Commit the inner-most transaction or savepoint + /// + /// If the transaction depth is greater than 1, + /// this should release the most recent savepoint. + /// This function is expected to decrement the transaction depth by 1. + fn commit_transaction(conn: &mut Conn) -> QueryResult<()>; + + /// Fetch the current transaction status as mutable + /// + /// Used to ensure that `begin_test_transaction` is not called when already + /// inside of a transaction, and that operations are not run in a `InError` + /// transaction manager. + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + fn transaction_manager_status_mut(conn: &mut Conn) -> &mut TransactionManagerStatus; + + /// Executes the given function inside of a database transaction + /// + /// Each implementation of this function needs to fulfill the documented + /// behaviour of [`Connection::transaction`] + fn transaction(conn: &mut Conn, callback: F) -> Result + where + F: FnOnce(&mut Conn) -> Result, + E: From, + { + Self::begin_transaction(conn)?; + match callback(&mut *conn) { + Ok(value) => { + Self::commit_transaction(conn)?; + Ok(value) + } + Err(user_error) => match Self::rollback_transaction(conn) { + Ok(()) => Err(user_error), + Err(Error::BrokenTransactionManager) => { + // In this case we are probably more interested by the + // original error, which likely caused this + Err(user_error) + } + Err(rollback_error) => Err(rollback_error.into()), + }, + } + } + + /// This methods checks if the connection manager is considered to be broken + /// by connection pool implementations + /// + /// A connection manager is considered to be broken by default if it either + /// contains an open transaction (because you don't want to have connections + /// with open transactions in your pool) or when the transaction manager is + /// in an error state. + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + fn is_broken_transaction_manager(conn: &mut Conn) -> bool { + match Self::transaction_manager_status_mut(conn).transaction_state() { + // all transactions are closed + // so we don't consider this connection broken + Ok(ValidTransactionManagerStatus { + in_transaction: None, + }) => false, + // The transaction manager is in an error state + // Therefore we consider this connection broken + Err(_) => true, + // The transaction manager contains a open transaction + // we do consider this connection broken + // if that transaction was not opened by `begin_test_transaction` + Ok(ValidTransactionManagerStatus { + in_transaction: Some(s), + }) => !s.test_transaction, + } + } +} + +/// An implementation of `TransactionManager` which can be used for backends +/// which use ANSI standard syntax for savepoints such as SQLite and PostgreSQL. +#[derive(Default, Debug)] +pub struct AnsiTransactionManager { + pub(crate) status: TransactionManagerStatus, +} + +/// Status of the transaction manager +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +#[derive(Debug)] +pub enum TransactionManagerStatus { + /// Valid status, the manager can run operations + Valid(ValidTransactionManagerStatus), + /// Error status, probably following a broken connection. The manager will no longer run operations + InError, +} + +impl Default for TransactionManagerStatus { + fn default() -> Self { + TransactionManagerStatus::Valid(ValidTransactionManagerStatus::default()) + } +} + +impl TransactionManagerStatus { + /// Returns the transaction depth if the transaction manager's status is valid, or returns + /// [`Error::BrokenTransactionManager`] if the transaction manager is in error. + pub fn transaction_depth(&self) -> QueryResult> { + match self { + TransactionManagerStatus::Valid(valid_status) => Ok(valid_status.transaction_depth()), + TransactionManagerStatus::InError => Err(Error::BrokenTransactionManager), + } + } + + #[cfg(any( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + feature = "postgres", + feature = "mysql", + test + ))] + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + /// If in transaction and transaction manager is not broken, registers that it's possible that + /// the connection can not be used anymore until top-level transaction is rolled back. + /// + /// If that is registered, savepoints rollbacks will still be attempted, but failure to do so + /// will not result in an error. (Some may succeed, some may not.) + pub(crate) fn set_requires_rollback_maybe_up_to_top_level(&mut self, to: bool) { + if let TransactionManagerStatus::Valid(ValidTransactionManagerStatus { + in_transaction: + Some(InTransactionStatus { + requires_rollback_maybe_up_to_top_level, + .. + }), + }) = self + { + *requires_rollback_maybe_up_to_top_level = to; + } + } + + /// Sets the transaction manager status to InError + /// + /// Subsequent attempts to use transaction-related features will result in a + /// [`Error::BrokenTransactionManager`] error + pub fn set_in_error(&mut self) { + *self = TransactionManagerStatus::InError + } + + /// Expose access to the inner transaction state + /// + /// This function returns an error if the Transaction manager is in a broken + /// state + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub(self) fn transaction_state(&mut self) -> QueryResult<&mut ValidTransactionManagerStatus> { + match self { + TransactionManagerStatus::Valid(valid_status) => Ok(valid_status), + TransactionManagerStatus::InError => Err(Error::BrokenTransactionManager), + } + } + + /// This function allows to flag a transaction manager + /// in such a way that it contains a test transaction. + /// + /// This will disable some checks in regards to open transactions + /// to allow `Connection::begin_test_transaction` to work with + /// pooled connections as well + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub(crate) fn set_test_transaction_flag(&mut self) { + if let TransactionManagerStatus::Valid(ValidTransactionManagerStatus { + in_transaction: Some(s), + }) = self + { + s.test_transaction = true; + } + } +} + +/// Valid transaction status for the manager. Can return the current transaction depth +#[allow(missing_copy_implementations)] +#[derive(Debug, Default)] +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + public_fields(in_transaction) +)] +pub struct ValidTransactionManagerStatus { + /// Inner status, or `None` if no transaction is running + in_transaction: Option, +} + +/// Various status fields to track the status of +/// a transaction manager with a started transaction +#[allow(missing_copy_implementations)] +#[derive(Debug)] +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + public_fields( + test_transaction, + transaction_depth, + requires_rollback_maybe_up_to_top_level + ) +)] +pub struct InTransactionStatus { + /// The current depth of nested transactions + transaction_depth: NonZeroU32, + /// If that is registered, savepoints rollbacks will still be attempted, but failure to do so + /// will not result in an error. (Some may succeed, some may not.) + requires_rollback_maybe_up_to_top_level: bool, + /// Is this transaction manager status marked as test-transaction? + test_transaction: bool, +} + +impl ValidTransactionManagerStatus { + /// Return the current transaction depth + /// + /// This value is `None` if no current transaction is running + /// otherwise the number of nested transactions is returned. + pub fn transaction_depth(&self) -> Option { + self.in_transaction.as_ref().map(|it| it.transaction_depth) + } + + /// Update the transaction depth by adding the value of the `transaction_depth_change` parameter if the `query` is + /// `Ok(())` + pub fn change_transaction_depth( + &mut self, + transaction_depth_change: TransactionDepthChange, + ) -> QueryResult<()> { + match (&mut self.in_transaction, transaction_depth_change) { + (Some(in_transaction), TransactionDepthChange::IncreaseDepth) => { + // Can be replaced with saturating_add directly on NonZeroU32 once + // is stable + in_transaction.transaction_depth = + NonZeroU32::new(in_transaction.transaction_depth.get().saturating_add(1)) + .expect("nz + nz is always non-zero"); + Ok(()) + } + (Some(in_transaction), TransactionDepthChange::DecreaseDepth) => { + // This sets `transaction_depth` to `None` as soon as we reach zero + match NonZeroU32::new(in_transaction.transaction_depth.get() - 1) { + Some(depth) => in_transaction.transaction_depth = depth, + None => self.in_transaction = None, + } + Ok(()) + } + (None, TransactionDepthChange::IncreaseDepth) => { + self.in_transaction = Some(InTransactionStatus { + transaction_depth: NonZeroU32::new(1).expect("1 is non-zero"), + requires_rollback_maybe_up_to_top_level: false, + test_transaction: false, + }); + Ok(()) + } + (None, TransactionDepthChange::DecreaseDepth) => { + // We screwed up something somewhere + // we cannot decrease the transaction count if + // we are not inside a transaction + Err(Error::NotInTransaction) + } + } + } +} + +/// Represents a change to apply to the depth of a transaction +#[derive(Debug, Clone, Copy)] +pub enum TransactionDepthChange { + /// Increase the depth of the transaction (corresponds to `BEGIN` or `SAVEPOINT`) + IncreaseDepth, + /// Decreases the depth of the transaction (corresponds to `COMMIT`/`RELEASE SAVEPOINT` or `ROLLBACK`) + DecreaseDepth, +} + +impl AnsiTransactionManager { + fn get_transaction_state( + conn: &mut Conn, + ) -> QueryResult<&mut ValidTransactionManagerStatus> + where + Conn: Connection, + { + conn.transaction_state().status.transaction_state() + } + + /// Begin a transaction with custom SQL + /// + /// This is used by connections to implement more complex transaction APIs + /// to set things such as isolation levels. + /// Returns an error if already inside of a transaction. + pub fn begin_transaction_sql(conn: &mut Conn, sql: &str) -> QueryResult<()> + where + Conn: Connection, + { + let state = Self::get_transaction_state(conn)?; + if let Some(_depth) = state.transaction_depth() { + return Err(Error::AlreadyInTransaction); + } + let instrumentation_depth = NonZeroU32::new(1); + // Keep remainder of this method in sync with `begin_transaction()`. + + conn.instrumentation().on_connection_event( + super::instrumentation::InstrumentationEvent::BeginTransaction { + depth: instrumentation_depth.expect("We know that 1 is not zero"), + }, + ); + conn.batch_execute(sql)?; + Self::get_transaction_state(conn)? + .change_transaction_depth(TransactionDepthChange::IncreaseDepth)?; + + Ok(()) + } +} + +impl TransactionManager for AnsiTransactionManager +where + Conn: Connection, +{ + type TransactionStateData = Self; + + fn begin_transaction(conn: &mut Conn) -> QueryResult<()> { + let transaction_state = Self::get_transaction_state(conn)?; + let transaction_depth = transaction_state.transaction_depth(); + let start_transaction_sql = match transaction_depth { + None => Cow::from("BEGIN"), + Some(transaction_depth) => { + Cow::from(format!("SAVEPOINT diesel_savepoint_{transaction_depth}")) + } + }; + let instrumentation_depth = + NonZeroU32::new(transaction_depth.map_or(0, NonZeroU32::get).wrapping_add(1)); + let sql = &start_transaction_sql; + // Keep remainder of this method in sync with `begin_transaction_sql()`. + + conn.instrumentation().on_connection_event( + super::instrumentation::InstrumentationEvent::BeginTransaction { + depth: instrumentation_depth.expect("Transaction depth is too large"), + }, + ); + conn.batch_execute(sql)?; + Self::get_transaction_state(conn)? + .change_transaction_depth(TransactionDepthChange::IncreaseDepth)?; + + Ok(()) + } + + fn rollback_transaction(conn: &mut Conn) -> QueryResult<()> { + let transaction_state = Self::get_transaction_state(conn)?; + + let ( + (rollback_sql, rolling_back_top_level), + requires_rollback_maybe_up_to_top_level_before_execute, + ) = match transaction_state.in_transaction { + Some(ref in_transaction) => ( + match in_transaction.transaction_depth.get() { + 1 => (Cow::Borrowed("ROLLBACK"), true), + depth_gt1 => ( + Cow::Owned(format!( + "ROLLBACK TO SAVEPOINT diesel_savepoint_{}", + depth_gt1 - 1 + )), + false, + ), + }, + in_transaction.requires_rollback_maybe_up_to_top_level, + ), + None => return Err(Error::NotInTransaction), + }; + let depth = transaction_state + .transaction_depth() + .expect("We know that we are in a transaction here"); + conn.instrumentation().on_connection_event( + super::instrumentation::InstrumentationEvent::RollbackTransaction { depth }, + ); + + match conn.batch_execute(&rollback_sql) { + Ok(()) => { + match Self::get_transaction_state(conn)? + .change_transaction_depth(TransactionDepthChange::DecreaseDepth) + { + Ok(()) => {} + Err(Error::NotInTransaction) if rolling_back_top_level => { + // Transaction exit may have already been detected by connection + // implementation. It's fine. + } + Err(e) => return Err(e), + } + Ok(()) + } + Err(rollback_error) => { + let tm_status = Self::transaction_manager_status_mut(conn); + match tm_status { + TransactionManagerStatus::Valid(ValidTransactionManagerStatus { + in_transaction: + Some(InTransactionStatus { + transaction_depth, + requires_rollback_maybe_up_to_top_level, + .. + }), + }) if transaction_depth.get() > 1 => { + // A savepoint failed to rollback - we may still attempt to repair + // the connection by rolling back higher levels. + + // To make it easier on the user (that they don't have to really + // look at actual transaction depth and can just rely on the number + // of times they have called begin/commit/rollback) we still + // decrement here: + *transaction_depth = NonZeroU32::new(transaction_depth.get() - 1) + .expect("Depth was checked to be > 1"); + *requires_rollback_maybe_up_to_top_level = true; + if requires_rollback_maybe_up_to_top_level_before_execute { + // In that case, we tolerate that savepoint releases fail + // -> we should ignore errors + return Ok(()); + } + } + TransactionManagerStatus::Valid(ValidTransactionManagerStatus { + in_transaction: None, + }) => { + // we would have returned `NotInTransaction` if that was already the state + // before we made our call + // => Transaction manager status has been fixed by the underlying connection + // so we don't need to set_in_error + } + _ => tm_status.set_in_error(), + } + Err(rollback_error) + } + } + } + + /// If the transaction fails to commit due to a `SerializationFailure` or a + /// `ReadOnlyTransaction` a rollback will be attempted. If the rollback succeeds, + /// the original error will be returned, otherwise the error generated by the rollback + /// will be returned. In the second case the connection will be considered broken + /// as it contains a uncommitted unabortable open transaction. + fn commit_transaction(conn: &mut Conn) -> QueryResult<()> { + let transaction_state = Self::get_transaction_state(conn)?; + let transaction_depth = transaction_state.transaction_depth(); + let (commit_sql, committing_top_level) = match transaction_depth { + None => return Err(Error::NotInTransaction), + Some(transaction_depth) if transaction_depth.get() == 1 => { + (Cow::Borrowed("COMMIT"), true) + } + Some(transaction_depth) => ( + Cow::Owned(format!( + "RELEASE SAVEPOINT diesel_savepoint_{}", + transaction_depth.get() - 1 + )), + false, + ), + }; + let depth = transaction_state + .transaction_depth() + .expect("We know that we are in a transaction here"); + conn.instrumentation().on_connection_event( + super::instrumentation::InstrumentationEvent::CommitTransaction { depth }, + ); + match conn.batch_execute(&commit_sql) { + Ok(()) => { + match Self::get_transaction_state(conn)? + .change_transaction_depth(TransactionDepthChange::DecreaseDepth) + { + Ok(()) => {} + Err(Error::NotInTransaction) if committing_top_level => { + // Transaction exit may have already been detected by connection. + // It's fine + } + Err(e) => return Err(e), + } + Ok(()) + } + Err(commit_error) => { + if let TransactionManagerStatus::Valid(ValidTransactionManagerStatus { + in_transaction: + Some(InTransactionStatus { + requires_rollback_maybe_up_to_top_level: true, + .. + }), + }) = conn.transaction_state().status + { + match Self::rollback_transaction(conn) { + Ok(()) => {} + Err(rollback_error) => { + conn.transaction_state().status.set_in_error(); + return Err(Error::RollbackErrorOnCommit { + rollback_error: Box::new(rollback_error), + commit_error: Box::new(commit_error), + }); + } + } + } + Err(commit_error) + } + } + } + + fn transaction_manager_status_mut(conn: &mut Conn) -> &mut TransactionManagerStatus { + &mut conn.transaction_state().status + } +} + +#[cfg(test)] +// that's a false positive for `panic!`/`assert!` on rust 2018 +#[allow(clippy::uninlined_format_args)] +mod test { + // Mock connection. + mod mock { + use crate::connection::transaction_manager::AnsiTransactionManager; + use crate::connection::Instrumentation; + use crate::connection::{ + Connection, ConnectionSealed, SimpleConnection, TransactionManager, + }; + use crate::result::QueryResult; + use crate::test_helpers::TestConnection; + use std::collections::VecDeque; + + pub(crate) struct MockConnection { + pub(crate) next_results: VecDeque>, + pub(crate) next_batch_execute_results: VecDeque>, + pub(crate) top_level_requires_rollback_after_next_batch_execute: bool, + transaction_state: AnsiTransactionManager, + instrumentation: Option>, + } + + impl SimpleConnection for MockConnection { + fn batch_execute(&mut self, _query: &str) -> QueryResult<()> { + let res = self + .next_batch_execute_results + .pop_front() + .expect("No next result"); + if self.top_level_requires_rollback_after_next_batch_execute { + self.transaction_state + .status + .set_requires_rollback_maybe_up_to_top_level(true); + } + res + } + } + + impl ConnectionSealed for MockConnection {} + + impl Connection for MockConnection { + type Backend = ::Backend; + + type TransactionManager = AnsiTransactionManager; + + fn establish(_database_url: &str) -> crate::ConnectionResult { + Ok(Self { + next_results: VecDeque::new(), + next_batch_execute_results: VecDeque::new(), + top_level_requires_rollback_after_next_batch_execute: false, + transaction_state: AnsiTransactionManager::default(), + instrumentation: None, + }) + } + + fn execute_returning_count(&mut self, _source: &T) -> QueryResult + where + T: crate::query_builder::QueryFragment + + crate::query_builder::QueryId, + { + self.next_results.pop_front().expect("No next result") + } + + fn transaction_state( + &mut self, + ) -> &mut >::TransactionStateData + { + &mut self.transaction_state + } + + fn instrumentation(&mut self) -> &mut dyn crate::connection::Instrumentation { + &mut self.instrumentation + } + + fn set_instrumentation( + &mut self, + instrumentation: impl crate::connection::Instrumentation, + ) { + self.instrumentation = Some(Box::new(instrumentation)); + } + } + } + + #[test] + #[cfg(feature = "postgres")] + fn transaction_manager_returns_an_error_when_attempting_to_commit_outside_of_a_transaction() { + use crate::connection::transaction_manager::AnsiTransactionManager; + use crate::connection::transaction_manager::TransactionManager; + use crate::result::Error; + use crate::PgConnection; + + let conn = &mut crate::test_helpers::pg_connection_no_transaction(); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + let result = AnsiTransactionManager::commit_transaction(conn); + assert!(matches!(result, Err(Error::NotInTransaction))) + } + + #[test] + #[cfg(feature = "postgres")] + fn transaction_manager_returns_an_error_when_attempting_to_rollback_outside_of_a_transaction() { + use crate::connection::transaction_manager::AnsiTransactionManager; + use crate::connection::transaction_manager::TransactionManager; + use crate::result::Error; + use crate::PgConnection; + + let conn = &mut crate::test_helpers::pg_connection_no_transaction(); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + let result = AnsiTransactionManager::rollback_transaction(conn); + assert!(matches!(result, Err(Error::NotInTransaction))) + } + + #[test] + fn transaction_manager_enters_broken_state_when_connection_is_broken() { + use crate::connection::transaction_manager::AnsiTransactionManager; + use crate::connection::transaction_manager::TransactionManager; + use crate::connection::TransactionManagerStatus; + use crate::result::{DatabaseErrorKind, Error}; + use crate::*; + + let mut conn = mock::MockConnection::establish("mock").expect("Mock connection"); + + // Set result for BEGIN + conn.next_batch_execute_results.push_back(Ok(())); + let result = conn.transaction(|conn| { + conn.next_results.push_back(Ok(1)); + let query_result = sql_query("SELECT 1").execute(conn); + assert!(query_result.is_ok()); + // Set result for COMMIT attempt + conn.next_batch_execute_results + .push_back(Err(Error::DatabaseError( + DatabaseErrorKind::Unknown, + Box::new("commit fails".to_string()), + ))); + conn.top_level_requires_rollback_after_next_batch_execute = true; + conn.next_batch_execute_results + .push_back(Err(Error::DatabaseError( + DatabaseErrorKind::Unknown, + Box::new("rollback also fails".to_string()), + ))); + Ok(()) + }); + assert!( + matches!( + &result, + Err(Error::RollbackErrorOnCommit { + rollback_error, + commit_error + }) if matches!(**commit_error, Error::DatabaseError(DatabaseErrorKind::Unknown, _)) + && matches!(&**rollback_error, + Error::DatabaseError(DatabaseErrorKind::Unknown, msg) + if msg.message() == "rollback also fails" + ) + ), + "Got {:?}", + result + ); + assert!(matches!( + *AnsiTransactionManager::transaction_manager_status_mut(&mut conn), + TransactionManagerStatus::InError + )); + // Ensure the transaction manager is unusable + let result = conn.transaction(|_conn| Ok(())); + assert!(matches!(result, Err(Error::BrokenTransactionManager))) + } + + #[test] + #[cfg(feature = "mysql")] + fn mysql_transaction_is_rolled_back_upon_syntax_error() { + use crate::connection::transaction_manager::AnsiTransactionManager; + use crate::connection::transaction_manager::TransactionManager; + use crate::*; + use std::num::NonZeroU32; + + let conn = &mut crate::test_helpers::connection_no_transaction(); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + let _result = conn.transaction(|conn| { + assert_eq!( + NonZeroU32::new(1), + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + // In MySQL, a syntax error does not break the transaction block + let query_result = sql_query("SELECT_SYNTAX_ERROR 1").execute(conn); + assert!(query_result.is_err()); + query_result + }); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + } + + #[test] + #[cfg(feature = "sqlite")] + fn sqlite_transaction_is_rolled_back_upon_syntax_error() { + use crate::connection::transaction_manager::AnsiTransactionManager; + use crate::connection::transaction_manager::TransactionManager; + use crate::*; + use std::num::NonZeroU32; + + let conn = &mut crate::test_helpers::connection(); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + let _result = conn.transaction(|conn| { + assert_eq!( + NonZeroU32::new(1), + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + // In Sqlite, a syntax error does not break the transaction block + let query_result = sql_query("SELECT_SYNTAX_ERROR 1").execute(conn); + assert!(query_result.is_err()); + query_result + }); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + } + + #[test] + #[cfg(feature = "mysql")] + fn nested_mysql_transaction_is_rolled_back_upon_syntax_error() { + use crate::connection::transaction_manager::AnsiTransactionManager; + use crate::connection::transaction_manager::TransactionManager; + use crate::*; + use std::num::NonZeroU32; + + let conn = &mut crate::test_helpers::connection_no_transaction(); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + let result = conn.transaction(|conn| { + assert_eq!( + NonZeroU32::new(1), + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + let result = conn.transaction(|conn| { + assert_eq!( + NonZeroU32::new(2), + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + // In MySQL, a syntax error does not break the transaction block + sql_query("SELECT_SYNTAX_ERROR 1").execute(conn) + }); + assert!(result.is_err()); + assert_eq!( + NonZeroU32::new(1), + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + let query_result = sql_query("SELECT 1").execute(conn); + assert!(query_result.is_ok()); + query_result + }); + assert!(result.is_ok()); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + } + + #[test] + #[cfg(feature = "mysql")] + // This function uses a collect with side effects (spawning threads) + // so clippy is wrong here + #[allow(clippy::needless_collect)] + fn mysql_transaction_depth_commits_tracked_properly_on_serialization_failure() { + use crate::result::DatabaseErrorKind::SerializationFailure; + use crate::result::Error::DatabaseError; + use crate::*; + use std::num::NonZeroU32; + use std::sync::{Arc, Barrier}; + use std::thread; + + table! { + #[sql_name = "mysql_transaction_depth_is_tracked_properly_on_commit_failure"] + serialization_example { + id -> Integer, + class -> Integer, + } + } + + let conn = &mut crate::test_helpers::connection_no_transaction(); + + sql_query( + "DROP TABLE IF EXISTS mysql_transaction_depth_is_tracked_properly_on_commit_failure;", + ) + .execute(conn) + .unwrap(); + sql_query( + r#" + CREATE TABLE mysql_transaction_depth_is_tracked_properly_on_commit_failure ( + id INT AUTO_INCREMENT PRIMARY KEY, + class INTEGER NOT NULL + ) + "#, + ) + .execute(conn) + .unwrap(); + + insert_into(serialization_example::table) + .values(&vec![ + serialization_example::class.eq(1), + serialization_example::class.eq(2), + ]) + .execute(conn) + .unwrap(); + + let before_barrier = Arc::new(Barrier::new(2)); + let after_barrier = Arc::new(Barrier::new(2)); + + let threads = (1..3) + .map(|i| { + let before_barrier = before_barrier.clone(); + let after_barrier = after_barrier.clone(); + thread::spawn(move || { + use crate::connection::transaction_manager::AnsiTransactionManager; + use crate::connection::transaction_manager::TransactionManager; + let conn = &mut crate::test_helpers::connection_no_transaction(); + assert_eq!(None, >::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth")); + crate::sql_query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE").execute(conn)?; + + let result = + conn.transaction(|conn| { + assert_eq!(NonZeroU32::new(1), >::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth")); + let _ = serialization_example::table + .filter(serialization_example::class.eq(i)) + .count() + .execute(conn)?; + + let other_i = if i == 1 { 2 } else { 1 }; + let q = insert_into(serialization_example::table) + .values(serialization_example::class.eq(other_i)); + before_barrier.wait(); + + let r = q.execute(conn); + after_barrier.wait(); + r + }); + + assert_eq!(None, >::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth")); + + let second_trans_result = conn.transaction(|conn| crate::sql_query("SELECT 1").execute(conn)); + assert!(second_trans_result.is_ok(), "Expected the thread connections to have been rolled back or committed, but second transaction exited with {:?}", second_trans_result); + result + }) + }) + .collect::>(); + let second_trans_result = + conn.transaction(|conn| crate::sql_query("SELECT 1").execute(conn)); + assert!(second_trans_result.is_ok(), "Expected the main connection to have been rolled back or committed, but second transaction exited with {:?}", second_trans_result); + + let mut results = threads + .into_iter() + .map(|t| t.join().unwrap()) + .collect::>(); + + results.sort_by_key(|r| r.is_err()); + assert!(results[0].is_ok(), "Got {:?} instead", results); + // Note that contrary to Postgres, this is not a commit failure + assert!( + matches!(&results[1], Err(DatabaseError(SerializationFailure, _))), + "Got {:?} instead", + results + ); + } + + #[test] + #[cfg(feature = "mysql")] + // This function uses a collect with side effects (spawning threads) + // so clippy is wrong here + #[allow(clippy::needless_collect)] + fn mysql_nested_transaction_depth_commits_tracked_properly_on_serialization_failure() { + use crate::result::DatabaseErrorKind::SerializationFailure; + use crate::result::Error::DatabaseError; + use crate::*; + use std::num::NonZeroU32; + use std::sync::{Arc, Barrier}; + use std::thread; + + table! { + #[sql_name = "mysql_nested_trans_depth_is_tracked_properly_on_commit_failure"] + serialization_example { + id -> Integer, + class -> Integer, + } + } + + let conn = &mut crate::test_helpers::connection_no_transaction(); + + sql_query( + "DROP TABLE IF EXISTS mysql_nested_trans_depth_is_tracked_properly_on_commit_failure;", + ) + .execute(conn) + .unwrap(); + sql_query( + r#" + CREATE TABLE mysql_nested_trans_depth_is_tracked_properly_on_commit_failure ( + id INT AUTO_INCREMENT PRIMARY KEY, + class INTEGER NOT NULL + ) + "#, + ) + .execute(conn) + .unwrap(); + + insert_into(serialization_example::table) + .values(&vec![ + serialization_example::class.eq(1), + serialization_example::class.eq(2), + ]) + .execute(conn) + .unwrap(); + + let before_barrier = Arc::new(Barrier::new(2)); + let after_barrier = Arc::new(Barrier::new(2)); + + let threads = (1..3) + .map(|i| { + let before_barrier = before_barrier.clone(); + let after_barrier = after_barrier.clone(); + thread::spawn(move || { + use crate::connection::transaction_manager::AnsiTransactionManager; + use crate::connection::transaction_manager::TransactionManager; + let conn = &mut crate::test_helpers::connection_no_transaction(); + assert_eq!(None, >::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth")); + crate::sql_query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE").execute(conn)?; + + let result = + conn.transaction(|conn| { + assert_eq!(NonZeroU32::new(1), >::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth")); + conn.transaction(|conn| { + assert_eq!(NonZeroU32::new(2), >::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth")); + let _ = serialization_example::table + .filter(serialization_example::class.eq(i)) + .count() + .execute(conn)?; + + let other_i = if i == 1 { 2 } else { 1 }; + let q = insert_into(serialization_example::table) + .values(serialization_example::class.eq(other_i)); + before_barrier.wait(); + + let r = q.execute(conn); + after_barrier.wait(); + r + }) + }); + + assert_eq!(None, >::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth")); + + let second_trans_result = conn.transaction(|conn| crate::sql_query("SELECT 1").execute(conn)); + assert!(second_trans_result.is_ok(), "Expected the thread connections to have been rolled back or committed, but second transaction exited with {:?}", second_trans_result); + result + }) + }) + .collect::>(); + let second_trans_result = + conn.transaction(|conn| crate::sql_query("SELECT 1").execute(conn)); + assert!(second_trans_result.is_ok(), "Expected the main connection to have been rolled back or committed, but second transaction exited with {:?}", second_trans_result); + + let mut results = threads + .into_iter() + .map(|t| t.join().unwrap()) + .collect::>(); + + results.sort_by_key(|r| r.is_err()); + assert!(results[0].is_ok(), "Got {:?} instead", results); + assert!( + matches!(&results[1], Err(DatabaseError(SerializationFailure, _))), + "Got {:?} instead", + results + ); + } + + #[test] + #[cfg(feature = "sqlite")] + fn sqlite_transaction_is_rolled_back_upon_deferred_constraint_failure() { + use crate::connection::transaction_manager::AnsiTransactionManager; + use crate::connection::transaction_manager::TransactionManager; + use crate::result::Error; + use crate::*; + use std::num::NonZeroU32; + + let conn = &mut crate::test_helpers::connection(); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + let result: Result<_, Error> = conn.transaction(|conn| { + assert_eq!( + NonZeroU32::new(1), + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + sql_query("DROP TABLE IF EXISTS deferred_commit").execute(conn)?; + sql_query("CREATE TABLE deferred_commit(id INT UNIQUE INITIALLY DEFERRED)").execute(conn)?; + sql_query("INSERT INTO deferred_commit VALUES(1)").execute(conn)?; + let result = sql_query("INSERT INTO deferred_commit VALUES(1)").execute(conn); + assert!(result.is_ok()); + Ok(()) + }); + assert!(result.is_err()); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + } + + // regression test for #3470 + // crates.io depends on this behaviour + #[test] + #[cfg(feature = "postgres")] + fn some_libpq_failures_are_recoverable_by_rolling_back_the_savepoint_only() { + use crate::connection::{AnsiTransactionManager, TransactionManager}; + use crate::prelude::*; + use crate::sql_query; + + crate::table! { + rollback_test (id) { + id -> Int4, + value -> Int4, + } + } + + let conn = &mut crate::test_helpers::pg_connection_no_transaction(); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + + let res = conn.transaction(|conn| { + sql_query( + "CREATE TABLE IF NOT EXISTS rollback_test (id INT PRIMARY KEY, value INT NOT NULL)", + ) + .execute(conn)?; + conn.transaction(|conn| { + sql_query("SET TRANSACTION READ ONLY").execute(conn)?; + crate::update(rollback_test::table) + .set(rollback_test::value.eq(0)) + .execute(conn) + }) + .map(|_| { + panic!("Should use the `or_else` branch"); + }) + .or_else(|_| sql_query("SELECT 1").execute(conn)) + .map(|_| ()) + }); + assert!(res.is_ok()); + + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + } + + #[test] + #[cfg(feature = "postgres")] + fn other_libpq_failures_are_not_recoverable_by_rolling_back_the_savepoint_only() { + use crate::connection::{AnsiTransactionManager, TransactionManager}; + use crate::prelude::*; + use crate::sql_query; + use std::num::NonZeroU32; + use std::sync::{Arc, Barrier}; + + crate::table! { + rollback_test2 (id) { + id -> Int4, + value -> Int4, + } + } + let conn = &mut crate::test_helpers::pg_connection_no_transaction(); + + sql_query( + "CREATE TABLE IF NOT EXISTS rollback_test2 (id INT PRIMARY KEY, value INT NOT NULL)", + ) + .execute(conn) + .unwrap(); + + let start_barrier = Arc::new(Barrier::new(2)); + let commit_barrier = Arc::new(Barrier::new(2)); + + let other_start_barrier = start_barrier.clone(); + let other_commit_barrier = commit_barrier.clone(); + + let t1 = std::thread::spawn(move || { + let conn = &mut crate::test_helpers::pg_connection_no_transaction(); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + let r = conn.build_transaction().serializable().run::<_, crate::result::Error, _>(|conn| { + assert_eq!( + NonZeroU32::new(1), + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + rollback_test2::table.load::<(i32, i32)>(conn)?; + crate::insert_into(rollback_test2::table) + .values((rollback_test2::id.eq(1), rollback_test2::value.eq(42))) + .execute(conn)?; + let r = conn.transaction(|conn| { + assert_eq!( + NonZeroU32::new(2), + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + start_barrier.wait(); + commit_barrier.wait(); + let r = rollback_test2::table.load::<(i32, i32)>(conn); + assert!(r.is_err()); + Err::<(), _>(crate::result::Error::RollbackTransaction) + }); + assert_eq!( + NonZeroU32::new(1), + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + assert!( + matches!(r, Err(crate::result::Error::RollbackTransaction)), + "rollback failed (such errors should be ignored by transaction manager): {}", + r.unwrap_err() + ); + let r = rollback_test2::table.load::<(i32, i32)>(conn); + assert!(r.is_err()); + // fun fact: if hitting "commit" after receiving a serialization failure, PG + // returns that the commit has succeeded, but in fact it was actually rolled back. + // soo.. one should avoid doing that + r + }); + assert!(r.is_err()); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + }); + + let t2 = std::thread::spawn(move || { + other_start_barrier.wait(); + let conn = &mut crate::test_helpers::pg_connection_no_transaction(); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + let r = conn.build_transaction().serializable().run::<_, crate::result::Error, _>(|conn| { + assert_eq!( + NonZeroU32::new(1), + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + let _ = rollback_test2::table.load::<(i32, i32)>(conn)?; + crate::insert_into(rollback_test2::table) + .values((rollback_test2::id.eq(23), rollback_test2::value.eq(42))) + .execute(conn)?; + Ok(()) + }); + other_commit_barrier.wait(); + assert!(r.is_ok(), "{:?}", r.unwrap_err()); + assert_eq!( + None, + >::transaction_manager_status_mut( + conn + ).transaction_depth().expect("Transaction depth") + ); + }); + crate::sql_query("DELETE FROM rollback_test2") + .execute(conn) + .unwrap(); + t1.join().unwrap(); + t2.join().unwrap(); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/data_types.rs b/collector/compile-benchmarks/diesel-2.2.10/src/data_types.rs new file mode 100644 index 000000000..084c3d775 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/data_types.rs @@ -0,0 +1,10 @@ +//! Structs to represent the primitive equivalent of SQL types where +//! there is no existing Rust primitive, or where using it would be +//! confusing (such as date and time types). This module will re-export +//! all backend specific data structures when compiled against that +//! backend. +#[cfg(feature = "postgres_backend")] +pub use crate::pg::data_types::*; + +#[cfg(feature = "mysql_backend")] +pub use crate::mysql::data_types::*; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/deserialize.rs b/collector/compile-benchmarks/diesel-2.2.10/src/deserialize.rs new file mode 100644 index 000000000..217bc6742 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/deserialize.rs @@ -0,0 +1,580 @@ +//! Types and traits related to deserializing values from the database + +use std::error::Error; +use std::result; + +use crate::backend::Backend; +use crate::expression::select_by::SelectBy; +use crate::row::{NamedRow, Row}; +use crate::sql_types::{SingleValue, SqlType, Untyped}; +use crate::Selectable; + +/// A specialized result type representing the result of deserializing +/// a value from the database. +pub type Result = result::Result>; + +/// Trait indicating that a record can be queried from the database. +/// +/// Types which implement `Queryable` represent the result of a SQL query. This +/// does not necessarily mean they represent a single database table. +/// +/// Diesel represents the return type of a query as a tuple. The purpose of this +/// trait is to convert from a tuple of Rust values that have been deserialized +/// into your struct. +/// +/// This trait can be [derived](derive@Queryable) +/// +/// ## How to resolve compiler errors while loading data from the database +/// +/// In case you getting uncomprehensable compiler errors while loading data +/// from the database you might want to consider using +/// [`#[derive(Selectable)]`](derive@crate::prelude::Selectable) + +/// `#[diesel(check_for_backend(YourBackendType))]` to check for mismatching fields at compile +/// time. This drastically improves the quality of the generated error messages by pointing +/// to concrete mismatches at field level. You need to specify the concrete database backend +/// this specific struct is indented to be used with, as otherwise rustc cannot correctly +/// identify the required deserialization implementation. +/// +/// ## Interaction with `NULL`/`Option` +/// [`Nullable`][crate::sql_types::Nullable] types can be queried into `Option`. +/// This is valid for single fields, tuples, and structures with `Queryable`. +/// +/// With tuples and structs, the process for deserializing an `Option<(A,B,C)>` is +/// to attempt to deserialize `A`, `B` and `C`, and if either of these return an +/// [`UnexpectedNullError`](crate::result::UnexpectedNullError), the `Option` will be +/// deserialized as `None`. +/// If all succeed, the `Option` will be deserialized as `Some((a,b,c))`. +/// +/// # Examples +/// +/// ## Simple mapping from query to struct +/// +/// If we just want to map a query to our struct, we can use `derive`. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # +/// #[derive(Queryable, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = &mut establish_connection(); +/// let first_user = users.order_by(id).first(connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// ## Interaction with `NULL`/`Option` +/// +/// ### Single field +/// ```rust +/// # include!("doctest_setup.rs"); +/// # use diesel::sql_types::*; +/// # +/// table! { +/// animals { +/// id -> Integer, +/// species -> VarChar, +/// legs -> Integer, +/// name -> Nullable, +/// } +/// } +/// # +/// #[derive(Queryable, PartialEq, Debug)] +/// struct Animal { +/// id: i32, +/// name: Option, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::animals::dsl::*; +/// # let connection = &mut establish_connection(); +/// let all_animals = animals.select((id, name)).order_by(id).load(connection)?; +/// let expected = vec![Animal { id: 1, name: Some("Jack".to_owned()) }, Animal { id: 2, name: None }]; +/// assert_eq!(expected, all_animals); +/// # Ok(()) +/// # } +/// ``` +/// +/// ### Multiple fields +/// ```rust +/// # include!("doctest_setup.rs"); +/// # +/// #[derive(Queryable, PartialEq, Debug)] +/// struct UserWithPost { +/// id: i32, +/// post: Option, +/// } +/// #[derive(Queryable, PartialEq, Debug)] +/// struct Post { +/// id: i32, +/// title: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::{posts, users}; +/// # let connection = &mut establish_connection(); +/// # diesel::insert_into(users::table) +/// # .values(users::name.eq("Ruby")) +/// # .execute(connection)?; +/// let all_posts = users::table +/// .left_join(posts::table) +/// .select(( +/// users::id, +/// (posts::id, posts::title).nullable() +/// )) +/// .order_by((users::id, posts::id)) +/// .load(connection)?; +/// let expected = vec![ +/// UserWithPost { id: 1, post: Some(Post { id: 1, title: "My first post".to_owned() }) }, +/// UserWithPost { id: 1, post: Some(Post { id: 2, title: "About Rust".to_owned() }) }, +/// UserWithPost { id: 2, post: Some(Post { id: 3, title: "My first post too".to_owned() }) }, +/// UserWithPost { id: 3, post: None }, +/// ]; +/// assert_eq!(expected, all_posts); +/// # Ok(()) +/// # } +/// ``` +/// +/// ## `deserialize_as` attribute +/// +/// If we want to do additional work during deserialization, we can use +/// `deserialize_as` to use a different implementation. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # +/// # use schema::users; +/// # use diesel::backend::{self, Backend}; +/// # use diesel::deserialize::{self, Queryable, FromSql}; +/// # use diesel::sql_types::Text; +/// # +/// struct LowercaseString(String); +/// +/// impl Into for LowercaseString { +/// fn into(self) -> String { +/// self.0 +/// } +/// } +/// +/// impl Queryable for LowercaseString +/// where +/// DB: Backend, +/// String: FromSql, +/// { +/// type Row = String; +/// +/// fn build(s: String) -> deserialize::Result { +/// Ok(LowercaseString(s.to_lowercase())) +/// } +/// } +/// +/// #[derive(Queryable, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// #[diesel(deserialize_as = LowercaseString)] +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = &mut establish_connection(); +/// let first_user = users.first(connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// ## Manual implementation +/// +/// Alternatively, we can implement the trait for our struct manually. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # +/// use schema::users; +/// use diesel::deserialize::{self, Queryable}; +/// +/// # /* +/// type DB = diesel::sqlite::Sqlite; +/// # */ +/// +/// #[derive(PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// impl Queryable for User { +/// type Row = (i32, String); +/// +/// fn build(row: Self::Row) -> deserialize::Result { +/// Ok(User { +/// id: row.0, +/// name: row.1.to_lowercase(), +/// }) +/// } +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = &mut establish_connection(); +/// let first_user = users.first(connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +pub trait Queryable: Sized +where + DB: Backend, +{ + /// The Rust type you'd like to map from. + /// + /// This is typically a tuple of all of your struct's fields. + type Row: FromStaticSqlRow; + + /// Construct an instance of this type + fn build(row: Self::Row) -> Result; +} + +#[doc(inline)] +pub use diesel_derives::Queryable; + +/// Deserializes the result of a query constructed with [`sql_query`]. +/// +/// This trait can be [derived](derive@QueryableByName) +/// +/// [`sql_query`]: crate::sql_query() +/// +/// # Examples +/// +/// If we just want to map a query to our struct, we can use `derive`. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # use schema::users; +/// # use diesel::sql_query; +/// # +/// #[derive(QueryableByName, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = &mut establish_connection(); +/// let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1") +/// .get_result(connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// If we want to do additional work during deserialization, we can use +/// `deserialize_as` to use a different implementation. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # use diesel::sql_query; +/// # use schema::users; +/// # use diesel::backend::{self, Backend}; +/// # use diesel::deserialize::{self, FromSql}; +/// # +/// struct LowercaseString(String); +/// +/// impl Into for LowercaseString { +/// fn into(self) -> String { +/// self.0 +/// } +/// } +/// +/// impl FromSql for LowercaseString +/// where +/// DB: Backend, +/// String: FromSql, +/// { +/// fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { +/// String::from_sql(bytes) +/// .map(|s| LowercaseString(s.to_lowercase())) +/// } +/// } +/// +/// #[derive(QueryableByName, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// #[diesel(deserialize_as = LowercaseString)] +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = &mut establish_connection(); +/// let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1") +/// .get_result(connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +pub trait QueryableByName +where + Self: Sized, + DB: Backend, +{ + /// Construct an instance of `Self` from the database row + fn build<'a>(row: &impl NamedRow<'a, DB>) -> Result; +} + +#[doc(inline)] +pub use diesel_derives::QueryableByName; + +/// Deserialize a single field of a given SQL type. +/// +/// When possible, implementations of this trait should prefer to use an +/// existing implementation, rather than reading from `bytes`. (For example, if +/// you are implementing this for an enum which is represented as an integer in +/// the database, prefer `i32::from_sql(bytes)` (or the explicit form +/// `>::from_sql(bytes)`) over reading from `bytes` +/// directly) +/// +/// Types which implement this trait should also have `#[derive(FromSqlRow)]` +/// +/// ### Backend specific details +/// +/// - For PostgreSQL, the bytes will be sent using the binary protocol, not text. +/// - For SQLite, the actual type of `DB::RawValue` is private API. All +/// implementations of this trait must be written in terms of an existing +/// primitive. +/// - For MySQL, the value of `bytes` will depend on the return value of +/// `type_metadata` for the given SQL type. See [`MysqlType`] for details. +/// - For third party backends, consult that backend's documentation. +/// +/// [`MysqlType`]: ../mysql/enum.MysqlType.html +/// +/// ### Examples +/// +/// Most implementations of this trait will be defined in terms of an existing +/// implementation. +/// +/// ```rust +/// # use diesel::backend::{self, Backend}; +/// # use diesel::sql_types::*; +/// # use diesel::deserialize::{self, FromSql, FromSqlRow}; +/// # +/// #[repr(i32)] +/// #[derive(Debug, Clone, Copy, FromSqlRow)] +/// pub enum MyEnum { +/// A = 1, +/// B = 2, +/// } +/// +/// impl FromSql for MyEnum +/// where +/// DB: Backend, +/// i32: FromSql, +/// { +/// fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { +/// match i32::from_sql(bytes)? { +/// 1 => Ok(MyEnum::A), +/// 2 => Ok(MyEnum::B), +/// x => Err(format!("Unrecognized variant {}", x).into()), +/// } +/// } +/// } +/// ``` +#[diagnostic::on_unimplemented( + message = "cannot deserialize a value of the database type `{A}` as `{Self}`", + note = "double check your type mappings via the documentation of `{A}`" +)] +pub trait FromSql: Sized { + /// See the trait documentation. + fn from_sql(bytes: DB::RawValue<'_>) -> Result; + + /// A specialized variant of `from_sql` for handling null values. + /// + /// The default implementation returns an `UnexpectedNullError` for + /// an encountered null value and calls `Self::from_sql` otherwise + /// + /// If your custom type supports null values you need to provide a + /// custom implementation. + #[inline(always)] + fn from_nullable_sql(bytes: Option>) -> Result { + match bytes { + Some(bytes) => Self::from_sql(bytes), + None => Err(Box::new(crate::result::UnexpectedNullError)), + } + } +} + +/// Deserialize a database row into a rust data structure +/// +/// Diesel provides wild card implementations of this trait for all types +/// that implement one of the following traits: +/// * [`Queryable`] +/// * [`QueryableByName`] +#[diagnostic::on_unimplemented( + note = "double check your type mappings via the documentation of `{ST}`", + note = "`diesel::sql_query` requires the loading target to column names for loading values.\n\ + You need to provide a type that explicitly derives `diesel::deserialize::QueryableByName`" +)] +pub trait FromSqlRow: Sized { + /// See the trait documentation. + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result; +} + +#[doc(inline)] +pub use diesel_derives::FromSqlRow; + +/// A marker trait indicating that the corresponding type consumes a static at +/// compile time known number of field +/// +/// There is normally no need to implement this trait. Diesel provides +/// wild card impls for all types that implement `FromSql` or `Queryable` +/// where the size of `ST` is known +pub trait StaticallySizedRow: FromSqlRow { + /// The number of fields that this type will consume. + const FIELD_COUNT: usize; +} + +impl FromSqlRow for T +where + DB: Backend, + T: QueryableByName, +{ + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result { + T::build(row) + } +} + +/// A helper trait to deserialize a statically sized row into a tuple +/// +/// **If you see an error message mentioning this trait you are likely trying to +/// map the result of a query to a struct with mismatching field types. Recheck +/// your field order and the concrete field types.** +/// +/// You should not need to implement this trait directly. +/// Diesel provides wild card implementations for any supported tuple size +/// and for any type that implements `FromSql`. +/// +// This is a distinct trait from `FromSqlRow` because otherwise we +// are getting conflicting implementation errors for our `FromSqlRow` +// implementation for tuples and our wild card impl for all types +// implementing `Queryable` +pub trait FromStaticSqlRow: Sized { + /// See the trait documentation + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result; +} + +#[doc(hidden)] +pub trait SqlTypeOrSelectable {} + +impl SqlTypeOrSelectable for ST where ST: SqlType + SingleValue {} +impl SqlTypeOrSelectable for SelectBy +where + U: Selectable, + DB: Backend, +{ +} + +impl FromSqlRow for T +where + T: Queryable, + ST: SqlTypeOrSelectable, + DB: Backend, + T::Row: FromStaticSqlRow, +{ + // This inline(always) attribute is here as benchmarks have shown + // up to 5% reduction in instruction count of having it here + // + // A plain inline attribute does not show similar improvements + #[inline(always)] + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result { + let row = >::build_from_row(row)?; + T::build(row) + } +} + +impl FromStaticSqlRow for T +where + DB: Backend, + T: FromSql, + ST: SingleValue, +{ + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result { + use crate::row::Field; + + let field = row.get(0).ok_or(crate::result::UnexpectedEndOfRow)?; + T::from_nullable_sql(field.value()).map_err(|e| { + if e.is::() { + e + } else { + Box::new(crate::result::DeserializeFieldError::new(field, e)) + } + }) + } +} + +// We cannot have this impl because rustc +// then complains in third party crates that +// diesel may implement `SingleValue` for tuples +// in the future. While that is theoretically true, +// that will likely not happen in practice. +// If we get negative trait impls at some point in time +// it should be possible to make this work. +/*impl Queryable for T +where + DB: Backend, + T: FromStaticSqlRow, + ST: SingleValue, +{ + type Row = Self; + + fn build(row: Self::Row) -> Self { + row + } +}*/ + +impl StaticallySizedRow for T +where + ST: SqlTypeOrSelectable + crate::util::TupleSize, + T: Queryable, + DB: Backend, +{ + const FIELD_COUNT: usize = ::SIZE; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/doctest_setup.rs b/collector/compile-benchmarks/diesel-2.2.10/src/doctest_setup.rs new file mode 100644 index 000000000..33968333b --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/doctest_setup.rs @@ -0,0 +1,309 @@ +use diesel::prelude::*; +use dotenvy::dotenv; + +cfg_if::cfg_if! { + if #[cfg(feature = "postgres")] { + #[allow(dead_code)] + type DB = diesel::pg::Pg; + type DbConnection = PgConnection; + + fn database_url_for_env() -> String { + database_url_from_env("PG_DATABASE_URL") + } + + fn connection_no_transaction() -> PgConnection { + let connection_url = database_url_for_env(); + PgConnection::establish(&connection_url).unwrap() + } + + fn setup_database(connection: &mut PgConnection) { + connection.begin_test_transaction().unwrap(); + clean_tables(connection); + create_tables_with_data(connection); + } + + fn clean_tables(connection: &mut PgConnection) { + diesel::sql_query("DROP TABLE IF EXISTS users CASCADE").execute(connection).unwrap(); + diesel::sql_query("DROP TABLE IF EXISTS animals CASCADE").execute(connection).unwrap(); + diesel::sql_query("DROP TABLE IF EXISTS posts CASCADE").execute(connection).unwrap(); + diesel::sql_query("DROP TABLE IF EXISTS comments CASCADE").execute(connection).unwrap(); + diesel::sql_query("DROP TABLE IF EXISTS brands CASCADE").execute(connection).unwrap(); + } + + fn connection_no_data() -> PgConnection { + let mut connection = connection_no_transaction(); + connection.begin_test_transaction().unwrap(); + clean_tables(&mut connection); + connection + } + + fn create_tables_with_data(connection: &mut PgConnection) { + diesel::sql_query("CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL + )") + .execute(connection) + .unwrap(); + diesel::sql_query("INSERT INTO users (name) VALUES ('Sean'), ('Tess')") + .execute(connection) + .unwrap(); + + diesel::sql_query("CREATE TABLE animals ( + id SERIAL PRIMARY KEY, + species VARCHAR NOT NULL, + legs INTEGER NOT NULL, + name VARCHAR + )") + .execute(connection) + .unwrap(); + diesel::sql_query("INSERT INTO animals (species, legs, name) VALUES + ('dog', 4, 'Jack'), + ('spider', 8, null)" + ).execute(connection).unwrap(); + + diesel::sql_query("CREATE TABLE posts ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL + )").execute(connection).unwrap(); + diesel::sql_query("INSERT INTO posts (user_id, title) VALUES + (1, 'My first post'), + (1, 'About Rust'), + (2, 'My first post too')").execute(connection).unwrap(); + + diesel::sql_query("CREATE TABLE comments ( + id SERIAL PRIMARY KEY, + post_id INTEGER NOT NULL, + body VARCHAR NOT NULL + )").execute(connection).unwrap(); + diesel::sql_query("INSERT INTO comments (post_id, body) VALUES + (1, 'Great post'), + (2, 'Yay! I am learning Rust'), + (3, 'I enjoyed your post')").execute(connection).unwrap(); + + diesel::sql_query("CREATE TABLE brands ( + id SERIAL PRIMARY KEY, + color VARCHAR NOT NULL DEFAULT 'Green', + accent VARCHAR DEFAULT 'Blue' + )").execute(connection).unwrap(); + } + + #[allow(dead_code)] + fn establish_connection() -> PgConnection { + let mut connection = connection_no_data(); + create_tables_with_data(&mut connection); + connection + } + } else if #[cfg(feature = "sqlite")] { + #[allow(dead_code)] + type DB = diesel::sqlite::Sqlite; + type DbConnection = SqliteConnection; + + fn database_url_for_env() -> String { + String::from(":memory:") + } + + fn connection_no_data() -> SqliteConnection { + SqliteConnection::establish(":memory:").unwrap() + } + + fn setup_database(connection: &mut SqliteConnection) { + create_tables_with_data(connection); + } + + fn create_tables_with_data(connection: &mut SqliteConnection) { + diesel::sql_query("CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR NOT NULL + )").execute(connection).unwrap(); + diesel::sql_query("INSERT INTO users (name) VALUES ('Sean'), ('Tess')") + .execute(connection).unwrap(); + + diesel::sql_query("CREATE TABLE animals ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + species VARCHAR NOT NULL, + legs INTEGER NOT NULL, + name VARCHAR + )").execute(connection).unwrap(); + diesel::sql_query("INSERT INTO animals (species, legs, name) VALUES + ('dog', 4, 'Jack'), + ('spider', 8, null)" + ).execute(connection).unwrap(); + + diesel::sql_query("CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL + )").execute(connection).unwrap(); + diesel::sql_query("INSERT INTO posts (user_id, title) VALUES + (1, 'My first post'), + (1, 'About Rust'), + (2, 'My first post too')").execute(connection).unwrap(); + + diesel::sql_query("CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + post_id INTEGER NOT NULL, + body VARCHAR NOT NULL + )").execute(connection).unwrap(); + diesel::sql_query("INSERT INTO comments (post_id, body) VALUES + (1, 'Great post'), + (2, 'Yay! I am learning Rust'), + (3, 'I enjoyed your post')").execute(connection).unwrap(); + } + + #[allow(dead_code)] + fn establish_connection() -> SqliteConnection { + let mut connection = connection_no_data(); + create_tables_with_data(&mut connection); + connection + } + } else if #[cfg(feature = "mysql")] { + #[allow(dead_code)] + type DB = diesel::mysql::Mysql; + type DbConnection = MysqlConnection; + + fn database_url_for_env() -> String { + database_url_from_env("MYSQL_UNIT_TEST_DATABASE_URL") + } + + fn setup_database(connection: &mut MysqlConnection) { + clean_tables(connection); + create_tables_with_data(connection); + } + + fn clean_tables(connection: &mut MysqlConnection) { + diesel::sql_query("SET FOREIGN_KEY_CHECKS=0").execute(connection).unwrap(); + diesel::sql_query("DROP TABLE IF EXISTS users CASCADE").execute(connection).unwrap(); + diesel::sql_query("DROP TABLE IF EXISTS animals CASCADE").execute(connection).unwrap(); + diesel::sql_query("DROP TABLE IF EXISTS posts CASCADE").execute(connection).unwrap(); + diesel::sql_query("DROP TABLE IF EXISTS comments CASCADE").execute(connection).unwrap(); + diesel::sql_query("DROP TABLE IF EXISTS brands CASCADE").execute(connection).unwrap(); + diesel::sql_query("SET FOREIGN_KEY_CHECKS=1").execute(connection).unwrap(); + } + + fn connection_no_data() -> MysqlConnection { + let connection_url = database_url_for_env(); + let mut connection = MysqlConnection::establish(&connection_url).unwrap(); + clean_tables(&mut connection); + connection + } + + fn create_tables_with_data(connection: &mut MysqlConnection) { + diesel::sql_query("CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + name TEXT NOT NULL + ) CHARACTER SET utf8mb4").execute(connection).unwrap(); + diesel::sql_query("INSERT INTO users (name) VALUES ('Sean'), ('Tess')") + .execute(connection).unwrap(); + + diesel::sql_query("CREATE TABLE animals ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + species TEXT NOT NULL, + legs INTEGER NOT NULL, + name TEXT + ) CHARACTER SET utf8mb4").execute(connection).unwrap(); + diesel::sql_query("INSERT INTO animals (species, legs, name) VALUES + ('dog', 4, 'Jack'), + ('spider', 8, null)").execute(connection).unwrap(); + + diesel::sql_query("CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + user_id INTEGER NOT NULL, + title TEXT NOT NULL + ) CHARACTER SET utf8mb4").execute(connection).unwrap(); + diesel::sql_query("INSERT INTO posts (user_id, title) VALUES + (1, 'My first post'), + (1, 'About Rust'), + (2, 'My first post too')").execute(connection).unwrap(); + + diesel::sql_query("CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + post_id INTEGER NOT NULL, + body TEXT NOT NULL + ) CHARACTER SET utf8mb4").execute(connection).unwrap(); + diesel::sql_query("INSERT INTO comments (post_id, body) VALUES + (1, 'Great post'), + (2, 'Yay! I am learning Rust'), + (3, 'I enjoyed your post')").execute(connection).unwrap(); + + diesel::sql_query("CREATE TABLE brands ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + color VARCHAR(255) NOT NULL DEFAULT 'Green', + accent VARCHAR(255) DEFAULT 'Blue' + )").execute(connection).unwrap(); + } + + #[allow(dead_code)] + fn establish_connection() -> MysqlConnection { + let mut connection = connection_no_data(); + create_tables_with_data(&mut connection); + connection.begin_test_transaction().unwrap(); + connection + } + } else { + compile_error!( + "At least one backend must be used to test this crate.\n \ + Pass argument `--features \"\"` with one or more of the following backends, \ + 'mysql', 'postgres', or 'sqlite'. \n\n \ + ex. cargo test --features \"mysql postgres sqlite\"\n" + ); + } +} + +fn database_url_from_env(backend_specific_env_var: &str) -> String { + use std::env; + + dotenv().ok(); + + env::var(backend_specific_env_var) + .or_else(|_| env::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests") +} + +mod schema { + use diesel::prelude::*; + + table! { + animals { + id -> Integer, + species -> VarChar, + legs -> Integer, + name -> Nullable, + } + } + + table! { + comments { + id -> Integer, + post_id -> Integer, + body -> VarChar, + } + } + + table! { + posts { + id -> Integer, + user_id -> Integer, + title -> VarChar, + } + } + + table! { + users { + id -> Integer, + name -> VarChar, + } + } + + #[cfg(not(feature = "sqlite"))] + table! { + brands { + id -> Integer, + color -> VarChar, + accent -> Nullable, + } + } + + joinable!(posts -> users (user_id)); + allow_tables_to_appear_in_same_query!(animals, comments, posts, users); +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/array_comparison.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/array_comparison.rs new file mode 100644 index 000000000..131b77425 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/array_comparison.rs @@ -0,0 +1,352 @@ +//! This module contains the query dsl node definitions +//! for array comparison operations like `IN` and `NOT IN` + +use crate::backend::{sql_dialect, Backend, SqlDialect}; +use crate::expression::subselect::Subselect; +use crate::expression::{ + AppearsOnTable, AsExpression, Expression, SelectableExpression, TypedExpressionType, + ValidGrouping, +}; +use crate::query_builder::combination_clause::CombinationClause; +use crate::query_builder::{ + AstPass, BoxedSelectStatement, QueryFragment, QueryId, SelectQuery, SelectStatement, +}; +use crate::result::QueryResult; +use crate::serialize::ToSql; +use crate::sql_types::{self, HasSqlType, SingleValue, SqlType}; +use std::marker::PhantomData; + +/// Query dsl node that represents a `left IN (values)` +/// expression +/// +/// Third party backend can customize the [`QueryFragment`] +/// implementation of this query dsl node via +/// [`SqlDialect::ArrayComparison`]. A customized implementation +/// is expected to provide the same semantics as an ANSI SQL +/// `IN` expression. +/// +/// The postgres backend provided a specialized implementation +/// by using `left = ANY(values)` as optimized variant instead. +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] +#[non_exhaustive] +pub struct In { + /// The expression on the left side of the `IN` keyword + pub left: T, + /// The values clause of the `IN` expression + pub values: U, +} + +/// Query dsl node that represents a `left NOT IN (values)` +/// expression +/// +/// Third party backend can customize the [`QueryFragment`] +/// implementation of this query dsl node via +/// [`SqlDialect::ArrayComparison`]. A customized implementation +/// is expected to provide the same semantics as an ANSI SQL +/// `NOT IN` expression.0 +/// +/// The postgres backend provided a specialized implementation +/// by using `left = ALL(values)` as optimized variant instead. +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] +#[non_exhaustive] +pub struct NotIn { + /// The expression on the left side of the `NOT IN` keyword + pub left: T, + /// The values clause of the `NOT IN` expression + pub values: U, +} + +impl In { + pub(crate) fn new(left: T, values: U) -> Self { + In { left, values } + } +} + +impl NotIn { + pub(crate) fn new(left: T, values: U) -> Self { + NotIn { left, values } + } +} + +impl Expression for In +where + T: Expression, + U: Expression, + T::SqlType: SqlType, + sql_types::is_nullable::IsSqlTypeNullable: + sql_types::MaybeNullableType, +{ + type SqlType = sql_types::is_nullable::MaybeNullable< + sql_types::is_nullable::IsSqlTypeNullable, + sql_types::Bool, + >; +} + +impl Expression for NotIn +where + T: Expression, + U: Expression, + T::SqlType: SqlType, + sql_types::is_nullable::IsSqlTypeNullable: + sql_types::MaybeNullableType, +{ + type SqlType = sql_types::is_nullable::MaybeNullable< + sql_types::is_nullable::IsSqlTypeNullable, + sql_types::Bool, + >; +} + +impl QueryFragment for In +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment for In +where + DB: Backend + + SqlDialect, + T: QueryFragment, + U: QueryFragment + MaybeEmpty, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + if self.values.is_empty() { + out.push_sql("1=0"); + } else { + self.left.walk_ast(out.reborrow())?; + out.push_sql(" IN ("); + self.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} + +impl QueryFragment for NotIn +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment + for NotIn +where + DB: Backend + + SqlDialect, + T: QueryFragment, + U: QueryFragment + MaybeEmpty, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + if self.values.is_empty() { + out.push_sql("1=1"); + } else { + self.left.walk_ast(out.reborrow())?; + out.push_sql(" NOT IN ("); + self.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} + +impl_selectable_expression!(In); +impl_selectable_expression!(NotIn); + +/// This trait describes how a type is transformed to the +/// `IN (values)` value expression +/// +/// Diesel provided several implementations here: +/// +/// - An implementation for any [`Iterator`] over values +/// that implement [`AsExpression`] for the corresponding +/// sql type ST. The corresponding values clause will contain +/// bind statements for each individual value. +/// - An implementation for select statements, that returns +/// a single field. The corresponding values clause will contain +/// the sub query. +/// +/// This trait is exposed for custom third party backends so +/// that they can restrict the [`QueryFragment`] implementations +/// for [`In`] and [`NotIn`]. +pub trait AsInExpression { + /// Type of the expression returned by [AsInExpression::as_in_expression] + type InExpression: MaybeEmpty + Expression; + + /// Construct the diesel query dsl representation of + /// the `IN (values)` clause for the given type + #[allow(clippy::wrong_self_convention)] + // That's a public api, we cannot just change it to + // appease clippy + fn as_in_expression(self) -> Self::InExpression; +} + +impl AsInExpression for I +where + I: IntoIterator, + T: AsExpression, + ST: SqlType + TypedExpressionType, +{ + type InExpression = Many; + + fn as_in_expression(self) -> Self::InExpression { + Many { + values: self.into_iter().collect(), + p: PhantomData, + } + } +} + +/// A helper trait to check if the values clause of +/// an [`In`] or [`NotIn`] query dsl node is empty or not +pub trait MaybeEmpty { + /// Returns `true` if self represents an empty collection + /// Otherwise `false` is returned. + fn is_empty(&self) -> bool; +} + +impl AsInExpression + for SelectStatement +where + ST: SqlType + TypedExpressionType, + Subselect: Expression, + Self: SelectQuery, +{ + type InExpression = Subselect; + + fn as_in_expression(self) -> Self::InExpression { + Subselect::new(self) + } +} + +impl<'a, ST, QS, DB, GB> AsInExpression for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + ST: SqlType + TypedExpressionType, + Subselect, ST>: Expression, +{ + type InExpression = Subselect; + + fn as_in_expression(self) -> Self::InExpression { + Subselect::new(self) + } +} + +impl AsInExpression + for CombinationClause +where + ST: SqlType + TypedExpressionType, + Self: SelectQuery, + Subselect: Expression, +{ + type InExpression = Subselect; + + fn as_in_expression(self) -> Self::InExpression { + Subselect::new(self) + } +} + +/// Query dsl node for an `IN (values)` clause containing +/// a variable number of bind values. +/// +/// Third party backend can customize the [`QueryFragment`] +/// implementation of this query dsl node via +/// [`SqlDialect::ArrayComparison`]. The default +/// implementation does generate one bind per value +/// in the `values` field. +/// +/// Diesel provides an optimized implementation for Postgresql +/// like database systems that bind all values with one +/// bind value of the type `Array` instead. +#[derive(Debug, Clone)] +pub struct Many { + /// The values contained in the `IN (values)` clause + pub values: Vec, + p: PhantomData, +} + +impl ValidGrouping for Many +where + ST: SingleValue, + I: AsExpression, + I::Expression: ValidGrouping, +{ + type IsAggregate = >::IsAggregate; +} + +impl Expression for Many +where + ST: TypedExpressionType, +{ + type SqlType = ST; +} + +impl MaybeEmpty for Many { + fn is_empty(&self) -> bool { + self.values.is_empty() + } +} + +impl SelectableExpression for Many +where + Many: AppearsOnTable, + ST: SingleValue, + I: AsExpression, + >::Expression: SelectableExpression, +{ +} + +impl AppearsOnTable for Many +where + Many: Expression, + I: AsExpression, + ST: SingleValue, + >::Expression: SelectableExpression, +{ +} + +impl QueryFragment for Many +where + Self: QueryFragment, + DB: Backend, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment + for Many +where + DB: Backend + + HasSqlType + + SqlDialect, + ST: SingleValue, + I: ToSql, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + let mut first = true; + for value in &self.values { + if first { + first = false; + } else { + out.push_sql(", "); + } + out.push_bind_param(value)?; + } + Ok(()) + } +} + +impl QueryId for Many { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/assume_not_null.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/assume_not_null.rs new file mode 100644 index 000000000..834e47f4b --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/assume_not_null.rs @@ -0,0 +1,59 @@ +use crate::expression::*; +use crate::query_builder::*; +use crate::query_source::joins::ToInnerJoin; +use crate::result::QueryResult; +use crate::sql_types::{DieselNumericOps, IntoNotNullable}; + +#[derive(Default, Debug, Copy, Clone, DieselNumericOps, ValidGrouping)] +pub struct AssumeNotNull(T); + +impl AssumeNotNull { + pub fn new(expr: T) -> Self { + AssumeNotNull(expr) + } +} + +impl Expression for AssumeNotNull +where + T: Expression, + T::SqlType: IntoNotNullable, + ::NotNullable: TypedExpressionType, +{ + type SqlType = ::NotNullable; +} + +impl QueryFragment for AssumeNotNull +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.0.walk_ast(pass) + } +} + +impl AppearsOnTable for AssumeNotNull +where + T: AppearsOnTable, + AssumeNotNull: Expression, +{ +} + +impl QueryId for AssumeNotNull { + type QueryId = T::QueryId; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl SelectableExpression for AssumeNotNull +where + Self: AppearsOnTable, + QS: ToInnerJoin, + T: SelectableExpression, +{ +} + +impl SelectableExpression for AssumeNotNull where + Self: AppearsOnTable +{ +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/bound.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/bound.rs new file mode 100644 index 000000000..33a46ebe7 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/bound.rs @@ -0,0 +1,56 @@ +use std::marker::PhantomData; + +use super::*; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::serialize::ToSql; +use crate::sql_types::DieselNumericOps; + +#[doc(hidden)] // This is used by the `AsExpression` derive +#[derive(Debug, Clone, Copy, DieselNumericOps)] +pub struct Bound { + pub(crate) item: U, + _marker: PhantomData, +} + +impl Bound { + #[doc(hidden)] // This is used by the `AsExpression` derive + pub fn new(item: U) -> Self { + Bound { + item, + _marker: PhantomData, + } + } +} + +impl Expression for Bound +where + T: SqlType + TypedExpressionType, +{ + type SqlType = T; +} + +impl QueryFragment for Bound +where + DB: Backend + HasSqlType, + U: ToSql, +{ + fn walk_ast<'b>(&'b self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + pass.push_bind_param(&self.item)?; + Ok(()) + } +} + +impl QueryId for Bound { + type QueryId = Bound; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl SelectableExpression for Bound where Bound: AppearsOnTable {} + +impl AppearsOnTable for Bound where Bound: Expression {} + +impl ValidGrouping for Bound { + type IsAggregate = is_aggregate::Never; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/case_when.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/case_when.rs new file mode 100644 index 000000000..e5c4df802 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/case_when.rs @@ -0,0 +1,415 @@ +use crate::expression::grouped::Grouped; +use crate::expression::{helper_types, Expression}; +use crate::sql_types::{BoolOrNullableBool, SqlType}; +use diesel_derives::{DieselNumericOps, QueryId, ValidGrouping}; + +use super::{AsExpression, TypedExpressionType}; + +/// Creates a SQL `CASE WHEN ... END` expression +/// +/// # Example +/// +/// ``` +/// # include!("../doctest_setup.rs"); +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # let connection = &mut establish_connection(); +/// use diesel::dsl::case_when; +/// +/// let users_with_name: Vec<(i32, Option)> = users +/// .select((id, case_when(name.eq("Sean"), id))) +/// .load(connection) +/// .unwrap(); +/// +/// assert_eq!(&[(1, Some(1)), (2, None)], users_with_name.as_slice()); +/// # } +/// ``` +/// +/// # `ELSE` clause +/// ``` +/// # include!("../doctest_setup.rs"); +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # let connection = &mut establish_connection(); +/// use diesel::dsl::case_when; +/// +/// let users_with_name: Vec<(i32, i32)> = users +/// .select((id, case_when(name.eq("Sean"), id).otherwise(0))) +/// .load(connection) +/// .unwrap(); +/// +/// assert_eq!(&[(1, 1), (2, 0)], users_with_name.as_slice()); +/// # } +/// ``` +/// +/// Note that the SQL types of the `case_when` and `else` expressions should +/// be equal. This includes whether they are wrapped in +/// [`Nullable`](crate::sql_types::Nullable), so you may need to call +/// [`nullable`](crate::expression_methods::NullableExpressionMethods::nullable) +/// on one of them. +/// +/// # More `WHEN` branches +/// ``` +/// # include!("../doctest_setup.rs"); +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # let connection = &mut establish_connection(); +/// use diesel::dsl::case_when; +/// +/// let users_with_name: Vec<(i32, Option)> = users +/// .select((id, case_when(name.eq("Sean"), id).when(name.eq("Tess"), 2))) +/// .load(connection) +/// .unwrap(); +/// +/// assert_eq!(&[(1, Some(1)), (2, Some(2))], users_with_name.as_slice()); +/// # } +/// ``` +pub fn case_when(condition: C, if_true: T) -> helper_types::case_when +where + C: Expression, + ::SqlType: BoolOrNullableBool, + T: AsExpression, + ST: SqlType + TypedExpressionType, +{ + CaseWhen { + whens: CaseWhenConditionsLeaf { + when: Grouped(condition), + then: Grouped(if_true.as_expression()), + }, + else_expr: NoElseExpression, + } +} + +/// A SQL `CASE WHEN ... END` expression +#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, ValidGrouping)] +pub struct CaseWhen { + whens: Whens, + else_expr: E, +} + +impl CaseWhen { + /// Add an additional `WHEN ... THEN ...` branch to the `CASE` expression + /// + /// See the [`case_when`] documentation for more details. + pub fn when(self, condition: C, if_true: T) -> helper_types::When + where + Self: CaseWhenTypesExtractor, + C: Expression, + ::SqlType: BoolOrNullableBool, + T: AsExpression<::OutputExpressionSpecifiedSqlType>, + { + CaseWhen { + whens: CaseWhenConditionsIntermediateNode { + first_whens: self.whens, + last_when: CaseWhenConditionsLeaf { + when: Grouped(condition), + then: Grouped(if_true.as_expression()), + }, + }, + else_expr: self.else_expr, + } + } +} + +impl CaseWhen { + /// Sets the `ELSE` branch of the `CASE` expression + /// + /// It is named this way because `else` is a reserved keyword in Rust + /// + /// See the [`case_when`] documentation for more details. + pub fn otherwise(self, if_no_other_branch_matched: E) -> helper_types::Otherwise + where + Self: CaseWhenTypesExtractor, + E: AsExpression<::OutputExpressionSpecifiedSqlType>, + { + CaseWhen { + whens: self.whens, + else_expr: ElseExpression { + expr: Grouped(if_no_other_branch_matched.as_expression()), + }, + } + } +} + +pub(crate) use non_public_types::*; +mod non_public_types { + use super::CaseWhen; + + use diesel_derives::{QueryId, ValidGrouping}; + + use crate::expression::{ + AppearsOnTable, Expression, SelectableExpression, TypedExpressionType, + }; + use crate::query_builder::{AstPass, QueryFragment}; + use crate::query_source::aliasing; + use crate::sql_types::{BoolOrNullableBool, IntoNullable, SqlType}; + + #[derive(Debug, Clone, Copy, QueryId, ValidGrouping)] + pub struct CaseWhenConditionsLeaf { + pub(super) when: W, + pub(super) then: T, + } + + #[derive(Debug, Clone, Copy, QueryId, ValidGrouping)] + pub struct CaseWhenConditionsIntermediateNode { + pub(super) first_whens: Whens, + pub(super) last_when: CaseWhenConditionsLeaf, + } + + pub trait CaseWhenConditions { + type OutputExpressionSpecifiedSqlType: SqlType + TypedExpressionType; + } + impl CaseWhenConditions for CaseWhenConditionsLeaf + where + ::SqlType: SqlType + TypedExpressionType, + { + type OutputExpressionSpecifiedSqlType = T::SqlType; + } + // This intentionally doesn't re-check inner `Whens` here, because this trait is + // only used to allow expression SQL type inference for `.when` calls so we + // want to make it as lightweight as possible for fast compilation. Actual + // guarantees are provided by the other implementations below + impl CaseWhenConditions for CaseWhenConditionsIntermediateNode + where + ::SqlType: SqlType + TypedExpressionType, + { + type OutputExpressionSpecifiedSqlType = T::SqlType; + } + + #[derive(Debug, Clone, Copy, QueryId, ValidGrouping)] + pub struct NoElseExpression; + #[derive(Debug, Clone, Copy, QueryId, ValidGrouping)] + pub struct ElseExpression { + pub(super) expr: E, + } + + /// Largely internal trait used to define the [`When`] and [`Otherwise`] + /// type aliases + /// + /// It should typically not be needed in user code unless writing extremely + /// generic functions + pub trait CaseWhenTypesExtractor { + /// The + /// This may not be the actual output expression type: if there is no + /// `else` it will be made `Nullable` + type OutputExpressionSpecifiedSqlType: SqlType + TypedExpressionType; + type Whens; + type Else; + } + impl CaseWhenTypesExtractor for CaseWhen + where + Whens: CaseWhenConditions, + { + type OutputExpressionSpecifiedSqlType = Whens::OutputExpressionSpecifiedSqlType; + type Whens = Whens; + type Else = E; + } + + impl SelectableExpression for CaseWhen, NoElseExpression> + where + CaseWhen, NoElseExpression>: AppearsOnTable, + W: SelectableExpression, + T: SelectableExpression, + { + } + + impl SelectableExpression + for CaseWhen, ElseExpression> + where + CaseWhen, ElseExpression>: AppearsOnTable, + W: SelectableExpression, + T: SelectableExpression, + E: SelectableExpression, + { + } + + impl SelectableExpression + for CaseWhen, E> + where + Self: AppearsOnTable, + W: SelectableExpression, + T: SelectableExpression, + CaseWhen: SelectableExpression, + { + } + + impl AppearsOnTable for CaseWhen, NoElseExpression> + where + CaseWhen, NoElseExpression>: Expression, + W: AppearsOnTable, + T: AppearsOnTable, + { + } + + impl AppearsOnTable for CaseWhen, ElseExpression> + where + CaseWhen, ElseExpression>: Expression, + W: AppearsOnTable, + T: AppearsOnTable, + E: AppearsOnTable, + { + } + + impl AppearsOnTable + for CaseWhen, E> + where + Self: Expression, + W: AppearsOnTable, + T: AppearsOnTable, + CaseWhen: AppearsOnTable, + { + } + + impl Expression for CaseWhen, NoElseExpression> + where + W: Expression, + ::SqlType: BoolOrNullableBool, + T: Expression, + ::SqlType: IntoNullable, + <::SqlType as IntoNullable>::Nullable: SqlType + TypedExpressionType, + { + type SqlType = <::SqlType as IntoNullable>::Nullable; + } + impl Expression for CaseWhen, ElseExpression> + where + W: Expression, + ::SqlType: BoolOrNullableBool, + T: Expression, + { + type SqlType = T::SqlType; + } + impl Expression for CaseWhen, E> + where + CaseWhen, E>: Expression, + CaseWhen: Expression< + SqlType = , E> as Expression>::SqlType, + >, + { + type SqlType = , E> as Expression>::SqlType; + } + + impl QueryFragment for CaseWhen + where + DB: crate::backend::Backend, + Whens: QueryFragment, + E: QueryFragment, + { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> crate::QueryResult<()> { + out.push_sql("CASE"); + self.whens.walk_ast(out.reborrow())?; + self.else_expr.walk_ast(out.reborrow())?; + out.push_sql(" END"); + Ok(()) + } + } + + impl QueryFragment for CaseWhenConditionsLeaf + where + DB: crate::backend::Backend, + W: QueryFragment, + T: QueryFragment, + { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> crate::QueryResult<()> { + out.push_sql(" WHEN "); + self.when.walk_ast(out.reborrow())?; + out.push_sql(" THEN "); + self.then.walk_ast(out.reborrow())?; + Ok(()) + } + } + + impl QueryFragment for CaseWhenConditionsIntermediateNode + where + DB: crate::backend::Backend, + Whens: QueryFragment, + W: QueryFragment, + T: QueryFragment, + { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> crate::QueryResult<()> { + self.first_whens.walk_ast(out.reborrow())?; + self.last_when.walk_ast(out.reborrow())?; + Ok(()) + } + } + + impl QueryFragment for NoElseExpression + where + DB: crate::backend::Backend, + { + fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> crate::result::QueryResult<()> { + let _ = out; + Ok(()) + } + } + impl QueryFragment for ElseExpression + where + E: QueryFragment, + DB: crate::backend::Backend, + { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> crate::result::QueryResult<()> { + out.push_sql(" ELSE "); + self.expr.walk_ast(out.reborrow())?; + Ok(()) + } + } + + impl aliasing::FieldAliasMapper for CaseWhen + where + S: aliasing::AliasSource, + Conditions: aliasing::FieldAliasMapper, + E: aliasing::FieldAliasMapper, + { + type Out = CaseWhen< + >::Out, + >::Out, + >; + fn map(self, alias: &aliasing::Alias) -> Self::Out { + CaseWhen { + whens: self.whens.map(alias), + else_expr: self.else_expr.map(alias), + } + } + } + + impl aliasing::FieldAliasMapper for CaseWhenConditionsLeaf + where + S: aliasing::AliasSource, + W: aliasing::FieldAliasMapper, + T: aliasing::FieldAliasMapper, + { + type Out = CaseWhenConditionsLeaf< + >::Out, + >::Out, + >; + fn map(self, alias: &aliasing::Alias) -> Self::Out { + CaseWhenConditionsLeaf { + when: self.when.map(alias), + then: self.then.map(alias), + } + } + } + + impl aliasing::FieldAliasMapper + for CaseWhenConditionsIntermediateNode + where + S: aliasing::AliasSource, + W: aliasing::FieldAliasMapper, + T: aliasing::FieldAliasMapper, + Whens: aliasing::FieldAliasMapper, + { + type Out = CaseWhenConditionsIntermediateNode< + >::Out, + >::Out, + >::Out, + >; + fn map(self, alias: &aliasing::Alias) -> Self::Out { + CaseWhenConditionsIntermediateNode { + first_whens: self.first_whens.map(alias), + last_when: self.last_when.map(alias), + } + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/coerce.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/coerce.rs new file mode 100644 index 000000000..929172263 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/coerce.rs @@ -0,0 +1,71 @@ +use crate::expression::*; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::DieselNumericOps; +use std::marker::PhantomData; + +#[derive(Debug, Copy, Clone, QueryId, DieselNumericOps)] +#[doc(hidden)] +/// Coerces an expression to be another type. No checks are performed to ensure +/// that the new type is valid in all positions that the previous type was. +/// This does not perform an actual cast, it just lies to our type system. +/// +/// This is used for a few expressions where we know that the types are actually +/// always interchangeable. (Examples of this include `Timestamp` vs +/// `Timestamptz`, `VarChar` vs `Text`, and `Json` vs `Jsonb`). +/// +/// This struct should not be considered a general solution to equivalent types. +/// It is a short term workaround for expressions which are known to be commonly +/// used. +pub struct Coerce { + expr: T, + _marker: PhantomData, +} + +impl Coerce { + pub fn new(expr: T) -> Self { + Coerce { + expr: expr, + _marker: PhantomData, + } + } +} + +impl Expression for Coerce +where + T: Expression, + ST: SqlType + TypedExpressionType, +{ + type SqlType = ST; +} + +impl SelectableExpression for Coerce +where + T: SelectableExpression, + Self: Expression, +{ +} + +impl AppearsOnTable for Coerce +where + T: AppearsOnTable, + Self: Expression, +{ +} + +impl QueryFragment for Coerce +where + T: QueryFragment, + DB: Backend, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.expr.walk_ast(pass) + } +} + +impl ValidGrouping for Coerce +where + T: ValidGrouping, +{ + type IsAggregate = T::IsAggregate; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/count.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/count.rs new file mode 100644 index 000000000..e3512b229 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/count.rs @@ -0,0 +1,158 @@ +use std::marker::PhantomData; + +use super::functions::define_sql_function; +use super::{is_aggregate, AsExpression}; +use super::{Expression, ValidGrouping}; +use crate::backend::Backend; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::{BigInt, DieselNumericOps, SingleValue, SqlType}; +use crate::{AppearsOnTable, SelectableExpression}; + +define_sql_function! { + /// Creates a SQL `COUNT` expression + /// + /// As with most bare functions, this is not exported by default. You can import + /// it specifically as `diesel::dsl::count`, or glob import + /// `diesel::dsl::*` + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// assert_eq!(Ok(1), animals.select(count(name)).first(connection)); + /// # } + /// ``` + #[aggregate] + fn count(expr: T) -> BigInt; +} + +/// Creates a SQL `COUNT(*)` expression +/// +/// For selecting the count of a query, and nothing else, you can just call +/// [`count`](crate::query_dsl::QueryDsl::count()) +/// on the query instead. +/// +/// As with most bare functions, this is not exported by default. You can import +/// it specifically as `diesel::dsl::count_star`, or glob import +/// `diesel::dsl::*` +/// +/// # Examples +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use diesel::dsl::*; +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # let connection = &mut establish_connection(); +/// assert_eq!(Ok(2), users.select(count_star()).first(connection)); +/// # } +/// ``` +pub fn count_star() -> CountStar { + CountStar +} + +#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, ValidGrouping)] +#[diesel(aggregate)] +#[doc(hidden)] +pub struct CountStar; + +impl Expression for CountStar { + type SqlType = BigInt; +} + +impl QueryFragment for CountStar { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql("COUNT(*)"); + Ok(()) + } +} + +impl_selectable_expression!(CountStar); + +/// Creates a SQL `COUNT(DISTINCT ...)` expression +/// +/// As with most bare functions, this is not exported by default. You can import +/// it specifically as `diesel::dsl::count_distinct`, or glob import +/// `diesel::dsl::*` +/// +/// # Examples +/// +/// ```rust +/// # #[macro_use] extern crate diesel; +/// # include!("../doctest_setup.rs"); +/// # use diesel::dsl::*; +/// # +/// # fn main() { +/// # use schema::posts::dsl::*; +/// # let connection = &mut establish_connection(); +/// let unique_user_count = posts.select(count_distinct(user_id)).first(connection); +/// assert_eq!(Ok(2), unique_user_count); +/// # } +/// ``` +pub fn count_distinct(expr: E) -> CountDistinct +where + T: SqlType + SingleValue, + E: AsExpression, +{ + CountDistinct { + expr: expr.as_expression(), + _marker: PhantomData, + } +} + +#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps)] +#[doc(hidden)] +pub struct CountDistinct { + expr: E, + _marker: PhantomData, +} + +impl Expression for CountDistinct +where + T: SqlType + SingleValue, + E: Expression, +{ + type SqlType = BigInt; +} + +impl ValidGrouping for CountDistinct +where + T: SqlType + SingleValue, +{ + type IsAggregate = is_aggregate::Yes; +} + +impl SelectableExpression for CountDistinct +where + Self: AppearsOnTable, + E: SelectableExpression, +{ +} + +impl AppearsOnTable for CountDistinct +where + Self: Expression, + E: AppearsOnTable, +{ +} + +impl QueryFragment for CountDistinct +where + T: SqlType + SingleValue, + DB: Backend, + E: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql("COUNT(DISTINCT "); + self.expr.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/exists.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/exists.rs new file mode 100644 index 000000000..ff6a8d5d6 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/exists.rs @@ -0,0 +1,104 @@ +//! This module contains the query dsl node definition +//! for `EXISTS` expressions + +use crate::backend::{sql_dialect, Backend, SqlDialect}; +use crate::expression::subselect::Subselect; +use crate::expression::{AppearsOnTable, Expression, SelectableExpression, ValidGrouping}; +use crate::helper_types; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::Bool; + +/// Creates a SQL `EXISTS` expression. +/// +/// The argument must be a complete SQL query. The query may reference columns +/// from the outer table. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # use diesel::select; +/// # use diesel::dsl::exists; +/// # let connection = &mut establish_connection(); +/// let sean_exists = select(exists(users.filter(name.eq("Sean")))) +/// .get_result(connection); +/// let jim_exists = select(exists(users.filter(name.eq("Jim")))) +/// .get_result(connection); +/// assert_eq!(Ok(true), sean_exists); +/// assert_eq!(Ok(false), jim_exists); +/// # } +/// ``` +pub fn exists(query: T) -> helper_types::exists { + Exists { + subselect: Subselect::new(query), + } +} + +/// The query dsl node that represents a SQL `EXISTS (subselect)` expression. +/// +/// Third party backend can customize the [`QueryFragment`] +/// implementation of this query dsl node via +/// [`SqlDialect::ExistsSyntax`]. A customized implementation +/// is expected to provide the same semantics as an ANSI SQL +/// `EXIST (subselect)` expression. +#[derive(Clone, Copy, QueryId, Debug)] +#[non_exhaustive] +pub struct Exists { + /// The inner subselect + pub subselect: Subselect, +} + +impl Expression for Exists +where + Subselect: Expression, +{ + type SqlType = Bool; +} + +impl ValidGrouping for Exists +where + Subselect: ValidGrouping, +{ + type IsAggregate = as ValidGrouping>::IsAggregate; +} + +impl QueryFragment for Exists +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment for Exists +where + DB: Backend + SqlDialect, + T: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql("EXISTS ("); + self.subselect.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +impl SelectableExpression for Exists +where + Self: AppearsOnTable, + Subselect: SelectableExpression, +{ +} + +impl AppearsOnTable for Exists +where + Self: Expression, + Subselect: AppearsOnTable, +{ +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/aggregate_folding.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/aggregate_folding.rs new file mode 100644 index 000000000..ae1b06ff5 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/aggregate_folding.rs @@ -0,0 +1,68 @@ +use crate::expression::functions::define_sql_function; +use crate::sql_types::Foldable; + +define_sql_function! { + /// Represents a SQL `SUM` function. This function can only take types which are + /// Foldable. + /// + /// # Examples + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// assert_eq!(Ok(Some(12i64)), animals.select(sum(legs)).first(connection)); + /// # } + /// ``` + #[aggregate] + fn sum(expr: ST) -> ST::Sum; +} + +define_sql_function! { + /// Represents a SQL `AVG` function. This function can only take types which are + /// Foldable. + /// + /// # Examples + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # #[cfg(feature = "numeric")] + /// # extern crate bigdecimal; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # table! { + /// # numbers (number) { + /// # number -> Integer, + /// # } + /// # } + /// # + /// # #[cfg(all(feature = "numeric", any(feature = "postgres", not(feature = "sqlite"))))] + /// # fn run_test() -> QueryResult<()> { + /// # use bigdecimal::BigDecimal; + /// # use self::numbers::dsl::*; + /// # let conn = &mut establish_connection(); + /// # diesel::sql_query("DROP TABLE IF EXISTS numbers").execute(conn)?; + /// # diesel::sql_query("CREATE TABLE numbers (number INTEGER)").execute(conn)?; + /// diesel::insert_into(numbers) + /// .values(&vec![number.eq(1), number.eq(2)]) + /// .execute(conn)?; + /// let average = numbers.select(avg(number)).get_result(conn)?; + /// let expected = "1.5".parse::().unwrap(); + /// assert_eq!(Some(expected), average); + /// # Ok(()) + /// # } + /// # + /// # #[cfg(not(all(feature = "numeric", any(feature = "postgres", not(feature = "sqlite")))))] + /// # fn run_test() -> QueryResult<()> { + /// # Ok(()) + /// # } + #[aggregate] + fn avg(expr: ST) -> ST::Avg; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/aggregate_ordering.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/aggregate_ordering.rs new file mode 100644 index 000000000..c36b6cc0b --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/aggregate_ordering.rs @@ -0,0 +1,55 @@ +use self::private::SqlOrdAggregate; +use crate::expression::functions::define_sql_function; + +define_sql_function! { + /// Represents a SQL `MAX` function. This function can only take types which are + /// ordered. + /// + /// # Examples + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// assert_eq!(Ok(Some(8)), animals.select(max(legs)).first(connection)); + /// # } + #[aggregate] + fn max(expr: ST) -> ST::Ret; +} + +define_sql_function! { + /// Represents a SQL `MIN` function. This function can only take types which are + /// ordered. + /// + /// # Examples + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// assert_eq!(Ok(Some(4)), animals.select(min(legs)).first(connection)); + /// # } + #[aggregate] + fn min(expr: ST) -> ST::Ret; +} + +mod private { + use crate::sql_types::{IntoNullable, SingleValue, SqlOrd, SqlType}; + pub trait SqlOrdAggregate: SingleValue { + type Ret: SqlType + SingleValue; + } + + impl SqlOrdAggregate for T + where + T: SqlOrd + IntoNullable + SingleValue, + T::Nullable: SqlType + SingleValue, + { + type Ret = T::Nullable; + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/date_and_time.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/date_and_time.rs new file mode 100644 index 000000000..8433d5db8 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/date_and_time.rs @@ -0,0 +1,124 @@ +use crate::backend::Backend; +use crate::expression::coerce::Coerce; +use crate::expression::functions::define_sql_function; +use crate::expression::{AsExpression, Expression, ValidGrouping}; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::*; + +/// Represents the SQL `CURRENT_TIMESTAMP` constant. This is equivalent to the +/// `NOW()` function on backends that support it. +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] +pub struct now; + +impl Expression for now { + type SqlType = Timestamp; +} + +impl QueryFragment for now { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql("CURRENT_TIMESTAMP"); + Ok(()) + } +} + +impl_selectable_expression!(now); + +operator_allowed!(now, Add, add); +operator_allowed!(now, Sub, sub); +define_sql_function! { + /// Represents the SQL `DATE` function. The argument should be a Timestamp + /// expression, and the return value will be an expression of type Date. + /// + /// # Examples + /// + /// ``` + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::{now, date}; + /// # use diesel::deserialize::Queryable; + /// # + /// # fn test + 'static>() -> QueryResult { + /// # let connection = &mut establish_connection(); + /// let today = diesel::select(date(now)).first(connection)?; + /// # Ok(today) + /// # } + /// # fn main() { + /// # + /// # } + /// ``` + fn date(expr: Timestamp) -> Date; +} + +impl AsExpression> for now { + type Expression = Coerce>; + + fn as_expression(self) -> Self::Expression { + Coerce::new(self) + } +} + +#[cfg(feature = "postgres_backend")] +impl AsExpression for now { + type Expression = Coerce; + + fn as_expression(self) -> Self::Expression { + Coerce::new(self) + } +} + +#[cfg(feature = "postgres_backend")] +impl AsExpression> for now { + type Expression = Coerce>; + + fn as_expression(self) -> Self::Expression { + Coerce::new(self) + } +} + +#[cfg(feature = "sqlite")] +impl AsExpression for now { + type Expression = Coerce; + + fn as_expression(self) -> Self::Expression { + Coerce::new(self) + } +} + +#[cfg(feature = "sqlite")] +impl AsExpression> for now { + type Expression = Coerce>; + + fn as_expression(self) -> Self::Expression { + Coerce::new(self) + } +} + +/// Represents the SQL `CURRENT_DATE` constant. +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] +pub struct today; + +impl Expression for today { + type SqlType = Date; +} + +impl QueryFragment for today { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql("CURRENT_DATE"); + Ok(()) + } +} + +impl_selectable_expression!(today); + +operator_allowed!(today, Add, add); +operator_allowed!(today, Sub, sub); + +impl AsExpression> for today { + type Expression = Coerce>; + + fn as_expression(self) -> Self::Expression { + Coerce::new(self) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/helper_types.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/helper_types.rs new file mode 100644 index 000000000..4f6bd2be0 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/helper_types.rs @@ -0,0 +1,23 @@ +#![allow(non_camel_case_types)] + +use crate::dsl::SqlTypeOf; +use crate::expression::grouped::Grouped; +use crate::expression::operators; + +/// The return type of [`not(expr)`](crate::dsl::not()) +pub type not = operators::Not>; + +/// The return type of [`max(expr)`](crate::dsl::max()) +pub type max = super::aggregate_ordering::max, Expr>; + +/// The return type of [`min(expr)`](crate::dsl::min()) +pub type min = super::aggregate_ordering::min, Expr>; + +/// The return type of [`sum(expr)`](crate::dsl::sum()) +pub type sum = super::aggregate_folding::sum, Expr>; + +/// The return type of [`avg(expr)`](crate::dsl::avg()) +pub type avg = super::aggregate_folding::avg, Expr>; + +/// The return type of [`exists(expr)`](crate::dsl::exists()) +pub type exists = crate::expression::exists::Exists; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/mod.rs new file mode 100644 index 000000000..db8f79e7a --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/functions/mod.rs @@ -0,0 +1,100 @@ +//! Helper macros to define custom sql functions + +#[doc(inline)] +pub use diesel_derives::define_sql_function; + +#[doc(inline)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +pub use diesel_derives::sql_function_proc as sql_function; + +#[macro_export] +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +macro_rules! no_arg_sql_function_body_except_to_sql { + ($type_name:ident, $return_type:ty, $docs:expr) => { + #[allow(non_camel_case_types)] + #[doc=$docs] + #[derive( + Debug, Clone, Copy, $crate::query_builder::QueryId, $crate::expression::ValidGrouping, + )] + pub struct $type_name; + + impl $crate::expression::Expression for $type_name { + type SqlType = $return_type; + } + + impl $crate::expression::SelectableExpression for $type_name {} + + impl $crate::expression::AppearsOnTable for $type_name {} + }; +} + +#[macro_export] +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +macro_rules! no_arg_sql_function_body { + ($type_name:ident, $return_type:ty, $docs:expr, $($constraint:ident)::+) => { + no_arg_sql_function_body_except_to_sql!($type_name, $return_type, $docs); + + impl $crate::query_builder::QueryFragment for $type_name where + DB: $crate::backend::Backend + $($constraint)::+, + { + fn walk_ast<'b>(&'b self, mut out: $crate::query_builder::AstPass<'_, 'b, DB>) -> $crate::result::QueryResult<()> + { + out.push_sql(concat!(stringify!($type_name), "()")); + Ok(()) + } + } + }; + + ($type_name:ident, $return_type:ty, $docs:expr) => { + no_arg_sql_function_body_except_to_sql!($type_name, $return_type, $docs); + + impl $crate::query_builder::QueryFragment for $type_name where + DB: $crate::backend::Backend, + { + fn walk_ast<'b>(&'b self, mut out: $crate::query_builder::AstPass<'_, 'b, DB>) -> $crate::result::QueryResult<()> { + out.push_sql(concat!(stringify!($type_name), "()")); + Ok(()) + } + } + }; +} + +#[macro_export] +/// Declare a 0 argument SQL function for use in your code. This will generate a +/// unit struct, which is an expression representing calling this function. See +/// [`now`](crate::expression::dsl::now) for example output. `now` was +/// generated using: +/// +/// ```no_run +/// # pub use diesel::*; +/// no_arg_sql_function!(now, sql_types::Timestamp, "Represents the SQL NOW() function"); +/// # fn main() {} +/// ``` +/// +/// You can optionally pass the name of a trait, as a constraint for backends which support the +/// function. +#[deprecated( + since = "2.0.0", + note = "Use `define_sql_function!` instead. See `CHANGELOG.md` for migration instructions" +)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +macro_rules! no_arg_sql_function { + ($type_name:ident, $return_type:ty) => { + no_arg_sql_function!($type_name, $return_type, ""); + }; + + ($type_name:ident, $return_type:ty, $docs:expr) => { + no_arg_sql_function_body!($type_name, $return_type, $docs); + }; + + ($type_name:ident, $return_type:ty, $docs:expr, $($constraint:ident)::+) => { + no_arg_sql_function_body!($type_name, $return_type, $docs, $($constraint)::+); + }; +} + +pub(crate) mod aggregate_folding; +pub(crate) mod aggregate_ordering; +pub(crate) mod date_and_time; +pub(crate) mod helper_types; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/grouped.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/grouped.rs new file mode 100644 index 000000000..b768a6bf0 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/grouped.rs @@ -0,0 +1,27 @@ +use crate::backend::{Backend, DieselReserveSpecialization}; +use crate::expression::{Expression, ValidGrouping}; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::DieselNumericOps; + +#[derive(Debug, Copy, Clone, QueryId, Default, DieselNumericOps, ValidGrouping)] +pub struct Grouped(pub T); + +impl Expression for Grouped { + type SqlType = T::SqlType; +} + +impl QueryFragment for Grouped +where + T: QueryFragment, + DB: Backend + DieselReserveSpecialization, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql("("); + self.0.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +impl_selectable_expression!(Grouped); diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/helper_types.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/helper_types.rs new file mode 100644 index 000000000..8c27e7bfd --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/helper_types.rs @@ -0,0 +1,187 @@ +//! The types in this module are all shorthand for `PredicateType>`. Since we often need to return concrete types, instead of +//! a boxed trait object, these can be useful for writing concise return types. +use super::array_comparison::{AsInExpression, In, NotIn}; +use super::grouped::Grouped; +use super::select_by::SelectBy; +use super::{AsExpression, Expression}; +use crate::expression; +use crate::expression_methods::PreferredBoolSqlType; +use crate::sql_types; + +/// The SQL type of an expression +pub type SqlTypeOf = ::SqlType; + +/// The type of `Item` when converted to an expression with the same type as `TargetExpr` +pub type AsExpr = AsExprOf>; + +/// The type of `Item` when converted to an expression of `Type` +pub type AsExprOf = >::Expression; + +/// The return type of +/// [`lhs.eq(rhs)`](crate::expression_methods::ExpressionMethods::eq()) +pub type Eq = Grouped>>; + +/// The return type of +/// [`lhs.ne(rhs)`](crate::expression_methods::ExpressionMethods::ne()) +pub type NotEq = Grouped>>; + +#[doc(hidden)] // required for `#[auto_type]` +pub type Ne = NotEq; + +/// The return type of +/// [`lhs.eq_any(rhs)`](crate::expression_methods::ExpressionMethods::eq_any()) +pub type EqAny = Grouped>>::InExpression>>; + +/// The return type of +/// [`lhs.ne_all(rhs)`](crate::expression_methods::ExpressionMethods::ne_all()) +pub type NeAny = + Grouped>>::InExpression>>; + +#[doc(hidden)] // required for `#[auto_type]` +pub type NeAll = NeAny; + +/// The return type of +/// [`expr.is_null()`](crate::expression_methods::ExpressionMethods::is_null()) +pub type IsNull = Grouped>; + +/// The return type of +/// [`expr.is_not_null()`](crate::expression_methods::ExpressionMethods::is_not_null()) +pub type IsNotNull = Grouped>; + +/// The return type of +/// [`lhs.gt(rhs)`](crate::expression_methods::ExpressionMethods::gt()) +pub type Gt = Grouped>>; + +/// The return type of +/// [`lhs.ge(rhs)`](crate::expression_methods::ExpressionMethods::ge()) +pub type GtEq = Grouped>>; + +#[doc(hidden)] // required for `#[auto_type]` +pub type Ge = GtEq; + +/// The return type of +/// [`lhs.lt(rhs)`](crate::expression_methods::ExpressionMethods::lt()) +pub type Lt = Grouped>>; + +/// The return type of +/// [`lhs.le(rhs)`](crate::expression_methods::ExpressionMethods::le()) +pub type LtEq = Grouped>>; + +#[doc(hidden)] // required for `#[auto_type]` +pub type Le = LtEq; + +/// The return type of +/// [`lhs.between(lower, upper)`](crate::expression_methods::ExpressionMethods::between()) +pub type Between = Grouped< + super::operators::Between, AsExpr>>, +>; + +/// The return type of +/// [`lhs.not_between(lower, upper)`](crate::expression_methods::ExpressionMethods::not_between()) +pub type NotBetween = Grouped< + super::operators::NotBetween< + Lhs, + super::operators::And, AsExpr>, + >, +>; + +/// The return type of +/// [`lhs.concat(rhs)`](crate::expression_methods::TextExpressionMethods::concat()) +pub type Concat = Grouped>>; + +/// The return type of +/// [`expr.desc()`](crate::expression_methods::ExpressionMethods::desc()) +pub type Desc = super::operators::Desc; + +/// The return type of +/// [`expr.asc()`](crate::expression_methods::ExpressionMethods::asc()) +pub type Asc = super::operators::Asc; + +/// The return type of +/// [`expr.nullable()`](crate::expression_methods::NullableExpressionMethods::nullable()) +pub type Nullable = super::nullable::Nullable; + +/// The return type of +/// [`expr.assume_not_null()`](crate::expression_methods::NullableExpressionMethods::assume_not_null()) +pub type AssumeNotNull = super::assume_not_null::AssumeNotNull; + +/// The return type of +/// [`lhs.and(rhs)`](crate::expression_methods::BoolExpressionMethods::and()) +pub type And::PreferredSqlType> = + Grouped>>; + +/// The return type of +/// [`lhs.or(rhs)`](crate::expression_methods::BoolExpressionMethods::or()) +pub type Or::PreferredSqlType> = + Grouped>>; + +/// The return type of +/// [`lhs.escape('x')`](crate::expression_methods::EscapeExpressionMethods::escape()) +pub type Escape = Grouped< + super::operators::Escape< + ::TextExpression, + AsExprOf, + >, +>; + +/// The return type of +/// [`lhs.like(rhs)`](crate::expression_methods::TextExpressionMethods::like()) +pub type Like = Grouped>>>; + +/// The return type of +/// [`lhs.not_like(rhs)`](crate::expression_methods::TextExpressionMethods::not_like()) +pub type NotLike = Grouped>>>; + +/// The return type of [`case_when()`](expression::case_when::case_when) +#[allow(non_camel_case_types)] // required for `#[auto_type]` +pub type case_when::SqlType> = expression::case_when::CaseWhen< + expression::case_when::CaseWhenConditionsLeaf, Grouped>>, + expression::case_when::NoElseExpression, +>; +/// The return type of [`case_when(...).when(...)`](expression::CaseWhen::when) +pub type When = expression::case_when::CaseWhen< + expression::case_when::CaseWhenConditionsIntermediateNode< + Grouped, + Grouped::OutputExpressionSpecifiedSqlType>>, + ::Whens, + >, + ::Else, +>; +/// The return type of [`case_when(...).otherwise(...)`](expression::case_when::CaseWhen::otherwise) +pub type Otherwise = expression::case_when::CaseWhen< + ::Whens, + expression::case_when::ElseExpression::OutputExpressionSpecifiedSqlType>>>, +>; + +/// Represents the return type of [`.as_select()`](crate::prelude::SelectableHelper::as_select) +pub type AsSelect = SelectBy; + +/// Represents the return type of [`.into_sql()`](crate::expression::IntoSql::into_sql) +pub type IntoSql = AsExprOf; + +/// The return type of [`alias.field(field)`](crate::query_source::Alias::field) +pub type Field = Fields; + +/// The return type of [`alias.fields(fields)`](crate::query_source::Alias::fields) +pub type Fields = ::Source, +>>::Out; + +// we allow unreachable_pub here +// as rustc otherwise shows false positives +// for every item in this module. We reexport +// everything from `crate::helper_types::` +#[doc(inline)] +#[allow(unreachable_pub)] +pub use super::functions::helper_types::*; + +#[doc(inline)] +#[cfg(feature = "postgres_backend")] +#[allow(unreachable_pub)] +pub use crate::pg::expression::helper_types::*; + +#[doc(inline)] +#[cfg(feature = "sqlite")] +#[allow(unreachable_pub)] +pub use crate::sqlite::expression::helper_types::*; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/mod.rs new file mode 100644 index 000000000..27a4342b5 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/mod.rs @@ -0,0 +1,1058 @@ +//! AST types representing various typed SQL expressions. +//! +//! Almost all types implement either [`Expression`] or +//! [`AsExpression`]. +//! +//! The most common expression to work with is a +//! [`Column`](crate::query_source::Column). There are various methods +//! that you can call on these, found in +//! [`expression_methods`](crate::expression_methods). +//! +//! You can also use numeric operators such as `+` on expressions of the +//! appropriate type. +//! +//! Any primitive which implements [`ToSql`](crate::serialize::ToSql) will +//! also implement [`AsExpression`], allowing it to be +//! used as an argument to any of the methods described here. +#[macro_use] +pub(crate) mod ops; +pub mod functions; + +#[cfg(not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))] +pub(crate) mod array_comparison; +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +pub mod array_comparison; +pub(crate) mod assume_not_null; +pub(crate) mod bound; +mod coerce; +pub(crate) mod count; +#[cfg(not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))] +pub(crate) mod exists; +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +pub mod exists; +pub(crate) mod grouped; +pub(crate) mod helper_types; +mod not; +pub(crate) mod nullable; +#[macro_use] +pub(crate) mod operators; +mod case_when; +pub(crate) mod select_by; +mod sql_literal; +pub(crate) mod subselect; + +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +pub use self::operators::Concat; + +// we allow unreachable_pub here +// as rustc otherwise shows false positives +// for every item in this module. We reexport +// everything from `crate::helper_types::` +#[allow(non_camel_case_types, unreachable_pub)] +pub(crate) mod dsl { + use crate::dsl::SqlTypeOf; + + #[doc(inline)] + pub use super::case_when::case_when; + #[doc(inline)] + pub use super::count::*; + #[doc(inline)] + pub use super::exists::exists; + #[doc(inline)] + pub use super::functions::aggregate_folding::*; + #[doc(inline)] + pub use super::functions::aggregate_ordering::*; + #[doc(inline)] + pub use super::functions::date_and_time::*; + #[doc(inline)] + pub use super::helper_types::{case_when, IntoSql, Otherwise, When}; + #[doc(inline)] + pub use super::not::not; + #[doc(inline)] + pub use super::sql_literal::sql; + + #[cfg(feature = "postgres_backend")] + pub use crate::pg::expression::dsl::*; + + /// The return type of [`count(expr)`](crate::dsl::count()) + pub type count = super::count::count, Expr>; + + /// The return type of [`count_star()`](crate::dsl::count_star()) + pub type count_star = super::count::CountStar; + + /// The return type of [`count_distinct()`](crate::dsl::count_distinct()) + pub type count_distinct = super::count::CountDistinct, Expr>; + + /// The return type of [`date(expr)`](crate::dsl::date()) + pub type date = super::functions::date_and_time::date; + + #[cfg(feature = "mysql_backend")] + pub use crate::mysql::query_builder::DuplicatedKeys; +} + +#[doc(inline)] +pub use self::case_when::CaseWhen; +#[doc(inline)] +pub use self::sql_literal::{SqlLiteral, UncheckedBind}; + +use crate::backend::Backend; +use crate::dsl::{AsExprOf, AsSelect}; +use crate::sql_types::{HasSqlType, SingleValue, SqlType}; + +/// Represents a typed fragment of SQL. +/// +/// Apps should not need to implement this type directly, but it may be common +/// to use this in where clauses. Libraries should consider using +/// [`infix_operator!`](crate::infix_operator!) or +/// [`postfix_operator!`](crate::postfix_operator!) instead of +/// implementing this directly. +pub trait Expression { + /// The type that this expression represents in SQL + type SqlType: TypedExpressionType; +} + +/// Marker trait for possible types of [`Expression::SqlType`] +/// +pub trait TypedExpressionType {} + +/// Possible types for []`Expression::SqlType`] +/// +pub mod expression_types { + use super::{QueryMetadata, TypedExpressionType}; + use crate::backend::Backend; + use crate::sql_types::SingleValue; + + /// Query nodes with this expression type do not have a statically at compile + /// time known expression type. + /// + /// An example for such a query node in diesel itself, is `sql_query` as + /// we do not know which fields are returned from such a query at compile time. + /// + /// For loading values from queries returning a type of this expression, consider + /// using [`#[derive(QueryableByName)]`](derive@crate::deserialize::QueryableByName) + /// on the corresponding result type. + /// + #[derive(Clone, Copy, Debug)] + pub struct Untyped; + + /// Query nodes witch cannot be part of a select clause. + /// + /// If you see an error message containing `FromSqlRow` and this type + /// recheck that you have written a valid select clause + #[derive(Debug, Clone, Copy)] + pub struct NotSelectable; + + impl TypedExpressionType for Untyped {} + impl TypedExpressionType for NotSelectable {} + + impl TypedExpressionType for ST where ST: SingleValue {} + + impl QueryMetadata for DB { + fn row_metadata(_: &mut DB::MetadataLookup, row: &mut Vec>) { + row.push(None) + } + } +} + +impl Expression for Box { + type SqlType = T::SqlType; +} + +impl Expression for &T { + type SqlType = T::SqlType; +} + +/// A helper to translate type level sql type information into +/// runtime type information for specific queries +/// +/// If you do not implement a custom backend implementation +/// this trait is likely not relevant for you. +pub trait QueryMetadata: Backend { + /// The exact return value of this function is considered to be a + /// backend specific implementation detail. You should not rely on those + /// values if you not own the corresponding backend + fn row_metadata(lookup: &mut Self::MetadataLookup, out: &mut Vec>); +} + +impl QueryMetadata for DB +where + DB: Backend + HasSqlType, + T: SingleValue, +{ + fn row_metadata(lookup: &mut Self::MetadataLookup, out: &mut Vec>) { + out.push(Some(>::metadata(lookup))) + } +} + +/// Converts a type to its representation for use in Diesel's query builder. +/// +/// This trait is used directly. Apps should typically use [`IntoSql`] instead. +/// +/// Implementations of this trait will generally do one of 3 things: +/// +/// - Return `self` for types which are already parts of Diesel's query builder +/// - Perform some implicit coercion (for example, allowing [`now`] to be used as +/// both [`Timestamp`] and [`Timestamptz`]. +/// - Indicate that the type has data which will be sent separately from the +/// query. This is generally referred as a "bind parameter". Types which +/// implement [`ToSql`] will generally implement `AsExpression` this way. +/// +/// [`IntoSql`]: crate::IntoSql +/// [`now`]: crate::dsl::now +/// [`Timestamp`]: crate::sql_types::Timestamp +/// [`Timestamptz`]: ../pg/types/sql_types/struct.Timestamptz.html +/// [`ToSql`]: crate::serialize::ToSql +/// +/// This trait could be [derived](derive@AsExpression) +pub trait AsExpression +where + T: SqlType + TypedExpressionType, +{ + /// The expression being returned + type Expression: Expression; + + /// Perform the conversion + #[allow(clippy::wrong_self_convention)] + // That's public API we cannot change it to appease clippy + fn as_expression(self) -> Self::Expression; +} + +#[doc(inline)] +pub use diesel_derives::AsExpression; + +impl AsExpression for T +where + T: Expression, + ST: SqlType + TypedExpressionType, +{ + type Expression = T; + + fn as_expression(self) -> T { + self + } +} + +/// Converts a type to its representation for use in Diesel's query builder. +/// +/// This trait only exists to make usage of `AsExpression` more ergonomic when +/// the `SqlType` cannot be inferred. It is generally used when you need to use +/// a Rust value as the left hand side of an expression, or when you want to +/// select a constant value. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::users; +/// # +/// # fn main() { +/// use diesel::sql_types::Text; +/// # let conn = &mut establish_connection(); +/// let names = users::table +/// .select("The Amazing ".into_sql::().concat(users::name)) +/// .load(conn); +/// let expected_names = vec![ +/// "The Amazing Sean".to_string(), +/// "The Amazing Tess".to_string(), +/// ]; +/// assert_eq!(Ok(expected_names), names); +/// # } +/// ``` +pub trait IntoSql { + /// Convert `self` to an expression for Diesel's query builder. + /// + /// There is no difference in behavior between `x.into_sql::()` and + /// `AsExpression::::as_expression(x)`. + fn into_sql(self) -> AsExprOf + where + Self: AsExpression + Sized, + T: SqlType + TypedExpressionType, + { + self.as_expression() + } + + /// Convert `&self` to an expression for Diesel's query builder. + /// + /// There is no difference in behavior between `x.as_sql::()` and + /// `AsExpression::::as_expression(&x)`. + fn as_sql<'a, T>(&'a self) -> AsExprOf<&'a Self, T> + where + &'a Self: AsExpression, + T: SqlType + TypedExpressionType, + { + <&'a Self as AsExpression>::as_expression(self) + } +} + +impl IntoSql for T {} + +/// Indicates that all elements of an expression are valid given a from clause. +/// +/// This is used to ensure that `users.filter(posts::id.eq(1))` fails to +/// compile. This constraint is only used in places where the nullability of a +/// SQL type doesn't matter (everything except `select` and `returning`). For +/// places where nullability is important, `SelectableExpression` is used +/// instead. +pub trait AppearsOnTable: Expression {} + +impl AppearsOnTable for Box +where + T: AppearsOnTable, + Box: Expression, +{ +} + +impl<'a, T: ?Sized, QS> AppearsOnTable for &'a T +where + T: AppearsOnTable, + &'a T: Expression, +{ +} + +/// Indicates that an expression can be selected from a source. +/// +/// Columns will implement this for their table. Certain special types, like +/// `CountStar` and `Bound` will implement this for all sources. Most compound +/// expressions will implement this if each of their parts implement it. +/// +/// Notably, columns will not implement this trait for the right side of a left +/// join. To select a column or expression using a column from the right side of +/// a left join, you must call `.nullable()` on it. +#[diagnostic::on_unimplemented( + message = "Cannot select `{Self}` from `{QS}`", + note = "`{Self}` is no valid selection for `{QS}`" +)] +pub trait SelectableExpression: AppearsOnTable {} + +impl SelectableExpression for Box +where + T: SelectableExpression, + Box: AppearsOnTable, +{ +} + +impl<'a, T: ?Sized, QS> SelectableExpression for &'a T +where + T: SelectableExpression, + &'a T: AppearsOnTable, +{ +} + +/// Trait indicating that a record can be selected and queried from the database. +/// +/// Types which implement `Selectable` represent the select clause of a SQL query. +/// Use [`SelectableHelper::as_select()`] to construct the select clause. Once you +/// called `.select(YourType::as_select())` we enforce at the type system level that you +/// use the same type to load the query result into. +/// +/// The constructed select clause can contain arbitrary expressions coming from different +/// tables. The corresponding [derive](derive@Selectable) provides a simple way to +/// construct a select clause matching fields to the corresponding table columns. +/// +/// # Examples +/// +/// If you just want to construct a select clause using an existing struct, you can use +/// `#[derive(Selectable)]`, See [`#[derive(Selectable)]`](derive@Selectable) for details. +/// +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// use schema::users; +/// +/// #[derive(Queryable, PartialEq, Debug, Selectable)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = &mut establish_connection(); +/// let first_user = users.select(User::as_select()).first(connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// Alternatively, we can implement the trait for our struct manually. +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// use schema::users; +/// use diesel::prelude::{Queryable, Selectable}; +/// use diesel::backend::Backend; +/// +/// #[derive(Queryable, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// impl Selectable for User +/// where +/// DB: Backend +/// { +/// type SelectExpression = (users::id, users::name); +/// +/// fn construct_selection() -> Self::SelectExpression { +/// (users::id, users::name) +/// } +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = &mut establish_connection(); +/// let first_user = users.select(User::as_select()).first(connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// When selecting from joined tables, you can select from a +/// composition of types that implement `Selectable`. The simplest way +/// is to use a tuple of all the types you wish to select. +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// use schema::{users, posts}; +/// +/// #[derive(Debug, PartialEq, Queryable, Selectable)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// #[derive(Debug, PartialEq, Queryable, Selectable)] +/// struct Post { +/// id: i32, +/// user_id: i32, +/// title: String, +/// } +/// +/// # fn main() -> QueryResult<()> { +/// # let connection = &mut establish_connection(); +/// # +/// let (first_user, first_post) = users::table +/// .inner_join(posts::table) +/// .select(<(User, Post)>::as_select()) +/// .first(connection)?; +/// +/// let expected_user = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected_user, first_user); +/// +/// let expected_post = Post { id: 1, user_id: 1, title: "My first post".into() }; +/// assert_eq!(expected_post, first_post); +/// # +/// # Ok(()) +/// # } +/// ``` +/// +/// If you want to load only a subset of fields, you can create types +/// with those fields and use them in the composition. +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// use schema::{users, posts}; +/// +/// #[derive(Debug, PartialEq, Queryable, Selectable)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// #[derive(Debug, PartialEq, Queryable, Selectable)] +/// #[diesel(table_name = posts)] +/// struct PostTitle { +/// title: String, +/// } +/// +/// # fn main() -> QueryResult<()> { +/// # let connection = &mut establish_connection(); +/// # +/// let (first_user, first_post_title) = users::table +/// .inner_join(posts::table) +/// .select(<(User, PostTitle)>::as_select()) +/// .first(connection)?; +/// +/// let expected_user = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected_user, first_user); +/// +/// let expected_post_title = PostTitle { title: "My first post".into() }; +/// assert_eq!(expected_post_title, first_post_title); +/// # +/// # Ok(()) +/// # } +/// ``` +/// +/// You are not limited to using only tuples to build the composed +/// type. The [`Selectable`](derive@Selectable) derive macro allows +/// you to *embed* other types. This is useful when you want to +/// implement methods or traits on the composed type. +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// use schema::{users, posts}; +/// +/// #[derive(Debug, PartialEq, Queryable, Selectable)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// #[derive(Debug, PartialEq, Queryable, Selectable)] +/// #[diesel(table_name = posts)] +/// struct PostTitle { +/// title: String, +/// } +/// +/// #[derive(Debug, PartialEq, Queryable, Selectable)] +/// struct UserPost { +/// #[diesel(embed)] +/// user: User, +/// #[diesel(embed)] +/// post_title: PostTitle, +/// } +/// +/// # fn main() -> QueryResult<()> { +/// # let connection = &mut establish_connection(); +/// # +/// let first_user_post = users::table +/// .inner_join(posts::table) +/// .select(UserPost::as_select()) +/// .first(connection)?; +/// +/// let expected_user_post = UserPost { +/// user: User { +/// id: 1, +/// name: "Sean".into(), +/// }, +/// post_title: PostTitle { +/// title: "My first post".into(), +/// }, +/// }; +/// assert_eq!(expected_user_post, first_user_post); +/// # +/// # Ok(()) +/// # } +/// ``` +/// +/// It is also possible to specify an entirely custom select expression +/// for fields when deriving [`Selectable`](derive@Selectable). +/// This is useful for example to +/// +/// * avoid nesting types, or to +/// * populate fields with values other than table columns, such as +/// the result of an SQL function like `CURRENT_TIMESTAMP()` +/// or a custom SQL function. +/// +/// The select expression is specified via the `select_expression` parameter. +/// +/// Query fragments created using [`dsl::auto_type`](crate::dsl::auto_type) are supported, which +/// may be useful as the select expression gets large: it may not be practical to inline it in +/// the attribute then. +/// +/// The type of the expression is usually inferred. If it can't be fully inferred automatically, +/// one may either: +/// - Put type annotations in inline blocks in the query fragment itself +/// - Use a dedicated [`dsl::auto_type`](crate::dsl::auto_type) function as `select_expression` +/// and use [`dsl::auto_type`'s type annotation features](crate::dsl::auto_type) +/// - Specify the type of the expression using the `select_expression_type` attribute +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// use schema::{users, posts}; +/// use diesel::dsl; +/// +/// #[derive(Debug, PartialEq, Queryable, Selectable)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// #[derive(Debug, PartialEq, Queryable, Selectable)] +/// #[diesel(table_name = posts)] +/// struct PostTitle { +/// title: String, +/// } +/// +/// #[derive(Debug, PartialEq, Queryable, Selectable)] +/// struct UserPost { +/// #[diesel(select_expression = users::columns::id)] +/// #[diesel(select_expression_type = users::columns::id)] +/// id: i32, +/// #[diesel(select_expression = users::columns::name)] +/// name: String, +/// #[diesel(select_expression = complex_fragment_for_title())] +/// title: String, +/// # #[cfg(feature = "chrono")] +/// #[diesel(select_expression = diesel::dsl::now)] +/// access_time: chrono::NaiveDateTime, +/// #[diesel(select_expression = users::columns::id.eq({let id: i32 = FOO; id}))] +/// user_id_is_foo: bool, +/// } +/// const FOO: i32 = 42; // Type of FOO can't be inferred automatically in the select_expression +/// #[dsl::auto_type] +/// fn complex_fragment_for_title() -> _ { +/// // See the `#[dsl::auto_type]` documentation for examples of more complex usage +/// posts::columns::title +/// } +/// +/// # fn main() -> QueryResult<()> { +/// # let connection = &mut establish_connection(); +/// # +/// let first_user_post = users::table +/// .inner_join(posts::table) +/// .select(UserPost::as_select()) +/// .first(connection)?; +/// +/// let expected_user_post = UserPost { +/// id: 1, +/// name: "Sean".into(), +/// title: "My first post".into(), +/// # #[cfg(feature = "chrono")] +/// access_time: first_user_post.access_time, +/// user_id_is_foo: false, +/// }; +/// assert_eq!(expected_user_post, first_user_post); +/// # +/// # Ok(()) +/// # } +/// ``` +/// +pub trait Selectable { + /// The expression you'd like to select. + /// + /// This is typically a tuple of corresponding to the table columns of your struct's fields. + type SelectExpression: Expression; + + /// Construct an instance of the expression + fn construct_selection() -> Self::SelectExpression; +} + +#[doc(inline)] +pub use diesel_derives::Selectable; + +/// This helper trait provides several methods for +/// constructing a select or returning clause based on a +/// [`Selectable`] implementation. +pub trait SelectableHelper: Selectable + Sized { + /// Construct a select clause based on a [`Selectable`] implementation. + /// + /// The returned select clause enforces that you use the same type + /// for constructing the select clause and for loading the query result into. + fn as_select() -> AsSelect; + + /// An alias for `as_select` that can be used with returning clauses + fn as_returning() -> AsSelect { + Self::as_select() + } +} + +impl SelectableHelper for T +where + T: Selectable, + DB: Backend, +{ + fn as_select() -> AsSelect { + select_by::SelectBy::new() + } +} + +/// Is this expression valid for a given group by clause? +/// +/// Implementations of this trait must ensure that aggregate expressions are +/// not mixed with non-aggregate expressions. +/// +/// For generic types, you can determine if your sub-expressions can appear +/// together using the [`MixedAggregates`] trait. +/// +/// `GroupByClause` will be a tuple containing the set of expressions appearing +/// in the `GROUP BY` portion of the query. If there is no `GROUP BY`, it will +/// be `()`. +/// +/// This trait can be [derived] +/// +/// [derived]: derive@ValidGrouping +pub trait ValidGrouping { + /// Is this expression aggregate? + /// + /// This type should always be one of the structs in the [`is_aggregate`] + /// module. See the documentation of those structs for more details. + /// + type IsAggregate; +} + +impl + ?Sized, GB> ValidGrouping for Box { + type IsAggregate = T::IsAggregate; +} + +impl + ?Sized, GB> ValidGrouping for &T { + type IsAggregate = T::IsAggregate; +} + +#[doc(inline)] +pub use diesel_derives::ValidGrouping; + +#[doc(hidden)] +pub trait IsContainedInGroupBy { + type Output; +} + +#[doc(hidden)] +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub mod is_contained_in_group_by { + pub struct Yes; + pub struct No; + + pub trait IsAny { + type Output; + } + + impl IsAny for Yes { + type Output = Yes; + } + + impl IsAny for No { + type Output = Yes; + } + + impl IsAny for No { + type Output = No; + } +} + +/// Can two `IsAggregate` types appear in the same expression? +/// +/// You should never implement this trait. It will eventually become a trait +/// alias. +/// +/// [`is_aggregate::Yes`] and [`is_aggregate::No`] can only appear with +/// themselves or [`is_aggregate::Never`]. [`is_aggregate::Never`] can appear +/// with anything. +/// +pub trait MixedAggregates { + /// What is the resulting `IsAggregate` type? + type Output; +} + +#[allow(missing_debug_implementations, missing_copy_implementations)] +/// Possible values for `ValidGrouping::IsAggregate` +pub mod is_aggregate { + use super::MixedAggregates; + + /// Yes, this expression is aggregate for the given group by clause. + pub struct Yes; + + /// No, this expression is not aggregate with the given group by clause, + /// but it might be aggregate with a different group by clause. + pub struct No; + + /// This expression is never aggregate, and can appear with any other + /// expression, regardless of whether it is aggregate. + /// + /// Examples of this are literals. `1` does not care about aggregation. + /// `foo + 1` is always valid, regardless of whether `foo` appears in the + /// group by clause or not. + pub struct Never; + + impl MixedAggregates for Yes { + type Output = Yes; + } + + impl MixedAggregates for Yes { + type Output = Yes; + } + + impl MixedAggregates for No { + type Output = No; + } + + impl MixedAggregates for No { + type Output = No; + } + + impl MixedAggregates for Never { + type Output = T; + } +} + +#[cfg(feature = "unstable")] +// this needs to be a separate module for the reasons given in +// https://github.com/rust-lang/rust/issues/65860 +mod unstable; + +#[cfg(feature = "unstable")] +#[doc(inline)] +pub use self::unstable::NonAggregate; + +// Note that these docs are similar to but slightly different than the unstable +// docs above. Make sure if you change these that you also change the docs +// above. +/// Trait alias to represent an expression that isn't aggregate by default. +/// +/// This trait should never be implemented directly. It is replaced with a +/// trait alias when the `unstable` feature is enabled. +/// +/// This alias represents a type which is not aggregate if there is no group by +/// clause. More specifically, it represents for types which implement +/// [`ValidGrouping<()>`] where `IsAggregate` is [`is_aggregate::No`] or +/// [`is_aggregate::Yes`]. +/// +/// While this trait is a useful stand-in for common cases, `T: NonAggregate` +/// cannot always be used when `T: ValidGrouping<(), IsAggregate = No>` or +/// `T: ValidGrouping<(), IsAggregate = Never>` could be. For that reason, +/// unless you need to abstract over both columns and literals, you should +/// prefer to use [`ValidGrouping<()>`] in your bounds instead. +/// +/// [`ValidGrouping<()>`]: ValidGrouping +#[cfg(not(feature = "unstable"))] +pub trait NonAggregate: ValidGrouping<()> {} + +#[cfg(not(feature = "unstable"))] +impl NonAggregate for T +where + T: ValidGrouping<()>, + T::IsAggregate: MixedAggregates, +{ +} + +use crate::query_builder::{QueryFragment, QueryId}; + +/// Helper trait used when boxing expressions. +/// +/// In Rust you cannot create a trait object with more than one trait. +/// This type has all of the additional traits you would want when using +/// `Box` as a single trait object. +/// +/// By default `BoxableExpression` is not usable in queries that have a custom +/// group by clause. Setting the generic parameters `GB` and `IsAggregate` allows +/// to configure the expression to be used with a specific group by clause. +/// +/// This is typically used as the return type of a function. +/// For cases where you want to dynamically construct a query, +/// [boxing the query] is usually more ergonomic. +/// +/// [boxing the query]: crate::query_dsl::QueryDsl::into_boxed() +/// +/// # Examples +/// +/// ## Usage without group by clause +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::users; +/// use diesel::sql_types::Bool; +/// +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let conn = &mut establish_connection(); +/// enum Search { +/// Id(i32), +/// Name(String), +/// } +/// +/// # /* +/// type DB = diesel::sqlite::Sqlite; +/// # */ +/// +/// fn find_user(search: Search) -> Box> { +/// match search { +/// Search::Id(id) => Box::new(users::id.eq(id)), +/// Search::Name(name) => Box::new(users::name.eq(name)), +/// } +/// } +/// +/// let user_one = users::table +/// .filter(find_user(Search::Id(1))) +/// .first(conn)?; +/// assert_eq!((1, String::from("Sean")), user_one); +/// +/// let tess = users::table +/// .filter(find_user(Search::Name("Tess".into()))) +/// .first(conn)?; +/// assert_eq!((2, String::from("Tess")), tess); +/// # Ok(()) +/// # } +/// ``` +/// +/// ## Allow usage with group by clause +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// +/// # use schema::users; +/// use diesel::sql_types::Text; +/// use diesel::dsl; +/// use diesel::expression::ValidGrouping; +/// +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let conn = &mut establish_connection(); +/// enum NameOrConst { +/// Name, +/// Const(String), +/// } +/// +/// # /* +/// type DB = diesel::sqlite::Sqlite; +/// # */ +/// +/// fn selection( +/// selection: NameOrConst +/// ) -> Box< +/// dyn BoxableExpression< +/// users::table, +/// DB, +/// GB, +/// >::IsAggregate, +/// SqlType = Text +/// > +/// > +/// where +/// users::name: BoxableExpression< +/// users::table, +/// DB, +/// GB, +/// >::IsAggregate, +/// SqlType = Text +/// > + ValidGrouping, +/// { +/// match selection { +/// NameOrConst::Name => Box::new(users::name), +/// NameOrConst::Const(name) => Box::new(name.into_sql::()), +/// } +/// } +/// +/// let user_one = users::table +/// .select(selection(NameOrConst::Name)) +/// .first::(conn)?; +/// assert_eq!(String::from("Sean"), user_one); +/// +/// let with_name = users::table +/// .group_by(users::name) +/// .select(selection(NameOrConst::Const("Jane Doe".into()))) +/// .first::(conn)?; +/// assert_eq!(String::from("Jane Doe"), with_name); +/// # Ok(()) +/// # } +/// ``` +/// +/// ## More advanced query source +/// +/// This example is a bit contrived, but in general, if you want to for example filter based on +/// different criteria on a joined table, you can use `InnerJoinQuerySource` and +/// `LeftJoinQuerySource` in the QS parameter of `BoxableExpression`. +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::{users, posts}; +/// use diesel::sql_types::Bool; +/// use diesel::dsl::InnerJoinQuerySource; +/// +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let conn = &mut establish_connection(); +/// enum UserPostFilter { +/// User(i32), +/// Post(i32), +/// } +/// +/// # /* +/// type DB = diesel::sqlite::Sqlite; +/// # */ +/// +/// fn filter_user_posts( +/// filter: UserPostFilter, +/// ) -> Box, DB, SqlType = Bool>> +/// { +/// match filter { +/// UserPostFilter::User(user_id) => Box::new(users::id.eq(user_id)), +/// UserPostFilter::Post(post_id) => Box::new(posts::id.eq(post_id)), +/// } +/// } +/// +/// let post_by_user_one = users::table +/// .inner_join(posts::table) +/// .filter(filter_user_posts(UserPostFilter::User(2))) +/// .select((posts::title, users::name)) +/// .first::<(String, String)>(conn)?; +/// +/// assert_eq!( +/// ("My first post too".to_string(), "Tess".to_string()), +/// post_by_user_one +/// ); +/// # Ok(()) +/// # } +/// ``` +pub trait BoxableExpression +where + DB: Backend, + Self: Expression, + Self: SelectableExpression, + Self: QueryFragment, + Self: Send, +{ +} + +impl BoxableExpression for T +where + DB: Backend, + T: Expression, + T: SelectableExpression, + T: ValidGrouping, + T: QueryFragment, + T: Send, + T::IsAggregate: MixedAggregates, +{ +} + +impl QueryId + for dyn BoxableExpression + '_ +{ + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl ValidGrouping + for dyn BoxableExpression + '_ +{ + type IsAggregate = IsAggregate; +} + +/// Converts a tuple of values into a tuple of Diesel expressions. +/// +/// This trait is similar to [`AsExpression`], but it operates on tuples. +/// The expressions must all be of the same SQL type. +/// +pub trait AsExpressionList { + /// The final output expression + type Expression; + + /// Perform the conversion + // That's public API, we cannot change + // that to appease clippy + #[allow(clippy::wrong_self_convention)] + fn as_expression_list(self) -> Self::Expression; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/not.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/not.rs new file mode 100644 index 000000000..8280dc218 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/not.rs @@ -0,0 +1,32 @@ +use crate::expression::grouped::Grouped; +use crate::expression::Expression; +use crate::helper_types; +use crate::sql_types::BoolOrNullableBool; + +/// Creates a SQL `NOT` expression +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # let connection = &mut establish_connection(); +/// use diesel::dsl::not; +/// +/// let users_with_name = users.select(id).filter(name.eq("Sean")); +/// let users_not_with_name = users.select(id).filter( +/// not(name.eq("Sean"))); +/// +/// assert_eq!(Ok(1), users_with_name.first(connection)); +/// assert_eq!(Ok(2), users_not_with_name.first(connection)); +/// # } +/// ``` +pub fn not(expr: T) -> helper_types::not +where + T: Expression, + ::SqlType: BoolOrNullableBool, +{ + super::operators::Not::new(Grouped(expr)) +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/nullable.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/nullable.rs new file mode 100644 index 000000000..5015a3b6e --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/nullable.rs @@ -0,0 +1,58 @@ +use crate::backend::DieselReserveSpecialization; +use crate::expression::*; +use crate::query_builder::*; +use crate::query_source::joins::ToInnerJoin; +use crate::result::QueryResult; +use crate::sql_types::{DieselNumericOps, IntoNullable}; + +#[doc(hidden)] // This is used by the `table!` macro internally +#[derive(Debug, Copy, Clone, DieselNumericOps, ValidGrouping)] +pub struct Nullable(pub(crate) T); + +impl Nullable { + pub(crate) fn new(expr: T) -> Self { + Nullable(expr) + } +} + +impl Expression for Nullable +where + T: Expression, + T::SqlType: IntoNullable, + ::Nullable: TypedExpressionType, +{ + type SqlType = ::Nullable; +} + +impl QueryFragment for Nullable +where + DB: Backend + DieselReserveSpecialization, + T: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.0.walk_ast(pass) + } +} + +impl AppearsOnTable for Nullable +where + T: AppearsOnTable, + Nullable: Expression, +{ +} + +impl QueryId for Nullable { + type QueryId = T::QueryId; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl SelectableExpression for Nullable +where + Self: AppearsOnTable, + QS: ToInnerJoin, + T: SelectableExpression, +{ +} + +impl SelectableExpression for Nullable where Self: AppearsOnTable {} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/operators.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/operators.rs new file mode 100644 index 000000000..5cc9e5db8 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/operators.rs @@ -0,0 +1,766 @@ +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_operator_body { + ( + notation = $notation:ident, + struct_name = $name:ident, + operator = $operator:expr, + return_ty = (ReturnBasedOnArgs), + ty_params = ($($ty_param:ident,)+), + field_names = $field_names:tt, + backend_ty_params = $backend_ty_params:tt, + backend_ty = $backend_ty:ty, + ) => { + $crate::__diesel_operator_body! { + notation = $notation, + struct_name = $name, + operator = $operator, + return_ty = (ST), + ty_params = ($($ty_param,)+), + field_names = $field_names, + backend_ty_params = $backend_ty_params, + backend_ty = $backend_ty, + expression_ty_params = (ST,), + expression_bounds = ($($ty_param: $crate::expression::Expression,)+), + } + }; + + ( + notation = $notation:ident, + struct_name = $name:ident, + operator = $operator:expr, + return_ty = ($($return_ty:tt)+), + ty_params = ($($ty_param:ident,)+), + field_names = $field_names:tt, + backend_ty_params = $backend_ty_params:tt, + backend_ty = $backend_ty:ty, + ) => { + $crate::__diesel_operator_body! { + notation = $notation, + struct_name = $name, + operator = $operator, + return_ty = ($($return_ty)*), + ty_params = ($($ty_param,)+), + field_names = $field_names, + backend_ty_params = $backend_ty_params, + backend_ty = $backend_ty, + expression_ty_params = (), + expression_bounds = ($($ty_param: $crate::expression::Expression,)+), + } + }; + + ( + notation = $notation:ident, + struct_name = $name:ident, + operator = $operator:expr, + return_ty = ($($return_ty:tt)+), + ty_params = ($($ty_param:ident,)+), + field_names = ($($field_name:ident,)+), + backend_ty_params = ($($backend_ty_param:ident,)*), + backend_ty = $backend_ty:ty, + expression_ty_params = ($($expression_ty_params:ident,)*), + expression_bounds = ($($expression_bounds:tt)*), + ) => { + #[derive( + Debug, + Clone, + Copy, + $crate::query_builder::QueryId, + $crate::sql_types::DieselNumericOps, + $crate::expression::ValidGrouping + )] + #[doc(hidden)] + pub struct $name<$($ty_param,)+> { + $(pub(crate) $field_name: $ty_param,)+ + } + + impl<$($ty_param,)+> $name<$($ty_param,)+> { + pub(crate) fn new($($field_name: $ty_param,)+) -> Self { + $name { $($field_name,)+ } + } + } + + $crate::impl_selectable_expression!($name<$($ty_param),+>); + + impl<$($ty_param,)+ $($expression_ty_params,)*> $crate::expression::Expression for $name<$($ty_param,)+> where + $($expression_bounds)* + { + type SqlType = $($return_ty)*; + } + + impl<$($ty_param,)+ $($backend_ty_param,)*> $crate::query_builder::QueryFragment<$backend_ty> + for $name<$($ty_param,)+> where + $($ty_param: $crate::query_builder::QueryFragment<$backend_ty>,)+ + $($backend_ty_param: $crate::backend::Backend,)* + { + fn walk_ast<'b>( + &'b self, + mut out: $crate::query_builder::AstPass<'_, 'b, $backend_ty> + ) -> $crate::result::QueryResult<()> + { + $crate::__diesel_operator_to_sql!( + notation = $notation, + operator_expr = out.push_sql($operator), + field_exprs = ($(self.$field_name.walk_ast(out.reborrow())?),+), + ); + Ok(()) + } + } + + impl $crate::internal::operators_macro::FieldAliasMapper for $name<$($ty_param,)+> + where + S: $crate::query_source::AliasSource, + $($ty_param: $crate::internal::operators_macro::FieldAliasMapper,)+ + { + type Out = $name< + $(<$ty_param as $crate::internal::operators_macro::FieldAliasMapper>::Out,)+ + >; + fn map(self, alias: &$crate::query_source::Alias) -> Self::Out { + $name { + $($field_name: self.$field_name.map(alias),)+ + } + } + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_operator_to_sql { + ( + notation = infix, + operator_expr = $op:expr, + field_exprs = ($left:expr, $right:expr), + ) => { + $left; + $op; + $right; + }; + + ( + notation = postfix, + operator_expr = $op:expr, + field_exprs = ($expr:expr), + ) => { + $expr; + $op; + }; + + ( + notation = prefix, + operator_expr = $op:expr, + field_exprs = ($expr:expr), + ) => { + $op; + $expr; + }; +} + +/// Useful for libraries adding support for new SQL types. Apps should never +/// need to call this. +/// +/// This will create a new type with the given name. It will implement all +/// methods needed to be used as an expression in Diesel, placing the given +/// SQL between the two elements. The third argument specifies the SQL type +/// that the operator returns. If it is not given, the type will be assumed +/// to be `Bool`. +/// +/// If the operator is specific to a single backend, you can specify this by +/// adding `backend: Pg` or similar as the last argument. +/// +/// It should be noted that the generated impls will not constrain the SQL +/// types of the arguments. You should ensure that they are of the right +/// type in your function which constructs the operator. +/// +/// Typically you would not expose the type that this generates directly. You'd +/// expose a function (or trait) used to construct the expression, and a helper +/// type which represents the return type of that function. See the source of +/// `diesel::expression::expression_methods` and +/// `diesel::expression::helper_types` for real world examples of this. +/// +/// # Examples +/// +/// # Possible invocations +/// +/// ```ignore +/// // The SQL type will be boolean. The backend will not be constrained +/// infix_operator!(Matches, " @@ "); +/// +/// // Queries which try to execute `Contains` on a backend other than Pg +/// // will fail to compile +/// infix_operator!(Contains, " @> ", backend: Pg); +/// +/// // The type of `Concat` will be `TsVector` rather than Bool +/// infix_operator!(Concat, " || ", TsVector); +/// +/// // It is perfectly fine to have multiple operators with the same SQL. +/// // Diesel will ensure that the queries are always unambiguous in which +/// // operator applies +/// infix_operator!(Or, " || ", TsQuery); +/// +/// // Specifying both the return types and the backend +/// infix_operator!(And, " && ", TsQuery, backend: Pg); +/// ``` +/// +/// ## Example usage +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use diesel::sql_types::SqlType; +/// # use diesel::expression::TypedExpressionType; +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # let connection = &mut establish_connection(); +/// diesel::infix_operator!(MyEq, " = "); +/// +/// use diesel::expression::AsExpression; +/// +/// // Normally you would put this on a trait instead +/// fn my_eq(left: T, right: U) -> MyEq where +/// T: Expression, +/// U: AsExpression, +/// ST: SqlType + TypedExpressionType, +/// { +/// MyEq::new(left, right.as_expression()) +/// } +/// +/// let users_with_name = users.select(id).filter(my_eq(name, "Sean")); +/// +/// assert_eq!(Ok(1), users_with_name.first(connection)); +/// # } +/// ``` +#[macro_export] +macro_rules! infix_operator { + ($name:ident, $operator:expr) => { + $crate::infix_operator!($name, $operator, $crate::sql_types::Bool); + }; + + ($name:ident, $operator:expr, backend: $backend:ty) => { + $crate::infix_operator!($name, $operator, $crate::sql_types::Bool, backend: $backend); + }; + + ($name:ident, $operator:expr, $($return_ty:tt)::*) => { + $crate::__diesel_infix_operator!( + name = $name, + operator = $operator, + return_ty = NullableBasedOnArgs ($($return_ty)::*), + backend_ty_params = (DB,), + backend_ty = DB, + ); + }; + + ($name:ident, $operator:expr, $($return_ty:tt)::*, backend: $backend:ty) => { + $crate::__diesel_infix_operator!( + name = $name, + operator = $operator, + return_ty = NullableBasedOnArgs ($($return_ty)::*), + backend_ty_params = (), + backend_ty = $backend, + ); + }; + +} +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_infix_operator { + ($name:ident, $operator:expr, ConstantNullability $($return_ty:tt)::*) => { + $crate::__diesel_infix_operator!( + name = $name, + operator = $operator, + return_ty = ($($return_ty)::*), + backend_ty_params = (DB,), + backend_ty = DB, + ); + }; + ($name:ident, $operator:expr, __diesel_internal_SameResultAsInput, backend: $backend:ty) => { + $crate::__diesel_infix_operator!( + name = $name, + operator = $operator, + return_ty = (::SqlType), + backend_ty_params = (), + backend_ty = $backend, + ); + }; + ($name:ident, $operator:expr, ConstantNullability $($return_ty:tt)::*, backend: $backend:ty) => { + $crate::__diesel_infix_operator!( + name = $name, + operator = $operator, + return_ty = ($($return_ty)::*), + backend_ty_params = (), + backend_ty = $backend, + ); + }; + + ( + name = $name:ident, + operator = $operator:expr, + return_ty = NullableBasedOnArgs ($($return_ty:tt)+), + backend_ty_params = $backend_ty_params:tt, + backend_ty = $backend_ty:ty, + ) => { + $crate::__diesel_infix_operator!( + name = $name, + operator = $operator, + return_ty = ( + $crate::sql_types::is_nullable::MaybeNullable< + $crate::sql_types::is_nullable::IsOneNullable< + ::SqlType, + ::SqlType + >, + $($return_ty)+ + > + ), + expression_bounds = ( + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + >: $crate::sql_types::OneIsNullable< + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + > + >, + $crate::sql_types::is_nullable::IsOneNullable< + ::SqlType, + ::SqlType + >: $crate::sql_types::MaybeNullableType<$($return_ty)+>, + ), + backend_ty_params = $backend_ty_params, + backend_ty = $backend_ty, + ); + }; + + ( + name = $name:ident, + operator = $operator:expr, + return_ty = ($($return_ty:tt)+), + backend_ty_params = $backend_ty_params:tt, + backend_ty = $backend_ty:ty, + ) => { + $crate::__diesel_infix_operator!( + name = $name, + operator = $operator, + return_ty = ($($return_ty)+), + expression_bounds = (), + backend_ty_params = $backend_ty_params, + backend_ty = $backend_ty, + ); + }; + + ( + name = $name:ident, + operator = $operator:expr, + return_ty = ($($return_ty:tt)+), + expression_bounds = ($($expression_bounds:tt)*), + backend_ty_params = $backend_ty_params:tt, + backend_ty = $backend_ty:ty, + ) => { + $crate::__diesel_operator_body!( + notation = infix, + struct_name = $name, + operator = $operator, + return_ty = ($($return_ty)+), + ty_params = (T, U,), + field_names = (left, right,), + backend_ty_params = $backend_ty_params, + backend_ty = $backend_ty, + expression_ty_params = (), + expression_bounds = ( + T: $crate::expression::Expression, + U: $crate::expression::Expression, + ::SqlType: $crate::sql_types::SqlType, + ::SqlType: $crate::sql_types::SqlType, + $($expression_bounds)* + ), + ); + }; +} + +#[macro_export] +#[deprecated(since = "2.0.0", note = "use `diesel::infix_operator!` instead")] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[doc(hidden)] +macro_rules! diesel_infix_operator { + ($($args:tt)*) => { + $crate::infix_operator!($($args)*); + } +} + +/// Useful for libraries adding support for new SQL types. Apps should never +/// need to call this. +/// +/// Similar to [`infix_operator!`], but the generated type will only take +/// a single argument rather than two. The operator SQL will be placed after +/// the single argument. See [`infix_operator!`] for example usage. +/// +#[macro_export] +macro_rules! postfix_operator { + ($name:ident, $operator:expr) => { + $crate::postfix_operator!($name, $operator, $crate::sql_types::Bool); + }; + + ($name:ident, $operator:expr, backend: $backend:ty) => { + $crate::postfix_operator!($name, $operator, $crate::sql_types::Bool, backend: $backend); + }; + + ($name:ident, $operator:expr, $return_ty:ty) => { + $crate::__diesel_operator_body!( + notation = postfix, + struct_name = $name, + operator = $operator, + return_ty = ($return_ty), + ty_params = (Expr,), + field_names = (expr,), + backend_ty_params = (DB,), + backend_ty = DB, + ); + }; + + ($name:ident, $operator:expr, $return_ty:ty, backend: $backend:ty) => { + $crate::__diesel_operator_body!( + notation = postfix, + struct_name = $name, + operator = $operator, + return_ty = ($return_ty), + ty_params = (Expr,), + field_names = (expr,), + backend_ty_params = (), + backend_ty = $backend, + ); + }; +} + +#[macro_export] +#[deprecated(since = "2.0.0", note = "use `diesel::postfix_operator!` instead")] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[doc(hidden)] +macro_rules! diesel_postfix_operator { + ($($args:tt)*) => { + $crate::postfix_operator!($($args)*); + } +} + +/// Useful for libraries adding support for new SQL types. Apps should never +/// need to call this. +/// +/// Similar to [`infix_operator!`], but the generated type will only take +/// a single argument rather than two. The operator SQL will be placed before +/// the single argument. See [`infix_operator!`] for example usage. +/// +#[macro_export] +macro_rules! prefix_operator { + ($name:ident, $operator:expr) => { + $crate::prefix_operator!($name, $operator, $crate::sql_types::Bool); + }; + + ($name:ident, $operator:expr, backend: $backend:ty) => { + $crate::prefix_operator!($name, $operator, $crate::sql_types::Bool, backend: $backend); + }; + + ($name:ident, $operator:expr, $return_ty:ty) => { + $crate::__diesel_operator_body!( + notation = prefix, + struct_name = $name, + operator = $operator, + return_ty = ( + $crate::sql_types::is_nullable::MaybeNullable< + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + >, + $return_ty, + > + ), + ty_params = (Expr,), + field_names = (expr,), + backend_ty_params = (DB,), + backend_ty = DB, + expression_ty_params = (), + expression_bounds = ( + Expr: $crate::expression::Expression, + ::SqlType: $crate::sql_types::SqlType, + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + >: $crate::sql_types::MaybeNullableType<$return_ty>, + ), + ); + }; + + ($name:ident, $operator:expr, $return_ty:ty, backend: $backend:ty) => { + $crate::__diesel_operator_body!( + notation = prefix, + struct_name = $name, + operator = $operator, + return_ty = ( + $crate::sql_types::is_nullable::MaybeNullable< + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + >, + $return_ty, + > + ), + ty_params = (Expr,), + field_names = (expr,), + backend_ty_params = (), + backend_ty = $backend, + expression_ty_params = (), + expression_bounds = ( + Expr: $crate::expression::Expression, + ::SqlType: $crate::sql_types::SqlType, + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + >: $crate::sql_types::MaybeNullableType<$return_ty>, + ), + ); + }; +} + +#[macro_export] +#[deprecated(since = "2.0.0", note = "use `diesel::prefix_operator!` instead")] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[doc(hidden)] +macro_rules! diesel_prefix_operator { + ($($args:tt)*) => { + $crate::prefix_operator!($($args)*); + } +} + +infix_operator!(And, " AND "); +infix_operator!(Or, " OR "); +infix_operator!(Escape, " ESCAPE "); +infix_operator!(Eq, " = "); +infix_operator!(Gt, " > "); +infix_operator!(GtEq, " >= "); +infix_operator!(Lt, " < "); +infix_operator!(LtEq, " <= "); +infix_operator!(NotEq, " != "); +infix_operator!(NotLike, " NOT LIKE "); +infix_operator!(Between, " BETWEEN "); +infix_operator!(NotBetween, " NOT BETWEEN "); + +postfix_operator!(IsNull, " IS NULL"); +postfix_operator!(IsNotNull, " IS NOT NULL"); +postfix_operator!( + Asc, + " ASC", + crate::expression::expression_types::NotSelectable +); +postfix_operator!( + Desc, + " DESC", + crate::expression::expression_types::NotSelectable +); + +prefix_operator!(Not, " NOT "); + +use crate::backend::{sql_dialect, Backend, SqlDialect}; +use crate::expression::{TypedExpressionType, ValidGrouping}; +use crate::insertable::{ColumnInsertValue, Insertable}; +use crate::query_builder::{QueryFragment, QueryId, ValuesClause}; +use crate::query_source::Column; +use crate::sql_types::{DieselNumericOps, SqlType}; + +impl Insertable for Eq +where + T: Column, +{ + type Values = ValuesClause, T::Table>; + + fn values(self) -> Self::Values { + ValuesClause::new(ColumnInsertValue::new(self.right)) + } +} + +impl<'a, T, Tab, U> Insertable for &'a Eq +where + T: Copy, + Eq: Insertable, +{ + type Values = as Insertable>::Values; + + fn values(self) -> Self::Values { + Eq::new(self.left, &self.right).values() + } +} + +/// This type represents a string concat operator +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + public_fields(left, right) +)] +#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, ValidGrouping)] +pub struct Concat { + /// The left side expression of the operator + pub(crate) left: L, + /// The right side expression of the operator + pub(crate) right: R, +} + +impl Concat { + pub(crate) fn new(left: L, right: R) -> Self { + Self { left, right } + } +} + +impl crate::expression::Expression for Concat +where + L: crate::expression::Expression, + R: crate::expression::Expression, + ST: SqlType + TypedExpressionType, +{ + type SqlType = ST; +} + +impl_selectable_expression!(Concat); + +impl QueryFragment for Concat +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>( + &'b self, + pass: crate::query_builder::AstPass<'_, 'b, DB>, + ) -> crate::result::QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment for Concat +where + L: QueryFragment, + R: QueryFragment, + DB: Backend + SqlDialect, +{ + fn walk_ast<'b>( + &'b self, + mut out: crate::query_builder::AstPass<'_, 'b, DB>, + ) -> crate::result::QueryResult<()> { + // Since popular MySQL scalability layer Vitess does not support pipes in query parsing + // CONCAT has been implemented separately for MySQL (see MysqlConcatClause) + out.push_sql("("); + self.left.walk_ast(out.reborrow())?; + out.push_sql(" || "); + self.right.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +// need an explicit impl here to control which types are allowed +#[derive( + Debug, + Clone, + Copy, + crate::query_builder::QueryId, + crate::sql_types::DieselNumericOps, + crate::expression::ValidGrouping, +)] +#[doc(hidden)] +pub struct Like { + pub(crate) left: T, + pub(crate) right: U, +} + +impl Like { + pub(crate) fn new(left: T, right: U) -> Self { + Like { left, right } + } +} + +impl crate::expression::SelectableExpression for Like +where + Like: crate::expression::AppearsOnTable, + T: crate::expression::SelectableExpression, + U: crate::expression::SelectableExpression, +{ +} + +impl crate::expression::AppearsOnTable for Like +where + Like: crate::expression::Expression, + T: crate::expression::AppearsOnTable, + U: crate::expression::AppearsOnTable, +{ +} + +impl crate::expression::Expression for Like +where + T: crate::expression::Expression, + U: crate::expression::Expression, + ::SqlType: crate::sql_types::SqlType, + ::SqlType: crate::sql_types::SqlType, + crate::sql_types::is_nullable::IsSqlTypeNullable<::SqlType>: + crate::sql_types::OneIsNullable< + crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType, + >, + >, + crate::sql_types::is_nullable::IsOneNullable< + ::SqlType, + ::SqlType, + >: crate::sql_types::MaybeNullableType, +{ + type SqlType = crate::sql_types::is_nullable::MaybeNullable< + crate::sql_types::is_nullable::IsOneNullable< + ::SqlType, + ::SqlType, + >, + crate::sql_types::Bool, + >; +} + +impl crate::query_builder::QueryFragment for Like +where + T: crate::query_builder::QueryFragment + crate::Expression, + U: crate::query_builder::QueryFragment, + DB: crate::backend::Backend, + DB: LikeIsAllowedForType, +{ + fn walk_ast<'b>( + &'b self, + mut out: crate::query_builder::AstPass<'_, 'b, DB>, + ) -> crate::result::QueryResult<()> { + (self.left.walk_ast(out.reborrow())?); + (out.push_sql(" LIKE ")); + (self.right.walk_ast(out.reborrow())?); + Ok(()) + } +} + +impl crate::internal::operators_macro::FieldAliasMapper for Like +where + S: crate::query_source::AliasSource, + T: crate::internal::operators_macro::FieldAliasMapper, + U: crate::internal::operators_macro::FieldAliasMapper, +{ + type Out = Like< + >::Out, + >::Out, + >; + fn map(self, alias: &crate::query_source::Alias) -> Self::Out { + Like { + left: self.left.map(alias), + right: self.right.map(alias), + } + } +} + +#[diagnostic::on_unimplemented( + message = "Cannot use the `LIKE` operator with expressions of the type `{ST}` for the backend `{Self}`", + note = "Expressions of the type `diesel::sql_types::Text` and `diesel::sql_types::Nullable` are \n\ + allowed for all backends" +)] +#[cfg_attr( + feature = "postgres_backend", + diagnostic::on_unimplemented( + note = "Expressions of the type `diesel::sql_types::Binary` and `diesel::sql_types::Nullable` are \n\ + allowed for the PostgreSQL backend" + ) +)] +pub trait LikeIsAllowedForType: Backend {} + +impl LikeIsAllowedForType for DB where DB: Backend {} + +impl LikeIsAllowedForType> for DB where + DB: Backend + LikeIsAllowedForType +{ +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/ops/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/ops/mod.rs new file mode 100644 index 000000000..f223377e5 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/ops/mod.rs @@ -0,0 +1,31 @@ +macro_rules! generic_numeric_expr_inner { + ($tpe: ident, ($($param: ident),*), $op: ident, $fn_name: ident) => { + impl ::std::ops::$op for $tpe<$($param),*> where + $tpe<$($param),*>: $crate::expression::Expression, + <$tpe<$($param),*> as $crate::Expression>::SqlType: $crate::sql_types::SqlType + $crate::sql_types::ops::$op, + <<$tpe<$($param),*> as $crate::Expression>::SqlType as $crate::sql_types::ops::$op>::Rhs: $crate::expression::TypedExpressionType, + Rhs: $crate::expression::AsExpression< + <<$tpe<$($param),*> as $crate::Expression>::SqlType as $crate::sql_types::ops::$op>::Rhs, + >, + { + type Output = $crate::expression::ops::$op; + + fn $fn_name(self, rhs: Rhs) -> Self::Output { + $crate::expression::ops::$op::new(self, rhs.as_expression()) + } + } + } +} + +macro_rules! generic_numeric_expr { + ($tpe: ident, $($param: ident),*) => { + generic_numeric_expr_inner!($tpe, ($($param),*), Add, add); + generic_numeric_expr_inner!($tpe, ($($param),*), Sub, sub); + generic_numeric_expr_inner!($tpe, ($($param),*), Div, div); + generic_numeric_expr_inner!($tpe, ($($param),*), Mul, mul); + } +} + +pub(crate) mod numeric; + +pub(crate) use self::numeric::{Add, Div, Mul, Sub}; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/ops/numeric.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/ops/numeric.rs new file mode 100644 index 000000000..539f02f64 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/ops/numeric.rs @@ -0,0 +1,64 @@ +use crate::backend::Backend; +use crate::expression::{Expression, TypedExpressionType, ValidGrouping}; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types; + +macro_rules! numeric_operation { + ($name:ident, $op:expr) => { + #[doc(hidden)] + #[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] + pub struct $name { + lhs: Lhs, + rhs: Rhs, + } + + impl $name { + // This function is used by `operator_allowed!` + // which is internally used by `table!` + // for "numeric" columns + #[doc(hidden)] + pub fn new(left: Lhs, right: Rhs) -> Self { + $name { + lhs: left, + rhs: right, + } + } + } + + impl Expression for $name + where + Lhs: Expression, + Lhs::SqlType: sql_types::ops::$name, + Rhs: Expression, + ::Output: TypedExpressionType, + { + type SqlType = ::Output; + } + + impl QueryFragment for $name + where + DB: Backend, + Lhs: QueryFragment, + Rhs: QueryFragment, + { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + { + out.push_sql("("); + self.lhs.walk_ast(out.reborrow())?; + out.push_sql($op); + self.rhs.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } + } + + impl_selectable_expression!($name); + generic_numeric_expr!($name, A, B); + }; +} + +numeric_operation!(Add, " + "); +numeric_operation!(Sub, " - "); +numeric_operation!(Mul, " * "); +numeric_operation!(Div, " / "); diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/select_by.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/select_by.rs new file mode 100644 index 000000000..a63052c62 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/select_by.rs @@ -0,0 +1,126 @@ +use crate::backend::{Backend, DieselReserveSpecialization}; +use crate::dsl::SqlTypeOf; +use crate::expression::{ + AppearsOnTable, Expression, QueryMetadata, Selectable, SelectableExpression, + TypedExpressionType, ValidGrouping, +}; +use crate::query_builder::*; +use crate::result::QueryResult; + +#[derive(Debug)] +pub struct SelectBy, DB: Backend> { + selection: T::SelectExpression, + p: std::marker::PhantomData<(T, DB)>, +} + +impl Clone for SelectBy +where + DB: Backend, + T: Selectable, +{ + fn clone(&self) -> Self { + Self { + selection: T::construct_selection(), + p: std::marker::PhantomData, + } + } +} + +impl Copy for SelectBy +where + T: Selectable, + DB: Backend, + T::SelectExpression: Copy, +{ +} + +impl QueryId for SelectBy +where + DB: Backend, + T: Selectable, + E: QueryId + Expression, +{ + type QueryId = E::QueryId; + + const HAS_STATIC_QUERY_ID: bool = E::HAS_STATIC_QUERY_ID; +} + +impl SelectBy +where + T: Selectable, + DB: Backend, +{ + pub(crate) fn new() -> Self { + Self { + selection: T::construct_selection(), + p: std::marker::PhantomData, + } + } +} + +impl Expression for SelectBy +where + DB: Backend, + T: Selectable, + E: QueryId + Expression, +{ + type SqlType = SelectBy; +} + +impl TypedExpressionType for SelectBy +where + T: Selectable, + DB: Backend, +{ +} + +impl ValidGrouping for SelectBy +where + DB: Backend, + T: Selectable, + E: Expression + ValidGrouping, +{ + type IsAggregate = E::IsAggregate; +} + +impl QueryMetadata> for DB +where + DB: Backend, + T: Selectable, + DB: QueryMetadata>, +{ + fn row_metadata(lookup: &mut Self::MetadataLookup, out: &mut Vec>) { + >::SelectExpression>>>::row_metadata( + lookup, out, + ) + } +} + +impl QueryFragment for SelectBy +where + T: Selectable, + T::SelectExpression: QueryFragment, + DB: Backend + DieselReserveSpecialization, +{ + fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.selection.walk_ast(out) + } +} + +impl SelectableExpression for SelectBy +where + DB: Backend, + T: Selectable, + T::SelectExpression: SelectableExpression, + Self: AppearsOnTable, +{ +} + +impl AppearsOnTable for SelectBy +where + DB: Backend, + T: Selectable, + T::SelectExpression: AppearsOnTable, + Self: Expression, +{ +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/sql_literal.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/sql_literal.rs new file mode 100644 index 000000000..75e8dc76c --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/sql_literal.rs @@ -0,0 +1,391 @@ +use std::marker::PhantomData; + +use crate::expression::*; +use crate::query_builder::*; +use crate::query_dsl::RunQueryDsl; +use crate::result::QueryResult; +use crate::sql_types::DieselNumericOps; + +#[derive(Debug, Clone, DieselNumericOps)] +#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."] +/// Returned by the [`sql()`] function. +/// +/// [`sql()`]: crate::dsl::sql() +pub struct SqlLiteral { + sql: String, + inner: T, + _marker: PhantomData, +} + +impl SqlLiteral +where + ST: TypedExpressionType, +{ + pub(crate) fn new(sql: String, inner: T) -> Self { + SqlLiteral { + sql, + inner, + _marker: PhantomData, + } + } + + /// Bind a value for use with this SQL query. + /// + /// # Safety + /// + /// This function should be used with care, as Diesel cannot validate that + /// the value is of the right type nor can it validate that you have passed + /// the correct number of parameters. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::dsl::sql; + /// # use diesel::sql_types::{Integer, Text, Bool}; + /// # let connection = &mut establish_connection(); + /// let seans_id = users + /// .select(id) + /// .filter(sql::("name = ").bind::("Sean")) + /// .get_result(connection); + /// assert_eq!(Ok(1), seans_id); + /// + /// let tess_id = sql::("SELECT id FROM users WHERE name = ") + /// .bind::("Tess") + /// .get_result(connection); + /// assert_eq!(Ok(2), tess_id); + /// # } + /// ``` + /// + /// ### Multiple Bind Params + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::dsl::sql; + /// # use diesel::sql_types::{Integer, Text, Bool}; + /// # let connection = &mut establish_connection(); + /// # diesel::insert_into(users).values(name.eq("Ryan")) + /// # .execute(connection).unwrap(); + /// let query = users + /// .select(name) + /// .filter( + /// sql::("id > ") + /// .bind::(1) + /// .sql(" AND name <> ") + /// .bind::("Ryan") + /// ) + /// .get_results(connection); + /// let expected = vec!["Tess".to_string()]; + /// assert_eq!(Ok(expected), query); + /// # } + /// ``` + pub fn bind(self, bind_value: U) -> UncheckedBind + where + BindST: SqlType + TypedExpressionType, + U: AsExpression, + { + UncheckedBind::new(self, bind_value.as_expression()) + } + + /// Use literal SQL in the query builder + /// + /// This function is intended for use when you need a small bit of raw SQL in + /// your query. If you want to write the entire query using raw SQL, use + /// [`sql_query`](crate::sql_query()) instead. + /// + /// # Safety + /// + /// This function should be used with care, as Diesel cannot validate that + /// the value is of the right type nor can it validate that you have passed + /// the correct number of parameters. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::dsl::sql; + /// # use diesel::sql_types::Bool; + /// # let connection = &mut establish_connection(); + /// # diesel::insert_into(users).values(name.eq("Ryan")) + /// # .execute(connection).unwrap(); + /// let query = users + /// .select(name) + /// .filter( + /// sql::("id > 1") + /// .sql(" AND name <> 'Ryan'") + /// ) + /// .get_results(connection); + /// let expected = vec!["Tess".to_string()]; + /// assert_eq!(Ok(expected), query); + /// # } + /// ``` + pub fn sql(self, sql: &str) -> SqlLiteral { + SqlLiteral::new(sql.into(), self) + } +} + +impl Expression for SqlLiteral +where + ST: TypedExpressionType, +{ + type SqlType = ST; +} + +impl QueryFragment for SqlLiteral +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + self.inner.walk_ast(out.reborrow())?; + out.push_sql(&self.sql); + Ok(()) + } +} + +impl QueryId for SqlLiteral { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl Query for SqlLiteral +where + Self: Expression, +{ + type SqlType = ST; +} + +impl RunQueryDsl for SqlLiteral {} + +impl SelectableExpression for SqlLiteral where Self: Expression {} + +impl AppearsOnTable for SqlLiteral where Self: Expression {} + +impl ValidGrouping for SqlLiteral { + type IsAggregate = is_aggregate::Never; +} + +/// Use literal SQL in the query builder. +/// +/// Available for when you truly cannot represent something using the expression +/// DSL. You will need to provide the SQL type of the expression, in addition to +/// the SQL. +/// +/// This function is intended for use when you need a small bit of raw SQL in +/// your query. If you want to write the entire query using raw SQL, use +/// [`sql_query`](crate::sql_query()) instead. +/// +/// Query parameters can be bound into the literal SQL using [`SqlLiteral::bind()`]. +/// +/// # Safety +/// +/// The compiler will be unable to verify the correctness of the annotated type. +/// If you give the wrong type, it'll either return an error when deserializing +/// the query result or produce unexpected values. +/// +/// # Examples +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # fn main() { +/// # run_test_1().unwrap(); +/// # run_test_2().unwrap(); +/// # } +/// # +/// # fn run_test_1() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # use diesel::sql_types::Bool; +/// use diesel::dsl::sql; +/// # let connection = &mut establish_connection(); +/// let user = users.filter(sql::("name = 'Sean'")).first(connection)?; +/// let expected = (1, String::from("Sean")); +/// assert_eq!(expected, user); +/// # Ok(()) +/// # } +/// # +/// # fn run_test_2() -> QueryResult<()> { +/// # use crate::schema::users::dsl::*; +/// # use diesel::dsl::sql; +/// # use diesel::sql_types::{Bool, Integer, Text}; +/// # let connection = &mut establish_connection(); +/// # diesel::insert_into(users) +/// # .values(name.eq("Ryan")) +/// # .execute(connection).unwrap(); +/// let query = users +/// .select(name) +/// .filter( +/// sql::("id > ") +/// .bind::(1) +/// .sql(" AND name <> ") +/// .bind::("Ryan") +/// ) +/// .get_results(connection); +/// let expected = vec!["Tess".to_string()]; +/// assert_eq!(Ok(expected), query); +/// # Ok(()) +/// # } +/// ``` +/// [`SqlLiteral::bind()`]: crate::expression::SqlLiteral::bind() +pub fn sql(sql: &str) -> SqlLiteral +where + ST: TypedExpressionType, +{ + SqlLiteral::new(sql.into(), self::private::Empty) +} + +#[derive(QueryId, Debug, Clone, Copy)] +#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."] +/// Returned by the [`SqlLiteral::bind()`] method when binding a value to a fragment of SQL. +/// +pub struct UncheckedBind { + query: Query, + value: Value, +} + +impl UncheckedBind +where + Query: Expression, +{ + pub(crate) fn new(query: Query, value: Value) -> Self { + UncheckedBind { query, value } + } + + /// Use literal SQL in the query builder. + /// + /// This function is intended for use when you need a small bit of raw SQL in + /// your query. If you want to write the entire query using raw SQL, use + /// [`sql_query`](crate::sql_query()) instead. + /// + /// # Safety + /// + /// This function should be used with care, as Diesel cannot validate that + /// the value is of the right type nor can it validate that you have passed + /// the correct number of parameters. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::dsl::sql; + /// # use diesel::sql_types::{Integer, Bool}; + /// # let connection = &mut establish_connection(); + /// # diesel::insert_into(users).values(name.eq("Ryan")) + /// # .execute(connection).unwrap(); + /// let query = users + /// .select(name) + /// .filter( + /// sql::("id > ") + /// .bind::(1) + /// .sql(" AND name <> 'Ryan'") + /// ) + /// .get_results(connection); + /// let expected = vec!["Tess".to_string()]; + /// assert_eq!(Ok(expected), query); + /// # } + /// ``` + pub fn sql(self, sql: &str) -> SqlLiteral { + SqlLiteral::new(sql.into(), self) + } +} + +impl Expression for UncheckedBind +where + Query: Expression, +{ + type SqlType = Query::SqlType; +} + +impl QueryFragment for UncheckedBind +where + DB: Backend, + Query: QueryFragment, + Value: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.query.walk_ast(out.reborrow())?; + self.value.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl Query for UncheckedBind +where + Q: Query, +{ + type SqlType = Q::SqlType; +} + +impl ValidGrouping for UncheckedBind { + type IsAggregate = is_aggregate::Never; +} + +impl SelectableExpression for UncheckedBind where + Self: AppearsOnTable +{ +} + +impl AppearsOnTable for UncheckedBind where Self: Expression {} + +impl RunQueryDsl for UncheckedBind {} + +mod private { + use crate::backend::{Backend, DieselReserveSpecialization}; + use crate::query_builder::{QueryFragment, QueryId}; + + #[derive(Debug, Clone, Copy, QueryId)] + pub struct Empty; + + impl QueryFragment for Empty + where + DB: Backend + DieselReserveSpecialization, + { + fn walk_ast<'b>( + &'b self, + _pass: crate::query_builder::AstPass<'_, 'b, DB>, + ) -> crate::QueryResult<()> { + Ok(()) + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/subselect.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/subselect.rs new file mode 100644 index 000000000..9bff4424a --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/subselect.rs @@ -0,0 +1,68 @@ +use std::marker::PhantomData; + +use crate::expression::array_comparison::MaybeEmpty; +use crate::expression::*; +use crate::query_builder::*; +use crate::result::QueryResult; + +#[derive(Debug, Copy, Clone, QueryId)] +pub struct Subselect { + values: T, + _sql_type: PhantomData, +} + +impl Subselect { + pub(crate) fn new(values: T) -> Self { + Self { + values, + _sql_type: PhantomData, + } + } +} + +impl Expression for Subselect +where + ST: SqlType + TypedExpressionType, +{ + type SqlType = ST; +} + +impl MaybeEmpty for Subselect { + fn is_empty(&self) -> bool { + false + } +} + +impl SelectableExpression for Subselect +where + Subselect: AppearsOnTable, + T: ValidSubselect, +{ +} + +impl AppearsOnTable for Subselect +where + Subselect: Expression, + T: ValidSubselect, +{ +} + +// FIXME: This probably isn't sound. The subselect can reference columns from +// the outer query, and is affected by the `GROUP BY` clause of the outer query +// identically to using it outside of a subselect +impl ValidGrouping for Subselect { + type IsAggregate = is_aggregate::Never; +} + +impl QueryFragment for Subselect +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.values.walk_ast(out.reborrow())?; + Ok(()) + } +} + +pub trait ValidSubselect {} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression/unstable.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression/unstable.rs new file mode 100644 index 000000000..bbfc2b16e --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression/unstable.rs @@ -0,0 +1,23 @@ +use crate::expression::{is_aggregate, MixedAggregates, ValidGrouping}; + +// Note that these docs are similar to but slightly different than the stable +// docs below. Make sure if you change these that you also change the docs +// below. +/// Trait alias to represent an expression that isn't aggregate by default. +/// +/// This alias represents a type which is not aggregate if there is no group by +/// clause. More specifically, it represents for types which implement +/// [`ValidGrouping<()>`] where `IsAggregate` is [`is_aggregate::No`] or +/// [`is_aggregate::Yes`]. +/// +/// While this trait is a useful stand-in for common cases, `T: NonAggregate` +/// cannot always be used when `T: ValidGrouping<(), IsAggregate = No>` or +/// `T: ValidGrouping<(), IsAggregate = Never>` could be. For that reason, +/// unless you need to abstract over both columns and literals, you should +/// prefer to use [`ValidGrouping<()>`] in your bounds instead. +/// +/// [`ValidGrouping<()>`]: ValidGrouping +pub trait NonAggregate = ValidGrouping<()> +where + >::IsAggregate: + MixedAggregates; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/bool_expression_methods.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/bool_expression_methods.rs new file mode 100644 index 000000000..dcc5ba03d --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/bool_expression_methods.rs @@ -0,0 +1,161 @@ +use crate::dsl; +use crate::expression::grouped::Grouped; +use crate::expression::operators::{And, Or}; +use crate::expression::{AsExpression, Expression, TypedExpressionType}; +use crate::sql_types::{self, BoolOrNullableBool, SqlType}; + +/// Methods present on boolean expressions +pub trait BoolExpressionMethods: Expression + Sized { + /// Creates a SQL `AND` expression + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// # + /// diesel::insert_into(animals) + /// .values(&vec![ + /// (species.eq("ferret"), legs.eq(4), name.eq("Freddy")), + /// (species.eq("ferret"), legs.eq(4), name.eq("Jack")), + /// ]) + /// .execute(connection)?; + /// + /// let data = animals.select((species, name)) + /// .filter(species.eq("ferret").and(name.eq("Jack"))) + /// .load(connection)?; + /// let expected = vec![ + /// (String::from("ferret"), Some(String::from("Jack"))), + /// ]; + /// assert_eq!(expected, data); + /// # Ok(()) + /// # } + /// ``` + fn and(self, other: T) -> dsl::And + where + Self::SqlType: SqlType, + ST: SqlType + TypedExpressionType + BoolOrNullableBool, + T: AsExpression, + And: Expression, + { + Grouped(And::new(self, other.as_expression())) + } + + /// Creates a SQL `OR` expression + /// + /// The result will be wrapped in parenthesis, so that precedence matches + /// that of your function calls. For example, `false.and(false.or(true))` + /// will generate the SQL `FALSE AND (FALSE OR TRUE)`, which returns `false` + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// # + /// diesel::insert_into(animals) + /// .values(&vec![ + /// (species.eq("ferret"), legs.eq(4), name.eq("Freddy")), + /// (species.eq("ferret"), legs.eq(4), name.eq("Jack")), + /// ]) + /// .execute(connection)?; + /// + /// let data = animals.select((species, name)) + /// .filter(species.eq("ferret").or(name.eq("Jack"))) + /// .load(connection)?; + /// let expected = vec![ + /// (String::from("dog"), Some(String::from("Jack"))), + /// (String::from("ferret"), Some(String::from("Freddy"))), + /// (String::from("ferret"), Some(String::from("Jack"))), + /// ]; + /// assert_eq!(expected, data); + /// # Ok(()) + /// # } + /// ``` + fn or(self, other: T) -> dsl::Or + where + Self::SqlType: SqlType, + ST: SqlType + TypedExpressionType + BoolOrNullableBool, + T: AsExpression, + Or: Expression, + { + Grouped(Or::new(self, other.as_expression())) + } +} + +impl BoolExpressionMethods for T +where + T: Expression, + T::SqlType: BoolOrNullableBool, +{ +} + +/// Allow ~type inference on [And](crate::helper_types::And) and [Or](crate::helper_types::Or) +/// helper types +/// +/// This is used to be statistically correct as last generic parameter of `dsl::And` and `dsl::Or` +/// without having to specify an additional type parameter. +/// +/// It works with types that are [Expression]s and have a [`SqlType`](Expression::SqlType) that is +/// either [`Bool`](sql_types::Bool) or [`Nullable`](sql_types::Nullable), and with [`bool`] +/// (and `Option` and references to those). +/// +/// Cases where an additional type parameter would still have to be specified in the helper type +/// generic parameters are: +/// - If this trait isn't implemented for the `other` parameter of the expression +/// (in that case the user (you?) probably wants to implement it) +/// - If the user actually was using the not-preferred implementation of `AsExpression` +/// (e.g. towards `Nullable` instead of `Bool`) +pub trait PreferredBoolSqlType { + /// The preferred `Bool` SQL type for this AsExpression implementation. + /// + /// That should be either `Bool` or `Nullable`. + type PreferredSqlType; +} + +impl PreferredBoolSqlType for E +where + E::SqlType: BoolOrNullableBool, +{ + type PreferredSqlType = ::SqlType; +} + +/// This impl has to live in Diesel because otherwise it would conflict with the blanket impl above +/// because "diesel might add an implementation of Expression for bool" +impl PreferredBoolSqlType for bool { + type PreferredSqlType = sql_types::Bool; +} + +impl PreferredBoolSqlType for &bool { + type PreferredSqlType = sql_types::Bool; +} + +impl PreferredBoolSqlType for &&bool { + type PreferredSqlType = sql_types::Bool; +} + +impl PreferredBoolSqlType for Option { + type PreferredSqlType = sql_types::Nullable; +} + +impl PreferredBoolSqlType for &Option { + type PreferredSqlType = sql_types::Nullable; +} + +impl PreferredBoolSqlType for &&Option { + type PreferredSqlType = sql_types::Nullable; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/eq_all.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/eq_all.rs new file mode 100644 index 000000000..53d2c50b3 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/eq_all.rs @@ -0,0 +1,72 @@ +use crate::expression::grouped::Grouped; +use crate::expression::operators::And; +use crate::expression::Expression; +use crate::expression_methods::*; +use crate::sql_types::Bool; + +/// This method is used by `FindDsl` to work with tuples. Because we cannot +/// express this without specialization or overlapping impls, it is brute force +/// implemented on columns in the `column!` macro. +#[doc(hidden)] +pub trait EqAll { + type Output: Expression; + + fn eq_all(self, rhs: Rhs) -> Self::Output; +} + +macro_rules! impl_eq_all { + // General case for 2+ elements + ( + ($Left1:ident, $($Left:ident,)+) + ($Right1:ident, $($Right:ident,)+) + ) => { + #[allow(non_snake_case)] + impl<$Left1, $($Left,)+ $Right1, $($Right,)+> + EqAll<($Right1, $($Right,)+)> for ($Left1, $($Left,)+) + where + $Left1: EqAll<$Right1>, + ($($Left,)+): EqAll<($($Right,)+)>, + { + type Output = Grouped>::Output, + <($($Left,)+) as EqAll<($($Right,)+)>>::Output, + >>; + + fn eq_all(self, rhs: ($Right1, $($Right,)+)) -> Self::Output { + let ($Left1, $($Left,)+) = self; + let ($Right1, $($Right,)+) = rhs; + $Left1.eq_all($Right1).and(($($Left,)+).eq_all(($($Right,)+))) + } + } + }; + + // Special case for 1 element + ( + ($Left:ident,) ($Right:ident,) + ) => { + impl<$Left, $Right> EqAll<($Right,)> for ($Left,) + where + $Left: EqAll<$Right>, + { + type Output = <$Left as EqAll<$Right>>::Output; + + fn eq_all(self, rhs: ($Right,)) -> Self::Output { + self.0.eq_all(rhs.0) + } + } + }; +} + +macro_rules! impl_eq_all_for_all_tuples { + ($( + $unused1:tt { + $($unused2:tt -> $Left:ident, $Right:ident, $unused3:tt,)+ + } + )+) => { + $( + impl_eq_all!(($($Left,)+) ($($Right,)+)); + )+ + }; +} + +diesel_derives::__diesel_for_each_tuple!(impl_eq_all_for_all_tuples); diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/escape_expression_methods.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/escape_expression_methods.rs new file mode 100644 index 000000000..6ee90aa45 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/escape_expression_methods.rs @@ -0,0 +1,62 @@ +use crate::dsl; +use crate::expression::grouped::Grouped; +use crate::expression::operators::{Escape, Like, NotLike}; +use crate::expression::IntoSql; +use crate::sql_types::VarChar; + +/// Adds the `escape` method to `LIKE` and `NOT LIKE`. This is used to specify +/// the escape character for the pattern. +/// +/// By default, the escape character is `\` on most backends. On SQLite, +/// there is no default escape character. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # use diesel::insert_into; +/// # let connection = &mut establish_connection(); +/// # insert_into(users).values(name.eq("Ha%%0r")) +/// # .execute(connection).unwrap(); +/// let users_with_percent = users.select(name) +/// .filter(name.like("%😀%%").escape('😀')) +/// .load(connection); +/// let users_without_percent = users.select(name) +/// .filter(name.not_like("%a%%").escape('a')) +/// .load(connection); +/// assert_eq!(Ok(vec![String::from("Ha%%0r")]), users_with_percent); +/// assert_eq!(Ok(vec![String::from("Sean"), String::from("Tess")]), users_without_percent); +/// # } +/// ``` +pub trait EscapeExpressionMethods: Sized { + #[doc(hidden)] + type TextExpression; + + /// See the trait documentation. + fn escape(self, _character: char) -> dsl::Escape; +} + +impl EscapeExpressionMethods for Grouped> { + type TextExpression = Like; + + fn escape(self, character: char) -> dsl::Escape { + Grouped(Escape::new( + self.0, + character.to_string().into_sql::(), + )) + } +} + +impl EscapeExpressionMethods for Grouped> { + type TextExpression = NotLike; + + fn escape(self, character: char) -> dsl::Escape { + Grouped(Escape::new( + self.0, + character.to_string().into_sql::(), + )) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/global_expression_methods.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/global_expression_methods.rs new file mode 100644 index 000000000..893867a0b --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/global_expression_methods.rs @@ -0,0 +1,663 @@ +use crate::dsl; +use crate::expression::array_comparison::{AsInExpression, In, NotIn}; +use crate::expression::grouped::Grouped; +use crate::expression::operators::*; +use crate::expression::{assume_not_null, nullable, AsExpression, Expression}; +use crate::sql_types::{SingleValue, SqlType}; + +/// Methods present on all expressions, except tuples +pub trait ExpressionMethods: Expression + Sized { + /// Creates a SQL `=` expression. + /// + /// Note that this function follows SQL semantics around `None`/`null` values, + /// so `eq(None)` will never match. Use [`is_null`](ExpressionMethods::is_null()) instead. + /// + /// + #[cfg_attr( + any(feature = "sqlite", feature = "postgres"), + doc = "To get behavior that is more like the Rust `=` operator you can also use the" + )] + #[cfg_attr( + feature = "sqlite", + doc = "sqlite-specific [`is`](crate::SqliteExpressionMethods::is())" + )] + #[cfg_attr(all(feature = "sqlite", feature = "postgres"), doc = "or the")] + #[cfg_attr( + feature = "postgres", + doc = "postgres-specific [`is_not_distinct_from`](crate::PgExpressionMethods::is_not_distinct_from())" + )] + #[cfg_attr(any(feature = "sqlite", feature = "postgres"), doc = ".")] + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let data = users.select(id).filter(name.eq("Sean")); + /// assert_eq!(Ok(1), data.first(connection)); + /// # } + /// ``` + /// + /// Matching against `None` follows SQL semantics: + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(name.eq::>(None)) + /// .first::(connection); + /// assert_eq!(Err(diesel::NotFound), data); + /// + /// let data = animals + /// .select(species) + /// .filter(name.is_null()) + /// .first::(connection)?; + /// assert_eq!("spider", data); + /// # Ok(()) + /// # } + /// ``` + #[doc(alias = "=")] + fn eq(self, other: T) -> dsl::Eq + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Eq::new(self, other.as_expression())) + } + + /// Creates a SQL `!=` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let data = users.select(id).filter(name.ne("Sean")); + /// assert_eq!(Ok(2), data.first(connection)); + /// # } + /// ``` + #[doc(alias = "<>")] + fn ne(self, other: T) -> dsl::NotEq + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(NotEq::new(self, other.as_expression())) + } + + /// Creates a SQL `IN` statement. + /// + /// Queries using this method will not typically be + /// placed in the prepared statement cache. However, + /// in cases when a subquery is passed to the method, that + /// query will use the cache (assuming the subquery + /// itself is safe to cache). + /// On PostgreSQL, this method automatically performs a `= ANY()` + /// query. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users; + /// # use schema::posts; + /// # let connection = &mut establish_connection(); + /// # diesel::sql_query("INSERT INTO users (name) VALUES + /// # ('Jim')").execute(connection).unwrap(); + /// let data = users::table.select(users::id).filter(users::name.eq_any(vec!["Sean", "Jim"])); + /// assert_eq!(Ok(vec![1, 3]), data.load(connection)); + /// + /// // Calling `eq_any` with an empty array is the same as doing `WHERE 1=0` + /// let data = users::table.select(users::id).filter(users::name.eq_any(Vec::::new())); + /// assert_eq!(Ok(vec![]), data.load::(connection)); + /// + /// // Calling `eq_any` with a subquery is the same as using + /// // `WHERE {column} IN {subquery}`. + /// + /// let subquery = users::table.filter(users::name.eq("Sean")).select(users::id).into_boxed(); + /// let data = posts::table.select(posts::id).filter(posts::user_id.eq_any(subquery)); + /// assert_eq!(Ok(vec![1, 2]), data.load::(connection)); + /// + /// # } + /// ``` + #[doc(alias = "in")] + fn eq_any(self, values: T) -> dsl::EqAny + where + Self::SqlType: SqlType, + T: AsInExpression, + { + Grouped(In::new(self, values.as_in_expression())) + } + + /// Creates a SQL `NOT IN` statement. + /// + /// Queries using this method will not be + /// placed in the prepared statement cache. On PostgreSQL, this + /// method automatically performs a `!= ALL()` query. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// # diesel::sql_query("INSERT INTO users (name) VALUES + /// # ('Jim')").execute(connection).unwrap(); + /// let data = users.select(id).filter(name.ne_all(vec!["Sean", "Jim"])); + /// assert_eq!(Ok(vec![2]), data.load(connection)); + /// + /// let data = users.select(id).filter(name.ne_all(vec!["Tess"])); + /// assert_eq!(Ok(vec![1, 3]), data.load(connection)); + /// + /// // Calling `ne_any` with an empty array is the same as doing `WHERE 1=1` + /// let data = users.select(id).filter(name.ne_all(Vec::::new())); + /// assert_eq!(Ok(vec![1, 2, 3]), data.load(connection)); + /// # } + /// ``` + #[doc(alias = "in")] + fn ne_all(self, values: T) -> dsl::NeAny + where + Self::SqlType: SqlType, + T: AsInExpression, + { + Grouped(NotIn::new(self, values.as_in_expression())) + } + + /// Creates a SQL `IS NULL` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(name.is_null()) + /// .first::(connection)?; + /// assert_eq!("spider", data); + /// # Ok(()) + /// # } + /// ``` + // This method is part of the public API, + // so we cannot just change the name to appease clippy + // (Otherwise it's also named after the `IS NULL` sql expression + // so that name is really fine) + #[allow(clippy::wrong_self_convention)] + fn is_null(self) -> dsl::IsNull { + Grouped(IsNull::new(self)) + } + + /// Creates a SQL `IS NOT NULL` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(name.is_not_null()) + /// .first::(connection)?; + /// assert_eq!("dog", data); + /// # Ok(()) + /// # } + /// ``` + // This method is part of the public API, + // so we cannot just change the name to appease clippy + // (Otherwise it's also named after the `IS NOT NULL` sql expression + // so that name is really fine) + #[allow(clippy::wrong_self_convention)] + fn is_not_null(self) -> dsl::IsNotNull { + Grouped(IsNotNull::new(self)) + } + + /// Creates a SQL `>` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let data = users + /// .select(name) + /// .filter(id.gt(1)) + /// .first::(connection)?; + /// assert_eq!("Tess", data); + /// # Ok(()) + /// # } + /// ``` + #[doc(alias = ">")] + fn gt(self, other: T) -> dsl::Gt + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Gt::new(self, other.as_expression())) + } + + /// Creates a SQL `>=` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let data = users + /// .select(name) + /// .filter(id.ge(2)) + /// .first::(connection)?; + /// assert_eq!("Tess", data); + /// # Ok(()) + /// # } + /// ``` + #[doc(alias = ">=")] + fn ge(self, other: T) -> dsl::GtEq + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(GtEq::new(self, other.as_expression())) + } + + /// Creates a SQL `<` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let data = users + /// .select(name) + /// .filter(id.lt(2)) + /// .first::(connection)?; + /// assert_eq!("Sean", data); + /// # Ok(()) + /// # } + /// ``` + #[doc(alias = "<")] + fn lt(self, other: T) -> dsl::Lt + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Lt::new(self, other.as_expression())) + } + + /// Creates a SQL `<=` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let data = users + /// .select(name) + /// .filter(id.le(2)) + /// .first::(connection)?; + /// assert_eq!("Sean", data); + /// # Ok(()) + /// # } + /// ``` + #[doc(alias = "<=")] + fn le(self, other: T) -> dsl::LtEq + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(LtEq::new(self, other.as_expression())) + } + + /// Creates a SQL `BETWEEN` expression using the given lower and upper + /// bounds. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(legs.between(2, 6)) + /// .first(connection); + /// # + /// assert_eq!(Ok("dog".to_string()), data); + /// # } + /// ``` + fn between(self, lower: T, upper: U) -> dsl::Between + where + Self::SqlType: SqlType, + T: AsExpression, + U: AsExpression, + { + Grouped(Between::new( + self, + And::new(lower.as_expression(), upper.as_expression()), + )) + } + + /// Creates a SQL `NOT BETWEEN` expression using the given lower and upper + /// bounds. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(legs.not_between(2, 6)) + /// .first::(connection)?; + /// assert_eq!("spider", data); + /// # Ok(()) + /// # } + /// ``` + fn not_between(self, lower: T, upper: U) -> dsl::NotBetween + where + Self::SqlType: SqlType, + T: AsExpression, + U: AsExpression, + { + Grouped(NotBetween::new( + self, + And::new(lower.as_expression(), upper.as_expression()), + )) + } + + /// Creates a SQL `DESC` expression, representing this expression in + /// descending order. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// # + /// let names = users + /// .select(name) + /// .order(name.desc()) + /// .load::(connection)?; + /// assert_eq!(vec!["Tess", "Sean"], names); + /// # Ok(()) + /// # } + /// ``` + fn desc(self) -> dsl::Desc { + Desc::new(self) + } + + /// Creates a SQL `ASC` expression, representing this expression in + /// ascending order. + /// + /// This is the same as leaving the direction unspecified. It is useful if + /// you need to provide an unknown ordering, and need to box the return + /// value of a function. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use diesel::expression::expression_types::NotSelectable; + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let order = "name"; + /// let ordering: Box> = + /// if order == "name" { + /// Box::new(name.desc()) + /// } else { + /// Box::new(id.asc()) + /// }; + /// # } + /// ``` + fn asc(self) -> dsl::Asc { + Asc::new(self) + } +} + +impl ExpressionMethods for T +where + T: Expression, + T::SqlType: SingleValue, +{ +} + +/// Methods present on all expressions +pub trait NullableExpressionMethods: Expression + Sized { + /// Converts this potentially non-null expression into one which is treated + /// as nullable. This method has no impact on the generated SQL, and is only + /// used to allow certain comparisons that would otherwise fail to compile. + /// + /// # Example + /// ```no_run + /// # #![allow(dead_code)] + /// # include!("../doctest_setup.rs"); + /// # use diesel::sql_types::*; + /// # use schema::users; + /// # + /// table! { + /// posts { + /// id -> Integer, + /// user_id -> Integer, + /// author_name -> Nullable, + /// } + /// } + /// # + /// # joinable!(posts -> users (user_id)); + /// # allow_tables_to_appear_in_same_query!(posts, users); + /// + /// fn main() { + /// use self::users::dsl::*; + /// use self::posts::dsl::{posts, author_name}; + /// let connection = &mut establish_connection(); + /// + /// let data = users.inner_join(posts) + /// .filter(name.nullable().eq(author_name)) + /// .select(name) + /// .load::(connection); + /// println!("{:?}", data); + /// } + /// ``` + fn nullable(self) -> dsl::Nullable { + nullable::Nullable::new(self) + } + + /// Converts this potentially nullable expression into one which will be **assumed** + /// to be not-null. This method has no impact on the generated SQL, however it will + /// enable you to attempt deserialization of the returned value in a non-`Option`. + /// + /// This is meant to cover for cases where you know that given the `WHERE` clause + /// the field returned by the database will never be `NULL`. + /// + /// This **will cause runtime errors** on `load()` if the "assume" turns out to be incorrect. + /// + /// # Examples + /// ## Normal usage + /// ```rust + /// # #![allow(dead_code)] + /// # include!("../doctest_setup.rs"); + /// # use diesel::sql_types::*; + /// # + /// table! { + /// animals { + /// id -> Integer, + /// species -> VarChar, + /// legs -> Integer, + /// name -> Nullable, + /// } + /// } + /// + /// fn main() { + /// use self::animals::dsl::*; + /// let connection = &mut establish_connection(); + /// + /// let result = animals + /// .filter(name.is_not_null()) + /// .select(name.assume_not_null()) + /// .load::(connection); + /// assert!(result.is_ok()); + /// } + /// ``` + /// + /// ## Incorrect usage + /// ```rust + /// # #![allow(dead_code)] + /// # include!("../doctest_setup.rs"); + /// # use diesel::sql_types::*; + /// # + /// table! { + /// animals { + /// id -> Integer, + /// species -> VarChar, + /// legs -> Integer, + /// name -> Nullable, + /// } + /// } + /// + /// fn main() { + /// use diesel::result::{Error, UnexpectedNullError}; + /// use self::animals::dsl::*; + /// let connection = &mut establish_connection(); + /// + /// let result = animals + /// .select(name.assume_not_null()) + /// .load::(connection); + /// assert!(matches!( + /// result, + /// Err(Error::DeserializationError(err)) if err.is::() + /// )); + /// } + /// ``` + /// + /// ## Advanced usage - use only if you're sure you know what you're doing! + /// + /// This will cause the `Option` to be `None` where the `left_join` succeeded but the + /// `author_name` turned out to be `NULL`, due to how `Option` deserialization works. + /// (see [`Queryable` documentation](crate::deserialize::Queryable)) + /// + /// ```rust + /// # #![allow(dead_code)] + /// # include!("../doctest_setup.rs"); + /// # use diesel::sql_types::*; + /// # use schema::users; + /// # + /// table! { + /// posts { + /// id -> Integer, + /// user_id -> Integer, + /// author_name -> Nullable, + /// } + /// } + /// # + /// # joinable!(posts -> users (user_id)); + /// # allow_tables_to_appear_in_same_query!(posts, users); + /// + /// fn main() { + /// use self::posts; + /// use self::users; + /// let connection = &mut establish_connection(); + /// + /// # diesel::sql_query("ALTER TABLE posts ADD COLUMN author_name Text") + /// # .execute(connection) + /// # .unwrap(); + /// # diesel::update(posts::table.filter(posts::user_id.eq(1))) + /// # .set(posts::author_name.eq("Sean")) + /// # .execute(connection); + /// + /// let result = posts::table.left_join(users::table) + /// .select((posts::id, (users::id, posts::author_name.assume_not_null()).nullable())) + /// .order_by(posts::id) + /// .load::<(i32, Option<(i32, String)>)>(connection); + /// let expected = Ok(vec![ + /// (1, Some((1, "Sean".to_owned()))), + /// (2, Some((1, "Sean".to_owned()))), + /// (3, None), + /// ]); + /// assert_eq!(expected, result); + /// } + /// ``` + fn assume_not_null(self) -> dsl::AssumeNotNull { + assume_not_null::AssumeNotNull::new(self) + } +} + +impl NullableExpressionMethods for T {} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/mod.rs new file mode 100644 index 000000000..5c4c5440b --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/mod.rs @@ -0,0 +1,30 @@ +//! Adds various methods to construct new expressions. These traits are exported +//! by default, and implemented automatically. +//! +//! You can rely on the methods provided by this trait existing on any +//! `Expression` of the appropriate type. You should not rely on the specific +//! traits existing, their names, or their organization. +mod bool_expression_methods; +mod eq_all; +mod escape_expression_methods; +mod global_expression_methods; +mod text_expression_methods; + +#[doc(inline)] +pub use self::bool_expression_methods::{BoolExpressionMethods, PreferredBoolSqlType}; +#[doc(hidden)] +pub use self::eq_all::EqAll; +#[doc(inline)] +pub use self::escape_expression_methods::EscapeExpressionMethods; +#[doc(inline)] +pub use self::global_expression_methods::{ExpressionMethods, NullableExpressionMethods}; +#[doc(inline)] +pub use self::text_expression_methods::TextExpressionMethods; + +#[cfg(feature = "postgres_backend")] +#[doc(inline)] +pub use crate::pg::expression::expression_methods::*; + +#[cfg(feature = "sqlite")] +#[doc(inline)] +pub use crate::sqlite::expression::expression_methods::*; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/text_expression_methods.rs b/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/text_expression_methods.rs new file mode 100644 index 000000000..9c2660aed --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/expression_methods/text_expression_methods.rs @@ -0,0 +1,158 @@ +use self::private::TextOrNullableText; +use crate::dsl; +use crate::expression::grouped::Grouped; +use crate::expression::operators::{Concat, Like, NotLike}; +use crate::expression::{AsExpression, Expression}; +use crate::sql_types::SqlType; + +/// Methods present on text expressions +pub trait TextExpressionMethods: Expression + Sized { + /// Concatenates two strings using the `||` operator. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # hair_color -> Nullable, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::insert_into; + /// # + /// # let connection = &mut connection_no_data(); + /// # diesel::sql_query("CREATE TABLE users ( + /// # id INTEGER PRIMARY KEY, + /// # name VARCHAR(255) NOT NULL, + /// # hair_color VARCHAR(255) + /// # )").execute(connection).unwrap(); + /// # + /// # insert_into(users) + /// # .values(&vec![ + /// # (id.eq(1), name.eq("Sean"), hair_color.eq(Some("Green"))), + /// # (id.eq(2), name.eq("Tess"), hair_color.eq(None)), + /// # ]) + /// # .execute(connection) + /// # .unwrap(); + /// # + /// let names = users.select(name.concat(" the Greatest")).load(connection); + /// let expected_names = vec![ + /// "Sean the Greatest".to_string(), + /// "Tess the Greatest".to_string(), + /// ]; + /// assert_eq!(Ok(expected_names), names); + /// + /// // If the value is nullable, the output will be nullable + /// let names = users.select(hair_color.concat("ish")).load(connection); + /// let expected_names = vec![ + /// Some("Greenish".to_string()), + /// None, + /// ]; + /// assert_eq!(Ok(expected_names), names); + /// # } + /// ``` + fn concat(self, other: T) -> dsl::Concat + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Concat::new(self, other.as_expression())) + } + + /// Returns a SQL `LIKE` expression + /// + /// This method is case insensitive for SQLite and MySQL. + /// On PostgreSQL, `LIKE` is case sensitive. You may use + /// [`ilike()`](../expression_methods/trait.PgTextExpressionMethods.html#method.ilike) + /// for case insensitive comparison on PostgreSQL. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// # + /// let starts_with_s = users + /// .select(name) + /// .filter(name.like("S%")) + /// .load::(connection)?; + /// assert_eq!(vec!["Sean"], starts_with_s); + /// # Ok(()) + /// # } + /// ``` + fn like(self, other: T) -> dsl::Like + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Like::new(self, other.as_expression())) + } + + /// Returns a SQL `NOT LIKE` expression + /// + /// This method is case insensitive for SQLite and MySQL. + /// On PostgreSQL `NOT LIKE` is case sensitive. You may use + /// [`not_ilike()`](../expression_methods/trait.PgTextExpressionMethods.html#method.not_ilike) + /// for case insensitive comparison on PostgreSQL. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// # + /// let doesnt_start_with_s = users + /// .select(name) + /// .filter(name.not_like("S%")) + /// .load::(connection)?; + /// assert_eq!(vec!["Tess"], doesnt_start_with_s); + /// # Ok(()) + /// # } + /// ``` + fn not_like(self, other: T) -> dsl::NotLike + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(NotLike::new(self, other.as_expression())) + } +} + +impl TextExpressionMethods for T +where + T: Expression, + T::SqlType: TextOrNullableText, +{ +} + +mod private { + use crate::sql_types::{Nullable, Text}; + + /// Marker trait used to implement `TextExpressionMethods` on the appropriate + /// types. Once coherence takes associated types into account, we can remove + /// this trait. + pub trait TextOrNullableText {} + + impl TextOrNullableText for Text {} + impl TextOrNullableText for Nullable {} +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/insertable.rs b/collector/compile-benchmarks/diesel-2.2.10/src/insertable.rs new file mode 100644 index 000000000..77fc28486 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/insertable.rs @@ -0,0 +1,406 @@ +use std::marker::PhantomData; + +use crate::backend::{sql_dialect, Backend, DieselReserveSpecialization, SqlDialect}; +use crate::expression::grouped::Grouped; +use crate::expression::{AppearsOnTable, Expression}; +use crate::query_builder::{ + AstPass, BatchInsert, InsertStatement, NoFromClause, QueryFragment, QueryId, + UndecoratedInsertRecord, ValuesClause, +}; +use crate::query_source::{Column, Table}; +use crate::result::QueryResult; + +/// Represents that a structure can be used to insert a new row into the +/// database. This is automatically implemented for `&[T]` and `&Vec` for +/// inserting more than one record. +/// +/// This trait can be [derived](derive@Insertable) +pub trait Insertable { + /// The `VALUES` clause to insert these records + /// + /// The types used here are generally internal to Diesel. + /// Implementations of this trait should use the `Values` + /// type of other `Insertable` types. + /// For example ` as Insertable>::Values`. + type Values; + + /// Construct `Self::Values` + /// + /// Implementations of this trait typically call `.values` + /// on other `Insertable` types. + fn values(self) -> Self::Values; + + /// Insert `self` into a given table. + /// + /// `foo.insert_into(table)` is identical to `insert_into(table).values(foo)`. + /// However, when inserting from a select statement, + /// this form is generally preferred. + /// + /// # Example + /// + /// ```rust + /// # include!("doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::{posts, users}; + /// # let conn = &mut establish_connection(); + /// # diesel::delete(posts::table).execute(conn)?; + /// users::table + /// .select(( + /// users::name.concat("'s First Post"), + /// users::id, + /// )) + /// .insert_into(posts::table) + /// .into_columns((posts::title, posts::user_id)) + /// .execute(conn)?; + /// + /// let inserted_posts = posts::table + /// .select(posts::title) + /// .load::(conn)?; + /// let expected = vec!["Sean's First Post", "Tess's First Post"]; + /// assert_eq!(expected, inserted_posts); + /// # Ok(()) + /// # } + /// ``` + fn insert_into(self, table: T) -> InsertStatement + where + T: Table, + Self: Sized, + { + crate::insert_into(table).values(self) + } +} + +#[doc(inline)] +pub use diesel_derives::Insertable; + +pub trait CanInsertInSingleQuery { + /// How many rows will this query insert? + /// + /// This function should only return `None` when the query is valid on all + /// backends, regardless of how many rows get inserted. + fn rows_to_insert(&self) -> Option; +} + +impl CanInsertInSingleQuery for &T +where + T: ?Sized + CanInsertInSingleQuery, + DB: Backend, +{ + fn rows_to_insert(&self) -> Option { + (*self).rows_to_insert() + } +} + +impl CanInsertInSingleQuery for ColumnInsertValue +where + DB: Backend, +{ + fn rows_to_insert(&self) -> Option { + Some(1) + } +} + +impl CanInsertInSingleQuery for DefaultableColumnInsertValue +where + DB: Backend, + V: CanInsertInSingleQuery, +{ + fn rows_to_insert(&self) -> Option { + Some(1) + } +} + +pub trait InsertValues: QueryFragment { + fn column_names(&self, out: AstPass<'_, '_, DB>) -> QueryResult<()>; +} + +#[derive(Debug, Copy, Clone, QueryId)] +#[doc(hidden)] +pub struct ColumnInsertValue { + pub(crate) expr: Expr, + p: PhantomData, +} + +impl ColumnInsertValue { + pub(crate) fn new(expr: Expr) -> Self { + Self { + expr, + p: PhantomData, + } + } +} + +#[derive(Debug, Copy, Clone)] +#[doc(hidden)] +pub enum DefaultableColumnInsertValue { + Expression(T), + Default, +} + +impl QueryId for DefaultableColumnInsertValue { + type QueryId = (); + const HAS_STATIC_QUERY_ID: bool = false; +} + +#[allow(clippy::derivable_impls)] // that's not supported on rust 1.65 +impl Default for DefaultableColumnInsertValue { + fn default() -> Self { + DefaultableColumnInsertValue::Default + } +} + +impl InsertValues + for DefaultableColumnInsertValue> +where + DB: Backend + SqlDialect, + Col: Column, + Expr: Expression + AppearsOnTable, + Self: QueryFragment, +{ + fn column_names(&self, mut out: AstPass<'_, '_, DB>) -> QueryResult<()> { + out.push_identifier(Col::NAME)?; + Ok(()) + } +} + +impl InsertValues for ColumnInsertValue +where + DB: Backend, + Col: Column, + Expr: Expression + AppearsOnTable, + Self: QueryFragment, +{ + fn column_names(&self, mut out: AstPass<'_, '_, DB>) -> QueryResult<()> { + out.push_identifier(Col::NAME)?; + Ok(()) + } +} + +impl QueryFragment for DefaultableColumnInsertValue +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment for DefaultableColumnInsertValue +where + DB: Backend + SqlDialect, + Expr: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + if let Self::Expression(ref inner) = *self { + inner.walk_ast(out.reborrow())?; + } else { + out.push_sql("DEFAULT"); + } + Ok(()) + } +} + +impl QueryFragment for ColumnInsertValue +where + DB: Backend + DieselReserveSpecialization, + Expr: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.expr.walk_ast(pass) + } +} + +#[cfg(feature = "sqlite")] +impl InsertValues + for DefaultableColumnInsertValue> +where + Col: Column, + Expr: Expression + AppearsOnTable, + Self: QueryFragment, +{ + fn column_names(&self, mut out: AstPass<'_, '_, crate::sqlite::Sqlite>) -> QueryResult<()> { + if let Self::Expression(..) = *self { + out.push_identifier(Col::NAME)?; + } + Ok(()) + } +} + +#[cfg(feature = "sqlite")] +impl + QueryFragment< + crate::sqlite::Sqlite, + crate::backend::sql_dialect::default_keyword_for_insert::DoesNotSupportDefaultKeyword, + > for DefaultableColumnInsertValue> +where + Expr: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, crate::sqlite::Sqlite>) -> QueryResult<()> { + if let Self::Expression(ref inner) = *self { + inner.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +impl<'a, T, Tab> Insertable for &'a [T] +where + &'a T: UndecoratedInsertRecord + Insertable, +{ + type Values = BatchInsert>::Values>, Tab, (), false>; + + fn values(self) -> Self::Values { + let values = self.iter().map(Insertable::values).collect::>(); + BatchInsert::new(values) + } +} + +impl<'a, T, Tab> Insertable for &'a Vec +where + &'a [T]: Insertable, +{ + type Values = <&'a [T] as Insertable>::Values; + + fn values(self) -> Self::Values { + (&**self).values() + } +} + +impl Insertable for Vec +where + T: Insertable + UndecoratedInsertRecord, +{ + type Values = BatchInsert, Tab, (), false>; + + fn values(self) -> Self::Values { + let values = self.into_iter().map(Insertable::values).collect::>(); + BatchInsert::new(values) + } +} + +impl Insertable for [T; N] +where + T: Insertable, +{ + type Values = BatchInsert, Tab, [T::Values; N], true>; + + // We must use the deprecated `IntoIter` function + // here as 1.51 (MSRV) does not support the new not + // deprecated variant + #[allow(deprecated)] + fn values(self) -> Self::Values { + let values = std::array::IntoIter::new(self) + .map(Insertable::values) + .collect::>(); + BatchInsert::new(values) + } +} + +impl<'a, T, Tab, const N: usize> Insertable for &'a [T; N] +where + T: Insertable, + &'a T: Insertable, +{ + // We can reuse the query id for [T; N] here as this + // compiles down to the same query + type Values = BatchInsert>::Values>, Tab, [T::Values; N], true>; + + fn values(self) -> Self::Values { + let values = self.iter().map(Insertable::values).collect(); + BatchInsert::new(values) + } +} + +impl Insertable for Box<[T; N]> +where + T: Insertable, +{ + // We can reuse the query id for [T; N] here as this + // compiles down to the same query + type Values = BatchInsert, Tab, [T::Values; N], true>; + + fn values(self) -> Self::Values { + let v = Vec::from(self as Box<[T]>); + let values = v.into_iter().map(Insertable::values).collect::>(); + BatchInsert::new(values) + } +} + +mod private { + // This helper exists to differentiate between + // Insertable implementations for tuples and for single values + #[allow(missing_debug_implementations)] + pub struct InsertableOptionHelper( + pub(crate) Option, + pub(crate) std::marker::PhantomData, + ); +} + +pub(crate) use self::private::InsertableOptionHelper; + +impl Insertable for Option +where + T: Insertable>, + InsertableOptionHelper: Insertable, +{ + type Values = as Insertable>::Values; + + fn values(self) -> Self::Values { + InsertableOptionHelper(self, PhantomData).values() + } +} + +impl Insertable for InsertableOptionHelper> +where + T: Insertable, Tab>>, +{ + type Values = ValuesClause>, Tab>; + + fn values(self) -> Self::Values { + ValuesClause::new( + self.0 + .map(|v| DefaultableColumnInsertValue::Expression(Insertable::values(v).values)) + .unwrap_or_default(), + ) + } +} + +impl<'a, T, Tab> Insertable for &'a Option +where + Option<&'a T>: Insertable, +{ + type Values = as Insertable>::Values; + + fn values(self) -> Self::Values { + self.as_ref().values() + } +} + +impl Insertable for Grouped> +where + crate::expression::operators::Eq: Insertable, +{ + type Values = as Insertable>::Values; + + fn values(self) -> Self::Values { + self.0.values() + } +} + +impl<'a, L, R, Tab> Insertable for &'a Grouped> +where + &'a crate::expression::operators::Eq: Insertable, +{ + type Values = <&'a crate::expression::operators::Eq as Insertable>::Values; + + fn values(self) -> Self::Values { + self.0.values() + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/internal/alias_macro.rs b/collector/compile-benchmarks/diesel-2.2.10/src/internal/alias_macro.rs new file mode 100644 index 000000000..8dabdec77 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/internal/alias_macro.rs @@ -0,0 +1,2 @@ +#[doc(hidden)] +pub use crate::query_source::aliasing::AliasAliasAppearsInFromClauseSameTable; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/internal/derives.rs b/collector/compile-benchmarks/diesel-2.2.10/src/internal/derives.rs new file mode 100644 index 000000000..216936cf6 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/internal/derives.rs @@ -0,0 +1,80 @@ +#[doc(hidden)] +pub mod insertable { + #[doc(hidden)] + pub use crate::query_builder::insert_statement::UndecoratedInsertRecord; +} + +#[doc(hidden)] +pub mod as_expression { + #[doc(hidden)] + pub use crate::expression::bound::Bound; +} + +#[doc(hidden)] +pub mod numeric_ops { + #[doc(hidden)] + pub use crate::expression::ops::numeric::*; +} + +#[doc(hidden)] +pub mod multiconnection { + #[doc(hidden)] + pub use crate::connection::private::{ConnectionSealed, MultiConnectionHelper}; + #[doc(hidden)] + pub use crate::expression::operators::Concat; + #[doc(hidden)] + pub use crate::query_builder::ast_pass::AstPassHelper; + #[doc(hidden)] + pub use crate::query_builder::insert_statement::DefaultValues; + #[doc(hidden)] + pub use crate::query_builder::limit_offset_clause::{ + BoxedLimitOffsetClause, LimitOffsetClause, + }; + #[doc(hidden)] + pub use crate::query_builder::returning_clause::ReturningClause; + #[doc(hidden)] + pub use crate::query_builder::select_statement::boxed::BoxedSelectStatement; + #[doc(hidden)] + pub use crate::query_builder::select_statement::SelectStatement; + #[doc(hidden)] + pub use crate::row::private::RowSealed; + #[doc(hidden)] + pub mod sql_dialect { + #[doc(hidden)] + pub use crate::backend::sql_dialect::*; + } + #[doc(hidden)] + pub use crate::backend::private::{DieselReserveSpecialization, TrustedBackend}; + #[doc(hidden)] + pub mod array_comparison { + #[doc(hidden)] + pub use crate::expression::array_comparison::*; + } + #[doc(hidden)] + pub use crate::expression::exists::Exists; + #[doc(hidden)] + pub use crate::query_builder::from_clause::NoFromClause; + #[doc(hidden)] + pub use crate::query_builder::insert_statement::batch_insert::BatchInsert; + #[doc(hidden)] + pub use crate::row::private::PartialRow; + + #[doc(hidden)] + pub use crate::query_builder::limit_clause::{LimitClause, NoLimitClause}; + #[doc(hidden)] + pub use crate::query_builder::offset_clause::{NoOffsetClause, OffsetClause}; + + #[doc(hidden)] + pub use crate::query_builder::select_statement::boxed::BoxedQueryHelper; + + #[doc(hidden)] + pub use crate::query_builder::select_statement::SelectStatementAccessor; + + #[doc(hidden)] + #[cfg(feature = "chrono")] + pub use chrono; + + #[doc(hidden)] + #[cfg(feature = "time")] + pub use time; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/internal/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/internal/mod.rs new file mode 100644 index 000000000..5ba2e3890 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/internal/mod.rs @@ -0,0 +1,9 @@ +//! This module contains API definitions which are not considered +//! to be part diesels public API +//! +//! **DO NOT EXPECT ANY STABILITY GUARANTEES HERE** + +pub mod alias_macro; +pub mod derives; +pub mod operators_macro; +pub mod table_macro; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/internal/operators_macro.rs b/collector/compile-benchmarks/diesel-2.2.10/src/internal/operators_macro.rs new file mode 100644 index 000000000..81e02428e --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/internal/operators_macro.rs @@ -0,0 +1,2 @@ +#[doc(hidden)] +pub use crate::query_source::aliasing::FieldAliasMapper; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/internal/table_macro.rs b/collector/compile-benchmarks/diesel-2.2.10/src/internal/table_macro.rs new file mode 100644 index 000000000..52c2105a3 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/internal/table_macro.rs @@ -0,0 +1,30 @@ +#[doc(hidden)] +pub use crate::expression::nullable::Nullable as NullableExpression; +#[doc(hidden)] +#[cfg(feature = "postgres_backend")] +pub use crate::pg::query_builder::tablesample::TablesampleMethod; +#[doc(hidden)] +pub use crate::query_builder::from_clause::{FromClause, NoFromClause}; +#[doc(hidden)] +pub use crate::query_builder::nodes::{ + Identifier, InfixNode, StaticQueryFragment, StaticQueryFragmentInstance, +}; +#[doc(hidden)] +pub use crate::query_builder::select_statement::boxed::BoxedSelectStatement; +#[doc(hidden)] +pub use crate::query_builder::select_statement::SelectStatement; +#[doc(hidden)] +pub use crate::query_source::aliasing::{ + AliasAliasAppearsInFromClause, AliasAliasAppearsInFromClauseSameTable, + AliasAppearsInFromClause, FieldAliasMapperAssociatedTypesDisjointnessTrick, +}; +#[doc(hidden)] +pub use crate::query_source::joins::{Inner, Join, JoinOn, LeftOuter}; +#[doc(hidden)] +pub use crate::query_source::private::Pick; + +#[doc(hidden)] +pub mod ops { + #[doc(hidden)] + pub use crate::expression::ops::numeric::*; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/lib.rs b/collector/compile-benchmarks/diesel-2.2.10/src/lib.rs new file mode 100644 index 000000000..881fa5d5c --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/lib.rs @@ -0,0 +1,775 @@ +//! # Diesel +//! +//! Diesel is an ORM and query builder designed to reduce the boilerplate for database interactions. +//! If this is your first time reading this documentation, +//! we recommend you start with the [getting started guide]. +//! We also have [many other long form guides]. +//! +//! [getting started guide]: https://diesel.rs/guides/getting-started/ +//! [many other long form guides]: https://diesel.rs/guides +//! +//! # Where to find things +//! +//! ## Declaring your schema +//! +//! For Diesel to validate your queries at compile time +//! it requires you to specify your schema in your code, +//! which you can do with [the `table!` macro][`table!`]. +//! `diesel print-schema` can be used +//! to automatically generate these macro calls +//! (by connecting to your database and querying its schema). +//! +//! +//! ## Getting started +//! +//! Queries usually start from either a table, or a function like [`update`]. +//! Those functions can be found [here](#functions). +//! +//! Diesel provides a [`prelude` module](prelude), +//! which exports most of the typically used traits and types. +//! We are conservative about what goes in this module, +//! and avoid anything which has a generic name. +//! Files which use Diesel are expected to have `use diesel::prelude::*;`. +//! +//! [`update`]: update() +//! +//! ## Constructing a query +//! +//! The tools the query builder gives you can be put into these three categories: +//! +//! - "Query builder methods" are things that map to portions of a whole query +//! (such as `ORDER` and `WHERE`). These methods usually have the same name +//! as the SQL they map to, except for `WHERE` which is called `filter` in Diesel +//! (To not conflict with the Rust keyword). +//! These methods live in [the `query_dsl` module](query_dsl). +//! - "Expression methods" are things you would call on columns +//! or other individual values. +//! These methods live in [the `expression_methods` module](expression_methods) +//! You can often find these by thinking "what would this be called" +//! if it were a method +//! and typing that into the search bar +//! (e.g. `LIKE` is called `like` in Diesel). +//! Most operators are named based on the Rust function which maps to that +//! operator in [`std::ops`][] +//! (For example `==` is called `.eq`, and `!=` is called `.ne`). +//! - "Bare functions" are normal SQL functions +//! such as `sum`. +//! They live in [the `dsl` module](dsl). +//! Diesel only supports a very small number of these functions. +//! You can declare additional functions you want to use +//! with [the `define_sql_function!` macro][`define_sql_function!`]. +//! +//! [`std::ops`]: //doc.rust-lang.org/stable/std/ops/index.html +//! +//! ## Serializing and Deserializing +//! +//! Types which represent the result of a SQL query implement +//! a trait called [`Queryable`]. +//! +//! Diesel maps "Rust types" (e.g. `i32`) to and from "SQL types" +//! (e.g. [`diesel::sql_types::Integer`]). +//! You can find all the types supported by Diesel in [the `sql_types` module](sql_types). +//! These types are only used to represent a SQL type. +//! You should never put them on your `Queryable` structs. +//! +//! To find all the Rust types which can be used with a given SQL type, +//! see the documentation for that SQL type. +//! +//! To find all the SQL types which can be used with a Rust type, +//! go to the docs for either [`ToSql`] or [`FromSql`], +//! go to the "Implementors" section, +//! and find the Rust type you want to use. +//! +//! [`Queryable`]: deserialize::Queryable +//! [`diesel::sql_types::Integer`]: sql_types::Integer +//! [`ToSql`]: serialize::ToSql +//! [`FromSql`]: deserialize::FromSql +//! +//! ## How to read diesels compile time error messages +//! +//! Diesel is known for generating large complicated looking errors. Usually +//! most of these error messages can be broken down easily. The following +//! section tries to give an overview of common error messages and how to read them. +//! As a general note it's always useful to read the complete error message as emitted +//! by rustc, including the `required because of …` part of the message. +//! Your IDE might hide important parts! +//! +//! The following error messages are common: +//! +//! * `the trait bound (diesel::sql_types::Integer, …, diesel::sql_types::Text): load_dsl::private::CompatibleType is not satisfied` +//! while trying to execute a query: +//! This error indicates a mismatch between what your query returns and what your model struct +//! expects the query to return. The fields need to match in terms of field order, field type +//! and field count. If you are sure that everything matches, double check the enabled diesel +//! features (for support for types from other crates) and double check (via `cargo tree`) +//! that there is only one version of such a shared crate in your dependency tree. +//! Consider using [`#[derive(Selectable)]`](derive@crate::prelude::Selectable) + +//! `#[diesel(check_for_backend(diesel::pg::Pg))]` +//! to improve the generated error message. +//! * `the trait bound i32: diesel::Expression is not satisfied` in the context of `Insertable` +//! model structs: +//! This error indicates a type mismatch between the field you are trying to insert into the database +//! and the actual database type. These error messages contain a line +//! like ` = note: required for i32 to implement AsExpression` +//! that show both the provided rust side type (`i32` in that case) and the expected +//! database side type (`Text` in that case). +//! * `the trait bound i32: AppearsOnTable is not satisfied` in the context of `AsChangeset` +//! model structs: +//! This error indicates a type mismatch between the field you are trying to update and the actual +//! database type. Double check your type mapping. +//! * `the trait bound SomeLargeType: QueryFragment is not satisfied` while +//! trying to execute a query. +//! This error message indicates that a given query is not supported by your backend. This usually +//! means that you are trying to use SQL features from one SQL dialect on a different database +//! system. Double check your query that everything required is supported by the selected +//! backend. If that's the case double check that the relevant feature flags are enabled +//! (for example, `returning_clauses_for_sqlite_3_35` for enabling support for returning clauses in newer +//! sqlite versions) +//! * `the trait bound posts::title: SelectableExpression is not satisfied` while +//! executing a query: +//! This error message indicates that you're trying to select a field from a table +//! that does not appear in your from clause. If your query joins the relevant table via +//! [`left_join`](crate::query_dsl::QueryDsl::left_join) you need to call +//! [`.nullable()`](crate::expression_methods::NullableExpressionMethods::nullable) +//! on the relevant column in your select clause. +//! +//! +//! ## Getting help +//! +//! If you run into problems, Diesel has an active community. +//! Open a new [discussion] thread at diesel github repository +//! and we will try to help you +//! +//! [discussion]: https://github.com/diesel-rs/diesel/discussions/categories/q-a +//! +//! # Crate feature flags +//! +//! The following feature flags are considered to be part of diesels public +//! API. Any feature flag that is not listed here is **not** considered to +//! be part of the public API and can disappear at any point in time: + +//! +//! - `sqlite`: This feature enables the diesel sqlite backend. Enabling this feature requires per default +//! a compatible copy of `libsqlite3` for your target architecture. Alternatively, you can add `libsqlite3-sys` +//! with the `bundled` feature as a dependency to your crate so SQLite will be bundled: +//! ```toml +//! [dependencies] +//! libsqlite3-sys = { version = "0.29", features = ["bundled"] } +//! ``` +//! - `postgres`: This feature enables the diesel postgres backend. Enabling this feature requires a compatible +//! copy of `libpq` for your target architecture. This features implies `postgres_backend` +//! - `mysql`: This feature enables the idesel mysql backend. Enabling this feature requires a compatible copy +//! of `libmysqlclient` for your target architecture. This feature implies `mysql_backend` +//! - `postgres_backend`: This feature enables those parts of diesels postgres backend, that are not dependent +//! on `libpq`. Diesel does not provide any connection implementation with only this feature enabled. +//! This feature can be used to implement a custom implementation of diesels `Connection` trait for the +//! postgres backend outside of diesel itself, while reusing the existing query dsl extensions for the +//! postgres backend +//! - `mysql_backend`: This feature enables those parts of diesels mysql backend, that are not dependent +//! on `libmysqlclient`. Diesel does not provide any connection implementation with only this feature enabled. +//! This feature can be used to implement a custom implementation of diesels `Connection` trait for the +//! mysql backend outside of diesel itself, while reusing the existing query dsl extensions for the +//! mysql backend +//! - `returning_clauses_for_sqlite_3_35`: This feature enables support for `RETURNING` clauses in the sqlite backend. +//! Enabling this feature requires sqlite 3.35.0 or newer. +//! - `32-column-tables`: This feature enables support for tables with up to 32 columns. +//! This feature is enabled by default. Consider disabling this feature if you write a library crate +//! providing general extensions for diesel or if you do not need to support tables with more than 16 columns +//! and you want to minimize your compile times. +//! - `64-column-tables`: This feature enables support for tables with up to 64 columns. It implies the +//! `32-column-tables` feature. Enabling this feature will increase your compile times. +//! - `128-column-tables`: This feature enables support for tables with up to 128 columns. It implies the +//! `64-column-tables` feature. Enabling this feature will increase your compile times significantly. +//! - `i-implement-a-third-party-backend-and-opt-into-breaking-changes`: This feature opens up some otherwise +//! private API, that can be useful to implement a third party [`Backend`](crate::backend::Backend) +//! or write a custom [`Connection`] implementation. **Do not use this feature for +//! any other usecase**. By enabling this feature you explicitly opt out diesel stability guarantees. We explicitly +//! reserve us the right to break API's exported under this feature flag in any upcoming minor version release. +//! If you publish a crate depending on this feature flag consider to restrict the supported diesel version to the +//! currently released minor version. +//! - `serde_json`: This feature flag enables support for (de)serializing json values from the database using +//! types provided by `serde_json`. +//! - `chrono`: This feature flags enables support for (de)serializing date/time values from the database using +//! types provided by `chrono` +//! - `uuid`: This feature flag enables support for (de)serializing uuid values from the database using types +//! provided by `uuid` +//! - `network-address`: This feature flag enables support for (de)serializing +//! IP values from the database using types provided by `ipnetwork`. +//! - `ipnet-address`: This feature flag enables support for (de)serializing IP +//! values from the database using types provided by `ipnet`. +//! - `numeric`: This feature flag enables support for (de)serializing numeric values from the database using types +//! provided by `bigdecimal` +//! - `r2d2`: This feature flag enables support for the `r2d2` connection pool implementation. +//! - `extras`: This feature enables the feature flagged support for any third party crate. This implies the +//! following feature flags: `serde_json`, `chrono`, `uuid`, `network-address`, `numeric`, `r2d2` +//! - `with-deprecated`: This feature enables items marked as `#[deprecated]`. It is enabled by default. +//! disabling this feature explicitly opts out diesels stability guarantee. +//! - `without-deprecated`: This feature disables any item marked as `#[deprecated]`. Enabling this feature +//! explicitly opts out the stability guarantee given by diesel. This feature overrides the `with-deprecated`. +//! Note that this may also remove items that are not shown as `#[deprecated]` in our documentation, due to +//! various bugs in rustdoc. It can be used to check if you depend on any such hidden `#[deprecated]` item. +//! +//! By default the following features are enabled: +//! +//! - `with-deprecated` +//! - `32-column-tables` + +#![cfg_attr(feature = "unstable", feature(trait_alias))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(feature = "128-column-tables", recursion_limit = "256")] +// Built-in Lints +#![warn( + unreachable_pub, + missing_debug_implementations, + missing_copy_implementations, + elided_lifetimes_in_paths, + missing_docs +)] +// Clippy lints +#![allow( + clippy::match_same_arms, + clippy::needless_doctest_main, + clippy::map_unwrap_or, + clippy::redundant_field_names, + clippy::type_complexity +)] +#![warn( + clippy::unwrap_used, + clippy::print_stdout, + clippy::mut_mut, + clippy::non_ascii_literal, + clippy::similar_names, + clippy::unicode_not_nfc, + clippy::enum_glob_use, + clippy::if_not_else, + clippy::items_after_statements, + clippy::used_underscore_binding, + clippy::cast_possible_wrap, + clippy::cast_possible_truncation, + clippy::cast_sign_loss +)] +#![deny(unsafe_code)] +#![cfg_attr(test, allow(clippy::map_unwrap_or, clippy::unwrap_used))] + +extern crate diesel_derives; + +#[macro_use] +#[doc(hidden)] +pub mod macros; +#[doc(hidden)] +pub mod internal; + +#[cfg(test)] +#[macro_use] +extern crate cfg_if; + +#[cfg(test)] +pub mod test_helpers; + +pub mod associations; +pub mod backend; +pub mod connection; +pub mod data_types; +pub mod deserialize; +#[macro_use] +pub mod expression; +pub mod expression_methods; +#[doc(hidden)] +pub mod insertable; +pub mod query_builder; +pub mod query_dsl; +pub mod query_source; +#[cfg(feature = "r2d2")] +pub mod r2d2; +pub mod result; +pub mod serialize; +pub mod upsert; +#[macro_use] +pub mod sql_types; +pub mod migration; +pub mod row; + +#[cfg(feature = "mysql_backend")] +pub mod mysql; +#[cfg(feature = "postgres_backend")] +pub mod pg; +#[cfg(feature = "sqlite")] +pub mod sqlite; + +mod type_impls; +mod util; + +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[deprecated(since = "2.0.0", note = "Use explicit macro imports instead")] +pub use diesel_derives::{ + AsChangeset, AsExpression, Associations, DieselNumericOps, FromSqlRow, Identifiable, + Insertable, QueryId, Queryable, QueryableByName, SqlType, +}; + +pub use diesel_derives::MultiConnection; + +#[allow(unknown_lints, ambiguous_glob_reexports)] +pub mod dsl { + //! Includes various helper types and bare functions which are named too + //! generically to be included in prelude, but are often used when using Diesel. + + #[doc(inline)] + pub use crate::helper_types::*; + + #[doc(inline)] + pub use crate::expression::dsl::*; + + #[doc(inline)] + pub use crate::query_builder::functions::{ + delete, insert_into, insert_or_ignore_into, replace_into, select, sql_query, update, + }; + + #[doc(inline)] + #[cfg(feature = "postgres_backend")] + pub use crate::query_builder::functions::{copy_from, copy_to}; + + #[doc(inline)] + pub use diesel_derives::auto_type; + + #[cfg(feature = "postgres_backend")] + #[doc(inline)] + pub use crate::pg::expression::extensions::OnlyDsl; + + #[cfg(feature = "postgres_backend")] + #[doc(inline)] + pub use crate::pg::expression::extensions::TablesampleDsl; +} + +pub mod helper_types { + //! Provide helper types for concisely writing the return type of functions. + //! As with iterators, it is unfortunately difficult to return a partially + //! constructed query without exposing the exact implementation of the + //! function. Without higher kinded types, these various DSLs can't be + //! combined into a single trait for boxing purposes. + //! + //! All types here are in the form `>::Output`. So the return type of + //! `users.filter(first_name.eq("John")).order(last_name.asc()).limit(10)` would + //! be `Limit, Asc>>` + use super::query_builder::combination_clause::{self, CombinationClause}; + use super::query_builder::{locking_clause as lock, AsQuery}; + use super::query_dsl::methods::*; + use super::query_dsl::*; + use super::query_source::{aliasing, joins}; + use crate::dsl::CountStar; + use crate::query_builder::select_clause::SelectClause; + + #[doc(inline)] + pub use crate::expression::helper_types::*; + + /// Represents the return type of [`.select(selection)`](crate::prelude::QueryDsl::select) + pub type Select = >::Output; + + /// Represents the return type of [`diesel::select(selection)`](crate::select) + #[allow(non_camel_case_types)] // required for `#[auto_type]` + pub type select = crate::query_builder::SelectStatement< + crate::query_builder::NoFromClause, + SelectClause, + >; + + #[doc(hidden)] + #[deprecated(note = "Use `select` instead")] + pub type BareSelect = crate::query_builder::SelectStatement< + crate::query_builder::NoFromClause, + SelectClause, + >; + + /// Represents the return type of [`.filter(predicate)`](crate::prelude::QueryDsl::filter) + pub type Filter = >::Output; + + /// Represents the return type of [`.filter(lhs.eq(rhs))`](crate::prelude::QueryDsl::filter) + pub type FindBy = Filter>; + + /// Represents the return type of [`.for_update()`](crate::prelude::QueryDsl::for_update) + pub type ForUpdate = >::Output; + + /// Represents the return type of [`.for_no_key_update()`](crate::prelude::QueryDsl::for_no_key_update) + pub type ForNoKeyUpdate = >::Output; + + /// Represents the return type of [`.for_share()`](crate::prelude::QueryDsl::for_share) + pub type ForShare = >::Output; + + /// Represents the return type of [`.for_key_share()`](crate::prelude::QueryDsl::for_key_share) + pub type ForKeyShare = >::Output; + + /// Represents the return type of [`.skip_locked()`](crate::prelude::QueryDsl::skip_locked) + pub type SkipLocked = >::Output; + + /// Represents the return type of [`.no_wait()`](crate::prelude::QueryDsl::no_wait) + pub type NoWait = >::Output; + + /// Represents the return type of [`.find(pk)`](crate::prelude::QueryDsl::find) + pub type Find = >::Output; + + /// Represents the return type of [`.or_filter(predicate)`](crate::prelude::QueryDsl::or_filter) + pub type OrFilter = >::Output; + + /// Represents the return type of [`.order(ordering)`](crate::prelude::QueryDsl::order) + pub type Order = >::Output; + + /// Represents the return type of [`.order_by(ordering)`](crate::prelude::QueryDsl::order_by) + /// + /// Type alias of [Order] + pub type OrderBy = Order; + + /// Represents the return type of [`.then_order_by(ordering)`](crate::prelude::QueryDsl::then_order_by) + pub type ThenOrderBy = >::Output; + + /// Represents the return type of [`.limit()`](crate::prelude::QueryDsl::limit) + pub type Limit = + >::Output; + + /// Represents the return type of [`.offset()`](crate::prelude::QueryDsl::offset) + pub type Offset = + >::Output; + + /// Represents the return type of [`.inner_join(rhs)`](crate::prelude::QueryDsl::inner_join) + pub type InnerJoin = + >::Output; + + /// Represents the return type of [`.inner_join(rhs.on(on))`](crate::prelude::QueryDsl::inner_join) + pub type InnerJoinOn = + >::Output; + + /// Represents the return type of [`.left_join(rhs)`](crate::prelude::QueryDsl::left_join) + pub type LeftJoin = + >::Output; + + /// Represents the return type of [`.left_join(rhs.on(on))`](crate::prelude::QueryDsl::left_join) + pub type LeftJoinOn = + >::Output; + + /// Represents the return type of [`rhs.on(on)`](crate::query_dsl::JoinOnDsl::on) + pub type On = joins::OnClauseWrapper; + + use super::associations::HasTable; + use super::query_builder::{AsChangeset, IntoUpdateTarget, UpdateStatement}; + + /// Represents the return type of [`update(lhs).set(rhs)`](crate::query_builder::UpdateStatement::set) + pub type Update = UpdateStatement< + ::Table, + ::WhereClause, + ::Changeset, + >; + + /// Represents the return type of [`.into_boxed::<'a, DB>()`](crate::prelude::QueryDsl::into_boxed) + pub type IntoBoxed<'a, Source, DB> = >::Output; + + /// Represents the return type of [`.distinct()`](crate::prelude::QueryDsl::distinct) + pub type Distinct = ::Output; + + /// Represents the return type of [`.distinct_on(expr)`](crate::prelude::QueryDsl::distinct_on) + #[cfg(feature = "postgres_backend")] + pub type DistinctOn = >::Output; + + /// Represents the return type of [`.single_value()`](SingleValueDsl::single_value) + pub type SingleValue = ::Output; + + /// Represents the return type of [`.nullable()`](SelectNullableDsl::nullable) + pub type NullableSelect = ::Output; + + /// Represents the return type of [`.group_by(expr)`](crate::prelude::QueryDsl::group_by) + pub type GroupBy = >::Output; + + /// Represents the return type of [`.having(predicate)`](crate::prelude::QueryDsl::having) + pub type Having = >::Output; + + /// Represents the return type of [`.union(rhs)`](crate::prelude::CombineDsl::union) + pub type Union = CombinationClause< + combination_clause::Union, + combination_clause::Distinct, + ::Query, + ::Query, + >; + + /// Represents the return type of [`.union_all(rhs)`](crate::prelude::CombineDsl::union_all) + pub type UnionAll = CombinationClause< + combination_clause::Union, + combination_clause::All, + ::Query, + ::Query, + >; + + /// Represents the return type of [`.intersect(rhs)`](crate::prelude::CombineDsl::intersect) + pub type Intersect = CombinationClause< + combination_clause::Intersect, + combination_clause::Distinct, + ::Query, + ::Query, + >; + + /// Represents the return type of [`.intersect_all(rhs)`](crate::prelude::CombineDsl::intersect_all) + pub type IntersectAll = CombinationClause< + combination_clause::Intersect, + combination_clause::All, + ::Query, + ::Query, + >; + + /// Represents the return type of [`.except(rhs)`](crate::prelude::CombineDsl::except) + pub type Except = CombinationClause< + combination_clause::Except, + combination_clause::Distinct, + ::Query, + ::Query, + >; + + /// Represents the return type of [`.except_all(rhs)`](crate::prelude::CombineDsl::except_all) + pub type ExceptAll = CombinationClause< + combination_clause::Except, + combination_clause::All, + ::Query, + ::Query, + >; + + type JoinQuerySource = joins::JoinOn, On>; + + /// A query source representing the inner join between two tables. + /// + /// The third generic type (`On`) controls how the tables are + /// joined. + /// + /// By default, the implicit join established by [`joinable!`][] + /// will be used, allowing you to omit the exact join + /// condition. For example, for the inner join between three + /// tables that implement [`JoinTo`][], you only need to specify + /// the tables: `InnerJoinQuerySource, table3>`. + /// + /// [`JoinTo`]: crate::query_source::JoinTo + /// + /// If you use an explicit `ON` clause, you will need to specify + /// the `On` generic type. + /// + /// ```rust + /// # include!("doctest_setup.rs"); + /// use diesel::{dsl, helper_types::InnerJoinQuerySource}; + /// # use diesel::{backend::Backend, serialize::ToSql, sql_types}; + /// use schema::*; + /// + /// # fn main() -> QueryResult<()> { + /// # let conn = &mut establish_connection(); + /// # + /// // If you have an explicit join like this... + /// let join_constraint = comments::columns::post_id.eq(posts::columns::id); + /// # let query = + /// posts::table.inner_join(comments::table.on(join_constraint)); + /// # + /// # // Dummy usage just to ensure the example compiles. + /// # let filter = posts::columns::id.eq(1); + /// # let filter: &FilterExpression<_> = &filter; + /// # query.filter(filter).select(posts::columns::id).get_result::(conn)?; + /// # + /// # Ok(()) + /// # } + /// + /// // ... you can use `InnerJoinQuerySource` like this. + /// type JoinConstraint = dsl::Eq; + /// type MyInnerJoinQuerySource = InnerJoinQuerySource; + /// # type FilterExpression = dyn BoxableExpression; + /// ``` + pub type InnerJoinQuerySource>::OnClause> = + JoinQuerySource; + + /// A query source representing the left outer join between two tables. + /// + /// The third generic type (`On`) controls how the tables are + /// joined. + /// + /// By default, the implicit join established by [`joinable!`][] + /// will be used, allowing you to omit the exact join + /// condition. For example, for the left join between three + /// tables that implement [`JoinTo`][], you only need to specify + /// the tables: `LeftJoinQuerySource, table3>`. + /// + /// [`JoinTo`]: crate::query_source::JoinTo + /// + /// If you use an explicit `ON` clause, you will need to specify + /// the `On` generic type. + /// + /// ```rust + /// # include!("doctest_setup.rs"); + /// use diesel::{dsl, helper_types::LeftJoinQuerySource}; + /// # use diesel::{backend::Backend, serialize::ToSql, sql_types}; + /// use schema::*; + /// + /// # fn main() -> QueryResult<()> { + /// # let conn = &mut establish_connection(); + /// # + /// // If you have an explicit join like this... + /// let join_constraint = comments::columns::post_id.eq(posts::columns::id); + /// # let query = + /// posts::table.left_join(comments::table.on(join_constraint)); + /// # + /// # // Dummy usage just to ensure the example compiles. + /// # let filter = posts::columns::id.eq(1); + /// # let filter: &FilterExpression<_> = &filter; + /// # query.filter(filter).select(posts::columns::id).get_result::(conn)?; + /// # + /// # Ok(()) + /// # } + /// + /// // ... you can use `LeftJoinQuerySource` like this. + /// type JoinConstraint = dsl::Eq; + /// type MyLeftJoinQuerySource = LeftJoinQuerySource; + /// # type FilterExpression = dyn BoxableExpression; + /// ``` + pub type LeftJoinQuerySource>::OnClause> = + JoinQuerySource; + + /// Maps `F` to `Alias` + /// + /// Any column `F` that belongs to `S::Table` will be transformed into + /// [`AliasedField`](crate::query_source::AliasedField) + /// + /// Any column `F` that does not belong to `S::Table` will be left untouched. + /// + /// This also works with tuples and some expressions. + pub type AliasedFields = >::Out; + + #[doc(hidden)] + #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] + #[deprecated(note = "Use `LoadQuery::RowIter` directly")] + pub type LoadIter<'conn, 'query, Q, Conn, U, B = crate::connection::DefaultLoadingMode> = + >::RowIter<'conn>; + + /// Represents the return type of [`diesel::delete`] + #[allow(non_camel_case_types)] // required for `#[auto_type]` + pub type delete = crate::query_builder::DeleteStatement< + ::Table, + ::WhereClause, + >; + + /// Represents the return type of [`diesel::insert_into`] + #[allow(non_camel_case_types)] // required for `#[auto_type]` + pub type insert_into = crate::query_builder::IncompleteInsertStatement; + + /// Represents the return type of [`diesel::update`] + #[allow(non_camel_case_types)] // required for `#[auto_type]` + pub type update = + UpdateStatement<::Table, ::WhereClause>; + + /// Represents the return type of [`diesel::insert_or_ignore_into`] + #[allow(non_camel_case_types)] // required for `#[auto_type]` + pub type insert_or_ignore_into = crate::query_builder::IncompleteInsertOrIgnoreStatement; + + /// Represents the return type of [`diesel::replace_into`] + #[allow(non_camel_case_types)] // required for `#[auto_type]` + pub type replace_into = crate::query_builder::IncompleteReplaceStatement; + + /// Represents the return type of + /// [`IncompleteInsertStatement::values()`](crate::query_builder::IncompleteInsertStatement::values) + pub type Values = crate::query_builder::InsertStatement< + ::Table, + ::Table, + >>::Values, + ::Op, + >; + + /// Represents the return type of + /// [`UpdateStatement::set()`](crate::query_builder::UpdateStatement::set) + pub type Set = crate::query_builder::UpdateStatement< + ::Table, + ::Where, + ::Changeset, + >; + + /// Represents the return type of + /// [`InsertStatement::returning`](crate::query_builder::InsertStatement::returning), + /// [`UpdateStatement::returning`] and + /// [`DeleteStatement::returning`](crate::query_builder::DeleteStatement::returning) + pub type Returning = + >::WithReturning; + + #[doc(hidden)] // used for `QueryDsl::count` + pub type Count = Select; +} + +pub mod prelude { + //! Re-exports important traits and types. Meant to be glob imported when using Diesel. + + #[doc(inline)] + pub use crate::associations::{Associations, GroupedBy, Identifiable}; + #[doc(inline)] + pub use crate::connection::Connection; + #[doc(inline)] + pub use crate::deserialize::{Queryable, QueryableByName}; + #[doc(inline)] + pub use crate::expression::{ + AppearsOnTable, BoxableExpression, Expression, IntoSql, Selectable, SelectableExpression, + }; + // If [`IntoSql`](crate::expression::helper_types::IntoSql) the type gets imported at the + // same time as IntoSql the trait (this one) gets imported via the prelude, then + // methods of the trait won't be resolved because the type may take priority over the trait. + // That issue can be avoided by also importing it anonymously: + pub use crate::expression::IntoSql as _; + + #[doc(inline)] + pub use crate::expression::functions::define_sql_function; + #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] + pub use crate::expression::functions::sql_function; + + #[doc(inline)] + pub use crate::expression::SelectableHelper; + #[doc(inline)] + pub use crate::expression_methods::*; + #[doc(inline)] + pub use crate::insertable::Insertable; + #[doc(inline)] + pub use crate::macros::prelude::*; + #[doc(inline)] + pub use crate::query_builder::AsChangeset; + #[doc(inline)] + pub use crate::query_builder::DecoratableTarget; + #[doc(inline)] + pub use crate::query_dsl::{ + BelongingToDsl, CombineDsl, JoinOnDsl, QueryDsl, RunQueryDsl, SaveChangesDsl, + }; + pub use crate::query_source::SizeRestrictedColumn as _; + #[doc(inline)] + pub use crate::query_source::{Column, JoinTo, QuerySource, Table}; + #[doc(inline)] + pub use crate::result::{ + ConnectionError, ConnectionResult, OptionalEmptyChangesetExtension, OptionalExtension, + QueryResult, + }; + #[doc(inline)] + pub use diesel_derives::table_proc as table; + + #[cfg(feature = "mysql")] + #[doc(inline)] + pub use crate::mysql::MysqlConnection; + #[doc(inline)] + #[cfg(feature = "postgres_backend")] + pub use crate::pg::query_builder::copy::ExecuteCopyFromDsl; + #[cfg(feature = "postgres")] + #[doc(inline)] + pub use crate::pg::PgConnection; + #[cfg(feature = "sqlite")] + #[doc(inline)] + pub use crate::sqlite::SqliteConnection; +} + +#[doc(inline)] +pub use crate::macros::table; +pub use crate::prelude::*; +#[doc(inline)] +pub use crate::query_builder::debug_query; +#[doc(inline)] +#[cfg(feature = "postgres")] +pub use crate::query_builder::functions::{copy_from, copy_to}; +#[doc(inline)] +pub use crate::query_builder::functions::{ + delete, insert_into, insert_or_ignore_into, replace_into, select, sql_query, update, +}; +pub use crate::result::Error::NotFound; + +extern crate self as diesel; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/macros/internal.rs b/collector/compile-benchmarks/diesel-2.2.10/src/macros/internal.rs new file mode 100644 index 000000000..71892ffc6 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/macros/internal.rs @@ -0,0 +1,36 @@ +/// This will implement `SelectableExpression` and `AppearsOnTable` for "simple" +/// composite nodes where the where clause is roughly `AllTyParams: +/// SelectableExpression, Self: Expression`. +/// +/// This macro is exported because we want to be able to call it from other +/// macros that are exported, but it is not part of our public API. +#[macro_export] +#[doc(hidden)] +macro_rules! impl_selectable_expression { + ($struct_name:ident) => { + $crate::impl_selectable_expression!(ty_params = (), struct_ty = $struct_name,); + }; + + ($struct_name:ident<$($ty_params:ident),+>) => { + $crate::impl_selectable_expression!( + ty_params = ($($ty_params),+), + struct_ty = $struct_name<$($ty_params),+>, + ); + }; + + (ty_params = ($($ty_params:ident),*), struct_ty = $struct_ty:ty,) => { + impl<$($ty_params,)* QS> $crate::expression::SelectableExpression + for $struct_ty where + $struct_ty: $crate::expression::AppearsOnTable, + $($ty_params: $crate::expression::SelectableExpression,)* + { + } + + impl<$($ty_params,)* QS> $crate::expression::AppearsOnTable + for $struct_ty where + $struct_ty: $crate::expression::Expression, + $($ty_params: $crate::expression::AppearsOnTable,)* + { + } + }; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/macros/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/macros/mod.rs new file mode 100644 index 000000000..8290d0517 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/macros/mod.rs @@ -0,0 +1,680 @@ +pub(crate) mod prelude { + #[cfg_attr( + any(feature = "huge-tables", feature = "large-tables"), + allow(deprecated) + )] + // This is a false positive, we reexport it later + #[allow(unreachable_pub, unused_imports)] + #[doc(inline)] + pub use crate::{ + allow_columns_to_appear_in_same_group_by_clause, allow_tables_to_appear_in_same_query, + joinable, table, + }; +} + +#[doc(inline)] +pub use diesel_derives::table_proc as table; + +/// Allow two tables to be referenced in a join query without providing an +/// explicit `ON` clause. +/// +/// The generated `ON` clause will always join to the primary key of the parent +/// table. This macro removes the need to call [`.on`] explicitly, you will +/// still need to invoke +/// [`allow_tables_to_appear_in_same_query!`](crate::allow_tables_to_appear_in_same_query) +/// for these two tables to be able to use the resulting query, unless you are +/// using `diesel print-schema` which will generate it for you. +/// +/// If you are using `diesel print-schema`, an invocation of this macro +/// will be generated for every foreign key in your database unless +/// one of the following is true: +/// +/// - The foreign key references something other than the primary key +/// - The foreign key is composite +/// - There is more than one foreign key connecting two tables +/// - The foreign key is self-referential +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// use schema::*; +/// +/// # /* +/// joinable!(posts -> users (user_id)); +/// allow_tables_to_appear_in_same_query!(posts, users); +/// # */ +/// +/// # fn main() { +/// let implicit_on_clause = users::table.inner_join(posts::table); +/// let implicit_on_clause_sql = diesel::debug_query::(&implicit_on_clause).to_string(); +/// +/// let explicit_on_clause = users::table +/// .inner_join(posts::table.on(posts::user_id.eq(users::id))); +/// let explicit_on_clause_sql = diesel::debug_query::(&explicit_on_clause).to_string(); +/// +/// assert_eq!(implicit_on_clause_sql, explicit_on_clause_sql); +/// # } +/// +/// ``` +/// +/// In the example above, the line `joinable!(posts -> users (user_id));` +/// +/// specifies the relation of the tables and the ON clause in the following way: +/// +/// `child_table -> parent_table (foreign_key)` +/// +/// * `parent_table` is the Table with the Primary key. +/// +/// * `child_table` is the Table with the Foreign key. +/// +/// So given the Table declaration from [Associations docs](crate::associations) +/// +/// * The parent table would be `User` +/// * The child table would be `Post` +/// * and the Foreign key would be `Post.user_id` +/// +/// For joins that do not explicitly use on clauses via [`JoinOnDsl`](crate::prelude::JoinOnDsl) +/// the following on clause is generated implicitly: +/// ```sql +/// post JOIN users ON posts.user_id = users.id +/// ``` +#[macro_export] +macro_rules! joinable { + ($($child:ident)::* -> $($parent:ident)::* ($source:ident)) => { + $crate::joinable_inner!($($child)::* ::table => $($parent)::* ::table : ($($child)::* ::$source = $($parent)::* ::table)); + $crate::joinable_inner!($($parent)::* ::table => $($child)::* ::table : ($($child)::* ::$source = $($parent)::* ::table)); + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! joinable_inner { + ($left_table:path => $right_table:path : ($foreign_key:path = $parent_table:path)) => { + $crate::joinable_inner!( + left_table_ty = $left_table, + right_table_ty = $right_table, + right_table_expr = $right_table, + foreign_key = $foreign_key, + primary_key_ty = <$parent_table as $crate::query_source::Table>::PrimaryKey, + primary_key_expr = + <$parent_table as $crate::query_source::Table>::primary_key(&$parent_table), + ); + }; + + ( + left_table_ty = $left_table_ty:ty, + right_table_ty = $right_table_ty:ty, + right_table_expr = $right_table_expr:expr, + foreign_key = $foreign_key:path, + primary_key_ty = $primary_key_ty:ty, + primary_key_expr = $primary_key_expr:expr, + ) => { + impl $crate::JoinTo<$right_table_ty> for $left_table_ty { + type FromClause = $right_table_ty; + type OnClause = $crate::dsl::Eq< + $crate::internal::table_macro::NullableExpression<$foreign_key>, + $crate::internal::table_macro::NullableExpression<$primary_key_ty>, + >; + + fn join_target(rhs: $right_table_ty) -> (Self::FromClause, Self::OnClause) { + use $crate::{ExpressionMethods, NullableExpressionMethods}; + + ( + rhs, + $foreign_key.nullable().eq($primary_key_expr.nullable()), + ) + } + } + }; +} + +/// Allow two or more tables which are otherwise unrelated to be used together +/// in a query. +/// +/// This macro must be invoked any time two tables need to appear in the same +/// query either because they are being joined together, or because one appears +/// in a subselect. When this macro is invoked with more than 2 tables, every +/// combination of those tables will be allowed to appear together. +/// +/// If you are using `diesel print-schema`, an invocation of +/// this macro will be generated for you for all tables in your schema. +/// +/// # Example +/// +/// ``` +/// # use diesel::{allow_tables_to_appear_in_same_query, table}; +/// # +/// // This would be required to do `users.inner_join(posts.inner_join(comments))` +/// allow_tables_to_appear_in_same_query!(comments, posts, users); +/// +/// table! { +/// comments { +/// id -> Integer, +/// post_id -> Integer, +/// body -> VarChar, +/// } +/// } +/// +/// table! { +/// posts { +/// id -> Integer, +/// user_id -> Integer, +/// title -> VarChar, +/// } +/// } +/// +/// table! { +/// users { +/// id -> Integer, +/// name -> VarChar, +/// } +/// } +/// ``` +/// +/// When more than two tables are passed, the relevant code is generated for +/// every combination of those tables. This code would be equivalent to the +/// previous example. +/// +/// ``` +/// # use diesel::{allow_tables_to_appear_in_same_query, table}; +/// # table! { +/// # comments { +/// # id -> Integer, +/// # post_id -> Integer, +/// # body -> VarChar, +/// # } +/// # } +/// # +/// # table! { +/// # posts { +/// # id -> Integer, +/// # user_id -> Integer, +/// # title -> VarChar, +/// # } +/// # } +/// # +/// # table! { +/// # users { +/// # id -> Integer, +/// # name -> VarChar, +/// # } +/// # } +/// # +/// allow_tables_to_appear_in_same_query!(comments, posts); +/// allow_tables_to_appear_in_same_query!(comments, users); +/// allow_tables_to_appear_in_same_query!(posts, users); +/// # +/// # fn main() {} +/// ``` +#[macro_export] +macro_rules! allow_tables_to_appear_in_same_query { + ($left_mod:ident, $($right_mod:ident),+ $(,)*) => { + $( + impl $crate::query_source::TableNotEqual<$left_mod::table> for $right_mod::table {} + impl $crate::query_source::TableNotEqual<$right_mod::table> for $left_mod::table {} + $crate::__diesel_internal_backend_specific_allow_tables_to_appear_in_same_query!($left_mod, $right_mod); + )+ + $crate::allow_tables_to_appear_in_same_query!($($right_mod,)+); + }; + + ($last_table:ident,) => {}; + + () => {}; +} +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "postgres_backend")] +macro_rules! __diesel_internal_backend_specific_allow_tables_to_appear_in_same_query { + ($left:ident, $right:ident) => { + impl $crate::query_source::TableNotEqual<$left::table> + for $crate::query_builder::Only<$right::table> + { + } + impl $crate::query_source::TableNotEqual<$right::table> + for $crate::query_builder::Only<$left::table> + { + } + impl $crate::query_source::TableNotEqual<$crate::query_builder::Only<$left::table>> + for $right::table + { + } + impl $crate::query_source::TableNotEqual<$crate::query_builder::Only<$right::table>> + for $left::table + { + } + impl $crate::query_source::TableNotEqual<$left::table> + for $crate::query_builder::Tablesample<$right::table, TSM> + where + TSM: $crate::internal::table_macro::TablesampleMethod, + { + } + impl $crate::query_source::TableNotEqual<$right::table> + for $crate::query_builder::Tablesample<$left::table, TSM> + where + TSM: $crate::internal::table_macro::TablesampleMethod, + { + } + impl + $crate::query_source::TableNotEqual< + $crate::query_builder::Tablesample<$left::table, TSM>, + > for $right::table + where + TSM: $crate::internal::table_macro::TablesampleMethod, + { + } + impl + $crate::query_source::TableNotEqual< + $crate::query_builder::Tablesample<$right::table, TSM>, + > for $left::table + where + TSM: $crate::internal::table_macro::TablesampleMethod, + { + } + }; +} +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "postgres_backend"))] +macro_rules! __diesel_internal_backend_specific_allow_tables_to_appear_in_same_query { + ($left:ident, $right:ident) => {}; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __diesel_impl_allow_in_same_group_by_clause { + ( + left = [$($left_path:tt)::+], + ) => {}; + ( + left = [$($left_path:tt)::+], + $($right_path:tt)::+ + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)+], + right = [$($right_path)+], + left_tbl = [], + left_path = [], + } + }; + ( + left = [$($left_path:tt)::+], + $($right_path:tt)::+, + $($other:tt)* + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)+], + right = [$($right_path)+], + left_tbl = [], + left_path = [], + } + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)::+], + $($other)* + } + }; + ( + left = [$left_path_p1: tt $($left_path: tt)+], + right = [$($right_path: tt)*], + left_tbl = [$($left_tbl:tt)?], + left_path = [$($left_out_path:tt)*], + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)+], + right = [$($right_path)*], + left_tbl = [$left_path_p1], + left_path = [$($left_out_path)* $($left_tbl)?], + } + }; + ( + left = [$left_col: tt], + right = [$($right_path: tt)*], + left_tbl = [$($left_tbl:tt)?], + left_path = [$($left_out_path:tt)*], + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$left_col], + right = [$($right_path)*], + left_tbl = [$($left_tbl)?], + left_path = [$($left_out_path)*], + right_tbl = [], + right_path = [], + } + }; + ( + left = [$left_col: tt ], + right = [$right_path_p1: tt $($right_path: tt)+], + left_tbl = [$($left_tbl:tt)?], + left_path = [$($left_out_path:tt)*], + right_tbl = [$($right_tbl:tt)?], + right_path = [$($right_out_path:tt)*], + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$left_col], + right = [$($right_path)+], + left_tbl = [$($left_tbl)?], + left_path = [$($left_out_path)*], + right_tbl = [$right_path_p1], + right_path = [$($right_out_path)* $($right_tbl)?], + } + }; + ( + left = [$left_col: tt], + right = [$right_col: tt], + left_tbl = [$left_tbl:tt], + left_path = [$($left_begin:tt)*], + right_tbl = [$right_tbl:tt], + right_path = [$($right_begin:tt)*], + ) => { + $crate::static_cond! { + if $left_tbl != $right_tbl { + impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $left_tbl :: $left_col> for $($right_begin ::)* $right_tbl :: $right_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + + impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $right_tbl :: $right_col> for $($left_begin ::)* $left_tbl :: $left_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + } + } + }; + ( + left = [$left_col: tt], + right = [$right_col: tt], + left_tbl = [$($left_tbl:tt)?], + left_path = [$($left_begin:tt)*], + right_tbl = [$($right_tbl:tt)?], + right_path = [$($right_begin:tt)*], + ) => { + impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $($left_tbl ::)? $left_col> for $($right_begin ::)* $($right_tbl ::)? $right_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + + impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $($right_tbl ::)? $right_col> for $($left_begin ::)* $($left_tbl ::)? $left_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + }; + +} + +/// Allow two or more columns which are otherwise unrelated to be used together +/// in a group by clause. +/// +/// This macro must be invoked any time two columns need to appear in the same +/// group by clause. When this macro is invoked with more than 2 columns, every +/// combination of those columns will be allowed to appear together. +/// +/// # Example +/// +/// ``` +/// # include!("../doctest_setup.rs"); +/// # use crate::schema::{users, posts}; +/// // This would be required +/// +/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::id, posts::title); +/// # fn main() { +/// // to do implement the following join +/// users::table.inner_join(posts::table).group_by((users::name, posts::id, posts::title)) +/// # ; +/// # } +/// ``` +/// +/// When more than two columns are passed, the relevant code is generated for +/// every combination of those columns. This code would be equivalent to the +/// previous example. +/// +/// ``` +/// # include!("../doctest_setup.rs"); +/// # use crate::schema::{users, posts}; +/// # +/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::title); +/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::id); +/// allow_columns_to_appear_in_same_group_by_clause!(posts::title, posts::id); +/// # fn main() {} +/// ``` +#[macro_export] +macro_rules! allow_columns_to_appear_in_same_group_by_clause { + ($($left_path:tt)::+, $($right_path:tt)::+ $(,)?) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)::+], + $($right_path)::+, + } + }; + ($($left_path:tt)::+, $($right_path:tt)::+, $($other: tt)*) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)::+], + $($right_path)::+, + $($other)* + } + $crate::allow_columns_to_appear_in_same_group_by_clause! { + $($right_path)::+, + $($other)* + } + }; + ($last_col:ty,) => {}; + () => {}; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_with_dollar_sign { + ($($body:tt)*) => { + macro_rules! __with_dollar_sign { $($body)* } + __with_dollar_sign!($); + } +} + +// The order of these modules is important (at least for those which have tests). +// Utility macros which don't call any others need to come first. +#[macro_use] +mod internal; +#[macro_use] +mod static_cond; +#[macro_use] +mod ops; + +#[cfg(test)] +mod tests { + use crate::prelude::*; + + table! { + foo.bars { + id -> Integer, + baz -> Text, + } + } + + mod my_types { + #[derive(Debug, Clone, Copy, crate::sql_types::SqlType)] + pub struct MyCustomType; + } + + table! { + use crate::sql_types::*; + use crate::macros::tests::my_types::*; + + table_with_custom_types { + id -> Integer, + my_type -> MyCustomType, + } + } + + table! { + use crate::sql_types::*; + use crate::macros::tests::my_types::*; + + /// Table documentation + /// + /// some in detail documentation + table_with_custom_type_and_id (a) { + /// Column documentation + /// + /// some more details + a -> Integer, + my_type -> MyCustomType, + } + } + + #[test] + #[cfg(feature = "postgres")] + fn table_with_custom_schema() { + use crate::pg::Pg; + let expected_sql = r#"SELECT "foo"."bars"."baz" FROM "foo"."bars" -- binds: []"#; + assert_eq!( + expected_sql, + &crate::debug_query::(&bars::table.select(bars::baz)).to_string() + ); + } + + table! { + use crate::sql_types; + use crate::sql_types::*; + + table_with_arbitrarily_complex_types { + id -> sql_types::Integer, + qualified_nullable -> sql_types::Nullable, + deeply_nested_type -> Nullable>, + // This actually should work, but there appears to be a rustc bug + // on the `AsExpression` bound for `EqAll` when the ty param is a projection + // projected_type -> as sql_types::IntoNullable>::Nullable, + //random_tuple -> (Integer, Integer), + } + } + + table!( + foo { + /// Column doc + id -> Integer, + + #[sql_name = "type"] + /// Also important to document this column + mytype -> Integer, + + /// And this one + #[sql_name = "bleh"] + hey -> Integer, + } + ); + + #[test] + #[cfg(feature = "postgres")] + fn table_with_column_renaming_postgres() { + use crate::pg::Pg; + let expected_sql = r#"SELECT "foo"."id", "foo"."type", "foo"."bleh" FROM "foo" WHERE ("foo"."type" = $1) -- binds: [1]"#; + assert_eq!( + expected_sql, + crate::debug_query::(&foo::table.filter(foo::mytype.eq(1))).to_string() + ); + } + + #[test] + #[cfg(feature = "mysql")] + fn table_with_column_renaming_mysql() { + use crate::mysql::Mysql; + let expected_sql = r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#; + assert_eq!( + expected_sql, + crate::debug_query::(&foo::table.filter(foo::mytype.eq(1))).to_string() + ); + } + + #[test] + #[cfg(feature = "sqlite")] + fn table_with_column_renaming_sqlite() { + use crate::sqlite::Sqlite; + let expected_sql = r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#; + assert_eq!( + expected_sql, + crate::debug_query::(&foo::table.filter(foo::mytype.eq(1))).to_string() + ); + } + + table!( + use crate::sql_types::*; + + /// Some documentation + #[sql_name="mod"] + /// Some more documentation + bar { + id -> Integer, + } + ); + + #[test] + #[cfg(feature = "postgres")] + fn table_renaming_postgres() { + use crate::pg::Pg; + let expected_sql = r#"SELECT "mod"."id" FROM "mod" -- binds: []"#; + assert_eq!( + expected_sql, + crate::debug_query::(&bar::table.select(bar::id)).to_string() + ); + } + + #[test] + #[cfg(feature = "mysql")] + fn table_renaming_mysql() { + use crate::mysql::Mysql; + let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#; + assert_eq!( + expected_sql, + crate::debug_query::(&bar::table.select(bar::id)).to_string() + ); + } + + #[test] + #[cfg(feature = "sqlite")] + fn table_renaming_sqlite() { + use crate::sqlite::Sqlite; + let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#; + assert_eq!( + expected_sql, + crate::debug_query::(&bar::table.select(bar::id)).to_string() + ); + } + + mod tests_for_allow_combined_group_by_syntax { + use crate::table; + + table! { + a(b) { + b -> Text, + c -> Text, + d -> Text, + e -> Text, + } + } + + table! { + b(a) { + a -> Text, + c -> Text, + d -> Text, + } + } + + table! { + c(a) { + a -> Text, + b -> Text, + d -> Text, + } + } + + // allow using table::column + allow_columns_to_appear_in_same_group_by_clause!(a::b, b::a, a::d,); + + // allow using full paths + allow_columns_to_appear_in_same_group_by_clause!(self::a::c, self::b::c, self::b::d,); + + use self::a::d as a_d; + use self::b::d as b_d; + use self::c::d as c_d; + + // allow using plain identifiers + allow_columns_to_appear_in_same_group_by_clause!(a_d, b_d, c_d); + + // allow mixing all variants + allow_columns_to_appear_in_same_group_by_clause!(c_d, self::b::a, a::e,); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/macros/ops.rs b/collector/compile-benchmarks/diesel-2.2.10/src/macros/ops.rs new file mode 100644 index 000000000..8413833e4 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/macros/ops.rs @@ -0,0 +1,109 @@ +#[macro_export] +/// Implements the Rust operator for a given type. If you create a new SQL +/// function, which returns a type that you'd like to use an operator on, you +/// should invoke this macro. Unfortunately, Rust disallows us from +/// automatically implementing `Add` and other traits from `std::ops`, under its +/// orphan rules. +macro_rules! operator_allowed { + ($tpe:ty, $op:ident, $fn_name:ident) => { + impl ::std::ops::$op for $tpe + where + Rhs: $crate::expression::AsExpression< + <<$tpe as $crate::Expression>::SqlType as $crate::sql_types::ops::$op>::Rhs, + >, + { + type Output = $crate::internal::table_macro::ops::$op; + + fn $fn_name(self, __diesel_internal_rhs: Rhs) -> Self::Output { + $crate::internal::table_macro::ops::$op::new( + self, + __diesel_internal_rhs.as_expression(), + ) + } + } + }; +} + +#[macro_export] +/// Indicates that an expression allows all numeric operators. If you create new +/// SQL functions that return a numeric type, you should invoke this macro that +/// type. Unfortunately, Rust disallows us from automatically implementing `Add` +/// for types which implement `Expression`, under its orphan rules. +macro_rules! numeric_expr { + ($tpe:ty) => { + $crate::operator_allowed!($tpe, Add, add); + $crate::operator_allowed!($tpe, Sub, sub); + $crate::operator_allowed!($tpe, Div, div); + $crate::operator_allowed!($tpe, Mul, mul); + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_generate_ops_impls_if_numeric { + ($column_name:ident, Nullable<$($inner:tt)::*>) => { $crate::__diesel_generate_ops_impls_if_numeric!($column_name, $($inner)::*); }; + + ($column_name:ident, Unsigned<$($inner:tt)::*>) => { $crate::__diesel_generate_ops_impls_if_numeric!($column_name, $($inner)::*); }; + + ($column_name:ident, SmallInt) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Int2) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Smallint) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, SmallSerial) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, Integer) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Int4) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Serial) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, BigInt) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Int8) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Bigint) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, BigSerial) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, Float) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Float4) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, Double) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Float8) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, Numeric) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, $non_numeric_type:ty) => {}; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! date_time_expr { + ($tpe:ty) => { + $crate::operator_allowed!($tpe, Add, add); + $crate::operator_allowed!($tpe, Sub, sub); + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_generate_ops_impls_if_date_time { + ($column_name:ident, Nullable<$($inner:tt)::*>) => { $crate::__diesel_generate_ops_impls_if_date_time!($column_name, $($inner)::*); }; + ($column_name:ident, Time) => { $crate::date_time_expr!($column_name); }; + ($column_name:ident, Date) => { $crate::date_time_expr!($column_name); }; + ($column_name:ident, Timestamp) => { $crate::date_time_expr!($column_name); }; + ($column_name:ident, Timestamptz) => { $crate::date_time_expr!($column_name); }; + ($column_name:ident, $non_date_time_type:ty) => {}; +} + +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! network_expr { + ($tpe:ty) => { + operator_allowed!($tpe, Add, add); + operator_allowed!($tpe, Sub, sub); + }; +} + +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! __diesel_generate_ops_impls_if_network { + ($column_name:ident, Nullable<$($inner:tt)::*>) => { __diesel_generate_ops_impls_if_network!($column_name, $($inner)::*); }; + ($column_name:ident, Cidr) => { network_expr!($column_name); }; + ($column_name:ident, Inet) => { network_expr!($column_name); }; + ($column_name:ident, $non_network_type:ty) => {}; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/macros/static_cond.rs b/collector/compile-benchmarks/diesel-2.2.10/src/macros/static_cond.rs new file mode 100644 index 000000000..d59c78ee2 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/macros/static_cond.rs @@ -0,0 +1,38 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] // https://github.com/rust-lang-nursery/rustfmt/issues/2754 + +// Vendored from the static-cond crate as macro re-exports are not available in stable Rust. +// https://github.com/durka/static-cond/blob/36aa2dd/src/lib.rs +// +// Code is dual licensed under MIT/Apache-2.0 +// Copyright (c) 2016 Alex Burka +#[macro_export] +#[doc(hidden)] +macro_rules! static_cond { + // private rule to define and call the local macro + (@go $lhs:tt $rhs:tt $arm1:tt $arm2:tt) => { + // note that the inner macro has no captures (it can't, because there's no way to escape `$`) + macro_rules! __static_cond { + ($lhs $lhs) => $arm1; + ($lhs $rhs) => $arm2 + } + + __static_cond!($lhs $rhs); + }; + + // no else condition provided: fall through with empty else + (if $lhs:tt == $rhs:tt $then:tt) => { + $crate::static_cond!(if $lhs == $rhs $then else { }); + }; + (if $lhs:tt != $rhs:tt $then:tt) => { + $crate::static_cond!(if $lhs != $rhs $then else { }); + }; + + // we evaluate a conditional by generating a new macro (in an inner scope, so name shadowing is + // not a big concern) and calling it + (if $lhs:tt == $rhs:tt $then:tt else $els:tt) => { + $crate::static_cond!(@go $lhs $rhs $then $els); + }; + (if $lhs:tt != $rhs:tt $then:tt else $els:tt) => { + $crate::static_cond!(@go $lhs $rhs $els $then); + }; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/migration/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/migration/mod.rs new file mode 100644 index 000000000..d3cd26e6a --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/migration/mod.rs @@ -0,0 +1,223 @@ +//! Representation of migrations + +use crate::backend::Backend; +use crate::connection::{BoxableConnection, Connection}; +use crate::deserialize::{FromSql, FromSqlRow}; +use crate::expression::AsExpression; +use crate::result::QueryResult; +use crate::serialize::ToSql; +use crate::sql_types::Text; +use std::borrow::Cow; +use std::error::Error; +use std::fmt::Display; + +/// A specialized result type representing the result of +/// a migration operation +pub type Result = std::result::Result>; + +/// A migration version identifier +/// +/// This is used by the migration harness to place migrations +/// in order, therefore two different instances of this type +/// must be sortable +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, FromSqlRow, AsExpression)] +#[diesel(sql_type = Text)] +pub struct MigrationVersion<'a>(Cow<'a, str>); + +impl MigrationVersion<'_> { + /// Convert the current migration version into + /// an owned variant with static life time + pub fn as_owned(&self) -> MigrationVersion<'static> { + MigrationVersion(Cow::Owned(self.0.as_ref().to_owned())) + } +} + +impl FromSql for MigrationVersion<'_> +where + String: FromSql, + DB: Backend, +{ + fn from_sql(bytes: DB::RawValue<'_>) -> crate::deserialize::Result { + let s = String::from_sql(bytes)?; + Ok(Self(Cow::Owned(s))) + } +} + +impl<'a, DB> ToSql for MigrationVersion<'a> +where + Cow<'a, str>: ToSql, + DB: Backend, +{ + fn to_sql<'b>( + &'b self, + out: &mut crate::serialize::Output<'b, '_, DB>, + ) -> crate::serialize::Result { + self.0.to_sql(out) + } +} + +impl From for MigrationVersion<'_> { + fn from(s: String) -> Self { + MigrationVersion(Cow::Owned(s)) + } +} + +impl<'a> From<&'a str> for MigrationVersion<'a> { + fn from(s: &'a str) -> Self { + MigrationVersion(Cow::Borrowed(s)) + } +} + +impl<'a> From<&'a String> for MigrationVersion<'a> { + fn from(s: &'a String) -> Self { + MigrationVersion(Cow::Borrowed(s)) + } +} + +impl Display for MigrationVersion<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.0.as_ref()) + } +} + +/// Represents the name of a migration +/// +/// Users should threat this as `impl Display` type, +/// for implementors of custom migration types +/// this opens the possibility to roll out their own versioning +/// schema. +pub trait MigrationName: Display { + /// The version corresponding to the current migration name + fn version(&self) -> MigrationVersion<'_>; +} + +/// Represents a migration that interacts with diesel +pub trait Migration { + /// Apply this migration + fn run(&self, conn: &mut dyn BoxableConnection) -> Result<()>; + + /// Revert this migration + fn revert(&self, conn: &mut dyn BoxableConnection) -> Result<()>; + + /// Get a the attached metadata for this migration + fn metadata(&self) -> &dyn MigrationMetadata; + + /// Get the name of the current migration + /// + /// The provided name is used by migration harness + /// to get the version of a migration and to + /// as something to that is displayed and allows + /// user to identify a specific migration + fn name(&self) -> &dyn MigrationName; +} + +/// This trait is designed to customize the behaviour +/// of the default migration harness of diesel +/// +/// Any new customization option will be added +/// as new function here. Each new function +/// will have a default implementation +/// returning the old a value corresponding +/// to the old uncustomized behaviour +pub trait MigrationMetadata { + /// Whether the current migration is executed in a transaction or not + /// + /// By default each migration is executed in a own transaction, but + /// certain operations (like creating an index on an existing column) + /// requires running the migration without transaction. + /// + /// By default this function returns true + fn run_in_transaction(&self) -> bool { + true + } +} + +/// A migration source is an entity that can be used +/// to receive a number of migrations from. +pub trait MigrationSource { + /// Get a list of migrations associated with this + /// migration source. + fn migrations(&self) -> Result>>>; +} + +impl Migration for Box + '_> { + fn run(&self, conn: &mut dyn BoxableConnection) -> Result<()> { + (**self).run(conn) + } + + fn revert(&self, conn: &mut dyn BoxableConnection) -> Result<()> { + (**self).revert(conn) + } + + fn metadata(&self) -> &dyn MigrationMetadata { + (**self).metadata() + } + + fn name(&self) -> &dyn MigrationName { + (**self).name() + } +} + +impl Migration for &dyn Migration { + fn run(&self, conn: &mut dyn BoxableConnection) -> Result<()> { + (**self).run(conn) + } + + fn revert(&self, conn: &mut dyn BoxableConnection) -> Result<()> { + (**self).revert(conn) + } + + fn metadata(&self) -> &dyn MigrationMetadata { + (**self).metadata() + } + + fn name(&self) -> &dyn MigrationName { + (**self).name() + } +} + +/// Create table statement for the `__diesel_schema_migrations` used +/// used by the postgresql, sqlite and mysql backend +pub const CREATE_MIGRATIONS_TABLE: &str = include_str!("setup_migration_table.sql"); + +/// A trait indicating that a connection could be used to manage migrations +/// +/// Only custom backend implementations need to think about this trait +pub trait MigrationConnection: Connection { + /// Setup the following table: + /// + /// ```rust + /// diesel::table! { + /// __diesel_schema_migrations(version) { + /// version -> Text, + /// /// defaults to `CURRENT_TIMESTAMP` + /// run_on -> Timestamp, + /// } + /// } + /// ``` + fn setup(&mut self) -> QueryResult; +} + +#[cfg(feature = "postgres")] +impl MigrationConnection for crate::pg::PgConnection { + fn setup(&mut self) -> QueryResult { + use crate::RunQueryDsl; + crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self) + } +} + +#[cfg(feature = "mysql")] +impl MigrationConnection for crate::mysql::MysqlConnection { + fn setup(&mut self) -> QueryResult { + use crate::RunQueryDsl; + crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self) + } +} + +#[cfg(feature = "sqlite")] +impl MigrationConnection for crate::sqlite::SqliteConnection { + fn setup(&mut self) -> QueryResult { + use crate::RunQueryDsl; + crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/migration/setup_migration_table.sql b/collector/compile-benchmarks/diesel-2.2.10/src/migration/setup_migration_table.sql new file mode 100644 index 000000000..6e2594703 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/migration/setup_migration_table.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS __diesel_schema_migrations ( + version VARCHAR(50) PRIMARY KEY NOT NULL, + run_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/backend.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/backend.rs new file mode 100644 index 000000000..290935d8e --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/backend.rs @@ -0,0 +1,106 @@ +//! The MySQL backend + +use super::query_builder::MysqlQueryBuilder; +use super::MysqlValue; +use crate::backend::sql_dialect::on_conflict_clause::SupportsOnConflictClause; +use crate::backend::*; +use crate::query_builder::bind_collector::RawBytesBindCollector; +use crate::sql_types::TypeMetadata; + +/// The MySQL backend +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)] +pub struct Mysql; + +#[allow(missing_debug_implementations)] +/// Represents possible types, that can be transmitted as via the +/// Mysql wire protocol +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum MysqlType { + /// A 8 bit signed integer + Tiny, + /// A 8 bit unsigned integer + UnsignedTiny, + /// A 16 bit signed integer + Short, + /// A 16 bit unsigned integer + UnsignedShort, + /// A 32 bit signed integer + Long, + /// A 32 bit unsigned integer + UnsignedLong, + /// A 64 bit signed integer + LongLong, + /// A 64 bit unsigned integer + UnsignedLongLong, + /// A 32 bit floating point number + Float, + /// A 64 bit floating point number + Double, + /// A fixed point decimal value + Numeric, + /// A datatype to store a time value + Time, + /// A datatype to store a date value + Date, + /// A datatype containing timestamp values ranging from + /// '1000-01-01 00:00:00' to '9999-12-31 23:59:59'. + DateTime, + /// A datatype containing timestamp values ranging from + /// 1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC. + Timestamp, + /// A datatype for string values + String, + /// A datatype containing binary large objects + Blob, + /// A value containing a set of bit's + Bit, + /// A user defined set type + Set, + /// A user defined enum type + Enum, +} + +impl Backend for Mysql { + type QueryBuilder = MysqlQueryBuilder; + type RawValue<'a> = MysqlValue<'a>; + type BindCollector<'a> = RawBytesBindCollector; +} + +impl TypeMetadata for Mysql { + type TypeMetadata = MysqlType; + type MetadataLookup = (); +} + +impl SqlDialect for Mysql { + type ReturningClause = sql_dialect::returning_clause::DoesNotSupportReturningClause; + + type OnConflictClause = MysqlOnConflictClause; + + type InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword; + type BatchInsertSupport = sql_dialect::batch_insert_support::PostgresLikeBatchInsertSupport; + type DefaultValueClauseForInsert = MysqlStyleDefaultValueClause; + + type EmptyFromClauseSyntax = sql_dialect::from_clause_syntax::AnsiSqlFromClauseSyntax; + type SelectStatementSyntax = sql_dialect::select_statement_syntax::AnsiSqlSelectStatement; + + type ExistsSyntax = sql_dialect::exists_syntax::AnsiSqlExistsSyntax; + type ArrayComparison = sql_dialect::array_comparison::AnsiSqlArrayComparison; + + type ConcatClause = MysqlConcatClause; + type AliasSyntax = sql_dialect::alias_syntax::AsAliasSyntax; +} + +impl DieselReserveSpecialization for Mysql {} +impl TrustedBackend for Mysql {} + +#[derive(Debug, Clone, Copy)] +pub struct MysqlStyleDefaultValueClause; + +#[derive(Debug, Clone, Copy)] +pub struct MysqlConcatClause; + +#[derive(Debug, Clone, Copy)] +pub struct MysqlOnConflictClause; + +impl SupportsOnConflictClause for MysqlOnConflictClause {} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/bind.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/bind.rs new file mode 100644 index 000000000..0bbceede6 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/bind.rs @@ -0,0 +1,1817 @@ +#![allow(unsafe_code)] // module uses ffi +use mysqlclient_sys as ffi; +use std::mem; +use std::mem::MaybeUninit; +use std::ops::Index; +use std::os::raw as libc; +use std::ptr::NonNull; + +use super::stmt::MysqlFieldMetadata; +use super::stmt::StatementUse; +use crate::mysql::connection::stmt::StatementMetadata; +use crate::mysql::types::date_and_time::MysqlTime; +use crate::mysql::{MysqlType, MysqlValue}; +use crate::result::QueryResult; + +pub(super) struct PreparedStatementBinds(Binds); + +pub(super) struct OutputBinds(Binds); + +impl Clone for OutputBinds { + fn clone(&self) -> Self { + Self(Binds { + data: self.0.data.clone(), + }) + } +} + +struct Binds { + data: Vec, +} + +impl PreparedStatementBinds { + pub(super) fn from_input_data(input: Iter) -> Self + where + Iter: IntoIterator>)>, + { + let data = input + .into_iter() + .map(BindData::for_input) + .collect::>(); + + Self(Binds { data }) + } + + pub(super) fn with_mysql_binds(&mut self, f: F) -> T + where + F: FnOnce(*mut ffi::MYSQL_BIND) -> T, + { + self.0.with_mysql_binds(f) + } +} + +impl OutputBinds { + pub(super) fn from_output_types( + types: &[Option], + metadata: &StatementMetadata, + ) -> Self { + let data = metadata + .fields() + .iter() + .zip(types.iter().copied().chain(std::iter::repeat(None))) + .map(|(field, tpe)| BindData::for_output(tpe, field)) + .collect(); + + Self(Binds { data }) + } + + pub(super) fn populate_dynamic_buffers(&mut self, stmt: &StatementUse<'_>) -> QueryResult<()> { + for (i, data) in self.0.data.iter_mut().enumerate() { + data.did_numeric_overflow_occur()?; + // This is safe because we are re-binding the invalidated buffers + // at the end of this function + unsafe { + if let Some((mut bind, offset)) = data.bind_for_truncated_data() { + stmt.fetch_column(&mut bind, i, offset)? + } else { + data.update_buffer_length() + } + } + } + + unsafe { self.with_mysql_binds(|bind_ptr| stmt.bind_result(bind_ptr)) } + } + + pub(super) fn update_buffer_lengths(&mut self) { + for data in &mut self.0.data { + data.update_buffer_length(); + } + } + + pub(super) fn with_mysql_binds(&mut self, f: F) -> T + where + F: FnOnce(*mut ffi::MYSQL_BIND) -> T, + { + self.0.with_mysql_binds(f) + } +} + +impl Binds { + fn with_mysql_binds(&mut self, f: F) -> T + where + F: FnOnce(*mut ffi::MYSQL_BIND) -> T, + { + let mut binds = self + .data + .iter_mut() + .map(|x| unsafe { x.mysql_bind() }) + .collect::>(); + f(binds.as_mut_ptr()) + } +} + +impl Index for OutputBinds { + type Output = BindData; + fn index(&self, index: usize) -> &Self::Output { + &self.0.data[index] + } +} + +bitflags::bitflags! { + #[derive(Clone, Copy, Debug)] + pub(crate) struct Flags: u32 { + const NOT_NULL_FLAG = 1; + const PRI_KEY_FLAG = 2; + const UNIQUE_KEY_FLAG = 4; + const MULTIPLE_KEY_FLAG = 8; + const BLOB_FLAG = 16; + const UNSIGNED_FLAG = 32; + const ZEROFILL_FLAG = 64; + const BINARY_FLAG = 128; + const ENUM_FLAG = 256; + const AUTO_INCREMENT_FLAG = 512; + const TIMESTAMP_FLAG = 1024; + const SET_FLAG = 2048; + const NO_DEFAULT_VALUE_FLAG = 4096; + const ON_UPDATE_NOW_FLAG = 8192; + const NUM_FLAG = 32768; + const PART_KEY_FLAG = 16384; + const GROUP_FLAG = 32768; + const UNIQUE_FLAG = 65536; + const BINCMP_FLAG = 130_172; + const GET_FIXED_FIELDS_FLAG = (1<<18); + const FIELD_IN_PART_FUNC_FLAG = (1 << 19); + } +} + +impl From for Flags { + fn from(flags: u32) -> Self { + Flags::from_bits(flags).expect( + "We encountered an unknown type flag while parsing \ + Mysql's type information. If you see this error message \ + please open an issue at diesels github page.", + ) + } +} + +#[derive(Debug)] +pub(super) struct BindData { + tpe: ffi::enum_field_types, + bytes: Option>, + length: libc::c_ulong, + capacity: usize, + flags: Flags, + is_null: ffi::my_bool, + is_truncated: Option, +} + +// We need to write a manual clone impl +// as we need to clone the underlying buffer +// instead of just copying the pointer +impl Clone for BindData { + fn clone(&self) -> Self { + let (ptr, len, capacity) = if let Some(ptr) = self.bytes { + let slice = unsafe { + // We know that this points to a slice and the pointer is not null at this + // location + // The length pointer is valid as long as none missuses `bind_for_truncated_data` + // as this is the only location that updates the length field before the corresponding data are + // written. At the time of writing this comment, the `BindData::bind_for_truncated_data` + // function is only called by `Binds::populate_dynamic_buffers` which ensures the corresponding + // invariant. + std::slice::from_raw_parts( + ptr.as_ptr(), + self.length.try_into().expect("usize is at least 32bit"), + ) + }; + let mut vec = slice.to_owned(); + let ptr = NonNull::new(vec.as_mut_ptr()); + let len = vec.len() as libc::c_ulong; + let capacity = vec.capacity(); + mem::forget(vec); + (ptr, len, capacity) + } else { + (None, 0, 0) + }; + Self { + tpe: self.tpe, + bytes: ptr, + length: len, + capacity, + flags: self.flags, + is_null: self.is_null, + is_truncated: self.is_truncated, + } + } +} + +impl Drop for BindData { + fn drop(&mut self) { + if let Some(bytes) = self.bytes { + std::mem::drop(unsafe { + // We know that this buffer was allocated by a vector, so constructing a vector from it is fine + // We know the correct capacity here + // We use 0 as length to prevent situations where the length is already updated but + // no date are already written as we could touch uninitialized memory otherwise + // Using 0 as length is fine as we don't need to call drop for `u8` + // (as there is no drop impl for primitive types) + Vec::from_raw_parts(bytes.as_ptr(), 0, self.capacity) + }); + self.bytes = None; + } + } +} + +impl BindData { + fn for_input((tpe, data): (MysqlType, Option>)) -> Self { + let (tpe, flags) = tpe.into(); + let is_null = ffi::my_bool::from(data.is_none()); + let mut bytes = data.unwrap_or_default(); + let ptr = NonNull::new(bytes.as_mut_ptr()); + let len = bytes.len() as libc::c_ulong; + let capacity = bytes.capacity(); + mem::forget(bytes); + Self { + tpe, + bytes: ptr, + length: len, + capacity, + flags, + is_null, + is_truncated: None, + } + } + + fn for_output(tpe: Option, metadata: &MysqlFieldMetadata<'_>) -> Self { + let (tpe, flags) = if let Some(tpe) = tpe { + match (tpe, metadata.field_type()) { + // Those are types where we handle the conversion in diesel itself + // and do not relay on libmysqlclient + (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_DECIMAL) + | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_TINY) + | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_SHORT) + | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_LONG) + | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_FLOAT) + | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_DOUBLE) + | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_INT24) + | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL) + | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_LONGLONG) + | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_DECIMAL) + | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_TINY) + | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_SHORT) + | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_LONG) + | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_FLOAT) + | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_DOUBLE) + | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_INT24) + | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL) + | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_LONGLONG) + | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_DECIMAL) + | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_TINY) + | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_SHORT) + | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_LONG) + | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_FLOAT) + | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_DOUBLE) + | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_INT24) + | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL) + | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_LONGLONG) + | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_DECIMAL) + | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_TINY) + | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_SHORT) + | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_LONG) + | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_FLOAT) + | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_DOUBLE) + | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_INT24) + | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL) + | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_LONGLONG) + | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_DECIMAL) + | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_TINY) + | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_SHORT) + | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_LONG) + | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_FLOAT) + | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_DOUBLE) + | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_INT24) + | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL) + | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_LONGLONG) + | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_DECIMAL) + | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_TINY) + | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_SHORT) + | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_LONG) + | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_FLOAT) + | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_DOUBLE) + | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_INT24) + | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL) + | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_LONGLONG) + | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_DECIMAL) + | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_TINY) + | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_SHORT) + | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_LONG) + | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_FLOAT) + | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_DOUBLE) + | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_INT24) + | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL) + | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_LONGLONG) + | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_DECIMAL) + | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_TINY) + | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_SHORT) + | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_LONG) + | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_FLOAT) + | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_DOUBLE) + | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_INT24) + | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL) + | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_LONGLONG) + | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_DECIMAL) + | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_TINY) + | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_SHORT) + | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_LONG) + | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_FLOAT) + | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_DOUBLE) + | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_INT24) + | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL) + | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_LONGLONG) + | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_DECIMAL) + | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_TINY) + | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_SHORT) + | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_LONG) + | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_FLOAT) + | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_DOUBLE) + | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_INT24) + | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL) + | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_LONGLONG) + | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_JSON) + | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_ENUM) + | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_SET) + | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB) + | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB) + | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB) + | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_BLOB) + | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_VAR_STRING) + | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_STRING) + | (MysqlType::Blob, ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB) + | (MysqlType::Blob, ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB) + | (MysqlType::Blob, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB) + | (MysqlType::Blob, ffi::enum_field_types::MYSQL_TYPE_BLOB) + | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_ENUM) + | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_SET) + | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB) + | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB) + | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB) + | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_BLOB) + | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_VAR_STRING) + | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_STRING) + | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_ENUM) + | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_SET) + | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB) + | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB) + | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB) + | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_BLOB) + | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_VAR_STRING) + | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_STRING) => { + (metadata.field_type(), metadata.flags()) + } + + (tpe, _) => tpe.into(), + } + } else { + (metadata.field_type(), metadata.flags()) + }; + Self::from_tpe_and_flags((tpe, flags)) + } + + fn from_tpe_and_flags((tpe, flags): (ffi::enum_field_types, Flags)) -> Self { + // newer mysqlclient versions do not accept a zero sized buffer + let len = known_buffer_size_for_ffi_type(tpe).unwrap_or(1); + let mut bytes = vec![0; len]; + let length = bytes.len() as libc::c_ulong; + let capacity = bytes.capacity(); + let ptr = NonNull::new(bytes.as_mut_ptr()); + mem::forget(bytes); + + Self { + tpe, + bytes: ptr, + length, + capacity, + flags, + is_null: super::raw::ffi_false(), + is_truncated: Some(super::raw::ffi_false()), + } + } + + fn is_truncated(&self) -> bool { + self.is_truncated.unwrap_or(super::raw::ffi_false()) != super::raw::ffi_false() + } + + fn is_fixed_size_buffer(&self) -> bool { + known_buffer_size_for_ffi_type(self.tpe).is_some() + } + + pub(super) fn value(&'_ self) -> Option> { + if self.is_null() { + None + } else { + let data = self.bytes?; + let tpe = (self.tpe, self.flags).into(); + let slice = unsafe { + // We know that this points to a slice and the pointer is not null at this + // location + // The length pointer is valid as long as none missuses `bind_for_truncated_data` + // as this is the only location that updates the length field before the corresponding data are + // written. At the time of writing this comment, the `BindData::bind_for_truncated_data` + // function is only called by `Binds::populate_dynamic_buffers` which ensures the corresponding + // invariant. + std::slice::from_raw_parts( + data.as_ptr(), + self.length.try_into().expect("Usize is at least 32 bit"), + ) + }; + Some(MysqlValue::new_internal(slice, tpe)) + } + } + + pub(super) fn is_null(&self) -> bool { + self.is_null != ffi::my_bool::default() + } + + fn update_buffer_length(&mut self) { + use std::cmp::min; + + let actual_bytes_in_buffer = min( + self.capacity, + self.length.try_into().expect("Usize is at least 32 bit"), + ); + self.length = actual_bytes_in_buffer as libc::c_ulong; + } + + // This function is marked as unsafe as it returns an owned value + // containing a pointer with a lifetime coupled to self. + // Callers need to ensure that the returned value cannot outlive `self` + unsafe fn mysql_bind(&mut self) -> ffi::MYSQL_BIND { + use std::ptr::addr_of_mut; + + let mut bind: MaybeUninit = mem::MaybeUninit::zeroed(); + let ptr = bind.as_mut_ptr(); + + addr_of_mut!((*ptr).buffer_type).write(self.tpe); + addr_of_mut!((*ptr).buffer).write( + self.bytes + .map(|p| p.as_ptr()) + .unwrap_or(std::ptr::null_mut()) as *mut libc::c_void, + ); + addr_of_mut!((*ptr).buffer_length).write(self.capacity as libc::c_ulong); + addr_of_mut!((*ptr).length).write(&mut self.length); + addr_of_mut!((*ptr).is_null).write(&mut self.is_null); + addr_of_mut!((*ptr).is_unsigned) + .write(self.flags.contains(Flags::UNSIGNED_FLAG) as ffi::my_bool); + + if let Some(ref mut is_truncated) = self.is_truncated { + addr_of_mut!((*ptr).error).write(is_truncated); + } + + // That's what the mysqlclient examples are doing + bind.assume_init() + } + + /// Resizes the byte buffer to fit the value of `self.length`, and returns + /// a tuple of a bind pointing at the truncated data, and the offset to use + /// in order to read the truncated data into it. + /// + /// This invalidates the bind previously returned by `mysql_bind`. Calling + /// this function is unsafe unless the binds are immediately rebound. + unsafe fn bind_for_truncated_data(&mut self) -> Option<(ffi::MYSQL_BIND, usize)> { + if self.is_truncated() { + if let Some(bytes) = self.bytes { + let mut bytes = Vec::from_raw_parts(bytes.as_ptr(), self.capacity, self.capacity); + self.bytes = None; + + let offset = self.capacity; + let truncated_amount = + usize::try_from(self.length).expect("Usize is at least 32 bit") - offset; + + debug_assert!( + truncated_amount > 0, + "output buffers were invalidated \ + without calling `mysql_stmt_bind_result`" + ); + + // reserve space for any missing byte + // we know the exact size here + bytes.reserve(truncated_amount); + self.capacity = bytes.capacity(); + self.bytes = NonNull::new(bytes.as_mut_ptr()); + mem::forget(bytes); + + let mut bind = self.mysql_bind(); + + if let Some(ptr) = self.bytes { + // Using offset is safe here as we have a u8 array (where std::mem::size_of:: == 1) + // and we have a buffer that has at least + bind.buffer = ptr.as_ptr().add(offset) as *mut libc::c_void; + bind.buffer_length = truncated_amount as libc::c_ulong; + } else { + bind.buffer_length = 0; + } + Some((bind, offset)) + } else { + // offset is zero here as we don't have a buffer yet + // we know the requested length here so we can just request + // the correct size + let mut vec = vec![0_u8; self.length.try_into().expect("usize is at least 32 bit")]; + self.capacity = vec.capacity(); + self.bytes = NonNull::new(vec.as_mut_ptr()); + mem::forget(vec); + + let bind = self.mysql_bind(); + // As we did not have a buffer before + // we couldn't have loaded any data yet, therefore + // request everything + Some((bind, 0)) + } + } else { + None + } + } + + fn did_numeric_overflow_occur(&self) -> QueryResult<()> { + use crate::result::Error::DeserializationError; + + if self.is_truncated() && self.is_fixed_size_buffer() { + Err(DeserializationError( + "Numeric overflow/underflow occurred".into(), + )) + } else { + Ok(()) + } + } +} + +impl From for (ffi::enum_field_types, Flags) { + fn from(tpe: MysqlType) -> Self { + use self::ffi::enum_field_types; + let mut flags = Flags::empty(); + let tpe = match tpe { + MysqlType::Tiny => enum_field_types::MYSQL_TYPE_TINY, + MysqlType::Short => enum_field_types::MYSQL_TYPE_SHORT, + MysqlType::Long => enum_field_types::MYSQL_TYPE_LONG, + MysqlType::LongLong => enum_field_types::MYSQL_TYPE_LONGLONG, + MysqlType::Float => enum_field_types::MYSQL_TYPE_FLOAT, + MysqlType::Double => enum_field_types::MYSQL_TYPE_DOUBLE, + MysqlType::Time => enum_field_types::MYSQL_TYPE_TIME, + MysqlType::Date => enum_field_types::MYSQL_TYPE_DATE, + MysqlType::DateTime => enum_field_types::MYSQL_TYPE_DATETIME, + MysqlType::Timestamp => enum_field_types::MYSQL_TYPE_TIMESTAMP, + MysqlType::String => enum_field_types::MYSQL_TYPE_STRING, + MysqlType::Blob => enum_field_types::MYSQL_TYPE_BLOB, + MysqlType::Numeric => enum_field_types::MYSQL_TYPE_NEWDECIMAL, + MysqlType::Bit => enum_field_types::MYSQL_TYPE_BIT, + MysqlType::UnsignedTiny => { + flags = Flags::UNSIGNED_FLAG; + enum_field_types::MYSQL_TYPE_TINY + } + MysqlType::UnsignedShort => { + flags = Flags::UNSIGNED_FLAG; + enum_field_types::MYSQL_TYPE_SHORT + } + MysqlType::UnsignedLong => { + flags = Flags::UNSIGNED_FLAG; + enum_field_types::MYSQL_TYPE_LONG + } + MysqlType::UnsignedLongLong => { + flags = Flags::UNSIGNED_FLAG; + enum_field_types::MYSQL_TYPE_LONGLONG + } + MysqlType::Set => { + flags = Flags::SET_FLAG; + enum_field_types::MYSQL_TYPE_STRING + } + MysqlType::Enum => { + flags = Flags::ENUM_FLAG; + enum_field_types::MYSQL_TYPE_STRING + } + }; + (tpe, flags) + } +} + +impl From<(ffi::enum_field_types, Flags)> for MysqlType { + fn from((tpe, flags): (ffi::enum_field_types, Flags)) -> Self { + use self::ffi::enum_field_types; + + let is_unsigned = flags.contains(Flags::UNSIGNED_FLAG); + + // https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/c-api-data-structures.html + // https://dev.mysql.com/doc/dev/mysql-server/8.0.12/binary__log__types_8h.html + // https://dev.mysql.com/doc/internals/en/binary-protocol-value.html + // https://mariadb.com/kb/en/packet_bindata/ + match tpe { + enum_field_types::MYSQL_TYPE_TINY if is_unsigned => MysqlType::UnsignedTiny, + enum_field_types::MYSQL_TYPE_YEAR | enum_field_types::MYSQL_TYPE_SHORT + if is_unsigned => + { + MysqlType::UnsignedShort + } + enum_field_types::MYSQL_TYPE_INT24 | enum_field_types::MYSQL_TYPE_LONG + if is_unsigned => + { + MysqlType::UnsignedLong + } + enum_field_types::MYSQL_TYPE_LONGLONG if is_unsigned => MysqlType::UnsignedLongLong, + enum_field_types::MYSQL_TYPE_TINY => MysqlType::Tiny, + enum_field_types::MYSQL_TYPE_SHORT => MysqlType::Short, + enum_field_types::MYSQL_TYPE_INT24 | enum_field_types::MYSQL_TYPE_LONG => { + MysqlType::Long + } + enum_field_types::MYSQL_TYPE_LONGLONG => MysqlType::LongLong, + enum_field_types::MYSQL_TYPE_FLOAT => MysqlType::Float, + enum_field_types::MYSQL_TYPE_DOUBLE => MysqlType::Double, + enum_field_types::MYSQL_TYPE_DECIMAL | enum_field_types::MYSQL_TYPE_NEWDECIMAL => { + MysqlType::Numeric + } + enum_field_types::MYSQL_TYPE_BIT => MysqlType::Bit, + + enum_field_types::MYSQL_TYPE_TIME => MysqlType::Time, + enum_field_types::MYSQL_TYPE_DATE => MysqlType::Date, + enum_field_types::MYSQL_TYPE_DATETIME => MysqlType::DateTime, + enum_field_types::MYSQL_TYPE_TIMESTAMP => MysqlType::Timestamp, + // Treat json as string because even mysql 8.0 + // throws errors sometimes if we use json for json + enum_field_types::MYSQL_TYPE_JSON => MysqlType::String, + + // The documentation states that + // MYSQL_TYPE_STRING is used for enums and sets + // but experimentation has shown that + // just any string like type works, so + // better be safe here + enum_field_types::MYSQL_TYPE_BLOB + | enum_field_types::MYSQL_TYPE_TINY_BLOB + | enum_field_types::MYSQL_TYPE_MEDIUM_BLOB + | enum_field_types::MYSQL_TYPE_LONG_BLOB + | enum_field_types::MYSQL_TYPE_VAR_STRING + | enum_field_types::MYSQL_TYPE_STRING + if flags.contains(Flags::ENUM_FLAG) => + { + MysqlType::Enum + } + enum_field_types::MYSQL_TYPE_BLOB + | enum_field_types::MYSQL_TYPE_TINY_BLOB + | enum_field_types::MYSQL_TYPE_MEDIUM_BLOB + | enum_field_types::MYSQL_TYPE_LONG_BLOB + | enum_field_types::MYSQL_TYPE_VAR_STRING + | enum_field_types::MYSQL_TYPE_STRING + if flags.contains(Flags::SET_FLAG) => + { + MysqlType::Set + } + + // "blobs" may contain binary data + // also "strings" can contain binary data + // but all only if the binary flag is set + // (see the check_all_the_types test case) + enum_field_types::MYSQL_TYPE_BLOB + | enum_field_types::MYSQL_TYPE_TINY_BLOB + | enum_field_types::MYSQL_TYPE_MEDIUM_BLOB + | enum_field_types::MYSQL_TYPE_LONG_BLOB + | enum_field_types::MYSQL_TYPE_VAR_STRING + | enum_field_types::MYSQL_TYPE_STRING + if flags.contains(Flags::BINARY_FLAG) => + { + MysqlType::Blob + } + + // If the binary flag is not set consider everything as string + enum_field_types::MYSQL_TYPE_BLOB + | enum_field_types::MYSQL_TYPE_TINY_BLOB + | enum_field_types::MYSQL_TYPE_MEDIUM_BLOB + | enum_field_types::MYSQL_TYPE_LONG_BLOB + | enum_field_types::MYSQL_TYPE_VAR_STRING + | enum_field_types::MYSQL_TYPE_STRING => MysqlType::String, + + // unsigned seems to be set for year in any case + enum_field_types::MYSQL_TYPE_YEAR => unreachable!( + "The year type should have set the unsigned flag. If you ever \ + see this error message, something has gone very wrong. Please \ + open an issue at the diesel github repo in this case" + ), + // Null value + enum_field_types::MYSQL_TYPE_NULL => unreachable!( + "We ensure at the call side that we do not hit this type here. \ + If you ever see this error, something has gone very wrong. \ + Please open an issue at the diesel github repo in this case" + ), + // Those exist in libmysqlclient + // but are just not supported + // + enum_field_types::MYSQL_TYPE_VARCHAR + | enum_field_types::MYSQL_TYPE_ENUM + | enum_field_types::MYSQL_TYPE_SET + | enum_field_types::MYSQL_TYPE_GEOMETRY => { + unimplemented!( + "Hit a type that should be unsupported in libmysqlclient. If \ + you ever see this error, they probably have added support for \ + one of those types. Please open an issue at the diesel github \ + repo in this case." + ) + } + + enum_field_types::MYSQL_TYPE_NEWDATE + | enum_field_types::MYSQL_TYPE_TIME2 + | enum_field_types::MYSQL_TYPE_DATETIME2 + | enum_field_types::MYSQL_TYPE_TIMESTAMP2 => unreachable!( + "The mysql documentation states that these types are \ + only used on the server side, so if you see this error \ + something has gone wrong. Please open an issue at \ + the diesel github repo." + ), + // depending on the bindings version + // there might be no unlisted field type + #[allow(unreachable_patterns)] + t => unreachable!( + "Unsupported type encountered: {t:?}. \ + If you ever see this error, something has gone wrong. \ + Please open an issue at the diesel github \ + repo in this case." + ), + } + } +} + +fn known_buffer_size_for_ffi_type(tpe: ffi::enum_field_types) -> Option { + use self::ffi::enum_field_types as t; + use std::mem::size_of; + + match tpe { + t::MYSQL_TYPE_TINY => Some(1), + t::MYSQL_TYPE_YEAR | t::MYSQL_TYPE_SHORT => Some(2), + t::MYSQL_TYPE_INT24 | t::MYSQL_TYPE_LONG | t::MYSQL_TYPE_FLOAT => Some(4), + t::MYSQL_TYPE_LONGLONG | t::MYSQL_TYPE_DOUBLE => Some(8), + t::MYSQL_TYPE_TIME + | t::MYSQL_TYPE_DATE + | t::MYSQL_TYPE_DATETIME + | t::MYSQL_TYPE_TIMESTAMP => Some(size_of::()), + _ => None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::connection::statement_cache::MaybeCached; + use crate::deserialize::FromSql; + use crate::mysql::connection::stmt::Statement; + use crate::prelude::*; + use crate::sql_types::*; + #[cfg(feature = "numeric")] + use std::str::FromStr; + + fn to_value( + bind: &BindData, + ) -> Result> + where + T: FromSql + std::fmt::Debug, + { + let meta = (bind.tpe, bind.flags).into(); + dbg!(meta); + + let value = bind.value().expect("Is not null"); + let value = MysqlValue::new_internal(value.as_bytes(), meta); + + dbg!(T::from_sql(value)) + } + + #[cfg(feature = "extras")] + #[test] + fn check_all_the_types() { + let conn = &mut crate::test_helpers::connection(); + + crate::sql_query("DROP TABLE IF EXISTS all_mysql_types CASCADE") + .execute(conn) + .unwrap(); + crate::sql_query( + "CREATE TABLE all_mysql_types ( + tiny_int TINYINT NOT NULL, + small_int SMALLINT NOT NULL, + medium_int MEDIUMINT NOT NULL, + int_col INTEGER NOT NULL, + big_int BIGINT NOT NULL, + unsigned_int INTEGER UNSIGNED NOT NULL, + zero_fill_int INTEGER ZEROFILL NOT NULL, + numeric_col NUMERIC(20,5) NOT NULL, + decimal_col DECIMAL(20,5) NOT NULL, + float_col FLOAT NOT NULL, + double_col DOUBLE NOT NULL, + bit_col BIT(8) NOT NULL, + date_col DATE NOT NULL, + date_time DATETIME NOT NULL, + timestamp_col TIMESTAMP NOT NULL, + time_col TIME NOT NULL, + year_col YEAR NOT NULL, + char_col CHAR(30) NOT NULL, + varchar_col VARCHAR(30) NOT NULL, + binary_col BINARY(30) NOT NULL, + varbinary_col VARBINARY(30) NOT NULL, + blob_col BLOB NOT NULL, + text_col TEXT NOT NULL, + enum_col ENUM('red', 'green', 'blue') NOT NULL, + set_col SET('one', 'two') NOT NULL, + geom GEOMETRY NOT NULL, + point_col POINT NOT NULL, + linestring_col LINESTRING NOT NULL, + polygon_col POLYGON NOT NULL, + multipoint_col MULTIPOINT NOT NULL, + multilinestring_col MULTILINESTRING NOT NULL, + multipolygon_col MULTIPOLYGON NOT NULL, + geometry_collection GEOMETRYCOLLECTION NOT NULL, + json_col JSON NOT NULL + )", + ) + .execute(conn) + .unwrap(); + crate::sql_query( + "INSERT INTO all_mysql_types VALUES ( + 0, -- tiny_int + 1, -- small_int + 2, -- medium_int + 3, -- int_col + -5, -- big_int + 42, -- unsigned_int + 1, -- zero_fill_int + -999.999, -- numeric_col, + 3.14, -- decimal_col, + 1.23, -- float_col + 4.5678, -- double_col + b'10101010', -- bit_col + '1000-01-01', -- date_col + '9999-12-31 12:34:45.012345', -- date_time + '2020-01-01 10:10:10', -- timestamp_col + '23:01:01', -- time_col + 2020, -- year_col + 'abc', -- char_col + 'foo', -- varchar_col + 'a ', -- binary_col + 'a ', -- varbinary_col + 'binary', -- blob_col + 'some text whatever', -- text_col + 'red', -- enum_col + 'one', -- set_col + ST_GeomFromText('POINT(1 1)'), -- geom + ST_PointFromText('POINT(1 1)'), -- point_col + ST_LineStringFromText('LINESTRING(0 0,1 1,2 2)'), -- linestring_col + ST_PolygonFromText('POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7, 5 5))'), -- polygon_col + ST_MultiPointFromText('MULTIPOINT(0 0,10 10,10 20,20 20)'), -- multipoint_col + ST_MultiLineStringFromText('MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48))'), -- multilinestring_col + ST_MultiPolygonFromText('MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))'), -- multipolygon_col + ST_GeomCollFromText('GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(0 0,1 1,2 2,3 3,4 4))'), -- geometry_collection + '{\"key1\": \"value1\", \"key2\": \"value2\"}' -- json_col +)", + ).execute(conn) + .unwrap(); + + let stmt = crate::mysql::connection::prepared_query( + &crate::sql_query( + "SELECT + tiny_int, small_int, medium_int, int_col, + big_int, unsigned_int, zero_fill_int, + numeric_col, decimal_col, float_col, double_col, bit_col, + date_col, date_time, timestamp_col, time_col, year_col, + char_col, varchar_col, binary_col, varbinary_col, blob_col, + text_col, enum_col, set_col, ST_AsText(geom), ST_AsText(point_col), ST_AsText(linestring_col), + ST_AsText(polygon_col), ST_AsText(multipoint_col), ST_AsText(multilinestring_col), + ST_AsText(multipolygon_col), ST_AsText(geometry_collection), json_col + FROM all_mysql_types", + ), + &mut conn.statement_cache, + &mut conn.raw_connection, + &mut conn.instrumentation, + ).unwrap(); + + let metadata = stmt.metadata().unwrap(); + let mut output_binds = + OutputBinds::from_output_types(&vec![None; metadata.fields().len()], &metadata); + let stmt = stmt.execute_statement(&mut output_binds).unwrap(); + stmt.populate_row_buffers(&mut output_binds).unwrap(); + + let results: Vec<(BindData, &_)> = output_binds + .0 + .data + .into_iter() + .zip(metadata.fields()) + .collect::>(); + + let tiny_int_col = &results[0].0; + assert_eq!(tiny_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_TINY); + assert!(tiny_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!tiny_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(tiny_int_col), Ok(0))); + + let small_int_col = &results[1].0; + assert_eq!(small_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_SHORT); + assert!(small_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!small_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(small_int_col), Ok(1))); + + let medium_int_col = &results[2].0; + assert_eq!(medium_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_INT24); + assert!(medium_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!medium_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(medium_int_col), Ok(2))); + + let int_col = &results[3].0; + assert_eq!(int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG); + assert!(int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(int_col), Ok(3))); + + let big_int_col = &results[4].0; + assert_eq!(big_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONGLONG); + assert!(big_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!big_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(big_int_col), Ok(-5))); + + let unsigned_int_col = &results[5].0; + assert_eq!(unsigned_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG); + assert!(unsigned_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(unsigned_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!( + to_value::, u32>(unsigned_int_col), + Ok(42) + )); + + let zero_fill_int_col = &results[6].0; + assert_eq!( + zero_fill_int_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG + ); + assert!(zero_fill_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(zero_fill_int_col.flags.contains(Flags::ZEROFILL_FLAG)); + assert!(matches!(to_value::(zero_fill_int_col), Ok(1))); + + let numeric_col = &results[7].0; + assert_eq!( + numeric_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL + ); + assert!(numeric_col.flags.contains(Flags::NUM_FLAG)); + assert!(!numeric_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert_eq!( + to_value::(numeric_col).unwrap(), + bigdecimal::BigDecimal::from_str("-999.99900").unwrap() + ); + + let decimal_col = &results[8].0; + assert_eq!( + decimal_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL + ); + assert!(decimal_col.flags.contains(Flags::NUM_FLAG)); + assert!(!decimal_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert_eq!( + to_value::(decimal_col).unwrap(), + bigdecimal::BigDecimal::from_str("3.14000").unwrap() + ); + + let float_col = &results[9].0; + assert_eq!(float_col.tpe, ffi::enum_field_types::MYSQL_TYPE_FLOAT); + assert!(float_col.flags.contains(Flags::NUM_FLAG)); + assert!(!float_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert_eq!(to_value::(float_col).unwrap(), 1.23); + + let double_col = &results[10].0; + assert_eq!(double_col.tpe, ffi::enum_field_types::MYSQL_TYPE_DOUBLE); + assert!(double_col.flags.contains(Flags::NUM_FLAG)); + assert!(!double_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert_eq!(to_value::(double_col).unwrap(), 4.5678); + + let bit_col = &results[11].0; + assert_eq!(bit_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BIT); + assert!(!bit_col.flags.contains(Flags::NUM_FLAG)); + assert!(bit_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(!bit_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::>(bit_col).unwrap(), vec![170]); + + let date_col = &results[12].0; + assert_eq!(date_col.tpe, ffi::enum_field_types::MYSQL_TYPE_DATE); + assert!(!date_col.flags.contains(Flags::NUM_FLAG)); + assert_eq!( + to_value::(date_col).unwrap(), + chrono::NaiveDate::from_ymd_opt(1000, 1, 1).unwrap(), + ); + + let date_time_col = &results[13].0; + assert_eq!( + date_time_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_DATETIME + ); + assert!(!date_time_col.flags.contains(Flags::NUM_FLAG)); + assert_eq!( + to_value::(date_time_col).unwrap(), + chrono::NaiveDateTime::parse_from_str("9999-12-31 12:34:45", "%Y-%m-%d %H:%M:%S") + .unwrap() + ); + + let timestamp_col = &results[14].0; + assert_eq!( + timestamp_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_TIMESTAMP + ); + assert!(!timestamp_col.flags.contains(Flags::NUM_FLAG)); + assert_eq!( + to_value::(timestamp_col).unwrap(), + chrono::NaiveDateTime::parse_from_str("2020-01-01 10:10:10", "%Y-%m-%d %H:%M:%S") + .unwrap() + ); + + let time_col = &results[15].0; + assert_eq!(time_col.tpe, ffi::enum_field_types::MYSQL_TYPE_TIME); + assert!(!time_col.flags.contains(Flags::NUM_FLAG)); + assert_eq!( + to_value::(time_col).unwrap(), + chrono::NaiveTime::from_hms_opt(23, 01, 01).unwrap() + ); + + let year_col = &results[16].0; + assert_eq!(year_col.tpe, ffi::enum_field_types::MYSQL_TYPE_YEAR); + assert!(year_col.flags.contains(Flags::NUM_FLAG)); + assert!(year_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(year_col), Ok(2020))); + + let char_col = &results[17].0; + assert_eq!(char_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!char_col.flags.contains(Flags::NUM_FLAG)); + assert!(!char_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!char_col.flags.contains(Flags::SET_FLAG)); + assert!(!char_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!char_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(char_col).unwrap(), "abc"); + + let varchar_col = &results[18].0; + assert_eq!( + varchar_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_VAR_STRING + ); + assert!(!varchar_col.flags.contains(Flags::NUM_FLAG)); + assert!(!varchar_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!varchar_col.flags.contains(Flags::SET_FLAG)); + assert!(!varchar_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!varchar_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(varchar_col).unwrap(), "foo"); + + let binary_col = &results[19].0; + assert_eq!(binary_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!binary_col.flags.contains(Flags::NUM_FLAG)); + assert!(!binary_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!binary_col.flags.contains(Flags::SET_FLAG)); + assert!(!binary_col.flags.contains(Flags::ENUM_FLAG)); + assert!(binary_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::>(binary_col).unwrap(), + b"a \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + ); + + let varbinary_col = &results[20].0; + assert_eq!( + varbinary_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_VAR_STRING + ); + assert!(!varbinary_col.flags.contains(Flags::NUM_FLAG)); + assert!(!varbinary_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!varbinary_col.flags.contains(Flags::SET_FLAG)); + assert!(!varbinary_col.flags.contains(Flags::ENUM_FLAG)); + assert!(varbinary_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::>(varbinary_col).unwrap(), b"a "); + + let blob_col = &results[21].0; + assert_eq!(blob_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!blob_col.flags.contains(Flags::NUM_FLAG)); + assert!(blob_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!blob_col.flags.contains(Flags::SET_FLAG)); + assert!(!blob_col.flags.contains(Flags::ENUM_FLAG)); + assert!(blob_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::>(blob_col).unwrap(), b"binary"); + + let text_col = &results[22].0; + assert_eq!(text_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!text_col.flags.contains(Flags::NUM_FLAG)); + assert!(text_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!text_col.flags.contains(Flags::SET_FLAG)); + assert!(!text_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!text_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(text_col).unwrap(), + "some text whatever" + ); + + let enum_col = &results[23].0; + assert_eq!(enum_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!enum_col.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col.flags.contains(Flags::SET_FLAG)); + assert!(enum_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(enum_col).unwrap(), "red"); + + let set_col = &results[24].0; + assert_eq!(set_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!set_col.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col.flags.contains(Flags::SET_FLAG)); + assert!(!set_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(set_col).unwrap(), "one"); + + let geom = &results[25].0; + assert_eq!(geom.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB); + assert!(!geom.flags.contains(Flags::NUM_FLAG)); + assert!(!geom.flags.contains(Flags::BLOB_FLAG)); + assert!(!geom.flags.contains(Flags::SET_FLAG)); + assert!(!geom.flags.contains(Flags::ENUM_FLAG)); + assert!(!geom.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(geom).unwrap(), "POINT(1 1)"); + + let point_col = &results[26].0; + assert_eq!(point_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB); + assert!(!point_col.flags.contains(Flags::NUM_FLAG)); + assert!(!point_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!point_col.flags.contains(Flags::SET_FLAG)); + assert!(!point_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!point_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(point_col).unwrap(), "POINT(1 1)"); + + let linestring_col = &results[27].0; + assert_eq!( + linestring_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB + ); + assert!(!linestring_col.flags.contains(Flags::NUM_FLAG)); + assert!(!linestring_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!linestring_col.flags.contains(Flags::SET_FLAG)); + assert!(!linestring_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!linestring_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(linestring_col).unwrap(), + "LINESTRING(0 0,1 1,2 2)" + ); + + let polygon_col = &results[28].0; + assert_eq!(polygon_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB); + assert!(!polygon_col.flags.contains(Flags::NUM_FLAG)); + assert!(!polygon_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!polygon_col.flags.contains(Flags::SET_FLAG)); + assert!(!polygon_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!polygon_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(polygon_col).unwrap(), + "POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7,5 5))" + ); + + let multipoint_col = &results[29].0; + assert_eq!( + multipoint_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB + ); + assert!(!multipoint_col.flags.contains(Flags::NUM_FLAG)); + assert!(!multipoint_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!multipoint_col.flags.contains(Flags::SET_FLAG)); + assert!(!multipoint_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!multipoint_col.flags.contains(Flags::BINARY_FLAG)); + // older mysql and mariadb versions get back another encoding here + // we test for both as there seems to be no clear pattern when one or + // the other is returned + let multipoint_res = to_value::(multipoint_col).unwrap(); + assert!( + multipoint_res == "MULTIPOINT((0 0),(10 10),(10 20),(20 20))" + || multipoint_res == "MULTIPOINT(0 0,10 10,10 20,20 20)" + ); + + let multilinestring_col = &results[30].0; + assert_eq!( + multilinestring_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB + ); + assert!(!multilinestring_col.flags.contains(Flags::NUM_FLAG)); + assert!(!multilinestring_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!multilinestring_col.flags.contains(Flags::SET_FLAG)); + assert!(!multilinestring_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!multilinestring_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(multilinestring_col).unwrap(), + "MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48))" + ); + + let polygon_col = &results[31].0; + assert_eq!(polygon_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB); + assert!(!polygon_col.flags.contains(Flags::NUM_FLAG)); + assert!(!polygon_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!polygon_col.flags.contains(Flags::SET_FLAG)); + assert!(!polygon_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!polygon_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(polygon_col).unwrap(), + "MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))" + ); + + let geometry_collection = &results[32].0; + assert_eq!( + geometry_collection.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB + ); + assert!(!geometry_collection.flags.contains(Flags::NUM_FLAG)); + assert!(!geometry_collection.flags.contains(Flags::BLOB_FLAG)); + assert!(!geometry_collection.flags.contains(Flags::SET_FLAG)); + assert!(!geometry_collection.flags.contains(Flags::ENUM_FLAG)); + assert!(!geometry_collection.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(geometry_collection).unwrap(), + "GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(0 0,1 1,2 2,3 3,4 4))" + ); + + let json_col = &results[33].0; + // mariadb >= 10.2 and mysql >=8.0 are supporting a json type + // from those mariadb >= 10.3 and mysql >= 8.0 are reporting + // json here, so we assert that we get back json + // mariadb 10.5 returns again blob + assert!( + json_col.tpe == ffi::enum_field_types::MYSQL_TYPE_JSON + || json_col.tpe == ffi::enum_field_types::MYSQL_TYPE_BLOB + ); + assert!(!json_col.flags.contains(Flags::NUM_FLAG)); + assert!(json_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col.flags.contains(Flags::SET_FLAG)); + assert!(!json_col.flags.contains(Flags::ENUM_FLAG)); + assert!(json_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(json_col).unwrap(), + "{\"key1\": \"value1\", \"key2\": \"value2\"}" + ); + } + + fn query_single_table( + query: &'static str, + conn: &MysqlConnection, + bind_tpe: impl Into<(ffi::enum_field_types, Flags)>, + ) -> BindData { + let stmt: Statement = conn.raw_connection.prepare(query).unwrap(); + let stmt = MaybeCached::CannotCache(stmt); + + let bind = BindData::from_tpe_and_flags(bind_tpe.into()); + + let mut binds = OutputBinds(Binds { data: vec![bind] }); + + let stmt = stmt.execute_statement(&mut binds).unwrap(); + stmt.populate_row_buffers(&mut binds).unwrap(); + + binds.0.data.remove(0) + } + + fn input_bind( + query: &'static str, + conn: &MysqlConnection, + id: i32, + (mut field, tpe): (Vec, impl Into<(ffi::enum_field_types, Flags)>), + ) { + let mut stmt = conn.raw_connection.prepare(query).unwrap(); + let length = field.len() as _; + let (tpe, flags) = tpe.into(); + let capacity = field.capacity(); + let ptr = NonNull::new(field.as_mut_ptr()); + mem::forget(field); + + let field_bind = BindData { + tpe, + bytes: ptr, + capacity, + length, + flags, + is_null: ffi::FALSE, + is_truncated: None, + }; + + let mut bytes = id.to_be_bytes().to_vec(); + let length = bytes.len() as _; + let capacity = bytes.capacity(); + let ptr = NonNull::new(bytes.as_mut_ptr()); + mem::forget(bytes); + + let id_bind = BindData { + tpe: ffi::enum_field_types::MYSQL_TYPE_LONG, + bytes: ptr, + capacity, + length, + flags: Flags::empty(), + is_null: ffi::FALSE, + is_truncated: None, + }; + + let binds = PreparedStatementBinds(Binds { + data: vec![id_bind, field_bind], + }); + stmt.input_bind(binds).unwrap(); + stmt.did_an_error_occur().unwrap(); + let stmt = MaybeCached::CannotCache(stmt); + unsafe { + stmt.execute().unwrap(); + } + } + + #[test] + fn check_json_bind() { + table! { + json_test { + id -> Integer, + json_field -> Text, + } + } + + let conn = &mut crate::test_helpers::connection(); + + crate::sql_query("DROP TABLE IF EXISTS json_test CASCADE") + .execute(conn) + .unwrap(); + + crate::sql_query( + "CREATE TABLE json_test(id INTEGER PRIMARY KEY, json_field JSON NOT NULL)", + ) + .execute(conn) + .unwrap(); + + crate::sql_query("INSERT INTO json_test(id, json_field) VALUES (1, '{\"key1\": \"value1\", \"key2\": \"value2\"}')").execute(conn).unwrap(); + + let json_col_as_json = query_single_table( + "SELECT json_field FROM json_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()), + ); + + assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON); + assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_json).unwrap(), + "{\"key1\": \"value1\", \"key2\": \"value2\"}" + ); + + let json_col_as_text = query_single_table( + "SELECT json_field FROM json_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_text).unwrap(), + "{\"key1\": \"value1\", \"key2\": \"value2\"}" + ); + assert_eq!( + json_col_as_json.value().unwrap().as_bytes(), + json_col_as_text.value().unwrap().as_bytes() + ); + + crate::sql_query("DELETE FROM json_test") + .execute(conn) + .unwrap(); + + input_bind( + "INSERT INTO json_test(id, json_field) VALUES (?, ?)", + conn, + 41, + ( + b"{\"abc\": 42}".to_vec(), + MysqlType::String, + // (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()), + ), + ); + + let json_col_as_json = query_single_table( + "SELECT json_field FROM json_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()), + ); + + assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON); + assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_json).unwrap(), + "{\"abc\": 42}" + ); + + let json_col_as_text = query_single_table( + "SELECT json_field FROM json_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_text).unwrap(), + "{\"abc\": 42}" + ); + assert_eq!( + json_col_as_json.value().unwrap().as_bytes(), + json_col_as_text.value().unwrap().as_bytes() + ); + + crate::sql_query("DELETE FROM json_test") + .execute(conn) + .unwrap(); + + input_bind( + "INSERT INTO json_test(id, json_field) VALUES (?, ?)", + conn, + 41, + (b"{\"abca\": 42}".to_vec(), MysqlType::String), + ); + + let json_col_as_json = query_single_table( + "SELECT json_field FROM json_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()), + ); + + assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON); + assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_json).unwrap(), + "{\"abca\": 42}" + ); + + let json_col_as_text = query_single_table( + "SELECT json_field FROM json_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_text).unwrap(), + "{\"abca\": 42}" + ); + assert_eq!( + json_col_as_json.value().unwrap().as_bytes(), + json_col_as_text.value().unwrap().as_bytes() + ); + } + + #[test] + fn check_enum_bind() { + let conn = &mut crate::test_helpers::connection(); + + crate::sql_query("DROP TABLE IF EXISTS enum_test CASCADE") + .execute(conn) + .unwrap(); + + crate::sql_query("CREATE TABLE enum_test(id INTEGER PRIMARY KEY, enum_field ENUM('red', 'green', 'blue') NOT NULL)").execute(conn) + .unwrap(); + + crate::sql_query("INSERT INTO enum_test(id, enum_field) VALUES (1, 'green')") + .execute(conn) + .unwrap(); + + let enum_col_as_enum: BindData = + query_single_table("SELECT enum_field FROM enum_test", conn, MysqlType::Enum); + + assert_eq!( + enum_col_as_enum.tpe, + ffi::enum_field_types::MYSQL_TYPE_STRING + ); + assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&enum_col_as_enum).unwrap(), + "green" + ); + + for tpe in &[ + ffi::enum_field_types::MYSQL_TYPE_BLOB, + ffi::enum_field_types::MYSQL_TYPE_VAR_STRING, + ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB, + ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB, + ] { + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + conn, + (*tpe, Flags::ENUM_FLAG), + ); + + assert_eq!(enum_col_as_text.tpe, *tpe); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&enum_col_as_text).unwrap(), + "green" + ); + assert_eq!( + enum_col_as_enum.value().unwrap().as_bytes(), + enum_col_as_text.value().unwrap().as_bytes() + ); + } + + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&enum_col_as_text).unwrap(), + "green" + ); + assert_eq!( + enum_col_as_enum.value().unwrap().as_bytes(), + enum_col_as_text.value().unwrap().as_bytes() + ); + + crate::sql_query("DELETE FROM enum_test") + .execute(conn) + .unwrap(); + + input_bind( + "INSERT INTO enum_test(id, enum_field) VALUES (?, ?)", + conn, + 41, + (b"blue".to_vec(), MysqlType::Enum), + ); + + let enum_col_as_enum = + query_single_table("SELECT enum_field FROM enum_test", conn, MysqlType::Enum); + + assert_eq!( + enum_col_as_enum.tpe, + ffi::enum_field_types::MYSQL_TYPE_STRING + ); + assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_enum).unwrap(), "blue"); + + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG), + ); + + assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_text).unwrap(), "blue"); + assert_eq!( + enum_col_as_enum.value().unwrap().as_bytes(), + enum_col_as_text.value().unwrap().as_bytes() + ); + + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG), + ); + + assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_text).unwrap(), "blue"); + assert_eq!( + enum_col_as_enum.value().unwrap().as_bytes(), + enum_col_as_text.value().unwrap().as_bytes() + ); + + crate::sql_query("DELETE FROM enum_test") + .execute(conn) + .unwrap(); + + input_bind( + "INSERT INTO enum_test(id, enum_field) VALUES (?, ?)", + conn, + 41, + ( + b"red".to_vec(), + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG), + ), + ); + + let enum_col_as_enum = + query_single_table("SELECT enum_field FROM enum_test", conn, MysqlType::Enum); + + assert_eq!( + enum_col_as_enum.tpe, + ffi::enum_field_types::MYSQL_TYPE_STRING + ); + assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_enum).unwrap(), "red"); + + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG), + ); + + assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_text).unwrap(), "red"); + assert_eq!( + enum_col_as_enum.value().unwrap().as_bytes(), + enum_col_as_text.value().unwrap().as_bytes() + ); + } + + #[test] + fn check_set_bind() { + let conn = &mut crate::test_helpers::connection(); + + crate::sql_query("DROP TABLE IF EXISTS set_test CASCADE") + .execute(conn) + .unwrap(); + + crate::sql_query("CREATE TABLE set_test(id INTEGER PRIMARY KEY, set_field SET('red', 'green', 'blue') NOT NULL)").execute(conn) + .unwrap(); + + crate::sql_query("INSERT INTO set_test(id, set_field) VALUES (1, 'green')") + .execute(conn) + .unwrap(); + + let set_col_as_set: BindData = + query_single_table("SELECT set_field FROM set_test", conn, MysqlType::Set); + + assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_set.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_set).unwrap(), "green"); + + for tpe in &[ + ffi::enum_field_types::MYSQL_TYPE_BLOB, + ffi::enum_field_types::MYSQL_TYPE_VAR_STRING, + ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB, + ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB, + ] { + let set_col_as_text = query_single_table( + "SELECT set_field FROM set_test", + conn, + (*tpe, Flags::SET_FLAG), + ); + + assert_eq!(set_col_as_text.tpe, *tpe); + assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_text).unwrap(), "green"); + assert_eq!( + set_col_as_set.value().unwrap().as_bytes(), + set_col_as_text.value().unwrap().as_bytes() + ); + } + let set_col_as_text = query_single_table( + "SELECT set_field FROM set_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_text).unwrap(), "green"); + assert_eq!( + set_col_as_set.value().unwrap().as_bytes(), + set_col_as_text.value().unwrap().as_bytes() + ); + + crate::sql_query("DELETE FROM set_test") + .execute(conn) + .unwrap(); + + input_bind( + "INSERT INTO set_test(id, set_field) VALUES (?, ?)", + conn, + 41, + (b"blue".to_vec(), MysqlType::Set), + ); + + let set_col_as_set = + query_single_table("SELECT set_field FROM set_test", conn, MysqlType::Set); + + assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_set.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_set).unwrap(), "blue"); + + let set_col_as_text = query_single_table( + "SELECT set_field FROM set_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::SET_FLAG), + ); + + assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_text).unwrap(), "blue"); + assert_eq!( + set_col_as_set.value().unwrap().as_bytes(), + set_col_as_text.value().unwrap().as_bytes() + ); + + crate::sql_query("DELETE FROM set_test") + .execute(conn) + .unwrap(); + + input_bind( + "INSERT INTO set_test(id, set_field) VALUES (?, ?)", + conn, + 41, + (b"red".to_vec(), MysqlType::String), + ); + + let set_col_as_set = + query_single_table("SELECT set_field FROM set_test", conn, MysqlType::Set); + + assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_set.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_set).unwrap(), "red"); + + let set_col_as_text = query_single_table( + "SELECT set_field FROM set_test", + conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::SET_FLAG), + ); + + assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_text).unwrap(), "red"); + assert_eq!( + set_col_as_set.value().unwrap().as_bytes(), + set_col_as_text.value().unwrap().as_bytes() + ); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/mod.rs new file mode 100644 index 000000000..cdfd50277 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/mod.rs @@ -0,0 +1,399 @@ +mod bind; +mod raw; +mod stmt; +mod url; + +use self::raw::RawConnection; +use self::stmt::iterator::StatementIterator; +use self::stmt::Statement; +use self::url::ConnectionOptions; +use super::backend::Mysql; +use crate::connection::instrumentation::DebugQuery; +use crate::connection::instrumentation::StrQueryHelper; +use crate::connection::statement_cache::{MaybeCached, StatementCache}; +use crate::connection::*; +use crate::expression::QueryMetadata; +use crate::query_builder::bind_collector::RawBytesBindCollector; +use crate::query_builder::*; +use crate::result::*; +use crate::RunQueryDsl; + +#[cfg(feature = "mysql")] +#[allow(missing_debug_implementations, missing_copy_implementations)] +/// A connection to a MySQL database. Connection URLs should be in the form +/// `mysql://[user[:password]@]host/database_name[?unix_socket=socket-path&ssl_mode=SSL_MODE*&ssl_ca=/etc/ssl/certs/ca-certificates.crt&ssl_cert=/etc/ssl/certs/client-cert.crt&ssl_key=/etc/ssl/certs/client-key.crt]` +/// +///* `host` can be an IP address or a hostname. If it is set to `localhost`, a connection +/// will be attempted through the socket at `/tmp/mysql.sock`. If you want to connect to +/// a local server via TCP (e.g. docker containers), use `0.0.0.0` or `127.0.0.1` instead. +/// * `unix_socket` expects the path to the unix socket +/// * `ssl_ca` accepts a path to the system's certificate roots +/// * `ssl_cert` accepts a path to the client's certificate file +/// * `ssl_key` accepts a path to the client's private key file +/// * `ssl_mode` expects a value defined for MySQL client command option `--ssl-mode` +/// See +/// +/// # Supported loading model implementations +/// +/// * [`DefaultLoadingMode`] +/// +/// As `MysqlConnection` only supports a single loading mode implementation +/// it is **not required** to explicitly specify a loading mode +/// when calling [`RunQueryDsl::load_iter()`] or [`LoadConnection::load`] +/// +/// ## DefaultLoadingMode +/// +/// `MysqlConnection` only supports a single loading mode, which loads +/// values row by row from the result set. +/// +/// ```rust +/// # include!("../../doctest_setup.rs"); +/// # +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users; +/// # let connection = &mut establish_connection(); +/// use diesel::connection::DefaultLoadingMode; +/// { // scope to restrict the lifetime of the iterator +/// let iter1 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?; +/// +/// for r in iter1 { +/// let (id, name) = r?; +/// println!("Id: {} Name: {}", id, name); +/// } +/// } +/// +/// // works without specifying the loading mode +/// let iter2 = users::table.load_iter::<(i32, String), _>(connection)?; +/// +/// for r in iter2 { +/// let (id, name) = r?; +/// println!("Id: {} Name: {}", id, name); +/// } +/// # Ok(()) +/// # } +/// ``` +/// +/// This mode does **not support** creating +/// multiple iterators using the same connection. +/// +/// ```compile_fail +/// # include!("../../doctest_setup.rs"); +/// # +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users; +/// # let connection = &mut establish_connection(); +/// use diesel::connection::DefaultLoadingMode; +/// +/// let iter1 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?; +/// let iter2 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?; +/// +/// for r in iter1 { +/// let (id, name) = r?; +/// println!("Id: {} Name: {}", id, name); +/// } +/// +/// for r in iter2 { +/// let (id, name) = r?; +/// println!("Id: {} Name: {}", id, name); +/// } +/// # Ok(()) +/// # } +/// ``` +pub struct MysqlConnection { + raw_connection: RawConnection, + transaction_state: AnsiTransactionManager, + statement_cache: StatementCache, + instrumentation: Option>, +} + +// mysql connection can be shared between threads according to libmysqlclients documentation +#[allow(unsafe_code)] +unsafe impl Send for MysqlConnection {} + +impl SimpleConnection for MysqlConnection { + fn batch_execute(&mut self, query: &str) -> QueryResult<()> { + self.instrumentation + .on_connection_event(InstrumentationEvent::StartQuery { + query: &StrQueryHelper::new(query), + }); + let r = self + .raw_connection + .enable_multi_statements(|| self.raw_connection.execute(query)); + self.instrumentation + .on_connection_event(InstrumentationEvent::FinishQuery { + query: &StrQueryHelper::new(query), + error: r.as_ref().err(), + }); + r + } +} + +impl ConnectionSealed for MysqlConnection {} + +impl Connection for MysqlConnection { + type Backend = Mysql; + type TransactionManager = AnsiTransactionManager; + + /// Establishes a new connection to the MySQL database + /// `database_url` may be enhanced by GET parameters + /// `mysql://[user[:password]@]host[:port]/database_name[?unix_socket=socket-path&ssl_mode=SSL_MODE*&ssl_ca=/etc/ssl/certs/ca-certificates.crt&ssl_cert=/etc/ssl/certs/client-cert.crt&ssl_key=/etc/ssl/certs/client-key.crt]` + /// + /// * `host` can be an IP address or a hostname. If it is set to `localhost`, a connection + /// will be attempted through the socket at `/tmp/mysql.sock`. If you want to connect to + /// a local server via TCP (e.g. docker containers), use `0.0.0.0` or `127.0.0.1` instead. + /// * `unix_socket` expects the path to the unix socket + /// * `ssl_ca` accepts a path to the system's certificate roots + /// * `ssl_cert` accepts a path to the client's certificate file + /// * `ssl_key` accepts a path to the client's private key file + /// * `ssl_mode` expects a value defined for MySQL client command option `--ssl-mode` + /// See + fn establish(database_url: &str) -> ConnectionResult { + let mut instrumentation = crate::connection::instrumentation::get_default_instrumentation(); + instrumentation.on_connection_event(InstrumentationEvent::StartEstablishConnection { + url: database_url, + }); + + let establish_result = Self::establish_inner(database_url); + instrumentation.on_connection_event(InstrumentationEvent::FinishEstablishConnection { + url: database_url, + error: establish_result.as_ref().err(), + }); + let mut conn = establish_result?; + conn.instrumentation = instrumentation; + Ok(conn) + } + + fn execute_returning_count(&mut self, source: &T) -> QueryResult + where + T: QueryFragment + QueryId, + { + #[allow(unsafe_code)] // call to unsafe function + update_transaction_manager_status( + prepared_query( + &source, + &mut self.statement_cache, + &mut self.raw_connection, + &mut self.instrumentation, + ) + .and_then(|stmt| { + // we have not called result yet, so calling `execute` is + // fine + let stmt_use = unsafe { stmt.execute() }?; + stmt_use.affected_rows() + }), + &mut self.transaction_state, + &mut self.instrumentation, + &crate::debug_query(source), + ) + } + + fn transaction_state(&mut self) -> &mut AnsiTransactionManager { + &mut self.transaction_state + } + + fn instrumentation(&mut self) -> &mut dyn Instrumentation { + &mut self.instrumentation + } + + fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) { + self.instrumentation = Some(Box::new(instrumentation)); + } +} + +#[inline(always)] +fn update_transaction_manager_status( + query_result: QueryResult, + transaction_manager: &mut AnsiTransactionManager, + instrumentation: &mut Option>, + query: &dyn DebugQuery, +) -> QueryResult { + if let Err(Error::DatabaseError(DatabaseErrorKind::SerializationFailure, _)) = query_result { + transaction_manager + .status + .set_requires_rollback_maybe_up_to_top_level(true) + } + instrumentation.on_connection_event(InstrumentationEvent::FinishQuery { + query, + error: query_result.as_ref().err(), + }); + query_result +} + +impl LoadConnection for MysqlConnection { + type Cursor<'conn, 'query> = self::stmt::iterator::StatementIterator<'conn>; + type Row<'conn, 'query> = self::stmt::iterator::MysqlRow; + + fn load<'conn, 'query, T>( + &'conn mut self, + source: T, + ) -> QueryResult> + where + T: Query + QueryFragment + QueryId + 'query, + Self::Backend: QueryMetadata, + { + update_transaction_manager_status( + prepared_query( + &source, + &mut self.statement_cache, + &mut self.raw_connection, + &mut self.instrumentation, + ) + .and_then(|stmt| { + let mut metadata = Vec::new(); + Mysql::row_metadata(&mut (), &mut metadata); + StatementIterator::from_stmt(stmt, &metadata) + }), + &mut self.transaction_state, + &mut self.instrumentation, + &crate::debug_query(&source), + ) + } +} + +#[cfg(feature = "r2d2")] +impl crate::r2d2::R2D2Connection for MysqlConnection { + fn ping(&mut self) -> QueryResult<()> { + crate::r2d2::CheckConnectionQuery.execute(self).map(|_| ()) + } + + fn is_broken(&mut self) -> bool { + AnsiTransactionManager::is_broken_transaction_manager(self) + } +} + +impl MultiConnectionHelper for MysqlConnection { + fn to_any<'a>( + lookup: &mut ::MetadataLookup, + ) -> &mut (dyn std::any::Any + 'a) { + lookup + } + + fn from_any( + lookup: &mut dyn std::any::Any, + ) -> Option<&mut ::MetadataLookup> { + lookup.downcast_mut() + } +} + +fn prepared_query<'a, T: QueryFragment + QueryId>( + source: &'_ T, + statement_cache: &'a mut StatementCache, + raw_connection: &'a mut RawConnection, + instrumentation: &mut dyn Instrumentation, +) -> QueryResult> { + instrumentation.on_connection_event(InstrumentationEvent::StartQuery { + query: &crate::debug_query(source), + }); + let mut stmt = statement_cache.cached_statement( + source, + &Mysql, + &[], + |sql, _| raw_connection.prepare(sql), + instrumentation, + )?; + + let mut bind_collector = RawBytesBindCollector::new(); + source.collect_binds(&mut bind_collector, &mut (), &Mysql)?; + let binds = bind_collector + .metadata + .into_iter() + .zip(bind_collector.binds); + stmt.bind(binds)?; + Ok(stmt) +} + +impl MysqlConnection { + fn set_config_options(&mut self) -> QueryResult<()> { + crate::sql_query("SET time_zone = '+00:00';").execute(self)?; + crate::sql_query("SET character_set_client = 'utf8mb4'").execute(self)?; + crate::sql_query("SET character_set_connection = 'utf8mb4'").execute(self)?; + crate::sql_query("SET character_set_results = 'utf8mb4'").execute(self)?; + Ok(()) + } + + fn establish_inner(database_url: &str) -> Result { + use crate::ConnectionError::CouldntSetupConfiguration; + + let raw_connection = RawConnection::new(); + let connection_options = ConnectionOptions::parse(database_url)?; + raw_connection.connect(&connection_options)?; + let mut conn = MysqlConnection { + raw_connection, + transaction_state: AnsiTransactionManager::default(), + statement_cache: StatementCache::new(), + instrumentation: None, + }; + conn.set_config_options() + .map_err(CouldntSetupConfiguration)?; + Ok(conn) + } +} + +#[cfg(test)] +mod tests { + extern crate dotenvy; + + use super::*; + use std::env; + + fn connection() -> MysqlConnection { + dotenvy::dotenv().ok(); + let database_url = env::var("MYSQL_UNIT_TEST_DATABASE_URL") + .or_else(|_| env::var("MYSQL_DATABASE_URL")) + .or_else(|_| env::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run unit tests"); + MysqlConnection::establish(&database_url).unwrap() + } + + #[test] + fn batch_execute_handles_single_queries_with_results() { + let connection = &mut connection(); + assert!(connection.batch_execute("SELECT 1").is_ok()); + assert!(connection.batch_execute("SELECT 1").is_ok()); + } + + #[test] + fn batch_execute_handles_multi_queries_with_results() { + let connection = &mut connection(); + let query = "SELECT 1; SELECT 2; SELECT 3;"; + assert!(connection.batch_execute(query).is_ok()); + assert!(connection.batch_execute(query).is_ok()); + } + + #[test] + fn execute_handles_queries_which_return_results() { + let connection = &mut connection(); + assert!(crate::sql_query("SELECT 1").execute(connection).is_ok()); + assert!(crate::sql_query("SELECT 1").execute(connection).is_ok()); + } + + #[test] + fn check_client_found_rows_flag() { + let conn = &mut crate::test_helpers::connection(); + crate::sql_query("DROP TABLE IF EXISTS update_test CASCADE") + .execute(conn) + .unwrap(); + + crate::sql_query("CREATE TABLE update_test(id INTEGER PRIMARY KEY, num INTEGER NOT NULL)") + .execute(conn) + .unwrap(); + + crate::sql_query("INSERT INTO update_test(id, num) VALUES (1, 5)") + .execute(conn) + .unwrap(); + + let output = crate::sql_query("UPDATE update_test SET num = 5 WHERE id = 1") + .execute(conn) + .unwrap(); + + assert_eq!(output, 1); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/raw.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/raw.rs new file mode 100644 index 000000000..b2d1ca9dd --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/raw.rs @@ -0,0 +1,277 @@ +#![allow(unsafe_code)] // module uses ffi +use mysqlclient_sys as ffi; +use std::ffi::CStr; +use std::os::raw as libc; +use std::ptr::{self, NonNull}; +use std::sync::Once; + +use super::stmt::Statement; +use super::url::ConnectionOptions; +use crate::result::{ConnectionError, ConnectionResult, QueryResult}; + +pub(super) struct RawConnection(NonNull); + +// old versions of mysqlclient do not expose +// ffi::FALSE, so we need to have our own compatibility +// wrapper here +// +// Depending on the bindings version ffi::my_bool +// might be an actual bool or a i8. For the former +// case `default()` corresponds to `false` for the later +// to `0` which is both interpreted as false +#[inline(always)] +pub(super) fn ffi_false() -> ffi::my_bool { + Default::default() +} + +impl RawConnection { + pub(super) fn new() -> Self { + perform_thread_unsafe_library_initialization(); + let raw_connection = unsafe { ffi::mysql_init(ptr::null_mut()) }; + // We're trusting https://dev.mysql.com/doc/refman/5.7/en/mysql-init.html + // that null return always means OOM + let raw_connection = + NonNull::new(raw_connection).expect("Insufficient memory to allocate connection"); + let result = RawConnection(raw_connection); + + // This is only non-zero for unrecognized options, which should never happen. + let charset_result = unsafe { + ffi::mysql_options( + result.0.as_ptr(), + ffi::mysql_option::MYSQL_SET_CHARSET_NAME, + c"utf8mb4".as_ptr() as *const libc::c_void, + ) + }; + assert_eq!( + 0, charset_result, + "MYSQL_SET_CHARSET_NAME was not \ + recognized as an option by MySQL. This should never \ + happen." + ); + + result + } + + pub(super) fn connect(&self, connection_options: &ConnectionOptions) -> ConnectionResult<()> { + let host = connection_options.host(); + let user = connection_options.user(); + let password = connection_options.password(); + let database = connection_options.database(); + let port = connection_options.port(); + let unix_socket = connection_options.unix_socket(); + let client_flags = connection_options.client_flags(); + + if let Some(ssl_mode) = connection_options.ssl_mode() { + self.set_ssl_mode(ssl_mode) + } + if let Some(ssl_ca) = connection_options.ssl_ca() { + self.set_ssl_ca(ssl_ca) + } + if let Some(ssl_cert) = connection_options.ssl_cert() { + self.set_ssl_cert(ssl_cert) + } + if let Some(ssl_key) = connection_options.ssl_key() { + self.set_ssl_key(ssl_key) + } + + unsafe { + // Make sure you don't use the fake one! + ffi::mysql_real_connect( + self.0.as_ptr(), + host.map(CStr::as_ptr).unwrap_or_else(ptr::null), + user.as_ptr(), + password.map(CStr::as_ptr).unwrap_or_else(ptr::null), + database.map(CStr::as_ptr).unwrap_or_else(ptr::null), + u32::from(port.unwrap_or(0)), + unix_socket.map(CStr::as_ptr).unwrap_or_else(ptr::null), + client_flags.bits().into(), + ) + }; + + let last_error_message = self.last_error_message(); + if last_error_message.is_empty() { + Ok(()) + } else { + Err(ConnectionError::BadConnection(last_error_message)) + } + } + + pub(super) fn last_error_message(&self) -> String { + unsafe { CStr::from_ptr(ffi::mysql_error(self.0.as_ptr())) } + .to_string_lossy() + .into_owned() + } + + pub(super) fn execute(&self, query: &str) -> QueryResult<()> { + unsafe { + // Make sure you don't use the fake one! + ffi::mysql_real_query( + self.0.as_ptr(), + query.as_ptr() as *const libc::c_char, + query.len() as libc::c_ulong, + ); + } + self.did_an_error_occur()?; + self.flush_pending_results()?; + Ok(()) + } + + pub(super) fn enable_multi_statements(&self, f: F) -> QueryResult + where + F: FnOnce() -> QueryResult, + { + unsafe { + ffi::mysql_set_server_option( + self.0.as_ptr(), + ffi::enum_mysql_set_option::MYSQL_OPTION_MULTI_STATEMENTS_ON, + ); + } + self.did_an_error_occur()?; + + let result = f(); + + unsafe { + ffi::mysql_set_server_option( + self.0.as_ptr(), + ffi::enum_mysql_set_option::MYSQL_OPTION_MULTI_STATEMENTS_OFF, + ); + } + self.did_an_error_occur()?; + + result + } + + pub(super) fn prepare(&self, query: &str) -> QueryResult { + let stmt = unsafe { ffi::mysql_stmt_init(self.0.as_ptr()) }; + // It is documented that the only reason `mysql_stmt_init` will fail + // is because of OOM. + // https://dev.mysql.com/doc/refman/5.7/en/mysql-stmt-init.html + let stmt = NonNull::new(stmt).expect("Out of memory creating prepared statement"); + let stmt = Statement::new(stmt); + stmt.prepare(query)?; + Ok(stmt) + } + + fn did_an_error_occur(&self) -> QueryResult<()> { + use crate::result::DatabaseErrorKind; + use crate::result::Error::DatabaseError; + + let error_message = self.last_error_message(); + if error_message.is_empty() { + Ok(()) + } else { + Err(DatabaseError( + DatabaseErrorKind::Unknown, + Box::new(error_message), + )) + } + } + + fn flush_pending_results(&self) -> QueryResult<()> { + // We may have a result to process before advancing + self.consume_current_result()?; + while self.more_results() { + self.next_result()?; + self.consume_current_result()?; + } + Ok(()) + } + + fn consume_current_result(&self) -> QueryResult<()> { + unsafe { + let res = ffi::mysql_store_result(self.0.as_ptr()); + if !res.is_null() { + ffi::mysql_free_result(res); + } + } + self.did_an_error_occur() + } + + fn more_results(&self) -> bool { + unsafe { ffi::mysql_more_results(self.0.as_ptr()) != ffi_false() } + } + + fn next_result(&self) -> QueryResult<()> { + unsafe { ffi::mysql_next_result(self.0.as_ptr()) }; + self.did_an_error_occur() + } + + fn set_ssl_mode(&self, ssl_mode: mysqlclient_sys::mysql_ssl_mode) { + let v = ssl_mode as u32; + let v_ptr: *const u32 = &v; + let n = ptr::NonNull::new(v_ptr as *mut u32).expect("NonNull::new failed"); + unsafe { + mysqlclient_sys::mysql_options( + self.0.as_ptr(), + mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_MODE, + n.as_ptr() as *const std::ffi::c_void, + ) + }; + } + + fn set_ssl_ca(&self, ssl_ca: &CStr) { + unsafe { + mysqlclient_sys::mysql_options( + self.0.as_ptr(), + mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_CA, + ssl_ca.as_ptr() as *const std::ffi::c_void, + ) + }; + } + + fn set_ssl_cert(&self, ssl_cert: &CStr) { + unsafe { + mysqlclient_sys::mysql_options( + self.0.as_ptr(), + mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_CERT, + ssl_cert.as_ptr() as *const std::ffi::c_void, + ) + }; + } + + fn set_ssl_key(&self, ssl_key: &CStr) { + unsafe { + mysqlclient_sys::mysql_options( + self.0.as_ptr(), + mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_KEY, + ssl_key.as_ptr() as *const std::ffi::c_void, + ) + }; + } +} + +impl Drop for RawConnection { + fn drop(&mut self) { + unsafe { + ffi::mysql_close(self.0.as_ptr()); + } + } +} + +/// > In a non-multi-threaded environment, `mysql_init()` invokes +/// > `mysql_library_init()` automatically as necessary. However, +/// > `mysql_library_init()` is not thread-safe in a multi-threaded environment, +/// > and thus neither is `mysql_init()`. Before calling `mysql_init()`, either +/// > call `mysql_library_init()` prior to spawning any threads, or use a mutex +/// > to protect the `mysql_library_init()` call. This should be done prior to +/// > any other client library call. +/// +/// +static MYSQL_THREAD_UNSAFE_INIT: Once = Once::new(); + +fn perform_thread_unsafe_library_initialization() { + MYSQL_THREAD_UNSAFE_INIT.call_once(|| { + // mysql_library_init is defined by `#define mysql_library_init mysql_server_init` + // which isn't picked up by bindgen + let error_code = unsafe { ffi::mysql_server_init(0, ptr::null_mut(), ptr::null_mut()) }; + if error_code != 0 { + // FIXME: This is documented as Nonzero if an error occurred. + // Presumably the value has some sort of meaning that we should + // reflect in this message. We are going to panic instead of return + // an error here, since the documentation does not indicate whether + // it is safe to call this function twice if the first call failed, + // so I will assume it is not. + panic!("Unable to perform MySQL global initialization"); + } + }) +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/stmt/iterator.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/stmt/iterator.rs new file mode 100644 index 000000000..ce3bb81d5 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/stmt/iterator.rs @@ -0,0 +1,376 @@ +#![allow(unsafe_code)] // module uses ffi +use std::cell::{Ref, RefCell}; +use std::rc::Rc; + +use super::{OutputBinds, Statement, StatementMetadata, StatementUse}; +use crate::backend::Backend; +use crate::connection::statement_cache::MaybeCached; +use crate::mysql::{Mysql, MysqlType}; +use crate::result::QueryResult; +use crate::row::*; + +#[allow(missing_debug_implementations)] +pub struct StatementIterator<'a> { + stmt: StatementUse<'a>, + last_row: Rc>, + metadata: Rc, + len: usize, +} + +impl<'a> StatementIterator<'a> { + pub fn from_stmt( + stmt: MaybeCached<'a, Statement>, + types: &[Option], + ) -> QueryResult { + let metadata = stmt.metadata()?; + + let mut output_binds = OutputBinds::from_output_types(types, &metadata); + + let mut stmt = stmt.execute_statement(&mut output_binds)?; + let size = unsafe { stmt.result_size() }?; + + Ok(StatementIterator { + metadata: Rc::new(metadata), + last_row: Rc::new(RefCell::new(PrivateMysqlRow::Direct(output_binds))), + len: size, + stmt, + }) + } +} + +impl Iterator for StatementIterator<'_> { + type Item = QueryResult; + + fn next(&mut self) -> Option { + // check if we own the only instance of the bind buffer + // if that's the case we can reuse the underlying allocations + // if that's not the case, we need to copy the output bind buffers + // to somewhere else + let res = if let Some(binds) = Rc::get_mut(&mut self.last_row) { + if let PrivateMysqlRow::Direct(ref mut binds) = RefCell::get_mut(binds) { + self.stmt.populate_row_buffers(binds) + } else { + // any other state than `PrivateMysqlRow::Direct` is invalid here + // and should not happen. If this ever happens this is a logic error + // in the code above + unreachable!( + "You've reached an impossible internal state. \ + If you ever see this error message please open \ + an issue at https://github.com/diesel-rs/diesel \ + providing example code how to trigger this error." + ) + } + } else { + // The shared bind buffer is in use by someone else, + // this means we copy out the values and replace the used reference + // by the copied values. After this we can advance the statement + // another step + let mut last_row = { + let mut last_row = match self.last_row.try_borrow_mut() { + Ok(o) => o, + Err(_e) => { + return Some(Err(crate::result::Error::DeserializationError( + "Failed to reborrow row. Try to release any `MysqlField` or `MysqlValue` \ + that exists at this point" + .into(), + ))); + } + }; + let last_row = &mut *last_row; + let duplicated = last_row.duplicate(); + std::mem::replace(last_row, duplicated) + }; + let res = if let PrivateMysqlRow::Direct(ref mut binds) = last_row { + self.stmt.populate_row_buffers(binds) + } else { + // any other state than `PrivateMysqlRow::Direct` is invalid here + // and should not happen. If this ever happens this is a logic error + // in the code above + unreachable!( + "You've reached an impossible internal state. \ + If you ever see this error message please open \ + an issue at https://github.com/diesel-rs/diesel \ + providing example code how to trigger this error." + ) + }; + self.last_row = Rc::new(RefCell::new(last_row)); + res + }; + + match res { + Ok(Some(())) => { + self.len = self.len.saturating_sub(1); + Some(Ok(MysqlRow { + metadata: self.metadata.clone(), + row: self.last_row.clone(), + })) + } + Ok(None) => None, + Err(e) => { + self.len = self.len.saturating_sub(1); + Some(Err(e)) + } + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.len(), Some(self.len())) + } + + fn count(self) -> usize + where + Self: Sized, + { + self.len() + } +} + +impl ExactSizeIterator for StatementIterator<'_> { + fn len(&self) -> usize { + self.len + } +} + +#[derive(Clone)] +#[allow(missing_debug_implementations)] +pub struct MysqlRow { + row: Rc>, + metadata: Rc, +} + +enum PrivateMysqlRow { + Direct(OutputBinds), + Copied(OutputBinds), +} + +impl PrivateMysqlRow { + fn duplicate(&self) -> Self { + match self { + Self::Copied(b) | Self::Direct(b) => Self::Copied(b.clone()), + } + } +} + +impl RowSealed for MysqlRow {} + +impl<'a> Row<'a, Mysql> for MysqlRow { + type Field<'f> + = MysqlField<'f> + where + 'a: 'f, + Self: 'f; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.metadata.fields().len() + } + + fn get<'b, I>(&'b self, idx: I) -> Option> + where + 'a: 'b, + Self: RowIndex, + { + let idx = self.idx(idx)?; + Some(MysqlField { + binds: self.row.borrow(), + metadata: self.metadata.clone(), + idx, + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow<'_, Self::InnerPartialRow> { + PartialRow::new(self, range) + } +} + +impl RowIndex for MysqlRow { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count() { + Some(idx) + } else { + None + } + } +} + +impl<'a> RowIndex<&'a str> for MysqlRow { + fn idx(&self, idx: &'a str) -> Option { + self.metadata + .fields() + .iter() + .enumerate() + .find(|(_, field_meta)| field_meta.field_name() == Some(idx)) + .map(|(idx, _)| idx) + } +} + +#[allow(missing_debug_implementations)] +pub struct MysqlField<'a> { + binds: Ref<'a, PrivateMysqlRow>, + metadata: Rc, + idx: usize, +} + +impl<'a> Field<'a, Mysql> for MysqlField<'a> { + fn field_name(&self) -> Option<&str> { + self.metadata.fields()[self.idx].field_name() + } + + fn is_null(&self) -> bool { + match &*self.binds { + PrivateMysqlRow::Copied(b) | PrivateMysqlRow::Direct(b) => b[self.idx].is_null(), + } + } + + fn value(&self) -> Option<::RawValue<'_>> { + match &*self.binds { + PrivateMysqlRow::Copied(b) | PrivateMysqlRow::Direct(b) => b[self.idx].value(), + } + } +} + +#[test] +#[allow(clippy::drop_non_drop)] // we want to explicitly extend lifetimes here +fn fun_with_row_iters() { + crate::table! { + #[allow(unused_parens)] + users(id) { + id -> Integer, + name -> Text, + } + } + + use crate::connection::LoadConnection; + use crate::deserialize::{FromSql, FromSqlRow}; + use crate::prelude::*; + use crate::row::{Field, Row}; + use crate::sql_types; + + let conn = &mut crate::test_helpers::connection(); + + crate::sql_query( + "CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);", + ) + .execute(conn) + .unwrap(); + crate::sql_query("DELETE FROM users;") + .execute(conn) + .unwrap(); + + crate::insert_into(users::table) + .values(vec![ + (users::id.eq(1), users::name.eq("Sean")), + (users::id.eq(2), users::name.eq("Tess")), + ]) + .execute(conn) + .unwrap(); + + let query = users::table.select((users::id, users::name)); + + let expected = vec![(1, String::from("Sean")), (2, String::from("Tess"))]; + + { + let row_iter = conn.load(query).unwrap(); + for (row, expected) in row_iter.zip(&expected) { + let row = row.unwrap(); + + let deserialized = <(i32, String) as FromSqlRow< + (sql_types::Integer, sql_types::Text), + _, + >>::build_from_row(&row) + .unwrap(); + + assert_eq!(&deserialized, expected); + } + } + + { + let collected_rows = conn.load(query).unwrap().collect::>(); + assert_eq!(collected_rows.len(), 2); + for (row, expected) in collected_rows.iter().zip(&expected) { + let deserialized = row + .as_ref() + .map(|row| { + <(i32, String) as FromSqlRow< + (sql_types::Integer, sql_types::Text), + _, + >>::build_from_row(row).unwrap() + }) + .unwrap(); + assert_eq!(&deserialized, expected); + } + } + + let mut row_iter = conn.load(query).unwrap(); + + let first_row = row_iter.next().unwrap().unwrap(); + let first_fields = ( + Row::get(&first_row, 0).unwrap(), + Row::get(&first_row, 1).unwrap(), + ); + let first_values = (first_fields.0.value(), first_fields.1.value()); + + assert!(row_iter.next().unwrap().is_err()); + std::mem::drop(first_values); + assert!(row_iter.next().unwrap().is_err()); + std::mem::drop(first_fields); + + let second_row = row_iter.next().unwrap().unwrap(); + let second_fields = ( + Row::get(&second_row, 0).unwrap(), + Row::get(&second_row, 1).unwrap(), + ); + let second_values = (second_fields.0.value(), second_fields.1.value()); + + assert!(row_iter.next().unwrap().is_err()); + std::mem::drop(second_values); + assert!(row_iter.next().unwrap().is_err()); + std::mem::drop(second_fields); + + assert!(row_iter.next().is_none()); + + let first_fields = ( + Row::get(&first_row, 0).unwrap(), + Row::get(&first_row, 1).unwrap(), + ); + let second_fields = ( + Row::get(&second_row, 0).unwrap(), + Row::get(&second_row, 1).unwrap(), + ); + + let first_values = (first_fields.0.value(), first_fields.1.value()); + let second_values = (second_fields.0.value(), second_fields.1.value()); + + assert_eq!( + >::from_nullable_sql(first_values.0).unwrap(), + expected[0].0 + ); + assert_eq!( + >::from_nullable_sql(first_values.1).unwrap(), + expected[0].1 + ); + + assert_eq!( + >::from_nullable_sql(second_values.0).unwrap(), + expected[1].0 + ); + assert_eq!( + >::from_nullable_sql(second_values.1).unwrap(), + expected[1].1 + ); + + let first_fields = ( + Row::get(&first_row, 0).unwrap(), + Row::get(&first_row, 1).unwrap(), + ); + let first_values = (first_fields.0.value(), first_fields.1.value()); + + assert_eq!( + >::from_nullable_sql(first_values.0).unwrap(), + expected[0].0 + ); + assert_eq!( + >::from_nullable_sql(first_values.1).unwrap(), + expected[0].1 + ); +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/stmt/metadata.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/stmt/metadata.rs new file mode 100644 index 000000000..a7da4fdd1 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/stmt/metadata.rs @@ -0,0 +1,64 @@ +#![allow(unsafe_code)] // module uses ffi +use std::ffi::CStr; +use std::ptr::NonNull; +use std::slice; + +use super::ffi; +use crate::mysql::connection::bind::Flags; + +pub(in crate::mysql::connection) struct StatementMetadata { + result: NonNull, +} + +impl StatementMetadata { + pub(in crate::mysql::connection) fn new(result: NonNull) -> Self { + StatementMetadata { result } + } + + pub(in crate::mysql::connection) fn fields(&'_ self) -> &'_ [MysqlFieldMetadata<'_>] { + unsafe { + let num_fields = ffi::mysql_num_fields(self.result.as_ptr()); + let field_ptr = ffi::mysql_fetch_fields(self.result.as_ptr()); + if field_ptr.is_null() { + &[] + } else { + slice::from_raw_parts(field_ptr as _, num_fields as usize) + } + } + } +} + +impl Drop for StatementMetadata { + fn drop(&mut self) { + unsafe { ffi::mysql_free_result(self.result.as_mut()) }; + } +} + +#[repr(transparent)] +pub(in crate::mysql::connection) struct MysqlFieldMetadata<'a>( + ffi::MYSQL_FIELD, + std::marker::PhantomData<&'a ()>, +); + +impl MysqlFieldMetadata<'_> { + pub(in crate::mysql::connection) fn field_name(&self) -> Option<&str> { + if self.0.name.is_null() { + None + } else { + unsafe { + Some(CStr::from_ptr(self.0.name).to_str().expect( + "Expect mysql field names to be UTF-8, because we \ + requested UTF-8 encoding on connection setup", + )) + } + } + } + + pub(in crate::mysql::connection) fn field_type(&self) -> ffi::enum_field_types { + self.0.type_ + } + + pub(in crate::mysql::connection) fn flags(&self) -> Flags { + Flags::from(self.0.flags) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/stmt/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/stmt/mod.rs new file mode 100644 index 000000000..e5b84b646 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/stmt/mod.rs @@ -0,0 +1,220 @@ +#![allow(unsafe_code)] // module uses ffi +use mysqlclient_sys as ffi; +use std::ffi::CStr; +use std::os::raw as libc; +use std::ptr::NonNull; + +use super::bind::{OutputBinds, PreparedStatementBinds}; +use crate::connection::statement_cache::MaybeCached; +use crate::mysql::MysqlType; +use crate::result::{DatabaseErrorKind, Error, QueryResult}; + +pub(super) mod iterator; +mod metadata; + +pub(super) use self::metadata::{MysqlFieldMetadata, StatementMetadata}; + +#[allow(dead_code, missing_debug_implementations)] +// https://github.com/rust-lang/rust/issues/81658 +pub struct Statement { + stmt: NonNull, + input_binds: Option, +} + +impl Statement { + pub(crate) fn new(stmt: NonNull) -> Self { + Statement { + stmt, + input_binds: None, + } + } + + pub fn prepare(&self, query: &str) -> QueryResult<()> { + unsafe { + ffi::mysql_stmt_prepare( + self.stmt.as_ptr(), + query.as_ptr() as *const libc::c_char, + query.len() as libc::c_ulong, + ); + } + self.did_an_error_occur() + } + + pub fn bind(&mut self, binds: Iter) -> QueryResult<()> + where + Iter: IntoIterator>)>, + { + let input_binds = PreparedStatementBinds::from_input_data(binds); + self.input_bind(input_binds) + } + + pub(super) fn input_bind( + &mut self, + mut input_binds: PreparedStatementBinds, + ) -> QueryResult<()> { + input_binds.with_mysql_binds(|bind_ptr| { + // This relies on the invariant that the current value of `self.input_binds` + // will not change without this function being called + unsafe { + ffi::mysql_stmt_bind_param(self.stmt.as_ptr(), bind_ptr); + } + }); + self.input_binds = Some(input_binds); + self.did_an_error_occur() + } + + fn last_error_message(&self) -> String { + unsafe { CStr::from_ptr(ffi::mysql_stmt_error(self.stmt.as_ptr())) } + .to_string_lossy() + .into_owned() + } + + pub(super) fn metadata(&self) -> QueryResult { + use crate::result::Error::DeserializationError; + + let result_ptr = unsafe { ffi::mysql_stmt_result_metadata(self.stmt.as_ptr()) }; + self.did_an_error_occur()?; + NonNull::new(result_ptr) + .map(StatementMetadata::new) + .ok_or_else(|| DeserializationError("No metadata exists".into())) + } + + pub(super) fn did_an_error_occur(&self) -> QueryResult<()> { + use crate::result::Error::DatabaseError; + + let error_message = self.last_error_message(); + if error_message.is_empty() { + Ok(()) + } else { + Err(DatabaseError( + self.last_error_type(), + Box::new(error_message), + )) + } + } + + fn last_error_type(&self) -> DatabaseErrorKind { + let last_error_number = unsafe { ffi::mysql_stmt_errno(self.stmt.as_ptr()) }; + // These values are not exposed by the C API, but are documented + // at https://dev.mysql.com/doc/refman/8.0/en/server-error-reference.html + // and are from the ANSI SQLSTATE standard + match last_error_number { + 1062 | 1586 | 1859 => DatabaseErrorKind::UniqueViolation, + 1216 | 1217 | 1451 | 1452 | 1830 | 1834 => DatabaseErrorKind::ForeignKeyViolation, + 1792 => DatabaseErrorKind::ReadOnlyTransaction, + 1048 | 1364 => DatabaseErrorKind::NotNullViolation, + 3819 => DatabaseErrorKind::CheckViolation, + 1213 => DatabaseErrorKind::SerializationFailure, + _ => DatabaseErrorKind::Unknown, + } + } + + /// If the pointers referenced by the `MYSQL_BIND` structures are invalidated, + /// you must call this function again before calling `mysql_stmt_fetch`. + pub unsafe fn bind_result(&self, binds: *mut ffi::MYSQL_BIND) -> QueryResult<()> { + ffi::mysql_stmt_bind_result(self.stmt.as_ptr(), binds); + self.did_an_error_occur() + } +} + +impl<'a> MaybeCached<'a, Statement> { + pub(super) fn execute_statement( + self, + binds: &mut OutputBinds, + ) -> QueryResult> { + unsafe { + binds.with_mysql_binds(|bind_ptr| self.bind_result(bind_ptr))?; + self.execute() + } + } + + /// This function should be called instead of `results` on queries which + /// have no return value. It should never be called on a statement on + /// which `results` has previously been called? + pub(super) unsafe fn execute(self) -> QueryResult> { + ffi::mysql_stmt_execute(self.stmt.as_ptr()); + self.did_an_error_occur()?; + ffi::mysql_stmt_store_result(self.stmt.as_ptr()); + let ret = StatementUse { inner: self }; + ret.inner.did_an_error_occur()?; + Ok(ret) + } +} + +impl Drop for Statement { + fn drop(&mut self) { + unsafe { ffi::mysql_stmt_close(self.stmt.as_ptr()) }; + } +} + +#[allow(missing_debug_implementations)] +pub(super) struct StatementUse<'a> { + inner: MaybeCached<'a, Statement>, +} + +impl StatementUse<'_> { + pub(in crate::mysql::connection) fn affected_rows(&self) -> QueryResult { + let affected_rows = unsafe { ffi::mysql_stmt_affected_rows(self.inner.stmt.as_ptr()) }; + affected_rows + .try_into() + .map_err(|e| Error::DeserializationError(Box::new(e))) + } + + /// This function should be called after `execute` only + /// otherwise it's not guaranteed to return a valid result + pub(in crate::mysql::connection) unsafe fn result_size(&mut self) -> QueryResult { + let size = ffi::mysql_stmt_num_rows(self.inner.stmt.as_ptr()); + usize::try_from(size).map_err(|e| Error::DeserializationError(Box::new(e))) + } + + pub(super) fn populate_row_buffers(&self, binds: &mut OutputBinds) -> QueryResult> { + let next_row_result = unsafe { ffi::mysql_stmt_fetch(self.inner.stmt.as_ptr()) }; + if next_row_result < 0 { + self.inner.did_an_error_occur().map(Some) + } else { + #[allow(clippy::cast_sign_loss)] // that's how it's supposed to be based on the API + match next_row_result as libc::c_uint { + ffi::MYSQL_NO_DATA => Ok(None), + ffi::MYSQL_DATA_TRUNCATED => binds.populate_dynamic_buffers(self).map(Some), + 0 => { + binds.update_buffer_lengths(); + Ok(Some(())) + } + _error => self.inner.did_an_error_occur().map(Some), + } + } + } + + pub(in crate::mysql::connection) unsafe fn fetch_column( + &self, + bind: &mut ffi::MYSQL_BIND, + idx: usize, + offset: usize, + ) -> QueryResult<()> { + ffi::mysql_stmt_fetch_column( + self.inner.stmt.as_ptr(), + bind, + idx.try_into() + .map_err(|e| Error::DeserializationError(Box::new(e)))?, + offset as libc::c_ulong, + ); + self.inner.did_an_error_occur() + } + + /// If the pointers referenced by the `MYSQL_BIND` structures are invalidated, + /// you must call this function again before calling `mysql_stmt_fetch`. + pub(in crate::mysql::connection) unsafe fn bind_result( + &self, + binds: *mut ffi::MYSQL_BIND, + ) -> QueryResult<()> { + self.inner.bind_result(binds) + } +} + +impl Drop for StatementUse<'_> { + fn drop(&mut self) { + unsafe { + ffi::mysql_stmt_free_result(self.inner.stmt.as_ptr()); + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/url.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/url.rs new file mode 100644 index 000000000..6ad11c4e4 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/connection/url.rs @@ -0,0 +1,423 @@ +extern crate percent_encoding; +extern crate url; + +use self::percent_encoding::percent_decode; +use self::url::{Host, Url}; +use std::collections::HashMap; +use std::ffi::{CStr, CString}; + +use crate::result::{ConnectionError, ConnectionResult}; + +use mysqlclient_sys::mysql_ssl_mode; + +bitflags::bitflags! { + #[derive(Clone, Copy)] + pub struct CapabilityFlags: u32 { + const CLIENT_LONG_PASSWORD = 0x00000001; + const CLIENT_FOUND_ROWS = 0x00000002; + const CLIENT_LONG_FLAG = 0x00000004; + const CLIENT_CONNECT_WITH_DB = 0x00000008; + const CLIENT_NO_SCHEMA = 0x00000010; + const CLIENT_COMPRESS = 0x00000020; + const CLIENT_ODBC = 0x00000040; + const CLIENT_LOCAL_FILES = 0x00000080; + const CLIENT_IGNORE_SPACE = 0x00000100; + const CLIENT_PROTOCOL_41 = 0x00000200; + const CLIENT_INTERACTIVE = 0x00000400; + const CLIENT_SSL = 0x00000800; + const CLIENT_IGNORE_SIGPIPE = 0x00001000; + const CLIENT_TRANSACTIONS = 0x00002000; + const CLIENT_RESERVED = 0x00004000; + const CLIENT_SECURE_CONNECTION = 0x00008000; + const CLIENT_MULTI_STATEMENTS = 0x00010000; + const CLIENT_MULTI_RESULTS = 0x00020000; + const CLIENT_PS_MULTI_RESULTS = 0x00040000; + const CLIENT_PLUGIN_AUTH = 0x00080000; + const CLIENT_CONNECT_ATTRS = 0x00100000; + const CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = 0x00200000; + const CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS = 0x00400000; + const CLIENT_SESSION_TRACK = 0x00800000; + const CLIENT_DEPRECATE_EOF = 0x01000000; + } +} + +pub(super) struct ConnectionOptions { + host: Option, + user: CString, + password: Option, + database: Option, + port: Option, + unix_socket: Option, + client_flags: CapabilityFlags, + ssl_mode: Option, + ssl_ca: Option, + ssl_cert: Option, + ssl_key: Option, +} + +impl ConnectionOptions { + pub(super) fn parse(database_url: &str) -> ConnectionResult { + let url = match Url::parse(database_url) { + Ok(url) => url, + Err(_) => return Err(connection_url_error()), + }; + + if url.scheme() != "mysql" { + return Err(connection_url_error()); + } + + if url.path_segments().map(Iterator::count).unwrap_or(0) > 1 { + return Err(connection_url_error()); + } + + let query_pairs = url.query_pairs().into_owned().collect::>(); + if query_pairs.contains_key("database") { + return Err(connection_url_error()); + } + + let unix_socket = match query_pairs.get("unix_socket") { + Some(v) => Some(CString::new(v.as_bytes())?), + _ => None, + }; + + let ssl_ca = match query_pairs.get("ssl_ca") { + Some(v) => Some(CString::new(v.as_bytes())?), + _ => None, + }; + + let ssl_cert = match query_pairs.get("ssl_cert") { + Some(v) => Some(CString::new(v.as_bytes())?), + _ => None, + }; + + let ssl_key = match query_pairs.get("ssl_key") { + Some(v) => Some(CString::new(v.as_bytes())?), + _ => None, + }; + + let ssl_mode = match query_pairs.get("ssl_mode") { + Some(v) => { + let ssl_mode = match v.to_lowercase().as_str() { + "disabled" => mysql_ssl_mode::SSL_MODE_DISABLED, + "preferred" => mysql_ssl_mode::SSL_MODE_PREFERRED, + "required" => mysql_ssl_mode::SSL_MODE_REQUIRED, + "verify_ca" => mysql_ssl_mode::SSL_MODE_VERIFY_CA, + "verify_identity" => mysql_ssl_mode::SSL_MODE_VERIFY_IDENTITY, + _ => { + let msg = "unknown ssl_mode"; + return Err(ConnectionError::InvalidConnectionUrl(msg.into())); + } + }; + Some(ssl_mode) + } + _ => None, + }; + + let host = match url.host() { + Some(Host::Ipv6(host)) => Some(CString::new(host.to_string())?), + Some(host) if host.to_string() == "localhost" && unix_socket.is_some() => None, + Some(host) => Some(CString::new(host.to_string())?), + None => None, + }; + let user = decode_into_cstring(url.username())?; + let password = match url.password() { + Some(password) => Some(decode_into_cstring(password)?), + None => None, + }; + + let database = match url.path_segments().and_then(|mut iter| iter.next()) { + Some("") | None => None, + Some(segment) => Some(CString::new(segment.as_bytes())?), + }; + + // this is not present in the database_url, using a default value + let client_flags = CapabilityFlags::CLIENT_FOUND_ROWS; + + Ok(ConnectionOptions { + host, + user, + password, + database, + port: url.port(), + client_flags, + ssl_mode, + unix_socket, + ssl_ca, + ssl_cert, + ssl_key, + }) + } + + pub(super) fn host(&self) -> Option<&CStr> { + self.host.as_deref() + } + + pub(super) fn user(&self) -> &CStr { + &self.user + } + + pub(super) fn password(&self) -> Option<&CStr> { + self.password.as_deref() + } + + pub(super) fn database(&self) -> Option<&CStr> { + self.database.as_deref() + } + + pub(super) fn port(&self) -> Option { + self.port + } + + pub(super) fn unix_socket(&self) -> Option<&CStr> { + self.unix_socket.as_deref() + } + + pub(super) fn ssl_ca(&self) -> Option<&CStr> { + self.ssl_ca.as_deref() + } + + pub(super) fn ssl_cert(&self) -> Option<&CStr> { + self.ssl_cert.as_deref() + } + + pub(super) fn ssl_key(&self) -> Option<&CStr> { + self.ssl_key.as_deref() + } + + pub(super) fn client_flags(&self) -> CapabilityFlags { + self.client_flags + } + + pub(super) fn ssl_mode(&self) -> Option { + self.ssl_mode + } +} + +fn decode_into_cstring(s: &str) -> ConnectionResult { + let decoded = percent_decode(s.as_bytes()) + .decode_utf8() + .map_err(|_| connection_url_error())?; + CString::new(decoded.as_bytes()).map_err(Into::into) +} + +fn connection_url_error() -> ConnectionError { + let msg = "MySQL connection URLs must be in the form \ + `mysql://[[user]:[password]@]host[:port][/database][?unix_socket=socket-path]`"; + ConnectionError::InvalidConnectionUrl(msg.into()) +} + +#[test] +fn urls_with_schemes_other_than_mysql_are_errors() { + assert!(ConnectionOptions::parse("postgres://localhost").is_err()); + assert!(ConnectionOptions::parse("http://localhost").is_err()); + assert!(ConnectionOptions::parse("file:///tmp/mysql.sock").is_err()); + assert!(ConnectionOptions::parse("socket:///tmp/mysql.sock").is_err()); + assert!(ConnectionOptions::parse("mysql://localhost?database=somedb").is_err()); + assert!(ConnectionOptions::parse("mysql://localhost").is_ok()); +} + +#[test] +fn urls_must_have_zero_or_one_path_segments() { + assert!(ConnectionOptions::parse("mysql://localhost/foo/bar").is_err()); + assert!(ConnectionOptions::parse("mysql://localhost/foo").is_ok()); +} + +#[test] +fn first_path_segment_is_treated_as_database() { + let foo_cstr = CString::new("foo").unwrap(); + let bar_cstr = CString::new("bar").unwrap(); + assert_eq!( + Some(&*foo_cstr), + ConnectionOptions::parse("mysql://localhost/foo") + .unwrap() + .database() + ); + assert_eq!( + Some(&*bar_cstr), + ConnectionOptions::parse("mysql://localhost/bar") + .unwrap() + .database() + ); + assert_eq!( + None, + ConnectionOptions::parse("mysql://localhost") + .unwrap() + .database() + ); +} + +#[test] +fn userinfo_should_be_percent_decode() { + use self::percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; + const USERINFO_ENCODE_SET: &AsciiSet = &CONTROLS + .add(b' ') + .add(b'"') + .add(b'<') + .add(b'>') + .add(b'`') + .add(b'#') + .add(b'?') + .add(b'{') + .add(b'}') + .add(b'/') + .add(b':') + .add(b';') + .add(b'=') + .add(b'@') + .add(b'[') + .add(b'\\') + .add(b']') + .add(b'^') + .add(b'|'); + + let username = "x#gfuL?4Zuj{n73m}eeJt0"; + let encoded_username = utf8_percent_encode(username, USERINFO_ENCODE_SET); + + let password = "x/gfuL?4Zuj{n73m}eeJt1"; + let encoded_password = utf8_percent_encode(password, USERINFO_ENCODE_SET); + + let db_url = format!("mysql://{encoded_username}:{encoded_password}@localhost/bar",); + let db_url = Url::parse(&db_url).unwrap(); + + let conn_opts = ConnectionOptions::parse(db_url.as_str()).unwrap(); + let username = CString::new(username.as_bytes()).unwrap(); + let password = CString::new(password.as_bytes()).unwrap(); + assert_eq!(username, conn_opts.user); + assert_eq!(password, conn_opts.password.unwrap()); +} + +#[test] +fn ipv6_host_not_wrapped_in_brackets() { + let host1 = CString::new("::1").unwrap(); + let host2 = CString::new("2001:db8:85a3::8a2e:370:7334").unwrap(); + + assert_eq!( + Some(&*host1), + ConnectionOptions::parse("mysql://[::1]").unwrap().host() + ); + assert_eq!( + Some(&*host2), + ConnectionOptions::parse("mysql://[2001:db8:85a3::8a2e:370:7334]") + .unwrap() + .host() + ); +} + +#[test] +fn unix_socket_tests() { + let unix_socket = "/var/run/mysqld.sock"; + let username = "foo"; + let password = "bar"; + let db_url = format!("mysql://{username}:{password}@localhost?unix_socket={unix_socket}",); + let conn_opts = ConnectionOptions::parse(db_url.as_str()).unwrap(); + let cstring = |s| CString::new(s).unwrap(); + assert_eq!(None, conn_opts.host); + assert_eq!(None, conn_opts.port); + assert_eq!(cstring(username), conn_opts.user); + assert_eq!(cstring(password), conn_opts.password.unwrap()); + assert_eq!( + CString::new(unix_socket).unwrap(), + conn_opts.unix_socket.unwrap() + ); +} + +#[test] +fn ssl_ca_tests() { + let ssl_ca = "/etc/ssl/certs/ca-certificates.crt"; + let username = "foo"; + let password = "bar"; + let db_url = format!("mysql://{username}:{password}@localhost?ssl_ca={ssl_ca}",); + let conn_opts = ConnectionOptions::parse(db_url.as_str()).unwrap(); + let cstring = |s| CString::new(s).unwrap(); + assert_eq!(Some(cstring("localhost")), conn_opts.host); + assert_eq!(None, conn_opts.port); + assert_eq!(cstring(username), conn_opts.user); + assert_eq!(cstring(password), conn_opts.password.unwrap()); + assert_eq!(CString::new(ssl_ca).unwrap(), conn_opts.ssl_ca.unwrap()); + + let url_with_unix_str_and_ssl_ca = format!( + "mysql://{username}:{password}@localhost?unix_socket=/var/run/mysqld.sock&ssl_ca={ssl_ca}" + ); + + let conn_opts2 = ConnectionOptions::parse(url_with_unix_str_and_ssl_ca.as_str()).unwrap(); + assert_eq!(None, conn_opts2.host); + assert_eq!(None, conn_opts2.port); + assert_eq!(CString::new(ssl_ca).unwrap(), conn_opts2.ssl_ca.unwrap()); +} + +#[test] +fn ssl_cert_tests() { + let ssl_cert = "/etc/ssl/certs/client-cert.crt"; + let username = "foo"; + let password = "bar"; + let db_url = format!("mysql://{username}:{password}@localhost?ssl_cert={ssl_cert}"); + let conn_opts = ConnectionOptions::parse(db_url.as_str()).unwrap(); + let cstring = |s| CString::new(s).unwrap(); + assert_eq!(Some(cstring("localhost")), conn_opts.host); + assert_eq!(None, conn_opts.port); + assert_eq!(cstring(username), conn_opts.user); + assert_eq!(cstring(password), conn_opts.password.unwrap()); + assert_eq!(CString::new(ssl_cert).unwrap(), conn_opts.ssl_cert.unwrap()); + + let url_with_unix_str_and_ssl_cert = format!( + "mysql://{username}:{password}@localhost?unix_socket=/var/run/mysqld.sock&ssl_cert={ssl_cert}" + ); + + let conn_opts2 = ConnectionOptions::parse(url_with_unix_str_and_ssl_cert.as_str()).unwrap(); + assert_eq!(None, conn_opts2.host); + assert_eq!(None, conn_opts2.port); + assert_eq!( + CString::new(ssl_cert).unwrap(), + conn_opts2.ssl_cert.unwrap() + ); +} + +#[test] +fn ssl_key_tests() { + let ssl_key = "/etc/ssl/certs/client-key.crt"; + let username = "foo"; + let password = "bar"; + let db_url = format!("mysql://{username}:{password}@localhost?ssl_key={ssl_key}"); + let conn_opts = ConnectionOptions::parse(db_url.as_str()).unwrap(); + let cstring = |s| CString::new(s).unwrap(); + assert_eq!(Some(cstring("localhost")), conn_opts.host); + assert_eq!(None, conn_opts.port); + assert_eq!(cstring(username), conn_opts.user); + assert_eq!(cstring(password), conn_opts.password.unwrap()); + assert_eq!(CString::new(ssl_key).unwrap(), conn_opts.ssl_key.unwrap()); + + let url_with_unix_str_and_ssl_key = format!( + "mysql://{username}:{password}@localhost?unix_socket=/var/run/mysqld.sock&ssl_key={ssl_key}" + ); + + let conn_opts2 = ConnectionOptions::parse(url_with_unix_str_and_ssl_key.as_str()).unwrap(); + assert_eq!(None, conn_opts2.host); + assert_eq!(None, conn_opts2.port); + assert_eq!(CString::new(ssl_key).unwrap(), conn_opts2.ssl_key.unwrap()); +} + +#[test] +fn ssl_mode() { + let ssl_mode = |url| ConnectionOptions::parse(url).unwrap().ssl_mode(); + assert_eq!(ssl_mode("mysql://localhost"), None); + assert_eq!( + ssl_mode("mysql://localhost?ssl_mode=disabled"), + Some(mysql_ssl_mode::SSL_MODE_DISABLED) + ); + assert_eq!( + ssl_mode("mysql://localhost?ssl_mode=PREFERRED"), + Some(mysql_ssl_mode::SSL_MODE_PREFERRED) + ); + assert_eq!( + ssl_mode("mysql://localhost?ssl_mode=required"), + Some(mysql_ssl_mode::SSL_MODE_REQUIRED) + ); + assert_eq!( + ssl_mode("mysql://localhost?ssl_mode=VERIFY_CA"), + Some(mysql_ssl_mode::SSL_MODE_VERIFY_CA) + ); + assert_eq!( + ssl_mode("mysql://localhost?ssl_mode=verify_identity"), + Some(mysql_ssl_mode::SSL_MODE_VERIFY_IDENTITY) + ); +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/mod.rs new file mode 100644 index 000000000..72d369500 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/mod.rs @@ -0,0 +1,34 @@ +//! Provides types and functions related to working with MySQL +//! +//! Much of this module is re-exported from database agnostic locations. +//! However, if you are writing code specifically to extend Diesel on +//! MySQL, you may need to work with this module directly. + +pub(crate) mod backend; +#[cfg(feature = "mysql")] +mod connection; +mod value; + +pub(crate) mod query_builder; +mod types; + +pub use self::backend::{Mysql, MysqlType}; +#[cfg(feature = "mysql")] +pub use self::connection::MysqlConnection; +pub use self::query_builder::MysqlQueryBuilder; +pub use self::value::{MysqlValue, NumericRepresentation}; + +/// Data structures for MySQL types which have no corresponding Rust type +/// +/// Most of these types are used to implement `ToSql` and `FromSql` for higher +/// level types. +pub mod data_types { + #[doc(inline)] + pub use super::types::date_and_time::{MysqlTime, MysqlTimestampType}; +} + +/// MySQL specific sql types +pub mod sql_types { + #[doc(inline)] + pub use super::types::{Datetime, Unsigned}; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/query_builder/limit_offset.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/query_builder/limit_offset.rs new file mode 100644 index 000000000..5a1fe59cc --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/query_builder/limit_offset.rs @@ -0,0 +1,104 @@ +use crate::mysql::Mysql; +use crate::query_builder::limit_clause::{LimitClause, NoLimitClause}; +use crate::query_builder::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +use crate::query_builder::offset_clause::{NoOffsetClause, OffsetClause}; +use crate::query_builder::{AstPass, IntoBoxedClause, QueryFragment}; +use crate::result::QueryResult; + +impl QueryFragment for LimitOffsetClause { + fn walk_ast<'b>(&'b self, _out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, NoOffsetClause> +where + LimitClause: QueryFragment, +{ + fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + self.limit_clause.walk_ast(out)?; + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, OffsetClause> +where + LimitClause: QueryFragment, + OffsetClause: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + self.limit_clause.walk_ast(out.reborrow())?; + self.offset_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl QueryFragment for BoxedLimitOffsetClause<'_, Mysql> { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + match (self.limit.as_ref(), self.offset.as_ref()) { + (Some(limit), Some(offset)) => { + limit.walk_ast(out.reborrow())?; + offset.walk_ast(out.reborrow())?; + } + (Some(limit), None) => { + limit.walk_ast(out.reborrow())?; + } + (None, Some(offset)) => { + // Mysql requires a limit clause in front of any offset clause + // The documentation proposes the following: + // > To retrieve all rows from a certain offset up to the end of the + // > result set, you can use some large number for the second parameter. + // https://dev.mysql.com/doc/refman/8.0/en/select.html + // Therefore we just use u64::MAX as limit here + // That does not result in any limitations because mysql only supports + // up to 64TB of data per table. Assuming 1 bit per row this means + // 1024 * 1024 * 1024 * 1024 * 8 = 562.949.953.421.312 rows which is smaller + // than 2^64 = 18.446.744.073.709.551.615 + out.push_sql(" LIMIT 18446744073709551615 "); + offset.walk_ast(out.reborrow())?; + } + (None, None) => {} + } + Ok(()) + } +} + +impl<'a> IntoBoxedClause<'a, Mysql> for LimitOffsetClause { + type BoxedClause = BoxedLimitOffsetClause<'a, Mysql>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: None, + offset: None, + } + } +} + +impl<'a, L> IntoBoxedClause<'a, Mysql> for LimitOffsetClause, NoOffsetClause> +where + L: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Mysql>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: None, + } + } +} + +impl<'a, L, O> IntoBoxedClause<'a, Mysql> for LimitOffsetClause, OffsetClause> +where + L: QueryFragment + Send + 'a, + O: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Mysql>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: Some(Box::new(self.offset_clause)), + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/query_builder/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/query_builder/mod.rs new file mode 100644 index 000000000..7feb05f02 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/query_builder/mod.rs @@ -0,0 +1,44 @@ +use super::backend::Mysql; +use crate::query_builder::QueryBuilder; +use crate::result::QueryResult; + +#[doc(inline)] +pub use self::query_fragment_impls::DuplicatedKeys; + +mod limit_offset; +mod query_fragment_impls; + +/// The MySQL query builder +#[allow(missing_debug_implementations)] +#[derive(Default)] +pub struct MysqlQueryBuilder { + sql: String, +} + +impl MysqlQueryBuilder { + /// Constructs a new query builder with an empty query + pub fn new() -> Self { + MysqlQueryBuilder::default() + } +} + +impl QueryBuilder for MysqlQueryBuilder { + fn push_sql(&mut self, sql: &str) { + self.sql.push_str(sql); + } + + fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> { + self.push_sql("`"); + self.push_sql(&identifier.replace('`', "``")); + self.push_sql("`"); + Ok(()) + } + + fn push_bind_param(&mut self) { + self.push_sql("?"); + } + + fn finish(self) -> String { + self.sql + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/query_builder/query_fragment_impls.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/query_builder/query_fragment_impls.rs new file mode 100644 index 000000000..daa450b9e --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/query_builder/query_fragment_impls.rs @@ -0,0 +1,220 @@ +use crate::expression::operators::Concat; +use crate::mysql::backend::MysqlOnConflictClause; +use crate::mysql::Mysql; +use crate::query_builder::insert_statement::DefaultValues; +use crate::query_builder::locking_clause::{ForShare, ForUpdate, NoModifier, NoWait, SkipLocked}; +use crate::query_builder::nodes::StaticQueryFragment; +use crate::query_builder::upsert::into_conflict_clause::OnConflictSelectWrapper; +use crate::query_builder::upsert::on_conflict_actions::{DoNothing, DoUpdate}; +use crate::query_builder::upsert::on_conflict_clause::OnConflictValues; +use crate::query_builder::upsert::on_conflict_target::{ConflictTarget, OnConflictTarget}; +use crate::query_builder::where_clause::NoWhereClause; +use crate::query_builder::{AstPass, QueryFragment}; +use crate::result::QueryResult; +use crate::{Column, Table}; + +impl QueryFragment for ForUpdate { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + out.push_sql(" FOR UPDATE"); + Ok(()) + } +} + +impl QueryFragment for ForShare { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + out.push_sql(" FOR SHARE"); + Ok(()) + } +} + +impl QueryFragment for NoModifier { + fn walk_ast<'b>(&'b self, _out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for SkipLocked { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + out.push_sql(" SKIP LOCKED"); + Ok(()) + } +} + +impl QueryFragment for NoWait { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + out.push_sql(" NOWAIT"); + Ok(()) + } +} + +impl QueryFragment for DefaultValues { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + out.push_sql("() VALUES ()"); + Ok(()) + } +} + +impl QueryFragment for Concat +where + L: QueryFragment, + R: QueryFragment, +{ + fn walk_ast<'b>( + &'b self, + mut out: crate::query_builder::AstPass<'_, 'b, Mysql>, + ) -> crate::result::QueryResult<()> { + out.push_sql("CONCAT("); + self.left.walk_ast(out.reborrow())?; + out.push_sql(","); + self.right.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +impl QueryFragment for DoNothing +where + T: Table + StaticQueryFragment, + T::Component: QueryFragment, + T::PrimaryKey: DoNothingClauseHelper, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + out.push_sql(" UPDATE "); + T::PrimaryKey::walk_ast::(out.reborrow())?; + Ok(()) + } +} + +impl QueryFragment for DoUpdate +where + T: QueryFragment, + Tab: Table + StaticQueryFragment, + Tab::PrimaryKey: DoNothingClauseHelper, + Tab::Component: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + out.push_sql(" UPDATE "); + if self.changeset.is_noop(out.backend())? { + Tab::PrimaryKey::walk_ast::(out.reborrow())?; + } else { + self.changeset.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +impl QueryFragment + for OnConflictValues +where + Values: QueryFragment, + Target: QueryFragment, + Action: QueryFragment, + NoWhereClause: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + self.values.walk_ast(out.reborrow())?; + out.push_sql(" ON DUPLICATE KEY"); + self.target.walk_ast(out.reborrow())?; + self.action.walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out)?; + Ok(()) + } +} + +/// A marker type signaling that the given `ON CONFLICT` clause +/// uses mysql's `ON DUPLICATE KEY` syntax that triggers on +/// all unique constraints +/// +/// See [`InsertStatement::on_conflict`](crate::query_builder::InsertStatement::on_conflict) +/// for examples +#[derive(Debug, Copy, Clone)] +pub struct DuplicatedKeys; + +impl OnConflictTarget for ConflictTarget {} + +impl QueryFragment for ConflictTarget { + fn walk_ast<'b>(&'b self, _out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for OnConflictSelectWrapper +where + S: QueryFragment, +{ + fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, crate::mysql::Mysql>) -> QueryResult<()> { + self.0.walk_ast(out) + } +} + +/// This is a helper trait +/// that provideds a fake `DO NOTHING` clause +/// based on reassigning the possible +/// composite primary key to itself +trait DoNothingClauseHelper { + fn walk_ast(out: AstPass<'_, '_, Mysql>) -> QueryResult<()> + where + T: StaticQueryFragment, + T::Component: QueryFragment; +} + +impl DoNothingClauseHelper for C +where + C: Column, +{ + fn walk_ast(mut out: AstPass<'_, '_, Mysql>) -> QueryResult<()> + where + T: StaticQueryFragment, + T::Component: QueryFragment, + { + T::STATIC_COMPONENT.walk_ast(out.reborrow())?; + out.push_sql("."); + out.push_identifier(C::NAME)?; + out.push_sql(" = "); + T::STATIC_COMPONENT.walk_ast(out.reborrow())?; + out.push_sql("."); + out.push_identifier(C::NAME)?; + Ok(()) + } +} + +macro_rules! do_nothing_for_composite_keys { + ($( + $Tuple:tt { + $(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)+ + } + )+) => { + $( + impl<$($T,)*> DoNothingClauseHelper for ($($T,)*) + where $($T: Column,)* + { + fn walk_ast
(mut out: AstPass<'_, '_, Mysql>) -> QueryResult<()> + where + Table: StaticQueryFragment, + Table::Component: QueryFragment, + { + let mut first = true; + $( + #[allow(unused_assignments)] + if first { + first = false; + } else { + out.push_sql(", "); + } + Table::STATIC_COMPONENT.walk_ast(out.reborrow())?; + out.push_sql("."); + out.push_identifier($T::NAME)?; + out.push_sql(" = "); + Table::STATIC_COMPONENT.walk_ast(out.reborrow())?; + out.push_sql("."); + out.push_identifier($T::NAME)?; + )* + Ok(()) + } + } + )* + } +} + +diesel_derives::__diesel_for_each_tuple!(do_nothing_for_composite_keys); diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/mysql/types/date_and_time/chrono.rs b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/types/date_and_time/chrono.rs new file mode 100644 index 000000000..80f188f79 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/mysql/types/date_and_time/chrono.rs @@ -0,0 +1,250 @@ +use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike}; +use std::os::raw as libc; + +use crate::deserialize::{self, FromSql}; +use crate::mysql::{Mysql, MysqlValue}; +use crate::serialize::{self, Output, ToSql}; +use crate::sql_types::{Date, Datetime, Time, Timestamp}; + +use super::{MysqlTime, MysqlTimestampType}; + +#[cfg(all(feature = "chrono", feature = "mysql_backend"))] +impl ToSql for NaiveDateTime { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + >::to_sql(self, out) + } +} + +#[cfg(all(feature = "chrono", feature = "mysql_backend"))] +impl FromSql for NaiveDateTime { + fn from_sql(bytes: MysqlValue<'_>) -> deserialize::Result { + >::from_sql(bytes) + } +} + +#[cfg(all(feature = "chrono", feature = "mysql_backend"))] +impl ToSql for NaiveDateTime { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + let mysql_time = MysqlTime { + year: self.year().try_into()?, + month: self.month() as libc::c_uint, + day: self.day() as libc::c_uint, + hour: self.hour() as libc::c_uint, + minute: self.minute() as libc::c_uint, + second: self.second() as libc::c_uint, + #[allow(deprecated)] // otherwise we would need to bump our minimal chrono version + second_part: libc::c_ulong::from(self.timestamp_subsec_micros()), + neg: false, + time_type: MysqlTimestampType::MYSQL_TIMESTAMP_DATETIME, + time_zone_displacement: 0, + }; + + >::to_sql(&mysql_time, &mut out.reborrow()) + } +} + +#[cfg(all(feature = "chrono", feature = "mysql_backend"))] +impl FromSql for NaiveDateTime { + fn from_sql(bytes: MysqlValue<'_>) -> deserialize::Result { + let mysql_time = >::from_sql(bytes)?; + + let micro = mysql_time.second_part.try_into()?; + NaiveDate::from_ymd_opt( + mysql_time.year.try_into()?, + mysql_time.month, + mysql_time.day, + ) + .and_then(|v| { + v.and_hms_micro_opt(mysql_time.hour, mysql_time.minute, mysql_time.second, micro) + }) + .ok_or_else(|| format!("Cannot parse this date: {mysql_time:?}").into()) + } +} + +#[cfg(all(feature = "chrono", feature = "mysql_backend"))] +impl ToSql for NaiveTime { + fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Mysql>) -> serialize::Result { + let mysql_time = MysqlTime { + hour: self.hour() as libc::c_uint, + minute: self.minute() as libc::c_uint, + second: self.second() as libc::c_uint, + day: 0, + month: 0, + second_part: 0, + year: 0, + neg: false, + time_type: MysqlTimestampType::MYSQL_TIMESTAMP_TIME, + time_zone_displacement: 0, + }; + + >::to_sql(&mysql_time, &mut out.reborrow()) + } +} + +#[cfg(all(feature = "chrono", feature = "mysql_backend"))] +impl FromSql for NaiveTime { + fn from_sql(bytes: MysqlValue<'_>) -> deserialize::Result { + let mysql_time = >::from_sql(bytes)?; + NaiveTime::from_hms_opt(mysql_time.hour, mysql_time.minute, mysql_time.second) + .ok_or_else(|| format!("Unable to convert {mysql_time:?} to chrono").into()) + } +} + +#[cfg(all(feature = "chrono", feature = "mysql_backend"))] +impl ToSql for NaiveDate { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + let mysql_time = MysqlTime { + year: self.year().try_into()?, + month: self.month() as libc::c_uint, + day: self.day() as libc::c_uint, + hour: 0, + minute: 0, + second: 0, + second_part: 0, + neg: false, + time_type: MysqlTimestampType::MYSQL_TIMESTAMP_DATE, + time_zone_displacement: 0, + }; + + >::to_sql(&mysql_time, &mut out.reborrow()) + } +} + +#[cfg(all(feature = "chrono", feature = "mysql_backend"))] +impl FromSql for NaiveDate { + fn from_sql(bytes: MysqlValue<'_>) -> deserialize::Result { + let mysql_time = >::from_sql(bytes)?; + NaiveDate::from_ymd_opt( + mysql_time.year.try_into()?, + mysql_time.month, + mysql_time.day, + ) + .ok_or_else(|| format!("Unable to convert {mysql_time:?} to chrono").into()) + } +} + +#[cfg(test)] +mod tests { + extern crate chrono; + extern crate dotenvy; + + use self::chrono::{Duration, NaiveDate, NaiveTime, Utc}; + + use crate::dsl::{now, sql}; + use crate::prelude::*; + use crate::select; + use crate::sql_types::{Date, Datetime, Time, Timestamp}; + use crate::test_helpers::connection; + + #[test] + fn unix_epoch_encodes_correctly() { + let connection = &mut connection(); + let time = NaiveDate::from_ymd_opt(1970, 1, 1) + .unwrap() + .and_hms_opt(0, 0, 0) + .unwrap(); + let query = select(sql::("CAST('1970-01-01' AS DATETIME)").eq(time)); + assert!(query.get_result::(connection).unwrap()); + let query = select(sql::("CAST('1970-01-01' AS DATETIME)").eq(time)); + assert!(query.get_result::(connection).unwrap()); + } + + #[test] + fn unix_epoch_decodes_correctly() { + let connection = &mut connection(); + let time = NaiveDate::from_ymd_opt(1970, 1, 1) + .unwrap() + .and_hms_opt(0, 0, 0) + .unwrap(); + let epoch_from_sql = + select(sql::("CAST('1970-01-01' AS DATETIME)")).get_result(connection); + assert_eq!(Ok(time), epoch_from_sql); + let epoch_from_sql = + select(sql::("CAST('1970-01-01' AS DATETIME)")).get_result(connection); + assert_eq!(Ok(time), epoch_from_sql); + } + + #[test] + fn times_relative_to_now_encode_correctly() { + let connection = &mut connection(); + let time = Utc::now().naive_utc() + Duration::try_days(1).unwrap(); + let query = select(now.lt(time)); + assert!(query.get_result::(connection).unwrap()); + + let time = Utc::now().naive_utc() - Duration::try_days(1).unwrap(); + let query = select(now.gt(time)); + assert!(query.get_result::(connection).unwrap()); + } + + #[test] + fn times_of_day_encode_correctly() { + let connection = &mut connection(); + + let midnight = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + let query = select(sql::
,)* + ($($ST,)*): CopyTarget, + $($TT: ToSql<$T, Pg>,)* + { + type Target = ($($ST,)*); + + // statically known to always fit + // as we don't support more than 128 columns + #[allow(clippy::cast_possible_truncation)] + const COLUMN_COUNT: i16 = $Tuple as i16; + + fn write_to_buffer(&self, idx: i16, out: &mut Vec) -> QueryResult { + use crate::query_builder::ByteWrapper; + use crate::serialize::Output; + + let values = &self.values; + match idx { + $($idx =>{ + let item = &values.$idx.expr.item; + let is_null = ToSql::<$T, Pg>::to_sql( + item, + &mut Output::new( ByteWrapper(out), &mut Dummy as _) + ).map_err(crate::result::Error::SerializationError)?; + return Ok(is_null); + })* + _ => unreachable!(), + } + } + } + + impl<'a, T, $($ST,)* $($T,)* $($TT,)*> CopyFromInsertableHelper for ValuesClause< + ($(ColumnInsertValue<$ST, &'a Bound<$T, $TT>>,)*), + T> + where + T: Table, + $($ST: Column
,)* + ($($ST,)*): CopyTarget, + $($TT: ToSql<$T, Pg>,)* + { + type Target = ($($ST,)*); + + // statically known to always fit + // as we don't support more than 128 columns + #[allow(clippy::cast_possible_truncation)] + const COLUMN_COUNT: i16 = $Tuple as i16; + + fn write_to_buffer(&self, idx: i16, out: &mut Vec) -> QueryResult { + use crate::query_builder::ByteWrapper; + use crate::serialize::Output; + + let values = &self.values; + match idx { + $($idx =>{ + let item = &values.$idx.expr.item; + let is_null = ToSql::<$T, Pg>::to_sql( + item, + &mut Output::new( ByteWrapper(out), &mut Dummy as _) + ).map_err(crate::result::Error::SerializationError)?; + return Ok(is_null); + })* + _ => unreachable!(), + } + } + } + )* + } +} + +diesel_derives::__diesel_for_each_tuple!(impl_copy_from_insertable_helper_for_values_clause); + +#[derive(Debug)] +pub struct InsertableWrapper(Option); + +impl CopyFromExpression for InsertableWrapper +where + I: Insertable, T, QId, STATIC_QUERY_ID>>, + V: CopyFromInsertableHelper, +{ + type Error = crate::result::Error; + + fn callback(&mut self, copy: &mut impl std::io::Write) -> Result<(), Self::Error> { + let io_result_mapper = |e| crate::result::Error::DeserializationError(Box::new(e)); + // see https://www.postgresql.org/docs/current/sql-copy.html for + // a description of the binary format + // + // We don't write oids + + // write the header + copy.write_all(&super::COPY_MAGIC_HEADER) + .map_err(io_result_mapper)?; + copy.write_i32::(0) + .map_err(io_result_mapper)?; + copy.write_i32::(0) + .map_err(io_result_mapper)?; + // write the data + // we reuse the same buffer here again and again + // as we expect the data to be "similar" + // this skips reallocating + let mut buffer = Vec::::new(); + let values = self + .0 + .take() + .expect("We only call this callback once") + .values(); + for i in values.values { + // column count + buffer + .write_i16::(V::COLUMN_COUNT) + .map_err(io_result_mapper)?; + for idx in 0..V::COLUMN_COUNT { + // first write the null indicator as dummy value + buffer + .write_i32::(-1) + .map_err(io_result_mapper)?; + let len_before = buffer.len(); + let is_null = i.write_to_buffer(idx, &mut buffer)?; + if is_null == IsNull::No { + // fill in the length afterwards + let len_after = buffer.len(); + let diff = (len_after - len_before) + .try_into() + .map_err(|e| crate::result::Error::SerializationError(Box::new(e)))?; + let bytes = i32::to_be_bytes(diff); + for (b, t) in bytes.into_iter().zip(&mut buffer[len_before - 4..]) { + *t = b; + } + } + } + copy.write_all(&buffer).map_err(io_result_mapper)?; + buffer.clear(); + } + // write the trailer + copy.write_i16::(-1) + .map_err(io_result_mapper)?; + Ok(()) + } + + fn options(&self) -> &CopyFromOptions { + &CopyFromOptions { + common: CommonOptions { + format: Some(CopyFormat::Binary), + freeze: None, + delimiter: None, + null: None, + quote: None, + escape: None, + }, + default: None, + header: None, + } + } + + fn walk_target<'b>( + &'b self, + pass: crate::query_builder::AstPass<'_, 'b, Pg>, + ) -> crate::QueryResult<()> { + ::Target::walk_target(pass) + } +} + +/// The structure returned by [`copy_from`] +/// +/// The [`from_raw_data`] and the [`from_insertable`] methods allow +/// to configure the data copied into the database +/// +/// The `with_*` methods allow to configure the settings used for the +/// copy statement. +/// +/// [`from_raw_data`]: CopyFromQuery::from_raw_data +/// [`from_insertable`]: CopyFromQuery::from_insertable +#[derive(Debug)] +#[must_use = "`COPY FROM` statements are only executed when calling `.execute()`."] +#[cfg(feature = "postgres_backend")] +pub struct CopyFromQuery { + table: T, + action: Action, +} + +impl CopyFromQuery +where + T: Table, +{ + /// Copy data into the database by directly providing the data in the corresponding format + /// + /// `target` specifies the column selection that is the target of the `COPY FROM` statement + /// `action` expects a callback which accepts a [`std::io::Write`] argument. The necessary format + /// accepted by this writer sink depends on the options provided via the `with_*` methods + #[allow(clippy::wrong_self_convention)] // the sql struct is named that way + pub fn from_raw_data(self, _target: C, action: F) -> CopyFromQuery> + where + C: CopyTarget
, + F: Fn(&mut dyn std::io::Write) -> Result<(), E>, + { + CopyFromQuery { + table: self.table, + action: CopyFrom { + p: PhantomData, + options: Default::default(), + copy_callback: action, + }, + } + } + + /// Copy a set of insertable values into the database. + /// + /// The `insertable` argument is expected to be a `Vec`, `&[I]` or similar, where `I` + /// needs to implement `Insertable`. If you use the [`#[derive(Insertable)]`](derive@crate::prelude::Insertable) + /// derive macro make sure to also set the `#[diesel(treat_none_as_default_value = false)]` option + /// to disable the default value handling otherwise implemented by `#[derive(Insertable)]`. + /// + /// This uses the binary format. It internally configures the correct + /// set of settings and does not allow to set other options + #[allow(clippy::wrong_self_convention)] // the sql struct is named that way + pub fn from_insertable(self, insertable: I) -> CopyFromQuery> + where + InsertableWrapper: CopyFromExpression, + { + CopyFromQuery { + table: self.table, + action: InsertableWrapper(Some(insertable)), + } + } +} + +impl CopyFromQuery> { + /// The format used for the copy statement + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_format(mut self, format: CopyFormat) -> Self { + self.action.options.common.format = Some(format); + self + } + + /// Whether or not the `freeze` option is set + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_freeze(mut self, freeze: bool) -> Self { + self.action.options.common.freeze = Some(freeze); + self + } + + /// Which delimiter should be used for textual input formats + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_delimiter(mut self, delimiter: char) -> Self { + self.action.options.common.delimiter = Some(delimiter); + self + } + + /// Which string should be used in place of a `NULL` value + /// for textual input formats + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_null(mut self, null: impl Into) -> Self { + self.action.options.common.null = Some(null.into()); + self + } + + /// Which quote character should be used for textual input formats + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_quote(mut self, quote: char) -> Self { + self.action.options.common.quote = Some(quote); + self + } + + /// Which escape character should be used for textual input formats + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_escape(mut self, escape: char) -> Self { + self.action.options.common.escape = Some(escape); + self + } + + /// Which string should be used to indicate that + /// the `default` value should be used in place of that string + /// for textual formats + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + /// + /// (This parameter was added with PostgreSQL 16) + pub fn with_default(mut self, default: impl Into) -> Self { + self.action.options.default = Some(default.into()); + self + } + + /// Is a header provided as part of the textual input or not + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_header(mut self, header: CopyHeader) -> Self { + self.action.options.header = Some(header); + self + } +} + +/// A custom execute function tailored for `COPY FROM` statements +/// +/// This trait can be used to execute `COPY FROM` queries constructed +/// via [`copy_from]` +pub trait ExecuteCopyFromDsl +where + C: Connection, +{ + /// The error type returned by the execute function + type Error: std::error::Error; + + /// See the trait documentation for details + fn execute(self, conn: &mut C) -> Result; +} + +#[cfg(feature = "postgres")] +impl ExecuteCopyFromDsl for CopyFromQuery +where + A: CopyFromExpression, +{ + type Error = A::Error; + + fn execute(self, conn: &mut crate::PgConnection) -> Result { + conn.copy_from::(self.action) + } +} + +#[cfg(feature = "r2d2")] +impl ExecuteCopyFromDsl>> + for CopyFromQuery +where + A: CopyFromExpression, + C: crate::r2d2::R2D2Connection + 'static, + Self: ExecuteCopyFromDsl, +{ + type Error = >::Error; + + fn execute( + self, + conn: &mut crate::r2d2::PooledConnection>, + ) -> Result { + self.execute(&mut **conn) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct NotSet; + +/// Creates a `COPY FROM` statement +/// +/// This function constructs `COPY FROM` statement which copies data +/// *from* a source into the database. It's designed to move larger +/// amounts of data into the database. +/// +/// This function accepts a target table as argument. +/// +/// There are two ways to construct a `COPY FROM` statement with +/// diesel: +/// +/// * By providing a `Vec` where `I` implements `Insertable` for the +/// given table +/// * By providing a target selection (column list or table name) +/// and a callback that provides the data +/// +/// The first variant uses the `BINARY` format internally to send +/// the provided data efficiently to the database. It automatically +/// sets the right options and does not allow changing them. +/// Use [`CopyFromQuery::from_insertable`] for this. +/// +/// The second variant allows you to control the behaviour +/// of the generated `COPY FROM` statement in detail. It can +/// be setup via the [`CopyFromQuery::from_raw_data`] function. +/// The callback accepts an opaque object as argument that allows +/// to write the corresponding data to the database. The exact +/// format depends on the settings chosen by the various +/// `CopyFromQuery::with_*` methods. See +/// [the postgresql documentation](https://www.postgresql.org/docs/current/sql-copy.html) +/// for more details about the expected formats. +/// +/// If you don't have any specific needs you should prefer +/// using the more convenient first variant. +/// +/// This functionality is postgresql specific. +/// +/// # Examples +/// +/// ## Via [`CopyFromQuery::from_insertable`] +/// +/// ```rust +/// # include!("../../../doctest_setup.rs"); +/// # use crate::schema::users; +/// +/// #[derive(Insertable)] +/// #[diesel(table_name = users)] +/// #[diesel(treat_none_as_default_value = false)] +/// struct NewUser { +/// name: &'static str, +/// } +/// +/// # fn run_test() -> QueryResult<()> { +/// # let connection = &mut establish_connection(); +/// +/// let data = vec![ +/// NewUser { name: "Diva Plavalaguna" }, +/// NewUser { name: "Father Vito Cornelius" }, +/// ]; +/// +/// let count = diesel::copy_from(users::table) +/// .from_insertable(&data) +/// .execute(connection)?; +/// +/// assert_eq!(count, 2); +/// # Ok(()) +/// # } +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// ``` +/// +/// ## Via [`CopyFromQuery::from_raw_data`] +/// +/// ```rust +/// # include!("../../../doctest_setup.rs"); +/// # fn run_test() -> QueryResult<()> { +/// # use crate::schema::users; +/// use diesel::pg::CopyFormat; +/// # let connection = &mut establish_connection(); +/// let count = diesel::copy_from(users::table) +/// .from_raw_data(users::table, |copy| { +/// writeln!(copy, "3,Diva Plavalaguna").unwrap(); +/// writeln!(copy, "4,Father Vito Cornelius").unwrap(); +/// diesel::QueryResult::Ok(()) +/// }) +/// .with_format(CopyFormat::Csv) +/// .execute(connection)?; +/// +/// assert_eq!(count, 2); +/// # Ok(()) +/// # } +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// ``` +#[cfg(feature = "postgres_backend")] +pub fn copy_from(table: T) -> CopyFromQuery +where + T: Table, +{ + CopyFromQuery { + table, + action: NotSet, + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/copy/copy_to.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/copy/copy_to.rs new file mode 100644 index 000000000..4b346b176 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/copy/copy_to.rs @@ -0,0 +1,586 @@ +use std::io::BufRead; +use std::marker::PhantomData; + +use super::CommonOptions; +use super::CopyFormat; +use super::CopyTarget; +use crate::deserialize::FromSqlRow; +#[cfg(feature = "postgres")] +use crate::pg::value::TypeOidLookup; +use crate::pg::Pg; +use crate::query_builder::QueryFragment; +use crate::query_builder::QueryId; +use crate::row::Row; +#[cfg(feature = "postgres")] +use crate::row::{self, Field, PartialRow, RowIndex, RowSealed}; +use crate::AppearsOnTable; +use crate::Connection; +use crate::Expression; +use crate::QueryResult; +use crate::Selectable; + +#[derive(Default, Debug)] +pub struct CopyToOptions { + common: CommonOptions, + header: Option, +} + +impl CopyToOptions { + fn any_set(&self) -> bool { + self.common.any_set() || self.header.is_some() + } +} + +impl QueryFragment for CopyToOptions { + fn walk_ast<'b>( + &'b self, + mut pass: crate::query_builder::AstPass<'_, 'b, Pg>, + ) -> crate::QueryResult<()> { + if self.any_set() { + let mut comma = ""; + pass.push_sql(" WITH ("); + self.common.walk_ast(pass.reborrow(), &mut comma); + if let Some(header_is_set) = self.header { + pass.push_sql(comma); + // commented out because rustc complains otherwise + //comma = ", "; + pass.push_sql("HEADER "); + pass.push_sql(if header_is_set { "1" } else { "0" }); + } + + pass.push_sql(")"); + } + Ok(()) + } +} + +#[derive(Debug)] +pub struct CopyToCommand { + options: CopyToOptions, + p: PhantomData, +} + +impl QueryId for CopyToCommand +where + S: CopyTarget, +{ + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl QueryFragment for CopyToCommand +where + S: CopyTarget, +{ + fn walk_ast<'b>( + &'b self, + mut pass: crate::query_builder::AstPass<'_, 'b, Pg>, + ) -> crate::QueryResult<()> { + pass.unsafe_to_cache_prepared(); + pass.push_sql("COPY "); + S::walk_target(pass.reborrow())?; + pass.push_sql(" TO STDOUT"); + self.options.walk_ast(pass.reborrow())?; + Ok(()) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct NotSet; + +pub trait CopyToMarker: Sized { + fn setup_options(q: CopyToQuery) -> CopyToQuery; +} + +impl CopyToMarker for NotSet { + fn setup_options(q: CopyToQuery) -> CopyToQuery { + CopyToQuery { + target: q.target, + options: CopyToOptions::default(), + } + } +} +impl CopyToMarker for CopyToOptions { + fn setup_options(q: CopyToQuery) -> CopyToQuery { + q + } +} +/// The structure returned by [`copy_to`] +/// +/// The [`load`] and the [`load_raw`] methods allow +/// to receive the configured data from the database. +/// If you don't have any special needs you should prefer using +/// the more convenient `load` method. +/// +/// The `with_*` methods allow to configure the settings used for the +/// copy statement. +/// +/// [`load`]: CopyToQuery::load +/// [`load_raw`]: CopyToQuery::load_raw +#[derive(Debug)] +#[must_use = "`COPY TO` statements are only executed when calling `.load()` or `load_raw()`."] +#[cfg(feature = "postgres_backend")] +pub struct CopyToQuery { + target: T, + options: O, +} + +#[cfg(feature = "postgres")] +struct CopyRow<'a> { + buffers: Vec>, + result: &'a crate::pg::connection::PgResult, +} + +#[cfg(feature = "postgres")] +struct CopyField<'a> { + field: &'a Option<&'a [u8]>, + result: &'a crate::pg::connection::PgResult, + col_idx: usize, +} + +#[cfg(feature = "postgres")] +impl<'f> Field<'f, Pg> for CopyField<'f> { + fn field_name(&self) -> Option<&str> { + None + } + + fn value(&self) -> Option<::RawValue<'_>> { + let value = self.field.as_deref()?; + Some(crate::pg::PgValue::new_internal(value, self)) + } +} + +#[cfg(feature = "postgres")] +impl TypeOidLookup for CopyField<'_> { + fn lookup(&self) -> std::num::NonZeroU32 { + self.result.column_type(self.col_idx) + } +} + +#[cfg(feature = "postgres")] +impl RowSealed for CopyRow<'_> {} + +#[cfg(feature = "postgres")] +impl RowIndex for CopyRow<'_> { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count() { + Some(idx) + } else { + None + } + } +} + +#[cfg(feature = "postgres")] +impl<'a> RowIndex<&'a str> for CopyRow<'_> { + fn idx(&self, _idx: &'a str) -> Option { + None + } +} + +#[cfg(feature = "postgres")] +impl<'a> Row<'a, Pg> for CopyRow<'_> { + type Field<'f> + = CopyField<'f> + where + 'a: 'f, + Self: 'f; + + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.buffers.len() + } + + fn get<'b, I>(&'b self, idx: I) -> Option> + where + 'a: 'b, + Self: RowIndex, + { + let idx = self.idx(idx)?; + let buffer = self.buffers.get(idx)?; + Some(CopyField { + field: buffer, + result: self.result, + col_idx: idx, + }) + } + + fn partial_row( + &self, + range: std::ops::Range, + ) -> row::PartialRow<'_, Self::InnerPartialRow> { + PartialRow::new(self, range) + } +} + +pub trait ExecuteCopyToConnection: Connection { + type CopyToBuffer<'a>: BufRead; + + fn make_row<'a, 'b>( + out: &'a Self::CopyToBuffer<'_>, + buffers: Vec>, + ) -> impl Row<'b, Pg> + 'a; + + fn get_buffer<'a>(out: &'a Self::CopyToBuffer<'_>) -> &'a [u8]; + + fn execute(&mut self, command: CopyToCommand) -> QueryResult> + where + T: CopyTarget; +} + +#[cfg(feature = "postgres")] +impl ExecuteCopyToConnection for crate::PgConnection { + type CopyToBuffer<'a> = crate::pg::connection::copy::CopyToBuffer<'a>; + + fn make_row<'a, 'b>( + out: &'a Self::CopyToBuffer<'_>, + buffers: Vec>, + ) -> impl Row<'b, Pg> + 'a { + CopyRow { + buffers, + result: out.get_result(), + } + } + + fn get_buffer<'a>(out: &'a Self::CopyToBuffer<'_>) -> &'a [u8] { + out.data_slice() + } + + fn execute(&mut self, command: CopyToCommand) -> QueryResult> + where + T: CopyTarget, + { + self.copy_to(command) + } +} + +#[cfg(feature = "r2d2")] +impl ExecuteCopyToConnection for crate::r2d2::PooledConnection> +where + C: ExecuteCopyToConnection + crate::r2d2::R2D2Connection + 'static, +{ + type CopyToBuffer<'a> = C::CopyToBuffer<'a>; + + fn make_row<'a, 'b>( + out: &'a Self::CopyToBuffer<'_>, + buffers: Vec>, + ) -> impl Row<'b, Pg> + 'a { + C::make_row(out, buffers) + } + + fn get_buffer<'a>(out: &'a Self::CopyToBuffer<'_>) -> &'a [u8] { + C::get_buffer(out) + } + + fn execute(&mut self, command: CopyToCommand) -> QueryResult> + where + T: CopyTarget, + { + C::execute(&mut **self, command) + } +} + +impl CopyToQuery +where + T: CopyTarget, +{ + /// Copy data from the database by returning an iterator of deserialized data + /// + /// This function allows to easily load data from the database via a `COPY TO` statement. + /// It does **not** allow to configure any settings via the `with_*` method, as it internally + /// sets the required options itself. It will use the binary format to deserialize the result + /// into the specified type `U`. Column selection is performed via [`Selectable`]. + pub fn load(self, conn: &mut C) -> QueryResult> + '_> + where + U: FromSqlRow<::SqlType, Pg> + Selectable, + U::SelectExpression: AppearsOnTable + CopyTarget
, + C: ExecuteCopyToConnection, + { + let io_result_mapper = |e| crate::result::Error::DeserializationError(Box::new(e)); + + let command = CopyToCommand { + p: PhantomData::, + options: CopyToOptions { + header: None, + common: CommonOptions { + format: Some(CopyFormat::Binary), + ..Default::default() + }, + }, + }; + // see https://www.postgresql.org/docs/current/sql-copy.html for + // a description of the binary format + // + // We don't write oids + + let mut out = ExecuteCopyToConnection::execute(conn, command)?; + let buffer = out.fill_buf().map_err(io_result_mapper)?; + if buffer[..super::COPY_MAGIC_HEADER.len()] != super::COPY_MAGIC_HEADER { + return Err(crate::result::Error::DeserializationError( + "Unexpected protocol header".into(), + )); + } + // we care only about bit 16-31 here, so we can just skip the bytes in between + let flags_backward_incompatible = i16::from_be_bytes( + (&buffer[super::COPY_MAGIC_HEADER.len() + 2..super::COPY_MAGIC_HEADER.len() + 4]) + .try_into() + .expect("Exactly 2 byte"), + ); + if flags_backward_incompatible != 0 { + return Err(crate::result::Error::DeserializationError( + format!("Unexpected flag value: {flags_backward_incompatible:x}").into(), + )); + } + let header_size = usize::try_from(i32::from_be_bytes( + (&buffer[super::COPY_MAGIC_HEADER.len() + 4..super::COPY_MAGIC_HEADER.len() + 8]) + .try_into() + .expect("Exactly 4 byte"), + )) + .map_err(|e| crate::result::Error::DeserializationError(Box::new(e)))?; + out.consume(super::COPY_MAGIC_HEADER.len() + 8 + header_size); + let mut len = None; + Ok(std::iter::from_fn(move || { + if let Some(len) = len { + out.consume(len); + if let Err(e) = out.fill_buf().map_err(io_result_mapper) { + return Some(Err(e)); + } + } + let buffer = C::get_buffer(&out); + len = Some(buffer.len()); + let tuple_count = + i16::from_be_bytes((&buffer[..2]).try_into().expect("Exactly 2 bytes")); + if tuple_count > 0 { + let tuple_count = match usize::try_from(tuple_count) { + Ok(o) => o, + Err(e) => { + return Some(Err(crate::result::Error::DeserializationError(Box::new(e)))) + } + }; + let mut buffers = Vec::with_capacity(tuple_count); + let mut offset = 2; + for _t in 0..tuple_count { + let data_size = i32::from_be_bytes( + (&buffer[offset..offset + 4]) + .try_into() + .expect("Exactly 4 bytes"), + ); + + if data_size < 0 { + buffers.push(None); + } else { + match usize::try_from(data_size) { + Ok(data_size) => { + buffers.push(Some(&buffer[offset + 4..offset + 4 + data_size])); + offset = offset + 4 + data_size; + } + Err(e) => { + return Some(Err(crate::result::Error::DeserializationError( + Box::new(e), + ))); + } + } + } + } + + let row = C::make_row(&out, buffers); + Some(U::build_from_row(&row).map_err(crate::result::Error::DeserializationError)) + } else { + None + } + })) + } +} + +impl CopyToQuery +where + O: CopyToMarker, + T: CopyTarget, +{ + /// Copy data from the database by directly accessing the provided response + /// + /// This function returns a type that implements [`std::io::BufRead`] which allows to directly read + /// the data as provided by the database. The exact format depends on what options are + /// set via the various `with_*` methods. + pub fn load_raw(self, conn: &mut C) -> QueryResult + where + C: ExecuteCopyToConnection, + { + let q = O::setup_options(self); + let command = CopyToCommand { + p: PhantomData::, + options: q.options, + }; + ExecuteCopyToConnection::execute(conn, command) + } + + /// The format used for the copy statement + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_format(self, format: CopyFormat) -> CopyToQuery { + let mut out = O::setup_options(self); + out.options.common.format = Some(format); + out + } + + /// Whether or not the `freeze` option is set + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_freeze(self, freeze: bool) -> CopyToQuery { + let mut out = O::setup_options(self); + out.options.common.freeze = Some(freeze); + out + } + + /// Which delimiter should be used for textual output formats + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_delimiter(self, delimiter: char) -> CopyToQuery { + let mut out = O::setup_options(self); + out.options.common.delimiter = Some(delimiter); + out + } + + /// Which string should be used in place of a `NULL` value + /// for textual output formats + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_null(self, null: impl Into) -> CopyToQuery { + let mut out = O::setup_options(self); + out.options.common.null = Some(null.into()); + out + } + + /// Which quote character should be used for textual output formats + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_quote(self, quote: char) -> CopyToQuery { + let mut out = O::setup_options(self); + out.options.common.quote = Some(quote); + out + } + + /// Which escape character should be used for textual output formats + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_escape(self, escape: char) -> CopyToQuery { + let mut out = O::setup_options(self); + out.options.common.escape = Some(escape); + out + } + + /// Is a header provided as part of the textual input or not + /// + /// See the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-copy.html) + /// for more details. + pub fn with_header(self, set: bool) -> CopyToQuery { + let mut out = O::setup_options(self); + out.options.header = Some(set); + out + } +} + +/// Creates a `COPY TO` statement +/// +/// This function constructs a `COPY TO` statement which copies data +/// from the database **to** a client side target. It's designed to move +/// larger amounts of data out of the database. +/// +/// This function accepts a target selection (table name or list of columns) as argument. +/// +/// There are two ways to use a `COPY TO` statement with diesel: +/// +/// * By using [`CopyToQuery::load`] directly to load the deserialized result +/// directly into a specified type +/// * By using the `with_*` methods to configure the format sent by the database +/// and then by calling [`CopyToQuery::load_raw`] to receive the raw data +/// sent by the database. +/// +/// The first variant uses the `BINARY` format internally to receive +/// the selected data efficiently. It automatically sets the right options +/// and does not allow to change them via `with_*` methods. +/// +/// The second variant allows you to control the behaviour of the +/// generated `COPY TO` statement in detail. You can use the various +/// `with_*` methods for that before issuing the statement via [`CopyToQuery::load_raw`]. +/// That method will return an type that implements [`std::io::BufRead`], which +/// allows you to directly read the response from the database in the configured +/// format. +/// See [the postgresql documentation](https://www.postgresql.org/docs/current/sql-copy.html) +/// for more details about the supported formats. +/// +/// If you don't have any specific needs you should prefer using the more +/// convenient first variant. +/// +/// This functionality is postgresql specific. +/// +/// # Examples +/// +/// ## Via [`CopyToQuery::load()`] +/// +/// ```rust +/// # include!("../../../doctest_setup.rs"); +/// # use crate::schema::users; +/// +/// #[derive(Queryable, Selectable, PartialEq, Debug)] +/// #[diesel(table_name = users)] +/// #[diesel(check_for_backend(diesel::pg::Pg))] +/// struct User { +/// name: String, +/// } +/// +/// # fn run_test() -> QueryResult<()> { +/// # let connection = &mut establish_connection(); +/// let out = diesel::copy_to(users::table) +/// .load::(connection)? +/// .collect::, _>>()?; +/// +/// assert_eq!(out, vec![User{ name: "Sean".into() }, User{ name: "Tess".into() }]); +/// # Ok(()) +/// # } +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// ``` +/// +/// ## Via [`CopyToQuery::load_raw()`] +/// +/// ```rust +/// # include!("../../../doctest_setup.rs"); +/// # fn run_test() -> QueryResult<()> { +/// # use crate::schema::users; +/// use diesel::pg::CopyFormat; +/// use std::io::Read; +/// # let connection = &mut establish_connection(); +/// +/// let mut copy = diesel::copy_to(users::table) +/// .with_format(CopyFormat::Csv) +/// .load_raw(connection)?; +/// +/// let mut out = String::new(); +/// copy.read_to_string(&mut out).unwrap(); +/// assert_eq!(out, "1,Sean\n2,Tess\n"); +/// # Ok(()) +/// # } +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// ``` +#[cfg(feature = "postgres_backend")] +pub fn copy_to(target: T) -> CopyToQuery +where + T: CopyTarget, +{ + CopyToQuery { + target, + options: NotSet, + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/copy/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/copy/mod.rs new file mode 100644 index 000000000..07f9efe77 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/copy/mod.rs @@ -0,0 +1,174 @@ +use crate::pg::Pg; +use crate::query_builder::nodes::StaticQueryFragment; +use crate::query_builder::ColumnList; +use crate::query_builder::QueryFragment; +use crate::sql_types::SqlType; +use crate::Expression; +use crate::{Column, Table}; + +pub(crate) mod copy_from; +pub(crate) mod copy_to; + +#[cfg(feature = "postgres")] +pub(crate) use self::copy_from::{CopyFromExpression, InternalCopyFromQuery}; +#[cfg(feature = "postgres")] +pub(crate) use self::copy_to::CopyToCommand; + +pub use self::copy_from::{CopyFromQuery, CopyHeader, ExecuteCopyFromDsl}; +pub use self::copy_to::CopyToQuery; + +const COPY_MAGIC_HEADER: [u8; 11] = [ + 0x50, 0x47, 0x43, 0x4F, 0x50, 0x59, 0x0A, 0xFF, 0x0D, 0x0A, 0x00, +]; + +/// Describes the format used by `COPY FROM` or `COPY TO` +/// statements +/// +/// See [the postgresql documentation](https://www.postgresql.org/docs/current/sql-copy.html) +/// for details about the different formats +#[derive(Default, Debug, Copy, Clone)] +pub enum CopyFormat { + /// The postgresql text format + /// + /// This format is the default if no format is explicitly set + #[default] + Text, + /// Represents the data as comma separated values (CSV) + Csv, + /// The postgresql binary format + Binary, +} + +impl CopyFormat { + fn to_sql_format(self) -> &'static str { + match self { + CopyFormat::Text => "text", + CopyFormat::Csv => "csv", + CopyFormat::Binary => "binary", + } + } +} + +#[derive(Default, Debug)] +struct CommonOptions { + format: Option, + freeze: Option, + delimiter: Option, + null: Option, + quote: Option, + escape: Option, +} + +impl CommonOptions { + fn any_set(&self) -> bool { + self.format.is_some() + || self.freeze.is_some() + || self.delimiter.is_some() + || self.null.is_some() + || self.quote.is_some() + || self.escape.is_some() + } + + fn walk_ast<'b>( + &'b self, + mut pass: crate::query_builder::AstPass<'_, 'b, Pg>, + comma: &mut &'static str, + ) { + if let Some(format) = self.format { + pass.push_sql(comma); + *comma = ", "; + pass.push_sql("FORMAT "); + pass.push_sql(format.to_sql_format()); + } + if let Some(freeze) = self.freeze { + pass.push_sql(&format!("{comma}FREEZE {}", freeze as u8)); + *comma = ", "; + } + if let Some(delimiter) = self.delimiter { + pass.push_sql(&format!("{comma}DELIMITER '{delimiter}'")); + *comma = ", "; + } + if let Some(ref null) = self.null { + pass.push_sql(comma); + *comma = ", "; + pass.push_sql("NULL '"); + // we cannot use binds here :( + pass.push_sql(null); + pass.push_sql("'"); + } + if let Some(quote) = self.quote { + pass.push_sql(&format!("{comma}QUOTE '{quote}'")); + *comma = ", "; + } + if let Some(escape) = self.escape { + pass.push_sql(&format!("{comma}ESCAPE '{escape}'")); + *comma = ", "; + } + } +} + +/// A expression that could be used as target/source for `COPY FROM` and `COPY TO` commands +/// +/// This trait is implemented for any table type and for tuples of columns from the same table +pub trait CopyTarget { + /// The table targeted by the command + type Table: Table; + /// The sql side type of the target expression + type SqlType: SqlType; + + #[doc(hidden)] + fn walk_target(pass: crate::query_builder::AstPass<'_, '_, Pg>) -> crate::QueryResult<()>; +} + +impl CopyTarget for T +where + T: Table + StaticQueryFragment, + T::SqlType: SqlType, + T::AllColumns: ColumnList, + T::Component: QueryFragment, +{ + type Table = Self; + type SqlType = T::SqlType; + + fn walk_target(mut pass: crate::query_builder::AstPass<'_, '_, Pg>) -> crate::QueryResult<()> { + T::STATIC_COMPONENT.walk_ast(pass.reborrow())?; + pass.push_sql("("); + T::all_columns().walk_ast(pass.reborrow())?; + pass.push_sql(")"); + Ok(()) + } +} + +macro_rules! copy_target_for_columns { + ($( + $Tuple:tt { + $(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)+ + } + )+) => { + $( + impl CopyTarget for ($($ST,)*) + where + $($ST: Column
+ Default,)* + ($(<$ST as Expression>::SqlType,)*): SqlType, + T: Table + StaticQueryFragment, + T::Component: QueryFragment, + Self: ColumnList, + { + type Table = T; + type SqlType = crate::dsl::SqlTypeOf; + + fn walk_target( + mut pass: crate::query_builder::AstPass<'_, '_, Pg>, + ) -> crate::QueryResult<()> { + T::STATIC_COMPONENT.walk_ast(pass.reborrow())?; + pass.push_sql("("); + ::walk_ast(&($($ST::default(),)*), pass.reborrow())?; + pass.push_sql(")"); + Ok(()) + } + } + )* + } +} + +diesel_derives::__diesel_for_each_tuple!(copy_target_for_columns); diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/distinct_on.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/distinct_on.rs new file mode 100644 index 000000000..aacff3891 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/distinct_on.rs @@ -0,0 +1,304 @@ +use crate::expression::SelectableExpression; +use crate::pg::Pg; +use crate::query_builder::order_clause::NoOrderClause; +use crate::query_builder::{ + AstPass, FromClause, QueryFragment, QueryId, SelectQuery, SelectStatement, +}; +use crate::query_dsl::methods::DistinctOnDsl; +use crate::query_dsl::order_dsl::ValidOrderingForDistinct; +use crate::result::QueryResult; +use crate::sql_types::SingleValue; +use crate::QuerySource; +use diesel::query_builder::order_clause::OrderClause; + +/// Represents `DISTINCT ON (...)` +#[derive(Debug, Clone, Copy, QueryId)] +#[cfg(feature = "postgres_backend")] +pub struct DistinctOnClause(pub(crate) T); + +impl ValidOrderingForDistinct> for NoOrderClause {} +impl ValidOrderingForDistinct> for OrderClause<(T,)> {} +impl ValidOrderingForDistinct> for OrderClause where T: crate::Expression {} + +impl ValidOrderingForDistinct> + for OrderClause> +where + T: crate::Expression, + T::SqlType: SingleValue, +{ +} + +impl ValidOrderingForDistinct> + for OrderClause> +where + T: crate::Expression, + T::SqlType: SingleValue, +{ +} + +macro_rules! valid_ordering { + // Special-case: for single tuple elements + // generate plain impls as well: + ( + @optional_untuple: + [generics: $($T: ident)*] + [distinct: $D:ident] + [order: $O: ty,] + ) => { + // nothing if both a single tuple elements + }; + ( + @optional_untuple: + [generics: $($T: ident)*] + [distinct: $D:ident] + [order: $($O: ty,)*] + ) => { + impl<$($T,)*> ValidOrderingForDistinct> + for OrderClause<($($O,)*)> + {} + }; + ( + @optional_untuple: + [generics: $($T: ident)*] + [distinct: $($D:ident)*] + [order: $O: ty,] + ) => { + impl<$($T,)*> ValidOrderingForDistinct> + for OrderClause<$O> + {} + }; + ( + @optional_untuple: + [generics: $($T: ident)*] + [distinct: $($D:ident)*] + [order: $($O: ty,)*] + ) => {}; + // Special-case: rule out the all ident case if the + // corresponding flag is set + // We want to have these impls if + // the tuple sizes do **not** match + // therefore we set the flag below + (@impl_one: + [allow_plain = false] + $generics:tt + $distinct:tt + $other:tt + [$($T_:ident, )*] + ) => { + /* skip this one */ + }; + (@impl_one: + [allow_plain = $allow_plain: expr] + [generics: $($T:ident)*] + [distinct: $($D:ident)*] + [other: $($O:ident)*] + [$($Ty:ty, )*] + ) => { + impl<$($T,)*> ValidOrderingForDistinct> + for OrderClause<($($Ty, )* $($O,)*)> + {} + valid_ordering!(@optional_untuple: [generics: $($T)*] [distinct: $($D)*] [order: $($Ty,)* $($O,)*]); + }; + ( + @perm: + $allow_plain:tt + $generics:tt + $distinct:tt + $other:tt + [acc: $([$($acc:tt)*])*] + $T:ident + $($rest:tt)* + ) => { + valid_ordering! { + @perm: + $allow_plain + $generics + $distinct + $other + [acc: + $( + [$($acc)* crate::expression::operators::Asc<$T>, ] + [$($acc)* $T , ] + [$($acc)* crate::expression::operators::Desc<$T>, ] + )* + ] + $($rest)* + } + }; + ( + @perm: + $allow_plain:tt + $generics:tt + $distinct:tt + $other:tt + [acc: $($Tys:tt)*] + /* nothing left */ + ) => ( + $( + valid_ordering! {@impl_one: + $allow_plain + $generics + $distinct + $other + $Tys + } + )* + ); + (@skip_distinct_rev: [generics: $($G: ident)*] [other: $($O: ident)*] [acc: $($T: ident)*]) => { + valid_ordering!(@perm: + [allow_plain = true] + [generics: $($G)*] + [distinct: $($T)*] + [other: $($O)* ] + [acc: []] + $($T)* + ); + }; + (@skip_distinct_rev: [generics: $($G: ident)*] [other: $($O: ident)*] [acc: $($I: ident)*] $T: ident $($Ts: ident)*) => { + valid_ordering!( + @skip_distinct_rev: + [generics: $($G)*] + [other: $($O)*] + [acc: $T $($I)*] + $($Ts)* + ); + }; + (@skip_distinct: + [generics: $($G: ident)*] + [acc: $($O: ident)*] + $T: ident + ) => {}; + (@skip_distinct: + [generics: $($G: ident)*] + [acc: $($O: ident)*] + $T:ident $($Ts: ident)* + ) => { + valid_ordering!(@skip_distinct_rev: + [generics: $($G)*] + [other: $($O)* $T] + [acc: ] + $($Ts)* + ); + valid_ordering!(@skip_distinct: [generics: $($G)*] [acc: $($O)* $T] $($Ts)*); + }; + (@skip_order_rev: [generics: $($G: ident)*] [acc: $($T: ident)*]) => { + valid_ordering!(@perm: + [allow_plain = true] + [generics: $($G)*] + [distinct: $($G)*] + [other: ] + [acc: []] + $($T)* + ); + }; + (@skip_order_rev: [generics: $($G: ident)*] [acc: $($I: ident)*] $T: ident $($Ts: ident)*) => { + valid_ordering!( + @skip_order_rev: + [generics: $($G)*] + [acc: $T $($I)*] + $($Ts)* + ); + }; + (@skip_order: + [generics: $($G: ident)*] + $T: ident + ) => {}; + (@skip_order: + [generics: $($G: ident)*] + $T: ident $($Ts: ident)* + ) => { + valid_ordering!(@skip_order_rev: [generics: $($G)*] [acc: ] $($Ts)*); + valid_ordering!(@skip_order: [generics: $($G)*] $($Ts)*); + }; + (@reverse_list: [generics: $($G: ident)*] [acc: $($I: ident)*]) => { + valid_ordering!(@skip_order: [generics: $($G)*] $($I)*); + valid_ordering!(@skip_distinct: [generics: $($G)*] [acc: ] $($I)*); + }; + (@reverse_list: [generics: $($G: ident)*] [acc: $($I: ident)*] $T: ident $($Ts: ident)*) => { + valid_ordering!(@reverse_list: [generics: $($G)*] [acc: $T $($I)*] $($Ts)*); + }; + ($( + $Tuple:tt { + $(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)+ + } + )+) => { + $( + valid_ordering!(@perm: + [allow_plain = false] + [generics: $($T)*] + [distinct: $($T)*] + [other: ] + [acc: []] + $($T)* + ); + valid_ordering!(@reverse_list: [generics: $($T)*] [acc: ] $($T)*); + )* + } +} + +// we only generate these impl up to a tuple size of 5 as we generate n*n + 4 impls here +// If we would generate these impls up to max_table_column_count tuple elements that +// would be a really large number for 128 tuple elements (~64k trait impls) +// It's fine to increase this number at some point in the future gradually +diesel_derives::__diesel_for_each_tuple!(valid_ordering, 5); + +/// A decorator trait for `OrderClause` +/// It helps to have bounds on either Col, Ascand Desc. +pub trait OrderDecorator { + /// A column on a database table. + type Column; +} + +impl OrderDecorator for C +where + C: crate::Column, +{ + type Column = C; +} + +impl OrderDecorator for crate::helper_types::Asc { + type Column = C; +} + +impl OrderDecorator for crate::helper_types::Desc { + type Column = C; +} + +impl QueryFragment for DistinctOnClause +where + T: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + out.push_sql("DISTINCT ON ("); + self.0.walk_ast(out.reborrow())?; + out.push_sql(") "); + Ok(()) + } +} + +impl DistinctOnDsl + for SelectStatement, S, D, W, O, LOf, G, H> +where + F: QuerySource, + Selection: SelectableExpression, + Self: SelectQuery, + O: ValidOrderingForDistinct>, + SelectStatement, S, DistinctOnClause, W, O, LOf, G, H>: + SelectQuery, +{ + type Output = SelectStatement, S, DistinctOnClause, W, O, LOf, G, H>; + + fn distinct_on(self, selection: Selection) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + DistinctOnClause(selection), + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/limit_offset.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/limit_offset.rs new file mode 100644 index 000000000..27384f859 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/limit_offset.rs @@ -0,0 +1,43 @@ +use crate::pg::Pg; +use crate::query_builder::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +use crate::query_builder::{AstPass, IntoBoxedClause, QueryFragment}; +use crate::result::QueryResult; + +impl<'a, L, O> IntoBoxedClause<'a, Pg> for LimitOffsetClause +where + L: QueryFragment + Send + 'a, + O: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Pg>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: Some(Box::new(self.offset_clause)), + } + } +} + +impl QueryFragment for BoxedLimitOffsetClause<'_, Pg> { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + if let Some(ref limit) = self.limit { + limit.walk_ast(out.reborrow())?; + } + if let Some(ref offset) = self.offset { + offset.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause +where + L: QueryFragment, + O: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + self.limit_clause.walk_ast(out.reborrow())?; + self.offset_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/mod.rs new file mode 100644 index 000000000..bfe36b10e --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/mod.rs @@ -0,0 +1,81 @@ +use super::backend::Pg; +use crate::query_builder::QueryBuilder; +use crate::result::QueryResult; + +pub(crate) mod copy; +mod distinct_on; +mod limit_offset; +pub(crate) mod on_constraint; +pub(crate) mod only; +mod query_fragment_impls; +pub(crate) mod tablesample; +pub use self::copy::{CopyFormat, CopyFromQuery, CopyHeader, CopyTarget, CopyToQuery}; +pub use self::distinct_on::DistinctOnClause; +pub use self::distinct_on::OrderDecorator; + +/// The PostgreSQL query builder +#[allow(missing_debug_implementations)] +#[derive(Default)] +#[cfg(feature = "postgres_backend")] +pub struct PgQueryBuilder { + sql: String, + bind_idx: u32, +} + +impl PgQueryBuilder { + /// Constructs a new query builder with an empty query + pub fn new() -> Self { + PgQueryBuilder::default() + } +} + +impl QueryBuilder for PgQueryBuilder { + fn push_sql(&mut self, sql: &str) { + self.sql.push_str(sql); + } + + fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> { + self.push_sql("\""); + self.push_sql(&identifier.replace('"', "\"\"")); + self.push_sql("\""); + Ok(()) + } + + fn push_bind_param(&mut self) { + self.push_bind_param_value_only(); + self.sql += "$"; + let mut buffer = itoa::Buffer::new(); + self.sql += buffer.format(self.bind_idx); + } + + fn push_bind_param_value_only(&mut self) { + self.bind_idx += 1; + } + + fn finish(self) -> String { + self.sql + } +} + +#[test] +fn check_sql_query_increments_bind_count() { + use crate::query_builder::{AstPass, AstPassToSqlOptions, QueryFragment}; + use crate::sql_types::*; + + let query = crate::sql_query("SELECT $1, $2, $3") + .bind::(42) + .bind::(3) + .bind::(342); + + let mut query_builder = PgQueryBuilder::default(); + + { + let mut options = AstPassToSqlOptions::default(); + let ast_pass = AstPass::::to_sql(&mut query_builder, &mut options, &Pg); + + query.walk_ast(ast_pass).unwrap(); + } + + assert_eq!(query_builder.bind_idx, 3); + assert_eq!(query_builder.sql, "SELECT $1, $2, $3"); +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/on_constraint.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/on_constraint.rs new file mode 100644 index 000000000..be553742d --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/on_constraint.rs @@ -0,0 +1,73 @@ +use crate::pg::Pg; +use crate::query_builder::upsert::on_conflict_target::{ConflictTarget, OnConflictTarget}; +use crate::query_builder::*; +use crate::result::QueryResult; + +/// Used to specify the constraint name for an upsert statement in the form `ON +/// CONFLICT ON CONSTRAINT`. Note that `constraint_name` must be the name of a +/// unique constraint, not the name of an index. +/// +/// # Example +/// +/// ```rust +/// # extern crate diesel; +/// # include!("../../upsert/on_conflict_docs_setup.rs"); +/// # +/// # fn main() { +/// # use self::users::dsl::*; +/// use diesel::upsert::*; +/// +/// # let conn = &mut establish_connection(); +/// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap(); +/// diesel::sql_query("ALTER TABLE users ADD CONSTRAINT users_name UNIQUE (name)") +/// .execute(conn) +/// .unwrap(); +/// let user = User { id: 1, name: "Sean" }; +/// let same_name_different_id = User { id: 2, name: "Sean" }; +/// let same_id_different_name = User { id: 1, name: "Pascal" }; +/// +/// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn)); +/// +/// let inserted_row_count = diesel::insert_into(users) +/// .values(&same_name_different_id) +/// .on_conflict(on_constraint("users_name")) +/// .do_nothing() +/// .execute(conn); +/// assert_eq!(Ok(0), inserted_row_count); +/// +/// let pk_conflict_result = diesel::insert_into(users) +/// .values(&same_id_different_name) +/// .on_conflict(on_constraint("users_name")) +/// .do_nothing() +/// .execute(conn); +/// assert!(pk_conflict_result.is_err()); +/// # } +/// ``` +pub fn on_constraint(constraint_name: &str) -> OnConstraint<'_> { + OnConstraint { constraint_name } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct OnConstraint<'a> { + constraint_name: &'a str, +} + +impl QueryId for OnConstraint<'_> { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl QueryFragment + for ConflictTarget> +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + out.push_sql(" ON CONSTRAINT "); + out.push_identifier(self.0.constraint_name)?; + Ok(()) + } +} + +impl
OnConflictTarget
for ConflictTarget> {} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/only.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/only.rs new file mode 100644 index 000000000..6fad25ea9 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/only.rs @@ -0,0 +1,95 @@ +use crate::expression::{Expression, ValidGrouping}; +use crate::pg::Pg; +use crate::query_builder::{AsQuery, AstPass, FromClause, QueryFragment, QueryId, SelectStatement}; +use crate::query_source::QuerySource; +use crate::result::QueryResult; +use crate::{JoinTo, SelectableExpression, Table}; + +/// Represents a query with an `ONLY` clause. +#[derive(Debug, Clone, Copy, Default)] +pub struct Only { + pub(crate) source: S, +} + +impl QueryId for Only +where + Self: 'static, + S: QueryId, +{ + type QueryId = Self; + const HAS_STATIC_QUERY_ID: bool = ::HAS_STATIC_QUERY_ID; +} + +impl QuerySource for Only +where + S: Table + Clone, + ::DefaultSelection: ValidGrouping<()> + SelectableExpression>, +{ + type FromClause = Self; + type DefaultSelection = ::DefaultSelection; + + fn from_clause(&self) -> Self::FromClause { + self.clone() + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.source.default_selection() + } +} + +impl QueryFragment for Only +where + S: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut pass: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + pass.push_sql(" ONLY "); + self.source.walk_ast(pass.reborrow())?; + Ok(()) + } +} + +impl AsQuery for Only +where + S: Table + Clone, + ::DefaultSelection: ValidGrouping<()> + SelectableExpression>, +{ + type SqlType = <::DefaultSelection as Expression>::SqlType; + type Query = SelectStatement>; + + fn as_query(self) -> Self::Query { + SelectStatement::simple(self) + } +} + +impl JoinTo for Only +where + S: JoinTo, + T: Table, + S: Table, +{ + type FromClause = >::FromClause; + type OnClause = >::OnClause; + + fn join_target(rhs: T) -> (Self::FromClause, Self::OnClause) { + >::join_target(rhs) + } +} +impl Table for Only +where + S: Table + Clone + AsQuery, + + ::PrimaryKey: SelectableExpression>, + ::AllColumns: SelectableExpression>, + ::DefaultSelection: ValidGrouping<()> + SelectableExpression>, +{ + type PrimaryKey = ::PrimaryKey; + type AllColumns = ::AllColumns; + + fn primary_key(&self) -> Self::PrimaryKey { + self.source.primary_key() + } + + fn all_columns() -> Self::AllColumns { + S::all_columns() + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/query_fragment_impls.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/query_fragment_impls.rs new file mode 100644 index 000000000..e8bc3cd74 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/query_fragment_impls.rs @@ -0,0 +1,122 @@ +use crate::expression::array_comparison::{In, Many, MaybeEmpty, NotIn}; +use crate::pg::backend::PgStyleArrayComparison; +use crate::pg::types::sql_types::Array; +use crate::pg::Pg; +use crate::query_builder::locking_clause::{ + ForKeyShare, ForNoKeyUpdate, ForShare, ForUpdate, NoModifier, NoWait, SkipLocked, +}; +use crate::query_builder::upsert::into_conflict_clause::OnConflictSelectWrapper; +use crate::query_builder::upsert::on_conflict_target_decorations::DecoratedConflictTarget; +use crate::query_builder::{AstPass, QueryFragment}; +use crate::result::QueryResult; +use crate::serialize::ToSql; +use crate::sql_types::{HasSqlType, SingleValue}; + +impl QueryFragment for ForUpdate { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + out.push_sql(" FOR UPDATE"); + Ok(()) + } +} + +impl QueryFragment for ForNoKeyUpdate { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + out.push_sql(" FOR NO KEY UPDATE"); + Ok(()) + } +} + +impl QueryFragment for ForShare { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + out.push_sql(" FOR SHARE"); + Ok(()) + } +} + +impl QueryFragment for ForKeyShare { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + out.push_sql(" FOR KEY SHARE"); + Ok(()) + } +} + +impl QueryFragment for NoModifier { + fn walk_ast<'b>(&'b self, _out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for SkipLocked { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + out.push_sql(" SKIP LOCKED"); + Ok(()) + } +} + +impl QueryFragment for NoWait { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + out.push_sql(" NOWAIT"); + Ok(()) + } +} + +impl QueryFragment for In +where + T: QueryFragment, + U: QueryFragment + MaybeEmpty, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + self.left.walk_ast(out.reborrow())?; + out.push_sql(" = ANY("); + self.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +impl QueryFragment for NotIn +where + T: QueryFragment, + U: QueryFragment + MaybeEmpty, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + self.left.walk_ast(out.reborrow())?; + out.push_sql(" != ALL("); + self.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +impl QueryFragment for Many +where + ST: SingleValue, + Vec: ToSql, Pg>, + Pg: HasSqlType, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + out.push_bind_param::, Vec>(&self.values) + } +} + +impl QueryFragment + for DecoratedConflictTarget +where + T: QueryFragment, + U: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + self.target.walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl QueryFragment for OnConflictSelectWrapper +where + S: QueryFragment, +{ + fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, crate::pg::Pg>) -> QueryResult<()> { + self.0.walk_ast(out) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/tablesample.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/tablesample.rs new file mode 100644 index 000000000..6906db780 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/query_builder/tablesample.rs @@ -0,0 +1,213 @@ +use crate::expression::{Expression, ValidGrouping}; +use crate::pg::Pg; +use crate::query_builder::{AsQuery, AstPass, FromClause, QueryFragment, QueryId, SelectStatement}; +use crate::query_source::QuerySource; +use crate::result::QueryResult; +use crate::sql_types::{Double, SmallInt}; +use crate::{JoinTo, SelectableExpression, Table}; +use std::marker::PhantomData; + +#[doc(hidden)] +pub trait TablesampleMethod: Clone { + fn method_name_sql() -> &'static str; +} + +#[derive(Clone, Copy, Debug)] +/// Used to specify the `BERNOULLI` sampling method. +pub struct BernoulliMethod; + +impl TablesampleMethod for BernoulliMethod { + fn method_name_sql() -> &'static str { + "BERNOULLI" + } +} + +#[derive(Clone, Copy, Debug)] +/// Used to specify the `SYSTEM` sampling method. +pub struct SystemMethod; + +impl TablesampleMethod for SystemMethod { + fn method_name_sql() -> &'static str { + "SYSTEM" + } +} + +/// Represents a query with a `TABLESAMPLE` clause. +#[derive(Debug, Clone, Copy)] +pub struct Tablesample +where + TSM: TablesampleMethod, +{ + source: S, + method: PhantomData, + portion: i16, + seed: Option, +} + +impl Tablesample +where + TSM: TablesampleMethod, +{ + pub(crate) fn new(source: S, portion: i16) -> Tablesample { + Tablesample { + source, + method: PhantomData, + portion, + seed: None, + } + } + + /// This method allows you to specify the random number generator seed to use in the sampling + /// method. This allows you to obtain repeatable results. + pub fn with_seed(self, seed: f64) -> Tablesample { + Tablesample { + source: self.source, + method: self.method, + portion: self.portion, + seed: Some(seed), + } + } +} + +impl QueryId for Tablesample +where + S: QueryId, + TSM: TablesampleMethod, +{ + type QueryId = (); + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl QuerySource for Tablesample +where + S: Table + Clone, + TSM: TablesampleMethod, + ::DefaultSelection: + ValidGrouping<()> + SelectableExpression>, +{ + type FromClause = Self; + type DefaultSelection = ::DefaultSelection; + + fn from_clause(&self) -> Self::FromClause { + self.clone() + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.source.default_selection() + } +} + +impl QueryFragment for Tablesample +where + S: QueryFragment, + TSM: TablesampleMethod, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + self.source.walk_ast(out.reborrow())?; + out.push_sql(" TABLESAMPLE "); + out.push_sql(TSM::method_name_sql()); + out.push_sql("("); + out.push_bind_param::(&self.portion)?; + out.push_sql(")"); + if let Some(f) = &self.seed { + out.push_sql(" REPEATABLE("); + out.push_bind_param::(f)?; + out.push_sql(")"); + } + Ok(()) + } +} + +impl AsQuery for Tablesample +where + S: Table + Clone, + TSM: TablesampleMethod, + ::DefaultSelection: + ValidGrouping<()> + SelectableExpression>, +{ + type SqlType = <::DefaultSelection as Expression>::SqlType; + type Query = SelectStatement>; + fn as_query(self) -> Self::Query { + SelectStatement::simple(self) + } +} + +impl JoinTo for Tablesample +where + S: JoinTo, + T: Table, + S: Table, + TSM: TablesampleMethod, +{ + type FromClause = >::FromClause; + type OnClause = >::OnClause; + + fn join_target(rhs: T) -> (Self::FromClause, Self::OnClause) { + >::join_target(rhs) + } +} + +impl Table for Tablesample +where + S: Table + Clone + AsQuery, + TSM: TablesampleMethod, + + ::PrimaryKey: SelectableExpression>, + ::AllColumns: SelectableExpression>, + ::DefaultSelection: + ValidGrouping<()> + SelectableExpression>, +{ + type PrimaryKey = ::PrimaryKey; + type AllColumns = ::AllColumns; + + fn primary_key(&self) -> Self::PrimaryKey { + self.source.primary_key() + } + + fn all_columns() -> Self::AllColumns { + S::all_columns() + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::backend::Backend; + use crate::query_builder::QueryBuilder; + use diesel::dsl::*; + use diesel::*; + + macro_rules! assert_sql { + ($query:expr, $sql:expr) => { + let mut query_builder = ::QueryBuilder::default(); + $query.to_sql(&mut query_builder, &Pg).unwrap(); + let sql = query_builder.finish(); + assert_eq!(sql, $sql); + }; + } + + table! { + users { + id -> Integer, + name -> VarChar, + } + } + + #[test] + fn test_generated_tablesample_sql() { + assert_sql!( + users::table.tablesample_bernoulli(10), + "\"users\" TABLESAMPLE BERNOULLI($1)" + ); + + assert_sql!( + users::table.tablesample_system(10), + "\"users\" TABLESAMPLE SYSTEM($1)" + ); + + assert_sql!( + users::table.tablesample_system(10).with_seed(42.0), + "\"users\" TABLESAMPLE SYSTEM($1) REPEATABLE($2)" + ); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/serialize/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/serialize/mod.rs new file mode 100644 index 000000000..6bc1f4bbf --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/serialize/mod.rs @@ -0,0 +1,3 @@ +mod write_tuple; + +pub use self::write_tuple::WriteTuple; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/serialize/write_tuple.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/serialize/write_tuple.rs new file mode 100644 index 000000000..49dfe6aef --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/serialize/write_tuple.rs @@ -0,0 +1,45 @@ +use crate::pg::Pg; +use crate::serialize::{self, Output}; + +/// Helper trait for writing tuples as named composite types +/// +/// This trait is essentially `ToSql>` for tuples. +/// While we can provide a valid body of `to_sql`, +/// PostgreSQL doesn't allow the use of bind parameters for unnamed composite types. +/// For this reason, we avoid implementing `ToSql` directly. +/// +/// This trait can be used by `ToSql` impls of named composite types. +/// +/// # Example +/// +/// ``` +/// # #[cfg(feature = "postgres")] +/// # mod the_impl { +/// # use diesel::prelude::*; +/// # use diesel::pg::Pg; +/// # use diesel::serialize::{self, ToSql, Output, WriteTuple}; +/// # use diesel::sql_types::{Integer, Text, SqlType}; +/// # +/// #[derive(SqlType)] +/// #[diesel(postgres_type(name = "my_type"))] +/// struct MyType; +/// +/// #[derive(Debug)] +/// struct MyStruct<'a>(i32, &'a str); +/// +/// impl<'a> ToSql for MyStruct<'a> { +/// fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { +/// WriteTuple::<(Integer, Text)>::write_tuple( +/// &(self.0, self.1), +/// &mut out.reborrow(), +/// ) +/// } +/// } +/// # } +/// # fn main() {} +/// ``` +#[cfg(feature = "postgres_backend")] +pub trait WriteTuple { + /// See trait documentation. + fn write_tuple(&self, out: &mut Output<'_, '_, Pg>) -> serialize::Result; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/transaction.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/transaction.rs new file mode 100644 index 000000000..700af018d --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/transaction.rs @@ -0,0 +1,436 @@ +#![allow(dead_code)] +use crate::backend::Backend; +use crate::connection::{AnsiTransactionManager, TransactionManager}; +use crate::pg::Pg; +use crate::prelude::*; +use crate::query_builder::{AstPass, QueryBuilder, QueryFragment}; +use crate::result::Error; + +/// Used to build a transaction, specifying additional details. +/// +/// This struct is returned by [`.build_transaction`]. +/// See the documentation for methods on this struct for usage examples. +/// See [the PostgreSQL documentation for `SET TRANSACTION`][pg-docs] +/// for details on the behavior of each option. +/// +/// [`.build_transaction`]: PgConnection::build_transaction() +/// [pg-docs]: https://www.postgresql.org/docs/current/static/sql-set-transaction.html +#[allow(missing_debug_implementations)] // False positive. Connection isn't Debug. +#[must_use = "Transaction builder does nothing unless you call `run` on it"] +#[cfg(feature = "postgres_backend")] +pub struct TransactionBuilder<'a, C> { + connection: &'a mut C, + isolation_level: Option, + read_mode: Option, + deferrable: Option, +} + +impl<'a, C> TransactionBuilder<'a, C> +where + C: Connection, +{ + /// Creates a new TransactionBuilder. + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + pub(crate) fn new(connection: &'a mut C) -> Self { + Self { + connection, + isolation_level: None, + read_mode: None, + deferrable: None, + } + } + + /// Makes the transaction `READ ONLY` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use diesel::sql_query; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # table! { + /// # users_for_read_only { + /// # id -> Integer, + /// # name -> Text, + /// # } + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use users_for_read_only::table as users; + /// # use users_for_read_only::columns::*; + /// # let conn = &mut connection_no_transaction(); + /// # sql_query("CREATE TABLE IF NOT EXISTS users_for_read_only ( + /// # id SERIAL PRIMARY KEY, + /// # name TEXT NOT NULL + /// # )").execute(conn)?; + /// conn.build_transaction() + /// .read_only() + /// .run::<_, diesel::result::Error, _>(|conn| { + /// let read_attempt = users.select(name).load::(conn); + /// assert!(read_attempt.is_ok()); + /// + /// let write_attempt = diesel::insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(conn); + /// assert!(write_attempt.is_err()); + /// + /// Ok(()) + /// })?; + /// # sql_query("DROP TABLE users_for_read_only").execute(conn)?; + /// # Ok(()) + /// # } + /// ``` + pub fn read_only(mut self) -> Self { + self.read_mode = Some(ReadMode::ReadOnly); + self + } + + /// Makes the transaction `READ WRITE` + /// + /// This is the default, unless you've changed the + /// `default_transaction_read_only` configuration parameter. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use diesel::result::Error::RollbackTransaction; + /// # use diesel::sql_query; + /// # + /// # fn main() { + /// # assert_eq!(run_test(), Err(RollbackTransaction)); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = &mut connection_no_transaction(); + /// conn.build_transaction() + /// .read_write() + /// .run(|conn| { + /// # sql_query("CREATE TABLE IF NOT EXISTS users ( + /// # id SERIAL PRIMARY KEY, + /// # name TEXT NOT NULL + /// # )").execute(conn)?; + /// let read_attempt = users.select(name).load::(conn); + /// assert!(read_attempt.is_ok()); + /// + /// let write_attempt = diesel::insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(conn); + /// assert!(write_attempt.is_ok()); + /// + /// # Err(RollbackTransaction) + /// # /* + /// Ok(()) + /// # */ + /// }) + /// # } + /// ``` + pub fn read_write(mut self) -> Self { + self.read_mode = Some(ReadMode::ReadWrite); + self + } + + /// Makes the transaction `DEFERRABLE` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = &mut connection_no_transaction(); + /// conn.build_transaction() + /// .deferrable() + /// .run(|conn| Ok(())) + /// # } + /// ``` + pub fn deferrable(mut self) -> Self { + self.deferrable = Some(Deferrable::Deferrable); + self + } + + /// Makes the transaction `NOT DEFERRABLE` + /// + /// This is the default, unless you've changed the + /// `default_transaction_deferrable` configuration parameter. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = &mut connection_no_transaction(); + /// conn.build_transaction() + /// .not_deferrable() + /// .run(|conn| Ok(())) + /// # } + /// ``` + pub fn not_deferrable(mut self) -> Self { + self.deferrable = Some(Deferrable::NotDeferrable); + self + } + + /// Makes the transaction `ISOLATION LEVEL READ COMMITTED` + /// + /// This is the default, unless you've changed the + /// `default_transaction_isolation_level` configuration parameter. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = &mut connection_no_transaction(); + /// conn.build_transaction() + /// .read_committed() + /// .run(|conn| Ok(())) + /// # } + /// ``` + pub fn read_committed(mut self) -> Self { + self.isolation_level = Some(IsolationLevel::ReadCommitted); + self + } + + /// Makes the transaction `ISOLATION LEVEL REPEATABLE READ` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = &mut connection_no_transaction(); + /// conn.build_transaction() + /// .repeatable_read() + /// .run(|conn| Ok(())) + /// # } + /// ``` + pub fn repeatable_read(mut self) -> Self { + self.isolation_level = Some(IsolationLevel::RepeatableRead); + self + } + + /// Makes the transaction `ISOLATION LEVEL SERIALIZABLE` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = &mut connection_no_transaction(); + /// conn.build_transaction() + /// .serializable() + /// .run(|conn| Ok(())) + /// # } + /// ``` + pub fn serializable(mut self) -> Self { + self.isolation_level = Some(IsolationLevel::Serializable); + self + } + + /// Runs the given function inside of the transaction + /// with the parameters given to this builder. + /// + /// This function executes the provided closure `f` inside a database + /// transaction. If there is already an open transaction for the current + /// connection it will return an error. The connection is committed if + /// the closure returns `Ok(_)`, it will be rolled back if it returns `Err(_)`. + /// For both cases the original result value will be returned from this function. + /// + /// If the transaction fails to commit and requires a rollback according to Postgres, + /// (e.g. serialization failure) a rollback will be attempted. + /// If the rollback fails, the error will be returned in a + /// [`Error::RollbackErrorOnCommit`](crate::result::Error::RollbackErrorOnCommit), + /// from which you will be able to extract both the original commit error and + /// the rollback error. + /// In addition, the connection will be considered broken + /// as it contains a uncommitted unabortable open transaction. Any further + /// interaction with the transaction system will result in an returned error + /// in this case. + pub fn run(&mut self, f: F) -> Result + where + F: FnOnce(&mut C) -> Result, + E: From, + { + let mut query_builder = ::QueryBuilder::default(); + self.to_sql(&mut query_builder, &Pg)?; + let sql = query_builder.finish(); + + AnsiTransactionManager::begin_transaction_sql(&mut *self.connection, &sql)?; + match f(&mut *self.connection) { + Ok(value) => { + AnsiTransactionManager::commit_transaction(&mut *self.connection)?; + Ok(value) + } + Err(user_error) => { + match AnsiTransactionManager::rollback_transaction(&mut *self.connection) { + Ok(()) => Err(user_error), + Err(Error::BrokenTransactionManager) => { + // In this case we are probably more interested by the + // original error, which likely caused this + Err(user_error) + } + Err(rollback_error) => Err(rollback_error.into()), + } + } + } + } +} + +impl QueryFragment for TransactionBuilder<'_, C> { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + out.push_sql("BEGIN TRANSACTION"); + if let Some(ref isolation_level) = self.isolation_level { + isolation_level.walk_ast(out.reborrow())?; + } + if let Some(ref read_mode) = self.read_mode { + read_mode.walk_ast(out.reborrow())?; + } + if let Some(ref deferrable) = self.deferrable { + deferrable.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +#[derive(Debug, Clone, Copy)] +enum IsolationLevel { + ReadCommitted, + RepeatableRead, + Serializable, +} + +impl QueryFragment for IsolationLevel { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + out.push_sql(" ISOLATION LEVEL "); + match *self { + IsolationLevel::ReadCommitted => out.push_sql("READ COMMITTED"), + IsolationLevel::RepeatableRead => out.push_sql("REPEATABLE READ"), + IsolationLevel::Serializable => out.push_sql("SERIALIZABLE"), + } + Ok(()) + } +} + +#[derive(Debug, Clone, Copy)] +enum ReadMode { + ReadOnly, + ReadWrite, +} + +impl QueryFragment for ReadMode { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + match *self { + ReadMode::ReadOnly => out.push_sql(" READ ONLY"), + ReadMode::ReadWrite => out.push_sql(" READ WRITE"), + } + Ok(()) + } +} + +#[derive(Debug, Clone, Copy)] +enum Deferrable { + Deferrable, + NotDeferrable, +} + +impl QueryFragment for Deferrable { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + match *self { + Deferrable::Deferrable => out.push_sql(" DEFERRABLE"), + Deferrable::NotDeferrable => out.push_sql(" NOT DEFERRABLE"), + } + Ok(()) + } +} + +#[test] +fn test_transaction_builder_generates_correct_sql() { + extern crate dotenvy; + + macro_rules! assert_sql { + ($query:expr, $sql:expr) => { + let mut query_builder = ::QueryBuilder::default(); + $query.to_sql(&mut query_builder, &Pg).unwrap(); + let sql = query_builder.finish(); + assert_eq!(sql, $sql); + }; + } + + let database_url = dotenvy::var("PG_DATABASE_URL") + .or_else(|_| dotenvy::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + let mut conn = PgConnection::establish(&database_url).unwrap(); + + assert_sql!(conn.build_transaction(), "BEGIN TRANSACTION"); + assert_sql!( + conn.build_transaction().read_only(), + "BEGIN TRANSACTION READ ONLY" + ); + assert_sql!( + conn.build_transaction().read_write(), + "BEGIN TRANSACTION READ WRITE" + ); + assert_sql!( + conn.build_transaction().deferrable(), + "BEGIN TRANSACTION DEFERRABLE" + ); + assert_sql!( + conn.build_transaction().not_deferrable(), + "BEGIN TRANSACTION NOT DEFERRABLE" + ); + assert_sql!( + conn.build_transaction().read_committed(), + "BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED" + ); + assert_sql!( + conn.build_transaction().repeatable_read(), + "BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ" + ); + assert_sql!( + conn.build_transaction().serializable(), + "BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE" + ); + assert_sql!( + conn.build_transaction() + .serializable() + .deferrable() + .read_only(), + "BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE" + ); +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/types/array.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/types/array.rs new file mode 100644 index 000000000..354ce1393 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/types/array.rs @@ -0,0 +1,164 @@ +use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; +use std::fmt; +use std::io::Write; + +use crate::deserialize::{self, FromSql}; +use crate::pg::{Pg, PgTypeMetadata, PgValue}; +use crate::query_builder::bind_collector::ByteWrapper; +use crate::serialize::{self, IsNull, Output, ToSql}; +use crate::sql_types::{Array, HasSqlType, Nullable}; + +#[cfg(feature = "postgres_backend")] +impl HasSqlType> for Pg +where + Pg: HasSqlType, +{ + fn metadata(lookup: &mut Self::MetadataLookup) -> PgTypeMetadata { + match >::metadata(lookup).0 { + Ok(tpe) => PgTypeMetadata::new(tpe.array_oid, 0), + c @ Err(_) => PgTypeMetadata(c), + } + } +} + +#[cfg(feature = "postgres_backend")] +impl FromSql, Pg> for Vec +where + T: FromSql, +{ + fn from_sql(value: PgValue<'_>) -> deserialize::Result { + let mut bytes = value.as_bytes(); + let num_dimensions = bytes.read_i32::()?; + let has_null = bytes.read_i32::()? != 0; + let _oid = bytes.read_i32::()?; + + if num_dimensions == 0 { + return Ok(Vec::new()); + } + + let num_elements = bytes.read_i32::()?; + let _lower_bound = bytes.read_i32::()?; + + if num_dimensions != 1 { + return Err("multi-dimensional arrays are not supported".into()); + } + + (0..num_elements) + .map(|_| { + let elem_size = bytes.read_i32::()?; + if has_null && elem_size == -1 { + T::from_nullable_sql(None) + } else { + let (elem_bytes, new_bytes) = bytes.split_at(elem_size.try_into()?); + bytes = new_bytes; + T::from_sql(PgValue::new_internal(elem_bytes, &value)) + } + }) + .collect() + } +} + +use crate::expression::bound::Bound; +use crate::expression::AsExpression; + +macro_rules! array_as_expression { + ($ty:ty, $sql_type:ty) => { + #[cfg(feature = "postgres_backend")] + // this simplifies the macro implementation + // as some macro calls use this lifetime + #[allow(clippy::extra_unused_lifetimes)] + impl<'a, 'b, ST: 'static, T> AsExpression<$sql_type> for $ty { + type Expression = Bound<$sql_type, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + }; +} + +array_as_expression!(&'a [T], Array); +array_as_expression!(&'a [T], Nullable>); +array_as_expression!(&'a &'b [T], Array); +array_as_expression!(&'a &'b [T], Nullable>); +array_as_expression!(Vec, Array); +array_as_expression!(Vec, Nullable>); +array_as_expression!(&'a Vec, Array); +array_as_expression!(&'a Vec, Nullable>); +array_as_expression!(&'a &'b Vec, Array); +array_as_expression!(&'a &'b Vec, Nullable>); + +#[cfg(feature = "postgres_backend")] +impl ToSql, Pg> for [T] +where + Pg: HasSqlType, + T: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + let num_dimensions = 1; + out.write_i32::(num_dimensions)?; + let flags = 0; + out.write_i32::(flags)?; + let element_oid = Pg::metadata(out.metadata_lookup()).oid()?; + out.write_u32::(element_oid)?; + out.write_i32::(self.len().try_into()?)?; + let lower_bound = 1; + out.write_i32::(lower_bound)?; + + // This buffer is created outside of the loop to reuse the underlying memory allocation + // For most cases all array elements will have the same serialized size + let mut buffer = Vec::new(); + + for elem in self.iter() { + let is_null = { + let mut temp_buffer = Output::new(ByteWrapper(&mut buffer), out.metadata_lookup()); + elem.to_sql(&mut temp_buffer)? + }; + + if let IsNull::No = is_null { + out.write_i32::(buffer.len().try_into()?)?; + out.write_all(&buffer)?; + buffer.clear(); + } else { + // https://github.com/postgres/postgres/blob/82f8107b92c9104ec9d9465f3f6a4c6dab4c124a/src/backend/utils/adt/arrayfuncs.c#L1461 + out.write_i32::(-1)?; + } + } + + Ok(IsNull::No) + } +} + +#[cfg(feature = "postgres_backend")] +impl ToSql>, Pg> for [T] +where + [T]: ToSql, Pg>, + ST: 'static, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + ToSql::, Pg>::to_sql(self, out) + } +} + +#[cfg(feature = "postgres_backend")] +impl ToSql, Pg> for Vec +where + ST: 'static, + [T]: ToSql, Pg>, + T: fmt::Debug, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + (self as &[T]).to_sql(out) + } +} + +#[cfg(feature = "postgres_backend")] +impl ToSql>, Pg> for Vec +where + ST: 'static, + Vec: ToSql, Pg>, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + ToSql::, Pg>::to_sql(self, out) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/pg/types/date_and_time/chrono.rs b/collector/compile-benchmarks/diesel-2.2.10/src/pg/types/date_and_time/chrono.rs new file mode 100644 index 000000000..756e0bf78 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/pg/types/date_and_time/chrono.rs @@ -0,0 +1,417 @@ +//! This module makes it possible to map `chrono::DateTime` values to postgres `Date` +//! and `Timestamp` fields. It is enabled with the `chrono` feature. + +extern crate chrono; +use self::chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; + +use super::{PgDate, PgInterval, PgTime, PgTimestamp}; +use crate::deserialize::{self, FromSql}; +use crate::pg::{Pg, PgValue}; +use crate::serialize::{self, Output, ToSql}; +use crate::sql_types::{Date, Interval, Time, Timestamp, Timestamptz}; + +// Postgres timestamps start from January 1st 2000. +fn pg_epoch() -> NaiveDateTime { + NaiveDate::from_ymd_opt(2000, 1, 1) + .expect("This is in supported range of chrono dates") + .and_hms_opt(0, 0, 0) + .expect("This is a valid input") +} + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl FromSql for NaiveDateTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let PgTimestamp(offset) = FromSql::::from_sql(bytes)?; + match pg_epoch().checked_add_signed(Duration::microseconds(offset)) { + Some(v) => Ok(v), + None => { + let message = "Tried to deserialize a timestamp that is too large for Chrono"; + Err(message.into()) + } + } + } +} + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl ToSql for NaiveDateTime { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + let time = match (self.signed_duration_since(pg_epoch())).num_microseconds() { + Some(time) => time, + None => { + let error_message = + format!("{self:?} as microseconds is too large to fit in an i64"); + return Err(error_message.into()); + } + }; + ToSql::::to_sql(&PgTimestamp(time), &mut out.reborrow()) + } +} + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl FromSql for NaiveDateTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + FromSql::::from_sql(bytes) + } +} + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl ToSql for NaiveDateTime { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + ToSql::::to_sql(self, out) + } +} + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl FromSql for DateTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let naive_date_time = >::from_sql(bytes)?; + Ok(Utc.from_utc_datetime(&naive_date_time)) + } +} + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl FromSql for DateTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let naive_date_time = >::from_sql(bytes)?; + Ok(Local::from_utc_datetime(&Local, &naive_date_time)) + } +} + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl ToSql for DateTime { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + ToSql::::to_sql(&self.naive_utc(), &mut out.reborrow()) + } +} + +fn midnight() -> NaiveTime { + NaiveTime::from_hms_opt(0, 0, 0).expect("This is a valid hms spec") +} + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl ToSql for NaiveTime { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + let duration = self.signed_duration_since(midnight()); + match duration.num_microseconds() { + Some(offset) => ToSql::::to_sql(&PgTime(offset), &mut out.reborrow()), + None => unreachable!(), + } + } +} + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl FromSql for NaiveTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let PgTime(offset) = FromSql::::from_sql(bytes)?; + let duration = Duration::microseconds(offset); + Ok(midnight() + duration) + } +} + +fn pg_epoch_date() -> NaiveDate { + NaiveDate::from_ymd_opt(2000, 1, 1).expect("This is in supported range of chrono dates") +} + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl ToSql for NaiveDate { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + let days_since_epoch = self.signed_duration_since(pg_epoch_date()).num_days(); + ToSql::::to_sql(&PgDate(days_since_epoch.try_into()?), &mut out.reborrow()) + } +} + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl FromSql for NaiveDate { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let PgDate(offset) = FromSql::::from_sql(bytes)?; + #[allow(deprecated)] // otherwise we would need to bump our minimal chrono version + let duration = Duration::days(i64::from(offset)); + match pg_epoch_date().checked_add_signed(duration) { + Some(date) => Ok(date), + None => { + let error_message = format!( + "Chrono can only represent dates up to {:?}", + chrono::NaiveDate::MAX + ); + Err(error_message.into()) + } + } + } +} + +const DAYS_PER_MONTH: i32 = 30; +const SECONDS_PER_DAY: i64 = 60 * 60 * 24; +const MICROSECONDS_PER_SECOND: i64 = 1_000_000; + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl ToSql for Duration { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + let microseconds: i64 = if let Some(v) = self.num_microseconds() { + v % (MICROSECONDS_PER_SECOND * SECONDS_PER_DAY) + } else { + return Err("Failed to create microseconds by overflow".into()); + }; + let days: i32 = self + .num_days() + .try_into() + .expect("Failed to get i32 days from i64"); + // We don't use months here, because in PostgreSQL + // `timestamp - timestamp` returns interval where + // every delta is contained in days and microseconds, and 0 months. + // https://www.postgresql.org/docs/current/functions-datetime.html + let interval = PgInterval { + microseconds, + days, + months: 0, + }; + >::to_sql(&interval, &mut out.reborrow()) + } +} + +#[cfg(all(feature = "chrono", feature = "postgres_backend"))] +impl FromSql for Duration { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let interval: PgInterval = FromSql::::from_sql(bytes)?; + // We use 1 month = 30 days and 1 day = 24 hours, as postgres + // use those ratios as default when explicitly converted. + // For reference, please read `justify_interval` from this page. + // https://www.postgresql.org/docs/current/functions-datetime.html + let days = interval.months * DAYS_PER_MONTH + interval.days; + Ok(Duration::days(days as i64) + Duration::microseconds(interval.microseconds)) + } +} + +#[cfg(test)] +mod tests { + extern crate chrono; + extern crate dotenvy; + + use self::chrono::{Duration, FixedOffset, NaiveDate, NaiveTime, TimeZone, Utc}; + + use crate::dsl::{now, sql}; + use crate::prelude::*; + use crate::select; + use crate::sql_types::{Date, Interval, Time, Timestamp, Timestamptz}; + use crate::test_helpers::connection; + + #[test] + fn unix_epoch_encodes_correctly() { + let connection = &mut connection(); + let time = NaiveDate::from_ymd_opt(1970, 1, 1) + .unwrap() + .and_hms_opt(0, 0, 0) + .unwrap(); + let query = select(sql::("'1970-01-01'").eq(time)); + assert!(query.get_result::(connection).unwrap()); + } + + #[test] + fn unix_epoch_encodes_correctly_with_utc_timezone() { + let connection = &mut connection(); + let time = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).single().unwrap(); + let query = select(sql::("'1970-01-01Z'::timestamptz").eq(time)); + assert!(query.get_result::(connection).unwrap()); + } + + #[test] + fn unix_epoch_encodes_correctly_with_timezone() { + let connection = &mut connection(); + let time = FixedOffset::west_opt(3600) + .unwrap() + .with_ymd_and_hms(1970, 1, 1, 0, 0, 0) + .single() + .unwrap(); + let query = select(sql::("'1970-01-01 01:00:00Z'::timestamptz").eq(time)); + assert!(query.get_result::(connection).unwrap()); + } + + #[test] + fn unix_epoch_decodes_correctly() { + let connection = &mut connection(); + let time = NaiveDate::from_ymd_opt(1970, 1, 1) + .unwrap() + .and_hms_opt(0, 0, 0) + .unwrap(); + let epoch_from_sql = + select(sql::("'1970-01-01'::timestamp")).get_result(connection); + assert_eq!(Ok(time), epoch_from_sql); + } + + #[test] + fn unix_epoch_decodes_correctly_with_timezone() { + let connection = &mut connection(); + let time = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).single().unwrap(); + let epoch_from_sql = + select(sql::("'1970-01-01Z'::timestamptz")).get_result(connection); + assert_eq!(Ok(time), epoch_from_sql); + } + + #[test] + fn times_relative_to_now_encode_correctly() { + let connection = &mut connection(); + let time = Utc::now().naive_utc() + Duration::try_seconds(60).unwrap(); + let query = select(now.at_time_zone("utc").lt(time)); + assert!(query.get_result::(connection).unwrap()); + + let time = Utc::now().naive_utc() - Duration::try_seconds(60).unwrap(); + let query = select(now.at_time_zone("utc").gt(time)); + assert!(query.get_result::(connection).unwrap()); + } + + #[test] + fn times_with_timezones_round_trip_after_conversion() { + let connection = &mut connection(); + let time = FixedOffset::east_opt(3600) + .unwrap() + .with_ymd_and_hms(2016, 1, 2, 1, 0, 0) + .unwrap(); + let expected = NaiveDate::from_ymd_opt(2016, 1, 1) + .unwrap() + .and_hms_opt(20, 0, 0) + .unwrap(); + let query = select(time.into_sql::().at_time_zone("EDT")); + assert_eq!(Ok(expected), query.get_result(connection)); + } + + #[test] + fn times_of_day_encode_correctly() { + let connection = &mut connection(); + + let midnight = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + let query = select(sql::
+ Expression, + U: Query, + { + InsertStatement::new( + self.target, + self.records.with_columns(columns), + self.operator, + self.returning, + ) + } +} + +impl QueryFragment for InsertStatement +where + DB: Backend + DieselReserveSpecialization, + T: Table, + T::FromClause: QueryFragment, + U: QueryFragment + CanInsertInSingleQuery, + Op: QueryFragment, + Ret: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + if self.records.rows_to_insert() == Some(0) { + out.push_sql("SELECT 1 FROM "); + self.into_clause.walk_ast(out.reborrow())?; + out.push_sql(" WHERE 1=0"); + return Ok(()); + } + + self.operator.walk_ast(out.reborrow())?; + out.push_sql(" INTO "); + self.into_clause.walk_ast(out.reborrow())?; + out.push_sql(" "); + self.records.walk_ast(out.reborrow())?; + self.returning.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl AsQuery for InsertStatement +where + T: Table, + InsertStatement>: Query, +{ + type SqlType = ::SqlType; + type Query = InsertStatement>; + + fn as_query(self) -> Self::Query { + self.returning(T::all_columns()) + } +} + +impl Query for InsertStatement> +where + T: QuerySource, + Ret: Expression + SelectableExpression + NonAggregate, +{ + type SqlType = Ret::SqlType; +} + +impl RunQueryDsl for InsertStatement {} + +impl InsertStatement { + /// Specify what expression is returned after execution of the `insert`. + /// # Examples + /// + /// ### Inserting records: + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # #[cfg(feature = "postgres")] + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let inserted_names = diesel::insert_into(users) + /// .values(&vec![name.eq("Timmy"), name.eq("Jimmy")]) + /// .returning(name) + /// .get_results(connection) + /// .unwrap(); + /// // Note that the returned order is not guaranteed to be preserved + /// assert_eq!(inserted_names.len(), 2); + /// assert!(inserted_names.contains(&"Timmy".to_string())); + /// assert!(inserted_names.contains(&"Jimmy".to_string())); + /// # } + /// # #[cfg(not(feature = "postgres"))] + /// # fn main() {} + /// ``` + pub fn returning(self, returns: E) -> InsertStatement> + where + InsertStatement>: Query, + { + InsertStatement::new( + self.target, + self.records, + self.operator, + ReturningClause(returns), + ) + } +} + +/// Marker trait to indicate that no additional operations have been added +/// to a record for insert. +/// +/// This is used to prevent things like +/// `.on_conflict_do_nothing().on_conflict_do_nothing()` +/// from compiling. +#[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes") +)] +pub trait UndecoratedInsertRecord
{} + +impl UndecoratedInsertRecord for &T where T: ?Sized + UndecoratedInsertRecord {} + +impl UndecoratedInsertRecord for ColumnInsertValue where T: Column {} + +impl UndecoratedInsertRecord + for DefaultableColumnInsertValue> +where + T: Column, +{ +} + +impl UndecoratedInsertRecord
for [T] where T: UndecoratedInsertRecord
{} + +impl UndecoratedInsertRecord
+ for BatchInsert +where + T: UndecoratedInsertRecord
, +{ +} + +impl UndecoratedInsertRecord
for Vec where [T]: UndecoratedInsertRecord
{} + +impl UndecoratedInsertRecord for Eq where Lhs: Column {} + +impl UndecoratedInsertRecord for Option> where + Eq: UndecoratedInsertRecord +{ +} + +impl UndecoratedInsertRecord for Grouped> where Lhs: Column {} + +impl UndecoratedInsertRecord for Option>> where + Eq: UndecoratedInsertRecord +{ +} + +impl UndecoratedInsertRecord
for ValuesClause where + T: UndecoratedInsertRecord
+{ +} + +#[derive(Debug, Clone, Copy, QueryId)] +#[doc(hidden)] +pub struct DefaultValues; + +impl CanInsertInSingleQuery for DefaultValues { + fn rows_to_insert(&self) -> Option { + Some(1) + } +} + +impl Insertable for DefaultValues { + type Values = DefaultValues; + + fn values(self) -> Self::Values { + self + } +} + +impl Insertable for &DefaultValues { + type Values = DefaultValues; + + fn values(self) -> Self::Values { + *self + } +} + +impl QueryFragment for DefaultValues +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment + for DefaultValues +where + DB: Backend + + SqlDialect< + DefaultValueClauseForInsert = sql_dialect::default_value_clause::AnsiDefaultValueClause, + >, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql("DEFAULT VALUES"); + Ok(()) + } +} + +/// This type represents a values clause used as part of insert statements +/// +/// Diesel exposes this type for third party backends so that +/// they can implement batch insert support +#[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes") +)] +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ValuesClause { + /// Values to insert + pub values: T, + _marker: PhantomData, +} + +impl Default for ValuesClause { + fn default() -> Self { + Self::new(T::default()) + } +} + +impl ValuesClause { + pub(crate) fn new(values: T) -> Self { + Self { + values, + _marker: PhantomData, + } + } +} + +impl CanInsertInSingleQuery for ValuesClause +where + DB: Backend, + T: CanInsertInSingleQuery, +{ + fn rows_to_insert(&self) -> Option { + self.values.rows_to_insert() + } +} + +impl QueryFragment for ValuesClause +where + DB: Backend, + Tab: Table, + T: InsertValues, + DefaultValues: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + if self.values.is_noop(out.backend())? { + DefaultValues.walk_ast(out)?; + } else { + out.push_sql("("); + self.values.column_names(out.reborrow())?; + out.push_sql(") VALUES ("); + self.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} + +mod private { + use crate::backend::{Backend, DieselReserveSpecialization}; + use crate::query_builder::{AstPass, QueryFragment, QueryId}; + use crate::QueryResult; + + #[derive(Debug, Copy, Clone, QueryId)] + pub struct Insert; + + impl QueryFragment for Insert + where + DB: Backend + DieselReserveSpecialization, + { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql("INSERT"); + Ok(()) + } + } + + /// A marker type for insert or ignore statements + #[derive(Debug, Copy, Clone, QueryId)] + pub struct InsertOrIgnore; + + #[cfg(feature = "sqlite")] + impl QueryFragment for InsertOrIgnore { + fn walk_ast<'b>( + &'b self, + mut out: AstPass<'_, 'b, crate::sqlite::Sqlite>, + ) -> QueryResult<()> { + out.push_sql("INSERT OR IGNORE"); + Ok(()) + } + } + + #[cfg(feature = "mysql_backend")] + impl QueryFragment for InsertOrIgnore { + fn walk_ast<'b>( + &'b self, + mut out: AstPass<'_, 'b, crate::mysql::Mysql>, + ) -> QueryResult<()> { + out.push_sql("INSERT IGNORE"); + Ok(()) + } + } + + /// A marker type for replace statements + #[derive(Debug, Copy, Clone, QueryId)] + pub struct Replace; + + #[cfg(feature = "sqlite")] + impl QueryFragment for Replace { + fn walk_ast<'b>( + &'b self, + mut out: AstPass<'_, 'b, crate::sqlite::Sqlite>, + ) -> QueryResult<()> { + out.push_sql("REPLACE"); + Ok(()) + } + } + + #[cfg(feature = "mysql_backend")] + impl QueryFragment for Replace { + fn walk_ast<'b>( + &'b self, + mut out: AstPass<'_, 'b, crate::mysql::Mysql>, + ) -> QueryResult<()> { + out.push_sql("REPLACE"); + Ok(()) + } + } + + // otherwise rustc complains at a different location that this trait is more private than the other item that uses it + #[allow(unreachable_pub)] + pub trait InsertAutoTypeHelper { + type Table; + type Op; + } + + impl InsertAutoTypeHelper for crate::query_builder::IncompleteInsertStatement { + type Table = T; + type Op = Op; + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/limit_clause.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/limit_clause.rs new file mode 100644 index 000000000..22f3ff526 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/limit_clause.rs @@ -0,0 +1,19 @@ +simple_clause!( + /// A query node indicating the absence of a limit clause + /// + /// This type is only relevant for implementing custom backends + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes") + )] + NoLimitClause, + /// A query node representing a limit clause + /// + /// This type is only relevant for implementing custom backends + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes") + )] + LimitClause, + " LIMIT " +); diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/limit_offset_clause.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/limit_offset_clause.rs new file mode 100644 index 000000000..64da8ab68 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/limit_offset_clause.rs @@ -0,0 +1,32 @@ +use super::QueryFragment; +use crate::query_builder::QueryId; + +/// A helper query node that contains both limit and offset clauses +/// +/// This type is only relevant for implementing custom backends +#[derive(Debug, Clone, Copy, QueryId)] +#[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) +)] +pub struct LimitOffsetClause { + /// The limit clause + pub limit_clause: Limit, + /// The offset clause + pub offset_clause: Offset, +} + +/// A boxed variant of [`LimitOffsetClause`](LimitOffsetClause) +/// +/// This type is only relevant for implementing custom backends +#[allow(missing_debug_implementations)] +#[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) +)] +pub struct BoxedLimitOffsetClause<'a, DB> { + /// The limit clause + pub limit: Option + Send + 'a>>, + /// The offset clause + pub offset: Option + Send + 'a>>, +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/locking_clause.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/locking_clause.rs new file mode 100644 index 000000000..bb855705c --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/locking_clause.rs @@ -0,0 +1,67 @@ +use crate::backend::{Backend, DieselReserveSpecialization}; +use crate::query_builder::{AstPass, QueryFragment, QueryId}; +use crate::result::QueryResult; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoLockingClause; + +impl QueryFragment for NoLockingClause +where + DB: Backend + DieselReserveSpecialization, +{ + fn walk_ast<'b>(&'b self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> { + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct LockingClause { + pub(crate) lock_mode: LockMode, + modifier: Modifier, +} + +impl LockingClause { + pub(crate) fn new(lock_mode: LockMode, modifier: Modifier) -> Self { + LockingClause { + lock_mode, + modifier, + } + } +} + +impl QueryFragment for LockingClause +where + DB: Backend + DieselReserveSpecialization, + L: QueryFragment, + M: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.lock_mode.walk_ast(out.reborrow())?; + self.modifier.walk_ast(out.reborrow()) + } +} + +// `LockMode` parameters +// All the different types of row locks that can be acquired. +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ForUpdate; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ForNoKeyUpdate; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ForShare; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ForKeyShare; + +// Modifiers +// To be used in conjunction with a lock mode. +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoModifier; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct SkipLocked; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoWait; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/mod.rs new file mode 100644 index 000000000..acaf7a753 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/mod.rs @@ -0,0 +1,430 @@ +//! Contains traits responsible for the actual construction of SQL statements +//! +//! The types in this module are part of Diesel's public API, but are generally +//! only useful for implementing Diesel plugins. Applications should generally +//! not need to care about the types inside of this module. + +#[macro_use] +mod query_id; +#[macro_use] +mod clause_macro; + +pub(crate) mod ast_pass; +pub mod bind_collector; +mod collected_query; +pub(crate) mod combination_clause; +mod debug_query; +mod delete_statement; +mod distinct_clause; +pub(crate) mod from_clause; +pub(crate) mod functions; +mod group_by_clause; +mod having_clause; +pub(crate) mod insert_statement; +pub(crate) mod limit_clause; +pub(crate) mod limit_offset_clause; +pub(crate) mod locking_clause; +pub(crate) mod nodes; +pub(crate) mod offset_clause; +pub(crate) mod order_clause; +pub(crate) mod returning_clause; +pub(crate) mod select_clause; +pub(crate) mod select_statement; +mod sql_query; +pub(crate) mod update_statement; +pub(crate) mod upsert; +pub(crate) mod where_clause; + +#[doc(inline)] +pub use self::ast_pass::AstPass; +#[doc(inline)] +pub use self::bind_collector::{BindCollector, MoveableBindCollector}; +#[doc(inline)] +pub use self::collected_query::CollectedQuery; +#[doc(inline)] +pub use self::debug_query::DebugQuery; +#[doc(inline)] +pub use self::delete_statement::{BoxedDeleteStatement, DeleteStatement}; +#[doc(inline)] +pub use self::insert_statement::{ + IncompleteInsertOrIgnoreStatement, IncompleteInsertStatement, IncompleteReplaceStatement, + InsertOrIgnoreStatement, InsertStatement, ReplaceStatement, +}; +#[doc(inline)] +pub use self::query_id::QueryId; +#[doc(inline)] +pub use self::sql_query::{BoxedSqlQuery, SqlQuery}; +#[doc(inline)] +pub use self::upsert::on_conflict_target_decorations::DecoratableTarget; + +#[doc(inline)] +pub use self::update_statement::changeset::AsChangeset; +#[doc(inline)] +pub use self::update_statement::target::{IntoUpdateTarget, UpdateTarget}; +#[doc(inline)] +pub use self::update_statement::{BoxedUpdateStatement, UpdateStatement}; + +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +pub use self::limit_clause::{LimitClause, NoLimitClause}; +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +pub use self::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +pub use self::offset_clause::{NoOffsetClause, OffsetClause}; +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +pub use self::order_clause::{NoOrderClause, OrderClause}; + +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +#[doc(inline)] +pub(crate) use self::insert_statement::batch_insert::BatchInsert; +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +pub(crate) use self::insert_statement::{UndecoratedInsertRecord, ValuesClause}; + +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +#[doc(inline)] +pub use self::insert_statement::DefaultValues; + +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +#[doc(inline)] +pub use self::returning_clause::ReturningClause; + +#[doc(inline)] +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +pub(crate) use self::ast_pass::AstPassToSqlOptions; + +#[doc(inline)] +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +pub(crate) use self::select_clause::SelectClauseExpression; + +#[doc(inline)] +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +pub(crate) use self::from_clause::{FromClause, NoFromClause}; +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +#[doc(inline)] +pub(crate) use self::select_statement::BoxedSelectStatement; + +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +#[doc(inline)] +pub(crate) use self::select_statement::SelectStatement; + +pub(crate) use self::insert_statement::ColumnList; + +#[cfg(feature = "postgres_backend")] +pub use crate::pg::query_builder::only::Only; + +#[cfg(feature = "postgres_backend")] +pub use crate::pg::query_builder::tablesample::{Tablesample, TablesampleMethod}; + +#[cfg(feature = "postgres_backend")] +pub(crate) use self::bind_collector::ByteWrapper; +use crate::backend::Backend; +use crate::result::QueryResult; +use std::error::Error; + +#[doc(hidden)] +pub type Binds = Vec>>; +/// A specialized Result type used with the query builder. +pub type BuildQueryResult = Result<(), Box>; + +/// Constructs a SQL query from a Diesel AST. +/// +/// The only reason you should ever need to interact with this trait is if you +/// are extending Diesel with support for a new backend. Plugins which extend +/// the query builder with new capabilities will interact with [`AstPass`] +/// instead. +/// +pub trait QueryBuilder { + /// Add `sql` to the end of the query being constructed. + fn push_sql(&mut self, sql: &str); + + /// Quote `identifier`, and add it to the end of the query being + /// constructed. + fn push_identifier(&mut self, identifier: &str) -> QueryResult<()>; + + /// Add a placeholder for a bind parameter to the end of the query being + /// constructed. + fn push_bind_param(&mut self); + + /// Increases the internal counter for bind parameters without adding the + /// bind parameter itself to the query + fn push_bind_param_value_only(&mut self) {} + + /// Returns the constructed SQL query. + fn finish(self) -> String; +} + +/// A complete SQL query with a return type. +/// +/// This can be a select statement, or a command such as `update` or `insert` +/// with a `RETURNING` clause. Unlike [`Expression`], types implementing this +/// trait are guaranteed to be executable on their own. +/// +/// A type which doesn't implement this trait may still represent a complete SQL +/// query. For example, an `INSERT` statement without a `RETURNING` clause will +/// not implement this trait, but can still be executed. +/// +/// [`Expression`]: crate::expression::Expression +pub trait Query { + /// The SQL type that this query represents. + /// + /// This is the SQL type of the `SELECT` clause for select statements, and + /// the SQL type of the `RETURNING` clause for insert, update, or delete + /// statements. + type SqlType; +} + +impl Query for &T { + type SqlType = T::SqlType; +} + +/// Indicates that a type is a `SELECT` statement. +/// +/// This trait differs from `Query` in two ways: +/// - It is implemented only for select statements, rather than all queries +/// which return a value. +/// - It has looser constraints. A type implementing `SelectQuery` is known to +/// be potentially valid if used as a subselect, but it is not necessarily +/// able to be executed. +pub trait SelectQuery { + /// The SQL type of the `SELECT` clause + type SqlType; +} + +/// An untyped fragment of SQL. +/// +/// This may be a complete SQL command (such as an update statement without a +/// `RETURNING` clause), or a subsection (such as our internal types used to +/// represent a `WHERE` clause). Implementations of [`ExecuteDsl`] and +/// [`LoadQuery`] will generally require that this trait be implemented. +/// +/// [`ExecuteDsl`]: crate::query_dsl::methods::ExecuteDsl +/// [`LoadQuery`]: crate::query_dsl::methods::LoadQuery +#[diagnostic::on_unimplemented( + message = "`{Self}` is no valid SQL fragment for the `{DB}` backend", + note = "this usually means that the `{DB}` database system does not support \n\ + this SQL syntax" +)] +pub trait QueryFragment { + /// Walk over this `QueryFragment` for all passes. + /// + /// This method is where the actual behavior of an AST node is implemented. + /// This method will contain the behavior required for all possible AST + /// passes. See [`AstPass`] for more details. + /// + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()>; + + /// Converts this `QueryFragment` to its SQL representation. + /// + /// This method should only be called by implementations of `Connection`. + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + fn to_sql(&self, out: &mut DB::QueryBuilder, backend: &DB) -> QueryResult<()> { + let mut options = AstPassToSqlOptions::default(); + self.walk_ast(AstPass::to_sql(out, &mut options, backend)) + } + + /// Serializes all bind parameters in this query. + /// + /// A bind parameter is a value which is sent separately from the query + /// itself. It is represented in SQL with a placeholder such as `?` or `$1`. + /// + /// This method should only be called by implementations of `Connection`. + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + fn collect_binds<'b>( + &'b self, + out: &mut DB::BindCollector<'b>, + metadata_lookup: &mut DB::MetadataLookup, + backend: &'b DB, + ) -> QueryResult<()> { + self.walk_ast(AstPass::collect_binds(out, metadata_lookup, backend)) + } + + /// Is this query safe to store in the prepared statement cache? + /// + /// In order to keep our prepared statement cache at a reasonable size, we + /// avoid caching any queries which represent a potentially unbounded number + /// of SQL queries. Generally this will only return `true` for queries for + /// which `to_sql` will always construct exactly identical SQL. + /// + /// Some examples of where this method will return `false` are: + /// + /// - `SqlLiteral` (We don't know if the SQL was constructed dynamically, so + /// we must assume that it was) + /// - `In` and `NotIn` (Each value requires a separate bind param + /// placeholder) + /// + /// This method should only be called by implementations of `Connection`. + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + fn is_safe_to_cache_prepared(&self, backend: &DB) -> QueryResult { + let mut result = true; + self.walk_ast(AstPass::is_safe_to_cache_prepared(&mut result, backend))?; + Ok(result) + } + + /// Does walking this AST have any effect? + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + fn is_noop(&self, backend: &DB) -> QueryResult { + let mut result = true; + self.walk_ast(AstPass::is_noop(&mut result, backend))?; + Ok(result) + } +} + +impl QueryFragment for Box +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + QueryFragment::walk_ast(&**self, pass) + } +} + +impl QueryFragment for &T +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + QueryFragment::walk_ast(&**self, pass) + } +} + +impl QueryFragment for () { + fn walk_ast<'b>(&'b self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for Option +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + match *self { + Some(ref c) => c.walk_ast(out), + None => Ok(()), + } + } +} + +/// A trait used to construct type erased boxed variant of the current query node +/// +/// Mainly useful for implementing third party backends +pub trait IntoBoxedClause<'a, DB> { + /// Resulting type + type BoxedClause; + + /// Convert the given query node in it's boxed representation + fn into_boxed(self) -> Self::BoxedClause; +} + +/// Types that can be converted into a complete, typed SQL query. +/// +/// This is used internally to automatically add the right select clause when +/// none is specified, or to automatically add `RETURNING *` in certain contexts. +/// +/// A type which implements this trait is guaranteed to be valid for execution. +pub trait AsQuery { + /// The SQL type of `Self::Query` + type SqlType; + + /// What kind of query does this type represent? + type Query: Query; + + /// Converts a type which semantically represents a SQL query into the + /// actual query being executed. See the trait level docs for more. + // This method is part of our public API, + // so we won't change the name to just appease clippy + // (Also the trait is literally named `AsQuery` so + // naming the method similarity is fine) + #[allow(clippy::wrong_self_convention)] + fn as_query(self) -> Self::Query; +} + +impl AsQuery for T { + type SqlType = ::SqlType; + type Query = T; + + fn as_query(self) -> ::Query { + self + } +} + +/// Takes a query `QueryFragment` expression as an argument and returns a type +/// that implements `fmt::Display` and `fmt::Debug` to show the query. +/// +/// The `Display` implementation will show the exact query being sent to the +/// server, with a comment showing the values of the bind parameters. The +/// `Debug` implementation will include the same information in a more +/// structured form, and respects pretty printing. +/// +/// # Example +/// +/// ### Returning SQL from a count statement: +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// # use diesel::*; +/// # use schema::*; +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// let sql = debug_query::(&users.count()).to_string(); +/// # if cfg!(feature = "postgres") { +/// # assert_eq!(sql, r#"SELECT COUNT(*) FROM "users" -- binds: []"#); +/// # } else { +/// assert_eq!(sql, "SELECT COUNT(*) FROM `users` -- binds: []"); +/// # } +/// +/// let query = users.find(1); +/// let debug = debug_query::(&query); +/// # if cfg!(feature = "postgres") { +/// # assert_eq!(debug.to_string(), "SELECT \"users\".\"id\", \"users\".\"name\" \ +/// # FROM \"users\" WHERE (\"users\".\"id\" = $1) -- binds: [1]"); +/// # } else { +/// assert_eq!(debug.to_string(), "SELECT `users`.`id`, `users`.`name` FROM `users` \ +/// WHERE (`users`.`id` = ?) -- binds: [1]"); +/// # } +/// +/// let debug = format!("{:?}", debug); +/// # if !cfg!(feature = "postgres") { // Escaping that string is a pain +/// let expected = "Query { \ +/// sql: \"SELECT `users`.`id`, `users`.`name` FROM `users` WHERE \ +/// (`users`.`id` = ?)\", \ +/// binds: [1] \ +/// }"; +/// assert_eq!(debug, expected); +/// # } +/// # } +/// ``` +pub fn debug_query(query: &T) -> DebugQuery<'_, T, DB> { + DebugQuery::new(query) +} + +mod private { + #[allow(missing_debug_implementations, missing_copy_implementations)] + pub struct NotSpecialized; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/nodes/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/nodes/mod.rs new file mode 100644 index 000000000..7c06e9b7b --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/nodes/mod.rs @@ -0,0 +1,81 @@ +use crate::backend::DieselReserveSpecialization; +use crate::query_builder::*; +use std::marker::PhantomData; + +#[doc(hidden)] // used by the table macro +pub trait StaticQueryFragment { + type Component: 'static; + const STATIC_COMPONENT: &'static Self::Component; +} + +#[derive(Debug, Copy, Clone)] +#[doc(hidden)] // used by the table macro +pub struct StaticQueryFragmentInstance(PhantomData); + +impl StaticQueryFragmentInstance { + #[doc(hidden)] // used by the table macro + pub const fn new() -> Self { + Self(PhantomData) + } +} + +impl QueryFragment for StaticQueryFragmentInstance +where + DB: Backend + DieselReserveSpecialization, + T: StaticQueryFragment, + T::Component: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + T::STATIC_COMPONENT.walk_ast(pass) + } +} + +#[derive(Debug, Copy, Clone)] +#[doc(hidden)] // used by the table macro +pub struct Identifier<'a>(pub &'a str); + +impl QueryFragment for Identifier<'_> { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_identifier(self.0) + } +} + +pub trait MiddleFragment { + fn push_sql(&self, pass: AstPass<'_, '_, DB>); +} + +impl MiddleFragment for &str { + fn push_sql(&self, mut pass: AstPass<'_, '_, DB>) { + pass.push_sql(self); + } +} + +#[derive(Debug, Copy, Clone)] +#[doc(hidden)] // used by the table macro +pub struct InfixNode { + lhs: T, + rhs: U, + middle: M, +} + +impl InfixNode { + #[doc(hidden)] // used by the table macro + pub const fn new(lhs: T, rhs: U, middle: M) -> Self { + InfixNode { lhs, rhs, middle } + } +} + +impl QueryFragment for InfixNode +where + DB: Backend + DieselReserveSpecialization, + T: QueryFragment, + U: QueryFragment, + M: MiddleFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.lhs.walk_ast(out.reborrow())?; + self.middle.push_sql(out.reborrow()); + self.rhs.walk_ast(out.reborrow())?; + Ok(()) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/offset_clause.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/offset_clause.rs new file mode 100644 index 000000000..2fa230265 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/offset_clause.rs @@ -0,0 +1,19 @@ +simple_clause!( + /// A query node indicating the absence of an offset clause + /// + /// This type is only relevant for implementing custom backends + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes") + )] + NoOffsetClause, + /// A query node representing an offset clause + /// + /// This type is only relevant for implementing custom backends + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes") + )] + OffsetClause, + " OFFSET " +); diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/order_clause.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/order_clause.rs new file mode 100644 index 000000000..3202670a6 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/order_clause.rs @@ -0,0 +1,20 @@ +simple_clause!(NoOrderClause, OrderClause, " ORDER BY "); + +impl<'a, DB, Expr> From> for Option + Send + 'a>> +where + DB: Backend, + Expr: QueryFragment + Send + 'a, +{ + fn from(order: OrderClause) -> Self { + Some(Box::new(order.0)) + } +} + +impl From for Option + Send + '_>> +where + DB: Backend, +{ + fn from(_: NoOrderClause) -> Self { + None + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/query_id.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/query_id.rs new file mode 100644 index 000000000..f354c78d8 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/query_id.rs @@ -0,0 +1,144 @@ +use super::QueryFragment; +use std::any::{Any, TypeId}; + +/// Uniquely identifies queries by their type for the purpose of prepared +/// statement caching. +/// +/// All types which implement `QueryFragment` should also implement this trait +/// (It is not an actual supertrait of `QueryFragment` for boxing purposes). +/// +/// See the documentation of [the `QueryId` type] and [`HAS_STATIC_QUERY_ID`] +/// for more details. +/// +/// [the `QueryId` type]: QueryId::QueryId +/// [`HAS_STATIC_QUERY_ID`]: QueryId::HAS_STATIC_QUERY_ID +/// +/// ### Deriving +/// +/// This trait can [be automatically derived](derive@QueryId) +/// by Diesel. +/// For example, given this struct: +/// +/// If the SQL generated by a struct is not uniquely identifiable by its type, +/// meaning that `HAS_STATIC_QUERY_ID` should always be false, +/// you should not derive this trait. +/// In that case you should manually implement it instead. +pub trait QueryId { + /// A type which uniquely represents `Self` in a SQL query. + /// + /// Typically this will be a re-construction of `Self` using the `QueryId` + /// type of each of your type parameters. For example, the type `And` would have `type QueryId = And`. + /// + /// The exception to this is when one of your type parameters does not + /// affect whether the same prepared statement can be used or not. For + /// example, a bind parameter is represented as `Bound`. + /// The actual Rust type we are serializing does not matter for the purposes + /// of prepared statement reuse, but a query which has identical SQL but + /// different types for its bind parameters requires a new prepared + /// statement. For this reason, `Bound` would have `type QueryId = + /// Bound`. + /// + /// If `HAS_STATIC_QUERY_ID` is `false`, you can put any type here + /// (typically `()`). + type QueryId: Any; + + /// Can the SQL generated by `Self` be uniquely identified by its type? + /// + /// Typically this question can be answered by looking at whether + /// `unsafe_to_cache_prepared` is called in your implementation of + /// `QueryFragment::walk_ast`. In Diesel itself, the only type which has + /// `false` here, but is potentially safe to store in the prepared statement + /// cache is a boxed query. + const HAS_STATIC_QUERY_ID: bool = true; + + /// Returns the type id of `Self::QueryId` if `Self::HAS_STATIC_QUERY_ID`. + /// Returns `None` otherwise. + /// + /// You should never need to override this method. + fn query_id() -> Option { + if Self::HAS_STATIC_QUERY_ID { + Some(TypeId::of::()) + } else { + None + } + } +} + +#[doc(inline)] +pub use diesel_derives::QueryId; + +impl QueryId for () { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = true; +} + +impl QueryId for Box { + type QueryId = T::QueryId; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl QueryId for &T { + type QueryId = T::QueryId; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl QueryId for dyn QueryFragment { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +#[cfg(test)] +#[allow(unused_parens)] // FIXME: Remove this attribute once false positive is resolved. +mod tests { + use std::any::TypeId; + + use super::QueryId; + use crate::prelude::*; + + table! { + users { + id -> Integer, + name -> VarChar, + } + } + + fn query_id(_: T) -> Option { + T::query_id() + } + + #[test] + fn queries_with_no_dynamic_elements_have_a_static_id() { + use self::users::dsl::*; + assert!(query_id(users).is_some()); + assert!(query_id(users.select(name)).is_some()); + assert!(query_id(users.filter(name.eq("Sean"))).is_some()); + } + + #[test] + fn queries_with_different_types_have_different_ids() { + let id1 = query_id(users::table.select(users::name)); + let id2 = query_id(users::table.select(users::id)); + assert_ne!(id1, id2); + } + + #[test] + fn bind_params_use_only_sql_type_for_query_id() { + use self::users::dsl::*; + let id1 = query_id(users.filter(name.eq("Sean"))); + let id2 = query_id(users.filter(name.eq("Tess".to_string()))); + + assert_eq!(id1, id2); + } + + #[test] + #[cfg(feature = "postgres")] + fn boxed_queries_do_not_have_static_query_id() { + use crate::pg::Pg; + assert!(query_id(users::table.into_boxed::()).is_none()); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/returning_clause.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/returning_clause.rs new file mode 100644 index 000000000..fc34e5f50 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/returning_clause.rs @@ -0,0 +1,83 @@ +use super::DeleteStatement; +use super::InsertStatement; +use super::UpdateStatement; +use super::{AstPass, QueryFragment}; +use crate::backend::{Backend, DieselReserveSpecialization}; +use crate::query_builder::QueryId; +use crate::result::QueryResult; +use crate::QuerySource; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoReturningClause; + +impl QueryFragment for NoReturningClause +where + DB: Backend + DieselReserveSpecialization, +{ + fn walk_ast<'b>(&'b self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> { + Ok(()) + } +} + +/// This type represents a SQL `Returning` clause +/// +/// Custom backends can specialize the [`QueryFragment`] +/// implementation via +/// [`SqlDialect::ReturningClause`](crate::backend::SqlDialect::ReturningClause) +#[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes") +)] +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ReturningClause(pub Expr); + +impl QueryFragment for ReturningClause +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl + QueryFragment + for ReturningClause +where + DB: Backend< + ReturningClause = crate::backend::sql_dialect::returning_clause::PgLikeReturningClause, + >, + Expr: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql(" RETURNING "); + self.0.walk_ast(out.reborrow())?; + Ok(()) + } +} + +pub trait ReturningClauseHelper { + type WithReturning; +} + +impl ReturningClauseHelper for InsertStatement +where + T: QuerySource, +{ + type WithReturning = InsertStatement>; +} + +impl ReturningClauseHelper for UpdateStatement +where + T: QuerySource, +{ + type WithReturning = UpdateStatement>; +} + +impl ReturningClauseHelper for DeleteStatement +where + T: QuerySource, +{ + type WithReturning = DeleteStatement>; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/select_clause.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/select_clause.rs new file mode 100644 index 000000000..006355c81 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/select_clause.rs @@ -0,0 +1,123 @@ +use super::from_clause::AsQuerySource; +use crate::expression::{Expression, SelectableExpression}; +use crate::query_builder::*; +use crate::query_source::QuerySource; + +#[doc(hidden)] +pub struct DefaultSelectClause { + default_selection: ::DefaultSelection, +} + +impl std::fmt::Debug for DefaultSelectClause +where + QS: AsQuerySource, + ::DefaultSelection: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DefaultSelectClause") + .field("default_selection", &self.default_selection) + .finish() + } +} + +impl Clone for DefaultSelectClause +where + QS: AsQuerySource, + ::DefaultSelection: Clone, +{ + fn clone(&self) -> Self { + Self { + default_selection: self.default_selection.clone(), + } + } +} + +impl Copy for DefaultSelectClause +where + QS: AsQuerySource, + ::DefaultSelection: Copy, +{ +} + +impl DefaultSelectClause { + pub(crate) fn new(qs: &QS) -> Self { + Self { + default_selection: qs.as_query_source().default_selection(), + } + } +} + +impl QueryId for DefaultSelectClause +where + QS: AsQuerySource, + ::DefaultSelection: QueryId, +{ + type QueryId = <::DefaultSelection as QueryId>::QueryId; + + const HAS_STATIC_QUERY_ID: bool = + <::DefaultSelection as QueryId>::HAS_STATIC_QUERY_ID; +} + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct SelectClause(pub T); + +/// Specialised variant of `Expression` for select clause types +/// +/// The difference to the normal `Expression` trait is the query source (`QS`) +/// generic type parameter. This allows to access the query source in generic code. +#[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes") +)] +pub trait SelectClauseExpression { + /// The expression represented by the given select clause + type Selection; + /// SQL type of the select clause + type SelectClauseSqlType; +} + +impl SelectClauseExpression> for SelectClause +where + QS: QuerySource, + T: SelectableExpression, +{ + type Selection = T; + type SelectClauseSqlType = T::SqlType; +} + +impl SelectClauseExpression for SelectClause +where + T: SelectableExpression, +{ + type Selection = T; + type SelectClauseSqlType = T::SqlType; +} + +impl SelectClauseExpression> for DefaultSelectClause> +where + QS: QuerySource, +{ + type Selection = QS::DefaultSelection; + type SelectClauseSqlType = ::SqlType; +} + +impl QueryFragment for SelectClause +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.0.walk_ast(pass) + } +} + +impl QueryFragment for DefaultSelectClause +where + DB: Backend, + QS: AsQuerySource, + ::DefaultSelection: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.default_selection.walk_ast(pass) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/select_statement/boxed.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/select_statement/boxed.rs new file mode 100644 index 000000000..bf2dea020 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/select_statement/boxed.rs @@ -0,0 +1,612 @@ +use std::marker::PhantomData; + +use crate::backend::{sql_dialect, DieselReserveSpecialization}; +use crate::dsl::AsExprOf; +use crate::expression::subselect::ValidSubselect; +use crate::expression::*; +use crate::insertable::Insertable; +use crate::query_builder::combination_clause::*; +use crate::query_builder::distinct_clause::DistinctClause; +use crate::query_builder::group_by_clause::ValidGroupByClause; +use crate::query_builder::having_clause::HavingClause; +use crate::query_builder::insert_statement::InsertFromSelect; +use crate::query_builder::limit_clause::LimitClause; +use crate::query_builder::limit_offset_clause::BoxedLimitOffsetClause; +use crate::query_builder::offset_clause::OffsetClause; +use crate::query_builder::order_clause::OrderClause; +use crate::query_builder::where_clause::{BoxedWhereClause, WhereAnd, WhereOr}; +use crate::query_builder::*; +use crate::query_dsl::methods::*; +use crate::query_dsl::*; +use crate::query_source::joins::*; +use crate::query_source::{QuerySource, Table}; +use crate::sql_types::{BigInt, BoolOrNullableBool, IntoNullable}; + +// This is used by the table macro internally +/// This type represents a boxed select query +/// +/// Using this type directly is only meaningful for custom backends +/// that need to provide a custom [`QueryFragment`] implementation +#[allow(missing_debug_implementations)] +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + public_fields( + select, + from, + distinct, + where_clause, + order, + limit_offset, + group_by, + having + ) +)] +pub struct BoxedSelectStatement<'a, ST, QS, DB, GB = ()> { + /// The select clause of the query + select: Box + Send + 'a>, + /// The from clause of the query + from: QS, + /// The distinct clause of the query + distinct: Box + Send + 'a>, + /// The where clause of the query + where_clause: BoxedWhereClause<'a, DB>, + /// The order clause of the query + order: Option + Send + 'a>>, + /// The combined limit/offset clause of the query + limit_offset: BoxedLimitOffsetClause<'a, DB>, + /// The group by clause of the query + group_by: Box + Send + 'a>, + /// The having clause of the query + having: Box + Send + 'a>, + _marker: PhantomData<(ST, GB)>, +} + +impl<'a, ST, QS: QuerySource, DB, GB> BoxedSelectStatement<'a, ST, FromClause, DB, GB> { + #[allow(clippy::too_many_arguments)] + pub(crate) fn new( + select: S, + from: FromClause, + distinct: Box + Send + 'a>, + where_clause: BoxedWhereClause<'a, DB>, + order: Option + Send + 'a>>, + limit_offset: BoxedLimitOffsetClause<'a, DB>, + group_by: G, + having: Box + Send + 'a>, + ) -> Self + where + DB: Backend, + G: ValidGroupByClause + QueryFragment + Send + 'a, + S: SelectClauseExpression, SelectClauseSqlType = ST> + + QueryFragment + + Send + + 'a, + S::Selection: ValidGrouping, + { + BoxedSelectStatement { + select: Box::new(select), + from, + distinct, + where_clause, + order, + limit_offset, + group_by: Box::new(group_by), + having, + _marker: PhantomData, + } + } +} + +impl<'a, ST, DB, GB> BoxedSelectStatement<'a, ST, NoFromClause, DB, GB> { + #[allow(clippy::too_many_arguments)] + pub(crate) fn new_no_from_clause( + select: S, + from: NoFromClause, + distinct: Box + Send + 'a>, + where_clause: BoxedWhereClause<'a, DB>, + order: Option + Send + 'a>>, + limit_offset: BoxedLimitOffsetClause<'a, DB>, + group_by: G, + having: Box + Send + 'a>, + ) -> Self + where + DB: Backend, + G: ValidGroupByClause + QueryFragment + Send + 'a, + S: SelectClauseExpression + + QueryFragment + + Send + + 'a, + S::Selection: ValidGrouping, + { + BoxedSelectStatement { + select: Box::new(select), + from, + distinct, + where_clause, + order, + limit_offset, + group_by: Box::new(group_by), + having, + _marker: PhantomData, + } + } +} + +// that's a trait to control who can access these methods +#[doc(hidden)] // exported via internal::derives::multiconnection +pub trait BoxedQueryHelper<'a, QS, DB> { + fn build_query<'b, 'c>( + &'b self, + out: AstPass<'_, 'c, DB>, + where_clause_handler: impl Fn( + &'b BoxedWhereClause<'a, DB>, + AstPass<'_, 'c, DB>, + ) -> QueryResult<()>, + ) -> QueryResult<()> + where + DB: Backend, + QS: QueryFragment, + BoxedLimitOffsetClause<'a, DB>: QueryFragment, + 'b: 'c; +} + +impl<'a, ST, QS, DB, GB> BoxedQueryHelper<'a, QS, DB> for BoxedSelectStatement<'a, ST, QS, DB, GB> { + fn build_query<'b, 'c>( + &'b self, + mut out: AstPass<'_, 'c, DB>, + where_clause_handler: impl Fn( + &'b BoxedWhereClause<'a, DB>, + AstPass<'_, 'c, DB>, + ) -> QueryResult<()>, + ) -> QueryResult<()> + where + DB: Backend, + QS: QueryFragment, + BoxedLimitOffsetClause<'a, DB>: QueryFragment, + 'b: 'c, + { + out.push_sql("SELECT "); + self.distinct.walk_ast(out.reborrow())?; + self.select.walk_ast(out.reborrow())?; + self.from.walk_ast(out.reborrow())?; + where_clause_handler(&self.where_clause, out.reborrow())?; + self.group_by.walk_ast(out.reborrow())?; + self.having.walk_ast(out.reborrow())?; + + if let Some(ref order) = self.order { + out.push_sql(" ORDER BY "); + order.walk_ast(out.reborrow())?; + } + self.limit_offset.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl Query for BoxedSelectStatement<'_, ST, QS, DB, GB> +where + DB: Backend, +{ + type SqlType = ST; +} + +impl SelectQuery for BoxedSelectStatement<'_, ST, QS, DB, GB> +where + DB: Backend, +{ + type SqlType = ST; +} + +impl ValidSubselect for BoxedSelectStatement<'_, ST, QS, DB, GB> where + Self: Query +{ +} + +impl QueryFragment for BoxedSelectStatement<'_, ST, QS, DB, GB> +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl<'a, ST, QS, DB, GB> + QueryFragment + for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend< + SelectStatementSyntax = sql_dialect::select_statement_syntax::AnsiSqlSelectStatement, + > + DieselReserveSpecialization, + QS: QueryFragment, + BoxedLimitOffsetClause<'a, DB>: QueryFragment, +{ + fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.build_query(out, |where_clause, out| where_clause.walk_ast(out)) + } +} + +impl QueryId for BoxedSelectStatement<'_, ST, QS, DB, GB> { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl<'a, ST, QS, DB, Rhs, Kind, On, GB> InternalJoinDsl + for BoxedSelectStatement<'a, ST, FromClause, DB, GB> +where + QS: QuerySource, + Rhs: QuerySource, + JoinOn, On>: QuerySource, + BoxedSelectStatement<'a, ST, FromClause, On>>, DB, GB>: AsQuery, +{ + type Output = BoxedSelectStatement<'a, ST, FromClause, On>>, DB, GB>; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { + BoxedSelectStatement { + select: self.select, + from: FromClause::new(Join::new(self.from.source, rhs, kind).on(on)), + distinct: self.distinct, + where_clause: self.where_clause, + order: self.order, + limit_offset: self.limit_offset, + group_by: self.group_by, + having: self.having, + _marker: PhantomData, + } + } +} + +impl DistinctDsl for BoxedSelectStatement<'_, ST, QS, DB, GB> +where + DB: Backend, + DistinctClause: QueryFragment, +{ + type Output = Self; + + fn distinct(mut self) -> Self::Output { + self.distinct = Box::new(DistinctClause); + self + } +} + +impl<'a, ST, QS, DB, Selection, GB> SelectDsl + for BoxedSelectStatement<'a, ST, FromClause, DB, GB> +where + DB: Backend, + QS: QuerySource, + Selection: SelectableExpression + QueryFragment + ValidGrouping + Send + 'a, +{ + type Output = BoxedSelectStatement<'a, Selection::SqlType, FromClause, DB, GB>; + + fn select(self, selection: Selection) -> Self::Output { + BoxedSelectStatement { + select: Box::new(selection), + from: self.from, + distinct: self.distinct, + where_clause: self.where_clause, + order: self.order, + limit_offset: self.limit_offset, + group_by: self.group_by, + having: self.having, + _marker: PhantomData, + } + } +} + +impl<'a, ST, DB, Selection, GB> SelectDsl + for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB> +where + DB: Backend, + Selection: + SelectableExpression + QueryFragment + ValidGrouping + Send + 'a, +{ + type Output = BoxedSelectStatement<'a, Selection::SqlType, NoFromClause, DB, GB>; + + fn select(self, selection: Selection) -> Self::Output { + BoxedSelectStatement { + select: Box::new(selection), + from: self.from, + distinct: self.distinct, + where_clause: self.where_clause, + order: self.order, + limit_offset: self.limit_offset, + group_by: self.group_by, + having: self.having, + _marker: PhantomData, + } + } +} + +impl<'a, ST, QS, DB, Predicate, GB> FilterDsl + for BoxedSelectStatement<'a, ST, FromClause, DB, GB> +where + QS: QuerySource, + BoxedWhereClause<'a, DB>: WhereAnd>, + Predicate: AppearsOnTable + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = Self; + + fn filter(mut self, predicate: Predicate) -> Self::Output { + self.where_clause = self.where_clause.and(predicate); + self + } +} + +impl<'a, ST, DB, Predicate, GB> FilterDsl + for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB> +where + BoxedWhereClause<'a, DB>: WhereAnd>, + Predicate: AppearsOnTable + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = Self; + + fn filter(mut self, predicate: Predicate) -> Self::Output { + self.where_clause = self.where_clause.and(predicate); + self + } +} + +impl<'a, ST, QS, DB, Predicate, GB> OrFilterDsl + for BoxedSelectStatement<'a, ST, FromClause, DB, GB> +where + QS: QuerySource, + BoxedWhereClause<'a, DB>: WhereOr>, + Predicate: AppearsOnTable + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = Self; + + fn or_filter(mut self, predicate: Predicate) -> Self::Output { + self.where_clause = self.where_clause.or(predicate); + self + } +} + +impl<'a, ST, DB, Predicate, GB> OrFilterDsl + for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB> +where + BoxedWhereClause<'a, DB>: WhereOr>, + Predicate: AppearsOnTable + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = Self; + + fn or_filter(mut self, predicate: Predicate) -> Self::Output { + self.where_clause = self.where_clause.or(predicate); + self + } +} + +impl LimitDsl for BoxedSelectStatement<'_, ST, QS, DB, GB> +where + DB: Backend, + LimitClause>: QueryFragment, +{ + type Output = Self; + + fn limit(mut self, limit: i64) -> Self::Output { + self.limit_offset.limit = Some(Box::new(LimitClause(limit.into_sql::()))); + self + } +} + +impl OffsetDsl for BoxedSelectStatement<'_, ST, QS, DB, GB> +where + DB: Backend, + OffsetClause>: QueryFragment, +{ + type Output = Self; + + fn offset(mut self, offset: i64) -> Self::Output { + self.limit_offset.offset = Some(Box::new(OffsetClause(offset.into_sql::()))); + self + } +} + +// no impls for `NoFromClause` here because order is not really supported there yet +impl<'a, ST, QS, DB, Order, GB> OrderDsl + for BoxedSelectStatement<'a, ST, FromClause, DB, GB> +where + DB: Backend, + QS: QuerySource, + Order: QueryFragment + AppearsOnTable + Send + 'a, +{ + type Output = Self; + + fn order(mut self, order: Order) -> Self::Output { + self.order = OrderClause(order).into(); + self + } +} + +impl<'a, ST, QS, DB, Order, GB> ThenOrderDsl + for BoxedSelectStatement<'a, ST, FromClause, DB, GB> +where + DB: Backend + 'a, + QS: QuerySource, + Order: QueryFragment + AppearsOnTable + Send + 'a, +{ + type Output = Self; + + fn then_order_by(mut self, order: Order) -> Self::Output { + self.order = match self.order { + Some(old) => Some(Box::new((old, order))), + None => Some(Box::new(order)), + }; + self + } +} + +impl JoinTo for BoxedSelectStatement<'_, ST, FromClause, DB, ()> +where + QS: JoinTo + QuerySource, +{ + type FromClause = >::FromClause; + type OnClause = QS::OnClause; + + fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) { + QS::join_target(rhs) + } +} + +impl QueryDsl for BoxedSelectStatement<'_, ST, QS, DB, GB> {} + +impl RunQueryDsl for BoxedSelectStatement<'_, ST, QS, DB, GB> {} + +impl Insertable for BoxedSelectStatement<'_, ST, QS, DB, GB> +where + T: Table, + Self: Query, + >::IsAggregate: + MixedAggregates, +{ + type Values = InsertFromSelect; + + fn values(self) -> Self::Values { + InsertFromSelect::new(self) + } +} + +impl Insertable for &BoxedSelectStatement<'_, ST, QS, DB, GB> +where + T: Table, + Self: Query, + >::IsAggregate: + MixedAggregates, +{ + type Values = InsertFromSelect; + + fn values(self) -> Self::Values { + InsertFromSelect::new(self) + } +} + +impl<'a, ST, QS, DB, GB> SelectNullableDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + ST: IntoNullable, +{ + type Output = BoxedSelectStatement<'a, ST::Nullable, QS, DB>; + + fn nullable(self) -> Self::Output { + BoxedSelectStatement { + select: self.select, + from: self.from, + distinct: self.distinct, + where_clause: self.where_clause, + order: self.order, + limit_offset: self.limit_offset, + group_by: self.group_by, + having: self.having, + _marker: PhantomData, + } + } +} + +impl<'a, ST, QS, DB, GB, Predicate> HavingDsl + for BoxedSelectStatement<'a, ST, FromClause, DB, GB> +where + QS: QuerySource, + DB: Backend, + GB: Expression, + HavingClause: QueryFragment + Send + 'a, + Predicate: AppearsOnTable, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = Self; + + fn having(mut self, predicate: Predicate) -> Self::Output { + self.having = Box::new(HavingClause(predicate)); + self + } +} + +impl CombineDsl for BoxedSelectStatement<'_, ST, QS, DB, GB> +where + Self: Query, +{ + type Query = Self; + + fn union(self, rhs: Rhs) -> crate::dsl::Union + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Union, Distinct, self, rhs.as_query()) + } + + fn union_all(self, rhs: Rhs) -> crate::dsl::UnionAll + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Union, All, self, rhs.as_query()) + } + + fn intersect(self, rhs: Rhs) -> crate::dsl::Intersect + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Intersect, Distinct, self, rhs.as_query()) + } + + fn intersect_all(self, rhs: Rhs) -> crate::dsl::IntersectAll + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Intersect, All, self, rhs.as_query()) + } + + fn except(self, rhs: Rhs) -> crate::dsl::Except + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Except, Distinct, self, rhs.as_query()) + } + + fn except_all(self, rhs: Rhs) -> crate::dsl::ExceptAll + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Except, All, self, rhs.as_query()) + } +} + +#[cfg(test)] +mod tests { + use crate::prelude::*; + + table! { + users { + id -> Integer, + } + } + + fn assert_send(_: T) + where + T: Send, + { + } + + macro_rules! assert_boxed_query_send { + ($backend:ty) => {{ + assert_send(users::table.into_boxed::<$backend>()); + assert_send( + users::table + .filter(users::id.eq(10)) + .into_boxed::<$backend>(), + ); + };}; + } + + #[test] + fn boxed_is_send() { + #[cfg(feature = "postgres")] + assert_boxed_query_send!(crate::pg::Pg); + + #[cfg(feature = "sqlite")] + assert_boxed_query_send!(crate::sqlite::Sqlite); + + #[cfg(feature = "mysql")] + assert_boxed_query_send!(crate::mysql::Mysql); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/select_statement/dsl_impls.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/select_statement/dsl_impls.rs new file mode 100644 index 000000000..ef14567ce --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/select_statement/dsl_impls.rs @@ -0,0 +1,736 @@ +use super::BoxedSelectStatement; +use crate::associations::HasTable; +use crate::backend::Backend; +use crate::dsl::AsExprOf; +use crate::expression::nullable::Nullable; +use crate::expression::*; +use crate::insertable::Insertable; +use crate::query_builder::combination_clause::*; +use crate::query_builder::distinct_clause::*; +use crate::query_builder::from_clause::AsQuerySource; +use crate::query_builder::from_clause::FromClause; +use crate::query_builder::group_by_clause::*; +use crate::query_builder::insert_statement::InsertFromSelect; +use crate::query_builder::limit_clause::*; +use crate::query_builder::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +use crate::query_builder::locking_clause::*; +use crate::query_builder::offset_clause::*; +use crate::query_builder::order_clause::*; +use crate::query_builder::select_clause::*; +use crate::query_builder::update_statement::target::*; +use crate::query_builder::where_clause::*; +use crate::query_builder::NoFromClause; +use crate::query_builder::{ + AsQuery, IntoBoxedClause, Query, QueryFragment, SelectQuery, SelectStatement, +}; +use crate::query_dsl::methods::*; +use crate::query_dsl::order_dsl::ValidOrderingForDistinct; +use crate::query_dsl::*; +use crate::query_source::joins::{Join, JoinOn, JoinTo}; +use crate::query_source::QuerySource; +use crate::sql_types::{BigInt, BoolOrNullableBool}; + +impl InternalJoinDsl + for SelectStatement, DefaultSelectClause>, D, W, O, LOf, G, H, LC> +where + F: QuerySource, + Rhs: QuerySource, + JoinOn, On>: QuerySource, + SelectStatement< + FromClause, On>>, + DefaultSelectClause, On>>>, + D, + W, + O, + LOf, + G, + H, + LC, + >: AsQuery, +{ + type Output = SelectStatement< + FromClause, On>>, + DefaultSelectClause, On>>>, + D, + W, + O, + LOf, + G, + H, + LC, + >; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { + let from = FromClause::new(Join::new(self.from.source, rhs, kind).on(on)); + SelectStatement::new( + DefaultSelectClause::new(&from), + from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + +impl InternalJoinDsl + for SelectStatement, SelectClause, D, W, O, LOf, G, H, LC> +where + F: QuerySource, + Rhs: QuerySource, + JoinOn, On>: QuerySource, + SelectStatement< + FromClause, On>>, + SelectClause, + D, + W, + O, + LOf, + G, + H, + LC, + >: AsQuery, +{ + type Output = SelectStatement< + FromClause, On>>, + SelectClause, + D, + W, + O, + LOf, + G, + H, + LC, + >; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { + SelectStatement::new( + self.select, + FromClause::new(Join::new(self.from.source, rhs, kind).on(on)), + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + +impl SelectDsl + for SelectStatement, S, D, W, O, LOf, G, H, LC> +where + G: ValidGroupByClause, + F: QuerySource, + Selection: SelectableExpression + ValidGrouping, + SelectStatement, SelectClause, D, W, O, LOf, G, H, LC>: SelectQuery, +{ + type Output = SelectStatement, SelectClause, D, W, O, LOf, G, H, LC>; + + fn select(self, selection: Selection) -> Self::Output { + SelectStatement::new( + SelectClause(selection), + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + +impl SelectDsl + for SelectStatement +where + G: ValidGroupByClause, + Selection: SelectableExpression + ValidGrouping, + SelectStatement, D, W, O, LOf, G, H, LC>: SelectQuery, +{ + type Output = SelectStatement, D, W, O, LOf, G, H, LC>; + + fn select(self, selection: Selection) -> Self::Output { + SelectStatement::new( + SelectClause(selection), + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + +impl DistinctDsl for SelectStatement +where + Self: SelectQuery, + SelectStatement: SelectQuery, +{ + type Output = SelectStatement; + + fn distinct(self) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + DistinctClause, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + +impl FilterDsl + for SelectStatement +where + Predicate: Expression + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, + W: WhereAnd, +{ + type Output = SelectStatement; + + fn filter(self, predicate: Predicate) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause.and(predicate), + self.order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + +impl OrFilterDsl + for SelectStatement +where + Predicate: Expression + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, + W: WhereOr, +{ + type Output = SelectStatement; + + fn or_filter(self, predicate: Predicate) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause.or(predicate), + self.order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + +use crate::dsl::Filter; +use crate::expression_methods::EqAll; +use crate::query_builder::having_clause::{HavingClause, NoHavingClause}; +use crate::query_source::Table; + +impl FindDsl + for SelectStatement, S, D, W, O, LOf, G, H, LC> +where + F: Table, + F::PrimaryKey: EqAll, + Self: FilterDsl<>::Output>, +{ + type Output = Filter>::Output>; + + fn find(self, id: PK) -> Self::Output { + let primary_key = self.from.source.primary_key(); + FilterDsl::filter(self, primary_key.eq_all(id)) + } +} + +// no impls for `NoFromClause` here because order is not really supported there yet +impl OrderDsl + for SelectStatement, S, D, W, O, LOf, G, H, LC> +where + F: QuerySource, + Expr: AppearsOnTable, + Self: SelectQuery, + SelectStatement, S, D, W, OrderClause, LOf, G, H, LC>: + SelectQuery, + OrderClause: ValidOrderingForDistinct, +{ + type Output = SelectStatement, S, D, W, OrderClause, LOf, G, H, LC>; + + fn order(self, expr: Expr) -> Self::Output { + let order = OrderClause(expr); + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + +impl ThenOrderDsl + for SelectStatement, S, D, W, OrderClause, LOf, G, H, LC> +where + F: QuerySource, + Expr: AppearsOnTable, +{ + type Output = SelectStatement, S, D, W, OrderClause<(O, Expr)>, LOf, G, H, LC>; + + fn then_order_by(self, expr: Expr) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + OrderClause((self.order.0, expr)), + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + +impl ThenOrderDsl + for SelectStatement +where + Expr: Expression, + Self: OrderDsl, +{ + type Output = crate::dsl::Order; + + fn then_order_by(self, expr: Expr) -> Self::Output { + self.order_by(expr) + } +} + +#[doc(hidden)] +type Limit = AsExprOf; + +impl LimitDsl + for SelectStatement, G, H, LC> +where + Self: SelectQuery, + SelectStatement, Of>, G, H, LC>: + SelectQuery, +{ + type Output = + SelectStatement, Of>, G, H, LC>; + + fn limit(self, limit: i64) -> Self::Output { + let limit_clause = LimitClause(limit.into_sql::()); + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + LimitOffsetClause { + limit_clause, + offset_clause: self.limit_offset.offset_clause, + }, + self.group_by, + self.having, + self.locking, + ) + } +} + +#[doc(hidden)] +type Offset = Limit; + +impl OffsetDsl + for SelectStatement, G, H, LC> +where + Self: SelectQuery, + SelectStatement>, G, H, LC>: + SelectQuery, +{ + type Output = + SelectStatement>, G, H, LC>; + + fn offset(self, offset: i64) -> Self::Output { + let offset_clause = OffsetClause(offset.into_sql::()); + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + LimitOffsetClause { + limit_clause: self.limit_offset.limit_clause, + offset_clause, + }, + self.group_by, + self.having, + self.locking, + ) + } +} + +impl GroupByDsl for SelectStatement +where + SelectStatement, H>: SelectQuery, + Expr: Expression + AppearsOnTable, +{ + type Output = SelectStatement, H>; + + fn group_by(self, expr: Expr) -> Self::Output { + let group_by = GroupByClause(expr); + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + group_by, + self.having, + self.locking, + ) + } +} + +impl LockingDsl + for SelectStatement +{ + type Output = SelectStatement< + F, + S, + NoDistinctClause, + W, + O, + LOf, + NoGroupByClause, + NoHavingClause, + LockingClause, + >; + + fn with_lock(self, lock: Lock) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.having, + LockingClause::new(lock, NoModifier), + ) + } +} + +impl ModifyLockDsl + for SelectStatement> +{ + type Output = SelectStatement>; + + fn modify_lock(self, modifier: Modifier) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.having, + LockingClause::new(self.locking.lock_mode, modifier), + ) + } +} + +impl<'a, F, S, D, W, O, LOf, G, H, DB> BoxedDsl<'a, DB> + for SelectStatement, S, D, W, O, LOf, G, H> +where + Self: AsQuery, + DB: Backend, + F: QuerySource, + S: SelectClauseExpression> + QueryFragment + Send + 'a, + S::Selection: ValidGrouping, + D: QueryFragment + Send + 'a, + W: Into>, + O: Into + Send + 'a>>>, + LOf: IntoBoxedClause<'a, DB, BoxedClause = BoxedLimitOffsetClause<'a, DB>>, + G: ValidGroupByClause + QueryFragment + Send + 'a, + H: QueryFragment + Send + 'a, +{ + type Output = + BoxedSelectStatement<'a, S::SelectClauseSqlType, FromClause, DB, G::Expressions>; + + fn internal_into_boxed(self) -> Self::Output { + BoxedSelectStatement::new( + self.select, + self.from, + Box::new(self.distinct), + self.where_clause.into(), + self.order.into(), + self.limit_offset.into_boxed(), + self.group_by, + Box::new(self.having), + ) + } +} + +impl<'a, S, D, W, O, LOf, G, H, DB> BoxedDsl<'a, DB> + for SelectStatement +where + Self: AsQuery, + DB: Backend, + S: SelectClauseExpression + QueryFragment + Send + 'a, + S::Selection: ValidGrouping, + D: QueryFragment + Send + 'a, + W: Into>, + O: Into + Send + 'a>>>, + LOf: IntoBoxedClause<'a, DB, BoxedClause = BoxedLimitOffsetClause<'a, DB>>, + G: ValidGroupByClause + QueryFragment + Send + 'a, + H: QueryFragment + Send + 'a, +{ + type Output = + BoxedSelectStatement<'a, S::SelectClauseSqlType, NoFromClause, DB, G::Expressions>; + + fn internal_into_boxed(self) -> Self::Output { + BoxedSelectStatement::new_no_from_clause( + self.select, + self.from, + Box::new(self.distinct), + self.where_clause.into(), + self.order.into(), + self.limit_offset.into_boxed(), + self.group_by, + Box::new(self.having), + ) + } +} + +impl HasTable + for SelectStatement, S, D, W, O, LOf, G, H, LC> +where + F: HasTable + QuerySource, +{ + type Table = F::Table; + + fn table() -> Self::Table { + F::table() + } +} + +impl IntoUpdateTarget + for SelectStatement, DefaultSelectClause>, NoDistinctClause, W> +where + F: QuerySource, + Self: HasTable, + W: ValidWhereClause, +{ + type WhereClause = W; + + fn into_update_target(self) -> UpdateTarget { + UpdateTarget { + table: Self::table(), + where_clause: self.where_clause, + } + } +} + +// FIXME: Should we disable joining when `.group_by` has been called? Are there +// any other query methods where a join no longer has the same semantics as +// joining on just the table? +impl JoinTo + for SelectStatement, S, D, W, O, LOf, G, H, LC> +where + F: JoinTo + QuerySource, +{ + type FromClause = >::FromClause; + type OnClause = F::OnClause; + + fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) { + F::join_target(rhs) + } +} + +impl QueryDsl for SelectStatement {} + +impl RunQueryDsl + for SelectStatement +{ +} + +impl Insertable + for SelectStatement +where + Tab: Table, + Self: Query, + >::IsAggregate: + MixedAggregates, +{ + type Values = InsertFromSelect; + + fn values(self) -> Self::Values { + InsertFromSelect::new(self) + } +} + +impl Insertable + for &SelectStatement +where + Tab: Table, + Self: Query, + >::IsAggregate: + MixedAggregates, +{ + type Values = InsertFromSelect; + + fn values(self) -> Self::Values { + InsertFromSelect::new(self) + } +} + +impl SelectNullableDsl + for SelectStatement, D, W, O, LOf, G, H> +{ + type Output = SelectStatement>, D, W, O, LOf, G, H>; + + fn nullable(self) -> Self::Output { + SelectStatement::new( + SelectClause(Nullable::new(self.select.0)), + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + +impl SelectNullableDsl + for SelectStatement, D, W, O, LOf, G, H> +where + F: AsQuerySource, +{ + type Output = SelectStatement< + F, + SelectClause::DefaultSelection>>, + D, + W, + O, + LOf, + G, + H, + >; + + fn nullable(self) -> Self::Output { + SelectStatement::new( + SelectClause(Nullable::new( + self.from.as_query_source().default_selection(), + )), + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + +impl HavingDsl + for SelectStatement, S, D, W, O, LOf, GroupByClause, H> +where + F: QuerySource, + Predicate: AppearsOnTable, + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = + SelectStatement, S, D, W, O, LOf, GroupByClause, HavingClause>; + + fn having(self, predicate: Predicate) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + HavingClause(predicate), + self.locking, + ) + } +} + +impl CombineDsl for SelectStatement +where + Self: Query, +{ + type Query = Self; + + fn union(self, rhs: Rhs) -> crate::dsl::Union + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Union, Distinct, self, rhs.as_query()) + } + + fn union_all(self, rhs: Rhs) -> crate::dsl::UnionAll + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Union, All, self, rhs.as_query()) + } + + fn intersect(self, rhs: Rhs) -> crate::dsl::Intersect + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Intersect, Distinct, self, rhs.as_query()) + } + + fn intersect_all(self, rhs: Rhs) -> crate::dsl::IntersectAll + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Intersect, All, self, rhs.as_query()) + } + + fn except(self, rhs: Rhs) -> crate::dsl::Except + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Except, Distinct, self, rhs.as_query()) + } + + fn except_all(self, rhs: Rhs) -> crate::dsl::ExceptAll + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Except, All, self, rhs.as_query()) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/select_statement/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/select_statement/mod.rs new file mode 100644 index 000000000..2c6441756 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/select_statement/mod.rs @@ -0,0 +1,373 @@ +//! Within this module, types commonly use the following abbreviations: +//! +//! F: From Clause +//! S: Select Clause +//! D: Distinct Clause +//! W: Where Clause +//! O: Order By Clause +//! L: Limit Clause +//! Of: Offset Clause +//! G: Group By Clause +//! H: Having clause +//! LC: For Update Clause + +pub(crate) mod boxed; +mod dsl_impls; +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +pub(crate) use self::boxed::BoxedSelectStatement; + +use super::distinct_clause::NoDistinctClause; +use super::from_clause::AsQuerySource; +use super::from_clause::FromClause; +use super::group_by_clause::*; +use super::limit_clause::NoLimitClause; +use super::locking_clause::NoLockingClause; +use super::offset_clause::NoOffsetClause; +use super::order_clause::NoOrderClause; +use super::select_clause::*; +use super::where_clause::*; +use super::NoFromClause; +use super::{AstPass, Query, QueryFragment}; +use crate::backend::{sql_dialect, Backend}; +use crate::expression::subselect::ValidSubselect; +use crate::expression::*; +use crate::query_builder::having_clause::NoHavingClause; +use crate::query_builder::limit_offset_clause::LimitOffsetClause; +use crate::query_builder::{QueryId, SelectQuery}; +use crate::query_dsl::order_dsl::ValidOrderingForDistinct; +use crate::query_source::joins::{AppendSelection, Inner, Join}; +use crate::query_source::*; +use crate::result::QueryResult; + +/// This type represents a select query +/// +/// Using this type directly is only meaningful for custom backends +/// that need to provide a custom [`QueryFragment`] implementation +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + public_fields( + select, + from, + distinct, + where_clause, + order, + limit_offset, + group_by, + having, + locking + ) +)] +#[derive(Debug, Clone, Copy, QueryId)] +#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] +pub struct SelectStatement< + From, + Select = DefaultSelectClause, + Distinct = NoDistinctClause, + Where = NoWhereClause, + Order = NoOrderClause, + LimitOffset = LimitOffsetClause, + GroupBy = NoGroupByClause, + Having = NoHavingClause, + Locking = NoLockingClause, +> { + /// The select clause of the query + pub(crate) select: Select, + /// The from clause of the query + pub(crate) from: From, + /// The distinct clause of the query + pub(crate) distinct: Distinct, + /// The where clause of the query + pub(crate) where_clause: Where, + /// The order clause of the query + pub(crate) order: Order, + /// The combined limit/offset clause of the query + pub(crate) limit_offset: LimitOffset, + /// The group by clause of the query + pub(crate) group_by: GroupBy, + /// The having clause of the query + pub(crate) having: Having, + /// The locking clause of the query + pub(crate) locking: Locking, +} + +/// Semi-Private trait for containing get-functions for all `SelectStatement` fields +// +// This is used by `#[derive(MultiConnection)]` +pub trait SelectStatementAccessor { + /// The type of the select clause + type Select; + /// The type of the from clause + type From; + /// The type of the distinct clause + type Distinct; + /// The type of the where clause + type Where; + /// The type of the order clause + type Order; + /// The type of the limit offset clause + type LimitOffset; + /// The type of the group by clause + type GroupBy; + /// The type of the having clause + type Having; + /// The type of the locking clause + type Locking; + + /// Access the select clause + fn select_clause(&self) -> &Self::Select; + /// Access the from clause + #[allow(clippy::wrong_self_convention)] // obviously wrong, as `from` refers to the clause name + fn from_clause(&self) -> &Self::From; + /// Access the distinct clause + fn distinct_clause(&self) -> &Self::Distinct; + /// Access the where clause + fn where_clause(&self) -> &Self::Where; + /// Access the order clause + fn order_clause(&self) -> &Self::Order; + /// Access the limit_offset clause + fn limit_offset_clause(&self) -> &Self::LimitOffset; + /// Access the group by clause + fn group_by_clause(&self) -> &Self::GroupBy; + /// Access the having clause + fn having_clause(&self) -> &Self::Having; + /// Access the locking clause + fn locking_clause(&self) -> &Self::Locking; +} + +impl SelectStatementAccessor + for SelectStatement +{ + type Select = S; + type From = F; + type Distinct = D; + type Where = W; + type Order = O; + type LimitOffset = LOf; + type GroupBy = G; + type Having = H; + type Locking = LC; + + fn select_clause(&self) -> &Self::Select { + &self.select + } + + fn from_clause(&self) -> &Self::From { + &self.from + } + + fn distinct_clause(&self) -> &Self::Distinct { + &self.distinct + } + + fn where_clause(&self) -> &Self::Where { + &self.where_clause + } + + fn order_clause(&self) -> &Self::Order { + &self.order + } + + fn limit_offset_clause(&self) -> &Self::LimitOffset { + &self.limit_offset + } + + fn group_by_clause(&self) -> &Self::GroupBy { + &self.group_by + } + + fn having_clause(&self) -> &Self::Having { + &self.having + } + + fn locking_clause(&self) -> &Self::Locking { + &self.locking + } +} + +impl SelectStatement { + #[allow(clippy::too_many_arguments)] + pub(crate) fn new( + select: S, + from: F, + distinct: D, + where_clause: W, + order: O, + limit_offset: LOf, + group_by: G, + having: H, + locking: LC, + ) -> Self { + SelectStatement { + select, + from, + distinct, + where_clause, + order, + limit_offset, + group_by, + having, + locking, + } + } +} + +impl SelectStatement> { + // This is used by the `table!` macro + #[doc(hidden)] + pub fn simple(from: F) -> Self { + let from = FromClause::new(from); + SelectStatement::new( + DefaultSelectClause::new(&from), + from, + NoDistinctClause, + NoWhereClause, + NoOrderClause, + LimitOffsetClause { + limit_clause: NoLimitClause, + offset_clause: NoOffsetClause, + }, + NoGroupByClause, + NoHavingClause, + NoLockingClause, + ) + } +} + +impl Query for SelectStatement +where + G: ValidGroupByClause, + S: SelectClauseExpression, + S::Selection: ValidGrouping, + W: ValidWhereClause, +{ + type SqlType = S::SelectClauseSqlType; +} + +impl SelectQuery for SelectStatement +where + S: SelectClauseExpression, + O: ValidOrderingForDistinct, +{ + type SqlType = S::SelectClauseSqlType; +} + +impl QueryFragment + for SelectStatement +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl + QueryFragment + for SelectStatement +where + DB: Backend< + SelectStatementSyntax = sql_dialect::select_statement_syntax::AnsiSqlSelectStatement, + >, + S: QueryFragment, + F: QueryFragment, + D: QueryFragment, + W: QueryFragment, + O: QueryFragment, + LOf: QueryFragment, + G: QueryFragment, + H: QueryFragment, + LC: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql("SELECT "); + self.distinct.walk_ast(out.reborrow())?; + self.select.walk_ast(out.reborrow())?; + self.from.walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + self.group_by.walk_ast(out.reborrow())?; + self.having.walk_ast(out.reborrow())?; + self.order.walk_ast(out.reborrow())?; + self.limit_offset.walk_ast(out.reborrow())?; + self.locking.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl ValidSubselect + for SelectStatement, S, D, W, O, LOf, G, H, LC> +where + Self: SelectQuery, + F: QuerySource, + QS: QuerySource, + Join: QuerySource, + W: ValidWhereClause>>, +{ +} + +impl ValidSubselect + for SelectStatement +where + Self: SelectQuery, + W: ValidWhereClause, +{ +} + +impl ValidSubselect + for SelectStatement, S, D, W, O, LOf, G, H, LC> +where + Self: SelectQuery, + F: QuerySource, + W: ValidWhereClause>, +{ +} + +impl ValidSubselect + for SelectStatement +where + Self: SelectQuery, + QS: QuerySource, + W: ValidWhereClause, +{ +} + +/// Allow `SelectStatement` to act as if it were `From` as long as +/// no other query methods have been called on it +impl AppearsInFromClause for SelectStatement +where + From: AsQuerySource, + From::QuerySource: AppearsInFromClause + QuerySource, +{ + type Count = >::Count; +} + +impl QuerySource for SelectStatement +where + From: AsQuerySource, + ::DefaultSelection: SelectableExpression, +{ + type FromClause = ::FromClause; + type DefaultSelection = ::DefaultSelection; + + fn from_clause(&self) -> ::FromClause { + self.from.as_query_source().from_clause() + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.from.as_query_source().default_selection() + } +} + +impl AppendSelection for SelectStatement +where + From: AsQuerySource, + From::QuerySource: AppendSelection, +{ + type Output = >::Output; + + fn append_selection(&self, selection: Selection) -> Self::Output { + self.from.as_query_source().append_selection(selection) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/sql_query.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/sql_query.rs new file mode 100644 index 000000000..cceb9b143 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/sql_query.rs @@ -0,0 +1,376 @@ +use std::marker::PhantomData; + +use super::Query; +use crate::backend::{Backend, DieselReserveSpecialization}; +use crate::connection::Connection; +use crate::query_builder::{AstPass, QueryFragment, QueryId}; +use crate::query_dsl::RunQueryDsl; +use crate::result::QueryResult; +use crate::serialize::ToSql; +use crate::sql_types::{HasSqlType, Untyped}; + +#[derive(Debug, Clone)] +#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] +/// The return value of `sql_query`. +/// +/// Unlike most queries in Diesel, `SqlQuery` loads its data by column name, +/// rather than by index. This means that you cannot deserialize this query into +/// a tuple, and any structs used must implement `QueryableByName`. +/// +/// See [`sql_query`](crate::sql_query()) for examples. +pub struct SqlQuery { + inner: Inner, + query: String, +} + +impl SqlQuery { + pub(crate) fn new(inner: Inner, query: String) -> Self { + SqlQuery { inner, query } + } + + /// Bind a value for use with this SQL query. The given query should have + /// placeholders that vary based on the database type, + /// like [SQLite Parameter](https://sqlite.org/lang_expr.html#varparam) syntax, + /// [PostgreSQL PREPARE syntax](https://www.postgresql.org/docs/current/sql-prepare.html), + /// or [MySQL bind syntax](https://dev.mysql.com/doc/refman/8.0/en/mysql-stmt-bind-param.html). + /// + /// # Safety + /// + /// This function should be used with care, as Diesel cannot validate that + /// the value is of the right type nor can it validate that you have passed + /// the correct number of parameters. + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # + /// # use schema::users; + /// # + /// # #[derive(QueryableByName, Debug, PartialEq)] + /// # struct User { + /// # id: i32, + /// # name: String, + /// # } + /// # + /// # fn main() { + /// # use diesel::sql_query; + /// # use diesel::sql_types::{Integer, Text}; + /// # + /// # let connection = &mut establish_connection(); + /// # diesel::insert_into(users::table) + /// # .values(users::name.eq("Jim")) + /// # .execute(connection).unwrap(); + /// # #[cfg(feature = "postgres")] + /// # let users = sql_query("SELECT * FROM users WHERE id > $1 AND name != $2"); + /// # #[cfg(not(feature = "postgres"))] + /// let users = sql_query("SELECT * FROM users WHERE id > ? AND name <> ?") + /// # ; + /// # let users = users + /// .bind::(1) + /// .bind::("Tess") + /// .get_results(connection); + /// let expected_users = vec![ + /// User { id: 3, name: "Jim".into() }, + /// ]; + /// assert_eq!(Ok(expected_users), users); + /// # } + /// ``` + pub fn bind(self, value: Value) -> UncheckedBind { + UncheckedBind::new(self, value) + } + + /// Internally boxes future calls on `bind` and `sql` so that they don't + /// change the type. + /// + /// This allows doing things you otherwise couldn't do, e.g. `bind`ing in a + /// loop. + pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> { + BoxedSqlQuery::new(self) + } + + /// Appends a piece of SQL code at the end. + pub fn sql>(mut self, sql: T) -> Self { + self.query += sql.as_ref(); + self + } +} + +impl SqlQuery { + pub(crate) fn from_sql(query: String) -> SqlQuery { + Self { + inner: self::private::Empty, + query, + } + } +} + +impl QueryFragment for SqlQuery +where + DB: Backend + DieselReserveSpecialization, + Inner: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + self.inner.walk_ast(out.reborrow())?; + out.push_sql(&self.query); + Ok(()) + } +} + +impl QueryId for SqlQuery { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl Query for SqlQuery { + type SqlType = Untyped; +} + +impl RunQueryDsl for SqlQuery {} + +#[derive(Debug, Clone, Copy)] +#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] +/// Returned by the [`SqlQuery::bind()`] method when binding a value to a fragment of SQL. +/// +pub struct UncheckedBind { + query: Query, + value: Value, + _marker: PhantomData, +} + +impl UncheckedBind { + pub fn new(query: Query, value: Value) -> Self { + UncheckedBind { + query, + value, + _marker: PhantomData, + } + } + + pub fn bind(self, value: Value2) -> UncheckedBind { + UncheckedBind::new(self, value) + } + + pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> { + BoxedSqlQuery::new(self) + } + + /// Construct a full SQL query using raw SQL. + /// + /// This function exists for cases where a query needs to be written that is not + /// supported by the query builder. Unlike most queries in Diesel, `sql_query` + /// will deserialize its data by name, not by index. That means that you cannot + /// deserialize into a tuple, and structs which you deserialize from this + /// function will need to have `#[derive(QueryableByName)]`. + /// + /// This function is intended for use when you want to write the entire query + /// using raw SQL. If you only need a small bit of raw SQL in your query, use + /// [`sql`](dsl::sql()) instead. + /// + /// Query parameters can be bound into the raw query using [`SqlQuery::bind()`]. + /// + /// # Safety + /// + /// The implementation of `QueryableByName` will assume that columns with a + /// given name will have a certain type. The compiler will be unable to verify + /// that the given type is correct. If your query returns a column of an + /// unexpected type, the result may have the wrong value, or return an error. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # use schema::users; + /// # + /// # #[derive(QueryableByName, Debug, PartialEq)] + /// # struct User { + /// # id: i32, + /// # name: String, + /// # } + /// # + /// # fn main() { + /// # use diesel::sql_query; + /// # use diesel::sql_types::{Integer, Text}; + /// # + /// # let connection = &mut establish_connection(); + /// # diesel::insert_into(users::table) + /// # .values(users::name.eq("Jim")) + /// # .execute(connection).unwrap(); + /// # #[cfg(feature = "postgres")] + /// # let users = sql_query("SELECT * FROM users WHERE id > $1 AND name != $2"); + /// # #[cfg(not(feature = "postgres"))] + /// let users = sql_query("SELECT * FROM users WHERE id > ? AND name <> ?") + /// # ; + /// # let users = users + /// .bind::(1) + /// .bind::("Tess") + /// .get_results(connection); + /// let expected_users = vec![ + /// User { id: 3, name: "Jim".into() }, + /// ]; + /// assert_eq!(Ok(expected_users), users); + /// # } + /// ``` + /// [`SqlQuery::bind()`]: query_builder::SqlQuery::bind() + pub fn sql>(self, sql: T) -> SqlQuery { + SqlQuery::new(self, sql.into()) + } +} + +impl QueryId for UncheckedBind +where + Query: QueryId, + ST: QueryId, +{ + type QueryId = UncheckedBind; + + const HAS_STATIC_QUERY_ID: bool = Query::HAS_STATIC_QUERY_ID && ST::HAS_STATIC_QUERY_ID; +} + +impl QueryFragment for UncheckedBind +where + DB: Backend + HasSqlType + DieselReserveSpecialization, + Query: QueryFragment, + Value: ToSql, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.query.walk_ast(out.reborrow())?; + out.push_bind_param_value_only(&self.value)?; + Ok(()) + } +} + +impl Query for UncheckedBind { + type SqlType = Untyped; +} + +impl RunQueryDsl for UncheckedBind {} + +#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."] +/// See [`SqlQuery::into_boxed`]. +/// +/// [`SqlQuery::into_boxed`]: SqlQuery::into_boxed() +#[allow(missing_debug_implementations)] +pub struct BoxedSqlQuery<'f, DB: Backend, Query> { + query: Query, + sql: String, + binds: Vec + Send + 'f>>, +} + +struct RawBind { + value: U, + p: PhantomData, +} + +impl QueryFragment for RawBind +where + DB: Backend + HasSqlType, + U: ToSql, +{ + fn walk_ast<'b>(&'b self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + pass.push_bind_param_value_only(&self.value) + } +} + +impl<'f, DB: Backend, Query> BoxedSqlQuery<'f, DB, Query> { + pub(crate) fn new(query: Query) -> Self { + BoxedSqlQuery { + query, + sql: "".to_string(), + binds: vec![], + } + } + + /// See [`SqlQuery::bind`]. + /// + /// [`SqlQuery::bind`]: SqlQuery::bind() + pub fn bind(mut self, b: Value) -> Self + where + DB: HasSqlType, + Value: ToSql + Send + 'f, + BindSt: Send + 'f, + { + self.binds.push(Box::new(RawBind { + value: b, + p: PhantomData, + }) as Box<_>); + self + } + + /// See [`SqlQuery::sql`]. + /// + /// [`SqlQuery::sql`]: SqlQuery::sql() + pub fn sql>(mut self, sql: T) -> Self { + self.sql += sql.as_ref(); + self + } +} + +impl QueryFragment for BoxedSqlQuery<'_, DB, Query> +where + DB: Backend + DieselReserveSpecialization, + Query: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + self.query.walk_ast(out.reborrow())?; + out.push_sql(&self.sql); + + for b in &self.binds { + b.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +impl QueryId for BoxedSqlQuery<'_, DB, Query> { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl Query for BoxedSqlQuery<'_, DB, Q> +where + DB: Backend, +{ + type SqlType = Untyped; +} + +impl RunQueryDsl for BoxedSqlQuery<'_, Conn::Backend, Query> {} + +mod private { + use crate::backend::{Backend, DieselReserveSpecialization}; + use crate::query_builder::{QueryFragment, QueryId}; + + #[derive(Debug, Clone, Copy, QueryId)] + pub struct Empty; + + impl QueryFragment for Empty + where + DB: Backend + DieselReserveSpecialization, + { + fn walk_ast<'b>( + &'b self, + _pass: crate::query_builder::AstPass<'_, 'b, DB>, + ) -> crate::QueryResult<()> { + Ok(()) + } + } +} + +#[cfg(test)] +mod tests { + fn assert_send(_: S) {} + + #[test] + fn check_boxed_sql_query_is_send() { + let query = crate::sql_query("SELECT 1") + .into_boxed::<::Backend>( + ); + + assert_send(query); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/update_statement/changeset.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/update_statement/changeset.rs new file mode 100644 index 000000000..b3349b372 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/update_statement/changeset.rs @@ -0,0 +1,130 @@ +use crate::backend::DieselReserveSpecialization; +use crate::expression::grouped::Grouped; +use crate::expression::operators::Eq; +use crate::expression::AppearsOnTable; +use crate::query_builder::*; +use crate::query_source::{Column, QuerySource}; +use crate::Table; + +/// Types which can be passed to +/// [`update.set`](UpdateStatement::set()). +/// +/// This trait can be [derived](derive@AsChangeset) +pub trait AsChangeset { + /// The table which `Self::Changeset` will be updating + type Target: QuerySource; + + /// The update statement this type represents + type Changeset; + + /// Convert `self` into the actual update statement being executed + // This method is part of our public API + // we won't change it to just appease clippy + #[allow(clippy::wrong_self_convention)] + fn as_changeset(self) -> Self::Changeset; +} + +// This is a false positive, we reexport it later +#[allow(unreachable_pub)] +#[doc(inline)] +pub use diesel_derives::AsChangeset; + +impl AsChangeset for Option { + type Target = T::Target; + type Changeset = Option; + + fn as_changeset(self) -> Self::Changeset { + self.map(AsChangeset::as_changeset) + } +} + +impl AsChangeset for Eq +where + Left: AssignmentTarget, + Right: AppearsOnTable, +{ + type Target = Left::Table; + type Changeset = Assign<::QueryAstNode, Right>; + + fn as_changeset(self) -> Self::Changeset { + Assign { + target: self.left.into_target(), + expr: self.right, + } + } +} + +impl AsChangeset for Grouped> +where + Eq: AsChangeset, +{ + type Target = as AsChangeset>::Target; + + type Changeset = as AsChangeset>::Changeset; + + fn as_changeset(self) -> Self::Changeset { + self.0.as_changeset() + } +} + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct Assign { + target: Target, + expr: Expr, +} + +impl QueryFragment for Assign +where + DB: Backend, + T: QueryFragment, + U: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + QueryFragment::walk_ast(&self.target, out.reborrow())?; + out.push_sql(" = "); + QueryFragment::walk_ast(&self.expr, out.reborrow()) + } +} + +/// Represents the left hand side of an assignment expression for an +/// assignment in [AsChangeset]. The vast majority of the time, this will +/// be a [Column]. However, in certain database backends, it's possible to +/// assign to an expression. For example, in Postgres, it's possible to +/// "UPDATE TABLE SET array_column\[1\] = 'foo'". +pub trait AssignmentTarget { + /// Table the assignment is to + type Table: Table; + /// A wrapper around a type to assign to (this wrapper should implement + /// [QueryFragment]). + type QueryAstNode; + + /// Move this in to the AST node which should implement [QueryFragment]. + fn into_target(self) -> Self::QueryAstNode; +} + +/// Represents a `Column` as an `AssignmentTarget`. The vast majority of +/// targets in an update statement will be `Column`s. +#[derive(Debug, Clone, Copy)] +pub struct ColumnWrapperForUpdate(pub C); + +impl QueryFragment for ColumnWrapperForUpdate +where + DB: Backend + DieselReserveSpecialization, + C: Column, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_identifier(C::NAME) + } +} + +impl AssignmentTarget for C +where + C: Column, +{ + type Table = C::Table; + type QueryAstNode = ColumnWrapperForUpdate; + + fn into_target(self) -> Self::QueryAstNode { + ColumnWrapperForUpdate(self) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/update_statement/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/update_statement/mod.rs new file mode 100644 index 000000000..6c7ab3db5 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/update_statement/mod.rs @@ -0,0 +1,307 @@ +pub(crate) mod changeset; +pub(super) mod target; + +use crate::backend::DieselReserveSpecialization; +use crate::dsl::{Filter, IntoBoxed}; +use crate::expression::{ + is_aggregate, AppearsOnTable, Expression, MixedAggregates, SelectableExpression, ValidGrouping, +}; +use crate::query_builder::returning_clause::*; +use crate::query_builder::where_clause::*; +use crate::query_dsl::methods::{BoxedDsl, FilterDsl}; +use crate::query_dsl::RunQueryDsl; +use crate::query_source::Table; +use crate::result::EmptyChangeset; +use crate::result::Error::QueryBuilderError; +use crate::{query_builder::*, QuerySource}; + +pub(crate) use self::private::UpdateAutoTypeHelper; + +impl UpdateStatement { + pub(crate) fn new(target: UpdateTarget) -> Self { + UpdateStatement { + from_clause: target.table.from_clause(), + where_clause: target.where_clause, + values: SetNotCalled, + returning: NoReturningClause, + } + } + + /// Provides the `SET` clause of the `UPDATE` statement. + /// + /// See [`update`](crate::update()) for usage examples, or [the update + /// guide](https://diesel.rs/guides/all-about-updates/) for a more exhaustive + /// set of examples. + pub fn set(self, values: V) -> UpdateStatement + where + T: Table, + V: changeset::AsChangeset, + UpdateStatement: AsQuery, + { + UpdateStatement { + from_clause: self.from_clause, + where_clause: self.where_clause, + values: values.as_changeset(), + returning: self.returning, + } + } +} + +#[derive(Clone, Debug)] +#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] +/// Represents a complete `UPDATE` statement. +/// +/// See [`update`](crate::update()) for usage examples, or [the update +/// guide](https://diesel.rs/guides/all-about-updates/) for a more exhaustive +/// set of examples. +pub struct UpdateStatement { + from_clause: T::FromClause, + where_clause: U, + values: V, + returning: Ret, +} + +/// An `UPDATE` statement with a boxed `WHERE` clause. +pub type BoxedUpdateStatement<'a, DB, T, V = SetNotCalled, Ret = NoReturningClause> = + UpdateStatement, V, Ret>; + +impl UpdateStatement { + /// Adds the given predicate to the `WHERE` clause of the statement being + /// constructed. + /// + /// If there is already a `WHERE` clause, the predicate will be appended + /// with `AND`. There is no difference in behavior between + /// `update(table.filter(x))` and `update(table).filter(x)`. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let updated_rows = diesel::update(users) + /// .set(name.eq("Jim")) + /// .filter(name.eq("Sean")) + /// .execute(connection); + /// assert_eq!(Ok(1), updated_rows); + /// + /// let expected_names = vec!["Jim".to_string(), "Tess".to_string()]; + /// let names = users.select(name).order(id).load(connection); + /// + /// assert_eq!(Ok(expected_names), names); + /// # } + /// ``` + pub fn filter(self, predicate: Predicate) -> Filter + where + Self: FilterDsl, + { + FilterDsl::filter(self, predicate) + } + + /// Boxes the `WHERE` clause of this update statement. + /// + /// This is useful for cases where you want to conditionally modify a query, + /// but need the type to remain the same. The backend must be specified as + /// part of this. It is not possible to box a query and have it be useable + /// on multiple backends. + /// + /// A boxed query will incur a minor performance penalty, as the query builder + /// can no longer be inlined by the compiler. For most applications this cost + /// will be minimal. + /// + /// ### Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use std::collections::HashMap; + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// # let mut params = HashMap::new(); + /// # params.insert("tess_has_been_a_jerk", false); + /// let mut query = diesel::update(users) + /// .set(name.eq("Jerk")) + /// .into_boxed(); + /// + /// if !params["tess_has_been_a_jerk"] { + /// query = query.filter(name.ne("Tess")); + /// } + /// + /// let updated_rows = query.execute(connection)?; + /// assert_eq!(1, updated_rows); + /// + /// let expected_names = vec!["Jerk", "Tess"]; + /// let names = users.select(name).order(id).load::(connection)?; + /// + /// assert_eq!(expected_names, names); + /// # Ok(()) + /// # } + /// ``` + pub fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB> + where + DB: Backend, + Self: BoxedDsl<'a, DB>, + { + BoxedDsl::internal_into_boxed(self) + } +} + +impl FilterDsl for UpdateStatement +where + T: QuerySource, + U: WhereAnd, + Predicate: AppearsOnTable, +{ + type Output = UpdateStatement; + + fn filter(self, predicate: Predicate) -> Self::Output { + UpdateStatement { + from_clause: self.from_clause, + where_clause: self.where_clause.and(predicate), + values: self.values, + returning: self.returning, + } + } +} + +impl<'a, T, U, V, Ret, DB> BoxedDsl<'a, DB> for UpdateStatement +where + T: QuerySource, + U: Into>, +{ + type Output = BoxedUpdateStatement<'a, DB, T, V, Ret>; + + fn internal_into_boxed(self) -> Self::Output { + UpdateStatement { + from_clause: self.from_clause, + where_clause: self.where_clause.into(), + values: self.values, + returning: self.returning, + } + } +} + +impl QueryFragment for UpdateStatement +where + DB: Backend + DieselReserveSpecialization, + T: Table, + T::FromClause: QueryFragment, + U: QueryFragment, + V: QueryFragment, + Ret: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + if self.values.is_noop(out.backend())? { + return Err(QueryBuilderError(Box::new(EmptyChangeset))); + } + + out.unsafe_to_cache_prepared(); + out.push_sql("UPDATE "); + self.from_clause.walk_ast(out.reborrow())?; + out.push_sql(" SET "); + self.values.walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + self.returning.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl QueryId for UpdateStatement +where + T: QuerySource, +{ + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl AsQuery for UpdateStatement +where + T: Table, + UpdateStatement>: Query, + T::AllColumns: ValidGrouping<()>, + >::IsAggregate: + MixedAggregates, +{ + type SqlType = ::SqlType; + type Query = UpdateStatement>; + + fn as_query(self) -> Self::Query { + self.returning(T::all_columns()) + } +} + +impl Query for UpdateStatement> +where + T: Table, + Ret: Expression + SelectableExpression + ValidGrouping<()>, + Ret::IsAggregate: MixedAggregates, +{ + type SqlType = Ret::SqlType; +} + +impl RunQueryDsl for UpdateStatement {} + +impl UpdateStatement { + /// Specify what expression is returned after execution of the `update`. + /// # Examples + /// + /// ### Updating a single record: + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # #[cfg(feature = "postgres")] + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let updated_name = diesel::update(users.filter(id.eq(1))) + /// .set(name.eq("Dean")) + /// .returning(name) + /// .get_result(connection); + /// assert_eq!(Ok("Dean".to_string()), updated_name); + /// # } + /// # #[cfg(not(feature = "postgres"))] + /// # fn main() {} + /// ``` + pub fn returning(self, returns: E) -> UpdateStatement> + where + T: Table, + UpdateStatement>: Query, + { + UpdateStatement { + from_clause: self.from_clause, + where_clause: self.where_clause, + values: self.values, + returning: ReturningClause(returns), + } + } +} + +/// Indicates that you have not yet called `.set` on an update statement +#[derive(Debug, Clone, Copy)] +pub struct SetNotCalled; + +mod private { + // otherwise rustc complains at a different location that this trait is more private than the other item that uses it + #[allow(unreachable_pub)] + pub trait UpdateAutoTypeHelper { + type Table; + type Where; + } + + impl UpdateAutoTypeHelper for crate::query_builder::UpdateStatement + where + T: crate::QuerySource, + { + type Table = T; + type Where = W; + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/update_statement/target.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/update_statement/target.rs new file mode 100644 index 000000000..46441051c --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/update_statement/target.rs @@ -0,0 +1,47 @@ +use crate::associations::{HasTable, Identifiable}; +use crate::dsl::Find; +use crate::query_dsl::methods::FindDsl; +use crate::query_source::Table; + +#[doc(hidden)] +#[derive(Debug)] +pub struct UpdateTarget { + pub table: Table, + pub where_clause: WhereClause, +} + +/// A type which can be passed to [`update`] or [`delete`]. +/// +/// Apps will never need to implement this type directly. There are three kinds +/// which implement this trait. Tables, queries which have only had `filter` +/// called on them, and types which implement `Identifiable`. +/// +/// When a table is passed to `update`, every row in the table will be updated. +/// You can scope this down by calling [`filter`] which will +/// result in `UPDATE your_table SET ... WHERE args_to_filter`. Passing a type +/// which implements `Identifiable` is the same as passing +/// `SomeStruct::table().find(some_struct)`. +/// +/// [`update`]: crate::update() +/// [`delete`]: crate::delete() +/// [`filter`]: crate::query_builder::UpdateStatement::filter() +pub trait IntoUpdateTarget: HasTable { + /// What is the `WHERE` clause of this target? + type WhereClause; + + /// Decomposes `self` into the table and where clause. + fn into_update_target(self) -> UpdateTarget; +} + +impl IntoUpdateTarget for T +where + T: Identifiable
, + Tab: Table + FindDsl, + Find: IntoUpdateTarget
, +{ + type WhereClause = V; + + fn into_update_target(self) -> UpdateTarget { + T::table().find(self.id()).into_update_target() + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/into_conflict_clause.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/into_conflict_clause.rs new file mode 100644 index 000000000..bbf3215f5 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/into_conflict_clause.rs @@ -0,0 +1,70 @@ +use crate::query_builder::insert_statement::{BatchInsert, InsertFromSelect}; +use crate::query_builder::{BoxedSelectStatement, Query, SelectStatement, ValuesClause}; + +pub trait IntoConflictValueClause { + type ValueClause; + + fn into_value_clause(self) -> Self::ValueClause; +} + +#[derive(Debug, Clone, Copy)] +pub struct OnConflictSelectWrapper(pub(crate) S); + +impl Query for OnConflictSelectWrapper +where + Q: Query, +{ + type SqlType = Q::SqlType; +} + +impl IntoConflictValueClause for ValuesClause { + type ValueClause = Self; + + fn into_value_clause(self) -> Self::ValueClause { + self + } +} + +impl IntoConflictValueClause + for BatchInsert +{ + type ValueClause = Self; + + fn into_value_clause(self) -> Self::ValueClause { + self + } +} + +impl IntoConflictValueClause + for InsertFromSelect, Columns> +{ + type ValueClause = InsertFromSelect< + OnConflictSelectWrapper>, + Columns, + >; + + fn into_value_clause(self) -> Self::ValueClause { + let InsertFromSelect { columns, query } = self; + InsertFromSelect { + query: OnConflictSelectWrapper(query), + columns, + } + } +} + +impl<'a, ST, QS, DB, GB, Columns> IntoConflictValueClause + for InsertFromSelect, Columns> +{ + type ValueClause = InsertFromSelect< + OnConflictSelectWrapper>, + Columns, + >; + + fn into_value_clause(self) -> Self::ValueClause { + let InsertFromSelect { columns, query } = self; + InsertFromSelect { + query: OnConflictSelectWrapper(query), + columns, + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/mod.rs new file mode 100644 index 000000000..25122db3b --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod into_conflict_clause; +pub(crate) mod on_conflict_actions; +pub(crate) mod on_conflict_clause; +pub(crate) mod on_conflict_target; +pub(crate) mod on_conflict_target_decorations; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/on_conflict_actions.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/on_conflict_actions.rs new file mode 100644 index 000000000..4f3e144ac --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/on_conflict_actions.rs @@ -0,0 +1,128 @@ +use std::marker::PhantomData; + +use crate::backend::sql_dialect::on_conflict_clause; +use crate::expression::{AppearsOnTable, Expression}; +use crate::query_builder::*; +use crate::query_source::*; + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, QueryId)] +pub struct DoNothing(PhantomData); + +impl DoNothing { + pub(crate) fn new() -> Self { + Self(PhantomData) + } +} + +impl QueryFragment for DoNothing +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment for DoNothing +where + DB: Backend, + SD: on_conflict_clause::PgLikeOnConflictClause, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql(" DO NOTHING"); + Ok(()) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, QueryId)] +pub struct DoUpdate { + pub(crate) changeset: T, + tab: PhantomData, +} + +impl DoUpdate { + pub(crate) fn new(changeset: T) -> Self { + DoUpdate { + changeset, + tab: PhantomData, + } + } +} + +impl QueryFragment for DoUpdate +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment for DoUpdate +where + DB: Backend, + T: QueryFragment, + SD: on_conflict_clause::PgLikeOnConflictClause, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + if self.changeset.is_noop(out.backend())? { + out.push_sql(" DO NOTHING"); + } else { + out.push_sql(" DO UPDATE SET "); + self.changeset.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, QueryId)] +pub struct Excluded(T); + +impl Excluded { + pub(crate) fn new(t: T) -> Self { + Excluded(t) + } +} + +impl QueryFragment for Excluded +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment for Excluded +where + DB: Backend, + T: Column, + SD: on_conflict_clause::PgLikeOnConflictClause, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql("excluded."); + out.push_identifier(T::NAME)?; + Ok(()) + } +} + +impl Expression for Excluded +where + T: Expression, +{ + type SqlType = T::SqlType; +} + +impl AppearsOnTable for Excluded +where + T: Column, + Excluded: Expression, +{ +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/on_conflict_clause.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/on_conflict_clause.rs new file mode 100644 index 000000000..81d1f787d --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/on_conflict_clause.rs @@ -0,0 +1,119 @@ +use super::on_conflict_actions::*; +use super::on_conflict_target::*; +use crate::backend::sql_dialect; +use crate::insertable::*; +use crate::query_builder::where_clause::{NoWhereClause, WhereClause}; +use crate::query_builder::*; + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct OnConflictValues { + pub(crate) values: Values, + pub(crate) target: Target, + pub(crate) action: Action, + /// Allow to apply filters on ON CONFLICT ... DO UPDATE ... WHERE ... + pub(crate) where_clause: WhereClause, +} + +impl QueryId + for OnConflictValues +{ + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl OnConflictValues, NoWhereClause> { + pub(crate) fn do_nothing(values: Values) -> Self { + Self::new(values, NoConflictTarget, DoNothing::new(), NoWhereClause) + } +} + +impl OnConflictValues { + pub(crate) fn new( + values: Values, + target: Target, + action: Action, + where_clause: WhereClause, + ) -> Self { + OnConflictValues { + values, + target, + action, + where_clause, + } + } + + pub(crate) fn replace_where( + self, + f: F, + ) -> OnConflictValues + where + F: FnOnce(WhereClause) -> Where, + { + OnConflictValues::new(self.values, self.target, self.action, f(self.where_clause)) + } +} + +impl CanInsertInSingleQuery + for OnConflictValues +where + DB: Backend, + DB::OnConflictClause: sql_dialect::on_conflict_clause::SupportsOnConflictClause, + Values: CanInsertInSingleQuery, +{ + fn rows_to_insert(&self) -> Option { + self.values.rows_to_insert() + } +} + +impl QueryFragment + for OnConflictValues +where + DB: Backend, + DB::OnConflictClause: sql_dialect::on_conflict_clause::SupportsOnConflictClause, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment + for OnConflictValues +where + DB: Backend, + SD: sql_dialect::on_conflict_clause::PgLikeOnConflictClause, + Values: QueryFragment, + Target: QueryFragment, + Action: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.values.walk_ast(out.reborrow())?; + out.push_sql(" ON CONFLICT"); + self.target.walk_ast(out.reborrow())?; + self.action.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl QueryFragment + for OnConflictValues> +where + DB: Backend, + DB::OnConflictClause: sql_dialect::on_conflict_clause::SupportsOnConflictClause, + DB::OnConflictClause: sql_dialect::on_conflict_clause::SupportsOnConflictClauseWhere, + Values: QueryFragment, + Target: QueryFragment, + Action: QueryFragment, + WhereClause: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.values.walk_ast(out.reborrow())?; + out.push_sql(" ON CONFLICT"); + self.target.walk_ast(out.reborrow())?; + self.action.walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/on_conflict_target.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/on_conflict_target.rs new file mode 100644 index 000000000..125cda7bd --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/on_conflict_target.rs @@ -0,0 +1,121 @@ +use crate::backend::sql_dialect; +use crate::expression::SqlLiteral; +use crate::query_builder::*; +use crate::query_source::Column; + +#[doc(hidden)] +pub trait OnConflictTarget
{} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoConflictTarget; + +impl QueryFragment for NoConflictTarget +where + DB: Backend, + DB::OnConflictClause: sql_dialect::on_conflict_clause::SupportsOnConflictClause, +{ + fn walk_ast<'b>(&'b self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> { + Ok(()) + } +} + +impl
OnConflictTarget
for NoConflictTarget {} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ConflictTarget(pub T); + +impl QueryFragment for ConflictTarget +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment for ConflictTarget +where + DB: Backend, + SP: sql_dialect::on_conflict_clause::PgLikeOnConflictClause, + T: Column, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql(" ("); + out.push_identifier(T::NAME)?; + out.push_sql(")"); + Ok(()) + } +} + +impl OnConflictTarget for ConflictTarget where T: Column {} + +impl QueryFragment for ConflictTarget> +where + DB: Backend, + SP: sql_dialect::on_conflict_clause::PgLikeOnConflictClause, + SqlLiteral: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql(" "); + self.0.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl OnConflictTarget for ConflictTarget> {} + +impl QueryFragment for ConflictTarget<(T,)> +where + DB: Backend, + SP: sql_dialect::on_conflict_clause::PgLikeOnConflictClause, + T: Column, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql(" ("); + out.push_identifier(T::NAME)?; + out.push_sql(")"); + Ok(()) + } +} + +impl OnConflictTarget for ConflictTarget<(T,)> where T: Column {} + +macro_rules! on_conflict_tuples { + ($( + $Tuple:tt { + $(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)* + } + )+) => { + $( + impl<_DB, _T, _SP, $($T),*> QueryFragment<_DB, _SP> for ConflictTarget<(_T, $($T),*)> where + _DB: Backend, + _SP: sql_dialect::on_conflict_clause::PgLikeOnConflictClause, + _T: Column, + $($T: Column,)* + { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, _DB>) -> QueryResult<()> + { + out.push_sql(" ("); + out.push_identifier(_T::NAME)?; + $( + out.push_sql(", "); + out.push_identifier($T::NAME)?; + )* + out.push_sql(")"); + Ok(()) + } + } + + impl<_T, $($T),*> OnConflictTarget<_T::Table> for ConflictTarget<(_T, $($T),*)> where + _T: Column, + $($T: Column,)* + { + } + )* + } +} + +diesel_derives::__diesel_for_each_tuple!(on_conflict_tuples); diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/on_conflict_target_decorations.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/on_conflict_target_decorations.rs new file mode 100644 index 000000000..015abb203 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/upsert/on_conflict_target_decorations.rs @@ -0,0 +1,68 @@ +use crate::backend::Backend; +use crate::expression::Expression; +use crate::query_builder::upsert::on_conflict_target::{ConflictTarget, NoConflictTarget}; +use crate::query_builder::where_clause::{NoWhereClause, WhereAnd, WhereClause}; +use crate::query_builder::{AstPass, QueryFragment, QueryResult}; +use crate::sql_types::BoolOrNullableBool; + +pub trait UndecoratedConflictTarget {} + +impl UndecoratedConflictTarget for NoConflictTarget {} +impl UndecoratedConflictTarget for ConflictTarget {} + +/// Interface to add information to conflict targets. +/// Designed to be open for further additions to conflict targets like constraints +pub trait DecoratableTarget

{ + /// Output type of filter_target operation + type FilterOutput; + /// equivalent to filter of FilterDsl but aimed at conflict targets + fn filter_target(self, predicate: P) -> Self::FilterOutput; +} + +#[derive(Debug)] +pub struct DecoratedConflictTarget { + pub(crate) target: T, + pub(crate) where_clause: U, +} + +impl DecoratableTarget

for T +where + P: Expression, + P::SqlType: BoolOrNullableBool, + T: UndecoratedConflictTarget, +{ + type FilterOutput = DecoratedConflictTarget>; + + fn filter_target(self, predicate: P) -> Self::FilterOutput { + DecoratedConflictTarget { + target: self, + where_clause: NoWhereClause.and(predicate), + } + } +} + +impl DecoratableTarget

for DecoratedConflictTarget +where + P: Expression, + P::SqlType: BoolOrNullableBool, + U: WhereAnd

, +{ + type FilterOutput = DecoratedConflictTarget>::Output>; + + fn filter_target(self, predicate: P) -> Self::FilterOutput { + DecoratedConflictTarget { + target: self.target, + where_clause: self.where_clause.and(predicate), + } + } +} + +impl QueryFragment for DecoratedConflictTarget +where + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/where_clause.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/where_clause.rs new file mode 100644 index 000000000..37d91cfb7 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_builder/where_clause.rs @@ -0,0 +1,205 @@ +use super::from_clause::AsQuerySource; +use super::*; +use crate::backend::DieselReserveSpecialization; +use crate::expression::grouped::Grouped; +use crate::expression::operators::{And, Or}; +use crate::expression::*; +use crate::sql_types::BoolOrNullableBool; + +/// Add `Predicate` to the current `WHERE` clause, joining with `AND` if +/// applicable. +pub trait WhereAnd { + /// What is the type of the resulting `WHERE` clause? + type Output; + + /// See the trait-level docs. + fn and(self, predicate: Predicate) -> Self::Output; +} + +/// Add `Predicate` to the current `WHERE` clause, joining with `OR` if +/// applicable. +pub trait WhereOr { + /// What is the type of the resulting `WHERE` clause? + type Output; + + /// See the trait-level docs. + fn or(self, predicate: Predicate) -> Self::Output; +} + +/// Represents that a query has no `WHERE` clause. +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoWhereClause; + +impl QueryFragment for NoWhereClause +where + DB: Backend + DieselReserveSpecialization, +{ + fn walk_ast<'b>(&'b self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> { + Ok(()) + } +} + +impl WhereAnd for NoWhereClause +where + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = WhereClause; + + fn and(self, predicate: Predicate) -> Self::Output { + WhereClause(predicate) + } +} + +impl WhereOr for NoWhereClause +where + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = WhereClause; + + fn or(self, predicate: Predicate) -> Self::Output { + WhereClause(predicate) + } +} + +impl From for BoxedWhereClause<'_, DB> { + fn from(_: NoWhereClause) -> Self { + BoxedWhereClause::None + } +} + +/// The `WHERE` clause of a query. +#[derive(Debug, Clone, Copy, QueryId)] +pub struct WhereClause(Expr); + +impl QueryFragment for WhereClause +where + DB: Backend + DieselReserveSpecialization, + Expr: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql(" WHERE "); + self.0.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl WhereAnd for WhereClause +where + Expr: Expression, + Expr::SqlType: BoolOrNullableBool, + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = WhereClause>>; + + fn and(self, predicate: Predicate) -> Self::Output { + WhereClause(Grouped(And::new(self.0, predicate))) + } +} + +impl WhereOr for WhereClause +where + Expr: Expression, + Expr::SqlType: BoolOrNullableBool, + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = WhereClause>>; + + fn or(self, predicate: Predicate) -> Self::Output { + WhereClause(Grouped(Or::new(self.0, predicate))) + } +} + +impl<'a, DB, Predicate> From> for BoxedWhereClause<'a, DB> +where + DB: Backend, + Predicate: QueryFragment + Send + 'a, +{ + fn from(where_clause: WhereClause) -> Self { + BoxedWhereClause::Where(Box::new(where_clause.0)) + } +} + +/// Marker trait indicating that a `WHERE` clause is valid for a given query +/// source. +pub trait ValidWhereClause {} + +impl ValidWhereClause for NoWhereClause {} + +impl ValidWhereClause for WhereClause +where + Expr: AppearsOnTable, + QS: AsQuerySource, +{ +} + +impl ValidWhereClause for WhereClause where + Expr: AppearsOnTable +{ +} + +#[allow(missing_debug_implementations)] // We can't... +pub enum BoxedWhereClause<'a, DB> { + Where(Box + Send + 'a>), + None, +} + +impl QueryFragment for BoxedWhereClause<'_, DB> +where + DB: Backend + DieselReserveSpecialization, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + match *self { + BoxedWhereClause::Where(ref where_clause) => { + out.push_sql(" WHERE "); + where_clause.walk_ast(out) + } + BoxedWhereClause::None => Ok(()), + } + } +} + +impl QueryId for BoxedWhereClause<'_, DB> { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl<'a, DB, Predicate> WhereAnd for BoxedWhereClause<'a, DB> +where + DB: Backend + 'a, + Predicate: QueryFragment + Send + 'a, + Grouped + Send + 'a>, Predicate>>: QueryFragment, +{ + type Output = Self; + + fn and(self, predicate: Predicate) -> Self::Output { + use self::BoxedWhereClause::Where; + + match self { + Where(where_clause) => Where(Box::new(Grouped(And::new(where_clause, predicate)))), + BoxedWhereClause::None => Where(Box::new(predicate)), + } + } +} + +impl<'a, DB, Predicate> WhereOr for BoxedWhereClause<'a, DB> +where + DB: Backend + 'a, + Predicate: QueryFragment + Send + 'a, + Grouped + Send + 'a>, Predicate>>: QueryFragment, +{ + type Output = Self; + + fn or(self, predicate: Predicate) -> Self::Output { + use self::BoxedWhereClause::Where; + + match self { + Where(where_clause) => Where(Box::new(Grouped(Or::new(where_clause, predicate)))), + BoxedWhereClause::None => Where(Box::new(predicate)), + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/belonging_to_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/belonging_to_dsl.rs new file mode 100644 index 000000000..95fef4d56 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/belonging_to_dsl.rs @@ -0,0 +1,54 @@ +/// Constructs a query that finds record(s) based on directional association with other record(s). +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::{posts, users}; +/// # +/// # #[derive(Identifiable, Queryable)] +/// # pub struct User { +/// # id: i32, +/// # name: String, +/// # } +/// # +/// # #[derive(Debug, PartialEq)] +/// # #[derive(Identifiable, Queryable, Associations)] +/// # #[diesel(belongs_to(User))] +/// # pub struct Post { +/// # id: i32, +/// # user_id: i32, +/// # title: String, +/// # } +/// # +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = &mut establish_connection(); +/// # use self::users::dsl::*; +/// # use self::posts::dsl::{posts, title}; +/// let sean = users.filter(name.eq("Sean")).first::(connection)?; +/// let tess = users.filter(name.eq("Tess")).first::(connection)?; +/// +/// let seans_posts = Post::belonging_to(&sean) +/// .select(title) +/// .load::(connection)?; +/// assert_eq!(vec!["My first post", "About Rust"], seans_posts); +/// +/// // A vec or slice can be passed as well +/// let more_posts = Post::belonging_to(&vec![sean, tess]) +/// .select(title) +/// .load::(connection)?; +/// assert_eq!(vec!["My first post", "About Rust", "My first post too"], more_posts); +/// # Ok(()) +/// # } +/// ``` +pub trait BelongingToDsl { + /// The query returned by `belonging_to` + type Output; + + /// Get the record(s) belonging to record(s) `other` + fn belonging_to(other: T) -> Self::Output; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/boxed_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/boxed_dsl.rs new file mode 100644 index 000000000..072e7a3f7 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/boxed_dsl.rs @@ -0,0 +1,37 @@ +use crate::dsl; +use crate::expression::TypedExpressionType; +use crate::expression::ValidGrouping; +use crate::query_builder::AsQuery; +use crate::query_builder::FromClause; +use crate::query_builder::SelectStatement; +use crate::query_source::Table; +use crate::Expression; + +/// The `into_boxed` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `into_boxed` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait BoxedDsl<'a, DB> { + /// The return type of `internal_into_boxed` + type Output; + + /// See the trait documentation. + fn internal_into_boxed(self) -> dsl::IntoBoxed<'a, Self, DB>; +} + +impl<'a, T, DB> BoxedDsl<'a, DB> for T +where + T: Table + AsQuery>>, + SelectStatement>: BoxedDsl<'a, DB>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = dsl::IntoBoxed<'a, SelectStatement>, DB>; + + fn internal_into_boxed(self) -> Self::Output { + self.as_query().internal_into_boxed() + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/combine_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/combine_dsl.rs new file mode 100644 index 000000000..3e820fe52 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/combine_dsl.rs @@ -0,0 +1,114 @@ +use crate::dsl; +use crate::query_builder::combination_clause::{ + All, CombinationClause, Distinct, Except, Intersect, Union, +}; +use crate::query_builder::{AsQuery, Query}; +use crate::Table; + +/// Extension trait to combine queries using a combinator like `UNION`, `INTERSECT` or `EXCEPT` +/// with or without `ALL` rule for duplicates +pub trait CombineDsl { + /// What kind of query does this type represent? + type Query: Query; + + /// Combine two queries using a SQL `UNION` + /// + /// # Examples + /// ```rust + /// # extern crate diesel; + /// # include!("../doctest_setup.rs"); + /// # use schema::{users, animals}; + /// # use crate::diesel::query_dsl::positional_order_dsl::PositionalOrderDsl; + /// # + /// # fn main() { + /// # use self::users::dsl::{users, name as user_name}; + /// # use self::animals::dsl::{animals, name as animal_name}; + /// # let connection = &mut establish_connection(); + /// let data = users.select(user_name.nullable()) + /// .union(animals.select(animal_name).filter(animal_name.is_not_null())) + /// # .positional_order_by(1) + /// .load(connection); + /// + /// let expected_data = vec![ + /// Some(String::from("Jack")), + /// Some(String::from("Sean")), + /// Some(String::from("Tess")), + /// ]; + /// assert_eq!(Ok(expected_data), data); + /// # } + /// ``` + fn union(self, rhs: Rhs) -> dsl::Union + where + Rhs: AsQuery::SqlType>; + + /// Combine two queries using a SQL `UNION ALL` + fn union_all(self, rhs: Rhs) -> dsl::UnionAll + where + Rhs: AsQuery::SqlType>; + + /// Combine two queries using a SQL `INTERSECT` + fn intersect(self, rhs: Rhs) -> dsl::Intersect + where + Rhs: AsQuery::SqlType>; + + /// Combine two queries using a SQL `INTERSECT ALL` + fn intersect_all(self, rhs: Rhs) -> dsl::IntersectAll + where + Rhs: AsQuery::SqlType>; + + /// Combine two queries using a SQL `EXCEPT` + fn except(self, rhs: Rhs) -> dsl::Except + where + Rhs: AsQuery::SqlType>; + + /// Combine two queries using a SQL `EXCEPT ALL` + fn except_all(self, rhs: Rhs) -> dsl::ExceptAll + where + Rhs: AsQuery::SqlType>; +} + +impl CombineDsl for T { + type Query = T::Query; + + fn union(self, rhs: Rhs) -> dsl::Union + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Union, Distinct, self.as_query(), rhs.as_query()) + } + + fn union_all(self, rhs: Rhs) -> dsl::UnionAll + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Union, All, self.as_query(), rhs.as_query()) + } + + fn intersect(self, rhs: Rhs) -> dsl::Intersect + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Intersect, Distinct, self.as_query(), rhs.as_query()) + } + + fn intersect_all(self, rhs: Rhs) -> dsl::IntersectAll + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Intersect, All, self.as_query(), rhs.as_query()) + } + + fn except(self, rhs: Rhs) -> dsl::Except + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Except, Distinct, self.as_query(), rhs.as_query()) + } + + fn except_all(self, rhs: Rhs) -> dsl::ExceptAll + where + Rhs: AsQuery::SqlType>, + { + CombinationClause::new(Except, All, self.as_query(), rhs.as_query()) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/distinct_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/distinct_dsl.rs new file mode 100644 index 000000000..64dab4cbf --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/distinct_dsl.rs @@ -0,0 +1,69 @@ +use crate::dsl; +#[cfg(feature = "postgres_backend")] +use crate::expression::SelectableExpression; +use crate::expression::TypedExpressionType; +use crate::expression::ValidGrouping; +use crate::query_builder::FromClause; +use crate::query_builder::{AsQuery, SelectStatement}; +use crate::query_source::Table; +use crate::Expression; + +/// The `distinct` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `distinct` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait DistinctDsl { + /// The type returned by `.distinct` + type Output; + + /// See the trait documentation. + fn distinct(self) -> dsl::Distinct; +} + +impl DistinctDsl for T +where + T: Table + AsQuery>>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = dsl::Distinct>>; + + fn distinct(self) -> dsl::Distinct>> { + self.as_query().distinct() + } +} + +/// The `distinct_on` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `distinct_on` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +#[cfg(feature = "postgres_backend")] +pub trait DistinctOnDsl { + /// The type returned by `.distinct_on` + type Output; + + /// See the trait documentation + fn distinct_on(self, selection: Selection) -> dsl::DistinctOn; +} + +#[cfg(feature = "postgres_backend")] +impl DistinctOnDsl for T +where + Selection: SelectableExpression, + T: Table + AsQuery>>, + SelectStatement>: DistinctOnDsl, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = dsl::DistinctOn>, Selection>; + + fn distinct_on(self, selection: Selection) -> dsl::DistinctOn { + self.as_query().distinct_on(selection) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/filter_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/filter_dsl.rs new file mode 100644 index 000000000..55fca2809 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/filter_dsl.rs @@ -0,0 +1,85 @@ +use crate::dsl::{Filter, OrFilter}; +use crate::expression_methods::*; +use crate::query_source::*; + +/// The `filter` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `filter` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait FilterDsl { + /// The type returned by `.filter`. + type Output; + + /// See the trait documentation. + fn filter(self, predicate: Predicate) -> Self::Output; +} + +impl FilterDsl for T +where + T: Table, + T::Query: FilterDsl, +{ + type Output = Filter; + + fn filter(self, predicate: Predicate) -> Self::Output { + self.as_query().filter(predicate) + } +} + +/// The `find` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `find` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait FindDsl { + /// The type returned by `.find`. + type Output; + + /// See the trait documentation. + fn find(self, id: PK) -> Self::Output; +} + +impl FindDsl for T +where + T: Table + FilterDsl<<::PrimaryKey as EqAll>::Output>, + T::PrimaryKey: EqAll, +{ + type Output = Filter>::Output>; + + fn find(self, id: PK) -> Self::Output { + let primary_key = self.primary_key(); + self.filter(primary_key.eq_all(id)) + } +} + +/// The `or_filter` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `or_filter` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait OrFilterDsl { + /// The type returned by `.filter`. + type Output; + + /// See the trait documentation. + fn or_filter(self, predicate: Predicate) -> Self::Output; +} + +impl OrFilterDsl for T +where + T: Table, + T::Query: OrFilterDsl, +{ + type Output = OrFilter; + + fn or_filter(self, predicate: Predicate) -> Self::Output { + self.as_query().or_filter(predicate) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/group_by_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/group_by_dsl.rs new file mode 100644 index 000000000..7a712b5af --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/group_by_dsl.rs @@ -0,0 +1,37 @@ +use crate::dsl; +use crate::expression::Expression; +use crate::expression::TypedExpressionType; +use crate::expression::ValidGrouping; +use crate::query_builder::FromClause; +use crate::query_builder::{AsQuery, SelectStatement}; +use crate::query_source::Table; + +/// The `group_by` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `group_by` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait GroupByDsl { + /// The type returned by `.group_by` + type Output; + + /// See the trait documentation. + fn group_by(self, expr: Expr) -> dsl::GroupBy; +} + +impl GroupByDsl for T +where + Expr: Expression, + T: Table + AsQuery>>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, + T::Query: GroupByDsl, +{ + type Output = dsl::GroupBy>, Expr>; + + fn group_by(self, expr: Expr) -> dsl::GroupBy { + self.as_query().group_by(expr) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/having_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/having_dsl.rs new file mode 100644 index 000000000..8551d19af --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/having_dsl.rs @@ -0,0 +1,16 @@ +use crate::dsl; + +/// The `having` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `having` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait HavingDsl { + /// The type returned by `.having`. + type Output; + + /// See the trait documentation. + fn having(self, predicate: Predicate) -> dsl::Having; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/join_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/join_dsl.rs new file mode 100644 index 000000000..b6ebdeb51 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/join_dsl.rs @@ -0,0 +1,81 @@ +use crate::helper_types; +use crate::query_builder::AsQuery; +use crate::query_source::joins::OnClauseWrapper; +use crate::query_source::{JoinTo, QuerySource, Table}; + +#[doc(hidden)] +/// `JoinDsl` support trait to emulate associated type constructors +pub trait InternalJoinDsl { + type Output; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output; +} + +impl InternalJoinDsl for T +where + T: Table + AsQuery, + T::Query: InternalJoinDsl, +{ + type Output = >::Output; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { + self.as_query().join(rhs, kind, on) + } +} + +#[doc(hidden)] +/// `JoinDsl` support trait to emulate associated type constructors and grab +/// the known on clause from the associations API +pub trait JoinWithImplicitOnClause { + type Output; + + fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output; +} + +impl JoinWithImplicitOnClause for Lhs +where + Lhs: JoinTo, + Lhs: InternalJoinDsl<>::FromClause, Kind, >::OnClause>, +{ + type Output = >::Output; + + fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output { + let (from, on) = Lhs::join_target(rhs); + self.join(from, kind, on) + } +} + +/// Specify the `ON` clause for a join statement. This will override +/// any implicit `ON` clause that would come from [`joinable!`] +/// +/// [`joinable!`]: crate::joinable! +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::{users, posts}; +/// # +/// # fn main() { +/// # let connection = &mut establish_connection(); +/// let data = users::table +/// .left_join(posts::table.on( +/// users::id.eq(posts::user_id).and( +/// posts::title.eq("My first post")) +/// )) +/// .select((users::name, posts::title.nullable())) +/// .load(connection); +/// let expected = vec![ +/// ("Sean".to_string(), Some("My first post".to_string())), +/// ("Tess".to_string(), None), +/// ]; +/// assert_eq!(Ok(expected), data); +/// # } +pub trait JoinOnDsl: Sized { + /// See the trait documentation. + fn on(self, on: On) -> helper_types::On { + OnClauseWrapper::new(self, on) + } +} + +impl JoinOnDsl for T {} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/limit_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/limit_dsl.rs new file mode 100644 index 000000000..3aa73662f --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/limit_dsl.rs @@ -0,0 +1,28 @@ +use crate::query_source::Table; + +/// The `limit` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `limit` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait LimitDsl { + /// The type returned by `.limit` + type Output; + + /// See the trait documentation + fn limit(self, limit: i64) -> Self::Output; +} + +impl LimitDsl for T +where + T: Table, + T::Query: LimitDsl, +{ + type Output = ::Output; + + fn limit(self, limit: i64) -> Self::Output { + self.as_query().limit(limit) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/load_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/load_dsl.rs new file mode 100644 index 000000000..6bddb0a33 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/load_dsl.rs @@ -0,0 +1,222 @@ +use self::private::LoadIter; +use super::RunQueryDsl; +use crate::backend::Backend; +use crate::connection::{Connection, DefaultLoadingMode, LoadConnection}; +use crate::deserialize::FromSqlRow; +use crate::expression::QueryMetadata; +use crate::query_builder::{AsQuery, QueryFragment, QueryId}; +use crate::result::QueryResult; + +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +pub use self::private::CompatibleType; + +#[cfg(not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))] +pub(crate) use self::private::CompatibleType; + +/// The `load` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`RunQueryDsl`]. However, you may need a where clause on this trait +/// to call `load` from generic code. +/// +/// [`RunQueryDsl`]: crate::RunQueryDsl +pub trait LoadQuery<'query, Conn, U, B = DefaultLoadingMode>: RunQueryDsl { + /// Return type of `LoadQuery::internal_load` + type RowIter<'conn>: Iterator> + where + Conn: 'conn; + + /// Load this query + #[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" + )] + fn internal_load(self, conn: &mut Conn) -> QueryResult>; +} + +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[deprecated(note = "Use `LoadQuery::Iter` directly")] +pub type LoadRet<'conn, 'query, Q, C, U, B = DefaultLoadingMode> = + >::RowIter<'conn>; + +impl<'query, Conn, T, U, DB, B> LoadQuery<'query, Conn, U, B> for T +where + Conn: Connection + LoadConnection, + T: AsQuery + RunQueryDsl, + T::Query: QueryFragment + QueryId + 'query, + T::SqlType: CompatibleType, + DB: Backend + QueryMetadata + 'static, + U: FromSqlRow<>::SqlType, DB> + 'static, + >::SqlType: 'static, +{ + type RowIter<'conn> + = LoadIter< + U, + >::Cursor<'conn, 'query>, + >::SqlType, + DB, + > + where + Conn: 'conn; + + fn internal_load(self, conn: &mut Conn) -> QueryResult> { + Ok(LoadIter { + cursor: conn.load(self.as_query())?, + _marker: Default::default(), + }) + } +} + +/// The `execute` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`RunQueryDsl`]. However, you may need a where clause on this trait +/// to call `execute` from generic code. +/// +/// [`RunQueryDsl`]: crate::RunQueryDsl +pub trait ExecuteDsl, DB: Backend = ::Backend>: + Sized +{ + /// Execute this command + fn execute(query: Self, conn: &mut Conn) -> QueryResult; +} + +use crate::result::Error; + +impl ExecuteDsl for T +where + Conn: Connection, + DB: Backend, + T: QueryFragment + QueryId, +{ + fn execute(query: T, conn: &mut Conn) -> Result { + conn.execute_returning_count(&query) + } +} + +// These types and traits are not part of the public API. +// +// * CompatibleType as we consider this as "sealed" trait. It shouldn't +// be implemented by a third party +// * LoadIter as it's an implementation detail +mod private { + use crate::backend::Backend; + use crate::deserialize::FromSqlRow; + use crate::expression::select_by::SelectBy; + use crate::expression::{Expression, TypedExpressionType}; + use crate::sql_types::{SqlType, Untyped}; + use crate::{QueryResult, Selectable}; + + #[allow(missing_debug_implementations)] + pub struct LoadIter { + pub(super) cursor: C, + pub(super) _marker: std::marker::PhantomData<(ST, U, DB)>, + } + + impl<'a, C, U, ST, DB, R> LoadIter + where + DB: Backend, + C: Iterator>, + R: crate::row::Row<'a, DB>, + U: FromSqlRow, + { + pub(super) fn map_row(row: Option>) -> Option> { + match row? { + Ok(row) => Some( + U::build_from_row(&row).map_err(crate::result::Error::DeserializationError), + ), + Err(e) => Some(Err(e)), + } + } + } + + impl<'a, C, U, ST, DB, R> Iterator for LoadIter + where + DB: Backend, + C: Iterator>, + R: crate::row::Row<'a, DB>, + U: FromSqlRow, + { + type Item = QueryResult; + + fn next(&mut self) -> Option { + Self::map_row(self.cursor.next()) + } + + fn size_hint(&self) -> (usize, Option) { + self.cursor.size_hint() + } + + fn count(self) -> usize + where + Self: Sized, + { + self.cursor.count() + } + + fn last(self) -> Option + where + Self: Sized, + { + Self::map_row(self.cursor.last()) + } + + fn nth(&mut self, n: usize) -> Option { + Self::map_row(self.cursor.nth(n)) + } + } + + impl<'a, C, U, ST, DB, R> ExactSizeIterator for LoadIter + where + DB: Backend, + C: ExactSizeIterator + Iterator>, + R: crate::row::Row<'a, DB>, + U: FromSqlRow, + { + fn len(&self) -> usize { + self.cursor.len() + } + } + + #[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) + )] + #[diagnostic::on_unimplemented( + note = "this is a mismatch between what your query returns and what your type expects the query to return", + note = "the fields in your struct need to match the fields returned by your query in count, order and type", + note = "consider using `#[diesel(check_for_backend({DB}))]` on either `#[derive(Selectable)]` or `#[derive(QueryableByName)]` \n\ + on your struct `{U}` and in your query `.select({U}::as_select())` to get a better error message" + )] + pub trait CompatibleType { + type SqlType; + } + + impl CompatibleType for ST + where + DB: Backend, + ST: SqlType + crate::sql_types::SingleValue, + U: FromSqlRow, + { + type SqlType = ST; + } + + impl CompatibleType for Untyped + where + U: FromSqlRow, + DB: Backend, + { + type SqlType = Untyped; + } + + impl CompatibleType for SelectBy + where + DB: Backend, + ST: SqlType + TypedExpressionType, + U: Selectable, + E: Expression, + U: FromSqlRow, + { + type SqlType = ST; + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/locking_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/locking_dsl.rs new file mode 100644 index 000000000..6ad6d9331 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/locking_dsl.rs @@ -0,0 +1,56 @@ +use crate::expression::TypedExpressionType; +use crate::expression::ValidGrouping; +use crate::query_builder::AsQuery; +use crate::query_builder::FromClause; +use crate::query_builder::SelectStatement; +use crate::query_source::Table; +use crate::Expression; + +/// Methods related to locking select statements +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `for_update` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait LockingDsl { + /// The type returned by `set_lock`. See [`dsl::ForUpdate`] and friends for + /// convenient access to this type. + /// + /// [`dsl::ForUpdate`]: crate::dsl::ForUpdate + type Output; + + /// See the trait level documentation + fn with_lock(self, lock: Lock) -> Self::Output; +} + +impl LockingDsl for T +where + T: Table + AsQuery>>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = > as LockingDsl>::Output; + + fn with_lock(self, lock: Lock) -> Self::Output { + self.as_query().with_lock(lock) + } +} + +/// Methods related to modifiers on locking select statements +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `skip_locked` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait ModifyLockDsl { + /// The type returned by `modify_lock`. See [`dsl::SkipLocked`] and friends + /// for convenient access to this type. + /// + /// [`dsl::SkipLocked`]: crate::dsl::SkipLocked + type Output; + + /// See the trait level documentation + fn modify_lock(self, modifier: Modifier) -> Self::Output; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/mod.rs new file mode 100644 index 000000000..8be915e75 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/mod.rs @@ -0,0 +1,1791 @@ +//! Traits that construct SELECT statements +//! +//! Traits in this module have methods that generally map to the keyword for the corresponding clause in SQL, +//! unless it conflicts with a Rust keyword (such as `WHERE`/`where`). +//! +//! Methods for constructing queries lives on the [`QueryDsl`] trait. +//! Methods for executing queries live on [`RunQueryDsl`]. +//! +//! See also [`expression_methods`][expression_methods] and [`dsl`][dsl]. +//! +//! [expression_methods]: super::expression_methods +//! [dsl]: super::dsl + +use crate::backend::Backend; +use crate::connection::Connection; +use crate::expression::count::CountStar; +use crate::expression::Expression; +use crate::helper_types::*; +use crate::query_builder::locking_clause as lock; +use crate::query_source::{joins, Table}; +use crate::result::QueryResult; + +mod belonging_to_dsl; +#[doc(hidden)] +pub mod boxed_dsl; +mod combine_dsl; +mod distinct_dsl; +#[doc(hidden)] +pub mod filter_dsl; +mod group_by_dsl; +mod having_dsl; +mod join_dsl; +#[doc(hidden)] +pub mod limit_dsl; +#[doc(hidden)] +pub mod load_dsl; +mod locking_dsl; +mod nullable_select_dsl; +mod offset_dsl; +pub(crate) mod order_dsl; +#[doc(hidden)] +pub mod positional_order_dsl; +mod save_changes_dsl; +#[doc(hidden)] +pub mod select_dsl; +mod single_value_dsl; + +pub use self::belonging_to_dsl::BelongingToDsl; +pub use self::combine_dsl::CombineDsl; +pub use self::join_dsl::{InternalJoinDsl, JoinOnDsl, JoinWithImplicitOnClause}; +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +pub use self::load_dsl::CompatibleType; +#[doc(hidden)] +pub use self::load_dsl::LoadQuery; +pub use self::save_changes_dsl::{SaveChangesDsl, UpdateAndFetchResults}; + +/// The traits used by `QueryDsl`. +/// +/// Each trait in this module represents exactly one method from `QueryDsl`. +/// Apps should general rely on `QueryDsl` directly, rather than these traits. +/// However, generic code may need to include a where clause that references +/// these traits. +pub mod methods { + pub use super::boxed_dsl::BoxedDsl; + pub use super::distinct_dsl::*; + #[doc(inline)] + pub use super::filter_dsl::*; + pub use super::group_by_dsl::GroupByDsl; + pub use super::having_dsl::HavingDsl; + pub use super::limit_dsl::LimitDsl; + pub use super::load_dsl::{ExecuteDsl, LoadQuery}; + pub use super::locking_dsl::{LockingDsl, ModifyLockDsl}; + pub use super::nullable_select_dsl::SelectNullableDsl; + pub use super::offset_dsl::OffsetDsl; + pub use super::order_dsl::{OrderDsl, ThenOrderDsl}; + pub use super::select_dsl::SelectDsl; + pub use super::single_value_dsl::SingleValueDsl; + + #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] + #[doc(hidden)] + #[allow(deprecated)] + #[deprecated(note = "Use `LoadQuery::RowIter` directly")] + pub use super::load_dsl::LoadRet; +} + +/// Methods used to construct select statements. +pub trait QueryDsl: Sized { + /// Adds the `DISTINCT` keyword to a query. + /// + /// This method will override any previous distinct clause that was present. + /// For example, on PostgreSQL, `foo.distinct_on(bar).distinct()` will + /// create the same query as `foo.distinct()`. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// # diesel::sql_query("DELETE FROM users").execute(connection).unwrap(); + /// diesel::insert_into(users) + /// .values(&vec![name.eq("Sean"); 3]) + /// .execute(connection)?; + /// let names = users.select(name).load::(connection)?; + /// let distinct_names = users.select(name).distinct().load::(connection)?; + /// + /// assert_eq!(vec!["Sean"; 3], names); + /// assert_eq!(vec!["Sean"; 1], distinct_names); + /// # Ok(()) + /// # } + /// ``` + fn distinct(self) -> Distinct + where + Self: methods::DistinctDsl, + { + methods::DistinctDsl::distinct(self) + } + + /// Adds the `DISTINCT ON` clause to a query. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::animals; + /// # + /// # #[derive(Queryable, Debug, PartialEq)] + /// # struct Animal { + /// # species: String, + /// # name: Option, + /// # legs: i32, + /// # } + /// # + /// # impl Animal { + /// # fn new>(species: S, name: Option<&str>, legs: i32) -> Self { + /// # Animal { + /// # species: species.into(), + /// # name: name.map(Into::into), + /// # legs + /// # } + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// # diesel::sql_query("DELETE FROM animals").execute(connection).unwrap(); + /// diesel::insert_into(animals) + /// .values(&vec![ + /// (species.eq("dog"), name.eq(Some("Jack")), legs.eq(4)), + /// (species.eq("dog"), name.eq(None), legs.eq(4)), + /// (species.eq("spider"), name.eq(None), legs.eq(8)), + /// ]) + /// .execute(connection) + /// .unwrap(); + /// let all_animals = animals.select((species, name, legs)).load(connection); + /// let distinct_animals = animals + /// .select((species, name, legs)) + /// .order_by((species, legs)) + /// .distinct_on(species) + /// .load(connection); + /// + /// assert_eq!(Ok(vec![Animal::new("dog", Some("Jack"), 4), + /// Animal::new("dog", None, 4), + /// Animal::new("spider", None, 8)]), all_animals); + /// assert_eq!(Ok(vec![Animal::new("dog", Some("Jack"), 4), + /// Animal::new("spider", None, 8)]), distinct_animals); + /// # } + /// ``` + #[cfg(feature = "postgres_backend")] + fn distinct_on(self, expr: Expr) -> DistinctOn + where + Self: methods::DistinctOnDsl, + { + methods::DistinctOnDsl::distinct_on(self, expr) + } + + // FIXME: Needs usage example and doc rewrite + /// Adds a `SELECT` clause to the query. + /// + /// If there was already a select clause present, it will be overridden. + /// For example, `foo.select(bar).select(baz)` will produce the same + /// query as `foo.select(baz)`. + /// + /// By default, the select clause will be roughly equivalent to `SELECT *` + /// (however, Diesel will list all columns to ensure that they are in the + /// order we expect). + /// + /// `select` has slightly stricter bounds on its arguments than other + /// methods. In particular, when used with a left outer join, `.nullable` + /// must be called on columns that come from the right side of a join. It + /// can be called on the column itself, or on an expression containing that + /// column. `title.nullable()`, `lower(title).nullable()`, and `(id, + /// title).nullable()` would all be valid. + /// + /// In order to use this method with columns from different tables + /// a method like [`.inner_join`] or [`.left_join`] needs to be called before + /// calling [`.select`] (See examples below). + /// This is because you can only access columns from tables + /// that appear in your query before that function call. + /// + /// [`.inner_join`]: QueryDsl::inner_join() + /// [`.left_join`]: QueryDsl::left_join() + /// [`.select`]: QueryDsl::select() + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use self::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// // By default, all columns will be selected + /// let all_users = users.load::<(i32, String)>(connection)?; + /// assert_eq!(vec![(1, String::from("Sean")), (2, String::from("Tess"))], all_users); + /// + /// let all_names = users.select(name).load::(connection)?; + /// assert_eq!(vec!["Sean", "Tess"], all_names); + /// # Ok(()) + /// # } + /// ``` + /// + /// ### When used with a left join + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::{users, posts}; + /// # + /// # #[derive(Queryable, PartialEq, Eq, Debug)] + /// # struct User { + /// # id: i32, + /// # name: String, + /// # } + /// # + /// # impl User { + /// # fn new(id: i32, name: &str) -> Self { + /// # User { + /// # id, + /// # name: name.into(), + /// # } + /// # } + /// # } + /// # + /// # #[derive(Queryable, PartialEq, Eq, Debug)] + /// # struct Post { + /// # id: i32, + /// # user_id: i32, + /// # title: String, + /// # } + /// # + /// # impl Post { + /// # fn new(id: i32, user_id: i32, title: &str) -> Self { + /// # Post { + /// # id, + /// # user_id, + /// # title: title.into(), + /// # } + /// # } + /// # } + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let connection = &mut establish_connection(); + /// # diesel::sql_query("DELETE FROM posts").execute(connection)?; + /// # diesel::insert_into(posts::table) + /// # .values((posts::user_id.eq(1), posts::title.eq("Sean's Post"))) + /// # .execute(connection)?; + /// # let post_id = posts::table.select(posts::id) + /// # .first::(connection)?; + /// let join = users::table.left_join(posts::table); + /// + /// // By default, all columns from both tables are selected. + /// // If no explicit select clause is used this means that the result + /// // type of this query must contain all fields from the original schema in order. + /// let all_data = join.load::<(User, Option)>(connection)?; + /// let expected_data = vec![ + /// (User::new(1, "Sean"), Some(Post::new(post_id, 1, "Sean's Post"))), + /// (User::new(2, "Tess"), None), + /// ]; + /// assert_eq!(expected_data, all_data); + /// + /// // Since `posts` is on the right side of a left join, `.nullable` is + /// // needed. + /// let names_and_titles = join.select((users::name, posts::title.nullable())) + /// .load::<(String, Option)>(connection)?; + /// let expected_data = vec![ + /// (String::from("Sean"), Some(String::from("Sean's Post"))), + /// (String::from("Tess"), None), + /// ]; + /// assert_eq!(expected_data, names_and_titles); + /// # Ok(()) + /// # } + /// ``` + fn select(self, selection: Selection) -> Select + where + Selection: Expression, + Self: methods::SelectDsl, + { + methods::SelectDsl::select(self, selection) + } + + /// Get the count of a query. This is equivalent to `.select(count_star())` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let count = users.count().get_result(connection); + /// assert_eq!(Ok(2), count); + /// # } + /// ``` + fn count(self) -> Select + where + Self: methods::SelectDsl, + { + use crate::dsl::count_star; + + QueryDsl::select(self, count_star()) + } + + /// Join two tables using a SQL `INNER JOIN`. + /// + /// If you have invoked [`joinable!`] for the two tables, you can pass that + /// table directly. Otherwise you will need to use [`.on`] to specify the `ON` + /// clause. + /// + /// [`joinable!`]: crate::joinable! + /// [`.on`]: JoinOnDsl::on() + /// + /// You can join to as many tables as you'd like in a query, with the + /// restriction that no table can appear in the query more than once. For + /// tables that appear more than once in a single query the usage of [`alias!`](crate::alias!) + /// is required. + /// + /// You will also need to call [`allow_tables_to_appear_in_same_query!`]. + /// If you are using `diesel print-schema`, this will + /// have been generated for you. + /// See the documentation for [`allow_tables_to_appear_in_same_query!`] for + /// details. + /// + /// Diesel expects multi-table joins to be semantically grouped based on the + /// relationships. For example, `users.inner_join(posts.inner_join(comments))` + /// is not the same as `users.inner_join(posts).inner_join(comments)`. The first + /// would deserialize into `(User, (Post, Comment))` and generate the following + /// SQL: + /// + /// ```sql + /// SELECT * FROM users + /// INNER JOIN ( + /// posts + /// INNER JOIN comments ON comments.post_id = posts.id + /// ) ON posts.user_id = users.id + /// + /// ``` + /// + /// While the second query would deserialize into `(User, Post, Comment)` and + /// generate the following SQL: + /// + /// ```sql + /// SELECT * FROM users + /// INNER JOIN posts ON posts.user_id = users.id + /// INNER JOIN comments ON comments.user_id = users.id + /// ``` + /// + /// The exact generated SQL may change in future diesel version as long as the + /// generated query continues to produce same results. The currently generated + /// SQL is referred as ["explicit join"](https://www.postgresql.org/docs/current/explicit-joins.html) + /// by the PostgreSQL documentation and may have implications on the chosen query plan + /// for large numbers of joins in the same query. Checkout the documentation of the + /// [`join_collapse_limit` parameter](https://www.postgresql.org/docs/current/runtime-config-query.html#GUC-JOIN-COLLAPSE-LIMIT) + /// to control this behaviour. + /// + /// [associations]: crate::associations + /// [`allow_tables_to_appear_in_same_query!`]: crate::allow_tables_to_appear_in_same_query! + /// + /// Note that in order to use this method with [`.select`], you will need to use it before calling + /// [`.select`] (See examples below). This is because you can only access columns from tables + /// that appear in your query before the call to [`.select`]. + /// + /// [`.select`]: QueryDsl::select() + /// + /// # Examples + /// + /// ### With implicit `ON` clause + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::{users, posts}; + /// # /* + /// joinable!(posts -> users (user_id)); + /// allow_tables_to_appear_in_same_query!(users, posts); + /// # */ + /// + /// # fn main() { + /// # use self::users::dsl::{users, name}; + /// # use self::posts::dsl::{posts, user_id, title}; + /// # let connection = &mut establish_connection(); + /// let data = users.inner_join(posts) + /// .select((name, title)) + /// .load(connection); + /// + /// let expected_data = vec![ + /// (String::from("Sean"), String::from("My first post")), + /// (String::from("Sean"), String::from("About Rust")), + /// (String::from("Tess"), String::from("My first post too")), + /// ]; + /// assert_eq!(Ok(expected_data), data); + /// # } + /// ``` + /// + /// ### With explicit `ON` clause + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::{users, posts}; + /// # + /// # /* + /// allow_tables_to_appear_in_same_query!(users, posts); + /// # */ + /// + /// # fn main() { + /// # use self::users::dsl::{users, name}; + /// # use self::posts::dsl::{posts, user_id, title}; + /// # let connection = &mut establish_connection(); + /// diesel::insert_into(posts) + /// .values(&vec![ + /// (user_id.eq(1), title.eq("Sean's post")), + /// (user_id.eq(2), title.eq("Sean is a jerk")), + /// ]) + /// .execute(connection) + /// .unwrap(); + /// + /// let data = users + /// .inner_join(posts.on(title.like(name.concat("%")))) + /// .select((name, title)) + /// .load(connection); + /// let expected_data = vec![ + /// (String::from("Sean"), String::from("Sean's post")), + /// (String::from("Sean"), String::from("Sean is a jerk")), + /// ]; + /// assert_eq!(Ok(expected_data), data); + /// # } + /// ``` + /// + /// ### With explicit `ON` clause (struct) + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::{users, posts}; + /// # + /// # /* + /// allow_tables_to_appear_in_same_query!(users, posts); + /// # */ + /// + /// # fn main() { + /// # use self::users::dsl::{users, name}; + /// # use self::posts::dsl::{posts, user_id, title}; + /// # let connection = &mut establish_connection(); + /// #[derive(Debug, PartialEq, Queryable)] + /// struct User { + /// id: i32, + /// name: String, + /// } + /// + /// #[derive(Debug, PartialEq, Queryable)] + /// struct Post { + /// id: i32, + /// user_id: i32, + /// title: String, + /// } + /// + /// diesel::insert_into(posts) + /// .values(&vec![ + /// (user_id.eq(1), title.eq("Sean's post")), + /// (user_id.eq(2), title.eq("Sean is a jerk")), + /// ]) + /// .execute(connection) + /// .unwrap(); + /// + /// // By default, all columns from both tables are selected. + /// // If no explicit select clause is used this means that the + /// // result type of this query must contain all fields from the + /// // original schema in order. + /// let data = users + /// .inner_join(posts.on(title.like(name.concat("%")))) + /// .load::<(User, Post)>(connection); // type could be elided + /// let expected_data = vec![ + /// ( + /// User { id: 1, name: String::from("Sean") }, + /// Post { id: 4, user_id: 1, title: String::from("Sean's post") }, + /// ), + /// ( + /// User { id: 1, name: String::from("Sean") }, + /// Post { id: 5, user_id: 2, title: String::from("Sean is a jerk") }, + /// ), + /// ]; + /// assert_eq!(Ok(expected_data), data); + /// # } + /// ``` + fn inner_join(self, rhs: Rhs) -> InnerJoin + where + Self: JoinWithImplicitOnClause, + { + self.join_with_implicit_on_clause(rhs, joins::Inner) + } + + /// Join two tables using a SQL `LEFT OUTER JOIN`. + /// + /// Behaves similarly to [`inner_join`], but will produce a left join + /// instead. See [`inner_join`] for usage examples. + /// + /// [`inner_join`]: QueryDsl::inner_join() + /// + /// Columns in the right hand table will become `Nullable` which means + /// you must call `nullable()` on the corresponding fields in the select + /// clause: + /// + /// ### Selecting after a left join + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::{users, posts}; + /// # + /// # #[derive(Queryable, PartialEq, Eq, Debug)] + /// # struct User { + /// # id: i32, + /// # name: String, + /// # } + /// # + /// # impl User { + /// # fn new(id: i32, name: &str) -> Self { + /// # User { + /// # id, + /// # name: name.into(), + /// # } + /// # } + /// # } + /// # + /// # #[derive(Queryable, PartialEq, Eq, Debug)] + /// # struct Post { + /// # id: i32, + /// # user_id: i32, + /// # title: String, + /// # } + /// # + /// # impl Post { + /// # fn new(id: i32, user_id: i32, title: &str) -> Self { + /// # Post { + /// # id, + /// # user_id, + /// # title: title.into(), + /// # } + /// # } + /// # } + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let connection = &mut establish_connection(); + /// # diesel::sql_query("DELETE FROM posts").execute(connection)?; + /// # diesel::insert_into(posts::table) + /// # .values((posts::user_id.eq(1), posts::title.eq("Sean's Post"))) + /// # .execute(connection)?; + /// # let post_id = posts::table.select(posts::id) + /// # .first::(connection)?; + /// let join = users::table.left_join(posts::table); + /// + /// // Since `posts` is on the right side of a left join, `.nullable` is + /// // needed. + /// let names_and_titles = join.select((users::name, posts::title.nullable())) + /// .load::<(String, Option)>(connection)?; + /// let expected_data = vec![ + /// (String::from("Sean"), Some(String::from("Sean's Post"))), + /// (String::from("Tess"), None), + /// ]; + /// assert_eq!(expected_data, names_and_titles); + /// # Ok(()) + /// # } + /// ``` + fn left_outer_join(self, rhs: Rhs) -> LeftJoin + where + Self: JoinWithImplicitOnClause, + { + self.join_with_implicit_on_clause(rhs, joins::LeftOuter) + } + + /// Alias for [`left_outer_join`]. + /// + /// [`left_outer_join`]: QueryDsl::left_outer_join() + fn left_join(self, rhs: Rhs) -> LeftJoin + where + Self: JoinWithImplicitOnClause, + { + self.left_outer_join(rhs) + } + + /// Adds to the `WHERE` clause of a query. + /// + /// If there is already a `WHERE` clause, the result will be `old AND new`. + /// + /// Note that in order to use this method with columns from different tables, you need to call + /// [`.inner_join`] or [`.left_join`] beforehand. + /// This is because you can only access columns from tables + /// that appear in your query before the call to [`.filter`]. + /// + /// [`.inner_join`]: QueryDsl::inner_join() + /// [`.left_join`]: QueryDsl::left_join() + /// [`.filter`]: QueryDsl::filter() + /// + /// # Example: + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let seans_id = users.filter(name.eq("Sean")).select(id) + /// .first(connection); + /// assert_eq!(Ok(1), seans_id); + /// let tess_id = users.filter(name.eq("Tess")).select(id) + /// .first(connection); + /// assert_eq!(Ok(2), tess_id); + /// # } + /// ``` + #[doc(alias = "where")] + fn filter(self, predicate: Predicate) -> Filter + where + Self: methods::FilterDsl, + { + methods::FilterDsl::filter(self, predicate) + } + + /// Adds to the `WHERE` clause of a query using `OR` + /// + /// If there is already a `WHERE` clause, the result will be `(old OR new)`. + /// Calling `foo.filter(bar).or_filter(baz)` + /// is identical to `foo.filter(bar.or(baz))`. + /// However, the second form is much harder to do dynamically. + /// + /// # Example: + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// # diesel::delete(animals).execute(connection)?; + /// diesel::insert_into(animals) + /// .values(&vec![ + /// (species.eq("cat"), legs.eq(4), name.eq("Sinatra")), + /// (species.eq("dog"), legs.eq(3), name.eq("Fido")), + /// (species.eq("spider"), legs.eq(8), name.eq("Charlotte")), + /// ]) + /// .execute(connection)?; + /// + /// let good_animals = animals + /// .filter(name.eq("Fido")) + /// .or_filter(legs.eq(4)) + /// .select(name) + /// .get_results::>(connection)?; + /// let expected = vec![ + /// Some(String::from("Sinatra")), + /// Some(String::from("Fido")), + /// ]; + /// assert_eq!(expected, good_animals); + /// # Ok(()) + /// # } + /// ``` + #[doc(alias = "where")] + fn or_filter(self, predicate: Predicate) -> OrFilter + where + Self: methods::OrFilterDsl, + { + methods::OrFilterDsl::or_filter(self, predicate) + } + + /// Attempts to find a single record from the given table by primary key. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # use diesel::result::Error::NotFound; + /// # let connection = &mut establish_connection(); + /// let sean = (1, "Sean".to_string()); + /// let tess = (2, "Tess".to_string()); + /// assert_eq!(Ok(sean), users.find(1).first(connection)); + /// assert_eq!(Ok(tess), users.find(2).first(connection)); + /// assert_eq!(Err::<(i32, String), _>(NotFound), users.find(3).first(connection)); + /// # } + /// ``` + fn find(self, id: PK) -> Find + where + Self: methods::FindDsl, + { + methods::FindDsl::find(self, id) + } + + /// Sets the order clause of a query. + /// + /// If there was already an order clause, it will be overridden. See + /// also: + /// [`.desc()`](crate::expression_methods::ExpressionMethods::desc()) + /// and + /// [`.asc()`](crate::expression_methods::ExpressionMethods::asc()) + /// + /// Ordering by multiple columns can be achieved by passing a tuple of those + /// columns. + /// To construct an order clause of an unknown number of columns, + /// see [`QueryDsl::then_order_by`](QueryDsl::then_order_by()) + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// # diesel::sql_query("DELETE FROM users").execute(connection)?; + /// diesel::insert_into(users) + /// .values(&vec![ + /// name.eq("Saul"), + /// name.eq("Steve"), + /// name.eq("Stan"), + /// ]) + /// .execute(connection)?; + /// + /// let ordered_names = users.select(name) + /// .order(name.desc()) + /// .load::(connection)?; + /// assert_eq!(vec!["Steve", "Stan", "Saul"], ordered_names); + /// + /// diesel::insert_into(users).values(name.eq("Stan")).execute(connection)?; + /// + /// let data = users.select((name, id)) + /// .order((name.asc(), id.desc())) + /// .load(connection)?; + /// let expected_data = vec![ + /// (String::from("Saul"), 3), + /// (String::from("Stan"), 6), + /// (String::from("Stan"), 5), + /// (String::from("Steve"), 4), + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + fn order(self, expr: Expr) -> Order + where + Expr: Expression, + Self: methods::OrderDsl, + { + methods::OrderDsl::order(self, expr) + } + + /// Alias for `order` + fn order_by(self, expr: Expr) -> OrderBy + where + Expr: Expression, + Self: methods::OrderDsl, + { + QueryDsl::order(self, expr) + } + + /// Appends to the `ORDER BY` clause of this SQL query. + /// + /// Unlike `.order`, this method will append rather than replace. + /// In other words, + /// `.order_by(foo).order_by(bar)` is equivalent to `.order_by(bar)`. + /// In contrast, + /// `.order_by(foo).then_order_by(bar)` is equivalent to `.order((foo, bar))`. + /// This method is only present on boxed queries. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// # diesel::sql_query("DELETE FROM users").execute(connection)?; + /// diesel::insert_into(users) + /// .values(&vec![ + /// name.eq("Saul"), + /// name.eq("Steve"), + /// name.eq("Stan"), + /// name.eq("Stan"), + /// ]) + /// .execute(connection)?; + /// + /// let data = users.select((name, id)) + /// .order_by(name.asc()) + /// .then_order_by(id.desc()) + /// .load(connection)?; + /// let expected_data = vec![ + /// (String::from("Saul"), 3), + /// (String::from("Stan"), 6), + /// (String::from("Stan"), 5), + /// (String::from("Steve"), 4), + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + fn then_order_by(self, order: Order) -> ThenOrderBy + where + Self: methods::ThenOrderDsl, + { + methods::ThenOrderDsl::then_order_by(self, order) + } + + /// Sets the limit clause of the query. + /// + /// If there was already a limit clause, it will be overridden. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use self::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// # diesel::delete(users).execute(connection)?; + /// # diesel::insert_into(users) + /// # .values(&vec![ + /// # name.eq("Sean"), + /// # name.eq("Bastien"), + /// # name.eq("Pascal"), + /// # ]) + /// # .execute(connection)?; + /// # + /// // Using a limit + /// let limited = users.select(name) + /// .order(id) + /// .limit(1) + /// .load::(connection)?; + /// + /// // Without a limit + /// let no_limit = users.select(name) + /// .order(id) + /// .load::(connection)?; + /// + /// assert_eq!(vec!["Sean"], limited); + /// assert_eq!(vec!["Sean", "Bastien", "Pascal"], no_limit); + /// # Ok(()) + /// # } + /// ``` + fn limit(self, limit: i64) -> Limit + where + Self: methods::LimitDsl, + { + methods::LimitDsl::limit(self, limit) + } + + /// Sets the offset clause of the query. + /// + /// If there was already a offset clause, it will be overridden. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use self::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// # diesel::delete(users).execute(connection)?; + /// # diesel::insert_into(users) + /// # .values(&vec![ + /// # name.eq("Sean"), + /// # name.eq("Bastien"), + /// # name.eq("Pascal"), + /// # ]) + /// # .execute(connection)?; + /// # + /// // Using an offset + /// let offset = users.select(name) + /// .order(id) + /// .limit(2) + /// .offset(1) + /// .load::(connection)?; + /// + /// // No Offset + /// let no_offset = users.select(name) + /// .order(id) + /// .limit(2) + /// .load::(connection)?; + /// + /// assert_eq!(vec!["Bastien", "Pascal"], offset); + /// assert_eq!(vec!["Sean", "Bastien"], no_offset); + /// # Ok(()) + /// # } + /// ``` + fn offset(self, offset: i64) -> Offset + where + Self: methods::OffsetDsl, + { + methods::OffsetDsl::offset(self, offset) + } + + /// Sets the `group by` clause of a query. + /// + /// **Note:** Queries having a `group by` clause require a custom select clause. + /// Use [`QueryDsl::select()`] to specify one. + /// + /// If there was already a group by clause, it will be overridden. + /// Grouping by multiple columns can be achieved by passing a tuple of those + /// columns. + /// + /// Diesel follows postgresql's group by semantic, this means any column + /// appearing in a group by clause is considered to be aggregated. If a + /// primary key is part of the group by clause every column from the + /// corresponding table is considered to be aggregated. Select clauses + /// cannot mix aggregated and non aggregated expressions. + /// + /// For group by clauses containing columns from more than one table it + /// is required to call [`allow_columns_to_appear_in_same_group_by_clause!`] + /// + /// [`allow_columns_to_appear_in_same_group_by_clause!`]: crate::allow_columns_to_appear_in_same_group_by_clause! + /// + /// # Examples + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use crate::schema::{users, posts}; + /// # use diesel::dsl::count; + /// # let connection = &mut establish_connection(); + /// let data = users::table.inner_join(posts::table) + /// .group_by(users::id) + /// .select((users::name, count(posts::id))) + /// # .order_by(users::id.asc()) + /// .load::<(String, i64)>(connection)?; + /// + /// assert_eq!(vec![(String::from("Sean"), 2), (String::from("Tess"), 1)], data); + /// # Ok(()) + /// # } + /// ``` + fn group_by(self, group_by: GB) -> GroupBy + where + GB: Expression, + Self: methods::GroupByDsl, + { + methods::GroupByDsl::group_by(self, group_by) + } + + /// Adds to the `HAVING` clause of a query. + /// + /// # Examples + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use crate::schema::{users, posts}; + /// # use diesel::dsl::count; + /// # let connection = &mut establish_connection(); + /// let data = users::table.inner_join(posts::table) + /// .group_by(users::id) + /// .having(count(posts::id).gt(1)) + /// .select((users::name, count(posts::id))) + /// .load::<(String, i64)>(connection)?; + /// + /// assert_eq!(vec![(String::from("Sean"), 2)], data); + /// # Ok(()) + /// # } + /// ``` + fn having(self, predicate: Predicate) -> Having + where + Self: methods::HavingDsl, + { + methods::HavingDsl::having(self, predicate) + } + + /// Adds `FOR UPDATE` to the end of the select statement. + /// + /// This method is only available for MySQL and PostgreSQL. SQLite does not + /// provide any form of row locking. + /// + /// Additionally, `.for_update` cannot be used on queries with a distinct + /// clause, group by clause, having clause, or any unions. Queries with + /// a `FOR UPDATE` clause cannot be boxed. + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # #[cfg(any(feature = "mysql", feature = "postgres"))] + /// # fn run_test() -> QueryResult<()> { + /// # use crate::schema::users; + /// # let connection = &mut establish_connection(); + /// // Executes `SELECT * FROM users FOR UPDATE` + /// let users_for_update = users::table.for_update().load(connection)?; + /// # let u: Vec<(i32, String)> = users_for_update; + /// # Ok(()) + /// # } + /// # #[cfg(feature = "sqlite")] + /// # fn run_test() -> QueryResult<()> { Ok(()) } + /// ``` + fn for_update(self) -> ForUpdate + where + Self: methods::LockingDsl, + { + methods::LockingDsl::with_lock(self, lock::ForUpdate) + } + + /// Adds `FOR NO KEY UPDATE` to the end of the select statement. + /// + /// This method is only available for PostgreSQL. SQLite does not + /// provide any form of row locking, and MySQL does not support anything + /// finer than row-level locking. + /// + /// Additionally, `.for_no_key_update` cannot be used on queries with a distinct + /// clause, group by clause, having clause, or any unions. Queries with + /// a `FOR NO KEY UPDATE` clause cannot be boxed. + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # #[cfg(feature = "postgres")] + /// # fn run_test() -> QueryResult<()> { + /// # use crate::schema::users; + /// # let connection = &mut establish_connection(); + /// // Executes `SELECT * FROM users FOR NO KEY UPDATE` + /// let users_for_no_key_update = users::table.for_no_key_update().load(connection)?; + /// # let u: Vec<(i32, String)> = users_for_no_key_update; + /// # Ok(()) + /// # } + /// # #[cfg(not(feature = "postgres"))] + /// # fn run_test() -> QueryResult<()> { Ok(()) } + /// ``` + fn for_no_key_update(self) -> ForNoKeyUpdate + where + Self: methods::LockingDsl, + { + methods::LockingDsl::with_lock(self, lock::ForNoKeyUpdate) + } + + /// Adds `FOR SHARE` to the end of the select statement. + /// + /// This method is only available for MySQL and PostgreSQL. SQLite does not + /// provide any form of row locking. + /// + /// Additionally, `.for_share` cannot be used on queries with a distinct + /// clause, group by clause, having clause, or any unions. Queries with + /// a `FOR SHARE` clause cannot be boxed. + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # #[cfg(any(feature = "mysql", feature = "postgres"))] + /// # fn run_test() -> QueryResult<()> { + /// # use crate::schema::users; + /// # let connection = &mut establish_connection(); + /// // Executes `SELECT * FROM users FOR SHARE` + /// let users_for_share = users::table.for_share().load(connection)?; + /// # let u: Vec<(i32, String)> = users_for_share; + /// # Ok(()) + /// # } + /// # #[cfg(feature = "sqlite")] + /// # fn run_test() -> QueryResult<()> { Ok(()) } + /// ``` + fn for_share(self) -> ForShare + where + Self: methods::LockingDsl, + { + methods::LockingDsl::with_lock(self, lock::ForShare) + } + + /// Adds `FOR KEY SHARE` to the end of the select statement. + /// + /// This method is only available for PostgreSQL. SQLite does not + /// provide any form of row locking, and MySQL does not support anything + /// finer than row-level locking. + /// + /// Additionally, `.for_key_share` cannot be used on queries with a distinct + /// clause, group by clause, having clause, or any unions. Queries with + /// a `FOR KEY SHARE` clause cannot be boxed. + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// + /// # #[cfg(feature = "postgres")] + /// # fn run_test() -> QueryResult<()> { + /// # use crate::schema::users; + /// # let connection = &mut establish_connection(); + /// // Executes `SELECT * FROM users FOR KEY SHARE` + /// let users_for_key_share = users::table.for_key_share().load(connection)?; + /// # let u: Vec<(i32, String)> = users_for_key_share; + /// # Ok(()) + /// # } + /// # #[cfg(not(feature = "postgres"))] + /// # fn run_test() -> QueryResult<()> { Ok(()) } + /// ``` + fn for_key_share(self) -> ForKeyShare + where + Self: methods::LockingDsl, + { + methods::LockingDsl::with_lock(self, lock::ForKeyShare) + } + + /// Adds `SKIP LOCKED` to the end of a `FOR UPDATE` clause. + /// + /// This modifier is only supported in PostgreSQL 9.5+ and MySQL 8+. + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # #[cfg(any(feature = "postgres", feature = "mysql"))] + /// # fn run_test() -> QueryResult<()> { + /// # use crate::schema::users; + /// # let connection = &mut establish_connection(); + /// // Executes `SELECT * FROM users FOR UPDATE SKIP LOCKED` + /// let user_skipped_locked = users::table.for_update().skip_locked().load(connection)?; + /// # let u: Vec<(i32, String)> = user_skipped_locked; + /// # Ok(()) + /// # } + /// # #[cfg(feature = "sqlite")] + /// # fn run_test() -> QueryResult<()> { Ok(()) } + /// ``` + fn skip_locked(self) -> SkipLocked + where + Self: methods::ModifyLockDsl, + { + methods::ModifyLockDsl::modify_lock(self, lock::SkipLocked) + } + + /// Adds `NOWAIT` to the end of a `FOR UPDATE` clause. + /// + /// This modifier is only supported in PostgreSQL 9.5+ and MySQL 8+. + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # #[cfg(any(feature = "mysql", feature = "postgres"))] + /// # fn run_test() -> QueryResult<()> { + /// # use crate::schema::users; + /// # let connection = &mut establish_connection(); + /// // Executes `SELECT * FROM users FOR UPDATE NOWAIT` + /// let users_no_wait = users::table.for_update().no_wait().load(connection)?; + /// # let u: Vec<(i32, String)> = users_no_wait; + /// # Ok(()) + /// # } + /// # #[cfg(feature = "sqlite")] + /// # fn run_test() -> QueryResult<()> { Ok(()) } + /// ``` + fn no_wait(self) -> NoWait + where + Self: methods::ModifyLockDsl, + { + methods::ModifyLockDsl::modify_lock(self, lock::NoWait) + } + + /// Boxes the pieces of a query into a single type. + /// + /// This is useful for cases where you want to conditionally modify a query, + /// but need the type to remain the same. The backend must be specified as + /// part of this. It is not possible to box a query and have it be useable + /// on multiple backends. + /// + /// A boxed query will incur a minor performance penalty, as the query builder + /// can no longer be inlined by the compiler. For most applications this cost + /// will be minimal. + /// + /// ### Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # use std::collections::HashMap; + /// # let connection = &mut establish_connection(); + /// # let mut params = HashMap::new(); + /// # params.insert("name", "Sean"); + /// let mut query = users::table.into_boxed(); + /// if let Some(name) = params.get("name") { + /// query = query.filter(users::name.eq(name)); + /// } + /// let users = query.load(connection); + /// # let expected = vec![(1, String::from("Sean"))]; + /// # assert_eq!(Ok(expected), users); + /// # } + /// ``` + /// + /// Diesel queries also have a similar problem to [`Iterator`], where + /// returning them from a function requires exposing the implementation of that + /// function. The [`helper_types`][helper_types] module exists to help with this, + /// but you might want to hide the return type or have it conditionally change. + /// Boxing can achieve both. + /// + /// [helper_types]: crate::helper_types + /// + /// ### Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # let connection = &mut establish_connection(); + /// fn users_by_name(name: &str) -> users::BoxedQuery { + /// users::table.filter(users::name.eq(name)).into_boxed() + /// } + /// + /// assert_eq!(Ok(1), users_by_name("Sean").select(users::id).first(connection)); + /// assert_eq!(Ok(2), users_by_name("Tess").select(users::id).first(connection)); + /// # } + /// ``` + fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB> + where + DB: Backend, + Self: methods::BoxedDsl<'a, DB>, + { + methods::BoxedDsl::internal_into_boxed(self) + } + + /// Wraps this select statement in parenthesis, allowing it to be used + /// as an expression. + /// + /// SQL allows queries such as `foo = (SELECT ...)`, as long as the + /// subselect returns only a single column, and 0 or 1 rows. This method + /// indicates that you expect the query to only return a single value (this + /// will be enforced by adding `LIMIT 1`). + /// + /// The SQL type of this will always be `Nullable`, as the query returns + /// `NULL` if the table is empty or it otherwise returns 0 rows. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # use schema::posts; + /// # let connection = &mut establish_connection(); + /// insert_into(posts::table) + /// .values(posts::user_id.eq(1)) + /// .execute(connection)?; + /// let last_post = posts::table + /// .order(posts::id.desc()); + /// let most_recently_active_user = users.select(name) + /// .filter(id.nullable().eq(last_post.select(posts::user_id).single_value())) + /// .first::(connection)?; + /// assert_eq!("Sean", most_recently_active_user); + /// # Ok(()) + /// # } + /// ``` + fn single_value(self) -> SingleValue + where + Self: methods::SingleValueDsl, + { + methods::SingleValueDsl::single_value(self) + } + + /// Coerce the SQL type of the select clause to it's nullable equivalent. + /// + /// This is useful for writing queries that contain subselects on non null + /// fields comparing them to nullable fields. + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let connection = &mut establish_connection(); + /// table! { + /// users { + /// id -> Integer, + /// name -> Text, + /// } + /// } + /// + /// table! { + /// posts { + /// id -> Integer, + /// by_user -> Nullable, + /// } + /// } + /// + /// allow_tables_to_appear_in_same_query!(users, posts); + /// + /// # let _: Vec<(i32, Option)> = + /// posts::table.filter( + /// posts::by_user.eq_any(users::table.select(users::name).nullable()) + /// ).load(connection)?; + /// # Ok(()) + /// # } + fn nullable(self) -> NullableSelect + where + Self: methods::SelectNullableDsl, + { + methods::SelectNullableDsl::nullable(self) + } +} + +impl QueryDsl for T {} + +/// Methods used to execute queries. +pub trait RunQueryDsl: Sized { + /// Executes the given command, returning the number of rows affected. + /// + /// `execute` is usually used in conjunction with [`insert_into`](crate::insert_into()), + /// [`update`](crate::update()) and [`delete`](crate::delete()) where the number of + /// affected rows is often enough information. + /// + /// When asking the database to return data from a query, [`load`](crate::query_dsl::RunQueryDsl::load()) should + /// probably be used instead. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let inserted_rows = insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(connection)?; + /// assert_eq!(1, inserted_rows); + /// + /// let inserted_rows = insert_into(users) + /// .values(&vec![name.eq("Jim"), name.eq("James")]) + /// .execute(connection)?; + /// assert_eq!(2, inserted_rows); + /// # Ok(()) + /// # } + /// ``` + fn execute(self, conn: &mut Conn) -> QueryResult + where + Conn: Connection, + Self: methods::ExecuteDsl, + { + methods::ExecuteDsl::execute(self, conn) + } + + /// Executes the given query, returning a [`Vec`] with the returned rows. + /// + /// When using the query builder, the return type can be + /// a tuple of the values, or a struct which implements [`Queryable`]. + /// + /// When this method is called on [`sql_query`], + /// the return type can only be a struct which implements [`QueryableByName`] + /// + /// For insert, update, and delete operations where only a count of affected is needed, + /// [`execute`] should be used instead. + /// + /// [`Queryable`]: crate::deserialize::Queryable + /// [`QueryableByName`]: crate::deserialize::QueryableByName + /// [`execute`]: crate::query_dsl::RunQueryDsl::execute() + /// [`sql_query`]: crate::sql_query() + /// + /// ## How to resolve compiler errors while loading data from the database + /// + /// In case you getting uncomprehensable compiler errors while loading data + /// from the database into a type using [`#[derive(Queryable)]`](derive@crate::prelude::Queryable) + /// you might want to consider + /// using [`#[derive(Selectable)]`](derive@crate::prelude::Selectable) + + /// `#[diesel(check_for_backend(YourBackendType))]` + /// to check for mismatching fields at compile time. This drastically improves + /// the quality of the generated error messages by pointing to concrete type mismatches at + /// field level.You need to specify the concrete database backend + /// this specific struct is indented to be used with, as otherwise rustc cannot correctly + /// identify the required deserialization implementation. + /// + /// # Examples + /// + /// ## Returning a single field + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let data = users.select(name) + /// .load::(connection)?; + /// assert_eq!(vec!["Sean", "Tess"], data); + /// # Ok(()) + /// # } + /// ``` + /// + /// ## Returning a tuple + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let data = users + /// .load::<(i32, String)>(connection)?; + /// let expected_data = vec![ + /// (1, String::from("Sean")), + /// (2, String::from("Tess")), + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + /// + /// ## Returning a struct + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// #[derive(Queryable, PartialEq, Debug)] + /// struct User { + /// id: i32, + /// name: String, + /// } + /// + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let data = users + /// .load::(connection)?; + /// let expected_data = vec![ + /// User { id: 1, name: String::from("Sean") }, + /// User { id: 2, name: String::from("Tess") }, + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + fn load<'query, U>(self, conn: &mut Conn) -> QueryResult> + where + Self: LoadQuery<'query, Conn, U>, + { + self.internal_load(conn)?.collect() + } + + /// Executes the given query, returning an [`Iterator`] with the returned rows. + /// + /// The iterator's item is [`QueryResult`](crate::result::QueryResult). + /// + /// You should normally prefer to use [`RunQueryDsl::load`] instead. This method + /// is provided for situations where the result needs to be collected into a different + /// container than a [`Vec`] + /// + /// When using the query builder, the return type can be + /// a tuple of the values, or a struct which implements [`Queryable`]. + /// This type is specified by the first generic type of this function. + /// + /// The second generic type parameter specifies the so called loading mode, + /// which describes how the connection implementation loads data from the database. + /// All connections should provide a implementation for + /// [`DefaultLoadingMode`](crate::connection::DefaultLoadingMode). + /// + /// They may provide additional modes. Checkout the documentation of the concrete + /// connection types for details. For connection implementations that provide + /// more than one loading mode it is **required** to specify this generic parameter. + /// This is currently true for `PgConnection`. + /// + /// When this method is called on [`sql_query`], + /// the return type can only be a struct which implements [`QueryableByName`] + /// + /// For insert, update, and delete operations where only a count of affected is needed, + /// [`execute`] should be used instead. + /// + /// [`Queryable`]: crate::deserialize::Queryable + /// [`QueryableByName`]: crate::deserialize::QueryableByName + /// [`execute`]: crate::query_dsl::RunQueryDsl::execute() + /// [`sql_query`]: crate::sql_query() + /// + /// # Examples + /// + /// ## Returning a single field + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// use diesel::connection::DefaultLoadingMode; + /// + /// let data = users.select(name) + /// .load_iter::(connection)? + /// .collect::>>()?; + /// assert_eq!(vec!["Sean", "Tess"], data); + /// # Ok(()) + /// # } + /// ``` + /// + /// ## Returning a tuple + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// use diesel::connection::DefaultLoadingMode; + /// + /// let data = users + /// .load_iter::<(i32, String), DefaultLoadingMode>(connection)? + /// .collect::>>()?; + /// let expected_data = vec![ + /// (1, String::from("Sean")), + /// (2, String::from("Tess")), + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + /// + /// ## Returning a struct + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// #[derive(Queryable, PartialEq, Debug)] + /// struct User { + /// id: i32, + /// name: String, + /// } + /// + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// use diesel::connection::DefaultLoadingMode; + /// + /// let data = users + /// .load_iter::(connection)? + /// .collect::>>()?; + /// let expected_data = vec![ + /// User { id: 1, name: String::from("Sean") }, + /// User { id: 2, name: String::from("Tess") }, + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + fn load_iter<'conn, 'query: 'conn, U, B>( + self, + conn: &'conn mut Conn, + ) -> QueryResult> + where + U: 'conn, + Self: LoadQuery<'query, Conn, U, B> + 'conn, + { + self.internal_load(conn) + } + + /// Runs the command, and returns the affected row. + /// + /// `Err(NotFound)` will be returned if the query affected 0 rows. You can + /// call `.optional()` on the result of this if the command was optional to + /// get back a `Result>` + /// + /// When this method is called on an insert, update, or delete statement, + /// it will implicitly add a `RETURNING *` to the query, + /// unless a returning clause was already specified. + /// + /// This method only returns the first row that was affected, even if more + /// rows are affected. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # #[cfg(feature = "postgres")] + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::{insert_into, update}; + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// let inserted_row = insert_into(users) + /// .values(name.eq("Ruby")) + /// .get_result(connection)?; + /// assert_eq!((3, String::from("Ruby")), inserted_row); + /// + /// // This will return `NotFound`, as there is no user with ID 4 + /// let update_result = update(users.find(4)) + /// .set(name.eq("Jim")) + /// .get_result::<(i32, String)>(connection); + /// assert_eq!(Err(diesel::NotFound), update_result); + /// # Ok(()) + /// # } + /// # + /// # #[cfg(not(feature = "postgres"))] + /// # fn run_test() -> QueryResult<()> { + /// # Ok(()) + /// # } + /// ``` + fn get_result<'query, U>(self, conn: &mut Conn) -> QueryResult + where + Self: LoadQuery<'query, Conn, U>, + { + match self.internal_load(conn)?.next() { + Some(v) => v, + None => Err(crate::result::Error::NotFound), + } + } + + /// Runs the command, returning an `Vec` with the affected rows. + /// + /// This method is an alias for [`load`], but with a name that makes more + /// sense for insert, update, and delete statements. + /// + /// [`load`]: crate::query_dsl::RunQueryDsl::load() + fn get_results<'query, U>(self, conn: &mut Conn) -> QueryResult> + where + Self: LoadQuery<'query, Conn, U>, + { + self.load(conn) + } + + /// Attempts to load a single record. + /// + /// This method is equivalent to `.limit(1).get_result()` + /// + /// Returns `Ok(record)` if found, and `Err(NotFound)` if no results are + /// returned. If the query truly is optional, you can call `.optional()` on + /// the result of this to get a `Result>`. + /// + /// # Example: + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = &mut establish_connection(); + /// diesel::insert_into(users) + /// .values(&vec![name.eq("Sean"), name.eq("Pascal")]) + /// .execute(connection)?; + /// + /// let first_name = users.order(id).select(name).first(connection); + /// assert_eq!(Ok(String::from("Sean")), first_name); + /// + /// let not_found = users + /// .filter(name.eq("Foo")) + /// .first::<(i32, String)>(connection); + /// assert_eq!(Err(diesel::NotFound), not_found); + /// # Ok(()) + /// # } + /// ``` + fn first<'query, U>(self, conn: &mut Conn) -> QueryResult + where + Self: methods::LimitDsl, + Limit: LoadQuery<'query, Conn, U>, + { + methods::LimitDsl::limit(self, 1).get_result(conn) + } +} + +// Note: We could have a blanket `AsQuery` impl here, which would apply to +// everything we want it to. However, when a query is invalid, we specifically +// want the error to happen on the where clause of the method instead of trait +// resolution. Otherwise our users will get an error saying `<3 page long type>: +// ExecuteDsl is not satisfied` instead of a specific error telling them what +// part of their query is wrong. +impl RunQueryDsl for T where T: Table {} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/nullable_select_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/nullable_select_dsl.rs new file mode 100644 index 000000000..1b16a505c --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/nullable_select_dsl.rs @@ -0,0 +1,14 @@ +/// The `nullable` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However you may need a where clause on this trait +/// to call `nullable` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl::nullable() +pub trait SelectNullableDsl { + /// The return type of `nullable` + type Output; + + /// See the trait documentation + fn nullable(self) -> Self::Output; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/offset_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/offset_dsl.rs new file mode 100644 index 000000000..fa6255463 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/offset_dsl.rs @@ -0,0 +1,28 @@ +use crate::query_source::Table; + +/// The `offset` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `offset` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait OffsetDsl { + /// The type returned by `.offset`. + type Output; + + /// See the trait documentation + fn offset(self, offset: i64) -> Self::Output; +} + +impl OffsetDsl for T +where + T: Table, + T::Query: OffsetDsl, +{ + type Output = ::Output; + + fn offset(self, offset: i64) -> Self::Output { + self.as_query().offset(offset) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/order_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/order_dsl.rs new file mode 100644 index 000000000..c13662b4d --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/order_dsl.rs @@ -0,0 +1,60 @@ +use crate::expression::Expression; +use crate::query_source::Table; + +/// The `order` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `order` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait OrderDsl { + /// The type returned by `.order`. + type Output; + + /// See the trait documentation. + fn order(self, expr: Expr) -> Self::Output; +} + +impl OrderDsl for T +where + Expr: Expression, + T: Table, + T::Query: OrderDsl, +{ + type Output = >::Output; + + fn order(self, expr: Expr) -> Self::Output { + self.as_query().order(expr) + } +} + +/// The `then_order_by` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `then_order_by` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait ThenOrderDsl { + /// The type returned by `.then_order_by`. + type Output; + + /// See the trait documentation. + fn then_order_by(self, expr: Expr) -> Self::Output; +} + +impl ThenOrderDsl for T +where + Expr: Expression, + T: Table, + T::Query: ThenOrderDsl, +{ + type Output = >::Output; + + fn then_order_by(self, expr: Expr) -> Self::Output { + self.as_query().then_order_by(expr) + } +} + +pub trait ValidOrderingForDistinct {} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/positional_order_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/positional_order_dsl.rs new file mode 100644 index 000000000..0b20fcab5 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/positional_order_dsl.rs @@ -0,0 +1,131 @@ +use crate::backend::{Backend, DieselReserveSpecialization}; +use crate::expression::helper_types::{Asc, Desc}; +use crate::query_builder::combination_clause::CombinationClause; +use crate::query_builder::{AstPass, Query, QueryFragment, QueryId}; +use crate::{QueryResult, RunQueryDsl}; + +/// This trait is not yet part of Diesel's public API. It may change in the +/// future without a major version bump. +/// +/// This trait exists as a stop-gap for users who need to order by column position +/// in their queries, so that they are not forced to drop entirely to raw SQL. The +/// arguments to `positional_order_by` are not checked, nor is the select statement +/// forced to be valid. +pub trait PositionalOrderDsl: Sized { + fn positional_order_by(self, expr: Expr) -> PositionalOrderClause { + PositionalOrderClause { + source: self, + expr: expr.into_fragment(), + } + } +} + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct PositionalOrderClause { + source: Source, + expr: Expr, +} + +impl PositionalOrderDsl + for CombinationClause +{ +} + +impl Query for PositionalOrderClause +where + Source: Query, +{ + type SqlType = Source::SqlType; +} + +impl RunQueryDsl for PositionalOrderClause {} + +impl QueryFragment for PositionalOrderClause +where + DB: Backend + DieselReserveSpecialization, + Source: QueryFragment, + Expr: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.source.walk_ast(pass.reborrow())?; + pass.push_sql(" ORDER BY "); + self.expr.walk_ast(pass) + } +} + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct OrderColumn(u32); + +impl QueryFragment for OrderColumn { + fn walk_ast<'b>(&'b self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + pass.push_sql(&self.0.to_string()); + Ok(()) + } +} + +impl From for OrderColumn { + fn from(order: u32) -> Self { + OrderColumn(order) + } +} + +pub trait IntoOrderColumn: Into { + fn asc(self) -> Asc { + Asc { expr: self.into() } + } + fn desc(self) -> Desc { + Desc { expr: self.into() } + } +} + +impl IntoOrderColumn for T where T: Into {} + +pub trait Order: Copy { + type Fragment; + + fn into_fragment(self) -> Self::Fragment; +} + +impl + Copy> Order for T { + type Fragment = OrderColumn; + + fn into_fragment(self) -> Self::Fragment { + self.into() + } +} + +impl Order for Asc { + type Fragment = Asc; + + fn into_fragment(self) -> Self::Fragment { + self + } +} + +impl Order for Desc { + type Fragment = Desc; + + fn into_fragment(self) -> Self::Fragment { + self + } +} + +macro_rules! impl_order_for_all_tuples { + ($( + $unused1:tt { + $(($idx:tt) -> $T:ident, $unused2:ident, $unused3:tt,)+ + } + )+) => { + $( + impl<$($T: Order),+> Order for ($($T,)+) { + type Fragment = ($(<$T as Order>::Fragment,)+); + + fn into_fragment(self) -> Self::Fragment { + ($(self.$idx.into_fragment(),)+) + } + } + )+ + }; +} + +diesel_derives::__diesel_for_each_tuple!(impl_order_for_all_tuples); diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/save_changes_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/save_changes_dsl.rs new file mode 100644 index 000000000..3a56e1ab3 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/save_changes_dsl.rs @@ -0,0 +1,155 @@ +use crate::associations::HasTable; +#[cfg(any(feature = "sqlite", feature = "mysql"))] +use crate::associations::Identifiable; +use crate::connection::Connection; +#[cfg(any(feature = "sqlite", feature = "mysql"))] +use crate::dsl::Find; +#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))] +use crate::dsl::Update; +#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))] +use crate::expression::{is_aggregate, MixedAggregates, ValidGrouping}; +use crate::query_builder::{AsChangeset, IntoUpdateTarget}; +#[cfg(any(feature = "sqlite", feature = "mysql"))] +use crate::query_dsl::methods::{ExecuteDsl, FindDsl}; +#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))] +use crate::query_dsl::{LoadQuery, RunQueryDsl}; +use crate::result::QueryResult; +#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))] +use crate::Table; + +/// A trait defining how to update a record and fetch the updated entry +/// on a certain backend. +/// +/// The only case where it is required to work with this trait is while +/// implementing a new connection type. +/// Otherwise use [`SaveChangesDsl`] +/// +/// For implementing this trait for a custom backend: +/// * The `Changes` generic parameter represents the changeset that should be stored +/// * The `Output` generic parameter represents the type of the response. +pub trait UpdateAndFetchResults: Connection { + /// See the traits documentation. + fn update_and_fetch(&mut self, changeset: Changes) -> QueryResult; +} + +#[cfg(feature = "postgres")] +use crate::pg::PgConnection; + +#[cfg(feature = "postgres")] +impl<'b, Changes, Output> UpdateAndFetchResults for PgConnection +where + Changes: Copy + AsChangeset::Table> + IntoUpdateTarget, + Update: LoadQuery<'b, PgConnection, Output>, + ::AllColumns: ValidGrouping<()>, + <::AllColumns as ValidGrouping<()>>::IsAggregate: + MixedAggregates, +{ + fn update_and_fetch(&mut self, changeset: Changes) -> QueryResult { + crate::update(changeset).set(changeset).get_result(self) + } +} + +#[cfg(feature = "sqlite")] +use crate::sqlite::SqliteConnection; + +#[cfg(feature = "sqlite")] +impl<'b, Changes, Output> UpdateAndFetchResults for SqliteConnection +where + Changes: Copy + Identifiable, + Changes: AsChangeset::Table> + IntoUpdateTarget, + Changes::Table: FindDsl, + Update: ExecuteDsl, + Find: LoadQuery<'b, SqliteConnection, Output>, + ::AllColumns: ValidGrouping<()>, + <::AllColumns as ValidGrouping<()>>::IsAggregate: + MixedAggregates, +{ + fn update_and_fetch(&mut self, changeset: Changes) -> QueryResult { + crate::update(changeset).set(changeset).execute(self)?; + Changes::table().find(changeset.id()).get_result(self) + } +} + +#[cfg(feature = "mysql")] +use crate::mysql::MysqlConnection; + +#[cfg(feature = "mysql")] +impl<'b, Changes, Output> UpdateAndFetchResults for MysqlConnection +where + Changes: Copy + Identifiable, + Changes: AsChangeset::Table> + IntoUpdateTarget, + Changes::Table: FindDsl, + Update: ExecuteDsl, + Find: LoadQuery<'b, MysqlConnection, Output>, + ::AllColumns: ValidGrouping<()>, + <::AllColumns as ValidGrouping<()>>::IsAggregate: + MixedAggregates, +{ + fn update_and_fetch(&mut self, changeset: Changes) -> QueryResult { + crate::update(changeset).set(changeset).execute(self)?; + Changes::table().find(changeset.id()).get_result(self) + } +} + +/// Sugar for types which implement both `AsChangeset` and `Identifiable` +/// +/// On backends which support the `RETURNING` keyword, +/// `foo.save_changes(&conn)` is equivalent to +/// `update(&foo).set(&foo).get_result(&conn)`. +/// On other backends, two queries will be executed. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::animals; +/// # +/// #[derive(Queryable, Debug, PartialEq)] +/// struct Animal { +/// id: i32, +/// species: String, +/// legs: i32, +/// name: Option, +/// } +/// +/// #[derive(AsChangeset, Identifiable)] +/// #[diesel(table_name = animals)] +/// struct AnimalForm<'a> { +/// id: i32, +/// name: &'a str, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use self::animals::dsl::*; +/// # let connection = &mut establish_connection(); +/// let form = AnimalForm { id: 2, name: "Super scary" }; +/// let changed_animal = form.save_changes(connection)?; +/// let expected_animal = Animal { +/// id: 2, +/// species: String::from("spider"), +/// legs: 8, +/// name: Some(String::from("Super scary")), +/// }; +/// assert_eq!(expected_animal, changed_animal); +/// # Ok(()) +/// # } +/// ``` +pub trait SaveChangesDsl { + /// See the trait documentation. + fn save_changes(self, connection: &mut Conn) -> QueryResult + where + Self: Sized, + Conn: UpdateAndFetchResults, + { + connection.update_and_fetch(self) + } +} + +impl SaveChangesDsl for T where + T: Copy + AsChangeset::Table> + IntoUpdateTarget +{ +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/select_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/select_dsl.rs new file mode 100644 index 000000000..60b0d7ac7 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/select_dsl.rs @@ -0,0 +1,33 @@ +use crate::expression::Expression; +use crate::query_source::Table; + +/// The `select` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `select` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait SelectDsl { + // FIXME: Once we've refactored the `impl Expression` on `SelectStatement` + // to not conditionally be `sql_types::Array`, it is probably worthwhile to + // add a `: Expression` bound here. + /// The type returned by `.select` + type Output; + + /// See the trait documentation + fn select(self, selection: Selection) -> Self::Output; +} + +impl SelectDsl for T +where + Selection: Expression, + T: Table, + T::Query: SelectDsl, +{ + type Output = >::Output; + + fn select(self, selection: Selection) -> Self::Output { + self.as_query().select(selection) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/single_value_dsl.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/single_value_dsl.rs new file mode 100644 index 000000000..7230bb57f --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_dsl/single_value_dsl.rs @@ -0,0 +1,34 @@ +use super::methods::LimitDsl; +use crate::dsl::Limit; +use crate::expression::grouped::Grouped; +use crate::expression::subselect::Subselect; +use crate::query_builder::SelectQuery; +use crate::sql_types::IntoNullable; + +/// The `single_value` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `single_value` from generic code. +/// +/// [`QueryDsl`]: crate::QueryDsl +pub trait SingleValueDsl { + /// The type returned by `.single_value`. + type Output; + + /// See the trait documentation. + fn single_value(self) -> Self::Output; +} + +impl SingleValueDsl for T +where + Self: SelectQuery + LimitDsl, + ::SqlType: IntoNullable, +{ + type Output = + Grouped, <::SqlType as IntoNullable>::Nullable>>; + + fn single_value(self) -> Self::Output { + Grouped(Subselect::new(self.limit(1))) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/alias.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/alias.rs new file mode 100644 index 000000000..42f645292 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/alias.rs @@ -0,0 +1,217 @@ +use super::field_alias_mapper::FieldAliasMapper; +use super::{AliasSource, AliasedField}; + +use crate::backend::{sql_dialect, Backend}; +use crate::expression::{Expression, SelectableExpression, ValidGrouping}; +use crate::helper_types::AliasedFields; +use crate::query_builder::{AsQuery, AstPass, FromClause, QueryFragment, QueryId, SelectStatement}; +use crate::query_source::{AppearsInFromClause, Column, Never, QuerySource, Table, TableNotEqual}; +use crate::result::QueryResult; + +use std::marker::PhantomData; + +#[derive(Debug, Clone, Copy, Default)] +/// Represents an alias within diesel's query builder +/// +/// See [`alias!`](crate::alias) for more details. +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + public_fields(source) +)] +pub struct Alias { + /// The inner alias definition + pub(crate) source: S, +} + +impl Alias { + /// Maps a single field of the source table in this alias + pub fn field(&self, field: F) -> AliasedField + where + F: Column

, + { + AliasedField { + _alias_source: PhantomData, + _field: field, + } + } + /// Maps multiple fields of the source table in this alias + /// (takes in tuples and some expressions) + pub fn fields(&self, fields: Fields) -> AliasedFields + where + Fields: FieldAliasMapper, + { + fields.map(self) + } +} + +impl Alias { + #[doc(hidden)] + /// May be used to create an alias. Used by the [`alias!`] macro. + pub const fn new(source: S) -> Self { + Self { source } + } +} + +impl QueryId for Alias +where + Self: 'static, + S: AliasSource, + S::Target: QueryId, +{ + type QueryId = Self; + const HAS_STATIC_QUERY_ID: bool = ::HAS_STATIC_QUERY_ID; +} + +impl QuerySource for Alias +where + Self: Clone, + S: AliasSource, + S::Target: QuerySource, + ::DefaultSelection: FieldAliasMapper, + <::DefaultSelection as FieldAliasMapper>::Out: + SelectableExpression, +{ + type FromClause = Self; + type DefaultSelection = + <::DefaultSelection as FieldAliasMapper>::Out; + + fn from_clause(&self) -> Self::FromClause { + self.clone() + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.fields(self.source.target().default_selection()) + } +} + +impl QueryFragment for Alias +where + S: AliasSource, + DB: Backend, + Self: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + >::walk_ast(self, pass) + } +} + +impl QueryFragment for Alias +where + S: AliasSource, + DB: Backend, + S::Target: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.source.target().walk_ast(pass.reborrow())?; + pass.push_sql(" AS "); + pass.push_identifier(S::NAME)?; + Ok(()) + } +} + +impl AsQuery for Alias +where + S: AliasSource, + S::Target: AsQuery, + Self: QuerySource, + ::DefaultSelection: ValidGrouping<()>, +{ + type SqlType = <::DefaultSelection as Expression>::SqlType; + type Query = SelectStatement>; + + fn as_query(self) -> Self::Query { + SelectStatement::simple(self) + } +} + +/// This trait is used to allow external crates to implement +/// `AppearsInFromClause for Alias` +/// +/// at the table level without running in conflicting impl issues +/// +/// Implementing this at the table level (in the crate that defines the tables) +/// does not result in conflicting impl issues because the table is the struct +/// we're implementing on. +pub trait AliasAppearsInFromClause { + /// Will be passed on to the `impl AppearsInFromClause` + type Count; +} +impl AppearsInFromClause for Alias +where + S: AliasSource, + S::Target: AliasAppearsInFromClause, +{ + type Count = >::Count; +} + +/// This trait is used to allow external crates to implement +/// `AppearsInFromClause> for Alias` +/// +/// at the table level without running in conflicting impl issues +/// +/// Implementing this at the table level (in the crate that defines the tables) +/// does not result in conflicting impl issues because the tables are specified as both first +/// argument of the trait and struct we're implementing on. +pub trait AliasAliasAppearsInFromClause { + /// Will be passed on to the `impl AppearsInFromClause` + type Count; +} +impl AliasAppearsInFromClause> for T1 +where + S2: AliasSource, + T1: AliasAliasAppearsInFromClause, +{ + type Count = >::Count; +} + +/// This trait is used to allow external crates to implement +/// `AppearsInFromClause> for Alias` +/// +/// at the alias level without running in conflicting impl issues +/// +/// Implementing this at the alias level (in the crate that defines the aliases) +/// does not result in conflicting impl issues because the aliases are specified as both first +/// argument of the trait and struct we're implementing on. +/// +/// The corresponding implementation of `AliasAliasAppearsInFromClause` for implementors of this +/// trait is done at the table level, to avoid conflict with the implementation for distinct tables +/// below. +pub trait AliasAliasAppearsInFromClauseSameTable { + /// Will be passed on to the `impl AppearsInFromClause` + type Count; +} + +// impl> AppearsInFromClause for Alias +// where T1 != T2 +impl AliasAppearsInFromClause for T1 +where + T1: TableNotEqual + Table, + T2: Table, + S: AliasSource, +{ + type Count = Never; +} + +// impl AppearsInFromClause> for Alias +// where S1: AliasSource, S2: AliasSource, S1::Table != S2::Table +impl AliasAliasAppearsInFromClause for T2 +where + T1: TableNotEqual + Table, + T2: Table, + S1: AliasSource, + S2: AliasSource, +{ + type Count = Never; +} + +/// To be able to define `helper_types::Fields` with the usual conventions +/// +/// This type is intentionally not publicly exported +#[allow(unreachable_pub)] +pub trait GetAliasSourceFromAlias { + type Source; +} + +impl GetAliasSourceFromAlias for Alias { + type Source = S; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/aliased_field.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/aliased_field.rs new file mode 100644 index 000000000..82c2ecb8b --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/aliased_field.rs @@ -0,0 +1,115 @@ +use diesel_derives::DieselNumericOps; + +use super::{Alias, AliasSource}; + +use crate::backend::Backend; +use crate::dsl; +use crate::expression::{ + is_aggregate, AppearsOnTable, AsExpression, Expression, SelectableExpression, ValidGrouping, +}; +use crate::expression_methods::{EqAll, ExpressionMethods}; +use crate::query_builder::{AstPass, FromClause, QueryFragment, QueryId, SelectStatement}; +use crate::query_source::{AppearsInFromClause, Column, Once, QuerySource}; +use crate::result::QueryResult; +use crate::sql_types; + +use std::marker::PhantomData; + +#[derive(Debug, Clone, Copy, DieselNumericOps)] +/// Represents an aliased field (column) within diesel's query builder +/// +/// See [`alias!`](crate::alias) for more details. +pub struct AliasedField { + pub(super) _alias_source: PhantomData, + pub(super) _field: F, +} + +impl QueryId for AliasedField +where + S: AliasSource + 'static, + S::Target: 'static, + C: Column
+ 'static + QueryId, +{ + type QueryId = Self; + const HAS_STATIC_QUERY_ID: bool = ::HAS_STATIC_QUERY_ID; +} + +impl AppearsOnTable for AliasedField +where + S: AliasSource, + QS: AppearsInFromClause, Count = Once>, + C: Column
, +{ +} + +impl QueryFragment for AliasedField +where + S: AliasSource, + DB: Backend, + C: Column
, +{ + fn walk_ast<'b>(&'b self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + pass.push_identifier(S::NAME)?; + pass.push_sql("."); + pass.push_identifier(C::NAME)?; + Ok(()) + } +} + +impl Expression for AliasedField +where + S: AliasSource, + C: Column
+ Expression, +{ + type SqlType = C::SqlType; +} + +impl SelectableExpression> for AliasedField +where + S: AliasSource, + C: Column
, + Self: AppearsOnTable>, +{ +} + +impl ValidGrouping<()> for AliasedField +where + S: AliasSource, + C: Column
, +{ + type IsAggregate = is_aggregate::No; +} + +impl ValidGrouping> for AliasedField +where + S: AliasSource, + C1: Column
, + C2: Column
, + C2: ValidGrouping, +{ + type IsAggregate = is_aggregate::Yes; +} + +// FIXME: Remove this when overlapping marker traits are stable +impl SelectableExpression>> for AliasedField +where + Self: SelectableExpression + AppearsOnTable>>, + From: QuerySource, +{ +} + +impl EqAll for AliasedField +where + S: AliasSource, + C: Column
, + Self: ExpressionMethods, + ::SqlType: sql_types::SqlType, + T: AsExpression<::SqlType>, + dsl::Eq: Expression, +{ + type Output = dsl::Eq; + + fn eq_all(self, rhs: T) -> Self::Output { + self.eq(rhs) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/dsl_impls.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/dsl_impls.rs new file mode 100644 index 000000000..1b8c4d930 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/dsl_impls.rs @@ -0,0 +1,269 @@ +use super::field_alias_mapper::FieldAliasMapper; +use super::{Alias, AliasSource}; + +use crate::dsl; +#[cfg(feature = "postgres_backend")] +use crate::expression::SelectableExpression; +use crate::expression::{Expression, TypedExpressionType, ValidGrouping}; +use crate::expression_methods::EqAll; +use crate::query_builder::{combination_clause, AsQuery, FromClause, Query, SelectStatement}; +use crate::query_dsl::methods::*; +use crate::query_dsl::{CombineDsl, QueryDsl, RunQueryDsl}; +use crate::query_source::{QuerySource, Table}; + +impl QueryDsl for Alias {} + +impl FilterDsl for Alias +where + Self: AsQuery, + ::Query: FilterDsl, +{ + type Output = dsl::Filter<::Query, Predicate>; + + fn filter(self, predicate: Predicate) -> Self::Output { + self.as_query().filter(predicate) + } +} + +impl SelectDsl for Alias +where + Selection: Expression, + Self: AsQuery, + ::Query: SelectDsl, +{ + type Output = dsl::Select<::Query, Selection>; + + fn select(self, selection: Selection) -> Self::Output { + self.as_query().select(selection) + } +} + +impl FindDsl for Alias +where + S: AliasSource, + S::Target: Table, + ::PrimaryKey: FieldAliasMapper, + <::PrimaryKey as FieldAliasMapper>::Out: EqAll, + Self: FilterDsl< + <<::PrimaryKey as FieldAliasMapper>::Out as EqAll>::Output, + >, +{ + type Output = dsl::Filter< + Self, + <<::PrimaryKey as FieldAliasMapper>::Out as EqAll>::Output, + >; + + fn find(self, id: PK) -> Self::Output { + let primary_key = self.source.target().primary_key(); + let predicate = self.fields(primary_key).eq_all(id); + QueryDsl::filter(self, predicate) + } +} + +impl<'a, S, DB> BoxedDsl<'a, DB> for Alias +where + Alias: QuerySource + AsQuery>>>, + SelectStatement>>: BoxedDsl<'a, DB>, + as QuerySource>::DefaultSelection: + Expression as AsQuery>::SqlType> + ValidGrouping<()>, + as AsQuery>::SqlType: TypedExpressionType, +{ + type Output = dsl::IntoBoxed<'a, SelectStatement>>, DB>; + + fn internal_into_boxed(self) -> Self::Output { + self.as_query().internal_into_boxed() + } +} + +impl CombineDsl for Alias +where + S: AliasSource, + S::Target: Table, + Self: AsQuery, +{ + type Query = ::Query; + + fn union(self, rhs: Rhs) -> dsl::Union + where + Rhs: AsQuery::Query as Query>::SqlType>, + { + combination_clause::CombinationClause::new( + combination_clause::Union, + combination_clause::Distinct, + self.as_query(), + rhs.as_query(), + ) + } + + fn union_all(self, rhs: Rhs) -> dsl::UnionAll + where + Rhs: AsQuery::Query as Query>::SqlType>, + { + combination_clause::CombinationClause::new( + combination_clause::Union, + combination_clause::All, + self.as_query(), + rhs.as_query(), + ) + } + + fn intersect(self, rhs: Rhs) -> dsl::Intersect + where + Rhs: AsQuery::Query as Query>::SqlType>, + { + combination_clause::CombinationClause::new( + combination_clause::Intersect, + combination_clause::Distinct, + self.as_query(), + rhs.as_query(), + ) + } + + fn intersect_all(self, rhs: Rhs) -> dsl::IntersectAll + where + Rhs: AsQuery::Query as Query>::SqlType>, + { + combination_clause::CombinationClause::new( + combination_clause::Intersect, + combination_clause::All, + self.as_query(), + rhs.as_query(), + ) + } + + fn except(self, rhs: Rhs) -> dsl::Except + where + Rhs: AsQuery::Query as Query>::SqlType>, + { + combination_clause::CombinationClause::new( + combination_clause::Except, + combination_clause::Distinct, + self.as_query(), + rhs.as_query(), + ) + } + + fn except_all(self, rhs: Rhs) -> dsl::ExceptAll + where + Rhs: AsQuery::Query as Query>::SqlType>, + { + combination_clause::CombinationClause::new( + combination_clause::Except, + combination_clause::All, + self.as_query(), + rhs.as_query(), + ) + } +} + +#[cfg(feature = "postgres_backend")] +impl DistinctOnDsl for Alias +where + S: AliasSource, + Selection: SelectableExpression, + Self: QuerySource + AsQuery>>, + SelectStatement>: DistinctOnDsl, + ::DefaultSelection: + Expression::SqlType> + ValidGrouping<()>, + ::SqlType: TypedExpressionType, +{ + type Output = dsl::DistinctOn>, Selection>; + + fn distinct_on(self, selection: Selection) -> dsl::DistinctOn { + DistinctOnDsl::distinct_on(self.as_query(), selection) + } +} + +impl OrFilterDsl for Alias +where + Self: AsQuery, + ::Query: OrFilterDsl, +{ + type Output = dsl::OrFilter<::Query, Predicate>; + + fn or_filter(self, predicate: Predicate) -> Self::Output { + self.as_query().or_filter(predicate) + } +} + +impl GroupByDsl for Alias +where + Expr: Expression, + Self: QuerySource + AsQuery>>, + ::DefaultSelection: + Expression::SqlType> + ValidGrouping<()>, + ::SqlType: TypedExpressionType, + ::Query: GroupByDsl, +{ + type Output = dsl::GroupBy>, Expr>; + + fn group_by(self, expr: Expr) -> dsl::GroupBy { + GroupByDsl::group_by(self.as_query(), expr) + } +} + +impl LimitDsl for Alias +where + Self: AsQuery, + ::Query: LimitDsl, +{ + type Output = <::Query as LimitDsl>::Output; + + fn limit(self, limit: i64) -> Self::Output { + self.as_query().limit(limit) + } +} + +impl LockingDsl for Alias +where + Self: QuerySource + AsQuery>>, + ::DefaultSelection: + Expression::SqlType> + ValidGrouping<()>, + ::SqlType: TypedExpressionType, +{ + type Output = > as LockingDsl>::Output; + + fn with_lock(self, lock: Lock) -> Self::Output { + self.as_query().with_lock(lock) + } +} + +impl RunQueryDsl for Alias {} + +impl OffsetDsl for Alias +where + Self: AsQuery, + ::Query: OffsetDsl, +{ + type Output = <::Query as OffsetDsl>::Output; + + fn offset(self, offset: i64) -> Self::Output { + self.as_query().offset(offset) + } +} + +impl OrderDsl for Alias +where + Expr: Expression, + Self: AsQuery, + ::Query: OrderDsl, +{ + type Output = <::Query as OrderDsl>::Output; + + fn order(self, expr: Expr) -> Self::Output { + self.as_query().order(expr) + } +} + +impl ThenOrderDsl for Alias +where + Expr: Expression, + Self: AsQuery, + ::Query: ThenOrderDsl, +{ + type Output = <::Query as ThenOrderDsl>::Output; + + fn then_order_by(self, expr: Expr) -> Self::Output { + self.as_query().then_order_by(expr) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/field_alias_mapper.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/field_alias_mapper.rs new file mode 100644 index 000000000..9c97d4f71 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/field_alias_mapper.rs @@ -0,0 +1,127 @@ +use super::{Alias, AliasSource, AliasedField}; + +use crate::expression; +use crate::query_source::{Column, Table, TableNotEqual}; + +/// Serves to map `Self` to `Alias` +/// +/// Any column `Self` that belongs to `S::Table` will be transformed into `AliasedField` +/// +/// Any column `Self` that does not belong to `S::Table` will be left untouched. +/// +/// This also works with tuples and some expressions. +/// +// This first part is implemented by the `table!` macro. +// The second part is useful to implement the joins, and may be useful to an end-user for +// ergonomics. +pub trait FieldAliasMapper { + /// Output type when mapping `C` to `Alias` + /// + /// If `C: Column
`, `Out = AliasedField` + /// Otherwise, `Out = C` + type Out; + + /// Does the mapping + fn map(self, alias: &Alias) -> Self::Out; +} + +#[doc(hidden)] +/// Allows implementing `FieldAliasMapper` in external crates without running into conflicting impl +/// errors due to https://github.com/rust-lang/rust/issues/20400 +/// +/// We will always have `Self = S::Table` and `CT = C::Table` +pub trait FieldAliasMapperAssociatedTypesDisjointnessTrick { + type Out; + fn map(column: C, alias: &Alias) -> Self::Out; +} +impl FieldAliasMapper for C +where + S: AliasSource, + C: Column, + S::Target: FieldAliasMapperAssociatedTypesDisjointnessTrick, +{ + type Out = >::Out; + + fn map(self, alias: &Alias) -> Self::Out { + >::map( + self, alias, + ) + } +} + +impl FieldAliasMapperAssociatedTypesDisjointnessTrick for TS +where + S: AliasSource, + C: Column
, + TC: Table, + TS: TableNotEqual, +{ + type Out = C; + + fn map(column: C, _alias: &Alias) -> Self::Out { + // left untouched because the tables are different + column + } +} + +macro_rules! field_alias_mapper { + ($( + $Tuple:tt { + $(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)+ + } + )+) => { + $( + impl<_S, $($T,)*> FieldAliasMapper<_S> for ($($T,)*) + where + _S: AliasSource, + $($T: FieldAliasMapper<_S>,)* + { + type Out = ($(<$T as FieldAliasMapper<_S>>::Out,)*); + + fn map(self, alias: &Alias<_S>) -> Self::Out { + ( + $(self.$idx.map(alias),)* + ) + } + } + )* + } +} + +diesel_derives::__diesel_for_each_tuple!(field_alias_mapper); + +// The following `FieldAliasMapper` impls are useful for the generic join implementations. +// More may be added. +impl FieldAliasMapper for AliasedField +where + SNew: AliasSource, +{ + type Out = Self; + + fn map(self, _alias: &Alias) -> Self::Out { + // left untouched because it has already been aliased + self + } +} + +impl FieldAliasMapper for expression::nullable::Nullable +where + F: FieldAliasMapper, +{ + type Out = expression::nullable::Nullable<>::Out>; + + fn map(self, alias: &Alias) -> Self::Out { + expression::nullable::Nullable::new(self.0.map(alias)) + } +} + +impl FieldAliasMapper for expression::grouped::Grouped +where + F: FieldAliasMapper, +{ + type Out = expression::grouped::Grouped<>::Out>; + + fn map(self, alias: &Alias) -> Self::Out { + expression::grouped::Grouped(self.0.map(alias)) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/joins.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/joins.rs new file mode 100644 index 000000000..680cb8764 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/joins.rs @@ -0,0 +1,127 @@ +//! Implements all the traits related to being able to join from/to aliases + +use super::field_alias_mapper::FieldAliasMapper; +use super::{Alias, AliasSource, AliasedField}; + +use crate::expression::{AppearsOnTable, SelectableExpression}; +use crate::query_builder::AsQuery; +use crate::query_dsl::InternalJoinDsl; +use crate::query_source::joins::{ + AppendSelection, Inner, Join, JoinOn, JoinTo, LeftOuter, OnClauseWrapper, ToInnerJoin, +}; +use crate::query_source::{ + AppearsInFromClause, FromClause, Never, Pick, QuerySource, SelectStatement, Table, +}; + +impl JoinTo for Alias +where + T: Table, + S: AliasSource + Default, + S::Target: JoinTo, + >::OnClause: FieldAliasMapper, +{ + type FromClause = >::FromClause; + type OnClause = <>::OnClause as FieldAliasMapper>::Out; + + fn join_target(rhs: T) -> (Self::FromClause, Self::OnClause) { + let (from_clause, on_clause) = >::join_target(rhs); + (from_clause, Self::default().fields(on_clause)) + } +} + +impl JoinTo> for Alias +where + S2: AliasSource, + S: AliasSource + Default, + S::Target: JoinTo>, + >>::OnClause: FieldAliasMapper, +{ + type FromClause = >>::FromClause; + type OnClause = <>>::OnClause as FieldAliasMapper>::Out; + + fn join_target(rhs: Alias) -> (Self::FromClause, Self::OnClause) { + let (from_clause, on_clause) = >>::join_target(rhs); + (from_clause, Self::default().fields(on_clause)) + } +} + +impl JoinTo> for Alias { + type FromClause = Rhs; + type OnClause = On; + + fn join_target(rhs: OnClauseWrapper) -> (Self::FromClause, Self::OnClause) { + (rhs.source, rhs.on) + } +} + +impl + JoinTo, Select, D, W, O, L, Of, G>> for Alias +where + F: QuerySource, + S: AliasSource + Default, + SelectStatement, Select, D, W, O, L, Of, G>: JoinTo>, +{ + type FromClause = SelectStatement, Select, D, W, O, L, Of, G>; + type OnClause = + , Select, D, W, O, L, Of, G> as JoinTo>>::OnClause; + + fn join_target( + rhs: SelectStatement, Select, D, W, O, L, Of, G>, + ) -> (Self::FromClause, Self::OnClause) { + let (_, on_clause) = + diesel::internal::table_macro::SelectStatement::join_target(Self::default()); + (rhs, on_clause) + } +} + +impl ToInnerJoin for Alias { + type InnerJoin = Self; +} + +impl InternalJoinDsl for Alias +where + Self: AsQuery, + ::Query: InternalJoinDsl, +{ + type Output = <::Query as InternalJoinDsl>::Output; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { + self.as_query().join(rhs, kind, on) + } +} + +impl SelectableExpression> for AliasedField +where + Self: AppearsOnTable>, + Self: SelectableExpression, + Left: QuerySource, + Right: AppearsInFromClause, Count = Never> + QuerySource, +{ +} + +impl SelectableExpression> for AliasedField +where + Self: AppearsOnTable>, + Left: AppearsInFromClause> + QuerySource, + Right: AppearsInFromClause> + QuerySource, + (Left::Count, Right::Count): Pick, + Self: SelectableExpression<<(Left::Count, Right::Count) as Pick>::Selection>, +{ +} + +// FIXME: Remove this when overlapping marker traits are stable +impl SelectableExpression> for AliasedField where + Self: SelectableExpression + AppearsOnTable> +{ +} + +impl AppendSelection for Alias +where + Self: QuerySource, +{ + type Output = (::DefaultSelection, Selection); + + fn append_selection(&self, selection: Selection) -> Self::Output { + (self.default_selection(), selection) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/macros.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/macros.rs new file mode 100644 index 000000000..98f50ffb9 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/macros.rs @@ -0,0 +1,161 @@ +/// Declare a new alias for a table +/// +/// This macro creates a value of the type [`Alias`](super::Alias) +/// +/// Example usage +/// ------------- +/// ```rust +/// # include!("../../doctest_setup.rs"); +/// fn main() { +/// use schema::users; +/// let connection = &mut establish_connection(); +/// let (users1, users2) = diesel::alias!(schema::users as user1, schema::users as user2); +/// let res = users1 +/// .inner_join(users2.on(users1.field(users::id).eq(users2.field(users::id)))) +/// .select((users1.fields((users::id, users::name)), users2.field(users::name))) +/// .order_by(users2.field(users::id)) +/// .load::<((i32, String), String)>(connection); +/// assert_eq!( +/// res, +/// Ok(vec![ +/// ((1, "Sean".to_owned()), "Sean".to_owned()), +/// ((2, "Tess".to_owned()), "Tess".to_owned()), +/// ]), +/// ); +/// } +/// ``` +/// +/// +/// Make type expressible +/// --------------------- +/// It may sometimes be useful to declare an alias at the module level, in such a way that the type +/// of a query using it can be expressed (to not declare it anonymously). +/// +/// This can be achieved in the following way +/// ```rust +/// # include!("../../doctest_setup.rs"); +/// use diesel::{query_source::Alias, dsl}; +/// +/// diesel::alias!(schema::users as users_alias: UsersAlias); +/// // or +/// diesel::alias!{ +/// pub const USERS_ALIAS_2: Alias = schema::users as users_alias_2; +/// } +/// +/// fn some_function_that_returns_a_query_fragment( +/// ) -> dsl::InnerJoin> +/// { +/// schema::posts::table.inner_join(users_alias) +/// } +/// # fn main() { +/// # some_function_that_returns_a_query_fragment(); +/// # schema::posts::table.inner_join(USERS_ALIAS_2); +/// # } +/// ``` +/// +/// Note that you may also use this form within a function, in the following way: +/// ```rust +/// # include!("../../doctest_setup.rs"); +/// fn main() { +/// diesel::alias!(schema::users as users_alias: UsersAlias); +/// users_alias.inner_join(schema::posts::table); +/// } +/// ``` +/// +/// Troubleshooting and limitations +/// ------------------------------- +/// If you encounter a **compilation error** where "the trait +/// `AppearsInFromClause>` is not implemented", when trying to use two aliases to +/// the same table within a single query note the following two limitations: +/// - You will need to declare these in a single `alias!` call. +/// - The path to the table module will have to be expressed in the exact same +/// manner. (That is, you can do `alias!(schema::users as user1, schema::users as user2)` +/// or `alias!(users as user1, users as user2)`, but not +/// `alias!(schema::users as user1, users as user2)`) +#[macro_export] +macro_rules! alias { + ($($($table: ident)::+ as $alias: ident),* $(,)?) => {{ + $crate::alias!(NoConst $($($table)::+ as $alias: $alias,)*); + ($($crate::query_source::Alias::<$alias>::default()),*) + }}; + ($($($table: ident)::+ as $alias_name: ident: $alias_ty: ident),* $(,)?) => { + $crate::alias! { + $( + pub const $alias_name: Alias<$alias_ty> = $($table)::+ as $alias_name; + )* + } + }; + ($($vis: vis const $const_name: ident: Alias<$alias_ty: ident> = $($table: ident)::+ as $alias_sql_name: ident);* $(;)?) => { + $crate::alias!(NoConst $($($table)::+ as $alias_sql_name: $vis $alias_ty,)*); + $( + #[allow(non_upper_case_globals)] + $vis const $const_name: $crate::query_source::Alias::<$alias_ty> = + $crate::query_source::Alias::new($alias_ty { table: $($table)::+::table }); + + #[allow(non_camel_case_types)] + $vis type $const_name = $crate::query_source::Alias::<$alias_ty>; + )* + }; + (NoConst $($($table: ident)::+ as $alias_sql_name: ident: $vis: vis $alias_ty: ident),* $(,)?) => { + $( + #[allow(non_camel_case_types)] + #[derive(Debug, Clone, Copy)] + $vis struct $alias_ty { + table: $($table)::+::table, + } + + impl $crate::query_source::AliasSource for $alias_ty { + const NAME: &'static str = stringify!($alias_sql_name); + type Target = $($table)::+::table; + fn target(&self) -> &Self::Target { &self.table } + } + + impl ::std::default::Default for $alias_ty { + fn default() -> Self { + Self { table: $($table)::+::table } + } + } + + // impl AppearsInFromClause> for Alias<$alias> + impl $crate::internal::alias_macro::AliasAliasAppearsInFromClauseSameTable<$alias_ty, $($table)::+::table> for $alias_ty { + type Count = $crate::query_source::Once; + } + )* + $crate::__internal_alias_helper!($(table_ty = $($table)::+::table, table_tt = ($($table)::+), alias_ty = $alias_ty, alias_sql_name = $alias_sql_name;)*); + }; +} + +#[macro_export] +#[doc(hidden)] +/// This only exists to hide internals from the doc +// The `($left_sql_name) != ($right_sql_name)` condition is not perfect because a user could still +// cause runtime errors by declaring two aliases to different tables with the same name then use +// them in the same query, but that would almost have to be voluntary, so this alone should prevent +// most mistakes. +macro_rules! __internal_alias_helper { + ( + table_ty = $left_table_ty: ty, table_tt = $left_table_tt: tt, alias_ty = $left_alias: ident, alias_sql_name = $left_sql_name: ident; + $(table_ty = $right_table_ty: ty, table_tt = $right_table_tt: tt, alias_ty = $right_alias: ident, alias_sql_name = $right_sql_name: ident;)+ + ) => { + $( + $crate::static_cond!{if ($left_table_tt) == ($right_table_tt) { + $crate::static_cond!{if ($left_sql_name) != ($right_sql_name) { + impl $crate::internal::alias_macro::AliasAliasAppearsInFromClauseSameTable<$left_alias, $left_table_ty> + for $right_alias + { + type Count = $crate::query_source::Never; + } + impl $crate::internal::alias_macro::AliasAliasAppearsInFromClauseSameTable<$right_alias, $left_table_ty> + for $left_alias + { + type Count = $crate::query_source::Never; + } + }} + }} + )* + $crate::__internal_alias_helper!($(table_ty = $right_table_ty, table_tt = $right_table_tt, alias_ty = $right_alias, alias_sql_name = $right_sql_name;)+); + }; + + (table_ty = $left_table_ty: ty, table_tt = $left_table_tt: tt, alias_ty = $left_alias: ident, alias_sql_name = $left_sql_name: ident;) => {}; + () => {}; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/mod.rs new file mode 100644 index 000000000..c8f0c45af --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/aliasing/mod.rs @@ -0,0 +1,46 @@ +//! Everything related to table aliasing +//! +//! See [`alias!`](crate::alias!) for more details + +mod alias; +mod aliased_field; +mod dsl_impls; +mod field_alias_mapper; +mod joins; +mod macros; + +// This is reexported from the parent module +#[allow(unreachable_pub)] +pub use alias::Alias; +// This is reexported from the parent module +#[allow(unreachable_pub)] +#[doc(hidden)] // This is used by the table macro +pub use alias::{ + AliasAliasAppearsInFromClause, AliasAliasAppearsInFromClauseSameTable, AliasAppearsInFromClause, +}; +#[allow(unreachable_pub)] +pub use aliased_field::AliasedField; +#[allow(unreachable_pub)] +#[doc(hidden)] // This is used by the table macro +pub use field_alias_mapper::{FieldAliasMapper, FieldAliasMapperAssociatedTypesDisjointnessTrick}; + +pub(crate) use alias::GetAliasSourceFromAlias; + +/// Types created by the `alias!` macro that serve to distinguish between aliases implement +/// this trait. +/// +/// In order to be able to implement within diesel a lot of traits on what will represent the alias, +/// we cannot use directly that new type within the query builder. Instead, we will use `Alias`, +/// where `S: AliasSource`. +/// +/// This trait should never be implemented by an end-user directly. +pub trait AliasSource { + /// The name of this alias in the query + const NAME: &'static str; + /// The table the alias maps to + type Target; + /// Obtain the table from the source + /// + /// (used by Diesel to implement some traits) + fn target(&self) -> &Self::Target; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_source/joins.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/joins.rs new file mode 100644 index 000000000..426dec14f --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/joins.rs @@ -0,0 +1,512 @@ +use super::{AppearsInFromClause, Plus}; +use crate::backend::Backend; +use crate::backend::DieselReserveSpecialization; +use crate::expression::grouped::Grouped; +use crate::expression::nullable::Nullable; +use crate::prelude::*; +use crate::query_builder::*; +use crate::query_dsl::InternalJoinDsl; +use crate::sql_types::BoolOrNullableBool; +use crate::util::TupleAppend; + +/// A query source representing the join between two tables +pub struct Join { + left: FromClause, + right: FromClause, + kind: Kind, +} + +impl Clone for Join +where + Left: QuerySource, + FromClause: Clone, + Right: QuerySource, + FromClause: Clone, + Kind: Clone, +{ + fn clone(&self) -> Self { + Self { + left: self.left.clone(), + right: self.right.clone(), + kind: self.kind.clone(), + } + } +} + +impl Copy for Join +where + Left: QuerySource, + FromClause: Copy, + Right: QuerySource, + FromClause: Copy, + Kind: Copy, +{ +} + +impl std::fmt::Debug for Join +where + Left: QuerySource, + FromClause: std::fmt::Debug, + Right: QuerySource, + FromClause: std::fmt::Debug, + Kind: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Join") + .field("left", &self.left) + .field("right", &self.right) + .field("kind", &self.kind) + .finish() + } +} + +impl QueryId for Join +where + Left: QueryId + QuerySource + 'static, + Right: QueryId + QuerySource + 'static, + Kind: QueryId, +{ + type QueryId = Join; + + const HAS_STATIC_QUERY_ID: bool = + Left::HAS_STATIC_QUERY_ID && Right::HAS_STATIC_QUERY_ID && Kind::HAS_STATIC_QUERY_ID; +} + +#[derive(Debug, Clone, Copy, QueryId)] +#[doc(hidden)] +/// A query source representing the join between two tables with an explicit +/// `ON` given. `Join` should usually be referenced instead, as all "type +/// safety" traits are implemented in terms of `Join` implementing them. +pub struct JoinOn { + join: Join, + on: On, +} + +impl Join +where + Left: QuerySource, + Right: QuerySource, +{ + pub(crate) fn new(left: Left, right: Right, kind: Kind) -> Self { + Join { + left: FromClause::new(left), + right: FromClause::new(right), + kind, + } + } + + pub(crate) fn on(self, on: On) -> JoinOn { + JoinOn { join: self, on: on } + } +} + +impl QuerySource for Join +where + Left: QuerySource + AppendSelection, + Right: QuerySource, + Left::Output: AppearsOnTable, + Self: Clone, +{ + type FromClause = Self; + // combining two valid selectable expressions for both tables will always yield a + // valid selectable expressions for the whole join, so no need to check that here + // again. These checked turned out to be quite expensive in terms of compile time + // so we use a wrapper type to just skip the check and forward other more relevant + // trait implementations to the inner type + // + // See https://github.com/diesel-rs/diesel/issues/3223 for details + type DefaultSelection = self::private::SkipSelectableExpressionBoundCheckWrapper; + + fn from_clause(&self) -> Self::FromClause { + self.clone() + } + + fn default_selection(&self) -> Self::DefaultSelection { + self::private::SkipSelectableExpressionBoundCheckWrapper( + self.left + .source + .append_selection(self.right.source.default_selection()), + ) + } +} + +impl QuerySource for Join +where + Left: QuerySource + AppendSelection>, + Right: QuerySource, + Left::Output: AppearsOnTable, + Self: Clone, +{ + type FromClause = Self; + // combining two valid selectable expressions for both tables will always yield a + // valid selectable expressions for the whole join, so no need to check that here + // again. These checked turned out to be quite expensive in terms of compile time + // so we use a wrapper type to just skip the check and forward other more relevant + // trait implementations to the inner type + // + // See https://github.com/diesel-rs/diesel/issues/3223 for details + type DefaultSelection = self::private::SkipSelectableExpressionBoundCheckWrapper; + + fn from_clause(&self) -> Self::FromClause { + self.clone() + } + + fn default_selection(&self) -> Self::DefaultSelection { + self::private::SkipSelectableExpressionBoundCheckWrapper( + self.left + .source + .append_selection(self.right.source.default_selection().nullable()), + ) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct OnKeyword; + +impl nodes::MiddleFragment for OnKeyword { + fn push_sql(&self, mut pass: AstPass<'_, '_, DB>) { + pass.push_sql(" ON "); + } +} + +impl QuerySource for JoinOn +where + Join: QuerySource, + On: AppearsOnTable + Clone, + On::SqlType: BoolOrNullableBool, + Join::DefaultSelection: SelectableExpression, +{ + type FromClause = Grouped>; + type DefaultSelection = Join::DefaultSelection; + + fn from_clause(&self) -> Self::FromClause { + Grouped(nodes::InfixNode::new( + self.join.from_clause(), + self.on.clone(), + OnKeyword, + )) + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.join.default_selection() + } +} + +impl QueryFragment for Join +where + DB: Backend + DieselReserveSpecialization, + Left: QuerySource, + Left::FromClause: QueryFragment, + Right: QuerySource, + Right::FromClause: QueryFragment, + Kind: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.left.from_clause.walk_ast(out.reborrow())?; + self.kind.walk_ast(out.reborrow())?; + out.push_sql(" JOIN "); + self.right.from_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} + +/// Indicates that two tables can be joined without an explicit `ON` clause. +/// +/// Implementations of this trait are generated by invoking [`joinable!`]. +/// Implementing this trait means that you can call +/// `left_table.inner_join(right_table)`, without supplying the `ON` clause +/// explicitly. To join two tables which do not implement this trait, you will +/// need to call [`.on`]. +/// +/// See [`joinable!`] and [`inner_join`] for usage examples. +/// +/// [`joinable!`]: crate::joinable! +/// [`.on`]: crate::query_dsl::JoinOnDsl::on() +/// [`inner_join`]: crate::query_dsl::QueryDsl::inner_join() +pub trait JoinTo { + #[doc(hidden)] + type FromClause; + #[doc(hidden)] + type OnClause; + #[doc(hidden)] + fn join_target(rhs: T) -> (Self::FromClause, Self::OnClause); +} + +#[doc(hidden)] +/// Used to ensure the sql type of `left.join(mid).join(right)` is +/// `(Left, Mid, Right)` and not `((Left, Mid), Right)`. This needs +/// to be separate from `TupleAppend` because we still want to keep +/// the column lists (which are tuples) separate. +pub trait AppendSelection { + type Output; + + fn append_selection(&self, selection: Selection) -> Self::Output; +} + +impl AppendSelection for T { + type Output = (T::AllColumns, Selection); + + fn append_selection(&self, selection: Selection) -> Self::Output { + (T::all_columns(), selection) + } +} + +impl AppendSelection for Join +where + Left: QuerySource, + Mid: QuerySource, + Self: QuerySource, + ::DefaultSelection: TupleAppend, +{ + type Output = <::DefaultSelection as TupleAppend>::Output; + + fn append_selection(&self, selection: Selection) -> Self::Output { + self.default_selection().tuple_append(selection) + } +} + +impl AppendSelection for JoinOn +where + Join: AppendSelection, +{ + type Output = Join::Output; + + fn append_selection(&self, selection: Selection) -> Self::Output { + self.join.append_selection(selection) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, Default, QueryId)] +pub struct Inner; + +impl QueryFragment for Inner +where + DB: Backend + DieselReserveSpecialization, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql(" INNER"); + Ok(()) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, Default, QueryId)] +pub struct LeftOuter; + +impl QueryFragment for LeftOuter +where + DB: Backend + DieselReserveSpecialization, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.push_sql(" LEFT OUTER"); + Ok(()) + } +} + +impl JoinTo for Join +where + Left: JoinTo + QuerySource, + Mid: QuerySource, +{ + type FromClause = >::FromClause; + type OnClause = Left::OnClause; + + fn join_target(rhs: Right) -> (Self::FromClause, Self::OnClause) { + Left::join_target(rhs) + } +} + +impl JoinTo for JoinOn +where + Join: JoinTo, +{ + type FromClause = Join::FromClause; + type OnClause = Join::OnClause; + + fn join_target(rhs: Right) -> (Self::FromClause, Self::OnClause) { + Join::join_target(rhs) + } +} + +impl AppearsInFromClause for Join +where + Left: AppearsInFromClause + QuerySource, + Right: AppearsInFromClause + QuerySource, + Left::Count: Plus, +{ + type Count = >::Output; +} + +impl AppearsInFromClause for JoinOn +where + Join: AppearsInFromClause, +{ + type Count = Join::Count; +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct OnClauseWrapper { + pub(crate) source: Source, + pub(crate) on: On, +} + +impl OnClauseWrapper { + pub fn new(source: Source, on: On) -> Self { + OnClauseWrapper { source, on } + } +} + +impl JoinTo> for Lhs +where + Lhs: Table, +{ + type FromClause = Rhs; + type OnClause = On; + + fn join_target(rhs: OnClauseWrapper) -> (Self::FromClause, Self::OnClause) { + (rhs.source, rhs.on) + } +} + +impl JoinTo for OnClauseWrapper +where + Lhs: JoinTo, +{ + type FromClause = >::FromClause; + type OnClause = >::OnClause; + + fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) { + >::join_target(rhs) + } +} + +impl InternalJoinDsl for OnClauseWrapper +where + Lhs: InternalJoinDsl, +{ + type Output = OnClauseWrapper<>::Output, On2>; + + fn join(self, rhs: Rhs, kind: Kind, on: On1) -> Self::Output { + OnClauseWrapper { + source: self.source.join(rhs, kind, on), + on: self.on, + } + } +} + +impl QueryDsl for OnClauseWrapper {} + +#[doc(hidden)] +/// Convert any joins in a `FROM` clause into an inner join. +/// +/// This trait is used to determine whether +/// `Nullable: SelectableExpression`. We consider it to be +/// selectable if `T: SelectableExpression`. Since `SomeJoin` +/// may be deeply nested, we need to recursively change any appearances of +/// `LeftOuter` to `Inner` in order to perform this check. +pub trait ToInnerJoin { + type InnerJoin; +} + +impl ToInnerJoin for Join +where + Left: ToInnerJoin + QuerySource, + Left::InnerJoin: QuerySource, + Right: ToInnerJoin + QuerySource, + Right::InnerJoin: QuerySource, +{ + type InnerJoin = Join; +} + +impl ToInnerJoin for JoinOn +where + Join: ToInnerJoin, +{ + type InnerJoin = JoinOn; +} + +impl ToInnerJoin for SelectStatement> +where + From: ToInnerJoin + QuerySource, + From::InnerJoin: QuerySource, +{ + type InnerJoin = SelectStatement>; +} + +impl ToInnerJoin for T { + type InnerJoin = T; +} + +mod private { + use crate::backend::Backend; + use crate::expression::{Expression, ValidGrouping}; + use crate::query_builder::{AstPass, QueryFragment, SelectClauseExpression}; + use crate::{AppearsOnTable, QueryResult, SelectableExpression}; + + #[derive(Debug, crate::query_builder::QueryId, Copy, Clone)] + pub struct SkipSelectableExpressionBoundCheckWrapper(pub(super) T); + + impl QueryFragment for SkipSelectableExpressionBoundCheckWrapper + where + T: QueryFragment, + DB: Backend, + { + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + self.0.walk_ast(pass) + } + } + + // The default select clause is only valid for no group by clause + // anyway so we can just skip the recursive check here + impl ValidGrouping<()> for SkipSelectableExpressionBoundCheckWrapper { + type IsAggregate = crate::expression::is_aggregate::No; + } + + // This needs to use the expression impl + impl SelectClauseExpression for SkipSelectableExpressionBoundCheckWrapper + where + T: SelectClauseExpression, + { + type Selection = T::Selection; + + type SelectClauseSqlType = T::SelectClauseSqlType; + } + + // The default select clause for joins is always valid assuming that + // the default select clause of all involved query sources is + // valid too. We can skip the recursive check here. + // This is the main optimization. + impl SelectableExpression for SkipSelectableExpressionBoundCheckWrapper where + Self: AppearsOnTable + { + } + + impl AppearsOnTable for SkipSelectableExpressionBoundCheckWrapper where + Self: Expression + { + } + + // Expression must recurse the whole expression + // as this is required for the return type of the query + impl Expression for SkipSelectableExpressionBoundCheckWrapper + where + T: Expression, + { + type SqlType = T::SqlType; + } + + impl crate::util::TupleAppend + for SkipSelectableExpressionBoundCheckWrapper + where + T: crate::util::TupleAppend, + { + // We're re-wrapping after anyway + type Output = T::Output; + + fn tuple_append(self, right: Selection) -> Self::Output { + self.0.tuple_append(right) + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_source/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/mod.rs new file mode 100644 index 000000000..70ae0b9b2 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/mod.rs @@ -0,0 +1,207 @@ +//! Types related to describing schema, and interactions between tables. +//! +//! Most traits in this module are derived or generated by [`table!`]. +//! +//! [`table!`]: crate::table! + +pub(crate) mod aliasing; +pub(crate) mod joins; +mod peano_numbers; + +use crate::expression::{Expression, SelectableExpression, ValidGrouping}; +use crate::query_builder::*; + +pub use self::aliasing::{Alias, AliasSource, AliasedField}; +pub use self::joins::JoinTo; +pub use self::peano_numbers::*; +pub(crate) use self::private::Pick; + +/// Represents a type which can appear in the `FROM` clause. Apps should not +/// need to concern themselves with this trait. +/// +/// Types which implement this trait include: +/// - Tables generated by the `table!` macro +/// - Internal structs which represent joins +/// - A select statement which has had no query builder methods called on it, +/// other than those which can affect the from clause. +pub trait QuerySource { + /// The type returned by `from_clause` + type FromClause; + /// The type returned by `default_selection` + type DefaultSelection: SelectableExpression; + + /// The actual `FROM` clause of this type. This is typically only called in + /// `QueryFragment` implementations. + // from here is something different than from in rust + // as this literally refers to SQL from. + #[allow(clippy::wrong_self_convention)] + fn from_clause(&self) -> Self::FromClause; + /// The default select clause of this type, which should be used if no + /// select clause was explicitly specified. This should always be a tuple of + /// all the desired columns, not `star` + fn default_selection(&self) -> Self::DefaultSelection; +} + +/// A column on a database table. Types which implement this trait should have +/// been generated by the [`table!` macro](crate::table!). +pub trait Column: Expression { + /// The table which this column belongs to + type Table: Table; + + /// The name of this column + const NAME: &'static str; +} + +/// A SQL database table. Types which implement this trait should have been +/// generated by the [`table!` macro](crate::table!). +pub trait Table: QuerySource + AsQuery + Sized { + /// The type returned by `primary_key` + type PrimaryKey: SelectableExpression + ValidGrouping<()>; + /// The type returned by `all_columns` + type AllColumns: SelectableExpression + ValidGrouping<()>; + + /// Returns the primary key of this table. + /// + /// If the table has a composite primary key, this will be a tuple. + fn primary_key(&self) -> Self::PrimaryKey; + /// Returns a tuple of all columns belonging to this table. + fn all_columns() -> Self::AllColumns; +} + +/// Determines how many times `Self` appears in `QS` +/// +/// This trait is primarily used to determine whether or not a column is +/// selectable from a given from clause. A column can be selected if its table +/// appears in the from clause *exactly once*. +/// +/// We do not allow the same table to appear in a query multiple times in any +/// context where referencing that table would be ambiguous (depending on the +/// context and backend being used, this may or may not be something that would +/// otherwise result in a runtime error). +#[diagnostic::on_unimplemented( + note = "double check that `{QS}` and `{Self}` appear in the same `allow_tables_to_appear_in_same_query!` \ncall if both are tables", + note = "double check that any two aliases to the same table in `{QS}` and `{Self}` appear in the same `alias!` call" +)] +pub trait AppearsInFromClause { + /// How many times does `Self` appear in `QS`? + type Count; +} + +/// Allows Diesel to implement some internal traits for two tables that are distinct. +/// +/// (Notably, a bunch of [`AppearsInFromClause`] for the tables and their aliases.) +/// +/// This trait is implemented by the [`allow_tables_to_appear_in_same_query!`] macro. +/// +/// Troubleshooting +/// --------------- +/// If you encounter an error mentioning this trait, it could mean that either: +/// - You are attempting to use tables that don't belong to the same database together +/// (no call to [`allow_tables_to_appear_in_same_query!`] was made) +/// - You are attempting to use two aliases to the same table in the same query, but they +/// were declared through different calls to [`alias!`](crate::alias) +#[diagnostic::on_unimplemented( + note = "double check that `{T}` and `{Self}` appear in the same `allow_tables_to_appear_in_same_query!` \ncall if both are tables" +)] +pub trait TableNotEqual: Table {} + +impl AppearsInFromClause for T1 +where + T1: TableNotEqual + Table, + T2: Table, +{ + type Count = Never; +} + +pub(crate) mod private { + use super::{Never, Once}; + + /// Used to determine which of two from clauses contains a given table. + /// + /// This trait can be used to emulate "or" conditions in where clauses when + /// we want a trait to be implemented with one of two type parameters. + /// + /// For example, if we wanted to write: + /// + /// ```rust,ignore + /// where + /// T: SelectableExpression | SelectableExpression, + /// ``` + /// + /// we can emulate this by writing: + /// + /// ```rust,ignore + /// where + /// Left: AppearsInFromClause, + /// Right: AppearsInFromClause, + /// (Left::Count, Right::Count): Pick, + /// T: SelectableExpression< + /// <(Left::Count, Right::Count) as Pick>::Selection, + /// >, + /// ``` + /// + /// In order to acquire the counts in the first place, we must already know + /// the table we're searching for. + #[doc(hidden)] // This is used as part of the `table!` implementation + pub trait Pick { + /// The selected type. + /// + /// For `(Once, Never)` this type will be `Left`. For `(Never, Once)`, this type will be + /// `Right` + type Selection; + } + + impl Pick for (Once, Never) { + type Selection = Left; + } + + impl Pick for (Never, Once) { + type Selection = Right; + } +} + +#[doc(hidden)] +#[allow( + non_camel_case_types, + missing_debug_implementations, + missing_copy_implementations +)] +/// Everything in this module is here to give something more helpful than: +/// +/// > (Never, Never): Pick is not satisfied +/// +/// Any of these impls can be deleted if they are getting in the way of +/// other functionality. Any code which is using these impls is already +/// failing to compile. +mod impls_which_are_only_here_to_improve_error_messages { + use super::*; + + pub struct this_table_doesnt_appear_in_the_from_clause_of_your_query; + + impl Pick for (Never, Never) { + type Selection = this_table_doesnt_appear_in_the_from_clause_of_your_query; + } + + pub struct this_table_appears_in_your_query_more_than_once_and_must_be_aliased; + + impl Pick for (MoreThanOnce, OtherCount) { + type Selection = this_table_appears_in_your_query_more_than_once_and_must_be_aliased; + } + + impl Pick for (Never, MoreThanOnce) { + type Selection = this_table_appears_in_your_query_more_than_once_and_must_be_aliased; + } + + impl Pick for (Once, MoreThanOnce) { + type Selection = this_table_appears_in_your_query_more_than_once_and_must_be_aliased; + } +} + +/// Max length for columns of type Char/Varchar... +/// +/// If a given column has a such constraint, this trait will be implemented and specify that +/// length. +pub trait SizeRestrictedColumn: Column { + /// Max length of that column + const MAX_LENGTH: usize; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/query_source/peano_numbers.rs b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/peano_numbers.rs new file mode 100644 index 000000000..0b9cd0686 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/query_source/peano_numbers.rs @@ -0,0 +1,45 @@ +//! A simple implementation of peano numbers. +//! +//! This is used to enforce that columns can only be selected from a given from +//! clause if their table appears exactly one time. + +/// A table never appears in the from clause. +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct Never; + +/// A table appears in the from clause exactly one time. +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct Once; + +/// A table appears in the from clause two or more times. +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct MoreThanOnce; + +/// Add two peano numbers together. +/// +/// This is used to determine the number of times a table appears in a from +/// clause when the from clause contains a join. +pub trait Plus { + /// The result of adding these numbers together + type Output; +} + +impl Plus for Never { + type Output = T; +} + +impl Plus for MoreThanOnce { + type Output = Self; +} + +impl Plus for Once { + type Output = Self; +} + +impl Plus for Once { + type Output = MoreThanOnce; +} + +impl Plus for Once { + type Output = MoreThanOnce; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/r2d2.rs b/collector/compile-benchmarks/diesel-2.2.10/src/r2d2.rs new file mode 100644 index 000000000..8abca2f38 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/r2d2.rs @@ -0,0 +1,598 @@ +//! Connection pooling via r2d2. +//! +//! Note: This module requires enabling the `r2d2` feature +//! +//! # Example +//! +//! The below snippet is a contrived example emulating a web application, +//! where one would first initialize the pool in the `main()` function +//! (at the start of a long-running process). One would then pass this +//! pool struct around as shared state, which, here, we've emulated using +//! threads instead of routes. +//! +//! ```rust +//! # include!("doctest_setup.rs"); +//! use diesel::prelude::*; +//! use diesel::r2d2::ConnectionManager; +//! # use diesel::r2d2::CustomizeConnection; +//! # use diesel::r2d2::Error as R2D2Error; +//! use diesel::r2d2::Pool; +//! use diesel::result::Error; +//! use std::thread; +//! +//! # #[derive(Copy, Clone, Debug)] +//! # pub struct SetupUserTableCustomizer; +//! # +//! # impl CustomizeConnection for SetupUserTableCustomizer +//! # { +//! # fn on_acquire(&self, conn: &mut DbConnection) -> Result<(), R2D2Error> { +//! # setup_database(conn); +//! # Ok(()) +//! # } +//! # } +//! +//! pub fn get_connection_pool() -> Pool> { +//! let url = database_url_for_env(); +//! let manager = ConnectionManager::::new(url); +//! // Refer to the `r2d2` documentation for more methods to use +//! // when building a connection pool +//! Pool::builder() +//! # .max_size(1) +//! .test_on_check_out(true) +//! # .connection_customizer(Box::new(SetupUserTableCustomizer)) +//! .build(manager) +//! .expect("Could not build connection pool") +//! } +//! +//! pub fn create_user(conn: &mut DbConnection, user_name: &str) -> Result { +//! use schema::users::dsl::*; +//! +//! diesel::insert_into(users) +//! .values(name.eq(user_name)) +//! .execute(conn) +//! } +//! +//! fn main() { +//! let pool = get_connection_pool(); +//! let mut threads = vec![]; +//! let max_users_to_create = 1; +//! +//! for i in 0..max_users_to_create { +//! let pool = pool.clone(); +//! threads.push(thread::spawn({ +//! move || { +//! let conn = &mut pool.get().unwrap(); +//! let name = format!("Person {}", i); +//! create_user(conn, &name).unwrap(); +//! } +//! })) +//! } +//! +//! for handle in threads { +//! handle.join().unwrap(); +//! } +//! } +//! ``` +//! +//! # A note on error handling +//! +//! When used inside a pool, if an individual connection becomes +//! broken (as determined by the [R2D2Connection::is_broken] method) +//! then, when the connection goes out of scope, `r2d2` will close +//! and return the connection to the DB. +//! +//! `diesel` determines broken connections by whether or not the current +//! thread is panicking or if individual `Connection` structs are +//! broken (determined by the `is_broken()` method). Generically, these +//! are left to individual backends to implement themselves. +//! +//! For SQLite, PG, and MySQL backends `is_broken()` is determined +//! by whether or not the `TransactionManagerStatus` (as a part +//! of the `AnsiTransactionManager` struct) is in an `InError` state +//! or contains an open transaction when the connection goes out of scope. +//! + +pub use r2d2::*; + +/// A re-export of [`r2d2::Error`], which is only used by methods on [`r2d2::Pool`]. +/// +/// [`r2d2::Error`]: r2d2::Error +/// [`r2d2::Pool`]: r2d2::Pool +pub type PoolError = r2d2::Error; + +use std::fmt; +use std::marker::PhantomData; + +use crate::backend::Backend; +use crate::connection::{ + ConnectionSealed, LoadConnection, SimpleConnection, TransactionManager, + TransactionManagerStatus, +}; +use crate::expression::QueryMetadata; +use crate::prelude::*; +use crate::query_builder::{Query, QueryFragment, QueryId}; + +/// An r2d2 connection manager for use with Diesel. +/// +/// See the [r2d2 documentation](https://docs.rs/r2d2/latest/r2d2/) for usage examples. +#[derive(Clone)] +pub struct ConnectionManager { + database_url: String, + _marker: PhantomData, +} + +impl fmt::Debug for ConnectionManager { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ConnectionManager<{}>", std::any::type_name::()) + } +} + +#[allow(unsafe_code)] // we do not actually hold a reference to `T` +unsafe impl Sync for ConnectionManager {} + +impl ConnectionManager { + /// Returns a new connection manager, + /// which establishes connections to the given database URL. + pub fn new>(database_url: S) -> Self { + ConnectionManager { + database_url: database_url.into(), + _marker: PhantomData, + } + } + + /// Modifies the URL which was supplied at initialization. + /// + /// This does not update any state for existing connections, + /// but this new URL is used for new connections that are created. + pub fn update_database_url>(&mut self, database_url: S) { + self.database_url = database_url.into(); + } +} + +/// The error used when managing connections with `r2d2`. +#[derive(Debug)] +pub enum Error { + /// An error occurred establishing the connection + ConnectionError(ConnectionError), + + /// An error occurred pinging the database + QueryError(crate::result::Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Error::ConnectionError(ref e) => e.fmt(f), + Error::QueryError(ref e) => e.fmt(f), + } + } +} + +impl ::std::error::Error for Error {} + +/// A trait indicating a connection could be used inside a r2d2 pool +pub trait R2D2Connection: Connection { + /// Check if a connection is still valid + fn ping(&mut self) -> QueryResult<()>; + + /// Checks if the connection is broken and should not be reused + /// + /// This method should return only contain a fast non-blocking check + /// if the connection is considered to be broken or not. See + /// [ManageConnection::has_broken] for details. + /// + /// The default implementation does not consider any connection as broken + fn is_broken(&mut self) -> bool { + false + } +} + +impl ManageConnection for ConnectionManager +where + T: R2D2Connection + Send + 'static, +{ + type Connection = T; + type Error = Error; + + fn connect(&self) -> Result { + T::establish(&self.database_url).map_err(Error::ConnectionError) + } + + fn is_valid(&self, conn: &mut T) -> Result<(), Error> { + conn.ping().map_err(Error::QueryError) + } + + fn has_broken(&self, conn: &mut T) -> bool { + std::thread::panicking() || conn.is_broken() + } +} + +impl SimpleConnection for PooledConnection +where + M: ManageConnection, + M::Connection: R2D2Connection + Send + 'static, +{ + fn batch_execute(&mut self, query: &str) -> QueryResult<()> { + (**self).batch_execute(query) + } +} + +impl ConnectionSealed for PooledConnection +where + M: ManageConnection, + M::Connection: ConnectionSealed, +{ +} + +impl Connection for PooledConnection +where + M: ManageConnection, + M::Connection: Connection + R2D2Connection + Send + 'static, +{ + type Backend = ::Backend; + type TransactionManager = + PoolTransactionManager<::TransactionManager>; + + fn establish(_: &str) -> ConnectionResult { + Err(ConnectionError::BadConnection(String::from( + "Cannot directly establish a pooled connection", + ))) + } + + fn begin_test_transaction(&mut self) -> QueryResult<()> { + (**self).begin_test_transaction() + } + + fn execute_returning_count(&mut self, source: &T) -> QueryResult + where + T: QueryFragment + QueryId, + { + (**self).execute_returning_count(source) + } + + fn transaction_state( + &mut self, + ) -> &mut >::TransactionStateData { + (**self).transaction_state() + } + + fn instrumentation(&mut self) -> &mut dyn crate::connection::Instrumentation { + (**self).instrumentation() + } + + fn set_instrumentation(&mut self, instrumentation: impl crate::connection::Instrumentation) { + (**self).set_instrumentation(instrumentation) + } +} + +impl LoadConnection for PooledConnection +where + M: ManageConnection, + M::Connection: LoadConnection + R2D2Connection, +{ + type Cursor<'conn, 'query> = >::Cursor<'conn, 'query>; + type Row<'conn, 'query> = >::Row<'conn, 'query>; + + fn load<'conn, 'query, T>( + &'conn mut self, + source: T, + ) -> QueryResult> + where + T: Query + QueryFragment + QueryId + 'query, + Self::Backend: QueryMetadata, + { + (**self).load(source) + } +} + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct PoolTransactionManager(std::marker::PhantomData); + +impl TransactionManager> for PoolTransactionManager +where + M: ManageConnection, + M::Connection: Connection + R2D2Connection, + T: TransactionManager, +{ + type TransactionStateData = T::TransactionStateData; + + fn begin_transaction(conn: &mut PooledConnection) -> QueryResult<()> { + T::begin_transaction(&mut **conn) + } + + fn rollback_transaction(conn: &mut PooledConnection) -> QueryResult<()> { + T::rollback_transaction(&mut **conn) + } + + fn commit_transaction(conn: &mut PooledConnection) -> QueryResult<()> { + T::commit_transaction(&mut **conn) + } + + fn transaction_manager_status_mut( + conn: &mut PooledConnection, + ) -> &mut TransactionManagerStatus { + T::transaction_manager_status_mut(&mut **conn) + } +} + +impl crate::migration::MigrationConnection for PooledConnection +where + M: ManageConnection, + M::Connection: crate::migration::MigrationConnection, + Self: Connection, +{ + fn setup(&mut self) -> QueryResult { + (**self).setup() + } +} + +impl crate::query_dsl::UpdateAndFetchResults + for PooledConnection +where + M: ManageConnection, + M::Connection: crate::query_dsl::UpdateAndFetchResults, + Self: Connection, +{ + fn update_and_fetch(&mut self, changeset: Changes) -> QueryResult { + (**self).update_and_fetch(changeset) + } +} + +#[derive(QueryId)] +pub(crate) struct CheckConnectionQuery; + +impl QueryFragment for CheckConnectionQuery +where + DB: Backend, +{ + fn walk_ast<'b>( + &'b self, + mut pass: crate::query_builder::AstPass<'_, 'b, DB>, + ) -> QueryResult<()> { + pass.push_sql("SELECT 1"); + Ok(()) + } +} + +impl Query for CheckConnectionQuery { + type SqlType = crate::sql_types::Integer; +} + +impl RunQueryDsl for CheckConnectionQuery {} + +#[cfg(test)] +mod tests { + use std::sync::mpsc; + use std::sync::Arc; + use std::thread; + + use crate::r2d2::*; + use crate::test_helpers::*; + + #[test] + fn establish_basic_connection() { + let manager = ConnectionManager::::new(database_url()); + let pool = Arc::new(Pool::builder().max_size(2).build(manager).unwrap()); + + let (s1, r1) = mpsc::channel(); + let (s2, r2) = mpsc::channel(); + + let pool1 = Arc::clone(&pool); + let t1 = thread::spawn(move || { + let conn = pool1.get().unwrap(); + s1.send(()).unwrap(); + r2.recv().unwrap(); + drop(conn); + }); + + let pool2 = Arc::clone(&pool); + let t2 = thread::spawn(move || { + let conn = pool2.get().unwrap(); + s2.send(()).unwrap(); + r1.recv().unwrap(); + drop(conn); + }); + + t1.join().unwrap(); + t2.join().unwrap(); + + pool.get().unwrap(); + } + + #[test] + fn is_valid() { + let manager = ConnectionManager::::new(database_url()); + let pool = Pool::builder() + .max_size(1) + .test_on_check_out(true) + .build(manager) + .unwrap(); + + pool.get().unwrap(); + } + + #[test] + fn pooled_connection_impls_connection() { + use crate::select; + use crate::sql_types::Text; + + let manager = ConnectionManager::::new(database_url()); + let pool = Pool::builder() + .max_size(1) + .test_on_check_out(true) + .build(manager) + .unwrap(); + let mut conn = pool.get().unwrap(); + + let query = select("foo".into_sql::()); + assert_eq!("foo", query.get_result::(&mut conn).unwrap()); + } + + #[test] + fn check_pool_does_actually_hold_connections() { + use std::sync::atomic::{AtomicU32, Ordering}; + + #[derive(Debug)] + struct TestEventHandler { + acquire_count: Arc, + release_count: Arc, + checkin_count: Arc, + checkout_count: Arc, + } + + impl r2d2::HandleEvent for TestEventHandler { + fn handle_acquire(&self, _event: r2d2::event::AcquireEvent) { + self.acquire_count.fetch_add(1, Ordering::Relaxed); + } + fn handle_release(&self, _event: r2d2::event::ReleaseEvent) { + self.release_count.fetch_add(1, Ordering::Relaxed); + } + fn handle_checkout(&self, _event: r2d2::event::CheckoutEvent) { + self.checkout_count.fetch_add(1, Ordering::Relaxed); + } + fn handle_checkin(&self, _event: r2d2::event::CheckinEvent) { + self.checkin_count.fetch_add(1, Ordering::Relaxed); + } + } + + let acquire_count = Arc::new(AtomicU32::new(0)); + let release_count = Arc::new(AtomicU32::new(0)); + let checkin_count = Arc::new(AtomicU32::new(0)); + let checkout_count = Arc::new(AtomicU32::new(0)); + + let handler = Box::new(TestEventHandler { + acquire_count: acquire_count.clone(), + release_count: release_count.clone(), + checkin_count: checkin_count.clone(), + checkout_count: checkout_count.clone(), + }); + + let manager = ConnectionManager::::new(database_url()); + let pool = Pool::builder() + .max_size(1) + .test_on_check_out(true) + .event_handler(handler) + .build(manager) + .unwrap(); + + assert_eq!(acquire_count.load(Ordering::Relaxed), 1); + assert_eq!(release_count.load(Ordering::Relaxed), 0); + assert_eq!(checkin_count.load(Ordering::Relaxed), 0); + assert_eq!(checkout_count.load(Ordering::Relaxed), 0); + + // check that we reuse connections with the pool + { + let conn = pool.get().unwrap(); + + assert_eq!(acquire_count.load(Ordering::Relaxed), 1); + assert_eq!(release_count.load(Ordering::Relaxed), 0); + assert_eq!(checkin_count.load(Ordering::Relaxed), 0); + assert_eq!(checkout_count.load(Ordering::Relaxed), 1); + std::mem::drop(conn); + } + + assert_eq!(acquire_count.load(Ordering::Relaxed), 1); + assert_eq!(release_count.load(Ordering::Relaxed), 0); + assert_eq!(checkin_count.load(Ordering::Relaxed), 1); + assert_eq!(checkout_count.load(Ordering::Relaxed), 1); + + // check that we remove a connection with open transactions from the pool + { + let mut conn = pool.get().unwrap(); + + assert_eq!(acquire_count.load(Ordering::Relaxed), 1); + assert_eq!(release_count.load(Ordering::Relaxed), 0); + assert_eq!(checkin_count.load(Ordering::Relaxed), 1); + assert_eq!(checkout_count.load(Ordering::Relaxed), 2); + + ::TransactionManager::begin_transaction(&mut *conn) + .unwrap(); + } + + // we are not interested in the acquire count here + // as the pool opens a new connection in the background + // that could lead to this test failing if that happens to fast + // (which is sometimes the case for sqlite) + //assert_eq!(acquire_count.load(Ordering::Relaxed), 1); + assert_eq!(release_count.load(Ordering::Relaxed), 1); + assert_eq!(checkin_count.load(Ordering::Relaxed), 2); + assert_eq!(checkout_count.load(Ordering::Relaxed), 2); + + // check that we remove a connection from the pool that was + // open during panicking + #[allow(unreachable_code, unused_variables)] + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let conn = pool.get(); + assert_eq!(acquire_count.load(Ordering::Relaxed), 2); + assert_eq!(release_count.load(Ordering::Relaxed), 1); + assert_eq!(checkin_count.load(Ordering::Relaxed), 2); + assert_eq!(checkout_count.load(Ordering::Relaxed), 3); + panic!(); + std::mem::drop(conn); + })) + .unwrap_err(); + + // we are not interested in the acquire count here + // as the pool opens a new connection in the background + // that could lead to this test failing if that happens to fast + // (which is sometimes the case for sqlite) + //assert_eq!(acquire_count.load(Ordering::Relaxed), 2); + assert_eq!(release_count.load(Ordering::Relaxed), 2); + assert_eq!(checkin_count.load(Ordering::Relaxed), 3); + assert_eq!(checkout_count.load(Ordering::Relaxed), 3); + } + + #[cfg(feature = "postgres")] + #[test] + fn verify_that_begin_test_transaction_works_with_pools() { + use crate::prelude::*; + use crate::r2d2::*; + + table! { + users { + id -> Integer, + name -> Text, + } + } + + #[derive(Debug)] + struct TestConnectionCustomizer; + + impl CustomizeConnection for TestConnectionCustomizer { + fn on_acquire(&self, conn: &mut PgConnection) -> Result<(), E> { + conn.begin_test_transaction() + .expect("Failed to start test transaction"); + + Ok(()) + } + } + + let manager = ConnectionManager::::new(database_url()); + let pool = Pool::builder() + .max_size(1) + .connection_customizer(Box::new(TestConnectionCustomizer)) + .build(manager) + .unwrap(); + + let mut conn = pool.get().unwrap(); + + crate::sql_query( + "CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name TEXT NOT NULL)", + ) + .execute(&mut conn) + .unwrap(); + + crate::insert_into(users::table) + .values(users::name.eq("John")) + .execute(&mut conn) + .unwrap(); + + std::mem::drop(conn); + + let mut conn2 = pool.get().unwrap(); + + let user_count = users::table.count().get_result::(&mut conn2).unwrap(); + assert_eq!(user_count, 1); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/result.rs b/collector/compile-benchmarks/diesel-2.2.10/src/result.rs new file mode 100644 index 000000000..92c4937fa --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/result.rs @@ -0,0 +1,516 @@ +//! Errors, type aliases, and functions related to working with `Result`. + +use std::error::Error as StdError; +use std::ffi::NulError; +use std::fmt::{self, Display}; + +#[derive(Debug)] +#[allow(clippy::enum_variant_names)] +/// Represents all the ways that a query can fail. +/// +/// This type is not intended to be exhaustively matched, and new variants may +/// be added in the future without a major version bump. +#[non_exhaustive] +pub enum Error { + /// The query contained a nul byte. + /// + /// This should never occur in normal usage. + InvalidCString(NulError), + + /// The database returned an error. + /// + /// While Diesel prevents almost all sources of runtime errors at compile + /// time, it does not attempt to prevent 100% of them. Typically this error + /// will occur from insert or update statements due to a constraint + /// violation. + DatabaseError( + DatabaseErrorKind, + Box, + ), + + /// No rows were returned by a query expected to return at least one row. + /// + /// This variant is only returned by [`get_result`] and [`first`]. [`load`] + /// does not treat 0 rows as an error. If you would like to allow either 0 + /// or 1 rows, call [`optional`] on the result. + /// + /// [`get_result`]: crate::query_dsl::RunQueryDsl::get_result() + /// [`first`]: crate::query_dsl::RunQueryDsl::first() + /// [`load`]: crate::query_dsl::RunQueryDsl::load() + /// [`optional`]: OptionalExtension::optional + NotFound, + + /// The query could not be constructed + /// + /// An example of when this error could occur is if you are attempting to + /// construct an update statement with no changes (e.g. all fields on the + /// struct are `None`). + QueryBuilderError(Box), + + /// An error occurred deserializing the data being sent to the database. + /// + /// Typically this error means that the stated type of the query is + /// incorrect. An example of when this error might occur in normal usage is + /// attempting to deserialize an infinite date into chrono. + DeserializationError(Box), + + /// An error occurred serializing the data being sent to the database. + /// + /// An example of when this error would be returned is if you attempted to + /// serialize a `chrono::NaiveDate` earlier than the earliest date supported + /// by PostgreSQL. + SerializationError(Box), + + /// An error occurred when attempting rollback of a transaction subsequently to a failed + /// commit attempt. + /// + /// When a commit attempt fails and Diesel believes that it can attempt a rollback to return + /// the connection back in a usable state (out of that transaction), it attempts it then + /// returns the original error. + /// + /// If that fails, you get this. + RollbackErrorOnCommit { + /// The error that was encountered when attempting the rollback + rollback_error: Box, + /// The error that was encountered during the failed commit attempt + commit_error: Box, + }, + + /// Roll back the current transaction. + /// + /// You can return this variant inside of a transaction when you want to + /// roll it back, but have no actual error to return. Diesel will never + /// return this variant unless you gave it to us, and it can be safely + /// ignored in error handling. + RollbackTransaction, + + /// Attempted to perform an operation that cannot be done inside a transaction + /// when a transaction was already open. + AlreadyInTransaction, + + /// Attempted to perform an operation that can only be done inside a transaction + /// when no transaction was open + NotInTransaction, + + /// Transaction manager broken, likely due to a broken connection. No other operations are possible. + BrokenTransactionManager, +} + +#[derive(Debug, Clone, Copy)] +/// The kind of database error that occurred. +/// +/// This is not meant to exhaustively cover all possible errors, but is used to +/// identify errors which are commonly recovered from programmatically. This enum +/// is not intended to be exhaustively matched, and new variants may be added in +/// the future without a major version bump. +#[non_exhaustive] +pub enum DatabaseErrorKind { + /// A unique constraint was violated. + UniqueViolation, + + /// A foreign key constraint was violated. + ForeignKeyViolation, + + /// The query could not be sent to the database due to a protocol violation. + /// + /// An example of a case where this would occur is if you attempted to send + /// a query with more than 65000 bind parameters using PostgreSQL. + UnableToSendCommand, + + /// A serializable transaction failed to commit due to a read/write + /// dependency on a concurrent transaction. + /// + /// Corresponds to SQLSTATE code 40001 + /// + /// This error is only detected for PostgreSQL, as we do not yet support + /// transaction isolation levels for other backends. + SerializationFailure, + + /// The command could not be completed because the transaction was read + /// only. + /// + /// This error will also be returned for `SELECT` statements which attempted + /// to lock the rows. + ReadOnlyTransaction, + + /// A not null constraint was violated. + NotNullViolation, + + /// A check constraint was violated. + CheckViolation, + + /// The connection to the server was unexpectedly closed. + /// + /// This error is only detected for PostgreSQL and is emitted on a best-effort basis + /// and may be missed. + ClosedConnection, + + #[doc(hidden)] + Unknown, // Match against _ instead, more variants may be added in the future +} + +/// Information about an error that was returned by the database. +pub trait DatabaseErrorInformation { + /// The primary human-readable error message. Typically one line. + fn message(&self) -> &str; + + /// An optional secondary error message providing more details about the + /// problem, if it was provided by the database. Might span multiple lines. + fn details(&self) -> Option<&str>; + + /// An optional suggestion of what to do about the problem, if one was + /// provided by the database. + fn hint(&self) -> Option<&str>; + + /// The name of the table the error was associated with, if the error was + /// associated with a specific table and the backend supports retrieving + /// that information. + /// + /// Currently this method will return `None` for all backends other than + /// PostgreSQL. + fn table_name(&self) -> Option<&str>; + + /// The name of the column the error was associated with, if the error was + /// associated with a specific column and the backend supports retrieving + /// that information. + /// + /// Currently this method will return `None` for all backends other than + /// PostgreSQL. + fn column_name(&self) -> Option<&str>; + + /// The constraint that was violated if this error is a constraint violation + /// and the backend supports retrieving that information. + /// + /// Currently this method will return `None` for all backends other than + /// PostgreSQL. + fn constraint_name(&self) -> Option<&str>; + + /// An optional integer indicating an error cursor position as an index into + /// the original statement string. + fn statement_position(&self) -> Option; +} + +impl fmt::Debug for dyn DatabaseErrorInformation + Send + Sync { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.message(), f) + } +} + +impl DatabaseErrorInformation for String { + fn message(&self) -> &str { + self + } + fn details(&self) -> Option<&str> { + None + } + fn hint(&self) -> Option<&str> { + None + } + fn table_name(&self) -> Option<&str> { + None + } + fn column_name(&self) -> Option<&str> { + None + } + fn constraint_name(&self) -> Option<&str> { + None + } + fn statement_position(&self) -> Option { + None + } +} + +/// Errors which can occur during [`Connection::establish`] +/// +/// [`Connection::establish`]: crate::connection::Connection::establish +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub enum ConnectionError { + /// The connection URL contained a `NUL` byte. + InvalidCString(NulError), + /// The database returned an error. + BadConnection(String), + /// The connection URL could not be parsed. + InvalidConnectionUrl(String), + /// Diesel could not configure the database connection. + /// + /// Diesel may try to automatically set session specific configuration + /// values, such as UTF8 encoding, or enabling the `||` operator on MySQL. + /// This variant is returned if an error occurred executing the query to set + /// those options. Diesel will never affect global configuration. + CouldntSetupConfiguration(Error), +} + +/// A specialized result type for queries. +/// +/// This type is exported by `diesel::prelude`, and is generally used by any +/// code which is interacting with Diesel. This type exists to avoid writing out +/// `diesel::result::Error`, and is otherwise a direct mapping to `Result`. +pub type QueryResult = Result; + +/// A specialized result type for establishing connections. +/// +/// This type exists to avoid writing out `diesel::result::ConnectionError`, and +/// is otherwise a direct mapping to `Result`. +pub type ConnectionResult = Result; + +/// See the [method documentation](OptionalExtension::optional). +pub trait OptionalExtension { + /// Converts a `QueryResult` into a `QueryResult>`. + /// + /// By default, Diesel treats 0 rows being returned from a query that is expected to return 1 + /// row as an error (e.g. the return value of [`get_result`] or [`first`]). This method will + /// handle that error, and give you back an `Option` instead. + /// + /// [`get_result`]: crate::query_dsl::RunQueryDsl::get_result() + /// [`first`]: crate::query_dsl::RunQueryDsl::first() + /// + /// # Example + /// + /// ```rust + /// use diesel::{QueryResult, NotFound, OptionalExtension}; + /// + /// let result: QueryResult = Ok(1); + /// assert_eq!(Ok(Some(1)), result.optional()); + /// + /// let result: QueryResult = Err(NotFound); + /// assert_eq!(Ok(None), result.optional()); + /// ``` + fn optional(self) -> Result, Error>; +} + +impl OptionalExtension for QueryResult { + fn optional(self) -> Result, Error> { + match self { + Ok(value) => Ok(Some(value)), + Err(Error::NotFound) => Ok(None), + Err(e) => Err(e), + } + } +} + +/// See the [method documentation](OptionalEmptyChangesetExtension::optional_empty_changeset). +pub trait OptionalEmptyChangesetExtension { + /// By default, Diesel treats an empty update as a `QueryBuilderError`. This method will + /// convert that error into `None`. + /// + /// # Example + /// + /// ```rust + /// use diesel::{QueryResult, OptionalEmptyChangesetExtension, result::Error::QueryBuilderError, result::EmptyChangeset}; + /// let result: QueryResult = Err(QueryBuilderError(Box::new(EmptyChangeset))); + /// assert_eq!(Ok(None), result.optional_empty_changeset()); + /// ``` + fn optional_empty_changeset(self) -> Result, Error>; +} + +impl OptionalEmptyChangesetExtension for QueryResult { + fn optional_empty_changeset(self) -> Result, Error> { + match self { + Ok(value) => Ok(Some(value)), + Err(Error::QueryBuilderError(e)) if e.is::() => Ok(None), + Err(e) => Err(e), + } + } +} + +impl From for ConnectionError { + fn from(e: NulError) -> Self { + ConnectionError::InvalidCString(e) + } +} + +impl From for Error { + fn from(e: NulError) -> Self { + Error::InvalidCString(e) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Error::InvalidCString(ref nul_err) => write!(f, "{nul_err}"), + Error::DatabaseError(_, ref e) => write!(f, "{}", e.message()), + Error::NotFound => f.write_str("Record not found"), + Error::QueryBuilderError(ref e) => e.fmt(f), + Error::DeserializationError(ref e) => e.fmt(f), + Error::SerializationError(ref e) => e.fmt(f), + Error::RollbackErrorOnCommit { + ref rollback_error, + ref commit_error, + } => { + write!( + f, + "Transaction rollback failed: {} \ + (rollback attempted because of failure to commit: {})", + &**rollback_error, &**commit_error + )?; + Ok(()) + } + Error::RollbackTransaction => { + write!(f, "You have asked diesel to rollback the transaction") + } + Error::BrokenTransactionManager => write!(f, "The transaction manager is broken"), + Error::AlreadyInTransaction => write!( + f, + "Cannot perform this operation while a transaction is open", + ), + Error::NotInTransaction => { + write!(f, "Cannot perform this operation outside of a transaction",) + } + } + } +} + +impl StdError for Error { + fn cause(&self) -> Option<&dyn StdError> { + match *self { + Error::InvalidCString(ref e) => Some(e), + Error::QueryBuilderError(ref e) => Some(&**e), + Error::DeserializationError(ref e) => Some(&**e), + Error::SerializationError(ref e) => Some(&**e), + _ => None, + } + } +} + +impl Display for ConnectionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ConnectionError::InvalidCString(ref nul_err) => nul_err.fmt(f), + ConnectionError::BadConnection(ref s) => write!(f, "{s}"), + ConnectionError::InvalidConnectionUrl(ref s) => write!(f, "{s}"), + ConnectionError::CouldntSetupConfiguration(ref e) => e.fmt(f), + } + } +} + +impl StdError for ConnectionError { + fn cause(&self) -> Option<&dyn StdError> { + match *self { + ConnectionError::InvalidCString(ref e) => Some(e), + ConnectionError::CouldntSetupConfiguration(ref e) => Some(e), + _ => None, + } + } +} + +impl PartialEq for Error { + fn eq(&self, other: &Error) -> bool { + match (self, other) { + (Error::InvalidCString(a), Error::InvalidCString(b)) => a == b, + (Error::DatabaseError(_, a), Error::DatabaseError(_, b)) => a.message() == b.message(), + (&Error::NotFound, &Error::NotFound) => true, + (&Error::RollbackTransaction, &Error::RollbackTransaction) => true, + (&Error::AlreadyInTransaction, &Error::AlreadyInTransaction) => true, + _ => false, + } + } +} + +#[cfg(test)] +#[allow(warnings)] +fn error_impls_send() { + let err: Error = unimplemented!(); + let x: &dyn Send = &err; +} + +/// An unexpected `NULL` was encountered during deserialization +#[derive(Debug, Clone, Copy)] +pub struct UnexpectedNullError; + +impl fmt::Display for UnexpectedNullError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Unexpected null for non-null column") + } +} + +impl StdError for UnexpectedNullError {} + +/// Expected more fields then present in the current row while deserializing results +#[derive(Debug, Clone, Copy)] +pub struct UnexpectedEndOfRow; + +impl fmt::Display for UnexpectedEndOfRow { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Unexpected end of row") + } +} + +impl StdError for UnexpectedEndOfRow {} + +/// Expected when an update has no changes to save. +/// +/// When using `optional_empty_changeset`, this error is turned into `None`. +#[derive(Debug, Clone, Copy)] +pub struct EmptyChangeset; + +impl fmt::Display for EmptyChangeset { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "There are no changes to save. This query cannot be built" + ) + } +} + +impl StdError for EmptyChangeset {} + +/// Expected when you try to execute an empty query +#[derive(Debug, Clone, Copy)] +pub struct EmptyQuery; + +impl fmt::Display for EmptyQuery { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Detected an empty query. These are not supported by your database system" + ) + } +} + +impl StdError for EmptyQuery {} + +/// An error occurred while deserializing a field +#[derive(Debug)] +#[non_exhaustive] +pub struct DeserializeFieldError { + /// The name of the field that failed to deserialize + pub field_name: Option, + /// The error that occurred while deserializing the field + pub error: Box, +} + +impl DeserializeFieldError { + #[cold] + pub(crate) fn new<'a, F, DB>(field: F, error: Box) -> Self + where + DB: crate::backend::Backend, + F: crate::row::Field<'a, DB>, + { + DeserializeFieldError { + field_name: field.field_name().map(|s| s.to_string()), + error, + } + } +} + +impl StdError for DeserializeFieldError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&*self.error) + } +} + +impl fmt::Display for DeserializeFieldError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(ref field_name) = self.field_name { + write!( + f, + "Error deserializing field '{}': {}", + field_name, self.error + ) + } else { + write!(f, "Error deserializing field: {}", self.error) + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/row.rs b/collector/compile-benchmarks/diesel-2.2.10/src/row.rs new file mode 100644 index 000000000..d8732bf71 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/row.rs @@ -0,0 +1,310 @@ +//! Contains the `Row` trait + +use crate::backend::Backend; +use crate::deserialize; +use deserialize::FromSql; +use std::default::Default; +use std::ops::Range; + +#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] +#[doc(inline)] +pub use self::private::{PartialRow, RowSealed}; + +#[cfg(not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))] +#[allow(unused_imports)] +pub(crate) use self::private::{PartialRow, RowSealed}; + +/// Representing a way to index into database rows +/// +/// * Crates using existing backends should use existing implementations of +/// this traits. Diesel provides `RowIndex` and `RowIndex<&str>` for +/// all built-in backends +/// +/// * Crates implementing custom backends need to provide `RowIndex` and +/// `RowIndex<&str>` impls for their [`Row`] type. +/// +pub trait RowIndex { + /// Get the numeric index inside the current row for the provided index value + fn idx(&self, idx: I) -> Option; +} + +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[deprecated(note = "Use `Row::Field` directly instead")] +pub type FieldRet<'a, R, DB> = >::Field<'a>; + +/// Represents a single database row. +/// +/// This trait is used as an argument to [`FromSqlRow`]. +/// +/// [`FromSqlRow`]: crate::deserialize::FromSqlRow +pub trait Row<'a, DB: Backend>: + RowIndex + for<'b> RowIndex<&'b str> + self::private::RowSealed + Sized +{ + /// Field type returned by a `Row` implementation + /// + /// * Crates using existing backends should not concern themself with the + /// concrete type of this associated type. + /// + /// * Crates implementing custom backends should provide their own type + /// meeting the required trait bounds + type Field<'f>: Field<'f, DB> + where + 'a: 'f, + Self: 'f; + + /// Return type of `PartialRow` + /// + /// For all implementations, beside of the `Row` implementation on `PartialRow` itself + /// this should be `Self`. + #[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc(hidden) + )] + type InnerPartialRow: Row<'a, DB>; + + /// Get the number of fields in the current row + fn field_count(&self) -> usize; + + /// Get the field with the provided index from the row. + /// + /// Returns `None` if there is no matching field for the given index + fn get<'b, I>(&'b self, idx: I) -> Option> + where + 'a: 'b, + Self: RowIndex; + + /// Get a deserialized value with the provided index from the row. + fn get_value(&self, idx: I) -> crate::deserialize::Result + where + Self: RowIndex, + T: FromSql, + { + let field = self.get(idx).ok_or(crate::result::UnexpectedEndOfRow)?; + >::from_nullable_sql(field.value()) + } + + /// Returns a wrapping row that allows only to access fields, where the index is part of + /// the provided range. + #[cfg_attr( + not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"), + doc(hidden) + )] + fn partial_row(&self, range: Range) -> PartialRow<'_, Self::InnerPartialRow>; +} + +/// Represents a single field in a database row. +/// +/// This trait allows retrieving information on the name of the column and on the value of the +/// field. +pub trait Field<'a, DB: Backend> { + /// The name of the current field + /// + /// Returns `None` if it's an unnamed field + fn field_name(&self) -> Option<&str>; + + /// Get the value representing the current field in the raw representation + /// as it is transmitted by the database + fn value(&self) -> Option>; + + /// Checks whether this field is null or not. + fn is_null(&self) -> bool { + self.value().is_none() + } +} + +/// Represents a row of a SQL query, where the values are accessed by name +/// rather than by index. +/// +/// This trait is used by implementations of +/// [`QueryableByName`](crate::deserialize::QueryableByName) +pub trait NamedRow<'a, DB: Backend>: Row<'a, DB> { + /// Retrieve and deserialize a single value from the query + /// + /// Note that `ST` *must* be the exact type of the value with that name in + /// the query. The compiler will not be able to verify that you have + /// provided the correct type. If there is a mismatch, you may receive an + /// incorrect value, or a runtime error. + /// + /// If two or more fields in the query have the given name, the result of + /// this function is undefined. + fn get(&self, column_name: &str) -> deserialize::Result + where + T: FromSql; +} + +impl<'a, R, DB> NamedRow<'a, DB> for R +where + R: Row<'a, DB>, + DB: Backend, +{ + fn get(&self, column_name: &str) -> deserialize::Result + where + T: FromSql, + { + let field = Row::get(self, column_name) + .ok_or_else(|| format!("Column `{column_name}` was not present in query"))?; + + T::from_nullable_sql(field.value()) + } +} + +/// A row that can be turned into an owned version +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +pub trait IntoOwnedRow<'a, DB: Backend>: Row<'a, DB> { + /// The owned version of the row + type OwnedRow: Row<'a, DB> + Send + 'static; + + /// A store for cached information between rows for faster access + type Cache: Default + 'static; + + /// Turn the row into its owned version + fn into_owned(self, cache: &mut Self::Cache) -> Self::OwnedRow; +} + +// These traits are not part of the public API +// because: +// * we want to control who can implement `Row` (for `RowSealed`) +// * `PartialRow` is an implementation detail +// * `RowLifetimeHelper` is an internal backward compatibility helper +pub(crate) mod private { + use super::*; + + /// This trait restricts who can implement `Row` + #[cfg_attr( + docsrs, + doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")) + )] + pub trait RowSealed {} + + /// A row type that wraps an inner row + /// + /// This type only allows to access fields of the inner row, whose index is + /// part of `range`. This type is used by diesel internally to implement + /// [`FromStaticSqlRow`](crate::deserialize::FromStaticSqlRow). + /// + /// Indexing via `usize` starts with 0 for this row type. The index is then shifted + /// by `self.range.start` to match the corresponding field in the underlying row. + #[derive(Debug)] + #[cfg_attr( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes") + )] + pub struct PartialRow<'a, R> { + inner: &'a R, + range: Range, + } + + impl<'a, R> PartialRow<'a, R> { + /// Create a new [`PartialRow`] instance based on an inner + /// row and a range of field that should be part of the constructed + /// wrapper. + /// + /// See the documentation of [`PartialRow`] for details. + pub fn new<'b, DB>(inner: &'a R, range: Range) -> Self + where + R: Row<'b, DB>, + DB: Backend, + { + let range_lower = std::cmp::min(range.start, inner.field_count()); + let range_upper = std::cmp::min(range.end, inner.field_count()); + Self { + inner, + range: range_lower..range_upper, + } + } + } + + impl RowSealed for PartialRow<'_, R> {} + + impl<'a, DB, R> Row<'a, DB> for PartialRow<'_, R> + where + DB: Backend, + R: Row<'a, DB>, + { + type Field<'f> + = R::Field<'f> + where + 'a: 'f, + R: 'f, + Self: 'f; + type InnerPartialRow = R; + + fn field_count(&self) -> usize { + self.range.len() + } + + fn get<'c, I>(&'c self, idx: I) -> Option> + where + 'a: 'c, + Self: RowIndex, + { + let idx = self.idx(idx)?; + self.inner.get(idx) + } + + fn partial_row(&self, range: Range) -> PartialRow<'_, R> { + let range_upper_bound = std::cmp::min(self.range.end, self.range.start + range.end); + let range = (self.range.start + range.start)..range_upper_bound; + PartialRow { + inner: self.inner, + range, + } + } + } + + impl<'a, R> RowIndex<&'a str> for PartialRow<'_, R> + where + R: RowIndex<&'a str>, + { + fn idx(&self, idx: &'a str) -> Option { + let idx = self.inner.idx(idx)?; + if self.range.contains(&idx) { + Some(idx) + } else { + None + } + } + } + + impl RowIndex for PartialRow<'_, R> + where + R: RowIndex, + { + fn idx(&self, idx: usize) -> Option { + let idx = self.inner.idx(idx + self.range.start)?; + if self.range.contains(&idx) { + Some(idx) + } else { + None + } + } + } + + // These impls are only there for backward compatibility reasons + // Remove them on the next breaking release + #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] + #[allow(unreachable_pub)] + pub trait RowLifetimeHelper: for<'a> super::Row<'a, DB> + where + DB: Backend, + { + type Field<'f> + where + Self: 'f; + } + + #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] + impl RowLifetimeHelper for R + where + DB: Backend, + for<'a> R: super::Row<'a, DB>, + { + type Field<'f> + = >::Field<'f> + where + R: 'f; + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/serialize.rs b/collector/compile-benchmarks/diesel-2.2.10/src/serialize.rs new file mode 100644 index 000000000..17a6088a6 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/serialize.rs @@ -0,0 +1,328 @@ +//! Types and traits related to serializing values for the database + +use std::error::Error; +use std::fmt; +use std::io::{self, Write}; +use std::result; + +use crate::backend::Backend; +use crate::query_builder::bind_collector::RawBytesBindCollector; +use crate::query_builder::BindCollector; + +#[doc(inline)] +#[cfg(feature = "postgres_backend")] +pub use crate::pg::serialize::WriteTuple; + +/// A specialized result type representing the result of serializing +/// a value for the database. +pub type Result = result::Result>; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +/// Tiny enum to make the return type of `ToSql` more descriptive +pub enum IsNull { + /// No data was written, as this type is null + Yes, + /// The value is not null + /// + /// This does not necessarily mean that any data was written to the buffer. + /// For example, an empty string has no data to be sent over the wire, but + /// also is not null. + No, +} + +/// Wraps a buffer to be written by `ToSql` with additional backend specific +/// utilities. +pub struct Output<'a, 'b, DB> +where + DB: Backend, + DB::MetadataLookup: 'a, +{ + out: as BindCollector<'a, DB>>::Buffer, + metadata_lookup: Option<&'b mut DB::MetadataLookup>, +} + +impl<'a, 'b, DB: Backend> Output<'a, 'b, DB> { + /// Construct a new `Output` + pub fn new( + out: as BindCollector<'a, DB>>::Buffer, + metadata_lookup: &'b mut DB::MetadataLookup, + ) -> Self { + Output { + out, + metadata_lookup: Some(metadata_lookup), + } + } + + /// Consume the current `Output` structure to access the inner buffer type + /// + /// This function is only useful for people implementing their own Backend. + pub fn into_inner(self) -> as BindCollector<'a, DB>>::Buffer { + self.out + } + + /// Returns the backend's mechanism for dynamically looking up type + /// metadata at runtime, if relevant for the given backend. + pub fn metadata_lookup(&mut self) -> &mut DB::MetadataLookup { + self.metadata_lookup.as_mut().expect("Lookup is there") + } + + /// Set the inner buffer to a specific value + /// + /// Checkout the documentation of the type of `BindCollector::Buffer` + /// for your specific backend for supported types. + pub fn set_value(&mut self, value: V) + where + V: Into< as BindCollector<'a, DB>>::Buffer>, + { + self.out = value.into(); + } +} + +#[cfg(test)] +impl<'a, DB: Backend> Output<'a, 'static, DB> { + /// Returns a `Output` suitable for testing `ToSql` implementations. + /// Unsafe to use for testing types which perform dynamic metadata lookup. + pub fn test(buffer: as BindCollector<'a, DB>>::Buffer) -> Self { + Self { + out: buffer, + metadata_lookup: None, + } + } +} + +impl Write for Output<'_, '_, DB> +where + for<'c> DB: Backend = RawBytesBindCollector>, +{ + fn write(&mut self, buf: &[u8]) -> io::Result { + self.out.0.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.out.0.flush() + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.out.0.write_all(buf) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + self.out.0.write_fmt(fmt) + } +} + +impl<'a, DB> Output<'a, '_, DB> +where + for<'c> DB: Backend = RawBytesBindCollector>, +{ + /// Call this method whenever you pass an instance of `Output` by value. + /// + /// Effectively copies `self`, with a narrower lifetime. When passing a + /// reference or a mutable reference, this is normally done by rust + /// implicitly. This is why you can pass `&mut Foo` to multiple functions, + /// even though mutable references are not `Copy`. However, this is only + /// done implicitly for references. For structs with lifetimes it must be + /// done explicitly. This method matches the semantics of what Rust would do + /// implicitly if you were passing a mutable reference + pub fn reborrow<'c>(&'c mut self) -> Output<'c, 'c, DB> + where + 'a: 'c, + { + Output { + out: RawBytesBindCollector::::reborrow_buffer(&mut self.out), + metadata_lookup: match &mut self.metadata_lookup { + None => None, + Some(m) => Some(&mut **m), + }, + } + } +} + +impl<'a, DB> fmt::Debug for Output<'a, '_, DB> +where + as BindCollector<'a, DB>>::Buffer: fmt::Debug, + DB: Backend, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.out.fmt(f) + } +} + +/// Serializes a single value to be sent to the database. +/// +/// The output is sent as a bind parameter, and the data must be written in the +/// expected format for the given backend. +/// +/// When possible, implementations of this trait should prefer using an existing +/// implementation, rather than writing to `out` directly. (For example, if you +/// are implementing this for an enum, which is represented as an integer in the +/// database, you should use `i32::to_sql(x, out)` instead of writing to `out` +/// yourself.) +/// +/// Any types which implement this trait should also +/// [`#[derive(AsExpression)]`](derive@crate::expression::AsExpression). +/// +/// ### Backend specific details +/// +/// - For PostgreSQL, the bytes will be sent using the binary protocol, not text. +/// - For SQLite, all implementations should be written in terms of an existing +/// `ToSql` implementation. +/// - For MySQL, the expected bytes will depend on the return value of +/// `type_metadata` for the given SQL type. See [`MysqlType`] for details. +/// - For third party backends, consult that backend's documentation. +/// +/// [`MysqlType`]: ../mysql/enum.MysqlType.html +/// +/// ### Examples +/// +/// Most implementations of this trait will be defined in terms of an existing +/// implementation. +/// +/// ```rust +/// # use diesel::backend::Backend; +/// # use diesel::expression::AsExpression; +/// # use diesel::sql_types::*; +/// # use diesel::serialize::{self, ToSql, Output}; +/// # use std::io::Write; +/// # +/// #[repr(i32)] +/// #[derive(Debug, Clone, Copy, AsExpression)] +/// #[diesel(sql_type = Integer)] +/// pub enum MyEnum { +/// A = 1, +/// B = 2, +/// } +/// +/// impl ToSql for MyEnum +/// where +/// DB: Backend, +/// i32: ToSql, +/// { +/// fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> serialize::Result { +/// match self { +/// MyEnum::A => 1.to_sql(out), +/// MyEnum::B => 2.to_sql(out), +/// } +/// } +/// } +/// ``` +/// +/// Example of creating a custom type mapping based on a MySQL [enum type](https://dev.mysql.com/doc/refman/8.0/en/enum.html) +/// +/// This is designed to reuse the SQL type definition generated by diesel-cli +/// +/// ```rust +/// # use diesel::backend::Backend; +/// # use diesel::expression::AsExpression; +/// # use diesel::sql_types::*; +/// # use diesel::serialize::{self, ToSql, Output, IsNull}; +/// # use std::io::Write; +/// # +/// pub mod sql_types { +/// #[derive(diesel::sql_types::SqlType)] +/// #[diesel(mysql_type(name = "Enum"))] +/// pub struct PostEnum; //<- generated by diesel cli +/// } +/// #[derive(Debug, AsExpression, PartialEq, Clone)] +/// #[diesel(sql_type = sql_types::PostEnum)] +/// pub enum Post { +/// FirstValue, +/// SecondValue, +/// } +/// +/// # #[cfg(feature = "mysql")] +/// impl ToSql for Post { +/// fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::mysql::Mysql>) -> serialize::Result { +/// match *self { +/// // these string values need to match the labels used in your +/// // enum definition in SQL. So this expects that you defined the +/// /// relevant enum type as`ENUM('one', 'two')` in your `CREATE TABLE` statement +/// Post::FirstValue => out.write_all(b"one")?, +/// Post::SecondValue => out.write_all(b"two")?, +/// } +/// Ok(IsNull::No) +/// } +/// } +/// ``` +/// +/// Using temporary values as part of the `ToSql` implementation requires additional +/// work. +/// +/// Backends using [`RawBytesBindCollector`] as [`BindCollector`] copy the serialized values as part +/// of `Write` implementation. This includes the `Mysql` and the `Pg` backend provided by diesel. +/// This means existing `ToSql` implementations can be used even with +/// temporary values. For these it is required to call +/// [`Output::reborrow`] to shorten the lifetime of the `Output` type correspondingly. +/// +/// ``` +/// # use diesel::backend::Backend; +/// # use diesel::expression::AsExpression; +/// # use diesel::sql_types::*; +/// # use diesel::serialize::{self, ToSql, Output}; +/// # use std::io::Write; +/// # +/// #[repr(i32)] +/// #[derive(Debug, Clone, Copy, AsExpression)] +/// #[diesel(sql_type = Integer)] +/// pub enum MyEnum { +/// A = 1, +/// B = 2, +/// } +/// +/// # #[cfg(feature = "postgres")] +/// impl ToSql for MyEnum +/// where +/// i32: ToSql, +/// { +/// fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::pg::Pg>) -> serialize::Result { +/// let v = *self as i32; +/// >::to_sql(&v, &mut out.reborrow()) +/// } +/// } +/// ```` +/// +/// For any other backend the [`Output::set_value`] method provides a way to +/// set the output value directly. Checkout the documentation of the corresponding +/// `BindCollector::Buffer` type for provided `From` implementations for a list +/// of accepted types. For the `Sqlite` backend see `SqliteBindValue`. +/// +/// ``` +/// # use diesel::backend::Backend; +/// # use diesel::expression::AsExpression; +/// # use diesel::sql_types::*; +/// # use diesel::serialize::{self, ToSql, Output, IsNull}; +/// # use std::io::Write; +/// # +/// #[repr(i32)] +/// #[derive(Debug, Clone, Copy, AsExpression)] +/// #[diesel(sql_type = Integer)] +/// pub enum MyEnum { +/// A = 1, +/// B = 2, +/// } +/// +/// # #[cfg(feature = "sqlite")] +/// impl ToSql for MyEnum +/// where +/// i32: ToSql, +/// { +/// fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::sqlite::Sqlite>) -> serialize::Result { +/// out.set_value(*self as i32); +/// Ok(IsNull::No) +/// } +/// } +/// ```` +pub trait ToSql: fmt::Debug { + /// See the trait documentation. + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> Result; +} + +impl ToSql for &T +where + DB: Backend, + T: ToSql + ?Sized, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> Result { + (*self).to_sql(out) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sql_types/fold.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sql_types/fold.rs new file mode 100644 index 000000000..04803cec3 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sql_types/fold.rs @@ -0,0 +1,47 @@ +use crate::sql_types::{self, is_nullable, SingleValue, SqlType}; + +/// Represents SQL types which can be used with `SUM` and `AVG` +pub trait Foldable: SingleValue { + /// The SQL type of `sum(this_type)` + type Sum: SqlType + SingleValue; + /// The SQL type of `avg(this_type)` + type Avg: SqlType + SingleValue; +} + +impl Foldable for sql_types::Nullable +where + T: Foldable + SqlType, +{ + type Sum = T::Sum; + type Avg = T::Avg; +} + +macro_rules! foldable_impls { + ($($Source:ty => ($SumType:ty, $AvgType:ty)),+,) => { + $( + impl Foldable for $Source { + type Sum = sql_types::Nullable<$SumType>; + type Avg = sql_types::Nullable<$AvgType>; + } + )+ + } +} + +foldable_impls! { + sql_types::SmallInt => (sql_types::BigInt, sql_types::Numeric), + sql_types::Integer => (sql_types::BigInt, sql_types::Numeric), + sql_types::BigInt => (sql_types::Numeric, sql_types::Numeric), + + sql_types::Float => (sql_types::Float, sql_types::Double), + sql_types::Double => (sql_types::Double, sql_types::Double), + sql_types::Numeric => (sql_types::Numeric, sql_types::Numeric), + + sql_types::Interval => (sql_types::Interval, sql_types::Interval), +} + +#[cfg(feature = "mysql_backend")] +foldable_impls! { + sql_types::Unsigned => (sql_types::Unsigned, sql_types::Numeric), + sql_types::Unsigned => (sql_types::Unsigned, sql_types::Numeric), + sql_types::Unsigned => (sql_types::Numeric, sql_types::Numeric), +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sql_types/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sql_types/mod.rs new file mode 100644 index 000000000..3c9e4e34c --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sql_types/mod.rs @@ -0,0 +1,675 @@ +//! Types which represent a SQL data type. +//! +//! The structs in this module are *only* used as markers to represent a SQL type. +//! They should never be used in your structs. +//! If you'd like to know the rust types which can be used for a given SQL type, +//! see the documentation for that SQL type. +//! Additional types may be provided by other crates. +//! +//! To see which SQL type can be used with a given Rust type, +//! see the "Implementors" section of [`FromSql`]. +//! +//! [`FromSql`]: super::deserialize::FromSql +//! +//! Any backend specific types are re-exported through this module + +mod fold; +pub mod ops; +mod ord; + +pub use self::fold::Foldable; +pub use self::ord::SqlOrd; + +use crate::expression::TypedExpressionType; +use crate::query_builder::QueryId; + +/// The boolean SQL type. +/// +/// On backends without a native boolean type, +/// this is emulated with the smallest supported integer. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`bool`][bool] +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`bool`][bool] +/// +/// [bool]: https://doc.rust-lang.org/nightly/std/primitive.bool.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 16, array_oid = 1000))] +#[diesel(sqlite_type(name = "Integer"))] +#[diesel(mysql_type(name = "Tiny"))] +pub struct Bool; + +/// The tiny integer SQL type. +/// +/// This is only available on MySQL. +/// Keep in mind that `diesel print-schema` will see `TINYINT(1)` as `Bool`, +/// not `TinyInt`. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`i8`][i8] +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`i8`][i8] +/// +/// [i8]: https://doc.rust-lang.org/nightly/std/primitive.i8.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(mysql_type(name = "Tiny"))] +pub struct TinyInt; +#[doc(hidden)] +pub type Tinyint = TinyInt; + +/// The small integer SQL type. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`i16`][i16] +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`i16`][i16] +/// +/// [i16]: https://doc.rust-lang.org/nightly/std/primitive.i16.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 21, array_oid = 1005))] +#[diesel(sqlite_type(name = "SmallInt"))] +#[diesel(mysql_type(name = "Short"))] +pub struct SmallInt; +#[doc(hidden)] +pub type Int2 = SmallInt; +#[doc(hidden)] +pub type Smallint = SmallInt; + +/// The integer SQL type. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`i32`][i32] +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`i32`][i32] +/// +/// [i32]: https://doc.rust-lang.org/nightly/std/primitive.i32.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 23, array_oid = 1007))] +#[diesel(sqlite_type(name = "Integer"))] +#[diesel(mysql_type(name = "Long"))] +pub struct Integer; +#[doc(hidden)] +pub type Int4 = Integer; + +/// The big integer SQL type. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`i64`][i64] +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`i64`][i64] +/// +/// [i64]: https://doc.rust-lang.org/nightly/std/primitive.i64.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 20, array_oid = 1016))] +#[diesel(sqlite_type(name = "Long"))] +#[diesel(mysql_type(name = "LongLong"))] +pub struct BigInt; +#[doc(hidden)] +pub type Int8 = BigInt; +#[doc(hidden)] +pub type Bigint = BigInt; + +/// The float SQL type. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`f32`][f32] +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`f32`][f32] +/// +/// [f32]: https://doc.rust-lang.org/nightly/std/primitive.f32.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 700, array_oid = 1021))] +#[diesel(sqlite_type(name = "Float"))] +#[diesel(mysql_type(name = "Float"))] +pub struct Float; +#[doc(hidden)] +pub type Float4 = Float; + +/// The double precision float SQL type. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`f64`][f64] +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`f64`][f64] +/// +/// [f64]: https://doc.rust-lang.org/nightly/std/primitive.f64.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 701, array_oid = 1022))] +#[diesel(sqlite_type(name = "Double"))] +#[diesel(mysql_type(name = "Double"))] +pub struct Double; +#[doc(hidden)] +pub type Float8 = Double; + +/// The arbitrary precision numeric SQL type. +/// +/// This type is only supported on PostgreSQL and MySQL. +/// On SQLite, [`Double`] should be used instead. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`bigdecimal::BigDecimal`] with `feature = ["numeric"]` +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`bigdecimal::BigDecimal`] with `feature = ["numeric"]` +/// +/// [`bigdecimal::BigDecimal`]: /bigdecimal/struct.BigDecimal.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 1700, array_oid = 1231))] +#[diesel(mysql_type(name = "Numeric"))] +#[diesel(sqlite_type(name = "Double"))] +pub struct Numeric; + +/// Alias for `Numeric` +pub type Decimal = Numeric; + +/// The text SQL type. +/// +/// On all backends strings must be valid UTF-8. +/// On PostgreSQL strings must not include nul bytes. +/// +/// Schema inference will treat all variants of `TEXT` as this type (e.g. +/// `VARCHAR`, `MEDIUMTEXT`, etc). +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`String`] +/// - [`&str`][str] +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`String`] +/// +/// [str]: https://doc.rust-lang.org/nightly/std/primitive.str.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 25, array_oid = 1009))] +#[diesel(sqlite_type(name = "Text"))] +#[diesel(mysql_type(name = "String"))] +pub struct Text; + +/// The SQL `VARCHAR` type +/// +/// This type is generally interchangeable with `TEXT`, so Diesel has this as an +/// alias rather than a separate type (Diesel does not currently support +/// implicit coercions). +/// +/// One notable exception to this is with arrays on PG. `TEXT[]` cannot be +/// coerced to `VARCHAR[]`. It is recommended that you always use `TEXT[]` if +/// you need a string array on PG. +pub type VarChar = Text; +#[doc(hidden)] +pub type Varchar = VarChar; +#[doc(hidden)] +pub type Char = Text; +#[doc(hidden)] +pub type Tinytext = Text; +#[doc(hidden)] +pub type Mediumtext = Text; +#[doc(hidden)] +pub type Longtext = Text; + +/// The binary SQL type. +/// +/// Schema inference will treat all variants of `BLOB` as this type (e.g. +/// `VARBINARY`, `MEDIUMBLOB`, etc). +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`Vec`][Vec] +/// - [`&[u8]`][slice] +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`Vec`][Vec] +/// +/// [Vec]: std::vec::Vec +/// [slice]: https://doc.rust-lang.org/nightly/std/primitive.slice.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 17, array_oid = 1001))] +#[diesel(sqlite_type(name = "Binary"))] +#[diesel(mysql_type(name = "Blob"))] +pub struct Binary; + +#[doc(hidden)] +pub type Tinyblob = Binary; +#[doc(hidden)] +pub type Blob = Binary; +#[doc(hidden)] +pub type Mediumblob = Binary; +#[doc(hidden)] +pub type Longblob = Binary; +#[doc(hidden)] +pub type Varbinary = Binary; +#[doc(hidden)] +pub type Bit = Binary; + +/// The date SQL type. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`chrono::NaiveDate`][NaiveDate] with `feature = "chrono"` +/// - [`time::Date`][Date] with `feature = "time"` +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`chrono::NaiveDate`][NaiveDate] with `feature = "chrono"` +/// - [`time::Date`][Date] with `feature = "time"` +/// +/// [NaiveDate]: https://docs.rs/chrono/*/chrono/naive/struct.NaiveDate.html +/// [Date]: https://docs.rs/time/0.3.9/time/struct.Date.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 1082, array_oid = 1182))] +#[diesel(sqlite_type(name = "Text"))] +#[diesel(mysql_type(name = "Date"))] +pub struct Date; + +/// The interval SQL type. +/// +/// This type is currently only implemented for PostgreSQL. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`PgInterval`] which can be constructed using [`IntervalDsl`] +/// - [`chrono::Duration`][Duration] with `feature = "chrono"` +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`PgInterval`] which can be constructed using [`IntervalDsl`] +/// - [`chrono::Duration`][Duration] with `feature = "chrono"` +/// (There might be some information loss due to special behavior for literal `month` (or longer) intervals; +/// Please read official documentation of [PostgreSQL Interval].) +/// +/// [`PgInterval`]: ../pg/data_types/struct.PgInterval.html +/// [`IntervalDsl`]: ../pg/expression/extensions/trait.IntervalDsl.html +/// [Duration]: https://docs.rs/chrono/*/chrono/type.Duration.html +/// [PostgreSQL Interval]: https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 1186, array_oid = 1187))] +pub struct Interval; + +/// The time SQL type. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`chrono::NaiveTime`][NaiveTime] with `feature = "chrono"` +/// - [`time::Time`][Time] with `feature = "time"` +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`chrono::NaiveTime`][NaiveTime] with `feature = "chrono"` +/// - [`time::Time`][Time] with `feature = "time"` +/// +/// [NaiveTime]: /chrono/naive/time/struct.NaiveTime.html +/// [Time]: /time/struct.Time.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 1083, array_oid = 1183))] +#[diesel(sqlite_type(name = "Text"))] +#[diesel(mysql_type(name = "Time"))] +pub struct Time; + +/// The timestamp SQL type. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - [`std::time::SystemTime`][SystemTime] (PG only) +/// - [`chrono::NaiveDateTime`][NaiveDateTime] with `feature = "chrono"` +/// - [`time::PrimitiveDateTime`] with `feature = "time"` +/// - [`time::OffsetDateTime`] with `feature = "time"` (MySQL only) +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - [`std::time::SystemTime`][SystemTime] (PG only) +/// - [`chrono::NaiveDateTime`][NaiveDateTime] with `feature = "chrono"` +/// - [`time::PrimitiveDateTime`] with `feature = "time"` +/// - [`time::OffsetDateTime`] with `feature = "time"` (MySQL only) +/// +/// [SystemTime]: std::time::SystemTime +#[cfg_attr( + feature = "chrono", + doc = " [NaiveDateTime]: chrono::naive::NaiveDateTime" +)] +#[cfg_attr( + not(feature = "chrono"), + doc = " [NaiveDateTime]: https://docs.rs/chrono/*/chrono/naive/struct.NaiveDateTime.html" +)] +#[cfg_attr( + feature = "time", + doc = " [`time::PrimitiveDateTime`]: time::PrimitiveDateTime" +)] +#[cfg_attr( + not(feature = "time"), + doc = " [`time::PrimitiveDateTime`]: https://docs.rs/time/0.3.9/time/struct.PrimitiveDateTime.html" +)] +#[cfg_attr( + feature = "time", + doc = " [`time::OffsetDateTime`]: time::OffsetDateTime" +)] +#[cfg_attr( + not(feature = "time"), + doc = " [`time::OffsetDateTime`]: https://docs.rs/time/0.3.9/time/struct.OffsetDateTime.html" +)] +/// [Timespec]: /time/struct.Timespec.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 1114, array_oid = 1115))] +#[diesel(sqlite_type(name = "Text"))] +#[diesel(mysql_type(name = "Timestamp"))] +pub struct Timestamp; + +/// The JSON SQL type. This type can only be used with `feature = +/// "serde_json"` +/// +/// For postgresql you should normally prefer [`Jsonb`](struct.Jsonb.html) instead, +/// for the reasons discussed there. +/// +/// ### [`ToSql`] impls +/// +/// - [`serde_json::Value`] +/// +/// ### [`FromSql`] impls +/// +/// - [`serde_json::Value`] +/// +/// [`ToSql`]: /serialize/trait.ToSql.html +/// [`FromSql`]: /deserialize/trait.FromSql.html +/// [`serde_json::Value`]: /../serde_json/value/enum.Value.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[diesel(postgres_type(oid = 114, array_oid = 199))] +#[diesel(mysql_type(name = "String"))] +pub struct Json; + +/// The nullable SQL type. +/// +/// This wraps another SQL type to indicate that it can be null. +/// By default all values are assumed to be `NOT NULL`. +/// +/// ### [`ToSql`](crate::serialize::ToSql) impls +/// +/// - Any `T` which implements `ToSql` +/// - `Option` for any `T` which implements `ToSql` +/// +/// ### [`FromSql`](crate::deserialize::FromSql) impls +/// +/// - `Option` for any `T` which implements `FromSql` +#[derive(Debug, Clone, Copy, Default)] +pub struct Nullable(ST); + +impl SqlType for Nullable +where + ST: SqlType, +{ + type IsNull = is_nullable::IsNullable; +} + +#[doc(inline)] +#[cfg(feature = "postgres_backend")] +pub use crate::pg::sql_types::*; + +#[doc(inline)] +#[cfg(feature = "mysql_backend")] +pub use crate::mysql::sql_types::{Datetime, Unsigned}; + +#[doc(inline)] +#[cfg(feature = "sqlite")] +pub use crate::sqlite::sql_types::Timestamptz as TimestamptzSqlite; + +/// Indicates that a SQL type exists for a backend. +/// +/// This trait can be derived using the [`SqlType` derive](derive@SqlType) +/// +/// # Example +/// +/// ```rust +/// #[derive(diesel::sql_types::SqlType)] +/// #[diesel(postgres_type(oid = 23, array_oid = 1007))] +/// #[diesel(sqlite_type(name = "Integer"))] +/// #[diesel(mysql_type(name = "Long"))] +/// pub struct Integer; +/// ``` +pub trait HasSqlType: TypeMetadata { + /// Fetch the metadata for the given type + /// + /// This method may use `lookup` to do dynamic runtime lookup. Implementors + /// of this method should not do dynamic lookup unless absolutely necessary + fn metadata(lookup: &mut Self::MetadataLookup) -> Self::TypeMetadata; +} + +/// Information about how a backend stores metadata about given SQL types +pub trait TypeMetadata { + /// The actual type used to represent metadata. + /// + /// On PostgreSQL, this is the type's OID. + /// On MySQL and SQLite, this is an enum representing all storage classes + /// they support. + type TypeMetadata; + /// The type used for runtime lookup of metadata. + /// + /// For most backends, which don't support user defined types, this will + /// be `()`. + type MetadataLookup: ?Sized; +} + +/// Converts a type which may or may not be nullable into its nullable +/// representation. +pub trait IntoNullable { + /// The nullable representation of this type. + /// + /// For all types except `Nullable`, this will be `Nullable`. + type Nullable; +} + +impl IntoNullable for T +where + T: SqlType + SingleValue, +{ + type Nullable = Nullable; +} + +impl IntoNullable for Nullable +where + T: SqlType, +{ + type Nullable = Self; +} + +/// Converts a type which may or may not be nullable into its not nullable +/// representation. +pub trait IntoNotNullable { + /// The not nullable representation of this type. + /// + /// For `Nullable`, this will be `T` otherwise the type itself + type NotNullable; +} + +impl IntoNotNullable for T +where + T: SqlType, +{ + type NotNullable = T; +} + +impl IntoNotNullable for Nullable +where + T: SqlType, +{ + type NotNullable = T; +} + +/// A marker trait indicating that a SQL type represents a single value, as +/// opposed to a list of values. +/// +/// This trait should generally be implemented for all SQL types with the +/// exception of Rust tuples. If a column could have this as its type, this +/// trait should be implemented. +/// +/// # Deriving +/// +/// This trait is automatically implemented by [`#[derive(SqlType)]`](derive@SqlType) +/// +pub trait SingleValue: SqlType {} + +impl SingleValue for Nullable {} + +#[doc(inline)] +pub use diesel_derives::DieselNumericOps; +#[doc(inline)] +pub use diesel_derives::SqlType; + +/// A marker trait for SQL types +/// +/// # Deriving +/// +/// This trait is automatically implemented by [`#[derive(SqlType)]`](derive@SqlType) +/// which sets `IsNull` to [`is_nullable::NotNull`] +/// +pub trait SqlType: 'static { + /// Is this type nullable? + /// + /// This type should always be one of the structs in the ['is_nullable`] + /// module. See the documentation of those structs for more details. + /// + /// ['is_nullable`]: is_nullable + type IsNull: OneIsNullable + OneIsNullable; +} + +/// Is one value of `IsNull` nullable? +/// +/// You should never implement this trait. +pub trait OneIsNullable { + /// See the trait documentation + type Out: OneIsNullable + OneIsNullable; +} + +/// Are both values of `IsNull` are nullable? +pub trait AllAreNullable { + /// See the trait documentation + type Out: AllAreNullable + AllAreNullable; +} + +/// A type level constructor for maybe nullable types +/// +/// Constructs either `Nullable` (for `Self` == `is_nullable::IsNullable`) +/// or `O` (for `Self` == `is_nullable::NotNull`) +pub trait MaybeNullableType { + /// See the trait documentation + type Out: SqlType + TypedExpressionType; +} + +/// Possible values for `SqlType::IsNullable` +pub mod is_nullable { + use super::*; + + /// No, this type cannot be null as it is marked as `NOT NULL` at database level + /// + /// This should be chosen for basically all manual impls of `SqlType` + /// beside implementing your own `Nullable<>` wrapper type + #[derive(Debug, Clone, Copy)] + pub struct NotNull; + + /// Yes, this type can be null + /// + /// The only diesel provided `SqlType` that uses this value is [`Nullable`] + /// + /// [`Nullable`]: Nullable + #[derive(Debug, Clone, Copy)] + pub struct IsNullable; + + impl OneIsNullable for NotNull { + type Out = NotNull; + } + + impl OneIsNullable for NotNull { + type Out = IsNullable; + } + + impl OneIsNullable for IsNullable { + type Out = IsNullable; + } + + impl OneIsNullable for IsNullable { + type Out = IsNullable; + } + + impl AllAreNullable for NotNull { + type Out = NotNull; + } + + impl AllAreNullable for NotNull { + type Out = NotNull; + } + + impl AllAreNullable for IsNullable { + type Out = NotNull; + } + + impl AllAreNullable for IsNullable { + type Out = IsNullable; + } + + impl MaybeNullableType for NotNull + where + O: SqlType + TypedExpressionType, + { + type Out = O; + } + + impl MaybeNullableType for IsNullable + where + O: SqlType, + Nullable: TypedExpressionType, + { + type Out = Nullable; + } + + /// Represents the output type of [`MaybeNullableType`] + pub type MaybeNullable = >::Out; + + /// Represents the output type of [`OneIsNullable`] + /// for two given SQL types + pub type IsOneNullable = + as OneIsNullable>>::Out; + + /// Represents the output type of [`AllAreNullable`] + /// for two given SQL types + pub type AreAllNullable = + as AllAreNullable>>::Out; + + /// Represents if the SQL type is nullable or not + pub type IsSqlTypeNullable = ::IsNull; +} + +/// A marker trait for accepting expressions of the type `Bool` and +/// `Nullable` in the same place +#[diagnostic::on_unimplemented( + message = "`{Self}` is neither `diesel::sql_types::Bool` nor `diesel::sql_types::Nullable`", + note = "try to provide an expression that produces one of the expected sql types" +)] +pub trait BoolOrNullableBool {} + +impl BoolOrNullableBool for Bool {} +impl BoolOrNullableBool for Nullable {} + +#[doc(inline)] +pub use crate::expression::expression_types::Untyped; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sql_types/ops.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sql_types/ops.rs new file mode 100644 index 000000000..1d4ebd9d8 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sql_types/ops.rs @@ -0,0 +1,184 @@ +//! Represents the output of numeric operators in SQL +//! +//! Apps should not need to concern themselves with this module. +//! If you are looking for where the actual implementation of `std::ops::Add` +//! and friends are generated for columns, see +//! [`numeric_expr!`](super::super::numeric_expr!). +//! +//! Crates which add new types which allow numeric operators should implement +//! these traits to specify what the output is for a given right hand side. +//! +//! Unlike the traits in `std::ops`, the right hand side is an associated type +//! rather than a type parameter. The biggest drawback of this is that any type +//! can only have one right hand type which can be added/subtracted, etc. The +//! most immediately noticeable effect of this is that you cannot add a nullable +//! number to one that is not nullable. +//! +//! The reason for this is because of the impl of `std::ops::Add` that we need +//! to be able to write. We want the right hand side to allow Rust values which +//! should be sent as bind parameters, not just other Diesel expressions. That +//! means the impl would look like this: +//! +//! ```ignore +//! impl std::ops::Add for my_column +//! where +//! T: AsExpression, +//! my_column::SqlType: diesel::ops::Add, +//! ``` +//! +//! This impl is not valid in Rust, as `ST` is not constrained by the trait or +//! the implementing type. If there were two valid types for `ST` which +//! satisfied all constraints, Rust would not know which one to use, and there +//! would be no way for the user to specify which one should be used. + +use super::*; + +/// Represents SQL types which can be added. +pub trait Add { + /// The SQL type which can be added to this one + type Rhs: SqlType; + /// The SQL type of the result of adding `Rhs` to `Self` + type Output: SqlType; +} + +/// Represents SQL types which can be subtracted. +pub trait Sub { + /// The SQL type which can be subtracted from this one + type Rhs: SqlType; + /// The SQL type of the result of subtracting `Rhs` from `Self` + type Output: SqlType; +} + +/// Represents SQL types which can be multiplied. +pub trait Mul { + /// The SQL type which this can be multiplied by + type Rhs: SqlType; + /// The SQL type of the result of multiplying `Self` by `Rhs` + type Output: SqlType; +} + +/// Represents SQL types which can be divided. +pub trait Div { + /// The SQL type which this one can be divided by + type Rhs: SqlType; + /// The SQL type of the result of dividing `Self` by `Rhs` + type Output: SqlType; +} + +macro_rules! numeric_type { + ($($tpe: ident),*) => { + $( + impl Add for $tpe { + type Rhs = $tpe; + type Output = $tpe; + } + + impl Sub for $tpe { + type Rhs = $tpe; + type Output = $tpe; + } + + impl Mul for $tpe { + type Rhs = $tpe; + type Output = $tpe; + } + + impl Div for $tpe { + type Rhs = $tpe; + type Output = $tpe; + } + )* + } +} + +numeric_type!(SmallInt, Integer, BigInt, Float, Double, Numeric); + +impl Add for Time { + type Rhs = Interval; + type Output = Time; +} + +impl Sub for Time { + type Rhs = Interval; + type Output = Time; +} + +impl Add for Date { + type Rhs = Interval; + type Output = Timestamp; +} + +impl Sub for Date { + type Rhs = Interval; + type Output = Timestamp; +} + +impl Add for Timestamp { + type Rhs = Interval; + type Output = Timestamp; +} + +impl Sub for Timestamp { + type Rhs = Interval; + type Output = Timestamp; +} + +impl Add for Interval { + type Rhs = Interval; + type Output = Interval; +} + +impl Sub for Interval { + type Rhs = Interval; + type Output = Interval; +} + +impl Mul for Interval { + type Rhs = Integer; + type Output = Interval; +} + +impl Div for Interval { + type Rhs = Integer; + type Output = Interval; +} + +impl Add for Nullable +where + T: Add + SqlType, + T::Rhs: SqlType, + T::Output: SqlType, +{ + type Rhs = Nullable; + type Output = Nullable; +} + +impl Sub for Nullable +where + T: Sub + SqlType, + T::Rhs: SqlType, + T::Output: SqlType, +{ + type Rhs = Nullable; + type Output = Nullable; +} + +impl Mul for Nullable +where + T: Mul + SqlType, + T::Rhs: SqlType, + T::Output: SqlType, +{ + type Rhs = Nullable; + type Output = Nullable; +} + +impl Div for Nullable +where + T: Div + SqlType, + T::Rhs: SqlType, + T::Output: SqlType, +{ + type Rhs = Nullable; + type Output = Nullable; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sql_types/ord.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sql_types/ord.rs new file mode 100644 index 000000000..1ea250360 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sql_types/ord.rs @@ -0,0 +1,33 @@ +use crate::sql_types::{self, is_nullable, SqlType}; + +/// Marker trait for types which can be used with `MAX` and `MIN` +#[diagnostic::on_unimplemented( + message = "expressions of the type `{Self}` cannot be ordered by the database" +)] +pub trait SqlOrd: SqlType {} + +impl SqlOrd for sql_types::SmallInt {} +impl SqlOrd for sql_types::Integer {} +impl SqlOrd for sql_types::BigInt {} +impl SqlOrd for sql_types::Float {} +impl SqlOrd for sql_types::Double {} +impl SqlOrd for sql_types::Text {} +impl SqlOrd for sql_types::Date {} +impl SqlOrd for sql_types::Interval {} +impl SqlOrd for sql_types::Time {} +impl SqlOrd for sql_types::Timestamp {} +impl SqlOrd for sql_types::Nullable where T: SqlOrd + SqlType {} + +#[cfg(feature = "postgres_backend")] +impl SqlOrd for sql_types::Timestamptz {} +#[cfg(feature = "postgres_backend")] +impl SqlOrd for sql_types::Array {} + +#[cfg(feature = "mysql_backend")] +impl SqlOrd for sql_types::Datetime {} +#[cfg(feature = "mysql_backend")] +impl SqlOrd for sql_types::Unsigned {} +#[cfg(feature = "mysql_backend")] +impl SqlOrd for sql_types::Unsigned {} +#[cfg(feature = "mysql_backend")] +impl SqlOrd for sql_types::Unsigned {} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/backend.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/backend.rs new file mode 100644 index 000000000..80ca6fc0b --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/backend.rs @@ -0,0 +1,87 @@ +//! The SQLite backend + +use super::connection::{SqliteBindCollector, SqliteValue}; +use super::query_builder::SqliteQueryBuilder; +use crate::backend::*; +use crate::sql_types::TypeMetadata; + +/// The SQLite backend +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)] +pub struct Sqlite; + +/// Determines how a bind parameter is given to SQLite +/// +/// Diesel deals with bind parameters after serialization as opaque blobs of +/// bytes. However, SQLite instead has several functions where it expects the +/// relevant C types. +/// +/// The variants of this struct determine what bytes are expected from +/// `ToSql` impls. +#[allow(missing_debug_implementations)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum SqliteType { + /// Bind using `sqlite3_bind_blob` + Binary, + /// Bind using `sqlite3_bind_text` + Text, + /// `bytes` should contain an `f32` + Float, + /// `bytes` should contain an `f64` + Double, + /// `bytes` should contain an `i16` + SmallInt, + /// `bytes` should contain an `i32` + Integer, + /// `bytes` should contain an `i64` + Long, +} + +impl Backend for Sqlite { + type QueryBuilder = SqliteQueryBuilder; + type RawValue<'a> = SqliteValue<'a, 'a, 'a>; + type BindCollector<'a> = SqliteBindCollector<'a>; +} + +impl TypeMetadata for Sqlite { + type TypeMetadata = SqliteType; + type MetadataLookup = (); +} + +impl SqlDialect for Sqlite { + #[cfg(not(feature = "returning_clauses_for_sqlite_3_35"))] + type ReturningClause = sql_dialect::returning_clause::DoesNotSupportReturningClause; + #[cfg(feature = "returning_clauses_for_sqlite_3_35")] + type ReturningClause = SqliteReturningClause; + + type OnConflictClause = SqliteOnConflictClause; + + type InsertWithDefaultKeyword = + sql_dialect::default_keyword_for_insert::DoesNotSupportDefaultKeyword; + type BatchInsertSupport = SqliteBatchInsert; + type ConcatClause = sql_dialect::concat_clause::ConcatWithPipesClause; + type DefaultValueClauseForInsert = sql_dialect::default_value_clause::AnsiDefaultValueClause; + + type EmptyFromClauseSyntax = sql_dialect::from_clause_syntax::AnsiSqlFromClauseSyntax; + type SelectStatementSyntax = sql_dialect::select_statement_syntax::AnsiSqlSelectStatement; + + type ExistsSyntax = sql_dialect::exists_syntax::AnsiSqlExistsSyntax; + type ArrayComparison = sql_dialect::array_comparison::AnsiSqlArrayComparison; + type AliasSyntax = sql_dialect::alias_syntax::AsAliasSyntax; +} + +impl DieselReserveSpecialization for Sqlite {} +impl TrustedBackend for Sqlite {} + +#[derive(Debug, Copy, Clone)] +pub struct SqliteOnConflictClause; + +impl sql_dialect::on_conflict_clause::SupportsOnConflictClause for SqliteOnConflictClause {} +impl sql_dialect::on_conflict_clause::PgLikeOnConflictClause for SqliteOnConflictClause {} + +#[derive(Debug, Copy, Clone)] +pub struct SqliteBatchInsert; + +#[derive(Debug, Copy, Clone)] +pub struct SqliteReturningClause; + +impl sql_dialect::returning_clause::SupportsReturningClause for SqliteReturningClause {} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/bind_collector.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/bind_collector.rs new file mode 100644 index 000000000..e1ce91e43 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/bind_collector.rs @@ -0,0 +1,279 @@ +use crate::query_builder::{BindCollector, MoveableBindCollector}; +use crate::serialize::{IsNull, Output}; +use crate::sql_types::HasSqlType; +use crate::sqlite::{Sqlite, SqliteType}; +use crate::QueryResult; + +#[derive(Debug, Default)] +pub struct SqliteBindCollector<'a> { + pub(in crate::sqlite) binds: Vec<(InternalSqliteBindValue<'a>, SqliteType)>, +} + +impl SqliteBindCollector<'_> { + pub(in crate::sqlite) fn new() -> Self { + Self { binds: Vec::new() } + } +} + +/// This type represents a value bound to +/// a sqlite prepared statement +/// +/// It can be constructed via the various `From` implementations +#[derive(Debug)] +pub struct SqliteBindValue<'a> { + pub(in crate::sqlite) inner: InternalSqliteBindValue<'a>, +} + +impl From for SqliteBindValue<'_> { + fn from(i: i32) -> Self { + Self { + inner: InternalSqliteBindValue::I32(i), + } + } +} + +impl From for SqliteBindValue<'_> { + fn from(i: i64) -> Self { + Self { + inner: InternalSqliteBindValue::I64(i), + } + } +} + +impl From for SqliteBindValue<'_> { + fn from(f: f64) -> Self { + Self { + inner: InternalSqliteBindValue::F64(f), + } + } +} + +impl<'a, T> From> for SqliteBindValue<'a> +where + T: Into>, +{ + fn from(o: Option) -> Self { + match o { + Some(v) => v.into(), + None => Self { + inner: InternalSqliteBindValue::Null, + }, + } + } +} + +impl<'a> From<&'a str> for SqliteBindValue<'a> { + fn from(s: &'a str) -> Self { + Self { + inner: InternalSqliteBindValue::BorrowedString(s), + } + } +} + +impl From for SqliteBindValue<'_> { + fn from(s: String) -> Self { + Self { + inner: InternalSqliteBindValue::String(s.into_boxed_str()), + } + } +} + +impl From> for SqliteBindValue<'_> { + fn from(b: Vec) -> Self { + Self { + inner: InternalSqliteBindValue::Binary(b.into_boxed_slice()), + } + } +} + +impl<'a> From<&'a [u8]> for SqliteBindValue<'a> { + fn from(b: &'a [u8]) -> Self { + Self { + inner: InternalSqliteBindValue::BorrowedBinary(b), + } + } +} + +#[derive(Debug)] +pub(crate) enum InternalSqliteBindValue<'a> { + BorrowedString(&'a str), + String(Box), + BorrowedBinary(&'a [u8]), + Binary(Box<[u8]>), + I32(i32), + I64(i64), + F64(f64), + Null, +} + +impl std::fmt::Display for InternalSqliteBindValue<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let n = match self { + InternalSqliteBindValue::BorrowedString(_) | InternalSqliteBindValue::String(_) => { + "Text" + } + InternalSqliteBindValue::BorrowedBinary(_) | InternalSqliteBindValue::Binary(_) => { + "Binary" + } + InternalSqliteBindValue::I32(_) | InternalSqliteBindValue::I64(_) => "Integer", + InternalSqliteBindValue::F64(_) => "Float", + InternalSqliteBindValue::Null => "Null", + }; + f.write_str(n) + } +} + +impl InternalSqliteBindValue<'_> { + #[allow(unsafe_code)] // ffi function calls + pub(in crate::sqlite) fn result_of( + self, + ctx: &mut libsqlite3_sys::sqlite3_context, + ) -> Result<(), std::num::TryFromIntError> { + use libsqlite3_sys as ffi; + use std::os::raw as libc; + // This unsafe block assumes the following invariants: + // + // - `ctx` points to valid memory + unsafe { + match self { + InternalSqliteBindValue::BorrowedString(s) => ffi::sqlite3_result_text( + ctx, + s.as_ptr() as *const libc::c_char, + s.len().try_into()?, + ffi::SQLITE_TRANSIENT(), + ), + InternalSqliteBindValue::String(s) => ffi::sqlite3_result_text( + ctx, + s.as_ptr() as *const libc::c_char, + s.len().try_into()?, + ffi::SQLITE_TRANSIENT(), + ), + InternalSqliteBindValue::Binary(b) => ffi::sqlite3_result_blob( + ctx, + b.as_ptr() as *const libc::c_void, + b.len().try_into()?, + ffi::SQLITE_TRANSIENT(), + ), + InternalSqliteBindValue::BorrowedBinary(b) => ffi::sqlite3_result_blob( + ctx, + b.as_ptr() as *const libc::c_void, + b.len().try_into()?, + ffi::SQLITE_TRANSIENT(), + ), + InternalSqliteBindValue::I32(i) => ffi::sqlite3_result_int(ctx, i as libc::c_int), + InternalSqliteBindValue::I64(l) => ffi::sqlite3_result_int64(ctx, l), + InternalSqliteBindValue::F64(d) => { + ffi::sqlite3_result_double(ctx, d as libc::c_double) + } + InternalSqliteBindValue::Null => ffi::sqlite3_result_null(ctx), + } + } + Ok(()) + } +} + +impl<'a> BindCollector<'a, Sqlite> for SqliteBindCollector<'a> { + type Buffer = SqliteBindValue<'a>; + + fn push_bound_value(&mut self, bind: &'a U, metadata_lookup: &mut ()) -> QueryResult<()> + where + Sqlite: crate::sql_types::HasSqlType, + U: crate::serialize::ToSql + ?Sized, + { + let value = SqliteBindValue { + inner: InternalSqliteBindValue::Null, + }; + let mut to_sql_output = Output::new(value, metadata_lookup); + let is_null = bind + .to_sql(&mut to_sql_output) + .map_err(crate::result::Error::SerializationError)?; + let bind = to_sql_output.into_inner(); + let metadata = Sqlite::metadata(metadata_lookup); + self.binds.push(( + match is_null { + IsNull::No => bind.inner, + IsNull::Yes => InternalSqliteBindValue::Null, + }, + metadata, + )); + Ok(()) + } + + fn push_null_value(&mut self, metadata: SqliteType) -> QueryResult<()> { + self.binds.push((InternalSqliteBindValue::Null, metadata)); + Ok(()) + } +} + +#[derive(Debug)] +enum OwnedSqliteBindValue { + String(Box), + Binary(Box<[u8]>), + I32(i32), + I64(i64), + F64(f64), + Null, +} + +impl<'a> std::convert::From<&InternalSqliteBindValue<'a>> for OwnedSqliteBindValue { + fn from(value: &InternalSqliteBindValue<'a>) -> Self { + match value { + InternalSqliteBindValue::String(s) => Self::String(s.clone()), + InternalSqliteBindValue::BorrowedString(s) => { + Self::String(String::from(*s).into_boxed_str()) + } + InternalSqliteBindValue::Binary(b) => Self::Binary(b.clone()), + InternalSqliteBindValue::BorrowedBinary(s) => { + Self::Binary(Vec::from(*s).into_boxed_slice()) + } + InternalSqliteBindValue::I32(val) => Self::I32(*val), + InternalSqliteBindValue::I64(val) => Self::I64(*val), + InternalSqliteBindValue::F64(val) => Self::F64(*val), + InternalSqliteBindValue::Null => Self::Null, + } + } +} + +impl std::convert::From<&OwnedSqliteBindValue> for InternalSqliteBindValue<'_> { + fn from(value: &OwnedSqliteBindValue) -> Self { + match value { + OwnedSqliteBindValue::String(s) => Self::String(s.clone()), + OwnedSqliteBindValue::Binary(b) => Self::Binary(b.clone()), + OwnedSqliteBindValue::I32(val) => Self::I32(*val), + OwnedSqliteBindValue::I64(val) => Self::I64(*val), + OwnedSqliteBindValue::F64(val) => Self::F64(*val), + OwnedSqliteBindValue::Null => Self::Null, + } + } +} + +#[derive(Debug)] +/// Sqlite bind collector data that is movable across threads +pub struct SqliteBindCollectorData { + binds: Vec<(OwnedSqliteBindValue, SqliteType)>, +} + +impl MoveableBindCollector for SqliteBindCollector<'_> { + type BindData = SqliteBindCollectorData; + + fn moveable(&self) -> Self::BindData { + let mut binds = Vec::with_capacity(self.binds.len()); + for b in self + .binds + .iter() + .map(|(bind, tpe)| (OwnedSqliteBindValue::from(bind), *tpe)) + { + binds.push(b); + } + SqliteBindCollectorData { binds } + } + + fn append_bind_data(&mut self, from: &Self::BindData) { + self.binds.reserve_exact(from.binds.len()); + self.binds.extend( + from.binds + .iter() + .map(|(bind, tpe)| (InternalSqliteBindValue::from(bind), *tpe)), + ); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/diesel_manage_updated_at.sql b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/diesel_manage_updated_at.sql new file mode 100644 index 000000000..83c3d33d4 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/diesel_manage_updated_at.sql @@ -0,0 +1,11 @@ +CREATE TRIGGER __diesel_manage_updated_at_{table_name} +AFTER UPDATE ON {table_name} +FOR EACH ROW WHEN + old.updated_at IS NULL AND + new.updated_at IS NULL OR + old.updated_at == new.updated_at +BEGIN + UPDATE {table_name} + SET updated_at = CURRENT_TIMESTAMP + WHERE ROWID = new.ROWID; +END diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/functions.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/functions.rs new file mode 100644 index 000000000..68891f339 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/functions.rs @@ -0,0 +1,257 @@ +extern crate libsqlite3_sys as ffi; + +use super::raw::RawConnection; +use super::row::PrivateSqliteRow; +use super::{Sqlite, SqliteAggregateFunction, SqliteBindValue}; +use crate::backend::Backend; +use crate::deserialize::{FromSqlRow, StaticallySizedRow}; +use crate::result::{DatabaseErrorKind, Error, QueryResult}; +use crate::row::{Field, PartialRow, Row, RowIndex, RowSealed}; +use crate::serialize::{IsNull, Output, ToSql}; +use crate::sql_types::HasSqlType; +use crate::sqlite::connection::bind_collector::InternalSqliteBindValue; +use crate::sqlite::connection::sqlite_value::OwnedSqliteValue; +use crate::sqlite::SqliteValue; +use std::cell::{Ref, RefCell}; +use std::marker::PhantomData; +use std::mem::ManuallyDrop; +use std::ops::DerefMut; +use std::rc::Rc; + +pub(super) fn register( + conn: &RawConnection, + fn_name: &str, + deterministic: bool, + mut f: F, +) -> QueryResult<()> +where + F: FnMut(&RawConnection, Args) -> Ret + std::panic::UnwindSafe + Send + 'static, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + Sqlite: HasSqlType, +{ + let fields_needed = Args::FIELD_COUNT; + if fields_needed > 127 { + return Err(Error::DatabaseError( + DatabaseErrorKind::UnableToSendCommand, + Box::new("SQLite functions cannot take more than 127 parameters".to_string()), + )); + } + + conn.register_sql_function(fn_name, fields_needed, deterministic, move |conn, args| { + let args = build_sql_function_args::(args)?; + + Ok(f(conn, args)) + })?; + Ok(()) +} + +pub(super) fn register_noargs( + conn: &RawConnection, + fn_name: &str, + deterministic: bool, + mut f: F, +) -> QueryResult<()> +where + F: FnMut() -> Ret + std::panic::UnwindSafe + Send + 'static, + Ret: ToSql, + Sqlite: HasSqlType, +{ + conn.register_sql_function(fn_name, 0, deterministic, move |_, _| Ok(f()))?; + Ok(()) +} + +pub(super) fn register_aggregate( + conn: &RawConnection, + fn_name: &str, +) -> QueryResult<()> +where + A: SqliteAggregateFunction + 'static + Send + std::panic::UnwindSafe, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + Sqlite: HasSqlType, +{ + let fields_needed = Args::FIELD_COUNT; + if fields_needed > 127 { + return Err(Error::DatabaseError( + DatabaseErrorKind::UnableToSendCommand, + Box::new("SQLite functions cannot take more than 127 parameters".to_string()), + )); + } + + conn.register_aggregate_function::( + fn_name, + fields_needed, + )?; + + Ok(()) +} + +pub(super) fn build_sql_function_args( + args: &mut [*mut ffi::sqlite3_value], +) -> Result +where + Args: FromSqlRow, +{ + let row = FunctionRow::new(args); + Args::build_from_row(&row).map_err(Error::DeserializationError) +} + +// clippy is wrong here, the let binding is required +// for lifetime reasons +#[allow(clippy::let_unit_value)] +pub(super) fn process_sql_function_result( + result: &'_ Ret, +) -> QueryResult> +where + Ret: ToSql, + Sqlite: HasSqlType, +{ + let mut metadata_lookup = (); + let value = SqliteBindValue { + inner: InternalSqliteBindValue::Null, + }; + let mut buf = Output::new(value, &mut metadata_lookup); + let is_null = result.to_sql(&mut buf).map_err(Error::SerializationError)?; + + if let IsNull::Yes = is_null { + Ok(InternalSqliteBindValue::Null) + } else { + Ok(buf.into_inner().inner) + } +} + +struct FunctionRow<'a> { + // we use `ManuallyDrop` to prevent dropping the content of the internal vector + // as this buffer is owned by sqlite not by diesel + args: Rc>>>, + field_count: usize, + marker: PhantomData<&'a ffi::sqlite3_value>, +} + +impl Drop for FunctionRow<'_> { + #[allow(unsafe_code)] // manual drop calls + fn drop(&mut self) { + if let Some(args) = Rc::get_mut(&mut self.args) { + if let PrivateSqliteRow::Duplicated { column_names, .. } = + DerefMut::deref_mut(RefCell::get_mut(args)) + { + if Rc::strong_count(column_names) == 1 { + // According the https://doc.rust-lang.org/std/mem/struct.ManuallyDrop.html#method.drop + // it's fine to just drop the values here + unsafe { std::ptr::drop_in_place(column_names as *mut _) } + } + } + } + } +} + +impl FunctionRow<'_> { + #[allow(unsafe_code)] // complicated ptr cast + fn new(args: &mut [*mut ffi::sqlite3_value]) -> Self { + let lengths = args.len(); + let args = unsafe { + Vec::from_raw_parts( + // This cast is safe because: + // * Casting from a pointer to an array to a pointer to the first array + // element is safe + // * Casting from a raw pointer to `NonNull` is safe, + // because `NonNull` is #[repr(transparent)] + // * Casting from `NonNull` to `OwnedSqliteValue` is safe, + // as the struct is `#[repr(transparent)] + // * Casting from `NonNull` to `Option>` as the documentation + // states: "This is so that enums may use this forbidden value as a discriminant – + // Option> has the same size as *mut T" + // * The last point remains true for `OwnedSqliteValue` as `#[repr(transparent)] + // guarantees the same layout as the inner type + // * It's unsafe to drop the vector (and the vector elements) + // because of this we wrap the vector (or better the Row) + // Into `ManualDrop` to prevent the dropping + args as *mut [*mut ffi::sqlite3_value] as *mut ffi::sqlite3_value + as *mut Option, + lengths, + lengths, + ) + }; + + Self { + field_count: lengths, + args: Rc::new(RefCell::new(ManuallyDrop::new( + PrivateSqliteRow::Duplicated { + values: args, + column_names: Rc::from(vec![None; lengths]), + }, + ))), + marker: PhantomData, + } + } +} + +impl RowSealed for FunctionRow<'_> {} + +impl<'a> Row<'a, Sqlite> for FunctionRow<'a> { + type Field<'f> + = FunctionArgument<'f> + where + 'a: 'f, + Self: 'f; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.field_count + } + + fn get<'b, I>(&'b self, idx: I) -> Option> + where + 'a: 'b, + Self: crate::row::RowIndex, + { + let col_idx = self.idx(idx)?; + Some(FunctionArgument { + args: self.args.borrow(), + col_idx, + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow<'_, Self::InnerPartialRow> { + PartialRow::new(self, range) + } +} + +impl RowIndex for FunctionRow<'_> { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count() { + Some(idx) + } else { + None + } + } +} + +impl<'a> RowIndex<&'a str> for FunctionRow<'_> { + fn idx(&self, _idx: &'a str) -> Option { + None + } +} + +struct FunctionArgument<'a> { + args: Ref<'a, ManuallyDrop>>, + col_idx: usize, +} + +impl<'a> Field<'a, Sqlite> for FunctionArgument<'a> { + fn field_name(&self) -> Option<&str> { + None + } + + fn is_null(&self) -> bool { + self.value().is_none() + } + + fn value(&self) -> Option<::RawValue<'_>> { + SqliteValue::new( + Ref::map(Ref::clone(&self.args), |drop| std::ops::Deref::deref(drop)), + self.col_idx, + ) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/mod.rs new file mode 100644 index 000000000..4f6e2e871 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/mod.rs @@ -0,0 +1,1013 @@ +extern crate libsqlite3_sys as ffi; + +mod bind_collector; +mod functions; +mod owned_row; +mod raw; +mod row; +mod serialized_database; +mod sqlite_value; +mod statement_iterator; +mod stmt; + +pub(in crate::sqlite) use self::bind_collector::SqliteBindCollector; +pub use self::bind_collector::SqliteBindValue; +pub use self::serialized_database::SerializedDatabase; +pub use self::sqlite_value::SqliteValue; + +use std::os::raw as libc; + +use self::raw::RawConnection; +use self::statement_iterator::*; +use self::stmt::{Statement, StatementUse}; +use super::SqliteAggregateFunction; +use crate::connection::instrumentation::StrQueryHelper; +use crate::connection::statement_cache::StatementCache; +use crate::connection::*; +use crate::deserialize::{FromSqlRow, StaticallySizedRow}; +use crate::expression::QueryMetadata; +use crate::query_builder::*; +use crate::result::*; +use crate::serialize::ToSql; +use crate::sql_types::{HasSqlType, TypeMetadata}; +use crate::sqlite::Sqlite; + +/// Connections for the SQLite backend. Unlike other backends, SQLite supported +/// connection URLs are: +/// +/// - File paths (`test.db`) +/// - [URIs](https://sqlite.org/uri.html) (`file://test.db`) +/// - Special identifiers (`:memory:`) +/// +/// # Supported loading model implementations +/// +/// * [`DefaultLoadingMode`] +/// +/// As `SqliteConnection` only supports a single loading mode implementation +/// it is **not required** to explicitly specify a loading mode +/// when calling [`RunQueryDsl::load_iter()`] or [`LoadConnection::load`] +/// +/// [`RunQueryDsl::load_iter()`]: crate::query_dsl::RunQueryDsl::load_iter +/// +/// ## DefaultLoadingMode +/// +/// `SqliteConnection` only supports a single loading mode, which loads +/// values row by row from the result set. +/// +/// ```rust +/// # include!("../../doctest_setup.rs"); +/// # +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users; +/// # let connection = &mut establish_connection(); +/// use diesel::connection::DefaultLoadingMode; +/// { // scope to restrict the lifetime of the iterator +/// let iter1 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?; +/// +/// for r in iter1 { +/// let (id, name) = r?; +/// println!("Id: {} Name: {}", id, name); +/// } +/// } +/// +/// // works without specifying the loading mode +/// let iter2 = users::table.load_iter::<(i32, String), _>(connection)?; +/// +/// for r in iter2 { +/// let (id, name) = r?; +/// println!("Id: {} Name: {}", id, name); +/// } +/// # Ok(()) +/// # } +/// ``` +/// +/// This mode does **not support** creating +/// multiple iterators using the same connection. +/// +/// ```compile_fail +/// # include!("../../doctest_setup.rs"); +/// # +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users; +/// # let connection = &mut establish_connection(); +/// use diesel::connection::DefaultLoadingMode; +/// +/// let iter1 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?; +/// let iter2 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?; +/// +/// for r in iter1 { +/// let (id, name) = r?; +/// println!("Id: {} Name: {}", id, name); +/// } +/// +/// for r in iter2 { +/// let (id, name) = r?; +/// println!("Id: {} Name: {}", id, name); +/// } +/// # Ok(()) +/// # } +/// ``` +#[allow(missing_debug_implementations)] +#[cfg(feature = "sqlite")] +pub struct SqliteConnection { + // statement_cache needs to be before raw_connection + // otherwise we will get errors about open statements before closing the + // connection itself + statement_cache: StatementCache, + raw_connection: RawConnection, + transaction_state: AnsiTransactionManager, + // this exists for the sole purpose of implementing `WithMetadataLookup` trait + // and avoiding static mut which will be deprecated in 2024 edition + metadata_lookup: (), + instrumentation: Option>, +} + +// This relies on the invariant that RawConnection or Statement are never +// leaked. If a reference to one of those was held on a different thread, this +// would not be thread safe. +#[allow(unsafe_code)] +unsafe impl Send for SqliteConnection {} + +impl SimpleConnection for SqliteConnection { + fn batch_execute(&mut self, query: &str) -> QueryResult<()> { + self.instrumentation + .on_connection_event(InstrumentationEvent::StartQuery { + query: &StrQueryHelper::new(query), + }); + let resp = self.raw_connection.exec(query); + self.instrumentation + .on_connection_event(InstrumentationEvent::FinishQuery { + query: &StrQueryHelper::new(query), + error: resp.as_ref().err(), + }); + resp + } +} + +impl ConnectionSealed for SqliteConnection {} + +impl Connection for SqliteConnection { + type Backend = Sqlite; + type TransactionManager = AnsiTransactionManager; + + /// Establish a connection to the database specified by `database_url`. + /// + /// See [SqliteConnection] for supported `database_url`. + /// + /// If the database does not exist, this method will try to + /// create a new database and then establish a connection to it. + fn establish(database_url: &str) -> ConnectionResult { + let mut instrumentation = crate::connection::instrumentation::get_default_instrumentation(); + instrumentation.on_connection_event(InstrumentationEvent::StartEstablishConnection { + url: database_url, + }); + + let establish_result = Self::establish_inner(database_url); + instrumentation.on_connection_event(InstrumentationEvent::FinishEstablishConnection { + url: database_url, + error: establish_result.as_ref().err(), + }); + let mut conn = establish_result?; + conn.instrumentation = instrumentation; + Ok(conn) + } + + fn execute_returning_count(&mut self, source: &T) -> QueryResult + where + T: QueryFragment + QueryId, + { + let statement_use = self.prepared_query(source)?; + statement_use.run().and_then(|_| { + self.raw_connection + .rows_affected_by_last_query() + .map_err(Error::DeserializationError) + }) + } + + fn transaction_state(&mut self) -> &mut AnsiTransactionManager + where + Self: Sized, + { + &mut self.transaction_state + } + + fn instrumentation(&mut self) -> &mut dyn Instrumentation { + &mut self.instrumentation + } + + fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) { + self.instrumentation = Some(Box::new(instrumentation)); + } +} + +impl LoadConnection for SqliteConnection { + type Cursor<'conn, 'query> = StatementIterator<'conn, 'query>; + type Row<'conn, 'query> = self::row::SqliteRow<'conn, 'query>; + + fn load<'conn, 'query, T>( + &'conn mut self, + source: T, + ) -> QueryResult> + where + T: Query + QueryFragment + QueryId + 'query, + Self::Backend: QueryMetadata, + { + let statement = self.prepared_query(source)?; + + Ok(StatementIterator::new(statement)) + } +} + +impl WithMetadataLookup for SqliteConnection { + fn metadata_lookup(&mut self) -> &mut ::MetadataLookup { + &mut self.metadata_lookup + } +} + +#[cfg(feature = "r2d2")] +impl crate::r2d2::R2D2Connection for crate::sqlite::SqliteConnection { + fn ping(&mut self) -> QueryResult<()> { + use crate::RunQueryDsl; + + crate::r2d2::CheckConnectionQuery.execute(self).map(|_| ()) + } + + fn is_broken(&mut self) -> bool { + AnsiTransactionManager::is_broken_transaction_manager(self) + } +} + +impl MultiConnectionHelper for SqliteConnection { + fn to_any<'a>( + lookup: &mut ::MetadataLookup, + ) -> &mut (dyn std::any::Any + 'a) { + lookup + } + + fn from_any( + lookup: &mut dyn std::any::Any, + ) -> Option<&mut ::MetadataLookup> { + lookup.downcast_mut() + } +} + +impl SqliteConnection { + /// Run a transaction with `BEGIN IMMEDIATE` + /// + /// This method will return an error if a transaction is already open. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let mut conn = SqliteConnection::establish(":memory:").unwrap(); + /// conn.immediate_transaction(|conn| { + /// // Do stuff in a transaction + /// Ok(()) + /// }) + /// # } + /// ``` + pub fn immediate_transaction(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + E: From, + { + self.transaction_sql(f, "BEGIN IMMEDIATE") + } + + /// Run a transaction with `BEGIN EXCLUSIVE` + /// + /// This method will return an error if a transaction is already open. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let mut conn = SqliteConnection::establish(":memory:").unwrap(); + /// conn.exclusive_transaction(|conn| { + /// // Do stuff in a transaction + /// Ok(()) + /// }) + /// # } + /// ``` + pub fn exclusive_transaction(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + E: From, + { + self.transaction_sql(f, "BEGIN EXCLUSIVE") + } + + fn transaction_sql(&mut self, f: F, sql: &str) -> Result + where + F: FnOnce(&mut Self) -> Result, + E: From, + { + AnsiTransactionManager::begin_transaction_sql(&mut *self, sql)?; + match f(&mut *self) { + Ok(value) => { + AnsiTransactionManager::commit_transaction(&mut *self)?; + Ok(value) + } + Err(e) => { + AnsiTransactionManager::rollback_transaction(&mut *self)?; + Err(e) + } + } + } + + fn prepared_query<'conn, 'query, T>( + &'conn mut self, + source: T, + ) -> QueryResult> + where + T: QueryFragment + QueryId + 'query, + { + self.instrumentation + .on_connection_event(InstrumentationEvent::StartQuery { + query: &crate::debug_query(&source), + }); + let raw_connection = &self.raw_connection; + let cache = &mut self.statement_cache; + let statement = match cache.cached_statement( + &source, + &Sqlite, + &[], + |sql, is_cached| Statement::prepare(raw_connection, sql, is_cached), + &mut self.instrumentation, + ) { + Ok(statement) => statement, + Err(e) => { + self.instrumentation + .on_connection_event(InstrumentationEvent::FinishQuery { + query: &crate::debug_query(&source), + error: Some(&e), + }); + + return Err(e); + } + }; + + StatementUse::bind(statement, source, &mut self.instrumentation) + } + + #[doc(hidden)] + pub fn register_sql_function( + &mut self, + fn_name: &str, + deterministic: bool, + mut f: F, + ) -> QueryResult<()> + where + F: FnMut(Args) -> Ret + std::panic::UnwindSafe + Send + 'static, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + Sqlite: HasSqlType, + { + functions::register( + &self.raw_connection, + fn_name, + deterministic, + move |_, args| f(args), + ) + } + + #[doc(hidden)] + pub fn register_noarg_sql_function( + &self, + fn_name: &str, + deterministic: bool, + f: F, + ) -> QueryResult<()> + where + F: FnMut() -> Ret + std::panic::UnwindSafe + Send + 'static, + Ret: ToSql, + Sqlite: HasSqlType, + { + functions::register_noargs(&self.raw_connection, fn_name, deterministic, f) + } + + #[doc(hidden)] + pub fn register_aggregate_function( + &mut self, + fn_name: &str, + ) -> QueryResult<()> + where + A: SqliteAggregateFunction + 'static + Send + std::panic::UnwindSafe, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + Sqlite: HasSqlType, + { + functions::register_aggregate::<_, _, _, _, A>(&self.raw_connection, fn_name) + } + + /// Register a collation function. + /// + /// `collation` must always return the same answer given the same inputs. + /// If `collation` panics and unwinds the stack, the process is aborted, since it is used + /// across a C FFI boundary, which cannot be unwound across and there is no way to + /// signal failures via the SQLite interface in this case.. + /// + /// If the name is already registered it will be overwritten. + /// + /// This method will return an error if registering the function fails, either due to an + /// out-of-memory situation or because a collation with that name already exists and is + /// currently being used in parallel by a query. + /// + /// The collation needs to be specified when creating a table: + /// `CREATE TABLE my_table ( str TEXT COLLATE MY_COLLATION )`, + /// where `MY_COLLATION` corresponds to name passed as `collation_name`. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let mut conn = SqliteConnection::establish(":memory:").unwrap(); + /// // sqlite NOCASE only works for ASCII characters, + /// // this collation allows handling UTF-8 (barring locale differences) + /// conn.register_collation("RUSTNOCASE", |rhs, lhs| { + /// rhs.to_lowercase().cmp(&lhs.to_lowercase()) + /// }) + /// # } + /// ``` + pub fn register_collation(&mut self, collation_name: &str, collation: F) -> QueryResult<()> + where + F: Fn(&str, &str) -> std::cmp::Ordering + Send + 'static + std::panic::UnwindSafe, + { + self.raw_connection + .register_collation_function(collation_name, collation) + } + + /// Serialize the current SQLite database into a byte buffer. + /// + /// The serialized data is identical to the data that would be written to disk if the database + /// was saved in a file. + /// + /// # Returns + /// + /// This function returns a byte slice representing the serialized database. + pub fn serialize_database_to_buffer(&mut self) -> SerializedDatabase { + self.raw_connection.serialize() + } + + /// Deserialize an SQLite database from a byte buffer. + /// + /// This function takes a byte slice and attempts to deserialize it into a SQLite database. + /// If successful, the database is loaded into the connection. If the deserialization fails, + /// an error is returned. + /// + /// The database is opened in READONLY mode. + /// + /// # Example + /// + /// ```no_run + /// # use diesel::sqlite::SerializedDatabase; + /// # use diesel::sqlite::SqliteConnection; + /// # use diesel::result::QueryResult; + /// # use diesel::sql_query; + /// # use diesel::Connection; + /// # use diesel::RunQueryDsl; + /// # fn main() { + /// let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + /// + /// sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)") + /// .execute(connection).unwrap(); + /// sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')") + /// .execute(connection).unwrap(); + /// + /// // Serialize the database to a byte vector + /// let serialized_db: SerializedDatabase = connection.serialize_database_to_buffer(); + /// + /// // Create a new in-memory SQLite database + /// let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + /// + /// // Deserialize the byte vector into the new database + /// connection.deserialize_readonly_database_from_buffer(serialized_db.as_slice()).unwrap(); + /// # + /// # } + /// ``` + pub fn deserialize_readonly_database_from_buffer(&mut self, data: &[u8]) -> QueryResult<()> { + self.raw_connection.deserialize(data) + } + + fn register_diesel_sql_functions(&self) -> QueryResult<()> { + use crate::sql_types::{Integer, Text}; + + functions::register::( + &self.raw_connection, + "diesel_manage_updated_at", + false, + |conn, table_name: String| { + conn.exec(&format!( + include_str!("diesel_manage_updated_at.sql"), + table_name = table_name + )) + .expect("Failed to create trigger"); + 0 // have to return *something* + }, + ) + } + + fn establish_inner(database_url: &str) -> Result { + use crate::result::ConnectionError::CouldntSetupConfiguration; + let raw_connection = RawConnection::establish(database_url)?; + let conn = Self { + statement_cache: StatementCache::new(), + raw_connection, + transaction_state: AnsiTransactionManager::default(), + metadata_lookup: (), + instrumentation: None, + }; + conn.register_diesel_sql_functions() + .map_err(CouldntSetupConfiguration)?; + Ok(conn) + } +} + +fn error_message(err_code: libc::c_int) -> &'static str { + ffi::code_to_str(err_code) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::dsl::sql; + use crate::prelude::*; + use crate::sql_types::Integer; + + #[test] + fn database_serializes_and_deserializes_successfully() { + let expected_users = vec![ + ( + 1, + "John Doe".to_string(), + "john.doe@example.com".to_string(), + ), + ( + 2, + "Jane Doe".to_string(), + "jane.doe@example.com".to_string(), + ), + ]; + + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + let _ = + crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)") + .execute(connection); + let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')") + .execute(connection); + + let serialized_database = connection.serialize_database_to_buffer(); + + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + connection + .deserialize_readonly_database_from_buffer(serialized_database.as_slice()) + .unwrap(); + + let query = sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id"); + let actual_users = query.load::<(i32, String, String)>(connection).unwrap(); + + assert_eq!(expected_users, actual_users); + } + + #[test] + fn prepared_statements_are_cached_when_run() { + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + let query = crate::select(1.into_sql::()); + + assert_eq!(Ok(1), query.get_result(connection)); + assert_eq!(Ok(1), query.get_result(connection)); + assert_eq!(1, connection.statement_cache.len()); + } + + #[test] + fn sql_literal_nodes_are_not_cached() { + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + let query = crate::select(sql::("1")); + + assert_eq!(Ok(1), query.get_result(connection)); + assert_eq!(0, connection.statement_cache.len()); + } + + #[test] + fn queries_containing_sql_literal_nodes_are_not_cached() { + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + let one_as_expr = 1.into_sql::(); + let query = crate::select(one_as_expr.eq(sql::("1"))); + + assert_eq!(Ok(true), query.get_result(connection)); + assert_eq!(0, connection.statement_cache.len()); + } + + #[test] + fn queries_containing_in_with_vec_are_not_cached() { + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + let one_as_expr = 1.into_sql::(); + let query = crate::select(one_as_expr.eq_any(vec![1, 2, 3])); + + assert_eq!(Ok(true), query.get_result(connection)); + assert_eq!(0, connection.statement_cache.len()); + } + + #[test] + fn queries_containing_in_with_subselect_are_cached() { + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + let one_as_expr = 1.into_sql::(); + let query = crate::select(one_as_expr.eq_any(crate::select(one_as_expr))); + + assert_eq!(Ok(true), query.get_result(connection)); + assert_eq!(1, connection.statement_cache.len()); + } + + use crate::sql_types::Text; + define_sql_function!(fn fun_case(x: Text) -> Text); + + #[test] + fn register_custom_function() { + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + fun_case_utils::register_impl(connection, |x: String| { + x.chars() + .enumerate() + .map(|(i, c)| { + if i % 2 == 0 { + c.to_lowercase().to_string() + } else { + c.to_uppercase().to_string() + } + }) + .collect::() + }) + .unwrap(); + + let mapped_string = crate::select(fun_case("foobar")) + .get_result::(connection) + .unwrap(); + assert_eq!("fOoBaR", mapped_string); + } + + define_sql_function!(fn my_add(x: Integer, y: Integer) -> Integer); + + #[test] + fn register_multiarg_function() { + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + my_add_utils::register_impl(connection, |x: i32, y: i32| x + y).unwrap(); + + let added = crate::select(my_add(1, 2)).get_result::(connection); + assert_eq!(Ok(3), added); + } + + define_sql_function!(fn answer() -> Integer); + + #[test] + fn register_noarg_function() { + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + answer_utils::register_impl(connection, || 42).unwrap(); + + let answer = crate::select(answer()).get_result::(connection); + assert_eq!(Ok(42), answer); + } + + #[test] + fn register_nondeterministic_noarg_function() { + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + answer_utils::register_nondeterministic_impl(connection, || 42).unwrap(); + + let answer = crate::select(answer()).get_result::(connection); + assert_eq!(Ok(42), answer); + } + + define_sql_function!(fn add_counter(x: Integer) -> Integer); + + #[test] + fn register_nondeterministic_function() { + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + let mut y = 0; + add_counter_utils::register_nondeterministic_impl(connection, move |x: i32| { + y += 1; + x + y + }) + .unwrap(); + + let added = crate::select((add_counter(1), add_counter(1), add_counter(1))) + .get_result::<(i32, i32, i32)>(connection); + assert_eq!(Ok((2, 3, 4)), added); + } + + define_sql_function! { + #[aggregate] + fn my_sum(expr: Integer) -> Integer; + } + + #[derive(Default)] + struct MySum { + sum: i32, + } + + impl SqliteAggregateFunction for MySum { + type Output = i32; + + fn step(&mut self, expr: i32) { + self.sum += expr; + } + + fn finalize(aggregator: Option) -> Self::Output { + aggregator.map(|a| a.sum).unwrap_or_default() + } + } + + table! { + my_sum_example { + id -> Integer, + value -> Integer, + } + } + + #[test] + fn register_aggregate_function() { + use self::my_sum_example::dsl::*; + + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + crate::sql_query( + "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)", + ) + .execute(connection) + .unwrap(); + crate::sql_query("INSERT INTO my_sum_example (value) VALUES (1), (2), (3)") + .execute(connection) + .unwrap(); + + my_sum_utils::register_impl::(connection).unwrap(); + + let result = my_sum_example + .select(my_sum(value)) + .get_result::(connection); + assert_eq!(Ok(6), result); + } + + #[test] + fn register_aggregate_function_returns_finalize_default_on_empty_set() { + use self::my_sum_example::dsl::*; + + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + crate::sql_query( + "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)", + ) + .execute(connection) + .unwrap(); + + my_sum_utils::register_impl::(connection).unwrap(); + + let result = my_sum_example + .select(my_sum(value)) + .get_result::(connection); + assert_eq!(Ok(0), result); + } + + define_sql_function! { + #[aggregate] + fn range_max(expr1: Integer, expr2: Integer, expr3: Integer) -> Nullable; + } + + #[derive(Default)] + struct RangeMax { + max_value: Option, + } + + impl SqliteAggregateFunction<(T, T, T)> for RangeMax { + type Output = Option; + + fn step(&mut self, (x0, x1, x2): (T, T, T)) { + let max = if x0 >= x1 && x0 >= x2 { + x0 + } else if x1 >= x0 && x1 >= x2 { + x1 + } else { + x2 + }; + + self.max_value = match self.max_value { + Some(current_max_value) if max > current_max_value => Some(max), + None => Some(max), + _ => self.max_value, + }; + } + + fn finalize(aggregator: Option) -> Self::Output { + aggregator?.max_value + } + } + + table! { + range_max_example { + id -> Integer, + value1 -> Integer, + value2 -> Integer, + value3 -> Integer, + } + } + + #[test] + fn register_aggregate_multiarg_function() { + use self::range_max_example::dsl::*; + + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + crate::sql_query( + r#"CREATE TABLE range_max_example ( + id integer primary key autoincrement, + value1 integer, + value2 integer, + value3 integer + )"#, + ) + .execute(connection) + .unwrap(); + crate::sql_query( + "INSERT INTO range_max_example (value1, value2, value3) VALUES (3, 2, 1), (2, 2, 2)", + ) + .execute(connection) + .unwrap(); + + range_max_utils::register_impl::, _, _, _>(connection).unwrap(); + let result = range_max_example + .select(range_max(value1, value2, value3)) + .get_result::>(connection) + .unwrap(); + assert_eq!(Some(3), result); + } + + table! { + my_collation_example { + id -> Integer, + value -> Text, + } + } + + #[test] + fn register_collation_function() { + use self::my_collation_example::dsl::*; + + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + + connection + .register_collation("RUSTNOCASE", |rhs, lhs| { + rhs.to_lowercase().cmp(&lhs.to_lowercase()) + }) + .unwrap(); + + crate::sql_query( + "CREATE TABLE my_collation_example (id integer primary key autoincrement, value text collate RUSTNOCASE)", + ).execute(connection) + .unwrap(); + crate::sql_query( + "INSERT INTO my_collation_example (value) VALUES ('foo'), ('FOo'), ('f00')", + ) + .execute(connection) + .unwrap(); + + let result = my_collation_example + .filter(value.eq("foo")) + .select(value) + .load::(connection); + assert_eq!( + Ok(&["foo".to_owned(), "FOo".to_owned()][..]), + result.as_ref().map(|vec| vec.as_ref()) + ); + + let result = my_collation_example + .filter(value.eq("FOO")) + .select(value) + .load::(connection); + assert_eq!( + Ok(&["foo".to_owned(), "FOo".to_owned()][..]), + result.as_ref().map(|vec| vec.as_ref()) + ); + + let result = my_collation_example + .filter(value.eq("f00")) + .select(value) + .load::(connection); + assert_eq!( + Ok(&["f00".to_owned()][..]), + result.as_ref().map(|vec| vec.as_ref()) + ); + + let result = my_collation_example + .filter(value.eq("F00")) + .select(value) + .load::(connection); + assert_eq!( + Ok(&["f00".to_owned()][..]), + result.as_ref().map(|vec| vec.as_ref()) + ); + + let result = my_collation_example + .filter(value.eq("oof")) + .select(value) + .load::(connection); + assert_eq!(Ok(&[][..]), result.as_ref().map(|vec| vec.as_ref())); + } + + // regression test for https://github.com/diesel-rs/diesel/issues/3425 + #[test] + fn test_correct_seralization_of_owned_strings() { + use crate::prelude::*; + + #[derive(Debug, crate::expression::AsExpression)] + #[diesel(sql_type = diesel::sql_types::Text)] + struct CustomWrapper(String); + + impl crate::serialize::ToSql for CustomWrapper { + fn to_sql<'b>( + &'b self, + out: &mut crate::serialize::Output<'b, '_, Sqlite>, + ) -> crate::serialize::Result { + out.set_value(self.0.to_string()); + Ok(crate::serialize::IsNull::No) + } + } + + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + + let res = crate::select( + CustomWrapper("".into()) + .into_sql::() + .nullable(), + ) + .get_result::>(connection) + .unwrap(); + assert_eq!(res, Some(String::new())); + } + + #[test] + fn test_correct_seralization_of_owned_bytes() { + use crate::prelude::*; + + #[derive(Debug, crate::expression::AsExpression)] + #[diesel(sql_type = diesel::sql_types::Binary)] + struct CustomWrapper(Vec); + + impl crate::serialize::ToSql for CustomWrapper { + fn to_sql<'b>( + &'b self, + out: &mut crate::serialize::Output<'b, '_, Sqlite>, + ) -> crate::serialize::Result { + out.set_value(self.0.clone()); + Ok(crate::serialize::IsNull::No) + } + } + + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + + let res = crate::select( + CustomWrapper(Vec::new()) + .into_sql::() + .nullable(), + ) + .get_result::>>(connection) + .unwrap(); + assert_eq!(res, Some(Vec::new())); + } + + #[test] + fn correctly_handle_empty_query() { + let check_empty_query_error = |r: crate::QueryResult| { + assert!(r.is_err()); + let err = r.unwrap_err(); + assert!( + matches!(err, crate::result::Error::QueryBuilderError(ref b) if b.is::()), + "Expected a query builder error, but got {err}" + ); + }; + let connection = &mut SqliteConnection::establish(":memory:").unwrap(); + check_empty_query_error(crate::sql_query("").execute(connection)); + check_empty_query_error(crate::sql_query(" ").execute(connection)); + check_empty_query_error(crate::sql_query("\n\t").execute(connection)); + check_empty_query_error(crate::sql_query("-- SELECT 1;").execute(connection)); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/owned_row.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/owned_row.rs new file mode 100644 index 000000000..867ac74e5 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/owned_row.rs @@ -0,0 +1,96 @@ +use std::sync::Arc; + +use super::sqlite_value::{OwnedSqliteValue, SqliteValue}; +use crate::backend::Backend; +use crate::row::{Field, PartialRow, Row, RowIndex, RowSealed}; +use crate::sqlite::Sqlite; + +#[derive(Debug)] +pub struct OwnedSqliteRow { + pub(super) values: Vec>, + column_names: Arc<[Option]>, +} + +impl OwnedSqliteRow { + pub(super) fn new( + values: Vec>, + column_names: Arc<[Option]>, + ) -> Self { + OwnedSqliteRow { + values, + column_names, + } + } +} + +impl RowSealed for OwnedSqliteRow {} + +impl<'a> Row<'a, Sqlite> for OwnedSqliteRow { + type Field<'field> + = OwnedSqliteField<'field> + where + 'a: 'field, + Self: 'field; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.values.len() + } + + fn get<'field, I>(&'field self, idx: I) -> Option> + where + 'a: 'field, + Self: RowIndex, + { + let idx = self.idx(idx)?; + Some(OwnedSqliteField { + row: self, + col_idx: idx, + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow<'_, Self::InnerPartialRow> { + PartialRow::new(self, range) + } +} + +impl RowIndex for OwnedSqliteRow { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count() { + Some(idx) + } else { + None + } + } +} + +impl<'idx> RowIndex<&'idx str> for OwnedSqliteRow { + fn idx(&self, field_name: &'idx str) -> Option { + self.column_names + .iter() + .position(|n| n.as_ref().map(|s| s as &str) == Some(field_name)) + } +} + +#[allow(missing_debug_implementations)] +pub struct OwnedSqliteField<'row> { + pub(super) row: &'row OwnedSqliteRow, + pub(super) col_idx: usize, +} + +impl<'row> Field<'row, Sqlite> for OwnedSqliteField<'row> { + fn field_name(&self) -> Option<&str> { + self.row + .column_names + .get(self.col_idx) + .and_then(|o| o.as_ref().map(|s| s.as_ref())) + } + + fn is_null(&self) -> bool { + self.value().is_none() + } + + fn value(&self) -> Option<::RawValue<'row>> { + SqliteValue::from_owned_row(self.row, self.col_idx) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/raw.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/raw.rs new file mode 100644 index 000000000..57d6a5fbc --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/raw.rs @@ -0,0 +1,634 @@ +#![allow(unsafe_code)] // ffi calls +extern crate libsqlite3_sys as ffi; + +use std::ffi::{CString, NulError}; +use std::io::{stderr, Write}; +use std::os::raw as libc; +use std::ptr::NonNull; +use std::{mem, ptr, slice, str}; + +use super::functions::{build_sql_function_args, process_sql_function_result}; +use super::serialized_database::SerializedDatabase; +use super::stmt::ensure_sqlite_ok; +use super::{Sqlite, SqliteAggregateFunction}; +use crate::deserialize::FromSqlRow; +use crate::result::Error::DatabaseError; +use crate::result::*; +use crate::serialize::ToSql; +use crate::sql_types::HasSqlType; + +/// For use in FFI function, which cannot unwind. +/// Print the message, ask to open an issue at Github and [`abort`](std::process::abort). +macro_rules! assert_fail { + ($fmt:expr $(,$args:tt)*) => { + eprint!(concat!( + $fmt, + "If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\n", + "Source location: {}:{}\n", + ), $($args,)* file!(), line!()); + std::process::abort() + }; +} + +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub(super) struct RawConnection { + pub(super) internal_connection: NonNull, +} + +impl RawConnection { + pub(super) fn establish(database_url: &str) -> ConnectionResult { + let mut conn_pointer = ptr::null_mut(); + + let database_url = if database_url.starts_with("sqlite://") { + CString::new(database_url.replacen("sqlite://", "file:", 1))? + } else { + CString::new(database_url)? + }; + let flags = ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_URI; + let connection_status = unsafe { + ffi::sqlite3_open_v2(database_url.as_ptr(), &mut conn_pointer, flags, ptr::null()) + }; + + match connection_status { + ffi::SQLITE_OK => { + let conn_pointer = unsafe { NonNull::new_unchecked(conn_pointer) }; + Ok(RawConnection { + internal_connection: conn_pointer, + }) + } + err_code => { + let message = super::error_message(err_code); + Err(ConnectionError::BadConnection(message.into())) + } + } + } + + pub(super) fn exec(&self, query: &str) -> QueryResult<()> { + let query = CString::new(query)?; + let callback_fn = None; + let callback_arg = ptr::null_mut(); + let result = unsafe { + ffi::sqlite3_exec( + self.internal_connection.as_ptr(), + query.as_ptr(), + callback_fn, + callback_arg, + ptr::null_mut(), + ) + }; + + ensure_sqlite_ok(result, self.internal_connection.as_ptr()) + } + + pub(super) fn rows_affected_by_last_query( + &self, + ) -> Result> { + let r = unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) }; + + Ok(r.try_into()?) + } + + pub(super) fn register_sql_function( + &self, + fn_name: &str, + num_args: usize, + deterministic: bool, + f: F, + ) -> QueryResult<()> + where + F: FnMut(&Self, &mut [*mut ffi::sqlite3_value]) -> QueryResult + + std::panic::UnwindSafe + + Send + + 'static, + Ret: ToSql, + Sqlite: HasSqlType, + { + let callback_fn = Box::into_raw(Box::new(CustomFunctionUserPtr { + callback: f, + function_name: fn_name.to_owned(), + })); + let fn_name = Self::get_fn_name(fn_name)?; + let flags = Self::get_flags(deterministic); + let num_args = num_args + .try_into() + .map_err(|e| Error::SerializationError(Box::new(e)))?; + + let result = unsafe { + ffi::sqlite3_create_function_v2( + self.internal_connection.as_ptr(), + fn_name.as_ptr(), + num_args, + flags, + callback_fn as *mut _, + Some(run_custom_function::), + None, + None, + Some(destroy_boxed::>), + ) + }; + + Self::process_sql_function_result(result) + } + + pub(super) fn register_aggregate_function( + &self, + fn_name: &str, + num_args: usize, + ) -> QueryResult<()> + where + A: SqliteAggregateFunction + 'static + Send + std::panic::UnwindSafe, + Args: FromSqlRow, + Ret: ToSql, + Sqlite: HasSqlType, + { + let fn_name = Self::get_fn_name(fn_name)?; + let flags = Self::get_flags(false); + let num_args = num_args + .try_into() + .map_err(|e| Error::SerializationError(Box::new(e)))?; + + let result = unsafe { + ffi::sqlite3_create_function_v2( + self.internal_connection.as_ptr(), + fn_name.as_ptr(), + num_args, + flags, + ptr::null_mut(), + None, + Some(run_aggregator_step_function::<_, _, _, _, A>), + Some(run_aggregator_final_function::<_, _, _, _, A>), + None, + ) + }; + + Self::process_sql_function_result(result) + } + + pub(super) fn register_collation_function( + &self, + collation_name: &str, + collation: F, + ) -> QueryResult<()> + where + F: Fn(&str, &str) -> std::cmp::Ordering + std::panic::UnwindSafe + Send + 'static, + { + let callback_fn = Box::into_raw(Box::new(CollationUserPtr { + callback: collation, + collation_name: collation_name.to_owned(), + })); + let collation_name = Self::get_fn_name(collation_name)?; + + let result = unsafe { + ffi::sqlite3_create_collation_v2( + self.internal_connection.as_ptr(), + collation_name.as_ptr(), + ffi::SQLITE_UTF8, + callback_fn as *mut _, + Some(run_collation_function::), + Some(destroy_boxed::>), + ) + }; + + let result = Self::process_sql_function_result(result); + if result.is_err() { + destroy_boxed::>(callback_fn as *mut _); + } + result + } + + pub(super) fn serialize(&mut self) -> SerializedDatabase { + unsafe { + let mut size: ffi::sqlite3_int64 = 0; + let data_ptr = ffi::sqlite3_serialize( + self.internal_connection.as_ptr(), + std::ptr::null(), + &mut size as *mut _, + 0, + ); + SerializedDatabase::new( + data_ptr, + size.try_into() + .expect("Cannot fit the serialized database into memory"), + ) + } + } + + pub(super) fn deserialize(&mut self, data: &[u8]) -> QueryResult<()> { + let db_size = data + .len() + .try_into() + .map_err(|e| Error::DeserializationError(Box::new(e)))?; + // the cast for `ffi::SQLITE_DESERIALIZE_READONLY` is required for old libsqlite3-sys versions + #[allow(clippy::unnecessary_cast)] + unsafe { + let result = ffi::sqlite3_deserialize( + self.internal_connection.as_ptr(), + std::ptr::null(), + data.as_ptr() as *mut u8, + db_size, + db_size, + ffi::SQLITE_DESERIALIZE_READONLY as u32, + ); + + ensure_sqlite_ok(result, self.internal_connection.as_ptr()) + } + } + + fn get_fn_name(fn_name: &str) -> Result { + CString::new(fn_name) + } + + fn get_flags(deterministic: bool) -> i32 { + let mut flags = ffi::SQLITE_UTF8; + if deterministic { + flags |= ffi::SQLITE_DETERMINISTIC; + } + flags + } + + fn process_sql_function_result(result: i32) -> Result<(), Error> { + if result == ffi::SQLITE_OK { + Ok(()) + } else { + let error_message = super::error_message(result); + Err(DatabaseError( + DatabaseErrorKind::Unknown, + Box::new(error_message.to_string()), + )) + } + } +} + +impl Drop for RawConnection { + fn drop(&mut self) { + use std::thread::panicking; + + let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) }; + if close_result != ffi::SQLITE_OK { + let error_message = super::error_message(close_result); + if panicking() { + write!(stderr(), "Error closing SQLite connection: {error_message}") + .expect("Error writing to `stderr`"); + } else { + panic!("Error closing SQLite connection: {}", error_message); + } + } + } +} + +enum SqliteCallbackError { + Abort(&'static str), + DieselError(crate::result::Error), + Panic(String), +} + +impl SqliteCallbackError { + fn emit(&self, ctx: *mut ffi::sqlite3_context) { + let s; + let msg = match self { + SqliteCallbackError::Abort(msg) => *msg, + SqliteCallbackError::DieselError(e) => { + s = e.to_string(); + &s + } + SqliteCallbackError::Panic(msg) => msg, + }; + unsafe { + context_error_str(ctx, msg); + } + } +} + +impl From for SqliteCallbackError { + fn from(e: crate::result::Error) -> Self { + Self::DieselError(e) + } +} + +struct CustomFunctionUserPtr { + callback: F, + function_name: String, +} + +#[allow(warnings)] +extern "C" fn run_custom_function( + ctx: *mut ffi::sqlite3_context, + num_args: libc::c_int, + value_ptr: *mut *mut ffi::sqlite3_value, +) where + F: FnMut(&RawConnection, &mut [*mut ffi::sqlite3_value]) -> QueryResult + + std::panic::UnwindSafe + + Send + + 'static, + Ret: ToSql, + Sqlite: HasSqlType, +{ + use std::ops::Deref; + static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen."; + static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen."; + + let conn = match unsafe { NonNull::new(ffi::sqlite3_context_db_handle(ctx)) } { + // We use `ManuallyDrop` here because we do not want to run the + // Drop impl of `RawConnection` as this would close the connection + Some(conn) => mem::ManuallyDrop::new(RawConnection { + internal_connection: conn, + }), + None => { + unsafe { context_error_str(ctx, NULL_CONN_ERR) }; + return; + } + }; + + let data_ptr = unsafe { ffi::sqlite3_user_data(ctx) }; + + let mut data_ptr = match NonNull::new(data_ptr as *mut CustomFunctionUserPtr) { + None => unsafe { + context_error_str(ctx, NULL_DATA_ERR); + return; + }, + Some(mut f) => f, + }; + let data_ptr = unsafe { data_ptr.as_mut() }; + + // We need this to move the reference into the catch_unwind part + // this is sound as `F` itself and the stored string is `UnwindSafe` + let callback = std::panic::AssertUnwindSafe(&mut data_ptr.callback); + + let result = std::panic::catch_unwind(move || { + let _ = &callback; + let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) }; + let res = (callback.0)(&*conn, args)?; + let value = process_sql_function_result(&res)?; + // We've checked already that ctx is not null + unsafe { + value.result_of(&mut *ctx); + } + Ok(()) + }) + .unwrap_or_else(|p| Err(SqliteCallbackError::Panic(data_ptr.function_name.clone()))); + if let Err(e) = result { + e.emit(ctx); + } +} + +// Need a custom option type here, because the std lib one does not have guarantees about the discriminate values +// See: https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md#opaque-tags +#[repr(u8)] +enum OptionalAggregator { + // Discriminant is 0 + None, + Some(A), +} + +#[allow(warnings)] +extern "C" fn run_aggregator_step_function( + ctx: *mut ffi::sqlite3_context, + num_args: libc::c_int, + value_ptr: *mut *mut ffi::sqlite3_value, +) where + A: SqliteAggregateFunction + 'static + Send + std::panic::UnwindSafe, + Args: FromSqlRow, + Ret: ToSql, + Sqlite: HasSqlType, +{ + let result = std::panic::catch_unwind(move || { + let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) }; + run_aggregator_step::(ctx, args) + }) + .unwrap_or_else(|e| { + Err(SqliteCallbackError::Panic(format!( + "{}::step() panicked", + std::any::type_name::() + ))) + }); + + match result { + Ok(()) => {} + Err(e) => e.emit(ctx), + } +} + +fn run_aggregator_step( + ctx: *mut ffi::sqlite3_context, + args: &mut [*mut ffi::sqlite3_value], +) -> Result<(), SqliteCallbackError> +where + A: SqliteAggregateFunction, + Args: FromSqlRow, +{ + static NULL_AG_CTX_ERR: &str = "An unknown error occurred. sqlite3_aggregate_context returned a null pointer. This should never happen."; + static NULL_CTX_ERR: &str = + "We've written the aggregator to the aggregate context, but it could not be retrieved."; + + let n_bytes: i32 = std::mem::size_of::>() + .try_into() + .expect("Aggregate context should be larger than 2^32"); + let aggregate_context = unsafe { + // This block of unsafe code makes the following assumptions: + // + // * sqlite3_aggregate_context allocates sizeof::> + // bytes of zeroed memory as documented here: + // https://www.sqlite.org/c3ref/aggregate_context.html + // A null pointer is returned for negative or zero sized types, + // which should be impossible in theory. We check that nevertheless + // + // * OptionalAggregator::None has a discriminant of 0 as specified by + // #[repr(u8)] + RFC 2195 + // + // * If all bytes are zero, the discriminant is also zero, so we can + // assume that we get OptionalAggregator::None in this case. This is + // not UB as we only access the discriminant here, so we do not try + // to read any other zeroed memory. After that we initialize our enum + // by writing a correct value at this location via ptr::write_unaligned + // + // * We use ptr::write_unaligned as we did not found any guarantees that + // the memory will have a correct alignment. + // (Note I(weiznich): would assume that it is aligned correctly, but we + // we cannot guarantee it, so better be safe than sorry) + ffi::sqlite3_aggregate_context(ctx, n_bytes) + }; + let aggregate_context = NonNull::new(aggregate_context as *mut OptionalAggregator); + let aggregator = unsafe { + match aggregate_context.map(|a| &mut *a.as_ptr()) { + Some(&mut OptionalAggregator::Some(ref mut agg)) => agg, + Some(a_ptr @ &mut OptionalAggregator::None) => { + ptr::write_unaligned(a_ptr as *mut _, OptionalAggregator::Some(A::default())); + if let OptionalAggregator::Some(ref mut agg) = a_ptr { + agg + } else { + return Err(SqliteCallbackError::Abort(NULL_CTX_ERR)); + } + } + None => { + return Err(SqliteCallbackError::Abort(NULL_AG_CTX_ERR)); + } + } + }; + let args = build_sql_function_args::(args)?; + + aggregator.step(args); + Ok(()) +} + +extern "C" fn run_aggregator_final_function( + ctx: *mut ffi::sqlite3_context, +) where + A: SqliteAggregateFunction + 'static + Send, + Args: FromSqlRow, + Ret: ToSql, + Sqlite: HasSqlType, +{ + static NO_AGGREGATOR_FOUND: &str = "We've written to the aggregator in the xStep callback. If xStep was never called, then ffi::sqlite_aggregate_context() would have returned a NULL pointer."; + let aggregate_context = unsafe { + // Within the xFinal callback, it is customary to set nBytes to 0 so no pointless memory + // allocations occur, a null pointer is returned in this case + // See: https://www.sqlite.org/c3ref/aggregate_context.html + // + // For the reasoning about the safety of the OptionalAggregator handling + // see the comment in run_aggregator_step_function. + ffi::sqlite3_aggregate_context(ctx, 0) + }; + + let result = std::panic::catch_unwind(|| { + let mut aggregate_context = NonNull::new(aggregate_context as *mut OptionalAggregator); + + let aggregator = if let Some(a) = aggregate_context.as_mut() { + let a = unsafe { a.as_mut() }; + match std::mem::replace(a, OptionalAggregator::None) { + OptionalAggregator::None => { + return Err(SqliteCallbackError::Abort(NO_AGGREGATOR_FOUND)); + } + OptionalAggregator::Some(a) => Some(a), + } + } else { + None + }; + + let res = A::finalize(aggregator); + let value = process_sql_function_result(&res)?; + // We've checked already that ctx is not null + let r = unsafe { value.result_of(&mut *ctx) }; + r.map_err(|e| { + SqliteCallbackError::DieselError(crate::result::Error::SerializationError(Box::new(e))) + })?; + Ok(()) + }) + .unwrap_or_else(|_e| { + Err(SqliteCallbackError::Panic(format!( + "{}::finalize() panicked", + std::any::type_name::() + ))) + }); + if let Err(e) = result { + e.emit(ctx); + } +} + +unsafe fn context_error_str(ctx: *mut ffi::sqlite3_context, error: &str) { + let len: i32 = error + .len() + .try_into() + .expect("Trying to set a error message with more than 2^32 byte is not supported"); + ffi::sqlite3_result_error(ctx, error.as_ptr() as *const _, len); +} + +struct CollationUserPtr { + callback: F, + collation_name: String, +} + +#[allow(warnings)] +extern "C" fn run_collation_function( + user_ptr: *mut libc::c_void, + lhs_len: libc::c_int, + lhs_ptr: *const libc::c_void, + rhs_len: libc::c_int, + rhs_ptr: *const libc::c_void, +) -> libc::c_int +where + F: Fn(&str, &str) -> std::cmp::Ordering + Send + std::panic::UnwindSafe + 'static, +{ + let user_ptr = user_ptr as *const CollationUserPtr; + let user_ptr = std::panic::AssertUnwindSafe(unsafe { user_ptr.as_ref() }); + + let result = std::panic::catch_unwind(|| { + let user_ptr = user_ptr.ok_or_else(|| { + SqliteCallbackError::Abort( + "Got a null pointer as data pointer. This should never happen", + ) + })?; + for (ptr, len, side) in &[(rhs_ptr, rhs_len, "rhs"), (lhs_ptr, lhs_len, "lhs")] { + if *len < 0 { + assert_fail!( + "An unknown error occurred. {}_len is negative. This should never happen.", + side + ); + } + if ptr.is_null() { + assert_fail!( + "An unknown error occurred. {}_ptr is a null pointer. This should never happen.", + side + ); + } + } + + let (rhs, lhs) = unsafe { + // Depending on the eTextRep-parameter to sqlite3_create_collation_v2() the strings can + // have various encodings. register_collation_function() always selects SQLITE_UTF8, so the + // pointers point to valid UTF-8 strings (assuming correct behavior of libsqlite3). + ( + str::from_utf8(slice::from_raw_parts(rhs_ptr as *const u8, rhs_len as _)), + str::from_utf8(slice::from_raw_parts(lhs_ptr as *const u8, lhs_len as _)), + ) + }; + + let rhs = + rhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for rhs"))?; + let lhs = + lhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for lhs"))?; + + Ok((user_ptr.callback)(rhs, lhs)) + }) + .unwrap_or_else(|p| { + Err(SqliteCallbackError::Panic( + user_ptr + .map(|u| u.collation_name.clone()) + .unwrap_or_default(), + )) + }); + + match result { + Ok(std::cmp::Ordering::Less) => -1, + Ok(std::cmp::Ordering::Equal) => 0, + Ok(std::cmp::Ordering::Greater) => 1, + Err(SqliteCallbackError::Abort(a)) => { + eprintln!( + "Collation function {} failed with: {}", + user_ptr + .map(|c| &c.collation_name as &str) + .unwrap_or_default(), + a + ); + std::process::abort() + } + Err(SqliteCallbackError::DieselError(e)) => { + eprintln!( + "Collation function {} failed with: {}", + user_ptr + .map(|c| &c.collation_name as &str) + .unwrap_or_default(), + e + ); + std::process::abort() + } + Err(SqliteCallbackError::Panic(msg)) => { + eprintln!("Collation function {} panicked", msg); + std::process::abort() + } + } +} + +extern "C" fn destroy_boxed(data: *mut libc::c_void) { + let ptr = data as *mut F; + unsafe { std::mem::drop(Box::from_raw(ptr)) }; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/row.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/row.rs new file mode 100644 index 000000000..44162a310 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/row.rs @@ -0,0 +1,417 @@ +use std::cell::{Ref, RefCell}; +use std::rc::Rc; +use std::sync::Arc; + +use super::owned_row::OwnedSqliteRow; +use super::sqlite_value::{OwnedSqliteValue, SqliteValue}; +use super::stmt::StatementUse; +use crate::backend::Backend; +use crate::row::{Field, IntoOwnedRow, PartialRow, Row, RowIndex, RowSealed}; +use crate::sqlite::Sqlite; + +#[allow(missing_debug_implementations)] +pub struct SqliteRow<'stmt, 'query> { + pub(super) inner: Rc>>, + pub(super) field_count: usize, +} + +pub(super) enum PrivateSqliteRow<'stmt, 'query> { + Direct(StatementUse<'stmt, 'query>), + Duplicated { + values: Vec>, + column_names: Rc<[Option]>, + }, +} + +impl<'stmt> IntoOwnedRow<'stmt, Sqlite> for SqliteRow<'stmt, '_> { + type OwnedRow = OwnedSqliteRow; + + type Cache = Option]>>; + + fn into_owned(self, column_name_cache: &mut Self::Cache) -> Self::OwnedRow { + self.inner.borrow().moveable(column_name_cache) + } +} + +impl<'stmt, 'query> PrivateSqliteRow<'stmt, 'query> { + pub(super) fn duplicate( + &mut self, + column_names: &mut Option]>>, + ) -> PrivateSqliteRow<'stmt, 'query> { + match self { + PrivateSqliteRow::Direct(stmt) => { + let column_names = if let Some(column_names) = column_names { + column_names.clone() + } else { + let c: Rc<[Option]> = Rc::from( + (0..stmt.column_count()) + .map(|idx| stmt.field_name(idx).map(|s| s.to_owned())) + .collect::>(), + ); + *column_names = Some(c.clone()); + c + }; + PrivateSqliteRow::Duplicated { + values: (0..stmt.column_count()) + .map(|idx| stmt.copy_value(idx)) + .collect(), + column_names, + } + } + PrivateSqliteRow::Duplicated { + values, + column_names, + } => PrivateSqliteRow::Duplicated { + values: values + .iter() + .map(|v| v.as_ref().map(|v| v.duplicate())) + .collect(), + column_names: column_names.clone(), + }, + } + } + + pub(super) fn moveable( + &self, + column_name_cache: &mut Option]>>, + ) -> OwnedSqliteRow { + match self { + PrivateSqliteRow::Direct(stmt) => { + if column_name_cache.is_none() { + *column_name_cache = Some( + (0..stmt.column_count()) + .map(|idx| stmt.field_name(idx).map(|s| s.to_owned())) + .collect::>() + .into(), + ); + } + let column_names = Arc::clone( + column_name_cache + .as_ref() + .expect("This is initialized above"), + ); + OwnedSqliteRow::new( + (0..stmt.column_count()) + .map(|idx| stmt.copy_value(idx)) + .collect(), + column_names, + ) + } + PrivateSqliteRow::Duplicated { + values, + column_names, + } => { + if column_name_cache.is_none() { + *column_name_cache = Some( + (*column_names) + .iter() + .map(|s| s.to_owned()) + .collect::>() + .into(), + ); + } + let column_names = Arc::clone( + column_name_cache + .as_ref() + .expect("This is initialized above"), + ); + OwnedSqliteRow::new( + values + .iter() + .map(|v| v.as_ref().map(|v| v.duplicate())) + .collect(), + column_names, + ) + } + } + } +} + +impl RowSealed for SqliteRow<'_, '_> {} + +impl<'stmt> Row<'stmt, Sqlite> for SqliteRow<'stmt, '_> { + type Field<'field> + = SqliteField<'field, 'field> + where + 'stmt: 'field, + Self: 'field; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.field_count + } + + fn get<'field, I>(&'field self, idx: I) -> Option> + where + 'stmt: 'field, + Self: RowIndex, + { + let idx = self.idx(idx)?; + Some(SqliteField { + row: self.inner.borrow(), + col_idx: idx, + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow<'_, Self::InnerPartialRow> { + PartialRow::new(self, range) + } +} + +impl RowIndex for SqliteRow<'_, '_> { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count { + Some(idx) + } else { + None + } + } +} + +impl<'idx> RowIndex<&'idx str> for SqliteRow<'_, '_> { + fn idx(&self, field_name: &'idx str) -> Option { + match &mut *self.inner.borrow_mut() { + PrivateSqliteRow::Direct(stmt) => stmt.index_for_column_name(field_name), + PrivateSqliteRow::Duplicated { column_names, .. } => column_names + .iter() + .position(|n| n.as_ref().map(|s| s as &str) == Some(field_name)), + } + } +} + +#[allow(missing_debug_implementations)] +pub struct SqliteField<'stmt, 'query> { + pub(super) row: Ref<'stmt, PrivateSqliteRow<'stmt, 'query>>, + pub(super) col_idx: usize, +} + +impl<'stmt> Field<'stmt, Sqlite> for SqliteField<'stmt, '_> { + fn field_name(&self) -> Option<&str> { + match &*self.row { + PrivateSqliteRow::Direct(stmt) => stmt.field_name( + self.col_idx + .try_into() + .expect("Diesel expects to run at least on a 32 bit platform"), + ), + PrivateSqliteRow::Duplicated { column_names, .. } => column_names + .get(self.col_idx) + .and_then(|t| t.as_ref().map(|n| n as &str)), + } + } + + fn is_null(&self) -> bool { + self.value().is_none() + } + + fn value(&self) -> Option<::RawValue<'_>> { + SqliteValue::new(Ref::clone(&self.row), self.col_idx) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fun_with_row_iters() { + crate::table! { + #[allow(unused_parens)] + users(id) { + id -> Integer, + name -> Text, + } + } + + use crate::connection::LoadConnection; + use crate::deserialize::{FromSql, FromSqlRow}; + use crate::prelude::*; + use crate::row::{Field, Row}; + use crate::sql_types; + + let conn = &mut crate::test_helpers::connection(); + + crate::sql_query("CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);") + .execute(conn) + .unwrap(); + + crate::insert_into(users::table) + .values(vec![ + (users::id.eq(1), users::name.eq("Sean")), + (users::id.eq(2), users::name.eq("Tess")), + ]) + .execute(conn) + .unwrap(); + + let query = users::table.select((users::id, users::name)); + + let expected = vec![(1, String::from("Sean")), (2, String::from("Tess"))]; + + let row_iter = conn.load(query).unwrap(); + for (row, expected) in row_iter.zip(&expected) { + let row = row.unwrap(); + + let deserialized = <(i32, String) as FromSqlRow< + (sql_types::Integer, sql_types::Text), + _, + >>::build_from_row(&row) + .unwrap(); + + assert_eq!(&deserialized, expected); + } + + { + let collected_rows = conn.load(query).unwrap().collect::>(); + + for (row, expected) in collected_rows.iter().zip(&expected) { + let deserialized = row + .as_ref() + .map(|row| { + <(i32, String) as FromSqlRow< + (sql_types::Integer, sql_types::Text), + _, + >>::build_from_row(row).unwrap() + }) + .unwrap(); + + assert_eq!(&deserialized, expected); + } + } + + let mut row_iter = conn.load(query).unwrap(); + + let first_row = row_iter.next().unwrap().unwrap(); + let first_fields = (first_row.get(0).unwrap(), first_row.get(1).unwrap()); + let first_values = (first_fields.0.value(), first_fields.1.value()); + + assert!(row_iter.next().unwrap().is_err()); + std::mem::drop(first_values); + assert!(row_iter.next().unwrap().is_err()); + std::mem::drop(first_fields); + + let second_row = row_iter.next().unwrap().unwrap(); + let second_fields = (second_row.get(0).unwrap(), second_row.get(1).unwrap()); + let second_values = (second_fields.0.value(), second_fields.1.value()); + + assert!(row_iter.next().unwrap().is_err()); + std::mem::drop(second_values); + assert!(row_iter.next().unwrap().is_err()); + std::mem::drop(second_fields); + + assert!(row_iter.next().is_none()); + + let first_fields = (first_row.get(0).unwrap(), first_row.get(1).unwrap()); + let second_fields = (second_row.get(0).unwrap(), second_row.get(1).unwrap()); + + let first_values = (first_fields.0.value(), first_fields.1.value()); + let second_values = (second_fields.0.value(), second_fields.1.value()); + + assert_eq!( + >::from_nullable_sql(first_values.0) + .unwrap(), + expected[0].0 + ); + assert_eq!( + >::from_nullable_sql(first_values.1) + .unwrap(), + expected[0].1 + ); + + assert_eq!( + >::from_nullable_sql(second_values.0) + .unwrap(), + expected[1].0 + ); + assert_eq!( + >::from_nullable_sql(second_values.1) + .unwrap(), + expected[1].1 + ); + + let first_fields = (first_row.get(0).unwrap(), first_row.get(1).unwrap()); + let first_values = (first_fields.0.value(), first_fields.1.value()); + + assert_eq!( + >::from_nullable_sql(first_values.0) + .unwrap(), + expected[0].0 + ); + assert_eq!( + >::from_nullable_sql(first_values.1) + .unwrap(), + expected[0].1 + ); + } + + #[cfg(feature = "returning_clauses_for_sqlite_3_35")] + crate::define_sql_function! {fn sleep(a: diesel::sql_types::Integer) -> diesel::sql_types::Integer} + + #[test] + #[cfg(feature = "returning_clauses_for_sqlite_3_35")] + #[allow(clippy::cast_sign_loss)] + fn parallel_iter_with_error() { + use crate::connection::Connection; + use crate::connection::LoadConnection; + use crate::connection::SimpleConnection; + use crate::expression_methods::ExpressionMethods; + use crate::SqliteConnection; + use std::sync::{Arc, Barrier}; + use std::time::Duration; + + let temp_dir = tempfile::tempdir().unwrap(); + let db_path = format!("{}/test.db", temp_dir.path().display()); + let mut conn1 = SqliteConnection::establish(&db_path).unwrap(); + let mut conn2 = SqliteConnection::establish(&db_path).unwrap(); + + crate::table! { + users { + id -> Integer, + name -> Text, + } + } + + conn1 + .batch_execute("CREATE TABLE users(id INTEGER NOT NULL PRIMARY KEY, name TEXT)") + .unwrap(); + + let barrier = Arc::new(Barrier::new(2)); + let barrier2 = barrier.clone(); + + // we unblock the main thread from the sleep function + sleep_utils::register_impl(&mut conn2, move |a: i32| { + barrier.wait(); + std::thread::sleep(Duration::from_secs(a as u64)); + a + }) + .unwrap(); + + // spawn a background thread that locks the database file + let handle = std::thread::spawn(move || { + use crate::query_dsl::RunQueryDsl; + + conn2 + .immediate_transaction(|conn| diesel::select(sleep(1)).execute(conn)) + .unwrap(); + }); + barrier2.wait(); + + // execute some action that also requires a lock + let mut iter = conn1 + .load( + diesel::insert_into(users::table) + .values((users::id.eq(1), users::name.eq("John"))) + .returning(users::id), + ) + .unwrap(); + + // get the first iterator result, that should return the lock error + let n = iter.next().unwrap(); + assert!(n.is_err()); + + // check that the iterator is now empty + let n = iter.next(); + assert!(n.is_none()); + + // join the background thread + handle.join().unwrap(); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/serialized_database.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/serialized_database.rs new file mode 100644 index 000000000..28a98a286 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/serialized_database.rs @@ -0,0 +1,45 @@ +#![allow(unsafe_code)] +extern crate libsqlite3_sys as ffi; + +use std::ops::Deref; + +/// `SerializedDatabase` is a wrapper for a serialized database that is dynamically allocated by calling `sqlite3_serialize`. +/// This RAII wrapper is necessary to deallocate the memory when it goes out of scope with `sqlite3_free`. +#[derive(Debug)] +pub struct SerializedDatabase { + data: *mut u8, + len: usize, +} + +impl SerializedDatabase { + /// Creates a new `SerializedDatabase` with the given data pointer and length. + /// + /// SAFETY: The data pointer needs to be returned by sqlite + /// and the length must match the underlying buffer pointer + pub(crate) unsafe fn new(data: *mut u8, len: usize) -> Self { + Self { data, len } + } + + /// Returns a slice of the serialized database. + pub fn as_slice(&self) -> &[u8] { + // The pointer is never null because we don't pass the NO_COPY flag + unsafe { std::slice::from_raw_parts(self.data, self.len) } + } +} + +impl Deref for SerializedDatabase { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +impl Drop for SerializedDatabase { + /// Deallocates the memory of the serialized database when it goes out of scope. + fn drop(&mut self) { + unsafe { + ffi::sqlite3_free(self.data as _); + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/sqlite_value.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/sqlite_value.rs new file mode 100644 index 000000000..4180d0969 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/sqlite_value.rs @@ -0,0 +1,307 @@ +#![allow(unsafe_code)] // ffi calls +extern crate libsqlite3_sys as ffi; + +use std::cell::Ref; +use std::ptr::NonNull; +use std::{slice, str}; + +use crate::sqlite::SqliteType; + +use super::owned_row::OwnedSqliteRow; +use super::row::PrivateSqliteRow; + +/// Raw sqlite value as received from the database +/// +/// Use the `read_*` functions to access the actual +/// value or use existing `FromSql` implementations +/// to convert this into rust values +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct SqliteValue<'row, 'stmt, 'query> { + // This field exists to ensure that nobody + // can modify the underlying row while we are + // holding a reference to some row value here + _row: Option>>, + // we extract the raw value pointer as part of the constructor + // to safe the match statements for each method + // According to benchmarks this leads to a ~20-30% speedup + // + // This is sound as long as nobody calls `stmt.step()` + // while holding this value. We ensure this by including + // a reference to the row above. + value: NonNull, +} + +#[derive(Debug)] +#[repr(transparent)] +pub(super) struct OwnedSqliteValue { + pub(super) value: NonNull, +} + +impl Drop for OwnedSqliteValue { + fn drop(&mut self) { + unsafe { ffi::sqlite3_value_free(self.value.as_ptr()) } + } +} + +// Unsafe Send impl safe since sqlite3_value is built with sqlite3_value_dup +// see https://www.sqlite.org/c3ref/value.html +unsafe impl Send for OwnedSqliteValue {} + +impl<'row, 'stmt, 'query> SqliteValue<'row, 'stmt, 'query> { + pub(super) fn new( + row: Ref<'row, PrivateSqliteRow<'stmt, 'query>>, + col_idx: usize, + ) -> Option> { + let value = match &*row { + PrivateSqliteRow::Direct(stmt) => stmt.column_value( + col_idx + .try_into() + .expect("Diesel expects to run at least on a 32 bit platform"), + )?, + PrivateSqliteRow::Duplicated { values, .. } => { + values.get(col_idx).and_then(|v| v.as_ref())?.value + } + }; + + let ret = Self { + _row: Some(row), + value, + }; + if ret.value_type().is_none() { + None + } else { + Some(ret) + } + } + + pub(super) fn from_owned_row( + row: &'row OwnedSqliteRow, + col_idx: usize, + ) -> Option> { + let value = row.values.get(col_idx).and_then(|v| v.as_ref())?.value; + let ret = Self { _row: None, value }; + if ret.value_type().is_none() { + None + } else { + Some(ret) + } + } + + pub(crate) fn parse_string<'value, R>(&'value mut self, f: impl FnOnce(&'value str) -> R) -> R { + let s = unsafe { + let ptr = ffi::sqlite3_value_text(self.value.as_ptr()); + let len = ffi::sqlite3_value_bytes(self.value.as_ptr()); + let bytes = slice::from_raw_parts( + ptr, + len.try_into() + .expect("Diesel expects to run at least on a 32 bit platform"), + ); + // The string is guaranteed to be utf8 according to + // https://www.sqlite.org/c3ref/value_blob.html + str::from_utf8_unchecked(bytes) + }; + f(s) + } + + /// Read the underlying value as string + /// + /// If the underlying value is not a string sqlite will convert it + /// into a string and return that value instead. + /// + /// Use the [`value_type()`](Self::value_type()) function to determine the actual + /// type of the value. + /// + /// See for details + pub fn read_text(&mut self) -> &str { + self.parse_string(|s| s) + } + + /// Read the underlying value as blob + /// + /// If the underlying value is not a blob sqlite will convert it + /// into a blob and return that value instead. + /// + /// Use the [`value_type()`](Self::value_type()) function to determine the actual + /// type of the value. + /// + /// See for details + pub fn read_blob(&mut self) -> &[u8] { + unsafe { + let ptr = ffi::sqlite3_value_blob(self.value.as_ptr()); + let len = ffi::sqlite3_value_bytes(self.value.as_ptr()); + if len == 0 { + // rusts std-lib has an debug_assert that prevents creating + // slices without elements from a pointer + &[] + } else { + slice::from_raw_parts( + ptr as *const u8, + len.try_into() + .expect("Diesel expects to run at least on a 32 bit platform"), + ) + } + } + } + + /// Read the underlying value as 32 bit integer + /// + /// If the underlying value is not an integer sqlite will convert it + /// into an integer and return that value instead. + /// + /// Use the [`value_type()`](Self::value_type()) function to determine the actual + /// type of the value. + /// + /// See for details + pub fn read_integer(&mut self) -> i32 { + unsafe { ffi::sqlite3_value_int(self.value.as_ptr()) } + } + + /// Read the underlying value as 64 bit integer + /// + /// If the underlying value is not a string sqlite will convert it + /// into a string and return that value instead. + /// + /// Use the [`value_type()`](Self::value_type()) function to determine the actual + /// type of the value. + /// + /// See for details + pub fn read_long(&mut self) -> i64 { + unsafe { ffi::sqlite3_value_int64(self.value.as_ptr()) } + } + + /// Read the underlying value as 64 bit float + /// + /// If the underlying value is not a string sqlite will convert it + /// into a string and return that value instead. + /// + /// Use the [`value_type()`](Self::value_type()) function to determine the actual + /// type of the value. + /// + /// See for details + pub fn read_double(&mut self) -> f64 { + unsafe { ffi::sqlite3_value_double(self.value.as_ptr()) } + } + + /// Get the type of the value as returned by sqlite + pub fn value_type(&self) -> Option { + let tpe = unsafe { ffi::sqlite3_value_type(self.value.as_ptr()) }; + match tpe { + ffi::SQLITE_TEXT => Some(SqliteType::Text), + ffi::SQLITE_INTEGER => Some(SqliteType::Long), + ffi::SQLITE_FLOAT => Some(SqliteType::Double), + ffi::SQLITE_BLOB => Some(SqliteType::Binary), + ffi::SQLITE_NULL => None, + _ => unreachable!( + "Sqlite's documentation state that this case ({}) is not reachable. \ + If you ever see this error message please open an issue at \ + https://github.com/diesel-rs/diesel.", + tpe + ), + } + } +} + +impl OwnedSqliteValue { + pub(super) fn copy_from_ptr(ptr: NonNull) -> Option { + let tpe = unsafe { ffi::sqlite3_value_type(ptr.as_ptr()) }; + if ffi::SQLITE_NULL == tpe { + return None; + } + let value = unsafe { ffi::sqlite3_value_dup(ptr.as_ptr()) }; + Some(Self { + value: NonNull::new(value)?, + }) + } + + pub(super) fn duplicate(&self) -> OwnedSqliteValue { + // self.value is a `NonNull` ptr so this cannot be null + let value = unsafe { ffi::sqlite3_value_dup(self.value.as_ptr()) }; + let value = NonNull::new(value).expect( + "Sqlite documentation states this returns only null if value is null \ + or OOM. If you ever see this panic message please open an issue at \ + https://github.com/diesel-rs/diesel.", + ); + OwnedSqliteValue { value } + } +} + +#[cfg(test)] +mod tests { + use crate::connection::{LoadConnection, SimpleConnection}; + use crate::row::Field; + use crate::row::Row; + use crate::sql_types::{Blob, Double, Int4, Text}; + use crate::*; + + #[expect(clippy::approx_constant)] // we really want to use 3.14 + #[test] + fn can_convert_all_values() { + let mut conn = SqliteConnection::establish(":memory:").unwrap(); + + conn.batch_execute("CREATE TABLE tests(int INTEGER, text TEXT, blob BLOB, float FLOAT)") + .unwrap(); + + diesel::sql_query("INSERT INTO tests(int, text, blob, float) VALUES(?, ?, ?, ?)") + .bind::(42) + .bind::("foo") + .bind::(b"foo") + .bind::(3.14) + .execute(&mut conn) + .unwrap(); + + let mut res = conn + .load(diesel::sql_query( + "SELECT int, text, blob, float FROM tests", + )) + .unwrap(); + let row = res.next().unwrap().unwrap(); + let int_field = row.get(0).unwrap(); + let text_field = row.get(1).unwrap(); + let blob_field = row.get(2).unwrap(); + let float_field = row.get(3).unwrap(); + + let mut int_value = int_field.value().unwrap(); + assert_eq!(int_value.read_integer(), 42); + let mut int_value = int_field.value().unwrap(); + assert_eq!(int_value.read_long(), 42); + let mut int_value = int_field.value().unwrap(); + assert_eq!(int_value.read_double(), 42.0); + let mut int_value = int_field.value().unwrap(); + assert_eq!(int_value.read_text(), "42"); + let mut int_value = int_field.value().unwrap(); + assert_eq!(int_value.read_blob(), b"42"); + + let mut text_value = text_field.value().unwrap(); + assert_eq!(text_value.read_integer(), 0); + let mut text_value = text_field.value().unwrap(); + assert_eq!(text_value.read_long(), 0); + let mut text_value = text_field.value().unwrap(); + assert_eq!(text_value.read_double(), 0.0); + let mut text_value = text_field.value().unwrap(); + assert_eq!(text_value.read_text(), "foo"); + let mut text_value = text_field.value().unwrap(); + assert_eq!(text_value.read_blob(), b"foo"); + + let mut blob_value = blob_field.value().unwrap(); + assert_eq!(blob_value.read_integer(), 0); + let mut blob_value = blob_field.value().unwrap(); + assert_eq!(blob_value.read_long(), 0); + let mut blob_value = blob_field.value().unwrap(); + assert_eq!(blob_value.read_double(), 0.0); + let mut blob_value = blob_field.value().unwrap(); + assert_eq!(blob_value.read_text(), "foo"); + let mut blob_value = blob_field.value().unwrap(); + assert_eq!(blob_value.read_blob(), b"foo"); + + let mut float_value = float_field.value().unwrap(); + assert_eq!(float_value.read_integer(), 3); + let mut float_value = float_field.value().unwrap(); + assert_eq!(float_value.read_long(), 3); + let mut float_value = float_field.value().unwrap(); + assert_eq!(float_value.read_double(), 3.14); + let mut float_value = float_field.value().unwrap(); + assert_eq!(float_value.read_text(), "3.14"); + let mut float_value = float_field.value().unwrap(); + assert_eq!(float_value.read_blob(), b"3.14"); + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/statement_iterator.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/statement_iterator.rs new file mode 100644 index 000000000..39ae81237 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/statement_iterator.rs @@ -0,0 +1,175 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use super::row::{PrivateSqliteRow, SqliteRow}; +use super::stmt::StatementUse; +use crate::result::QueryResult; + +#[allow(missing_debug_implementations)] +pub struct StatementIterator<'stmt, 'query> { + inner: PrivateStatementIterator<'stmt, 'query>, + column_names: Option]>>, + field_count: usize, +} + +impl<'stmt, 'query> StatementIterator<'stmt, 'query> { + #[cold] + #[allow(unsafe_code)] // call to unsafe function + fn handle_duplicated_row_case( + outer_last_row: &mut Rc>>, + column_names: &mut Option]>>, + field_count: usize, + ) -> Option>> { + // We don't own the statement. There is another existing reference, likely because + // a user stored the row in some long time container before calling next another time + // In this case we copy out the current values into a temporary store and advance + // the statement iterator internally afterwards + let last_row = { + let mut last_row = match outer_last_row.try_borrow_mut() { + Ok(o) => o, + Err(_e) => { + return Some(Err(crate::result::Error::DeserializationError( + "Failed to reborrow row. Try to release any `SqliteField` or `SqliteValue` \ + that exists at this point" + .into(), + ))); + } + }; + let last_row = &mut *last_row; + let duplicated = last_row.duplicate(column_names); + std::mem::replace(last_row, duplicated) + }; + if let PrivateSqliteRow::Direct(mut stmt) = last_row { + let res = unsafe { + // This is actually safe here as we've already + // performed one step. For the first step we would have + // used `PrivateStatementIterator::NotStarted` where we don't + // have access to `PrivateSqliteRow` at all + stmt.step(false) + }; + *outer_last_row = Rc::new(RefCell::new(PrivateSqliteRow::Direct(stmt))); + match res { + Err(e) => Some(Err(e)), + Ok(false) => None, + Ok(true) => Some(Ok(SqliteRow { + inner: Rc::clone(outer_last_row), + field_count, + })), + } + } else { + // any other state than `PrivateSqliteRow::Direct` is invalid here + // and should not happen. If this ever happens this is a logic error + // in the code above + unreachable!( + "You've reached an impossible internal state. \ + If you ever see this error message please open \ + an issue at https://github.com/diesel-rs/diesel \ + providing example code how to trigger this error." + ) + } + } +} + +enum PrivateStatementIterator<'stmt, 'query> { + NotStarted(Option>), + Started(Rc>>), +} + +impl<'stmt, 'query> StatementIterator<'stmt, 'query> { + pub fn new(stmt: StatementUse<'stmt, 'query>) -> StatementIterator<'stmt, 'query> { + Self { + inner: PrivateStatementIterator::NotStarted(Some(stmt)), + column_names: None, + field_count: 0, + } + } +} + +impl<'stmt, 'query> Iterator for StatementIterator<'stmt, 'query> { + type Item = QueryResult>; + + #[allow(unsafe_code)] // call to unsafe function + fn next(&mut self) -> Option { + use PrivateStatementIterator::{NotStarted, Started}; + match &mut self.inner { + NotStarted(ref mut stmt @ Some(_)) => { + let mut stmt = stmt + .take() + .expect("It must be there because we checked that above"); + let step = unsafe { + // This is safe as we pass `first_step = true` to reset the cached column names + stmt.step(true) + }; + match step { + Err(e) => Some(Err(e)), + Ok(false) => None, + Ok(true) => { + let field_count = stmt + .column_count() + .try_into() + .expect("Diesel expects to run at least on a 32 bit platform"); + self.field_count = field_count; + let inner = Rc::new(RefCell::new(PrivateSqliteRow::Direct(stmt))); + self.inner = Started(inner.clone()); + Some(Ok(SqliteRow { inner, field_count })) + } + } + } + Started(ref mut last_row) => { + // There was already at least one iteration step + // We check here if the caller already released the row value or not + // by checking if our Rc owns the data or not + if let Some(last_row_ref) = Rc::get_mut(last_row) { + // We own the statement, there is no other reference here. + // This means we don't need to copy out values from the sqlite provided + // datastructures for now + // We don't need to use the runtime borrowing system of the RefCell here + // as we have a mutable reference, so all of this below is checked at compile time + if let PrivateSqliteRow::Direct(ref mut stmt) = last_row_ref.get_mut() { + let step = unsafe { + // This is actually safe here as we've already + // performed one step. For the first step we would have + // used `PrivateStatementIterator::NotStarted` where we don't + // have access to `PrivateSqliteRow` at all + + stmt.step(false) + }; + match step { + Err(e) => Some(Err(e)), + Ok(false) => None, + Ok(true) => { + let field_count = self.field_count; + Some(Ok(SqliteRow { + inner: Rc::clone(last_row), + field_count, + })) + } + } + } else { + // any other state than `PrivateSqliteRow::Direct` is invalid here + // and should not happen. If this ever happens this is a logic error + // in the code above + unreachable!( + "You've reached an impossible internal state. \ + If you ever see this error message please open \ + an issue at https://github.com/diesel-rs/diesel \ + providing example code how to trigger this error." + ) + } + } else { + Self::handle_duplicated_row_case( + last_row, + &mut self.column_names, + self.field_count, + ) + } + } + NotStarted(_s) => { + // we likely got an error while executing the other + // `NotStarted` branch above. In this case we just want to stop + // iterating here + None + } + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/stmt.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/stmt.rs new file mode 100644 index 000000000..55e88f804 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/connection/stmt.rs @@ -0,0 +1,564 @@ +#![allow(unsafe_code)] // fii code +use super::bind_collector::{InternalSqliteBindValue, SqliteBindCollector}; +use super::raw::RawConnection; +use super::sqlite_value::OwnedSqliteValue; +use crate::connection::statement_cache::{MaybeCached, PrepareForCache}; +use crate::connection::Instrumentation; +use crate::query_builder::{QueryFragment, QueryId}; +use crate::result::Error::DatabaseError; +use crate::result::*; +use crate::sqlite::{Sqlite, SqliteType}; +use libsqlite3_sys as ffi; +use std::cell::OnceCell; +use std::ffi::{CStr, CString}; +use std::io::{stderr, Write}; +use std::os::raw as libc; +use std::ptr::{self, NonNull}; + +pub(super) struct Statement { + inner_statement: NonNull, +} + +impl Statement { + pub(super) fn prepare( + raw_connection: &RawConnection, + sql: &str, + is_cached: PrepareForCache, + ) -> QueryResult { + let mut stmt = ptr::null_mut(); + let mut unused_portion = ptr::null(); + let n_byte = sql + .len() + .try_into() + .map_err(|e| Error::SerializationError(Box::new(e)))?; + // the cast for `ffi::SQLITE_PREPARE_PERSISTENT` is required for old libsqlite3-sys versions + #[allow(clippy::unnecessary_cast)] + let prepare_result = unsafe { + ffi::sqlite3_prepare_v3( + raw_connection.internal_connection.as_ptr(), + CString::new(sql)?.as_ptr(), + n_byte, + if matches!(is_cached, PrepareForCache::Yes) { + ffi::SQLITE_PREPARE_PERSISTENT as u32 + } else { + 0 + }, + &mut stmt, + &mut unused_portion, + ) + }; + + ensure_sqlite_ok(prepare_result, raw_connection.internal_connection.as_ptr())?; + + // sqlite3_prepare_v3 returns a null pointer for empty statements. This includes + // empty or only whitespace strings or any other non-op query string like a comment + let inner_statement = NonNull::new(stmt).ok_or_else(|| { + crate::result::Error::QueryBuilderError(Box::new(crate::result::EmptyQuery)) + })?; + Ok(Statement { inner_statement }) + } + + // The caller of this function has to ensure that: + // * Any buffer provided as `SqliteBindValue::BorrowedBinary`, `SqliteBindValue::Binary` + // `SqliteBindValue::String` or `SqliteBindValue::BorrowedString` is valid + // till either a new value is bound to the same parameter or the underlying + // prepared statement is dropped. + unsafe fn bind( + &mut self, + tpe: SqliteType, + value: InternalSqliteBindValue<'_>, + bind_index: i32, + ) -> QueryResult>> { + let mut ret_ptr = None; + let result = match (tpe, value) { + (_, InternalSqliteBindValue::Null) => { + ffi::sqlite3_bind_null(self.inner_statement.as_ptr(), bind_index) + } + (SqliteType::Binary, InternalSqliteBindValue::BorrowedBinary(bytes)) => { + let n = bytes + .len() + .try_into() + .map_err(|e| Error::SerializationError(Box::new(e)))?; + ffi::sqlite3_bind_blob( + self.inner_statement.as_ptr(), + bind_index, + bytes.as_ptr() as *const libc::c_void, + n, + ffi::SQLITE_STATIC(), + ) + } + (SqliteType::Binary, InternalSqliteBindValue::Binary(mut bytes)) => { + let len = bytes + .len() + .try_into() + .map_err(|e| Error::SerializationError(Box::new(e)))?; + // We need a separate pointer here to pass it to sqlite + // as the returned pointer is a pointer to a dyn sized **slice** + // and not the pointer to the first element of the slice + let ptr = bytes.as_mut_ptr(); + ret_ptr = NonNull::new(Box::into_raw(bytes)); + ffi::sqlite3_bind_blob( + self.inner_statement.as_ptr(), + bind_index, + ptr as *const libc::c_void, + len, + ffi::SQLITE_STATIC(), + ) + } + (SqliteType::Text, InternalSqliteBindValue::BorrowedString(bytes)) => { + let len = bytes + .len() + .try_into() + .map_err(|e| Error::SerializationError(Box::new(e)))?; + ffi::sqlite3_bind_text( + self.inner_statement.as_ptr(), + bind_index, + bytes.as_ptr() as *const libc::c_char, + len, + ffi::SQLITE_STATIC(), + ) + } + (SqliteType::Text, InternalSqliteBindValue::String(bytes)) => { + let mut bytes = Box::<[u8]>::from(bytes); + let len = bytes + .len() + .try_into() + .map_err(|e| Error::SerializationError(Box::new(e)))?; + // We need a separate pointer here to pass it to sqlite + // as the returned pointer is a pointer to a dyn sized **slice** + // and not the pointer to the first element of the slice + let ptr = bytes.as_mut_ptr(); + ret_ptr = NonNull::new(Box::into_raw(bytes)); + ffi::sqlite3_bind_text( + self.inner_statement.as_ptr(), + bind_index, + ptr as *const libc::c_char, + len, + ffi::SQLITE_STATIC(), + ) + } + (SqliteType::Float, InternalSqliteBindValue::F64(value)) + | (SqliteType::Double, InternalSqliteBindValue::F64(value)) => { + ffi::sqlite3_bind_double( + self.inner_statement.as_ptr(), + bind_index, + value as libc::c_double, + ) + } + (SqliteType::SmallInt, InternalSqliteBindValue::I32(value)) + | (SqliteType::Integer, InternalSqliteBindValue::I32(value)) => { + ffi::sqlite3_bind_int(self.inner_statement.as_ptr(), bind_index, value) + } + (SqliteType::Long, InternalSqliteBindValue::I64(value)) => { + ffi::sqlite3_bind_int64(self.inner_statement.as_ptr(), bind_index, value) + } + (t, b) => { + return Err(Error::SerializationError( + format!("Type mismatch: Expected {t:?}, got {b}").into(), + )) + } + }; + match ensure_sqlite_ok(result, self.raw_connection()) { + Ok(()) => Ok(ret_ptr), + Err(e) => { + if let Some(ptr) = ret_ptr { + // This is a `NonNul` ptr so it cannot be null + // It points to a slice internally as we did not apply + // any cast above. + std::mem::drop(Box::from_raw(ptr.as_ptr())) + } + Err(e) + } + } + } + + fn reset(&mut self) { + unsafe { ffi::sqlite3_reset(self.inner_statement.as_ptr()) }; + } + + fn raw_connection(&self) -> *mut ffi::sqlite3 { + unsafe { ffi::sqlite3_db_handle(self.inner_statement.as_ptr()) } + } +} + +pub(super) fn ensure_sqlite_ok( + code: libc::c_int, + raw_connection: *mut ffi::sqlite3, +) -> QueryResult<()> { + if code == ffi::SQLITE_OK { + Ok(()) + } else { + Err(last_error(raw_connection)) + } +} + +fn last_error(raw_connection: *mut ffi::sqlite3) -> Error { + let error_message = last_error_message(raw_connection); + let error_information = Box::new(error_message); + let error_kind = match last_error_code(raw_connection) { + ffi::SQLITE_CONSTRAINT_UNIQUE | ffi::SQLITE_CONSTRAINT_PRIMARYKEY => { + DatabaseErrorKind::UniqueViolation + } + ffi::SQLITE_CONSTRAINT_FOREIGNKEY => DatabaseErrorKind::ForeignKeyViolation, + ffi::SQLITE_CONSTRAINT_NOTNULL => DatabaseErrorKind::NotNullViolation, + ffi::SQLITE_CONSTRAINT_CHECK => DatabaseErrorKind::CheckViolation, + _ => DatabaseErrorKind::Unknown, + }; + DatabaseError(error_kind, error_information) +} + +fn last_error_message(conn: *mut ffi::sqlite3) -> String { + let c_str = unsafe { CStr::from_ptr(ffi::sqlite3_errmsg(conn)) }; + c_str.to_string_lossy().into_owned() +} + +fn last_error_code(conn: *mut ffi::sqlite3) -> libc::c_int { + unsafe { ffi::sqlite3_extended_errcode(conn) } +} + +impl Drop for Statement { + fn drop(&mut self) { + use std::thread::panicking; + + let raw_connection = self.raw_connection(); + let finalize_result = unsafe { ffi::sqlite3_finalize(self.inner_statement.as_ptr()) }; + if let Err(e) = ensure_sqlite_ok(finalize_result, raw_connection) { + if panicking() { + write!( + stderr(), + "Error finalizing SQLite prepared statement: {e:?}" + ) + .expect("Error writing to `stderr`"); + } else { + panic!("Error finalizing SQLite prepared statement: {:?}", e); + } + } + } +} + +// A warning for future editors: +// Changing this code to something "simpler" may +// introduce undefined behaviour. Make sure you read +// the following discussions for details about +// the current version: +// +// * https://github.com/weiznich/diesel/pull/7 +// * https://users.rust-lang.org/t/code-review-for-unsafe-code-in-diesel/66798/ +// * https://github.com/rust-lang/unsafe-code-guidelines/issues/194 +struct BoundStatement<'stmt, 'query> { + statement: MaybeCached<'stmt, Statement>, + // we need to store the query here to ensure no one does + // drop it till the end of the statement + // We use a boxed queryfragment here just to erase the + // generic type, we use NonNull to communicate + // that this is a shared buffer + query: Option + 'query>>, + // we need to store any owned bind values separately, as they are not + // contained in the query itself. We use NonNull to + // communicate that this is a shared buffer + binds_to_free: Vec<(i32, Option>)>, + instrumentation: &'stmt mut dyn Instrumentation, + has_error: bool, +} + +impl<'stmt, 'query> BoundStatement<'stmt, 'query> { + fn bind( + statement: MaybeCached<'stmt, Statement>, + query: T, + instrumentation: &'stmt mut dyn Instrumentation, + ) -> QueryResult> + where + T: QueryFragment + QueryId + 'query, + { + // Don't use a trait object here to prevent using a virtual function call + // For sqlite this can introduce a measurable overhead + // Query is boxed here to make sure it won't move in memory anymore, so any bind + // it could output would stay valid. + let query = Box::new(query); + + let mut bind_collector = SqliteBindCollector::new(); + query.collect_binds(&mut bind_collector, &mut (), &Sqlite)?; + let SqliteBindCollector { binds } = bind_collector; + + let mut ret = BoundStatement { + statement, + query: None, + binds_to_free: Vec::new(), + instrumentation, + has_error: false, + }; + + ret.bind_buffers(binds)?; + + let query = query as Box + 'query>; + ret.query = NonNull::new(Box::into_raw(query)); + + Ok(ret) + } + + // This is a separated function so that + // not the whole constructor is generic over the query type T. + // This hopefully prevents binary bloat. + fn bind_buffers( + &mut self, + binds: Vec<(InternalSqliteBindValue<'_>, SqliteType)>, + ) -> QueryResult<()> { + // It is useful to preallocate `binds_to_free` because it + // - Guarantees that pushing inside it cannot panic, which guarantees the `Drop` + // impl of `BoundStatement` will always re-`bind` as needed + // - Avoids reallocations + self.binds_to_free.reserve( + binds + .iter() + .filter(|&(b, _)| { + matches!( + b, + InternalSqliteBindValue::BorrowedBinary(_) + | InternalSqliteBindValue::BorrowedString(_) + | InternalSqliteBindValue::String(_) + | InternalSqliteBindValue::Binary(_) + ) + }) + .count(), + ); + for (bind_idx, (bind, tpe)) in (1..).zip(binds) { + let is_borrowed_bind = matches!( + bind, + InternalSqliteBindValue::BorrowedString(_) + | InternalSqliteBindValue::BorrowedBinary(_) + ); + + // It's safe to call bind here as: + // * The type and value matches + // * We ensure that corresponding buffers lives long enough below + // * The statement is not used yet by `step` or anything else + let res = unsafe { self.statement.bind(tpe, bind, bind_idx) }?; + + // it's important to push these only after + // the call to bind succeeded, otherwise we might attempt to + // call bind to an non-existing bind position in + // the destructor + if let Some(ptr) = res { + // Store the id + pointer for a owned bind + // as we must unbind and free them on drop + self.binds_to_free.push((bind_idx, Some(ptr))); + } else if is_borrowed_bind { + // Store the id's of borrowed binds to unbind them on drop + self.binds_to_free.push((bind_idx, None)); + } + } + Ok(()) + } + + fn finish_query_with_error(mut self, e: &Error) { + self.has_error = true; + if let Some(q) = self.query { + // it's safe to get a reference from this ptr as it's guaranteed to not be null + let q = unsafe { q.as_ref() }; + self.instrumentation.on_connection_event( + crate::connection::InstrumentationEvent::FinishQuery { + query: &crate::debug_query(&q), + error: Some(e), + }, + ); + } + } +} + +impl Drop for BoundStatement<'_, '_> { + fn drop(&mut self) { + // First reset the statement, otherwise the bind calls + // below will fails + self.statement.reset(); + + for (idx, buffer) in std::mem::take(&mut self.binds_to_free) { + unsafe { + // It's always safe to bind null values, as there is no buffer that needs to outlife something + self.statement + .bind(SqliteType::Text, InternalSqliteBindValue::Null, idx) + .expect( + "Binding a null value should never fail. \ + If you ever see this error message please open \ + an issue at diesels issue tracker containing \ + code how to trigger this message.", + ); + } + + if let Some(buffer) = buffer { + unsafe { + // Constructing the `Box` here is safe as we + // got the pointer from a box + it is guaranteed to be not null. + std::mem::drop(Box::from_raw(buffer.as_ptr())); + } + } + } + + if let Some(query) = self.query { + let query = unsafe { + // Constructing the `Box` here is safe as we + // got the pointer from a box + it is guaranteed to be not null. + Box::from_raw(query.as_ptr()) + }; + if !self.has_error { + self.instrumentation.on_connection_event( + crate::connection::InstrumentationEvent::FinishQuery { + query: &crate::debug_query(&query), + error: None, + }, + ); + } + std::mem::drop(query); + self.query = None; + } + } +} + +#[allow(missing_debug_implementations)] +pub struct StatementUse<'stmt, 'query> { + statement: BoundStatement<'stmt, 'query>, + column_names: OnceCell>, +} + +impl<'stmt, 'query> StatementUse<'stmt, 'query> { + pub(super) fn bind( + statement: MaybeCached<'stmt, Statement>, + query: T, + instrumentation: &'stmt mut dyn Instrumentation, + ) -> QueryResult> + where + T: QueryFragment + QueryId + 'query, + { + Ok(Self { + statement: BoundStatement::bind(statement, query, instrumentation)?, + column_names: OnceCell::new(), + }) + } + + pub(super) fn run(mut self) -> QueryResult<()> { + let r = unsafe { + // This is safe as we pass `first_step = true` + // and we consume the statement so nobody could + // access the columns later on anyway. + self.step(true).map(|_| ()) + }; + if let Err(ref e) = r { + self.statement.finish_query_with_error(e); + } + r + } + + // This function is marked as unsafe incorrectly passing `false` to `first_step` + // for a first call to this function could cause access to freed memory via + // the cached column names. + // + // It's always safe to call this function with `first_step = true` as this removes + // the cached column names + pub(super) unsafe fn step(&mut self, first_step: bool) -> QueryResult { + let res = match ffi::sqlite3_step(self.statement.statement.inner_statement.as_ptr()) { + ffi::SQLITE_DONE => Ok(false), + ffi::SQLITE_ROW => Ok(true), + _ => Err(last_error(self.statement.statement.raw_connection())), + }; + if first_step { + self.column_names = OnceCell::new(); + } + res + } + + // The returned string pointer is valid until either the prepared statement is + // destroyed by sqlite3_finalize() or until the statement is automatically + // reprepared by the first call to sqlite3_step() for a particular run or + // until the next call to sqlite3_column_name() or sqlite3_column_name16() + // on the same column. + // + // https://sqlite.org/c3ref/column_name.html + // + // Note: This function is marked as unsafe, as calling it can invalidate + // other existing column name pointers on the same column. To prevent that, + // it should maximally be called once per column at all. + unsafe fn column_name(&self, idx: i32) -> *const str { + let name = { + let column_name = + ffi::sqlite3_column_name(self.statement.statement.inner_statement.as_ptr(), idx); + assert!( + !column_name.is_null(), + "The Sqlite documentation states that it only returns a \ + null pointer here if we are in a OOM condition." + ); + CStr::from_ptr(column_name) + }; + name.to_str().expect( + "The Sqlite documentation states that this is UTF8. \ + If you see this error message something has gone \ + horribly wrong. Please open an issue at the \ + diesel repository.", + ) as *const str + } + + pub(super) fn column_count(&self) -> i32 { + unsafe { ffi::sqlite3_column_count(self.statement.statement.inner_statement.as_ptr()) } + } + + pub(super) fn index_for_column_name(&mut self, field_name: &str) -> Option { + (0..self.column_count()) + .find(|idx| self.field_name(*idx) == Some(field_name)) + .map(|v| { + v.try_into() + .expect("Diesel expects to run at least on a 32 bit platform") + }) + } + + pub(super) fn field_name(&self, idx: i32) -> Option<&str> { + let column_names = self.column_names.get_or_init(|| { + let count = self.column_count(); + (0..count) + .map(|idx| unsafe { + // By initializing the whole vec at once we ensure that + // we really call this only once. + self.column_name(idx) + }) + .collect() + }); + + column_names + .get(usize::try_from(idx).expect("Diesel expects to run at least on a 32 bit platform")) + .and_then(|c| unsafe { c.as_ref() }) + } + + pub(super) fn copy_value(&self, idx: i32) -> Option { + OwnedSqliteValue::copy_from_ptr(self.column_value(idx)?) + } + + pub(super) fn column_value(&self, idx: i32) -> Option> { + let ptr = unsafe { + ffi::sqlite3_column_value(self.statement.statement.inner_statement.as_ptr(), idx) + }; + NonNull::new(ptr) + } +} + +#[cfg(test)] +mod tests { + use crate::prelude::*; + use crate::sql_types::Text; + + // this is a regression test for + // https://github.com/diesel-rs/diesel/issues/3558 + #[test] + fn check_out_of_bounds_bind_does_not_panic_on_drop() { + let mut conn = SqliteConnection::establish(":memory:").unwrap(); + + let e = crate::sql_query("SELECT '?'") + .bind::("foo") + .execute(&mut conn); + + assert!(e.is_err()); + let e = e.unwrap_err(); + if let crate::result::Error::DatabaseError(crate::result::DatabaseErrorKind::Unknown, m) = e + { + assert_eq!(m.message(), "column index out of range"); + } else { + panic!("Wrong error returned"); + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/expression/expression_methods.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/expression/expression_methods.rs new file mode 100644 index 000000000..1708c4975 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/expression/expression_methods.rs @@ -0,0 +1,84 @@ +//! Sqlite specific expression methods. + +use super::operators::*; +use crate::dsl; +use crate::expression::grouped::Grouped; +use crate::expression::{AsExpression, Expression}; +use crate::sql_types::SqlType; + +/// Sqlite specific methods which are present on all expressions. +#[cfg(feature = "sqlite")] +pub trait SqliteExpressionMethods: Expression + Sized { + /// Creates a Sqlite `IS` expression. + /// + /// The `IS` operator work like = except when one or both of the operands are NULL. + /// In this case, if both operands are NULL, then the `IS` operator evaluates to true. + /// If one operand is NULL and the other is not, then the `IS` operator evaluates to false. + /// It is not possible for an `IS` expression to evaluate to NULL. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// let jack_is_a_dog = animals + /// .select(name) + /// .filter(species.is("dog")) + /// .get_results::>(connection)?; + /// assert_eq!(vec![Some("Jack".to_string())], jack_is_a_dog); + /// # Ok(()) + /// # } + /// ``` + fn is(self, other: T) -> dsl::Is + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Is::new(self, other.as_expression())) + } + + /// Creates a Sqlite `IS NOT` expression. + /// + /// The `IS NOT` operator work like != except when one or both of the operands are NULL. + /// In this case, if both operands are NULL, then the `IS NOT` operator evaluates to false. + /// If one operand is NULL and the other is not, then the `IS NOT` operator is true. + /// It is not possible for an `IS NOT` expression to evaluate to NULL. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = &mut establish_connection(); + /// let jack_is_not_a_spider = animals + /// .select(name) + /// .filter(species.is_not("spider")) + /// .get_results::>(connection)?; + /// assert_eq!(vec![Some("Jack".to_string())], jack_is_not_a_spider); + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::wrong_self_convention)] // This is named after the sql operator + fn is_not(self, other: T) -> dsl::IsNot + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(IsNot::new(self, other.as_expression())) + } +} + +impl SqliteExpressionMethods for T {} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/expression/helper_types.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/expression/helper_types.rs new file mode 100644 index 000000000..77f54ebe0 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/expression/helper_types.rs @@ -0,0 +1,8 @@ +use crate::dsl::AsExpr; +use crate::expression::grouped::Grouped; + +/// The return type of `lhs.is(rhs)`. +pub type Is = Grouped>>; + +/// The return type of `lhs.is_not(rhs)`. +pub type IsNot = Grouped>>; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/expression/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/expression/mod.rs new file mode 100644 index 000000000..00a224ca9 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/expression/mod.rs @@ -0,0 +1,9 @@ +//! Sqlite related query builder extensions. +//! +//! Everything in this module is re-exported from database agnostic locations. +//! You should rely on the re-exports rather than this module directly. It is +//! kept separate purely for documentation purposes. + +pub(crate) mod expression_methods; +pub(crate) mod helper_types; +mod operators; diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/expression/operators.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/expression/operators.rs new file mode 100644 index 000000000..4419bd60c --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/expression/operators.rs @@ -0,0 +1,5 @@ +use crate::sql_types::Bool; +use crate::sqlite::Sqlite; + +__diesel_infix_operator!(Is, " IS ", ConstantNullability Bool, backend: Sqlite); +__diesel_infix_operator!(IsNot, " IS NOT ", ConstantNullability Bool, backend: Sqlite); diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/mod.rs new file mode 100644 index 000000000..45cabe6f8 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/mod.rs @@ -0,0 +1,52 @@ +//! Provides types and functions related to working with SQLite +//! +//! Much of this module is re-exported from database agnostic locations. +//! However, if you are writing code specifically to extend Diesel on +//! SQLite, you may need to work with this module directly. + +pub(crate) mod backend; +mod connection; +pub(crate) mod expression; + +pub mod query_builder; + +mod types; + +pub use self::backend::{Sqlite, SqliteType}; +pub use self::connection::SerializedDatabase; +pub use self::connection::SqliteBindValue; +pub use self::connection::SqliteConnection; +pub use self::connection::SqliteValue; +pub use self::query_builder::SqliteQueryBuilder; + +/// Trait for the implementation of a SQLite aggregate function +/// +/// This trait is to be used in conjunction with the `define_sql_function!` +/// macro for defining a custom SQLite aggregate function. See +/// the documentation [there](super::prelude::define_sql_function!) for details. +pub trait SqliteAggregateFunction: Default { + /// The result type of the SQLite aggregate function + type Output; + + /// The `step()` method is called once for every record of the query. + /// + /// This is called through a C FFI, as such panics do not propagate to the caller. Panics are + /// caught and cause a return with an error value. The implementation must still ensure that + /// state remains in a valid state (refer to [`std::panic::UnwindSafe`] for a bit more detail). + fn step(&mut self, args: Args); + + /// After the last row has been processed, the `finalize()` method is + /// called to compute the result of the aggregate function. If no rows + /// were processed `aggregator` will be `None` and `finalize()` can be + /// used to specify a default result. + /// + /// This is called through a C FFI, as such panics do not propagate to the caller. Panics are + /// caught and cause a return with an error value. + fn finalize(aggregator: Option) -> Self::Output; +} + +/// SQLite specific sql types +pub mod sql_types { + #[doc(inline)] + pub use super::types::Timestamptz; +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/query_builder/limit_offset.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/query_builder/limit_offset.rs new file mode 100644 index 000000000..376853d76 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/query_builder/limit_offset.rs @@ -0,0 +1,126 @@ +use crate::query_builder::limit_clause::{LimitClause, NoLimitClause}; +use crate::query_builder::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +use crate::query_builder::offset_clause::{NoOffsetClause, OffsetClause}; +use crate::query_builder::{AstPass, IntoBoxedClause, QueryFragment}; +use crate::result::QueryResult; +use crate::sqlite::Sqlite; + +impl QueryFragment for LimitOffsetClause { + fn walk_ast<'b>(&'b self, _out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, NoOffsetClause> +where + LimitClause: QueryFragment, +{ + fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> { + self.limit_clause.walk_ast(out)?; + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause> +where + OffsetClause: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> { + // Sqlite requires a limit clause in front of any offset clause + // using `LIMIT -1` is the same as not having any limit clause + // https://sqlite.org/lang_select.html + out.push_sql(" LIMIT -1 "); + self.offset_clause.walk_ast(out)?; + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, OffsetClause> +where + LimitClause: QueryFragment, + OffsetClause: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> { + self.limit_clause.walk_ast(out.reborrow())?; + self.offset_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl QueryFragment for BoxedLimitOffsetClause<'_, Sqlite> { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> { + match (self.limit.as_ref(), self.offset.as_ref()) { + (Some(limit), Some(offset)) => { + limit.walk_ast(out.reborrow())?; + offset.walk_ast(out.reborrow())?; + } + (Some(limit), None) => { + limit.walk_ast(out.reborrow())?; + } + (None, Some(offset)) => { + // See the `QueryFragment` implementation for `LimitOffsetClause` for details. + out.push_sql(" LIMIT -1 "); + offset.walk_ast(out.reborrow())?; + } + (None, None) => {} + } + Ok(()) + } +} + +// Have explicit impls here because we need to set `Some`/`None` for the clauses +// correspondingly, otherwise we cannot match on it in the `QueryFragment` impl +// above +impl<'a> IntoBoxedClause<'a, Sqlite> for LimitOffsetClause { + type BoxedClause = BoxedLimitOffsetClause<'a, Sqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: None, + offset: None, + } + } +} + +impl<'a, L> IntoBoxedClause<'a, Sqlite> for LimitOffsetClause, NoOffsetClause> +where + L: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Sqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: None, + } + } +} + +impl<'a, O> IntoBoxedClause<'a, Sqlite> for LimitOffsetClause> +where + O: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Sqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: None, + offset: Some(Box::new(self.offset_clause)), + } + } +} + +impl<'a, L, O> IntoBoxedClause<'a, Sqlite> for LimitOffsetClause, OffsetClause> +where + L: QueryFragment + Send + 'a, + O: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Sqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: Some(Box::new(self.offset_clause)), + } + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/query_builder/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/query_builder/mod.rs new file mode 100644 index 000000000..64d08595e --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/query_builder/mod.rs @@ -0,0 +1,44 @@ +//! The SQLite query builder + +use super::backend::Sqlite; +use crate::query_builder::QueryBuilder; +use crate::result::QueryResult; + +mod limit_offset; +mod query_fragment_impls; +mod returning; + +/// Constructs SQL queries for use with the SQLite backend +#[allow(missing_debug_implementations)] +#[derive(Default)] +pub struct SqliteQueryBuilder { + sql: String, +} + +impl SqliteQueryBuilder { + /// Construct a new query builder with an empty query + pub fn new() -> Self { + SqliteQueryBuilder::default() + } +} + +impl QueryBuilder for SqliteQueryBuilder { + fn push_sql(&mut self, sql: &str) { + self.sql.push_str(sql); + } + + fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> { + self.push_sql("`"); + self.push_sql(&identifier.replace('`', "``")); + self.push_sql("`"); + Ok(()) + } + + fn push_bind_param(&mut self) { + self.push_sql("?"); + } + + fn finish(self) -> String { + self.sql + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/query_builder/query_fragment_impls.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/query_builder/query_fragment_impls.rs new file mode 100644 index 000000000..c7cfb0bfb --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/query_builder/query_fragment_impls.rs @@ -0,0 +1,43 @@ +use crate::query_builder::select_statement::boxed::BoxedQueryHelper; +use crate::query_builder::upsert::into_conflict_clause::OnConflictSelectWrapper; +use crate::query_builder::where_clause::BoxedWhereClause; +use crate::query_builder::where_clause::WhereClause; +use crate::query_builder::AstPass; +use crate::query_builder::BoxedSelectStatement; +use crate::query_builder::QueryFragment; +use crate::query_builder::SelectStatement; +use crate::QueryResult; + +// The corresponding impl for`NoWhereClause` is missing because of +// https://www.sqlite.org/lang_UPSERT.html (Parsing Ambiguity) +#[cfg(feature = "sqlite")] +impl QueryFragment + for OnConflictSelectWrapper, O, LOf, G, H, LC>> +where + SelectStatement, O, LOf, G, H, LC>: + QueryFragment, +{ + fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, crate::sqlite::Sqlite>) -> QueryResult<()> { + self.0.walk_ast(out) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, ST, QS, GB> QueryFragment + for OnConflictSelectWrapper> +where + BoxedSelectStatement<'a, ST, QS, crate::sqlite::Sqlite, GB>: + QueryFragment, + QS: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, crate::sqlite::Sqlite>) -> QueryResult<()> { + // https://www.sqlite.org/lang_UPSERT.html (Parsing Ambiguity) + self.0.build_query(pass, |where_clause, mut pass| { + match where_clause { + BoxedWhereClause::None => pass.push_sql(" WHERE 1=1 "), + w => w.walk_ast(pass.reborrow())?, + } + Ok(()) + }) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/query_builder/returning.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/query_builder/returning.rs new file mode 100644 index 000000000..fa626f840 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/query_builder/returning.rs @@ -0,0 +1,18 @@ +use crate::backend::Backend; +use crate::query_builder::returning_clause::ReturningClause; +use crate::query_builder::{AstPass, QueryFragment}; +use crate::result::QueryResult; +use crate::sqlite::backend::SqliteReturningClause; + +impl QueryFragment for ReturningClause +where + DB: Backend, + Expr: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { + out.skip_from(true); + out.push_sql(" RETURNING "); + self.0.walk_ast(out.reborrow())?; + Ok(()) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/types/date_and_time/chrono.rs b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/types/date_and_time/chrono.rs new file mode 100644 index 000000000..9fc00818f --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/sqlite/types/date_and_time/chrono.rs @@ -0,0 +1,703 @@ +extern crate chrono; + +use self::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; + +use crate::backend::Backend; +use crate::deserialize::{self, FromSql}; +use crate::serialize::{self, IsNull, Output, ToSql}; +use crate::sql_types::{Date, Time, Timestamp, TimestamptzSqlite}; +use crate::sqlite::Sqlite; + +/// Warning to future editors: +/// Changes in the following formats need to be kept in sync +/// with the formats of the ["time"](super::time) module. +/// We do not need a distinction between whole second and +/// subsecond since %.f will only print the dot if needed. +/// We always print as many subsecond as his given to us, +/// this means the subsecond part can be 3, 6 or 9 digits. +const DATE_FORMAT: &str = "%F"; + +const ENCODE_TIME_FORMAT: &str = "%T%.f"; + +const TIME_FORMATS: [&str; 9] = [ + // Most likely formats + "%T%.f", "%T", // All other valid formats in order of increasing specificity + "%R", "%RZ", "%R%:z", "%TZ", "%T%:z", "%T%.fZ", "%T%.f%:z", +]; + +const ENCODE_NAIVE_DATETIME_FORMAT: &str = "%F %T%.f"; + +const ENCODE_DATETIME_FORMAT: &str = "%F %T%.f%:z"; + +const NAIVE_DATETIME_FORMATS: [&str; 18] = [ + // Most likely formats + "%F %T%.f", + "%F %T%.f%:z", + "%F %T", + "%F %T%:z", + // All other formats in order of increasing specificity + "%F %R", + "%F %RZ", + "%F %R%:z", + "%F %TZ", + "%F %T%.fZ", + "%FT%R", + "%FT%RZ", + "%FT%R%:z", + "%FT%T", + "%FT%TZ", + "%FT%T%:z", + "%FT%T%.f", + "%FT%T%.fZ", + "%FT%T%.f%:z", +]; + +const DATETIME_FORMATS: [&str; 12] = [ + // Most likely formats + "%F %T%.f%:z", + "%F %T%:z", + // All other formats in order of increasing specificity + "%F %RZ", + "%F %R%:z", + "%F %TZ", + "%F %T%.fZ", + "%FT%RZ", + "%FT%R%:z", + "%FT%TZ", + "%FT%T%:z", + "%FT%T%.fZ", + "%FT%T%.f%:z", +]; + +fn parse_julian(julian_days: f64) -> Option { + const EPOCH_IN_JULIAN_DAYS: f64 = 2_440_587.5; + const SECONDS_IN_DAY: f64 = 86400.0; + let timestamp = (julian_days - EPOCH_IN_JULIAN_DAYS) * SECONDS_IN_DAY; + #[allow(clippy::cast_possible_truncation)] // we want to truncate + let seconds = timestamp.trunc() as i64; + // that's not true, `fract` is always > 0 + #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] + let nanos = (timestamp.fract() * 1E9) as u32; + #[allow(deprecated)] // otherwise we would need to bump our minimal chrono version + NaiveDateTime::from_timestamp_opt(seconds, nanos) +} + +#[cfg(all(feature = "sqlite", feature = "chrono"))] +impl FromSql for NaiveDate { + fn from_sql(mut value: ::RawValue<'_>) -> deserialize::Result { + value + .parse_string(|s| Self::parse_from_str(s, DATE_FORMAT)) + .map_err(Into::into) + } +} + +#[cfg(all(feature = "sqlite", feature = "chrono"))] +impl ToSql for NaiveDate { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { + out.set_value(self.format(DATE_FORMAT).to_string()); + Ok(IsNull::No) + } +} + +#[cfg(all(feature = "sqlite", feature = "chrono"))] +impl FromSql for NaiveTime { + fn from_sql(mut value: ::RawValue<'_>) -> deserialize::Result { + value.parse_string(|text| { + for format in TIME_FORMATS { + if let Ok(time) = Self::parse_from_str(text, format) { + return Ok(time); + } + } + + Err(format!("Invalid time {text}").into()) + }) + } +} + +#[cfg(all(feature = "sqlite", feature = "chrono"))] +impl ToSql for NaiveTime { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { + out.set_value(self.format(ENCODE_TIME_FORMAT).to_string()); + Ok(IsNull::No) + } +} + +#[cfg(all(feature = "sqlite", feature = "chrono"))] +impl FromSql for NaiveDateTime { + fn from_sql(mut value: ::RawValue<'_>) -> deserialize::Result { + value.parse_string(|text| { + for format in NAIVE_DATETIME_FORMATS { + if let Ok(dt) = Self::parse_from_str(text, format) { + return Ok(dt); + } + } + + if let Ok(julian_days) = text.parse::() { + if let Some(timestamp) = parse_julian(julian_days) { + return Ok(timestamp); + } + } + + Err(format!("Invalid datetime {text}").into()) + }) + } +} + +#[cfg(all(feature = "sqlite", feature = "chrono"))] +impl ToSql for NaiveDateTime { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { + out.set_value(self.format(ENCODE_NAIVE_DATETIME_FORMAT).to_string()); + Ok(IsNull::No) + } +} + +#[cfg(all(feature = "sqlite", feature = "chrono"))] +impl FromSql for NaiveDateTime { + fn from_sql(mut value: ::RawValue<'_>) -> deserialize::Result { + value.parse_string(|text| { + for format in NAIVE_DATETIME_FORMATS { + if let Ok(dt) = Self::parse_from_str(text, format) { + return Ok(dt); + } + } + + if let Ok(julian_days) = text.parse::() { + if let Some(timestamp) = parse_julian(julian_days) { + return Ok(timestamp); + } + } + + Err(format!("Invalid datetime {text}").into()) + }) + } +} + +#[cfg(all(feature = "sqlite", feature = "chrono"))] +impl ToSql for NaiveDateTime { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { + out.set_value(self.format(ENCODE_NAIVE_DATETIME_FORMAT).to_string()); + Ok(IsNull::No) + } +} + +#[cfg(all(feature = "sqlite", feature = "chrono"))] +impl FromSql for DateTime { + fn from_sql(mut value: ::RawValue<'_>) -> deserialize::Result { + // First try to parse the timezone + if let Ok(dt) = value.parse_string(|text| { + for format in DATETIME_FORMATS { + if let Ok(dt) = DateTime::parse_from_str(text, format) { + return Ok(dt.with_timezone(&Utc)); + } + } + + Err(()) + }) { + return Ok(dt); + } + + // Fallback on assuming Utc + let naive_date_time = + >::from_sql(value)?; + Ok(Utc.from_utc_datetime(&naive_date_time)) + } +} + +#[cfg(all(feature = "sqlite", feature = "chrono"))] +impl FromSql for DateTime { + fn from_sql(mut value: ::RawValue<'_>) -> deserialize::Result { + // First try to parse the timezone + if let Ok(dt) = value.parse_string(|text| { + for format in DATETIME_FORMATS { + if let Ok(dt) = DateTime::parse_from_str(text, format) { + return Ok(dt.with_timezone(&Local)); + } + } + + Err(()) + }) { + return Ok(dt); + } + + // Fallback on assuming Local + let naive_date_time = + >::from_sql(value)?; + Ok(Local::from_utc_datetime(&Local, &naive_date_time)) + } +} + +#[cfg(all(feature = "sqlite", feature = "chrono"))] +impl ToSql for DateTime { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { + // Converting to UTC ensures consistency + let dt_utc = self.with_timezone(&Utc); + out.set_value(dt_utc.format(ENCODE_DATETIME_FORMAT).to_string()); + Ok(IsNull::No) + } +} + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod tests { + extern crate chrono; + extern crate dotenvy; + + use self::chrono::{ + DateTime, Duration, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, + Utc, + }; + + use crate::dsl::{now, sql}; + use crate::prelude::*; + use crate::select; + use crate::sql_types::{Text, Time, Timestamp, TimestamptzSqlite}; + use crate::test_helpers::connection; + + define_sql_function!(fn datetime(x: Text) -> Timestamp); + define_sql_function!(fn time(x: Text) -> Time); + define_sql_function!(fn date(x: Text) -> Date); + + #[test] + fn unix_epoch_encodes_correctly() { + let connection = &mut connection(); + let time = NaiveDate::from_ymd_opt(1970, 1, 1) + .unwrap() + .and_hms_opt(0, 0, 0) + .unwrap(); + let query = select(datetime("1970-01-01 00:00:00.000000").eq(time)); + assert_eq!(Ok(true), query.get_result(connection)); + } + + #[test] + fn unix_epoch_decodes_correctly_in_all_possible_formats() { + let connection = &mut connection(); + let time = NaiveDate::from_ymd_opt(1970, 1, 1) + .unwrap() + .and_hms_opt(0, 0, 0) + .unwrap(); + let valid_epoch_formats = vec![ + "1970-01-01 00:00", + "1970-01-01 00:00:00", + "1970-01-01 00:00:00.000", + "1970-01-01 00:00:00.000000", + "1970-01-01T00:00", + "1970-01-01T00:00:00", + "1970-01-01T00:00:00.000", + "1970-01-01T00:00:00.000000", + "1970-01-01 00:00Z", + "1970-01-01 00:00:00Z", + "1970-01-01 00:00:00.000Z", + "1970-01-01 00:00:00.000000Z", + "1970-01-01T00:00Z", + "1970-01-01T00:00:00Z", + "1970-01-01T00:00:00.000Z", + "1970-01-01T00:00:00.000000Z", + "1970-01-01 00:00+00:00", + "1970-01-01 00:00:00+00:00", + "1970-01-01 00:00:00.000+00:00", + "1970-01-01 00:00:00.000000+00:00", + "1970-01-01T00:00+00:00", + "1970-01-01T00:00:00+00:00", + "1970-01-01T00:00:00.000+00:00", + "1970-01-01T00:00:00.000000+00:00", + "1970-01-01 00:00+01:00", + "1970-01-01 00:00:00+01:00", + "1970-01-01 00:00:00.000+01:00", + "1970-01-01 00:00:00.000000+01:00", + "1970-01-01T00:00+01:00", + "1970-01-01T00:00:00+01:00", + "1970-01-01T00:00:00.000+01:00", + "1970-01-01T00:00:00.000000+01:00", + "1970-01-01T00:00-01:00", + "1970-01-01T00:00:00-01:00", + "1970-01-01T00:00:00.000-01:00", + "1970-01-01T00:00:00.000000-01:00", + "1970-01-01T00:00-01:00", + "1970-01-01T00:00:00-01:00", + "1970-01-01T00:00:00.000-01:00", + "1970-01-01T00:00:00.000000-01:00", + "2440587.5", + ]; + + for s in valid_epoch_formats { + let epoch_from_sql = + select(sql::(&format!("'{}'", s))).get_result(connection); + assert_eq!(Ok(time), epoch_from_sql, "format {} failed", s); + } + } + + #[test] + fn times_relative_to_now_encode_correctly() { + let connection = &mut connection(); + let time = Utc::now().naive_utc() + Duration::try_seconds(60).unwrap(); + let query = select(now.lt(time)); + assert_eq!(Ok(true), query.get_result(connection)); + + let time = Utc::now().naive_utc() - Duration::try_seconds(600).unwrap(); + let query = select(now.gt(time)); + assert_eq!(Ok(true), query.get_result(connection)); + } + + #[test] + fn times_of_day_encode_correctly() { + let connection = &mut connection(); + + let midnight = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + let query = select(time("00:00:00.000000").eq(midnight)); + assert!(query.get_result::(connection).unwrap()); + + let noon = NaiveTime::from_hms_opt(12, 0, 0).unwrap(); + let query = select(time("12:00:00.000000").eq(noon)); + assert!(query.get_result::(connection).unwrap()); + + let roughly_half_past_eleven = NaiveTime::from_hms_micro_opt(23, 37, 4, 2200).unwrap(); + let query = select(sql::
,)+ + { + type Table = Tab; + + fn walk_ast<__DB: Backend>(&self, mut out: AstPass<'_, '_, __DB>) -> QueryResult<()> { + $( + if $idx != 0 { + out.push_sql(", "); + } + self.$idx.walk_ast(out.reborrow())?; + )+ + Ok(()) + } + } + + impl<$($T: QueryId),+> QueryId for ($($T,)+) { + type QueryId = ($($T::QueryId,)+); + + const HAS_STATIC_QUERY_ID: bool = $($T::HAS_STATIC_QUERY_ID &&)+ true; + } + + impl_valid_grouping_for_tuple_of_columns!($($T,)*); + + impl<$($T,)+ Tab> UndecoratedInsertRecord for ($($T,)+) + where + $($T: UndecoratedInsertRecord,)+ + { + } + + impl<$($T,)+ __DB> CanInsertInSingleQuery<__DB> for ($($T,)+) + where + __DB: Backend, + $($T: CanInsertInSingleQuery<__DB>,)+ + { + fn rows_to_insert(&self) -> Option { + $(debug_assert_eq!(self.$idx.rows_to_insert(), Some(1));)+ + Some(1) + } + } + + impl<$($T,)+ $($ST,)+ Tab> Insertable for ($($T,)+) + where + $($T: Insertable>,)+ + { + type Values = ValuesClause<($($ST,)+), Tab>; + + fn values(self) -> Self::Values { + ValuesClause::new(($(self.$idx.values().values,)+)) + } + } + + impl<'a, $($T,)+ Tab> Insertable for &'a ($($T,)+) + where + ($(&'a $T,)+): Insertable, + { + type Values = <($(&'a $T,)+) as Insertable>::Values; + + fn values(self) -> Self::Values { + ($(&self.$idx,)+).values() + } + } + + #[allow(unused_assignments)] + impl<$($T,)+ Tab, __DB> InsertValues<__DB, Tab> for ($($T,)+) + where + Tab: Table, + __DB: Backend, + $($T: InsertValues<__DB, Tab>,)+ + { + fn column_names(&self, mut out: AstPass<'_, '_, __DB>) -> QueryResult<()> { + let mut needs_comma = false; + $( + let noop_element = self.$idx.is_noop(out.backend())?; + if !noop_element { + if needs_comma { + out.push_sql(", "); + } + self.$idx.column_names(out.reborrow())?; + needs_comma = true; + } + )+ + Ok(()) + } + } + + impl<__T, $($ST,)* Tab> Insertable for InsertableOptionHelper<__T, ($($ST,)*)> + where + __T: Insertable, + __T::Values: Default, + { + type Values = __T::Values; + + fn values(self) -> Self::Values { + self.0.map(|v| v.values()).unwrap_or_default() + } + } + + impl<$($T,)+ QS> SelectableExpression for ($($T,)+) where + $($T: SelectableExpression,)+ + ($($T,)+): AppearsOnTable, + { + } + + impl<$($T,)+ QS> AppearsOnTable for ($($T,)+) where + $($T: AppearsOnTable,)+ + ($($T,)+): Expression, + { + } + + impl AsChangeset for ($($T,)+) where + $($T: AsChangeset,)+ + Target: QuerySource, + { + type Target = Target; + type Changeset = ($($T::Changeset,)+); + + fn as_changeset(self) -> Self::Changeset { + ($(self.$idx.as_changeset(),)+) + } + } + + impl<$($T,)+ Parent> BelongsTo for ($($T,)+) where + T0: BelongsTo, + { + type ForeignKey = T0::ForeignKey; + type ForeignKeyColumn = T0::ForeignKeyColumn; + + fn foreign_key(&self) -> Option<&Self::ForeignKey> { + self.0.foreign_key() + } + + fn foreign_key_column() -> Self::ForeignKeyColumn { + T0::foreign_key_column() + } + } + + impl<$($T,)+ Next> TupleAppend for ($($T,)+) { + type Output = ($($T,)+ Next); + + #[allow(non_snake_case)] + fn tuple_append(self, next: Next) -> Self::Output { + let ($($T,)+) = self; + ($($T,)+ next) + } + } + + impl<$($T,)+ ST> AsExpressionList for ($($T,)+) where + $($T: AsExpression,)+ + ST: SqlType + TypedExpressionType, + { + type Expression = ($($T::Expression,)+); + + fn as_expression_list(self) -> Self::Expression { + ($(self.$idx.as_expression(),)+) + } + } + + impl_sql_type!($($T,)*); + + impl<$($T,)* __DB, $($ST,)*> Queryable<($($ST,)*), __DB> for ($($T,)*) + where __DB: Backend, + Self: FromStaticSqlRow<($($ST,)*), __DB>, + { + type Row = Self; + + fn build(row: Self::Row) -> deserialize::Result { + Ok(row) + } + } + + impl<__T, $($ST,)* __DB> FromStaticSqlRow, __DB> for Option<__T> where + __DB: Backend, + ($($ST,)*): SqlType, + __T: FromSqlRow<($($ST,)*), __DB>, + { + + #[allow(non_snake_case, unused_variables, unused_mut)] + fn build_from_row<'a>(row: &impl Row<'a, __DB>) + -> deserialize::Result + { + match <__T as FromSqlRow<($($ST,)*), __DB>>::build_from_row(row) { + Ok(v) => Ok(Some(v)), + Err(e) if e.is::() => Ok(None), + Err(e) => Err(e) + } + } + } + + impl<__T, __DB, $($ST,)*> Queryable, __DB> for Option<__T> + where __DB: Backend, + Self: FromStaticSqlRow, __DB>, + ($($ST,)*): SqlType, + { + type Row = Self; + + fn build(row: Self::Row) -> deserialize::Result { + Ok(row) + } + } + + impl<$($T,)*> TupleSize for ($($T,)*) + where $($T: TupleSize,)* + { + const SIZE: usize = $($T::SIZE +)* 0; + } + + impl<$($T,)*> TupleSize for Nullable<($($T,)*)> + where $($T: TupleSize,)* + ($($T,)*): SqlType, + { + const SIZE: usize = $($T::SIZE +)* 0; + } + + impl<$($T,)* __DB> QueryMetadata<($($T,)*)> for __DB + where __DB: Backend, + $(__DB: QueryMetadata<$T>,)* + { + fn row_metadata(lookup: &mut Self::MetadataLookup, row: &mut Vec>) { + $( + <__DB as QueryMetadata<$T>>::row_metadata(lookup, row); + )* + } + } + + impl<$($T,)* __DB> QueryMetadata> for __DB + where __DB: Backend, + $(__DB: QueryMetadata<$T>,)* + { + fn row_metadata(lookup: &mut Self::MetadataLookup, row: &mut Vec>) { + $( + <__DB as QueryMetadata<$T>>::row_metadata(lookup, row); + )* + } + } + + impl<$($T,)* __DB> deserialize::QueryableByName< __DB> for ($($T,)*) + where __DB: Backend, + $($T: deserialize::QueryableByName<__DB>,)* + { + fn build<'a>(row: &impl NamedRow<'a, __DB>) -> deserialize::Result { + Ok(($( + <$T as deserialize::QueryableByName<__DB>>::build(row)?, + )*)) + } + } + + impl<__T, $($ST,)* __DB> CompatibleType<__T, __DB> for ($($ST,)*) + where + __DB: Backend, + __T: FromSqlRow<($($ST,)*), __DB>, + { + type SqlType = Self; + } + + impl<__T, $($ST,)* __DB> CompatibleType, __DB> for Nullable<($($ST,)*)> + where + __DB: Backend, + ($($ST,)*): CompatibleType<__T, __DB> + { + type SqlType = Nullable<<($($ST,)*) as CompatibleType<__T, __DB>>::SqlType>; + } + + impl<$($ST,)*> SqlTypeOrSelectable for ($($ST,)*) + where $($ST: SqlTypeOrSelectable,)* + {} + + impl<$($ST,)*> SqlTypeOrSelectable for Nullable<($($ST,)*)> + where ($($ST,)*): SqlTypeOrSelectable + {} + )+ + } +} + +macro_rules! impl_from_sql_row { + (($T1: ident,), ($ST1: ident,)) => { + impl<$T1, $ST1, __DB> crate::deserialize::FromStaticSqlRow<($ST1,), __DB> for ($T1,) where + __DB: Backend, + $ST1: CompatibleType<$T1, __DB>, + $T1: FromSqlRow<<$ST1 as CompatibleType<$T1, __DB>>::SqlType, __DB>, + { + + #[allow(non_snake_case, unused_variables, unused_mut)] + fn build_from_row<'a>(row: &impl Row<'a, __DB>) + -> deserialize::Result + { + Ok(($T1::build_from_row(row)?,)) + } + } + }; + (($T1: ident, $($T: ident,)*), ($ST1: ident, $($ST: ident,)*)) => { + impl<$T1, $($T,)* $($ST,)* __DB> FromSqlRow<($($ST,)* crate::sql_types::Untyped), __DB> for ($($T,)* $T1) + where __DB: Backend, + $T1: FromSqlRow, + $( + $T: FromSqlRow<$ST, __DB> + StaticallySizedRow<$ST, __DB>, + )* + { + #[allow(non_snake_case, unused_variables, unused_mut)] + fn build_from_row<'a>(full_row: &impl Row<'a, __DB>) + -> deserialize::Result + { + let field_count = full_row.field_count(); + + let mut static_field_count = 0; + $( + let row = full_row.partial_row(static_field_count..static_field_count + $T::FIELD_COUNT); + static_field_count += $T::FIELD_COUNT; + let $T = $T::build_from_row(&row)?; + )* + + let row = full_row.partial_row(static_field_count..field_count); + + Ok(($($T,)* $T1::build_from_row(&row)?,)) + } + } + + impl<$T1, $ST1, $($T,)* $($ST,)* __DB> FromStaticSqlRow<($($ST,)* $ST1,), __DB> for ($($T,)* $T1,) where + __DB: Backend, + $ST1: CompatibleType<$T1, __DB>, + $T1: FromSqlRow<<$ST1 as CompatibleType<$T1, __DB>>::SqlType, __DB>, + $( + $ST: CompatibleType<$T, __DB>, + $T: FromSqlRow<<$ST as CompatibleType<$T, __DB>>::SqlType, __DB> + StaticallySizedRow<<$ST as CompatibleType<$T, __DB>>::SqlType, __DB>, + )* + + { + + #[allow(non_snake_case, unused_variables, unused_mut)] + fn build_from_row<'a>(full_row: &impl Row<'a, __DB>) + -> deserialize::Result + { + let field_count = full_row.field_count(); + + let mut static_field_count = 0; + $( + let row = full_row.partial_row(static_field_count..static_field_count + $T::FIELD_COUNT); + static_field_count += $T::FIELD_COUNT; + let $T = <$T as FromSqlRow<<$ST as CompatibleType<$T, __DB>>::SqlType, __DB>>::build_from_row(&row)?; + )* + + let row = full_row.partial_row(static_field_count..field_count); + + Ok(($($T,)* $T1::build_from_row(&row)?,)) + } + } + } +} + +macro_rules! impl_valid_grouping_for_tuple_of_columns { + ($T1: ident, $($T: ident,)+) => { + impl<$T1, $($T,)* __GroupByClause> ValidGrouping<__GroupByClause> for ($T1, $($T,)*) + where + $T1: ValidGrouping<__GroupByClause>, + ($($T,)*): ValidGrouping<__GroupByClause>, + $T1::IsAggregate: MixedAggregates<<($($T,)*) as ValidGrouping<__GroupByClause>>::IsAggregate>, + { + type IsAggregate = <$T1::IsAggregate as MixedAggregates<<($($T,)*) as ValidGrouping<__GroupByClause>>::IsAggregate>>::Output; + } + + impl<$T1, $($T,)* Col> IsContainedInGroupByfor ($T1, $($T,)*) + where Col: Column, + ($($T,)*): IsContainedInGroupBy, + $T1: IsContainedInGroupBy, + $T1::Output: is_contained_in_group_by::IsAny<<($($T,)*) as IsContainedInGroupBy>::Output> + { + type Output = <$T1::Output as is_contained_in_group_by::IsAny<<($($T,)*) as IsContainedInGroupBy>::Output>>::Output; + } + }; + ($T1: ident,) => { + impl<$T1, Col> IsContainedInGroupByfor ($T1,) + where Col: Column, + $T1: IsContainedInGroupBy+ { + type Output = <$T1 as IsContainedInGroupBy>::Output; + } + + impl<$T1, __GroupByClause> ValidGrouping<__GroupByClause> for ($T1,) + where $T1: ValidGrouping<__GroupByClause> + { + type IsAggregate = $T1::IsAggregate; + } + }; +} + +macro_rules! impl_sql_type { + ( + @build + start_ts = [$($ST: ident,)*], + ts = [$T1: ident,], + bounds = [$($bounds: tt)*], + is_null = [$($is_null: tt)*], + )=> { + impl<$($ST,)*> SqlType for ($($ST,)*) + where + $($ST: SqlType,)* + $($bounds)* + $T1::IsNull: OneIsNullable<$($is_null)*>, + { + type IsNull = <$T1::IsNull as OneIsNullable<$($is_null)*>>::Out; + } + + }; + ( + @build + start_ts = [$($ST: ident,)*], + ts = [$T1: ident, $($T: ident,)+], + bounds = [$($bounds: tt)*], + is_null = [$($is_null: tt)*], + )=> { + impl_sql_type!{ + @build + start_ts = [$($ST,)*], + ts = [$($T,)*], + bounds = [$($bounds)* $T1::IsNull: OneIsNullable<$($is_null)*>,], + is_null = [<$T1::IsNull as OneIsNullable<$($is_null)*>>::Out], + } + }; + ($T1: ident, $($T: ident,)+) => { + impl_sql_type!{ + @build + start_ts = [$T1, $($T,)*], + ts = [$($T,)*], + bounds = [], + is_null = [$T1::IsNull], + } + }; + ($T1: ident,) => { + impl<$T1> SqlType for ($T1,) + where $T1: SqlType, + { + type IsNull = $T1::IsNull; + } + } +} + +diesel_derives::__diesel_for_each_tuple!(tuple_impls); diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/upsert/mod.rs b/collector/compile-benchmarks/diesel-2.2.10/src/upsert/mod.rs new file mode 100644 index 000000000..e83b256cb --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/upsert/mod.rs @@ -0,0 +1,28 @@ +//! Types and functions related to PG's and Sqlite's `ON CONFLICT` clause +//! +//! Upsert is currently supported by diesel for the following database systems: +//! +//! * PostgreSQL version 9.5 or newer +//! * Sqlite3 version 3.24.0 or newer +//! * MySQL version 5.7 or newer +//! +//! See [the methods on `InsertStatement`](crate::query_builder::InsertStatement#impl-2) +//! for usage examples. +//! +//! Constructing an upsert statement from an existing select statement +//! requires a where clause on sqlite due to a ambiguity in their +//! parser. See [the corresponding documentation](https://www.sqlite.org/lang_UPSERT.html) +//! for details. +use crate::query_builder::upsert::on_conflict_actions::Excluded; + +mod on_conflict_extension; + +pub use self::on_conflict_extension::DecoratableTarget; +pub use self::on_conflict_extension::*; +#[cfg(feature = "postgres_backend")] +pub use crate::pg::query_builder::on_constraint::*; + +/// Represents `excluded.column` in an `ON CONFLICT DO UPDATE` clause. +pub fn excluded(excluded: T) -> Excluded { + Excluded::new(excluded) +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/upsert/on_conflict_docs_setup.rs b/collector/compile-benchmarks/diesel-2.2.10/src/upsert/on_conflict_docs_setup.rs new file mode 100644 index 000000000..a7da999b0 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/upsert/on_conflict_docs_setup.rs @@ -0,0 +1,9 @@ +include!("../doctest_setup.rs"); +use crate::schema::users; + +#[derive(Clone, Copy, Insertable, AsChangeset)] +#[diesel(table_name = users)] +struct User<'a> { + id: i32, + name: &'a str, +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/upsert/on_conflict_extension.rs b/collector/compile-benchmarks/diesel-2.2.10/src/upsert/on_conflict_extension.rs new file mode 100644 index 000000000..1ba53ea22 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/upsert/on_conflict_extension.rs @@ -0,0 +1,649 @@ +use crate::expression::Expression; +use crate::query_builder::upsert::into_conflict_clause::IntoConflictValueClause; +use crate::query_builder::upsert::on_conflict_actions::*; +use crate::query_builder::upsert::on_conflict_clause::*; +use crate::query_builder::upsert::on_conflict_target::*; +pub use crate::query_builder::upsert::on_conflict_target_decorations::DecoratableTarget; +use crate::query_builder::where_clause::{NoWhereClause, WhereAnd, WhereOr}; +use crate::query_builder::{AsChangeset, InsertStatement, UndecoratedInsertRecord}; +use crate::query_dsl::filter_dsl::FilterDsl; +use crate::query_dsl::methods::OrFilterDsl; +use crate::query_source::QuerySource; +use crate::sql_types::BoolOrNullableBool; + +impl InsertStatement +where + T: QuerySource, + U: UndecoratedInsertRecord + IntoConflictValueClause, +{ + /// Adds `ON CONFLICT DO NOTHING` to the insert statement, without + /// specifying any columns or constraints to restrict the conflict to. + /// + /// # Examples + /// + /// ### Single Record + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap() + /// # } + /// # fn run_test() -> QueryResult<()> { + /// # use self::users::dsl::*; + /// # let conn = &mut establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap(); + /// # #[cfg(any(feature = "sqlite", feature = "mysql"))] + /// # diesel::sql_query("DELETE FROM users").execute(conn).unwrap(); + /// let user = User { id: 1, name: "Sean" }; + /// + /// let user_count = users.count().get_result::(conn)?; + /// assert_eq!(user_count, 0); + /// + /// diesel::insert_into(users) + /// .values(&user) + /// .on_conflict_do_nothing() + /// .execute(conn)?; + /// let user_count = users.count().get_result::(conn)?; + /// assert_eq!(user_count, 1); + /// + /// diesel::insert_into(users) + /// .values(&user) + /// .on_conflict_do_nothing() + /// .execute(conn)?; + /// let user_count = users.count().get_result::(conn)?; + /// assert_eq!(user_count, 1); + /// # Ok(()) + /// # } + /// ``` + /// + /// ### Vec of Records + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap() + /// # } + /// # + /// # fn run_test() -> diesel::QueryResult<()> { + /// # use self::users::dsl::*; + /// # let conn = &mut establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap(); + /// # #[cfg(any(feature = "mysql", feature = "sqlite"))] + /// # diesel::sql_query("DELETE FROM users").execute(conn).unwrap(); + /// # #[cfg(any(feature = "postgres", feature = "mysql"))] + /// let user = User { id: 1, name: "Sean" }; + /// + /// # #[cfg(any(feature = "postgres", feature = "mysql"))] + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&vec![user, user]) + /// .on_conflict_do_nothing() + /// .execute(conn)?; + /// # #[cfg(any(feature = "postgres", feature = "mysql"))] + /// let user_count = users.count().get_result::(conn)?; + /// # #[cfg(any(feature = "postgres", feature = "mysql"))] + /// assert_eq!(user_count, 1); + /// # Ok(()) + /// # } + /// ``` + pub fn on_conflict_do_nothing( + self, + ) -> InsertStatement>, Op, Ret> + { + self.replace_values(|values| OnConflictValues::do_nothing(values.into_value_clause())) + } + + /// Adds an `ON CONFLICT` to the insert statement, if a conflict occurs + /// for the given unique constraint. + /// + /// `Target` can be one of: + /// + /// - A column + /// - A tuple of columns + /// - [`on_constraint("constraint_name")`][`on_constraint`] + /// + /// # Examples + /// + /// ### Specifying a column as the target + /// + /// This is supported by sqlite and postgres only + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # hair_color -> VarChar, + /// # } + /// # } + /// # + /// # #[derive(Clone, Copy, Insertable)] + /// # #[diesel(table_name = users)] + /// # struct User<'a> { + /// # id: i32, + /// # name: &'a str, + /// # } + /// # + /// # fn main() { + /// # run_test().unwrap() + /// # } + /// # #[cfg(any(feature = "postgres", feature = "sqlite"))] + /// # fn run_test() -> diesel::QueryResult<()> { + /// # use self::users::dsl::*; + /// use diesel::upsert::*; + /// + /// # let conn = &mut establish_connection(); + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # diesel::sql_query("DROP TABLE users").execute(conn).unwrap(); + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # diesel::sql_query("CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT)").execute(conn).unwrap(); + /// diesel::sql_query("CREATE UNIQUE INDEX users_name ON users (name)").execute(conn).unwrap(); + /// let user = User { id: 1, name: "Sean" }; + /// let same_name_different_id = User { id: 2, name: "Sean" }; + /// let same_id_different_name = User { id: 1, name: "Pascal" }; + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn)); + /// + /// let query = diesel::insert_into(users) + /// .values(&same_id_different_name) + /// .on_conflict(id) + /// .do_nothing() + /// .execute(conn)?; + /// + /// let user_names = users.select(name).load::(conn)?; + /// assert_eq!(user_names, vec![String::from("Sean")]); + /// + /// let idx_conflict_result = diesel::insert_into(users) + /// .values(&same_name_different_id) + /// .on_conflict(id) + /// .do_nothing() + /// .execute(conn); + /// assert!(idx_conflict_result.is_err()); + /// # Ok(()) + /// # } + /// #[cfg(feature = "mysql")] + /// fn run_test() -> diesel::QueryResult<()> { Ok(()) } + /// ``` + /// + /// ### Specifying multiple columns as the target + /// + /// This is supported by sqlite and postgres only + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # hair_color -> VarChar, + /// # } + /// # } + /// # + /// # #[derive(Clone, Copy, Insertable)] + /// # #[diesel(table_name = users)] + /// # struct User<'a> { + /// # id: i32, + /// # name: &'a str, + /// # hair_color: &'a str, + /// # } + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// use diesel::upsert::*; + /// + /// # let conn = &mut establish_connection(); + /// # diesel::sql_query("DROP TABLE users").execute(conn).unwrap(); + /// # diesel::sql_query("CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT, hair_color TEXT)").execute(conn).unwrap(); + /// diesel::sql_query("CREATE UNIQUE INDEX users_name_hair_color ON users (name, hair_color)").execute(conn).unwrap(); + /// let user = User { id: 1, name: "Sean", hair_color: "black" }; + /// let same_name_different_hair_color = User { id: 2, name: "Sean", hair_color: "brown" }; + /// let same_name_same_hair_color = User { id: 3, name: "Sean", hair_color: "black" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn)); + /// + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&same_name_different_hair_color) + /// .on_conflict((name, hair_color)) + /// .do_nothing() + /// .execute(conn); + /// assert_eq!(Ok(1), inserted_row_count); + /// + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&same_name_same_hair_color) + /// .on_conflict((name, hair_color)) + /// .do_nothing() + /// .execute(conn); + /// assert_eq!(Ok(0), inserted_row_count); + /// # } + /// + /// #[cfg(feature = "mysql")] + /// fn main() {} + /// ``` + /// + /// ### ON DUPLICATE KEY + /// + /// Mysql supports only catching all duplicated keys at once: + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # hair_color -> VarChar, + /// # } + /// # } + /// # + /// # #[derive(Clone, Copy, Insertable)] + /// # #[diesel(table_name = users)] + /// # struct User<'a> { + /// # id: i32, + /// # name: &'a str, + /// # } + /// # + /// # fn main() { + /// # run_test().unwrap() + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn run_test() -> diesel::QueryResult<()> { + /// # use self::users::dsl::*; + /// use diesel::upsert::*; + /// + /// # let conn = &mut establish_connection(); + /// # diesel::sql_query("CREATE TEMPORARY TABLE users (id SERIAL PRIMARY KEY, name VARCHAR(255), hair_color VARCHAR(255))").execute(conn).unwrap(); + /// diesel::sql_query("CREATE UNIQUE INDEX users_name ON users (name)").execute(conn).unwrap(); + /// let user = User { id: 1, name: "Sean" }; + /// let same_name_different_id = User { id: 2, name: "Sean" }; + /// let same_id_different_name = User { id: 1, name: "Pascal" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn)); + /// + /// # diesel::delete(users.filter(name.ne("Sean"))).execute(conn)?; + /// let user_names = users.select(name).load::(conn)?; + /// assert_eq!(user_names, vec![String::from("Sean")]); + /// + /// let query = diesel::insert_into(users) + /// .values(&same_id_different_name) + /// .on_conflict(diesel::dsl::DuplicatedKeys) + /// .do_nothing() + /// .execute(conn)?; + /// + /// let user_names = users.select(name).load::(conn)?; + /// assert_eq!(user_names, vec![String::from("Sean")]); + /// + /// let idx_conflict_result = diesel::insert_into(users) + /// .values(&same_name_different_id) + /// .on_conflict(diesel::dsl::DuplicatedKeys) + /// .do_nothing() + /// .execute(conn)?; + /// + /// let user_names = users.select(name).load::(conn)?; + /// assert_eq!(user_names, vec![String::from("Sean")]); + /// # Ok(()) + /// # } + /// #[cfg(not(feature = "mysql"))] + /// fn run_test() -> diesel::QueryResult<()> {Ok(())} + /// ``` + /// + /// See the documentation for [`on_constraint`] and [`do_update`] for + /// more examples. + /// + /// [`on_constraint`]: ../upsert/fn.on_constraint.html + /// [`do_update`]: crate::upsert::IncompleteOnConflict::do_update() + pub fn on_conflict( + self, + target: Target, + ) -> IncompleteOnConflict, ConflictTarget> + where + ConflictTarget: OnConflictTarget, + { + IncompleteOnConflict { + stmt: self.replace_values(IntoConflictValueClause::into_value_clause), + target: ConflictTarget(target), + } + } +} + +impl DecoratableTarget

for IncompleteOnConflict +where + P: Expression, + P::SqlType: BoolOrNullableBool, + T: DecoratableTarget

, +{ + type FilterOutput = IncompleteOnConflict>::FilterOutput>; + fn filter_target(self, predicate: P) -> Self::FilterOutput { + IncompleteOnConflict { + stmt: self.stmt, + target: self.target.filter_target(predicate), + } + } +} + +/// A partially constructed `ON CONFLICT` clause. +#[derive(Debug, Clone, Copy)] +pub struct IncompleteOnConflict { + stmt: Stmt, + target: Target, +} + +impl + IncompleteOnConflict, Target> +{ + /// Creates a query with `ON CONFLICT (target) DO NOTHING` + /// + /// If you want to do nothing when *any* constraint conflicts, use + /// [`on_conflict_do_nothing`] instead. See [`on_conflict`] for usage + /// examples. + /// + /// [`on_conflict_do_nothing`]: crate::query_builder::InsertStatement::on_conflict_do_nothing() + /// [`on_conflict`]: crate::query_builder::InsertStatement::on_conflict() + pub fn do_nothing( + self, + ) -> InsertStatement>, Op, Ret> { + let target = self.target; + self.stmt.replace_values(|values| { + OnConflictValues::new(values, target, DoNothing::new(), NoWhereClause) + }) + } +} + +impl IncompleteOnConflict { + /// Used to create a query in the form `ON CONFLICT (...) DO UPDATE ... [WHERE ...]` + /// + /// Call `.set` on the result of this function with the changes you want to + /// apply. The argument to `set` can be anything that implements `AsChangeset` + /// (e.g. anything you could pass to `set` on a normal update statement). + /// + /// Note: When inserting more than one row at a time, this query can still fail + /// if the rows being inserted conflict with each other. + /// + /// Some backends (PostgreSQL) support `WHERE` clause is used to limit the rows actually updated. + /// For PostgreSQL you can use the `.filter()` method to add conditions like this. + /// + /// # Examples + /// + /// ## Set specific value on conflict + /// + /// PostgreSQL/SQLite: + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(not(feature = "mysql"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// # let conn = &mut establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap(); + /// # #[cfg(feature = "sqlite")] + /// # diesel::sql_query("DELETE FROM users").execute(conn).unwrap(); + /// let user = User { id: 1, name: "Pascal" }; + /// let user2 = User { id: 1, name: "Sean" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn)); + /// + /// let insert_count = diesel::insert_into(users) + /// .values(&user2) + /// .on_conflict(id) + /// .do_update() + /// .set(name.eq("I DONT KNOW ANYMORE")) + /// .execute(conn); + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// assert_eq!(Ok(1), insert_count); + /// # #[cfg(feature = "mysql")] + /// assert_eq!(Ok(2), insert_count); + /// + /// let users_in_db = users.load(conn); + /// assert_eq!(Ok(vec![(1, "I DONT KNOW ANYMORE".to_string())]), users_in_db); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// MySQL: + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(feature = "mysql")] + /// # fn main() -> diesel::QueryResult<()> { + /// # use self::users::dsl::*; + /// # let conn = &mut establish_connection(); + /// # diesel::sql_query("DELETE FROM users").execute(conn).unwrap(); + /// let user = User { id: 1, name: "Pascal" }; + /// let user2 = User { id: 1, name: "Sean" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn)); + /// + /// diesel::insert_into(users) + /// .values(&user2) + /// .on_conflict(diesel::dsl::DuplicatedKeys) + /// .do_update() + /// .set(name.eq("I DONT KNOW ANYMORE")) + /// .execute(conn)?; + /// + /// let users_in_db = users.load(conn); + /// assert_eq!(Ok(vec![(1, "I DONT KNOW ANYMORE".to_string())]), users_in_db); + /// # Ok(()) + /// # } + /// # #[cfg(not(feature = "mysql"))] + /// # fn main() {} + /// ``` + /// + /// ## Set `AsChangeset` struct on conflict + /// + /// PostgreSQL & SQLite: + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(not(feature = "mysql"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// # let conn = &mut establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap(); + /// # #[cfg(feature = "sqlite")] + /// # diesel::sql_query("DELETE FROM users").execute(conn).unwrap(); + /// let user = User { id: 1, name: "Pascal" }; + /// let user2 = User { id: 1, name: "Sean" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn)); + /// + /// let insert_count = diesel::insert_into(users) + /// .values(&user2) + /// .on_conflict(id) + /// .do_update() + /// .set(&user2) + /// .execute(conn); + /// assert_eq!(Ok(1), insert_count); + /// + /// let users_in_db = users.load(conn); + /// assert_eq!(Ok(vec![(1, "Sean".to_string())]), users_in_db); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// MySQL: + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// + /// # #[cfg(feature = "mysql")] + /// # fn main() -> diesel::QueryResult<()> { + /// # use self::users::dsl::*; + /// # let conn = &mut establish_connection(); + /// # diesel::sql_query("DELETE FROM users").execute(conn).unwrap(); + /// let user = User { id: 1, name: "Pascal" }; + /// let user2 = User { id: 1, name: "Sean" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn)); + /// + /// diesel::insert_into(users) + /// .values(&user2) + /// .on_conflict(diesel::dsl::DuplicatedKeys) + /// .do_update() + /// .set(&user2) + /// .execute(conn)?; + /// + /// let users_in_db = users.load(conn); + /// assert_eq!(Ok(vec![(1, "Sean".to_string())]), users_in_db); + /// # Ok(()) + /// # } + /// + /// # #[cfg(not(feature = "mysql"))] + /// # fn main() {} + /// ``` + /// + /// ## Use `excluded` to get the rejected value + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// use diesel::upsert::excluded; + /// + /// # let conn = &mut establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap(); + /// let user = User { id: 1, name: "Pascal" }; + /// let user2 = User { id: 1, name: "Sean" }; + /// let user3 = User { id: 2, name: "Tess" }; + /// + /// # #[cfg(feature = "postgres")] + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn)); + /// + /// #[cfg(feature = "postgres")] + /// let insert_count = diesel::insert_into(users) + /// .values(&vec![user2, user3]) + /// .on_conflict(id) + /// .do_update() + /// .set(name.eq(excluded(name))) + /// .execute(conn); + /// # #[cfg(feature = "postgres")] + /// assert_eq!(Ok(2), insert_count); + /// + /// # #[cfg(feature = "postgres")] + /// let users_in_db = users.load(conn); + /// # #[cfg(feature = "postgres")] + /// assert_eq!(Ok(vec![(1, "Sean".to_string()), (2, "Tess".to_string())]), users_in_db); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// ## Use `.filter()`method to limit the rows actually updated + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(feature = "postgres")] + /// # fn main() { + /// # use diesel::QueryDsl; + /// # use diesel::query_dsl::methods::FilterDsl; + /// use self::users::dsl::*; + /// # let conn = &mut establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # diesel::sql_query("TRUNCATE TABLE users").execute(conn).unwrap(); + /// let user = User { id: 1, name: "Pascal" }; + /// let user2 = User { id: 1, name: "Sean" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(conn)); + /// + /// let insert_count = diesel::insert_into(users) + /// .values(&user2) + /// .on_conflict(id) + /// .do_update() + /// .set(&user2) + /// .filter(id.ge(5)) + /// .execute(conn); + /// assert_eq!(Ok(0), insert_count); + /// + /// let users_in_db = users.load(conn); + /// assert_eq!(Ok(vec![(1, "Pascal".to_string())]), users_in_db); + /// # } + /// # #[cfg(any(feature = "sqlite", feature = "mysql"))] + /// # fn main() {} + /// ``` + pub fn do_update(self) -> IncompleteDoUpdate { + IncompleteDoUpdate { + stmt: self.stmt, + target: self.target, + } + } +} + +/// A partially constructed `ON CONFLICT DO UPDATE` clause. +#[derive(Debug, Clone, Copy)] +pub struct IncompleteDoUpdate { + stmt: Stmt, + target: Target, +} + +impl + IncompleteDoUpdate, Target> +{ + /// See [`do_update`] for usage examples. + /// + /// [`do_update`]: IncompleteOnConflict::do_update() + pub fn set( + self, + changes: Changes, + ) -> InsertStatement>, Op, Ret> + where + T: QuerySource, + Changes: AsChangeset, + { + let target = self.target; + self.stmt.replace_values(|values| { + OnConflictValues::new( + values, + target, + DoUpdate::new(changes.as_changeset()), + NoWhereClause, + ) + }) + } +} + +impl FilterDsl + for InsertStatement, Op, Ret> +where + T: QuerySource, + WhereClause: WhereAnd, +{ + type Output = + InsertStatement, Op, Ret>; + + fn filter(self, predicate: Predicate) -> Self::Output { + self.replace_values(|values| { + values.replace_where(|where_clause| where_clause.and(predicate)) + }) + } +} + +impl OrFilterDsl + for InsertStatement, Op, Ret> +where + T: QuerySource, + WhereClause: WhereOr, +{ + type Output = + InsertStatement, Op, Ret>; + + fn or_filter(self, predicate: Predicate) -> Self::Output { + self.replace_values(|values| { + values.replace_where(|where_clause| where_clause.or(predicate)) + }) + } +} diff --git a/collector/compile-benchmarks/diesel-2.2.10/src/util.rs b/collector/compile-benchmarks/diesel-2.2.10/src/util.rs new file mode 100644 index 000000000..ed0439d49 --- /dev/null +++ b/collector/compile-benchmarks/diesel-2.2.10/src/util.rs @@ -0,0 +1,11 @@ +/// Treats tuples as a list which can be appended to. e.g. +/// `(a,).tuple_append(b) == (a, b)` +pub trait TupleAppend { + type Output; + + fn tuple_append(self, right: T) -> Self::Output; +} + +pub trait TupleSize { + const SIZE: usize; +}