diff --git a/Cargo.toml b/Cargo.toml index b7ed7ac2cb..3ad344c3f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -313,7 +313,7 @@ required-features = ["sqlite"] [[test]] name = "sqlite-any" path = "tests/sqlite/any.rs" -required-features = ["sqlite"] +required-features = ["sqlite", "any"] [[test]] name = "sqlite-types" @@ -415,6 +415,11 @@ name = "mysql-rustsec" path = "tests/mysql/rustsec.rs" required-features = ["mysql"] +[[test]] +name = "mysql-any" +path = "tests/mysql/any.rs" +required-features = ["mysql", "any"] + # # PostgreSQL # @@ -468,3 +473,8 @@ required-features = ["postgres"] name = "postgres-rustsec" path = "tests/postgres/rustsec.rs" required-features = ["postgres", "macros", "migrate"] + +[[test]] +name = "postgres-any" +path = "tests/postgres/any.rs" +required-features = ["postgres", "any"] diff --git a/README.md b/README.md index ff98f45350..e95d79b717 100644 --- a/README.md +++ b/README.md @@ -133,26 +133,26 @@ SQLx is compatible with the [`async-std`], [`tokio`], and [`actix`] runtimes; an # PICK ONE OF THE FOLLOWING: # tokio (no TLS) -sqlx = { version = "0.8", features = [ "runtime-tokio" ] } +sqlx = { version = "0.9", features = [ "runtime-tokio" ] } # tokio + native-tls -sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-native-tls" ] } +sqlx = { version = "0.9", features = [ "runtime-tokio", "tls-native-tls" ] } # tokio + rustls with ring and WebPKI CA certificates -sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls-ring-webpki" ] } +sqlx = { version = "0.9", features = [ "runtime-tokio", "tls-rustls-ring-webpki" ] } # tokio + rustls with ring and platform's native CA certificates -sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls-ring-native-roots" ] } +sqlx = { version = "0.9", features = [ "runtime-tokio", "tls-rustls-ring-native-roots" ] } # tokio + rustls with aws-lc-rs -sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls-aws-lc-rs" ] } +sqlx = { version = "0.9", features = [ "runtime-tokio", "tls-rustls-aws-lc-rs" ] } # async-std (no TLS) -sqlx = { version = "0.8", features = [ "runtime-async-std" ] } +sqlx = { version = "0.9", features = [ "runtime-async-std" ] } # async-std + native-tls -sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-native-tls" ] } +sqlx = { version = "0.9", features = [ "runtime-async-std", "tls-native-tls" ] } # async-std + rustls with ring and WebPKI CA certificates -sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-rustls-ring-webpki" ] } +sqlx = { version = "0.9", features = [ "runtime-async-std", "tls-rustls-ring-webpki" ] } # async-std + rustls with ring and platform's native CA certificates -sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-rustls-ring-native-roots" ] } +sqlx = { version = "0.9", features = [ "runtime-async-std", "tls-rustls-ring-native-roots" ] } # async-std + rustls with aws-lc-rs -sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-rustls-aws-lc-rs" ] } +sqlx = { version = "0.9", features = [ "runtime-async-std", "tls-rustls-aws-lc-rs" ] } ``` #### Cargo Feature Flags diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000000..1e59e6d32f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,6 @@ +# Examples + +Some of the examples require the use of the `sqlx` command line tool that is distributed via the [sqlx-cli](https://crates.io/crates/sqlx-cli). + +You can install it using the `cargo install sqlx-cli` command. + diff --git a/sqlx-cli/LICENSE-APACHE b/sqlx-cli/LICENSE-APACHE deleted file mode 120000 index 965b606f33..0000000000 --- a/sqlx-cli/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/sqlx-cli/LICENSE-APACHE b/sqlx-cli/LICENSE-APACHE new file mode 100644 index 0000000000..e14699fb14 --- /dev/null +++ b/sqlx-cli/LICENSE-APACHE @@ -0,0 +1,202 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/sqlx-cli/LICENSE-MIT b/sqlx-cli/LICENSE-MIT deleted file mode 120000 index 76219eb72e..0000000000 --- a/sqlx-cli/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/sqlx-cli/LICENSE-MIT b/sqlx-cli/LICENSE-MIT new file mode 100644 index 0000000000..6fe828e8ea --- /dev/null +++ b/sqlx-cli/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/sqlx-cli/src/opt.rs b/sqlx-cli/src/opt.rs index 48ec0207ba..9f0cd43e49 100644 --- a/sqlx-cli/src/opt.rs +++ b/sqlx-cli/src/opt.rs @@ -16,7 +16,6 @@ use std::path::PathBuf; const HELP_STYLES: Styles = Styles::styled() .header(AnsiColor::Blue.on_default().bold()) .usage(AnsiColor::Blue.on_default().bold()) - .literal(AnsiColor::White.on_default()) .placeholder(AnsiColor::Green.on_default()); #[derive(Parser, Debug)] diff --git a/sqlx-cli/src/prepare.rs b/sqlx-cli/src/prepare.rs index f3688add2a..b8dfe139b4 100644 --- a/sqlx-cli/src/prepare.rs +++ b/sqlx-cli/src/prepare.rs @@ -41,8 +41,7 @@ pub async fn run( connect_opts: ConnectOpts, cargo_args: Vec, ) -> anyhow::Result<()> { - let cargo = env::var_os("CARGO") - .context("failed to get value of `CARGO`; `prepare` subcommand may only be invoked as `cargo sqlx prepare`")?; + let cargo = cargo_command(env::var_os("CARGO"))?; anyhow::ensure!( Path::new("Cargo.toml").exists(), @@ -68,6 +67,13 @@ hint: This command only works in the manifest directory of a Cargo package or wo } } +fn cargo_command(cargo: Option) -> anyhow::Result { + cargo.context( + "the `prepare` subcommand must be invoked as `cargo sqlx prepare`; \ + running `sqlx prepare` directly is not supported", + ) +} + async fn prepare(ctx: &PrepareCtx<'_>) -> anyhow::Result<()> { if ctx.connect_opts.database_url.is_some() { check_backend(ctx.config, &ctx.connect_opts).await?; @@ -372,6 +378,16 @@ mod tests { use super::*; use std::assert_eq; + #[test] + fn missing_cargo_environment_explains_prepare_invocation() { + let error = cargo_command(None).expect_err("missing CARGO should fail"); + + assert_eq!( + error.to_string(), + "the `prepare` subcommand must be invoked as `cargo sqlx prepare`; running `sqlx prepare` directly is not supported" + ); + } + #[test] fn minimal_project_recompile_action_works() -> anyhow::Result<()> { let sample_metadata_path = Path::new("tests") diff --git a/sqlx-core/LICENSE-APACHE b/sqlx-core/LICENSE-APACHE deleted file mode 120000 index 965b606f33..0000000000 --- a/sqlx-core/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/sqlx-core/LICENSE-APACHE b/sqlx-core/LICENSE-APACHE new file mode 100644 index 0000000000..e14699fb14 --- /dev/null +++ b/sqlx-core/LICENSE-APACHE @@ -0,0 +1,202 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/sqlx-core/LICENSE-MIT b/sqlx-core/LICENSE-MIT deleted file mode 120000 index 76219eb72e..0000000000 --- a/sqlx-core/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/sqlx-core/LICENSE-MIT b/sqlx-core/LICENSE-MIT new file mode 100644 index 0000000000..6fe828e8ea --- /dev/null +++ b/sqlx-core/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/sqlx-core/src/any/arguments.rs b/sqlx-core/src/any/arguments.rs index 59d6f4d6e0..abb7098072 100644 --- a/sqlx-core/src/any/arguments.rs +++ b/sqlx-core/src/any/arguments.rs @@ -67,8 +67,8 @@ impl AnyArguments { AnyValueKind::Null(AnyTypeInfoKind::SmallInt) => out.add(Option::::None), AnyValueKind::Null(AnyTypeInfoKind::Integer) => out.add(Option::::None), AnyValueKind::Null(AnyTypeInfoKind::BigInt) => out.add(Option::::None), - AnyValueKind::Null(AnyTypeInfoKind::Real) => out.add(Option::::None), - AnyValueKind::Null(AnyTypeInfoKind::Double) => out.add(Option::::None), + AnyValueKind::Null(AnyTypeInfoKind::Real) => out.add(Option::::None), + AnyValueKind::Null(AnyTypeInfoKind::Double) => out.add(Option::::None), AnyValueKind::Null(AnyTypeInfoKind::Text) => out.add(Option::::None), AnyValueKind::Null(AnyTypeInfoKind::Blob) => out.add(Option::>::None), AnyValueKind::Bool(b) => out.add(b), diff --git a/sqlx-core/src/any/connection/mod.rs b/sqlx-core/src/any/connection/mod.rs index 894b109ccd..44a26f2fb3 100644 --- a/sqlx-core/src/any/connection/mod.rs +++ b/sqlx-core/src/any/connection/mod.rs @@ -101,7 +101,7 @@ impl Connection for AnyConnection { } fn close_hard(self) -> impl Future> + Send + 'static { - self.backend.close() + self.backend.close_hard() } fn ping(&mut self) -> impl Future> + Send + '_ { diff --git a/sqlx-core/src/net/socket/mod.rs b/sqlx-core/src/net/socket/mod.rs index 0f9aae61b4..c5cac47069 100644 --- a/sqlx-core/src/net/socket/mod.rs +++ b/sqlx-core/src/net/socket/mod.rs @@ -188,9 +188,9 @@ pub async fn connect_tcp( ) -> crate::Result { #[cfg(feature = "_rt-tokio")] if crate::rt::rt_tokio::available() { - return Ok(with_socket - .with_socket(tokio::net::TcpStream::connect((host, port)).await?) - .await); + let socket = tokio::net::TcpStream::connect((host, port)).await?; + socket.set_nodelay(true)?; + return Ok(with_socket.with_socket(socket).await); } cfg_if! { @@ -206,7 +206,7 @@ pub async fn connect_tcp( /// /// If `host` is a hostname, attempt to connect to each address it resolves to. /// -/// This implements the same behavior as [`tokio::net::TcpStream::connect()`]. +/// This implements the same behavior as [`tokio::net::TcpStream::connect()`] and additionally sets the `TCP_NODELAY` flag. #[cfg(feature = "_rt-async-io")] async fn connect_tcp_async_io(host: &str, port: u16) -> crate::Result { use async_io::Async; @@ -216,7 +216,9 @@ async fn connect_tcp_async_io(host: &str, port: u16) -> crate::Result() { - return Ok(Async::::connect((addr, port)).await?); + let socket = Async::::connect((addr, port)).await?; + socket.get_ref().set_nodelay(true)?; + return Ok(socket); } let host = host.to_string(); @@ -232,7 +234,10 @@ async fn connect_tcp_async_io(host: &str, port: u16) -> crate::Result::connect(socket_addr).await { - Ok(stream) => return Ok(stream), + Ok(stream) => { + stream.get_ref().set_nodelay(true)?; + return Ok(stream); + } Err(e) => last_err = Some(e), } } diff --git a/sqlx-core/src/net/tls/mod.rs b/sqlx-core/src/net/tls/mod.rs index 7bb1744189..5957e70d40 100644 --- a/sqlx-core/src/net/tls/mod.rs +++ b/sqlx-core/src/net/tls/mod.rs @@ -60,15 +60,37 @@ impl std::fmt::Display for CertificateInput { pub struct TlsConfig<'a> { pub accept_invalid_certs: bool, pub accept_invalid_hostnames: bool, - pub hostname: &'a str, pub root_cert_path: Option<&'a CertificateInput>, pub client_cert_path: Option<&'a CertificateInput>, pub client_key_path: Option<&'a CertificateInput>, } +#[cfg(feature = "_tls-native-tls")] +pub use self::tls_native_tls::NativeTlsConnector as TlsConnector; +#[cfg(all(feature = "_tls-rustls", not(feature = "_tls-native-tls")))] +pub use self::tls_rustls::RustlsConnector as TlsConnector; +#[cfg(not(any(feature = "_tls-native-tls", feature = "_tls-rustls")))] +#[derive(Debug, Clone)] +pub struct TlsConnector(std::convert::Infallible); + +pub async fn connector(config: TlsConfig<'_>) -> crate::Result { + #[cfg(feature = "_tls-native-tls")] + return tls_native_tls::connector(config).await; + + #[cfg(all(feature = "_tls-rustls", not(feature = "_tls-native-tls")))] + return tls_rustls::connector(config).await; + + #[cfg(not(any(feature = "_tls-native-tls", feature = "_tls-rustls")))] + { + _ = config; + panic!("one of the `runtime-*-native-tls` or `runtime-*-rustls` features must be enabled") + } +} + pub async fn handshake( socket: S, - config: TlsConfig<'_>, + hostname: &str, + connector: &TlsConnector, with_socket: Ws, ) -> crate::Result where @@ -77,18 +99,18 @@ where { #[cfg(feature = "_tls-native-tls")] return Ok(with_socket - .with_socket(tls_native_tls::handshake(socket, config).await?) + .with_socket(tls_native_tls::handshake(socket, hostname, connector).await?) .await); #[cfg(all(feature = "_tls-rustls", not(feature = "_tls-native-tls")))] return Ok(with_socket - .with_socket(tls_rustls::handshake(socket, config).await?) + .with_socket(tls_rustls::handshake(socket, hostname, connector).await?) .await); #[cfg(not(any(feature = "_tls-native-tls", feature = "_tls-rustls")))] { - drop((socket, config, with_socket)); - panic!("one of the `runtime-*-native-tls` or `runtime-*-rustls` features must be enabled") + drop((socket, hostname, with_socket)); + match connector.0 {} } } diff --git a/sqlx-core/src/net/tls/tls_native_tls.rs b/sqlx-core/src/net/tls/tls_native_tls.rs index 3423e48f8c..936cca093d 100644 --- a/sqlx-core/src/net/tls/tls_native_tls.rs +++ b/sqlx-core/src/net/tls/tls_native_tls.rs @@ -39,10 +39,12 @@ impl Socket for NativeTlsSocket { } } -pub async fn handshake( - socket: S, - config: TlsConfig<'_>, -) -> crate::Result> { +#[derive(Debug, Clone)] +pub struct NativeTlsConnector { + connector: native_tls::TlsConnector, +} + +pub async fn connector(config: TlsConfig<'_>) -> crate::Result { let mut builder = native_tls::TlsConnector::builder(); builder @@ -67,8 +69,18 @@ pub async fn handshake( let connector = rt::spawn_blocking(move || builder.build()) .await .map_err(Error::tls)?; + Ok(NativeTlsConnector { connector }) +} - let mut mid_handshake = match connector.connect(config.hostname, StdSocket::new(socket)) { +pub async fn handshake( + socket: S, + hostname: &str, + connector: &NativeTlsConnector, +) -> crate::Result> { + let mut mid_handshake = match connector + .connector + .connect(hostname, StdSocket::new(socket)) + { Ok(tls_stream) => return Ok(NativeTlsSocket { stream: tls_stream }), Err(HandshakeError::Failure(e)) => return Err(Error::tls(e)), Err(HandshakeError::WouldBlock(mid_handshake)) => mid_handshake, diff --git a/sqlx-core/src/net/tls/tls_rustls.rs b/sqlx-core/src/net/tls/tls_rustls.rs index 1ecbbad519..abc195a4f2 100644 --- a/sqlx-core/src/net/tls/tls_rustls.rs +++ b/sqlx-core/src/net/tls/tls_rustls.rs @@ -87,10 +87,12 @@ impl Socket for RustlsSocket { } } -pub async fn handshake(socket: S, tls_config: TlsConfig<'_>) -> Result, Error> -where - S: Socket, -{ +#[derive(Debug, Clone)] +pub struct RustlsConnector { + config: Arc, +} + +pub async fn connector(tls_config: TlsConfig<'_>) -> Result { #[cfg(all( feature = "_tls-rustls-aws-lc-rs", not(feature = "_tls-rustls-ring-webpki"), @@ -180,11 +182,24 @@ where } }; - let host = ServerName::try_from(tls_config.hostname.to_owned()).map_err(Error::tls)?; + Ok(RustlsConnector { + config: Arc::new(config), + }) +} + +pub async fn handshake( + socket: S, + hostname: &str, + connector: &RustlsConnector, +) -> Result, Error> +where + S: Socket, +{ + let host = ServerName::try_from(hostname.to_owned()).map_err(Error::tls)?; let mut socket = RustlsSocket { inner: StdSocket::new(socket), - state: ClientConnection::new(Arc::new(config), host).map_err(Error::tls)?, + state: ClientConnection::new(connector.config.clone(), host).map_err(Error::tls)?, close_notify_sent: false, }; diff --git a/sqlx-core/src/pool/mod.rs b/sqlx-core/src/pool/mod.rs index f11ff1d76a..a59a71778d 100644 --- a/sqlx-core/src/pool/mod.rs +++ b/sqlx-core/src/pool/mod.rs @@ -531,12 +531,14 @@ impl Pool { self.0.close_event() } - /// Returns the number of connections currently active. This includes idle connections. + /// Returns the total number of connections owned by the pool. + /// + /// This includes idle connections and ones still being opened. pub fn size(&self) -> u32 { self.0.size() } - /// Returns the number of connections active and idle (not in use). + /// Returns the number of idle connections (not checked out). pub fn num_idle(&self) -> usize { self.0.num_idle() } diff --git a/sqlx-macros-core/LICENSE-APACHE b/sqlx-macros-core/LICENSE-APACHE deleted file mode 120000 index 965b606f33..0000000000 --- a/sqlx-macros-core/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/sqlx-macros-core/LICENSE-APACHE b/sqlx-macros-core/LICENSE-APACHE new file mode 100644 index 0000000000..e14699fb14 --- /dev/null +++ b/sqlx-macros-core/LICENSE-APACHE @@ -0,0 +1,202 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/sqlx-macros-core/LICENSE-MIT b/sqlx-macros-core/LICENSE-MIT deleted file mode 120000 index 76219eb72e..0000000000 --- a/sqlx-macros-core/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/sqlx-macros-core/LICENSE-MIT b/sqlx-macros-core/LICENSE-MIT new file mode 100644 index 0000000000..6fe828e8ea --- /dev/null +++ b/sqlx-macros-core/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/sqlx-macros-core/src/query/metadata.rs b/sqlx-macros-core/src/query/metadata.rs index 5d5d3885d7..fef3b8aa57 100644 --- a/sqlx-macros-core/src/query/metadata.rs +++ b/sqlx-macros-core/src/query/metadata.rs @@ -175,7 +175,7 @@ fn load_env( })) } -/// Returns `true` if `val` is `"true"`, +/// Returns `true` if `val` is `"true"` (case-insensitive) or `"1"`. fn is_truthy_bool(val: &str) -> bool { val.eq_ignore_ascii_case("true") || val == "1" } diff --git a/sqlx-macros/LICENSE-APACHE b/sqlx-macros/LICENSE-APACHE deleted file mode 120000 index 965b606f33..0000000000 --- a/sqlx-macros/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/sqlx-macros/LICENSE-APACHE b/sqlx-macros/LICENSE-APACHE new file mode 100644 index 0000000000..e14699fb14 --- /dev/null +++ b/sqlx-macros/LICENSE-APACHE @@ -0,0 +1,202 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/sqlx-macros/LICENSE-MIT b/sqlx-macros/LICENSE-MIT deleted file mode 120000 index 76219eb72e..0000000000 --- a/sqlx-macros/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/sqlx-macros/LICENSE-MIT b/sqlx-macros/LICENSE-MIT new file mode 100644 index 0000000000..6fe828e8ea --- /dev/null +++ b/sqlx-macros/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/sqlx-mysql/LICENSE-APACHE b/sqlx-mysql/LICENSE-APACHE deleted file mode 120000 index 965b606f33..0000000000 --- a/sqlx-mysql/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/sqlx-mysql/LICENSE-APACHE b/sqlx-mysql/LICENSE-APACHE new file mode 100644 index 0000000000..e14699fb14 --- /dev/null +++ b/sqlx-mysql/LICENSE-APACHE @@ -0,0 +1,202 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/sqlx-mysql/LICENSE-MIT b/sqlx-mysql/LICENSE-MIT deleted file mode 120000 index 76219eb72e..0000000000 --- a/sqlx-mysql/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/sqlx-mysql/LICENSE-MIT b/sqlx-mysql/LICENSE-MIT new file mode 100644 index 0000000000..6fe828e8ea --- /dev/null +++ b/sqlx-mysql/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/sqlx-mysql/src/any.rs b/sqlx-mysql/src/any.rs index 57c895826f..9917d73871 100644 --- a/sqlx-mysql/src/any.rs +++ b/sqlx-mysql/src/any.rs @@ -96,7 +96,7 @@ impl AnyConnectionBackend for MySqlConnection { .try_flatten_stream() .map(|res| { Ok(match res? { - Either::Left(result) => Either::Left(map_result(result)), + Either::Left(result) => Either::Left(result.into()), Either::Right(row) => Either::Right(AnyRow::try_from(&row)?), }) }), @@ -210,11 +210,12 @@ impl<'a> TryFrom<&'a AnyConnectOptions> for MySqlConnectOptions { } } -fn map_result(result: MySqlQueryResult) -> AnyQueryResult { - AnyQueryResult { - rows_affected: result.rows_affected, - // Don't expect this to be a problem - #[allow(clippy::cast_possible_wrap)] - last_insert_id: Some(result.last_insert_id as i64), +/// This conversion attempts to save last_insert_id by converting to i64. +impl From for AnyQueryResult { + fn from(done: MySqlQueryResult) -> Self { + AnyQueryResult { + rows_affected: done.rows_affected(), + last_insert_id: done.last_insert_id().try_into().ok(), + } } } diff --git a/sqlx-mysql/src/connection/establish.rs b/sqlx-mysql/src/connection/establish.rs index f61654d876..9e670cfc42 100644 --- a/sqlx-mysql/src/connection/establish.rs +++ b/sqlx-mysql/src/connection/establish.rs @@ -42,7 +42,7 @@ impl<'a> DoHandshake<'a> { fn new(options: &'a MySqlConnectOptions) -> Result { if options.enable_cleartext_plugin && matches!( - options.ssl_mode, + options.ssl_options.ssl_mode, MySqlSslMode::Disabled | MySqlSslMode::Preferred ) { diff --git a/sqlx-mysql/src/connection/stream.rs b/sqlx-mysql/src/connection/stream.rs index e6aa8b48c8..25c9f3c7ed 100644 --- a/sqlx-mysql/src/connection/stream.rs +++ b/sqlx-mysql/src/connection/stream.rs @@ -35,7 +35,6 @@ impl MySqlStream { let mut capabilities = Capabilities::PROTOCOL_41 | Capabilities::IGNORE_SPACE | Capabilities::DEPRECATE_EOF - | Capabilities::FOUND_ROWS | Capabilities::TRANSACTIONS | Capabilities::SECURE_CONNECTION | Capabilities::PLUGIN_AUTH_LENENC_DATA @@ -49,6 +48,10 @@ impl MySqlStream { capabilities |= Capabilities::CONNECT_WITH_DB; } + if options.found_rows { + capabilities |= Capabilities::FOUND_ROWS; + } + Self { waiting: VecDeque::new(), capabilities, diff --git a/sqlx-mysql/src/connection/tls.rs b/sqlx-mysql/src/connection/tls.rs index 9034fbd63a..dddb4c6aa5 100644 --- a/sqlx-mysql/src/connection/tls.rs +++ b/sqlx-mysql/src/connection/tls.rs @@ -20,13 +20,13 @@ pub(super) async fn maybe_upgrade( ) -> Result { let server_supports_tls = stream.capabilities.contains(Capabilities::SSL); - if matches!(options.ssl_mode, MySqlSslMode::Disabled) || !tls::available() { + if matches!(options.ssl_options.ssl_mode, MySqlSslMode::Disabled) || !tls::available() { // remove the SSL capability if SSL has been explicitly disabled stream.capabilities.remove(Capabilities::SSL); } // https://www.postgresql.org/docs/12/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS - match options.ssl_mode { + match options.ssl_options.ssl_mode { MySqlSslMode::Disabled => return Ok(stream.boxed_socket()), MySqlSslMode::Preferred => { @@ -53,16 +53,27 @@ pub(super) async fn maybe_upgrade( } } - let tls_config = TlsConfig { - accept_invalid_certs: !matches!( - options.ssl_mode, - MySqlSslMode::VerifyCa | MySqlSslMode::VerifyIdentity - ), - accept_invalid_hostnames: !matches!(options.ssl_mode, MySqlSslMode::VerifyIdentity), - hostname: &options.host, - root_cert_path: options.ssl_ca.as_ref(), - client_cert_path: options.ssl_client_cert.as_ref(), - client_key_path: options.ssl_client_key.as_ref(), + let connector = if let Some(c) = options.ssl_options.cached_connector.get() { + c + } else { + let tls_config = TlsConfig { + accept_invalid_certs: !matches!( + options.ssl_options.ssl_mode, + MySqlSslMode::VerifyCa | MySqlSslMode::VerifyIdentity + ), + accept_invalid_hostnames: !matches!( + options.ssl_options.ssl_mode, + MySqlSslMode::VerifyIdentity + ), + root_cert_path: options.ssl_options.ssl_ca.as_ref(), + client_cert_path: options.ssl_options.ssl_client_cert.as_ref(), + client_key_path: options.ssl_options.ssl_client_key.as_ref(), + }; + let connector = tls::connector(tls_config).await?; + options + .ssl_options + .cached_connector + .get_or_init(|| connector) }; // Request TLS upgrade @@ -75,7 +86,8 @@ pub(super) async fn maybe_upgrade( tls::handshake( stream.socket.into_inner(), - tls_config, + &options.host, + connector, MapStream { server_version: stream.server_version, capabilities: stream.capabilities, diff --git a/sqlx-mysql/src/options/mod.rs b/sqlx-mysql/src/options/mod.rs index 421bfb700e..95413b6b11 100644 --- a/sqlx-mysql/src/options/mod.rs +++ b/sqlx-mysql/src/options/mod.rs @@ -1,10 +1,14 @@ -use std::path::{Path, PathBuf}; +use std::{ + path::{Path, PathBuf}, + sync::{Arc, OnceLock}, +}; mod connect; mod parse; mod ssl_mode; use crate::{connection::LogSettings, net::tls::CertificateInput}; +use sqlx_core::net::tls::TlsConnector; pub use ssl_mode::MySqlSslMode; /// Options and flags which can be used to configure a MySQL connection. @@ -67,10 +71,7 @@ pub struct MySqlConnectOptions { pub(crate) username: String, pub(crate) password: Option, pub(crate) database: Option, - pub(crate) ssl_mode: MySqlSslMode, - pub(crate) ssl_ca: Option, - pub(crate) ssl_client_cert: Option, - pub(crate) ssl_client_key: Option, + pub(crate) ssl_options: SslOptions, pub(crate) statement_cache_capacity: usize, pub(crate) charset: String, pub(crate) collation: Option, @@ -80,6 +81,7 @@ pub struct MySqlConnectOptions { pub(crate) no_engine_substitution: bool, pub(crate) timezone: Option, pub(crate) set_names: bool, + pub(crate) found_rows: bool, } impl Default for MySqlConnectOptions { @@ -88,6 +90,15 @@ impl Default for MySqlConnectOptions { } } +#[derive(Debug, Clone)] +pub(crate) struct SslOptions { + pub(crate) ssl_mode: MySqlSslMode, + pub(crate) ssl_ca: Option, + pub(crate) ssl_client_cert: Option, + pub(crate) ssl_client_key: Option, + pub(crate) cached_connector: Arc>, +} + impl MySqlConnectOptions { /// Creates a new, default set of options ready for configuration pub fn new() -> Self { @@ -100,10 +111,13 @@ impl MySqlConnectOptions { database: None, charset: String::from("utf8mb4"), collation: None, - ssl_mode: MySqlSslMode::Preferred, - ssl_ca: None, - ssl_client_cert: None, - ssl_client_key: None, + ssl_options: SslOptions { + ssl_mode: MySqlSslMode::Preferred, + ssl_ca: None, + ssl_client_cert: None, + ssl_client_key: None, + cached_connector: Arc::new(OnceLock::new()), + }, statement_cache_capacity: 100, log_settings: Default::default(), pipes_as_concat: true, @@ -111,6 +125,7 @@ impl MySqlConnectOptions { no_engine_substitution: true, timezone: Some(String::from("+00:00")), set_names: true, + found_rows: true, } } @@ -158,6 +173,11 @@ impl MySqlConnectOptions { self } + fn ssl_options_mut(&mut self) -> &mut SslOptions { + Arc::make_mut(&mut self.ssl_options.cached_connector).take(); + &mut self.ssl_options + } + /// Sets whether or with what priority a secure SSL TCP/IP connection will be negotiated /// with the server. /// @@ -172,7 +192,7 @@ impl MySqlConnectOptions { /// .ssl_mode(MySqlSslMode::Required); /// ``` pub fn ssl_mode(mut self, mode: MySqlSslMode) -> Self { - self.ssl_mode = mode; + self.ssl_options_mut().ssl_mode = mode; self } @@ -187,7 +207,7 @@ impl MySqlConnectOptions { /// .ssl_ca("path/to/ca.crt"); /// ``` pub fn ssl_ca(mut self, file_name: impl AsRef) -> Self { - self.ssl_ca = Some(CertificateInput::File(file_name.as_ref().to_owned())); + self.ssl_options_mut().ssl_ca = Some(CertificateInput::File(file_name.as_ref().to_owned())); self } @@ -202,7 +222,7 @@ impl MySqlConnectOptions { /// .ssl_ca_from_pem(vec![]); /// ``` pub fn ssl_ca_from_pem(mut self, pem_certificate: Vec) -> Self { - self.ssl_ca = Some(CertificateInput::Inline(pem_certificate)); + self.ssl_options_mut().ssl_ca = Some(CertificateInput::Inline(pem_certificate)); self } @@ -217,7 +237,8 @@ impl MySqlConnectOptions { /// .ssl_client_cert("path/to/client.crt"); /// ``` pub fn ssl_client_cert(mut self, cert: impl AsRef) -> Self { - self.ssl_client_cert = Some(CertificateInput::File(cert.as_ref().to_path_buf())); + self.ssl_options_mut().ssl_client_cert = + Some(CertificateInput::File(cert.as_ref().to_path_buf())); self } @@ -242,7 +263,8 @@ impl MySqlConnectOptions { /// .ssl_client_cert_from_pem(CERT); /// ``` pub fn ssl_client_cert_from_pem(mut self, cert: impl AsRef<[u8]>) -> Self { - self.ssl_client_cert = Some(CertificateInput::Inline(cert.as_ref().to_vec())); + self.ssl_options_mut().ssl_client_cert = + Some(CertificateInput::Inline(cert.as_ref().to_vec())); self } @@ -257,7 +279,8 @@ impl MySqlConnectOptions { /// .ssl_client_key("path/to/client.key"); /// ``` pub fn ssl_client_key(mut self, key: impl AsRef) -> Self { - self.ssl_client_key = Some(CertificateInput::File(key.as_ref().to_path_buf())); + self.ssl_options_mut().ssl_client_key = + Some(CertificateInput::File(key.as_ref().to_path_buf())); self } @@ -282,7 +305,8 @@ impl MySqlConnectOptions { /// .ssl_client_key_from_pem(KEY); /// ``` pub fn ssl_client_key_from_pem(mut self, key: impl AsRef<[u8]>) -> Self { - self.ssl_client_key = Some(CertificateInput::Inline(key.as_ref().to_vec())); + self.ssl_options_mut().ssl_client_key = + Some(CertificateInput::Inline(key.as_ref().to_vec())); self } @@ -414,6 +438,15 @@ impl MySqlConnectOptions { self.set_names = flag_val; self } + + /// Sets the flag that enables or disables CLIENT_FOUND_ROWS, + /// to return the number of found (matched) rows, and not the number of changed rows. + /// + /// The default value is set to true. + pub fn found_rows(mut self, flag_val: bool) -> Self { + self.found_rows = flag_val; + self + } } impl MySqlConnectOptions { @@ -497,7 +530,7 @@ impl MySqlConnectOptions { /// assert!(matches!(options.get_ssl_mode(), MySqlSslMode::Preferred)); /// ``` pub fn get_ssl_mode(&self) -> MySqlSslMode { - self.ssl_mode + self.ssl_options.ssl_mode } /// Get the server charset. diff --git a/sqlx-mysql/src/options/parse.rs b/sqlx-mysql/src/options/parse.rs index e31ddc46d4..37db00ef6c 100644 --- a/sqlx-mysql/src/options/parse.rs +++ b/sqlx-mysql/src/options/parse.rs @@ -103,7 +103,7 @@ impl MySqlConnectOptions { url.set_path(database); } - let ssl_mode = match self.ssl_mode { + let ssl_mode = match self.ssl_options.ssl_mode { MySqlSslMode::Disabled => "DISABLED", MySqlSslMode::Preferred => "PREFERRED", MySqlSslMode::Required => "REQUIRED", @@ -112,7 +112,7 @@ impl MySqlConnectOptions { }; url.query_pairs_mut().append_pair("ssl-mode", ssl_mode); - if let Some(ssl_ca) = &self.ssl_ca { + if let Some(ssl_ca) = &self.ssl_options.ssl_ca { url.query_pairs_mut() .append_pair("ssl-ca", &ssl_ca.to_string()); } @@ -123,12 +123,12 @@ impl MySqlConnectOptions { url.query_pairs_mut().append_pair("charset", collation); } - if let Some(ssl_client_cert) = &self.ssl_client_cert { + if let Some(ssl_client_cert) = &self.ssl_options.ssl_client_cert { url.query_pairs_mut() .append_pair("ssl-cert", &ssl_client_cert.to_string()); } - if let Some(ssl_client_key) = &self.ssl_client_key { + if let Some(ssl_client_key) = &self.ssl_options.ssl_client_key { url.query_pairs_mut() .append_pair("ssl-key", &ssl_client_key.to_string()); } diff --git a/sqlx-mysql/src/query_result.rs b/sqlx-mysql/src/query_result.rs index f008db06ae..9951b26acb 100644 --- a/sqlx-mysql/src/query_result.rs +++ b/sqlx-mysql/src/query_result.rs @@ -24,13 +24,3 @@ impl Extend for MySqlQueryResult { } } } -#[cfg(feature = "any")] -/// This conversion attempts to save last_insert_id by converting to i64. -impl From for sqlx_core::any::AnyQueryResult { - fn from(done: MySqlQueryResult) -> Self { - sqlx_core::any::AnyQueryResult { - rows_affected: done.rows_affected(), - last_insert_id: done.last_insert_id().try_into().ok(), - } - } -} diff --git a/sqlx-mysql/src/types/mysql_time.rs b/sqlx-mysql/src/types/mysql_time.rs index 6af10aa216..84ae9a9bbc 100644 --- a/sqlx-mysql/src/types/mysql_time.rs +++ b/sqlx-mysql/src/types/mysql_time.rs @@ -213,7 +213,7 @@ impl MySqlTime { /// Returns `true` if `self` is negative, `false` if positive or zero. pub fn is_negative(&self) -> bool { - self.sign.is_positive() + self.sign.is_negative() } /// Returns `true` if this interval is a valid time-of-day. @@ -691,6 +691,13 @@ mod tests { assert_eq!(format!("{negative:.9}"), "-123:45:56.890011000"); } + #[test] + fn test_is_negative() { + assert!(!MySqlTime::ZERO.is_negative()); + assert!(!MySqlTime::MAX.is_negative()); + assert!(MySqlTime::MIN.is_negative()); + } + #[test] fn test_parse_microseconds() { assert_eq!(parse_microseconds("010").unwrap(), 10_000); diff --git a/sqlx-postgres/LICENSE-APACHE b/sqlx-postgres/LICENSE-APACHE deleted file mode 120000 index 965b606f33..0000000000 --- a/sqlx-postgres/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/sqlx-postgres/LICENSE-APACHE b/sqlx-postgres/LICENSE-APACHE new file mode 100644 index 0000000000..e14699fb14 --- /dev/null +++ b/sqlx-postgres/LICENSE-APACHE @@ -0,0 +1,202 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/sqlx-postgres/LICENSE-MIT b/sqlx-postgres/LICENSE-MIT deleted file mode 120000 index 76219eb72e..0000000000 --- a/sqlx-postgres/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/sqlx-postgres/LICENSE-MIT b/sqlx-postgres/LICENSE-MIT new file mode 100644 index 0000000000..6fe828e8ea --- /dev/null +++ b/sqlx-postgres/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/sqlx-postgres/src/any.rs b/sqlx-postgres/src/any.rs index 62b3dedbac..1329d7fa09 100644 --- a/sqlx-postgres/src/any.rs +++ b/sqlx-postgres/src/any.rs @@ -97,7 +97,7 @@ impl AnyConnectionBackend for PgConnection { .try_flatten_stream() .map( move |res: sqlx_core::Result>| match res? { - Either::Left(result) => Ok(Either::Left(map_result(result))), + Either::Left(result) => Ok(Either::Left(result.into())), Either::Right(row) => Ok(Either::Right(AnyRow::try_from(&row)?)), }, ), @@ -246,9 +246,11 @@ impl<'a> TryFrom<&'a AnyConnectOptions> for PgConnectOptions { } } -fn map_result(res: PgQueryResult) -> AnyQueryResult { - AnyQueryResult { - rows_affected: res.rows_affected(), - last_insert_id: None, +impl From for AnyQueryResult { + fn from(done: PgQueryResult) -> Self { + AnyQueryResult { + rows_affected: done.rows_affected(), + last_insert_id: None, + } } } diff --git a/sqlx-postgres/src/connection/executor.rs b/sqlx-postgres/src/connection/executor.rs index e0f4c3d44a..00a2b434d9 100644 --- a/sqlx-postgres/src/connection/executor.rs +++ b/sqlx-postgres/src/connection/executor.rs @@ -28,7 +28,9 @@ async fn prepare( persistent: bool, resolve_column_origin: bool, ) -> Result<(StatementId, Arc), Error> { - let id = if persistent { + // if cache is disabled, persistent statements get an id but are never evicted + // which causes a memory leak + let id = if persistent && conn.inner.cache_statement.is_enabled() { let id = conn.inner.next_statement_id; conn.inner.next_statement_id = id.next(); id diff --git a/sqlx-postgres/src/connection/tls.rs b/sqlx-postgres/src/connection/tls.rs index a49c9caa8c..7b8bea3341 100644 --- a/sqlx-postgres/src/connection/tls.rs +++ b/sqlx-postgres/src/connection/tls.rs @@ -20,7 +20,7 @@ async fn maybe_upgrade( options: &PgConnectOptions, ) -> Result, Error> { // https://www.postgresql.org/docs/12/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS - match options.ssl_mode { + match options.ssl_options.ssl_mode { // FIXME: Implement ALLOW PgSslMode::Allow | PgSslMode::Disable => return Ok(Box::new(socket)), @@ -45,22 +45,31 @@ async fn maybe_upgrade( } } - let accept_invalid_certs = !matches!( - options.ssl_mode, - PgSslMode::VerifyCa | PgSslMode::VerifyFull - ); - let accept_invalid_hostnames = !matches!(options.ssl_mode, PgSslMode::VerifyFull); - - let config = TlsConfig { - accept_invalid_certs, - accept_invalid_hostnames, - hostname: &options.host, - root_cert_path: options.ssl_root_cert.as_ref(), - client_cert_path: options.ssl_client_cert.as_ref(), - client_key_path: options.ssl_client_key.as_ref(), + let connector = if let Some(c) = options.ssl_options.cached_connector.get() { + c + } else { + let accept_invalid_certs = !matches!( + options.ssl_options.ssl_mode, + PgSslMode::VerifyCa | PgSslMode::VerifyFull + ); + let accept_invalid_hostnames = + !matches!(options.ssl_options.ssl_mode, PgSslMode::VerifyFull); + + let config = TlsConfig { + accept_invalid_certs, + accept_invalid_hostnames, + root_cert_path: options.ssl_options.ssl_root_cert.as_ref(), + client_cert_path: options.ssl_options.ssl_client_cert.as_ref(), + client_key_path: options.ssl_options.ssl_client_key.as_ref(), + }; + let connector = tls::connector(config).await?; + options + .ssl_options + .cached_connector + .get_or_init(|| connector) }; - tls::handshake(socket, config, SocketIntoBox).await + tls::handshake(socket, &options.host, connector, SocketIntoBox).await } async fn request_upgrade( diff --git a/sqlx-postgres/src/options/doc.md b/sqlx-postgres/src/options/doc.md index 33dd63b7a8..a51157f6ab 100644 --- a/sqlx-postgres/src/options/doc.md +++ b/sqlx-postgres/src/options/doc.md @@ -105,6 +105,34 @@ This behavior is _only_ implemented for the environment variables, not the URL p Note: passing the SSL private key via environment variable may be a security risk. +# Note: TimeZone +Upon connection, SQLx sets the session `TimeZone` parameter to `UTC`, +regardless of the server's default. This ensures consistent behavior +across environments (e.g., replicas in different regions, servers with +locale-specific configurations) and matches the default of the MySQL +driver, which is required for correct binary timestamp interpretation. + +This differs from the behavior of `psql`, which does not set a session +`TimeZone` and thus inherits the server's default. As a result, functions +whose output depends on the session `TimeZone` — such as [`date_trunc`], +[`now`], [`current_time`] and [`AT TIME ZONE`] — may produce different +results in SQLx than in `psql` against the same database. + +For example, `date_trunc('day', ts)` on a `timestamptz` truncates relative +to the session `TimeZone`, so under SQLx it always truncates relative to UTC. +To truncate relative to a specific time zone, pass an explicit third argument: + +```text +date_trunc('day', ts, 'Europe/Helsinki') +``` + +If you need `psql`-compatible behavior, you can override the session +`TimeZone` after connection with `SET TIME ZONE`, or handle time zones +explicitly in your SQL. + +The general recommendation is to store timestamps as `TIMESTAMP WITH TIME ZONE` +and to keep all application logic in UTC. + # Note: Unix Domain Sockets If you want to connect to Postgres over a Unix domain socket, you can pass the path to the _directory_ containing the socket as the `host` parameter. @@ -182,4 +210,8 @@ let pool = PgPool::connect_with(opts).await?; [libpq-params]: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS [libpq-envars]: https://www.postgresql.org/docs/current/libpq-envars.html [rfc7468]: https://datatracker.ietf.org/doc/html/rfc7468 -[`webpki-roots`]: https://docs.rs/webpki-roots \ No newline at end of file +[`webpki-roots`]: https://docs.rs/webpki-roots +[`date_trunc`]: https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC +[`now`]: https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT +[`current_time`]: https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT +[`AT TIME ZONE`]: https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT \ No newline at end of file diff --git a/sqlx-postgres/src/options/mod.rs b/sqlx-postgres/src/options/mod.rs index 21e6628cae..1432673720 100644 --- a/sqlx-postgres/src/options/mod.rs +++ b/sqlx-postgres/src/options/mod.rs @@ -2,7 +2,9 @@ use std::borrow::Cow; use std::env::var; use std::fmt::{self, Display, Write}; use std::path::{Path, PathBuf}; +use std::sync::{Arc, OnceLock}; +use sqlx_core::net::tls::TlsConnector; pub use ssl_mode::PgSslMode; use crate::{connection::LogSettings, net::tls::CertificateInput}; @@ -21,10 +23,7 @@ pub struct PgConnectOptions { pub(crate) username: String, pub(crate) password: Option, pub(crate) database: Option, - pub(crate) ssl_mode: PgSslMode, - pub(crate) ssl_root_cert: Option, - pub(crate) ssl_client_cert: Option, - pub(crate) ssl_client_key: Option, + pub(crate) ssl_options: SslOptions, pub(crate) statement_cache_capacity: usize, pub(crate) application_name: Option, pub(crate) log_settings: LogSettings, @@ -38,6 +37,15 @@ impl Default for PgConnectOptions { } } +#[derive(Debug, Clone)] +pub(crate) struct SslOptions { + pub(crate) ssl_mode: PgSslMode, + pub(crate) ssl_root_cert: Option, + pub(crate) ssl_client_cert: Option, + pub(crate) ssl_client_key: Option, + pub(crate) cached_connector: Arc>, +} + impl PgConnectOptions { /// Create a default set of connection options populated from the current environment. /// @@ -82,16 +90,19 @@ impl PgConnectOptions { username, password: var("PGPASSWORD").ok(), database, - ssl_root_cert: var("PGSSLROOTCERT").ok().map(CertificateInput::from), - ssl_client_cert: var("PGSSLCERT").ok().map(CertificateInput::from), - // As of writing, the implementation of `From` only looks for - // `-----BEGIN CERTIFICATE-----` and so will not attempt to parse - // a PEM-encoded private key. - ssl_client_key: var("PGSSLKEY").ok().map(CertificateInput::from), - ssl_mode: var("PGSSLMODE") - .ok() - .and_then(|v| v.parse().ok()) - .unwrap_or_default(), + ssl_options: SslOptions { + ssl_root_cert: var("PGSSLROOTCERT").ok().map(CertificateInput::from), + ssl_client_cert: var("PGSSLCERT").ok().map(CertificateInput::from), + // As of writing, the implementation of `From` only looks for + // `-----BEGIN CERTIFICATE-----` and so will not attempt to parse + // a PEM-encoded private key. + ssl_client_key: var("PGSSLKEY").ok().map(CertificateInput::from), + ssl_mode: var("PGSSLMODE") + .ok() + .and_then(|v| v.parse().ok()) + .unwrap_or_default(), + cached_connector: Arc::new(OnceLock::new()), + }, statement_cache_capacity: 100, application_name: var("PGAPPNAME").ok(), extra_float_digits: Some("2".into()), @@ -205,6 +216,11 @@ impl PgConnectOptions { self } + fn ssl_options_mut(&mut self) -> &mut SslOptions { + Arc::make_mut(&mut self.ssl_options.cached_connector).take(); + &mut self.ssl_options + } + /// Sets whether or with what priority a secure SSL TCP/IP connection will be negotiated /// with the server. /// @@ -221,7 +237,7 @@ impl PgConnectOptions { /// .ssl_mode(PgSslMode::Require); /// ``` pub fn ssl_mode(mut self, mode: PgSslMode) -> Self { - self.ssl_mode = mode; + self.ssl_options_mut().ssl_mode = mode; self } @@ -239,7 +255,8 @@ impl PgConnectOptions { /// .ssl_root_cert("./ca-certificate.crt"); /// ``` pub fn ssl_root_cert(mut self, cert: impl AsRef) -> Self { - self.ssl_root_cert = Some(CertificateInput::File(cert.as_ref().to_path_buf())); + self.ssl_options_mut().ssl_root_cert = + Some(CertificateInput::File(cert.as_ref().to_path_buf())); self } @@ -255,7 +272,8 @@ impl PgConnectOptions { /// .ssl_client_cert("./client.crt"); /// ``` pub fn ssl_client_cert(mut self, cert: impl AsRef) -> Self { - self.ssl_client_cert = Some(CertificateInput::File(cert.as_ref().to_path_buf())); + self.ssl_options_mut().ssl_client_cert = + Some(CertificateInput::File(cert.as_ref().to_path_buf())); self } @@ -274,14 +292,15 @@ impl PgConnectOptions { /// -----BEGIN CERTIFICATE----- /// /// -----END CERTIFICATE-----"; - /// + /// /// let options = PgConnectOptions::new() /// // Providing a CA certificate with less than VerifyCa is pointless /// .ssl_mode(PgSslMode::VerifyCa) /// .ssl_client_cert_from_pem(CERT); /// ``` pub fn ssl_client_cert_from_pem(mut self, cert: impl AsRef<[u8]>) -> Self { - self.ssl_client_cert = Some(CertificateInput::Inline(cert.as_ref().to_vec())); + self.ssl_options_mut().ssl_client_cert = + Some(CertificateInput::Inline(cert.as_ref().to_vec())); self } @@ -297,7 +316,8 @@ impl PgConnectOptions { /// .ssl_client_key("./client.key"); /// ``` pub fn ssl_client_key(mut self, key: impl AsRef) -> Self { - self.ssl_client_key = Some(CertificateInput::File(key.as_ref().to_path_buf())); + self.ssl_options_mut().ssl_client_key = + Some(CertificateInput::File(key.as_ref().to_path_buf())); self } @@ -323,7 +343,8 @@ impl PgConnectOptions { /// .ssl_client_key_from_pem(KEY); /// ``` pub fn ssl_client_key_from_pem(mut self, key: impl AsRef<[u8]>) -> Self { - self.ssl_client_key = Some(CertificateInput::Inline(key.as_ref().to_vec())); + self.ssl_options_mut().ssl_client_key = + Some(CertificateInput::Inline(key.as_ref().to_vec())); self } @@ -339,7 +360,7 @@ impl PgConnectOptions { /// .ssl_root_cert_from_pem(vec![]); /// ``` pub fn ssl_root_cert_from_pem(mut self, pem_certificate: Vec) -> Self { - self.ssl_root_cert = Some(CertificateInput::Inline(pem_certificate)); + self.ssl_options_mut().ssl_root_cert = Some(CertificateInput::Inline(pem_certificate)); self } @@ -550,7 +571,7 @@ impl PgConnectOptions { /// assert!(matches!(options.get_ssl_mode(), PgSslMode::Prefer)); /// ``` pub fn get_ssl_mode(&self) -> PgSslMode { - self.ssl_mode + self.ssl_options.ssl_mode } /// Get the application name. diff --git a/sqlx-postgres/src/options/parse.rs b/sqlx-postgres/src/options/parse.rs index e911305698..df8be6366d 100644 --- a/sqlx-postgres/src/options/parse.rs +++ b/sqlx-postgres/src/options/parse.rs @@ -136,7 +136,7 @@ impl PgConnectOptions { url.set_path(database); } - let ssl_mode = match self.ssl_mode { + let ssl_mode = match self.ssl_options.ssl_mode { PgSslMode::Allow => "allow", PgSslMode::Disable => "disable", PgSslMode::Prefer => "prefer", @@ -146,17 +146,17 @@ impl PgConnectOptions { }; url.query_pairs_mut().append_pair("sslmode", ssl_mode); - if let Some(ssl_root_cert) = &self.ssl_root_cert { + if let Some(ssl_root_cert) = &self.ssl_options.ssl_root_cert { url.query_pairs_mut() .append_pair("sslrootcert", &ssl_root_cert.to_string()); } - if let Some(ssl_client_cert) = &self.ssl_client_cert { + if let Some(ssl_client_cert) = &self.ssl_options.ssl_client_cert { url.query_pairs_mut() .append_pair("sslcert", &ssl_client_cert.to_string()); } - if let Some(ssl_client_key) = &self.ssl_client_key { + if let Some(ssl_client_key) = &self.ssl_options.ssl_client_key { url.query_pairs_mut() .append_pair("sslkey", &ssl_client_key.to_string()); } diff --git a/sqlx-postgres/src/query_result.rs b/sqlx-postgres/src/query_result.rs index 3a243f3ee6..f96f4f3a6a 100644 --- a/sqlx-postgres/src/query_result.rs +++ b/sqlx-postgres/src/query_result.rs @@ -18,13 +18,3 @@ impl Extend for PgQueryResult { } } } - -#[cfg(feature = "any")] -impl From for sqlx_core::any::AnyQueryResult { - fn from(done: PgQueryResult) -> Self { - sqlx_core::any::AnyQueryResult { - rows_affected: done.rows_affected, - last_insert_id: None, - } - } -} diff --git a/sqlx-sqlite/LICENSE-APACHE b/sqlx-sqlite/LICENSE-APACHE deleted file mode 120000 index 965b606f33..0000000000 --- a/sqlx-sqlite/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/sqlx-sqlite/LICENSE-APACHE b/sqlx-sqlite/LICENSE-APACHE new file mode 100644 index 0000000000..e14699fb14 --- /dev/null +++ b/sqlx-sqlite/LICENSE-APACHE @@ -0,0 +1,202 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/sqlx-sqlite/LICENSE-MIT b/sqlx-sqlite/LICENSE-MIT deleted file mode 120000 index 76219eb72e..0000000000 --- a/sqlx-sqlite/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/sqlx-sqlite/LICENSE-MIT b/sqlx-sqlite/LICENSE-MIT new file mode 100644 index 0000000000..6fe828e8ea --- /dev/null +++ b/sqlx-sqlite/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/sqlx-sqlite/src/any.rs b/sqlx-sqlite/src/any.rs index b3a5af5543..8642399bd6 100644 --- a/sqlx-sqlite/src/any.rs +++ b/sqlx-sqlite/src/any.rs @@ -95,7 +95,7 @@ impl AnyConnectionBackend for SqliteConnection { .try_flatten_stream() .map( move |res: sqlx_core::Result>| match res? { - Either::Left(result) => Ok(Either::Left(map_result(result))), + Either::Left(result) => Ok(Either::Left(result.into())), Either::Right(row) => Ok(Either::Right(AnyRow::try_from(&row)?)), }, ), @@ -234,9 +234,16 @@ fn map_arguments(args: AnyArguments) -> SqliteArguments { } } -fn map_result(res: SqliteQueryResult) -> AnyQueryResult { - AnyQueryResult { - rows_affected: res.rows_affected(), - last_insert_id: None, +impl From for AnyQueryResult { + fn from(done: SqliteQueryResult) -> Self { + // logic as per: https://www.sqlite.org/c3ref/last_insert_rowid.html + let last_insert_id = match done.last_insert_rowid() { + 0 => None, + n => Some(n), + }; + AnyQueryResult { + rows_affected: done.rows_affected(), + last_insert_id, + } } } diff --git a/sqlx-sqlite/src/logger.rs b/sqlx-sqlite/src/logger.rs index 1464a730c7..9e1fa087b5 100644 --- a/sqlx-sqlite/src/logger.rs +++ b/sqlx-sqlite/src/logger.rs @@ -224,7 +224,7 @@ impl core::fmt::Display for QueryPlanL let max_branch_id: i64 = [ self.branch_operations.last_index().unwrap_or(0), self.branch_results.last_index().unwrap_or(0), - self.branch_results.last_index().unwrap_or(0), + self.branch_origins.last_index().unwrap_or(0), ] .into_iter() .max() diff --git a/sqlx-sqlite/src/query_result.rs b/sqlx-sqlite/src/query_result.rs index 8c8c27fcf4..088e032db6 100644 --- a/sqlx-sqlite/src/query_result.rs +++ b/sqlx-sqlite/src/query_result.rs @@ -24,17 +24,3 @@ impl Extend for SqliteQueryResult { } } } - -#[cfg(feature = "any")] -impl From for sqlx_core::any::AnyQueryResult { - fn from(done: SqliteQueryResult) -> Self { - let last_insert_id = match done.last_insert_rowid() { - 0 => None, - n => Some(n), - }; - sqlx_core::any::AnyQueryResult { - rows_affected: done.rows_affected(), - last_insert_id, - } - } -} diff --git a/sqlx-sqlite/src/types/chrono.rs b/sqlx-sqlite/src/types/chrono.rs index 8d987538d0..8805f0f56a 100644 --- a/sqlx-sqlite/src/types/chrono.rs +++ b/sqlx-sqlite/src/types/chrono.rs @@ -176,10 +176,16 @@ fn decode_datetime_from_float(value: f64) -> Option> { // We checked above if the value is infinite or NaN which could otherwise cause problems #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] { - let seconds = timestamp.trunc() as i64; - let nanos = (timestamp.fract() * 1E9).abs() as u32; - - Utc.fix().timestamp_opt(seconds, nanos).single() + // Split into whole seconds and a non-negative sub-second remainder. + // `timestamp_opt` always adds `nanos` *forward* in time, so we must round + // `seconds` toward negative infinity (not toward zero) and take the + // fraction relative to that. Using `trunc()`/`fract().abs()` here would + // push pre-epoch timestamps up to ~2 seconds off (and onto the wrong side + // of the epoch), since the fractional part is negative below zero. + let seconds = timestamp.floor(); + let nanos = ((timestamp - seconds) * 1E9) as u32; + + Utc.fix().timestamp_opt(seconds as i64, nanos).single() } } @@ -218,3 +224,57 @@ impl<'r> Decode<'r, Sqlite> for NaiveTime { Err(format!("invalid time: {value}").into()) } } + +#[cfg(test)] +mod tests { + use super::decode_datetime_from_float; + use chrono::{Offset, TimeZone, Utc}; + + // SQLite may store a datetime as a REAL holding a Julian day number, e.g. the + // result of `julianday(...)`. This mirrors that conversion so a test can feed + // `decode_datetime_from_float` the value for a known instant. + fn julian_day(unix_seconds: f64) -> f64 { + 2_440_587.5 + unix_seconds / 86_400.0 + } + + // Assert that the Julian day for `unix_seconds + subsec_nanos` decodes back to + // that same instant. The tolerance only absorbs f64 round-off in the + // round-trip (tens of microseconds), well below the errors under test. + #[track_caller] + fn assert_decodes_near(unix_seconds: i64, subsec_nanos: u32) { + let input = julian_day(unix_seconds as f64 + f64::from(subsec_nanos) / 1e9); + let decoded = decode_datetime_from_float(input).expect("valid Julian day should decode"); + let expected = Utc + .fix() + .timestamp_opt(unix_seconds, subsec_nanos) + .single() + .unwrap(); + + let diff_us = (decoded - expected) + .num_microseconds() + .expect("difference fits in microseconds") + .abs(); + assert!( + diff_us < 1_000, + "decoded {decoded} differs from expected {expected} by {diff_us} us", + ); + } + + // A Julian day landing before the UNIX epoch must keep its sub-second part in + // the correct direction. Before the fix these decoded up to ~2 seconds off, + // and could even cross to the wrong side of the epoch. + #[test] + fn decodes_pre_epoch_float_datetime() { + // 1969-12-31 23:59:59.500 UTC + assert_decodes_near(-1, 500_000_000); + // 1969-12-31 23:59:58.750 UTC + assert_decodes_near(-2, 750_000_000); + } + + // Post-epoch values were already correct; guard against a regression. + #[test] + fn decodes_post_epoch_float_datetime() { + // 1970-01-01 00:00:01.500 UTC + assert_decodes_near(1, 500_000_000); + } +} diff --git a/sqlx-test/LICENSE-APACHE b/sqlx-test/LICENSE-APACHE deleted file mode 120000 index 965b606f33..0000000000 --- a/sqlx-test/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/sqlx-test/LICENSE-APACHE b/sqlx-test/LICENSE-APACHE new file mode 100644 index 0000000000..e14699fb14 --- /dev/null +++ b/sqlx-test/LICENSE-APACHE @@ -0,0 +1,202 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/sqlx-test/LICENSE-MIT b/sqlx-test/LICENSE-MIT deleted file mode 120000 index 76219eb72e..0000000000 --- a/sqlx-test/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/sqlx-test/LICENSE-MIT b/sqlx-test/LICENSE-MIT new file mode 100644 index 0000000000..6fe828e8ea --- /dev/null +++ b/sqlx-test/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (C) SQLx Contributors +Portions of this work Copyright (C) LaunchBadge, LLC + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/macros/mod.rs b/src/macros/mod.rs index 0db6f0c2e7..ef9b6a3a2e 100644 --- a/src/macros/mod.rs +++ b/src/macros/mod.rs @@ -130,18 +130,42 @@ /// * MySQL/SQLite: `?` which matches arguments in order that it appears in the query /// /// ## Nullability: Bind Parameters -/// For a given expected type `T`, both `T` and `Option` are allowed (as well as either -/// behind references). `Option::None` will be bound as `NULL`, so if binding a type behind `Option` -/// be sure your query can support it. +/// **The nullability of bind parameters is _not_ verified at compile time.** Unlike output +/// columns (see the [next section](#nullability-output-columns)), the `query!()` family of +/// macros does not check whether an `Option` bound to a parameter is compatible with the +/// nullability of the target column. This is a fundamental limitation, not an oversight: +/// determining which parameter maps to which column would require the macros to parse and +/// analyze the SQL themselves, which SQLx explicitly does not do +/// (see [the FAQ][faq-parse-sql] for the reasoning). +/// +/// For any bind parameter, both `T` and `Option` are accepted (as well as either behind +/// references). `Option::None` is bound as SQL `NULL`. If the target column has a `NOT NULL` +/// constraint, binding `None` will compile successfully but fail **at runtime** with a +/// database error, for example: /// -/// Note, however, if binding in a `where` clause, that equality comparisons with `NULL` may not -/// work as expected; instead you must use `IS NOT NULL` or `IS NULL` to check if a column is not +/// ```rust,ignore +/// // Schema: `CREATE TABLE foo (data TEXT NOT NULL);` +/// // Compiles fine, fails at runtime: +/// sqlx::query!("INSERT INTO foo (data) VALUES ($1)", None::) +/// .execute(&pool) +/// .await?; +/// ``` +/// +/// If you need this kind of safety, encode the constraint in your Rust types (e.g. use `String` +/// instead of `Option` for the field that feeds the bind parameter) or cover the +/// invariant with an integration test against a real database. +/// +/// ### Bind parameters in `WHERE` clauses +/// If binding in a `WHERE` clause, note that equality comparisons with `NULL` may not work +/// as expected; instead you must use `IS NOT NULL` or `IS NULL` to check if a column is not /// null or is null, respectively. /// /// In Postgres and MySQL you may also use `IS [NOT] DISTINCT FROM` to compare with a possibly /// `NULL` value. In MySQL `IS NOT DISTINCT FROM` can be shortened to `<=>`. /// In SQLite you can use `IS` or `IS NOT`. Note that operator precedence may be different. /// +/// [faq-parse-sql]: https://github.com/transact-rs/sqlx/blob/main/FAQ.md#why-cant-sqlx-just-look-at-my-database-schemamigrations-and-parse-the-sql-itself +/// /// ## Nullability: Output Columns /// In most cases, the database engine can tell us whether or not a column may be `NULL`, and /// the `query!()` macro adjusts the field types of the returned struct accordingly. diff --git a/tests/mysql/any.rs b/tests/mysql/any.rs new file mode 100644 index 0000000000..4f579772ff --- /dev/null +++ b/tests/mysql/any.rs @@ -0,0 +1,26 @@ +use sqlx::Any; +use sqlx_test::new; + +/// ensure Any type with MySQL backing returns last_insert_id properly +/// https://github.com/launchbadge/sqlx/issues/2982 +#[sqlx_macros::test] +async fn any_sets_last_insert_id() -> anyhow::Result<()> { + sqlx::any::install_default_drivers(); + + let mut conn = new::().await?; + // syntax as per: https://dev.mysql.com/doc/refman/9.6/en/example-auto-increment.html + let _ = sqlx::query( + "CREATE TEMPORARY TABLE users (id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, name TEXT NOT NULL)", + ) + .execute(&mut conn) + .await?; + + let result = sqlx::query("INSERT INTO users (name) VALUES (?)") + .bind("Glorbo") + .execute(&mut conn) + .await?; + + assert_eq!(result.last_insert_id(), Some(1)); + + Ok(()) +} diff --git a/tests/mysql/mysql.rs b/tests/mysql/mysql.rs index 5374e651c8..78d3e3a761 100644 --- a/tests/mysql/mysql.rs +++ b/tests/mysql/mysql.rs @@ -727,3 +727,55 @@ async fn any_blob_conversions() -> anyhow::Result<()> { Ok(()) } + +#[sqlx_macros::test] +async fn test_client_found_rows() -> anyhow::Result<()> { + setup_if_needed(); + + let url = url::Url::parse(&env::var("DATABASE_URL")?)?; + + // CLIENT_AFFECTED_ROWS unspecified = true by default. + let mut conn = MySqlConnectOptions::from_url(&url)?.connect().await?; + let mut tx = conn.begin().await?; + + tx.execute(sqlx::query( + "CREATE TEMPORARY TABLE found_rows_testing (id INT PRIMARY KEY, field INT NOT NULL)", + )) + .await?; + + let result = tx.execute(sqlx::query("INSERT INTO found_rows_testing VALUES (0, 10) ON DUPLICATE KEY UPDATE field = VALUES(field)")).await?; + assert_eq!(result.rows_affected(), 1); + + let result = tx.execute(sqlx::query("INSERT INTO found_rows_testing VALUES (0, 10) ON DUPLICATE KEY UPDATE field = VALUES(field)")).await?; + assert_eq!(result.rows_affected(), 1); + + let result = tx.execute(sqlx::query("INSERT INTO found_rows_testing VALUES (0, 20) ON DUPLICATE KEY UPDATE field = VALUES(field)")).await?; + assert_eq!(result.rows_affected(), 2); + + tx.rollback().await?; + + // Explicitly unset CLIENT_AFFECTED_ROWS. + let mut conn = MySqlConnectOptions::from_url(&url)? + .found_rows(false) + .connect() + .await?; + let mut tx = conn.begin().await?; + + tx.execute(sqlx::query( + "CREATE TEMPORARY TABLE found_rows_testing (id INT PRIMARY KEY, field INT NOT NULL)", + )) + .await?; + + let result = tx.execute(sqlx::query("INSERT INTO found_rows_testing VALUES (0, 10) ON DUPLICATE KEY UPDATE field = VALUES(field)")).await?; + assert_eq!(result.rows_affected(), 1); + + let result = tx.execute(sqlx::query("INSERT INTO found_rows_testing VALUES (0, 10) ON DUPLICATE KEY UPDATE field = VALUES(field)")).await?; + assert_eq!(result.rows_affected(), 0); + + let result = tx.execute(sqlx::query("INSERT INTO found_rows_testing VALUES (0, 20) ON DUPLICATE KEY UPDATE field = VALUES(field)")).await?; + assert_eq!(result.rows_affected(), 2); + + tx.rollback().await?; + + Ok(()) +} diff --git a/tests/postgres/any.rs b/tests/postgres/any.rs new file mode 100644 index 0000000000..8e140143ba --- /dev/null +++ b/tests/postgres/any.rs @@ -0,0 +1,27 @@ +use sqlx::Any; +use sqlx_test::new; + +/// ensure Any type with PostgreSQL backing returns last_insert_id properly +/// https://github.com/launchbadge/sqlx/issues/2982 +#[sqlx_macros::test] +async fn any_sets_last_insert_id() -> anyhow::Result<()> { + sqlx::any::install_default_drivers(); + + let mut conn = new::().await?; + // syntax as per: https://www.postgresql.org/docs/current/ddl-identity-columns.html + let _ = sqlx::query( + "CREATE TEMPORARY TABLE users (id INTEGER GENERATED ALWAYS AS IDENTITY, name TEXT NOT NULL)", + ) + .execute(&mut conn) + .await?; + + let result = sqlx::query("INSERT INTO users (name) VALUES ($1)") + .bind("Glorbo") + .execute(&mut conn) + .await?; + + // NOTE: PgQueryResult does not implement an equivalent concept and can only return None + assert_eq!(result.last_insert_id(), None); + + Ok(()) +} diff --git a/tests/postgres/postgres.rs b/tests/postgres/postgres.rs index 126771565a..26f827b837 100644 --- a/tests/postgres/postgres.rs +++ b/tests/postgres/postgres.rs @@ -845,6 +845,32 @@ async fn it_closes_statements_when_not_persistent_issue_3850() -> anyhow::Result Ok(()) } +#[sqlx_macros::test] +async fn it_closes_statements_when_caching_is_disabled_issue_4328() -> anyhow::Result<()> { + sqlx_test::setup_if_needed(); + + let mut options: PgConnectOptions = env::var("DATABASE_URL")?.parse().unwrap(); + + options = options.statement_cache_capacity(0); + + let mut conn = PgConnection::connect_with(&options).await?; + + let _row = sqlx::query("SELECT $1 AS val") + .bind(Oid(1)) + .fetch_one(&mut conn) + .await?; + + let row = sqlx::query("SELECT count(*) AS num_prepared_statements FROM pg_prepared_statements") + .persistent(false) + .fetch_one(&mut conn) + .await?; + + let n: i64 = row.get("num_prepared_statements"); + assert_eq!(0, n, "no prepared statements should be open"); + + Ok(()) +} + #[sqlx_macros::test] async fn it_sets_application_name() -> anyhow::Result<()> { sqlx_test::setup_if_needed(); diff --git a/tests/sqlite/any.rs b/tests/sqlite/any.rs index b71c3ba43d..388fedcbfa 100644 --- a/tests/sqlite/any.rs +++ b/tests/sqlite/any.rs @@ -33,3 +33,27 @@ async fn issue_3179() -> anyhow::Result<()> { Ok(()) } + +/// ensure Any type with SQLite backing returns last_insert_id properly +/// https://github.com/launchbadge/sqlx/issues/2982 +#[sqlx_macros::test] +async fn any_sets_last_insert_id() -> anyhow::Result<()> { + sqlx::any::install_default_drivers(); + + let mut conn = new::().await?; + // syntax as per: https://sqlite.org/autoinc.html + let _ = sqlx::query( + "CREATE TEMPORARY TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)", + ) + .execute(&mut conn) + .await?; + + let result = sqlx::query("INSERT INTO users (name) VALUES (?)") + .bind("Glorbo") + .execute(&mut conn) + .await?; + + assert_eq!(result.last_insert_id(), Some(1)); + + Ok(()) +}