# SQL Database API

A SQL Database API do Azion Runtime permite que uma function leia um banco de dados do [SQL Database](/pt-br/documentacao/plataforma/sql-database/). A function abre o banco de dados pelo nome, executa queries SQL com parâmetros posicionais ou nomeados, prepara statements e lê o resultado linha a linha. Cinco objetos compõem a API: `Database`, `Connection`, `Statement`, `Rows` e `Row`.

> **nota**
>
> Com `azion dev`, `Azion.Sql` é `undefined`, e a function falha com `Cannot destructure property 'Database' of 'Azion.Sql' as it is undefined.` Teste as leituras do banco de dados em uma function com deploy feito.

---

## Acesso

A classe `Database` fica no global `Azion.Sql`. Leia a classe no início da function:

```javascript
const { Database } = Azion.Sql;
```

O import de módulo `import { Database } from "azion:sql"` não passa no build: o build para com `Could not resolve "azion:sql"`. Use o global no lugar dele.

---

## Database

`Database` abre uma conexão com um banco de dados da sua conta. A classe recebe o nome do banco de dados e nenhum token.

| Método                    | Descrição                                                    | Parâmetros     | Retorno      |
| ------------------------- | ------------------------------------------------------------ | -------------- | ------------ |
| `static async open(name)` | Abre uma conexão com a réplica de leitura do banco de dados. | `name`: string | `Connection` |

A conexão é somente leitura. Uma instrução que escreve, como `insert` ou `delete`, falha com ``Error: SQLite failure: `attempt to write a readonly database` ``. Para escrever linhas, execute as instruções pela Azion API. Para mais informações, consulte [Bancos de dados e consultas](/pt-br/documentacao/plataforma/sql-database/bancos-de-dados-e-consultas/).

---

## Connection

Uma `Connection` é o canal para um banco de dados. `Database.open()` retorna a conexão.

| Método                       | Descrição                                                          | Parâmetros                               | Retorno     |
| ---------------------------- | ------------------------------------------------------------------ | ---------------------------------------- | ----------- |
| `async query(sql, params)`   | Executa uma instrução SQL e retorna o conjunto de resultados dela. | `sql`: string; `params`: array ou objeto | `Rows`      |
| `async execute(sql, params)` | Executa uma instrução SQL sem conjunto de resultados.              | `sql`: string; `params`: array ou objeto | —           |
| `async prepare(sql)`         | Prepara uma instrução SQL para execuções posteriores.              | `sql`: string                            | `Statement` |

Uma conexão também tem os métodos `close()` e `tryClose()`.

### Parâmetros

A string `sql` aceita dois tipos de parâmetro, e `params` carrega os valores deles:

- **Posicional**: um `?` na instrução, com os valores em um array, na ordem. `query("select name from users where id = ?", [2])` retorna `User2`.
- **Nomeado**: um `:<param_name>` na instrução, com os valores em um objeto. Cada chave mantém os dois-pontos: `{ ":id": 3 }` associa `:id` e retorna `User3`. A chave `id` sem os dois-pontos não associa nada, e a query não retorna nenhuma linha.

Valores inteiros são associados. Uma string JavaScript passada como valor de parâmetro é recusada em `query` e `execute` com ``TypeError: unknown variant `String`, expected one of `Null`, `Integer`, `Real`, `Text`, `Blob` ``.

---

## Statement

Um `Statement` é uma instrução SQL preparada uma vez e executada com os valores dos parâmetros dela. `Connection.prepare()` retorna o statement.

| Método                 | Descrição                                                                                                      | Parâmetros                | Retorno          |
| ---------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------- | ---------------- |
| `async query(params)`  | Executa o statement com os valores em `params` e retorna o conjunto de resultados dele.                        | `params`: array ou objeto | `Rows`           |
| `parameterCount()`     | Retorna o número de parâmetros do statement, por exemplo `1` para um `?`.                                      | —                         | inteiro          |
| `parameterName(index)` | Retorna o nome do parâmetro na posição `index`. Um parâmetro `?` não tem nome, e o método retorna `null`.      | `index`: inteiro          | string ou `null` |
| `columns()`            | Retorna um objeto por coluna do resultado: `name`, `origin_name`, `table_name`, `database_name` e `decl_type`. | —                         | array de objetos |

Um statement também tem os métodos `execute()`, que o executa sem conjunto de resultados, e `tryClose()`.

Passe os valores dos parâmetros para o `query()` do statement, não para `prepare()`. Valores passados para `prepare()` não são associados: `prepare("select name from users where id = ?", [1])` seguido de `query()` não retorna nenhuma linha.

Para `select name from users where id = ?` em uma tabela `users`, `columns()` retorna:

```json
[
 {
  "name": "name",
  "origin_name": "name",
  "table_name": "users",
  "database_name": "main",
  "decl_type": "TEXT"
 }
]
```

---

## Rows

Um objeto `Rows` é o conjunto de resultados que uma query retorna. Leia o resultado uma linha por vez com `next()`.

| Método              | Descrição                                                                                                         | Parâmetros       | Retorno         |
| ------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------- | --------------- |
| `async next()`      | Retorna a próxima linha do resultado, ou `null` depois da última linha.                                           | —                | `Row` ou `null` |
| `columnCount()`     | Retorna o número de colunas do resultado.                                                                         | —                | inteiro         |
| `columnName(index)` | Retorna o nome da coluna na posição `index`.                                                                      | `index`: inteiro | string          |
| `columnType(index)` | Retorna o código de tipo da coluna na posição `index`: `1` para uma coluna `INTEGER`, `3` para uma coluna `TEXT`. | `index`: inteiro | inteiro         |

---

## Row

Um `Row` contém os valores de uma linha de um conjunto de resultados. As colunas são endereçadas por `index`, a partir de `0`.

| Método              | Descrição                                                                                                         | Parâmetros       | Retorno          |
| ------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------- | ---------------- |
| `columnName(index)` | Retorna o nome da coluna na posição `index`.                                                                      | `index`: inteiro | string           |
| `columnType(index)` | Retorna o código de tipo da coluna na posição `index`: `1` para uma coluna `INTEGER`, `3` para uma coluna `TEXT`. | `index`: inteiro | inteiro          |
| `getValue(index)`   | Retorna o valor no tipo dele: um número para uma coluna `INTEGER`, uma string para uma coluna `TEXT`.             | `index`: inteiro | número ou string |
| `getString(index)`  | Retorna o valor como string, por exemplo `"1"` para o inteiro `1`.                                                | `index`: inteiro | string           |

---

## Erros

`Database.open()`, `query()` e `execute()` lançam os erros abaixo. Capture o erro e leia o `name` e a `message` dele.

| Erro                                                                                                | Causa                                                                                                     |
| --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `EdgeSqlError: Database not found: <name>, maybe it wasn't propagated to edge yet`                  | `Database.open()` recebeu um nome que não corresponde a nenhum banco de dados da conta. Verifique o nome. |
| ``Error: SQLite failure: `no such table: <table>` ``                                                | A instrução cita uma tabela que o banco de dados não tem.                                                 |
| ``Error: SQLite failure: `attempt to write a readonly database` ``                                  | A instrução escreve. A conexão lê uma réplica; escreva pela Azion API.                                    |
| ``TypeError: unknown variant `String`, expected one of `Null`, `Integer`, `Real`, `Text`, `Blob` `` | Um valor de parâmetro é uma string JavaScript.                                                            |

---

## Exemplo

Esta function lê todas as linhas da tabela `users` em `my-database` e retorna a tabela como texto, uma linha da tabela por linha de texto, com `|` entre os valores:

```javascript
const { Database } = Azion.Sql;

async function db_query() {
  let connection = await Database.open("my-database");
  let rows = await connection.query("select * from users");
  let column_count = rows.columnCount();
  let column_names = [];
  for (let i = 0; i < column_count; i++) {
    column_names.push(rows.columnName(i));
  }
  let response_lines = [];
  response_lines.push(column_names.join("|"));
  let row = await rows.next();
  while (row) {
    let row_items = [];
    for (let i = 0; i < column_count; i++) {
      row_items.push(row.getString(i));
    }
    response_lines.push(row_items.join("|"));
    row = await rows.next();
  }
  const response_text = response_lines.join("\n");
  return response_text;
}

async function handle_request(request) {
  if (request.method != "GET") {
    return new Response("Method not allowed", { status: 405 });
  }
  try {
    return new Response(await db_query());
  } catch (e) {
    console.log(e.message, e.stack);
    return new Response(e.message, { status: 500 });
  }
}

addEventListener("fetch", (event) =>
  event.respondWith(handle_request(event.request))
);
```

Com uma tabela `users` de duas colunas, `id` e `name`, e quatro linhas, uma requisição `GET` para a function com deploy feito retorna:

```text
id|name
1|User1
2|User2
3|User3
4|User4
```

---

## Recursos relacionados

- [Bancos de dados e consultas](/pt-br/documentacao/plataforma/sql-database/bancos-de-dados-e-consultas.md): Crie um banco de dados e escreva as linhas dele pela Azion API.
- [Limites do SQL Database](/pt-br/documentacao/plataforma/sql-database/limites.md): Os limites de um banco de dados, como o tamanho do nome e as colunas por tabela.
- [Biblioteca SQL da Azion](/pt-br/documentacao/devtools/azion-lib/sql.md): O pacote `@aziontech/sql`, que encapsula as operações da Azion API em um banco de dados para Node e TypeScript.
- [Handlers](/pt-br/documentacao/devtools/runtime/api-reference/handlers.md): Os formatos de handler que uma function exporta e a requisição que cada um recebe.
