Skip to content
Siglata Docs
English
Esc
↑↓navigate↵open⌘Jpreview
On this page

Spreadsheets with SQL

Query the cells of every spreadsheet with one SQL statement and fill reports without the data passing through the agent.

Every uploaded .xlsx, .xlsm or .csv spreadsheet becomes facts the agent queries with SQL. A question about ten spreadsheets fits in one sql call instead of ten file reads. sql and sql_schema run inside query scripts; sql_write and sheet_write run inside execute scripts (MCP).

When the facts are ready

Every file has a status, which get, files_list, file_read and document_search report; one that is not ready says what to do in statusMessage:

Status Meaning
processing The file is still being read.
ready Its cells or text can be queried.
too_large The file exceeds the plan’s per-spreadsheet cell or size limit. It stays downloadable; split it into smaller parts and upload them again, or change plans.
limit_reached The organization’s processed data reached the plan’s limit. The file stays stored and is read on its own once there is room: after a plan change, or deleting files or SQL tables.
failed The file could not be read.
stored A type Siglata keeps but does not read, such as a video.

Siglata stores deterministic facts only: it never recalculates formulas and never guesses versions or locales. Interpretation is up to the agent.

Tables and functions

Call sql_schema for each table’s columns, the functions and the objects you can read.

Table Contents
sheet_files One row per spreadsheet, with the state of its facts.
cells One row per cell: file, sheet, address, row, column, raw text, typed value, formula and number format.
styles Each style’s font, fill, border and alignment.
sheets Sheets and their used ranges.
excel_tables Excel Tables.
defined_names Defined names.
merges Merged cells.
Function Use
sheet_rows(file, sheet) A sheet’s wide rows, with columns by letter (A, B, C…).
parse_number(text, locale) Turns text such as 1.234,56 (pt-BR) or 1,234.56 (en-US) into a number.
parse_date(text, format) Turns text into a date with a pattern such as DD/MM/YYYY.
column_letters(col) Turns a column number into letters.

CSV values stay text; use parse_number and parse_date to interpret them.

Query

sql (files:read) runs one SELECT in Postgres syntax, in a query script:

return await sql({
  query: `SELECT raw AS filial, count(*) AS celulas
FROM cells
WHERE sheet = 'Vendas' AND col = 2 AND row >= 2
GROUP BY raw`,
});
  • It returns the rows that fit in 100,000 bytes of JSON, with no row limit, and runs for at most 10 s. When something is left out, the result carries truncated: true and next, which says how to page with ORDER BY and LIMIT/OFFSET or narrow the SELECT.
  • Name tables without a schema. Only the listed functions and types are accepted; system catalogs and statements that change data are refused.
  • Before a statement runs, Siglata bounds the database memory it could take at worst, from the longest values the tables it reads hold and a size rule for each function, operator and cast, through every CTE, subquery and view. A statement over 128 MiB is refused with too_large, before it runs, in SELECT, CREATE VIEW and CREATE TABLE AS: build smaller values (fewer nested concat, replace, casts or JSON and array constructors), select fewer columns, or split the work. WITH RECURSIVE is refused (use generate_series), and a regular-expression or SIMILAR TO pattern must be a string constant of at most 2,000 compiled states. At most 4 statements run at once; a statement that finds them all busy fails with conflict, so retry it.
  • Every refusal has a code, such as relation_not_allowed, function_not_allowed, timeout or conflict (retry the call).

Views and snapshots

sql_write (files:write) creates and drops your own objects, in an execute script:

Statement Result
CREATE VIEW name AS SELECT … A view shared with the organization. Each member reads it with their own access.
CREATE TABLE name AS SELECT … A snapshot only you read, over sources (the ids of the files it may read; every file you can read when omitted). It counts toward the plan’s limit for spreadsheets the agent queries.
DROP VIEW name or DROP TABLE name Drops an object you created.
return await sql_write({
  query:
    "CREATE TABLE vendas_agosto AS SELECT raw FROM cells WHERE sheet = 'Agosto'",
  sources: ["<spreadsheet id>"],
});

A snapshot follows its source files: it is hidden while a source is in the trash, returns when the source is restored, and is dropped when the source is purged.

Fill a report

sheet_write, in an execute script, accepts a query patch { sheet, anchor, query } (it also needs files:read). It runs the SELECT and writes every row from the anchor cell of a new edition of the template, keeping its formatting:

return await sheet_write({
  fileId: "<template id>",
  requestId: "vendas-setembro-2026",
  patches: [
    {
      sheet: "Relatório",
      anchor: "A2",
      query: "SELECT filial, receita FROM vendas_mes",
    },
  ],
});

The rows never pass through the agent’s context; the response carries only the new file and the written range. A result over 5,000 rows, 8 MiB or 1,000,000 cells fails with too_large and creates no file.

Permissions

SQL sees only the cells of active files the connection’s user can read. A restricted file needs a grant; another organization never appears. Trashed files stay hidden until they are restored.

Was this page helpful?