> ## 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.

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

## 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 contendo os bytes brutos do argumento, sem verificação de UTF-8 (um argumento latin1 ou binário mantém seus bytes) | `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!`](https://villagesql.com/docs/pt-BR/sdk/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. Um aviso não faz a instrução falhar, inclusive `INSERT` e `UPDATE` no modo estrito. 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"),
    }
}
```

Retorne um `VdfReturn` para cada falha, e não cause um panic. O SDK não
captura um panic em uma função, agregação, prerun ou postrun, nem nas operações
de um tipo personalizado, então um panic que chega ao ponto de entrada
`extern "C"` do SDK aborta o processo do servidor. Isso inclui `.unwrap()` e `.expect()` em um `None` ou
em um `Err`, e a indexação de slice fora dos limites.

## 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](https://villagesql.com/docs/pt-BR/sdk/rust/preview-capabilities.md) 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. Oito formas — quatro sem estado por instrução (sem parâmetros, apenas `buffer_size`, apenas `deterministic`, ambos), duas que anexam estado por instrução por meio de uma função `prerun` e mais duas que adicionam uma `postrun:` opcional sobre essas. Consulte [Estado por instrução](#per-statement-state) abaixo.

```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)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, state: StateType, prerun: prerun_fn, postrun: postrun_fn)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, state: StateType, prerun: prerun_fn, postrun: postrun_fn, buffer_size: N, deterministic: true)
```

| 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, com um mínimo de 1024. Use `0` ou qualquer valor abaixo de 1024 para o padrão do servidor (1024 bytes). Quando uma função retorna um valor de string ou binário maior que o buffer, 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. Somente uma função determinística pode aparecer em uma coluna gerada ou em uma restrição `CHECK`. Defina isso apenas quando for verdadeiro. |
| `postrun: postrun_fn` | Opcional, somente nas formas `state:`/`prerun:`. `fn(&mut StateType)`, chamada uma vez após a última linha, antes de o SDK descartar o estado. |

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

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

### Estado por instrução

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!` sempre gera a postrun que o descarta quando a instrução termina, então liberá-lo não é responsabilidade sua, mesmo quando você fornece a sua própria finalização. Essa etapa de descarte é o oposto do SDK C++, onde sua postrun tem que chamar `delete_state<T>()` — consulte [Estado por Instrução](https://villagesql.com/docs/pt-BR/sdk/cpp/development.md#per-statement-state-prerun-and-postrun).

Nomeie uma `postrun: postrun_fn`, `fn(&mut StateType)`, para executar primeiro a sua própria finalização: ela é executada uma vez após a última linha, enquanto o estado ainda pode ser tomado emprestado, e antes do descarte feito pelo próprio SDK. Consulte `examples/vsql_postrun_order/src/lib.rs` no [repositório do SDK Rust](https://github.com/villagesql/vsql-rust-sdk), cuja saída de teste registrada inclui a linha `prerun,postrun,drop|rows_seen=3`, confirmando a ordem.

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](https://villagesql.com/docs/pt-BR/extensions/build/rust-sdk.md), 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. |

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](https://villagesql.com/docs/pt-BR/sdk/cpp/development.md#varargs-vdfs).

Oito formas — três formatos, cada um com uma forma abreviada e uma forma completa que adiciona
`buffer_size` e `deterministic` juntos (nunca isoladamente). O formato `state:`/
`prerun:` tem mais duas sobre essas, adicionando uma `postrun:` opcional —
mesmo significado que em `func!`:

```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)

// Per-statement state, validating prerun, and author postrun.
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type,
    state: StateType, prerun: prerun_fn, postrun: postrun_fn)
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type,
    state: StateType, prerun: prerun_fn, postrun: postrun_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. |
| `postrun: postrun_fn` | Opcional, somente nas formas `state:`/`prerun:` — significa o mesmo que em `func!`: `fn(&mut StateType)`, executada uma vez após a última linha e antes de o SDK descartar o estado. |
| `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`. Um valor `DECIMAL`, como o literal `1.5`, é um argumento `STRING`. |
| `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.

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` é opcional, mas recomendado. Forneça `default` ou `intrinsic_default_fn`, não ambos.

```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. Refira-se a ele como `<extension>.<type>` quando mais de uma extensão instalada registrar o mesmo nome. |
| `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. `Err(msg)` sempre reporta o valor como inválido, o mesmo tratamento que o MySQL dá a um valor inválido para um tipo nativo: ERROR 1366 no modo estrito, um aviso no modo não estrito ou com `IGNORE`. Nesse caminho, `msg` é descartado de qualquer forma — quem chama vê apenas o texto genérico `Incorrect <type> value: '<input>' for column '<col>' at row <n>`, e não o próprio `msg`. |
| `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 `GROUP BY`, `DISTINCT` e `UNION`. 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. |
| `intrinsic_default_fn` | `fn() -> Result<String, String>` | Opcional. Calcula essa string em vez de declará-la, para um padrão que não pode ser escrito como literal. |

O campo `default` não é um valor padrão de coluna; uma coluna omitida é `NULL`. Ele fornece a string que o servidor codifica ao inicializar o tipo, e isso acontece na primeira vez em que uma instrução nomeia o tipo, e não quando a extensão é instalada. Se você não fornecer nem `default` nem `intrinsic_default_fn`, o servidor codifica a string vazia. Em qualquer caso, um encode que retorna `Err` faz essa instrução falhar e deixa o tipo inutilizável:

```
ERROR 3219 (HY000): Type '<extension>.<type>' failed to initialize: from_string VDF failed to encode intrinsic default input '<default>' (expected persisted_length=<N>)
```

Fornecer tanto `default` quanto `intrinsic_default_fn` é recusado quando a extensão é instalada, e não adiado para o primeiro uso.

## 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, 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 | Breve descrição da extensão. |
| `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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.