From 8e849a67d65d45a28d23a69506c9d8f366a48fd1 Mon Sep 17 00:00:00 2001 From: Bo Lu Date: Wed, 12 Jun 2024 11:08:17 +1000 Subject: [PATCH] add data modify to paddle fdw --- wasm-wrappers/fdw/paddle_fdw/src/bindings.rs | 391 +++++++++++++++---- wasm-wrappers/fdw/paddle_fdw/src/lib.rs | 82 +++- wasm-wrappers/wit/http.wit | 4 + wasm-wrappers/wit/time.wit | 3 + wrappers/src/fdw/wasm_fdw/host/http.rs | 58 +-- wrappers/src/fdw/wasm_fdw/host/time.rs | 6 + 6 files changed, 427 insertions(+), 117 deletions(-) diff --git a/wasm-wrappers/fdw/paddle_fdw/src/bindings.rs b/wasm-wrappers/fdw/paddle_fdw/src/bindings.rs index 1b0e02f5..c3e8bb29 100644 --- a/wasm-wrappers/fdw/paddle_fdw/src/bindings.rs +++ b/wasm-wrappers/fdw/paddle_fdw/src/bindings.rs @@ -17,12 +17,18 @@ pub mod supabase { pub enum Method { Get, Post, + Put, + Patch, + Delete, } impl ::core::fmt::Debug for Method { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { Method::Get => f.debug_tuple("Method::Get").finish(), Method::Post => f.debug_tuple("Method::Post").finish(), + Method::Put => f.debug_tuple("Method::Put").finish(), + Method::Patch => f.debug_tuple("Method::Patch").finish(), + Method::Delete => f.debug_tuple("Method::Delete").finish(), } } } @@ -77,6 +83,9 @@ pub mod supabase { let result1 = match method0 { Method::Get => 0i32, Method::Post => 1i32, + Method::Put => 2i32, + Method::Patch => 3i32, + Method::Delete => 4i32, }; let vec2 = url0; let ptr2 = vec2.as_ptr().cast::(); @@ -234,6 +243,9 @@ pub mod supabase { let result1 = match method0 { Method::Get => 0i32, Method::Post => 1i32, + Method::Put => 2i32, + Method::Patch => 3i32, + Method::Delete => 4i32, }; let vec2 = url0; let ptr2 = vec2.as_ptr().cast::(); @@ -391,6 +403,9 @@ pub mod supabase { let result1 = match method0 { Method::Get => 0i32, Method::Post => 1i32, + Method::Put => 2i32, + Method::Patch => 3i32, + Method::Delete => 4i32, }; let vec2 = url0; let ptr2 = vec2.as_ptr().cast::(); @@ -534,6 +549,166 @@ pub mod supabase { } } #[allow(unused_unsafe, clippy::all)] + pub fn patch(req: &Request) -> HttpResult { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 32]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 32]); + let Request { + method: method0, + url: url0, + headers: headers0, + body: body0, + } = req; + let result1 = match method0 { + Method::Get => 0i32, + Method::Post => 1i32, + Method::Put => 2i32, + Method::Patch => 3i32, + Method::Delete => 4i32, + }; + let vec2 = url0; + let ptr2 = vec2.as_ptr().cast::(); + let len2 = vec2.len(); + let vec6 = headers0; + let len6 = vec6.len(); + let layout6 = _rt::alloc::Layout::from_size_align_unchecked(vec6.len() * 16, 4); + let result6 = if layout6.size() != 0 { + let ptr = _rt::alloc::alloc(layout6).cast::(); + if ptr.is_null() { + _rt::alloc::handle_alloc_error(layout6); + } + ptr + } else { + { + ::core::ptr::null_mut() + } + }; + for (i, e) in vec6.into_iter().enumerate() { + let base = result6.add(i * 16); + { + let (t3_0, t3_1) = e; + let vec4 = t3_0; + let ptr4 = vec4.as_ptr().cast::(); + let len4 = vec4.len(); + *base.add(4).cast::() = len4; + *base.add(0).cast::<*mut u8>() = ptr4.cast_mut(); + let vec5 = t3_1; + let ptr5 = vec5.as_ptr().cast::(); + let len5 = vec5.len(); + *base.add(12).cast::() = len5; + *base.add(8).cast::<*mut u8>() = ptr5.cast_mut(); + } + } + let vec7 = body0; + let ptr7 = vec7.as_ptr().cast::(); + let len7 = vec7.len(); + let ptr8 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "supabase:wrappers/http@0.1.0")] + extern "C" { + #[link_name = "patch"] + fn wit_import( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + ); + } + + #[cfg(not(target_arch = "wasm32"))] + fn wit_import( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import( + result1, + ptr2.cast_mut(), + len2, + result6, + len6, + ptr7.cast_mut(), + len7, + ptr8, + ); + let l9 = i32::from(*ptr8.add(0).cast::()); + if layout6.size() != 0 { + _rt::alloc::dealloc(result6.cast(), layout6); + } + match l9 { + 0 => { + let e = { + let l10 = *ptr8.add(4).cast::<*mut u8>(); + let l11 = *ptr8.add(8).cast::(); + let len12 = l11; + let bytes12 = _rt::Vec::from_raw_parts(l10.cast(), len12, len12); + let l13 = i32::from(*ptr8.add(12).cast::()); + let l14 = *ptr8.add(16).cast::<*mut u8>(); + let l15 = *ptr8.add(20).cast::(); + let base22 = l14; + let len22 = l15; + let mut result22 = _rt::Vec::with_capacity(len22); + for i in 0..len22 { + let base = base22.add(i * 16); + let e22 = { + let l16 = *base.add(0).cast::<*mut u8>(); + let l17 = *base.add(4).cast::(); + let len18 = l17; + let bytes18 = + _rt::Vec::from_raw_parts(l16.cast(), len18, len18); + let l19 = *base.add(8).cast::<*mut u8>(); + let l20 = *base.add(12).cast::(); + let len21 = l20; + let bytes21 = + _rt::Vec::from_raw_parts(l19.cast(), len21, len21); + + (_rt::string_lift(bytes18), _rt::string_lift(bytes21)) + }; + result22.push(e22); + } + _rt::cabi_dealloc(base22, len22 * 16, 4); + let l23 = *ptr8.add(24).cast::<*mut u8>(); + let l24 = *ptr8.add(28).cast::(); + let len25 = l24; + let bytes25 = _rt::Vec::from_raw_parts(l23.cast(), len25, len25); + + Response { + url: _rt::string_lift(bytes12), + status_code: l13 as u16, + headers: result22, + body: _rt::string_lift(bytes25), + } + }; + Ok(e) + } + 1 => { + let e = { + let l26 = *ptr8.add(4).cast::<*mut u8>(); + let l27 = *ptr8.add(8).cast::(); + let len28 = l27; + let bytes28 = _rt::Vec::from_raw_parts(l26.cast(), len28, len28); + + _rt::string_lift(bytes28) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + } + } + #[allow(unused_unsafe, clippy::all)] pub fn delete(req: &Request) -> HttpResult { unsafe { #[repr(align(4))] @@ -548,6 +723,9 @@ pub mod supabase { let result1 = match method0 { Method::Get => 0i32, Method::Post => 1i32, + Method::Put => 2i32, + Method::Patch => 3i32, + Method::Delete => 4i32, }; let vec2 = url0; let ptr2 = vec2.as_ptr().cast::(); @@ -1191,6 +1369,54 @@ pub mod supabase { } } #[allow(unused_unsafe, clippy::all)] + /// convert microseconds since Unix epoch to RFC3339 string + pub fn epoch_ms_to_rfc3339(msecs: i64) -> Result<_rt::String, TimeError> { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 12]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 12]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "supabase:wrappers/time@0.1.0")] + extern "C" { + #[link_name = "epoch-ms-to-rfc3339"] + fn wit_import(_: i64, _: *mut u8); + } + + #[cfg(not(target_arch = "wasm32"))] + fn wit_import(_: i64, _: *mut u8) { + unreachable!() + } + wit_import(_rt::as_i64(&msecs), ptr0); + let l1 = i32::from(*ptr0.add(0).cast::()); + match l1 { + 0 => { + let e = { + let l2 = *ptr0.add(4).cast::<*mut u8>(); + let l3 = *ptr0.add(8).cast::(); + let len4 = l3; + let bytes4 = _rt::Vec::from_raw_parts(l2.cast(), len4, len4); + + _rt::string_lift(bytes4) + }; + Ok(e) + } + 1 => { + let e = { + let l5 = *ptr0.add(4).cast::<*mut u8>(); + let l6 = *ptr0.add(8).cast::(); + let len7 = l6; + let bytes7 = _rt::Vec::from_raw_parts(l5.cast(), len7, len7); + + _rt::string_lift(bytes7) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + } + } + #[allow(unused_unsafe, clippy::all)] /// sleep for a while pub fn sleep(millis: u64) { unsafe { @@ -4637,88 +4863,89 @@ pub(crate) use __export_paddle_impl as export; #[cfg(target_arch = "wasm32")] #[link_section = "component-type:wit-bindgen:0.24.0:paddle:encoded world"] #[doc(hidden)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 3581] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x80\x1b\x01A\x02\x01\ -A\x13\x01B\x15\x01o\x02ss\x01p\0\x04\0\x07headers\x03\0\x01\x01q\x02\x03get\0\0\x04\ -post\0\0\x04\0\x06method\x03\0\x03\x01r\x04\x06method\x04\x03urls\x07headers\x02\ -\x04bodys\x04\0\x07request\x03\0\x05\x01r\x04\x03urls\x0bstatus-code{\x07headers\ -\x02\x04bodys\x04\0\x08response\x03\0\x07\x01s\x04\0\x0ahttp-error\x03\0\x09\x01\ -j\x01\x08\x01\x0a\x04\0\x0bhttp-result\x03\0\x0b\x01@\x01\x03req\x06\0\x0c\x04\0\ -\x03get\x01\x0d\x04\0\x04post\x01\x0d\x04\0\x03put\x01\x0d\x04\0\x06delete\x01\x0d\ -\x01j\0\x01\x0a\x01@\x01\x04resp\x08\0\x0e\x04\0\x10error-for-status\x01\x0f\x03\ -\x01\x1csupabase:wrappers/http@0.1.0\x05\0\x01B\x08\x01s\x04\0\x09jwt-error\x03\0\ -\0\x01j\x01s\x01\x01\x04\0\x0ajwt-result\x03\0\x02\x01o\x02ss\x01p\x04\x01@\x04\x07\ -payload\x05\x04algos\x03keys\x09ttl-hoursy\0\x03\x04\0\x06encode\x01\x06\x03\x01\ -\x1bsupabase:wrappers/jwt@0.1.0\x05\x01\x01B\x0a\x01ks\x04\0\x08metadata\x03\0\0\ -\x01q\x05\x0ccreate-times\0\0\x07rows-in\0\0\x08rows-out\0\0\x08bytes-in\0\0\x09\ -bytes-out\0\0\x04\0\x06metric\x03\0\x02\x01@\x03\x08fdw-names\x06metric\x03\x03i\ -ncx\x01\0\x04\0\x09inc-stats\x01\x04\x01@\x01\x08fdw-names\0\x01\x04\0\x0cget-me\ -tadata\x01\x05\x01@\x02\x08fdw-names\x08metadata\x01\x01\0\x04\0\x0cset-metadata\ -\x01\x06\x03\x01\x1dsupabase:wrappers/stats@0.1.0\x05\x02\x01B\x0c\x01s\x04\0\x0a\ -time-error\x03\0\0\x01j\x01x\x01\x01\x04\0\x0btime-result\x03\0\x02\x01@\0\0x\x04\ -\0\x0aepoch-secs\x01\x04\x01@\x01\x01ss\0\x03\x04\0\x12parse-from-rfc3339\x01\x05\ -\x01@\x02\x01ss\x03fmts\0\x03\x04\0\x0eparse-from-str\x01\x06\x01@\x01\x06millis\ -w\x01\0\x04\0\x05sleep\x01\x07\x03\x01\x1csupabase:wrappers/time@0.1.0\x05\x03\x01\ -Br\x01q\x0d\x04bool\0\0\x02i8\0\0\x03i16\0\0\x03f32\0\0\x03i32\0\0\x03f64\0\0\x03\ -i64\0\0\x07numeric\0\0\x06string\0\0\x04date\0\0\x09timestamp\0\0\x0btimestamptz\ -\0\0\x04json\0\0\x04\0\x08type-oid\x03\0\0\x01q\x0d\x04bool\x01\x7f\0\x02i8\x01~\ -\0\x03i16\x01|\0\x03f32\x01v\0\x03i32\x01z\0\x03f64\x01u\0\x03i64\x01x\0\x07nume\ -ric\x01u\0\x06string\x01s\0\x04date\x01x\0\x09timestamp\x01x\0\x0btimestamptz\x01\ -x\0\x04json\x01s\0\x04\0\x04cell\x03\0\x02\x04\0\x03row\x03\x01\x04\0\x06column\x03\ -\x01\x01p\x03\x01q\x02\x04cell\x01\x03\0\x05array\x01\x06\0\x04\0\x05value\x03\0\ -\x07\x01r\x02\x02idy\x08type-oidy\x04\0\x05param\x03\0\x09\x04\0\x04qual\x03\x01\ -\x04\0\x04sort\x03\x01\x04\0\x05limit\x03\x01\x01q\x02\x06server\0\0\x05table\0\0\ -\x04\0\x0coptions-type\x03\0\x0e\x04\0\x07options\x03\x01\x04\0\x07context\x03\x01\ -\x01s\x04\0\x09fdw-error\x03\0\x12\x01j\0\x01\x13\x04\0\x0afdw-result\x03\0\x14\x01\ -i\x04\x01@\0\0\x16\x04\0\x10[constructor]row\x01\x17\x01h\x04\x01ps\x01@\x01\x04\ -self\x18\0\x19\x04\0\x10[method]row.cols\x01\x1a\x01k\x03\x01p\x1b\x01@\x01\x04s\ -elf\x18\0\x1c\x04\0\x11[method]row.cells\x01\x1d\x01@\x02\x04self\x18\x04cell\x1b\ -\x01\0\x04\0\x10[method]row.push\x01\x1e\x01i\x05\x01@\x01\x05indexy\0\x1f\x04\0\ -\x13[constructor]column\x01\x20\x01h\x05\x01@\x01\x04self!\0s\x04\0\x13[method]c\ -olumn.name\x01\"\x01@\x01\x04self!\0y\x04\0\x12[method]column.num\x01#\x01@\x01\x04\ -self!\0\x01\x04\0\x17[method]column.type-oid\x01$\x01i\x0b\x01@\x01\x05indexy\0%\ -\x04\0\x11[constructor]qual\x01&\x01h\x0b\x01@\x01\x04self'\0s\x04\0\x12[method]\ -qual.field\x01(\x04\0\x15[method]qual.operator\x01(\x01@\x01\x04self'\0\x08\x04\0\ -\x12[method]qual.value\x01)\x01@\x01\x04self'\0\x7f\x04\0\x13[method]qual.use-or\ -\x01*\x01k\x0a\x01@\x01\x04self'\0+\x04\0\x12[method]qual.param\x01,\x04\0\x14[m\ -ethod]qual.deparse\x01(\x01i\x0c\x01@\x01\x05indexy\0-\x04\0\x11[constructor]sor\ -t\x01.\x01h\x0c\x01@\x01\x04self/\0s\x04\0\x12[method]sort.field\x010\x01@\x01\x04\ -self/\0y\x04\0\x15[method]sort.field-no\x011\x01@\x01\x04self/\0\x7f\x04\0\x15[m\ -ethod]sort.reversed\x012\x04\0\x18[method]sort.nulls-first\x012\x01ks\x01@\x01\x04\ -self/\03\x04\0\x14[method]sort.collate\x014\x04\0\x14[method]sort.deparse\x010\x04\ -\0![method]sort.deparse-with-collate\x010\x01i\x0d\x01@\0\05\x04\0\x12[construct\ -or]limit\x016\x01h\x0d\x01@\x01\x04self7\0x\x04\0\x13[method]limit.count\x018\x04\ -\0\x14[method]limit.offset\x018\x01@\x01\x04self7\0s\x04\0\x15[method]limit.depa\ -rse\x019\x01i\x10\x01@\x01\x0coptions-type\x0f\0:\x04\0\x14[constructor]options\x01\ -;\x01h\x10\x01@\x02\x04self<\x03keys\03\x04\0\x13[method]options.get\x01=\x01j\x01\ -s\x01\x13\x01@\x02\x04self<\x03keys\0>\x04\0\x17[method]options.require\x01?\x01\ -@\x03\x04self<\x03keys\x07defaults\0s\x04\0\x1a[method]options.require-or\x01@\x01\ -i\x11\x01@\0\0\xc1\0\x04\0\x14[constructor]context\x01B\x01h\x11\x01@\x02\x04sel\ -f\xc3\0\x0coptions-type\x0f\0:\x04\0\x1b[method]context.get-options\x01D\x01p%\x01\ -@\x01\x04self\xc3\0\0\xc5\0\x04\0\x19[method]context.get-quals\x01F\x01p\x1f\x01\ -@\x01\x04self\xc3\0\0\xc7\0\x04\0\x1b[method]context.get-columns\x01H\x01p-\x01@\ -\x01\x04self\xc3\0\0\xc9\0\x04\0\x19[method]context.get-sorts\x01J\x01k5\x01@\x01\ -\x04self\xc3\0\0\xcb\0\x04\0\x19[method]context.get-limit\x01L\x03\x01\x1dsupaba\ -se:wrappers/types@0.1.0\x05\x04\x02\x03\0\x04\x04cell\x01B\x0d\x02\x03\x02\x01\x05\ -\x04\0\x04cell\x03\0\0\x01@\x01\x03msgs\x01\0\x04\0\x0breport-info\x01\x02\x04\0\ -\x0dreport-notice\x01\x02\x04\0\x0ereport-warning\x01\x02\x04\0\x0creport-error\x01\ -\x02\x01k\x01\x01@\x01\x04cell\x03\0s\x04\0\x0ecell-to-string\x01\x04\x01ks\x01@\ -\x01\x09secret-ids\0\x05\x04\0\x10get-vault-secret\x01\x06\x03\x01\x1dsupabase:w\ -rappers/utils@0.1.0\x05\x06\x02\x03\0\x04\x03row\x02\x03\0\x04\x07context\x02\x03\ -\0\x04\x09fdw-error\x02\x03\0\x04\x0afdw-result\x01B\x1f\x02\x03\x02\x01\x05\x04\ -\0\x04cell\x03\0\0\x02\x03\x02\x01\x07\x04\0\x03row\x03\0\x02\x02\x03\x02\x01\x08\ -\x04\0\x07context\x03\0\x04\x02\x03\x02\x01\x09\x04\0\x09fdw-error\x03\0\x06\x02\ -\x03\x02\x01\x0a\x04\0\x0afdw-result\x03\0\x08\x01@\0\0s\x04\0\x18host-version-r\ -equirement\x01\x0a\x01h\x05\x01@\x01\x03ctx\x0b\0\x09\x04\0\x04init\x01\x0c\x04\0\ -\x0abegin-scan\x01\x0c\x01h\x03\x01ky\x01j\x01\x0e\x01\x07\x01@\x02\x03ctx\x0b\x03\ -row\x0d\0\x0f\x04\0\x09iter-scan\x01\x10\x04\0\x07re-scan\x01\x0c\x04\0\x08end-s\ -can\x01\x0c\x04\0\x0cbegin-modify\x01\x0c\x01@\x02\x03ctx\x0b\x03row\x0d\0\x09\x04\ -\0\x06insert\x01\x11\x01@\x03\x03ctx\x0b\x05rowid\x01\x07new-row\x0d\0\x09\x04\0\ -\x06update\x01\x12\x01@\x02\x03ctx\x0b\x05rowid\x01\0\x09\x04\0\x06delete\x01\x13\ -\x04\0\x0aend-modify\x01\x0c\x04\x01\x20supabase:wrappers/routines@0.1.0\x05\x0b\ -\x04\x01\x20supabase:paddle-fdw/paddle@0.1.0\x04\0\x0b\x0c\x01\0\x06paddle\x03\0\ -\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.202.0\x10wit-bi\ -ndgen-rust\x060.24.0"; +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 3656] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xcb\x1b\x01A\x02\x01\ +A\x13\x01B\x16\x01o\x02ss\x01p\0\x04\0\x07headers\x03\0\x01\x01q\x05\x03get\0\0\x04\ +post\0\0\x03put\0\0\x05patch\0\0\x06delete\0\0\x04\0\x06method\x03\0\x03\x01r\x04\ +\x06method\x04\x03urls\x07headers\x02\x04bodys\x04\0\x07request\x03\0\x05\x01r\x04\ +\x03urls\x0bstatus-code{\x07headers\x02\x04bodys\x04\0\x08response\x03\0\x07\x01\ +s\x04\0\x0ahttp-error\x03\0\x09\x01j\x01\x08\x01\x0a\x04\0\x0bhttp-result\x03\0\x0b\ +\x01@\x01\x03req\x06\0\x0c\x04\0\x03get\x01\x0d\x04\0\x04post\x01\x0d\x04\0\x03p\ +ut\x01\x0d\x04\0\x05patch\x01\x0d\x04\0\x06delete\x01\x0d\x01j\0\x01\x0a\x01@\x01\ +\x04resp\x08\0\x0e\x04\0\x10error-for-status\x01\x0f\x03\x01\x1csupabase:wrapper\ +s/http@0.1.0\x05\0\x01B\x08\x01s\x04\0\x09jwt-error\x03\0\0\x01j\x01s\x01\x01\x04\ +\0\x0ajwt-result\x03\0\x02\x01o\x02ss\x01p\x04\x01@\x04\x07payload\x05\x04algos\x03\ +keys\x09ttl-hoursy\0\x03\x04\0\x06encode\x01\x06\x03\x01\x1bsupabase:wrappers/jw\ +t@0.1.0\x05\x01\x01B\x0a\x01ks\x04\0\x08metadata\x03\0\0\x01q\x05\x0ccreate-time\ +s\0\0\x07rows-in\0\0\x08rows-out\0\0\x08bytes-in\0\0\x09bytes-out\0\0\x04\0\x06m\ +etric\x03\0\x02\x01@\x03\x08fdw-names\x06metric\x03\x03incx\x01\0\x04\0\x09inc-s\ +tats\x01\x04\x01@\x01\x08fdw-names\0\x01\x04\0\x0cget-metadata\x01\x05\x01@\x02\x08\ +fdw-names\x08metadata\x01\x01\0\x04\0\x0cset-metadata\x01\x06\x03\x01\x1dsupabas\ +e:wrappers/stats@0.1.0\x05\x02\x01B\x0f\x01s\x04\0\x0atime-error\x03\0\0\x01j\x01\ +x\x01\x01\x04\0\x0btime-result\x03\0\x02\x01@\0\0x\x04\0\x0aepoch-secs\x01\x04\x01\ +@\x01\x01ss\0\x03\x04\0\x12parse-from-rfc3339\x01\x05\x01@\x02\x01ss\x03fmts\0\x03\ +\x04\0\x0eparse-from-str\x01\x06\x01j\x01s\x01\x01\x01@\x01\x05msecsx\0\x07\x04\0\ +\x13epoch-ms-to-rfc3339\x01\x08\x01@\x01\x06millisw\x01\0\x04\0\x05sleep\x01\x09\ +\x03\x01\x1csupabase:wrappers/time@0.1.0\x05\x03\x01Br\x01q\x0d\x04bool\0\0\x02i\ +8\0\0\x03i16\0\0\x03f32\0\0\x03i32\0\0\x03f64\0\0\x03i64\0\0\x07numeric\0\0\x06s\ +tring\0\0\x04date\0\0\x09timestamp\0\0\x0btimestamptz\0\0\x04json\0\0\x04\0\x08t\ +ype-oid\x03\0\0\x01q\x0d\x04bool\x01\x7f\0\x02i8\x01~\0\x03i16\x01|\0\x03f32\x01\ +v\0\x03i32\x01z\0\x03f64\x01u\0\x03i64\x01x\0\x07numeric\x01u\0\x06string\x01s\0\ +\x04date\x01x\0\x09timestamp\x01x\0\x0btimestamptz\x01x\0\x04json\x01s\0\x04\0\x04\ +cell\x03\0\x02\x04\0\x03row\x03\x01\x04\0\x06column\x03\x01\x01p\x03\x01q\x02\x04\ +cell\x01\x03\0\x05array\x01\x06\0\x04\0\x05value\x03\0\x07\x01r\x02\x02idy\x08ty\ +pe-oidy\x04\0\x05param\x03\0\x09\x04\0\x04qual\x03\x01\x04\0\x04sort\x03\x01\x04\ +\0\x05limit\x03\x01\x01q\x02\x06server\0\0\x05table\0\0\x04\0\x0coptions-type\x03\ +\0\x0e\x04\0\x07options\x03\x01\x04\0\x07context\x03\x01\x01s\x04\0\x09fdw-error\ +\x03\0\x12\x01j\0\x01\x13\x04\0\x0afdw-result\x03\0\x14\x01i\x04\x01@\0\0\x16\x04\ +\0\x10[constructor]row\x01\x17\x01h\x04\x01ps\x01@\x01\x04self\x18\0\x19\x04\0\x10\ +[method]row.cols\x01\x1a\x01k\x03\x01p\x1b\x01@\x01\x04self\x18\0\x1c\x04\0\x11[\ +method]row.cells\x01\x1d\x01@\x02\x04self\x18\x04cell\x1b\x01\0\x04\0\x10[method\ +]row.push\x01\x1e\x01i\x05\x01@\x01\x05indexy\0\x1f\x04\0\x13[constructor]column\ +\x01\x20\x01h\x05\x01@\x01\x04self!\0s\x04\0\x13[method]column.name\x01\"\x01@\x01\ +\x04self!\0y\x04\0\x12[method]column.num\x01#\x01@\x01\x04self!\0\x01\x04\0\x17[\ +method]column.type-oid\x01$\x01i\x0b\x01@\x01\x05indexy\0%\x04\0\x11[constructor\ +]qual\x01&\x01h\x0b\x01@\x01\x04self'\0s\x04\0\x12[method]qual.field\x01(\x04\0\x15\ +[method]qual.operator\x01(\x01@\x01\x04self'\0\x08\x04\0\x12[method]qual.value\x01\ +)\x01@\x01\x04self'\0\x7f\x04\0\x13[method]qual.use-or\x01*\x01k\x0a\x01@\x01\x04\ +self'\0+\x04\0\x12[method]qual.param\x01,\x04\0\x14[method]qual.deparse\x01(\x01\ +i\x0c\x01@\x01\x05indexy\0-\x04\0\x11[constructor]sort\x01.\x01h\x0c\x01@\x01\x04\ +self/\0s\x04\0\x12[method]sort.field\x010\x01@\x01\x04self/\0y\x04\0\x15[method]\ +sort.field-no\x011\x01@\x01\x04self/\0\x7f\x04\0\x15[method]sort.reversed\x012\x04\ +\0\x18[method]sort.nulls-first\x012\x01ks\x01@\x01\x04self/\03\x04\0\x14[method]\ +sort.collate\x014\x04\0\x14[method]sort.deparse\x010\x04\0![method]sort.deparse-\ +with-collate\x010\x01i\x0d\x01@\0\05\x04\0\x12[constructor]limit\x016\x01h\x0d\x01\ +@\x01\x04self7\0x\x04\0\x13[method]limit.count\x018\x04\0\x14[method]limit.offse\ +t\x018\x01@\x01\x04self7\0s\x04\0\x15[method]limit.deparse\x019\x01i\x10\x01@\x01\ +\x0coptions-type\x0f\0:\x04\0\x14[constructor]options\x01;\x01h\x10\x01@\x02\x04\ +self<\x03keys\03\x04\0\x13[method]options.get\x01=\x01j\x01s\x01\x13\x01@\x02\x04\ +self<\x03keys\0>\x04\0\x17[method]options.require\x01?\x01@\x03\x04self<\x03keys\ +\x07defaults\0s\x04\0\x1a[method]options.require-or\x01@\x01i\x11\x01@\0\0\xc1\0\ +\x04\0\x14[constructor]context\x01B\x01h\x11\x01@\x02\x04self\xc3\0\x0coptions-t\ +ype\x0f\0:\x04\0\x1b[method]context.get-options\x01D\x01p%\x01@\x01\x04self\xc3\0\ +\0\xc5\0\x04\0\x19[method]context.get-quals\x01F\x01p\x1f\x01@\x01\x04self\xc3\0\ +\0\xc7\0\x04\0\x1b[method]context.get-columns\x01H\x01p-\x01@\x01\x04self\xc3\0\0\ +\xc9\0\x04\0\x19[method]context.get-sorts\x01J\x01k5\x01@\x01\x04self\xc3\0\0\xcb\ +\0\x04\0\x19[method]context.get-limit\x01L\x03\x01\x1dsupabase:wrappers/types@0.\ +1.0\x05\x04\x02\x03\0\x04\x04cell\x01B\x0d\x02\x03\x02\x01\x05\x04\0\x04cell\x03\ +\0\0\x01@\x01\x03msgs\x01\0\x04\0\x0breport-info\x01\x02\x04\0\x0dreport-notice\x01\ +\x02\x04\0\x0ereport-warning\x01\x02\x04\0\x0creport-error\x01\x02\x01k\x01\x01@\ +\x01\x04cell\x03\0s\x04\0\x0ecell-to-string\x01\x04\x01ks\x01@\x01\x09secret-ids\ +\0\x05\x04\0\x10get-vault-secret\x01\x06\x03\x01\x1dsupabase:wrappers/utils@0.1.\ +0\x05\x06\x02\x03\0\x04\x03row\x02\x03\0\x04\x07context\x02\x03\0\x04\x09fdw-err\ +or\x02\x03\0\x04\x0afdw-result\x01B\x1f\x02\x03\x02\x01\x05\x04\0\x04cell\x03\0\0\ +\x02\x03\x02\x01\x07\x04\0\x03row\x03\0\x02\x02\x03\x02\x01\x08\x04\0\x07context\ +\x03\0\x04\x02\x03\x02\x01\x09\x04\0\x09fdw-error\x03\0\x06\x02\x03\x02\x01\x0a\x04\ +\0\x0afdw-result\x03\0\x08\x01@\0\0s\x04\0\x18host-version-requirement\x01\x0a\x01\ +h\x05\x01@\x01\x03ctx\x0b\0\x09\x04\0\x04init\x01\x0c\x04\0\x0abegin-scan\x01\x0c\ +\x01h\x03\x01ky\x01j\x01\x0e\x01\x07\x01@\x02\x03ctx\x0b\x03row\x0d\0\x0f\x04\0\x09\ +iter-scan\x01\x10\x04\0\x07re-scan\x01\x0c\x04\0\x08end-scan\x01\x0c\x04\0\x0cbe\ +gin-modify\x01\x0c\x01@\x02\x03ctx\x0b\x03row\x0d\0\x09\x04\0\x06insert\x01\x11\x01\ +@\x03\x03ctx\x0b\x05rowid\x01\x07new-row\x0d\0\x09\x04\0\x06update\x01\x12\x01@\x02\ +\x03ctx\x0b\x05rowid\x01\0\x09\x04\0\x06delete\x01\x13\x04\0\x0aend-modify\x01\x0c\ +\x04\x01\x20supabase:wrappers/routines@0.1.0\x05\x0b\x04\x01\x20supabase:paddle-\ +fdw/paddle@0.1.0\x04\0\x0b\x0c\x01\0\x06paddle\x03\0\0\0G\x09producers\x01\x0cpr\ +ocessed-by\x02\x0dwit-component\x070.202.0\x10wit-bindgen-rust\x060.24.0"; #[inline(never)] #[doc(hidden)] diff --git a/wasm-wrappers/fdw/paddle_fdw/src/lib.rs b/wasm-wrappers/fdw/paddle_fdw/src/lib.rs index 044f5bfc..da266c9a 100644 --- a/wasm-wrappers/fdw/paddle_fdw/src/lib.rs +++ b/wasm-wrappers/fdw/paddle_fdw/src/lib.rs @@ -1,6 +1,6 @@ #[allow(warnings)] mod bindings; -use serde_json::{json, Value as JsonValue}; +use serde_json::{json, Map as JsonMap, Value as JsonValue}; use bindings::{ exports::supabase::wrappers::routines::Guest, @@ -19,6 +19,7 @@ struct PaddleFdw { object: String, src_rows: Vec, src_idx: usize, + rowid_col: String, } static mut INSTANCE: *mut PaddleFdw = std::ptr::null_mut::(); @@ -100,7 +101,7 @@ impl PaddleFdw { return Ok(()); } - http::error_for_status(&resp)?; + http::error_for_status(&resp).map_err(|err| format!("{}: {}", err, resp.body))?; // save source rows self.src_rows = resp_json @@ -189,11 +190,38 @@ impl PaddleFdw { None } } - TypeOid::Json => src.as_str().map(|v| Cell::Json(v.to_owned())), + TypeOid::Json => src.as_object().map(|_| Cell::Json(src.to_string())), }; Ok(cell) } + + // convert a row to JSON string, which is used as request body for row update + fn row_to_body(&self, row: &Row) -> Result { + let mut map = JsonMap::new(); + + for (col_name, cell) in row.cols().iter().zip(row.cells().iter()) { + if let Some(cell) = cell { + let value = match cell { + Cell::Bool(v) => JsonValue::Bool(*v), + Cell::I64(v) => JsonValue::String(v.to_string()), + Cell::String(v) => JsonValue::String(v.to_string()), + Cell::Date(v) => JsonValue::String(time::epoch_ms_to_rfc3339(v * 1_000_000)?), + Cell::Timestamp(v) => JsonValue::String(time::epoch_ms_to_rfc3339(*v)?), + Cell::Timestamptz(v) => JsonValue::String(time::epoch_ms_to_rfc3339(*v)?), + Cell::Json(v) => { + serde_json::from_str::(v).map_err(|e| e.to_string())? + } + _ => { + return Err(format!("column '{}' type is not supported", col_name)); + } + }; + map.insert(col_name.to_owned(), value); + } + } + + Ok(JsonValue::Object(map).to_string()) + } } impl Guest for PaddleFdw { @@ -283,24 +311,56 @@ impl Guest for PaddleFdw { Ok(()) } - fn begin_modify(_ctx: &Context) -> FdwResult { - unimplemented!("update on foreign table is not supported"); + fn begin_modify(ctx: &Context) -> FdwResult { + let this = Self::this_mut(); + let opts = ctx.get_options(OptionsType::Table); + this.object = opts.require("object")?; + this.rowid_col = opts.require("rowid_column")?; + Ok(()) } - fn insert(_ctx: &Context, _row: &Row) -> FdwResult { - unimplemented!("update on foreign table is not supported"); + fn insert(_ctx: &Context, row: &Row) -> FdwResult { + let this = Self::this_mut(); + let url = format!("{}/{}", this.base_url, this.object); + let body = this.row_to_body(row)?; + let req = http::Request { + method: http::Method::Post, + url, + headers: this.headers.clone(), + body, + }; + let resp = http::post(&req)?; + http::error_for_status(&resp).map_err(|err| format!("{}: {}", err, resp.body))?; + stats::inc_stats(FDW_NAME, stats::Metric::RowsOut, 1); + Ok(()) } - fn update(_ctx: &Context, _rowid: Cell, _row: &Row) -> FdwResult { - unimplemented!("update on foreign table is not supported"); + fn update(_ctx: &Context, rowid: Cell, row: &Row) -> FdwResult { + let this = Self::this_mut(); + let id = match rowid { + Cell::String(s) => s.clone(), + _ => return Err("invalid rowid column value".to_string()), + }; + let url = format!("{}/{}/{}", this.base_url, this.object, id); + let body = this.row_to_body(row)?; + let req = http::Request { + method: http::Method::Patch, + url, + headers: this.headers.clone(), + body, + }; + let resp = http::patch(&req)?; + http::error_for_status(&resp).map_err(|err| format!("{}: {}", err, resp.body))?; + stats::inc_stats(FDW_NAME, stats::Metric::RowsOut, 1); + Ok(()) } fn delete(_ctx: &Context, _rowid: Cell) -> FdwResult { - unimplemented!("update on foreign table is not supported"); + unimplemented!("delete on foreign table is not supported"); } fn end_modify(_ctx: &Context) -> FdwResult { - unimplemented!("update on foreign table is not supported"); + Ok(()) } } diff --git a/wasm-wrappers/wit/http.wit b/wasm-wrappers/wit/http.wit index 974c88df..afd8bebe 100644 --- a/wasm-wrappers/wit/http.wit +++ b/wasm-wrappers/wit/http.wit @@ -4,6 +4,9 @@ interface http { variant method { get, post, + put, + patch, + delete, } record request { @@ -26,6 +29,7 @@ interface http { get: func(req: request) -> http-result; post: func(req: request) -> http-result; put: func(req: request) -> http-result; + patch: func(req: request) -> http-result; delete: func(req: request) -> http-result; error-for-status: func(resp: response) -> result<_, http-error>; diff --git a/wasm-wrappers/wit/time.wit b/wasm-wrappers/wit/time.wit index 7ba9249c..4200f4f8 100644 --- a/wasm-wrappers/wit/time.wit +++ b/wasm-wrappers/wit/time.wit @@ -11,6 +11,9 @@ interface time { // parse string from an user-specified format to microseconds since Unix epoch parse-from-str: func(s: string, fmt: string) -> time-result; + // convert microseconds since Unix epoch to RFC3339 string + epoch-ms-to-rfc3339: func(msecs: s64) -> result; + // sleep for a while sleep: func(millis: u64); } diff --git a/wrappers/src/fdw/wasm_fdw/host/http.rs b/wrappers/src/fdw/wasm_fdw/host/http.rs index 7e174140..e33b57bd 100644 --- a/wrappers/src/fdw/wasm_fdw/host/http.rs +++ b/wrappers/src/fdw/wasm_fdw/host/http.rs @@ -48,6 +48,27 @@ fn create_client(req: &http::Request) -> Result { } impl FdwHost { + // make a http request + fn http_request(&mut self, req: http::Request) -> http::HttpResult { + supabase_wrappers::prelude::report_info(&format!("req: {:?}", req)); + let client = create_client(&req)?; + let resp = self + .rt + .block_on( + match req.method { + http::Method::Get => client.get(req.url), + http::Method::Post => client.post(req.url), + http::Method::Put => client.put(req.url), + http::Method::Patch => client.patch(req.url), + http::Method::Delete => client.delete(req.url), + } + .body(req.body) + .send(), + ) + .map_err(|e| e.to_string())?; + self.convert_to_guest_response(resp) + } + // convert reqwest response to guest response fn convert_to_guest_response(&mut self, resp: Response) -> http::HttpResult { let url = resp.url().to_string(); @@ -64,40 +85,29 @@ impl FdwHost { } impl http::Host for FdwHost { + #[inline] fn get(&mut self, req: http::Request) -> http::HttpResult { - let client = create_client(&req)?; - let resp = self - .rt - .block_on(client.get(req.url).send()) - .map_err(|e| e.to_string())?; - self.convert_to_guest_response(resp) + self.http_request(req) } + #[inline] fn post(&mut self, req: http::Request) -> http::HttpResult { - let client = create_client(&req)?; - let resp = self - .rt - .block_on(client.post(req.url).body(req.body).send()) - .map_err(|e| e.to_string())?; - self.convert_to_guest_response(resp) + self.http_request(req) } + #[inline] fn put(&mut self, req: http::Request) -> http::HttpResult { - let client = create_client(&req)?; - let resp = self - .rt - .block_on(client.put(req.url).body(req.body).send()) - .map_err(|e| e.to_string())?; - self.convert_to_guest_response(resp) + self.http_request(req) } + #[inline] + fn patch(&mut self, req: http::Request) -> http::HttpResult { + self.http_request(req) + } + + #[inline] fn delete(&mut self, req: http::Request) -> http::HttpResult { - let client = create_client(&req)?; - let resp = self - .rt - .block_on(client.delete(req.url).send()) - .map_err(|e| e.to_string())?; - self.convert_to_guest_response(resp) + self.http_request(req) } fn error_for_status(&mut self, resp: http::Response) -> Result<(), http::HttpError> { diff --git a/wrappers/src/fdw/wasm_fdw/host/time.rs b/wrappers/src/fdw/wasm_fdw/host/time.rs index 9f0cc0fd..d37e4106 100644 --- a/wrappers/src/fdw/wasm_fdw/host/time.rs +++ b/wrappers/src/fdw/wasm_fdw/host/time.rs @@ -24,6 +24,12 @@ impl time::Host for FdwHost { .map_err(|e| e.to_string()) } + fn epoch_ms_to_rfc3339(&mut self, msecs: i64) -> Result { + DateTime::from_timestamp_micros(msecs) + .map(|ts| ts.to_rfc3339()) + .ok_or("invalid microseconds since Unix Epoch".to_string()) + } + fn sleep(&mut self, millis: u64) { std::thread::sleep(std::time::Duration::from_millis(millis)); }