From f3e453a12b2b3fd00178c5236ab73ebbb3e139d2 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Mon, 30 Nov 2020 16:27:16 -0800 Subject: [PATCH 01/15] add Aurora driver --- Cargo.lock | 553 +++++++++++++++++++- Cargo.toml | 3 +- sqlx-core/Cargo.toml | 11 +- sqlx-core/src/aurora/arguments.rs | 27 + sqlx-core/src/aurora/column.rs | 29 + sqlx-core/src/aurora/connection/executor.rs | 183 +++++++ sqlx-core/src/aurora/connection/mod.rs | 115 ++++ sqlx-core/src/aurora/database.rs | 54 ++ sqlx-core/src/aurora/done.rs | 24 + sqlx-core/src/aurora/error.rs | 43 ++ sqlx-core/src/aurora/mod.rs | 42 ++ sqlx-core/src/aurora/options/connect.rs | 29 + sqlx-core/src/aurora/options/mod.rs | 79 +++ sqlx-core/src/aurora/options/parse.rs | 35 ++ sqlx-core/src/aurora/row.rs | 50 ++ sqlx-core/src/aurora/statement.rs | 61 +++ sqlx-core/src/aurora/transaction.rs | 97 ++++ sqlx-core/src/aurora/type_info.rs | 95 ++++ sqlx-core/src/aurora/types/mod.rs | 1 + sqlx-core/src/aurora/types/str.rs | 110 ++++ sqlx-core/src/aurora/value.rs | 59 +++ sqlx-core/src/common/statement_cache.rs | 2 +- sqlx-core/src/lib.rs | 4 + src/lib.rs | 4 + 24 files changed, 1687 insertions(+), 23 deletions(-) create mode 100644 sqlx-core/src/aurora/arguments.rs create mode 100644 sqlx-core/src/aurora/column.rs create mode 100644 sqlx-core/src/aurora/connection/executor.rs create mode 100644 sqlx-core/src/aurora/connection/mod.rs create mode 100644 sqlx-core/src/aurora/database.rs create mode 100644 sqlx-core/src/aurora/done.rs create mode 100644 sqlx-core/src/aurora/error.rs create mode 100644 sqlx-core/src/aurora/mod.rs create mode 100644 sqlx-core/src/aurora/options/connect.rs create mode 100644 sqlx-core/src/aurora/options/mod.rs create mode 100644 sqlx-core/src/aurora/options/parse.rs create mode 100644 sqlx-core/src/aurora/row.rs create mode 100644 sqlx-core/src/aurora/statement.rs create mode 100644 sqlx-core/src/aurora/transaction.rs create mode 100644 sqlx-core/src/aurora/type_info.rs create mode 100644 sqlx-core/src/aurora/types/mod.rs create mode 100644 sqlx-core/src/aurora/types/str.rs create mode 100644 sqlx-core/src/aurora/value.rs diff --git a/Cargo.lock b/Cargo.lock index 24f199fd9c..fd2d4289e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,6 +74,18 @@ version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7" +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "async-attributes" version = "1.1.1" @@ -170,7 +182,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c238bd34d425674d8003b8d674cc04baf74e1b71802f3c62451e3bf86f2858ef" dependencies = [ "futures-lite", - "rustls", + "rustls 0.18.1", "webpki", ] @@ -196,7 +208,7 @@ dependencies = [ "memchr", "num_cpus", "once_cell", - "pin-project-lite", + "pin-project-lite 0.1.11", "pin-utils", "slab", "wasm-bindgen-futures", @@ -263,6 +275,12 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "base64" version = "0.12.3" @@ -298,6 +316,17 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -356,6 +385,9 @@ name = "bytes" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" +dependencies = [ + "serde", +] [[package]] name = "cache-padded" @@ -411,6 +443,7 @@ dependencies = [ "libc", "num-integer", "num-traits", + "serde", "time 0.1.44", "winapi 0.3.9", ] @@ -519,22 +552,44 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab" +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "copyless" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys 0.7.0", + "libc", +] + [[package]] name = "core-foundation" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.8.2", "libc", ] +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + [[package]] name = "core-foundation-sys" version = "0.8.2" @@ -556,6 +611,15 @@ dependencies = [ "build_const", ] +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "criterion" version = "0.3.3" @@ -671,6 +735,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "crypto-mac" version = "0.9.1" @@ -703,6 +777,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ct-logs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3686f5fa27dbc1d76c751300376e167c5a43387f44bb451fd1c24776e49113" +dependencies = [ + "sct", +] + [[package]] name = "derive_more" version = "0.99.11" @@ -735,6 +818,27 @@ dependencies = [ "generic-array", ] +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if 0.1.10", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + [[package]] name = "discard" version = "1.0.4" @@ -905,7 +1009,7 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite", + "pin-project-lite 0.1.11", "waker-fn", ] @@ -949,7 +1053,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project", + "pin-project 1.0.1", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -1007,6 +1111,26 @@ dependencies = [ "web-sys", ] +[[package]] +name = "h2" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", + "tracing-futures", +] + [[package]] name = "half" version = "1.6.0" @@ -1043,16 +1167,59 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest", +] + [[package]] name = "hmac" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff" dependencies = [ - "crypto-mac", + "crypto-mac 0.9.1", "digest", ] +[[package]] +name = "http" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" + +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" + [[package]] name = "humantime" version = "1.3.0" @@ -1062,6 +1229,61 @@ dependencies = [ "quick-error", ] +[[package]] +name = "hyper" +version = "0.13.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project 1.0.1", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac965ea399ec3a25ac7d13b8affd4b8f39325cca00858ddf5eb29b79e6b14b08" +dependencies = [ + "bytes", + "ct-logs", + "futures-util", + "hyper", + "log", + "rustls 0.17.0", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.13.1", + "webpki", +] + +[[package]] +name = "hyper-tls" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-tls", +] + [[package]] name = "idna" version = "0.2.0" @@ -1259,6 +1481,12 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + [[package]] name = "memchr" version = "2.3.4" @@ -1351,8 +1579,8 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", - "security-framework-sys", + "security-framework 2.0.0", + "security-framework-sys 2.0.0", "tempfile", ] @@ -1607,13 +1835,33 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pin-project" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" +dependencies = [ + "pin-project-internal 0.4.27", +] + [[package]] name = "pin-project" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841" dependencies = [ - "pin-project-internal", + "pin-project-internal 1.0.1", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1633,6 +1881,12 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" +[[package]] +name = "pin-project-lite" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" + [[package]] name = "pin-utils" version = "0.1.0" @@ -1808,6 +2062,17 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +dependencies = [ + "getrandom 0.1.15", + "redox_syscall", + "rust-argon2", +] + [[package]] name = "regex" version = "1.4.2" @@ -1893,6 +2158,107 @@ dependencies = [ "zeroize 1.1.1", ] +[[package]] +name = "rusoto_core" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e977941ee0658df96fca7291ecc6fc9a754600b21ad84b959eb1dbbc9d5abcc7" +dependencies = [ + "async-trait", + "base64 0.12.3", + "bytes", + "crc32fast", + "futures", + "http", + "hyper", + "hyper-rustls", + "hyper-tls", + "lazy_static", + "log", + "md5", + "percent-encoding", + "pin-project 0.4.27", + "rusoto_credential", + "rusoto_signature", + "rustc_version", + "serde", + "serde_json", + "tokio", + "xml-rs", +] + +[[package]] +name = "rusoto_credential" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac05563f83489b19b4d413607a30821ab08bbd9007d14fa05618da3ef09d8b" +dependencies = [ + "async-trait", + "chrono", + "dirs", + "futures", + "hyper", + "pin-project 0.4.27", + "regex", + "serde", + "serde_json", + "shlex", + "tokio", + "zeroize 1.1.1", +] + +[[package]] +name = "rusoto_rds_data" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec99b111f51e5ff929fd7d6aaf5ce8b4d60443a337f3e0a37dd95ea34fa09122" +dependencies = [ + "async-trait", + "bytes", + "futures", + "rusoto_core", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "rusoto_signature" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a740a88dde8ded81b6f2cff9cd5e054a5a2e38a38397260f7acdd2c85d17dd" +dependencies = [ + "base64 0.12.3", + "bytes", + "futures", + "hex", + "hmac 0.8.1", + "http", + "hyper", + "log", + "md5", + "percent-encoding", + "pin-project 0.4.27", + "rusoto_credential", + "rustc_version", + "serde", + "sha2", + "time 0.2.22", + "tokio", +] + +[[package]] +name = "rust-argon2" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" +dependencies = [ + "base64 0.13.0", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils 0.8.0", +] + [[package]] name = "rust_decimal" version = "1.8.1" @@ -1912,6 +2278,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustls" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" +dependencies = [ + "base64 0.11.0", + "log", + "ring", + "sct", + "webpki", +] + [[package]] name = "rustls" version = "0.18.1" @@ -1925,6 +2304,18 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustls-native-certs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75ffeb84a6bd9d014713119542ce415db3a3e4748f0bfce1e1416cd224a23a5" +dependencies = [ + "openssl-probe", + "rustls 0.17.0", + "schannel", + "security-framework 0.4.4", +] + [[package]] name = "ryu" version = "1.0.5" @@ -1966,6 +2357,19 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" +dependencies = [ + "bitflags", + "core-foundation 0.7.0", + "core-foundation-sys 0.7.0", + "libc", + "security-framework-sys 0.4.3", +] + [[package]] name = "security-framework" version = "2.0.0" @@ -1973,10 +2377,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" dependencies = [ "bitflags", - "core-foundation", - "core-foundation-sys", + "core-foundation 0.9.1", + "core-foundation-sys 0.8.2", + "libc", + "security-framework-sys 2.0.0", +] + +[[package]] +name = "security-framework-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" +dependencies = [ + "core-foundation-sys 0.7.0", "libc", - "security-framework-sys", ] [[package]] @@ -1985,7 +2399,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.8.2", "libc", ] @@ -2079,6 +2493,12 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + [[package]] name = "signal-hook-registry" version = "1.2.2" @@ -2221,7 +2641,7 @@ dependencies = [ "futures-util", "generic-array", "hex", - "hmac", + "hmac 0.9.0", "ipnetwork", "itoa", "libc", @@ -2237,8 +2657,10 @@ dependencies = [ "rand", "regex", "rsa", + "rusoto_core", + "rusoto_rds_data", "rust_decimal", - "rustls", + "rustls 0.18.1", "serde", "serde_json", "sha-1", @@ -2335,7 +2757,7 @@ dependencies = [ "once_cell", "tokio", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.14.1", ] [[package]] @@ -2673,7 +3095,7 @@ dependencies = [ "mio-named-pipes", "mio-uds", "num_cpus", - "pin-project-lite", + "pin-project-lite 0.1.11", "signal-hook-registry", "slab", "tokio-macros", @@ -2701,6 +3123,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4" +dependencies = [ + "futures-core", + "rustls 0.17.0", + "tokio", + "webpki", +] + [[package]] name = "tokio-rustls" version = "0.14.1" @@ -2708,11 +3142,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e12831b255bcfa39dc0436b01e19fea231a37db570686c06ee72c423479f889a" dependencies = [ "futures-core", - "rustls", + "rustls 0.18.1", "tokio", "webpki", ] +[[package]] +name = "tokio-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite 0.1.11", + "tokio", +] + [[package]] name = "toml" version = "0.5.7" @@ -2722,6 +3180,49 @@ dependencies = [ "serde", ] +[[package]] +name = "tower-service" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" + +[[package]] +name = "tracing" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" +dependencies = [ + "cfg-if 1.0.0", + "log", + "pin-project-lite 0.2.0", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +dependencies = [ + "pin-project 0.4.27", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + [[package]] name = "trybuild" version = "1.0.35" @@ -2843,6 +3344,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -3018,6 +3529,12 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "xml-rs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" + [[package]] name = "zeroize" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index 7c7610cd91..92910fea4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ offline = [ "sqlx-macros/offline", "sqlx-core/offline" ] # intended mainly for CI and docs all = [ "tls", "all-databases", "all-types" ] -all-databases = [ "mysql", "sqlite", "postgres", "mssql", "any" ] +all-databases = [ "mysql", "sqlite", "postgres", "mssql", "any", "aurora" ] all-types = [ "bigdecimal", "decimal", "json", "time", "chrono", "ipnetwork", "uuid", "bit-vec" ] # previous runtimes, available as features for error messages better than just @@ -79,6 +79,7 @@ postgres = [ "sqlx-core/postgres", "sqlx-macros/postgres" ] mysql = [ "sqlx-core/mysql", "sqlx-macros/mysql" ] sqlite = [ "sqlx-core/sqlite", "sqlx-macros/sqlite" ] mssql = [ "sqlx-core/mssql", "sqlx-macros/mssql" ] +aurora = [ "sqlx-core/aurora" ] # types bigdecimal = [ "sqlx-core/bigdecimal", "sqlx-macros/bigdecimal" ] diff --git a/sqlx-core/Cargo.toml b/sqlx-core/Cargo.toml index 8ab9740df8..698f8fd1ba 100644 --- a/sqlx-core/Cargo.toml +++ b/sqlx-core/Cargo.toml @@ -20,11 +20,12 @@ default = [ "migrate" ] migrate = [ "sha2", "crc" ] # databases -all-databases = [ "postgres", "mysql", "sqlite", "mssql", "any" ] +all-databases = [ "postgres", "mysql", "sqlite", "mssql", "aurora", "any" ] 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", "rsa" ] sqlite = [ "libsqlite3-sys" ] mssql = [ "uuid", "encoding_rs", "regex" ] +aurora = [ "rusoto_core", "rusoto_rds_data" ] any = [] # types @@ -46,8 +47,8 @@ runtime-tokio-rustls = [ "sqlx-rt/runtime-tokio-rustls", "_tls-rustls", "_rt-tok _rt-actix = [] _rt-async-std = [] _rt-tokio = [] -_tls-native-tls = [] -_tls-rustls = [ "rustls", "webpki", "webpki-roots" ] +_tls-native-tls = [ "rusoto_core/native-tls", "rusoto_rds_data/native-tls" ] +_tls-rustls = [ "rustls", "webpki", "webpki-roots", "rusoto_core/rustls", "rusoto_rds_data/rustls" ] # support offline/decoupled building (enables serialization of `Describe`) offline = [ "serde", "either/serde" ] @@ -107,3 +108,7 @@ webpki-roots = { version = "0.20.0", optional = true } whoami = "0.9.0" stringprep = "0.1.2" lru-cache = "0.1.2" + +# Auroroa dependencies +rusoto_core = { version = "0.45.0", default-features = false, optional = true } +rusoto_rds_data = { version = "0.45.0", default-features = false, features = ["serialize_structs", "deserialize_structs"], optional = true } \ No newline at end of file diff --git a/sqlx-core/src/aurora/arguments.rs b/sqlx-core/src/aurora/arguments.rs new file mode 100644 index 0000000000..4b80607dd1 --- /dev/null +++ b/sqlx-core/src/aurora/arguments.rs @@ -0,0 +1,27 @@ +use crate::arguments::Arguments; +use crate::aurora::Aurora; +use crate::encode::Encode; +use crate::types::Type; + +use rusoto_rds_data::SqlParameter; + +/// Implementation of [`Arguments`] for Aurora. +#[derive(Default)] +pub struct AuroraArguments { + pub(crate) parameters: Vec, +} + +impl<'q> Arguments<'q> for AuroraArguments { + type Database = Aurora; + + fn reserve(&mut self, additional: usize, _size: usize) { + self.parameters.reserve(additional); + } + + fn add(&mut self, value: T) + where + T: Encode<'q, Self::Database> + Type, + { + let _ = value.encode(&mut self.parameters); + } +} diff --git a/sqlx-core/src/aurora/column.rs b/sqlx-core/src/aurora/column.rs new file mode 100644 index 0000000000..83de3174d6 --- /dev/null +++ b/sqlx-core/src/aurora/column.rs @@ -0,0 +1,29 @@ +use crate::aurora::type_info::AuroraTypeInfo; +use crate::aurora::Aurora; +use crate::column::Column; +use crate::ext::ustr::UStr; + +#[derive(Debug, Clone)] +pub struct AuroraColumn { + pub(crate) ordinal: usize, + pub(crate) name: UStr, + pub(crate) type_info: AuroraTypeInfo, +} + +impl crate::column::private_column::Sealed for AuroraColumn {} + +impl Column for AuroraColumn { + type Database = Aurora; + + fn ordinal(&self) -> usize { + self.ordinal + } + + fn name(&self) -> &str { + &*self.name + } + + fn type_info(&self) -> &AuroraTypeInfo { + &self.type_info + } +} diff --git a/sqlx-core/src/aurora/connection/executor.rs b/sqlx-core/src/aurora/connection/executor.rs new file mode 100644 index 0000000000..6d11f4682e --- /dev/null +++ b/sqlx-core/src/aurora/connection/executor.rs @@ -0,0 +1,183 @@ +use super::AuroraConnection; +use crate::aurora::error::AuroraDatabaseError; +use crate::aurora::statement::AuroraStatementMetadata; +use crate::aurora::type_info::AuroraType; +use crate::aurora::{ + Aurora, AuroraArguments, AuroraColumn, AuroraDone, AuroraRow, AuroraStatement, AuroraTypeInfo, +}; +use crate::describe::Describe; +use crate::error::Error; +use crate::executor::{Execute, Executor}; +use crate::ext::ustr::UStr; + +use either::Either; +use futures_core::future::BoxFuture; +use futures_core::stream::BoxStream; +use futures_core::Stream; +use futures_util::stream; +use futures_util::{pin_mut, TryStreamExt}; +use rusoto_rds_data::{ExecuteStatementRequest, ExecuteStatementResponse, RdsData}; +use std::borrow::Cow; +use std::sync::Arc; + +impl AuroraConnection { + async fn run<'e, 'c: 'e, 'q: 'e>( + &'c mut self, + query: &'q str, + arguments: Option, + ) -> Result, Error>> + 'e, Error> { + // TODO: is this correct? + let transaction_id = self.transaction_ids.last().cloned(); + + let request = ExecuteStatementRequest { + sql: query.to_owned(), + parameters: arguments.map(|m| m.parameters), + resource_arn: self.resource_arn.clone(), + secret_arn: self.secret_arn.clone(), + database: self.database.clone(), + schema: self.schema.clone(), + transaction_id, + include_result_metadata: Some(true), + ..Default::default() + }; + + let ExecuteStatementResponse { + column_metadata, + number_of_records_updated, + records, + .. + } = self + .client + .execute_statement(request) + .await + .map_err(AuroraDatabaseError)?; + + let rows_affected = number_of_records_updated.unwrap_or_default() as u64; + let column_metadata = column_metadata.unwrap_or_default(); + + let mut rows = records + .unwrap_or_default() + .into_iter() + .map(|fields| { + let columns: Vec<_> = fields + .iter() + .zip(&column_metadata) + .enumerate() + .map(|(ordinal, (field, metadata))| AuroraColumn { + ordinal, + name: UStr::new(metadata.name.as_deref().unwrap_or_default()), + type_info: AuroraTypeInfo::from(field), + }) + .collect(); + + let column_names = columns + .iter() + .map(|column| (column.name.clone(), column.ordinal)) + .collect(); + let parameters = columns.iter().map(|column| column.type_info).collect(); + + let metadata = Arc::new(AuroraStatementMetadata { + columns, + column_names, + parameters, + }); + + let row = AuroraRow { fields, metadata }; + + Ok(Either::Right(row)) + }) + .collect::>(); + + rows.push(Ok(Either::Left(AuroraDone { rows_affected }))); + + Ok(stream::iter(rows)) + } +} + +impl<'c> Executor<'c> for &'c mut AuroraConnection { + type Database = Aurora; + + fn fetch_many<'e, 'q: 'e, E: 'q>( + self, + mut query: E, + ) -> BoxStream<'e, Result, Error>> + where + 'c: 'e, + E: Execute<'q, Self::Database>, + { + let sql = query.sql(); + let arguments = query.take_arguments(); + + // TODO: implement statement caching? + //let metadata = query.statement(); + //let persistent = query.persistent(); + + Box::pin(try_stream! { + let s = self.run(sql, arguments).await?; + pin_mut!(s); + + while let Some(v) = s.try_next().await? { + r#yield!(v); + } + + Ok(()) + }) + } + + fn fetch_optional<'e, 'q: 'e, E: 'q>( + self, + query: E, + ) -> BoxFuture<'e, Result, Error>> + where + 'c: 'e, + E: Execute<'q, Self::Database>, + { + let mut s = self.fetch_many(query); + + Box::pin(async move { + while let Some(v) = s.try_next().await? { + if let Either::Right(r) = v { + return Ok(Some(r)); + } + } + + Ok(None) + }) + } + + fn prepare_with<'e, 'q: 'e>( + self, + sql: &'q str, + _parameters: &[AuroraTypeInfo], + ) -> BoxFuture<'e, Result, Error>> + where + 'c: 'e, + { + Box::pin(async move { + Ok(AuroraStatement { + sql: Cow::Borrowed(sql), + metadata: Default::default(), + }) + }) + } + + fn describe<'e, 'q: 'e>( + self, + sql: &'q str, + ) -> BoxFuture<'e, Result, Error>> + where + 'c: 'e, + { + Box::pin(async move { + let metadata: AuroraStatementMetadata = Default::default(); + + let nullable = Vec::with_capacity(metadata.columns.len()); + + Ok(Describe { + nullable, + columns: metadata.columns, + parameters: None, + }) + }) + } +} diff --git a/sqlx-core/src/aurora/connection/mod.rs b/sqlx-core/src/aurora/connection/mod.rs new file mode 100644 index 0000000000..697885e153 --- /dev/null +++ b/sqlx-core/src/aurora/connection/mod.rs @@ -0,0 +1,115 @@ +use crate::aurora::options::AuroraConnectOptions; +use crate::aurora::statement::AuroraStatementMetadata; +use crate::aurora::Aurora; +use crate::common::StatementCache; +use crate::connection::Connection; +use crate::connection::LogSettings; +use crate::error::Error; +use crate::transaction::Transaction; + +use futures_core::future::BoxFuture; +use futures_util::future; +use rusoto_rds_data::RdsDataClient; +use std::fmt::{self, Debug, Formatter}; +use std::sync::Arc; + +mod executor; + +/// A connection to an Aurora database. +pub struct AuroraConnection { + pub(crate) resource_arn: String, + pub(crate) secret_arn: String, + pub(crate) database: Option, + pub(crate) schema: Option, + + pub(crate) client: RdsDataClient, + + // Transaction identifiers + pub(crate) transaction_ids: Vec, + + // cache statement by query string to the id and columns + cache_statement: StatementCache<(u32, Arc)>, + + log_settings: LogSettings, +} + +impl AuroraConnection { + pub(crate) fn new(options: &AuroraConnectOptions) -> Result { + let region = options.region.parse().map_err(Error::config)?; + + let resource_arn = options + .resource_arn + .as_ref() + .cloned() + .ok_or_else(|| Error::Configuration("Resource ARN not specified".into()))?; + let secret_arn = options + .secret_arn + .as_ref() + .cloned() + .ok_or_else(|| Error::Configuration("Secret ARN not specified".into()))?; + + let client = RdsDataClient::new(region); + + Ok(Self { + resource_arn, + secret_arn, + database: options.database.clone(), + schema: options.schema.clone(), + client, + transaction_ids: vec![], + cache_statement: StatementCache::new(options.statement_cache_capacity), + log_settings: options.log_settings.clone(), + }) + } +} + +impl Debug for AuroraConnection { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("AuroraConnection").finish() + } +} + +impl Connection for AuroraConnection { + type Database = Aurora; + + type Options = AuroraConnectOptions; + + fn close(self) -> BoxFuture<'static, Result<(), Error>> { + // nothing explicit to do; connection will close in drop + Box::pin(future::ok(())) + } + + fn ping(&mut self) -> BoxFuture<'_, Result<(), Error>> { + // For Aurora connections, PING does effectively nothing + Box::pin(future::ok(())) + } + + fn begin(&mut self) -> BoxFuture<'_, Result, Error>> + where + Self: Sized, + { + Transaction::begin(self) + } + + fn cached_statements_size(&self) -> usize { + self.cache_statement.len() + } + + fn clear_cached_statements(&mut self) -> BoxFuture<'_, Result<(), Error>> { + Box::pin(async move { + self.cache_statement.clear(); + Ok(()) + }) + } + + #[doc(hidden)] + fn flush(&mut self) -> BoxFuture<'_, Result<(), Error>> { + // For SQLite, FLUSH does effectively nothing + Box::pin(future::ok(())) + } + + #[doc(hidden)] + fn should_flush(&self) -> bool { + false + } +} diff --git a/sqlx-core/src/aurora/database.rs b/sqlx-core/src/aurora/database.rs new file mode 100644 index 0000000000..df09ad0713 --- /dev/null +++ b/sqlx-core/src/aurora/database.rs @@ -0,0 +1,54 @@ +use crate::aurora::arguments::AuroraArguments; +use crate::aurora::column::AuroraColumn; +use crate::aurora::connection::AuroraConnection; +use crate::aurora::done::AuroraDone; +use crate::aurora::row::AuroraRow; +use crate::aurora::statement::AuroraStatement; +use crate::aurora::transaction::AuroraTransactionManager; +use crate::aurora::type_info::AuroraTypeInfo; +use crate::aurora::value::{AuroraValue, AuroraValueRef}; +use crate::database::{Database, HasArguments, HasStatement, HasStatementCache, HasValueRef}; + +use rusoto_rds_data::SqlParameter; + +/// Aurora serverless database driver. +#[derive(Debug)] +pub struct Aurora; + +impl Database for Aurora { + type Connection = AuroraConnection; + + type TransactionManager = AuroraTransactionManager; + + type Row = AuroraRow; + + type Done = AuroraDone; + + type Column = AuroraColumn; + + type TypeInfo = AuroraTypeInfo; + + type Value = AuroraValue; +} + +impl<'r> HasValueRef<'r> for Aurora { + type Database = Aurora; + + type ValueRef = AuroraValueRef<'r>; +} + +impl HasArguments<'_> for Aurora { + type Database = Aurora; + + type Arguments = AuroraArguments; + + type ArgumentBuffer = Vec; +} + +impl<'q> HasStatement<'q> for Aurora { + type Database = Aurora; + + type Statement = AuroraStatement<'q>; +} + +impl HasStatementCache for Aurora {} diff --git a/sqlx-core/src/aurora/done.rs b/sqlx-core/src/aurora/done.rs new file mode 100644 index 0000000000..0efe26f6c2 --- /dev/null +++ b/sqlx-core/src/aurora/done.rs @@ -0,0 +1,24 @@ +use crate::aurora::Aurora; +use crate::done::Done; +use std::iter::{Extend, IntoIterator}; + +#[derive(Debug, Default)] +pub struct AuroraDone { + pub(super) rows_affected: u64, +} + +impl Done for AuroraDone { + type Database = Aurora; + + fn rows_affected(&self) -> u64 { + self.rows_affected + } +} + +impl Extend for AuroraDone { + fn extend>(&mut self, iter: T) { + for elem in iter { + self.rows_affected += elem.rows_affected; + } + } +} diff --git a/sqlx-core/src/aurora/error.rs b/sqlx-core/src/aurora/error.rs new file mode 100644 index 0000000000..e1b5477e1a --- /dev/null +++ b/sqlx-core/src/aurora/error.rs @@ -0,0 +1,43 @@ +use crate::error::DatabaseError; + +use rusoto_core::RusotoError; +use std::borrow::Cow; +use std::error::Error; +use std::fmt::{self, Debug, Display, Formatter}; + +/// An error returned from the Aurora database. +#[derive(Debug)] +pub struct AuroraDatabaseError(pub(crate) RusotoError); + +impl Display for AuroraDatabaseError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("{}", self.0)) + } +} + +impl Error for AuroraDatabaseError {} + +impl DatabaseError for AuroraDatabaseError { + fn message(&self) -> &str { + self.0.description() + } + + fn code(&self) -> Option> { + None + } + + #[doc(hidden)] + fn as_error(&self) -> &(dyn Error + Send + Sync + 'static) { + self + } + + #[doc(hidden)] + fn as_error_mut(&mut self) -> &mut (dyn Error + Send + Sync + 'static) { + self + } + + #[doc(hidden)] + fn into_error(self: Box) -> Box { + self + } +} diff --git a/sqlx-core/src/aurora/mod.rs b/sqlx-core/src/aurora/mod.rs new file mode 100644 index 0000000000..4e85793e17 --- /dev/null +++ b/sqlx-core/src/aurora/mod.rs @@ -0,0 +1,42 @@ +mod arguments; +mod column; +mod connection; +mod database; +mod done; +mod error; +mod options; +mod row; +mod statement; +mod transaction; +mod type_info; +mod types; +mod value; + +pub use arguments::AuroraArguments; +pub use column::AuroraColumn; +pub use connection::AuroraConnection; +pub use database::Aurora; +pub use done::AuroraDone; +pub use error::AuroraDatabaseError; +pub use options::AuroraConnectOptions; +pub use row::AuroraRow; +pub use statement::AuroraStatement; +pub use transaction::AuroraTransactionManager; +pub use type_info::AuroraTypeInfo; +pub use value::{AuroraValue, AuroraValueRef}; + +/// An alias for [`Pool`][crate::pool::Pool], specialized for Aurora. +pub type AuroraPool = crate::pool::Pool; + +/// An alias for [`PoolOptions`][crate::pool::PoolOptions], specialized for Aurora. +pub type PgPoolOptions = crate::pool::PoolOptions; + +impl_into_arguments_for_arguments!(AuroraArguments); +impl_executor_for_pool_connection!(Aurora, AuroraConnection, AuroraRow); +impl_executor_for_transaction!(Aurora, AuroraRow); +impl_map_row!(Aurora, AuroraRow); +impl_acquire!(Aurora, AuroraConnection); +impl_column_index_for_row!(AuroraRow); +impl_column_index_for_statement!(AuroraStatement); +impl_into_maybe_pool!(Aurora, AuroraConnection); +impl_encode_for_option!(Aurora); diff --git a/sqlx-core/src/aurora/options/connect.rs b/sqlx-core/src/aurora/options/connect.rs new file mode 100644 index 0000000000..55b25c0677 --- /dev/null +++ b/sqlx-core/src/aurora/options/connect.rs @@ -0,0 +1,29 @@ +use crate::aurora::connection::AuroraConnection; +use crate::aurora::options::AuroraConnectOptions; +use crate::connection::ConnectOptions; +use crate::error::Error; + +use futures_core::future::BoxFuture; +use log::LevelFilter; +use std::time::Duration; + +impl ConnectOptions for AuroraConnectOptions { + type Connection = AuroraConnection; + + fn connect(&self) -> BoxFuture<'_, Result> + where + Self::Connection: Sized, + { + Box::pin(async move { AuroraConnection::new(self) }) + } + + fn log_statements(&mut self, level: LevelFilter) -> &mut Self { + self.log_settings.log_statements(level); + self + } + + fn log_slow_statements(&mut self, level: LevelFilter, duration: Duration) -> &mut Self { + self.log_settings.log_slow_statements(level, duration); + self + } +} diff --git a/sqlx-core/src/aurora/options/mod.rs b/sqlx-core/src/aurora/options/mod.rs new file mode 100644 index 0000000000..d26a00edd3 --- /dev/null +++ b/sqlx-core/src/aurora/options/mod.rs @@ -0,0 +1,79 @@ +use std::env::var; + +mod connect; +mod parse; +use crate::connection::LogSettings; + +#[derive(Debug, Clone)] +pub struct AuroraConnectOptions { + pub(crate) region: String, + pub(crate) resource_arn: Option, + pub(crate) secret_arn: Option, + pub(crate) database: Option, + pub(crate) schema: Option, + pub(crate) statement_cache_capacity: usize, + pub(crate) log_settings: LogSettings, +} + +impl Default for AuroraConnectOptions { + fn default() -> Self { + Self::new() + } +} + +impl AuroraConnectOptions { + pub fn new() -> Self { + let region = var("AURORA_REGION") + .ok() + .unwrap_or_else(|| "us-east-1".to_owned()); + + let resource_arn = var("AURORA_RESOURCE_ARN").ok(); + let secret_arn = var("AURORA_SECRET_ARN").ok(); + + AuroraConnectOptions { + region, + resource_arn, + secret_arn, + database: None, + schema: None, + statement_cache_capacity: 100, + log_settings: Default::default(), + } + } + + pub fn region(mut self, region: &str) -> Self { + self.region = region.to_owned(); + self + } + + pub fn resource_arn(mut self, resource_arn: &str) -> Self { + self.resource_arn = Some(resource_arn.to_owned()); + self + } + + pub fn secret_arn(mut self, secret_arn: &str) -> Self { + self.secret_arn = Some(secret_arn.to_owned()); + self + } + + pub fn schema(mut self, schema: &str) -> Self { + self.schema = Some(schema.to_owned()); + self + } + + pub fn database(mut self, database: &str) -> Self { + self.database = Some(database.to_owned()); + self + } + + /// Sets the capacity of the connection's statement cache in a number of stored + /// distinct statements. Caching is handled using LRU, meaning when the + /// amount of queries hits the defined limit, the oldest statement will get + /// dropped. + /// + /// The default cache capacity is 100 statements. + pub fn statement_cache_capacity(mut self, capacity: usize) -> Self { + self.statement_cache_capacity = capacity; + self + } +} diff --git a/sqlx-core/src/aurora/options/parse.rs b/sqlx-core/src/aurora/options/parse.rs new file mode 100644 index 0000000000..2545f50fa5 --- /dev/null +++ b/sqlx-core/src/aurora/options/parse.rs @@ -0,0 +1,35 @@ +use crate::aurora::options::AuroraConnectOptions; +use crate::error::Error; +use std::str::FromStr; +use url::Url; + +impl FromStr for AuroraConnectOptions { + type Err = Error; + + fn from_str(s: &str) -> Result { + let url: Url = s.parse().map_err(Error::config)?; + + let mut options = Self::default(); + + for (key, value) in url.query_pairs().into_iter() { + match &*key { + "statement-cache-capacity" => { + options = + options.statement_cache_capacity(value.parse().map_err(Error::config)?); + } + "region" => { + options = options.region(&*value); + } + "resource-arn" => { + options = options.resource_arn(&*value); + } + "secret-arn" => { + options = options.secret_arn(&*value); + } + _ => log::warn!("ignoring unrecognized connect parameter: {}={}", key, value), + } + } + + Ok(options) + } +} diff --git a/sqlx-core/src/aurora/row.rs b/sqlx-core/src/aurora/row.rs new file mode 100644 index 0000000000..f241594a06 --- /dev/null +++ b/sqlx-core/src/aurora/row.rs @@ -0,0 +1,50 @@ +use crate::aurora::column::AuroraColumn; +use crate::aurora::statement::AuroraStatementMetadata; +use crate::aurora::value::AuroraValueRef; +use crate::aurora::Aurora; +use crate::column::ColumnIndex; +use crate::error::Error; +use crate::row::Row; + +use rusoto_rds_data::Field; +use std::sync::Arc; + +/// Implementation of [`Row`] for Aurora. +pub struct AuroraRow { + pub(crate) fields: Vec, + pub(crate) metadata: Arc, +} + +impl crate::row::private_row::Sealed for AuroraRow {} + +impl Row for AuroraRow { + type Database = Aurora; + + fn columns(&self) -> &[AuroraColumn] { + &self.metadata.columns + } + + fn try_get_raw(&self, index: I) -> Result, Error> + where + I: ColumnIndex, + { + let index = index.index(self)?; + let field = &self.fields[index]; + let column = &self.metadata.columns[index]; + + Ok(AuroraValueRef { + field, + type_info: column.type_info, + }) + } +} + +impl ColumnIndex for &'_ str { + fn index(&self, row: &AuroraRow) -> Result { + row.metadata + .column_names + .get(*self) + .ok_or_else(|| Error::ColumnNotFound((*self).into())) + .map(|v| *v) + } +} diff --git a/sqlx-core/src/aurora/statement.rs b/sqlx-core/src/aurora/statement.rs new file mode 100644 index 0000000000..54bafcea5f --- /dev/null +++ b/sqlx-core/src/aurora/statement.rs @@ -0,0 +1,61 @@ +use crate::aurora::arguments::AuroraArguments; +use crate::aurora::column::AuroraColumn; +use crate::aurora::type_info::AuroraTypeInfo; +use crate::aurora::Aurora; +use crate::column::ColumnIndex; +use crate::error::Error; +use crate::ext::ustr::UStr; +use crate::statement::Statement; +use crate::HashMap; +use either::Either; +use std::borrow::Cow; +use std::sync::Arc; + +#[derive(Debug, Clone)] +pub struct AuroraStatement<'q> { + pub(crate) sql: Cow<'q, str>, + pub(crate) metadata: Arc, +} + +#[derive(Debug, Default)] +pub(crate) struct AuroraStatementMetadata { + pub(crate) columns: Vec, + pub(crate) column_names: HashMap, + pub(crate) parameters: Vec, +} + +impl<'q> Statement<'q> for AuroraStatement<'q> { + type Database = Aurora; + + fn to_owned(&self) -> AuroraStatement<'static> { + AuroraStatement::<'static> { + sql: Cow::Owned(self.sql.clone().into_owned()), + metadata: self.metadata.clone(), + } + } + + fn sql(&self) -> &str { + &self.sql + } + + fn parameters(&self) -> Option> { + Some(Either::Left(&self.metadata.parameters)) + } + + fn columns(&self) -> &[AuroraColumn] { + &self.metadata.columns + } + + impl_statement_query!(AuroraArguments); +} + +impl ColumnIndex> for &'_ str { + fn index(&self, statement: &AuroraStatement<'_>) -> Result { + statement + .metadata + .column_names + .get(*self) + .ok_or_else(|| Error::ColumnNotFound((*self).into())) + .map(|v| *v) + } +} diff --git a/sqlx-core/src/aurora/transaction.rs b/sqlx-core/src/aurora/transaction.rs new file mode 100644 index 0000000000..b3b92c05d0 --- /dev/null +++ b/sqlx-core/src/aurora/transaction.rs @@ -0,0 +1,97 @@ +use futures_core::future::BoxFuture; + +use crate::aurora::connection::AuroraConnection; +use crate::aurora::error::AuroraDatabaseError; +use crate::aurora::Aurora; +use crate::error::Error; +use crate::transaction::TransactionManager; + +use rusoto_rds_data::{ + BeginTransactionRequest, CommitTransactionRequest, RdsData, RollbackTransactionRequest, +}; + +/// Implementation of [`TransactionManager`] for Aurora. +pub struct AuroraTransactionManager; + +impl TransactionManager for AuroraTransactionManager { + type Database = Aurora; + + fn begin(conn: &mut AuroraConnection) -> BoxFuture<'_, Result<(), Error>> { + Box::pin(async move { + let request = BeginTransactionRequest { + database: conn.database.clone(), + resource_arn: conn.resource_arn.clone(), + schema: conn.schema.clone(), + secret_arn: conn.secret_arn.clone(), + }; + + let response = conn + .client + .begin_transaction(request) + .await + .map_err(AuroraDatabaseError)?; + + // TODO: when can `BeginTransactionResponse.transaction_id` be `None` + // and what do we do in that instance? + if let Some(id) = response.transaction_id { + conn.transaction_ids.push(id); + } + + Ok(()) + }) + } + + fn commit(conn: &mut AuroraConnection) -> BoxFuture<'_, Result<(), Error>> { + Box::pin(async move { + if let Some(id) = conn.transaction_ids.last() { + let request = CommitTransactionRequest { + resource_arn: conn.resource_arn.clone(), + secret_arn: conn.secret_arn.clone(), + transaction_id: id.clone(), + }; + + let _response = conn + .client + .commit_transaction(request) + .await + .map_err(AuroraDatabaseError)?; + + // TODO: what does `CommitTransactionResponse.transaction_status` + // signify and how should we handle it? + + conn.transaction_ids.pop(); + } + + Ok(()) + }) + } + + fn rollback(conn: &mut AuroraConnection) -> BoxFuture<'_, Result<(), Error>> { + Box::pin(async move { + if let Some(id) = conn.transaction_ids.last() { + let request = RollbackTransactionRequest { + resource_arn: conn.resource_arn.clone(), + secret_arn: conn.secret_arn.clone(), + transaction_id: id.clone(), + }; + + let _response = conn + .client + .rollback_transaction(request) + .await + .map_err(AuroraDatabaseError)?; + + // TODO: what does `RollbackTransactionRequest.transaction_status` + // signify and how should we handle it? + + conn.transaction_ids.pop(); + } + + Ok(()) + }) + } + + fn start_rollback(conn: &mut AuroraConnection) { + // TODO: Not sure how to implement this + } +} diff --git a/sqlx-core/src/aurora/type_info.rs b/sqlx-core/src/aurora/type_info.rs new file mode 100644 index 0000000000..2bda7da875 --- /dev/null +++ b/sqlx-core/src/aurora/type_info.rs @@ -0,0 +1,95 @@ +use crate::type_info::TypeInfo; + +use rusoto_rds_data::Field; +use std::fmt::{self, Display, Formatter}; + +/// Type information for an Aurora type. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct AuroraTypeInfo(pub(crate) AuroraType); + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum AuroraType { + Blob, + Boolean, + BooleanArray, + Double, + DoubleArray, + Long, + LongArray, + String, + StringArray, + Decimal, + DecimalArray, + Timestamp, + TimestampArray, + Date, + DateArray, + Time, + TimeArray, +} + +impl AuroraType { + pub(crate) fn display_name(&self) -> &str { + match self { + AuroraType::Blob => "BLOB", + AuroraType::Boolean => "BOOL", + AuroraType::BooleanArray => "BOOL[]", + AuroraType::Double => "DOUBLE", + AuroraType::DoubleArray => "DOUBLE[]", + AuroraType::Long => "LONG", + AuroraType::LongArray => "LONG[]", + AuroraType::String => "STRING", + AuroraType::StringArray => "STRING[]", + AuroraType::Decimal => "DECIMAL", + AuroraType::DecimalArray => "DECIMAL[]", + AuroraType::Timestamp => "TIMESTAMP", + AuroraType::TimestampArray => "TIMESTAMP[]", + AuroraType::Date => "DATE", + AuroraType::DateArray => "DATE[]", + AuroraType::Time => "TIME", + AuroraType::TimeArray => "TIME[]", + } + } +} + +impl TypeInfo for AuroraTypeInfo { + fn name(&self) -> &str { + self.0.display_name() + } + + fn is_null(&self) -> bool { + false + } +} + +impl Display for AuroraTypeInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.pad(self.name()) + } +} + +impl From<&Field> for AuroraTypeInfo { + fn from(field: &Field) -> Self { + if let Some(array_value) = &field.array_value { + if array_value.boolean_values.is_some() { + AuroraTypeInfo(AuroraType::BooleanArray) + } else if array_value.double_values.is_some() { + AuroraTypeInfo(AuroraType::DoubleArray) + } else if array_value.long_values.is_some() { + AuroraTypeInfo(AuroraType::LongArray) + } else { + AuroraTypeInfo(AuroraType::StringArray) + } + } else if field.blob_value.is_some() { + AuroraTypeInfo(AuroraType::Blob) + } else if field.boolean_value.is_some() { + AuroraTypeInfo(AuroraType::Boolean) + } else if field.double_value.is_some() { + AuroraTypeInfo(AuroraType::Double) + } else if field.long_value.is_some() { + AuroraTypeInfo(AuroraType::Long) + } else { + AuroraTypeInfo(AuroraType::String) + } + } +} diff --git a/sqlx-core/src/aurora/types/mod.rs b/sqlx-core/src/aurora/types/mod.rs new file mode 100644 index 0000000000..1dfe386a08 --- /dev/null +++ b/sqlx-core/src/aurora/types/mod.rs @@ -0,0 +1 @@ +mod str; diff --git a/sqlx-core/src/aurora/types/str.rs b/sqlx-core/src/aurora/types/str.rs new file mode 100644 index 0000000000..7efb6852c6 --- /dev/null +++ b/sqlx-core/src/aurora/types/str.rs @@ -0,0 +1,110 @@ +use crate::decode::Decode; +use crate::encode::{Encode, IsNull}; +use crate::error::{BoxDynError, Error}; +//use crate::aurora::types::array_compatible; +use crate::aurora::type_info::AuroraType; +use crate::aurora::{Aurora, AuroraTypeInfo, AuroraValueRef}; +use crate::types::Type; + +use rusoto_rds_data::{Field, SqlParameter}; + +impl Type for str { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::String) + } + + fn compatible(ty: &AuroraTypeInfo) -> bool { + [AuroraTypeInfo(AuroraType::String)].contains(ty) + } +} + +impl Type for [&'_ str] { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::StringArray) + } + + fn compatible(ty: &AuroraTypeInfo) -> bool { + [AuroraTypeInfo(AuroraType::StringArray)].contains(ty) + } +} + +impl Type for Vec<&'_ str> { + fn type_info() -> AuroraTypeInfo { + <[&str] as Type>::type_info() + } + + fn compatible(ty: &AuroraTypeInfo) -> bool { + <[&str] as Type>::compatible(ty) + } +} + +impl Encode<'_, Aurora> for &'_ str { + fn encode_by_ref(&self, buf: &mut Vec) -> IsNull { + buf.push(SqlParameter { + value: Some(Field { + string_value: Some(self.to_string()), + ..Default::default() + }), + ..Default::default() + }); + + IsNull::No + } +} + +impl Encode<'_, Aurora> for String { + fn encode_by_ref(&self, buf: &mut Vec) -> IsNull { + <&str as Encode>::encode(&**self, buf) + } +} + +impl<'r> Decode<'r, Aurora> for &'r str { + fn decode(value: AuroraValueRef<'r>) -> Result { + Ok(value + .field + .string_value + .as_deref() + .ok_or_else(|| Error::Decode("Not a str value".into()))?) + } +} + +impl Type for String { + fn type_info() -> AuroraTypeInfo { + <&str as Type>::type_info() + } + + fn compatible(ty: &AuroraTypeInfo) -> bool { + <&str as Type>::compatible(ty) + } +} + +impl Type for [String] { + fn type_info() -> AuroraTypeInfo { + <[&str] as Type>::type_info() + } + + fn compatible(ty: &AuroraTypeInfo) -> bool { + <[&str] as Type>::compatible(ty) + } +} + +impl Type for Vec { + fn type_info() -> AuroraTypeInfo { + <[String] as Type>::type_info() + } + + fn compatible(ty: &AuroraTypeInfo) -> bool { + <[String] as Type>::compatible(ty) + } +} + +impl Decode<'_, Aurora> for String { + fn decode(value: AuroraValueRef<'_>) -> Result { + Ok(value + .field + .string_value + .as_ref() + .cloned() + .ok_or_else(|| Error::Decode("Not a str value".into()))?) + } +} diff --git a/sqlx-core/src/aurora/value.rs b/sqlx-core/src/aurora/value.rs new file mode 100644 index 0000000000..3dc2a9be51 --- /dev/null +++ b/sqlx-core/src/aurora/value.rs @@ -0,0 +1,59 @@ +use crate::aurora::type_info::AuroraTypeInfo; +use crate::aurora::Aurora; +use crate::value::{Value, ValueRef}; + +use rusoto_rds_data::Field; +use std::borrow::Cow; + +/// Implementation of [`ValueRef`] for Aurora. +#[derive(Clone)] +pub struct AuroraValueRef<'r> { + pub(crate) field: &'r Field, + pub(crate) type_info: AuroraTypeInfo, +} + +/// Implementation of [`Value`] for Aurora. +#[derive(Clone)] +pub struct AuroraValue { + pub(crate) field: Field, + pub(crate) type_info: AuroraTypeInfo, +} + +impl Value for AuroraValue { + type Database = Aurora; + + #[inline] + fn as_ref(&self) -> AuroraValueRef<'_> { + AuroraValueRef { + field: &self.field, + type_info: self.type_info, + } + } + + fn type_info(&self) -> Cow<'_, AuroraTypeInfo> { + Cow::Borrowed(&self.type_info) + } + + fn is_null(&self) -> bool { + self.field.is_null.unwrap_or_default() + } +} + +impl<'r> ValueRef<'r> for AuroraValueRef<'r> { + type Database = Aurora; + + fn to_owned(&self) -> AuroraValue { + AuroraValue { + field: self.field.clone(), + type_info: self.type_info, + } + } + + fn type_info(&self) -> Cow<'_, AuroraTypeInfo> { + Cow::Borrowed(&self.type_info) + } + + fn is_null(&self) -> bool { + self.field.is_null.unwrap_or_default() + } +} diff --git a/sqlx-core/src/common/statement_cache.rs b/sqlx-core/src/common/statement_cache.rs index 2ae097203c..f79466ea44 100644 --- a/sqlx-core/src/common/statement_cache.rs +++ b/sqlx-core/src/common/statement_cache.rs @@ -49,7 +49,7 @@ impl StatementCache { } /// Clear all cached statements from the cache. - #[cfg(feature = "sqlite")] + #[cfg(any(feature = "sqlite", feature = "aurora"))] pub fn clear(&mut self) { self.inner.clear(); } diff --git a/sqlx-core/src/lib.rs b/sqlx-core/src/lib.rs index 6e18f16fc2..21b890c1ff 100644 --- a/sqlx-core/src/lib.rs +++ b/sqlx-core/src/lib.rs @@ -100,6 +100,10 @@ pub mod mysql; #[cfg_attr(docsrs, doc(cfg(feature = "mssql")))] pub mod mssql; +#[cfg(feature = "aurora")] +#[cfg_attr(docsrs, doc(cfg(feature = "aurora")))] +pub mod aurora; + /// sqlx uses ahash for increased performance, at the cost of reduced DoS resistance. use ahash::AHashMap as HashMap; //type HashMap = std::collections::HashMap; diff --git a/src/lib.rs b/src/lib.rs index e2a404b074..9f53f251e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,6 +65,10 @@ pub use sqlx_core::postgres::{self, PgConnection, PgPool, Postgres}; #[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))] pub use sqlx_core::sqlite::{self, Sqlite, SqliteConnection, SqlitePool}; +#[cfg(feature = "aurora")] +#[cfg_attr(docsrs, doc(cfg(feature = "aurora")))] +pub use sqlx_core::aurora::{self, Aurora, AuroraConnection, AuroraPool}; + #[cfg(feature = "macros")] #[doc(hidden)] pub extern crate sqlx_macros; From 6f74ce35e582e15adb426c9b885a8b80a0ffad5e Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Tue, 1 Dec 2020 11:30:00 -0800 Subject: [PATCH 02/15] add null type --- sqlx-core/src/aurora/connection/executor.rs | 1 - sqlx-core/src/aurora/type_info.rs | 8 ++++++-- sqlx-core/src/aurora/value.rs | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/sqlx-core/src/aurora/connection/executor.rs b/sqlx-core/src/aurora/connection/executor.rs index 6d11f4682e..da095e28f9 100644 --- a/sqlx-core/src/aurora/connection/executor.rs +++ b/sqlx-core/src/aurora/connection/executor.rs @@ -1,7 +1,6 @@ use super::AuroraConnection; use crate::aurora::error::AuroraDatabaseError; use crate::aurora::statement::AuroraStatementMetadata; -use crate::aurora::type_info::AuroraType; use crate::aurora::{ Aurora, AuroraArguments, AuroraColumn, AuroraDone, AuroraRow, AuroraStatement, AuroraTypeInfo, }; diff --git a/sqlx-core/src/aurora/type_info.rs b/sqlx-core/src/aurora/type_info.rs index 2bda7da875..ce8d86012c 100644 --- a/sqlx-core/src/aurora/type_info.rs +++ b/sqlx-core/src/aurora/type_info.rs @@ -9,6 +9,7 @@ pub struct AuroraTypeInfo(pub(crate) AuroraType); #[derive(Debug, Clone, Copy, PartialEq)] pub enum AuroraType { + Null, Blob, Boolean, BooleanArray, @@ -31,6 +32,7 @@ pub enum AuroraType { impl AuroraType { pub(crate) fn display_name(&self) -> &str { match self { + AuroraType::Null => "NULL", AuroraType::Blob => "BLOB", AuroraType::Boolean => "BOOL", AuroraType::BooleanArray => "BOOL[]", @@ -58,7 +60,7 @@ impl TypeInfo for AuroraTypeInfo { } fn is_null(&self) -> bool { - false + matches!(self, AuroraTypeInfo(AuroraType::Null)) } } @@ -70,7 +72,9 @@ impl Display for AuroraTypeInfo { impl From<&Field> for AuroraTypeInfo { fn from(field: &Field) -> Self { - if let Some(array_value) = &field.array_value { + if field.is_null == Some(true) { + AuroraTypeInfo(AuroraType::Null) + } else if let Some(array_value) = &field.array_value { if array_value.boolean_values.is_some() { AuroraTypeInfo(AuroraType::BooleanArray) } else if array_value.double_values.is_some() { diff --git a/sqlx-core/src/aurora/value.rs b/sqlx-core/src/aurora/value.rs index 3dc2a9be51..633f65562b 100644 --- a/sqlx-core/src/aurora/value.rs +++ b/sqlx-core/src/aurora/value.rs @@ -1,4 +1,4 @@ -use crate::aurora::type_info::AuroraTypeInfo; +use crate::aurora::type_info::{AuroraType, AuroraTypeInfo}; use crate::aurora::Aurora; use crate::value::{Value, ValueRef}; @@ -35,7 +35,7 @@ impl Value for AuroraValue { } fn is_null(&self) -> bool { - self.field.is_null.unwrap_or_default() + matches!(self.type_info, AuroraTypeInfo(AuroraType::Null)) } } @@ -54,6 +54,6 @@ impl<'r> ValueRef<'r> for AuroraValueRef<'r> { } fn is_null(&self) -> bool { - self.field.is_null.unwrap_or_default() + matches!(self.type_info, AuroraTypeInfo(AuroraType::Null)) } } From ae73c0a29156a29922f7b104c01e04e0d13c17bd Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Tue, 1 Dec 2020 13:22:29 -0800 Subject: [PATCH 03/15] add query logger --- sqlx-core/Cargo.toml | 2 +- sqlx-core/src/aurora/connection/executor.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/sqlx-core/Cargo.toml b/sqlx-core/Cargo.toml index 698f8fd1ba..c8e72009ae 100644 --- a/sqlx-core/Cargo.toml +++ b/sqlx-core/Cargo.toml @@ -25,7 +25,7 @@ postgres = [ "md-5", "sha2", "base64", "sha-1", "rand", "hmac", "futures-channel mysql = [ "sha-1", "sha2", "generic-array", "num-bigint", "base64", "digest", "rand", "rsa" ] sqlite = [ "libsqlite3-sys" ] mssql = [ "uuid", "encoding_rs", "regex" ] -aurora = [ "rusoto_core", "rusoto_rds_data" ] +aurora = [ "rusoto_core", "rusoto_rds_data", "regex" ] any = [] # types diff --git a/sqlx-core/src/aurora/connection/executor.rs b/sqlx-core/src/aurora/connection/executor.rs index da095e28f9..5307eb1f80 100644 --- a/sqlx-core/src/aurora/connection/executor.rs +++ b/sqlx-core/src/aurora/connection/executor.rs @@ -8,6 +8,7 @@ use crate::describe::Describe; use crate::error::Error; use crate::executor::{Execute, Executor}; use crate::ext::ustr::UStr; +use crate::logger::QueryLogger; use either::Either; use futures_core::future::BoxFuture; @@ -25,6 +26,8 @@ impl AuroraConnection { query: &'q str, arguments: Option, ) -> Result, Error>> + 'e, Error> { + let mut logger = QueryLogger::new(query, self.log_settings.clone()); + // TODO: is this correct? let transaction_id = self.transaction_ids.last().cloned(); @@ -83,12 +86,16 @@ impl AuroraConnection { let row = AuroraRow { fields, metadata }; + logger.increment_rows(); + Ok(Either::Right(row)) }) .collect::>(); rows.push(Ok(Either::Left(AuroraDone { rows_affected }))); + logger.finish(); + Ok(stream::iter(rows)) } } From 19548d1928d40292beb609f56cf9365b49e73fb9 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Tue, 1 Dec 2020 14:19:04 -0800 Subject: [PATCH 04/15] finish is called on drop --- sqlx-core/src/aurora/connection/executor.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/sqlx-core/src/aurora/connection/executor.rs b/sqlx-core/src/aurora/connection/executor.rs index 5307eb1f80..3c9cb211b4 100644 --- a/sqlx-core/src/aurora/connection/executor.rs +++ b/sqlx-core/src/aurora/connection/executor.rs @@ -94,8 +94,6 @@ impl AuroraConnection { rows.push(Ok(Either::Left(AuroraDone { rows_affected }))); - logger.finish(); - Ok(stream::iter(rows)) } } From ae3b0249269dfc1baccb7b7964db98548bd4027a Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Tue, 1 Dec 2020 17:17:52 -0800 Subject: [PATCH 05/15] add more types --- sqlx-core/src/aurora/types/bool.rs | 49 +++++++++++++++++++++++++ sqlx-core/src/aurora/types/bytes.rs | 57 +++++++++++++++++++++++++++++ sqlx-core/src/aurora/types/float.rs | 49 +++++++++++++++++++++++++ sqlx-core/src/aurora/types/int.rs | 49 +++++++++++++++++++++++++ sqlx-core/src/aurora/types/mod.rs | 4 ++ sqlx-core/src/aurora/types/str.rs | 25 ++++--------- 6 files changed, 216 insertions(+), 17 deletions(-) create mode 100644 sqlx-core/src/aurora/types/bool.rs create mode 100644 sqlx-core/src/aurora/types/bytes.rs create mode 100644 sqlx-core/src/aurora/types/float.rs create mode 100644 sqlx-core/src/aurora/types/int.rs diff --git a/sqlx-core/src/aurora/types/bool.rs b/sqlx-core/src/aurora/types/bool.rs new file mode 100644 index 0000000000..fa82e4ad59 --- /dev/null +++ b/sqlx-core/src/aurora/types/bool.rs @@ -0,0 +1,49 @@ +use crate::aurora::type_info::AuroraType; +use crate::aurora::{Aurora, AuroraTypeInfo, AuroraValueRef}; +use crate::decode::Decode; +use crate::encode::{Encode, IsNull}; +use crate::error::{BoxDynError, Error}; +use crate::types::Type; + +use rusoto_rds_data::{Field, SqlParameter}; + +impl Type for bool { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::Boolean) + } +} + +impl Type for [bool] { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::Boolean) + } +} + +impl Type for Vec { + fn type_info() -> AuroraTypeInfo { + <[bool] as Type>::type_info() + } +} + +impl Encode<'_, Aurora> for bool { + fn encode_by_ref(&self, buf: &mut Vec) -> IsNull { + buf.push(SqlParameter { + value: Some(Field { + boolean_value: Some(*self), + ..Default::default() + }), + ..Default::default() + }); + + IsNull::No + } +} + +impl Decode<'_, Aurora> for bool { + fn decode(value: AuroraValueRef<'_>) -> Result { + Ok(value + .field + .boolean_value + .ok_or_else(|| Error::Decode("Not a bool value".into()))?) + } +} diff --git a/sqlx-core/src/aurora/types/bytes.rs b/sqlx-core/src/aurora/types/bytes.rs new file mode 100644 index 0000000000..f72de771cc --- /dev/null +++ b/sqlx-core/src/aurora/types/bytes.rs @@ -0,0 +1,57 @@ +use crate::aurora::type_info::AuroraType; +use crate::aurora::{Aurora, AuroraTypeInfo, AuroraValueRef}; +use crate::decode::Decode; +use crate::encode::{Encode, IsNull}; +use crate::error::{BoxDynError, Error}; +use crate::types::Type; + +use bytes::Bytes; +use rusoto_rds_data::{Field, SqlParameter}; + +impl Type for [u8] { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::Blob) + } +} + +impl Type for Vec { + fn type_info() -> AuroraTypeInfo { + <[u8] as Type>::type_info() + } +} + +impl Encode<'_, Aurora> for &'_ [u8] { + fn encode_by_ref(&self, buf: &mut Vec) -> IsNull { + buf.push(SqlParameter { + value: Some(Field { + blob_value: Some(Bytes::from(self.to_vec())), + ..Default::default() + }), + ..Default::default() + }); + + IsNull::No + } +} + +impl Encode<'_, Aurora> for Vec { + fn encode_by_ref(&self, buf: &mut Vec) -> IsNull { + <&[u8] as Encode>::encode(self, buf) + } +} + +impl<'r> Decode<'r, Aurora> for &'r [u8] { + fn decode(value: AuroraValueRef<'r>) -> Result { + Ok(value + .field + .blob_value + .as_ref() + .ok_or_else(|| Error::Decode("Not a blob value".into()))?) + } +} + +impl Decode<'_, Aurora> for Vec { + fn decode(value: AuroraValueRef<'_>) -> Result { + <&[u8] as Decode>::decode(value).map(|v| v.to_vec()) + } +} diff --git a/sqlx-core/src/aurora/types/float.rs b/sqlx-core/src/aurora/types/float.rs new file mode 100644 index 0000000000..4a0a150e54 --- /dev/null +++ b/sqlx-core/src/aurora/types/float.rs @@ -0,0 +1,49 @@ +use crate::aurora::type_info::AuroraType; +use crate::aurora::{Aurora, AuroraTypeInfo, AuroraValueRef}; +use crate::decode::Decode; +use crate::encode::{Encode, IsNull}; +use crate::error::{BoxDynError, Error}; +use crate::types::Type; + +use rusoto_rds_data::{Field, SqlParameter}; + +impl Type for f64 { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::Double) + } +} + +impl Type for [f64] { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::DoubleArray) + } +} + +impl Type for Vec { + fn type_info() -> AuroraTypeInfo { + <[f64] as Type>::type_info() + } +} + +impl Encode<'_, Aurora> for f64 { + fn encode_by_ref(&self, buf: &mut Vec) -> IsNull { + buf.push(SqlParameter { + value: Some(Field { + double_value: Some(*self), + ..Default::default() + }), + ..Default::default() + }); + + IsNull::No + } +} + +impl Decode<'_, Aurora> for f64 { + fn decode(value: AuroraValueRef<'_>) -> Result { + Ok(value + .field + .double_value + .ok_or_else(|| Error::Decode("Not a double value".into()))?) + } +} diff --git a/sqlx-core/src/aurora/types/int.rs b/sqlx-core/src/aurora/types/int.rs new file mode 100644 index 0000000000..b3c843f016 --- /dev/null +++ b/sqlx-core/src/aurora/types/int.rs @@ -0,0 +1,49 @@ +use crate::aurora::type_info::AuroraType; +use crate::aurora::{Aurora, AuroraTypeInfo, AuroraValueRef}; +use crate::decode::Decode; +use crate::encode::{Encode, IsNull}; +use crate::error::{BoxDynError, Error}; +use crate::types::Type; + +use rusoto_rds_data::{Field, SqlParameter}; + +impl Type for i64 { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::Long) + } +} + +impl Type for [i64] { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::LongArray) + } +} + +impl Type for Vec { + fn type_info() -> AuroraTypeInfo { + <[i64] as Type>::type_info() + } +} + +impl Encode<'_, Aurora> for i64 { + fn encode_by_ref(&self, buf: &mut Vec) -> IsNull { + buf.push(SqlParameter { + value: Some(Field { + long_value: Some(*self), + ..Default::default() + }), + ..Default::default() + }); + + IsNull::No + } +} + +impl Decode<'_, Aurora> for i64 { + fn decode(value: AuroraValueRef<'_>) -> Result { + Ok(value + .field + .long_value + .ok_or_else(|| Error::Decode("Not a double value".into()))?) + } +} diff --git a/sqlx-core/src/aurora/types/mod.rs b/sqlx-core/src/aurora/types/mod.rs index 1dfe386a08..95969b2cb1 100644 --- a/sqlx-core/src/aurora/types/mod.rs +++ b/sqlx-core/src/aurora/types/mod.rs @@ -1 +1,5 @@ +mod bool; +mod bytes; +mod float; +mod int; mod str; diff --git a/sqlx-core/src/aurora/types/str.rs b/sqlx-core/src/aurora/types/str.rs index 7efb6852c6..f57a564746 100644 --- a/sqlx-core/src/aurora/types/str.rs +++ b/sqlx-core/src/aurora/types/str.rs @@ -1,9 +1,8 @@ +use crate::aurora::type_info::AuroraType; +use crate::aurora::{Aurora, AuroraTypeInfo, AuroraValueRef}; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::{BoxDynError, Error}; -//use crate::aurora::types::array_compatible; -use crate::aurora::type_info::AuroraType; -use crate::aurora::{Aurora, AuroraTypeInfo, AuroraValueRef}; use crate::types::Type; use rusoto_rds_data::{Field, SqlParameter}; @@ -12,20 +11,12 @@ impl Type for str { fn type_info() -> AuroraTypeInfo { AuroraTypeInfo(AuroraType::String) } - - fn compatible(ty: &AuroraTypeInfo) -> bool { - [AuroraTypeInfo(AuroraType::String)].contains(ty) - } } impl Type for [&'_ str] { fn type_info() -> AuroraTypeInfo { AuroraTypeInfo(AuroraType::StringArray) } - - fn compatible(ty: &AuroraTypeInfo) -> bool { - [AuroraTypeInfo(AuroraType::StringArray)].contains(ty) - } } impl Type for Vec<&'_ str> { @@ -52,12 +43,6 @@ impl Encode<'_, Aurora> for &'_ str { } } -impl Encode<'_, Aurora> for String { - fn encode_by_ref(&self, buf: &mut Vec) -> IsNull { - <&str as Encode>::encode(&**self, buf) - } -} - impl<'r> Decode<'r, Aurora> for &'r str { fn decode(value: AuroraValueRef<'r>) -> Result { Ok(value @@ -98,6 +83,12 @@ impl Type for Vec { } } +impl Encode<'_, Aurora> for String { + fn encode_by_ref(&self, buf: &mut Vec) -> IsNull { + <&str as Encode>::encode(&**self, buf) + } +} + impl Decode<'_, Aurora> for String { fn decode(value: AuroraValueRef<'_>) -> Result { Ok(value From fa7f2af17c490b64bcd984af201cd93f31fa0dc4 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 4 Dec 2020 11:45:58 -0800 Subject: [PATCH 06/15] add db type this will be used to execute the correct sql for migrations. --- sqlx-core/src/aurora/connection/mod.rs | 8 ++++- sqlx-core/src/aurora/options/mod.rs | 45 +++++++++++++++++++++++++- sqlx-core/src/aurora/options/parse.rs | 3 ++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/sqlx-core/src/aurora/connection/mod.rs b/sqlx-core/src/aurora/connection/mod.rs index 697885e153..756b40463f 100644 --- a/sqlx-core/src/aurora/connection/mod.rs +++ b/sqlx-core/src/aurora/connection/mod.rs @@ -1,4 +1,4 @@ -use crate::aurora::options::AuroraConnectOptions; +use crate::aurora::options::{AuroraConnectOptions, AuroraDbType}; use crate::aurora::statement::AuroraStatementMetadata; use crate::aurora::Aurora; use crate::common::StatementCache; @@ -17,6 +17,7 @@ mod executor; /// A connection to an Aurora database. pub struct AuroraConnection { + pub(crate) db_type: AuroraDbType, pub(crate) resource_arn: String, pub(crate) secret_arn: String, pub(crate) database: Option, @@ -35,6 +36,10 @@ pub struct AuroraConnection { impl AuroraConnection { pub(crate) fn new(options: &AuroraConnectOptions) -> Result { + let db_type = options + .db_type + .ok_or_else(|| Error::Configuration("db type not specified".into()))?; + let region = options.region.parse().map_err(Error::config)?; let resource_arn = options @@ -51,6 +56,7 @@ impl AuroraConnection { let client = RdsDataClient::new(region); Ok(Self { + db_type, resource_arn, secret_arn, database: options.database.clone(), diff --git a/sqlx-core/src/aurora/options/mod.rs b/sqlx-core/src/aurora/options/mod.rs index d26a00edd3..c57d769ed4 100644 --- a/sqlx-core/src/aurora/options/mod.rs +++ b/sqlx-core/src/aurora/options/mod.rs @@ -1,11 +1,39 @@ +use crate::connection::LogSettings; +use crate::error::Error; + use std::env::var; +use std::str::FromStr; mod connect; mod parse; -use crate::connection::LogSettings; + +#[derive(Debug, Clone, Copy)] +pub enum AuroraDbType { + MySQL, + Postgres, +} + +impl FromStr for AuroraDbType { + type Err = Error; + + fn from_str(s: &str) -> Result { + let t = match s { + "mysql" => AuroraDbType::MySQL, + "postgres" => AuroraDbType::Postgres, + _ => { + return Err(Error::Configuration( + "db type must be `postgres` or `mysql`".into(), + )) + } + }; + + Ok(t) + } +} #[derive(Debug, Clone)] pub struct AuroraConnectOptions { + pub(crate) db_type: Option, pub(crate) region: String, pub(crate) resource_arn: Option, pub(crate) secret_arn: Option, @@ -23,6 +51,15 @@ impl Default for AuroraConnectOptions { impl AuroraConnectOptions { pub fn new() -> Self { + let db_type = match var("AURORA_DB_TYPE").map(|s| s.parse()) { + Ok(Ok(t)) => Some(t), + Ok(Err(e)) => { + log::error!("{}", e); + None + } + Err(_) => None, + }; + let region = var("AURORA_REGION") .ok() .unwrap_or_else(|| "us-east-1".to_owned()); @@ -31,6 +68,7 @@ impl AuroraConnectOptions { let secret_arn = var("AURORA_SECRET_ARN").ok(); AuroraConnectOptions { + db_type, region, resource_arn, secret_arn, @@ -41,6 +79,11 @@ impl AuroraConnectOptions { } } + pub fn db_type(mut self, db_type: AuroraDbType) -> Self { + self.db_type = Some(db_type); + self + } + pub fn region(mut self, region: &str) -> Self { self.region = region.to_owned(); self diff --git a/sqlx-core/src/aurora/options/parse.rs b/sqlx-core/src/aurora/options/parse.rs index 2545f50fa5..af6853fd8f 100644 --- a/sqlx-core/src/aurora/options/parse.rs +++ b/sqlx-core/src/aurora/options/parse.rs @@ -17,6 +17,9 @@ impl FromStr for AuroraConnectOptions { options = options.statement_cache_capacity(value.parse().map_err(Error::config)?); } + "db-type" => { + options = options.db_type(value.parse()?); + } "region" => { options = options.region(&*value); } From 57087cb4966cb0754d91d8de29e1fd4c14d1a70b Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 4 Dec 2020 12:20:31 -0800 Subject: [PATCH 07/15] add migrate for aurora --- sqlx-core/src/aurora/migrate.rs | 362 ++++++++++++++++++++++++++++++++ sqlx-core/src/aurora/mod.rs | 3 +- 2 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 sqlx-core/src/aurora/migrate.rs diff --git a/sqlx-core/src/aurora/migrate.rs b/sqlx-core/src/aurora/migrate.rs new file mode 100644 index 0000000000..da0ac6a8ed --- /dev/null +++ b/sqlx-core/src/aurora/migrate.rs @@ -0,0 +1,362 @@ +use crate::aurora::{Aurora, AuroraConnectOptions, AuroraConnection, AuroraDbType}; +use crate::connection::{ConnectOptions, Connection}; +use crate::error::Error; +use crate::executor::Executor; +use crate::migrate::MigrateError; +use crate::migrate::Migration; +use crate::migrate::{Migrate, MigrateDatabase}; +use crate::query::query; +use crate::query_as::query_as; +use crate::query_scalar::query_scalar; +use crc::crc32; +use futures_core::future::BoxFuture; +use std::str::FromStr; +use std::time::Duration; +use std::time::Instant; + +fn parse_for_maintenance(uri: &str) -> Result<(AuroraConnectOptions, String, AuroraDbType), Error> { + let mut options = AuroraConnectOptions::from_str(uri)?; + + let db_type = if let Some(db_type) = options.db_type { + db_type + } else { + return Err(Error::Configuration( + "DATABASE_URL does not specify a db type".into(), + )); + }; + + let database = if let Some(database) = &options.database { + database.to_owned() + } else { + return Err(Error::Configuration( + "DATABASE_URL does not specify a database".into(), + )); + }; + + match db_type { + AuroraDbType::MySQL => options.database = None, + AuroraDbType::Postgres => options.database = Some("postgres".into()), + } + + Ok((options, database, db_type)) +} + +impl MigrateDatabase for Aurora { + fn create_database(uri: &str) -> BoxFuture<'_, Result<(), Error>> { + Box::pin(async move { + let (options, database, db_type) = parse_for_maintenance(uri)?; + let mut conn = options.connect().await?; + + let sql = match db_type { + AuroraDbType::MySQL => format!("CREATE DATABASE `{}`", database), + AuroraDbType::Postgres => { + format!("CREATE DATABASE \"{}\"", database.replace('"', "\"\"")) + } + }; + + let _ = conn.execute(&*sql).await?; + + Ok(()) + }) + } + + fn database_exists(uri: &str) -> BoxFuture<'_, Result> { + Box::pin(async move { + let (options, database, db_type) = parse_for_maintenance(uri)?; + let mut conn = options.connect().await?; + + let sql = match db_type { + AuroraDbType::MySQL => { + "select exists(SELECT 1 from INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?)" + } + AuroraDbType::Postgres => { + "select exists(SELECT 1 from pg_database WHERE datname = $1)" + } + }; + + let exists: bool = query_scalar(sql) + .bind(database) + .fetch_one(&mut conn) + .await?; + + Ok(exists) + }) + } + + fn drop_database(uri: &str) -> BoxFuture<'_, Result<(), Error>> { + Box::pin(async move { + let (options, database, db_type) = parse_for_maintenance(uri)?; + let mut conn = options.connect().await?; + + let sql = match db_type { + AuroraDbType::MySQL => { + format!("DROP DATABASE IF EXISTS `{}`", database,) + } + AuroraDbType::Postgres => { + format!( + "DROP DATABASE IF EXISTS \"{}\"", + database.replace('"', "\"\"") + ) + } + }; + + let _ = conn.execute(&*sql).await?; + + Ok(()) + }) + } +} + +impl Migrate for AuroraConnection { + fn ensure_migrations_table(&mut self) -> BoxFuture<'_, Result<(), MigrateError>> { + Box::pin(async move { + let sql = match self.db_type { + AuroraDbType::MySQL => { + r#" + CREATE TABLE IF NOT EXISTS _sqlx_migrations ( + version BIGINT PRIMARY KEY, + description TEXT NOT NULL, + installed_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + success BOOLEAN NOT NULL, + checksum BLOB NOT NULL, + execution_time BIGINT NOT NULL + ); + "# + } + AuroraDbType::Postgres => { + r#" + CREATE TABLE IF NOT EXISTS _sqlx_migrations ( + version BIGINT PRIMARY KEY, + description TEXT NOT NULL, + installed_on TIMESTAMPTZ NOT NULL DEFAULT now(), + success BOOLEAN NOT NULL, + checksum BYTEA NOT NULL, + execution_time BIGINT NOT NULL + ); + "# + } + }; + + // language=SQL + self.execute(sql).await?; + + Ok(()) + }) + } + + fn version(&mut self) -> BoxFuture<'_, Result, MigrateError>> { + Box::pin(async move { + // language=SQL + let row = query_as( + "SELECT version, NOT success FROM _sqlx_migrations ORDER BY version DESC LIMIT 1", + ) + .fetch_optional(self) + .await?; + + Ok(row) + }) + } + + fn lock(&mut self) -> BoxFuture<'_, Result<(), MigrateError>> { + Box::pin(async move { + let database_name = current_database(self).await?; + let lock_id = generate_lock_id(&database_name); + + let sql = match self.db_type { + AuroraDbType::MySQL => "SELECT GET_LOCK(?, -1)", + AuroraDbType::Postgres => "SELECT pg_advisory_lock($1)", + }; + + // language=SQL + let query = query(sql); + + match self.db_type { + AuroraDbType::MySQL => { + query.bind(format!("{:x}", lock_id)).execute(self).await?; + } + AuroraDbType::Postgres => { + query.bind(lock_id).execute(self).await?; + } + }; + + Ok(()) + }) + } + + fn unlock(&mut self) -> BoxFuture<'_, Result<(), MigrateError>> { + Box::pin(async move { + let database_name = current_database(self).await?; + let lock_id = generate_lock_id(&database_name); + + let sql = match self.db_type { + AuroraDbType::MySQL => "SELECT RELEASE_LOCK(?)", + AuroraDbType::Postgres => "SELECT pg_advisory_unlock($1)", + }; + + // language=SQL + let query = query(sql); + + match self.db_type { + AuroraDbType::MySQL => { + query.bind(format!("{:x}", lock_id)).execute(self).await?; + } + AuroraDbType::Postgres => { + query.bind(lock_id).execute(self).await?; + } + }; + + Ok(()) + }) + } + + fn validate<'e: 'm, 'm>( + &'e mut self, + migration: &'m Migration, + ) -> BoxFuture<'m, Result<(), MigrateError>> { + Box::pin(async move { + let sql = match self.db_type { + AuroraDbType::MySQL => "SELECT checksum FROM _sqlx_migrations WHERE version = ?", + AuroraDbType::Postgres => { + "SELECT checksum FROM _sqlx_migrations WHERE version = $1" + } + }; + + // language=SQL + let checksum: Option> = query_scalar(sql) + .bind(migration.version) + .fetch_optional(self) + .await?; + + if let Some(checksum) = checksum { + if checksum == &*migration.checksum { + Ok(()) + } else { + Err(MigrateError::VersionMismatch(migration.version)) + } + } else { + Err(MigrateError::VersionMissing(migration.version)) + } + }) + } + + fn apply<'e: 'm, 'm>( + &'e mut self, + migration: &'m Migration, + ) -> BoxFuture<'m, Result> { + Box::pin(async move { + match self.db_type { + AuroraDbType::MySQL => { + let start = Instant::now(); + + let res = self.execute(&*migration.sql).await; + + let elapsed = start.elapsed(); + + // language=MySQL + let _ = query( + r#" + INSERT INTO _sqlx_migrations ( version, description, success, checksum, execution_time ) + VALUES ( ?, ?, ?, ?, ? ) + "#, + ) + .bind(migration.version) + .bind(&*migration.description) + .bind(res.is_ok()) + .bind(&*migration.checksum) + .bind(elapsed.as_nanos() as i64) + .execute(self) + .await?; + + res?; + + Ok(elapsed) + } + AuroraDbType::Postgres => { + let mut tx = self.begin().await?; + let start = Instant::now(); + + let _ = tx.execute(&*migration.sql).await?; + + tx.commit().await?; + + let elapsed = start.elapsed(); + + // language=SQL + let _ = query( + r#" + INSERT INTO _sqlx_migrations ( version, description, success, checksum, execution_time ) + VALUES ( $1, $2, TRUE, $3, $4 ) + "#, + ) + .bind(migration.version) + .bind(&*migration.description) + .bind(&*migration.checksum) + .bind(elapsed.as_nanos() as i64) + .execute(self) + .await?; + + Ok(elapsed) + } + } + }) + } + + fn revert<'e: 'm, 'm>( + &'e mut self, + migration: &'m Migration, + ) -> BoxFuture<'m, Result> { + Box::pin(async move { + match self.db_type { + AuroraDbType::MySQL => { + let start = Instant::now(); + + self.execute(&*migration.sql).await?; + + let elapsed = start.elapsed(); + + // language=SQL + let _ = query(r#"DELETE FROM _sqlx_migrations WHERE version = ?"#) + .bind(migration.version) + .execute(self) + .await?; + + Ok(elapsed) + } + AuroraDbType::Postgres => { + let mut tx = self.begin().await?; + let start = Instant::now(); + + let _ = tx.execute(&*migration.sql).await?; + + tx.commit().await?; + + let elapsed = start.elapsed(); + + // language=SQL + let _ = query(r#"DELETE FROM _sqlx_migrations WHERE version = $1"#) + .bind(migration.version) + .execute(self) + .await?; + + Ok(elapsed) + } + } + }) + } +} + +async fn current_database(conn: &mut AuroraConnection) -> Result { + let sql = match conn.db_type { + AuroraDbType::MySQL => "SELECT DATABASE()", + AuroraDbType::Postgres => "SELECT current_database()", + }; + + // language=SQL + Ok(query_scalar(sql).fetch_one(conn).await?) +} + +// inspired from rails: https://github.com/rails/rails/blob/6e49cc77ab3d16c06e12f93158eaf3e507d4120e/activerecord/lib/active_record/migration.rb#L1308 +fn generate_lock_id(database_name: &str) -> i64 { + // 0x3d32ad9e chosen by fair dice roll + 0x3d32ad9e * (crc32::checksum_ieee(database_name.as_bytes()) as i64) +} diff --git a/sqlx-core/src/aurora/mod.rs b/sqlx-core/src/aurora/mod.rs index 4e85793e17..3b1f5c09d3 100644 --- a/sqlx-core/src/aurora/mod.rs +++ b/sqlx-core/src/aurora/mod.rs @@ -4,6 +4,7 @@ mod connection; mod database; mod done; mod error; +mod migrate; mod options; mod row; mod statement; @@ -18,7 +19,7 @@ pub use connection::AuroraConnection; pub use database::Aurora; pub use done::AuroraDone; pub use error::AuroraDatabaseError; -pub use options::AuroraConnectOptions; +pub use options::{AuroraConnectOptions, AuroraDbType}; pub use row::AuroraRow; pub use statement::AuroraStatement; pub use transaction::AuroraTransactionManager; From 8ed86818dbe6f3a44f5d13b30cc085ea083bc0bf Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 4 Dec 2020 15:17:43 -0800 Subject: [PATCH 08/15] add aurora to Any --- Cargo.toml | 2 +- sqlx-core/Cargo.toml | 2 +- sqlx-core/src/any/arguments.rs | 27 +++++++++++++++++ sqlx-core/src/any/column.rs | 30 +++++++++++++++++++ sqlx-core/src/any/connection/establish.rs | 7 +++++ sqlx-core/src/any/connection/executor.rs | 18 ++++++++++++ sqlx-core/src/any/connection/mod.rs | 21 +++++++++++++ sqlx-core/src/any/decode.rs | 20 +++++++++++++ sqlx-core/src/any/encode.rs | 18 ++++++++++++ sqlx-core/src/any/kind.rs | 13 ++++++++ sqlx-core/src/any/migrate.rs | 30 +++++++++++++++++++ sqlx-core/src/any/options.rs | 31 +++++++++++++++++++ sqlx-core/src/any/row.rs | 12 ++++++++ sqlx-core/src/any/transaction.rs | 20 +++++++++++++ sqlx-core/src/any/type.rs | 20 +++++++++++++ sqlx-core/src/any/type_info.rs | 15 ++++++++++ sqlx-core/src/any/value.rs | 21 +++++++++++++ sqlx-core/src/aurora/arguments.rs | 11 ++++++- sqlx-core/src/aurora/column.rs | 11 +++++++ sqlx-core/src/aurora/done.rs | 10 +++++++ sqlx-core/src/aurora/mod.rs | 1 + sqlx-core/src/aurora/row.rs | 17 +++++++++++ sqlx-core/src/aurora/statement.rs | 25 ++++++++++++++++ sqlx-core/src/aurora/type_info.rs | 8 +++++ sqlx-core/src/aurora/types/float.rs | 36 +++++++++++++++++++++++ sqlx-core/src/aurora/types/int.rs | 31 +++++++++++++++++++ sqlx-core/src/aurora/value.rs | 22 ++++++++++++++ 27 files changed, 476 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 92910fea4b..733e0d400c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ offline = [ "sqlx-macros/offline", "sqlx-core/offline" ] # intended mainly for CI and docs all = [ "tls", "all-databases", "all-types" ] -all-databases = [ "mysql", "sqlite", "postgres", "mssql", "any", "aurora" ] +all-databases = [ "mysql", "sqlite", "postgres", "mssql", "any" ] all-types = [ "bigdecimal", "decimal", "json", "time", "chrono", "ipnetwork", "uuid", "bit-vec" ] # previous runtimes, available as features for error messages better than just diff --git a/sqlx-core/Cargo.toml b/sqlx-core/Cargo.toml index c8e72009ae..a956924b61 100644 --- a/sqlx-core/Cargo.toml +++ b/sqlx-core/Cargo.toml @@ -20,7 +20,7 @@ default = [ "migrate" ] migrate = [ "sha2", "crc" ] # databases -all-databases = [ "postgres", "mysql", "sqlite", "mssql", "aurora", "any" ] +all-databases = [ "postgres", "mysql", "sqlite", "mssql", "any" ] 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", "rsa" ] sqlite = [ "libsqlite3-sys" ] diff --git a/sqlx-core/src/any/arguments.rs b/sqlx-core/src/any/arguments.rs index 41b0b72946..2f2aab0b3c 100644 --- a/sqlx-core/src/any/arguments.rs +++ b/sqlx-core/src/any/arguments.rs @@ -46,6 +46,12 @@ pub(crate) enum AnyArgumentBufferKind<'q> { crate::mssql::MssqlArguments, std::marker::PhantomData<&'q ()>, ), + + #[cfg(feature = "aurora")] + Aurora( + crate::aurora::AuroraArguments, + std::marker::PhantomData<&'q ()>, + ), } // control flow inferred type bounds would be fun @@ -131,3 +137,24 @@ impl<'q> From> for crate::postgres::PgArguments { } } } + +#[cfg(feature = "aurora")] +#[allow(irrefutable_let_patterns)] +impl<'q> From> for crate::aurora::AuroraArguments { + fn from(args: AnyArguments<'q>) -> Self { + let mut buf = AnyArgumentBuffer(AnyArgumentBufferKind::Aurora( + Default::default(), + std::marker::PhantomData, + )); + + for value in args.values { + let _ = value.encode_by_ref(&mut buf); + } + + if let AnyArgumentBufferKind::Aurora(args, _) = buf.0 { + args + } else { + unreachable!() + } + } +} diff --git a/sqlx-core/src/any/column.rs b/sqlx-core/src/any/column.rs index 22049033a8..8ee87ec123 100644 --- a/sqlx-core/src/any/column.rs +++ b/sqlx-core/src/any/column.rs @@ -13,6 +13,9 @@ use crate::sqlite::{SqliteColumn, SqliteRow, SqliteStatement}; #[cfg(feature = "mssql")] use crate::mssql::{MssqlColumn, MssqlRow, MssqlStatement}; +#[cfg(feature = "aurora")] +use crate::aurora::{AuroraColumn, AuroraRow, AuroraStatement}; + #[derive(Debug, Clone)] pub struct AnyColumn { pub(crate) kind: AnyColumnKind, @@ -34,6 +37,9 @@ pub(crate) enum AnyColumnKind { #[cfg(feature = "mssql")] Mssql(MssqlColumn), + + #[cfg(feature = "aurora")] + Aurora(AuroraColumn), } impl Column for AnyColumn { @@ -52,6 +58,9 @@ impl Column for AnyColumn { #[cfg(feature = "mssql")] AnyColumnKind::Mssql(row) => row.ordinal(), + + #[cfg(feature = "aurora")] + AnyColumnKind::Aurora(row) => row.ordinal(), } } @@ -68,6 +77,9 @@ impl Column for AnyColumn { #[cfg(feature = "mssql")] AnyColumnKind::Mssql(row) => row.name(), + + #[cfg(feature = "aurora")] + AnyColumnKind::Aurora(row) => row.name(), } } @@ -441,3 +453,21 @@ impl AnyColumnIndex for I where I: ColumnIndex + for<'q> ColumnIndex> { } + +#[cfg(all( + not(any(feature = "mysql", feature = "mssql", feature = "postgres", feature = "sqlite")), + feature = "aurora" +))] +pub trait AnyColumnIndex: + ColumnIndex + for<'q> ColumnIndex> +{ +} + +#[cfg(all( + not(any(feature = "mysql", feature = "mssql", feature = "postgres", feature = "sqlite")), + feature = "aurora" +))] +impl AnyColumnIndex for I where + I: ColumnIndex + for<'q> ColumnIndex> +{ +} diff --git a/sqlx-core/src/any/connection/establish.rs b/sqlx-core/src/any/connection/establish.rs index 290a499cdd..f745d28d33 100644 --- a/sqlx-core/src/any/connection/establish.rs +++ b/sqlx-core/src/any/connection/establish.rs @@ -34,6 +34,13 @@ impl AnyConnection { .await .map(AnyConnectionKind::Mssql) } + + #[cfg(feature = "aurora")] + AnyConnectOptionsKind::Aurora(options) => { + crate::aurora::AuroraConnection::connect_with(options) + .await + .map(AnyConnectionKind::Aurora) + } } .map(AnyConnection) } diff --git a/sqlx-core/src/any/connection/executor.rs b/sqlx-core/src/any/connection/executor.rs index 1aae7dab84..ab22d73b06 100644 --- a/sqlx-core/src/any/connection/executor.rs +++ b/sqlx-core/src/any/connection/executor.rs @@ -47,6 +47,12 @@ impl<'c> Executor<'c> for &'c mut AnyConnection { .fetch_many((query, arguments.map(Into::into))) .map_ok(|v| v.map_right(Into::into).map_left(Into::into)) .boxed(), + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn + .fetch_many((query, arguments.map(Into::into))) + .map_ok(|v| v.map_right(Into::into).map_left(Into::into)) + .boxed(), } } @@ -86,6 +92,12 @@ impl<'c> Executor<'c> for &'c mut AnyConnection { .fetch_optional((query, arguments.map(Into::into))) .await? .map(Into::into), + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn + .fetch_optional((query, arguments.map(Into::into))) + .await? + .map(Into::into), }) }) } @@ -112,6 +124,9 @@ impl<'c> Executor<'c> for &'c mut AnyConnection { #[cfg(feature = "mssql")] AnyConnectionKind::Mssql(conn) => conn.prepare(sql).await.map(Into::into)?, + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.prepare(sql).await.map(Into::into)?, }) }) } @@ -136,6 +151,9 @@ impl<'c> Executor<'c> for &'c mut AnyConnection { #[cfg(feature = "mssql")] AnyConnectionKind::Mssql(conn) => conn.describe(sql).await.map(map_describe)?, + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.describe(sql).await.map(map_describe)?, }) }) } diff --git a/sqlx-core/src/any/connection/mod.rs b/sqlx-core/src/any/connection/mod.rs index 6ed4bfbd06..e1130c41e8 100644 --- a/sqlx-core/src/any/connection/mod.rs +++ b/sqlx-core/src/any/connection/mod.rs @@ -17,6 +17,9 @@ use crate::mssql; use crate::mysql; use crate::transaction::Transaction; +#[cfg(feature = "aurora")] +use crate::aurora; + mod establish; mod executor; @@ -45,6 +48,9 @@ pub(crate) enum AnyConnectionKind { #[cfg(feature = "sqlite")] Sqlite(sqlite::SqliteConnection), + + #[cfg(feature = "aurora")] + Aurora(aurora::AuroraConnection), } macro_rules! delegate_to { @@ -61,6 +67,9 @@ macro_rules! delegate_to { #[cfg(feature = "mssql")] AnyConnectionKind::Mssql(conn) => conn.$method($($arg),*), + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.$method($($arg),*), } }; } @@ -79,6 +88,9 @@ macro_rules! delegate_to_mut { #[cfg(feature = "mssql")] AnyConnectionKind::Mssql(conn) => conn.$method($($arg),*), + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.$method($($arg),*), } }; } @@ -101,6 +113,9 @@ impl Connection for AnyConnection { #[cfg(feature = "mssql")] AnyConnectionKind::Mssql(conn) => conn.close(), + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.close(), } } @@ -129,6 +144,9 @@ impl Connection for AnyConnection { // no cache #[cfg(feature = "mssql")] AnyConnectionKind::Mssql(_) => 0, + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.cached_statements_size(), } } @@ -146,6 +164,9 @@ impl Connection for AnyConnection { // no cache #[cfg(feature = "mssql")] AnyConnectionKind::Mssql(_) => Box::pin(futures_util::future::ok(())), + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.clear_cached_statements(), } } diff --git a/sqlx-core/src/any/decode.rs b/sqlx-core/src/any/decode.rs index 28d1872f6e..09de48605a 100644 --- a/sqlx-core/src/any/decode.rs +++ b/sqlx-core/src/any/decode.rs @@ -13,6 +13,9 @@ use crate::mssql::Mssql; #[cfg(feature = "sqlite")] use crate::sqlite::Sqlite; +#[cfg(feature = "aurora")] +use crate::aurora::Aurora; + // Implements Decode for any T where T supports Decode for any database that has support currently // compiled into SQLx macro_rules! impl_any_decode { @@ -44,6 +47,11 @@ macro_rules! impl_any_decode { crate::any::value::AnyValueRefKind::Postgres(value) => { <$ty as crate::decode::Decode<'r, crate::postgres::Postgres>>::decode(value) } + + #[cfg(feature = "aurora")] + crate::any::value::AnyValueRefKind::Aurora(value) => { + <$ty as crate::decode::Decode<'r, crate::aurora::Aurora>>::decode(value) + } } } } @@ -361,3 +369,15 @@ pub trait AnyDecode<'r>: Decode<'r, Sqlite> + Type {} feature = "sqlite" ))] impl<'r, T> AnyDecode<'r> for T where T: Decode<'r, Sqlite> + Type {} + +#[cfg(all( + not(any(feature = "mysql", feature = "mssql", feature = "postgres", feature = "sqlite")), + feature = "aurora" +))] +pub trait AnyDecode<'r>: Decode<'r, Aurora> + Type {} + +#[cfg(all( + not(any(feature = "mysql", feature = "mssql", feature = "postgres", feature = "sqlite")), + feature = "aurora" +))] +impl<'r, T> AnyDecode<'r> for T where T: Decode<'r, Aurora> + Type {} \ No newline at end of file diff --git a/sqlx-core/src/any/encode.rs b/sqlx-core/src/any/encode.rs index edde3bcd70..86d533d55b 100644 --- a/sqlx-core/src/any/encode.rs +++ b/sqlx-core/src/any/encode.rs @@ -13,6 +13,9 @@ use crate::mssql::Mssql; #[cfg(feature = "sqlite")] use crate::sqlite::Sqlite; +#[cfg(feature = "aurora")] +use crate::aurora::Aurora; + // Implements Encode for any T where T supports Encode for any database that has support currently // compiled into SQLx macro_rules! impl_any_encode { @@ -39,6 +42,9 @@ macro_rules! impl_any_encode { #[cfg(feature = "sqlite")] crate::any::arguments::AnyArgumentBufferKind::Sqlite(args) => args.add(self), + + #[cfg(feature = "aurora")] + crate::any::arguments::AnyArgumentBufferKind::Aurora(args, _) => args.add(self), } // unused @@ -359,3 +365,15 @@ pub trait AnyEncode<'q>: Encode<'q, Sqlite> + Type {} feature = "sqlite" ))] impl<'q, T> AnyEncode<'q> for T where T: Encode<'q, Sqlite> + Type {} + +#[cfg(all( + not(any(feature = "mysql", feature = "mssql", feature = "postgres", feature = "sqlite")), + feature = "aurora" +))] +pub trait AnyEncode<'q>: Encode<'q, Aurora> + Type {} + +#[cfg(all( + not(any(feature = "mysql", feature = "mssql", feature = "postgres", feature = "sqlite")), + feature = "aurora" +))] +impl<'q, T> AnyEncode<'q> for T where T: Encode<'q, Aurora> + Type {} diff --git a/sqlx-core/src/any/kind.rs b/sqlx-core/src/any/kind.rs index 8d5454ed45..2175f855cd 100644 --- a/sqlx-core/src/any/kind.rs +++ b/sqlx-core/src/any/kind.rs @@ -14,6 +14,9 @@ pub enum AnyKind { #[cfg(feature = "mssql")] Mssql, + + #[cfg(feature = "aurora")] + Aurora, } impl FromStr for AnyKind { @@ -61,6 +64,16 @@ impl FromStr for AnyKind { Err(Error::Configuration("database URL has the scheme of a MSSQL database but the `mssql` feature is not enabled".into())) } + #[cfg(feature = "aurora")] + _ if uri.starts_with("aurora+data:") => { + Ok(AnyKind::Aurora) + } + + #[cfg(not(feature = "aurora"))] + _ if uri.starts_with("aurora+data:") => { + Err(Error::Configuration("database URL has the scheme of an Aurora database but the `aurora` feature is not enabled".into())) + } + _ => Err(Error::Configuration(format!("unrecognized database url: {:?}", uri).into())) } } diff --git a/sqlx-core/src/any/migrate.rs b/sqlx-core/src/any/migrate.rs index d5d7df5b3e..3d64110e6c 100644 --- a/sqlx-core/src/any/migrate.rs +++ b/sqlx-core/src/any/migrate.rs @@ -22,6 +22,9 @@ impl MigrateDatabase for Any { #[cfg(feature = "mssql")] AnyKind::Mssql => unimplemented!(), + + #[cfg(feature = "aurora")] + AnyKind::Aurora => crate::aurora::Aurora::create_database(uri).await, } }) } @@ -40,6 +43,9 @@ impl MigrateDatabase for Any { #[cfg(feature = "mssql")] AnyKind::Mssql => unimplemented!(), + + #[cfg(feature = "aurora")] + AnyKind::Aurora => crate::aurora::Aurora::database_exists(uri).await, } }) } @@ -58,6 +64,9 @@ impl MigrateDatabase for Any { #[cfg(feature = "mssql")] AnyKind::Mssql => unimplemented!(), + + #[cfg(feature = "aurora")] + AnyKind::Aurora => crate::aurora::Aurora::drop_database(uri).await, } }) } @@ -77,6 +86,9 @@ impl Migrate for AnyConnection { #[cfg(feature = "mssql")] AnyConnectionKind::Mssql(_conn) => unimplemented!(), + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.ensure_migrations_table(), } } @@ -93,6 +105,9 @@ impl Migrate for AnyConnection { #[cfg(feature = "mssql")] AnyConnectionKind::Mssql(_conn) => unimplemented!(), + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.version(), } } @@ -109,6 +124,9 @@ impl Migrate for AnyConnection { #[cfg(feature = "mssql")] AnyConnectionKind::Mssql(_conn) => unimplemented!(), + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.lock(), } } @@ -125,6 +143,9 @@ impl Migrate for AnyConnection { #[cfg(feature = "mssql")] AnyConnectionKind::Mssql(_conn) => unimplemented!(), + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.unlock(), } } @@ -147,6 +168,9 @@ impl Migrate for AnyConnection { let _ = migration; unimplemented!() } + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.validate(migration), } } @@ -169,6 +193,9 @@ impl Migrate for AnyConnection { let _ = migration; unimplemented!() } + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.apply(migration), } } @@ -188,6 +215,9 @@ impl Migrate for AnyConnection { #[cfg(feature = "mssql")] AnyConnectionKind::Mssql(conn) => unimplemented!(), + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => conn.revert(migration), } } } diff --git a/sqlx-core/src/any/options.rs b/sqlx-core/src/any/options.rs index a28ca0990a..684b1ead30 100644 --- a/sqlx-core/src/any/options.rs +++ b/sqlx-core/src/any/options.rs @@ -19,6 +19,9 @@ use crate::any::kind::AnyKind; #[cfg(feature = "mssql")] use crate::mssql::MssqlConnectOptions; +#[cfg(feature = "aurora")] +use crate::aurora::AuroraConnectOptions; + /// Opaque options for connecting to a database. These may only be constructed by parsing from /// a connection uri. /// @@ -43,6 +46,9 @@ impl AnyConnectOptions { #[cfg(feature = "mssql")] AnyConnectOptionsKind::Mssql(_) => AnyKind::Mssql, + + #[cfg(feature = "aurora")] + AnyConnectOptionsKind::Aurora(_) => AnyKind::Aurora, } } } @@ -60,6 +66,9 @@ pub(crate) enum AnyConnectOptionsKind { #[cfg(feature = "mssql")] Mssql(MssqlConnectOptions), + + #[cfg(feature = "aurora")] + Aurora(AuroraConnectOptions), } #[cfg(feature = "postgres")] @@ -90,6 +99,13 @@ impl From for AnyConnectOptions { } } +#[cfg(feature = "aurora")] +impl From for AnyConnectOptions { + fn from(options: AuroraConnectOptions) -> Self { + Self(AnyConnectOptionsKind::Aurora(options)) + } +} + impl FromStr for AnyConnectOptions { type Err = Error; @@ -110,6 +126,11 @@ impl FromStr for AnyConnectOptions { #[cfg(feature = "mssql")] AnyKind::Mssql => MssqlConnectOptions::from_str(url).map(AnyConnectOptionsKind::Mssql), + + #[cfg(feature = "aurora")] + AnyKind::Aurora => { + AuroraConnectOptions::from_str(url).map(AnyConnectOptionsKind::Aurora) + } } .map(AnyConnectOptions) } @@ -144,6 +165,11 @@ impl ConnectOptions for AnyConnectOptions { AnyConnectOptionsKind::Mssql(o) => { o.log_statements(level); } + + #[cfg(feature = "aurora")] + AnyConnectOptionsKind::Aurora(o) => { + o.log_statements(level); + } }; self } @@ -169,6 +195,11 @@ impl ConnectOptions for AnyConnectOptions { AnyConnectOptionsKind::Mssql(o) => { o.log_slow_statements(level, duration); } + + #[cfg(feature = "aurora")] + AnyConnectOptionsKind::Aurora(o) => { + o.log_slow_statements(level, duration); + } }; self } diff --git a/sqlx-core/src/any/row.rs b/sqlx-core/src/any/row.rs index f619b5c239..fe45bebd91 100644 --- a/sqlx-core/src/any/row.rs +++ b/sqlx-core/src/any/row.rs @@ -16,6 +16,9 @@ use crate::sqlite::SqliteRow; #[cfg(feature = "mssql")] use crate::mssql::MssqlRow; +#[cfg(feature = "aurora")] +use crate::aurora::AuroraRow; + pub struct AnyRow { pub(crate) kind: AnyRowKind, pub(crate) columns: Vec, @@ -35,6 +38,9 @@ pub(crate) enum AnyRowKind { #[cfg(feature = "mssql")] Mssql(MssqlRow), + + #[cfg(feature = "aurora")] + Aurora(AuroraRow), } impl Row for AnyRow { @@ -65,6 +71,9 @@ impl Row for AnyRow { #[cfg(feature = "mssql")] AnyRowKind::Mssql(row) => row.try_get_raw(index).map(Into::into), + + #[cfg(feature = "aurora")] + AnyRowKind::Aurora(row) => row.try_get_raw(index).map(Into::into), } } } @@ -86,6 +95,9 @@ where #[cfg(feature = "mssql")] AnyRowKind::Mssql(row) => self.index(row), + + #[cfg(feature = "aurora")] + AnyRowKind::Aurora(row) => self.index(row), } } } diff --git a/sqlx-core/src/any/transaction.rs b/sqlx-core/src/any/transaction.rs index 248e25847c..a5bd1f19bc 100644 --- a/sqlx-core/src/any/transaction.rs +++ b/sqlx-core/src/any/transaction.rs @@ -32,6 +32,11 @@ impl TransactionManager for AnyTransactionManager { AnyConnectionKind::Mssql(conn) => { ::TransactionManager::begin(conn) } + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => { + ::TransactionManager::begin(conn) + } } } @@ -56,6 +61,11 @@ impl TransactionManager for AnyTransactionManager { AnyConnectionKind::Mssql(conn) => { ::TransactionManager::commit(conn) } + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => { + ::TransactionManager::commit(conn) + } } } @@ -80,6 +90,11 @@ impl TransactionManager for AnyTransactionManager { AnyConnectionKind::Mssql(conn) => { ::TransactionManager::rollback(conn) } + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => { + ::TransactionManager::rollback(conn) + } } } @@ -104,6 +119,11 @@ impl TransactionManager for AnyTransactionManager { AnyConnectionKind::Mssql(conn) => { ::TransactionManager::start_rollback(conn) } + + #[cfg(feature = "aurora")] + AnyConnectionKind::Aurora(conn) => { + ::TransactionManager::start_rollback(conn) + } } } } diff --git a/sqlx-core/src/any/type.rs b/sqlx-core/src/any/type.rs index fc45e32258..fa20014c3c 100644 --- a/sqlx-core/src/any/type.rs +++ b/sqlx-core/src/any/type.rs @@ -12,6 +12,9 @@ use crate::mssql::Mssql; #[cfg(feature = "sqlite")] use crate::sqlite::Sqlite; +#[cfg(feature = "aurora")] +use crate::aurora::Aurora; + // Type is required by the bounds of the [Row] and [Arguments] trait but its been overridden in // AnyRow and AnyArguments to not use this implementation; but instead, delegate to the // database-specific implementation. @@ -50,6 +53,11 @@ macro_rules! impl_any_type { crate::any::type_info::AnyTypeInfoKind::Mssql(ty) => { <$ty as crate::types::Type>::compatible(&ty) } + + #[cfg(feature = "aurora")] + crate::any::type_info::AnyTypeInfoKind::Aurora(ty) => { + <$ty as crate::types::Type>::compatible(&ty) + } } } } @@ -250,3 +258,15 @@ pub trait AnyType: Type {} feature = "sqlite" ))] impl AnyType for T where T: Type {} + +#[cfg(all( + not(any(feature = "mysql", feature = "mssql", feature = "postgres", feature = "sqlite")), + feature = "aurora" +))] +pub trait AnyType: Type {} + +#[cfg(all( + not(any(feature = "mysql", feature = "mssql", feature = "postgres", feature = "sqlite")), + feature = "aurora" +))] +impl AnyType for T where T: Type {} \ No newline at end of file diff --git a/sqlx-core/src/any/type_info.rs b/sqlx-core/src/any/type_info.rs index 3d3c914f16..eae32f30ec 100644 --- a/sqlx-core/src/any/type_info.rs +++ b/sqlx-core/src/any/type_info.rs @@ -14,6 +14,9 @@ use crate::sqlite::SqliteTypeInfo; #[cfg(feature = "mssql")] use crate::mssql::MssqlTypeInfo; +#[cfg(feature = "aurora")] +use crate::aurora::AuroraTypeInfo; + #[derive(Debug, Clone, PartialEq)] pub struct AnyTypeInfo(pub(crate) AnyTypeInfoKind); @@ -30,6 +33,9 @@ pub(crate) enum AnyTypeInfoKind { #[cfg(feature = "mssql")] Mssql(MssqlTypeInfo), + + #[cfg(feature = "aurora")] + Aurora(AuroraTypeInfo), } impl TypeInfo for AnyTypeInfo { @@ -46,6 +52,9 @@ impl TypeInfo for AnyTypeInfo { #[cfg(feature = "mssql")] AnyTypeInfoKind::Mssql(ty) => ty.is_null(), + + #[cfg(feature = "aurora")] + AnyTypeInfoKind::Aurora(ty) => ty.is_null(), } } @@ -62,6 +71,9 @@ impl TypeInfo for AnyTypeInfo { #[cfg(feature = "mssql")] AnyTypeInfoKind::Mssql(ty) => ty.name(), + + #[cfg(feature = "aurora")] + AnyTypeInfoKind::Aurora(ty) => ty.name(), } } } @@ -80,6 +92,9 @@ impl Display for AnyTypeInfo { #[cfg(feature = "mssql")] AnyTypeInfoKind::Mssql(ty) => ty.fmt(f), + + #[cfg(feature = "aurora")] + AnyTypeInfoKind::Aurora(ty) => ty.fmt(f), } } } diff --git a/sqlx-core/src/any/value.rs b/sqlx-core/src/any/value.rs index b01d3ab774..3dbc63d09e 100644 --- a/sqlx-core/src/any/value.rs +++ b/sqlx-core/src/any/value.rs @@ -15,6 +15,9 @@ use crate::sqlite::{SqliteValue, SqliteValueRef}; #[cfg(feature = "mssql")] use crate::mssql::{MssqlValue, MssqlValueRef}; +#[cfg(feature = "aurora")] +use crate::aurora::{AuroraValue, AuroraValueRef}; + pub struct AnyValue { pub(crate) kind: AnyValueKind, pub(crate) type_info: AnyTypeInfo, @@ -32,6 +35,9 @@ pub(crate) enum AnyValueKind { #[cfg(feature = "mssql")] Mssql(MssqlValue), + + #[cfg(feature = "aurora")] + Aurora(AuroraValue), } pub struct AnyValueRef<'r> { @@ -51,6 +57,9 @@ pub(crate) enum AnyValueRefKind<'r> { #[cfg(feature = "mssql")] Mssql(MssqlValueRef<'r>), + + #[cfg(feature = "aurora")] + Aurora(AuroraValueRef<'r>), } impl Value for AnyValue { @@ -69,6 +78,9 @@ impl Value for AnyValue { #[cfg(feature = "mssql")] AnyValueKind::Mssql(value) => value.as_ref().into(), + + #[cfg(feature = "aurora")] + AnyValueKind::Aurora(value) => value.as_ref().into(), } } @@ -89,6 +101,9 @@ impl Value for AnyValue { #[cfg(feature = "mssql")] AnyValueKind::Mssql(value) => value.is_null(), + + #[cfg(feature = "aurora")] + AnyValueKind::Aurora(value) => value.is_null(), } } } @@ -109,6 +124,9 @@ impl<'r> ValueRef<'r> for AnyValueRef<'r> { #[cfg(feature = "mssql")] AnyValueRefKind::Mssql(value) => ValueRef::to_owned(value).into(), + + #[cfg(feature = "aurora")] + AnyValueRefKind::Aurora(value) => ValueRef::to_owned(value).into(), } } @@ -129,6 +147,9 @@ impl<'r> ValueRef<'r> for AnyValueRef<'r> { #[cfg(feature = "mssql")] AnyValueRefKind::Mssql(value) => value.is_null(), + + #[cfg(feature = "aurora")] + AnyValueRefKind::Aurora(value) => value.is_null(), } } } diff --git a/sqlx-core/src/aurora/arguments.rs b/sqlx-core/src/aurora/arguments.rs index 4b80607dd1..d5cfc990ef 100644 --- a/sqlx-core/src/aurora/arguments.rs +++ b/sqlx-core/src/aurora/arguments.rs @@ -11,6 +11,15 @@ pub struct AuroraArguments { pub(crate) parameters: Vec, } +impl AuroraArguments { + pub(crate) fn add<'q, T>(&mut self, value: T) + where + T: Encode<'q, Aurora> + Type, + { + let _ = value.encode(&mut self.parameters); + } +} + impl<'q> Arguments<'q> for AuroraArguments { type Database = Aurora; @@ -22,6 +31,6 @@ impl<'q> Arguments<'q> for AuroraArguments { where T: Encode<'q, Self::Database> + Type, { - let _ = value.encode(&mut self.parameters); + self.add(value); } } diff --git a/sqlx-core/src/aurora/column.rs b/sqlx-core/src/aurora/column.rs index 83de3174d6..9227ffac06 100644 --- a/sqlx-core/src/aurora/column.rs +++ b/sqlx-core/src/aurora/column.rs @@ -27,3 +27,14 @@ impl Column for AuroraColumn { &self.type_info } } + +#[cfg(feature = "any")] +impl From for crate::any::AnyColumn { + #[inline] + fn from(column: AuroraColumn) -> Self { + crate::any::AnyColumn { + type_info: column.type_info.into(), + kind: crate::any::column::AnyColumnKind::Aurora(column), + } + } +} diff --git a/sqlx-core/src/aurora/done.rs b/sqlx-core/src/aurora/done.rs index 0efe26f6c2..cff307f09d 100644 --- a/sqlx-core/src/aurora/done.rs +++ b/sqlx-core/src/aurora/done.rs @@ -22,3 +22,13 @@ impl Extend for AuroraDone { } } } + +#[cfg(feature = "any")] +impl From for crate::any::AnyDone { + fn from(done: AuroraDone) -> Self { + crate::any::AnyDone { + rows_affected: done.rows_affected, + last_insert_id: None, + } + } +} diff --git a/sqlx-core/src/aurora/mod.rs b/sqlx-core/src/aurora/mod.rs index 3b1f5c09d3..9bbb3a8a7b 100644 --- a/sqlx-core/src/aurora/mod.rs +++ b/sqlx-core/src/aurora/mod.rs @@ -40,4 +40,5 @@ impl_acquire!(Aurora, AuroraConnection); impl_column_index_for_row!(AuroraRow); impl_column_index_for_statement!(AuroraStatement); impl_into_maybe_pool!(Aurora, AuroraConnection); + impl_encode_for_option!(Aurora); diff --git a/sqlx-core/src/aurora/row.rs b/sqlx-core/src/aurora/row.rs index f241594a06..33c418b364 100644 --- a/sqlx-core/src/aurora/row.rs +++ b/sqlx-core/src/aurora/row.rs @@ -48,3 +48,20 @@ impl ColumnIndex for &'_ str { .map(|v| *v) } } + +#[cfg(feature = "any")] +impl From for crate::any::AnyRow { + #[inline] + fn from(row: AuroraRow) -> Self { + crate::any::AnyRow { + columns: row + .metadata + .columns + .iter() + .map(|col| col.clone().into()) + .collect(), + + kind: crate::any::row::AnyRowKind::Aurora(row), + } + } +} diff --git a/sqlx-core/src/aurora/statement.rs b/sqlx-core/src/aurora/statement.rs index 54bafcea5f..3aad419ec0 100644 --- a/sqlx-core/src/aurora/statement.rs +++ b/sqlx-core/src/aurora/statement.rs @@ -59,3 +59,28 @@ impl ColumnIndex> for &'_ str { .map(|v| *v) } } + +#[cfg(feature = "any")] +impl<'q> From> for crate::any::AnyStatement<'q> { + #[inline] + fn from(statement: AuroraStatement<'q>) -> Self { + crate::any::AnyStatement::<'q> { + columns: statement + .metadata + .columns + .iter() + .map(|col| col.clone().into()) + .collect(), + column_names: std::sync::Arc::new(statement.metadata.column_names.clone()), + parameters: Some(Either::Left( + statement + .metadata + .parameters + .iter() + .map(|ty| ty.clone().into()) + .collect(), + )), + sql: statement.sql, + } + } +} diff --git a/sqlx-core/src/aurora/type_info.rs b/sqlx-core/src/aurora/type_info.rs index ce8d86012c..9cbc8d2a75 100644 --- a/sqlx-core/src/aurora/type_info.rs +++ b/sqlx-core/src/aurora/type_info.rs @@ -97,3 +97,11 @@ impl From<&Field> for AuroraTypeInfo { } } } + +#[cfg(feature = "any")] +impl From for crate::any::AnyTypeInfo { + #[inline] + fn from(ty: AuroraTypeInfo) -> Self { + crate::any::AnyTypeInfo(crate::any::type_info::AnyTypeInfoKind::Aurora(ty)) + } +} diff --git a/sqlx-core/src/aurora/types/float.rs b/sqlx-core/src/aurora/types/float.rs index 4a0a150e54..e2863e8a6a 100644 --- a/sqlx-core/src/aurora/types/float.rs +++ b/sqlx-core/src/aurora/types/float.rs @@ -47,3 +47,39 @@ impl Decode<'_, Aurora> for f64 { .ok_or_else(|| Error::Decode("Not a double value".into()))?) } } + +impl Type for f32 { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::Double) + } +} + +impl Type for [f32] { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::DoubleArray) + } +} + +impl Type for Vec { + fn type_info() -> AuroraTypeInfo { + <[f32] as Type>::type_info() + } +} + +impl Encode<'_, Aurora> for f32 { + fn encode_by_ref(&self, buf: &mut Vec) -> IsNull { + >::encode_by_ref(&(*self as f64), buf) + } +} + +impl Decode<'_, Aurora> for f32 { + fn decode(value: AuroraValueRef<'_>) -> Result { + let f = >::decode(value)?; + + if f.gt(&(f32::MAX as f64)) { + return Err("".into()); + } else { + Ok(f as f32) + } + } +} diff --git a/sqlx-core/src/aurora/types/int.rs b/sqlx-core/src/aurora/types/int.rs index b3c843f016..412b2afa3b 100644 --- a/sqlx-core/src/aurora/types/int.rs +++ b/sqlx-core/src/aurora/types/int.rs @@ -6,6 +6,7 @@ use crate::error::{BoxDynError, Error}; use crate::types::Type; use rusoto_rds_data::{Field, SqlParameter}; +use std::convert::TryFrom; impl Type for i64 { fn type_info() -> AuroraTypeInfo { @@ -47,3 +48,33 @@ impl Decode<'_, Aurora> for i64 { .ok_or_else(|| Error::Decode("Not a double value".into()))?) } } + +impl Type for i32 { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::Long) + } +} + +impl Type for [i32] { + fn type_info() -> AuroraTypeInfo { + AuroraTypeInfo(AuroraType::LongArray) + } +} + +impl Type for Vec { + fn type_info() -> AuroraTypeInfo { + <[i32] as Type>::type_info() + } +} + +impl Encode<'_, Aurora> for i32 { + fn encode_by_ref(&self, buf: &mut Vec) -> IsNull { + >::encode_by_ref(&(*self as i64), buf) + } +} + +impl Decode<'_, Aurora> for i32 { + fn decode(value: AuroraValueRef<'_>) -> Result { + Ok(i32::try_from(>::decode(value)?)?) + } +} diff --git a/sqlx-core/src/aurora/value.rs b/sqlx-core/src/aurora/value.rs index 633f65562b..ae5ba59b15 100644 --- a/sqlx-core/src/aurora/value.rs +++ b/sqlx-core/src/aurora/value.rs @@ -57,3 +57,25 @@ impl<'r> ValueRef<'r> for AuroraValueRef<'r> { matches!(self.type_info, AuroraTypeInfo(AuroraType::Null)) } } + +#[cfg(feature = "any")] +impl<'r> From> for crate::any::AnyValueRef<'r> { + #[inline] + fn from(value: AuroraValueRef<'r>) -> Self { + crate::any::AnyValueRef { + type_info: value.type_info.clone().into(), + kind: crate::any::value::AnyValueRefKind::Aurora(value), + } + } +} + +#[cfg(feature = "any")] +impl From for crate::any::AnyValue { + #[inline] + fn from(value: AuroraValue) -> Self { + crate::any::AnyValue { + type_info: value.type_info.clone().into(), + kind: crate::any::value::AnyValueKind::Aurora(value), + } + } +} From 5d8b40bf352fe33175991bffc3efcbda47f95536 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 4 Dec 2020 15:25:09 -0800 Subject: [PATCH 09/15] add database to uri --- sqlx-core/src/aurora/options/mod.rs | 4 +++- sqlx-core/src/aurora/options/parse.rs | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sqlx-core/src/aurora/options/mod.rs b/sqlx-core/src/aurora/options/mod.rs index c57d769ed4..008271057a 100644 --- a/sqlx-core/src/aurora/options/mod.rs +++ b/sqlx-core/src/aurora/options/mod.rs @@ -67,12 +67,14 @@ impl AuroraConnectOptions { let resource_arn = var("AURORA_RESOURCE_ARN").ok(); let secret_arn = var("AURORA_SECRET_ARN").ok(); + let database = var("AURORA_DATABASE").ok(); + AuroraConnectOptions { db_type, region, resource_arn, secret_arn, - database: None, + database, schema: None, statement_cache_capacity: 100, log_settings: Default::default(), diff --git a/sqlx-core/src/aurora/options/parse.rs b/sqlx-core/src/aurora/options/parse.rs index af6853fd8f..9076efd739 100644 --- a/sqlx-core/src/aurora/options/parse.rs +++ b/sqlx-core/src/aurora/options/parse.rs @@ -29,6 +29,9 @@ impl FromStr for AuroraConnectOptions { "secret-arn" => { options = options.secret_arn(&*value); } + "database" => { + options = options.database(&*value); + } _ => log::warn!("ignoring unrecognized connect parameter: {}={}", key, value), } } From 53aa60efa7a0774f67d884da082f50534ba030dd Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 4 Dec 2020 15:25:58 -0800 Subject: [PATCH 10/15] fix decode error message --- sqlx-core/src/aurora/types/float.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlx-core/src/aurora/types/float.rs b/sqlx-core/src/aurora/types/float.rs index e2863e8a6a..dd2332f237 100644 --- a/sqlx-core/src/aurora/types/float.rs +++ b/sqlx-core/src/aurora/types/float.rs @@ -77,7 +77,7 @@ impl Decode<'_, Aurora> for f32 { let f = >::decode(value)?; if f.gt(&(f32::MAX as f64)) { - return Err("".into()); + Err("value is larger than f32::MAX".into()) } else { Ok(f as f32) } From 88e1e43f69c5376c724a18acd68f04f3a252050f Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 4 Dec 2020 15:32:04 -0800 Subject: [PATCH 11/15] fix cfg --- sqlx-core/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sqlx-core/src/lib.rs b/sqlx-core/src/lib.rs index 21b890c1ff..5c79d02938 100644 --- a/sqlx-core/src/lib.rs +++ b/sqlx-core/src/lib.rs @@ -78,7 +78,8 @@ pub mod migrate; feature = "postgres", feature = "mysql", feature = "mssql", - feature = "sqlite" + feature = "sqlite", + feature = "aurora" ), feature = "any" ))] From 5db4e59303c4f052c2f24818939eed2d2028cdd8 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 4 Dec 2020 15:45:23 -0800 Subject: [PATCH 12/15] add aurora feature to cli --- sqlx-cli/Cargo.toml | 1 + sqlx-cli/src/prepare.rs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/sqlx-cli/Cargo.toml b/sqlx-cli/Cargo.toml index 7547a4e007..be540338bd 100644 --- a/sqlx-cli/Cargo.toml +++ b/sqlx-cli/Cargo.toml @@ -51,6 +51,7 @@ default = [ "postgres", "sqlite", "mysql" ] mysql = [ "sqlx/mysql" ] postgres = [ "sqlx/postgres" ] sqlite = [ "sqlx/sqlite" ] +aurora = [ "sqlx/aurora" ] # workaround for musl + openssl issues openssl-vendored = [ "openssl/vendored" ] diff --git a/sqlx-cli/src/prepare.rs b/sqlx-cli/src/prepare.rs index 8a588ba1d3..45024c77a7 100644 --- a/sqlx-cli/src/prepare.rs +++ b/sqlx-cli/src/prepare.rs @@ -162,5 +162,8 @@ fn get_db_kind(url: &str) -> anyhow::Result<&'static str> { #[cfg(feature = "mssql")] AnyKind::Mssql => Ok("MSSQL"), + + #[cfg(feature = "aurora")] + AnyKind::Aurora => Ok("Aurora"), } } From 332cdd4f6ef34aea318387021e1f1193a7053e09 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 4 Dec 2020 15:58:52 -0800 Subject: [PATCH 13/15] fix cfg --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9f53f251e6..3792f85ce2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,8 @@ pub use sqlx_core::migrate; feature = "mysql", feature = "sqlite", feature = "postgres", - feature = "mssql" + feature = "mssql", + feature = "aurora" ), feature = "any" ))] From 258c51906850e0994d13392a4790cae526ae4a86 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 4 Dec 2020 17:24:09 -0800 Subject: [PATCH 14/15] prepare sql & parameters --- sqlx-core/src/aurora/connection/executor.rs | 41 +++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/sqlx-core/src/aurora/connection/executor.rs b/sqlx-core/src/aurora/connection/executor.rs index 3c9cb211b4..872f78d1f7 100644 --- a/sqlx-core/src/aurora/connection/executor.rs +++ b/sqlx-core/src/aurora/connection/executor.rs @@ -2,7 +2,8 @@ use super::AuroraConnection; use crate::aurora::error::AuroraDatabaseError; use crate::aurora::statement::AuroraStatementMetadata; use crate::aurora::{ - Aurora, AuroraArguments, AuroraColumn, AuroraDone, AuroraRow, AuroraStatement, AuroraTypeInfo, + Aurora, AuroraArguments, AuroraColumn, AuroraDbType, AuroraDone, AuroraRow, AuroraStatement, + AuroraTypeInfo, }; use crate::describe::Describe; use crate::error::Error; @@ -16,6 +17,8 @@ use futures_core::stream::BoxStream; use futures_core::Stream; use futures_util::stream; use futures_util::{pin_mut, TryStreamExt}; +use once_cell::sync::Lazy; +use regex::Regex; use rusoto_rds_data::{ExecuteStatementRequest, ExecuteStatementResponse, RdsData}; use std::borrow::Cow; use std::sync::Arc; @@ -24,15 +27,47 @@ impl AuroraConnection { async fn run<'e, 'c: 'e, 'q: 'e>( &'c mut self, query: &'q str, - arguments: Option, + mut arguments: Option, ) -> Result, Error>> + 'e, Error> { let mut logger = QueryLogger::new(query, self.log_settings.clone()); + static MYSQL_PARAMS_RE: Lazy = Lazy::new(|| Regex::new(r"\?").unwrap()); + static POSTGRES_PARAMS_RE: Lazy = Lazy::new(|| Regex::new(r"\$\d+").unwrap()); + + let regex = match self.db_type { + AuroraDbType::MySQL => &MYSQL_PARAMS_RE, + AuroraDbType::Postgres => &POSTGRES_PARAMS_RE, + }; + + let mut offset = 0; + let mut owned_query = query.to_owned(); + + if let Some(arguments) = arguments.as_mut() { + regex + .find_iter(query) + .zip(arguments.parameters.iter_mut()) + .enumerate() + .for_each(|(idx, (mat, param))| { + let name = format!("param_{}", idx + 1); + + owned_query.replace_range( + (mat.start() + offset)..(mat.end() + offset), + &format!(":{}", name), + ); + + offset += name.len() + 1 - mat.as_str().len(); + + param.name = Some(name); + }); + } + + dbg!(&owned_query); + // TODO: is this correct? let transaction_id = self.transaction_ids.last().cloned(); let request = ExecuteStatementRequest { - sql: query.to_owned(), + sql: owned_query, parameters: arguments.map(|m| m.parameters), resource_arn: self.resource_arn.clone(), secret_arn: self.secret_arn.clone(), From e4a468a7082f2d4f40777db12893e747c5b80952 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 5 Dec 2020 09:14:32 -0800 Subject: [PATCH 15/15] remove dbg --- sqlx-core/src/aurora/connection/executor.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/sqlx-core/src/aurora/connection/executor.rs b/sqlx-core/src/aurora/connection/executor.rs index 872f78d1f7..3d18548321 100644 --- a/sqlx-core/src/aurora/connection/executor.rs +++ b/sqlx-core/src/aurora/connection/executor.rs @@ -61,8 +61,6 @@ impl AuroraConnection { }); } - dbg!(&owned_query); - // TODO: is this correct? let transaction_id = self.transaction_ids.last().cloned();