# SMQL language reference

**Applies to:** development tools; not published APT 0.1.0-2. For examples and
pagination instructions, start with [Query with SMQL](smql.html).

## Statement structure

```text
SELECT (* | column [, column ...])
FROM [database.]table
[WHERE predicate [AND predicate ...]]
[LIMIT integer]
[;]

predicate := column (= | < | <= | > | >=) literal
           | column IS NOT NULL
```

Only one statement is accepted. There are no SQL comments, aliases, arithmetic
expressions, parentheses, parameter placeholders, or functions. Joins, OR, IN,
LIKE, BETWEEN, NOT, !=, <>, ORDER BY, GROUP BY, DISTINCT, COUNT, OFFSET, subqueries,
and write/DDL statements are unsupported. `NOT` is accepted only in `IS NOT NULL`.
An unsupported clause fails the entire query; no prefix is executed.

## Identifiers and database selection

Bare identifiers start with an ASCII letter or underscore, followed by ASCII
letters, digits, or underscores. Use double quotes for other names, for example
`"display-name"` or `"用户"`; a doubled double quote represents a literal quote.
Table and column names contain 1–64 UTF-8 bytes. Names are case-sensitive;
keywords are case-insensitive. Duplicate projection columns are rejected.

Database names contain 1–64 ASCII letters, digits, underscores, or hyphens.
Quote names containing hyphens. A qualified `database.table` overrides the
selected database; an unqualified table uses `--database` in the CLI or the web
Database field. The CLI defaults to `default`.

## Values and comparisons

| Type | Literal example | Behavior |
| --- | --- | --- |
| String | `'O''Brien'` | Single quotes are doubled; backslash is literal. UTF-8 supported. |
| Integer | `-9223372036854775808` | Signed int64, through `9223372036854775807`. |
| Float | `1.5`, `-1.25e2` | Finite floating-point value; include a decimal point or exponent. |
| Boolean | `true`, `FALSE` | Case-insensitive literal. |
| Bytes | `X'00ff'` | Hexadecimal pairs; hex letter case does not matter. |

Equality requires matching types; there is no implicit string/number or
integer/float conversion. Numeric range operators also require matching numeric
types. Missing columns do not match comparisons. String ranges are unsupported.
`IS NOT NULL` tests column presence; SMKV does not store null column values.
Missing projected columns are omitted from the result. Filters run before
projection, so a predicate can use a column that is not returned.

## Execution limits

| Bound | Value |
| --- | --- |
| Total result LIMIT | Default 100; permitted 1–1000 |
| Returned rows per request | At most 100, possibly fewer or zero |
| SQL text | 4096 bytes |
| Tokens | 512 |
| Projected columns | 64 |
| AND predicates | 16 |
| Serialized parsed query | 1536 bytes |

There is no ordering guarantee, snapshot consistency, or secondary-index
acceleration. Existing scan work/response limits, admission controls, and
permissions apply. Reads and writes retain priority over scans. LIMIT restricts
returned rows, not how many records may need examination over multiple pages.
Each request does bounded work; continuation requires another explicit request.

## CLI and web API

```sh
smkv-ctl --seed 127.0.0.1:7381 --timeout-ms 5000 \
  query 'SELECT * FROM users LIMIT 10' --database customers
```

Pass `--cursor` to continue the same query. `--watch` is not supported. Output is
JSON regardless of the global display option. There is no dedicated SQL method
in the language SDKs yet.

The SMKV web service exposes `GET /api/query?q=...`; `q` is URL-encoded JSON:

```json
{
  "sql": "SELECT name FROM users LIMIT 10",
  "database": "customers",
  "cursor": null
}
```

`sql` and `database` are required; `cursor` may be omitted or null. This endpoint
belongs to the web service, not the database data or management port. It uses the
web token when configured, rejects cross-origin requests, and requires an
established cluster identity. Its request timeout is five seconds. Errors return
non-success HTTP statuses and text; successful responses use the same typed JSON
page format as the CLI. Malformed SQL returns HTTP 400.

Treat cursors as opaque continuation state. They are not credentials and do not
bypass authorization. Stop when `cursor` is null, even if `limit_remaining` is
nonzero. A topology or storage change may require restarting the query.
