Skip to content

Commit f0e0364

Browse files
authored
Merge pull request #83 from itowlson/pg-column-lookup-helper
PostgreSQL result set helper for column-oriented lookup
2 parents 896dd56 + 45a6d94 commit f0e0364

File tree

2 files changed

+66
-6
lines changed

2 files changed

+66
-6
lines changed

examples/postgres-v4/src/lib.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![allow(dead_code)]
22
use anyhow::Result;
33
use http::{Request, Response};
4-
use spin_sdk::{http_component, pg3, pg3::Decode};
4+
use spin_sdk::{http_component, pg4};
55

66
// The environment variable set in `spin.toml` that points to the
77
// address of the Pg server that the component will write to
@@ -10,7 +10,7 @@ const DB_URL_ENV: &str = "DB_URL";
1010
#[http_component]
1111
fn process(req: Request<()>) -> Result<Response<String>> {
1212
let address = std::env::var(DB_URL_ENV)?;
13-
let conn = pg3::Connection::open(&address)?;
13+
let conn = pg4::Connection::open(&address)?;
1414

1515
let year_header = req
1616
.headers()
@@ -31,10 +31,9 @@ fn process(req: Request<()>) -> Result<Response<String>> {
3131
"it was anarchy".to_owned()
3232
} else {
3333
let ruler_names = rulers
34-
.rows
35-
.into_iter()
36-
.map(|r| Decode::decode(&r[0]))
37-
.collect::<Result<Vec<String>, _>>()?;
34+
.rows()
35+
.map(|r| r.get::<String>("name").unwrap())
36+
.collect::<Vec<_>>();
3837
ruler_names.join(" and ")
3938
};
4039

src/pg4.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,67 @@ pub use super::wit::pg4::Connection;
133133
/// ```
134134
pub use super::wit::pg4::RowSet;
135135

136+
impl RowSet {
137+
/// Get all the rows for this query result
138+
pub fn rows(&self) -> impl Iterator<Item = Row<'_>> {
139+
self.rows.iter().map(|r| Row {
140+
columns: self.columns.as_slice(),
141+
result: r,
142+
})
143+
}
144+
}
145+
146+
/// A database row result.
147+
///
148+
/// There are two representations of a SQLite row in the SDK. This type is useful for
149+
/// addressing elements by column name, and is obtained from the [RowSet::rows()] function.
150+
/// The [DbValue] vector representation is obtained from the [field@RowSet::rows] field, and provides
151+
/// index-based lookup or low-level access to row values via a vector.
152+
pub struct Row<'a> {
153+
columns: &'a [super::wit::pg4::Column],
154+
result: &'a [DbValue],
155+
}
156+
157+
impl Row<'_> {
158+
/// Get a value by its column name. The value is converted to the target type as per the
159+
/// conversion table shown in the module documentation.
160+
///
161+
/// This function returns None for both no such column _and_ failed conversion. You should use
162+
/// it only if you do not need to address errors (that is, if you know that conversion should
163+
/// never fail). If your code does not know the type in advance, use the raw [field@RowSet::rows] vector
164+
/// instead of the `Row` wrapper to access the underlying [DbValue] enum: this will allow you to
165+
/// determine the type and process it accordingly.
166+
///
167+
/// Additionally, this function performs a name lookup each time it is called. If you are iterating
168+
/// over a large number of rows, it's more efficient to use column indexes, either calculated or
169+
/// statically known from the column order in the SQL.
170+
///
171+
/// # Examples
172+
///
173+
/// ```no_run
174+
/// use spin_sdk::pg4::{Connection, DbValue};
175+
///
176+
/// # fn main() -> anyhow::Result<()> {
177+
/// # let user_id = 0;
178+
/// let db = Connection::open("host=localhost user=postgres password=my_password dbname=mydb")?;
179+
/// let query_result = db.query(
180+
/// "SELECT * FROM users WHERE id = $1",
181+
/// &[user_id.into()]
182+
/// )?;
183+
/// let user_row = query_result.rows().next().unwrap();
184+
///
185+
/// let name = user_row.get::<String>("name").unwrap();
186+
/// let age = user_row.get::<i16>("age").unwrap();
187+
/// # Ok(())
188+
/// # }
189+
/// ```
190+
pub fn get<T: Decode>(&self, column: &str) -> Option<T> {
191+
let i = self.columns.iter().position(|c| c.name == column)?;
192+
let db_value = self.result.get(i)?;
193+
Decode::decode(db_value).ok()
194+
}
195+
}
196+
136197
#[doc(inline)]
137198
pub use super::wit::pg4::{Error as PgError, *};
138199

0 commit comments

Comments
 (0)