Skip to content

Commit

Permalink
doc: document api/sql HTTP endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean Loiselle committed Sep 24, 2022
1 parent a33bf43 commit 4342d30
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 9 deletions.
196 changes: 196 additions & 0 deletions doc/user/content/integrations/http-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
---
title: "Connect to Materialize via HTTP"
description: "How to use Materialize via HTTP"
menu:
main:
parent: "integrations"
weight: 6
name: "HTTP API"
---

You can access Materialize through its "session-less" HTTP API, reachable at
`https://<MZ host address>/api/sql`.

## Details

### General semantics

The API:

- Requires username/password authentication, just as connecting via `psql`.
Materialize provides you the username and password upon setting up your
account.
- Requires that you provide the entirety of your request. The API does not
provide session-like semantics, so there is no way to e.g. interactively use
transactions.
- Ceases to process requests upon encountering the first error.
- Does not support statements whose semantics rely on sessions or whose state is
indeterminate at the end of processing, including:
- `CLOSE`
- `COPY`
- `DECLARE`
- `SET`
- `TAIL`

### Transactional semantics

The HTTP API provides two modes with slightly different transactional semantics from one another:

- **Simple**, which mirrors PostgreSQL's [Simple Query][simple-query] protocol.
- Supports a single query, but the single query string may contain multiple
statements, e.g. `SELECT 1; SELECT 2;`
- Treats all statements as in an implicit transaction unless other
transaction control is invoked.
- **Extended**, which mirrors PostgreSQL's [Extended Query][extended-query] protocol.
- Supports multiple queries, but only one statement per query string.
- Supports parameters.
- Eagerly commits DDL (e.g. `CREATE TABLE`) in implicit transactions, but
not DML (e.g. `INSERT`).

## Usage

### Endpoint

```
https://<MZ host address>/api/sql
```

Accessing the endpoint requires username + password authentication.

### Input format

#### Simple

The request body is a JSON object containing a key, `query`, which specifies the
SQL string to execute. `query` may contain multiple SQL statements separated by
semicolons.

```json
{
"query": "select * from a; select * from b;"
}
```

#### Extended

The request body is a JSON object containing a key `queries`, whose value is
array of objects, whose structure is:

Key | Value
----|------
`query` | A SQL string containing one statement to execute
`params` | An optional array of text values to be used as the parameters to `query`. _null_ values are converted to _null_ values in Materialize. Note that all parameter values' elements must be text or _null_; the API will not accept JSON numbers.

```json
{
"queries": [
{ "query": "select * from a;" },
{ "query": "select a + $1 from a;", "params": ["100"] }
{ "query": "select a + $1 from a;", "params": [null] }
]
}
```

#### TypeScript definition

You can model these with the following TypeScript definitions:

```typescript
interface Simple {
query: string;
}

interface ExtendedRequest {
query: string;
params?: (string | null)[];
}

interface Extended {
queries: ExtendedRequest;
}

type Sql = Simple | Extended;
```

### Output format

The output format is a JSON object with one key, `results`, whose value is
an array of the following:

Result | JSON value
---------------------|------------
Rows | `{"rows": <2D array of JSON-ified results>, "col_names": <array of text>}`
Error | `{"error": <Error string from execution>}`
Ok | `{ "ok": <tag>}`

Each committed statement returns exactly one of these values; e.g. in the case
of "complex responses", such as `INSERT INTO...RETURNING`, the presence of a
`"rows"` object implies `"ok"`.

Note that the returned values include the results of statements which were
ultimately rolled back because of an error in a later part of the transaction.
You must parse the results to understand which statements ultimately reflect
the resultant state.

## Examples

```bash
curl https://`https://<MZ host address>/api/sql/api/sql \
--header "Content-Type: application/json" \
--user <username>:<passsword> \
--data '{
"queries": [
{ "query": "CREATE TABLE IF NOT EXISTS t (a int);" },
{ "query": "CREATE TABLE IF NOT EXISTS s (a int);" },
{ "query": "BEGIN;" },
{ "query": "INSERT INTO t VALUES ($1), ($2)", "params": ["100", "200"] },
{ "query": "COMMIT;" },
{ "query": "BEGIN;" },
{ "query": "INSERT INTO s VALUES ($1), ($2)", "params": ["9", null] },
{ "query": "COMMIT;" }
]
}'
```
```json
{
"results": [
{"ok": "CREATE TABLE"},
{"ok": "CREATE TABLE"},
{"ok": "BEGIN"},
{"ok": "INSERT 0 2"},
{"ok": "COMMIT"},
{"ok": "BEGIN"},
{"ok": "INSERT 0 2"},
{"ok": "COMMIT"}
]
}
```

```bash
curl https://`https://<MZ host address>/api/sql/api/sql \
--header "Content-Type: application/json" \
--user <username>:<passsword> \
--data '{
"query": "SELECT t.a + s.a AS cross_add FROM t CROSS JOIN s; SELECT a FROM t WHERE a IS NOT NULL;"
}'
```
```json
{
"results": [
{
"rows": [[null],[null],[109],[209]],
"col_names": ["cross_add"]
},
{
"rows":[[100],[200]],
"col_names":["a"]
}
]
}
```

## See also
- [SQL Clients](../sql-clients)

[simple-query]: https://www.postgresql.org/docs/current/protocol-flow.html#id-1.10.5.7.4
[extended-query]: https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY
26 changes: 17 additions & 9 deletions test/pgtest/ddl-extended.pt
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ send
Parse {"query": "CREATE TABLE a (a int)"}
Bind
Execute
Parse {"query": "ROLLBACK"}
Bind
Execute
Parse {"query": "CREATE TABLE b (a int)"}
Bind
Execute
Parse {"query": "SELECT * FROM a"}
Parse {"query": "INSERT INTO a VALUES (1)"}
Bind
Execute
Parse {"query": "SELECT * FROM b"}
Parse {"query": "SELECT 1/0"}
Bind
Execute
Sync
Expand All @@ -26,14 +23,25 @@ BindComplete
CommandComplete {"tag":"CREATE TABLE"}
ParseComplete
BindComplete
NoticeResponse {"fields":[{"typ":"S","value":"WARNING"},{"typ":"C","value":"25P01"},{"typ":"M","value":"there is no transaction in progress"}]}
CommandComplete {"tag":"ROLLBACK"}
CommandComplete {"tag":"CREATE TABLE"}
ParseComplete
BindComplete
CommandComplete {"tag":"CREATE TABLE"}
CommandComplete {"tag":"INSERT 0 1"}
ParseComplete
BindComplete
CommandComplete {"tag":"SELECT 0"}
ErrorResponse {"fields":[{"typ":"S","value":"ERROR"},{"typ":"C","value":"XX000"},{"typ":"M","value":"division by zero"}]}
ReadyForQuery {"status":"I"}

send
Parse {"query": "SELECT * FROM a"}
Bind
Execute
Sync
----

until
ReadyForQuery
----
ParseComplete
BindComplete
CommandComplete {"tag":"SELECT 0"}
Expand Down

0 comments on commit 4342d30

Please sign in to comment.