Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions api/src/paths/agent.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,10 @@ get:
an import tag. The install creates normal cards in catalog ordinal
order and logical media assets without copying object-storage bytes.
SELECT returns at most
100 rows per statement, and INSERT, UPDATE, and DELETE may affect
at most 100 rows per statement. If you need more than 100
writes, split the work into multiple batches of at most 100
records across separate SQL statements or separate tool calls.
100 rows per statement. Bulk-write split arithmetic: at most 100
rows affected per statement, at most 50 statements per batch, and
a batch must not mix read and write statements. Split larger work
across separate statements or separate requests.
Use
https://api.flashcards-open-source-app.com/v1/agent/openapi.json
for the published external agent contract. The SQL surface is intentionally limited
Expand Down
60 changes: 54 additions & 6 deletions api/src/paths/agent_sql_execute.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,60 @@ post:
currently selected workspace. This is not full PostgreSQL. Supported
statements are INSERT, UPDATE, and DELETE across published writable
resources only. This endpoint rejects SHOW TABLES, DESCRIBE, SHOW COLUMNS,
and SELECT; use POST /agent/sql/query for reads. Multiple write statements
may be separated with semicolons in one sql string and are executed
atomically: all statements succeed or the whole batch fails. INSERT,
UPDATE, and DELETE may affect at most 100 rows per statement. Array columns
(e.g. tags) take a parenthesized list such as ('tag1', 'tag2'), or () for
empty.
and SELECT; use POST /agent/sql/query for reads. Cards have no deck_id column
and no deck membership: a deck is a saved tag filter whose tags column
defines the filter, so the only association between a card and a deck is
matching tags. Decks expose deck_id, name, tags, created_at, updated_at, and
deleted_at, and have no description column. deleted_at exists only on cards
and decks, is read-only, and can never appear in a WHERE clause.
Multiple write statements may be
separated with semicolons in one sql string and are executed atomically: all
statements succeed or the whole batch fails. Send schema discovery to POST
/agent/sql/query as its own request, and never in the same
semicolon-separated sql string as statements that depend on the result,
because the whole batch is composed before any statement runs. Bulk-write
split arithmetic: at most 100 rows affected per statement, at most 50
statements per batch, and a batch must not mix read and write statements.
Split larger work across separate statements or separate requests. Array
columns (e.g. tags) take a parenthesized list such as ('tag1', 'tag2'), or ()
for empty. UPDATE and DELETE WHERE clauses support parenthesized AND/OR
groups where AND binds tighter than OR; scalar comparison with =, <, <=, >,
and >=; IS NULL and IS NOT NULL; LIKE, NOT LIKE, ILIKE,
LOWER(column) LIKE, LOWER(column) NOT LIKE, LOWER(column) ILIKE, and
LOWER(column) = 'value', which is a case-insensitive whole-string LIKE
match, so % and _ in the value are wildcards rather than literal characters;
exact value matches via column IN (...);
case-insensitive exact string matches via LOWER(column) IN (...) and
LOWER(column) NOT IN (...); array comparison against a literal tag list such as
tags = ('english', 'slang'), which is exact set equality, meaning the row
array must equal exactly the listed values, order-independent and
case-sensitive, so a card carrying any additional tag does not match;
tags = ()
for rows with no tags; array-column intersection such as
tags OVERLAP ('english', 'slang') for rows carrying at least one of the
listed values, compared exactly and case-sensitively, so pass tag values as
they are stored; for cards carrying all of several tags, combine
intersections such as
tags OVERLAP ('english') AND tags OVERLAP ('slang');
MATCH('text') to keep rows where any column of the row contains
that text as a case-insensitive substring (it scans every column, including
tags and JSON metadata, and there is no tokenization, so prefer one word or an
exact phrase). LIKE, NOT LIKE, ILIKE, their LOWER(column) variants, and
LOWER(column) = 'value' apply only to text-valued columns, meaning the
string, uuid, and datetime column types; on array columns such as
tags they are rejected with a clear error.
LOWER(column) IN (...) and LOWER(column) NOT IN (...)
compare text values only; plain column IN (...) compares the column value
exactly, so pass integer and boolean literals unquoted. On an array column
such as tags none of these IN forms are rejected, but they all match no
rows. The negated form exists only as
LOWER(column) NOT IN (...); a plain column NOT IN (...) is rejected as an
unsupported predicate for every column type.
Match tags with tags OVERLAP ('english')
or tags = ('english', 'slang') instead. metadata is neither filterable nor
sortable, so it can never appear in a WHERE clause.
Filter by tag in UPDATE and DELETE with
tags OVERLAP ('tag'), because UNNEST is only available in SELECT.
security:
- ApiKeyHeader: []
requestBody:
Expand Down
54 changes: 48 additions & 6 deletions api/src/paths/agent_sql_query.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,54 @@ post:
the currently selected workspace. This is not full PostgreSQL. Supported
statements are SHOW TABLES, DESCRIBE, SHOW COLUMNS, and SELECT across
published logical resources only. This endpoint rejects INSERT, UPDATE, and
DELETE; use POST /agent/sql/execute for writes. Multiple read statements may
be separated with semicolons in one sql string. SELECT supports projected
column lists, LIKE, case-insensitive exact string matches via
LOWER(column) = 'value', LOWER(column) IN (...), and LOWER(column) NOT IN
(...), aggregates, GROUP BY, NOW(), standalone ORDER BY RANDOM(), and cards
UNNEST tags AS tag.
DELETE; use POST /agent/sql/execute for writes. Cards have no deck_id column
and no deck membership: a deck is a saved tag filter whose tags column
defines the filter, so the only association between a card and a deck is
matching tags. Decks expose deck_id, name, tags, created_at, updated_at, and
deleted_at, and have no description column. deleted_at exists only on cards
and decks, is returned by reads, and can never appear in a WHERE clause or
in ORDER BY. Multiple read
statements may be separated with semicolons in one sql string. Send SHOW
TABLES, DESCRIBE, and SHOW COLUMNS as their own request, and never in the same
semicolon-separated sql string as statements that depend on the result,
because the whole batch is composed before any statement runs. SELECT
supports projected column lists, aggregates, GROUP BY, NOW(), standalone
ORDER BY RANDOM(), and cards UNNEST tags AS tag. SELECT WHERE clauses
support parenthesized AND/OR groups where AND binds tighter than OR; scalar
comparison with =, <, <=, >, and >=; IS NULL and IS NOT NULL; LIKE,
NOT LIKE, ILIKE, LOWER(column) LIKE, LOWER(column) NOT LIKE,
LOWER(column) ILIKE, and LOWER(column) = 'value', which is a
case-insensitive whole-string LIKE match, so % and _ in the value are
wildcards rather than literal characters; exact value matches via
column IN (...); case-insensitive exact string
matches via LOWER(column) IN (...) and LOWER(column) NOT IN (...); array
comparison against a
literal tag list such as tags = ('english', 'slang'), which is exact set
equality, meaning the row array must equal exactly the listed values,
order-independent and case-sensitive, so a card carrying any additional tag
does not match; tags = () for rows with no tags; array-column
intersection such as tags OVERLAP ('english', 'slang') for rows carrying at
least one of the listed values, compared exactly and case-sensitively, so
pass tag values as they are stored; for cards carrying all of several tags,
combine intersections such as
tags OVERLAP ('english') AND tags OVERLAP ('slang');
MATCH('text') to keep rows where any column
of the row contains that text as a case-insensitive substring (it scans every
column, including tags and JSON metadata, and there is no tokenization, so
prefer one word or an exact phrase). LIKE, NOT LIKE, ILIKE, their
LOWER(column) variants, and LOWER(column) = 'value' apply only to
text-valued columns, meaning the string, uuid, and datetime column types; on
array columns such as tags they are rejected with a
clear error. LOWER(column) IN (...) and LOWER(column) NOT IN (...) compare
text values only; plain column IN (...) compares the column value exactly,
so pass integer and boolean literals unquoted. On an array column such as
tags none of these IN forms are rejected, but they all match no rows. The
negated form exists
only as LOWER(column) NOT IN (...); a plain column NOT IN (...) is rejected
as an unsupported predicate for every column type. Match tags
with tags OVERLAP ('english') or tags = ('english', 'slang') instead, or
with LOWER(tag) IN (...) over cards UNNEST tags AS tag. metadata is neither
filterable nor sortable, so it can never appear in WHERE or in ORDER BY.
security:
- ApiKeyHeader: []
requestBody:
Expand Down
Loading
Loading