Skip to content

updated sql.wit.md, and README.md #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 30, 2023
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: WebAssembly/wit-abi-up-to-date@v6
with:
wit-abi-tag: wit-abi-0.6.0
# - uses: WebAssembly/wit-abi-up-to-date@v6
# with:
# wit-abi-tag: wit-abi-0.6.0
65 changes: 42 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
# [Example WASI proposal]

This template can be used to start a new proposal, which can then be proposed in the WASI Subgroup meetings.

The sections below are recommended. However, every proposal is different, and the community can help you flesh out the proposal, so don't block on having something filled in for each one of them.

Thank you to the W3C Privacy CG for the [inspiration](https://github.com/privacycg/template)!

# [Title]
# `wasi-sql`

A proposed [WebAssembly System Interface](https://github.com/WebAssembly/WASI) API.

### Current Phase

[Fill in the current phase, e.g. Phase 1]
`wasi-messaging` is currently in [Phase 1](https://github.com/WebAssembly/WASI/blob/main/Proposals.md#phase-1---feature-proposal-cg).

### Champions

- [Champion 1]
- [Champion 2]
- [etc.]
- [Dan Chiarlone](https://github.com/danbugs)
- [David Justice](https://github.com/devigned)
- [Jiaxiao Zhou](https://github.com/Mossaka)

### Phase 4 Advancement Criteria

TODO before entering Phase 2.
`wasi-sql` should have at least two implementations (i.e., from service providers, and or cloud providers), and, at the very minimum, pass the testsuite for Windows, Linux, and MacOS.

## Table of Contents [if the explainer is longer than one printed page]
## Table of Contents

- [Introduction](#introduction)
- [Goals [or Motivating Use Cases, or Scenarios]](#goals-or-motivating-use-cases-or-scenarios)
Expand All @@ -43,27 +35,54 @@ TODO before entering Phase 2.

### Introduction

[The "executive summary" or "abstract". Explain in a few sentences what the goals of the project are, and a brief overview of how the solution works. This should be no more than 1-2 paragraphs.]
The `wasi-sql` interface allows WebAssembly programs to interact with SQL databases in a generic and safe way. It provides functions for querying and modifying data, using prepared statements and handling errors. The interface is flexible and consistent, supporting various SQL flavors.

### Goals

### Goals [or Motivating Use Cases, or Scenarios]
The `wasi-sql` interface aims to provide a consistent and easy-to-use way for WebAssembly programs to access and manipulate data stored in SQL databases. It targets the features commonly used by 80% of user applications. By focusing on commonly used features, the interface aims to provide a simple and reliable way to build stateful services that access SQL databases.

[What is the end-user need which this project aims to address?]
The `wasi-sql` interface abstracts away specific SQL flavors and database APIs, allowing WebAssembly programs to be portable across different SQL databases that support the interface. It also abstracts away the network stack, allowing WebAssembly programs to access SQL databases without worrying about the specific network protocol used. This allows users to focus on building their applications, rather than communication details with the database.

### Non-goals

[If there are "adjacent" goals which may appear to be in scope but aren't, enumerate them here. This section may be fleshed out as your design progresses and you encounter necessary technical and other trade-offs.]
- The `wasi-sql` interface does not aim to provide support for every possible feature of SQL databases. Instead, it focuses on the features that are commonly used by 80% of user applications.
- The `wasi-sql` interface does not aim to provide support for specific database APIs or network protocols. It abstracts away these implementation details to allow WebAssembly programs to be portable across different SQL databases that support the interface.
- The `wasi-sql` interface does not aim to address control-plane behavior or functionality, such as cluster management, monitoring, data consistency, replication, or sharding. These are provider-specific and are not specified by the wasi-sql interface.

### API walk-through

[Walk through of how someone would use this API.]

#### [Use case 1]
#### Use case 1: Query data from a table

[Provide example code snippets and diagrams explaining how the API would be used to solve the given problem]
Imagine you have a WebAssembly program that needs to query data from a table in a SQL database. The following Rust code shows how you can use the `wasi-sql` interface to execute a SELECT statement and iterate over the resulting rows.

#### [Use case 2]
```rs
// Create a prepared statement
let stmt = sql::statement::create("SELECT * FROM users WHERE name = ? AND age = ?", vec!["John Doe", "32"])?;

[etc.]
// Execute the query and get the result set
let result = sql::query(stmt)?;

// Iterate over the rows in the result set
while let Some(row) = result.next() {
// Print the column names and values for the current row
println!("Column name: {:?}", row.field_name);
println!("Value: {:?}", row.value);
}
```

#### Use case 2: Insert data into a table

Imagine you have a WebAssembly program that needs to insert a row into a table in a SQL database. The following Rust code shows how you can use the wasi-sql interface to execute an INSERT statement.

```rs
// Create a prepared statement
let stmt = sql::statement::create("INSERT INTO users (name, age) VALUES (?, ?)", vec!["Jane Doe", "30"])?;

// Execute the statement
sql::exec(stmt)?;
```

### Detailed design discussion

Expand Down
31 changes: 0 additions & 31 deletions proposal-template.abi.md

This file was deleted.

32 changes: 0 additions & 32 deletions proposal-template.wit.md

This file was deleted.

71 changes: 71 additions & 0 deletions sql.wit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# `wasi-sql` API

## Interfaces

```wit
interface "wasi:sql" {
// iterator item type
record item {
field-name: string
values: data-type
index: u32
}

// common data types
variant data-type {
int32(s32),
int64(s64),
uint32(u32),
uint64(u64),
float(float64),
double(float64),
str(string),
boolean(bool),
date(string),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting... are you thinking the standard would define a canonical string format and implementations would adapt however they saw the database's DATE type into that string format? (I don't have a better plan.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. Initially, I was thinking of just relying on the format used by the underlying database. On the other hand, it might be a good idea to define a standardized format for storing dates in the interface itself for consistency's sake — we could use ISO 8601, or smt of the sort. That said, I'm not sure. How do you feel about just relying on the underlying format?

Copy link

@itowlson itowlson Dec 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what "the underlying format" is. When I ask SQL Server for a datetimeoffset column, I assume it is stored in some opaque binary format, and surfaced as a System.DateTimeOffset or chrono::DateTime or whatever type by the driver. So we would probably need to format it anyway.

I could be wrong though - could you expand on your understanding of "underlying format"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@itowlson ~ I tried out implementing this interface for postgres, and here's what I did w/ regard to this:

                        "date" => {
                            let v: String = row.get(i);
                            let parsed = NaiveDate::parse_from_str(&v, "%Y-%m-%d").unwrap();
                            DataType::Date(parsed.to_string())
                        }
                        "time" => {
                            let v: String = row.get(i);
                            let parsed = NaiveTime::parse_from_str(&v, "%H:%M:%S").unwrap();
                            DataType::Time(parsed.to_string())
                        }
                        "timestamp" => {
                            let v: String = row.get(i);
                            let parsed =
                                NaiveDateTime::parse_from_str(&v, "%Y-%m-%d %H:%M:%S").unwrap();
                            DataType::Timestamp(parsed.to_string())
                        }

time(string),
timestamp(string),
binary(list<u8>),
null
}

// iterator for item
resource row {
next: func() -> option<row>
}

// allows parameterized queries
resource statement {
// e.g., create("SELECT * FROM users WHERE name = ? AND age = ?", vec!["John Doe", "32"])
create: func(s: string, p: list<string>) -> result<statement, error>
}

// query is optimized for querying data, and
// implementors can make use of that fact to optimize
// the performance of query execution (e.g., using
// indexes).
query: func(q: statement) -> result<row, error>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect the query() to be able to return multiple rows. Is there a plan to add a new resource as collection of rows?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks at the moment like query is returning the first row, and then you call next to get to the next row - this fits with the sample in the read-me. But then I can't see how to get to an item. @danbugs I think something might have got out of whack during the various revisions - looking at the read-me has row gotten mixed up with item in some places?

FWIW, the Spin prototype has query returning a rowset which contained a list of row, and a row contained a list of value (the field info was separate). However that doesn't handle very large datasets well (everything has to be realised into the list). This might be remedies by streams, I'm not sure; but in the meantime, a resource that fetches the next record on demand (potentially lazily) makes sense to me. Not sure if there are performance considerations relating to going across a marshalling boundary on every row though.

Copy link

@chrisgacsal chrisgacsal Dec 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking at the read-me has row gotten mixed up with item in some places?

It made me confused hence my comments.

This might be remedies by streams

I assume that stream would be the right solution here at some point, might be wrong though.

a resource that fetches the next record on demand (potentially lazily) makes sense to me

That would my expectation here as well.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering about how to get the item as well. I also wonder, does query() block until a row is ready, or does it return immediately (possibly with an error indicating the query couldn't be run) and calling row.next() blocks until the row is ready? I'm just wondering if it will be possible to fire queries on multiple connections in the case where the queries are long running and you can allow to server to process them simultaneously.


// exec is for modifying data in the database.
exec: func(q: statement) -> result<_, error>

// common error types
variant error {
syntax-error(string),
constraint-violation(string),
access-violation(string)
}
}
```

## Worlds

```wit
world "wasi:sql/http-sql" {
import sql: "wasi:sql"

export handle-create: "wasi:http/handler"
export handle-read: "wasi:http/handler"
export handle-update: "wasi:http/handler"
export handle-delete: "wasi:http/handler"
}
```