Quarry
Quarry e um construtor de consultas type-safe para PetraDB que gera objetos AST em vez de strings SQL, ignorando completamente o parser. Definicoes de schema servem como fonte unica de verdade para DDL, consultas e tipos TypeScript em tempo de compilacao.
Instalacao
Seção intitulada “Instalacao”npm install @petradb/quarryConfiguracao
Seção intitulada “Configuracao”import { Session } from "@petradb/engine";import { quarry } from "@petradb/quarry";
const session = new Session({ storage: "memory" });const db = quarry(session);Modos de armazenamento
Seção intitulada “Modos de armazenamento”// Em memoria (padrao)new Session({ storage: "memory" });
// Armazenamento persistente em arquivonew Session({ storage: "persistent", path: "./mydb.petra" });Definicao de schema
Seção intitulada “Definicao de schema”Defina tabelas usando os construtores de coluna do Quarry. O schema direciona a criacao de tabelas, construcao de consultas e inferencia de tipos TypeScript:
import { table, serial, text, integer, boolean } from "@petradb/quarry";
const users = table("users", { id: serial("id").primaryKey(), name: text("name").notNull(), email: text("email").notNull().unique(), age: integer("age"), active: boolean("active").notNull().default(true),});Tipos de coluna
Seção intitulada “Tipos de coluna”| Construtor | Tipo SQL | Tipo TypeScript |
|---|---|---|
serial(name) | SERIAL | number |
bigserial(name) | BIGSERIAL | number |
text(name) | TEXT | string |
varchar(name, length?) | VARCHAR(n) | string |
char(name, length?) | CHAR(n) | string |
integer(name) | INTEGER | number |
smallint(name) | SMALLINT | number |
bigint(name) | BIGINT | number |
doublePrecision(name) | DOUBLE | number |
real(name) | REAL | number |
numeric(name, precision?, scale?) | NUMERIC(p,s) | number |
boolean(name) | BOOLEAN | boolean |
uuid(name) | UUID | string |
timestamp(name) | TIMESTAMP | string |
timestamptz(name) | TIMESTAMPTZ | string |
date(name) | DATE | string |
time(name) | TIME | string |
timetz(name) | TIMETZ | string |
interval(name) | INTERVAL | string |
json(name) | JSON | unknown |
bytea(name) | BYTEA | number[] |
Modificadores de coluna
Seção intitulada “Modificadores de coluna”| Modificador | Efeito |
|---|---|
.notNull() | Coluna nao pode ser nula; tipo InferSelect exclui null |
.default(value) | Coluna e opcional em InferInsert |
.primaryKey() | Chave primaria; implica notNull + hasDefault (auto-incremento para serial) |
.unique() | Adiciona restricao unique |
.references(table, column) | Adiciona referencia de chave estrangeira |
Tipos inferidos
Seção intitulada “Tipos inferidos”O Quarry infere dois tipos de cada definicao de tabela:
import type { InferSelect, InferInsert } from "@petradb/quarry";
type User = InferSelect<typeof users>;// { id: number, name: string, email: string, age: number | null, active: boolean }
type NewUser = InferInsert<typeof users>;// { name: string, email: string, age?: number | null, active?: boolean, id?: number }InferSelect — o tipo de linha retornado por consultas:
- Colunas
notNull-> tipo nao nulo - Colunas nulaveis ->
type | null
InferInsert — o tipo aceito por .values():
- Colunas
notNullsem defaults -> obrigatorias - Colunas com defaults (
.default(),.primaryKey(), serial) -> opcionais - Colunas nulaveis -> opcionais, aceitam
null
Criar tabela
Seção intitulada “Criar tabela”await db.createTable(users);Isso gera e executa um comando CREATE TABLE a partir da definicao do schema — sem necessidade de SQL.
// Linha unica — retorna a linha inserida com todas as colunasconst [user] = await db .insert(users) .values({ name: "Alice", email: "alice@example.com", age: 30 }) .execute();// user.id -> serial gerado automaticamente// user.active -> true (padrao)
// Multiplas linhasawait db .insert(users) .values( { name: "Bob", email: "bob@example.com", age: 25 }, { name: "Charlie", email: "charlie@example.com" }, ) .execute();Insert requer todas as colunas notNull sem defaults. Campos opcionais podem ser omitidos. TypeScript impoe isso em tempo de compilacao.
RETURNING
Seção intitulada “RETURNING”Por padrao, insert retorna todas as colunas (*). Use .returning() para selecionar colunas especificas:
const [{ id }] = await db .insert(users) .values({ name: "Alice", email: "alice@example.com" }) .returning(users.id) .execute();Upsert (ON CONFLICT)
Seção intitulada “Upsert (ON CONFLICT)”Trate conflitos no insert com onConflictDoNothing() ou onConflictDoUpdate():
// Ignorar linhas conflitantes silenciosamenteawait db .insert(users) .values({ name: "Alice", email: "alice@example.com" }) .onConflictDoNothing() .execute();
// Atualizar colunas especificas quando um conflito ocorreawait db .insert(users) .values({ name: "Alice", email: "alice@example.com", age: 31 }) .onConflictDoUpdate(["email"], { name: "Alice Updated", age: 31 }) .execute();O primeiro argumento de onConflictDoUpdate especifica as colunas de conflito, o segundo especifica quais colunas atualizar. Ambos sao type-safe — TypeScript impoe que apenas chaves de coluna validas sejam usadas.
INSERT…SELECT
Seção intitulada “INSERT…SELECT”Insira linhas de uma consulta em vez de valores literais:
// Arquivar todos os usuarios ativosconst query = db .select(users.name, users.email) .from(users) .where(eq(users.active, true));
await db.insertFrom(archive, query, ["name", "email"]).execute();O segundo argumento e um select builder. O terceiro argumento opcional especifica quais colunas de destino preencher — se omitido, o engine espera que a consulta produza valores para todas as colunas.
// Sem lista de colunas (a consulta deve corresponder a todas as colunas de destino)await db.insertFrom(archive, query).execute();
// Com onConflictDoNothingawait db.insertFrom(archive, query, ["name", "email"]).onConflictDoNothing().execute();import { eq, gt, asc, desc } from "@petradb/quarry";
// Todas as linhas (SELECT *)const allUsers = await db.from(users).execute();
// Clausula whereconst alice = await db .from(users) .where(eq(users.name, "Alice")) .execute();
// Colunas especificasconst names = await db .select(users.name, users.email) .from(users) .execute();
// Ordenacao, limite, offsetconst page = await db .from(users) .orderBy(asc(users.name)) .limit(10) .offset(20) .execute();
// Distinctconst statuses = await db .select(users.active) .from(users) .distinct() .execute();
// Distinct on — uma linha por valor distinto das colunas especificadasconst perCategory = await db .from(products) .distinctOn(products.category) .orderBy(asc(products.category), asc(products.price)) .execute();// Retorna o produto mais barato de cada categoriaReferencias de coluna
Seção intitulada “Referencias de coluna”Colunas sao acessadas diretamente como propriedades no objeto da tabela. TypeScript impede o acesso a colunas que nao existem no schema:
users.name; // ✓ compilausers.title; // ✗ erro de compilacao — 'title' nao existe em usersExpressoes
Seção intitulada “Expressoes”Comparacao
Seção intitulada “Comparacao”import { eq, ne, gt, gte, lt, lte, like, ilike } from "@petradb/quarry";import { notLike, notIlike, isDistinctFrom, isNotDistinctFrom } from "@petradb/quarry";
eq(users.name, "Alice") // name = 'Alice'ne(users.name, "Bob") // name != 'Bob'gt(users.age, 21) // age > 21gte(users.age, 18) // age >= 18lt(users.age, 65) // age < 65lte(users.age, 30) // age <= 30like(users.name, "A%") // name LIKE 'A%'notLike(users.name, "A%") // name NOT LIKE 'A%'ilike(users.email, "%@x%") // email ILIKE '%@x%'notIlike(users.email, "%@x%")
// Comparacao null-safeisDistinctFrom(users.age, null) // age IS DISTINCT FROM NULLisNotDistinctFrom(users.age, null) // age IS NOT DISTINCT FROM NULLLogicas
Seção intitulada “Logicas”import { and, or, not } from "@petradb/quarry";
and(eq(users.active, true), gt(users.age, 18))or(eq(users.name, "Alice"), eq(users.name, "Bob"))not(eq(users.active, false))and() e or() aceitam qualquer numero de argumentos:
and(cond1, cond2, cond3) // cond1 AND cond2 AND cond3Verificacoes de null
Seção intitulada “Verificacoes de null”import { isNull, isNotNull } from "@petradb/quarry";
isNull(users.age) // age IS NULLisNotNull(users.age) // age IS NOT NULLTestes booleanos
Seção intitulada “Testes booleanos”import { isTrue, isNotTrue, isFalse, isNotFalse, isUnknown, isNotUnknown } from "@petradb/quarry";
isTrue(users.active) // active IS TRUEisNotTrue(users.active) // active IS NOT TRUEisFalse(users.active) // active IS FALSEisNotFalse(users.active) // active IS NOT FALSEisUnknown(users.active) // active IS UNKNOWNisNotUnknown(users.active) // active IS NOT UNKNOWNColecoes
Seção intitulada “Colecoes”import { inList, notInList, between, notBetween, betweenSymmetric } from "@petradb/quarry";
inList(users.name, ["Alice", "Bob", "Charlie"]) // name IN (...)notInList(users.id, [1, 2, 3]) // id NOT IN (...)between(users.age, 18, 65) // age BETWEEN 18 AND 65notBetween(users.age, 18, 65) // age NOT BETWEEN 18 AND 65betweenSymmetric(users.age, 65, 18) // age BETWEEN SYMMETRIC 65 AND 18notBetweenSymmetric(users.age, 65, 18) // age NOT BETWEEN SYMMETRIC 65 AND 18Aritmetica
Seção intitulada “Aritmetica”import { add, sub, mul, div, mod, pow, neg } from "@petradb/quarry";
add(users.age, 10) // age + 10sub(users.age, 5) // age - 5mul(users.age, 2) // age * 2div(users.age, 3) // age / 3mod(users.age, 2) // age % 2pow(users.age, 2) // age ^ 2neg(users.age) // -ageOperadores de string
Seção intitulada “Operadores de string”import { concat } from "@petradb/quarry";
concat(users.name, " Jr.") // name || ' Jr.'Operadores bitwise
Seção intitulada “Operadores bitwise”import { bitAnd, bitOr, bitXor, bitNot, leftShift, rightShift } from "@petradb/quarry";
bitAnd(users.flags, 0xFF) // flags & 255bitOr(users.flags, 1) // flags | 1bitXor(users.flags, 0xFF) // flags # 255bitNot(users.flags) // ~flagsleftShift(users.flags, 2) // flags << 2rightShift(users.flags, 1) // flags >> 1Operadores JSON
Seção intitulada “Operadores JSON”import { jsonGet, jsonGetText, jsonPath, jsonPathText } from "@petradb/quarry";import { jsonContains, jsonContainedBy, jsonHasKey, jsonHasAnyKey, jsonHasAllKeys } from "@petradb/quarry";
jsonGet(t.data, "name") // data -> 'name'jsonGetText(t.data, "name") // data ->> 'name'jsonPath(t.data, path) // data #> pathjsonPathText(t.data, path) // data #>> pathjsonContains(t.data, other) // data @> otherjsonContainedBy(t.data, other) // data <@ otherjsonHasKey(t.data, "key") // data ? 'key'jsonHasAnyKey(t.data, keys) // data ?| keysjsonHasAllKeys(t.data, keys) // data ?& keysOperadores de array
Seção intitulada “Operadores de array”import { arrayOverlap } from "@petradb/quarry";
arrayOverlap(t.tags, t.otherTags) // tags && otherTags (arrays se sobrepoem)Operadores genericos
Seção intitulada “Operadores genericos”Para operadores nao cobertos por um helper nomeado, use op() e unaryOp():
import { op, unaryOp } from "@petradb/quarry";
op(users.age, ">=", 18) // age >= 18unaryOp("NOT", eq(users.active, true))Expressao CASE
Seção intitulada “Expressao CASE”import { caseWhen, literal } from "@petradb/quarry";
caseWhen( [ { when: gt(users.age, 60), then: literal("senior") }, { when: gt(users.age, 18), then: literal("adult") }, ], "minor", // else)Expressao CAST
Seção intitulada “Expressao CAST”import { cast } from "@petradb/quarry";
cast(users.age, "text") // CAST(age AS TEXT)cast(users.age, "double") // CAST(age AS DOUBLE)Aliases e literais
Seção intitulada “Aliases e literais”import { alias, literal } from "@petradb/quarry";
alias(add(users.age, 10), "age_plus_10")
literal("hello") // stringliteral(42) // numberliteral(true) // booleanliteral(null) // nullAgregacoes e agrupamento
Seção intitulada “Agregacoes e agrupamento”Agregacoes embutidas
Seção intitulada “Agregacoes embutidas”import { count, sum, avg, min, max, alias } from "@petradb/quarry";import { stringAgg, arrayAgg, boolAnd, boolOr, jsonAgg, jsonObjectAgg } from "@petradb/quarry";
// Contar todas as linhasconst [{ total }] = await db .select(alias(count(), "total")) .from(users) .execute();
// Agrupar por com agregacaoconst stats = await db .select(users.active, alias(count(), "cnt")) .from(users) .groupBy(users.active) .execute();
// Havingconst popular = await db .select(users.active, alias(count(), "cnt")) .from(users) .groupBy(users.active) .having(gt(alias(count(), "cnt"), 5)) .execute();
// Outras agregacoessum(users.age) // SUM(age)avg(users.age) // AVG(age)min(users.age) // MIN(age)max(users.age) // MAX(age)stringAgg(users.name, ", ") // STRING_AGG(name, ', ')arrayAgg(users.name) // ARRAY_AGG(name)boolAnd(users.active) // BOOL_AND(active)boolOr(users.active) // BOOL_OR(active)jsonAgg(users.name) // JSON_AGG(name)jsonObjectAgg(users.name, users.age) // JSON_OBJECT_AGG(name, age)Agregacoes estatisticas
Seção intitulada “Agregacoes estatisticas”import { variance, varSamp, varPop, stddev, stddevSamp, stddevPop } from "@petradb/quarry";
variance(emp.salary) // VARIANCE(salary) — variancia amostralvarSamp(emp.salary) // VAR_SAMP(salary) — igual a variancevarPop(emp.salary) // VAR_POP(salary) — variancia populacionalstddev(emp.salary) // STDDEV(salary) — desvio padrao amostralstddevSamp(emp.salary) // STDDEV_SAMP(salary) — igual a stddevstddevPop(emp.salary) // STDDEV_POP(salary) — desvio padrao populacionalAgregacoes bitwise
Seção intitulada “Agregacoes bitwise”import { bitAndAgg, bitOrAgg, bitXorAgg } from "@petradb/quarry";
bitAndAgg(emp.flags) // BIT_AND(flags)bitOrAgg(emp.flags) // BIT_OR(flags)bitXorAgg(emp.flags) // BIT_XOR(flags)import { every } from "@petradb/quarry";
every(emp.active) // EVERY(active) — verdadeiro quando todas as linhas sao verdadeirasFILTER em agregacoes
Seção intitulada “FILTER em agregacoes”Restrinja quais linhas uma agregacao processa com filter():
import { filter } from "@petradb/quarry";
// COUNT(*) FILTER (WHERE salary > 100)filter(count(), gt(emp.salary, 100))
// SUM(salary) FILTER (WHERE active = true)filter(sum(emp.salary), eq(emp.active, true))Exemplo com multiplas agregacoes filtradas:
const [row] = await db .select( alias(count(), "total"), alias(filter(count(), gt(employees.salary, 100)), "high_earners"), alias(filter(sum(employees.salary), eq(employees.active, true)), "active_payroll"), ) .from(employees) .execute();Funcoes
Seção intitulada “Funcoes”Chame qualquer funcao SQL com fn():
import { fn } from "@petradb/quarry";
fn("upper", users.name) // UPPER(name)fn("coalesce", users.age, 0) // COALESCE(age, 0)fn("length", users.name) // LENGTH(name)fn("lower", users.email) // LOWER(email)fn("abs", users.age) // ABS(age)fn("round", users.score, 2) // ROUND(score, 2)O Quarry suporta joins inner, left, right, full e cross com tipagem de resultado em tempo de compilacao.
Inner join
Seção intitulada “Inner join”Todas as colunas de ambas as tabelas sao incluidas no resultado. A nulabilidade e preservada do schema original:
const posts = table("posts", { id: serial("id").primaryKey(), userId: integer("user_id").notNull(), title: text("title").notNull(), body: text("body"),});
const rows = await db .from(users) .innerJoin(posts, eq(users.id, posts.userId)) .where(eq(users.name, "Alice")) .execute();
// Tipo do resultado: (InferSelect<users> & InferSelect<posts>)[]// rows[0].name -> string// rows[0].title -> string// rows[0].body -> string | null (nulavel no schema de posts)Left join
Seção intitulada “Left join”As colunas da tabela juntada se tornam todas nulaveis, ja que linhas sem correspondencia produzem null:
const rows = await db .from(users) .leftJoin(posts, eq(users.id, posts.userId)) .execute();
// Tipo do resultado: (InferSelect<users> & Nullable<InferSelect<posts>>)[]// rows[0].name -> string (tabela base, nao afetada)// rows[0].title -> string | null (left join torna nulavel)// rows[0].userId -> number | null (left join torna nulavel)Right join
Seção intitulada “Right join”As colunas da tabela base se tornam nulaveis, as colunas da tabela juntada preservam sua nulabilidade original:
const rows = await db .from(users) .rightJoin(posts, eq(users.id, posts.userId)) .execute();
// Tipo do resultado: (Nullable<InferSelect<users>> & InferSelect<posts>)[]// rows[0].name -> string | null (right join torna tabela base nulavel)// rows[0].title -> string (tabela juntada, nao afetada)Full join
Seção intitulada “Full join”Ambos os lados se tornam nulaveis:
const rows = await db .from(users) .fullJoin(posts, eq(users.id, posts.userId)) .execute();
// Tipo do resultado: (Nullable<InferSelect<users>> & Nullable<InferSelect<posts>>)[]// rows[0].name -> string | null// rows[0].title -> string | nullCross join
Seção intitulada “Cross join”Produz o produto cartesiano de ambas as tabelas — sem condicao on:
const rows = await db .from(users) .crossJoin(posts) .execute();
// Tipo do resultado: (InferSelect<users> & InferSelect<posts>)[]// Cada combinacao de usuario x postJoins encadeados
Seção intitulada “Joins encadeados”Multiplos joins acumulam tipos corretamente:
const comments = table("comments", { id: serial("id").primaryKey(), postId: integer("post_id").notNull(), content: text("content").notNull(),});
const rows = await db .from(users) .innerJoin(posts, eq(users.id, posts.userId)) .leftJoin(comments, eq(posts.id, comments.postId)) .execute();
// colunas de posts: nao nulas (inner join)// colunas de comments: nulaveis (left join)// rows[0].title -> string (inner join)// rows[0].content -> string | null (left join)Join com selecao de colunas
Seção intitulada “Join com selecao de colunas”const rows = await db .select(users.name, posts.title) .from(users) .innerJoin(posts, eq(users.id, posts.userId)) .execute();Join com agregacoes
Seção intitulada “Join com agregacoes”const rows = await db .select(users.name, alias(count(), "post_count")) .from(users) .innerJoin(posts, eq(users.id, posts.userId)) .groupBy(users.name) .orderBy(desc(alias(count(), "post_count"))) .execute();Aliases de tabela
Seção intitulada “Aliases de tabela”Use tableAs() para criar tabelas com alias para self-joins ou quando a mesma tabela aparece varias vezes:
import { tableAs } from "@petradb/quarry";
const mgr = tableAs(employees, "mgr");const emp = tableAs(employees, "emp");
const rows = await db .select( alias(emp.name, "employee"), alias(mgr.name, "manager"), ) .from(emp) .leftJoin(mgr, eq(emp.managerId, mgr.id)) .execute();Aliases sao type-safe — mgr.name ainda impoe que name exista no schema de employees.
Subconsultas
Seção intitulada “Subconsultas”Subconsulta IN
Seção intitulada “Subconsulta IN”import { inSubquery, notInSubquery } from "@petradb/quarry";
// Usuarios que tem pelo menos um postconst rows = await db .from(users) .where( inSubquery(users.id, db.select(posts.userId).from(posts)), ) .execute();
// Usuarios que NAO tem postsconst rows = await db .from(users) .where( notInSubquery(users.id, db.select(posts.userId).from(posts)), ) .execute();Subconsulta EXISTS
Seção intitulada “Subconsulta EXISTS”import { exists } from "@petradb/quarry";
const rows = await db .from(users) .where( exists( db.select(literal(1)).from(posts).where(eq(posts.userId, users.id)), ), ) .execute();Subconsulta escalar
Seção intitulada “Subconsulta escalar”Use subquery() para encapsular um select como valor escalar:
import { subquery } from "@petradb/quarry";
// Usuarios mais velhos que a idade mediaconst rows = await db .from(users) .where( gt(users.age, subquery(db.select(avg(users.age)).from(users))), ) .execute();As funcoes de subconsulta (subquery, exists, inSubquery, notInSubquery) aceitam qualquer query builder diretamente — nenhuma conversao intermediaria e necessaria.
Ordenacao
Seção intitulada “Ordenacao”import { asc, desc } from "@petradb/quarry";
// Ordenacao basicadb.from(users).orderBy(asc(users.name))db.from(users).orderBy(desc(users.age))
// Multiplas colunasdb.from(users).orderBy(asc(users.name), desc(users.age))
// NULLS FIRST / NULLS LASTdb.from(users).orderBy(asc(users.age, { nulls: "first" }))db.from(users).orderBy(desc(users.age, { nulls: "last" }))Quando nulls nao e especificado, o engine usa o comportamento padrao (nulos vem por ultimo em ordem ascendente, primeiro em ordem descendente).
// Update com whereconst result = await db .update(users) .set({ age: 31 }) .where(eq(users.name, "Alice")) .execute();// result.rowCount -> 1
// Atualizar multiplos camposawait db .update(users) .set({ name: "Alice Smith", age: 32, active: false }) .where(eq(users.id, 1)) .execute();
// Definir como nullawait db .update(users) .set({ age: null }) .where(eq(users.name, "Bob")) .execute();O metodo .set() aceita Partial<InferSelect<T>> — TypeScript impoe nomes de coluna e tipos validos.
UPDATE…FROM
Seção intitulada “UPDATE…FROM”Junte outra tabela para direcionar atualizacoes:
const priceUpdates = table("price_updates", { id: serial("id").primaryKey(), productName: text("product_name").notNull(), newPrice: integer("new_price").notNull(),});
await db .update(products) .set({ price: 0 }) // definir valor; use referencias de coluna no WHERE para logica condicional .from(priceUpdates) .where(eq(products.name, priceUpdates.productName)) .execute();.from() aceita multiplas tabelas:
db.update(t1).set({ ... }).from(t2, t3).where(and(...))RETURNING
Seção intitulada “RETURNING”Update e delete suportam .returning() para obter de volta as linhas afetadas:
const result = await db .update(users) .set({ active: false }) .where(lt(users.age, 18)) .returning(users.id, users.name) .execute();// result.rows -> [{ id: 3, name: "Charlie" }, ...]const result = await db .delete(users) .where(eq(users.name, "Alice")) .execute();// result.rowCount -> 1DELETE…USING
Seção intitulada “DELETE…USING”Junte outra tabela para determinar quais linhas excluir:
const deleteList = table("delete_list", { id: serial("id").primaryKey(), userName: text("user_name").notNull(),});
await db .delete(users) .using(deleteList) .where(eq(users.name, deleteList.userName)) .execute();.using() aceita multiplas tabelas:
db.delete(t1).using(t2, t3).where(and(...))Transacoes
Seção intitulada “Transacoes”Encapsule multiplas operacoes em uma transacao com commit/rollback automatico:
const result = await db.transaction(async (tx) => { const [user] = await tx .insert(users) .values({ name: "Alice", email: "alice@example.com" }) .execute();
await tx .insert(posts) .values({ userId: user.id, title: "First Post" }) .execute();
return user;});// Se qualquer operacao lancar erro, toda a transacao sofre rollbackO callback recebe uma instancia QuarryDB com escopo na transacao. O valor de retorno do callback se torna o valor de retorno de transaction(), com o tipo preservado.
Inspecao de AST
Seção intitulada “Inspecao de AST”Todo builder tem um metodo .toAST() que retorna o objeto AST bruto sem executa-lo. Isso e util para depuracao, logging ou construcao de abstracoes de nivel superior:
const ast = db .from(users) .where(eq(users.name, "Alice")) .orderBy(asc(users.id)) .limit(10) .toAST();
console.log(JSON.stringify(ast, null, 2));// {// "kind": "query",// "query": {// "kind": "select",// "exprs": [{ "kind": "star" }],// "from": [{ "kind": "table", "name": "users" }],// "where": { "kind": "binary", "left": ..., "op": "=", "right": ... },// "orderBy": [{ "expr": ..., "direction": "asc" }],// "limit": 10// }// }Como funciona
Seção intitulada “Como funciona”O Quarry constroi objetos JavaScript simples (unions discriminadas com um campo kind) que representam o AST da consulta. Quando voce chama .execute(), esses objetos sao passados para o metodo executeAST() do engine, que os converte diretamente no AST interno Scala do engine — pulando completamente a geracao e o parsing de strings SQL.
Schema -> Builder API -> Objetos AST JS -> AST Engine -> Reescrita -> Execucao ↑ sem parser SQLIsso da ao Quarry as mesmas capacidades de consulta que SQL enquanto elimina overhead de parsing e habilita seguranca de tipo completa em tempo de compilacao.
Limpeza
Seção intitulada “Limpeza”await session.close();