diff --git a/Cargo.lock b/Cargo.lock index 04d748f2fb..90eabbae4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4173,6 +4173,7 @@ dependencies = [ "postgres-native-tls", "spin-core", "spin-world", + "table", "tokio", "tokio-postgres", "tracing", diff --git a/crates/outbound-pg/Cargo.toml b/crates/outbound-pg/Cargo.toml index 42e2c3c2b6..137c4fcec6 100644 --- a/crates/outbound-pg/Cargo.toml +++ b/crates/outbound-pg/Cargo.toml @@ -13,6 +13,7 @@ native-tls = "0.2.11" postgres-native-tls = "0.5.0" spin-core = { path = "../core" } spin-world = { path = "../world" } -tokio = { version = "1", features = [ "rt-multi-thread" ] } +table = { path = "../table" } +tokio = { version = "1", features = ["rt-multi-thread"] } tokio-postgres = { version = "0.7.7" } tracing = { workspace = true } diff --git a/crates/outbound-pg/src/lib.rs b/crates/outbound-pg/src/lib.rs index f2a7013a3f..4b6bb86310 100644 --- a/crates/outbound-pg/src/lib.rs +++ b/crates/outbound-pg/src/lib.rs @@ -1,12 +1,12 @@ use anyhow::{anyhow, Result}; use native_tls::TlsConnector; use postgres_native_tls::MakeTlsConnector; -use spin_core::{async_trait, HostComponent}; +use spin_core::{async_trait, wasmtime::component::Resource, HostComponent}; use spin_world::v1::{ - postgres::{self, PgError}, + postgres as v1, rdbms_types::{Column, DbDataType, DbValue, ParameterValue, RowSet}, }; -use std::collections::HashMap; +use spin_world::v2::postgres::{self as v2, Connection}; use tokio_postgres::{ config::SslMode, types::{ToSql, Type}, @@ -16,7 +16,15 @@ use tokio_postgres::{ /// A simple implementation to support outbound pg connection #[derive(Default)] pub struct OutboundPg { - pub connections: HashMap, + pub connections: table::Table, +} + +impl OutboundPg { + async fn get_client(&mut self, connection: Resource) -> Result<&Client, v2::Error> { + self.connections + .get(connection.rep()) + .ok_or_else(|| v2::Error::ConnectionFailed("no connection found".into())) + } } impl HostComponent for OutboundPg { @@ -26,7 +34,8 @@ impl HostComponent for OutboundPg { linker: &mut spin_core::Linker, get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, ) -> anyhow::Result<()> { - postgres::add_to_linker(linker, get) + v1::add_to_linker(linker, get)?; + v2::add_to_linker(linker, get) } fn build_data(&self) -> Self::Data { @@ -35,27 +44,43 @@ impl HostComponent for OutboundPg { } #[async_trait] -impl postgres::Host for OutboundPg { +impl v2::Host for OutboundPg {} + +#[async_trait] +impl v2::HostConnection for OutboundPg { + async fn open(&mut self, address: String) -> Result, v2::Error>> { + Ok(async { + self.connections + .push( + build_client(&address) + .await + .map_err(|e| v2::Error::ConnectionFailed(format!("{e:?}")))?, + ) + .map_err(|_| v2::Error::Other("too many connections".into())) + .map(Resource::new_own) + } + .await) + } + async fn execute( &mut self, - address: String, + connection: Resource, statement: String, params: Vec, - ) -> Result> { + ) -> Result> { Ok(async { let params: Vec<&(dyn ToSql + Sync)> = params .iter() .map(to_sql_parameter) .collect::>>() - .map_err(|e| PgError::ValueConversionFailed(format!("{:?}", e)))?; + .map_err(|e| v2::Error::ValueConversionFailed(format!("{:?}", e)))?; let nrow = self - .get_client(&address) - .await - .map_err(|e| PgError::ConnectionFailed(format!("{:?}", e)))? + .get_client(connection) + .await? .execute(&statement, params.as_slice()) .await - .map_err(|e| PgError::QueryFailed(format!("{:?}", e)))?; + .map_err(|e| v2::Error::QueryFailed(format!("{:?}", e)))?; Ok(nrow) } @@ -64,24 +89,23 @@ impl postgres::Host for OutboundPg { async fn query( &mut self, - address: String, + connection: Resource, statement: String, params: Vec, - ) -> Result> { + ) -> Result> { Ok(async { let params: Vec<&(dyn ToSql + Sync)> = params .iter() .map(to_sql_parameter) .collect::>>() - .map_err(|e| PgError::BadParameter(format!("{:?}", e)))?; + .map_err(|e| v2::Error::BadParameter(format!("{:?}", e)))?; let results = self - .get_client(&address) - .await - .map_err(|e| PgError::ConnectionFailed(format!("{:?}", e)))? + .get_client(connection) + .await? .query(&statement, params.as_slice()) .await - .map_err(|e| PgError::QueryFailed(format!("{:?}", e)))?; + .map_err(|e| v2::Error::QueryFailed(format!("{:?}", e)))?; if results.is_empty() { return Ok(RowSet { @@ -95,12 +119,17 @@ impl postgres::Host for OutboundPg { .iter() .map(convert_row) .collect::, _>>() - .map_err(|e| PgError::QueryFailed(format!("{:?}", e)))?; + .map_err(|e| v2::Error::QueryFailed(format!("{:?}", e)))?; Ok(RowSet { columns, rows }) } .await) } + + fn drop(&mut self, connection: Resource) -> anyhow::Result<()> { + self.connections.remove(connection.rep()); + Ok(()) + } } fn to_sql_parameter(value: &ParameterValue) -> anyhow::Result<&(dyn ToSql + Sync)> { @@ -233,16 +262,6 @@ fn convert_entry(row: &Row, index: usize) -> Result anyhow::Result<&Client> { - let client = match self.connections.entry(address.to_owned()) { - std::collections::hash_map::Entry::Occupied(o) => o.into_mut(), - std::collections::hash_map::Entry::Vacant(v) => v.insert(build_client(address).await?), - }; - Ok(client) - } -} - async fn build_client(address: &str) -> anyhow::Result { let config = address.parse::()?; @@ -325,3 +344,47 @@ impl std::fmt::Debug for PgNull { f.debug_struct("NULL").finish() } } + +/// Delegate a function call to the v2::HostConnection implementation +macro_rules! delegate { + ($self:ident.$name:ident($address:expr, $($arg:expr),*)) => {{ + let connection = match ::open($self, $address).await? { + Ok(c) => c, + Err(e) => return Ok(Err(to_legacy_error(e))), + }; + Ok(::$name($self, connection, $($arg),*) + .await? + .map_err(|e| to_legacy_error(e))) + }}; +} + +#[async_trait] +impl v1::Host for OutboundPg { + async fn execute( + &mut self, + address: String, + statement: String, + params: Vec, + ) -> Result> { + delegate!(self.execute(address, statement, params)) + } + + async fn query( + &mut self, + address: String, + statement: String, + params: Vec, + ) -> Result> { + delegate!(self.query(address, statement, params)) + } +} + +fn to_legacy_error(error: v2::Error) -> v1::PgError { + match error { + v2::Error::ConnectionFailed(e) => v1::PgError::ConnectionFailed(e), + v2::Error::BadParameter(e) => v1::PgError::BadParameter(e), + v2::Error::QueryFailed(e) => v1::PgError::QueryFailed(e), + v2::Error::ValueConversionFailed(e) => v1::PgError::ValueConversionFailed(e), + v2::Error::Other(e) => v1::PgError::OtherError(e), + } +} diff --git a/crates/table/Cargo.toml b/crates/table/Cargo.toml index 0dfcf531e5..083501e678 100644 --- a/crates/table/Cargo.toml +++ b/crates/table/Cargo.toml @@ -4,6 +4,4 @@ version.workspace = true authors.workspace = true edition.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] diff --git a/crates/table/src/lib.rs b/crates/table/src/lib.rs index 43f094ffab..d89cb4b9c2 100644 --- a/crates/table/src/lib.rs +++ b/crates/table/src/lib.rs @@ -13,6 +13,12 @@ pub struct Table { tuples: HashMap, } +impl Default for Table { + fn default() -> Self { + Self::new(1024) + } +} + impl Table { /// Create a new, empty table with the specified capacity. pub fn new(capacity: u32) -> Self { diff --git a/examples/rust-outbound-pg/src/lib.rs b/examples/rust-outbound-pg/src/lib.rs index 748db7edc8..58e34e8312 100644 --- a/examples/rust-outbound-pg/src/lib.rs +++ b/examples/rust-outbound-pg/src/lib.rs @@ -53,9 +53,10 @@ fn process(req: Request) -> Result { fn read(_req: Request) -> Result { let address = std::env::var(DB_URL_ENV)?; + let conn = pg::Connection::open(&address)?; let sql = "SELECT id, title, content, authorname, coauthor FROM articletest"; - let rowset = pg::query(&address, sql, &[])?; + let rowset = conn.query(sql, &[])?; let column_summary = rowset .columns @@ -89,14 +90,15 @@ fn read(_req: Request) -> Result { fn write(_req: Request) -> Result { let address = std::env::var(DB_URL_ENV)?; + let conn = pg::Connection::open(&address)?; let sql = "INSERT INTO articletest (title, content, authorname) VALUES ('aaa', 'bbb', 'ccc')"; - let nrow_executed = pg::execute(&address, sql, &[])?; + let nrow_executed = conn.execute(sql, &[])?; println!("nrow_executed: {}", nrow_executed); let sql = "SELECT COUNT(id) FROM articletest"; - let rowset = pg::query(&address, sql, &[])?; + let rowset = conn.query(sql, &[])?; let row = &rowset.rows[0]; let count = i64::decode(&row[0])?; let response = format!("Count: {}\n", count); @@ -108,10 +110,11 @@ fn write(_req: Request) -> Result { fn pg_backend_pid(_req: Request) -> Result { let address = std::env::var(DB_URL_ENV)?; + let conn = pg::Connection::open(&address)?; let sql = "SELECT pg_backend_pid()"; let get_pid = || { - let rowset = pg::query(&address, sql, &[])?; + let rowset = conn.query(sql, &[])?; let row = &rowset.rows[0]; i32::decode(&row[0]) diff --git a/examples/spin-timer/Cargo.lock b/examples/spin-timer/Cargo.lock index edd7017cc4..1bf801c668 100644 --- a/examples/spin-timer/Cargo.lock +++ b/examples/spin-timer/Cargo.lock @@ -2565,6 +2565,7 @@ dependencies = [ "postgres-native-tls", "spin-core", "spin-world", + "table", "tokio", "tokio-postgres", "tracing", @@ -3588,7 +3589,7 @@ dependencies = [ [[package]] name = "spin-componentize" version = "0.1.0" -source = "git+https://github.com/fermyon/spin-componentize?rev=ed1305044fd1455ded4bb47a4d3b1ed8a505fc86#ed1305044fd1455ded4bb47a4d3b1ed8a505fc86" +source = "git+https://github.com/fermyon/spin-componentize?rev=0c68c5f2afae65c2011fa23b30fd136682506e2a#0c68c5f2afae65c2011fa23b30fd136682506e2a" dependencies = [ "anyhow", "wasm-encoder 0.35.0", diff --git a/sdk/rust/src/pg.rs b/sdk/rust/src/pg.rs index 09a8b0fa90..d757d0ec51 100644 --- a/sdk/rust/src/pg.rs +++ b/sdk/rust/src/pg.rs @@ -13,8 +13,10 @@ //! | `String` | str(string) | VARCHAR, CHAR(N), TEXT | //! | `Vec` | binary(list\) | BYTEA | -pub use super::wit::v1::postgres::{execute, query, PgError}; +#[doc(inline)] pub use super::wit::v1::rdbms_types::*; +#[doc(inline)] +pub use super::wit::v2::postgres::{Connection, Error as PgError}; /// A pg error #[derive(Debug, thiserror::Error)] @@ -23,7 +25,7 @@ pub enum Error { #[error("error value decoding: {0}")] Decode(String), /// Pg query failed with an error - #[error("{0}")] + #[error(transparent)] PgError(#[from] PgError), } diff --git a/tests/http/vault-config-test/Cargo.lock b/tests/http/vault-config-test/Cargo.lock index 6e16b805e9..447d6e6aa7 100644 --- a/tests/http/vault-config-test/Cargo.lock +++ b/tests/http/vault-config-test/Cargo.lock @@ -16,15 +16,15 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "equivalent" @@ -40,9 +40,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -103,7 +103,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -181,9 +181,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "leb128" @@ -193,15 +193,15 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "once_cell" @@ -211,9 +211,9 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" @@ -229,9 +229,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -263,28 +263,28 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "semver" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -389,9 +389,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -400,29 +400,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" @@ -526,7 +526,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "wit-bindgen-core", "wit-bindgen-rust", "wit-component", diff --git a/tests/testcases/http-rust-outbound-pg/src/lib.rs b/tests/testcases/http-rust-outbound-pg/src/lib.rs index 6f7f36eb11..5c7faa396b 100644 --- a/tests/testcases/http-rust-outbound-pg/src/lib.rs +++ b/tests/testcases/http-rust-outbound-pg/src/lib.rs @@ -55,6 +55,7 @@ fn process(req: Request) -> Result { fn test_numeric_types(_req: Request) -> Result { let address = std::env::var(DB_URL_ENV)?; + let conn = pg::Connection::open(&address)?; let create_table_sql = r#" CREATE TEMPORARY TABLE test_numeric_types ( @@ -73,7 +74,7 @@ fn test_numeric_types(_req: Request) -> Result { ); "#; - pg::execute(&address, create_table_sql, &[])?; + conn.execute(create_table_sql, &[])?; let insert_sql = r#" INSERT INTO test_numeric_types @@ -82,7 +83,7 @@ fn test_numeric_types(_req: Request) -> Result { (0, 0, 0, 0, 0, 0, 0, 0); "#; - pg::execute(&address, insert_sql, &[])?; + conn.execute(insert_sql, &[])?; let sql = r#" SELECT @@ -101,7 +102,7 @@ fn test_numeric_types(_req: Request) -> Result { FROM test_numeric_types; "#; - let rowset = pg::query(&address, sql, &[])?; + let rowset = conn.query(sql, &[])?; let column_summary = rowset .columns @@ -158,6 +159,7 @@ fn test_numeric_types(_req: Request) -> Result { fn test_character_types(_req: Request) -> Result { let address = std::env::var(DB_URL_ENV)?; + let conn = pg::Connection::open(&address)?; let create_table_sql = r#" CREATE TEMPORARY TABLE test_character_types ( @@ -167,7 +169,7 @@ fn test_character_types(_req: Request) -> Result { ); "#; - pg::execute(&address, create_table_sql, &[])?; + conn.execute(create_table_sql, &[])?; let insert_sql = r#" INSERT INTO test_character_types @@ -176,7 +178,7 @@ fn test_character_types(_req: Request) -> Result { ('rvarchar', 'rtext', 'rchar'); "#; - pg::execute(&address, insert_sql, &[])?; + conn.execute(insert_sql, &[])?; let sql = r#" SELECT @@ -184,7 +186,7 @@ fn test_character_types(_req: Request) -> Result { FROM test_character_types; "#; - let rowset = pg::query(&address, sql, &[])?; + let rowset = conn.query(sql, &[])?; let column_summary = rowset .columns @@ -223,6 +225,7 @@ fn test_character_types(_req: Request) -> Result { fn test_general_types(_req: Request) -> Result { let address = std::env::var(DB_URL_ENV)?; + let conn = pg::Connection::open(&address)?; let create_table_sql = r#" CREATE TEMPORARY TABLE test_general_types ( @@ -232,7 +235,7 @@ fn test_general_types(_req: Request) -> Result { ); "#; - pg::execute(&address, create_table_sql, &[])?; + conn.execute(create_table_sql, &[])?; let insert_sql = r" INSERT INTO test_general_types @@ -241,7 +244,7 @@ fn test_general_types(_req: Request) -> Result { (TRUE, '\176'::bytea); "; - pg::execute(&address, insert_sql, &[])?; + conn.execute(insert_sql, &[])?; let sql = r#" SELECT @@ -249,7 +252,7 @@ fn test_general_types(_req: Request) -> Result { FROM test_general_types; "#; - let rowset = pg::query(&address, sql, &[])?; + let rowset = conn.query(sql, &[])?; let column_summary = rowset .columns @@ -288,10 +291,11 @@ fn test_general_types(_req: Request) -> Result { fn pg_backend_pid(_req: Request) -> Result { let address = std::env::var(DB_URL_ENV)?; + let conn = pg::Connection::open(&address)?; let sql = "SELECT pg_backend_pid()"; let get_pid = || { - let rowset = pg::query(&address, sql, &[])?; + let rowset = conn.query(sql, &[])?; let row = &rowset.rows[0]; i32::decode(&row[0]) diff --git a/wit/preview2/postgres.wit b/wit/preview2/postgres.wit new file mode 100644 index 0000000000..97bc78285a --- /dev/null +++ b/wit/preview2/postgres.wit @@ -0,0 +1,24 @@ +interface postgres { + use fermyon:spin/rdbms-types.{parameter-value, row-set} + + /// Errors related to interacting with Postgres. + variant error { + connection-failed(string), + bad-parameter(string), + query-failed(string), + value-conversion-failed(string), + other(string) + } + + /// A connection to a postgres database. + resource connection { + /// Open a connection to the Postgres instance at `address`. + open: static func(address: string) -> result + + /// Query the database. + query: func(statement: string, params: list) -> result + + /// Execute command to the database. + execute: func(statement: string, params: list) -> result + } +} diff --git a/wit/preview2/world.wit b/wit/preview2/world.wit index 26bbcdf6db..5aa0294754 100644 --- a/wit/preview2/world.wit +++ b/wit/preview2/world.wit @@ -18,11 +18,11 @@ world http-trigger { world platform { import fermyon:spin/config import fermyon:spin/http - import fermyon:spin/postgres import fermyon:spin/mysql import fermyon:spin/llm import redis + import postgres import sqlite import key-value }