mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-04-27 11:56:05 +02:00
feat(sql): Improve type decoding. Adds support for time and dates (#266)
* feat(sql): Improve type decoding. Adds support for time and dates * remove default feat * typo
This commit is contained in:
Generated
+3
-1
@@ -3901,6 +3901,7 @@ dependencies = [
|
||||
"sqlx-rt",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"time 0.3.17",
|
||||
"tokio-stream",
|
||||
"url",
|
||||
"webpki-roots",
|
||||
@@ -4380,13 +4381,14 @@ dependencies = [
|
||||
name = "tauri-plugin-sql"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"futures-core",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tauri",
|
||||
"thiserror",
|
||||
"time 0.3.17",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
||||
@@ -15,9 +15,10 @@ serde_json.workspace = true
|
||||
tauri.workspace = true
|
||||
log.workspace = true
|
||||
thiserror.workspace = true
|
||||
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "json"] }
|
||||
futures-core = "0.3"
|
||||
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "json", "time"] }
|
||||
time = "0.3"
|
||||
tokio = { version = "1", features = ["sync"] }
|
||||
futures = "0.3"
|
||||
|
||||
[features]
|
||||
sqlite = ["sqlx/sqlite"]
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#[cfg(feature = "mysql")]
|
||||
mod mysql;
|
||||
#[cfg(feature = "postgres")]
|
||||
mod postgres;
|
||||
#[cfg(feature = "sqlite")]
|
||||
mod sqlite;
|
||||
|
||||
#[cfg(feature = "mysql")]
|
||||
pub(crate) use mysql::to_json;
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
pub(crate) use postgres::to_json;
|
||||
|
||||
#[cfg(feature = "sqlite")]
|
||||
pub(crate) use sqlite::to_json;
|
||||
@@ -0,0 +1,90 @@
|
||||
use serde_json::Value as JsonValue;
|
||||
use sqlx::{mysql::MySqlValueRef, TypeInfo, Value, ValueRef};
|
||||
use time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
|
||||
|
||||
use crate::Error;
|
||||
|
||||
pub(crate) fn to_json(v: MySqlValueRef) -> Result<JsonValue, Error> {
|
||||
if v.is_null() {
|
||||
return Ok(JsonValue::Null);
|
||||
}
|
||||
|
||||
let res = match v.type_info().name() {
|
||||
"CHAR" | "VARCHAR" | "TINYTEXT" | "TEXT" | "MEDIUMTEXT" | "LONGTEXT" | "ENUM" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode() {
|
||||
JsonValue::String(v)
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"FLOAT" | "DOUBLE" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<f64>() {
|
||||
JsonValue::from(v)
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"TINYINT" | "SMALLINT" | "INT" | "MEDIUMINT" | "BIGINT" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<i64>() {
|
||||
JsonValue::Number(v.into())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"TINYINT UNSIGNED" | "SMALLINT UNSIGNED" | "INT UNSIGNED" | "MEDIUMINT UNSIGNED"
|
||||
| "BIGINT UNSIGNED" | "YEAR" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<u64>() {
|
||||
JsonValue::Number(v.into())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"BOOLEAN" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode() {
|
||||
JsonValue::Bool(v)
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"DATE" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<Date>() {
|
||||
JsonValue::String(v.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"TIME" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<Time>() {
|
||||
JsonValue::String(v.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"DATETIME" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<PrimitiveDateTime>() {
|
||||
JsonValue::String(v.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"TIMESTAMP" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<OffsetDateTime>() {
|
||||
JsonValue::String(v.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"JSON" => ValueRef::to_owned(&v).try_decode().unwrap_or_default(),
|
||||
"TINIYBLOB" | "MEDIUMBLOB" | "BLOB" | "LONGBLOB" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<Vec<u8>>() {
|
||||
JsonValue::Array(v.into_iter().map(|n| JsonValue::Number(n.into())).collect())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"NULL" => JsonValue::Null,
|
||||
_ => return Err(Error::UnsupportedDatatype(v.type_info().name().to_string())),
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
use serde_json::Value as JsonValue;
|
||||
use sqlx::{postgres::PgValueRef, TypeInfo, Value, ValueRef};
|
||||
use time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
|
||||
|
||||
use crate::Error;
|
||||
|
||||
pub(crate) fn to_json(v: PgValueRef) -> Result<JsonValue, Error> {
|
||||
if v.is_null() {
|
||||
return Ok(JsonValue::Null);
|
||||
}
|
||||
|
||||
let res = match v.type_info().name() {
|
||||
"CHAR" | "VARCHAR" | "TEXT" | "NAME" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode() {
|
||||
JsonValue::String(v)
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"FLOAT4" | "FLOAT8" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<f64>() {
|
||||
JsonValue::from(v)
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"INT2" | "INT4" | "INT8" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<i64>() {
|
||||
JsonValue::Number(v.into())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"BOOL" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode() {
|
||||
JsonValue::Bool(v)
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"DATE" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<Date>() {
|
||||
JsonValue::String(v.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"TIME" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<Time>() {
|
||||
JsonValue::String(v.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"TIMESTAMP" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<PrimitiveDateTime>() {
|
||||
JsonValue::String(v.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"TIMESTAMPTZ" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<OffsetDateTime>() {
|
||||
JsonValue::String(v.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"JSON" | "JSONB" => ValueRef::to_owned(&v).try_decode().unwrap_or_default(),
|
||||
"BYTEA" => {
|
||||
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<Vec<u8>>() {
|
||||
JsonValue::Array(v.into_iter().map(|n| JsonValue::Number(n.into())).collect())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"VOID" => JsonValue::Null,
|
||||
_ => return Err(Error::UnsupportedDatatype(v.type_info().name().to_string())),
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
use serde_json::Value as JsonValue;
|
||||
use sqlx::{sqlite::SqliteValueRef, TypeInfo, Value, ValueRef};
|
||||
use time::{Date, PrimitiveDateTime, Time};
|
||||
|
||||
use crate::Error;
|
||||
|
||||
pub(crate) fn to_json(v: SqliteValueRef) -> Result<JsonValue, Error> {
|
||||
if v.is_null() {
|
||||
return Ok(JsonValue::Null);
|
||||
}
|
||||
|
||||
let res = match v.type_info().name() {
|
||||
"TEXT" => {
|
||||
if let Ok(v) = v.to_owned().try_decode() {
|
||||
JsonValue::String(v)
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"REAL" => {
|
||||
if let Ok(v) = v.to_owned().try_decode::<f64>() {
|
||||
JsonValue::from(v)
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"INTEGER" | "NUMERIC" => {
|
||||
if let Ok(v) = v.to_owned().try_decode::<i64>() {
|
||||
JsonValue::Number(v.into())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"BOOLEAN" => {
|
||||
if let Ok(v) = v.to_owned().try_decode() {
|
||||
JsonValue::Bool(v)
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"DATE" => {
|
||||
if let Ok(v) = v.to_owned().try_decode::<Date>() {
|
||||
JsonValue::String(v.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"TIME" => {
|
||||
if let Ok(v) = v.to_owned().try_decode::<Time>() {
|
||||
JsonValue::String(v.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"DATETIME" => {
|
||||
if let Ok(v) = v.to_owned().try_decode::<PrimitiveDateTime>() {
|
||||
JsonValue::String(v.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"BLOB" => {
|
||||
if let Ok(v) = v.to_owned().try_decode::<Vec<u8>>() {
|
||||
JsonValue::Array(v.into_iter().map(|n| JsonValue::Number(n.into())).collect())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"NULL" => JsonValue::Null,
|
||||
_ => return Err(Error::UnsupportedDatatype(v.type_info().name().to_string())),
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
+1
-10
@@ -14,15 +14,6 @@ compile_error!(
|
||||
"Database driver not defined. Please set the feature flag for the driver of your choice."
|
||||
);
|
||||
|
||||
#[cfg(any(
|
||||
all(feature = "sqlite", not(any(feature = "mysql", feature = "postgres"))),
|
||||
all(feature = "mysql", not(any(feature = "sqlite", feature = "postgres"))),
|
||||
all(feature = "postgres", not(any(feature = "sqlite", feature = "mysql"))),
|
||||
))]
|
||||
mod decode;
|
||||
mod plugin;
|
||||
#[cfg(any(
|
||||
all(feature = "sqlite", not(any(feature = "mysql", feature = "postgres"))),
|
||||
all(feature = "mysql", not(any(feature = "sqlite", feature = "postgres"))),
|
||||
all(feature = "postgres", not(any(feature = "sqlite", feature = "mysql"))),
|
||||
))]
|
||||
pub use plugin::*;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use futures::future::BoxFuture;
|
||||
use futures_core::future::BoxFuture;
|
||||
use serde::{ser::Serializer, Deserialize, Serialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
use sqlx::{
|
||||
@@ -10,7 +10,7 @@ use sqlx::{
|
||||
migrate::{
|
||||
MigrateDatabase, Migration as SqlxMigration, MigrationSource, MigrationType, Migrator,
|
||||
},
|
||||
Column, Pool, Row, TypeInfo, ValueRef,
|
||||
Column, Pool, Row,
|
||||
};
|
||||
use tauri::{
|
||||
command,
|
||||
@@ -250,58 +250,7 @@ async fn select(
|
||||
for (i, column) in row.columns().iter().enumerate() {
|
||||
let v = row.try_get_raw(i)?;
|
||||
|
||||
let v = if v.is_null() {
|
||||
JsonValue::Null
|
||||
} else {
|
||||
// TODO: postgresql's JSON type
|
||||
match v.type_info().name() {
|
||||
"VARCHAR" | "STRING" | "TEXT" | "TINYTEXT" | "LONGTEXT" | "NVARCHAR"
|
||||
| "BIGVARCHAR" | "CHAR" | "BIGCHAR" | "NCHAR" | "DATETIME" | "DATE"
|
||||
| "TIME" | "YEAR" | "TIMESTAMP" => {
|
||||
if let Ok(s) = row.try_get(i) {
|
||||
JsonValue::String(s)
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"BOOL" | "BOOLEAN" => {
|
||||
if let Ok(b) = row.try_get(i) {
|
||||
JsonValue::Bool(b)
|
||||
} else {
|
||||
let x: String = row.get(i);
|
||||
JsonValue::Bool(x.to_lowercase() == "true")
|
||||
}
|
||||
}
|
||||
"INT" | "NUMBER" | "INTEGER" | "BIGINT" | "INT2" | "INT4" | "INT8"
|
||||
| "NUMERIC" | "TINYINT" | "SMALLINT" | "MEDIUMINT" | "TINYINT UNSINGED"
|
||||
| "SMALLINT UNSINGED" | "INT UNSINGED" | "MEDIUMINT UNSINGED"
|
||||
| "BIGINT UNSINGED" => {
|
||||
if let Ok(n) = row.try_get::<i64, usize>(i) {
|
||||
JsonValue::Number(n.into())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"REAL" | "FLOAT" | "DOUBLE" | "FLOAT4" | "FLOAT8" => {
|
||||
if let Ok(n) = row.try_get::<f64, usize>(i) {
|
||||
JsonValue::from(n)
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
"BLOB" | "TINYBLOB" | "MEDIUMBLOB" | "LONGBLOB" | "BINARY" | "VARBINARY"
|
||||
| "BYTEA" => {
|
||||
if let Ok(n) = row.try_get::<Vec<u8>, usize>(i) {
|
||||
JsonValue::Array(
|
||||
n.into_iter().map(|n| JsonValue::Number(n.into())).collect(),
|
||||
)
|
||||
} else {
|
||||
JsonValue::Null
|
||||
}
|
||||
}
|
||||
_ => return Err(Error::UnsupportedDatatype(v.type_info().name().to_string())),
|
||||
}
|
||||
};
|
||||
let v = crate::decode::to_json(v)?;
|
||||
|
||||
value.insert(column.name().to_string(), v);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user