diff --git a/.github/workflows/sqlx.yml b/.github/workflows/sqlx.yml index eb0d26e378..4b080b42d2 100644 --- a/.github/workflows/sqlx.yml +++ b/.github/workflows/sqlx.yml @@ -419,7 +419,7 @@ jobs: runs-on: ubuntu-24.04 strategy: matrix: - mariadb: [ verylatest, 11_4, 10_11, 10_4 ] + mariadb: [ verylatest, 11_8, 11_4, 10_11, 10_6 ] runtime: [ async-std, tokio ] tls: [ native-tls, rustls-aws-lc-rs, rustls-ring, none ] needs: check @@ -446,7 +446,7 @@ jobs: env: DATABASE_URL: mysql://root:password@localhost:3306/sqlx SQLX_OFFLINE_DIR: .sqlx - RUSTFLAGS: --cfg mariadb_${{ matrix.mariadb }} + RUSTFLAGS: --cfg mariadb="${{ matrix.mariadb }}" # Run the `test-attr` test again to cover cleanup. - run: > @@ -457,7 +457,7 @@ jobs: env: DATABASE_URL: mysql://root:password@localhost:3306/sqlx SQLX_OFFLINE_DIR: .sqlx - RUSTFLAGS: --cfg mariadb_${{ matrix.mariadb }} + RUSTFLAGS: --cfg mariadb="${{ matrix.mariadb }}" # Remove test artifacts - run: cargo clean -p sqlx @@ -471,7 +471,7 @@ jobs: env: SQLX_OFFLINE: true SQLX_OFFLINE_DIR: .sqlx - RUSTFLAGS: -D warnings --cfg mariadb_${{ matrix.mariadb }} + RUSTFLAGS: -D warnings --cfg mariadb="${{ matrix.mariadb }}" # Test macros in offline mode (still needs DATABASE_URL to run) - run: > @@ -483,7 +483,7 @@ jobs: DATABASE_URL: mysql://root:password@localhost:3306/sqlx SQLX_OFFLINE: true SQLX_OFFLINE_DIR: .sqlx - RUSTFLAGS: --cfg mariadb_${{ matrix.mariadb }} + RUSTFLAGS: --cfg mariadb="${{ matrix.mariadb }}" # client SSL authentication @@ -500,4 +500,4 @@ jobs: --features any,mysql,macros,migrate,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: DATABASE_URL: mysql://root@localhost:3306/sqlx?sslmode=verify_ca&ssl-ca=.%2Ftests%2Fcerts%2Fca.crt&ssl-key=.%2Ftests%2Fcerts%2Fkeys%2Fclient.key&ssl-cert=.%2Ftests%2Fcerts%2Fclient.crt - RUSTFLAGS: --cfg mariadb_${{ matrix.mariadb }} + RUSTFLAGS: --cfg mariadb="${{ matrix.mariadb }}" diff --git a/Cargo.lock b/Cargo.lock index 321eaadf0c..9ca4a23b34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -772,9 +772,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.26" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -782,9 +782,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.26" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -804,9 +804,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck 0.5.0", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index b72ec9afbd..35d6da36be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -210,6 +210,9 @@ cast_sign_loss = 'deny' # See `clippy.toml` disallowed_methods = 'deny' +[lints.rust] +unexpected_cfgs = { level = 'warn', check-cfg = ['cfg(mariadb, values(any()))'] } + # # Any # diff --git a/sqlx-core/src/raw_sql.rs b/sqlx-core/src/raw_sql.rs index f4104348bc..6ea9cbcd81 100644 --- a/sqlx-core/src/raw_sql.rs +++ b/sqlx-core/src/raw_sql.rs @@ -245,12 +245,12 @@ impl<'q> RawSql<'q> { /// /// Otherwise, you might want to add `LIMIT 1` to your query. #[inline] - pub async fn fetch_optional<'e, E, DB>(self, executor: E) -> crate::Result + pub async fn fetch_optional<'e, E, DB>(self, executor: E) -> crate::Result> where 'q: 'e, DB: Database, E: Executor<'e, Database = DB>, { - executor.fetch_one(self).await + executor.fetch_optional(self).await } } diff --git a/sqlx-mysql/src/collation.rs b/sqlx-mysql/src/collation.rs index 46a3a3f90c..554c7cf21a 100644 --- a/sqlx-mysql/src/collation.rs +++ b/sqlx-mysql/src/collation.rs @@ -1,900 +1,49 @@ -use crate::error::Error; -use std::str::FromStr; - -#[allow(non_camel_case_types)] -#[derive(Copy, Clone)] -pub(crate) enum CharSet { - armscii8, - ascii, - big5, - binary, - cp1250, - cp1251, - cp1256, - cp1257, - cp850, - cp852, - cp866, - cp932, - dec8, - eucjpms, - euckr, - gb18030, - gb2312, - gbk, - geostd8, - greek, - hebrew, - hp8, - keybcs2, - koi8r, - koi8u, - latin1, - latin2, - latin5, - latin7, - macce, - macroman, - sjis, - swe7, - tis620, - ucs2, - ujis, - utf16, - utf16le, - utf32, - utf8, - utf8mb4, -} - -impl CharSet { - pub(crate) fn as_str(&self) -> &'static str { - match self { - CharSet::armscii8 => "armscii8", - CharSet::ascii => "ascii", - CharSet::big5 => "big5", - CharSet::binary => "binary", - CharSet::cp1250 => "cp1250", - CharSet::cp1251 => "cp1251", - CharSet::cp1256 => "cp1256", - CharSet::cp1257 => "cp1257", - CharSet::cp850 => "cp850", - CharSet::cp852 => "cp852", - CharSet::cp866 => "cp866", - CharSet::cp932 => "cp932", - CharSet::dec8 => "dec8", - CharSet::eucjpms => "eucjpms", - CharSet::euckr => "euckr", - CharSet::gb18030 => "gb18030", - CharSet::gb2312 => "gb2312", - CharSet::gbk => "gbk", - CharSet::geostd8 => "geostd8", - CharSet::greek => "greek", - CharSet::hebrew => "hebrew", - CharSet::hp8 => "hp8", - CharSet::keybcs2 => "keybcs2", - CharSet::koi8r => "koi8r", - CharSet::koi8u => "koi8u", - CharSet::latin1 => "latin1", - CharSet::latin2 => "latin2", - CharSet::latin5 => "latin5", - CharSet::latin7 => "latin7", - CharSet::macce => "macce", - CharSet::macroman => "macroman", - CharSet::sjis => "sjis", - CharSet::swe7 => "swe7", - CharSet::tis620 => "tis620", - CharSet::ucs2 => "ucs2", - CharSet::ujis => "ujis", - CharSet::utf16 => "utf16", - CharSet::utf16le => "utf16le", - CharSet::utf32 => "utf32", - CharSet::utf8 => "utf8", - CharSet::utf8mb4 => "utf8mb4", - } - } - - pub(crate) fn default_collation(&self) -> Collation { - match self { - CharSet::armscii8 => Collation::armscii8_general_ci, - CharSet::ascii => Collation::ascii_general_ci, - CharSet::big5 => Collation::big5_chinese_ci, - CharSet::binary => Collation::binary, - CharSet::cp1250 => Collation::cp1250_general_ci, - CharSet::cp1251 => Collation::cp1251_general_ci, - CharSet::cp1256 => Collation::cp1256_general_ci, - CharSet::cp1257 => Collation::cp1257_general_ci, - CharSet::cp850 => Collation::cp850_general_ci, - CharSet::cp852 => Collation::cp852_general_ci, - CharSet::cp866 => Collation::cp866_general_ci, - CharSet::cp932 => Collation::cp932_japanese_ci, - CharSet::dec8 => Collation::dec8_swedish_ci, - CharSet::eucjpms => Collation::eucjpms_japanese_ci, - CharSet::euckr => Collation::euckr_korean_ci, - CharSet::gb18030 => Collation::gb18030_chinese_ci, - CharSet::gb2312 => Collation::gb2312_chinese_ci, - CharSet::gbk => Collation::gbk_chinese_ci, - CharSet::geostd8 => Collation::geostd8_general_ci, - CharSet::greek => Collation::greek_general_ci, - CharSet::hebrew => Collation::hebrew_general_ci, - CharSet::hp8 => Collation::hp8_english_ci, - CharSet::keybcs2 => Collation::keybcs2_general_ci, - CharSet::koi8r => Collation::koi8r_general_ci, - CharSet::koi8u => Collation::koi8u_general_ci, - CharSet::latin1 => Collation::latin1_swedish_ci, - CharSet::latin2 => Collation::latin2_general_ci, - CharSet::latin5 => Collation::latin5_turkish_ci, - CharSet::latin7 => Collation::latin7_general_ci, - CharSet::macce => Collation::macce_general_ci, - CharSet::macroman => Collation::macroman_general_ci, - CharSet::sjis => Collation::sjis_japanese_ci, - CharSet::swe7 => Collation::swe7_swedish_ci, - CharSet::tis620 => Collation::tis620_thai_ci, - CharSet::ucs2 => Collation::ucs2_general_ci, - CharSet::ujis => Collation::ujis_japanese_ci, - CharSet::utf16 => Collation::utf16_general_ci, - CharSet::utf16le => Collation::utf16le_general_ci, - CharSet::utf32 => Collation::utf32_general_ci, - CharSet::utf8 => Collation::utf8_unicode_ci, - CharSet::utf8mb4 => Collation::utf8mb4_unicode_ci, - } - } -} - -impl FromStr for CharSet { - type Err = Error; - - fn from_str(char_set: &str) -> Result { - Ok(match char_set { - "armscii8" => CharSet::armscii8, - "ascii" => CharSet::ascii, - "big5" => CharSet::big5, - "binary" => CharSet::binary, - "cp1250" => CharSet::cp1250, - "cp1251" => CharSet::cp1251, - "cp1256" => CharSet::cp1256, - "cp1257" => CharSet::cp1257, - "cp850" => CharSet::cp850, - "cp852" => CharSet::cp852, - "cp866" => CharSet::cp866, - "cp932" => CharSet::cp932, - "dec8" => CharSet::dec8, - "eucjpms" => CharSet::eucjpms, - "euckr" => CharSet::euckr, - "gb18030" => CharSet::gb18030, - "gb2312" => CharSet::gb2312, - "gbk" => CharSet::gbk, - "geostd8" => CharSet::geostd8, - "greek" => CharSet::greek, - "hebrew" => CharSet::hebrew, - "hp8" => CharSet::hp8, - "keybcs2" => CharSet::keybcs2, - "koi8r" => CharSet::koi8r, - "koi8u" => CharSet::koi8u, - "latin1" => CharSet::latin1, - "latin2" => CharSet::latin2, - "latin5" => CharSet::latin5, - "latin7" => CharSet::latin7, - "macce" => CharSet::macce, - "macroman" => CharSet::macroman, - "sjis" => CharSet::sjis, - "swe7" => CharSet::swe7, - "tis620" => CharSet::tis620, - "ucs2" => CharSet::ucs2, - "ujis" => CharSet::ujis, - "utf16" => CharSet::utf16, - "utf16le" => CharSet::utf16le, - "utf32" => CharSet::utf32, - "utf8" => CharSet::utf8, - "utf8mb4" => CharSet::utf8mb4, - - _ => { - return Err(Error::Configuration( - format!("unsupported MySQL charset: {char_set}").into(), - )); - } - }) - } -} - -#[derive(Copy, Clone)] -#[allow(non_camel_case_types)] -#[repr(u8)] -pub(crate) enum Collation { - armscii8_bin = 64, - armscii8_general_ci = 32, - ascii_bin = 65, - ascii_general_ci = 11, - big5_bin = 84, - big5_chinese_ci = 1, - binary = 63, - cp1250_bin = 66, - cp1250_croatian_ci = 44, - cp1250_czech_cs = 34, - cp1250_general_ci = 26, - cp1250_polish_ci = 99, - cp1251_bin = 50, - cp1251_bulgarian_ci = 14, - cp1251_general_ci = 51, - cp1251_general_cs = 52, - cp1251_ukrainian_ci = 23, - cp1256_bin = 67, - cp1256_general_ci = 57, - cp1257_bin = 58, - cp1257_general_ci = 59, - cp1257_lithuanian_ci = 29, - cp850_bin = 80, - cp850_general_ci = 4, - cp852_bin = 81, - cp852_general_ci = 40, - cp866_bin = 68, - cp866_general_ci = 36, - cp932_bin = 96, - cp932_japanese_ci = 95, - dec8_bin = 69, - dec8_swedish_ci = 3, - eucjpms_bin = 98, - eucjpms_japanese_ci = 97, - euckr_bin = 85, - euckr_korean_ci = 19, - gb18030_bin = 249, - gb18030_chinese_ci = 248, - gb18030_unicode_520_ci = 250, - gb2312_bin = 86, - gb2312_chinese_ci = 24, - gbk_bin = 87, - gbk_chinese_ci = 28, - geostd8_bin = 93, - geostd8_general_ci = 92, - greek_bin = 70, - greek_general_ci = 25, - hebrew_bin = 71, - hebrew_general_ci = 16, - hp8_bin = 72, - hp8_english_ci = 6, - keybcs2_bin = 73, - keybcs2_general_ci = 37, - koi8r_bin = 74, - koi8r_general_ci = 7, - koi8u_bin = 75, - koi8u_general_ci = 22, - latin1_bin = 47, - latin1_danish_ci = 15, - latin1_general_ci = 48, - latin1_general_cs = 49, - latin1_german1_ci = 5, - latin1_german2_ci = 31, - latin1_spanish_ci = 94, - latin1_swedish_ci = 8, - latin2_bin = 77, - latin2_croatian_ci = 27, - latin2_czech_cs = 2, - latin2_general_ci = 9, - latin2_hungarian_ci = 21, - latin5_bin = 78, - latin5_turkish_ci = 30, - latin7_bin = 79, - latin7_estonian_cs = 20, - latin7_general_ci = 41, - latin7_general_cs = 42, - macce_bin = 43, - macce_general_ci = 38, - macroman_bin = 53, - macroman_general_ci = 39, - sjis_bin = 88, - sjis_japanese_ci = 13, - swe7_bin = 82, - swe7_swedish_ci = 10, - tis620_bin = 89, - tis620_thai_ci = 18, - ucs2_bin = 90, - ucs2_croatian_ci = 149, - ucs2_czech_ci = 138, - ucs2_danish_ci = 139, - ucs2_esperanto_ci = 145, - ucs2_estonian_ci = 134, - ucs2_general_ci = 35, - ucs2_general_mysql500_ci = 159, - ucs2_german2_ci = 148, - ucs2_hungarian_ci = 146, - ucs2_icelandic_ci = 129, - ucs2_latvian_ci = 130, - ucs2_lithuanian_ci = 140, - ucs2_persian_ci = 144, - ucs2_polish_ci = 133, - ucs2_roman_ci = 143, - ucs2_romanian_ci = 131, - ucs2_sinhala_ci = 147, - ucs2_slovak_ci = 141, - ucs2_slovenian_ci = 132, - ucs2_spanish_ci = 135, - ucs2_spanish2_ci = 142, - ucs2_swedish_ci = 136, - ucs2_turkish_ci = 137, - ucs2_unicode_520_ci = 150, - ucs2_unicode_ci = 128, - ucs2_vietnamese_ci = 151, - ujis_bin = 91, - ujis_japanese_ci = 12, - utf16_bin = 55, - utf16_croatian_ci = 122, - utf16_czech_ci = 111, - utf16_danish_ci = 112, - utf16_esperanto_ci = 118, - utf16_estonian_ci = 107, - utf16_general_ci = 54, - utf16_german2_ci = 121, - utf16_hungarian_ci = 119, - utf16_icelandic_ci = 102, - utf16_latvian_ci = 103, - utf16_lithuanian_ci = 113, - utf16_persian_ci = 117, - utf16_polish_ci = 106, - utf16_roman_ci = 116, - utf16_romanian_ci = 104, - utf16_sinhala_ci = 120, - utf16_slovak_ci = 114, - utf16_slovenian_ci = 105, - utf16_spanish_ci = 108, - utf16_spanish2_ci = 115, - utf16_swedish_ci = 109, - utf16_turkish_ci = 110, - utf16_unicode_520_ci = 123, - utf16_unicode_ci = 101, - utf16_vietnamese_ci = 124, - utf16le_bin = 62, - utf16le_general_ci = 56, - utf32_bin = 61, - utf32_croatian_ci = 181, - utf32_czech_ci = 170, - utf32_danish_ci = 171, - utf32_esperanto_ci = 177, - utf32_estonian_ci = 166, - utf32_general_ci = 60, - utf32_german2_ci = 180, - utf32_hungarian_ci = 178, - utf32_icelandic_ci = 161, - utf32_latvian_ci = 162, - utf32_lithuanian_ci = 172, - utf32_persian_ci = 176, - utf32_polish_ci = 165, - utf32_roman_ci = 175, - utf32_romanian_ci = 163, - utf32_sinhala_ci = 179, - utf32_slovak_ci = 173, - utf32_slovenian_ci = 164, - utf32_spanish_ci = 167, - utf32_spanish2_ci = 174, - utf32_swedish_ci = 168, - utf32_turkish_ci = 169, - utf32_unicode_520_ci = 182, - utf32_unicode_ci = 160, - utf32_vietnamese_ci = 183, - utf8_bin = 83, - utf8_croatian_ci = 213, - utf8_czech_ci = 202, - utf8_danish_ci = 203, - utf8_esperanto_ci = 209, - utf8_estonian_ci = 198, - utf8_general_ci = 33, - utf8_general_mysql500_ci = 223, - utf8_german2_ci = 212, - utf8_hungarian_ci = 210, - utf8_icelandic_ci = 193, - utf8_latvian_ci = 194, - utf8_lithuanian_ci = 204, - utf8_persian_ci = 208, - utf8_polish_ci = 197, - utf8_roman_ci = 207, - utf8_romanian_ci = 195, - utf8_sinhala_ci = 211, - utf8_slovak_ci = 205, - utf8_slovenian_ci = 196, - utf8_spanish_ci = 199, - utf8_spanish2_ci = 206, - utf8_swedish_ci = 200, - utf8_tolower_ci = 76, - utf8_turkish_ci = 201, - utf8_unicode_520_ci = 214, - utf8_unicode_ci = 192, - utf8_vietnamese_ci = 215, - utf8mb4_0900_ai_ci = 255, - utf8mb4_bin = 46, - utf8mb4_croatian_ci = 245, - utf8mb4_czech_ci = 234, - utf8mb4_danish_ci = 235, - utf8mb4_esperanto_ci = 241, - utf8mb4_estonian_ci = 230, - utf8mb4_general_ci = 45, - utf8mb4_german2_ci = 244, - utf8mb4_hungarian_ci = 242, - utf8mb4_icelandic_ci = 225, - utf8mb4_latvian_ci = 226, - utf8mb4_lithuanian_ci = 236, - utf8mb4_persian_ci = 240, - utf8mb4_polish_ci = 229, - utf8mb4_roman_ci = 239, - utf8mb4_romanian_ci = 227, - utf8mb4_sinhala_ci = 243, - utf8mb4_slovak_ci = 237, - utf8mb4_slovenian_ci = 228, - utf8mb4_spanish_ci = 231, - utf8mb4_spanish2_ci = 238, - utf8mb4_swedish_ci = 232, - utf8mb4_turkish_ci = 233, - utf8mb4_unicode_520_ci = 246, - utf8mb4_unicode_ci = 224, - utf8mb4_vietnamese_ci = 247, -} +// One of several questionable design decisions in MySQL is the choice to conflate +// *how stored data is sorted* with the character encoding used over the wire. +// +// The documentation for `Protocol::HandshakeResponse41` implies that +// the lower 8 bits of the collation ID may be used to uniquely identify the character set: +// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_handshake_response.html +// +// However, this isn't at _all_ true in practice. Collation IDs are assigned without any apparent +// rhyme or reason, mostly just sequential with unexplained gaps. Masking the collation ID with 0xFF +// doesn't actually tell you anything meaningful, except obviously for collation IDs under 256 +// which just gives you the same collation ID again. +// +// Hanlon's razor would suggest they just forgot that they told clients they could do this. +// Occam's razor suggests no one ever bothers to set the connection charset/collation this way, +// and they all just default to `latin1_swedish_ci` (8), `utf8mb4_general_ci` (45), +// or `utf8mb4_0900_ai_ci` (255). +// +// This would seem to mean that if we want to be *sure* of the character encoding of a given column, +// we have to reference the _full_ catalog of collations. Because new ones are added occasionally, +// we can't just assume a collation we don't recognize is UTF-8 as that's not always the case. +// +// This is especially true when we include MariaDB because they've started creating +// their *own* collations, and even character sets, separately from MySQL. +// +// Awesome, right? +// +// However, as long as `character_set_client` and `character_set_results` are set correctly, +// we can assume that any non-binary collation is a valid string, because the server will transcode. +// As it turns out, the collation specified in the `Protocol::ColumnDefinition` +// is *purely* informational. It has no bearing on what's sent over the wire except for `binary` (63), +// which is never transcoded. +// +// So at the end of the day, none of this matters anyway! To know if a column is a string or not, +// we merely need to check if it's not `binary` (63). If the protocol was just a *bit* +// better documented, it would have saved me literally six hours spent figuring this out. +// +// Thanks, MySQL. + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "offline", derive(serde::Deserialize, serde::Serialize))] +pub struct Collation(pub u16); impl Collation { - pub(crate) fn as_str(&self) -> &'static str { - match self { - Collation::armscii8_bin => "armscii8_bin", - Collation::armscii8_general_ci => "armscii8_general_ci", - Collation::ascii_bin => "ascii_bin", - Collation::ascii_general_ci => "ascii_general_ci", - Collation::big5_bin => "big5_bin", - Collation::big5_chinese_ci => "big5_chinese_ci", - Collation::binary => "binary", - Collation::cp1250_bin => "cp1250_bin", - Collation::cp1250_croatian_ci => "cp1250_croatian_ci", - Collation::cp1250_czech_cs => "cp1250_czech_cs", - Collation::cp1250_general_ci => "cp1250_general_ci", - Collation::cp1250_polish_ci => "cp1250_polish_ci", - Collation::cp1251_bin => "cp1251_bin", - Collation::cp1251_bulgarian_ci => "cp1251_bulgarian_ci", - Collation::cp1251_general_ci => "cp1251_general_ci", - Collation::cp1251_general_cs => "cp1251_general_cs", - Collation::cp1251_ukrainian_ci => "cp1251_ukrainian_ci", - Collation::cp1256_bin => "cp1256_bin", - Collation::cp1256_general_ci => "cp1256_general_ci", - Collation::cp1257_bin => "cp1257_bin", - Collation::cp1257_general_ci => "cp1257_general_ci", - Collation::cp1257_lithuanian_ci => "cp1257_lithuanian_ci", - Collation::cp850_bin => "cp850_bin", - Collation::cp850_general_ci => "cp850_general_ci", - Collation::cp852_bin => "cp852_bin", - Collation::cp852_general_ci => "cp852_general_ci", - Collation::cp866_bin => "cp866_bin", - Collation::cp866_general_ci => "cp866_general_ci", - Collation::cp932_bin => "cp932_bin", - Collation::cp932_japanese_ci => "cp932_japanese_ci", - Collation::dec8_bin => "dec8_bin", - Collation::dec8_swedish_ci => "dec8_swedish_ci", - Collation::eucjpms_bin => "eucjpms_bin", - Collation::eucjpms_japanese_ci => "eucjpms_japanese_ci", - Collation::euckr_bin => "euckr_bin", - Collation::euckr_korean_ci => "euckr_korean_ci", - Collation::gb18030_bin => "gb18030_bin", - Collation::gb18030_chinese_ci => "gb18030_chinese_ci", - Collation::gb18030_unicode_520_ci => "gb18030_unicode_520_ci", - Collation::gb2312_bin => "gb2312_bin", - Collation::gb2312_chinese_ci => "gb2312_chinese_ci", - Collation::gbk_bin => "gbk_bin", - Collation::gbk_chinese_ci => "gbk_chinese_ci", - Collation::geostd8_bin => "geostd8_bin", - Collation::geostd8_general_ci => "geostd8_general_ci", - Collation::greek_bin => "greek_bin", - Collation::greek_general_ci => "greek_general_ci", - Collation::hebrew_bin => "hebrew_bin", - Collation::hebrew_general_ci => "hebrew_general_ci", - Collation::hp8_bin => "hp8_bin", - Collation::hp8_english_ci => "hp8_english_ci", - Collation::keybcs2_bin => "keybcs2_bin", - Collation::keybcs2_general_ci => "keybcs2_general_ci", - Collation::koi8r_bin => "koi8r_bin", - Collation::koi8r_general_ci => "koi8r_general_ci", - Collation::koi8u_bin => "koi8u_bin", - Collation::koi8u_general_ci => "koi8u_general_ci", - Collation::latin1_bin => "latin1_bin", - Collation::latin1_danish_ci => "latin1_danish_ci", - Collation::latin1_general_ci => "latin1_general_ci", - Collation::latin1_general_cs => "latin1_general_cs", - Collation::latin1_german1_ci => "latin1_german1_ci", - Collation::latin1_german2_ci => "latin1_german2_ci", - Collation::latin1_spanish_ci => "latin1_spanish_ci", - Collation::latin1_swedish_ci => "latin1_swedish_ci", - Collation::latin2_bin => "latin2_bin", - Collation::latin2_croatian_ci => "latin2_croatian_ci", - Collation::latin2_czech_cs => "latin2_czech_cs", - Collation::latin2_general_ci => "latin2_general_ci", - Collation::latin2_hungarian_ci => "latin2_hungarian_ci", - Collation::latin5_bin => "latin5_bin", - Collation::latin5_turkish_ci => "latin5_turkish_ci", - Collation::latin7_bin => "latin7_bin", - Collation::latin7_estonian_cs => "latin7_estonian_cs", - Collation::latin7_general_ci => "latin7_general_ci", - Collation::latin7_general_cs => "latin7_general_cs", - Collation::macce_bin => "macce_bin", - Collation::macce_general_ci => "macce_general_ci", - Collation::macroman_bin => "macroman_bin", - Collation::macroman_general_ci => "macroman_general_ci", - Collation::sjis_bin => "sjis_bin", - Collation::sjis_japanese_ci => "sjis_japanese_ci", - Collation::swe7_bin => "swe7_bin", - Collation::swe7_swedish_ci => "swe7_swedish_ci", - Collation::tis620_bin => "tis620_bin", - Collation::tis620_thai_ci => "tis620_thai_ci", - Collation::ucs2_bin => "ucs2_bin", - Collation::ucs2_croatian_ci => "ucs2_croatian_ci", - Collation::ucs2_czech_ci => "ucs2_czech_ci", - Collation::ucs2_danish_ci => "ucs2_danish_ci", - Collation::ucs2_esperanto_ci => "ucs2_esperanto_ci", - Collation::ucs2_estonian_ci => "ucs2_estonian_ci", - Collation::ucs2_general_ci => "ucs2_general_ci", - Collation::ucs2_general_mysql500_ci => "ucs2_general_mysql500_ci", - Collation::ucs2_german2_ci => "ucs2_german2_ci", - Collation::ucs2_hungarian_ci => "ucs2_hungarian_ci", - Collation::ucs2_icelandic_ci => "ucs2_icelandic_ci", - Collation::ucs2_latvian_ci => "ucs2_latvian_ci", - Collation::ucs2_lithuanian_ci => "ucs2_lithuanian_ci", - Collation::ucs2_persian_ci => "ucs2_persian_ci", - Collation::ucs2_polish_ci => "ucs2_polish_ci", - Collation::ucs2_roman_ci => "ucs2_roman_ci", - Collation::ucs2_romanian_ci => "ucs2_romanian_ci", - Collation::ucs2_sinhala_ci => "ucs2_sinhala_ci", - Collation::ucs2_slovak_ci => "ucs2_slovak_ci", - Collation::ucs2_slovenian_ci => "ucs2_slovenian_ci", - Collation::ucs2_spanish_ci => "ucs2_spanish_ci", - Collation::ucs2_spanish2_ci => "ucs2_spanish2_ci", - Collation::ucs2_swedish_ci => "ucs2_swedish_ci", - Collation::ucs2_turkish_ci => "ucs2_turkish_ci", - Collation::ucs2_unicode_520_ci => "ucs2_unicode_520_ci", - Collation::ucs2_unicode_ci => "ucs2_unicode_ci", - Collation::ucs2_vietnamese_ci => "ucs2_vietnamese_ci", - Collation::ujis_bin => "ujis_bin", - Collation::ujis_japanese_ci => "ujis_japanese_ci", - Collation::utf16_bin => "utf16_bin", - Collation::utf16_croatian_ci => "utf16_croatian_ci", - Collation::utf16_czech_ci => "utf16_czech_ci", - Collation::utf16_danish_ci => "utf16_danish_ci", - Collation::utf16_esperanto_ci => "utf16_esperanto_ci", - Collation::utf16_estonian_ci => "utf16_estonian_ci", - Collation::utf16_general_ci => "utf16_general_ci", - Collation::utf16_german2_ci => "utf16_german2_ci", - Collation::utf16_hungarian_ci => "utf16_hungarian_ci", - Collation::utf16_icelandic_ci => "utf16_icelandic_ci", - Collation::utf16_latvian_ci => "utf16_latvian_ci", - Collation::utf16_lithuanian_ci => "utf16_lithuanian_ci", - Collation::utf16_persian_ci => "utf16_persian_ci", - Collation::utf16_polish_ci => "utf16_polish_ci", - Collation::utf16_roman_ci => "utf16_roman_ci", - Collation::utf16_romanian_ci => "utf16_romanian_ci", - Collation::utf16_sinhala_ci => "utf16_sinhala_ci", - Collation::utf16_slovak_ci => "utf16_slovak_ci", - Collation::utf16_slovenian_ci => "utf16_slovenian_ci", - Collation::utf16_spanish_ci => "utf16_spanish_ci", - Collation::utf16_spanish2_ci => "utf16_spanish2_ci", - Collation::utf16_swedish_ci => "utf16_swedish_ci", - Collation::utf16_turkish_ci => "utf16_turkish_ci", - Collation::utf16_unicode_520_ci => "utf16_unicode_520_ci", - Collation::utf16_unicode_ci => "utf16_unicode_ci", - Collation::utf16_vietnamese_ci => "utf16_vietnamese_ci", - Collation::utf16le_bin => "utf16le_bin", - Collation::utf16le_general_ci => "utf16le_general_ci", - Collation::utf32_bin => "utf32_bin", - Collation::utf32_croatian_ci => "utf32_croatian_ci", - Collation::utf32_czech_ci => "utf32_czech_ci", - Collation::utf32_danish_ci => "utf32_danish_ci", - Collation::utf32_esperanto_ci => "utf32_esperanto_ci", - Collation::utf32_estonian_ci => "utf32_estonian_ci", - Collation::utf32_general_ci => "utf32_general_ci", - Collation::utf32_german2_ci => "utf32_german2_ci", - Collation::utf32_hungarian_ci => "utf32_hungarian_ci", - Collation::utf32_icelandic_ci => "utf32_icelandic_ci", - Collation::utf32_latvian_ci => "utf32_latvian_ci", - Collation::utf32_lithuanian_ci => "utf32_lithuanian_ci", - Collation::utf32_persian_ci => "utf32_persian_ci", - Collation::utf32_polish_ci => "utf32_polish_ci", - Collation::utf32_roman_ci => "utf32_roman_ci", - Collation::utf32_romanian_ci => "utf32_romanian_ci", - Collation::utf32_sinhala_ci => "utf32_sinhala_ci", - Collation::utf32_slovak_ci => "utf32_slovak_ci", - Collation::utf32_slovenian_ci => "utf32_slovenian_ci", - Collation::utf32_spanish_ci => "utf32_spanish_ci", - Collation::utf32_spanish2_ci => "utf32_spanish2_ci", - Collation::utf32_swedish_ci => "utf32_swedish_ci", - Collation::utf32_turkish_ci => "utf32_turkish_ci", - Collation::utf32_unicode_520_ci => "utf32_unicode_520_ci", - Collation::utf32_unicode_ci => "utf32_unicode_ci", - Collation::utf32_vietnamese_ci => "utf32_vietnamese_ci", - Collation::utf8_bin => "utf8_bin", - Collation::utf8_croatian_ci => "utf8_croatian_ci", - Collation::utf8_czech_ci => "utf8_czech_ci", - Collation::utf8_danish_ci => "utf8_danish_ci", - Collation::utf8_esperanto_ci => "utf8_esperanto_ci", - Collation::utf8_estonian_ci => "utf8_estonian_ci", - Collation::utf8_general_ci => "utf8_general_ci", - Collation::utf8_general_mysql500_ci => "utf8_general_mysql500_ci", - Collation::utf8_german2_ci => "utf8_german2_ci", - Collation::utf8_hungarian_ci => "utf8_hungarian_ci", - Collation::utf8_icelandic_ci => "utf8_icelandic_ci", - Collation::utf8_latvian_ci => "utf8_latvian_ci", - Collation::utf8_lithuanian_ci => "utf8_lithuanian_ci", - Collation::utf8_persian_ci => "utf8_persian_ci", - Collation::utf8_polish_ci => "utf8_polish_ci", - Collation::utf8_roman_ci => "utf8_roman_ci", - Collation::utf8_romanian_ci => "utf8_romanian_ci", - Collation::utf8_sinhala_ci => "utf8_sinhala_ci", - Collation::utf8_slovak_ci => "utf8_slovak_ci", - Collation::utf8_slovenian_ci => "utf8_slovenian_ci", - Collation::utf8_spanish_ci => "utf8_spanish_ci", - Collation::utf8_spanish2_ci => "utf8_spanish2_ci", - Collation::utf8_swedish_ci => "utf8_swedish_ci", - Collation::utf8_tolower_ci => "utf8_tolower_ci", - Collation::utf8_turkish_ci => "utf8_turkish_ci", - Collation::utf8_unicode_520_ci => "utf8_unicode_520_ci", - Collation::utf8_unicode_ci => "utf8_unicode_ci", - Collation::utf8_vietnamese_ci => "utf8_vietnamese_ci", - Collation::utf8mb4_0900_ai_ci => "utf8mb4_0900_ai_ci", - Collation::utf8mb4_bin => "utf8mb4_bin", - Collation::utf8mb4_croatian_ci => "utf8mb4_croatian_ci", - Collation::utf8mb4_czech_ci => "utf8mb4_czech_ci", - Collation::utf8mb4_danish_ci => "utf8mb4_danish_ci", - Collation::utf8mb4_esperanto_ci => "utf8mb4_esperanto_ci", - Collation::utf8mb4_estonian_ci => "utf8mb4_estonian_ci", - Collation::utf8mb4_general_ci => "utf8mb4_general_ci", - Collation::utf8mb4_german2_ci => "utf8mb4_german2_ci", - Collation::utf8mb4_hungarian_ci => "utf8mb4_hungarian_ci", - Collation::utf8mb4_icelandic_ci => "utf8mb4_icelandic_ci", - Collation::utf8mb4_latvian_ci => "utf8mb4_latvian_ci", - Collation::utf8mb4_lithuanian_ci => "utf8mb4_lithuanian_ci", - Collation::utf8mb4_persian_ci => "utf8mb4_persian_ci", - Collation::utf8mb4_polish_ci => "utf8mb4_polish_ci", - Collation::utf8mb4_roman_ci => "utf8mb4_roman_ci", - Collation::utf8mb4_romanian_ci => "utf8mb4_romanian_ci", - Collation::utf8mb4_sinhala_ci => "utf8mb4_sinhala_ci", - Collation::utf8mb4_slovak_ci => "utf8mb4_slovak_ci", - Collation::utf8mb4_slovenian_ci => "utf8mb4_slovenian_ci", - Collation::utf8mb4_spanish_ci => "utf8mb4_spanish_ci", - Collation::utf8mb4_spanish2_ci => "utf8mb4_spanish2_ci", - Collation::utf8mb4_swedish_ci => "utf8mb4_swedish_ci", - Collation::utf8mb4_turkish_ci => "utf8mb4_turkish_ci", - Collation::utf8mb4_unicode_520_ci => "utf8mb4_unicode_520_ci", - Collation::utf8mb4_unicode_ci => "utf8mb4_unicode_ci", - Collation::utf8mb4_vietnamese_ci => "utf8mb4_vietnamese_ci", - } - } -} - -// Handshake packet have only 1 byte for collation_id. -// So we can't use collations with ID > 255. -impl FromStr for Collation { - type Err = Error; - - fn from_str(collation: &str) -> Result { - Ok(match collation { - "big5_chinese_ci" => Collation::big5_chinese_ci, - "swe7_swedish_ci" => Collation::swe7_swedish_ci, - "utf16_unicode_ci" => Collation::utf16_unicode_ci, - "utf16_icelandic_ci" => Collation::utf16_icelandic_ci, - "utf16_latvian_ci" => Collation::utf16_latvian_ci, - "utf16_romanian_ci" => Collation::utf16_romanian_ci, - "utf16_slovenian_ci" => Collation::utf16_slovenian_ci, - "utf16_polish_ci" => Collation::utf16_polish_ci, - "utf16_estonian_ci" => Collation::utf16_estonian_ci, - "utf16_spanish_ci" => Collation::utf16_spanish_ci, - "utf16_swedish_ci" => Collation::utf16_swedish_ci, - "ascii_general_ci" => Collation::ascii_general_ci, - "utf16_turkish_ci" => Collation::utf16_turkish_ci, - "utf16_czech_ci" => Collation::utf16_czech_ci, - "utf16_danish_ci" => Collation::utf16_danish_ci, - "utf16_lithuanian_ci" => Collation::utf16_lithuanian_ci, - "utf16_slovak_ci" => Collation::utf16_slovak_ci, - "utf16_spanish2_ci" => Collation::utf16_spanish2_ci, - "utf16_roman_ci" => Collation::utf16_roman_ci, - "utf16_persian_ci" => Collation::utf16_persian_ci, - "utf16_esperanto_ci" => Collation::utf16_esperanto_ci, - "utf16_hungarian_ci" => Collation::utf16_hungarian_ci, - "ujis_japanese_ci" => Collation::ujis_japanese_ci, - "utf16_sinhala_ci" => Collation::utf16_sinhala_ci, - "utf16_german2_ci" => Collation::utf16_german2_ci, - "utf16_croatian_ci" => Collation::utf16_croatian_ci, - "utf16_unicode_520_ci" => Collation::utf16_unicode_520_ci, - "utf16_vietnamese_ci" => Collation::utf16_vietnamese_ci, - "ucs2_unicode_ci" => Collation::ucs2_unicode_ci, - "ucs2_icelandic_ci" => Collation::ucs2_icelandic_ci, - "sjis_japanese_ci" => Collation::sjis_japanese_ci, - "ucs2_latvian_ci" => Collation::ucs2_latvian_ci, - "ucs2_romanian_ci" => Collation::ucs2_romanian_ci, - "ucs2_slovenian_ci" => Collation::ucs2_slovenian_ci, - "ucs2_polish_ci" => Collation::ucs2_polish_ci, - "ucs2_estonian_ci" => Collation::ucs2_estonian_ci, - "ucs2_spanish_ci" => Collation::ucs2_spanish_ci, - "ucs2_swedish_ci" => Collation::ucs2_swedish_ci, - "ucs2_turkish_ci" => Collation::ucs2_turkish_ci, - "ucs2_czech_ci" => Collation::ucs2_czech_ci, - "ucs2_danish_ci" => Collation::ucs2_danish_ci, - "cp1251_bulgarian_ci" => Collation::cp1251_bulgarian_ci, - "ucs2_lithuanian_ci" => Collation::ucs2_lithuanian_ci, - "ucs2_slovak_ci" => Collation::ucs2_slovak_ci, - "ucs2_spanish2_ci" => Collation::ucs2_spanish2_ci, - "ucs2_roman_ci" => Collation::ucs2_roman_ci, - "ucs2_persian_ci" => Collation::ucs2_persian_ci, - "ucs2_esperanto_ci" => Collation::ucs2_esperanto_ci, - "ucs2_hungarian_ci" => Collation::ucs2_hungarian_ci, - "ucs2_sinhala_ci" => Collation::ucs2_sinhala_ci, - "ucs2_german2_ci" => Collation::ucs2_german2_ci, - "ucs2_croatian_ci" => Collation::ucs2_croatian_ci, - "latin1_danish_ci" => Collation::latin1_danish_ci, - "ucs2_unicode_520_ci" => Collation::ucs2_unicode_520_ci, - "ucs2_vietnamese_ci" => Collation::ucs2_vietnamese_ci, - "ucs2_general_mysql500_ci" => Collation::ucs2_general_mysql500_ci, - "hebrew_general_ci" => Collation::hebrew_general_ci, - "utf32_unicode_ci" => Collation::utf32_unicode_ci, - "utf32_icelandic_ci" => Collation::utf32_icelandic_ci, - "utf32_latvian_ci" => Collation::utf32_latvian_ci, - "utf32_romanian_ci" => Collation::utf32_romanian_ci, - "utf32_slovenian_ci" => Collation::utf32_slovenian_ci, - "utf32_polish_ci" => Collation::utf32_polish_ci, - "utf32_estonian_ci" => Collation::utf32_estonian_ci, - "utf32_spanish_ci" => Collation::utf32_spanish_ci, - "utf32_swedish_ci" => Collation::utf32_swedish_ci, - "utf32_turkish_ci" => Collation::utf32_turkish_ci, - "utf32_czech_ci" => Collation::utf32_czech_ci, - "utf32_danish_ci" => Collation::utf32_danish_ci, - "utf32_lithuanian_ci" => Collation::utf32_lithuanian_ci, - "utf32_slovak_ci" => Collation::utf32_slovak_ci, - "utf32_spanish2_ci" => Collation::utf32_spanish2_ci, - "utf32_roman_ci" => Collation::utf32_roman_ci, - "utf32_persian_ci" => Collation::utf32_persian_ci, - "utf32_esperanto_ci" => Collation::utf32_esperanto_ci, - "utf32_hungarian_ci" => Collation::utf32_hungarian_ci, - "utf32_sinhala_ci" => Collation::utf32_sinhala_ci, - "tis620_thai_ci" => Collation::tis620_thai_ci, - "utf32_german2_ci" => Collation::utf32_german2_ci, - "utf32_croatian_ci" => Collation::utf32_croatian_ci, - "utf32_unicode_520_ci" => Collation::utf32_unicode_520_ci, - "utf32_vietnamese_ci" => Collation::utf32_vietnamese_ci, - "euckr_korean_ci" => Collation::euckr_korean_ci, - "utf8_unicode_ci" => Collation::utf8_unicode_ci, - "utf8_icelandic_ci" => Collation::utf8_icelandic_ci, - "utf8_latvian_ci" => Collation::utf8_latvian_ci, - "utf8_romanian_ci" => Collation::utf8_romanian_ci, - "utf8_slovenian_ci" => Collation::utf8_slovenian_ci, - "utf8_polish_ci" => Collation::utf8_polish_ci, - "utf8_estonian_ci" => Collation::utf8_estonian_ci, - "utf8_spanish_ci" => Collation::utf8_spanish_ci, - "latin2_czech_cs" => Collation::latin2_czech_cs, - "latin7_estonian_cs" => Collation::latin7_estonian_cs, - "utf8_swedish_ci" => Collation::utf8_swedish_ci, - "utf8_turkish_ci" => Collation::utf8_turkish_ci, - "utf8_czech_ci" => Collation::utf8_czech_ci, - "utf8_danish_ci" => Collation::utf8_danish_ci, - "utf8_lithuanian_ci" => Collation::utf8_lithuanian_ci, - "utf8_slovak_ci" => Collation::utf8_slovak_ci, - "utf8_spanish2_ci" => Collation::utf8_spanish2_ci, - "utf8_roman_ci" => Collation::utf8_roman_ci, - "utf8_persian_ci" => Collation::utf8_persian_ci, - "utf8_esperanto_ci" => Collation::utf8_esperanto_ci, - "latin2_hungarian_ci" => Collation::latin2_hungarian_ci, - "utf8_hungarian_ci" => Collation::utf8_hungarian_ci, - "utf8_sinhala_ci" => Collation::utf8_sinhala_ci, - "utf8_german2_ci" => Collation::utf8_german2_ci, - "utf8_croatian_ci" => Collation::utf8_croatian_ci, - "utf8_unicode_520_ci" => Collation::utf8_unicode_520_ci, - "utf8_vietnamese_ci" => Collation::utf8_vietnamese_ci, - "koi8u_general_ci" => Collation::koi8u_general_ci, - "utf8_general_mysql500_ci" => Collation::utf8_general_mysql500_ci, - "utf8mb4_unicode_ci" => Collation::utf8mb4_unicode_ci, - "utf8mb4_icelandic_ci" => Collation::utf8mb4_icelandic_ci, - "utf8mb4_latvian_ci" => Collation::utf8mb4_latvian_ci, - "utf8mb4_romanian_ci" => Collation::utf8mb4_romanian_ci, - "utf8mb4_slovenian_ci" => Collation::utf8mb4_slovenian_ci, - "utf8mb4_polish_ci" => Collation::utf8mb4_polish_ci, - "cp1251_ukrainian_ci" => Collation::cp1251_ukrainian_ci, - "utf8mb4_estonian_ci" => Collation::utf8mb4_estonian_ci, - "utf8mb4_spanish_ci" => Collation::utf8mb4_spanish_ci, - "utf8mb4_swedish_ci" => Collation::utf8mb4_swedish_ci, - "utf8mb4_turkish_ci" => Collation::utf8mb4_turkish_ci, - "utf8mb4_czech_ci" => Collation::utf8mb4_czech_ci, - "utf8mb4_danish_ci" => Collation::utf8mb4_danish_ci, - "utf8mb4_lithuanian_ci" => Collation::utf8mb4_lithuanian_ci, - "utf8mb4_slovak_ci" => Collation::utf8mb4_slovak_ci, - "utf8mb4_spanish2_ci" => Collation::utf8mb4_spanish2_ci, - "utf8mb4_roman_ci" => Collation::utf8mb4_roman_ci, - "gb2312_chinese_ci" => Collation::gb2312_chinese_ci, - "utf8mb4_persian_ci" => Collation::utf8mb4_persian_ci, - "utf8mb4_esperanto_ci" => Collation::utf8mb4_esperanto_ci, - "utf8mb4_hungarian_ci" => Collation::utf8mb4_hungarian_ci, - "utf8mb4_sinhala_ci" => Collation::utf8mb4_sinhala_ci, - "utf8mb4_german2_ci" => Collation::utf8mb4_german2_ci, - "utf8mb4_croatian_ci" => Collation::utf8mb4_croatian_ci, - "utf8mb4_unicode_520_ci" => Collation::utf8mb4_unicode_520_ci, - "utf8mb4_vietnamese_ci" => Collation::utf8mb4_vietnamese_ci, - "gb18030_chinese_ci" => Collation::gb18030_chinese_ci, - "gb18030_bin" => Collation::gb18030_bin, - "greek_general_ci" => Collation::greek_general_ci, - "gb18030_unicode_520_ci" => Collation::gb18030_unicode_520_ci, - "utf8mb4_0900_ai_ci" => Collation::utf8mb4_0900_ai_ci, - "cp1250_general_ci" => Collation::cp1250_general_ci, - "latin2_croatian_ci" => Collation::latin2_croatian_ci, - "gbk_chinese_ci" => Collation::gbk_chinese_ci, - "cp1257_lithuanian_ci" => Collation::cp1257_lithuanian_ci, - "dec8_swedish_ci" => Collation::dec8_swedish_ci, - "latin5_turkish_ci" => Collation::latin5_turkish_ci, - "latin1_german2_ci" => Collation::latin1_german2_ci, - "armscii8_general_ci" => Collation::armscii8_general_ci, - "utf8_general_ci" => Collation::utf8_general_ci, - "cp1250_czech_cs" => Collation::cp1250_czech_cs, - "ucs2_general_ci" => Collation::ucs2_general_ci, - "cp866_general_ci" => Collation::cp866_general_ci, - "keybcs2_general_ci" => Collation::keybcs2_general_ci, - "macce_general_ci" => Collation::macce_general_ci, - "macroman_general_ci" => Collation::macroman_general_ci, - "cp850_general_ci" => Collation::cp850_general_ci, - "cp852_general_ci" => Collation::cp852_general_ci, - "latin7_general_ci" => Collation::latin7_general_ci, - "latin7_general_cs" => Collation::latin7_general_cs, - "macce_bin" => Collation::macce_bin, - "cp1250_croatian_ci" => Collation::cp1250_croatian_ci, - "utf8mb4_general_ci" => Collation::utf8mb4_general_ci, - "utf8mb4_bin" => Collation::utf8mb4_bin, - "latin1_bin" => Collation::latin1_bin, - "latin1_general_ci" => Collation::latin1_general_ci, - "latin1_general_cs" => Collation::latin1_general_cs, - "latin1_german1_ci" => Collation::latin1_german1_ci, - "cp1251_bin" => Collation::cp1251_bin, - "cp1251_general_ci" => Collation::cp1251_general_ci, - "cp1251_general_cs" => Collation::cp1251_general_cs, - "macroman_bin" => Collation::macroman_bin, - "utf16_general_ci" => Collation::utf16_general_ci, - "utf16_bin" => Collation::utf16_bin, - "utf16le_general_ci" => Collation::utf16le_general_ci, - "cp1256_general_ci" => Collation::cp1256_general_ci, - "cp1257_bin" => Collation::cp1257_bin, - "cp1257_general_ci" => Collation::cp1257_general_ci, - "hp8_english_ci" => Collation::hp8_english_ci, - "utf32_general_ci" => Collation::utf32_general_ci, - "utf32_bin" => Collation::utf32_bin, - "utf16le_bin" => Collation::utf16le_bin, - "binary" => Collation::binary, - "armscii8_bin" => Collation::armscii8_bin, - "ascii_bin" => Collation::ascii_bin, - "cp1250_bin" => Collation::cp1250_bin, - "cp1256_bin" => Collation::cp1256_bin, - "cp866_bin" => Collation::cp866_bin, - "dec8_bin" => Collation::dec8_bin, - "koi8r_general_ci" => Collation::koi8r_general_ci, - "greek_bin" => Collation::greek_bin, - "hebrew_bin" => Collation::hebrew_bin, - "hp8_bin" => Collation::hp8_bin, - "keybcs2_bin" => Collation::keybcs2_bin, - "koi8r_bin" => Collation::koi8r_bin, - "koi8u_bin" => Collation::koi8u_bin, - "utf8_tolower_ci" => Collation::utf8_tolower_ci, - "latin2_bin" => Collation::latin2_bin, - "latin5_bin" => Collation::latin5_bin, - "latin7_bin" => Collation::latin7_bin, - "latin1_swedish_ci" => Collation::latin1_swedish_ci, - "cp850_bin" => Collation::cp850_bin, - "cp852_bin" => Collation::cp852_bin, - "swe7_bin" => Collation::swe7_bin, - "utf8_bin" => Collation::utf8_bin, - "big5_bin" => Collation::big5_bin, - "euckr_bin" => Collation::euckr_bin, - "gb2312_bin" => Collation::gb2312_bin, - "gbk_bin" => Collation::gbk_bin, - "sjis_bin" => Collation::sjis_bin, - "tis620_bin" => Collation::tis620_bin, - "latin2_general_ci" => Collation::latin2_general_ci, - "ucs2_bin" => Collation::ucs2_bin, - "ujis_bin" => Collation::ujis_bin, - "geostd8_general_ci" => Collation::geostd8_general_ci, - "geostd8_bin" => Collation::geostd8_bin, - "latin1_spanish_ci" => Collation::latin1_spanish_ci, - "cp932_japanese_ci" => Collation::cp932_japanese_ci, - "cp932_bin" => Collation::cp932_bin, - "eucjpms_japanese_ci" => Collation::eucjpms_japanese_ci, - "eucjpms_bin" => Collation::eucjpms_bin, - "cp1250_polish_ci" => Collation::cp1250_polish_ci, + /// Collation used for all non-string data. + pub const BINARY: Self = Collation(63); - _ => { - return Err(Error::Configuration( - format!("unsupported MySQL collation: {collation}").into(), - )); - } - }) - } + /// Most broadly supported UTF-8 collation. + pub const UTF8MB4_GENERAL_CI: Self = Collation(45); } diff --git a/sqlx-mysql/src/connection/establish.rs b/sqlx-mysql/src/connection/establish.rs index ec7d8e4c2c..f61654d876 100644 --- a/sqlx-mysql/src/connection/establish.rs +++ b/sqlx-mysql/src/connection/establish.rs @@ -1,7 +1,6 @@ use bytes::buf::Buf; use bytes::Bytes; -use crate::collation::{CharSet, Collation}; use crate::common::StatementCache; use crate::connection::{tls, MySqlConnectionInner, MySqlStream, MAX_PACKET_SIZE}; use crate::error::Error; @@ -37,20 +36,10 @@ impl MySqlConnection { struct DoHandshake<'a> { options: &'a MySqlConnectOptions, - charset: CharSet, - collation: Collation, } impl<'a> DoHandshake<'a> { fn new(options: &'a MySqlConnectOptions) -> Result { - let charset: CharSet = options.charset.parse()?; - let collation: Collation = options - .collation - .as_deref() - .map(|collation| collation.parse()) - .transpose()? - .unwrap_or_else(|| charset.default_collation()); - if options.enable_cleartext_plugin && matches!( options.ssl_mode, @@ -60,21 +49,13 @@ impl<'a> DoHandshake<'a> { log::warn!("Security warning: sending cleartext passwords without requiring SSL"); } - Ok(Self { - options, - charset, - collation, - }) + Ok(Self { options }) } async fn do_handshake(self, socket: S) -> Result { - let DoHandshake { - options, - charset, - collation, - } = self; + let DoHandshake { options } = self; - let mut stream = MySqlStream::with_socket(charset, collation, options, socket); + let mut stream = MySqlStream::with_socket(options, socket); // https://dev.mysql.com/doc/internals/en/connection-phase.html // https://mariadb.com/kb/en/connection/ @@ -125,7 +106,7 @@ impl<'a> DoHandshake<'a> { }; stream.write_packet(HandshakeResponse { - collation: stream.collation as u8, + charset: super::INITIAL_CHARSET, max_packet_size: MAX_PACKET_SIZE, username: &options.username, database: options.database.as_deref(), diff --git a/sqlx-mysql/src/connection/mod.rs b/sqlx-mysql/src/connection/mod.rs index 26613a31d1..a5663b7b69 100644 --- a/sqlx-mysql/src/connection/mod.rs +++ b/sqlx-mysql/src/connection/mod.rs @@ -5,6 +5,7 @@ use std::future::Future; pub(crate) use sqlx_core::connection::*; pub(crate) use stream::{MySqlStream, Waiting}; +use crate::collation::Collation; use crate::common::StatementCache; use crate::error::Error; use crate::protocol::response::Status; @@ -22,6 +23,13 @@ mod tls; const MAX_PACKET_SIZE: u32 = 1024; +/// The charset parameter sent in the `Protocol::HandshakeResponse41` packet. +/// +/// This becomes the default if `set_names = false`, +/// and also ensures that any error messages returned before `SET NAMES` are encoded correctly. +#[allow(clippy::cast_possible_truncation)] +const INITIAL_CHARSET: u8 = Collation::UTF8MB4_GENERAL_CI.0 as u8; + /// A connection to a MySQL database. pub struct MySqlConnection { pub(crate) inner: Box, diff --git a/sqlx-mysql/src/connection/stream.rs b/sqlx-mysql/src/connection/stream.rs index 1f93ed11a5..ff931b2f46 100644 --- a/sqlx-mysql/src/connection/stream.rs +++ b/sqlx-mysql/src/connection/stream.rs @@ -3,7 +3,6 @@ use std::ops::{Deref, DerefMut}; use bytes::{Buf, Bytes, BytesMut}; -use crate::collation::{CharSet, Collation}; use crate::error::Error; use crate::io::MySqlBufExt; use crate::io::{ProtocolDecode, ProtocolEncode}; @@ -19,8 +18,6 @@ pub struct MySqlStream> { pub(super) capabilities: Capabilities, pub(crate) sequence_id: u8, pub(crate) waiting: VecDeque, - pub(crate) charset: CharSet, - pub(crate) collation: Collation, pub(crate) is_tls: bool, } @@ -34,12 +31,7 @@ pub(crate) enum Waiting { } impl MySqlStream { - pub(crate) fn with_socket( - charset: CharSet, - collation: Collation, - options: &MySqlConnectOptions, - socket: S, - ) -> Self { + pub(crate) fn with_socket(options: &MySqlConnectOptions, socket: S) -> Self { let mut capabilities = Capabilities::PROTOCOL_41 | Capabilities::IGNORE_SPACE | Capabilities::DEPRECATE_EOF @@ -62,8 +54,6 @@ impl MySqlStream { capabilities, server_version: (0, 0, 0), sequence_id: 0, - collation, - charset, socket: BufferedSocket::new(socket), is_tls: false, } @@ -222,8 +212,6 @@ impl MySqlStream { capabilities: self.capabilities, sequence_id: self.sequence_id, waiting: self.waiting, - charset: self.charset, - collation: self.collation, is_tls: self.is_tls, } } diff --git a/sqlx-mysql/src/connection/tls.rs b/sqlx-mysql/src/connection/tls.rs index eb077c621b..9034fbd63a 100644 --- a/sqlx-mysql/src/connection/tls.rs +++ b/sqlx-mysql/src/connection/tls.rs @@ -1,4 +1,3 @@ -use crate::collation::{CharSet, Collation}; use crate::connection::{MySqlStream, Waiting}; use crate::error::Error; use crate::net::tls::TlsConfig; @@ -13,8 +12,6 @@ struct MapStream { capabilities: Capabilities, sequence_id: u8, waiting: VecDeque, - charset: CharSet, - collation: Collation, } pub(super) async fn maybe_upgrade( @@ -71,7 +68,7 @@ pub(super) async fn maybe_upgrade( // Request TLS upgrade stream.write_packet(SslRequest { max_packet_size: super::MAX_PACKET_SIZE, - collation: stream.collation as u8, + charset: super::INITIAL_CHARSET, })?; stream.flush().await?; @@ -84,8 +81,6 @@ pub(super) async fn maybe_upgrade( capabilities: stream.capabilities, sequence_id: stream.sequence_id, waiting: stream.waiting, - charset: stream.charset, - collation: stream.collation, }, ) .await @@ -101,8 +96,6 @@ impl WithSocket for MapStream { capabilities: self.capabilities, sequence_id: self.sequence_id, waiting: self.waiting, - charset: self.charset, - collation: self.collation, is_tls: true, } } diff --git a/sqlx-mysql/src/options/connect.rs b/sqlx-mysql/src/options/connect.rs index 58bf21ac2f..8582c6a3b5 100644 --- a/sqlx-mysql/src/options/connect.rs +++ b/sqlx-mysql/src/options/connect.rs @@ -63,15 +63,23 @@ impl ConnectOptions for MySqlConnectOptions { sql_mode.join(",") )); } + if let Some(timezone) = &self.timezone { options.push(format!(r#"time_zone='{}'"#, timezone)); } + if self.set_names { - options.push(format!( - r#"NAMES {} COLLATE {}"#, - conn.inner.stream.charset.as_str(), - conn.inner.stream.collation.as_str() - )) + // As it turns out, we don't _have_ to set a collation if we don't want to. + // We can let the server choose the default collation for the charset. + let set_names = if let Some(collation) = &self.collation { + format!(r#"NAMES {} COLLATE {collation}"#, self.charset,) + } else { + // Leaves the default collation up to the server, + // but ensures statements and results are encoded using the proper charset. + format!("NAMES {}", self.charset) + }; + + options.push(set_names); } if !options.is_empty() { diff --git a/sqlx-mysql/src/options/mod.rs b/sqlx-mysql/src/options/mod.rs index 87732cb40c..421bfb700e 100644 --- a/sqlx-mysql/src/options/mod.rs +++ b/sqlx-mysql/src/options/mod.rs @@ -301,16 +301,24 @@ impl MySqlConnectOptions { /// /// The default character set is `utf8mb4`. This is supported from MySQL 5.5.3. /// If you need to connect to an older version, we recommend you to change this to `utf8`. + /// + /// Implies [`.set_names(true)`][Self::set_names()]. pub fn charset(mut self, charset: &str) -> Self { + self.set_names = true; charset.clone_into(&mut self.charset); self } /// Sets the collation for the connection. /// - /// The default collation is derived from the `charset`. Normally, you should only have to set - /// the `charset`. + /// The default collation is derived on the server from the `charset`, if set. + /// Normally, you should only have to set the `charset`. + /// + /// If setting this, it is recommended to also set [`charset`][Self::charset()]. + /// + /// Implies [`.set_names(true)`][Self::set_names()]. pub fn collation(mut self, collation: &str) -> Self { + self.set_names = true; self.collation = Some(collation.to_owned()); self } @@ -381,8 +389,9 @@ impl MySqlConnectOptions { self } - /// If enabled, `SET NAMES '{charset}' COLLATE '{collation}'` is passed with the values of - /// [`.charset()`] and [`.collation()`] after connecting to the database. + /// If enabled, [`.charset()`] and [`.collation()`] are set with the appropriate command. + /// + /// If only `.charset()` /// /// This ensures the connection uses the specified character set and collation. /// @@ -399,6 +408,8 @@ impl MySqlConnectOptions { /// /// Instead of disabling this, you may also consider setting [`.charset()`] to a charset that /// is supported by your MySQL or MariaDB server version and compatible with UTF-8. + /// + /// [`.charset`]: Self::charset() pub fn set_names(mut self, flag_val: bool) -> Self { self.set_names = flag_val; self diff --git a/sqlx-mysql/src/protocol/connect/handshake_response.rs b/sqlx-mysql/src/protocol/connect/handshake_response.rs index 2e6fec1c0d..6911419d98 100644 --- a/sqlx-mysql/src/protocol/connect/handshake_response.rs +++ b/sqlx-mysql/src/protocol/connect/handshake_response.rs @@ -14,8 +14,8 @@ pub struct HandshakeResponse<'a> { /// Max size of a command packet that the client wants to send to the server pub max_packet_size: u32, - /// Default collation for the connection - pub collation: u8, + /// Default charset (collation ID < 256) for the connection + pub charset: u8, /// Name of the SQL account which client wants to log in pub username: &'a str, @@ -41,7 +41,7 @@ impl ProtocolEncode<'_, Capabilities> for HandshakeResponse<'_> { // NOTE: Half of this packet is identical to the SSL Request packet SslRequest { max_packet_size: self.max_packet_size, - collation: self.collation, + charset: self.charset, } .encode_with(buf, context)?; diff --git a/sqlx-mysql/src/protocol/connect/ssl_request.rs b/sqlx-mysql/src/protocol/connect/ssl_request.rs index cdfc9e5178..833c1bad27 100644 --- a/sqlx-mysql/src/protocol/connect/ssl_request.rs +++ b/sqlx-mysql/src/protocol/connect/ssl_request.rs @@ -7,7 +7,7 @@ use crate::protocol::Capabilities; #[derive(Debug)] pub struct SslRequest { pub max_packet_size: u32, - pub collation: u8, + pub charset: u8, } impl ProtocolEncode<'_, Capabilities> for SslRequest { @@ -16,7 +16,7 @@ impl ProtocolEncode<'_, Capabilities> for SslRequest { #[allow(clippy::cast_possible_truncation)] buf.extend(&(context.bits() as u32).to_le_bytes()); buf.extend(&self.max_packet_size.to_le_bytes()); - buf.push(self.collation); + buf.push(self.charset); // reserved: string<19> buf.extend(&[0_u8; 19]); diff --git a/sqlx-mysql/src/protocol/text/column.rs b/sqlx-mysql/src/protocol/text/column.rs index a7d95f7166..6ff65b3fcc 100644 --- a/sqlx-mysql/src/protocol/text/column.rs +++ b/sqlx-mysql/src/protocol/text/column.rs @@ -1,12 +1,12 @@ use std::str; -use bitflags::bitflags; -use bytes::{Buf, Bytes}; - +use crate::collation::Collation; use crate::error::Error; use crate::io::MySqlBufExt; use crate::io::ProtocolDecode; use crate::protocol::Capabilities; +use bitflags::bitflags; +use bytes::{Buf, Bytes}; // https://dev.mysql.com/doc/dev/mysql-server/8.0.12/group__group__cs__column__definition__flags.html @@ -110,8 +110,7 @@ pub(crate) struct ColumnDefinition { table: Bytes, alias: Bytes, name: Bytes, - #[allow(unused)] - pub(crate) collation: u16, + pub(crate) collation: Collation, pub(crate) max_size: u32, pub(crate) r#type: ColumnType, pub(crate) flags: ColumnFlags, @@ -162,7 +161,7 @@ impl ProtocolDecode<'_, Capabilities> for ColumnDefinition { table, alias, name, - collation, + collation: Collation(collation), max_size, r#type: ColumnType::try_from_u16(type_id)?, flags: ColumnFlags::from_bits_truncate(flags), diff --git a/sqlx-mysql/src/type_info.rs b/sqlx-mysql/src/type_info.rs index a80b233fc9..d37997b665 100644 --- a/sqlx-mysql/src/type_info.rs +++ b/sqlx-mysql/src/type_info.rs @@ -1,8 +1,8 @@ use std::fmt::{self, Display, Formatter}; -pub(crate) use sqlx_core::type_info::*; - +use crate::collation::Collation; use crate::protocol::text::{ColumnDefinition, ColumnFlags, ColumnType}; +pub(crate) use sqlx_core::type_info::*; /// Type information for a MySql type. #[derive(Debug, Clone)] @@ -10,6 +10,7 @@ use crate::protocol::text::{ColumnDefinition, ColumnFlags, ColumnType}; pub struct MySqlTypeInfo { pub(crate) r#type: ColumnType, pub(crate) flags: ColumnFlags, + pub(crate) collation: Collation, // [max_size] for integer types, this is (M) in BIT(M) or TINYINT(M) #[cfg_attr(feature = "offline", serde(default))] @@ -21,6 +22,7 @@ impl MySqlTypeInfo { Self { r#type: ty, flags: ColumnFlags::BINARY, + collation: Collation::BINARY, max_size: None, } } @@ -38,6 +40,7 @@ impl MySqlTypeInfo { Self { r#type: ColumnType::String, flags: ColumnFlags::ENUM, + collation: Collation::UTF8MB4_GENERAL_CI, max_size: None, } } @@ -60,6 +63,7 @@ impl MySqlTypeInfo { Self { r#type: column.r#type, flags: column.flags, + collation: column.collation, max_size: Some(column.max_size), } } diff --git a/sqlx-mysql/src/types/bool.rs b/sqlx-mysql/src/types/bool.rs index 7d8c243d36..6f236642f6 100644 --- a/sqlx-mysql/src/types/bool.rs +++ b/sqlx-mysql/src/types/bool.rs @@ -1,3 +1,4 @@ +use crate::collation::Collation; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; @@ -9,9 +10,10 @@ use crate::{ impl Type for bool { fn type_info() -> MySqlTypeInfo { - // MySQL has no actual `BOOLEAN` type, the type is an alias of `TINYINT(1)` + // MySQL has no actual `BOOLEAN` type, the type is an alias of `[UNSIGNED] TINYINT(1)` MySqlTypeInfo { flags: ColumnFlags::BINARY | ColumnFlags::UNSIGNED, + collation: Collation::BINARY, max_size: Some(1), r#type: ColumnType::Tiny, } diff --git a/sqlx-mysql/src/types/str.rs b/sqlx-mysql/src/types/str.rs index 3a306fec45..64bc926bfa 100644 --- a/sqlx-mysql/src/types/str.rs +++ b/sqlx-mysql/src/types/str.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::rc::Rc; use std::sync::Arc; +use crate::collation::Collation; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; @@ -15,6 +16,8 @@ impl Type for str { MySqlTypeInfo { r#type: ColumnType::VarString, // VARCHAR flags: ColumnFlags::empty(), + // Doesn't matter because we never send this. + collation: Collation::UTF8MB4_GENERAL_CI, max_size: None, } } @@ -31,7 +34,13 @@ impl Type for str { | ColumnType::String | ColumnType::VarString | ColumnType::Enum - ) && !ty.flags.contains(ColumnFlags::BINARY) + ) + // Any collation that *isn't* `binary` generally indicates string data. + // The actual collation used for storage doesn't matter, + // because the server will transcode to the charset we specify. + // + // See comment in `src/collation.rs` for details. + && ty.collation != Collation::BINARY } } diff --git a/sqlx-mysql/src/types/uint.rs b/sqlx-mysql/src/types/uint.rs index ca8eb75347..5ea85cf7a6 100644 --- a/sqlx-mysql/src/types/uint.rs +++ b/sqlx-mysql/src/types/uint.rs @@ -1,3 +1,4 @@ +use crate::collation::Collation; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; @@ -10,6 +11,7 @@ fn uint_type_info(ty: ColumnType) -> MySqlTypeInfo { MySqlTypeInfo { r#type: ty, flags: ColumnFlags::BINARY | ColumnFlags::UNSIGNED, + collation: Collation::BINARY, max_size: None, } } diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index 487f589c37..89ca096169 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -58,125 +58,114 @@ services: MYSQL_DATABASE: sqlx MYSQL_ALLOW_EMPTY_PASSWORD: 1 - # - # MariaDB 10.11, 10.6, 10.5, 10.4 + # MariaDB + # Testing will be dropped when Community Support ends. # https://mariadb.org/about/#maintenance-policy - # - - mariadb_11_4: - image: mariadb:11.4 - volumes: - - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" - ports: - - 3306 - environment: - MARIADB_ROOT_PASSWORD: password - MARIADB_DATABASE: sqlx - - mariadb_11_4_client_ssl: - build: - context: . - dockerfile: mysql/Dockerfile - args: - IMAGE: mariadb:11.4 - volumes: - - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" - ports: - - 3306 - environment: - MARIADB_DATABASE: sqlx - MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 - mariadb_10_11: - image: mariadb:10.11 + # EOL: 2028/06/04 (sooner than 11.4 for some reason) + mariadb_11_8: + image: mariadb:11.8 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" + - "./mysql/setup-mariadb.sql:/docker-entrypoint-initdb.d/setup-mariadb.sql:z" ports: - 3306 environment: MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: sqlx - mariadb_10_11_client_ssl: + mariadb_11_8_client_ssl: build: context: . dockerfile: mysql/Dockerfile args: - IMAGE: mariadb:10.11 + IMAGE: mariadb:11.8 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" + - "./mysql/setup-mariadb.sql:/docker-entrypoint-initdb.d/setup-mariadb.sql:z" ports: - 3306 environment: MARIADB_DATABASE: sqlx MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 - mariadb_10_6: - image: mariadb:10.6 + # EOL: 2029/05/29 + mariadb_11_4: + image: mariadb:11.4 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" + - "./mysql/setup-mariadb.sql:/docker-entrypoint-initdb.d/setup-mariadb.sql:z" ports: - 3306 environment: MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: sqlx - mariadb_10_6_client_ssl: + mariadb_11_4_client_ssl: build: context: . dockerfile: mysql/Dockerfile args: - IMAGE: mariadb:10.6 + IMAGE: mariadb:11.4 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" + - "./mysql/setup-mariadb.sql:/docker-entrypoint-initdb.d/setup-mariadb.sql:z" ports: - 3306 environment: MARIADB_DATABASE: sqlx MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 - mariadb_10_5: - image: mariadb:10.5 + # EOL: 2028/02/16 + mariadb_10_11: + image: mariadb:10.11 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" + - "./mysql/setup-mariadb.sql:/docker-entrypoint-initdb.d/setup-mariadb.sql:z" ports: - 3306 environment: MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: sqlx - mariadb_10_5_client_ssl: + mariadb_10_11_client_ssl: build: context: . dockerfile: mysql/Dockerfile args: - IMAGE: mariadb:10.5 + IMAGE: mariadb:10.11 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" + - "./mysql/setup-mariadb.sql:/docker-entrypoint-initdb.d/setup-mariadb.sql:z" ports: - 3306 environment: MARIADB_DATABASE: sqlx MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 - mariadb_10_4: - image: mariadb:10.4 + # EOL: 2026/07/06 + mariadb_10_6: + image: mariadb:10.6 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" + # UUID support was not added until 10.10 + # - "./mysql/setup-mariadb.sql:/docker-entrypoint-initdb.d/setup-mariadb.sql:z" ports: - 3306 environment: MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: sqlx - mariadb_10_4_client_ssl: + mariadb_10_6_client_ssl: build: context: . dockerfile: mysql/Dockerfile args: - IMAGE: mariadb:10.4 + IMAGE: mariadb:10.6 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" + # UUID support was not added until 10.10 + # - "./mysql/setup-mariadb.sql:/docker-entrypoint-initdb.d/setup-mariadb.sql:z" ports: - 3306 environment: @@ -189,6 +178,7 @@ services: image: quay.io/mariadb-foundation/mariadb-devel:verylatest volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" + - "./mysql/setup-mariadb.sql:/docker-entrypoint-initdb.d/setup-mariadb.sql:z" ports: - 3306 environment: @@ -203,6 +193,7 @@ services: IMAGE: quay.io/mariadb-foundation/mariadb-devel:verylatest volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" + - "./mysql/setup-mariadb.sql:/docker-entrypoint-initdb.d/setup-mariadb.sql:z" ports: - 3306 environment: diff --git a/tests/mysql/macros.rs b/tests/mysql/macros.rs index 8187f6d8d8..8186e344c4 100644 --- a/tests/mysql/macros.rs +++ b/tests/mysql/macros.rs @@ -555,4 +555,34 @@ async fn test_from_row_json_try_from_attr() -> anyhow::Result<()> { Ok(()) } +#[cfg(all(mariadb, not(mariadb = "10_6"), feature = "time"))] +#[sqlx_macros::test] +async fn test_uuid_is_compatible_mariadb() -> anyhow::Result<()> { + use sqlx::types::time::OffsetDateTime; + use sqlx::types::Uuid; + + struct Tweet { + id: Uuid, + text: String, + created_at: OffsetDateTime, + owner_id: Option, + } + + let mut conn = new::().await?; + + sqlx::query!("INSERT INTO tweet_with_uuid(text) VALUES ('Hello, world!')") + .execute(&mut conn) + .await?; + + let tweets: Vec = sqlx::query_as!(Tweet, "SELECT * FROM tweet_with_uuid") + .fetch_all(&mut conn) + .await?; + + assert_eq!(tweets.len(), 1); + + assert_eq!(tweets[0].text, "Hello, world!"); + + Ok(()) +} + // we don't emit bind parameter type-checks for MySQL so testing the overrides is redundant diff --git a/tests/mysql/mysql.rs b/tests/mysql/mysql.rs index c7f7a47960..a4421f3ec0 100644 --- a/tests/mysql/mysql.rs +++ b/tests/mysql/mysql.rs @@ -603,3 +603,34 @@ async fn select_statement_count(conn: &mut MySqlConnection) -> Result anyhow::Result<()> { + let mut conn = new::().await?; + + sqlx::raw_sql( + "\ + CREATE TABLE IF NOT EXISTS users + ( + `id` BIGINT AUTO_INCREMENT, + `username` VARCHAR(128) NOT NULL, + PRIMARY KEY (id) + ); + ", + ) + .execute(&mut conn) + .await?; + + let result = sqlx::raw_sql( + "\ + SET @myvar := 'test@test.com'; + select id from users where username = @myvar; + ", + ) + .fetch_optional(&mut conn) + .await?; + + assert!(result.is_none(), "{result:?}"); + + Ok(()) +} diff --git a/tests/mysql/setup-mariadb.sql b/tests/mysql/setup-mariadb.sql new file mode 100644 index 0000000000..77e52156e2 --- /dev/null +++ b/tests/mysql/setup-mariadb.sql @@ -0,0 +1,10 @@ +-- additional SQL to execute for MariaDB databases + +CREATE TABLE tweet_with_uuid +( + -- UUID is only a bespoke datatype in MariaDB. + id UUID PRIMARY KEY DEFAULT UUID(), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + text TEXT NOT NULL, + owner_id UUID +);