[go: up one dir, main page]

worker/d1/
mod.rs

1use std::fmt::Display;
2use std::fmt::Formatter;
3use std::iter::{once, Once};
4use std::ops::Deref;
5use std::result::Result as StdResult;
6
7use js_sys::Array;
8use js_sys::ArrayBuffer;
9use js_sys::JsString;
10use js_sys::Uint8Array;
11use serde::Deserialize;
12use wasm_bindgen::{JsCast, JsValue};
13use wasm_bindgen_futures::JsFuture;
14use worker_sys::types::D1Database as D1DatabaseSys;
15use worker_sys::types::D1ExecResult;
16use worker_sys::types::D1PreparedStatement as D1PreparedStatementSys;
17use worker_sys::types::D1Result as D1ResultSys;
18
19use crate::env::EnvBinding;
20use crate::Error;
21use crate::Result;
22
23pub use serde_wasm_bindgen;
24
25pub mod macros;
26
27// A D1 Database.
28#[derive(Debug)]
29pub struct D1Database(D1DatabaseSys);
30
31unsafe impl Sync for D1Database {}
32unsafe impl Send for D1Database {}
33
34impl D1Database {
35    /// Prepare a query statement from a query string.
36    pub fn prepare<T: Into<String>>(&self, query: T) -> D1PreparedStatement {
37        self.0.prepare(&query.into()).unwrap().into()
38    }
39
40    /// Dump the data in the database to a `Vec`.
41    pub async fn dump(&self) -> Result<Vec<u8>> {
42        let result = JsFuture::from(self.0.dump()?).await;
43        let array_buffer = cast_to_d1_error(result)?;
44        let array_buffer = array_buffer.dyn_into::<ArrayBuffer>()?;
45        let array = Uint8Array::new(&array_buffer);
46        Ok(array.to_vec())
47    }
48
49    /// Batch execute one or more statements against the database.
50    ///
51    /// Returns the results in the same order as the provided statements.
52    pub async fn batch(&self, statements: Vec<D1PreparedStatement>) -> Result<Vec<D1Result>> {
53        let statements = statements.into_iter().map(|s| s.0).collect::<Array>();
54        let results = JsFuture::from(self.0.batch(statements)?).await;
55        let results = cast_to_d1_error(results)?;
56        let results = results.dyn_into::<Array>()?;
57        let mut vec = Vec::with_capacity(results.length() as usize);
58        for result in results.iter() {
59            let result = result.unchecked_into::<D1ResultSys>();
60            vec.push(D1Result(result));
61        }
62        Ok(vec)
63    }
64
65    /// Execute one or more queries directly against the database.
66    ///
67    /// The input can be one or multiple queries separated by `\n`.
68    ///
69    /// # Considerations
70    ///
71    /// This method can have poorer performance (prepared statements can be reused
72    /// in some cases) and, more importantly, is less safe. Only use this
73    /// method for maintenance and one-shot tasks (example: migration jobs).
74    ///
75    /// If an error occurs, an exception is thrown with the query and error
76    /// messages, execution stops and further statements are not executed.
77    pub async fn exec(&self, query: &str) -> Result<D1ExecResult> {
78        let result = JsFuture::from(self.0.exec(query)?).await;
79        let result = cast_to_d1_error(result)?;
80        Ok(result.into())
81    }
82}
83
84impl EnvBinding for D1Database {
85    const TYPE_NAME: &'static str = "D1Database";
86
87    // Workaround for Miniflare D1 Beta
88    fn get(val: JsValue) -> Result<Self> {
89        let obj = js_sys::Object::from(val);
90        if obj.constructor().name() == Self::TYPE_NAME || obj.constructor().name() == "BetaDatabase"
91        {
92            Ok(obj.unchecked_into())
93        } else {
94            Err(format!(
95                "Binding cannot be cast to the type {} from {}",
96                Self::TYPE_NAME,
97                obj.constructor().name()
98            )
99            .into())
100        }
101    }
102}
103
104impl JsCast for D1Database {
105    fn instanceof(val: &JsValue) -> bool {
106        val.is_instance_of::<D1DatabaseSys>()
107    }
108
109    fn unchecked_from_js(val: JsValue) -> Self {
110        Self(val.into())
111    }
112
113    fn unchecked_from_js_ref(val: &JsValue) -> &Self {
114        unsafe { &*(val as *const JsValue as *const Self) }
115    }
116}
117
118impl From<D1Database> for JsValue {
119    fn from(database: D1Database) -> Self {
120        JsValue::from(database.0)
121    }
122}
123
124impl AsRef<JsValue> for D1Database {
125    fn as_ref(&self) -> &JsValue {
126        &self.0
127    }
128}
129
130impl From<D1DatabaseSys> for D1Database {
131    fn from(inner: D1DatabaseSys) -> Self {
132        Self(inner)
133    }
134}
135
136/// Possible argument types that can be bound to [`D1PreparedStatement`]
137/// See https://developers.cloudflare.com/d1/build-with-d1/d1-client-api/#type-conversion
138#[derive(Debug)]
139pub enum D1Type<'a> {
140    Null,
141    Real(f64),
142    // I believe JS always casts to float. Documentation states it can accept up to 53 bits of signed precision
143    // so I went with i32 here. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#number_type
144    // D1 does not support `BigInt`
145    Integer(i32),
146    Text(&'a str),
147    Boolean(bool),
148    Blob(&'a [u8]),
149}
150
151/// A pre-computed argument for `bind_refs`.
152///
153/// Arguments must be converted to `JsValue` when bound. If you plan to
154/// re-use the same argument multiple times, consider using a `D1PreparedArgument`
155/// which does this once on construction.
156#[derive(Debug)]
157pub struct D1PreparedArgument<'a> {
158    value: &'a D1Type<'a>,
159    js_value: JsValue,
160}
161
162impl<'a> D1PreparedArgument<'a> {
163    pub fn new(value: &'a D1Type) -> D1PreparedArgument<'a> {
164        Self {
165            value,
166            js_value: value.into(),
167        }
168    }
169}
170
171impl<'a> From<&'a D1Type<'a>> for JsValue {
172    fn from(value: &'a D1Type<'a>) -> Self {
173        match *value {
174            D1Type::Null => JsValue::null(),
175            D1Type::Real(f) => JsValue::from_f64(f),
176            D1Type::Integer(i) => JsValue::from_f64(i as f64),
177            D1Type::Text(s) => JsValue::from_str(s),
178            D1Type::Boolean(b) => JsValue::from_bool(b),
179            D1Type::Blob(a) => serde_wasm_bindgen::to_value(a).unwrap(),
180        }
181    }
182}
183
184impl<'a> Deref for D1PreparedArgument<'a> {
185    type Target = D1Type<'a>;
186    fn deref(&self) -> &Self::Target {
187        self.value
188    }
189}
190
191impl<'a> IntoIterator for &'a D1Type<'a> {
192    type Item = &'a D1Type<'a>;
193    type IntoIter = Once<&'a D1Type<'a>>;
194    /// Allows a single &D1Type to be passed to `bind_refs`, without placing it in an array.
195    fn into_iter(self) -> Self::IntoIter {
196        once(self)
197    }
198}
199
200impl<'a> IntoIterator for &'a D1PreparedArgument<'a> {
201    type Item = &'a D1PreparedArgument<'a>;
202    type IntoIter = Once<&'a D1PreparedArgument<'a>>;
203    /// Allows a single &D1PreparedArgument to be passed to `bind_refs`, without placing it in an array.
204    fn into_iter(self) -> Self::IntoIter {
205        once(self)
206    }
207}
208
209pub trait D1Argument {
210    fn js_value(&self) -> impl AsRef<JsValue>;
211}
212
213impl D1Argument for D1Type<'_> {
214    fn js_value(&self) -> impl AsRef<JsValue> {
215        Into::<JsValue>::into(self)
216    }
217}
218
219impl D1Argument for D1PreparedArgument<'_> {
220    fn js_value(&self) -> impl AsRef<JsValue> {
221        &self.js_value
222    }
223}
224
225// A D1 prepared query statement.
226#[derive(Debug, Clone)]
227pub struct D1PreparedStatement(D1PreparedStatementSys);
228
229impl D1PreparedStatement {
230    /// Bind one or more parameters to the statement.
231    /// Consumes the old statement and returns a new statement with the bound parameters.
232    ///
233    /// D1 follows the SQLite convention for prepared statements parameter binding.
234    ///
235    /// # Considerations
236    ///
237    /// Supports Ordered (?NNNN) and Anonymous (?) parameters - named parameters are currently not supported.
238    ///
239    pub fn bind(self, values: &[JsValue]) -> Result<Self> {
240        let array: Array = values.iter().collect::<Array>();
241
242        match self.0.bind(array) {
243            Ok(stmt) => Ok(D1PreparedStatement(stmt)),
244            Err(err) => Err(Error::from(err)),
245        }
246    }
247
248    /// Bind one or more parameters to the statement.
249    /// Returns a new statement with the bound parameters, leaving the old statement available for reuse.
250    pub fn bind_refs<'a, T: IntoIterator<Item = &'a U>, U: D1Argument + 'a>(
251        &self,
252        values: T,
253    ) -> Result<Self> {
254        let array: Array = values.into_iter().map(|t| t.js_value()).collect::<Array>();
255
256        match self.0.bind(array) {
257            Ok(stmt) => Ok(D1PreparedStatement(stmt)),
258            Err(err) => Err(Error::from(err)),
259        }
260    }
261
262    /// Bind a batch of parameter values, returning a batch of prepared statements.
263    /// Result can be passed to [`D1Database::batch`] to execute the statements.
264    pub fn batch_bind<
265        'a,
266        U: IntoIterator<Item = &'a V> + 'a,
267        T: IntoIterator<Item = U> + 'a,
268        V: D1Argument + 'a,
269    >(
270        &self,
271        values: T,
272    ) -> Result<Vec<Self>> {
273        values
274            .into_iter()
275            .map(|batch| self.bind_refs(batch))
276            .collect()
277    }
278
279    /// Return the first row of results.
280    ///
281    /// If `col_name` is `Some`, returns that single value, otherwise returns the entire object.
282    ///
283    /// If the query returns no rows, then this will return `None`.
284    ///
285    /// If the query returns rows, but column does not exist, then this will return an `Err`.
286    pub async fn first<T>(&self, col_name: Option<&str>) -> Result<Option<T>>
287    where
288        T: for<'a> Deserialize<'a>,
289    {
290        let result = JsFuture::from(self.0.first(col_name)?).await;
291        let js_value = cast_to_d1_error(result)?;
292        let value = serde_wasm_bindgen::from_value(js_value)?;
293        Ok(value)
294    }
295
296    /// Executes a query against the database but only return metadata.
297    pub async fn run(&self) -> Result<D1Result> {
298        let result = JsFuture::from(self.0.run()?).await;
299        let result = cast_to_d1_error(result)?;
300        Ok(D1Result(result.into()))
301    }
302
303    /// Executes a query against the database and returns all rows and metadata.
304    pub async fn all(&self) -> Result<D1Result> {
305        let result = JsFuture::from(self.0.all()?).await?;
306        Ok(D1Result(result.into()))
307    }
308
309    /// Executes a query against the database and returns a `Vec` of rows instead of objects.
310    pub async fn raw<T>(&self) -> Result<Vec<Vec<T>>>
311    where
312        T: for<'a> Deserialize<'a>,
313    {
314        let result = JsFuture::from(self.0.raw()?).await;
315        let result = cast_to_d1_error(result)?;
316        let result = result.dyn_into::<Array>()?;
317        let mut vec = Vec::with_capacity(result.length() as usize);
318        for value in result.iter() {
319            let value = serde_wasm_bindgen::from_value(value)?;
320            vec.push(value);
321        }
322        Ok(vec)
323    }
324
325    /// Executes a query against the database and returns a `Vec` of JsValues.
326    pub async fn raw_js_value(&self) -> Result<Vec<JsValue>> {
327        let result = JsFuture::from(self.0.raw()?).await;
328        let result = cast_to_d1_error(result)?;
329        let array = result.dyn_into::<Array>()?;
330
331        Ok(array.to_vec())
332    }
333
334    /// Returns the inner JsValue bindings object.
335    pub fn inner(&self) -> &D1PreparedStatementSys {
336        &self.0
337    }
338}
339
340impl From<D1PreparedStatementSys> for D1PreparedStatement {
341    fn from(inner: D1PreparedStatementSys) -> Self {
342        Self(inner)
343    }
344}
345
346// The result of a D1 query execution.
347#[derive(Debug)]
348pub struct D1Result(D1ResultSys);
349
350// The meta object of D1 result.
351#[derive(Debug, Clone, Deserialize)]
352pub struct D1ResultMeta {
353    pub changed_db: Option<bool>,
354    pub changes: Option<usize>,
355    pub duration: Option<f64>,
356    pub last_row_id: Option<i64>,
357    pub rows_read: Option<usize>,
358    pub rows_written: Option<usize>,
359    pub size_after: Option<usize>,
360}
361
362impl D1Result {
363    /// Returns `true` if the result indicates a success, otherwise `false`.
364    pub fn success(&self) -> bool {
365        self.0.success().unwrap()
366    }
367
368    /// Return the error contained in this result.
369    ///
370    /// Returns `None` if the result indicates a success.
371    pub fn error(&self) -> Option<String> {
372        self.0.error().unwrap()
373    }
374
375    /// Retrieve the collection of result objects, or an `Err` if an error occurred.
376    pub fn results<T>(&self) -> Result<Vec<T>>
377    where
378        T: for<'a> Deserialize<'a>,
379    {
380        if let Some(results) = self.0.results()? {
381            let mut vec = Vec::with_capacity(results.length() as usize);
382            for result in results.iter() {
383                let result = serde_wasm_bindgen::from_value(result).unwrap();
384                vec.push(result);
385            }
386            Ok(vec)
387        } else {
388            Ok(Vec::new())
389        }
390    }
391
392    /// Return the meta data in this result.
393    ///
394    /// Returns `None` if `meta` field is not populated.
395    pub fn meta(&self) -> Result<Option<D1ResultMeta>> {
396        if let Ok(meta) = self.0.meta() {
397            let meta: D1ResultMeta = serde_wasm_bindgen::from_value(meta.into())?;
398            Ok(Some(meta))
399        } else {
400            Ok(None)
401        }
402    }
403}
404
405#[derive(Clone)]
406pub struct D1Error {
407    inner: js_sys::Error,
408}
409
410impl D1Error {
411    /// Gets the cause of the error specific to D1.
412    pub fn cause(&self) -> String {
413        if let Ok(cause) = self.inner.cause().dyn_into::<js_sys::Error>() {
414            cause.message().into()
415        } else {
416            "unknown error".into()
417        }
418    }
419}
420
421impl std::fmt::Debug for D1Error {
422    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
423        let cause = self.inner.cause();
424
425        f.debug_struct("D1Error").field("cause", &cause).finish()
426    }
427}
428
429impl Display for D1Error {
430    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
431        let cause = self.inner.cause();
432        let cause = JsString::from(cause);
433        write!(f, "{cause}")
434    }
435}
436
437impl AsRef<js_sys::Error> for D1Error {
438    fn as_ref(&self) -> &js_sys::Error {
439        &self.inner
440    }
441}
442
443impl AsRef<JsValue> for D1Error {
444    fn as_ref(&self) -> &JsValue {
445        &self.inner
446    }
447}
448
449fn cast_to_d1_error<T>(result: StdResult<T, JsValue>) -> StdResult<T, crate::Error> {
450    let err = match result {
451        Ok(value) => return Ok(value),
452        Err(err) => err,
453    };
454
455    let err: JsValue = match err.dyn_into::<js_sys::Error>() {
456        Ok(err) => {
457            let message: String = err.message().into();
458
459            if message.starts_with("D1") {
460                return Err(D1Error { inner: err }.into());
461            };
462            err.into()
463        }
464        Err(err) => err,
465    };
466
467    Err(err.into())
468}