alibi_sqlx/
additional_fields.rs1use crate::pool::Exec;
4use crate::sql::Sql;
5use crate::value::{ColumnKind, SqlValue};
6use alibi_core::store::adapter::RawFieldValue;
7use alibi_core::{AuthError, AuthResult, utils::json::JsValue};
8
9pub fn raw_value(value: &JsValue) -> AuthResult<SqlValue> {
15 Ok(match RawFieldValue::from_js(value)? {
16 RawFieldValue::Null => SqlValue::Text(None),
17 RawFieldValue::Bool(value) => SqlValue::Bool(Some(value)),
18 RawFieldValue::Integer(value) => SqlValue::BigInt(Some(value)),
19 RawFieldValue::Number(value) => SqlValue::Double(Some(value)),
20 RawFieldValue::Text(value) => SqlValue::Text(Some(value)),
21 RawFieldValue::Json(value) => SqlValue::Json(Some(Box::new(value))),
22 })
23}
24
25pub(crate) async fn prepare_string_value(
26 exec: Exec<'_>,
27 value: SqlValue,
28) -> AuthResult<Option<String>> {
29 if let SqlValue::Text(value) | SqlValue::BpChar(value) = value {
30 return Ok(value);
31 }
32 if matches!(&value, SqlValue::Json(_)) {
33 return Err(AuthError::internal(
34 "object cannot bind to a scalar session field",
35 ));
36 }
37 let mut sql = Sql::with(exec.engine(), "SELECT CAST(");
38 sql.bind(value);
39 sql.push(" AS TEXT) AS value");
40 exec.fetch_scalar::<Option<String>>(sql)
41 .await?
42 .ok_or_else(|| AuthError::internal("session TEXT affinity returned no row"))
43}
44
45#[expect(
46 clippy::as_conversions,
47 clippy::cast_possible_truncation,
48 clippy::cast_precision_loss,
49 reason = "JavaScript-compatible numbers deliberately retain IEEE754 rounding and guarded integer coercion at the wire or adapter boundary"
50)]
51pub(crate) async fn prepare_value(
52 exec: Exec<'_>,
53 kind: ColumnKind,
54 value: SqlValue,
55) -> AuthResult<SqlValue> {
56 let backend = exec.engine();
57 match kind {
58 ColumnKind::Text | ColumnKind::BpChar => {
59 Ok(SqlValue::Text(prepare_string_value(exec, value).await?))
60 }
61 ColumnKind::Json => {
62 let json = match value {
63 SqlValue::Bool(Some(value)) => serde_json::json!(value),
64 SqlValue::BigInt(Some(value)) => serde_json::json!(value),
65 SqlValue::Double(Some(value)) => serde_json::json!(value),
66 SqlValue::Text(Some(value)) => serde_json::json!(value),
67 SqlValue::Json(Some(value)) => *value,
68 SqlValue::Bool(None)
69 | SqlValue::Int(_)
70 | SqlValue::BigInt(None)
71 | SqlValue::Float(_)
72 | SqlValue::Double(None)
73 | SqlValue::Text(None)
74 | SqlValue::BpChar(_)
75 | SqlValue::Bytes(_)
76 | SqlValue::Json(None)
77 | SqlValue::Timestamp(_)
78 | SqlValue::NaiveTimestamp(_)
79 | SqlValue::Uuid(_) => serde_json::Value::Null,
80 };
81 Ok(crate::value::SqlxValue::into_sql_value(
82 crate::JsonMetadata::for_backend(json, backend)?,
83 ))
84 }
85 ColumnKind::Double => Ok(match value {
86 SqlValue::BigInt(Some(value)) => SqlValue::Double(Some(value as f64)),
87 SqlValue::Text(None) => SqlValue::Double(None),
88 other => other,
89 }),
90 ColumnKind::Float => Ok(match value {
91 SqlValue::BigInt(Some(value)) => SqlValue::Float(Some(value as f32)),
92 SqlValue::Double(Some(value)) => SqlValue::Float(Some(value as f32)),
93 SqlValue::Text(None) => SqlValue::Float(None),
94 other => other,
95 }),
96 ColumnKind::Boolean => Ok(match value {
97 SqlValue::Text(None) => SqlValue::Bool(None),
98 other => other,
99 }),
100 ColumnKind::Int | ColumnKind::BigInt | ColumnKind::Other | ColumnKind::NaiveTimestamp => {
101 Ok(value)
102 }
103 }
104}