mirror of
https://github.com/launchbadge/sqlx.git
synced 2026-03-23 10:38:57 +00:00
refactor(core): remove the HRTB (higher rank trait bound) on Row in the aim of improving ergonomics
* removes the lifetime from Row * removes MySqlQueryAs, SqliteQueryAs, etc. (no longer needed) * introduce query_scalar * introduce Decode::accepts to allow overriding runtime type checking per-type (replaces TypeInfo::compatible) * introduce Encode::produces to allow overriding the encoded type per-value * adds a lifetime to Arguments (and introduce the HRTB HasArguments) to support zero-copy encoding with SQLite * renames Database::RawBuffer to HasArguments::ArgumentBuffer * introduce Connect::connect_with to provide an ConnectOptions type explicitly to opt-out of connection string parsing * introduce Value and ValueRef traits to allow decoding-deferred extraction of values from Rows * introduce Encode::encode_by_ref and change Encode::encode to take by-value to try and re-use memory where possible * use thiserror to generate sqlx::Error * [!] temporarily removes query logging * [!] temporarily removes transactions
This commit is contained in:
543
Cargo.lock
generated
543
Cargo.lock
generated
@@ -1,5 +1,51 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "actix-macros"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a60f9ba7c4e6df97f3aacb14bb5c0cd7d98a49dcbaed0d7f292912ad9a6a3ed2"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-rt"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227"
|
||||
dependencies = [
|
||||
"actix-macros",
|
||||
"actix-threadpool",
|
||||
"copyless",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"smallvec 1.4.0",
|
||||
"tokio 0.2.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-threadpool"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91164716d956745c79dcea5e66d2aa04506549958accefcede5368c70f2fd4ff"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"futures-channel",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"num_cpus",
|
||||
"parking_lot 0.10.2",
|
||||
"threadpool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f3e0bf23f51883cce372d5d5892211236856e4bb37fb942e1eb135ee0f146e3"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.10"
|
||||
@@ -61,35 +107,32 @@ dependencies = [
|
||||
"async-std",
|
||||
"native-tls",
|
||||
"thiserror",
|
||||
"tokio 0.2.13",
|
||||
"url 2.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-std"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267"
|
||||
checksum = "a45cee2749d880d7066e328a7e161c7470ced883b2fd000ca4643e9f1dd5083a"
|
||||
dependencies = [
|
||||
"async-attributes",
|
||||
"async-task",
|
||||
"broadcaster",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils 0.7.2",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-timer",
|
||||
"kv-log-macro",
|
||||
"log",
|
||||
"memchr",
|
||||
"mio",
|
||||
"mio-uds",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
"smol",
|
||||
"wasm-bindgen-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -115,13 +158,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "1.3.1"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
checksum = "c17772156ef2829aadc587461c7753af20b7e8db1529bc66855add962a3b35d3"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
@@ -134,6 +173,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0afb7287b68575f5ca0e5c7e40191cbd4be59d325781f46faa603e176eaef47"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@@ -249,20 +297,6 @@ dependencies = [
|
||||
"byte-tools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "broadcaster"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c972e21e0d055a36cf73e4daae870941fe7a8abcd5ac3396aab9e4c126bd87"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"parking_lot 0.10.0",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.2.1"
|
||||
@@ -402,6 +436,12 @@ dependencies = [
|
||||
"url 1.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "copyless"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ff9c56c9fb2a49c05ef0e431485a22400af20d33226dc0764d891d09e724127"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.7.0"
|
||||
@@ -418,6 +458,20 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.4.2"
|
||||
@@ -501,6 +555,17 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11c0346158a19b3627234e15596f5e465c360fcdb97d817bcb255e0510f5a788"
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2127768764f1556535c01b5326ef94bd60ff08dcfbdc544d53e69ed155610f5d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dialoguer"
|
||||
version = "0.5.0"
|
||||
@@ -634,9 +699,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8"
|
||||
checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -644,9 +709,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a"
|
||||
checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
|
||||
|
||||
[[package]]
|
||||
name = "futures-cpupool"
|
||||
@@ -671,15 +736,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6"
|
||||
checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7"
|
||||
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
@@ -689,27 +754,34 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6"
|
||||
checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27"
|
||||
checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
version = "2.0.2"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6"
|
||||
checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
|
||||
dependencies = [
|
||||
"gloo-timers",
|
||||
"send_wrapper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5"
|
||||
checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
|
||||
dependencies = [
|
||||
"futures 0.1.29",
|
||||
"futures-channel",
|
||||
@@ -719,6 +791,7 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project",
|
||||
"pin-utils",
|
||||
"proc-macro-hack",
|
||||
"proc-macro-nested",
|
||||
@@ -752,6 +825,19 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.1.26"
|
||||
@@ -770,6 +856,16 @@ dependencies = [
|
||||
"tokio-io",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.1"
|
||||
@@ -958,6 +1054,15 @@ version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce10c23ad2ea25ceca0093bd3192229da4c5b3c0f2de499c1ecac0d98d452177"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonwebtoken"
|
||||
version = "6.0.1"
|
||||
@@ -1000,15 +1105,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.68"
|
||||
version = "0.2.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0"
|
||||
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.17.1"
|
||||
version = "0.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "266eb8c361198e8d1f682bc974e5d9e2ae90049fb1943890904d11dad7d4a77d"
|
||||
checksum = "56d90181c2904c287e5390186be820e5ef311a3c62edebb7d6ca3d6a48ce041d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
@@ -1017,9 +1122,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b"
|
||||
checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
@@ -1176,6 +1281,19 @@ dependencies = [
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.2.6"
|
||||
@@ -1274,12 +1392,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.10.0"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc"
|
||||
checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.7.0",
|
||||
"parking_lot_core 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1299,15 +1417,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.7.0"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1"
|
||||
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cloudabi",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec 1.2.0",
|
||||
"smallvec 1.4.0",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
@@ -1372,6 +1490,70 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edc93aeee735e60ecb40cf740eb319ff23eab1c5748abfdb5c180e4ce49f7791"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.1.4"
|
||||
@@ -1380,9 +1562,21 @@ checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0-alpha.4"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "piper"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b0deb65f46e873ba8aa7c6a8dbe3f23cb1bf59c339a81a1d56361dde4d66ac8"
|
||||
dependencies = [
|
||||
"crossbeam-utils 0.7.2",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
@@ -1436,9 +1630,9 @@ checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.10"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3"
|
||||
checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
@@ -1469,6 +1663,7 @@ dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
"rand_pcg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1499,6 +1694,15 @@ dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.56"
|
||||
@@ -1579,17 +1783,6 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.3"
|
||||
@@ -1606,6 +1799,12 @@ dependencies = [
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls-hkt"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2e9d7eaddb227e8fbaaa71136ae0e1e913ca159b86c7da82f3e8f0044ad3a63"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
@@ -1651,6 +1850,12 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "send_wrapper"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.110"
|
||||
@@ -1735,6 +1940,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.2"
|
||||
@@ -1752,15 +1963,34 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.2.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
|
||||
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
|
||||
|
||||
[[package]]
|
||||
name = "smol"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "686c634ad1873fffef6aed20f180eede424fbf3bb31802394c90fd7335a661b7"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"crossbeam",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"piper",
|
||||
"scoped-tls-hkt",
|
||||
"slab",
|
||||
"socket2",
|
||||
"wepoll-binding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.3.11"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85"
|
||||
checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -1800,8 +2030,8 @@ dependencies = [
|
||||
"sqlx-core",
|
||||
"sqlx-macros",
|
||||
"sqlx-test",
|
||||
"time 0.2.9",
|
||||
"tokio 0.2.13",
|
||||
"time 0.2.16",
|
||||
"tokio 0.2.21",
|
||||
"trybuild",
|
||||
]
|
||||
|
||||
@@ -1822,7 +2052,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"structopt",
|
||||
"tokio 0.2.13",
|
||||
"tokio 0.2.21",
|
||||
"url 2.1.1",
|
||||
]
|
||||
|
||||
@@ -1830,41 +2060,51 @@ dependencies = [
|
||||
name = "sqlx-core"
|
||||
version = "0.3.5"
|
||||
dependencies = [
|
||||
"async-native-tls",
|
||||
"async-std",
|
||||
"async-stream",
|
||||
"atoi",
|
||||
"base64 0.12.0",
|
||||
"bigdecimal",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"bytes 0.5.4",
|
||||
"chrono",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils 0.7.2",
|
||||
"digest",
|
||||
"either",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"generic-array",
|
||||
"hashbrown",
|
||||
"hex",
|
||||
"hmac",
|
||||
"ipnetwork",
|
||||
"itoa",
|
||||
"libc",
|
||||
"libsqlite3-sys",
|
||||
"log",
|
||||
"md-5",
|
||||
"memchr",
|
||||
"num-bigint",
|
||||
"parking_lot 0.10.2",
|
||||
"percent-encoding 2.1.0",
|
||||
"phf",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha-1",
|
||||
"sha2",
|
||||
"smallvec 1.4.0",
|
||||
"sqlformat",
|
||||
"time 0.2.9",
|
||||
"tokio 0.2.13",
|
||||
"sqlx-rt",
|
||||
"thiserror",
|
||||
"threadpool",
|
||||
"time 0.2.16",
|
||||
"url 2.1.1",
|
||||
"uuid",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1956,10 +2196,23 @@ dependencies = [
|
||||
"sha2",
|
||||
"sqlx-core",
|
||||
"syn",
|
||||
"tokio 0.2.13",
|
||||
"tokio 0.2.21",
|
||||
"url 2.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-rt"
|
||||
version = "0.1.0-pre"
|
||||
dependencies = [
|
||||
"actix-rt",
|
||||
"actix-threadpool",
|
||||
"async-native-tls",
|
||||
"async-std",
|
||||
"native-tls",
|
||||
"tokio 0.2.21",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-test"
|
||||
version = "0.1.0"
|
||||
@@ -1969,14 +2222,14 @@ dependencies = [
|
||||
"dotenv",
|
||||
"env_logger",
|
||||
"sqlx",
|
||||
"tokio 0.2.13",
|
||||
"tokio 0.2.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "standback"
|
||||
version = "0.2.1"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4edf667ea8f60afc06d6aeec079d20d5800351109addec1faea678a8663da4e1"
|
||||
checksum = "47e4b8c631c998468961a9ea159f064c5c8499b95b5e4a34b77849d45949d540"
|
||||
|
||||
[[package]]
|
||||
name = "stdweb"
|
||||
@@ -2074,9 +2327,9 @@ checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.17"
|
||||
version = "1.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03"
|
||||
checksum = "bb37da98a55b1d08529362d9cbb863be17556873df2585904ab9d2bc951291d0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2137,18 +2390,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.14"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0570dc61221295909abdb95c739f2e74325e14293b2026b0a7e195091ec54ae"
|
||||
checksum = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.14"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "227362df41d566be41a28f64401e07a043157c21c14b9785a0d8e256f940a8fd"
|
||||
checksum = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2164,6 +2417,15 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "threadpool"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865"
|
||||
dependencies = [
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tide"
|
||||
version = "0.6.0"
|
||||
@@ -2198,16 +2460,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.2.9"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6329a7835505d46f5f3a9a2c237f8d6bf5ca6f0015decb3698ba57fcdbb609ba"
|
||||
checksum = "3a51cadc5b1eec673a685ff7c33192ff7b7603d0b75446fb354939ee615acb15"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"rustversion",
|
||||
"standback",
|
||||
"stdweb",
|
||||
"time-macros",
|
||||
"version_check",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
@@ -2253,9 +2515,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "0.2.13"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa5e81d6bc4e67fe889d5783bd2a128ab2e0cfa487e0be16b6a8d177b101616"
|
||||
checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58"
|
||||
dependencies = [
|
||||
"bytes 0.5.4",
|
||||
"fnv",
|
||||
@@ -2328,6 +2590,16 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd608593a919a8e05a7d1fc6df885e40f6a88d3a70a3a7eff23ff27964eda069"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio 0.2.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-reactor"
|
||||
version = "0.1.12"
|
||||
@@ -2450,7 +2722,7 @@ version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4"
|
||||
dependencies = [
|
||||
"smallvec 1.2.0",
|
||||
"smallvec 1.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2523,6 +2795,12 @@ version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.2.0"
|
||||
@@ -2542,9 +2820,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.60"
|
||||
version = "0.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f"
|
||||
checksum = "4c2dc4aa152834bc334f506c1a06b866416a8b6697d5c9f75b9a689c8486def0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -2552,9 +2830,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.60"
|
||||
version = "0.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd"
|
||||
checksum = "ded84f06e0ed21499f6184df0e0cb3494727b0c5da89534e0fcc55c51d812101"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
@@ -2566,10 +2844,22 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.60"
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4"
|
||||
checksum = "64487204d863f109eb77e8462189d111f27cb5712cc9fdb3461297a76963a2f6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "838e423688dac18d73e31edce74ddfac468e37b1506ad163ffaf0a46f703ffe3"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -2577,9 +2867,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.60"
|
||||
version = "0.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931"
|
||||
checksum = "3156052d8ec77142051a533cdd686cba889537b213f948cd1d20869926e68e92"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2590,9 +2880,44 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.60"
|
||||
version = "0.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639"
|
||||
checksum = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b72fe77fd39e4bd3eaa4412fd299a0be6b3dfe9d2597e2f1c20beb968f41d17"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wepoll-binding"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "374fff4ff9701ff8b6ad0d14bacd3156c44063632d8c136186ff5967d48999a7"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"wepoll-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wepoll-sys"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9082a777aed991f6769e2b654aa0cb29f1c3d615daf009829b07b66c7aff6a24"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a08eb844b158ea881e81b94556eede7f7e306e4c7b976aad88f49e6e36dec391"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
|
||||
@@ -6,74 +6,80 @@ description = "Core of SQLx, the rust SQL toolkit. Not intended to be used direc
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2018"
|
||||
authors = [
|
||||
"Ryan Leckey <leckey.ryan@gmail.com>", # ryan@launchbadge.com
|
||||
"Austin Bonander <austin.bonander@gmail.com>", # austin@launchbadge.com
|
||||
"Zachery Gyurkovitz <zgyurkovitz@gmail.com>", # zach@launchbadge.com
|
||||
"Daniel Akhterov <akhterovd@gmail.com>", # daniel@launchbadge.com
|
||||
"Ryan Leckey <leckey.ryan@gmail.com>",
|
||||
"Austin Bonander <austin.bonander@gmail.com>",
|
||||
"Zachery Gyurkovitz <zgyurkovitz@gmail.com>",
|
||||
"Daniel Akhterov <akhterovd@gmail.com>",
|
||||
]
|
||||
|
||||
[features]
|
||||
default = [ "runtime-async-std" ]
|
||||
unstable = []
|
||||
# intended mainly for CI and docs
|
||||
all = ["all-database", "all-type"]
|
||||
all-database = ["mysql", "sqlite", "postgres"]
|
||||
all-type = ["bigdecimal", "json", "time", "chrono", "ipnetwork", "uuid"]
|
||||
# we need a feature which activates `num-bigint` as well because
|
||||
# `bigdecimal` uses types from it but does not reexport (tsk tsk)
|
||||
bigdecimal = ["bigdecimal_", "num-bigint"]
|
||||
postgres = [ "md-5", "sha2", "base64", "sha-1", "rand", "hmac", "futures-channel/sink", "futures-util/sink", "tokio/uds" ]
|
||||
json = ["serde", "serde_json"]
|
||||
|
||||
# [deprecated] TLS is not possible to disable due to it being conditional on multiple features
|
||||
# Hopefully Cargo can handle this in the future
|
||||
tls = []
|
||||
|
||||
# databases
|
||||
postgres = [ "md-5", "sha2", "base64", "sha-1", "rand", "hmac", "futures-channel/sink", "futures-util/sink" ]
|
||||
mysql = [ "sha-1", "sha2", "generic-array", "num-bigint", "base64", "digest", "rand" ]
|
||||
sqlite = [ "libsqlite3-sys" ]
|
||||
tls = [ "async-native-tls" ]
|
||||
runtime-async-std = [ "async-native-tls/runtime-async-std", "async-std" ]
|
||||
runtime-tokio = [ "async-native-tls/runtime-tokio", "tokio" ]
|
||||
# intended for internal benchmarking, do not use
|
||||
bench = []
|
||||
|
||||
# types
|
||||
all-types = [ "chrono", "time", "bigdecimal", "ipnetwork", "json", "uuid" ]
|
||||
bigdecimal = [ "bigdecimal_", "num-bigint" ]
|
||||
json = [ "serde", "serde_json" ]
|
||||
|
||||
# runtimes
|
||||
runtime-async-std = [ "sqlx-rt/runtime-async-std" ]
|
||||
runtime-tokio = [ "sqlx-rt/runtime-tokio" ]
|
||||
runtime-actix = [ "sqlx-rt/runtime-actix" ]
|
||||
|
||||
# support offline/decoupled building (enables serialization of `Describe`)
|
||||
offline = ["serde"]
|
||||
|
||||
[dependencies]
|
||||
async-native-tls = { version = "0.3.2", default-features = false, optional = true }
|
||||
async-std = { version = "1.5.0", features = [ "unstable" ], optional = true }
|
||||
atoi = "0.3.2"
|
||||
sqlx-rt = { path = "../sqlx-rt", version = "0.1.0-pre" }
|
||||
async-stream = { version = "0.2.1", default-features = false }
|
||||
base64 = { version = "0.12.0", default-features = false, optional = true, features = [ "std" ] }
|
||||
bigdecimal_ = { version = "0.1.0", optional = true, package = "bigdecimal" }
|
||||
bitflags = { version = "1.2.1", default-features = false }
|
||||
bytes = "0.5.4"
|
||||
byteorder = { version = "1.3.4", default-features = false, features = [ "std" ] }
|
||||
chrono = { version = "0.4.10", default-features = false, features = [ "clock" ], optional = true }
|
||||
chrono = { version = "0.4.11", default-features = false, features = [ "clock" ], optional = true }
|
||||
crossbeam-queue = "0.2.1"
|
||||
crossbeam-channel = "0.4.2"
|
||||
crossbeam-utils = { version = "0.7.2", default-features = false }
|
||||
digest = { version = "0.8.1", default-features = false, optional = true, features = [ "std" ] }
|
||||
either = "1.5.3"
|
||||
futures-channel = { version = "0.3.4", default-features = false, features = [ "alloc", "std" ] }
|
||||
futures-core = { version = "0.3.4", default-features = false }
|
||||
futures-util = { version = "0.3.4", default-features = false }
|
||||
futures-util = "0.3.4"
|
||||
generic-array = { version = "0.12.3", default-features = false, optional = true }
|
||||
hashbrown = "0.7.1"
|
||||
hex = "0.4.2"
|
||||
hmac = { version = "0.7.1", default-features = false, optional = true }
|
||||
itoa = "0.4.5"
|
||||
ipnetwork = { version = "0.16.0", default-features = false, optional = true }
|
||||
libc = "0.2.68"
|
||||
libc = "0.2.69"
|
||||
libsqlite3-sys = { version = "0.17.3", optional = true, default-features = false, features = [ "pkg-config", "vcpkg", "bundled" ] }
|
||||
log = { version = "0.4.8", default-features = false }
|
||||
md-5 = { version = "0.8.0", default-features = false, optional = true }
|
||||
memchr = { version = "2.3.3", default-features = false }
|
||||
num-bigint = { version = "0.2.6", default-features = false, optional = true, features = [ "std" ] }
|
||||
percent-encoding = "2.1.0"
|
||||
parking_lot = "0.10.2"
|
||||
threadpool = "*"
|
||||
phf = { version = "0.8.0", features = [ "macros" ] }
|
||||
rand = { version = "0.7.3", default-features = false, optional = true, features = [ "std" ] }
|
||||
serde = { version = "1.0.106", features = [ "derive" ], optional = true }
|
||||
serde_json = { version = "1.0.51", features = [ "raw_value" ], optional = true }
|
||||
sha-1 = { version = "0.8.2", default-features = false, optional = true }
|
||||
sha2 = { version = "0.8.1", default-features = false, optional = true }
|
||||
tokio = { version = "0.2.13", default-features = false, features = [ "dns", "fs", "time", "tcp" ], optional = true }
|
||||
sqlformat = "0.1.0"
|
||||
thiserror = "1.0.15"
|
||||
time = { version = "0.2.10", optional = true }
|
||||
smallvec = "1.4.0"
|
||||
url = { version = "2.1.1", default-features = false }
|
||||
uuid = { version = "0.8.1", default-features = false, optional = true, features = [ "std" ] }
|
||||
serde = { version = "1.0", features = [ "derive" ], optional = true }
|
||||
time = { version = "0.2.7", optional = true }
|
||||
serde_json = { version = "1.0", features = [ "raw_value" ], optional = true }
|
||||
sqlformat = "0.1.0"
|
||||
|
||||
# <https://github.com/jgallagher/rusqlite/tree/master/libsqlite3-sys>
|
||||
[dependencies.libsqlite3-sys]
|
||||
version = "0.17.1"
|
||||
optional = true
|
||||
default-features = false
|
||||
features = [ "pkg-config", "vcpkg", "bundled" ]
|
||||
whoami = "0.8.1"
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
//! Traits for passing arguments to SQL queries.
|
||||
//! Types and traits for passing arguments to SQL queries.
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::encode::Encode;
|
||||
use crate::types::Type;
|
||||
|
||||
/// A tuple of arguments to be sent to the database.
|
||||
pub trait Arguments: Send + Sized + Default + 'static {
|
||||
type Database: Database + ?Sized;
|
||||
pub trait Arguments<'q>: Send + Sized + Default {
|
||||
type Database: Database;
|
||||
|
||||
/// Reserves the capacity for at least `len` more values (of `size` bytes) to
|
||||
/// be added to the arguments without a reallocation.
|
||||
fn reserve(&mut self, len: usize, size: usize);
|
||||
/// Reserves the capacity for at least `additional` more values (of `size` total bytes) to
|
||||
/// be added to the arguments without a reallocation.
|
||||
fn reserve(&mut self, additional: usize, size: usize);
|
||||
|
||||
/// Add the value to the end of the arguments.
|
||||
fn add<T>(&mut self, value: T)
|
||||
where
|
||||
T: Type<Self::Database>,
|
||||
T: Encode<Self::Database>;
|
||||
T: 'q + Encode<'q, Self::Database>;
|
||||
}
|
||||
|
||||
@@ -1,123 +1,40 @@
|
||||
//! Contains the `Connection` and `Connect` traits.
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::str::FromStr;
|
||||
|
||||
use futures_core::future::BoxFuture;
|
||||
|
||||
use crate::executor::Executor;
|
||||
use crate::pool::{Pool, PoolConnection};
|
||||
use crate::transaction::Transaction;
|
||||
use crate::url::Url;
|
||||
use crate::database::Database;
|
||||
use crate::error::{BoxDynError, Error};
|
||||
|
||||
/// Represents a single database connection rather than a pool of database connections.
|
||||
///
|
||||
/// Connections can be manually established outside of a [`Pool`] with [`Connect::connect`].
|
||||
///
|
||||
/// Prefer running queries from [`Pool`] unless there is a specific need for a single, sticky
|
||||
/// connection.
|
||||
pub trait Connection
|
||||
where
|
||||
Self: Send + 'static,
|
||||
Self: Executor,
|
||||
{
|
||||
/// Starts a new transaction.
|
||||
///
|
||||
/// Wraps this connection in [`Transaction`] to manage the transaction lifecycle. To get the
|
||||
/// original connection back, explicitly [`commit`] or [`rollback`] and this connection will
|
||||
/// be returned.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// let mut tx = conn.begin().await?;
|
||||
/// // conn is now inaccessible as its wrapped in a transaction
|
||||
///
|
||||
/// let conn = tx.commit().await?;
|
||||
/// // conn is back now and out of the transaction
|
||||
/// ```
|
||||
///
|
||||
/// [`commit`]: crate::transaction::Transaction::commit
|
||||
/// [`rollback`]: crate::transaction::Transaction::rollback
|
||||
fn begin(self) -> BoxFuture<'static, crate::Result<Transaction<Self>>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Box::pin(Transaction::new(0, self))
|
||||
}
|
||||
/// Represents a single database connection.
|
||||
pub trait Connection: Send + 'static {
|
||||
type Database: Database;
|
||||
|
||||
/// Explicitly close this database connection.
|
||||
///
|
||||
/// This method is **not required** for safe and consistent operation. However, it is
|
||||
/// recommended to call it instead of letting a connection `drop` as the database server
|
||||
/// recommended to call it instead of letting a connection `drop` as the database backend
|
||||
/// will be faster at cleaning up resources.
|
||||
fn close(self) -> BoxFuture<'static, crate::Result<()>>;
|
||||
fn close(self) -> BoxFuture<'static, Result<(), Error>>;
|
||||
|
||||
/// Checks if a connection to the database is still valid.
|
||||
fn ping(&mut self) -> BoxFuture<crate::Result<()>>;
|
||||
fn ping(&mut self) -> BoxFuture<Result<(), Error>>;
|
||||
}
|
||||
|
||||
/// Represents a type that can directly establish a new connection.
|
||||
pub trait Connect: Connection {
|
||||
pub trait Connect: Sized + Connection {
|
||||
type Options: FromStr<Err = BoxDynError> + Send + Sync;
|
||||
|
||||
/// Establish a new database connection.
|
||||
fn connect<T>(url: T) -> BoxFuture<'static, crate::Result<Self>>
|
||||
where
|
||||
T: TryInto<Url, Error = url::ParseError>,
|
||||
Self: Sized;
|
||||
}
|
||||
///
|
||||
/// A value of `Options` is parsed from the provided connection string. This parsing
|
||||
/// is database-specific.
|
||||
#[inline]
|
||||
fn connect(url: &str) -> BoxFuture<'static, Result<Self, Error>> {
|
||||
let options = url.parse().map_err(Error::ParseConnectOptions);
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum ConnectionSource<'c, C>
|
||||
where
|
||||
C: Connect,
|
||||
{
|
||||
ConnectionRef(&'c mut C),
|
||||
Connection(C),
|
||||
PoolConnection(Pool<C>, PoolConnection<C>),
|
||||
Pool(Pool<C>),
|
||||
}
|
||||
|
||||
impl<'c, C> ConnectionSource<'c, C>
|
||||
where
|
||||
C: Connect,
|
||||
{
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn resolve(&mut self) -> crate::Result<&'_ mut C> {
|
||||
if let ConnectionSource::Pool(pool) = self {
|
||||
let conn = pool.acquire().await?;
|
||||
|
||||
*self = ConnectionSource::PoolConnection(pool.clone(), conn);
|
||||
}
|
||||
|
||||
Ok(match self {
|
||||
ConnectionSource::ConnectionRef(conn) => conn,
|
||||
ConnectionSource::PoolConnection(_, ref mut conn) => conn,
|
||||
ConnectionSource::Connection(ref mut conn) => conn,
|
||||
ConnectionSource::Pool(_) => unreachable!(),
|
||||
})
|
||||
Box::pin(async move { Ok(Self::connect_with(&options?).await?) })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, C> From<C> for ConnectionSource<'c, C>
|
||||
where
|
||||
C: Connect,
|
||||
{
|
||||
fn from(connection: C) -> Self {
|
||||
ConnectionSource::Connection(connection)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, C> From<PoolConnection<C>> for ConnectionSource<'c, C>
|
||||
where
|
||||
C: Connect,
|
||||
{
|
||||
fn from(connection: PoolConnection<C>) -> Self {
|
||||
ConnectionSource::PoolConnection(Pool(connection.pool.clone()), connection)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, C> From<Pool<C>> for ConnectionSource<'c, C>
|
||||
where
|
||||
C: Connect,
|
||||
{
|
||||
fn from(pool: Pool<C>) -> Self {
|
||||
ConnectionSource::Pool(pool)
|
||||
}
|
||||
/// Establish a new database connection with the provided options.
|
||||
fn connect_with(options: &Self::Options) -> BoxFuture<'_, Result<Self, Error>>;
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
//! Contains the `Cursor` trait.
|
||||
|
||||
use futures_core::future::BoxFuture;
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::executor::Execute;
|
||||
use crate::pool::Pool;
|
||||
use crate::row::HasRow;
|
||||
|
||||
/// Represents a result set, which is generated by executing a query against the database.
|
||||
///
|
||||
/// A `Cursor` can be created by either [`Executor::fetch`] or [`Query::fetch`].
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// let mut cursor = sqlx::query("SELECT slug, title, description FROM articles")
|
||||
/// .fetch(&mut conn);
|
||||
/// ```
|
||||
///
|
||||
/// Initially the `Cursor` is positioned before the first row. The `next` method moves the cursor
|
||||
/// to the next row, and because it returns `None` when there are no more rows, it can be used
|
||||
/// in a `while` loop to iterate through all returned rows.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// # #[derive(sqlx::FromRow)]
|
||||
/// # struct Article<'a> {
|
||||
/// # slug: &'a str,
|
||||
/// # title: &'a str,
|
||||
/// # description: &'a str,
|
||||
/// # }
|
||||
/// #
|
||||
/// // For each row in the result set ..
|
||||
/// while let Some(row) = cursor.next().await? {
|
||||
/// // .. decode a domain type from the row
|
||||
/// let obj = Article::from_row(row)?;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This trait is sealed and cannot be implemented for types outside of SQLx.
|
||||
///
|
||||
/// [`Executor::fetch`]: crate::executor::Executor::fetch
|
||||
/// [`Query::fetch`]: crate::query::Query::fetch
|
||||
#[must_use = "cursor must have `.next()` called to execute query"]
|
||||
pub trait Cursor<'c, 'q>
|
||||
where
|
||||
Self: Send + Unpin + private::Sealed,
|
||||
{
|
||||
/// The `Database` this `Cursor` is implemented for.
|
||||
type Database: Database;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn from_pool<E>(pool: &Pool<<Self::Database as Database>::Connection>, query: E) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
E: Execute<'q, Self::Database>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn from_connection<E>(
|
||||
connection: &'c mut <Self::Database as Database>::Connection,
|
||||
query: E,
|
||||
) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
E: Execute<'q, Self::Database>;
|
||||
|
||||
/// Creates a future that attempts to resolve the next row in the cursor.
|
||||
fn next<'cur>(
|
||||
&'cur mut self,
|
||||
) -> BoxFuture<'cur, crate::Result<Option<<Self::Database as HasRow<'cur>>::Row>>>;
|
||||
}
|
||||
|
||||
// Prevent users from implementing the `Row` trait.
|
||||
pub(crate) mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
/// Associate [`Database`] with a [`Cursor`] of a generic lifetime.
|
||||
///
|
||||
/// ---
|
||||
///
|
||||
/// The upcoming Rust feature, [Generic Associated Types], should obviate
|
||||
/// the need for this trait.
|
||||
///
|
||||
/// [Generic Associated Types]: https://www.google.com/search?q=generic+associated+types+rust&oq=generic+associated+types+rust&aqs=chrome..69i57j0l5.3327j0j7&sourceid=chrome&ie=UTF-8
|
||||
pub trait HasCursor<'c, 'q> {
|
||||
type Database: Database;
|
||||
|
||||
/// The concrete `Cursor` implementation for this database.
|
||||
type Cursor: Cursor<'c, 'q, Database = Self::Database>;
|
||||
}
|
||||
@@ -1,42 +1,66 @@
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::arguments::Arguments;
|
||||
use crate::connection::Connect;
|
||||
use crate::cursor::HasCursor;
|
||||
use crate::error::DatabaseError;
|
||||
use crate::row::HasRow;
|
||||
use crate::types::TypeInfo;
|
||||
use crate::value::HasRawValue;
|
||||
use crate::row::Row;
|
||||
use crate::type_info::TypeInfo;
|
||||
use crate::value::{Value, ValueRef};
|
||||
|
||||
/// A database driver.
|
||||
///
|
||||
/// This trait encapsulates a complete driver implementation to a specific
|
||||
/// database (e.g., **MySQL**, **Postgres**).
|
||||
pub trait Database
|
||||
where
|
||||
Self: Debug + Sized + Send + 'static,
|
||||
Self: for<'c> HasRow<'c, Database = Self>,
|
||||
Self: for<'c> HasRawValue<'c, Database = Self>,
|
||||
Self: for<'c, 'q> HasCursor<'c, 'q, Database = Self>,
|
||||
/// This trait encapsulates a complete set of traits that implement a driver for a
|
||||
/// specific database (e.g., MySQL, PostgreSQL).
|
||||
pub trait Database:
|
||||
Sized
|
||||
+ Send
|
||||
+ Debug
|
||||
+ for<'r> HasValueRef<'r, Database = Self>
|
||||
+ for<'q> HasArguments<'q, Database = Self>
|
||||
{
|
||||
/// The concrete `Connection` implementation for this database.
|
||||
type Connection: Connect<Database = Self>;
|
||||
|
||||
/// The concrete `Arguments` implementation for this database.
|
||||
type Arguments: Arguments<Database = Self>;
|
||||
/// The concrete `Row` implementation for this database.
|
||||
type Row: Row<Database = Self>;
|
||||
|
||||
/// The concrete `TypeInfo` implementation for this database.
|
||||
type TypeInfo: TypeInfo;
|
||||
|
||||
/// The Rust type of table identifiers for this database.
|
||||
type TableId: Display + Clone;
|
||||
|
||||
/// The Rust type used as the buffer when encoding arguments.
|
||||
///
|
||||
/// For example, **Postgres** and **MySQL** use `Vec<u8>`;
|
||||
/// however, **SQLite** uses `Vec<SqliteArgumentValue>`.
|
||||
type RawBuffer: Default;
|
||||
|
||||
/// The concrete `DatabaseError` type used to report errors from the database.
|
||||
type Error: DatabaseError + Send + Sync;
|
||||
/// The concrete type used to hold an owned copy of the not-yet-decoded value that was
|
||||
/// received from the database.
|
||||
type Value: Value<Database = Self> + 'static;
|
||||
}
|
||||
|
||||
/// Associate [`Database`] with a [`ValueRef`](crate::value::ValueRef) of a generic lifetime.
|
||||
///
|
||||
/// ---
|
||||
///
|
||||
/// The upcoming Rust feature, [Generic Associated Types], should obviate
|
||||
/// the need for this trait.
|
||||
///
|
||||
/// [Generic Associated Types]: https://www.google.com/search?q=generic+associated+types+rust&oq=generic+associated+types+rust&aqs=chrome..69i57j0l5.3327j0j7&sourceid=chrome&ie=UTF-8
|
||||
pub trait HasValueRef<'r> {
|
||||
type Database: Database;
|
||||
|
||||
/// The concrete type used to hold a reference to the not-yet-decoded value that has just been
|
||||
/// received from the database.
|
||||
type ValueRef: ValueRef<'r, Database = Self::Database>;
|
||||
}
|
||||
|
||||
/// Associate [`Database`] with an [`Arguments`](crate::arguments::Arguments) of a generic lifetime.
|
||||
///
|
||||
/// ---
|
||||
///
|
||||
/// The upcoming Rust feature, [Generic Associated Types], should obviate
|
||||
/// the need for this trait.
|
||||
///
|
||||
/// [Generic Associated Types]: https://www.google.com/search?q=generic+associated+types+rust&oq=generic+associated+types+rust&aqs=chrome..69i57j0l5.3327j0j7&sourceid=chrome&ie=UTF-8
|
||||
pub trait HasArguments<'q> {
|
||||
type Database: Database;
|
||||
|
||||
/// The concrete `Arguments` implementation for this database.
|
||||
type Arguments: Arguments<'q, Database = Self::Database>;
|
||||
|
||||
/// The concrete type used as a buffer for arguments while encoding.
|
||||
type ArgumentBuffer: Default;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,37 @@
|
||||
//! Types and traits for decoding values from the database.
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::value::HasRawValue;
|
||||
use crate::database::{Database, HasValueRef};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::types::Type;
|
||||
use crate::value::ValueRef;
|
||||
|
||||
/// A type that can be decoded from the database.
|
||||
pub trait Decode<'de, DB>
|
||||
where
|
||||
Self: Sized + 'de,
|
||||
DB: Database,
|
||||
{
|
||||
fn decode(value: <DB as HasRawValue<'de>>::RawValue) -> crate::Result<Self>;
|
||||
pub trait Decode<'r, DB: Database>: Sized + Type<DB> {
|
||||
/// Determines if a value of this type can be created from a value with the
|
||||
/// given type information.
|
||||
fn accepts(ty: &DB::TypeInfo) -> bool {
|
||||
*ty == Self::type_info()
|
||||
}
|
||||
|
||||
/// Decode a new value of this type using a raw value from the database.
|
||||
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError>;
|
||||
}
|
||||
|
||||
// implement `Decode` for Option<T> for all SQL types
|
||||
impl<'r, DB, T> Decode<'r, DB> for Option<T>
|
||||
where
|
||||
DB: Database,
|
||||
T: Decode<'r, DB>,
|
||||
{
|
||||
fn accepts(ty: &DB::TypeInfo) -> bool {
|
||||
T::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> {
|
||||
if value.is_null() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(T::decode(value)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +1,67 @@
|
||||
//! Types for returning SQL type information about queries.
|
||||
|
||||
use std::fmt::{self, Debug};
|
||||
//!
|
||||
//! The compile-time type checking within the query macros heavily lean on the information
|
||||
//! provided within these types.
|
||||
|
||||
use crate::database::Database;
|
||||
|
||||
/// The return type of [`Executor::describe`].
|
||||
// TODO(@mehcode): Remove [pub] from Describe/Column and use methods to expose the properties
|
||||
|
||||
/// A representation of a statement that _could_ have been executed against the database.
|
||||
///
|
||||
/// [`Executor::describe`]: crate::executor::Executor::describe
|
||||
/// Returned from [`Executor::describe`](crate::executor::Executor::describe).
|
||||
///
|
||||
/// The compile-time verification within the query macros utilizes `describe` and this type to
|
||||
/// act on an arbitrary query.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
#[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "offline",
|
||||
serde(bound(
|
||||
serialize = "DB::TypeInfo: serde::Serialize, Column<DB>: serde::Serialize",
|
||||
deserialize = "DB::TypeInfo: serde::de::DeserializeOwned, Column<DB>: serde::de::DeserializeOwned"
|
||||
serialize = "DB::TypeInfo: serde::Serialize",
|
||||
deserialize = "DB::TypeInfo: serde::de::DeserializeOwned"
|
||||
))
|
||||
)]
|
||||
#[non_exhaustive]
|
||||
pub struct Describe<DB>
|
||||
where
|
||||
DB: Database + ?Sized,
|
||||
{
|
||||
// TODO: Describe#param_types should probably be Option<TypeInfo[]> as we either know all the params or we know none
|
||||
/// The expected types for the parameters of the query.
|
||||
pub param_types: Box<[Option<DB::TypeInfo>]>,
|
||||
|
||||
/// The type and table information, if any for the results of the query.
|
||||
pub result_columns: Box<[Column<DB>]>,
|
||||
}
|
||||
|
||||
impl<DB> Debug for Describe<DB>
|
||||
where
|
||||
DB: Database,
|
||||
DB::TypeInfo: Debug,
|
||||
Column<DB>: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Describe")
|
||||
.field("param_types", &self.param_types)
|
||||
.field("result_columns", &self.result_columns)
|
||||
.finish()
|
||||
}
|
||||
/// The expected types of the parameters. This is currently always an array of `None` values
|
||||
/// on all databases drivers aside from PostgreSQL.
|
||||
pub params: Vec<Option<DB::TypeInfo>>,
|
||||
|
||||
/// The columns that will be found in the results from this query.
|
||||
pub columns: Vec<Column<DB>>,
|
||||
}
|
||||
|
||||
/// A single column of a result set.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
#[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "offline",
|
||||
serde(bound(
|
||||
serialize = "DB::TableId: serde::Serialize, DB::TypeInfo: serde::Serialize",
|
||||
deserialize = "DB::TableId: serde::de::DeserializeOwned, DB::TypeInfo: serde::de::DeserializeOwned"
|
||||
serialize = "DB::TypeInfo: serde::Serialize",
|
||||
deserialize = "DB::TypeInfo: serde::de::DeserializeOwned"
|
||||
))
|
||||
)]
|
||||
#[non_exhaustive]
|
||||
pub struct Column<DB>
|
||||
where
|
||||
DB: Database + ?Sized,
|
||||
DB: Database,
|
||||
{
|
||||
pub name: Option<Box<str>>,
|
||||
pub table_id: Option<DB::TableId>,
|
||||
pub type_info: Option<DB::TypeInfo>,
|
||||
/// Whether or not the column cannot be `NULL` (or if that is even knowable).
|
||||
pub non_null: Option<bool>,
|
||||
}
|
||||
/// The name of the result column.
|
||||
///
|
||||
/// The column name is unreliable (and can change between database minor versions) if this
|
||||
/// result column is an expression that has not been aliased.
|
||||
pub name: String,
|
||||
|
||||
impl<DB> Debug for Column<DB>
|
||||
where
|
||||
DB: Database + ?Sized,
|
||||
DB::TableId: Debug,
|
||||
DB::TypeInfo: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Column")
|
||||
.field("name", &self.name)
|
||||
.field("table_id", &self.table_id)
|
||||
.field("type_info", &self.type_info)
|
||||
.field("non_null", &self.non_null)
|
||||
.finish()
|
||||
}
|
||||
/// The type information for the result column.
|
||||
///
|
||||
/// This may be `None` if the type cannot be determined. This occurs in SQLite when
|
||||
/// the column is an expression.
|
||||
pub type_info: Option<DB::TypeInfo>,
|
||||
|
||||
/// Whether the column cannot be `NULL` (or if that is even knowable).
|
||||
/// This value is only not `None` if received from a call to `describe`.
|
||||
pub not_null: Option<bool>,
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! Types and traits for encoding values to the database.
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::types::Type;
|
||||
use std::mem;
|
||||
|
||||
use crate::database::{Database, HasArguments};
|
||||
use crate::types::Type;
|
||||
|
||||
/// The return type of [Encode::encode].
|
||||
pub enum IsNull {
|
||||
/// The value is null; no data was written.
|
||||
@@ -16,64 +16,87 @@ pub enum IsNull {
|
||||
}
|
||||
|
||||
/// Encode a single value to be sent to the database.
|
||||
pub trait Encode<DB>
|
||||
where
|
||||
DB: Database + ?Sized,
|
||||
{
|
||||
/// Writes the value of `self` into `buf` in the expected format for the database.
|
||||
fn encode(&self, buf: &mut DB::RawBuffer);
|
||||
|
||||
fn encode_nullable(&self, buf: &mut DB::RawBuffer) -> IsNull {
|
||||
self.encode(buf);
|
||||
|
||||
IsNull::No
|
||||
pub trait Encode<'q, DB: Database>: Type<DB> {
|
||||
fn produces(&self) -> DB::TypeInfo {
|
||||
Self::type_info()
|
||||
}
|
||||
|
||||
/// Writes the value of `self` into `buf` in the expected format for the database.
|
||||
#[must_use]
|
||||
fn encode(self, buf: &mut <DB as HasArguments<'q>>::ArgumentBuffer) -> IsNull
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.encode_by_ref(buf)
|
||||
}
|
||||
|
||||
/// Writes the value of `self` into `buf` without moving `self`.
|
||||
///
|
||||
/// Where possible, make use of `encode` instead as it can take advantage of re-using
|
||||
/// memory.
|
||||
#[must_use]
|
||||
fn encode_by_ref(&self, buf: &mut <DB as HasArguments<'q>>::ArgumentBuffer) -> IsNull;
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> usize {
|
||||
mem::size_of_val(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, DB> Encode<DB> for &'_ T
|
||||
impl<'q, T, DB: Database> Encode<'q, DB> for &'_ T
|
||||
where
|
||||
DB: Database,
|
||||
T: Type<DB>,
|
||||
T: Encode<DB>,
|
||||
T: Encode<'q, DB>,
|
||||
{
|
||||
fn encode(&self, buf: &mut DB::RawBuffer) {
|
||||
(*self).encode(buf)
|
||||
#[inline]
|
||||
fn produces(&self) -> DB::TypeInfo {
|
||||
(**self).produces()
|
||||
}
|
||||
|
||||
fn encode_nullable(&self, buf: &mut DB::RawBuffer) -> IsNull {
|
||||
(*self).encode_nullable(buf)
|
||||
#[inline]
|
||||
fn encode(self, buf: &mut <DB as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
|
||||
<T as Encode<DB>>::encode_by_ref(self, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn encode_by_ref(&self, buf: &mut <DB as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
|
||||
<&T as Encode<DB>>::encode(self, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> usize {
|
||||
(*self).size_hint()
|
||||
(**self).size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, DB> Encode<DB> for Option<T>
|
||||
where
|
||||
DB: Database,
|
||||
T: Type<DB>,
|
||||
T: Encode<DB>,
|
||||
{
|
||||
fn encode(&self, buf: &mut DB::RawBuffer) {
|
||||
// Forward to [encode_nullable] and ignore the result
|
||||
let _ = self.encode_nullable(buf);
|
||||
impl<'q, T: 'q + Encode<'q, DB>, DB: Database> Encode<'q, DB> for Option<T> {
|
||||
#[inline]
|
||||
fn produces(&self) -> DB::TypeInfo {
|
||||
if let Some(v) = self {
|
||||
v.produces()
|
||||
} else {
|
||||
T::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_nullable(&self, buf: &mut DB::RawBuffer) -> IsNull {
|
||||
if let Some(self_) = self {
|
||||
self_.encode(buf);
|
||||
|
||||
IsNull::No
|
||||
#[inline]
|
||||
fn encode(self, buf: &mut <DB as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
|
||||
if let Some(v) = self {
|
||||
v.encode(buf)
|
||||
} else {
|
||||
IsNull::Yes
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn encode_by_ref(&self, buf: &mut <DB as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
|
||||
if let Some(v) = self {
|
||||
v.encode_by_ref(buf)
|
||||
} else {
|
||||
IsNull::Yes
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> usize {
|
||||
self.as_ref().map_or(0, Encode::size_hint)
|
||||
}
|
||||
|
||||
@@ -1,387 +1,148 @@
|
||||
//! Errorand Result types.
|
||||
use std::any::type_name;
|
||||
use std::borrow::Cow;
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt::Display;
|
||||
use std::io;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::types::Type;
|
||||
use std::any::type_name;
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::io;
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! decode_err {
|
||||
($s:literal, $($args:tt)*) => {
|
||||
crate::Error::Decode(format!($s, $($args)*).into())
|
||||
};
|
||||
|
||||
($expr:expr) => {
|
||||
crate::Error::decode($expr)
|
||||
};
|
||||
}
|
||||
|
||||
/// A specialized `Result` type for SQLx.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
pub type Result<T> = StdResult<T, Error>;
|
||||
|
||||
/// A generic error that represents all the ways a method can fail inside of SQLx.
|
||||
#[derive(Debug)]
|
||||
// Convenience type alias for usage within SQLx.
|
||||
pub(crate) type BoxDynError = Box<dyn StdError + 'static + Send + Sync>;
|
||||
|
||||
/// An unexpected `NULL` was encountered during decoding.
|
||||
///
|
||||
/// Returned from [`Row::get`] if the value from the database is `NULL`,
|
||||
/// and you are not decoding into an `Option`.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[error("unexpected null; try decoding as an `Option`")]
|
||||
pub struct UnexpectedNullError;
|
||||
|
||||
/// Represents all the ways a method can fail within SQLx.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Error communicating with the database.
|
||||
Io(io::Error),
|
||||
/// Error occurred while parsing a connection string.
|
||||
#[error("error occurred while parsing a connection string: {0}")]
|
||||
ParseConnectOptions(#[source] BoxDynError),
|
||||
|
||||
/// Connection URL was malformed.
|
||||
UrlParse(url::ParseError),
|
||||
|
||||
/// An error was returned by the database.
|
||||
/// Error returned from the database.
|
||||
#[error("error returned from database: {0}")]
|
||||
Database(Box<dyn DatabaseError>),
|
||||
|
||||
/// No row was returned during [`query::Map::fetch_one`] or `QueryAs::fetch_one`.
|
||||
/// Error communicating with the database backend.
|
||||
#[error("error communicating with the server: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
|
||||
/// Error occurred while attempting to establish a TLS connection.
|
||||
#[error("error occurred while attempting to establish a TLS connection: {0}")]
|
||||
Tls(#[source] BoxDynError),
|
||||
|
||||
/// Unexpected or invalid data encountered while communicating with the database.
|
||||
///
|
||||
/// [`query::Map::fetch_one`]: crate::query::Map::fetch_one
|
||||
/// This should indicate there is a programming error in a SQLx driver or there
|
||||
/// is something corrupted with the connection to the database itself.
|
||||
#[error("encountered unexpected or invalid data: {0}")]
|
||||
Protocol(String),
|
||||
|
||||
/// No rows returned by a query that expected to return at least one row.
|
||||
#[error("no rows returned by a query that expected to return at least one row")]
|
||||
RowNotFound,
|
||||
|
||||
/// Column was not found by name in a Row (during [`Row::get`]).
|
||||
///
|
||||
/// [`Row::get`]: crate::row::Row::get
|
||||
ColumnNotFound(Box<str>),
|
||||
|
||||
/// Column index was out of bounds (e.g., asking for column 4 in a 2-column row).
|
||||
/// Column index was out of bounds.
|
||||
#[error("column index out of bounds: the len is {len}, but the index is {index}")]
|
||||
ColumnIndexOutOfBounds { index: usize, len: usize },
|
||||
|
||||
/// Unexpected or invalid data was encountered. This would indicate that we received
|
||||
/// data that we were not expecting or it was in a format we did not understand. This
|
||||
/// generally means either there is a programming error in a SQLx driver or
|
||||
/// something with the connection or the database database itself is corrupted.
|
||||
///
|
||||
/// Context is provided by the included error message.
|
||||
Protocol(Box<str>),
|
||||
/// No column found for the given name.
|
||||
#[error("no column found for name: {0}")]
|
||||
ColumnNotFound(String),
|
||||
|
||||
/// Error occurred while decoding a value from a specific column.
|
||||
#[error("error occurred while decoding column {index}: {source}")]
|
||||
ColumnDecode {
|
||||
index: String,
|
||||
|
||||
#[source]
|
||||
source: BoxDynError,
|
||||
},
|
||||
|
||||
/// Error occurred while decoding a value.
|
||||
#[error("error occurred while decoding: {0}")]
|
||||
Decode(#[source] BoxDynError),
|
||||
|
||||
/// A [`Pool::acquire`] timed out due to connections not becoming available or
|
||||
/// because another task encountered too many errors while trying to open a new connection.
|
||||
///
|
||||
/// [`Pool::acquire`]: crate::pool::Pool::acquire
|
||||
PoolTimedOut(Option<Box<dyn StdError + Send + Sync>>),
|
||||
#[error("pool timed out while waiting for an open connection")]
|
||||
PoolTimedOut,
|
||||
|
||||
/// [`Pool::close`] was called while we were waiting in [`Pool::acquire`].
|
||||
///
|
||||
/// [`Pool::acquire`]: crate::pool::Pool::acquire
|
||||
/// [`Pool::close`]: crate::pool::Pool::close
|
||||
#[error("attempted to acquire a connection on a closed pool")]
|
||||
PoolClosed,
|
||||
|
||||
/// An error occurred while attempting to setup TLS.
|
||||
/// This should only be returned from an explicit ask for TLS.
|
||||
Tls(Box<dyn StdError + Send + Sync>),
|
||||
|
||||
/// An error occurred decoding data received from the database.
|
||||
Decode(Box<dyn StdError + Send + Sync>),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn decode<E>(err: E) -> Self
|
||||
where
|
||||
E: StdError + Send + Sync + 'static,
|
||||
{
|
||||
Error::Decode(err.into())
|
||||
#[inline]
|
||||
pub(crate) fn protocol(err: impl Display) -> Self {
|
||||
Error::Protocol(err.to_string())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn mismatched_types<DB: Database, T>(expected: DB::TypeInfo) -> Self
|
||||
where
|
||||
T: Type<DB>,
|
||||
{
|
||||
let ty_name = type_name::<T>();
|
||||
|
||||
return decode_err!(
|
||||
"mismatched types; Rust type `{}` (as SQL type {}) is not compatible with SQL type {}",
|
||||
ty_name,
|
||||
T::type_info(),
|
||||
expected
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
match self {
|
||||
Error::Io(error) => Some(error),
|
||||
Error::UrlParse(error) => Some(error),
|
||||
Error::PoolTimedOut(Some(error)) => Some(&**error),
|
||||
Error::Decode(error) => Some(&**error),
|
||||
Error::Tls(error) => Some(&**error),
|
||||
Error::Database(error) => Some(error.as_ref_err()),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
// IntellijRust does not understand that [non_exhaustive] applies only for downstream crates
|
||||
// noinspection RsMatchCheck
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Error::Io(error) => write!(f, "{}", error),
|
||||
|
||||
Error::UrlParse(error) => write!(f, "{}", error),
|
||||
|
||||
Error::Decode(error) => write!(f, "{}", error),
|
||||
|
||||
Error::Database(error) => Display::fmt(error, f),
|
||||
|
||||
Error::RowNotFound => f.write_str("found no row when we expected at least one"),
|
||||
|
||||
Error::ColumnNotFound(ref name) => {
|
||||
write!(f, "no column found with the name {:?}", name)
|
||||
}
|
||||
|
||||
Error::ColumnIndexOutOfBounds { index, len } => write!(
|
||||
f,
|
||||
"column index out of bounds: there are {} columns but the index is {}",
|
||||
len, index
|
||||
),
|
||||
|
||||
Error::Protocol(ref err) => f.write_str(err),
|
||||
|
||||
Error::PoolTimedOut(Some(ref err)) => {
|
||||
write!(f, "timed out while waiting for an open connection: {}", err)
|
||||
}
|
||||
|
||||
Error::PoolTimedOut(None) => {
|
||||
write!(f, "timed out while waiting for an open connection")
|
||||
}
|
||||
|
||||
Error::PoolClosed => f.write_str("attempted to acquire a connection on a closed pool"),
|
||||
|
||||
Error::Tls(ref err) => write!(f, "error during TLS upgrade: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
#[inline]
|
||||
fn from(err: io::Error) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::ErrorKind> for Error {
|
||||
#[inline]
|
||||
fn from(err: io::ErrorKind) -> Self {
|
||||
Error::Io(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<url::ParseError> for Error {
|
||||
#[inline]
|
||||
fn from(err: url::ParseError) -> Self {
|
||||
Error::UrlParse(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ProtocolError<'_>> for Error {
|
||||
#[inline]
|
||||
fn from(err: ProtocolError) -> Self {
|
||||
Error::Protocol(err.args.to_string().into_boxed_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UnexpectedNullError> for Error {
|
||||
#[inline]
|
||||
fn from(err: UnexpectedNullError) -> Self {
|
||||
Error::Decode(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "tls")))]
|
||||
impl From<async_native_tls::Error> for Error {
|
||||
#[inline]
|
||||
fn from(err: async_native_tls::Error) -> Self {
|
||||
pub(crate) fn tls(err: impl StdError + Send + Sync + 'static) -> Self {
|
||||
Error::Tls(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TlsError<'_>> for Error {
|
||||
#[inline]
|
||||
fn from(err: TlsError<'_>) -> Self {
|
||||
Error::Tls(err.args.to_string().into())
|
||||
}
|
||||
pub(crate) fn mismatched_types<DB: Database, T>(
|
||||
actual: &DB::TypeInfo,
|
||||
expected: &DB::TypeInfo,
|
||||
) -> BoxDynError {
|
||||
let ty_name = type_name::<T>();
|
||||
|
||||
return format!(
|
||||
"mismatched types; Rust type `{}` (as SQL type `{}`) is not compatible with SQL type `{}`",
|
||||
ty_name, actual, expected
|
||||
)
|
||||
.into();
|
||||
}
|
||||
|
||||
/// An error that was returned by the database.
|
||||
pub trait DatabaseError: StdError + Send + Sync + 'static {
|
||||
/// An error that was returned from the database.
|
||||
pub trait DatabaseError: 'static + Send + Sync + StdError {
|
||||
/// The primary, human-readable error message.
|
||||
fn message(&self) -> &str;
|
||||
|
||||
/// The (SQLSTATE) code for the error.
|
||||
fn code(&self) -> Option<&str> {
|
||||
fn code(&self) -> Option<Cow<str>> {
|
||||
None
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn as_ref_err(&self) -> &(dyn StdError + Send + Sync + 'static);
|
||||
|
||||
#[doc(hidden)]
|
||||
fn as_mut_err(&mut self) -> &mut (dyn StdError + Send + Sync + 'static);
|
||||
|
||||
#[doc(hidden)]
|
||||
fn into_box_err(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static>;
|
||||
}
|
||||
|
||||
impl dyn DatabaseError {
|
||||
/// Downcast this `&dyn DatabaseError` to a specific database error type:
|
||||
///
|
||||
/// * [PgError][crate::postgres::PgError] (if the `postgres` feature is active)
|
||||
/// * [MySqlError][crate::mysql::MySqlError] (if the `mysql` feature is active)
|
||||
/// * [SqliteError][crate::sqlite::SqliteError] (if the `sqlite` feature is active)
|
||||
///
|
||||
/// In a generic context you can use the [crate::database::Database::Error] associated type.
|
||||
///
|
||||
/// ### Panics
|
||||
/// If the type does not match; this is in contrast with [StdError::downcast_ref]
|
||||
/// which returns `Option`. This was a deliberate design decision in favor of brevity as in
|
||||
/// almost all cases you should know which database error type you're expecting.
|
||||
///
|
||||
/// In any other cases, use [Self::try_downcast_ref] instead.
|
||||
pub fn downcast_ref<T: DatabaseError>(&self) -> &T {
|
||||
self.try_downcast_ref::<T>().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"downcasting to wrong DatabaseError type; original error: {:?}",
|
||||
self
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Downcast this `&dyn DatabaseError` to a specific database error type:
|
||||
///
|
||||
/// * [PgError][crate::postgres::PgError] (if the `postgres` feature is active)
|
||||
/// * [MySqlError][crate::mysql::MySqlError] (if the `mysql` feature is active)
|
||||
/// * [SqliteError][crate::sqlite::SqliteError] (if the `sqlite` feature is active)
|
||||
///
|
||||
/// In a generic context you can use the [crate::database::Database::Error] associated type.
|
||||
///
|
||||
/// Returns `None` if the downcast fails (the types do not match)
|
||||
pub fn try_downcast_ref<T: DatabaseError>(&self) -> Option<&T> {
|
||||
self.as_ref_err().downcast_ref()
|
||||
}
|
||||
|
||||
/// Only meant for internal use so no `try_` variant is currently provided
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn downcast_mut<T: DatabaseError>(&mut self) -> &mut T {
|
||||
// tried to express this as the following:
|
||||
//
|
||||
// if let Some(e) = self.as_mut_err().downcast_mut() { return e; }
|
||||
//
|
||||
// however it didn't like using `self` again in the panic format
|
||||
if self.as_ref_err().is::<T>() {
|
||||
return self.as_mut_err().downcast_mut().unwrap();
|
||||
}
|
||||
|
||||
panic!(
|
||||
"downcasting to wrong DatabaseError type; original error: {:?}",
|
||||
self
|
||||
)
|
||||
}
|
||||
|
||||
/// Downcast this `Box<dyn DatabaseError>` to a specific database error type:
|
||||
///
|
||||
/// * [PgError][crate::postgres::PgError] (if the `postgres` feature is active)
|
||||
/// * [MySqlError][crate::mysql::MySqlError] (if the `mysql` feature is active)
|
||||
/// * [SqliteError][crate::sqlite::SqliteError] (if the `sqlite` feature is active)
|
||||
///
|
||||
/// In a generic context you can use the [crate::database::Database::Error] associated type.
|
||||
///
|
||||
/// ### Panics
|
||||
/// If the type does not match; this is in contrast with [std::error::Error::downcast]
|
||||
/// which returns `Result`. This was a deliberate design decision in favor of
|
||||
/// brevity as in almost all cases you should know which database error type you're expecting.
|
||||
///
|
||||
/// In any other cases, use [Self::try_downcast] instead.
|
||||
pub fn downcast<T: DatabaseError>(self: Box<Self>) -> Box<T> {
|
||||
self.try_downcast().unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"downcasting to wrong DatabaseError type; original error: {:?}",
|
||||
e
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Downcast this `Box<dyn DatabaseError>` to a specific database error type:
|
||||
///
|
||||
/// * [PgError][crate::postgres::PgError] (if the `postgres` feature is active)
|
||||
/// * [MySqlError][crate::mysql::MySqlError] (if the `mysql` feature is active)
|
||||
/// * [SqliteError][crate::sqlite::SqliteError] (if the `sqlite` feature is active)
|
||||
///
|
||||
/// In a generic context you can use the [crate::database::Database::Error] associated type.
|
||||
///
|
||||
/// Returns `Err(self)` if the downcast fails (the types do not match).
|
||||
pub fn try_downcast<T: DatabaseError>(
|
||||
self: Box<Self>,
|
||||
) -> std::result::Result<Box<T>, Box<Self>> {
|
||||
if self.as_ref_err().is::<T>() {
|
||||
Ok(self
|
||||
.into_box_err()
|
||||
.downcast()
|
||||
.expect("type mismatch between DatabaseError::as_ref_err() and into_box_err()"))
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used by the `protocol_error!()` macro for a lazily evaluated conversion to
|
||||
/// `crate::Error::Protocol` so we can use the macro with `.ok_or()` without Clippy complaining.
|
||||
pub(crate) struct ProtocolError<'a> {
|
||||
pub args: fmt::Arguments<'a>,
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! protocol_err (
|
||||
($($args:tt)*) => {
|
||||
$crate::error::ProtocolError { args: format_args!($($args)*) }
|
||||
}
|
||||
);
|
||||
|
||||
pub(crate) struct TlsError<'a> {
|
||||
pub args: fmt::Arguments<'a>,
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! tls_err {
|
||||
($($args:tt)*) => { crate::error::TlsError { args: format_args!($($args)*)} };
|
||||
}
|
||||
|
||||
/// An unexpected `NULL` was encountered during decoding.
|
||||
///
|
||||
/// Returned from `Row::get` if the value from the database is `NULL`
|
||||
/// and you are not decoding into an `Option`.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct UnexpectedNullError;
|
||||
|
||||
impl Display for UnexpectedNullError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("unexpected null; try decoding as an `Option`")
|
||||
impl<E> From<E> for Error
|
||||
where
|
||||
E: DatabaseError,
|
||||
{
|
||||
#[inline]
|
||||
fn from(error: E) -> Self {
|
||||
Error::Database(Box::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for UnexpectedNullError {}
|
||||
// Format an error message as a `Protocol` error
|
||||
macro_rules! err_protocol {
|
||||
($expr:expr) => {
|
||||
$crate::error::Error::Protocol($expr.into())
|
||||
};
|
||||
|
||||
($fmt:expr, $($arg:tt)*) => {
|
||||
$crate::error::Error::Protocol(format!($fmt, $($arg)*))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,156 +1,162 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use either::Either;
|
||||
use futures_core::future::BoxFuture;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::{future, FutureExt, StreamExt, TryFutureExt, TryStreamExt};
|
||||
|
||||
use crate::cursor::HasCursor;
|
||||
use crate::database::Database;
|
||||
use crate::database::{Database, HasArguments};
|
||||
use crate::describe::Describe;
|
||||
use crate::error::Error;
|
||||
|
||||
/// A type that contains or can provide a database connection to use for executing queries
|
||||
/// against the database.
|
||||
/// A type that contains or can provide a database
|
||||
/// connection to use for executing queries against the database.
|
||||
///
|
||||
/// No guarantees are provided that successive queries run on the same physical database
|
||||
/// connection. A [`Connection`](trait.Connection.html) is an `Executor` that guarantees that successive
|
||||
/// queries are run on the same physical database connection.
|
||||
/// No guarantees are provided that successive queries run on the same
|
||||
/// physical database connection.
|
||||
///
|
||||
/// Implementations are provided for [`&Pool`](struct.Pool.html),
|
||||
/// [`&mut PoolConnection`](struct.PoolConnection.html),
|
||||
/// and [`&mut Connection`](trait.Connection.html).
|
||||
pub trait Executor
|
||||
where
|
||||
Self: Send,
|
||||
{
|
||||
/// The specific database that this type is implemented for.
|
||||
/// A [`Connection`](crate::connection::Connection) is an `Executor` that guarantees that
|
||||
/// successive queries are ran on the same physical database connection.
|
||||
///
|
||||
/// Implemented for the following:
|
||||
///
|
||||
/// * [`&Pool`]
|
||||
/// * [`&mut PoolConnection`]
|
||||
/// * [`&mut Connection`]
|
||||
///
|
||||
pub trait Executor<'c>: Send + Debug + Sized {
|
||||
type Database: Database;
|
||||
|
||||
/// Executes the query for its side-effects and
|
||||
/// discarding any potential result rows.
|
||||
///
|
||||
/// Returns the number of rows affected, or 0 if not applicable.
|
||||
fn execute<'e, 'q: 'e, 'c: 'e, E: 'e>(
|
||||
&'c mut self,
|
||||
/// Execute the query and return the total number of rows affected.
|
||||
fn execute<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result<u64, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
self.execute_many(query)
|
||||
.try_fold(0, |acc, x| async move { Ok(acc + x) })
|
||||
.boxed()
|
||||
}
|
||||
|
||||
/// Execute multiple queries and return the rows affected from each query, in a stream.
|
||||
fn execute_many<'q: 'c, E>(self, query: E) -> BoxStream<'c, Result<u64, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
self.fetch_many(query)
|
||||
.try_filter_map(|step| async move {
|
||||
Ok(match step {
|
||||
Either::Left(rows) => Some(rows),
|
||||
Either::Right(_) => None,
|
||||
})
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
/// Execute the query and return the generated results as a stream.
|
||||
fn fetch<'q: 'c, E>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'e, crate::Result<u64>>
|
||||
) -> BoxStream<'c, Result<<Self::Database as Database>::Row, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
self.fetch_many(query)
|
||||
.try_filter_map(|step| async move {
|
||||
Ok(match step {
|
||||
Either::Left(_) => None,
|
||||
Either::Right(row) => Some(row),
|
||||
})
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
/// Execute multiple queries and return the generated results as a stream
|
||||
/// from each query, in a stream.
|
||||
fn fetch_many<'q: 'c, E>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxStream<'c, Result<Either<u64, <Self::Database as Database>::Row>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>;
|
||||
|
||||
/// Executes a query for its result.
|
||||
///
|
||||
/// Returns a [`Cursor`] that can be used to iterate through the [`Row`]s
|
||||
/// of the result.
|
||||
///
|
||||
/// [`Cursor`]: crate::cursor::Cursor
|
||||
/// [`Row`]: crate::row::Row
|
||||
fn fetch<'e, 'q, E>(&'e mut self, query: E) -> <Self::Database as HasCursor<'e, 'q>>::Cursor
|
||||
/// Execute the query and return all the generated results, collected into a [`Vec`].
|
||||
fn fetch_all<'q: 'c, E>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'c, Result<Vec<<Self::Database as Database>::Row>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
self.fetch(query).try_collect().boxed()
|
||||
}
|
||||
|
||||
/// Execute the query and returns exactly one row.
|
||||
fn fetch_one<'q: 'c, E>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'c, Result<<Self::Database as Database>::Row, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
self.fetch_optional(query)
|
||||
.and_then(|row| match row {
|
||||
Some(row) => future::ok(row),
|
||||
None => future::err(Error::RowNotFound),
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
/// Execute the query and returns at most one row.
|
||||
fn fetch_optional<'q: 'c, E>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'c, Result<Option<<Self::Database as Database>::Row>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>;
|
||||
|
||||
/// Prepare the SQL query and return type information about its parameters
|
||||
/// and results.
|
||||
///
|
||||
/// This is used by the query macros during compilation to
|
||||
/// This is used by compile-time verification in the query macros to
|
||||
/// power their type inference.
|
||||
#[doc(hidden)]
|
||||
fn describe<'e, 'q, E: 'e>(
|
||||
&'e mut self,
|
||||
fn describe<'q: 'c, E>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>;
|
||||
}
|
||||
|
||||
// HACK: Generic Associated Types (GATs) will enable us to rework how the Executor bound is done
|
||||
// in Query to remove the need for this.
|
||||
pub trait RefExecutor<'e> {
|
||||
type Database: Database;
|
||||
|
||||
fn fetch_by_ref<'q, E>(self, query: E) -> <Self::Database as HasCursor<'e, 'q>>::Cursor
|
||||
) -> BoxFuture<'c, Result<Describe<Self::Database>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>;
|
||||
}
|
||||
|
||||
/// A type that may be executed against a database connection.
|
||||
pub trait Execute<'q, DB>
|
||||
where
|
||||
Self: Send,
|
||||
DB: Database,
|
||||
{
|
||||
/// Returns the query to be executed and the arguments to bind against the query, if any.
|
||||
///
|
||||
/// Implemented for the following:
|
||||
///
|
||||
/// * [`&str`]
|
||||
/// * [`Query`]
|
||||
///
|
||||
pub trait Execute<'q, DB: Database>: Send {
|
||||
/// Returns the query string that will be executed.
|
||||
fn query(&self) -> &'q str;
|
||||
|
||||
/// Returns the arguments to be bound against the query string.
|
||||
///
|
||||
/// Returning `None` for `Arguments` indicates to use a "simple" query protocol and to not
|
||||
/// prepare the query. Returning `Some(Default::default())` is an empty arguments object that
|
||||
/// will be prepared (and cached) before execution.
|
||||
fn into_parts(self) -> (&'q str, Option<DB::Arguments>);
|
||||
|
||||
/// Returns the query string, without any parameters replaced.
|
||||
#[doc(hidden)]
|
||||
fn query_string(&self) -> &'q str;
|
||||
fn take_arguments(&mut self) -> Option<<DB as HasArguments<'q>>::Arguments>;
|
||||
}
|
||||
|
||||
impl<'q, DB> Execute<'q, DB> for &'q str
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
// NOTE: `Execute` is explicitly not implemented for String and &String to make it slightly more
|
||||
// involved to write `conn.execute(format!("SELECT {}", val))`
|
||||
impl<'q, DB: Database> Execute<'q, DB> for &'q str {
|
||||
#[inline]
|
||||
fn into_parts(self) -> (&'q str, Option<DB::Arguments>) {
|
||||
(self, None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
fn query_string(&self) -> &'q str {
|
||||
fn query(&self) -> &'q str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Executor for &'_ mut T
|
||||
where
|
||||
T: Executor,
|
||||
{
|
||||
type Database = T::Database;
|
||||
|
||||
fn execute<'e, 'q: 'e, 'c: 'e, E: 'e>(
|
||||
&'c mut self,
|
||||
query: E,
|
||||
) -> BoxFuture<'e, crate::Result<u64>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
(**self).execute(query)
|
||||
}
|
||||
|
||||
fn fetch<'e, 'q, E>(&'e mut self, query: E) -> <Self::Database as HasCursor<'_, 'q>>::Cursor
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
(**self).fetch(query)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn describe<'e, 'q, E: 'e>(
|
||||
&'e mut self,
|
||||
query: E,
|
||||
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
(**self).describe(query)
|
||||
}
|
||||
}
|
||||
|
||||
// The following impl lets `&mut &Pool` continue to work
|
||||
// This pattern was required in SQLx < 0.3
|
||||
// Going forward users will likely naturally use `&Pool` instead
|
||||
|
||||
impl<'c, T> RefExecutor<'c> for &'c mut T
|
||||
where
|
||||
T: Copy + RefExecutor<'c>,
|
||||
{
|
||||
type Database = T::Database;
|
||||
|
||||
#[inline]
|
||||
fn fetch_by_ref<'q, E>(self, query: E) -> <Self::Database as HasCursor<'c, 'q>>::Cursor
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
(*self).fetch_by_ref(query)
|
||||
fn take_arguments(&mut self) -> Option<<DB as HasArguments<'q>>::Arguments> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
1
sqlx-core/src/ext/mod.rs
Normal file
1
sqlx-core/src/ext/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod ustr;
|
||||
76
sqlx-core/src/ext/ustr.rs
Normal file
76
sqlx-core/src/ext/ustr.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
// U meaning micro
|
||||
// a micro-string is either a reference-counted string or a static string
|
||||
// this guarantees these are cheap to clone everywhere
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
pub(crate) enum UStr {
|
||||
Static(&'static str),
|
||||
Shared(Arc<str>),
|
||||
}
|
||||
|
||||
impl UStr {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn new(s: &str) -> Self {
|
||||
UStr::Shared(Arc::from(s.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for UStr {
|
||||
type Target = str;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &str {
|
||||
match self {
|
||||
UStr::Static(s) => s,
|
||||
UStr::Shared(s) => s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for UStr {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// Forward the hash to the string representation of this
|
||||
// A derive(Hash) encodes the enum discriminant
|
||||
(&**self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for UStr {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &str {
|
||||
&**self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<UStr> for UStr {
|
||||
fn eq(&self, other: &UStr) -> bool {
|
||||
(**self).eq(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for UStr {
|
||||
#[inline]
|
||||
fn from(s: &'static str) -> Self {
|
||||
UStr::Static(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for UStr {
|
||||
#[inline]
|
||||
fn from(s: String) -> Self {
|
||||
UStr::Shared(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for UStr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.pad(self)
|
||||
}
|
||||
}
|
||||
116
sqlx-core/src/from_row.rs
Normal file
116
sqlx-core/src/from_row.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
use crate::error::Error;
|
||||
use crate::row::Row;
|
||||
|
||||
/// A record that can be built from a row returned by the database.
|
||||
///
|
||||
/// In order to use [`query_as`] the output type must implement `FromRow`.
|
||||
///
|
||||
/// # Deriving
|
||||
///
|
||||
/// This trait can be automatically derived by SQLx for any struct. The generated implementation
|
||||
/// will consist of a sequence of calls to [`Row::try_get`] using the name from each
|
||||
/// struct field.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[derive(sqlx::FromRow)]
|
||||
/// struct User {
|
||||
/// id: i32,
|
||||
/// name: String,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`query_as`]: crate::query_as
|
||||
/// [`Row::try_get`]: crate::row::Row::try_get
|
||||
pub trait FromRow<'r, R: Row>: Sized {
|
||||
fn from_row(row: &'r R) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
// implement FromRow for tuples of types that implement Decode
|
||||
// up to tuples of 9 values
|
||||
|
||||
macro_rules! impl_from_row_for_tuple {
|
||||
($( ($idx:tt) -> $T:ident );+;) => {
|
||||
impl<'r, R, $($T,)+> FromRow<'r, R> for ($($T,)+)
|
||||
where
|
||||
R: Row,
|
||||
$($T: crate::decode::Decode<'r, R::Database>,)+
|
||||
{
|
||||
#[inline]
|
||||
fn from_row(row: &'r R) -> Result<Self, Error> {
|
||||
Ok(($(row.try_get($idx as usize)?,)+))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_from_row_for_tuple!(
|
||||
(0) -> T1;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!(
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!(
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!(
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
(3) -> T4;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!(
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
(3) -> T4;
|
||||
(4) -> T5;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!(
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
(3) -> T4;
|
||||
(4) -> T5;
|
||||
(5) -> T6;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!(
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
(3) -> T4;
|
||||
(4) -> T5;
|
||||
(5) -> T6;
|
||||
(6) -> T7;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!(
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
(3) -> T4;
|
||||
(4) -> T5;
|
||||
(5) -> T6;
|
||||
(6) -> T7;
|
||||
(7) -> T8;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!(
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
(3) -> T4;
|
||||
(4) -> T5;
|
||||
(5) -> T6;
|
||||
(6) -> T7;
|
||||
(7) -> T8;
|
||||
(8) -> T9;
|
||||
);
|
||||
@@ -1,144 +1,58 @@
|
||||
use byteorder::ByteOrder;
|
||||
use std::str::from_utf8;
|
||||
|
||||
use bytes::{Buf, Bytes};
|
||||
use memchr::memchr;
|
||||
use std::{io, slice, str};
|
||||
|
||||
pub trait Buf<'a> {
|
||||
fn advance(&mut self, cnt: usize);
|
||||
use crate::error::Error;
|
||||
|
||||
fn get_uint<T: ByteOrder>(&mut self, n: usize) -> io::Result<u64>;
|
||||
pub trait BufExt: Buf {
|
||||
// Read a nul-terminated byte sequence
|
||||
fn get_bytes_nul(&mut self) -> Result<Bytes, Error>;
|
||||
|
||||
fn get_i8(&mut self) -> io::Result<i8>;
|
||||
// Read a byte sequence of the exact length
|
||||
fn get_bytes(&mut self, len: usize) -> Bytes;
|
||||
|
||||
fn get_u8(&mut self) -> io::Result<u8>;
|
||||
// Read a nul-terminated string
|
||||
fn get_str_nul(&mut self) -> Result<String, Error>;
|
||||
|
||||
fn get_u16<T: ByteOrder>(&mut self) -> io::Result<u16>;
|
||||
|
||||
fn get_i16<T: ByteOrder>(&mut self) -> io::Result<i16>;
|
||||
|
||||
fn get_u24<T: ByteOrder>(&mut self) -> io::Result<u32>;
|
||||
|
||||
fn get_i32<T: ByteOrder>(&mut self) -> io::Result<i32>;
|
||||
|
||||
fn get_i64<T: ByteOrder>(&mut self) -> io::Result<i64>;
|
||||
|
||||
fn get_u32<T: ByteOrder>(&mut self) -> io::Result<u32>;
|
||||
|
||||
fn get_u64<T: ByteOrder>(&mut self) -> io::Result<u64>;
|
||||
|
||||
fn get_str(&mut self, len: usize) -> io::Result<&'a str>;
|
||||
|
||||
fn get_str_nul(&mut self) -> io::Result<&'a str>;
|
||||
|
||||
fn get_bytes(&mut self, len: usize) -> io::Result<&'a [u8]>;
|
||||
// Read a string of the exact length
|
||||
fn get_str(&mut self, len: usize) -> Result<String, Error>;
|
||||
}
|
||||
|
||||
impl<'a> Buf<'a> for &'a [u8] {
|
||||
fn advance(&mut self, cnt: usize) {
|
||||
*self = &self[cnt..];
|
||||
impl BufExt for Bytes {
|
||||
fn get_bytes_nul(&mut self) -> Result<Bytes, Error> {
|
||||
let nul = memchr(b'\0', self.bytes())
|
||||
.ok_or_else(|| err_protocol!("expected NUL in byte sequence"))?;
|
||||
|
||||
let v = self.slice(0..nul);
|
||||
|
||||
self.advance(nul + 1);
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn get_uint<T: ByteOrder>(&mut self, n: usize) -> io::Result<u64> {
|
||||
let val = T::read_uint(*self, n);
|
||||
self.advance(n);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn get_i8(&mut self) -> io::Result<i8> {
|
||||
let val = self[0];
|
||||
self.advance(1);
|
||||
|
||||
Ok(val as i8)
|
||||
}
|
||||
|
||||
fn get_u8(&mut self) -> io::Result<u8> {
|
||||
let val = self[0];
|
||||
self.advance(1);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn get_u16<T: ByteOrder>(&mut self) -> io::Result<u16> {
|
||||
let val = T::read_u16(*self);
|
||||
self.advance(2);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn get_i16<T: ByteOrder>(&mut self) -> io::Result<i16> {
|
||||
let val = T::read_i16(*self);
|
||||
self.advance(2);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn get_u24<T: ByteOrder>(&mut self) -> io::Result<u32> {
|
||||
let val = T::read_u24(*self);
|
||||
self.advance(3);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn get_i32<T: ByteOrder>(&mut self) -> io::Result<i32> {
|
||||
let val = T::read_i32(*self);
|
||||
self.advance(4);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn get_i64<T: ByteOrder>(&mut self) -> io::Result<i64> {
|
||||
let val = T::read_i64(*self);
|
||||
self.advance(4);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn get_u32<T: ByteOrder>(&mut self) -> io::Result<u32> {
|
||||
let val = T::read_u32(*self);
|
||||
self.advance(4);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn get_u64<T: ByteOrder>(&mut self) -> io::Result<u64> {
|
||||
let val = T::read_u64(*self);
|
||||
self.advance(8);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn get_str(&mut self, len: usize) -> io::Result<&'a str> {
|
||||
str::from_utf8(self.get_bytes(len)?)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
|
||||
}
|
||||
|
||||
fn get_str_nul(&mut self) -> io::Result<&'a str> {
|
||||
let len = memchr(b'\0', &*self).ok_or(io::ErrorKind::InvalidData)?;
|
||||
let s = &self.get_str(len + 1)?[..len];
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
fn get_bytes(&mut self, len: usize) -> io::Result<&'a [u8]> {
|
||||
let buf = &self[..len];
|
||||
fn get_bytes(&mut self, len: usize) -> Bytes {
|
||||
let v = self.slice(..len);
|
||||
self.advance(len);
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToBuf {
|
||||
fn to_buf(&self) -> &[u8];
|
||||
}
|
||||
|
||||
impl ToBuf for [u8] {
|
||||
fn to_buf(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToBuf for u8 {
|
||||
fn to_buf(&self) -> &[u8] {
|
||||
slice::from_ref(self)
|
||||
v
|
||||
}
|
||||
|
||||
fn get_str_nul(&mut self) -> Result<String, Error> {
|
||||
self.get_bytes_nul().and_then(|bytes| {
|
||||
from_utf8(&*bytes)
|
||||
.map(ToOwned::to_owned)
|
||||
.map_err(|err| err_protocol!("{}", err))
|
||||
})
|
||||
}
|
||||
|
||||
fn get_str(&mut self, len: usize) -> Result<String, Error> {
|
||||
let v = from_utf8(&self[..len])
|
||||
.map_err(|err| err_protocol!("{}", err))
|
||||
.map(ToOwned::to_owned)?;
|
||||
|
||||
self.advance(len);
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,85 +1,12 @@
|
||||
use byteorder::ByteOrder;
|
||||
use std::{str, u16, u32, u8};
|
||||
use bytes::BufMut;
|
||||
|
||||
pub trait BufMut {
|
||||
fn advance(&mut self, cnt: usize);
|
||||
|
||||
fn put_u8(&mut self, val: u8);
|
||||
|
||||
fn put_u16<T: ByteOrder>(&mut self, val: u16);
|
||||
|
||||
fn put_i16<T: ByteOrder>(&mut self, val: i16);
|
||||
|
||||
fn put_u24<T: ByteOrder>(&mut self, val: u32);
|
||||
|
||||
fn put_i32<T: ByteOrder>(&mut self, val: i32);
|
||||
|
||||
fn put_u32<T: ByteOrder>(&mut self, val: u32);
|
||||
|
||||
fn put_u64<T: ByteOrder>(&mut self, val: u64);
|
||||
|
||||
fn put_bytes(&mut self, val: &[u8]);
|
||||
|
||||
fn put_str(&mut self, val: &str);
|
||||
|
||||
fn put_str_nul(&mut self, val: &str);
|
||||
pub trait BufMutExt: BufMut {
|
||||
fn put_str_nul(&mut self, s: &str);
|
||||
}
|
||||
|
||||
impl BufMut for Vec<u8> {
|
||||
fn advance(&mut self, cnt: usize) {
|
||||
self.resize(self.len() + cnt, 0);
|
||||
}
|
||||
|
||||
fn put_u8(&mut self, val: u8) {
|
||||
self.push(val);
|
||||
}
|
||||
|
||||
fn put_u16<T: ByteOrder>(&mut self, val: u16) {
|
||||
let mut buf = [0; 2];
|
||||
T::write_u16(&mut buf, val);
|
||||
self.extend_from_slice(&buf);
|
||||
}
|
||||
|
||||
fn put_i16<T: ByteOrder>(&mut self, val: i16) {
|
||||
let mut buf = [0; 2];
|
||||
T::write_i16(&mut buf, val);
|
||||
self.extend_from_slice(&buf);
|
||||
}
|
||||
|
||||
fn put_u24<T: ByteOrder>(&mut self, val: u32) {
|
||||
let mut buf = [0; 3];
|
||||
T::write_u24(&mut buf, val);
|
||||
self.extend_from_slice(&buf);
|
||||
}
|
||||
|
||||
fn put_i32<T: ByteOrder>(&mut self, val: i32) {
|
||||
let mut buf = [0; 4];
|
||||
T::write_i32(&mut buf, val);
|
||||
self.extend_from_slice(&buf);
|
||||
}
|
||||
|
||||
fn put_u32<T: ByteOrder>(&mut self, val: u32) {
|
||||
let mut buf = [0; 4];
|
||||
T::write_u32(&mut buf, val);
|
||||
self.extend_from_slice(&buf);
|
||||
}
|
||||
|
||||
fn put_u64<T: ByteOrder>(&mut self, val: u64) {
|
||||
let mut buf = [0; 8];
|
||||
T::write_u64(&mut buf, val);
|
||||
self.extend_from_slice(&buf);
|
||||
}
|
||||
|
||||
fn put_bytes(&mut self, val: &[u8]) {
|
||||
self.extend_from_slice(val);
|
||||
}
|
||||
|
||||
fn put_str(&mut self, val: &str) {
|
||||
self.extend_from_slice(val.as_bytes());
|
||||
}
|
||||
|
||||
fn put_str_nul(&mut self, val: &str) {
|
||||
self.put_str(val);
|
||||
impl BufMutExt for Vec<u8> {
|
||||
fn put_str_nul(&mut self, s: &str) {
|
||||
self.extend(s.as_bytes());
|
||||
self.push(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
use std::future::Future;
|
||||
use std::io::{self, BufRead};
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::io;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use futures_util::ready;
|
||||
use bytes::BytesMut;
|
||||
use sqlx_rt::{AsyncRead, AsyncReadExt, AsyncWrite};
|
||||
|
||||
use crate::runtime::{AsyncRead, AsyncReadExt, AsyncWrite};
|
||||
use crate::error::Error;
|
||||
use crate::io::write_and_flush::WriteAndFlush;
|
||||
use crate::io::{decode::Decode, encode::Encode};
|
||||
use std::io::Cursor;
|
||||
|
||||
const RBUF_SIZE: usize = 8 * 1024;
|
||||
pub struct BufStream<S>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
stream: S,
|
||||
|
||||
pub struct BufStream<S> {
|
||||
pub(crate) stream: S,
|
||||
// writes with `write` to the underlying stream are buffered
|
||||
// this can be flushed with `flush`
|
||||
pub(crate) wbuf: Vec<u8>,
|
||||
|
||||
// Have we reached end-of-file (been disconnected)
|
||||
stream_eof: bool,
|
||||
|
||||
// Buffer used when sending outgoing messages
|
||||
wbuf: Vec<u8>,
|
||||
|
||||
// Buffer used when reading incoming messages
|
||||
rbuf: Vec<u8>,
|
||||
rbuf_rindex: usize,
|
||||
rbuf_windex: usize,
|
||||
}
|
||||
|
||||
pub struct GuardedFlush<'a, S: 'a> {
|
||||
stream: &'a mut S,
|
||||
buf: io::Cursor<&'a mut Vec<u8>>,
|
||||
// we read into the read buffer using 100% safe code
|
||||
rbuf: BytesMut,
|
||||
}
|
||||
|
||||
impl<S> BufStream<S>
|
||||
@@ -37,109 +32,72 @@ where
|
||||
pub fn new(stream: S) -> Self {
|
||||
Self {
|
||||
stream,
|
||||
stream_eof: false,
|
||||
wbuf: Vec::with_capacity(1024),
|
||||
rbuf: vec![0; RBUF_SIZE],
|
||||
rbuf_rindex: 0,
|
||||
rbuf_windex: 0,
|
||||
wbuf: Vec::with_capacity(512),
|
||||
rbuf: BytesMut::with_capacity(4096),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
#[inline]
|
||||
pub fn buffer<'c>(&'c self) -> &'c [u8] {
|
||||
&self.rbuf[self.rbuf_rindex..]
|
||||
pub fn write<'en, T>(&mut self, value: T)
|
||||
where
|
||||
T: Encode<'en, ()>,
|
||||
{
|
||||
self.write_with(value, ())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn buffer_mut(&mut self) -> &mut Vec<u8> {
|
||||
&mut self.wbuf
|
||||
pub fn write_with<'en, T, C>(&mut self, value: T, context: C)
|
||||
where
|
||||
T: Encode<'en, C>,
|
||||
{
|
||||
value.encode_with(&mut self.wbuf, context);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use = "write buffer is cleared on-drop even if future is not polled"]
|
||||
pub fn flush(&mut self) -> GuardedFlush<S> {
|
||||
GuardedFlush {
|
||||
pub fn flush(&mut self) -> WriteAndFlush<'_, S> {
|
||||
WriteAndFlush {
|
||||
stream: &mut self.stream,
|
||||
buf: io::Cursor::new(&mut self.wbuf),
|
||||
buf: Cursor::new(&mut self.wbuf),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn consume(&mut self, cnt: usize) {
|
||||
self.rbuf_rindex += cnt;
|
||||
pub async fn read<'de, T>(&mut self, cnt: usize) -> Result<T, Error>
|
||||
where
|
||||
T: Decode<'de, ()>,
|
||||
{
|
||||
self.read_with(cnt, ()).await
|
||||
}
|
||||
|
||||
pub async fn peek(&mut self, cnt: usize) -> io::Result<&[u8]> {
|
||||
self.try_peek(cnt)
|
||||
.await
|
||||
.transpose()
|
||||
.ok_or(io::ErrorKind::ConnectionAborted)?
|
||||
}
|
||||
pub async fn read_with<'de, T, C>(&mut self, cnt: usize, context: C) -> Result<T, Error>
|
||||
where
|
||||
T: Decode<'de, C>,
|
||||
{
|
||||
// zero-fills the space in the read buffer
|
||||
self.rbuf.resize(cnt, 0);
|
||||
|
||||
pub async fn try_peek(&mut self, cnt: usize) -> io::Result<Option<&[u8]>> {
|
||||
loop {
|
||||
// Reaching end-of-file (read 0 bytes) will continuously
|
||||
// return None from all future calls to read
|
||||
if self.stream_eof {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// If we have enough bytes in our read buffer,
|
||||
// return immediately
|
||||
if self.rbuf_windex >= (self.rbuf_rindex + cnt) {
|
||||
let buf = &self.rbuf[self.rbuf_rindex..(self.rbuf_rindex + cnt)];
|
||||
|
||||
return Ok(Some(buf));
|
||||
}
|
||||
|
||||
// If we are out of space to write to in the read buffer ..
|
||||
if self.rbuf.len() < (self.rbuf_windex + cnt) {
|
||||
if self.rbuf_rindex == self.rbuf_windex {
|
||||
// We have consumed all data; simply reset the indexes
|
||||
self.rbuf_rindex = 0;
|
||||
self.rbuf_windex = 0;
|
||||
} else {
|
||||
// Allocate a new buffer
|
||||
let mut new_rbuf = Vec::with_capacity(RBUF_SIZE);
|
||||
|
||||
// Take the minimum of the read and write indexes
|
||||
let min_index = self.rbuf_rindex.min(self.rbuf_windex);
|
||||
|
||||
// Copy the old buffer to our new buffer
|
||||
new_rbuf.extend_from_slice(&self.rbuf[min_index..]);
|
||||
|
||||
// Zero-extend the new buffer
|
||||
new_rbuf.resize(new_rbuf.capacity(), 0);
|
||||
|
||||
// Replace the old buffer with our new buffer
|
||||
self.rbuf = new_rbuf;
|
||||
|
||||
// And reduce the indexes
|
||||
self.rbuf_rindex -= min_index;
|
||||
self.rbuf_windex -= min_index;
|
||||
}
|
||||
|
||||
// Do we need more space still
|
||||
if self.rbuf.len() < (self.rbuf_windex + cnt) {
|
||||
let needed = (self.rbuf_windex + cnt) - self.rbuf.len();
|
||||
|
||||
self.rbuf.resize(self.rbuf.len() + needed, 0);
|
||||
}
|
||||
}
|
||||
|
||||
let n = self.stream.read(&mut self.rbuf[self.rbuf_windex..]).await?;
|
||||
|
||||
self.rbuf_windex += n;
|
||||
let mut read = 0;
|
||||
while cnt > read {
|
||||
// read in bytes from the stream into the read buffer starting
|
||||
// from the offset we last read from
|
||||
let n = self.stream.read(&mut self.rbuf[read..]).await?;
|
||||
|
||||
if n == 0 {
|
||||
self.stream_eof = true;
|
||||
// a zero read when we had space in the read buffer
|
||||
// should be treated as an EOF
|
||||
|
||||
// and an unexpected EOF means the server told us to go away
|
||||
|
||||
return Err(io::Error::from(io::ErrorKind::ConnectionAborted).into());
|
||||
}
|
||||
|
||||
read += n;
|
||||
}
|
||||
|
||||
T::decode_with(self.rbuf.split_to(cnt).freeze(), context)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Deref for BufStream<S> {
|
||||
impl<S> Deref for BufStream<S>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
type Target = S;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@@ -147,53 +105,11 @@ impl<S> Deref for BufStream<S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DerefMut for BufStream<S> {
|
||||
impl<S> DerefMut for BufStream<S>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.stream
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Find a nicer way to do this
|
||||
// Return `Ok(None)` immediately from a function if the wrapped value is `None`
|
||||
#[allow(unused)]
|
||||
macro_rules! ret_if_none {
|
||||
($val:expr) => {
|
||||
match $val {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<'a, S: AsyncWrite + Unpin> Future for GuardedFlush<'a, S> {
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let Self {
|
||||
ref mut stream,
|
||||
ref mut buf,
|
||||
} = *self;
|
||||
|
||||
loop {
|
||||
let read = buf.fill_buf()?;
|
||||
|
||||
if !read.is_empty() {
|
||||
let written = ready!(Pin::new(&mut *stream).poll_write(cx, read)?);
|
||||
buf.consume(written);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Pin::new(stream).poll_flush(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> Drop for GuardedFlush<'a, S> {
|
||||
fn drop(&mut self) {
|
||||
// clear the buffer regardless of whether the flush succeeded or not
|
||||
self.buf.get_mut().clear();
|
||||
}
|
||||
}
|
||||
|
||||
29
sqlx-core/src/io/decode.rs
Normal file
29
sqlx-core/src/io/decode.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use bytes::Bytes;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
pub trait Decode<'de, Context = ()>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
fn decode(buf: Bytes) -> Result<Self, Error>
|
||||
where
|
||||
Self: Decode<'de, ()>,
|
||||
{
|
||||
Self::decode_with(buf, ())
|
||||
}
|
||||
|
||||
fn decode_with(buf: Bytes, context: Context) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
impl Decode<'_> for Bytes {
|
||||
fn decode_with(buf: Bytes, _: ()) -> Result<Self, Error> {
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<'_> for () {
|
||||
fn decode_with(_: Bytes, _: ()) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
16
sqlx-core/src/io/encode.rs
Normal file
16
sqlx-core/src/io/encode.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
pub trait Encode<'en, Context = ()> {
|
||||
fn encode(&self, buf: &mut Vec<u8>)
|
||||
where
|
||||
Self: Encode<'en, ()>,
|
||||
{
|
||||
self.encode_with(buf, ());
|
||||
}
|
||||
|
||||
fn encode_with(&self, buf: &mut Vec<u8>, context: Context);
|
||||
}
|
||||
|
||||
impl<'en, C> Encode<'en, C> for &'_ [u8] {
|
||||
fn encode_with(&self, buf: &mut Vec<u8>, _: C) {
|
||||
buf.extend_from_slice(self);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,12 @@
|
||||
#[macro_use]
|
||||
mod buf_stream;
|
||||
|
||||
mod buf;
|
||||
mod buf_mut;
|
||||
mod byte_str;
|
||||
mod tls;
|
||||
mod buf_stream;
|
||||
mod decode;
|
||||
mod encode;
|
||||
mod write_and_flush;
|
||||
|
||||
pub use self::{
|
||||
buf::{Buf, ToBuf},
|
||||
buf_mut::BufMut,
|
||||
buf_stream::BufStream,
|
||||
byte_str::ByteStr,
|
||||
tls::MaybeTlsStream,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
#[doc(hidden)]
|
||||
macro_rules! bytes (
|
||||
($($b: expr), *) => {{
|
||||
use $crate::io::ToBuf;
|
||||
|
||||
let mut buf = Vec::new();
|
||||
$(
|
||||
buf.extend_from_slice($b.to_buf());
|
||||
)*
|
||||
|
||||
buf
|
||||
}}
|
||||
);
|
||||
pub use buf::BufExt;
|
||||
pub use buf_mut::BufMutExt;
|
||||
pub use buf_stream::BufStream;
|
||||
pub use decode::Decode;
|
||||
pub use encode::Encode;
|
||||
|
||||
45
sqlx-core/src/io/write_and_flush.rs
Normal file
45
sqlx-core/src/io/write_and_flush.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use crate::error::Error;
|
||||
use futures_core::Future;
|
||||
use futures_util::ready;
|
||||
use sqlx_rt::AsyncWrite;
|
||||
use std::io::{BufRead, Cursor};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
// Atomic operation that writes the full buffer to the stream, flushes the stream, and then
|
||||
// clears the buffer (even if either of the two previous operations failed).
|
||||
pub struct WriteAndFlush<'a, S: 'a> {
|
||||
pub(super) stream: &'a mut S,
|
||||
pub(super) buf: Cursor<&'a mut Vec<u8>>,
|
||||
}
|
||||
|
||||
impl<'a, S: AsyncWrite + Unpin> Future for WriteAndFlush<'a, S> {
|
||||
type Output = Result<(), Error>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let Self {
|
||||
ref mut stream,
|
||||
ref mut buf,
|
||||
} = *self;
|
||||
|
||||
loop {
|
||||
let read = buf.fill_buf()?;
|
||||
|
||||
if !read.is_empty() {
|
||||
let written = ready!(Pin::new(&mut *stream).poll_write(cx, read)?);
|
||||
buf.consume(written);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Pin::new(stream).poll_flush(cx).map_err(Error::Io)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> Drop for WriteAndFlush<'a, S> {
|
||||
fn drop(&mut self) {
|
||||
// clear the buffer regardless of whether the flush succeeded or not
|
||||
self.buf.get_mut().clear();
|
||||
}
|
||||
}
|
||||
@@ -1,68 +1,42 @@
|
||||
//! Core of SQLx, the rust SQL toolkit. Not intended to be used directly.
|
||||
|
||||
//! Core of SQLx, the rust SQL toolkit.
|
||||
//! Not intended to be used directly.
|
||||
#![recursion_limit = "512"]
|
||||
//
|
||||
// Allows an API be documented as only available in some specific platforms.
|
||||
// <https://doc.rust-lang.org/unstable-book/language-features/doc-cfg.html>
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
//
|
||||
// When compiling with support for SQLite we must allow some unsafe code in order to
|
||||
// interface with the inherently unsafe C module. This unsafe code is contained
|
||||
// to the sqlite module.
|
||||
#![cfg_attr(feature = "sqlite", deny(unsafe_code))]
|
||||
#![cfg_attr(not(feature = "sqlite"), forbid(unsafe_code))]
|
||||
#![recursion_limit = "512"]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(all(test, feature = "bench"), feature(test))]
|
||||
// #![warn(missing_docs)]
|
||||
|
||||
#[cfg(all(test, feature = "bench"))]
|
||||
extern crate test;
|
||||
|
||||
// HACK: Allow a feature name the same name as a dependency
|
||||
#[cfg(feature = "bigdecimal")]
|
||||
extern crate bigdecimal_ as bigdecimal;
|
||||
|
||||
mod runtime;
|
||||
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
|
||||
#[cfg(any(feature = "mysql", feature = "postgres"))]
|
||||
#[macro_use]
|
||||
mod io;
|
||||
|
||||
pub mod connection;
|
||||
pub mod cursor;
|
||||
pub mod database;
|
||||
pub mod value;
|
||||
|
||||
#[macro_use]
|
||||
pub mod executor;
|
||||
|
||||
pub mod transaction;
|
||||
mod url;
|
||||
|
||||
#[macro_use]
|
||||
pub mod arguments;
|
||||
pub mod connection;
|
||||
pub mod database;
|
||||
pub mod decode;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod describe;
|
||||
|
||||
pub mod encode;
|
||||
pub mod executor;
|
||||
mod ext;
|
||||
pub mod from_row;
|
||||
mod io;
|
||||
mod net;
|
||||
pub mod pool;
|
||||
pub mod query;
|
||||
|
||||
#[macro_use]
|
||||
pub mod query_as;
|
||||
|
||||
pub mod types;
|
||||
|
||||
#[macro_use]
|
||||
pub mod query_scalar;
|
||||
pub mod row;
|
||||
|
||||
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
|
||||
#[macro_use]
|
||||
mod logging;
|
||||
|
||||
#[cfg(feature = "mysql")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "mysql")))]
|
||||
pub mod mysql;
|
||||
pub mod type_info;
|
||||
pub mod types;
|
||||
pub mod value;
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "postgres")))]
|
||||
@@ -72,4 +46,6 @@ pub mod postgres;
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))]
|
||||
pub mod sqlite;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
#[cfg(feature = "mysql")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "mysql")))]
|
||||
pub mod mysql;
|
||||
|
||||
5
sqlx-core/src/net/mod.rs
Normal file
5
sqlx-core/src/net/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod socket;
|
||||
mod tls;
|
||||
|
||||
pub use socket::Socket;
|
||||
pub use tls::MaybeTlsStream;
|
||||
140
sqlx-core/src/net/socket.rs
Normal file
140
sqlx-core/src/net/socket.rs
Normal file
@@ -0,0 +1,140 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::io;
|
||||
use std::net::Shutdown;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use sqlx_rt::{AsyncRead, AsyncWrite, TcpStream};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Socket {
|
||||
Tcp(TcpStream),
|
||||
|
||||
#[cfg(unix)]
|
||||
Unix(sqlx_rt::UnixStream),
|
||||
}
|
||||
|
||||
impl Socket {
|
||||
#[cfg(not(unix))]
|
||||
pub async fn connect(host: &str, port: u16) -> io::Result<Self> {
|
||||
TcpStream::connect((host, port)).await.map(Socket::Tcp)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub async fn connect(host: &str, port: u16) -> io::Result<Self> {
|
||||
if host.starts_with('/') {
|
||||
// if the host starts with a forward slash, assume that this is a request
|
||||
// to connect to a local socket
|
||||
sqlx_rt::UnixStream::connect(format!("{}/.s.PGSQL.{}", host, port))
|
||||
.await
|
||||
.map(Socket::Unix)
|
||||
} else {
|
||||
TcpStream::connect((host, port)).await.map(Socket::Tcp)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shutdown(&self) -> io::Result<()> {
|
||||
match self {
|
||||
Socket::Tcp(s) => s.shutdown(Shutdown::Both),
|
||||
|
||||
#[cfg(unix)]
|
||||
Socket::Unix(s) => s.shutdown(Shutdown::Both),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Socket {
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
match &mut *self {
|
||||
Socket::Tcp(s) => Pin::new(s).poll_read(cx, buf),
|
||||
|
||||
#[cfg(unix)]
|
||||
Socket::Unix(s) => Pin::new(s).poll_read(cx, buf),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "runtime-actix", feature = "runtime-tokio"))]
|
||||
fn poll_read_buf<B>(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut B,
|
||||
) -> Poll<io::Result<usize>>
|
||||
where
|
||||
Self: Sized,
|
||||
B: bytes::BufMut,
|
||||
{
|
||||
match &mut *self {
|
||||
Socket::Tcp(s) => Pin::new(s).poll_read_buf(cx, buf),
|
||||
|
||||
#[cfg(unix)]
|
||||
Socket::Unix(s) => Pin::new(s).poll_read_buf(cx, buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Socket {
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
match &mut *self {
|
||||
Socket::Tcp(s) => Pin::new(s).poll_write(cx, buf),
|
||||
|
||||
#[cfg(unix)]
|
||||
Socket::Unix(s) => Pin::new(s).poll_write(cx, buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
match &mut *self {
|
||||
Socket::Tcp(s) => Pin::new(s).poll_flush(cx),
|
||||
|
||||
#[cfg(unix)]
|
||||
Socket::Unix(s) => Pin::new(s).poll_flush(cx),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "runtime-actix", feature = "runtime-tokio"))]
|
||||
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
match &mut *self {
|
||||
Socket::Tcp(s) => Pin::new(s).poll_shutdown(cx),
|
||||
|
||||
#[cfg(unix)]
|
||||
Socket::Unix(s) => Pin::new(s).poll_shutdown(cx),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-async-std")]
|
||||
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
match &mut *self {
|
||||
Socket::Tcp(s) => Pin::new(s).poll_close(cx),
|
||||
|
||||
#[cfg(unix)]
|
||||
Socket::Unix(s) => Pin::new(s).poll_close(cx),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "runtime-actix", feature = "runtime-tokio"))]
|
||||
fn poll_write_buf<B>(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut B,
|
||||
) -> Poll<io::Result<usize>>
|
||||
where
|
||||
Self: Sized,
|
||||
B: bytes::Buf,
|
||||
{
|
||||
match &mut *self {
|
||||
Socket::Tcp(s) => Pin::new(s).poll_write_buf(cx, buf),
|
||||
|
||||
#[cfg(unix)]
|
||||
Socket::Unix(s) => Pin::new(s).poll_write_buf(cx, buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
197
sqlx-core/src/net/tls.rs
Normal file
197
sqlx-core/src/net/tls.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::io;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use sqlx_rt::{AsyncRead, AsyncWrite, TlsConnector, TlsStream};
|
||||
|
||||
use crate::error::Error;
|
||||
use std::mem::replace;
|
||||
|
||||
pub enum MaybeTlsStream<S>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
Raw(S),
|
||||
Tls(TlsStream<S>),
|
||||
Upgrading,
|
||||
}
|
||||
|
||||
impl<S> MaybeTlsStream<S>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
#[inline]
|
||||
pub fn is_tls(&self) -> bool {
|
||||
matches!(self, Self::Tls(_))
|
||||
}
|
||||
|
||||
pub async fn upgrade(&mut self, host: &str, connector: TlsConnector) -> Result<(), Error> {
|
||||
let stream = match replace(self, MaybeTlsStream::Upgrading) {
|
||||
MaybeTlsStream::Raw(stream) => stream,
|
||||
|
||||
MaybeTlsStream::Tls(_) => {
|
||||
// ignore upgrade, we are already a TLS connection
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
MaybeTlsStream::Upgrading => {
|
||||
// we previously failed to upgrade and now hold no connection
|
||||
// this should only happen from an internal misuse of this method
|
||||
return Err(Error::Io(io::ErrorKind::ConnectionAborted.into()));
|
||||
}
|
||||
};
|
||||
|
||||
*self = MaybeTlsStream::Tls(
|
||||
connector
|
||||
.connect(host, stream)
|
||||
.await
|
||||
.map_err(|err| Error::Tls(err.into()))?,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> AsyncRead for MaybeTlsStream<S>
|
||||
where
|
||||
S: Unpin + AsyncWrite + AsyncRead,
|
||||
{
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
match &mut *self {
|
||||
MaybeTlsStream::Raw(s) => Pin::new(s).poll_read(cx, buf),
|
||||
MaybeTlsStream::Tls(s) => Pin::new(s).poll_read(cx, buf),
|
||||
|
||||
MaybeTlsStream::Upgrading => Poll::Ready(Err(io::ErrorKind::ConnectionAborted.into())),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "runtime-actix", feature = "runtime-tokio"))]
|
||||
fn poll_read_buf<B>(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut B,
|
||||
) -> Poll<io::Result<usize>>
|
||||
where
|
||||
Self: Sized,
|
||||
B: bytes::BufMut,
|
||||
{
|
||||
match &mut *self {
|
||||
MaybeTlsStream::Raw(s) => Pin::new(s).poll_read_buf(cx, buf),
|
||||
MaybeTlsStream::Tls(s) => Pin::new(s).poll_read_buf(cx, buf),
|
||||
|
||||
MaybeTlsStream::Upgrading => Poll::Ready(Err(io::ErrorKind::ConnectionAborted.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> AsyncWrite for MaybeTlsStream<S>
|
||||
where
|
||||
S: Unpin + AsyncWrite + AsyncRead,
|
||||
{
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
match &mut *self {
|
||||
MaybeTlsStream::Raw(s) => Pin::new(s).poll_write(cx, buf),
|
||||
MaybeTlsStream::Tls(s) => Pin::new(s).poll_write(cx, buf),
|
||||
|
||||
MaybeTlsStream::Upgrading => Poll::Ready(Err(io::ErrorKind::ConnectionAborted.into())),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
match &mut *self {
|
||||
MaybeTlsStream::Raw(s) => Pin::new(s).poll_flush(cx),
|
||||
MaybeTlsStream::Tls(s) => Pin::new(s).poll_flush(cx),
|
||||
|
||||
MaybeTlsStream::Upgrading => Poll::Ready(Err(io::ErrorKind::ConnectionAborted.into())),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "runtime-actix", feature = "runtime-tokio"))]
|
||||
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
match &mut *self {
|
||||
MaybeTlsStream::Raw(s) => Pin::new(s).poll_shutdown(cx),
|
||||
MaybeTlsStream::Tls(s) => Pin::new(s).poll_shutdown(cx),
|
||||
|
||||
MaybeTlsStream::Upgrading => Poll::Ready(Err(io::ErrorKind::ConnectionAborted.into())),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-async-std")]
|
||||
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
match &mut *self {
|
||||
MaybeTlsStream::Raw(s) => Pin::new(s).poll_close(cx),
|
||||
MaybeTlsStream::Tls(s) => Pin::new(s).poll_close(cx),
|
||||
|
||||
MaybeTlsStream::Upgrading => Poll::Ready(Err(io::ErrorKind::ConnectionAborted.into())),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "runtime-actix", feature = "runtime-tokio"))]
|
||||
fn poll_write_buf<B>(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut B,
|
||||
) -> Poll<io::Result<usize>>
|
||||
where
|
||||
Self: Sized,
|
||||
B: bytes::Buf,
|
||||
{
|
||||
match &mut *self {
|
||||
MaybeTlsStream::Raw(s) => Pin::new(s).poll_write_buf(cx, buf),
|
||||
MaybeTlsStream::Tls(s) => Pin::new(s).poll_write_buf(cx, buf),
|
||||
|
||||
MaybeTlsStream::Upgrading => Poll::Ready(Err(io::ErrorKind::ConnectionAborted.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Deref for MaybeTlsStream<S>
|
||||
where
|
||||
S: Unpin + AsyncWrite + AsyncRead,
|
||||
{
|
||||
type Target = S;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
MaybeTlsStream::Raw(s) => s,
|
||||
|
||||
#[cfg(not(feature = "runtime-async-std"))]
|
||||
MaybeTlsStream::Tls(s) => s.get_ref().get_ref().get_ref(),
|
||||
|
||||
#[cfg(feature = "runtime-async-std")]
|
||||
MaybeTlsStream::Tls(s) => s.get_ref(),
|
||||
|
||||
MaybeTlsStream::Upgrading => panic!(io::Error::from(io::ErrorKind::ConnectionAborted)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DerefMut for MaybeTlsStream<S>
|
||||
where
|
||||
S: Unpin + AsyncWrite + AsyncRead,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
MaybeTlsStream::Raw(s) => s,
|
||||
|
||||
#[cfg(not(feature = "runtime-async-std"))]
|
||||
MaybeTlsStream::Tls(s) => s.get_mut().get_mut().get_mut(),
|
||||
|
||||
#[cfg(feature = "runtime-async-std")]
|
||||
MaybeTlsStream::Tls(s) => s.get_mut(),
|
||||
|
||||
MaybeTlsStream::Upgrading => panic!(io::Error::from(io::ErrorKind::ConnectionAborted)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
use futures_core::future::BoxFuture;
|
||||
use std::borrow::{Borrow, BorrowMut};
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use futures_core::future::BoxFuture;
|
||||
|
||||
use super::inner::{DecrementSizeGuard, SharedPool};
|
||||
use crate::connection::{Connect, Connection};
|
||||
use crate::error::Error;
|
||||
|
||||
/// A connection checked out from [`Pool`][crate::pool::Pool].
|
||||
///
|
||||
@@ -36,6 +39,13 @@ pub(super) struct Floating<'p, C> {
|
||||
|
||||
const DEREF_ERR: &str = "(bug) connection already released to pool";
|
||||
|
||||
impl<C: Connect> Debug for PoolConnection<C> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
// TODO: Show the type name of the connection ?
|
||||
f.debug_struct("PoolConnection").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Borrow<C> for PoolConnection<C>
|
||||
where
|
||||
C: Connect,
|
||||
@@ -78,7 +88,9 @@ impl<C> Connection for PoolConnection<C>
|
||||
where
|
||||
C: Connect,
|
||||
{
|
||||
fn close(mut self) -> BoxFuture<'static, crate::Result<()>> {
|
||||
type Database = C::Database;
|
||||
|
||||
fn close(mut self) -> BoxFuture<'static, Result<(), Error>> {
|
||||
Box::pin(async move {
|
||||
let live = self.live.take().expect("PoolConnection double-dropped");
|
||||
live.float(&self.pool).into_idle().close().await
|
||||
@@ -86,7 +98,7 @@ where
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ping(&mut self) -> BoxFuture<crate::Result<()>> {
|
||||
fn ping(&mut self) -> BoxFuture<Result<(), Error>> {
|
||||
Box::pin(self.deref_mut().ping())
|
||||
}
|
||||
}
|
||||
@@ -185,7 +197,7 @@ impl<'s, C> Floating<'s, Idle<C>> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn ping(&mut self) -> crate::Result<()>
|
||||
pub async fn ping(&mut self) -> Result<(), Error>
|
||||
where
|
||||
C: Connection,
|
||||
{
|
||||
@@ -199,7 +211,7 @@ impl<'s, C> Floating<'s, Idle<C>> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn close(self) -> crate::Result<()>
|
||||
pub async fn close(self) -> Result<(), Error>
|
||||
where
|
||||
C: Connection,
|
||||
{
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
use futures_core::future::BoxFuture;
|
||||
|
||||
use super::PoolConnection;
|
||||
use crate::connection::Connect;
|
||||
use crate::cursor::{Cursor, HasCursor};
|
||||
use crate::database::Database;
|
||||
use crate::describe::Describe;
|
||||
use crate::executor::Execute;
|
||||
use crate::executor::{Executor, RefExecutor};
|
||||
use crate::pool::Pool;
|
||||
|
||||
impl<'p, C, DB> Executor for &'p Pool<C>
|
||||
where
|
||||
C: Connect<Database = DB>,
|
||||
DB: Database<Connection = C>,
|
||||
DB: for<'c, 'q> HasCursor<'c, 'q, Database = DB>,
|
||||
{
|
||||
type Database = DB;
|
||||
|
||||
fn execute<'e, 'q: 'e, 'c: 'e, E: 'e>(
|
||||
&'c mut self,
|
||||
query: E,
|
||||
) -> BoxFuture<'e, crate::Result<u64>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
Box::pin(async move { self.acquire().await?.execute(query).await })
|
||||
}
|
||||
|
||||
fn fetch<'e, 'q, E>(&'e mut self, query: E) -> <Self::Database as HasCursor<'_, 'q>>::Cursor
|
||||
where
|
||||
E: Execute<'q, DB>,
|
||||
{
|
||||
DB::Cursor::from_pool(self, query)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn describe<'e, 'q, E: 'e>(
|
||||
&'e mut self,
|
||||
query: E,
|
||||
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
Box::pin(async move { self.acquire().await?.describe(query).await })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, C, DB> RefExecutor<'p> for &'p Pool<C>
|
||||
where
|
||||
C: Connect<Database = DB>,
|
||||
DB: Database<Connection = C>,
|
||||
DB: for<'c, 'q> HasCursor<'c, 'q>,
|
||||
for<'c> &'c mut C: RefExecutor<'c>,
|
||||
{
|
||||
type Database = DB;
|
||||
|
||||
fn fetch_by_ref<'q, E>(self, query: E) -> <Self::Database as HasCursor<'p, 'q>>::Cursor
|
||||
where
|
||||
E: Execute<'q, DB>,
|
||||
{
|
||||
DB::Cursor::from_pool(self, query)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Executor for PoolConnection<C>
|
||||
where
|
||||
C: Connect,
|
||||
{
|
||||
type Database = C::Database;
|
||||
|
||||
fn execute<'e, 'q: 'e, 'c: 'e, E: 'e>(
|
||||
&'c mut self,
|
||||
query: E,
|
||||
) -> BoxFuture<'e, crate::Result<u64>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
(**self).execute(query)
|
||||
}
|
||||
|
||||
fn fetch<'e, 'q, E>(&'e mut self, query: E) -> <C::Database as HasCursor<'_, 'q>>::Cursor
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
(**self).fetch(query)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn describe<'e, 'q, E: 'e>(
|
||||
&'e mut self,
|
||||
query: E,
|
||||
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
(**self).describe(query)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, C, DB> RefExecutor<'c> for &'c mut PoolConnection<C>
|
||||
where
|
||||
C: Connect<Database = DB>,
|
||||
DB: Database<Connection = C>,
|
||||
DB: for<'c2, 'q> HasCursor<'c2, 'q, Database = DB>,
|
||||
&'c mut C: RefExecutor<'c, Database = DB>,
|
||||
{
|
||||
type Database = DB;
|
||||
|
||||
fn fetch_by_ref<'q, E>(self, query: E) -> <Self::Database as HasCursor<'c, 'q>>::Cursor
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
(**self).fetch(query)
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,11 @@ use std::time::Instant;
|
||||
use crossbeam_queue::{ArrayQueue, SegQueue};
|
||||
use futures_core::task::{Poll, Waker};
|
||||
use futures_util::future;
|
||||
use sqlx_rt::{sleep, spawn, timeout};
|
||||
|
||||
use crate::connection::{Connect, Connection};
|
||||
use crate::error::Error;
|
||||
use crate::pool::deadline_as_timeout;
|
||||
use crate::runtime::{sleep, spawn, timeout};
|
||||
use crate::{
|
||||
connection::{Connect, Connection},
|
||||
error::Error,
|
||||
};
|
||||
|
||||
use super::connection::{Floating, Idle, Live};
|
||||
use super::Options;
|
||||
@@ -106,7 +104,7 @@ where
|
||||
/// open a new connection, or if an idle connection is returned to the pool.
|
||||
///
|
||||
/// Returns an error if `deadline` elapses before we are woken.
|
||||
async fn wait_for_conn(&self, deadline: Instant) -> crate::Result<()> {
|
||||
async fn wait_for_conn(&self, deadline: Instant) -> Result<(), Error> {
|
||||
let mut waker_pushed = false;
|
||||
|
||||
timeout(
|
||||
@@ -124,7 +122,7 @@ where
|
||||
}),
|
||||
)
|
||||
.await
|
||||
.map_err(|_| crate::Error::PoolTimedOut(None))
|
||||
.map_err(|_| Error::PoolTimedOut)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +130,7 @@ impl<C> SharedPool<C>
|
||||
where
|
||||
C: Connect,
|
||||
{
|
||||
pub(super) async fn new_arc(url: &str, options: Options) -> crate::Result<Arc<Self>> {
|
||||
pub(super) async fn new_arc(url: &str, options: Options) -> Result<Arc<Self>, Error> {
|
||||
let mut pool = Self {
|
||||
url: url.to_owned(),
|
||||
idle_conns: ArrayQueue::new(options.max_size as usize),
|
||||
@@ -151,7 +149,8 @@ where
|
||||
Ok(pool)
|
||||
}
|
||||
|
||||
pub(super) async fn acquire<'s>(&'s self) -> crate::Result<Floating<'s, Live<C>>> {
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
pub(super) async fn acquire<'s>(&'s self) -> Result<Floating<'s, Live<C>>, Error> {
|
||||
let start = Instant::now();
|
||||
let deadline = start + self.options.connect_timeout;
|
||||
|
||||
@@ -185,7 +184,7 @@ where
|
||||
}
|
||||
|
||||
// takes `&mut self` so this can only be called during init
|
||||
async fn init_min_connections(&mut self) -> crate::Result<()> {
|
||||
async fn init_min_connections(&mut self) -> Result<(), Error> {
|
||||
for _ in 0..self.options.min_size {
|
||||
let deadline = Instant::now() + self.options.connect_timeout;
|
||||
|
||||
@@ -208,7 +207,7 @@ where
|
||||
&'s self,
|
||||
deadline: Instant,
|
||||
guard: DecrementSizeGuard<'s>,
|
||||
) -> crate::Result<Option<Floating<'s, Live<C>>>> {
|
||||
) -> Result<Option<Floating<'s, Live<C>>>, Error> {
|
||||
if self.is_closed() {
|
||||
return Err(Error::PoolClosed);
|
||||
}
|
||||
@@ -216,25 +215,25 @@ where
|
||||
let timeout = super::deadline_as_timeout::<C::Database>(deadline)?;
|
||||
|
||||
// result here is `Result<Result<C, Error>, TimeoutError>`
|
||||
match crate::runtime::timeout(timeout, C::connect(&self.url)).await {
|
||||
match sqlx_rt::timeout(timeout, C::connect(&self.url)).await {
|
||||
// successfully established connection
|
||||
Ok(Ok(raw)) => Ok(Some(Floating::new_live(raw, guard))),
|
||||
|
||||
// an IO error while connecting is assumed to be the system starting up
|
||||
Ok(Err(crate::Error::Io(_))) => Ok(None),
|
||||
Ok(Err(Error::Io(_))) => Ok(None),
|
||||
|
||||
// TODO: Handle other database "boot period"s
|
||||
|
||||
// [postgres] the database system is starting up
|
||||
// TODO: Make this check actually check if this is postgres
|
||||
Ok(Err(crate::Error::Database(error))) if error.code() == Some("57P03") => Ok(None),
|
||||
Ok(Err(Error::Database(error))) if error.code().as_deref() == Some("57P03") => Ok(None),
|
||||
|
||||
// Any other error while connection should immediately
|
||||
// terminate and bubble the error up
|
||||
Ok(Err(e)) => Err(e),
|
||||
|
||||
// timed out
|
||||
Err(e) => Err(crate::Error::PoolTimedOut(Some(Box::new(e)))),
|
||||
Err(_) => Err(Error::PoolTimedOut),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! **Pool** for SQLx database connections.
|
||||
//! Connection pool for SQLx database connections.
|
||||
|
||||
use std::{
|
||||
fmt,
|
||||
@@ -8,13 +8,12 @@ use std::{
|
||||
|
||||
use crate::connection::Connect;
|
||||
use crate::database::Database;
|
||||
use crate::transaction::Transaction;
|
||||
use crate::error::Error;
|
||||
|
||||
use self::inner::SharedPool;
|
||||
use self::options::Options;
|
||||
|
||||
mod connection;
|
||||
mod executor;
|
||||
mod inner;
|
||||
mod options;
|
||||
|
||||
@@ -35,17 +34,15 @@ where
|
||||
///
|
||||
/// * MySQL/MariaDB: [crate::mysql::MySqlConnection]
|
||||
/// * PostgreSQL: [crate::postgres::PgConnection]
|
||||
pub async fn new(url: &str) -> crate::Result<Self> {
|
||||
pub async fn new(url: &str) -> Result<Self, Error> {
|
||||
Self::builder().build(url).await
|
||||
}
|
||||
|
||||
async fn with_options(url: &str, options: Options) -> crate::Result<Self> {
|
||||
let inner = SharedPool::<C>::new_arc(url, options).await?;
|
||||
|
||||
Ok(Pool(inner))
|
||||
async fn new_with(url: &str, options: Options) -> Result<Self, Error> {
|
||||
Ok(Pool(SharedPool::<C>::new_arc(url, options).await?))
|
||||
}
|
||||
|
||||
/// Returns a [Builder] to configure a new connection pool.
|
||||
/// Returns a [`Builder`] to configure a new connection pool.
|
||||
pub fn builder() -> Builder<C> {
|
||||
Builder::new()
|
||||
}
|
||||
@@ -53,7 +50,7 @@ where
|
||||
/// Retrieves a connection from the pool.
|
||||
///
|
||||
/// Waits for at most the configured connection timeout before returning an error.
|
||||
pub async fn acquire(&self) -> crate::Result<PoolConnection<C>> {
|
||||
pub async fn acquire(&self) -> Result<PoolConnection<C>, Error> {
|
||||
self.0.acquire().await.map(|conn| conn.attach(&self.0))
|
||||
}
|
||||
|
||||
@@ -64,11 +61,6 @@ where
|
||||
self.0.try_acquire().map(|conn| conn.attach(&self.0))
|
||||
}
|
||||
|
||||
/// Retrieves a new connection and immediately begins a new transaction.
|
||||
pub async fn begin(&self) -> crate::Result<Transaction<PoolConnection<C>>> {
|
||||
Ok(Transaction::new(0, self.acquire().await?).await?)
|
||||
}
|
||||
|
||||
/// Ends the use of a connection pool. Prevents any new connections
|
||||
/// and will close all active connections when they are returned to the pool.
|
||||
///
|
||||
@@ -143,10 +135,10 @@ where
|
||||
/// get the time between the deadline and now and use that as our timeout
|
||||
///
|
||||
/// returns `Error::PoolTimedOut` if the deadline is in the past
|
||||
fn deadline_as_timeout<DB: Database>(deadline: Instant) -> crate::Result<Duration> {
|
||||
fn deadline_as_timeout<DB: Database>(deadline: Instant) -> Result<Duration, Error> {
|
||||
deadline
|
||||
.checked_duration_since(Instant::now())
|
||||
.ok_or(crate::Error::PoolTimedOut(None))
|
||||
.ok_or(Error::PoolTimedOut)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::{marker::PhantomData, time::Duration};
|
||||
use super::Pool;
|
||||
use crate::connection::Connect;
|
||||
use crate::database::Database;
|
||||
use crate::error::Error;
|
||||
|
||||
/// Builder for [Pool].
|
||||
pub struct Builder<C> {
|
||||
@@ -25,7 +26,7 @@ where
|
||||
max_size: 10,
|
||||
// don't open connections until necessary
|
||||
min_size: 0,
|
||||
// try to connect for 10 seconds before erroring
|
||||
// try to connect for 10 seconds before giving up
|
||||
connect_timeout: Duration::from_secs(60),
|
||||
// reap connections that have been alive > 30 minutes
|
||||
// prevents unbounded live-leaking of memory due to naive prepared statement caching
|
||||
@@ -113,11 +114,11 @@ where
|
||||
/// opened and placed into the pool.
|
||||
///
|
||||
/// [`min_size`]: #method.min_size
|
||||
pub async fn build(self, url: &str) -> crate::Result<Pool<C>>
|
||||
pub async fn build(self, url: &str) -> Result<Pool<C>, Error>
|
||||
where
|
||||
C: Connect,
|
||||
{
|
||||
Pool::<C>::with_options(url, self.options).await
|
||||
Pool::<C>::new_with(url, self.options).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
|
||||
use async_stream::try_stream;
|
||||
use futures_core::Stream;
|
||||
use futures_util::future::ready;
|
||||
use futures_util::TryFutureExt;
|
||||
use either::Either;
|
||||
use futures_core::future::BoxFuture;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::{future, FutureExt, StreamExt, TryFutureExt, TryStreamExt};
|
||||
|
||||
use crate::arguments::Arguments;
|
||||
use crate::cursor::{Cursor, HasCursor};
|
||||
use crate::database::Database;
|
||||
use crate::database::{Database, HasArguments};
|
||||
use crate::encode::Encode;
|
||||
use crate::executor::{Execute, Executor, RefExecutor};
|
||||
use crate::row::HasRow;
|
||||
use crate::types::Type;
|
||||
use crate::error::Error;
|
||||
use crate::executor::{Execute, Executor};
|
||||
|
||||
/// Raw SQL query with bind parameters. Returned by [`query`][crate::query::query].
|
||||
#[must_use = "query must be executed to affect database"]
|
||||
pub struct Query<'q, DB>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
pub(crate) query: &'q str,
|
||||
pub(crate) arguments: DB::Arguments,
|
||||
pub struct Query<'q, DB: Database> {
|
||||
query: &'q str,
|
||||
pub(crate) arguments: <DB as HasArguments<'q>>::Arguments,
|
||||
database: PhantomData<DB>,
|
||||
}
|
||||
|
||||
@@ -31,30 +28,25 @@ where
|
||||
/// [Query::execute] as it doesn't make sense to map the result type and then ignore it.
|
||||
///
|
||||
/// [Query::bind] is also omitted; stylistically we recommend placing your `.bind()` calls
|
||||
/// before `.try_map()` anyway.
|
||||
/// before `.try_map()`.
|
||||
#[must_use = "query must be executed to affect database"]
|
||||
pub struct Map<'q, DB, F>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
query: Query<'q, DB>,
|
||||
pub struct Map<'q, DB: Database, F> {
|
||||
inner: Query<'q, DB>,
|
||||
mapper: F,
|
||||
}
|
||||
|
||||
// necessary because we can't have a blanket impl for `Query<'q, DB>`
|
||||
// the compiler thinks that `ImmutableArguments<DB>` could be `DB::Arguments` even though
|
||||
// that would be an infinitely recursive type
|
||||
impl<'q, DB> Execute<'q, DB> for Query<'q, DB>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
fn into_parts(self) -> (&'q str, Option<DB::Arguments>) {
|
||||
(self.query, Some(self.arguments))
|
||||
#[inline]
|
||||
fn query(&self) -> &'q str {
|
||||
self.query
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn query_string(&self) -> &'q str {
|
||||
self.query
|
||||
#[inline]
|
||||
fn take_arguments(&mut self) -> Option<<DB as HasArguments<'q>>::Arguments> {
|
||||
Some(mem::take(&mut self.arguments))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,181 +61,236 @@ where
|
||||
/// will be returned when this query is executed.
|
||||
///
|
||||
/// There is no validation that the value is of the type expected by the query. Most SQL
|
||||
/// flavors will perform type coercion (Postgres will return a database error).s
|
||||
pub fn bind<T>(mut self, value: T) -> Self
|
||||
where
|
||||
T: Type<DB>,
|
||||
T: Encode<DB>,
|
||||
{
|
||||
/// flavors will perform type coercion (Postgres will return a database error).
|
||||
pub fn bind<T: 'q + Encode<'q, DB>>(mut self, value: T) -> Self {
|
||||
self.arguments.add(value);
|
||||
self
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn bind_all(self, arguments: DB::Arguments) -> Query<'q, DB> {
|
||||
Query {
|
||||
query: self.query,
|
||||
arguments,
|
||||
database: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB> Query<'q, DB>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
/// Map each row in the result to another type.
|
||||
///
|
||||
/// The returned type has most of the same methods but does not have
|
||||
/// [`.execute()`][Query::execute] or [`.bind()][Query::bind].
|
||||
/// See [`try_map`](Query::try_map) for a fallible version of this method.
|
||||
///
|
||||
/// See also: [query_as][crate::query_as::query_as].
|
||||
pub fn map<F, O>(self, mapper: F) -> Map<'q, DB, impl TryMapRow<DB, Output = O>>
|
||||
/// The [`query_as`](crate::query_as::query_as) method will construct a mapped query using
|
||||
/// a [`FromRow`](crate::row::FromRow) implementation.
|
||||
#[inline]
|
||||
pub fn map<F, O>(self, f: F) -> Map<'q, DB, impl Fn(DB::Row) -> Result<O, Error>>
|
||||
where
|
||||
O: Unpin,
|
||||
F: MapRow<DB, Output = O>,
|
||||
F: Fn(DB::Row) -> O,
|
||||
{
|
||||
self.try_map(MapRowAdapter(mapper))
|
||||
self.try_map(move |row| Ok(f(row)))
|
||||
}
|
||||
|
||||
/// Map each row in the result to another type.
|
||||
///
|
||||
/// See also: [query_as][crate::query_as::query_as].
|
||||
pub fn try_map<F>(self, mapper: F) -> Map<'q, DB, F>
|
||||
/// The [`query_as`](crate::query_as::query_as) method will construct a mapped query using
|
||||
/// a [`FromRow`](crate::row::FromRow) implementation.
|
||||
#[inline]
|
||||
pub fn try_map<F, O>(self, f: F) -> Map<'q, DB, F>
|
||||
where
|
||||
F: TryMapRow<DB>,
|
||||
F: Fn(DB::Row) -> Result<O, Error>,
|
||||
{
|
||||
Map {
|
||||
query: self,
|
||||
mapper,
|
||||
inner: self,
|
||||
mapper: f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB> Query<'q, DB>
|
||||
where
|
||||
DB: Database,
|
||||
Self: Execute<'q, DB>,
|
||||
{
|
||||
pub async fn execute<E>(self, mut executor: E) -> crate::Result<u64>
|
||||
/// Execute the query and return the total number of rows affected.
|
||||
#[inline]
|
||||
pub async fn execute<'c, E>(self, executor: E) -> Result<u64, Error>
|
||||
where
|
||||
E: Executor<Database = DB>,
|
||||
'q: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.execute(self).await
|
||||
}
|
||||
|
||||
pub fn fetch<'e, E>(self, executor: E) -> <DB as HasCursor<'e, 'q>>::Cursor
|
||||
/// Execute multiple queries and return the rows affected from each query, in a stream.
|
||||
#[inline]
|
||||
pub async fn execute_many<'c, E>(self, executor: E) -> BoxStream<'c, Result<u64, Error>>
|
||||
where
|
||||
E: RefExecutor<'e, Database = DB>,
|
||||
'q: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.fetch_by_ref(self)
|
||||
executor.execute_many(self)
|
||||
}
|
||||
|
||||
/// Execute the query and return the generated results as a stream.
|
||||
#[inline]
|
||||
pub fn fetch<'c, E>(self, executor: E) -> BoxStream<'c, Result<DB::Row, Error>>
|
||||
where
|
||||
'q: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.fetch(self)
|
||||
}
|
||||
|
||||
/// Execute multiple queries and return the generated results as a stream
|
||||
/// from each query, in a stream.
|
||||
#[inline]
|
||||
pub fn fetch_many<'c, E>(
|
||||
self,
|
||||
executor: E,
|
||||
) -> BoxStream<'c, Result<Either<u64, DB::Row>, Error>>
|
||||
where
|
||||
'q: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.fetch_many(self)
|
||||
}
|
||||
|
||||
/// Execute the query and return all the generated results, collected into a [`Vec`].
|
||||
#[inline]
|
||||
pub async fn fetch_all<'c, E>(self, executor: E) -> Result<Vec<DB::Row>, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.fetch_all(self).await
|
||||
}
|
||||
|
||||
/// Execute the query and returns exactly one row.
|
||||
#[inline]
|
||||
pub async fn fetch_one<'c, E>(self, executor: E) -> Result<DB::Row, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.fetch_one(self).await
|
||||
}
|
||||
|
||||
/// Execute the query and returns at most one row.
|
||||
#[inline]
|
||||
pub async fn fetch_optional<'c, E>(self, executor: E) -> Result<Option<DB::Row>, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.fetch_optional(self).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB, F> Map<'q, DB, F>
|
||||
impl<'q, DB, F: Send> Execute<'q, DB> for Map<'q, DB, F>
|
||||
where
|
||||
DB: Database,
|
||||
Query<'q, DB>: Execute<'q, DB>,
|
||||
F: TryMapRow<DB>,
|
||||
{
|
||||
/// Execute the query and get a [Stream] of the results, returning our mapped type.
|
||||
pub fn fetch<'e: 'q, E>(
|
||||
mut self,
|
||||
executor: E,
|
||||
) -> impl Stream<Item = crate::Result<F::Output>> + Unpin + 'e
|
||||
#[inline]
|
||||
fn query(&self) -> &'q str {
|
||||
self.inner.query
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn take_arguments(&mut self) -> Option<<DB as HasArguments<'q>>::Arguments> {
|
||||
Some(mem::take(&mut self.inner.arguments))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB, F, O> Map<'q, DB, F>
|
||||
where
|
||||
DB: Database,
|
||||
F: Send + Sync + Fn(DB::Row) -> Result<O, Error>,
|
||||
O: Send + Unpin,
|
||||
{
|
||||
// FIXME: This is very close 1:1 with [`Executor::fetch`]
|
||||
// noinspection DuplicatedCode
|
||||
/// Execute the query and return the generated results as a stream.
|
||||
pub fn fetch<'c, E>(self, executor: E) -> BoxStream<'c, Result<O, Error>>
|
||||
where
|
||||
'q: 'e,
|
||||
E: RefExecutor<'e, Database = DB> + 'e,
|
||||
F: 'e,
|
||||
F::Output: 'e,
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
F: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
self.fetch_many(executor)
|
||||
.try_filter_map(|step| async move {
|
||||
Ok(match step {
|
||||
Either::Left(_) => None,
|
||||
Either::Right(o) => Some(o),
|
||||
})
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
/// Execute multiple queries and return the generated results as a stream
|
||||
/// from each query, in a stream.
|
||||
pub fn fetch_many<'c, E>(self, executor: E) -> BoxStream<'c, Result<Either<u64, O>, Error>>
|
||||
where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
F: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
Box::pin(try_stream! {
|
||||
let mut cursor = executor.fetch_by_ref(self.query);
|
||||
while let Some(next) = cursor.next().await? {
|
||||
let mapped = self.mapper.try_map_row(next)?;
|
||||
yield mapped;
|
||||
let mut s = executor.fetch_many(self.inner);
|
||||
while let Some(v) = s.try_next().await? {
|
||||
match v {
|
||||
Either::Left(v) => yield Either::Left(v),
|
||||
Either::Right(row) => {
|
||||
let mapped = (self.mapper)(row)?;
|
||||
yield Either::Right(mapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the first row in the result
|
||||
pub async fn fetch_optional<'e, E>(self, executor: E) -> crate::Result<Option<F::Output>>
|
||||
/// Execute the query and return all the generated results, collected into a [`Vec`].
|
||||
#[inline]
|
||||
pub fn fetch_all<'c, E>(self, executor: E) -> BoxFuture<'c, Result<Vec<O>, Error>>
|
||||
where
|
||||
E: RefExecutor<'e, Database = DB>,
|
||||
'q: 'e,
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
F: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
// could be implemented in terms of `fetch()` but this avoids overhead from `try_stream!`
|
||||
let mut cursor = executor.fetch_by_ref(self.query);
|
||||
let mut mapper = self.mapper;
|
||||
let val = cursor.next().await?;
|
||||
val.map(|row| mapper.try_map_row(row)).transpose()
|
||||
self.fetch(executor).try_collect().boxed()
|
||||
}
|
||||
|
||||
pub async fn fetch_one<'e, E>(self, executor: E) -> crate::Result<F::Output>
|
||||
// FIXME: This is very close 1:1 with [`Executor::fetch_one`]
|
||||
// noinspection DuplicatedCode
|
||||
/// Execute the query and returns exactly one row.
|
||||
pub fn fetch_one<'c, E>(self, executor: E) -> BoxFuture<'c, Result<O, Error>>
|
||||
where
|
||||
E: RefExecutor<'e, Database = DB>,
|
||||
'q: 'e,
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
F: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
self.fetch_optional(executor)
|
||||
.and_then(|row| match row {
|
||||
Some(row) => ready(Ok(row)),
|
||||
None => ready(Err(crate::Error::RowNotFound)),
|
||||
Some(row) => future::ok(row),
|
||||
None => future::err(Error::RowNotFound),
|
||||
})
|
||||
.await
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub async fn fetch_all<'e, E>(mut self, executor: E) -> crate::Result<Vec<F::Output>>
|
||||
/// Execute the query and returns at most one row.
|
||||
pub fn fetch_optional<'c, E>(self, executor: E) -> BoxFuture<'c, Result<Option<O>, Error>>
|
||||
where
|
||||
E: RefExecutor<'e, Database = DB>,
|
||||
'q: 'e,
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
F: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
let mut cursor = executor.fetch_by_ref(self.query);
|
||||
let mut out = vec![];
|
||||
|
||||
while let Some(row) = cursor.next().await? {
|
||||
out.push(self.mapper.try_map_row(row)?);
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
||||
// A (hopefully) temporary workaround for an internal compiler error (ICE) involving higher-ranked
|
||||
// trait bounds (HRTBs), associated types and closures.
|
||||
//
|
||||
// See https://github.com/rust-lang/rust/issues/62529
|
||||
|
||||
pub trait TryMapRow<DB: Database> {
|
||||
type Output: Unpin;
|
||||
|
||||
fn try_map_row(&mut self, row: <DB as HasRow>::Row) -> crate::Result<Self::Output>;
|
||||
}
|
||||
|
||||
pub trait MapRow<DB: Database> {
|
||||
type Output: Unpin;
|
||||
|
||||
fn map_row(&mut self, row: <DB as HasRow>::Row) -> Self::Output;
|
||||
}
|
||||
|
||||
// An adapter that implements [MapRow] in terms of [TryMapRow]
|
||||
// Just ends up Ok wrapping it
|
||||
|
||||
struct MapRowAdapter<F>(F);
|
||||
|
||||
impl<DB: Database, O, F> TryMapRow<DB> for MapRowAdapter<F>
|
||||
where
|
||||
O: Unpin,
|
||||
F: MapRow<DB, Output = O>,
|
||||
{
|
||||
type Output = O;
|
||||
|
||||
fn try_map_row(&mut self, row: <DB as HasRow>::Row) -> crate::Result<Self::Output> {
|
||||
Ok(self.0.map_row(row))
|
||||
Box::pin(async move {
|
||||
let row = executor.fetch_optional(self.inner).await?;
|
||||
if let Some(row) = row {
|
||||
(self.mapper)(row).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a raw SQL query that can be chained to bind parameters and executed.
|
||||
#[inline]
|
||||
pub fn query<DB>(sql: &str) -> Query<DB>
|
||||
where
|
||||
DB: Database,
|
||||
|
||||
@@ -1,204 +1,151 @@
|
||||
use core::marker::PhantomData;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use async_stream::try_stream;
|
||||
use either::Either;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::{StreamExt, TryStreamExt};
|
||||
|
||||
use crate::arguments::Arguments;
|
||||
use crate::database::Database;
|
||||
use crate::database::{Database, HasArguments};
|
||||
use crate::encode::Encode;
|
||||
use crate::executor::Execute;
|
||||
use crate::types::Type;
|
||||
use crate::error::Error;
|
||||
use crate::executor::{Execute, Executor};
|
||||
use crate::from_row::FromRow;
|
||||
use crate::query::{query, Query};
|
||||
|
||||
/// Raw SQL query with bind parameters, mapped to a concrete type
|
||||
/// using [`FromRow`](trait.FromRow.html). Returned
|
||||
/// by [`query_as`](fn.query_as.html).
|
||||
/// Raw SQL query with bind parameters, mapped to a concrete type using [`FromRow`].
|
||||
/// Returned from [`query_as`].
|
||||
#[must_use = "query must be executed to affect database"]
|
||||
pub struct QueryAs<'q, DB, O>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
query: &'q str,
|
||||
arguments: <DB as Database>::Arguments,
|
||||
database: PhantomData<DB>,
|
||||
pub struct QueryAs<'q, DB: Database, O> {
|
||||
pub(crate) inner: Query<'q, DB>,
|
||||
output: PhantomData<O>,
|
||||
}
|
||||
|
||||
impl<'q, DB, O> QueryAs<'q, DB, O>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
/// Bind a value for use with this SQL query.
|
||||
#[inline]
|
||||
pub fn bind<T>(mut self, value: T) -> Self
|
||||
where
|
||||
T: Type<DB>,
|
||||
T: Encode<DB>,
|
||||
{
|
||||
self.arguments.add(value);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB, O: Send> Execute<'q, DB> for QueryAs<'q, DB, O>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
#[inline]
|
||||
fn into_parts(self) -> (&'q str, Option<<DB as Database>::Arguments>) {
|
||||
(self.query, Some(self.arguments))
|
||||
fn query(&self) -> &'q str {
|
||||
self.inner.query()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn take_arguments(&mut self) -> Option<<DB as HasArguments<'q>>::Arguments> {
|
||||
self.inner.take_arguments()
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This is very close, nearly 1:1 with `Map`
|
||||
// noinspection DuplicatedCode
|
||||
impl<'q, DB, O> QueryAs<'q, DB, O>
|
||||
where
|
||||
DB: Database,
|
||||
O: Send + Unpin + for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
/// Bind a value for use with this SQL query.
|
||||
///
|
||||
/// See [`Query::bind`](crate::query::Query::bind).
|
||||
#[inline]
|
||||
pub fn bind<T: 'q + Encode<'q, DB>>(mut self, value: T) -> Self {
|
||||
self.inner.arguments.add(value);
|
||||
self
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn query_string(&self) -> &'q str {
|
||||
self.query
|
||||
pub fn __bind_all(mut self, arguments: <DB as HasArguments<'q>>::Arguments) -> Self {
|
||||
self.inner.arguments = arguments;
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute the query and return the generated results as a stream.
|
||||
pub fn fetch<'c, E>(self, executor: E) -> BoxStream<'c, Result<O, Error>>
|
||||
where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
self.fetch_many(executor)
|
||||
.try_filter_map(|step| async move { Ok(step.right()) })
|
||||
.boxed()
|
||||
}
|
||||
|
||||
/// Execute multiple queries and return the generated results as a stream
|
||||
/// from each query, in a stream.
|
||||
pub fn fetch_many<'c, E>(self, executor: E) -> BoxStream<'c, Result<Either<u64, O>, Error>>
|
||||
where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
Box::pin(try_stream! {
|
||||
let mut s = executor.fetch_many(self.inner);
|
||||
while let Some(v) = s.try_next().await? {
|
||||
match v {
|
||||
Either::Left(v) => yield Either::Left(v),
|
||||
Either::Right(row) => {
|
||||
let mapped = O::from_row(&row)?;
|
||||
yield Either::Right(mapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Execute the query and return all the generated results, collected into a [`Vec`].
|
||||
#[inline]
|
||||
pub async fn fetch_all<'c, E>(self, executor: E) -> Result<Vec<O>, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
self.fetch(executor).try_collect().await
|
||||
}
|
||||
|
||||
/// Execute the query and returns exactly one row.
|
||||
pub async fn fetch_one<'c, E>(self, executor: E) -> Result<O, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
self.fetch_optional(executor)
|
||||
.await
|
||||
.and_then(|row| row.ok_or(Error::RowNotFound))
|
||||
}
|
||||
|
||||
/// Execute the query and returns at most one row.
|
||||
pub async fn fetch_optional<'c, E>(self, executor: E) -> Result<Option<O>, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
let row = executor.fetch_optional(self.inner).await?;
|
||||
if let Some(row) = row {
|
||||
O::from_row(&row).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a raw SQL query that is mapped to a concrete type
|
||||
/// using [`FromRow`](crate::row::FromRow).
|
||||
///
|
||||
/// Returns [`QueryAs`].
|
||||
#[inline]
|
||||
pub fn query_as<DB, O>(sql: &str) -> QueryAs<DB, O>
|
||||
where
|
||||
DB: Database,
|
||||
O: for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
QueryAs {
|
||||
query: sql,
|
||||
arguments: Default::default(),
|
||||
database: PhantomData,
|
||||
inner: query(sql),
|
||||
output: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// We need database-specific QueryAs traits to work around:
|
||||
// https://github.com/rust-lang/rust/issues/62529
|
||||
|
||||
// If for some reason we miss that issue being resolved in a _stable_ edition of
|
||||
// rust, please open up a 100 issues and shout as loud as you can to remove
|
||||
// this unseemly hack.
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! make_query_as {
|
||||
($name:ident, $db:ident, $row:ident) => {
|
||||
pub trait $name<'q, O> {
|
||||
fn fetch<'e, E>(
|
||||
self,
|
||||
executor: E,
|
||||
) -> futures_core::stream::BoxStream<'e, crate::Result<O>>
|
||||
where
|
||||
E: 'e + Send + crate::executor::RefExecutor<'e, Database = $db>,
|
||||
O: 'e + Send + Unpin + for<'c> crate::row::FromRow<'c, $row<'c>>,
|
||||
'q: 'e;
|
||||
|
||||
fn fetch_all<'e, E>(
|
||||
self,
|
||||
executor: E,
|
||||
) -> futures_core::future::BoxFuture<'e, crate::Result<Vec<O>>>
|
||||
where
|
||||
E: 'e + Send + crate::executor::RefExecutor<'e, Database = $db>,
|
||||
O: 'e + Send + for<'c> crate::row::FromRow<'c, $row<'c>>,
|
||||
'q: 'e;
|
||||
|
||||
fn fetch_one<'e, E>(
|
||||
self,
|
||||
executor: E,
|
||||
) -> futures_core::future::BoxFuture<'e, crate::Result<O>>
|
||||
where
|
||||
E: 'e + Send + crate::executor::RefExecutor<'e, Database = $db>,
|
||||
O: 'e + Send + for<'c> crate::row::FromRow<'c, $row<'c>>,
|
||||
'q: 'e;
|
||||
|
||||
fn fetch_optional<'e, E>(
|
||||
self,
|
||||
executor: E,
|
||||
) -> futures_core::future::BoxFuture<'e, crate::Result<Option<O>>>
|
||||
where
|
||||
E: 'e + Send + crate::executor::RefExecutor<'e, Database = $db>,
|
||||
O: 'e + Send + for<'c> crate::row::FromRow<'c, $row<'c>>,
|
||||
'q: 'e;
|
||||
}
|
||||
|
||||
impl<'q, O> $name<'q, O> for crate::query_as::QueryAs<'q, $db, O> {
|
||||
fn fetch<'e, E>(
|
||||
self,
|
||||
executor: E,
|
||||
) -> futures_core::stream::BoxStream<'e, crate::Result<O>>
|
||||
where
|
||||
E: 'e + Send + crate::executor::RefExecutor<'e, Database = $db>,
|
||||
O: 'e + Send + Unpin + for<'c> crate::row::FromRow<'c, $row<'c>>,
|
||||
'q: 'e,
|
||||
{
|
||||
use crate::cursor::Cursor;
|
||||
|
||||
Box::pin(async_stream::try_stream! {
|
||||
let mut cursor = executor.fetch_by_ref(self);
|
||||
|
||||
while let Some(row) = cursor.next().await? {
|
||||
let obj = O::from_row(&row)?;
|
||||
|
||||
yield obj;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_optional<'e, E>(
|
||||
self,
|
||||
executor: E,
|
||||
) -> futures_core::future::BoxFuture<'e, crate::Result<Option<O>>>
|
||||
where
|
||||
E: 'e + Send + crate::executor::RefExecutor<'e, Database = $db>,
|
||||
O: 'e + Send + for<'c> crate::row::FromRow<'c, $row<'c>>,
|
||||
'q: 'e,
|
||||
{
|
||||
use crate::cursor::Cursor;
|
||||
|
||||
Box::pin(async move {
|
||||
let mut cursor = executor.fetch_by_ref(self);
|
||||
let row = cursor.next().await?;
|
||||
|
||||
row.as_ref().map(O::from_row).transpose()
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_one<'e, E>(
|
||||
self,
|
||||
executor: E,
|
||||
) -> futures_core::future::BoxFuture<'e, crate::Result<O>>
|
||||
where
|
||||
E: 'e + Send + crate::executor::RefExecutor<'e, Database = $db>,
|
||||
O: 'e + Send + for<'c> crate::row::FromRow<'c, $row<'c>>,
|
||||
'q: 'e,
|
||||
{
|
||||
use futures_util::TryFutureExt;
|
||||
|
||||
Box::pin(self.fetch_optional(executor).and_then(|row| match row {
|
||||
Some(row) => futures_util::future::ready(Ok(row)),
|
||||
None => futures_util::future::ready(Err(crate::Error::RowNotFound)),
|
||||
}))
|
||||
}
|
||||
|
||||
fn fetch_all<'e, E>(
|
||||
self,
|
||||
executor: E,
|
||||
) -> futures_core::future::BoxFuture<'e, crate::Result<Vec<O>>>
|
||||
where
|
||||
E: 'e + Send + crate::executor::RefExecutor<'e, Database = $db>,
|
||||
O: 'e + Send + for<'c> crate::row::FromRow<'c, $row<'c>>,
|
||||
'q: 'e,
|
||||
{
|
||||
use crate::cursor::Cursor;
|
||||
|
||||
Box::pin(async move {
|
||||
let mut cursor = executor.fetch_by_ref(self);
|
||||
let mut out = Vec::new();
|
||||
|
||||
while let Some(row) = cursor.next().await? {
|
||||
let obj = O::from_row(&row)?;
|
||||
|
||||
out.push(obj);
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
132
sqlx-core/src/query_scalar.rs
Normal file
132
sqlx-core/src/query_scalar.rs
Normal file
@@ -0,0 +1,132 @@
|
||||
use either::Either;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::{StreamExt, TryFutureExt, TryStreamExt};
|
||||
|
||||
use crate::arguments::Arguments;
|
||||
use crate::database::{Database, HasArguments};
|
||||
use crate::encode::Encode;
|
||||
use crate::error::Error;
|
||||
use crate::executor::{Execute, Executor};
|
||||
use crate::from_row::FromRow;
|
||||
use crate::query_as::{query_as, QueryAs};
|
||||
|
||||
/// Raw SQL query with bind parameters, mapped to a concrete type using [`FromRow`] on `(O,)`.
|
||||
/// Returned from [`query_scalar`].
|
||||
#[must_use = "query must be executed to affect database"]
|
||||
pub struct QueryScalar<'q, DB: Database, O> {
|
||||
inner: QueryAs<'q, DB, (O,)>,
|
||||
}
|
||||
|
||||
impl<'q, DB, O: Send> Execute<'q, DB> for QueryScalar<'q, DB, O>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
#[inline]
|
||||
fn query(&self) -> &'q str {
|
||||
self.inner.query()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn take_arguments(&mut self) -> Option<<DB as HasArguments<'q>>::Arguments> {
|
||||
self.inner.take_arguments()
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This is very close, nearly 1:1 with `Map`
|
||||
// noinspection DuplicatedCode
|
||||
impl<'q, DB, O> QueryScalar<'q, DB, O>
|
||||
where
|
||||
DB: Database,
|
||||
O: Send + Unpin,
|
||||
(O,): Send + Unpin + for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
/// Bind a value for use with this SQL query.
|
||||
///
|
||||
/// See [`Query::bind`](crate::query::Query::bind).
|
||||
#[inline]
|
||||
pub fn bind<T: 'q + Encode<'q, DB>>(mut self, value: T) -> Self {
|
||||
self.inner.inner.arguments.add(value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute the query and return the generated results as a stream.
|
||||
#[inline]
|
||||
pub fn fetch<'c, E>(self, executor: E) -> BoxStream<'c, Result<O, Error>>
|
||||
where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
self.inner.fetch(executor).map_ok(|it| it.0).boxed()
|
||||
}
|
||||
|
||||
/// Execute multiple queries and return the generated results as a stream
|
||||
/// from each query, in a stream.
|
||||
#[inline]
|
||||
pub fn fetch_many<'c, E>(self, executor: E) -> BoxStream<'c, Result<Either<u64, O>, Error>>
|
||||
where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
self.inner
|
||||
.fetch_many(executor)
|
||||
.map_ok(|v| v.map_right(|it| it.0))
|
||||
.boxed()
|
||||
}
|
||||
|
||||
/// Execute the query and return all the generated results, collected into a [`Vec`].
|
||||
#[inline]
|
||||
pub async fn fetch_all<'c, E>(self, executor: E) -> Result<Vec<O>, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
(O,): 'c,
|
||||
{
|
||||
self.inner
|
||||
.fetch(executor)
|
||||
.map_ok(|it| it.0)
|
||||
.try_collect()
|
||||
.await
|
||||
}
|
||||
|
||||
/// Execute the query and returns exactly one row.
|
||||
#[inline]
|
||||
pub async fn fetch_one<'c, E>(self, executor: E) -> Result<O, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
self.inner.fetch_one(executor).map_ok(|it| it.0).await
|
||||
}
|
||||
|
||||
/// Execute the query and returns at most one row.
|
||||
#[inline]
|
||||
pub async fn fetch_optional<'c, E>(self, executor: E) -> Result<Option<O>, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
Ok(self.inner.fetch_optional(executor).await?.map(|it| it.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a raw SQL query that is mapped to a concrete type
|
||||
/// using [`FromRow`](crate::row::FromRow) on `(O,)`.
|
||||
#[inline]
|
||||
pub fn query_scalar<DB, O>(sql: &str) -> QueryScalar<DB, O>
|
||||
where
|
||||
DB: Database,
|
||||
(O,): for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
QueryScalar {
|
||||
inner: query_as(sql),
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
//! Contains the `ColumnIndex`, `Row`, and `FromRow` traits.
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::database::{Database, HasValueRef};
|
||||
use crate::decode::Decode;
|
||||
use crate::types::{Type, TypeInfo};
|
||||
use crate::value::{HasRawValue, RawValue};
|
||||
use crate::error::{mismatched_types, Error};
|
||||
use crate::value::ValueRef;
|
||||
|
||||
/// A type that can be used to index into a [`Row`].
|
||||
///
|
||||
@@ -16,33 +16,42 @@ use crate::value::{HasRawValue, RawValue};
|
||||
/// [`Row`]: trait.Row.html
|
||||
/// [`get`]: trait.Row.html#method.get
|
||||
/// [`try_get`]: trait.Row.html#method.try_get
|
||||
pub trait ColumnIndex<'c, R>
|
||||
where
|
||||
Self: private_column_index::Sealed,
|
||||
R: Row<'c> + ?Sized,
|
||||
{
|
||||
pub trait ColumnIndex<R: Row + ?Sized>: private_column_index::Sealed + Debug {
|
||||
/// Returns a valid positional index into the row, [`ColumnIndexOutOfBounds`], or,
|
||||
/// [`ColumnNotFound`].
|
||||
///
|
||||
/// [`ColumnNotFound`]: ../enum.Error.html#variant.ColumnNotFound
|
||||
/// [`ColumnIndexOutOfBounds`]: ../enum.Error.html#variant.ColumnIndexOutOfBounds
|
||||
fn index(&self, row: &R) -> crate::Result<usize>;
|
||||
fn index(&self, row: &R) -> Result<usize, Error>;
|
||||
}
|
||||
|
||||
impl<'c, R, I> ColumnIndex<'c, R> for &'_ I
|
||||
impl<R, I> ColumnIndex<R> for &'_ I
|
||||
where
|
||||
R: Row<'c>,
|
||||
I: ColumnIndex<'c, R> + ?Sized,
|
||||
R: Row + ?Sized,
|
||||
I: ColumnIndex<R> + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn index(&self, row: &R) -> crate::Result<usize> {
|
||||
fn index(&self, row: &R) -> Result<usize, Error> {
|
||||
(**self).index(row)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Row> ColumnIndex<R> for usize {
|
||||
fn index(&self, row: &R) -> Result<usize, Error> {
|
||||
let len = row.len();
|
||||
|
||||
if *self >= len {
|
||||
return Err(Error::ColumnIndexOutOfBounds { len, index: *self });
|
||||
}
|
||||
|
||||
Ok(*self)
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent users from implementing the `ColumnIndex` trait.
|
||||
mod private_column_index {
|
||||
pub trait Sealed {}
|
||||
|
||||
impl Sealed for usize {}
|
||||
impl Sealed for str {}
|
||||
impl<T> Sealed for &'_ T where T: Sealed + ?Sized {}
|
||||
@@ -50,20 +59,12 @@ mod private_column_index {
|
||||
|
||||
/// Represents a single row from the database.
|
||||
///
|
||||
/// Applications should not generally need to use this trait. Values of this trait are only
|
||||
/// encountered when manually implementing [`FromRow`] (as opposed to deriving) or iterating
|
||||
/// a [`Cursor`] (returned from [`Query::fetch`]).
|
||||
///
|
||||
/// This trait is sealed and cannot be implemented for types outside of SQLx.
|
||||
///
|
||||
/// [`FromRow`]: crate::row::FromRow
|
||||
/// [`Cursor`]: crate::cursor::Cursor
|
||||
/// [`Query::fetch`]: crate::query::Query::fetch
|
||||
pub trait Row<'c>
|
||||
where
|
||||
Self: private_row::Sealed + Unpin + Send + Sync,
|
||||
{
|
||||
/// The `Database` this `Row` is implemented for.
|
||||
pub trait Row: private_row::Sealed + Unpin + Send + Sync + 'static {
|
||||
type Database: Database;
|
||||
|
||||
/// Returns `true` if this row has no columns.
|
||||
@@ -80,38 +81,36 @@ where
|
||||
/// A string index can be used to access a column by name and a `usize` index
|
||||
/// can be used to access a column by position.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// # let mut cursor = sqlx::query("SELECT id, name FROM users")
|
||||
/// # .fetch(&mut conn);
|
||||
/// #
|
||||
/// # let row = cursor.next().await?.unwrap();
|
||||
/// #
|
||||
/// let id: i32 = row.get("id"); // a column named "id"
|
||||
/// let name: &str = row.get(1); // the second column in the result
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the column does not exist or its value cannot be decoded into the requested type.
|
||||
/// See [`try_get`](#method.try_get) for a non-panicking version.
|
||||
///
|
||||
#[inline]
|
||||
fn get<T, I>(&self, index: I) -> T
|
||||
fn get<'r, T, I>(&'r self, index: I) -> T
|
||||
where
|
||||
T: Type<Self::Database>,
|
||||
I: ColumnIndex<'c, Self>,
|
||||
T: Decode<'c, Self::Database>,
|
||||
I: ColumnIndex<Self>,
|
||||
T: Decode<'r, Self::Database>,
|
||||
{
|
||||
self.try_get::<T, I>(index).unwrap()
|
||||
}
|
||||
|
||||
/// Index into the database row and decode a single value.
|
||||
///
|
||||
/// See [`try_get_unchecked`](#method.try_get_unchecked).
|
||||
/// Unlike [`get`](#method.get), this method does not check that the type
|
||||
/// being returned from the database is compatible with the Rust type and blindly tries
|
||||
/// to decode the value.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the column does not exist or its value cannot be decoded into the requested type.
|
||||
/// See [`try_get_unchecked`](#method.try_get_unchecked) for a non-panicking version.
|
||||
///
|
||||
#[inline]
|
||||
fn get_unchecked<T, I>(&self, index: I) -> T
|
||||
fn get_unchecked<'r, T, I>(&'r self, index: I) -> T
|
||||
where
|
||||
T: Type<Self::Database>,
|
||||
I: ColumnIndex<'c, Self>,
|
||||
T: Decode<'c, Self::Database>,
|
||||
I: ColumnIndex<Self>,
|
||||
T: Decode<'r, Self::Database>,
|
||||
{
|
||||
self.try_get_unchecked::<T, I>(index).unwrap()
|
||||
}
|
||||
@@ -121,254 +120,91 @@ where
|
||||
/// A string index can be used to access a column by name and a `usize` index
|
||||
/// can be used to access a column by position.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// # let mut cursor = sqlx::query("SELECT id, name FROM users")
|
||||
/// # .fetch(&mut conn);
|
||||
/// #
|
||||
/// # let row = cursor.next().await?.unwrap();
|
||||
/// #
|
||||
/// let id: i32 = row.try_get("id")?; // a column named "id"
|
||||
/// let name: &str = row.try_get(1)?; // the second column in the result
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`ColumnNotFound`] if the column by the given name was not found.
|
||||
/// * [`ColumnIndexOutOfBounds`] if the `usize` index was greater than the number of columns in the row.
|
||||
/// * [`Decode`] if the value could not be decoded into the requested type.
|
||||
/// * [`ColumnDecode`] if the value could not be decoded into the requested type.
|
||||
///
|
||||
/// [`Decode`]: crate::Error::Decode
|
||||
/// [`ColumnDecode`]: crate::Error::ColumnDecode
|
||||
/// [`ColumnNotFound`]: crate::Error::ColumnNotFound
|
||||
/// [`ColumnIndexOutOfBounds`]: crate::Error::ColumnIndexOutOfBounds
|
||||
fn try_get<T, I>(&self, index: I) -> crate::Result<T>
|
||||
///
|
||||
fn try_get<'r, T, I>(&'r self, index: I) -> Result<T, Error>
|
||||
where
|
||||
T: Type<Self::Database>,
|
||||
I: ColumnIndex<'c, Self>,
|
||||
T: Decode<'c, Self::Database>,
|
||||
I: ColumnIndex<Self>,
|
||||
T: Decode<'r, Self::Database>,
|
||||
{
|
||||
let value = self.try_get_raw(index)?;
|
||||
let value = self.try_get_raw(&index)?;
|
||||
|
||||
if let Some(expected_ty) = value.type_info() {
|
||||
// NOTE: If there is no type, the value is NULL. This is fine. If the user tries
|
||||
// to get this into a non-Option we catch that elsewhere and report as
|
||||
// UnexpectedNullError.
|
||||
|
||||
if !expected_ty.compatible(&T::type_info()) {
|
||||
return Err(crate::Error::mismatched_types::<Self::Database, T>(
|
||||
expected_ty,
|
||||
));
|
||||
if !value.is_null() {
|
||||
if let Some(actual_ty) = value.type_info() {
|
||||
// NOTE: we opt-out of asserting the type equivalency of NULL because of the
|
||||
// high false-positive rate (e.g., `NULL` in Postgres is `TEXT`).
|
||||
if !T::accepts(&actual_ty) {
|
||||
return Err(Error::ColumnDecode {
|
||||
index: format!("{:?}", index),
|
||||
source: mismatched_types::<Self::Database, T>(&T::type_info(), &actual_ty),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T::decode(value)
|
||||
T::decode(value).map_err(|source| Error::ColumnDecode {
|
||||
index: format!("{:?}", index),
|
||||
source,
|
||||
})
|
||||
}
|
||||
|
||||
/// Index into the database row and decode a single value.
|
||||
///
|
||||
/// Unlike [`try_get`](#method.try_get), this method does not check that the type
|
||||
/// being returned from the database is compatible with the Rust type and just blindly tries
|
||||
/// to decode the value. An example of where this could be useful is decoding a Postgres
|
||||
/// enumeration as a Rust string (instead of deriving a new Rust enum).
|
||||
/// being returned from the database is compatible with the Rust type and blindly tries
|
||||
/// to decode the value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`ColumnNotFound`] if the column by the given name was not found.
|
||||
/// * [`ColumnIndexOutOfBounds`] if the `usize` index was greater than the number of columns in the row.
|
||||
/// * [`ColumnDecode`] if the value could not be decoded into the requested type.
|
||||
///
|
||||
/// [`ColumnDecode`]: crate::Error::ColumnDecode
|
||||
/// [`ColumnNotFound`]: crate::Error::ColumnNotFound
|
||||
/// [`ColumnIndexOutOfBounds`]: crate::Error::ColumnIndexOutOfBounds
|
||||
///
|
||||
#[inline]
|
||||
fn try_get_unchecked<T, I>(&self, index: I) -> crate::Result<T>
|
||||
fn try_get_unchecked<'r, T, I>(&'r self, index: I) -> Result<T, Error>
|
||||
where
|
||||
T: Type<Self::Database>,
|
||||
I: ColumnIndex<'c, Self>,
|
||||
T: Decode<'c, Self::Database>,
|
||||
I: ColumnIndex<Self>,
|
||||
T: Decode<'r, Self::Database>,
|
||||
{
|
||||
self.try_get_raw(index).and_then(T::decode)
|
||||
let value = self.try_get_raw(&index)?;
|
||||
T::decode(value).map_err(|source| Error::ColumnDecode {
|
||||
index: format!("{:?}", index),
|
||||
source,
|
||||
})
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn try_get_raw<I>(
|
||||
&self,
|
||||
/// Index into the database row and decode a single value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`ColumnNotFound`] if the column by the given name was not found.
|
||||
/// * [`ColumnIndexOutOfBounds`] if the `usize` index was greater than the number of columns in the row.
|
||||
///
|
||||
/// [`ColumnNotFound`]: crate::Error::ColumnNotFound
|
||||
/// [`ColumnIndexOutOfBounds`]: crate::Error::ColumnIndexOutOfBounds
|
||||
///
|
||||
// noinspection RsNeedlessLifetimes
|
||||
fn try_get_raw<'r, I>(
|
||||
&'r self,
|
||||
index: I,
|
||||
) -> crate::Result<<Self::Database as HasRawValue<'c>>::RawValue>
|
||||
) -> Result<<Self::Database as HasValueRef<'r>>::ValueRef, Error>
|
||||
where
|
||||
I: ColumnIndex<'c, Self>;
|
||||
I: ColumnIndex<Self>;
|
||||
}
|
||||
|
||||
// Prevent users from implementing the `Row` trait.
|
||||
pub(crate) mod private_row {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
/// Associate [`Database`] with a [`Row`] of a generic lifetime.
|
||||
///
|
||||
/// ---
|
||||
///
|
||||
/// The upcoming Rust feature, [Generic Associated Types], should obviate
|
||||
/// the need for this trait.
|
||||
///
|
||||
/// [Generic Associated Types]: https://www.google.com/search?q=generic+associated+types+rust&oq=generic+associated+types+rust&aqs=chrome..69i57j0l5.3327j0j7&sourceid=chrome&ie=UTF-8
|
||||
pub trait HasRow<'c> {
|
||||
type Database: Database;
|
||||
|
||||
/// The concrete `Row` implementation for this database.
|
||||
type Row: Row<'c, Database = Self::Database>;
|
||||
}
|
||||
|
||||
/// A record that can be built from a row returned by the database.
|
||||
///
|
||||
/// In order to use [`query_as`] the output type must implement `FromRow`.
|
||||
///
|
||||
/// # Deriving
|
||||
/// This trait can be automatically derived by SQLx for any struct. The generated implementation
|
||||
/// will consist of a sequence of calls to [`Row::try_get`] using the name from each
|
||||
/// struct field.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[derive(sqlx::FromRow)]
|
||||
/// struct User {
|
||||
/// id: i32,
|
||||
/// name: String,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`query_as`]: crate::query_as
|
||||
/// [`Row::try_get`]: crate::row::Row::try_get
|
||||
pub trait FromRow<'c, R>
|
||||
where
|
||||
Self: Sized,
|
||||
R: Row<'c>,
|
||||
{
|
||||
#[allow(missing_docs)]
|
||||
fn from_row(row: &R) -> crate::Result<Self>;
|
||||
}
|
||||
|
||||
// Macros to help unify the internal implementations as a good chunk
|
||||
// is very similar
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! impl_from_row_for_tuple {
|
||||
($db:ident, $r:ident; $( ($idx:tt) -> $T:ident );+;) => {
|
||||
impl<'c, $($T,)+> crate::row::FromRow<'c, $r<'c>> for ($($T,)+)
|
||||
where
|
||||
$($T: 'c,)+
|
||||
$($T: crate::types::Type<$db>,)+
|
||||
$($T: crate::decode::Decode<'c, $db>,)+
|
||||
{
|
||||
#[inline]
|
||||
fn from_row(row: &$r<'c>) -> crate::Result<Self> {
|
||||
use crate::row::Row;
|
||||
|
||||
Ok(($(row.try_get($idx as usize)?,)+))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! impl_from_row_for_tuples {
|
||||
($db:ident, $r:ident) => {
|
||||
impl_from_row_for_tuple!($db, $r;
|
||||
(0) -> T1;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!($db, $r;
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!($db, $r;
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!($db, $r;
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
(3) -> T4;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!($db, $r;
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
(3) -> T4;
|
||||
(4) -> T5;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!($db, $r;
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
(3) -> T4;
|
||||
(4) -> T5;
|
||||
(5) -> T6;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!($db, $r;
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
(3) -> T4;
|
||||
(4) -> T5;
|
||||
(5) -> T6;
|
||||
(6) -> T7;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!($db, $r;
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
(3) -> T4;
|
||||
(4) -> T5;
|
||||
(5) -> T6;
|
||||
(6) -> T7;
|
||||
(7) -> T8;
|
||||
);
|
||||
|
||||
impl_from_row_for_tuple!($db, $r;
|
||||
(0) -> T1;
|
||||
(1) -> T2;
|
||||
(2) -> T3;
|
||||
(3) -> T4;
|
||||
(4) -> T5;
|
||||
(5) -> T6;
|
||||
(6) -> T7;
|
||||
(7) -> T8;
|
||||
(8) -> T9;
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! impl_map_row_for_row {
|
||||
($DB:ident, $R:ident) => {
|
||||
impl<O: Unpin, F> crate::query::MapRow<$DB> for F
|
||||
where
|
||||
F: for<'c> FnMut($R<'c>) -> O,
|
||||
{
|
||||
type Output = O;
|
||||
|
||||
fn map_row(&mut self, row: $R) -> O {
|
||||
(self)(row)
|
||||
}
|
||||
}
|
||||
|
||||
impl<O: Unpin, F> crate::query::TryMapRow<$DB> for F
|
||||
where
|
||||
F: for<'c> FnMut($R<'c>) -> crate::Result<O>,
|
||||
{
|
||||
type Output = O;
|
||||
|
||||
fn try_map_row(&mut self, row: $R) -> crate::Result<O> {
|
||||
(self)(row)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! impl_from_row_for_row {
|
||||
($R:ident) => {
|
||||
impl<'c> crate::row::FromRow<'c, $R<'c>> for $R<'c> {
|
||||
#[inline]
|
||||
fn from_row(row: $R<'c>) -> crate::Result<Self> {
|
||||
Ok(row)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#![allow(unused_imports)]
|
||||
|
||||
#[cfg(not(any(feature = "runtime-tokio", feature = "runtime-async-std")))]
|
||||
compile_error!("one of 'runtime-async-std' or 'runtime-tokio' features must be enabled");
|
||||
|
||||
#[cfg(all(feature = "runtime-tokio", feature = "runtime-async-std"))]
|
||||
compile_error!("only one of 'runtime-async-std' or 'runtime-tokio' features must be enabled");
|
||||
|
||||
#[cfg(feature = "runtime-async-std")]
|
||||
pub(crate) use async_std::{
|
||||
fs,
|
||||
future::timeout,
|
||||
io::prelude::ReadExt as AsyncReadExt,
|
||||
io::{Read as AsyncRead, Write as AsyncWrite},
|
||||
net::TcpStream,
|
||||
task::sleep,
|
||||
task::spawn,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "runtime-async-std", feature = "postgres", unix))]
|
||||
pub(crate) use async_std::os::unix::net::UnixStream;
|
||||
|
||||
#[cfg(feature = "runtime-tokio")]
|
||||
pub(crate) use tokio::{
|
||||
fs,
|
||||
io::{AsyncRead, AsyncReadExt, AsyncWrite},
|
||||
net::TcpStream,
|
||||
task::spawn,
|
||||
time::delay_for as sleep,
|
||||
time::timeout,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "runtime-tokio", feature = "postgres", unix))]
|
||||
pub(crate) use tokio::net::UnixStream;
|
||||
@@ -1,225 +0,0 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use futures_core::future::BoxFuture;
|
||||
|
||||
use crate::connection::Connection;
|
||||
use crate::cursor::HasCursor;
|
||||
use crate::database::Database;
|
||||
use crate::describe::Describe;
|
||||
use crate::executor::{Execute, Executor, RefExecutor};
|
||||
use crate::runtime::spawn;
|
||||
|
||||
/// Represents an in-progress database transaction.
|
||||
///
|
||||
/// A transaction ends with a call to [`commit`] or [`rollback`] in which the wrapped connection (
|
||||
/// or outer transaction) is returned. If neither are called before the transaction
|
||||
/// goes out-of-scope, [`rollback`] is called. In other words, [`rollback`] is called on `drop`
|
||||
/// if the transaction is still in-progress.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// // Acquire a new connection and immediately begin a transaction
|
||||
/// let mut tx = pool.begin().await?;
|
||||
///
|
||||
/// sqlx::query("INSERT INTO articles (slug) VALUES ('this-is-a-slug')")
|
||||
/// .execute(&mut tx)
|
||||
/// // As we didn't fill in all the required fields in this INSERT,
|
||||
/// // this statement will fail. Since we used `?`, this function
|
||||
/// // will immediately return with the error which will cause
|
||||
/// // this transaction to be rolled back.
|
||||
/// .await?;
|
||||
/// ```
|
||||
///
|
||||
/// [`commit`]: #method.commit
|
||||
/// [`rollback`]: #method.rollback
|
||||
// Transaction<PoolConnection<PgConnection>>
|
||||
// Transaction<PgConnection>
|
||||
#[must_use = "transaction rolls back if not explicitly `.commit()`ed"]
|
||||
pub struct Transaction<C>
|
||||
where
|
||||
C: Connection,
|
||||
{
|
||||
inner: Option<C>,
|
||||
depth: u32,
|
||||
}
|
||||
|
||||
impl<C> Transaction<C>
|
||||
where
|
||||
C: Connection,
|
||||
{
|
||||
pub(crate) async fn new(depth: u32, mut inner: C) -> crate::Result<Self> {
|
||||
if depth == 0 {
|
||||
inner.execute("BEGIN").await?;
|
||||
} else {
|
||||
let stmt = format!("SAVEPOINT _sqlx_savepoint_{}", depth);
|
||||
|
||||
inner.execute(&*stmt).await?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
inner: Some(inner),
|
||||
depth: depth + 1,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new save point in the current transaction and returns
|
||||
/// a new `Transaction` object to manage its scope.
|
||||
pub async fn begin(self) -> crate::Result<Transaction<Transaction<C>>> {
|
||||
Transaction::new(self.depth, self).await
|
||||
}
|
||||
|
||||
/// Commits the current transaction or save point.
|
||||
/// Returns the inner connection or transaction.
|
||||
pub async fn commit(mut self) -> crate::Result<C> {
|
||||
let mut inner = self.inner.take().expect(ERR_FINALIZED);
|
||||
let depth = self.depth;
|
||||
|
||||
if depth == 1 {
|
||||
inner.execute("COMMIT").await?;
|
||||
} else {
|
||||
let stmt = format!("RELEASE SAVEPOINT _sqlx_savepoint_{}", depth - 1);
|
||||
|
||||
inner.execute(&*stmt).await?;
|
||||
}
|
||||
|
||||
Ok(inner)
|
||||
}
|
||||
|
||||
/// Rollback the current transaction or save point.
|
||||
/// Returns the inner connection or transaction.
|
||||
pub async fn rollback(mut self) -> crate::Result<C> {
|
||||
let mut inner = self.inner.take().expect(ERR_FINALIZED);
|
||||
let depth = self.depth;
|
||||
|
||||
if depth == 1 {
|
||||
inner.execute("ROLLBACK").await?;
|
||||
} else {
|
||||
let stmt = format!("ROLLBACK TO SAVEPOINT _sqlx_savepoint_{}", depth - 1);
|
||||
|
||||
inner.execute(&*stmt).await?;
|
||||
}
|
||||
|
||||
Ok(inner)
|
||||
}
|
||||
}
|
||||
|
||||
const ERR_FINALIZED: &str = "(bug) transaction already finalized";
|
||||
|
||||
impl<C> Deref for Transaction<C>
|
||||
where
|
||||
C: Connection,
|
||||
{
|
||||
type Target = C;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner.as_ref().expect(ERR_FINALIZED)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> DerefMut for Transaction<C>
|
||||
where
|
||||
C: Connection,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.inner.as_mut().expect(ERR_FINALIZED)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Connection for Transaction<C>
|
||||
where
|
||||
C: Connection,
|
||||
{
|
||||
// Close is equivalent to
|
||||
fn close(mut self) -> BoxFuture<'static, crate::Result<()>> {
|
||||
Box::pin(async move {
|
||||
let mut inner = self.inner.take().expect(ERR_FINALIZED);
|
||||
|
||||
if self.depth == 1 {
|
||||
// This is the root transaction, call rollback
|
||||
let res = inner.execute("ROLLBACK").await;
|
||||
|
||||
// No matter the result of the above, call close
|
||||
let _ = inner.close().await;
|
||||
|
||||
// Now raise the error if there was one
|
||||
res?;
|
||||
} else {
|
||||
// This is not the root transaction, forward to a nested
|
||||
// transaction (to eventually call rollback)
|
||||
inner.close().await?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ping(&mut self) -> BoxFuture<'_, crate::Result<()>> {
|
||||
self.deref_mut().ping()
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB, C> Executor for Transaction<C>
|
||||
where
|
||||
DB: Database,
|
||||
C: Connection<Database = DB>,
|
||||
{
|
||||
type Database = C::Database;
|
||||
|
||||
fn execute<'e, 'q: 'e, 'c: 'e, E: 'e>(
|
||||
&'c mut self,
|
||||
query: E,
|
||||
) -> BoxFuture<'e, crate::Result<u64>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
(**self).execute(query)
|
||||
}
|
||||
|
||||
fn fetch<'e, 'q, E>(&'e mut self, query: E) -> <Self::Database as HasCursor<'e, 'q>>::Cursor
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
(**self).fetch(query)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn describe<'e, 'q, E: 'e>(
|
||||
&'e mut self,
|
||||
query: E,
|
||||
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
(**self).describe(query)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'e, DB, C> RefExecutor<'e> for &'e mut Transaction<C>
|
||||
where
|
||||
DB: Database,
|
||||
C: Connection<Database = DB>,
|
||||
{
|
||||
type Database = DB;
|
||||
|
||||
fn fetch_by_ref<'q, E>(self, query: E) -> <Self::Database as HasCursor<'e, 'q>>::Cursor
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
(**self).fetch(query)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Drop for Transaction<C>
|
||||
where
|
||||
C: Connection,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
if self.depth > 0 {
|
||||
if let Some(inner) = self.inner.take() {
|
||||
spawn(async move {
|
||||
let _ = inner.close().await;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
sqlx-core/src/type_info.rs
Normal file
3
sqlx-core/src/type_info.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
pub trait TypeInfo: Debug + Display + Clone + PartialEq<Self> {}
|
||||
@@ -1,146 +0,0 @@
|
||||
//! Conversions between Rust and SQL types.
|
||||
//!
|
||||
//! To see how each SQL type maps to a Rust type, see the corresponding `types` module for each
|
||||
//! database:
|
||||
//!
|
||||
//! * [PostgreSQL](../postgres/types/index.html)
|
||||
//! * [MySQL](../mysql/types/index.html)
|
||||
//! * [SQLite](../sqlite/types/index.html)
|
||||
//!
|
||||
//! Any external types that have had [`Type`] implemented for, are re-exported in this module
|
||||
//! for convenience as downstream users need to use a compatible version of the external crate
|
||||
//! to take advantage of the implementation.
|
||||
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
use crate::database::Database;
|
||||
|
||||
#[cfg(feature = "uuid")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
|
||||
pub use uuid::Uuid;
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
|
||||
pub mod chrono {
|
||||
pub use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
|
||||
pub mod time {
|
||||
pub use time::{Date, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset};
|
||||
}
|
||||
|
||||
#[cfg(feature = "bigdecimal")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "bigdecimal")))]
|
||||
pub use bigdecimal::BigDecimal;
|
||||
|
||||
#[cfg(feature = "ipnetwork")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "ipnetwork")))]
|
||||
pub mod ipnetwork {
|
||||
pub use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
|
||||
}
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
pub mod json {
|
||||
use crate::database::Database;
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::Encode;
|
||||
use crate::value::HasRawValue;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue as JsonRawValue;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Json<T>(pub T);
|
||||
|
||||
impl<T> Deref for Json<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for Json<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB> Encode<DB> for JsonValue
|
||||
where
|
||||
for<'a> Json<&'a Self>: Encode<DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn encode(&self, buf: &mut DB::RawBuffer) {
|
||||
<Json<&Self> as Encode<DB>>::encode(&Json(self), buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, DB> Decode<'de, DB> for JsonValue
|
||||
where
|
||||
Json<Self>: Decode<'de, DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn decode(value: <DB as HasRawValue<'de>>::RawValue) -> crate::Result<Self> {
|
||||
<Json<Self> as Decode<DB>>::decode(value).map(|item| item.0)
|
||||
}
|
||||
}
|
||||
|
||||
// We don't have to implement Encode for JsonRawValue because that's covered by the default
|
||||
// implementation for Encode
|
||||
impl<'de, DB> Decode<'de, DB> for &'de JsonRawValue
|
||||
where
|
||||
Json<Self>: Decode<'de, DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn decode(value: <DB as HasRawValue<'de>>::RawValue) -> crate::Result<Self> {
|
||||
<Json<Self> as Decode<DB>>::decode(value).map(|item| item.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "json")]
|
||||
pub use self::json::Json;
|
||||
|
||||
pub trait TypeInfo: PartialEq<Self> + Debug + Display + Clone {
|
||||
/// Compares type information to determine if `other` is compatible at the Rust level
|
||||
/// with `self`.
|
||||
fn compatible(&self, other: &Self) -> bool;
|
||||
}
|
||||
|
||||
/// Indicates that a SQL type is supported for a database.
|
||||
pub trait Type<DB>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
/// Returns the canonical type information on the database for the type `T`.
|
||||
fn type_info() -> DB::TypeInfo;
|
||||
}
|
||||
|
||||
// For references to types in Rust, the underlying SQL type information
|
||||
// is equivalent
|
||||
impl<T: ?Sized, DB> Type<DB> for &'_ T
|
||||
where
|
||||
DB: Database,
|
||||
T: Type<DB>,
|
||||
{
|
||||
fn type_info() -> DB::TypeInfo {
|
||||
<T as Type<DB>>::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
// For optional types in Rust, the underlying SQL type information
|
||||
// is equivalent
|
||||
impl<T, DB> Type<DB> for Option<T>
|
||||
where
|
||||
DB: Database,
|
||||
T: Type<DB>,
|
||||
{
|
||||
fn type_info() -> DB::TypeInfo {
|
||||
<T as Type<DB>>::type_info()
|
||||
}
|
||||
}
|
||||
90
sqlx-core/src/types/json.rs
Normal file
90
sqlx-core/src/types/json.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use serde_json::value::RawValue as JsonRawValue;
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
use crate::database::{Database, HasArguments, HasValueRef};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::types::Type;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct Json<T>(pub T);
|
||||
|
||||
impl<T> Deref for Json<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for Json<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB> Type<DB> for JsonValue
|
||||
where
|
||||
Json<Self>: Type<DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn type_info() -> DB::TypeInfo {
|
||||
<Json<Self> as Type<DB>>::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB> Encode<'q, DB> for JsonValue
|
||||
where
|
||||
Self: Type<DB>,
|
||||
for<'a> Json<&'a Self>: Encode<'q, DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn encode_by_ref(&self, buf: &mut <DB as HasArguments<'q>>::Arguments) -> IsNull {
|
||||
<Json<&Self> as Encode<'q, DB>>::encode(Json(self), buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, DB> Decode<'r, DB> for JsonValue
|
||||
where
|
||||
Self: Type<DB>,
|
||||
Json<Self>: Decode<'r, DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn accepts(ty: &DB::TypeInfo) -> bool {
|
||||
<Json<Self> as Decode<DB>>::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> {
|
||||
<Json<Self> as Decode<DB>>::decode(value).map(|item| item.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB> Type<DB> for JsonRawValue
|
||||
where
|
||||
for<'a> Json<&'a Self>: Type<DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn type_info() -> DB::TypeInfo {
|
||||
<Json<&Self> as Type<DB>>::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
// We don't have to implement Encode for JsonRawValue because that's covered by the default
|
||||
// implementation for Encode
|
||||
impl<'r, DB> Decode<'r, DB> for &'r JsonRawValue
|
||||
where
|
||||
Self: Type<DB>,
|
||||
Json<Self>: Decode<'r, DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn accepts(ty: &DB::TypeInfo) -> bool {
|
||||
<Json<Self> as Decode<DB>>::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> {
|
||||
<Json<Self> as Decode<DB>>::decode(value).map(|item| item.0)
|
||||
}
|
||||
}
|
||||
69
sqlx-core/src/types/mod.rs
Normal file
69
sqlx-core/src/types/mod.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
//! Conversions between Rust and SQL types.
|
||||
//!
|
||||
//! To see how each SQL type maps to a Rust type, see the corresponding `types` module for each
|
||||
//! database:
|
||||
//!
|
||||
//! * [PostgreSQL](../postgres/types/index.html)
|
||||
//! * [MySQL](../mysql/types/index.html)
|
||||
//! * [SQLite](../sqlite/types/index.html)
|
||||
//!
|
||||
//! Any external types that have had [`Type`] implemented for, are re-exported in this module
|
||||
//! for convenience as downstream users need to use a compatible version of the external crate
|
||||
//! to take advantage of the implementation.
|
||||
|
||||
use crate::database::Database;
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
|
||||
mod json;
|
||||
|
||||
#[cfg(feature = "uuid")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
|
||||
pub use uuid::Uuid;
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
|
||||
pub mod chrono {
|
||||
pub use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
|
||||
pub mod time {
|
||||
pub use time::{Date, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset};
|
||||
}
|
||||
|
||||
#[cfg(feature = "bigdecimal")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "bigdecimal")))]
|
||||
pub use bigdecimal::BigDecimal;
|
||||
|
||||
#[cfg(feature = "ipnetwork")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "ipnetwork")))]
|
||||
pub mod ipnetwork {
|
||||
pub use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
|
||||
}
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
pub use json::Json;
|
||||
|
||||
/// Indicates that a SQL type is supported for a database.
|
||||
pub trait Type<DB: Database> {
|
||||
/// Returns the canonical type information on the database for the type `T`.
|
||||
fn type_info() -> DB::TypeInfo;
|
||||
}
|
||||
|
||||
// for references, the underlying SQL type is identical
|
||||
impl<T: ?Sized + Type<DB>, DB: Database> Type<DB> for &'_ T {
|
||||
#[inline]
|
||||
fn type_info() -> DB::TypeInfo {
|
||||
<T as Type<DB>>::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
// for optionals, the underlying SQL type is identical
|
||||
impl<T: Type<DB>, DB: Database> Type<DB> for Option<T> {
|
||||
#[inline]
|
||||
fn type_info() -> DB::TypeInfo {
|
||||
<T as Type<DB>>::type_info()
|
||||
}
|
||||
}
|
||||
@@ -1,148 +1 @@
|
||||
use std::borrow::Cow;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Url(url::Url);
|
||||
|
||||
impl TryFrom<String> for Url {
|
||||
type Error = url::ParseError;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
(&value).try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> TryFrom<&'s str> for Url {
|
||||
type Error = url::ParseError;
|
||||
|
||||
fn try_from(value: &'s str) -> Result<Self, Self::Error> {
|
||||
Ok(Url(value.parse()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> TryFrom<&'s String> for Url {
|
||||
type Error = url::ParseError;
|
||||
|
||||
fn try_from(value: &'s String) -> Result<Self, Self::Error> {
|
||||
(value.as_str()).try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<url::Url> for Url {
|
||||
type Error = url::ParseError;
|
||||
|
||||
fn try_from(value: url::Url) -> Result<Self, Self::Error> {
|
||||
Ok(Url(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Url {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn as_str(&self) -> &str {
|
||||
self.0.as_str()
|
||||
}
|
||||
|
||||
pub fn host(&self) -> Option<&str> {
|
||||
match self.0.host_str()? {
|
||||
"" => None,
|
||||
host => Some(host),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn port(&self, default: u16) -> u16 {
|
||||
self.0.port().unwrap_or(default)
|
||||
}
|
||||
|
||||
pub fn username(&self) -> Option<Cow<str>> {
|
||||
let username = self.0.username();
|
||||
|
||||
if username.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
percent_encoding::percent_decode_str(username)
|
||||
.decode_utf8()
|
||||
.expect("percent-encoded username contained non-UTF-8 bytes"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn password(&self) -> Option<Cow<str>> {
|
||||
match self.0.password() {
|
||||
Some(s) => {
|
||||
let decoded = percent_encoding::percent_decode_str(s);
|
||||
|
||||
// FIXME: Handle error
|
||||
Some(
|
||||
decoded
|
||||
.decode_utf8()
|
||||
.expect("percent-encoded password contained non-UTF-8 bytes"),
|
||||
)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Undo URL percent-encoding and return [authority]path[query]
|
||||
///
|
||||
/// Mostly a hack to fix special-character handling for SQLite as its connection string is a
|
||||
/// file path and not _really_ a URL
|
||||
pub fn path_decoded(&self) -> Cow<str> {
|
||||
// omit scheme (e.g. `sqlite://`, `mysql://`)
|
||||
let url_str = &self.0.as_str()[self.0.scheme().len()..]
|
||||
.trim_start_matches(':')
|
||||
.trim_start_matches("//");
|
||||
|
||||
// decode
|
||||
percent_encoding::percent_decode_str(url_str)
|
||||
.decode_utf8()
|
||||
.expect("percent-encoded path contained non-UTF-8 bytes")
|
||||
}
|
||||
|
||||
pub fn database(&self) -> Option<&str> {
|
||||
let database = self.0.path().trim_start_matches('/');
|
||||
|
||||
if database.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(database)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn param(&self, key: &str) -> Option<Cow<str>> {
|
||||
self.0
|
||||
.query_pairs()
|
||||
.find_map(|(key_, val)| if key == key_ { Some(val) } else { None })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn azure_connection_string_username_unencoded() {
|
||||
let connection_string =
|
||||
"postgres://username@servername:password@example.postgres.database.azure.com/db";
|
||||
|
||||
let url = Url::try_from(connection_string).expect("Failed to parse URL");
|
||||
|
||||
assert_eq!(
|
||||
url.username().map(|u| u.to_string()),
|
||||
Some(String::from("username@servername"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn azure_connection_string_username_encoded() {
|
||||
let connection_string =
|
||||
"postgres://username%40servername:password@example.postgres.database.azure.com/db";
|
||||
|
||||
let url = Url::try_from(connection_string).expect("Failed to parse URL");
|
||||
|
||||
assert_eq!(
|
||||
url.username().map(|u| u.to_string()),
|
||||
Some(String::from("username@servername"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,121 @@
|
||||
use crate::database::Database;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Associate [`Database`] with a `RawValue` of a generic lifetime.
|
||||
///
|
||||
/// ---
|
||||
///
|
||||
/// The upcoming Rust feature, [Generic Associated Types], should obviate
|
||||
/// the need for this trait.
|
||||
///
|
||||
/// [Generic Associated Types]: https://www.google.com/search?q=generic+associated+types+rust&oq=generic+associated+types+rust&aqs=chrome..69i57j0l5.3327j0j7&sourceid=chrome&ie=UTF-8
|
||||
pub trait HasRawValue<'c> {
|
||||
use crate::database::{Database, HasValueRef};
|
||||
use crate::decode::Decode;
|
||||
use crate::error::{mismatched_types, Error};
|
||||
|
||||
/// An owned value from the database.
|
||||
pub trait Value {
|
||||
type Database: Database;
|
||||
|
||||
/// The Rust type used to hold a not-yet-decoded value that has just been
|
||||
/// received from the database.
|
||||
type RawValue: RawValue<'c, Database = Self::Database>;
|
||||
/// Get this value as a reference.
|
||||
fn as_ref(&self) -> <Self::Database as HasValueRef<'_>>::ValueRef;
|
||||
|
||||
/// Get the type information, if available, for this value.
|
||||
///
|
||||
/// Some database implementations do not implement type deduction for
|
||||
/// expressions (`SELECT 2 + 5`); and, this will return `None` in those cases.
|
||||
fn type_info(&self) -> Option<Cow<'_, <Self::Database as Database>::TypeInfo>>;
|
||||
|
||||
/// Returns `true` if the SQL value is `NULL`.
|
||||
fn is_null(&self) -> bool;
|
||||
|
||||
/// Decode this single value into the requested type.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the value cannot be decoded into the requested type.
|
||||
/// See [`try_decode`](#method.try_decode) for a non-panicking version.
|
||||
///
|
||||
#[inline]
|
||||
fn decode<'r, T>(&'r self) -> T
|
||||
where
|
||||
T: Decode<'r, Self::Database>,
|
||||
{
|
||||
self.try_decode::<T>().unwrap()
|
||||
}
|
||||
|
||||
/// Decode this single value into the requested type.
|
||||
///
|
||||
/// Unlike [`decode`](#method.decode), this method does not check that the type of this
|
||||
/// value is compatible with the Rust type and blindly tries to decode the value.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the value cannot be decoded into the requested type.
|
||||
/// See [`try_decode_unchecked`](#method.try_decode_unchecked) for a non-panicking version.
|
||||
///
|
||||
#[inline]
|
||||
fn decode_unchecked<'r, T>(&'r self) -> T
|
||||
where
|
||||
T: Decode<'r, Self::Database>,
|
||||
{
|
||||
self.try_decode_unchecked::<T>().unwrap()
|
||||
}
|
||||
|
||||
/// Decode this single value into the requested type.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`Decode`] if the value could not be decoded into the requested type.
|
||||
///
|
||||
/// [`Decode`]: crate::Error::Decode
|
||||
///
|
||||
#[inline]
|
||||
fn try_decode<'r, T>(&'r self) -> Result<T, Error>
|
||||
where
|
||||
T: Decode<'r, Self::Database>,
|
||||
{
|
||||
if !self.is_null() {
|
||||
if let Some(actual_ty) = self.type_info() {
|
||||
if !T::accepts(&actual_ty) {
|
||||
return Err(Error::Decode(mismatched_types::<Self::Database, T>(
|
||||
&T::type_info(),
|
||||
&actual_ty,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.try_decode_unchecked()
|
||||
}
|
||||
|
||||
/// Decode this single value into the requested type.
|
||||
///
|
||||
/// Unlike [`try_decode`](#method.try_decode), this method does not check that the type of this
|
||||
/// value is compatible with the Rust type and blindly tries to decode the value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`Decode`] if the value could not be decoded into the requested type.
|
||||
///
|
||||
/// [`Decode`]: crate::Error::Decode
|
||||
///
|
||||
#[inline]
|
||||
fn try_decode_unchecked<'r, T>(&'r self) -> Result<T, Error>
|
||||
where
|
||||
T: Decode<'r, Self::Database>,
|
||||
{
|
||||
T::decode(self.as_ref()).map_err(Error::Decode)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RawValue<'c> {
|
||||
/// A reference to a single value from the database.
|
||||
pub trait ValueRef<'r>: Sized {
|
||||
type Database: Database;
|
||||
|
||||
fn type_info(&self) -> Option<<Self::Database as Database>::TypeInfo>;
|
||||
/// Creates an owned value from this value reference.
|
||||
///
|
||||
/// This is just a reference increment in PostgreSQL and MySQL and thus is `O(1)`. In SQLite,
|
||||
/// this is a copy.
|
||||
fn to_owned(&self) -> <Self::Database as Database>::Value;
|
||||
|
||||
/// Get the type information, if available, for this value.
|
||||
///
|
||||
/// Some database implementations do not implement type deduction for
|
||||
/// expressions (`SELECT 2 + 5`); and, this will return `None` in those cases.
|
||||
fn type_info(&self) -> Option<Cow<'_, <Self::Database as Database>::TypeInfo>>;
|
||||
|
||||
/// Returns `true` if the SQL value is `NULL`.
|
||||
fn is_null(&self) -> bool;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ pub use native_tls;
|
||||
))]
|
||||
pub use tokio::{
|
||||
self, fs, io::AsyncRead, io::AsyncReadExt, io::AsyncWrite, io::AsyncWriteExt, net::TcpStream,
|
||||
task::yield_now,
|
||||
task::yield_now, time::delay_for as sleep, time::timeout,
|
||||
};
|
||||
|
||||
#[cfg(all(
|
||||
@@ -89,8 +89,9 @@ macro_rules! blocking {
|
||||
not(any(feature = "runtime-actix", feature = "runtime-tokio",))
|
||||
))]
|
||||
pub use async_std::{
|
||||
self, fs, io::prelude::ReadExt as AsyncReadExt, io::prelude::WriteExt as AsyncWriteExt,
|
||||
io::Read as AsyncRead, io::Write as AsyncWrite, net::TcpStream, task::spawn, task::yield_now,
|
||||
self, fs, future::timeout, io::prelude::ReadExt as AsyncReadExt,
|
||||
io::prelude::WriteExt as AsyncWriteExt, io::Read as AsyncRead, io::Write as AsyncWrite,
|
||||
net::TcpStream, task::sleep, task::spawn, task::yield_now,
|
||||
};
|
||||
|
||||
#[cfg(all(
|
||||
|
||||
Reference in New Issue
Block a user