1use crate::{D1, type_info::D1TypeInfo, value::D1Value, error::D1Error};
4use sqlx_core::encode::{IsNull, Encode};
5use sqlx_core::decode::Decode;
6use sqlx_core::types::Type;
7use worker::{serde_wasm_bindgen, wasm_bindgen::JsValue};
8
9
10#[doc(hidden)]
15pub trait Compatible<X: TypeChecker>: Sized {
16 fn then(self) -> Self {self}
17}
18impl<X: TypeChecker, C: Compatible<X>> Compatible<Option<X>> for Option<C> {}
19
20#[doc(hidden)]
21pub trait TypeChecker {
22 const TYPE_INFO: D1TypeInfo;
23}
24impl<X: TypeChecker> TypeChecker for Option<X> {
25 const TYPE_INFO: D1TypeInfo = X::TYPE_INFO;
26}
27const _: () = {
28 impl TypeChecker for bool {
29 const TYPE_INFO: D1TypeInfo = D1TypeInfo::boolean();
30 }
31 impl TypeChecker for i64 {
32 const TYPE_INFO: D1TypeInfo = D1TypeInfo::integer();
33 }
34 impl TypeChecker for f64 {
35 const TYPE_INFO: D1TypeInfo = D1TypeInfo::real();
36 }
37 impl TypeChecker for String {
38 const TYPE_INFO: D1TypeInfo = D1TypeInfo::text();
39 }
40 impl TypeChecker for Vec<u8> {
41 const TYPE_INFO: D1TypeInfo = D1TypeInfo::blob();
42 }
43 #[cfg(feature = "chrono")]
44 impl TypeChecker for sqlx_core::types::chrono::NaiveDate {
45 const TYPE_INFO: D1TypeInfo = D1TypeInfo::date();
46 }
47 #[cfg(feature = "chrono")]
48 impl TypeChecker for sqlx_core::types::chrono::NaiveTime {
49 const TYPE_INFO: D1TypeInfo = D1TypeInfo::time();
50 }
51 #[cfg(feature = "chrono")]
52 impl TypeChecker for sqlx_core::types::chrono::NaiveDateTime {
53 const TYPE_INFO: D1TypeInfo = D1TypeInfo::datetime();
54 }
55};
56
57sqlx_core::impl_type_checking! {
59 crate::D1 {
60 bool,
62 i64,
64 f64,
66 String,
68 Vec<u8>,
70 #[cfg(feature = "chrono")]
72 sqlx_core::types::chrono::NaiveDate,
73 #[cfg(feature = "chrono")]
75 sqlx_core::types::chrono::NaiveTime,
76 #[cfg(feature = "chrono")]
78 sqlx_core::types::chrono::NaiveDateTime,
79 },
80 ParamChecking::Weak,
81 feature-types: _info => None,
82}
83
84
85impl<'q, E: Encode<'q, D1>> Encode<'q, D1> for Option<E> {
90 fn encode_by_ref(
91 &self,
92 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<'q>,
93 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
94 match self {
95 Some(e) => {
96 <E as Encode<'q, D1>>::encode_by_ref(e, buf)
97 }
98 None => {
99 buf.push(D1Value::null());
100 Ok(IsNull::Yes)
101 }
102 }
103 }
104}
105
106macro_rules! serialize {
107 ($q:lifetime) => {
108 fn encode_by_ref(
109 &self,
110 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<$q>,
111 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
112 buf.push(D1Value::from(serde_wasm_bindgen::to_value(self).map_err(D1Error::from_rust)?));
113 Ok(IsNull::No)
114 }
115 };
116}
117
118macro_rules! deserialize {
119 () => {
120 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
121 Ok(serde_wasm_bindgen::from_value(value.into()).map_err(D1Error::from_rust)?)
122 }
123 };
124}
125
126macro_rules! serde_wasm_bindgen {
127 ($T:ty where $type_cheker:ty) => {
128 impl Compatible<$type_cheker> for $T {}
129
130 impl Type<D1> for $T {
131 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
132 <$type_cheker as TypeChecker>::TYPE_INFO
133 }
134 }
135
136 impl<'q> Encode<'q, D1> for $T {
137 serialize!('q);
138 }
139
140 impl Decode<'_, D1> for $T {
141 deserialize!();
142 }
143 };
144}
145
146impl Type<D1> for [u8] {
147 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
148 D1TypeInfo::blob()
149 }
150}
151impl<'q> Encode<'q, D1> for &'q [u8] {
152 serialize!('q);
153}
154
155serde_wasm_bindgen!(Vec<u8> where Vec<u8>);
156serde_wasm_bindgen!(Box<[u8]> where Vec<u8>);
157
158serde_wasm_bindgen!(f32 where f64);
159serde_wasm_bindgen!(f64 where f64);
160
161serde_wasm_bindgen!(i8 where i64);
162serde_wasm_bindgen!(i16 where i64);
163serde_wasm_bindgen!(i32 where i64);
164serde_wasm_bindgen!(i64 where i64);
165serde_wasm_bindgen!(isize where i64);
166
167serde_wasm_bindgen!(u8 where i64);
168serde_wasm_bindgen!(u16 where i64);
169serde_wasm_bindgen!(u32 where i64);
170serde_wasm_bindgen!(u64 where i64);
171serde_wasm_bindgen!(usize where i64);
172
173impl Type<D1> for str {
174 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
175 D1TypeInfo::blob()
176 }
177}
178impl<'q> Encode<'q, D1> for &'q str {
179 serialize!('q);
180}
181
182serde_wasm_bindgen!(Box<str> where String);
183serde_wasm_bindgen!(String where String);
184serde_wasm_bindgen!(std::borrow::Cow<'_, str> where String);
185
186const _: () = {
188 impl Compatible<bool> for bool {}
189
190 impl Type<D1> for bool {
191 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
192 D1TypeInfo::boolean()
193 }
194 }
195
196 impl<'q> Encode<'q, D1> for bool {
197 fn encode_by_ref(
198 &self,
199 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<'q>,
200 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
201 buf.push(D1Value::from(JsValue::from_f64(if *self {1.} else {0.})));
202 Ok(IsNull::No)
203 }
204 }
205
206 impl Decode<'_, D1> for bool {
207 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
208 Ok((&*value).as_f64().is_some_and(|n| n != 0.))
209 }
210 }
211};
212
213const _: () = {
215 use sqlx_core::types::Text;
216
217 impl<C: TypeChecker, T> Compatible<C> for Text<T>
218 where
219 String: Compatible<C>,
220 {}
221
222 impl<T> Type<D1> for Text<T> {
223 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
224 <String as Type<D1>>::type_info()
225 }
226 }
227
228 impl<'q, T> Encode<'q, D1> for Text<T>
229 where
230 T: std::fmt::Display,
231 {
232 fn encode_by_ref(
233 &self,
234 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<'q>,
235 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
236 <String as Encode<'q, D1>>::encode(self.0.to_string(), buf)
237 }
238 }
239
240 impl<T> Decode<'_, D1> for Text<T>
241 where
242 T: std::str::FromStr,
243 sqlx_core::error::BoxDynError: From<<T as std::str::FromStr>::Err>,
244 {
245 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
246 Ok(Self(<String as Decode<D1>>::decode(value)?.parse()?))
247 }
248 }
249};
250
251#[cfg(feature = "json")]
252const _: () = {
254 use sqlx_core::types::Json;
255
256 impl<C: TypeChecker, T> Compatible<C> for Json<T>
257 where
258 String: Compatible<C>,
259 {}
260
261 impl<T> Type<D1> for Json<T> {
262 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
263 <String as Type<D1>>::type_info()
264 }
265 }
266
267 impl<'q, T> Encode<'q, D1> for Json<T>
268 where
269 T: serde::Serialize,
270 {
271 fn encode_by_ref(
272 &self,
273 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<'q>,
274 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
275 <String as Encode<'q, D1>>::encode(self.encode_to_string()?, buf)
276 }
277 }
278
279 impl<T> Decode<'_, D1> for Json<T>
280 where
281 T: for<'de> serde::Deserialize<'de>,
282 {
283 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
284 Self::decode_from_string(&<String as Decode<D1>>::decode(value)?)
285 }
286 }
287};
288
289#[cfg(feature = "uuid")]
290const _: () = {
292 use sqlx_core::types::uuid::{Uuid, fmt::{Hyphenated, Simple}};
293
294 impl<C: TypeChecker> Compatible<C> for Uuid
295 where
296 Vec<u8>: Compatible<C>,
297 {}
298 impl Type<D1> for Uuid {
299 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
300 <Vec<u8> as Type<D1>>::type_info()
301 }
302 }
303 impl<'q> Encode<'q, D1> for Uuid {
304 fn encode_by_ref(
305 &self,
306 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<'q>,
307 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
308 <Vec<u8> as Encode<'q, D1>>::encode(self.into_bytes().into(), buf)
309 }
310 }
311 impl Decode<'_, D1> for Uuid {
312 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
313 Ok(Uuid::from_slice(&<Vec<u8> as Decode<D1>>::decode(value)?).map_err(D1Error::from_rust)?)
314 }
315 }
316
317 impl<C: TypeChecker> Compatible<C> for Hyphenated
318 where
319 String: Compatible<C>,
320 {}
321 impl Type<D1> for Hyphenated {
322 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
323 <String as Type<D1>>::type_info()
324 }
325 }
326 impl<'q> Encode<'q, D1> for Hyphenated {
327 fn encode_by_ref(
328 &self,
329 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<'q>,
330 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
331 <String as Encode<'q, D1>>::encode(self.to_string(), buf)
332 }
333 }
334 impl Decode<'_, D1> for Hyphenated {
335 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
336 let uuid = Uuid::parse_str(&<String as Decode<D1>>::decode(value)?).map_err(D1Error::from_rust)?;
337 Ok(uuid.hyphenated())
338 }
339 }
340
341 impl<C: TypeChecker> Compatible<C> for Simple
342 where
343 String: Compatible<C>,
344 {}
345 impl Type<D1> for Simple {
346 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
347 <String as Type<D1>>::type_info()
348 }
349 }
350 impl<'q> Encode<'q, D1> for Simple {
351 fn encode_by_ref(
352 &self,
353 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<'q>,
354 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
355 <String as Encode<'q, D1>>::encode(self.to_string(), buf)
356 }
357 }
358 impl Decode<'_, D1> for Simple {
359 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
360 let uuid = Uuid::parse_str(&<String as Decode<D1>>::decode(value)?).map_err(D1Error::from_rust)?;
361 Ok(uuid.simple())
362 }
363 }
364};
365
366#[cfg(feature = "chrono")]
367const _: () = {
369 use sqlx_core::types::chrono::{
370 FixedOffset,
371 DateTime,
372 Local,
373 NaiveDate,
374 NaiveTime,
375 NaiveDateTime,
376 TimeZone,
377 Utc,
378 };
379
380 impl<C: TypeChecker, Tz: TimeZone> Compatible<C> for DateTime<Tz>
381 where
382 NaiveDateTime: Compatible<C>,
383 {}
384 impl<Tz: TimeZone> Type<D1> for DateTime<Tz> {
385 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
386 <NaiveDateTime as Type<D1>>::type_info()
387 }
388 fn compatible(ty: &<D1 as sqlx_core::database::Database>::TypeInfo) -> bool {
389 <NaiveDateTime as Type<D1>>::compatible(ty)
390 }
391 }
392 impl<Tz: TimeZone> Encode<'_, D1> for DateTime<Tz> {
393 fn encode_by_ref(
394 &self,
395 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<'_>,
396 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
397 let mut rfc3339 = self.to_rfc3339();
398 if rfc3339.ends_with('Z') {let _ = rfc3339.pop().unwrap();}
399 <String as Encode<'_, D1>>::encode(rfc3339, buf)
400 }
401 }
402 impl Decode<'_, D1> for DateTime<Utc> {
403 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
404 let fixed_offset = <DateTime<FixedOffset> as Decode<'_, D1>>::decode(value)?;
405 Ok(Utc.from_utc_datetime(&fixed_offset.naive_utc()))
406 }
407 }
408 impl Decode<'_, D1> for DateTime<Local> {
409 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
410 let fixed_offset = <DateTime<FixedOffset> as Decode<'_, D1>>::decode(value)?;
411 Ok(Local.from_utc_datetime(&fixed_offset.naive_utc()))
412 }
413 }
414 impl Decode<'_, D1> for DateTime<FixedOffset> {
415 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
416 return decode_or_none(&value).ok_or_else(|| From::from(format!(
417 "expected datetime but got unparsable `{value:?}`"
418 )));
419
420 fn decode_or_none(
421 value: &<D1 as sqlx_core::database::Database>::ValueRef<'_>
422 ) -> Option<DateTime<FixedOffset>> {
423 use {sqlx_core::value::ValueRef, crate::type_info::D1Type::*};
424
425 macro_rules! return_some_if_ok {
426 ($result:expr) => {
427 if let Ok(it) = $result {
428 return Some(it);
429 }
430 };
431 ($result:expr => |$v:ident| $conv:expr) => {
432 if let Ok(it) = $result {
433 return Some((|$v| $conv)(it));
434 }
435 };
436 }
437
438 match &**value.type_info() {
439 Text => {
440 let value = value.as_string()?;
441 return_some_if_ok!(DateTime::parse_from_rfc3339(&value));
442 for format in &[
443 "%F %T%.f",
444 "%F %R",
445 "%F %RZ",
446 "%F %R%:z",
447 "%F %T%.fZ",
448 "%F %T%.f%:z",
449 "%FT%R",
450 "%FT%RZ",
451 "%FT%R%:z",
452 "%FT%T%.f",
453 "%FT%T%.fZ",
454 "%FT%T%.f%:z",
455 ] {
456 return_some_if_ok!(DateTime::parse_from_str(&value, format));
457 return_some_if_ok!(NaiveDateTime::parse_from_str(&value, format)
458 => |it| FixedOffset::east_opt(0).unwrap().from_utc_datetime(&it));
459 }
460 None
461 }
462 Integer => {
463 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
464 let value = value.as_f64()? as i64;
465 FixedOffset::east_opt(0).unwrap().timestamp_opt(value, 0).single()
466 }
467 Real => {
468 let value = value.as_f64()?;
469
470 let epoch_in_julian_days = 2_440_587.5;
471 let seconds_in_day = 86400.0;
472 let timestamp = (value - epoch_in_julian_days) * seconds_in_day;
473
474 if !timestamp.is_finite() {
475 return None;
476 }
477
478 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
481 {
482 let seconds = timestamp.trunc() as i64;
483 let nanos = (timestamp.fract() * 1E9).abs() as u32;
484 FixedOffset::east_opt(0).unwrap().timestamp_opt(seconds, nanos).single()
485 }
486 }
487 _ => None
488 }
489 }
490 }
491 }
492
493 impl Compatible<NaiveDateTime> for NaiveDateTime {}
494 impl Compatible<String> for NaiveDateTime {}
495 impl Compatible<i64> for NaiveDateTime {}
496 impl Compatible<f64> for NaiveDateTime {}
497 impl Type<D1> for NaiveDateTime {
498 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
499 D1TypeInfo::datetime()
500 }
501 fn compatible(ty: &<D1 as sqlx_core::database::Database>::TypeInfo) -> bool {
502 use crate::type_info::D1Type::*;
503 matches!(**ty, Datetime | Text | Integer | Real)
504 }
505 }
506 impl Encode<'_, D1> for NaiveDateTime {
507 fn encode_by_ref(
508 &self,
509 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<'_>,
510 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
511 <String as Encode<'_, D1>>::encode(self.format("%F %T%.f").to_string(), buf)
512 }
513 }
514 impl Decode<'_, D1> for NaiveDateTime {
515 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
516 Ok(<DateTime<FixedOffset> as Decode<'_, D1>>::decode(value)?.naive_local())
517 }
518 }
519
520 impl Compatible<NaiveDate> for NaiveDate {}
521 impl Compatible<String> for NaiveDate {}
522 impl Type<D1> for NaiveDate {
523 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
524 D1TypeInfo::date()
525 }
526 fn compatible(ty: &<D1 as sqlx_core::database::Database>::TypeInfo) -> bool {
527 use crate::type_info::D1Type::*;
528 matches!(**ty, Date | Text)
529 }
530 }
531 impl Encode<'_, D1> for NaiveDate {
532 fn encode_by_ref(
533 &self,
534 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<'_>,
535 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
536 <String as Encode<'_, D1>>::encode(self.format("%F").to_string(), buf)
537 }
538 }
539 impl Decode<'_, D1> for NaiveDate {
540 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
541 let value = value.as_string().ok_or_else(|| format!("expected `chrono::NaiveDate` but got unparsable: {value:?}"))?;
542 Ok(NaiveDate::parse_from_str(&value, "%F")?)
543 }
544 }
545
546 impl Compatible<NaiveTime> for NaiveTime {}
547 impl Compatible<String> for NaiveTime {}
548 impl Type<D1> for NaiveTime {
549 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
550 D1TypeInfo::time()
551 }
552 fn compatible(ty: &<D1 as sqlx_core::database::Database>::TypeInfo) -> bool {
553 use crate::type_info::D1Type::*;
554 matches!(**ty, Time | Text)
555 }
556 }
557 impl Encode<'_, D1> for NaiveTime {
558 fn encode_by_ref(
559 &self,
560 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<'_>,
561 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
562 <String as Encode<'_, D1>>::encode(self.format("%T%.f").to_string(), buf)
563 }
564 }
565 impl Decode<'_, D1> for NaiveTime {
566 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
567 let value = value.as_string().ok_or_else(|| format!("expected `chrono::NaiveDate` but got unparsable: {value:?}"))?;
568 for format in [
569 "%T.f",
570 "%T%.f",
571 "%R",
572 "%RZ",
573 "%T%.fZ",
574 "%R%:z",
575 "%T%.f%:z",
576 ] {
577 if let Ok(t) = NaiveTime::parse_from_str(&value, format) {
578 return Ok(t);
579 }
580 }
581 Err(From::from(format!("invalid time: {value:?}")))
582 }
583 }
584};
585
586#[cfg(feature = "decimal")]
587const _: () = {
588 use rust_decimal::Decimal;
589 use std::str::FromStr;
590
591 impl<C: TypeChecker> Compatible<C> for Decimal where String: Compatible<C> {}
592
593 impl Type<D1> for Decimal {
594 fn type_info() -> <D1 as sqlx_core::database::Database>::TypeInfo {
595 <String as Type<D1>>::type_info()
597 }
598 fn compatible(ty: &<D1 as sqlx_core::database::Database>::TypeInfo) -> bool {
599 <String as Type<D1>>::compatible(ty)
600 }
601 }
602
603 impl<'q> Encode<'q, D1> for Decimal {
604 fn encode_by_ref(
605 &self,
606 buf: &mut <D1 as sqlx_core::database::Database>::ArgumentBuffer<'q>,
607 ) -> Result<IsNull, sqlx_core::error::BoxDynError> {
608 <String as Encode<'q, D1>>::encode(self.to_string(), buf)
609 }
610 }
611
612 impl Decode<'_, D1> for Decimal {
613 fn decode(value: <D1 as sqlx_core::database::Database>::ValueRef<'_>) -> Result<Self, sqlx_core::error::BoxDynError> {
614 let s = <String as Decode<D1>>::decode(value)?;
615 Ok(Decimal::from_str(&s)?)
616 }
617 }
618};