Skip to content

Commit 08cca83

Browse files
committed
Expose row columns for MySQL
This is my attempt to fix launchbadge#181 . I am not sure if you have the same implementation in mind so let me know if I am on the right track. I was not sure how to verify the column type in tests as I couldn't construct `MySqlTypeInfo` due to `TypeId` being crate-private.
1 parent c285e28 commit 08cca83

File tree

5 files changed

+73
-4
lines changed

5 files changed

+73
-4
lines changed

sqlx-core/src/mysql/row.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use std::collections::HashMap;
22
use std::sync::Arc;
3+
use std::vec::Vec;
34

45
use crate::mysql::protocol;
56
use crate::mysql::{MySql, MySqlValue};
6-
use crate::row::{ColumnIndex, Row};
7+
use crate::row::{Column, ColumnIndex, Row};
78

89
pub struct MySqlRow<'c> {
910
pub(super) row: protocol::Row<'c>,
@@ -35,6 +36,24 @@ impl<'c> Row<'c> for MySqlRow<'c> {
3536

3637
Ok(value)
3738
}
39+
40+
fn columns(&self) -> Box<[Column<Self::Database>]> {
41+
let mut columns = Vec::with_capacity(self.row.columns.len());
42+
for (index, column_type) in self.row.columns.iter().enumerate() {
43+
let name = self
44+
.names
45+
.iter()
46+
.find(|(_name, name_index)| (**name_index as usize) == index)
47+
.map(|(name, _)| name.as_ref());
48+
49+
columns.push(Column {
50+
name,
51+
type_info: Some(column_type),
52+
});
53+
}
54+
55+
columns.into_boxed_slice()
56+
}
3857
}
3958

4059
impl<'c> ColumnIndex<'c, MySqlRow<'c>> for usize {

sqlx-core/src/postgres/row.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::postgres::protocol::{DataRow, TypeFormat};
55
use crate::postgres::type_info::SharedStr;
66
use crate::postgres::value::PgValue;
77
use crate::postgres::{PgTypeInfo, Postgres};
8-
use crate::row::{ColumnIndex, Row};
8+
use crate::row::{self, ColumnIndex, Row};
99

1010
// A statement has 0 or more columns being returned from the database
1111
// For Postgres, each column has an OID and a format (binary or text)
@@ -68,6 +68,10 @@ impl<'c> Row<'c> for PgRow<'c> {
6868

6969
Ok(value)
7070
}
71+
72+
fn columns(&self) -> Box<[row::Column<Self::Database>]> {
73+
todo!()
74+
}
7175
}
7276

7377
impl<'c> ColumnIndex<'c, PgRow<'c>> for usize {

sqlx-core/src/row.rs

+8
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ where
185185
) -> crate::Result<<Self::Database as HasRawValue<'c>>::RawValue>
186186
where
187187
I: ColumnIndex<'c, Self>;
188+
189+
/// Get all columns.
190+
fn columns(&self) -> Box<[Column<Self::Database>]>;
188191
}
189192

190193
// Prevent users from implementing the `Row` trait.
@@ -235,6 +238,11 @@ where
235238
fn from_row(row: &R) -> crate::Result<Self>;
236239
}
237240

241+
pub struct Column<'r, DB: Database> {
242+
pub name: Option<&'r str>,
243+
pub type_info: Option<&'r DB::TypeInfo>,
244+
}
245+
238246
// Macros to help unify the internal implementations as a good chunk
239247
// is very similar
240248

sqlx-core/src/sqlite/row.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::row::{ColumnIndex, Row};
1+
use crate::row::{Column, ColumnIndex, Row};
22
use crate::sqlite::statement::Statement;
33
use crate::sqlite::value::SqliteValue;
44
use crate::sqlite::{Sqlite, SqliteConnection};
@@ -43,6 +43,10 @@ impl<'c> Row<'c> for SqliteRow<'c> {
4343
index: index.index(self)? as i32,
4444
})
4545
}
46+
47+
fn columns(&self) -> Box<[Column<Self::Database>]> {
48+
todo!()
49+
}
4650
}
4751

4852
impl<'c> ColumnIndex<'c, SqliteRow<'c>> for usize {

tests/mysql.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use futures::TryStreamExt;
2-
use sqlx::{mysql::MySqlQueryAs, Connection, Executor, MySql, MySqlPool};
2+
use sqlx::{mysql::MySqlQueryAs, Connection, Cursor, Executor, MySql, MySqlPool, Row};
33
use sqlx_test::new;
44
use std::time::Duration;
55

@@ -232,3 +232,37 @@ async fn test_fetch_one_and_ping() -> anyhow::Result<()> {
232232

233233
Ok(())
234234
}
235+
236+
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
237+
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
238+
async fn test_row_columns() -> anyhow::Result<()> {
239+
let mut conn = new::<MySql>().await?;
240+
241+
let _ = conn
242+
.execute(
243+
r#"
244+
CREATE TEMPORARY TABLE row_columns_test (
245+
id int primary key auto_increment,
246+
name text not null,
247+
age int
248+
);
249+
250+
INSERT INTO row_columns_test (name, age) VALUES ('James', 12);
251+
"#,
252+
)
253+
.await?;
254+
255+
let mut cursor = conn.fetch("SELECT * FROM row_columns_test");
256+
let row = cursor.next().await?.unwrap();
257+
let columns = row.columns();
258+
259+
assert_eq!(columns.len(), 3);
260+
assert_eq!(columns[0].name, Some("id"));
261+
assert_eq!(columns[0].type_info.is_some(), true);
262+
assert_eq!(columns[1].name, Some("name"));
263+
assert_eq!(columns[1].type_info.is_some(), true);
264+
assert_eq!(columns[2].name, Some("age"));
265+
assert_eq!(columns[2].type_info.is_some(), true);
266+
267+
Ok(())
268+
}

0 commit comments

Comments
 (0)