Identifiers
TesseraQL has one rule for names: the column name is the name. The identifier you
write in DDL is the same string you write as the YAML input name, the 2-way SQL bind,
the URL path parameter, the suite parameter, the template model key, and the JSON
response key. There is no case-conversion layer — nothing turns order_date into
orderDate between the database and the browser, so every layer can be grepped with
one string.
What counts as an identifier
Section titled “What counts as an identifier”A table, column, alias, or field name is one Unicode letter or underscore followed by Unicode letters, combining marks, digits, or underscores:
identifier = [letter or _] [letter, combining mark, digit, or _]*qualified name = identifier [ "." identifier ]That includes Japanese — 受注, 顧客名, 受注番号 are names like any other — and
every other script: the marks are what an abugida needs, so ग्राहक and มุ่งมั่น are names too.
A name typed in decomposed (NFC vs NFD) form is the same kind of name, and nothing normalizes it —
no database does either, so a name spelled one way works and a name spelled two ways never matches.
The linter reports that pair as TQL-SQL-2121 rather than letting it bind null. It excludes anything that could read as SQL syntax: spaces,
quotes, dashes, semicolons, comment markers. Identifiers land in generated SQL
verbatim and unquoted, and this character class is what makes that safe.
A params: key is a bind name, so it is an identifier too, and the linter reports one that is
not with TQL-SQL-2120. The rule is not pedantry: a bind name is an expression the directive
parses, so params: { order-id: query.order-id } with a matching /* order-id */ bind reads as
the subtraction order - id, binds null on every request, and says nothing.
A complete Japanese example ships in the gallery: examples/juchu-kanri-app defines
the 受注 table, routes at /受注 and /受注/{受注番号}, a 送料区分 decision, and
a suite that exercises them with Japanese parameter names.
-- The bind name is the column name; the JSON key will be too.select 受注番号, 顧客名, 状態from 受注where 受注番号 = /* 受注番号 */'J-1001'What stays ASCII
Section titled “What stays ASCII”Names that address infrastructure rather than data keep the narrow ASCII shapes their targets require: app names (they become URL prefixes, directories, and the per-app migration-history table suffix), topic and environment-profile names, preference keys, DuckDB extension and secret names, Prometheus label names, and SCIM attributes.
Dialect notes
Section titled “Dialect notes”Every supported dialect accepts unquoted Unicode identifiers, and because CJK has no letter case, the engines’ case-folding differences cannot touch them. Two practical limits to know:
| Engine | Identifier length limit |
|---|---|
| PostgreSQL | 63 bytes (about 21 kanji in UTF-8) — the tightest |
| Oracle | 128 bytes from 12.2 (30 bytes before) |
| MySQL | 64 characters |
| SQL Server | 128 characters |
Length is not linted — the database’s own error is authoritative. On Oracle the
database character set must be Unicode (AL32UTF8, the modern default) for non-ASCII
identifiers. One adjacent note about values rather than names: on SQL Server,
store Japanese text in nvarchar columns — TesseraQL always sends values as bind
parameters (which the driver transmits as Unicode), but a hand-written plain
'…' literal in your own SQL tooling would pass through the database code page.
How Unicode names travel over HTTP
Section titled “How Unicode names travel over HTTP”Browsers percent-encode non-ASCII URLs. The runtime decodes exactly the non-ASCII
percent-sequences of a request path before route matching, so GET /%E5%8F%97%E6%B3%A8 reaches the route declared at web/受注/. ASCII sequences such
as %2F deliberately stay encoded — decoding them would let an encoded slash cross a
path-segment boundary. Path parameters keep their declared names throughout your app
and the OpenAPI document; the HTTP router internally carries positional stand-ins for
names it cannot represent, and the request binder maps them back before your SQL sees
anything.
Searching and sorting
Section titled “Searching and sorting”Studio’s documentation search indexes Japanese identifiers so that any substring run
finds them — 管理 finds 受注管理. Message-catalog placeholders ({顧客名}),
view link templates, and workflow stamp columns all accept the same identifier
contract, so a Japanese app gets the same lint coverage — including the write-scope
guard — as an ASCII one.
- two-way-sql.md — where the names are bound.
- scaffolding.md — what the generator makes of your column names.