Service data
Query and change data in a deployed database, from the terminal.
eqs db runs a statement against a database you deployed from the marketplace. The api holds the credentials, checks the permission and records what ran, so nothing has to be copied to your machine first.
Read. Held to reading by the engine, not by us reading your statement.
eqs db query pg-main "select id, email from users order by id desc"Change rows: insert, update, delete.
eqs db exec pg-main "delete from sessions where expires_at < now()"Change structure: create, alter, drop. Its own permission.
eqs db ddl pg-main "alter table users add column nickname text"What the service holds: tables, collections, or labels and relationship types.
eqs db tables pg-mainA table's columns, a collection's indexes, or a label's observed properties.
eqs db describe pg-main usersThe statement is the engine's own
There is no common dialect to learn, on purpose. A query language that covered SQL, Redis commands, Mongo documents and Cypher at once would be a worse version of each. What is uniform is everything around the statement: how you address the service, which permission you need, how results come back, and what gets recorded.
| Engine | Statement | Parameters |
|---|---|---|
| PostgreSQL | SQL | $1, $2 … |
| MySQL / MariaDB | SQL | ? |
| ClickHouse | SQL | {p1:String} … |
| Redis | a command line | appended as arguments |
| MongoDB | one JSON command document | none — values live in the document |
| Neo4j | Cypher | $p1, $p2 … |
eqs db query cache "LRANGE queue 0 -1" eqs db query docs '{"find":"users","filter":{"active":true},"limit":20}' eqs db query graph "MATCH (p:Person) RETURN p.name AS name LIMIT 10"
How firmly "read-only" holds
eqs db query never guesses whether your statement writes. Reading SQL to
decide that is brittle and fails open, so the engine decides instead — and the
output tells you which of three answers you got.
| Engines | What it means | |
|---|---|---|
enforced | PostgreSQL, MySQL, ClickHouse, Neo4j | The engine refuses the write itself, inside a read-only transaction. Nothing had to be detected |
checked | Redis, MongoDB | The engine classified the command and we refused on its answer. Still not a guess at your statement, but it can let through what a transaction could not |
declared | — | The verb was a declaration and the audit record is what makes it accountable |
checked: a MongoDB aggregate ending in $out or $merge writes, and MongoDB classifies aggregate as a read. Catching it would mean parsing the pipeline, which is the guessing this design avoids — so it is written down here instead of quietly assumed away.Permissions
Each verb needs its own action, and they are not interchangeable.
| Verb | Needs | Held by |
|---|---|---|
query, tables, describe | data.read | Reader, Contributor, Owner |
exec | data.write | Contributor, Owner |
ddl | data.ddl | Owner |
data.ddl is separate from data.write on purpose: editing a row and dropping
a table are not the same risk, and a role that can do the first should not
silently do the second.
Values, limits and the record
--param over pasting a value into the statement. It is bound by the driver rather than spliced in, so a quote or a semicolon inside it is data and never syntax — and it stays out of the audit log.Every statement is recorded: who ran it, against which service, how long it took
and whether it worked. Failures are recorded too, because a statement that
errored still happened. What is never recorded is your --param values, which
are the ones most likely to be personal data.
Reads stop at 100 rows by default, and the limit is applied before the rows
reach the api — a cursor on PostgreSQL, a streamed result on MySQL — so a wide
select * is never buffered whole just to be cut. Raise it with --limit, up
to 10000, and --timeout up to 120000 ms. When a result was cut, the output
says so rather than letting it read as "that is all there is".