Skip to content

Commit c5dee62

Browse files
authored
Merge pull request #130 from kinode-dao/dr/sqlite-overhaul
sqlite: new types from runtime
2 parents bf89fd6 + 7444568 commit c5dee62

File tree

1 file changed

+129
-39
lines changed

1 file changed

+129
-39
lines changed

src/sqlite.rs

Lines changed: 129 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,115 @@ use serde::{Deserialize, Serialize};
33
use std::collections::HashMap;
44
use thiserror::Error;
55

6-
/// Actions are sent to a specific sqlite database, `db` is the name,
7-
/// `package_id` is the [`PackageId`]. Capabilities are checked, you can access another process's
8-
/// database if it has given you the [`crate::Capability`].
9-
#[derive(Debug, Serialize, Deserialize)]
6+
/// Actions are sent to a specific SQLite database. `db` is the name,
7+
/// `package_id` is the [`PackageId`] that created the database. Capabilities
8+
/// are checked: you can access another process's database if it has given
9+
/// you the read and/or write capability to do so.
10+
#[derive(Clone, Debug, Serialize, Deserialize)]
1011
pub struct SqliteRequest {
1112
pub package_id: PackageId,
1213
pub db: String,
1314
pub action: SqliteAction,
1415
}
1516

16-
#[derive(Debug, Serialize, Deserialize)]
17+
/// IPC Action format representing operations that can be performed on the
18+
/// SQLite runtime module. These actions are included in a [`SqliteRequest`]
19+
/// sent to the `sqlite:distro:sys` runtime module.
20+
#[derive(Clone, Debug, Serialize, Deserialize)]
1721
pub enum SqliteAction {
22+
/// Opens an existing key-value database or creates a new one if it doesn't exist.
23+
/// Requires `package_id` in [`SqliteRequest`] to match the package ID of the sender.
24+
/// The sender will own the database and can remove it with [`SqliteAction::RemoveDb`].
25+
///
26+
/// A successful open will respond with [`SqliteResponse::Ok`]. Any error will be
27+
/// contained in the [`SqliteResponse::Err`] variant.
1828
Open,
29+
/// Permanently deletes the entire key-value database.
30+
/// Requires `package_id` in [`SqliteRequest`] to match the package ID of the sender.
31+
/// Only the owner can remove the database.
32+
///
33+
/// A successful remove will respond with [`SqliteResponse::Ok`]. Any error will be
34+
/// contained in the [`SqliteResponse::Err`] variant.
1935
RemoveDb,
36+
/// Executes a write statement (INSERT/UPDATE/DELETE)
37+
///
38+
/// * `statement` - SQL statement to execute
39+
/// * `tx_id` - Optional transaction ID
40+
/// * blob: Vec<SqlValue> - Parameters for the SQL statement, where SqlValue can be:
41+
/// - null
42+
/// - boolean
43+
/// - i64
44+
/// - f64
45+
/// - String
46+
/// - Vec<u8> (binary data)
47+
///
48+
/// Using this action requires the sender to have the write capability
49+
/// for the database.
50+
///
51+
/// A successful write will respond with [`SqliteResponse::Ok`]. Any error will be
52+
/// contained in the [`SqliteResponse::Err`] variant.
2053
Write {
2154
statement: String,
2255
tx_id: Option<u64>,
2356
},
24-
Read {
25-
query: String,
26-
},
57+
/// Executes a read query (SELECT)
58+
///
59+
/// * blob: Vec<SqlValue> - Parameters for the SQL query, where SqlValue can be:
60+
/// - null
61+
/// - boolean
62+
/// - i64
63+
/// - f64
64+
/// - String
65+
/// - Vec<u8> (binary data)
66+
///
67+
/// Using this action requires the sender to have the read capability
68+
/// for the database.
69+
///
70+
/// A successful query will respond with [`SqliteResponse::Query`], where the
71+
/// response blob contains the results of the query. Any error will be contained
72+
/// in the [`SqliteResponse::Err`] variant.
73+
Query(String),
74+
/// Begins a new transaction for atomic operations.
75+
///
76+
/// Sending this will prompt a [`SqliteResponse::BeginTx`] response with the
77+
/// transaction ID. Any error will be contained in the [`SqliteResponse::Err`] variant.
2778
BeginTx,
28-
Commit {
29-
tx_id: u64,
30-
},
31-
Backup,
79+
/// Commits all operations in the specified transaction.
80+
///
81+
/// # Parameters
82+
/// * `tx_id` - The ID of the transaction to commit
83+
///
84+
/// A successful commit will respond with [`SqliteResponse::Ok`]. Any error will be
85+
/// contained in the [`SqliteResponse::Err`] variant.
86+
Commit { tx_id: u64 },
3287
}
3388

34-
#[derive(Debug, Serialize, Deserialize)]
89+
#[derive(Clone, Debug, Serialize, Deserialize)]
3590
pub enum SqliteResponse {
91+
/// Indicates successful completion of an operation.
92+
/// Sent in response to actions Open, RemoveDb, Write, Query, BeginTx, and Commit.
3693
Ok,
94+
/// Returns the results of a query.
95+
///
96+
/// * blob: Vec<Vec<SqlValue>> - Array of rows, where each row contains SqlValue types:
97+
/// - null
98+
/// - boolean
99+
/// - i64
100+
/// - f64
101+
/// - String
102+
/// - Vec<u8> (binary data)
37103
Read,
104+
/// Returns the transaction ID for a newly created transaction.
105+
///
106+
/// # Fields
107+
/// * `tx_id` - The ID of the newly created transaction
38108
BeginTx { tx_id: u64 },
109+
/// Indicates an error occurred during the operation.
39110
Err(SqliteError),
40111
}
41112

42-
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
113+
/// Used in blobs to represent array row values in SQLite.
114+
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
43115
pub enum SqlValue {
44116
Integer(i64),
45117
Real(f64),
@@ -49,28 +121,50 @@ pub enum SqlValue {
49121
Null,
50122
}
51123

52-
#[derive(Debug, Serialize, Deserialize, Error)]
124+
#[derive(Clone, Debug, Serialize, Deserialize, Error)]
53125
pub enum SqliteError {
54-
#[error("sqlite: DbDoesNotExist")]
55-
NoDb,
56-
#[error("sqlite: NoTx")]
57-
NoTx,
58-
#[error("sqlite: No capability: {error}")]
59-
NoCap { error: String },
60-
#[error("sqlite: UnexpectedResponse")]
61-
UnexpectedResponse,
62-
#[error("sqlite: NotAWriteKeyword")]
126+
#[error("db [{0}, {1}] does not exist")]
127+
NoDb(PackageId, String),
128+
#[error("no transaction {0} found")]
129+
NoTx(u64),
130+
#[error("no write capability for requested DB")]
131+
NoWriteCap,
132+
#[error("no read capability for requested DB")]
133+
NoReadCap,
134+
#[error("request to open or remove DB with mismatching package ID")]
135+
MismatchingPackageId,
136+
#[error("failed to generate capability for new DB")]
137+
AddCapFailed,
138+
#[error("write statement started with non-existent write keyword")]
63139
NotAWriteKeyword,
64-
#[error("sqlite: NotAReadKeyword")]
140+
#[error("read query started with non-existent read keyword")]
65141
NotAReadKeyword,
66-
#[error("sqlite: Invalid Parameters")]
142+
#[error("parameters blob in read/write was misshapen or contained invalid JSON objects")]
67143
InvalidParameters,
68-
#[error("sqlite: IO error: {error}")]
69-
IOError { error: String },
70-
#[error("sqlite: rusqlite error: {error}")]
71-
RusqliteError { error: String },
72-
#[error("sqlite: input bytes/json/key error: {error}")]
73-
InputError { error: String },
144+
#[error("sqlite got a malformed request that failed to deserialize")]
145+
MalformedRequest,
146+
#[error("rusqlite error: {0}")]
147+
RusqliteError(String),
148+
#[error("IO error: {0}")]
149+
IOError(String),
150+
}
151+
152+
/// The JSON parameters contained in all capabilities issued by `sqlite:distro:sys`.
153+
///
154+
/// # Fields
155+
/// * `kind` - The kind of capability, either [`SqliteCapabilityKind::Read`] or [`SqliteCapabilityKind::Write`]
156+
/// * `db_key` - The database key, a tuple of the [`PackageId`] that created the database and the database name
157+
#[derive(Clone, Debug, Serialize, Deserialize)]
158+
pub struct SqliteCapabilityParams {
159+
pub kind: SqliteCapabilityKind,
160+
pub db_key: (PackageId, String),
161+
}
162+
163+
#[derive(Clone, Debug, Serialize, Deserialize)]
164+
#[serde(rename_all = "lowercase")]
165+
pub enum SqliteCapabilityKind {
166+
Read,
167+
Write,
74168
}
75169

76170
/// Sqlite helper struct for a db.
@@ -95,7 +189,7 @@ impl Sqlite {
95189
.body(serde_json::to_vec(&SqliteRequest {
96190
package_id: self.package_id.clone(),
97191
db: self.db.clone(),
98-
action: SqliteAction::Read { query },
192+
action: SqliteAction::Query(query),
99193
})?)
100194
.blob_bytes(serde_json::to_vec(&params)?)
101195
.send_and_await_response(self.timeout)?;
@@ -106,15 +200,11 @@ impl Sqlite {
106200

107201
match response {
108202
SqliteResponse::Read => {
109-
let blob = get_blob().ok_or_else(|| SqliteError::InputError {
110-
error: "sqlite: no blob".to_string(),
111-
})?;
203+
let blob = get_blob().ok_or_else(|| SqliteError::MalformedRequest)?;
112204
let values = serde_json::from_slice::<
113205
Vec<HashMap<String, serde_json::Value>>,
114206
>(&blob.bytes)
115-
.map_err(|e| SqliteError::InputError {
116-
error: format!("sqlite: gave unparsable response: {}", e),
117-
})?;
207+
.map_err(|_| SqliteError::MalformedRequest)?;
118208
Ok(values)
119209
}
120210
SqliteResponse::Err(error) => Err(error.into()),

0 commit comments

Comments
 (0)