> ## Documentation Index
> Fetch the complete documentation index at: https://villagesql.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Referência da API Rust

> Referência completa do SDK Rust do VillageSQL — InValue, VdfReturn, extension!, func!, agg_func!, varargs_func!, custom_type!, custom! e campos do manifest.json.

<Warning>
  O SDK Rust está em alpha — espere mudanças incompatíveis de API entre
  versões. Extensões apenas de funções, funções de agregação, funções varargs
  e tipos personalizados (encode, decode, compare, hash) são compatíveis,
  assim como as capabilities Preview `sys_var`, `status_var`, `thread_worker`
  e `keyring`. A ABI de armazenamento de coluna é exclusiva do C++ hoje — use
  o [SDK C++](/docs/pt-BR/mysql-9.7/stable/create) se você precisar dela.
</Warning>

Esta página é uma referência da API do crate `villagesql`. Para o tutorial de introdução, consulte [Criando Extensões em Rust](/docs/pt-BR/mysql-9.7/stable/rust-sdk). Para tipos personalizados, consulte [Tipos Personalizados em Rust](/docs/pt-BR/mysql-9.7/stable/rust-custom-types).

## InValue

`InValue` é o enum que o servidor passa para cada argumento de função. Sua função recebe `args: &[InValue]` e deve verificar cada argumento antes de usar seu valor.

```rust theme={null}
pub enum InValue<'a> {
    String(&'a str),
    Real(f64),
    Int(i64),
    Null,
    Custom(&'a [u8]),
    CustomWithParams { bytes: &'a [u8], params: TypeParams<'a> },
}
```

| Variante                             | Tipo Rust                    | Tipo SQL correspondente                                                                                                                   |
| ------------------------------------ | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `String(&str)`                       | Fatia de string UTF-8        | `STRING` / `VARCHAR` / `TEXT`                                                                                                             |
| `Real(f64)`                          | Ponto flutuante de 64 bits   | `REAL` / `DOUBLE` / `FLOAT`                                                                                                               |
| `Int(i64)`                           | Inteiro com sinal de 64 bits | `INT` / `BIGINT` / `TINYINT`                                                                                                              |
| `Null`                               | —                            | `NULL` do SQL para qualquer tipo                                                                                                          |
| `Custom(&[u8])`                      | Bytes binários brutos        | Qualquer tipo personalizado registrado via `custom_type!`                                                                                 |
| `CustomWithParams { bytes, params }` | Bytes brutos + `TypeParams`  | Um tipo personalizado parametrizado registrado via [`parameterized_type!`](/docs/pt-BR/mysql-9.7/stable/rust-custom-types#parameterized-types) |

Sempre faça a correspondência de `Null` explicitamente. Chamar `.unwrap()` ou fazer a correspondência de padrão apenas das variantes de valor é um bug — o NULL do SQL é uma entrada normal, não um erro.

## VdfReturn

`VdfReturn` é o que sua função retorna ao servidor. Construa-o com uma das funções associadas:

| Construtor                 | Efeito no SQL                                                                     |
| -------------------------- | --------------------------------------------------------------------------------- |
| `VdfReturn::null()`        | Retorna NULL do SQL para esta linha                                               |
| `VdfReturn::string(s)`     | Retorna um valor `String`; `s` é `impl Into<String>`                              |
| `VdfReturn::real(v)`       | Retorna um valor `f64`                                                            |
| `VdfReturn::int(v)`        | Retorna um valor `i64`                                                            |
| `VdfReturn::binary(bytes)` | Retorna bytes binários para uma coluna de tipo personalizado; `bytes` é `Vec<u8>` |
| `VdfReturn::warning(msg)`  | Retorna NULL para esta linha, adiciona um aviso SQL, a execução continua          |
| `VdfReturn::error(msg)`    | Aborta a instrução com um erro fatal                                              |

**Aviso vs. erro:**

Use `warning` para falhas de validação de entrada do usuário em que faz sentido continuar com o restante do conjunto de resultados. No modo estrito, o MySQL promove avisos a erros em `INSERT` e `UPDATE`. Use `error` para condições em que prosseguir é inseguro: dados armazenados corrompidos, violações de invariantes internas. Um erro fatal aborta a instrução inteira.

```rust theme={null}
fn validate_impl(args: &[InValue]) -> VdfReturn {
    match args.first() {
        Some(InValue::Int(n)) if *n >= 0 => VdfReturn::int(*n),
        Some(InValue::Int(_)) => VdfReturn::warning("value must be non-negative"),
        Some(InValue::Null) | None => VdfReturn::null(),
        _ => VdfReturn::error("validate: expected an INT argument"),
    }
}
```

## macro extension!

`extension!` gera os pontos de entrada VEF que o servidor chama ao carregar seu arquivo VEB. Ele deve aparecer exatamente uma vez no crate.

```rust theme={null}
villagesql::extension! {
    funcs: [
        // One or more villagesql::func!(...) declarations
    ],
    types: [
        // One or more villagesql::custom_type!(...) declarations
    ],
    requires: [
        // Zero or more &'static capability references, e.g. &KEYRING
    ]
}
```

`types:` e `requires:` são opcionais por si só, mas `funcs:` deve estar sempre presente — escreva `funcs: []` para uma extensão apenas de tipos. Uma extensão apenas de funções omite `types:`. Um bloco `extension!` com `funcs: []` e sem tipos é válido, mas produz uma extensão que não faz nada.

`requires:` declara as [capabilities Preview](/docs/pt-BR/mysql-9.7/stable/rust-preview-capabilities) que a extensão usa, como referências a objetos `static` de capability. Ela deve vir por último, depois de uma seção `funcs:` — inclua `funcs: []` se a extensão não registrar nenhuma função.

## macro func!

`func!` declara uma função chamável via SQL. Seis formas — quatro sem estado por instrução (sem parâmetros, apenas `buffer_size`, apenas `deterministic`, ambos) e duas que anexam estado por instrução por meio de uma função `prerun`:

```rust theme={null}
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, buffer_size: N)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, deterministic: true)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, buffer_size: N, deterministic: true)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, state: StateType, prerun: prerun_fn)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, state: StateType, prerun: prerun_fn, buffer_size: N, deterministic: true)
```

<Note>
  O parâmetro `buffer_size` requer o crate `villagesql` **0.0.2 ou
  posterior**. A versão atual do [crates.io](https://crates.io/crates/villagesql)
  (`0.0.1`) não o expõe — até que o `0.0.2` seja lançado, use as formas sem
  `buffer_size`.
</Note>

| Argumento             | Descrição                                                                                                                                                                                                                                                                                                                      |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `rust_fn`             | A função Rust que implementa a VDF. Assinatura: `fn(&[InValue]) -> VdfReturn`, ou `fn(&mut StateType, &[InValue]) -> VdfReturn` para as formas `state:` / `prerun:`.                                                                                                                                                           |
| `"sql_name"`          | O nome da função SQL como um literal de string. É isso que os usuários chamam a partir do SQL.                                                                                                                                                                                                                                 |
| `[param_types]`       | Lista separada por vírgulas de valores `villagesql::Type::*` ou `villagesql::custom!("name")`. Use `[]` para funções de aridade zero.                                                                                                                                                                                          |
| `return_type`         | `villagesql::Type::*` ou `villagesql::custom!("name")`.                                                                                                                                                                                                                                                                        |
| `buffer_size: N`      | Opcional. Tamanho em bytes do buffer de resultado para retornos de string/binário. Use `0` para o padrão do servidor (256 bytes). Quando uma função retorna um valor de string ou binário maior que `buffer_size`, a função gera um erro em vez de truncar — declare um `buffer_size` maior para lidar com resultados maiores. |
| `deterministic: true` | Opcional. Declara a função como determinística — as mesmas entradas sempre produzem a mesma saída, sem efeitos colaterais. O otimizador pode armazenar em cache os resultados para entradas idênticas. Defina isso apenas quando for verdadeiro.                                                                               |

**Constantes de tipo** para uso em `func!`:

| `villagesql::Type::*`      | Tipo SQL |
| -------------------------- | -------- |
| `villagesql::Type::String` | `STRING` |
| `villagesql::Type::Real`   | `REAL`   |
| `villagesql::Type::Int`    | `INT`    |

<h3 id="per-statement-state">
  Estado por instrução
</h3>

Algumas funções precisam de um estado que abrange todas as linhas de uma única instrução — um contador de chamadas, um acumulador. Declare o tipo do estado com `state:` e uma função de configuração com `prerun:`. A função prerun é executada uma vez, antes da primeira linha; a função de linha então é executada uma vez por linha com acesso `&mut` a esse estado.

Uma função prerun tem a assinatura `fn(PrerunArgs, PrerunResult<T>)`, e a função de linha que ela alimenta recebe o estado primeiro: `fn(state: &mut T, args: &[InValue]) -> VdfReturn`. `T` é o tipo nomeado por `state:`, e o compilador verifica se a prerun e a função de linha concordam sobre ele.

| Método de `PrerunResult<T>`                | Efeito                                                                                                                                                 |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `set_state(self, state: T)`                | Aloca o estado por instrução e o entrega ao servidor. Ele consome `self` em vez de tomá-lo emprestado, então o compilador rejeita uma segunda chamada. |
| `request_buffer_size(&mut self, n: usize)` | Solicita ao servidor um tamanho específico de buffer de resultado.                                                                                     |
| `error(self, msg: &str)`                   | Faz a instrução inteira falhar com uma mensagem.                                                                                                       |

`PrerunArgs::len()` é o número de argumentos que cada linha vai receber, e `PrerunArgs::is_empty()` é verdadeiro quando a função foi chamada sem argumentos.

Você não deve liberar o estado por conta própria: `func!` gera a postrun que o descarta quando a instrução termina. Isso é o oposto do SDK C++, onde sua postrun tem que chamar `delete_state<T>()` — consulte [Estado por Instrução](/docs/pt-BR/mysql-9.7/stable/development#per-statement-state-prerun-and-postrun).

<Note>
  Os parâmetros `state` e `prerun` ainda não estão em uma versão publicada. A
  versão atual do [crates.io](https://crates.io/crates/villagesql) (`0.0.1`)
  não os expõe.
</Note>

Uma extensão completa cuja função retorna o próprio índice de chamada dentro da instrução:

```rust theme={null}
use villagesql::{InValue, PrerunArgs, PrerunResult, VdfReturn};

/// Per-statement state: how many times the row function has been called.
struct CallCounter {
    n: i64,
}

/// Runs once, before the first row: allocate the counter at zero.
fn call_index_prerun(_args: PrerunArgs, out: PrerunResult<CallCounter>) {
    out.set_state(CallCounter { n: 0 });
}

/// Runs once per row: bump the counter and return its new value.
fn call_index(state: &mut CallCounter, _args: &[InValue]) -> VdfReturn {
    state.n += 1;
    VdfReturn::int(state.n)
}

villagesql::extension! {
    funcs: [
        villagesql::func!(call_index, "call_index", [] -> villagesql::Type::Int,
            state: CallCounter, prerun: call_index_prerun),
    ]
}
```

Compile e instale a extensão conforme descrito em [Criando Extensões em Rust](/docs/pt-BR/mysql-9.7/stable/rust-sdk), e então:

```sql theme={null}
INSTALL EXTENSION vsql_call_index;
CREATE TABLE t (id INT);
INSERT INTO t VALUES (10), (20), (30);
SELECT SUM(vsql_call_index.call_index()) AS total FROM t;
-- → 6
SELECT SUM(vsql_call_index.call_index()) AS total FROM t;
-- → 6
```

A tabela tem três linhas, então `call_index()` é executada três vezes e retorna `1`, depois `2`, depois `3` — um valor por linha. `SUM` soma esses três valores, o que dá `6`.

O segundo `SELECT` retorna o mesmo total do primeiro, não um maior: o contador é alocado para uma instrução e descartado quando ela termina.

## macro agg\_func!

`agg_func!` declara uma função SQL de agregação — no estilo de SUM/COUNT, chamada sobre as linhas de cada grupo em vez de uma vez por linha. Duas formas:

```rust theme={null}
villagesql::agg_func!(result_fn, "sql_name", [param_types] -> return_type,
    state: StateType, clear: clear_fn, accumulate: accumulate_fn)
villagesql::agg_func!(result_fn, "sql_name", [param_types] -> return_type,
    state: StateType, clear: clear_fn, accumulate: accumulate_fn,
    buffer_size: N, deterministic: true)
```

| Argumento                                | Descrição                                                                                                                                                                                                                                                               |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `result_fn`                              | `fn(&State) -> VdfReturn`. Produz a saída de um grupo a partir do acumulador finalizado. É executada uma vez por grupo, depois que a última linha desse grupo foi incorporada. Recebe `&State`, não `&mut` — a função de resultado lê o acumulador, ela não o redefine. |
| `"sql_name"`                             | O nome da função SQL como um literal de string.                                                                                                                                                                                                                         |
| `[param_types]`                          | A lista de argumentos, igual à de `func!` — valores `villagesql::Type::*` ou `villagesql::custom!("name")`.                                                                                                                                                             |
| `return_type`                            | `villagesql::Type::*` ou `villagesql::custom!("name")`.                                                                                                                                                                                                                 |
| `state: StateType`                       | O tipo do acumulador. Ele deve implementar `Default`: `agg_func!` gera a prerun para você, e essa prerun inicializa o acumulador com `StateType::default()`. Derive `Default` ou implemente-o manualmente.                                                              |
| `clear: clear_fn`                        | `fn(&mut State)`. Redefine o acumulador no início de cada grupo.                                                                                                                                                                                                        |
| `accumulate: accumulate_fn`              | `fn(&mut State, &[InValue])`. Incorpora uma linha ao acumulador. É executada uma vez por linha. Não retorna nada — o valor só sai por meio de `result_fn`.                                                                                                              |
| `buffer_size: N` / `deterministic: true` | Opcionais, mas fornecidos juntos ou não fornecidos — `agg_func!` não tem formas de opção única como `func!` tem. Mesmo significado que em `func!` quando fornecidos.                                                                                                    |

<Note>
  `agg_func!` ainda não está em uma versão publicada. A versão atual do
  [crates.io](https://crates.io/crates/villagesql) (`0.0.1`) não o expõe.
</Note>

O acumulador é alocado uma vez por instrução e descartado quando a instrução termina — `agg_func!` gera tanto a prerun que o cria quanto a postrun que o descarta, então você nunca escreve nenhuma das duas. `clear_fn` é o que dá a você o comportamento por grupo: com `GROUP BY`, o mesmo acumulador é reutilizado entre os grupos, então qualquer campo que não deva vazar entre grupos tem que ser redefinido ali.

Uma agregação completa equivalente a SUM — o exemplo `vsql_agg_sum` no repositório do SDK:

```rust theme={null}
use villagesql::{InValue, VdfReturn};

/// Accumulator for `agg_sum`: the running total for the current group.
#[derive(Default)]
struct SumState {
    total: i64,
    seen: bool,
}

/// clear: reset the total at the start of each group.
fn agg_sum_clear(state: &mut SumState) {
    state.total = 0;
    state.seen = false;
}

/// accumulate: fold one row's int into the running total.
fn agg_sum_acc(state: &mut SumState, args: &[InValue]) {
    if let Some(InValue::Int(n)) = args.first() {
        state.total += *n;
        state.seen = true;
    }
}

/// result: emit the group's total once every row has been folded in.
fn agg_sum_result(state: &SumState) -> VdfReturn {
    if state.seen {
        VdfReturn::int(state.total)
    } else {
        VdfReturn::Null
    }
}

villagesql::extension! {
    funcs: [
        villagesql::agg_func!(agg_sum_result, "agg_sum",
            [villagesql::Type::Int] -> villagesql::Type::Int,
            state: SumState, clear: agg_sum_clear, accumulate: agg_sum_acc),
    ]
}
```

`accumulate` fazer a correspondência apenas de `InValue::Int` é o que pula os NULLs, correspondendo ao `SUM` embutido. O flag `seen` é o que faz um grupo todo de NULLs e um grupo vazio retornarem NULL em vez de `0`:

```sql theme={null}
INSTALL EXTENSION vsql_agg_sum;
CREATE TABLE t (grp INT, val INT);
INSERT INTO t VALUES (1, 10), (1, 20), (2, 100), (2, 200), (2, 300);
SELECT grp, vsql_agg_sum.agg_sum(val) AS mine, SUM(val) AS builtin
  FROM t GROUP BY grp ORDER BY grp;
-- grp  mine  builtin
--   1    30       30
--   2   600      600
```

## macro varargs\_func!

`varargs_func!` declara uma VDF que aceita qualquer número de argumentos, de qualquer tipo. A lista de parâmetros é escrita `[..]` — um literal obrigatório, não o `[]` usado para uma `func!` de aridade zero.

<Warning>
  O servidor não realiza nenhuma validação de contagem nem de tipo de
  argumentos para uma VDF varargs. Não há uma lista de parâmetros declarada
  contra a qual verificar uma chamada, então toda chamada chega à sua função
  com o que quer que o texto SQL tenha passado, incluindo zero argumentos e
  tipos que você nunca esperou. A validação é inteiramente trabalho do gancho
  `prerun`. O registro de varargs também requer o Protocol 3 do VEF —
  servidores mais antigos rejeitam a extensão no momento da instalação. Isso
  corresponde ao SDK C++, onde [o framework também não pode validar a contagem
  ou os tipos de argumento para VDFs com
  varargs](/docs/pt-BR/mysql-9.7/stable/development#varargs-vdfs).
</Warning>

Seis formas — três formatos, cada um com uma forma abreviada e uma forma completa que adiciona `buffer_size` e `deterministic` juntos (nunca isoladamente):

```rust theme={null}
// Per-statement state plus a validating prerun.
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type,
    state: StateType, prerun: prerun_fn)
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type,
    state: StateType, prerun: prerun_fn, buffer_size: N, deterministic: true)

// Validating prerun, no state.
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type,
    prerun: prerun_fn)
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type,
    prerun: prerun_fn, buffer_size: N, deterministic: true)

// Bare: no prerun, no state, no validation.
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type)
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type,
    buffer_size: N, deterministic: true)
```

| Argumento                                | Descrição                                                                                                                                                                                                                                                                                         |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `impl_fn`                                | A função de linha. `fn(&[InValue]) -> VdfReturn` para a forma apenas com prerun e para a forma básica; `fn(&mut StateType, &[InValue]) -> VdfReturn` para as formas com `state:`. O comprimento da fatia `args` varia de chamada para chamada.                                                    |
| `"sql_name"`                             | O nome da função SQL como um literal de string.                                                                                                                                                                                                                                                   |
| `[..]`                                   | Marca a função como varargs; não há lista de tipos.                                                                                                                                                                                                                                               |
| `return_type`                            | `villagesql::Type::*` ou `villagesql::custom!("name")`. O tipo de retorno é fixo mesmo que os argumentos não sejam.                                                                                                                                                                               |
| `state: StateType`                       | Opcional. Estado por instrução, alocado pela `prerun` e tomado emprestado como `&mut` por cada chamada de linha. Requer `prerun:`. Diferente de `agg_func!`, não há a exigência de `Default` — a sua prerun constrói o valor.                                                                     |
| `prerun: prerun_fn`                      | Opcional, e o único lugar onde a validação de argumentos pode acontecer. `fn(PrerunArgs, PrerunResult<T>)`, onde `T` é o tipo nomeado por `state:`, ou `()` na forma apenas com prerun.                                                                                                           |
| `buffer_size: N` / `deterministic: true` | Opcionais, fornecidos juntos ou não fornecidos. Mesmo significado que em `func!` quando fornecidos — para varargs, um valor fixo costuma ser a escolha errada, já que o resultado normalmente cresce com a contagem de argumentos; dimensione-o na prerun com `request_buffer_size` em vez disso. |

A forma básica não tem validação e aceita uma chamada com zero argumentos — uma escolha legítima para uma função que é total sobre toda entrada, mas isso significa que a função de linha sozinha é responsável por toda entrada que pode receber. Apenas a forma com `state:` aloca e descarta o estado por instrução; a forma apenas com prerun usa `PrerunResult<()>` e não armazena nada, então não há postrun para ela — uma prerun dessas usa `PrerunResult` apenas para `error` e `request_buffer_size`, nunca para `set_state`.

### Inspecionando tipos de argumento em uma prerun

Como o servidor não valida nada, uma prerun de varargs precisa ver os tipos dos argumentos antes que a primeira linha seja executada. `PrerunArgs::type_at` fornece essa visão, junto com `len()`/`is_empty()` e os métodos de `PrerunResult` descritos em [Estado por instrução](#per-statement-state).

| Método de `PrerunArgs` | Retorna                                                                                            |
| ---------------------- | -------------------------------------------------------------------------------------------------- |
| `type_at(i: usize)`    | `Option<ArgType>` — o tipo declarado do argumento `i`, ou `None` se `i` estiver fora do intervalo. |

| Método de `ArgType` | Retorna                                                                                                                         |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `is_str()`          | `true` para um argumento `STRING`.                                                                                              |
| `is_real()`         | `true` para um argumento `REAL`.                                                                                                |
| `is_int()`          | `true` para um argumento `INT`.                                                                                                 |
| `is_custom()`       | `true` para um argumento de um tipo personalizado registrado por qualquer extensão instalada.                                   |
| `custom_name()`     | `Option<&str>` — o nome do tipo personalizado. `Some` apenas quando `is_custom()` é verdadeiro; `None` para os tipos escalares. |

Combine `is_custom()` com `custom_name()` para aceitar exatamente um tipo personalizado: `is_custom()` sozinho aceita todo tipo personalizado no servidor.

<Note>
  `varargs_func!` e `PrerunArgs::type_at` ainda não estão em uma versão
  publicada. A versão atual do
  [crates.io](https://crates.io/crates/villagesql) (`0.0.1`) não os expõe.
</Note>

O exemplo `vsql_varargs` no repositório do SDK declara uma função por forma. Uma função varargs com estado, validada na prerun e carregando um contador de chamadas por instrução:

```rust theme={null}
use villagesql::{InValue, PrerunArgs, PrerunResult, VdfReturn};

/// Per-statement state: how many times the row handler has run this statement.
#[derive(Default)]
struct JoinState {
    calls: i64,
}

/// Validate the call and set up the statement. The server does no validation for
/// varargs, so this is the only gate.
fn str_join_prerun(args: PrerunArgs, mut out: PrerunResult<JoinState>) {
    // Reject a zero-argument call.
    if args.is_empty() {
        out.error("str_join requires at least one argument");
        return;
    }

    // Every argument must be a string.
    for i in 0..args.len() {
        if !args.type_at(i).is_some_and(|t| t.is_str()) {
            out.error("str_join: every argument must be a string");
            return;
        }
    }

    // Size the result buffer from the arg count.
    out.request_buffer_size(32 + args.len() * 64);

    // Hand the fresh counter to the server.
    out.set_state(JoinState::default());
}

/// Join a variable number of string arguments, prefixed with the per-statement
/// call count.
fn str_join(state: &mut JoinState, args: &[InValue]) -> VdfReturn {
    state.calls += 1;

    let mut joined = String::new();
    for (i, arg) in args.iter().enumerate() {
        match arg {
            InValue::String(s) => {
                if i > 0 {
                    joined.push_str(", ");
                }
                joined.push_str(s);
            }
            // A string column can carry NULL. SQL-style: NULL in -> NULL out.
            InValue::Null => return VdfReturn::Null,
            _ => return VdfReturn::error("str_join: non-string argument at runtime"),
        }
    }
    VdfReturn::string(format!("#{}: {joined}", state.calls))
}

/// Bare varargs: no prerun, no state, no validation. Returns how many arguments
/// it was called with, including zero.
fn arg_count(args: &[InValue]) -> VdfReturn {
    VdfReturn::int(i64::try_from(args.len()).unwrap_or(i64::MAX))
}

villagesql::extension! {
    funcs: [
        villagesql::varargs_func!(str_join, "str_join", [..] -> villagesql::Type::String,
            state: JoinState, prerun: str_join_prerun),
        villagesql::varargs_func!(arg_count, "arg_count", [..] -> villagesql::Type::Int),
    ]
}
```

`str_join` ainda faz a correspondência de `InValue` na função de linha mesmo que a prerun tenha provado que todo argumento é uma string: a prerun vê tipos declarados, não valores, e uma coluna `STRING` pode carregar NULL em qualquer linha.

```sql theme={null}
INSTALL EXTENSION vsql_varargs;
SELECT vsql_varargs.str_join('alpha', 'beta');
-- #1: alpha, beta
SELECT vsql_varargs.str_join('a', 'b', 'c', 'd');
-- #1: a, b, c, d
SELECT vsql_varargs.str_join('ok', 123);
-- ERROR 1123 (HY000): Can't initialize function 'str_join'; str_join: every argument must be a string
SELECT vsql_varargs.arg_count();
-- 0
SELECT vsql_varargs.arg_count(1, 2.5, 'mix');
-- 3
```

O exemplo também declara `describe` (uma função apenas com prerun que rejeita zero argumentos e argumentos não escalares e, em seguida, formata uma lista heterogênea de argumentos) e `point_path` (que valida com `is_custom()` e `custom_name()`). Consulte `examples/vsql_varargs/src/lib.rs` no [repositório do SDK Rust](https://github.com/villagesql/vsql-rust-sdk).

## macro custom\_type!

`custom_type!` registra um novo tipo de coluna. `type_name`, `persisted_length`, `max_decode_buffer_length`, `encode`, `decode` e `compare` são obrigatórios. `hash` e `default` são opcionais, mas recomendados.

```rust theme={null}
villagesql::custom_type!(
    type_name: "sql_type_name",
    persisted_length: N,
    max_decode_buffer_length: M,
    encode: encode_fn,
    decode: decode_fn,
    compare: compare_fn,
    hash: hash_fn,
    default: "default_string",
)
```

| Campo                      | Tipo                                     | Descrição                                                                                                                                                                  |
| -------------------------- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type_name`                | Literal `&str`                           | Nome SQL para o tipo. Não diferencia maiúsculas de minúsculas no SQL. Deve ser único entre todas as extensões instaladas.                                                  |
| `persisted_length`         | `usize`                                  | Comprimento fixo em bytes para armazenamento em disco. Todos os valores codificados devem produzir exatamente essa quantidade de bytes.                                    |
| `max_decode_buffer_length` | `usize`                                  | Comprimento máximo em bytes da string decodificada. Usado para dimensionar o buffer de saída antes de chamar `decode`.                                                     |
| `encode`                   | `fn(&str) -> Result<Vec<u8>, String>`    | Chamado no momento do `INSERT`. Converte um literal de string SQL em binário. Retorne `Err(msg)` para rejeitar a entrada.                                                  |
| `decode`                   | `fn(&[u8]) -> Result<String, String>`    | Chamado para exibir o valor. Converte binário de volta em uma string.                                                                                                      |
| `compare`                  | `fn(&[u8], &[u8]) -> std::cmp::Ordering` | Chamado para `ORDER BY`, `MIN`, `MAX`. Retorne `Less`, `Equal` ou `Greater`.                                                                                               |
| `hash`                     | `fn(&[u8]) -> usize`                     | Opcional. Chamado para `COUNT(DISTINCT)` e operações de conjunto. Valores que comparam como `Equal` devem ter o mesmo hash. Recomendado para colunas indexadas.            |
| `default`                  | Literal `&str`                           | Opcional. Uma string válida que o servidor codifica na inicialização do tipo para verificar se o callback funciona. Deve codificar em exatamente `persisted_length` bytes. |

O campo `default` não é um valor padrão de coluna — é uma sondagem de inicialização. O servidor chama `encode(default)` ao carregar a extensão para verificar se o callback funciona. Se `encode` retornar `Err` para o padrão, a extensão falha ao carregar.

## macro custom!

`villagesql::custom!("type_name")` referencia um tipo personalizado pelo nome em uma declaração `func!`:

```rust theme={null}
villagesql::func!(
    my_fn,
    "my_sql_func",
    [villagesql::custom!("mytype")] -> villagesql::custom!("mytype"),
    deterministic: true
)
```

Use-o em qualquer lugar onde um `villagesql::Type::*` apareceria em uma lista de parâmetros ou posição de tipo de retorno. A string deve corresponder ao `type_name` declarado no `custom_type!` correspondente.

## campos do manifest.json

Toda extensão precisa de um `manifest.json` ao lado de seu `Cargo.toml`:

```json theme={null}
{
  "name": "vsql_my_extension",
  "version": "0.1.0",
  "description": "Brief description of what the extension does",
  "author": "Your Name",
  "license": "GPL-2.0"
}
```

| Campo         | Obrigatório | Formato                              | Descrição                                                                                                                            |
| ------------- | ----------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `name`        | Sim         | letras minúsculas, dígitos, `_`, `-` | Identificador da extensão. Deve corresponder ao nome do `INSTALL EXTENSION`. Use underscores — hifens exigem o uso de crases no SQL. |
| `version`     | Sim         | MAJOR.MINOR.PATCH                    | Versão semântica.                                                                                                                    |
| `description` | Não         | String                               | Exibido em `INFORMATION_SCHEMA.EXTENSIONS`.                                                                                          |
| `author`      | Não         | String                               | Nome do autor ou organização.                                                                                                        |
| `license`     | Não         | String                               | Identificador de licença. `GPL-2.0` recomendado para extensões de código aberto.                                                     |

Regras de validação de `name`: deve começar com uma letra, terminar com uma letra ou dígito, no máximo 64 caracteres. Um manifesto inválido faz com que `INSTALL EXTENSION` falhe.
