-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MySQL VARCHAR is mapped to Vec<u8>
instead of String
#1690
Comments
What is the character set on those In the MySQL protocol, character strings and byte strings can use the same type IDs so we distinguish them based on character set. We only recognize a handful of character sets that are known to be compatible with UTF-8 so if the columns use an unrecognized character set then the |
CREATE TABLE `users` (
`id` varchar(255) COLLATE utf8mb4_bin NOT NULL,
`username` varchar(50) COLLATE utf8mb4_bin NOT NULL,
`email` varchar(255) COLLATE utf8mb4_bin NOT NULL,
`full_name` varchar(60) COLLATE utf8mb4_bin DEFAULT NULL,
`password_hash` varchar(255) COLLATE utf8mb4_bin NOT NULL,
`avatar_url` text COLLATE utf8mb4_bin,
`bio` text COLLATE utf8mb4_bin,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; To be specific, this is the schema generated from PlanetScale. It seems like it is using the |
We do recognize that character set, in fact it's what the connection is set to use by default for sanity reasons. Can you execute this query against the database? Select ID, CHARACTER_SET_NAME, COLLATION_NAME
FROM INFORMATION_SCHEMA.COLLATIONS
WHERE COLLATION_NAME = 'utf8mb4_bin`; |
This is what I got from the query fyp/slide-service> SELECT ID, CHARACTER_SET_NAME, COLLATION_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE COLLATION_NAME = 'utf8mb4_bin';
+----+--------------------+----------------+
| ID | CHARACTER_SET_NAME | COLLATION_NAME |
+----+--------------------+----------------+
| 46 | utf8mb4 | utf8mb4_bin |
+----+--------------------+----------------+
fyp/slide-service>
|
Yeah, that's correct, so it doesn't make sense that the macros think it's a binary string. Could you execute this program against your database and tell me what it outputs: use sqlx::{MySqlConnection, Executor};
// or #[async_std::main]
#[tokio::main]
async fn main() {
let mut conn = MySqlConnection::connect("<your database URL>").await.unwrap();
// This is the method the macros use. It's hidden in the docs because it's not considered part of the stable API.
let describe = conn.describe("SELECT * FROM users WHERE username = ?").await.unwrap();
println!("describe output: {:?}", describe);
} |
What would also really help is a Wireshark capture taken while this program executes, although if you're required to use TLS to connect to the database then it'll unfortunately be useless. |
warning: `auth-service` (bin "auth-service") generated 10 warnings
Finished dev [unoptimized + debuginfo] target(s) in 4.44s
Running `/Users/marcus/Projects/fyp/backend/target/debug/auth-service`
describe output: Describe { columns: [MySqlColumn { ordinal: 0, name: id, type_info: MySqlTypeInfo { type: VarString, flags: NOT_NULL | PRIMARY_KEY | BINARY | NO_DEFAULT_VALUE, char_set: 255, max_size: Some(1020) }, flags: Some(NOT_NULL | PRIMARY_KEY | BINARY | NO_DEFAULT_VALUE) }, MySqlColumn { ordinal: 1, name: username, type_info: MySqlTypeInfo { type: VarString, flags: NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE, char_set: 255, max_size: Some(200) }, flags: Some(NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE) }, MySqlColumn { ordinal: 2, name: email, type_info: MySqlTypeInfo { type: VarString, flags: NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE, char_set: 255, max_size: Some(1020) }, flags: Some(NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE) }, MySqlColumn { ordinal: 3, name: full_name, type_info: MySqlTypeInfo { type: VarString, flags: BINARY, char_set: 255, max_size: Some(240) }, flags: Some(BINARY) }, MySqlColumn { ordinal: 4, name: password_hash, type_info: MySqlTypeInfo { type: VarString, flags: NOT_NULL | BINARY | NO_DEFAULT_VALUE, char_set: 255, max_size: Some(1020) }, flags: Some(NOT_NULL | BINARY | NO_DEFAULT_VALUE) }, MySqlColumn { ordinal: 5, name: avatar_url, type_info: MySqlTypeInfo { type: VarString, flags: BINARY, char_set: 255, max_size: Some(8332) }, flags: Some(BINARY) }, MySqlColumn { ordinal: 6, name: bio, type_info: MySqlTypeInfo { type: VarString, flags: BINARY, char_set: 255, max_size: Some(1020) }, flags: Some(BINARY) }, MySqlColumn { ordinal: 7, name: created_at, type_info: MySqlTypeInfo { type: Timestamp, flags: NOT_NULL | BINARY | TIMESTAMP, char_set: 63, max_size: Some(19) }, flags: Some(NOT_NULL | BINARY | TIMESTAMP) }, MySqlColumn { ordinal: 8, name: updated_at, type_info: MySqlTypeInfo { type: Timestamp, flags: NOT_NULL | BINARY | TIMESTAMP | ON_UPDATE_NOW, char_set: 63, max_size: Some(19) }, flags: Some(NOT_NULL | BINARY | TIMESTAMP | ON_UPDATE_NOW) }], parameters: Some(Right(1)), nullable: [Some(false), Some(false), Some(false), Some(true), Some(false), Some(true), Some(true), Some(false), Some(false)] } This is the output, I wasn't able to use Wireshark as it is required to use |
Yep, it's using a charset/collation (they're used interchangeably in places but in MySQL a collation is a charset + sorting rules) that SQLx doesn't recognize: So that collation is UTF-8 4-byte encoding, Unicode version 9.0 with accent-insensitive and case-insensitive sorting. That is not the same as the We probably need some sort of connection-local cache of collations, because there's a whole list of them for different encodings, Unicode versions and sorting rules, but all we really care about is if they're compatible with UTF-8. We could just add this and more to the hardcoded list that we currently have, but we're already doing something similar in Postgres to map type names to type IDs so I don't think it'd be the worst thing. |
No, I didn't modify the collation. However, now I found out it's because of that, so in theory if I change the charset/collation to a supported one then this issue will be resolved right? I assume sqlx supports either collation of |
The collations that SQLx considers compatible with As a sanity check, can you give me the output of |
fyp/slide-service> SELECT * FROM INFORMATION_SCHEMA.COLLATIONS WHERE ID = 255;
+--------------------+--------------------+-----+------------+-------------+---------+---------------+
| COLLATION_NAME | CHARACTER_SET_NAME | ID | IS_DEFAULT | IS_COMPILED | SORTLEN | PAD_ATTRIBUTE |
+--------------------+--------------------+-----+------------+-------------+---------+---------------+
| utf8mb4_0900_ai_ci | utf8mb4 | 255 | Yes | Yes | 0 | NO PAD |
+--------------------+--------------------+-----+------------+-------------+---------+---------------+
fyp/slide-service> Here you go, thanks for helping with this issue ;D |
Yeah, that's matching the information I get from MySQL locally. That's really weird, though. Silently changing from a case-sensitive collation to a case-insensitive one seems like a pretty bad thing for a database to do. It'd certainly change the behavior of indexes. |
According to the MySQL docs, the default collation unless otherwise specified is
It's possible that PlanetScale is ignoring the collation we set on the connection. It might be worth opening a thread in their beta discussions: https://github.com/planetscale/beta/discussions |
Opened a bug report and I'm labeling this as |
From the Planetscale team:
It looks like they're unable to fix the issue on their end anytime soon. It looks like adding |
I have run into the same issue: VARCHAR is mapped to Vec instead of String. describe test_table
SELECT ID, CHARACTER_SET_NAME, COLLATION_NAME
FROM INFORMATION_SCHEMA.COLLATIONS
WHERE COLLATION_NAME = 'utf8mb4_bin';
SELECT * FROM INFORMATION_SCHEMA.COLLATIONS WHERE ID = 255;
let mut conn = MySqlConnection::connect(URL).await.unwrap();
let describe = conn.describe("SELECT * FROM test_table WHERE id = ?").await.unwrap();
println!("{:?}", describe);
Cargo.toml
Is this a bug on my side, or new variant of the previous issue perhaps? |
Have you solved this problem? How to handle it? |
I just got hit with this when upgrading from 0.7.x to 0.8.2. Previously accepted
|
The following is my database schema, I expect the VARCHAR and TEXT types are both mapped to Rust's
String
. However, I got the error in the next following log.This is the compile-time error
This following is the Rust struct.
One more note, I'm not sure if this is related, I'm using PlanetScale's MySQL which uses Vitess under the hood.
The text was updated successfully, but these errors were encountered: