#![allow(clippy::unwrap_used, clippy::expect_used)]
use mssql_client::{Client, Config, Error, Ready};
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt::init();
let host = std::env::var("MSSQL_HOST").expect(
"MSSQL_HOST environment variable is required.\n\
Usage: MSSQL_HOST=<host> MSSQL_USER=<user> MSSQL_PASSWORD=<pass> cargo run --example collation_encoding"
);
let user = std::env::var("MSSQL_USER").expect(
"MSSQL_USER environment variable is required.\n\
Usage: MSSQL_HOST=<host> MSSQL_USER=<user> MSSQL_PASSWORD=<pass> cargo run --example collation_encoding"
);
let password = std::env::var("MSSQL_PASSWORD").expect(
"MSSQL_PASSWORD environment variable is required.\n\
Usage: MSSQL_HOST=<host> MSSQL_USER=<user> MSSQL_PASSWORD=<pass> cargo run --example collation_encoding"
);
let database = std::env::var("MSSQL_DATABASE").unwrap_or_else(|_| "master".into());
let encrypt = std::env::var("MSSQL_ENCRYPT").unwrap_or_else(|_| "true".into());
let conn_str = format!(
"Server={host};Database={database};User Id={user};Password={password};TrustServerCertificate=true;Encrypt={encrypt}"
);
let config = Config::from_connection_string(&conn_str)?;
println!("Connecting to SQL Server at {host}...");
let mut client = Client::connect(config).await?;
println!("Connected successfully!\n");
demonstrate_collation_metadata(&mut client).await?;
demonstrate_nvarchar_unicode(&mut client).await?;
if let Err(e) = demonstrate_varchar_with_collations(&mut client).await {
println!("\nNote: VARCHAR collation tests skipped or partially failed: {e}");
println!(
"This is expected if the SQL Server instance doesn't have the required collations."
);
}
client.close().await?;
println!("\nConnection closed.");
Ok(())
}
async fn demonstrate_collation_metadata(client: &mut Client<Ready>) -> Result<(), Error> {
println!("=== Column Collation Metadata ===\n");
let rows = client
.query(
"SELECT
CAST('hello' AS VARCHAR(50)) AS varchar_col,
N'hello' AS nvarchar_col,
CAST('test' AS CHAR(10)) AS char_col",
&[],
)
.await?;
for result in rows {
let row = result?;
println!("Column metadata:");
for (i, col) in row.columns().iter().enumerate() {
println!(" [{}] {} ({})", i, col.name, col.type_name,);
#[cfg(feature = "encoding")]
{
println!(" Encoding: {}", col.encoding_name());
println!(" Is UTF-8: {}", col.is_utf8_collation());
}
}
println!();
}
Ok(())
}
async fn demonstrate_nvarchar_unicode(client: &mut Client<Ready>) -> Result<(), Error> {
println!("=== NVARCHAR Unicode Handling ===\n");
let rows = client
.query(
"SELECT
N'Hello, 世界!' AS chinese,
N'こんにちは' AS japanese,
N'안녕하세요' AS korean,
N'Привет мир' AS russian,
N'مرحبا بالعالم' AS arabic",
&[],
)
.await?;
for result in rows {
let row = result?;
println!("NVARCHAR results (always Unicode):");
println!(" Chinese: {}", row.get::<String>(0)?);
println!(" Japanese: {}", row.get::<String>(1)?);
println!(" Korean: {}", row.get::<String>(2)?);
println!(" Russian: {}", row.get::<String>(3)?);
println!(" Arabic: {}", row.get::<String>(4)?);
}
println!();
Ok(())
}
async fn demonstrate_varchar_with_collations(client: &mut Client<Ready>) -> Result<(), Error> {
println!("=== VARCHAR Collation-Aware Decoding ===\n");
client
.execute(
"IF OBJECT_ID('tempdb..#collation_test') IS NOT NULL
DROP TABLE #collation_test",
&[],
)
.await?;
let create_result = client
.execute(
"CREATE TABLE #collation_test (
id INT IDENTITY(1,1),
-- Latin1 (Western European)
latin1_col VARCHAR(100) COLLATE SQL_Latin1_General_CP1_CI_AS,
-- We use NVARCHAR for CJK to avoid collation availability issues
unicode_col NVARCHAR(100)
)",
&[],
)
.await;
if let Err(e) = create_result {
println!("Could not create test table: {e}");
return Ok(());
}
client
.execute(
"INSERT INTO #collation_test (latin1_col, unicode_col) VALUES
('Hello World', N'Hello World'),
('Café résumé', N'Café résumé'),
('Müller Böse', N'Müller Böse')",
&[],
)
.await?;
let rows = client.query("SELECT * FROM #collation_test", &[]).await?;
println!("Test data with Latin1 (Windows-1252) collation:");
for result in rows {
let row = result?;
let id: i32 = row.get(0)?;
let latin1: String = row.get(1)?;
let unicode: String = row.get(2)?;
println!(" Row {id}: latin1='{latin1}' unicode='{unicode}'");
if latin1 == unicode {
println!(" ✓ Encoding preserved correctly");
} else {
println!(" ⚠ Encoding mismatch (may indicate decoding issue)");
}
}
println!();
println!("Understanding VARCHAR encoding:");
println!(" - VARCHAR stores data in the collation's code page");
println!(" - Windows-1252 (Latin1): 'é' = 0xE9 (single byte)");
println!(" - Shift_JIS (Japanese): '日' = 0x93FA (two bytes)");
println!(" - UTF-16 (NVARCHAR): '日' = 0x65E5 (two bytes, different value)");
println!();
client.execute("DROP TABLE #collation_test", &[]).await?;
Ok(())
}