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:
Ryan Leckey
2020-05-26 01:56:03 -07:00
parent a233fbfdb7
commit 757a930e21
43 changed files with 2549 additions and 2668 deletions

543
Cargo.lock generated
View File

@@ -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"

View File

@@ -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"

View File

@@ -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>;
}

View File

@@ -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>>;
}

View File

@@ -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>;
}

View File

@@ -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;
}

View File

@@ -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)?))
}
}
}

View File

@@ -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>,
}

View File

@@ -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)
}

View File

@@ -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)*))
};
}

View File

@@ -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
View File

@@ -0,0 +1 @@
pub mod ustr;

76
sqlx-core/src/ext/ustr.rs Normal file
View 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
View 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;
);

View File

@@ -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)
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View 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(())
}
}

View 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);
}
}

View File

@@ -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;

View 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();
}
}

View File

@@ -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
View 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
View 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
View 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)),
}
}
}

View File

@@ -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,
{

View File

@@ -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)
}
}

View File

@@ -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),
}
}
}

View File

@@ -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]

View File

@@ -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
}
}

View File

@@ -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,

View File

@@ -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)
})
}
}
};
}

View 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),
}
}

View File

@@ -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)
}
}
};
}

View File

@@ -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;

View File

@@ -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;
});
}
}
}
}

View File

@@ -0,0 +1,3 @@
use std::fmt::{Debug, Display};
pub trait TypeInfo: Debug + Display + Clone + PartialEq<Self> {}

View File

@@ -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()
}
}

View 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)
}
}

View 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()
}
}

View File

@@ -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"))
);
}
}

View File

@@ -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;
}

View File

@@ -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(