Constructions · The Only DDL Path

One narrow door, locked by default

Constructions lets someone design a schema in the browser, compile it to dialect-specific DDL and apply it. It is the only path in Qrly that changes the shape of a customer database — and it is gated by a three-tier cascade in which every tier defaults to off, on top of a full data-access requirement, with every applied statement recorded verbatim.

What it is

A narrow, audited, default-off path

  • A structured design document, compiled to DDL by a generator
  • Preview the exact ordered statement list before anything runs
  • Tenant AND organisation AND connection must each enable DDL
  • Caller must hold FULL data access on the target
  • Every run records the verbatim SQL and how far it got
  • Identifier, default-expression and referential-action allow-lists
What it is not

Not a console, not a migration tool

  • Not an open SQL console — you cannot type DDL and have it run
  • No free-text field on the path to the database
  • No transaction promise on implicit-commit engines
  • Not a replacement for your migration pipeline
  • Not enabled anywhere until an administrator enables it
  • Not how anything else in Qrly touches your database
Everywhere else

The rest of the product is read-only

  • The pool hands out setReadOnly(true) connections
  • All native SQL passes the sanitizer
  • Every parameter is bound
  • MCP publishes no write tools, by design
  • The query audit log records the full SQL executed
  • Constructions is the single, deliberate exception
Element
What you can set
Constraint
Schemas and tables
Per table: schema, name, comment, columns, indexes, foreign keys, opt-in drop-if-exists
Drop-if-exists is opt-in, never a default
Columns
Name, logical type, length/precision/scale, nullable, primary key, auto-increment, unique, default expression, comment
Default expressions come from an allow-list
Indexes
Named or auto-named, ordered columns, unique flag
Warned where the dialect has no CREATE INDEX
Foreign keys
Named or auto-named, multi-column, cross-schema, may reference pre-existing tables
ON DELETE / ON UPDATE from the referential-action allow-list
Referential actions CASCADE SET NULL SET DEFAULT RESTRICT NO ACTION
Logical types · text

Portable, rendered per dialect

  • STRING
  • TEXT
  • UUID
  • JSON
  • BINARY
  • BOOLEAN
Logical types · numeric

Sized where the dialect allows it

  • INTEGER
  • BIGINT
  • SMALLINT
  • DECIMAL
  • FLOAT
  • DOUBLE
Logical types · temporal

Sixteen types in total

  • DATE
  • TIME
  • TIMESTAMP
  • TIMESTAMPTZ
  • Each rendered to the target dialect's own spelling
  • The design stays portable; the DDL does not have to be
01 / PREVIEW

An ordered statement list, plus warnings

Planning a Construction produces the exact ordered list of statements Qrly intends to execute, rendered as a runnable script, together with warnings about anything the target dialect will not do — for example that Snowflake has no CREATE INDEX.

Crucially, preview needs no DDL permission at all. Anyone allowed to design can see precisely what would be applied. That is the right split: reading a plan is not dangerous, and making people ask for write rights in order to review a change is how reviews stop happening.

02 / ORDERING

Schemas, drops, tables, indexes, foreign keys

Statements are emitted in a fixed order: schemas first, then any opted-in drops, then tables, then indexes, then foreign keys. Deferring the foreign keys to the end is what allows two tables that reference each other to be created at all — neither has to exist before the other.

SQLite cannot add foreign keys after the fact, so it inlines them, and the plan says so with a warning rather than failing halfway through.

03 / EXECUTION

One statement at a time, in auto-commit

Statements run individually in auto-commit. This is deliberate: on most engines DDL commits implicitly, so wrapping a batch in a transaction would promise a rollback that cannot happen. Qrly declines to make that promise.

Execution stops at the first failure. Nothing after the failing statement is attempted, so a broken plan leaves a partially built schema you can inspect rather than an unpredictable one it kept pushing through.

04 / THE RECORD

Verbatim SQL and how far it got

Each run records the verbatim SQL, the total number of statements and the number actually run, status, error, duration and who triggered it. Constructions carry a status of DRAFT, APPLIED or ERROR; runs carry RUNNING, SUCCESS or ERROR.

An apply-history table shows when each attempt happened, its status, its statements and its detail. When something is wrong in the database at nine on a Monday, the answer to "what did this tool actually execute" is one page away, exact, and not reconstructed from logs.

Nothing is enabled until somebody enables it

Each of the three ddl_enabled flags defaults to off. A freshly installed Qrly cannot execute DDL anywhere, against any connection, for anyone — including administrators. Turning it on is a series of explicit decisions taken at three different levels of the hierarchy, each by someone with authority at that level.

  1. Tenant. The tenant must have ddl_enabled. This is the platform-level decision: does this installation permit schema authoring at all?
  2. Organisation. The organisation must have ddl_enabled. A tenant that permits DDL in principle can still withhold it from most of its organisations.
  3. Connection. The connection must have ddl_enabled. This is where the decision gets specific: the warehouse where a modelling team builds staging tables can allow it while the production replica beside it does not.
  4. The caller. On top of all three flags, the user applying a Construction must hold FULL data access on the target connection — the same level required to author native SQL. RESTRICTED access, which only runs saved questions, is nowhere near enough.
  5. Told which gate is shut. The policy service reports the outermost blocking tier. An administrator who sees "blocked at tenant" does not waste an afternoon toggling connection settings, and a user sees a policy-blocked banner instead of a failure at execution time.
SQL importer
Handled
Limit
Statements parsed
CREATE SCHEMA; CREATE TABLE with inline and table-level PK, UNIQUE and FK; CREATE [UNIQUE] INDEX; ALTER TABLE … ADD FOREIGN KEY
200 tables, 2,000,000 characters
Dialect spellings folded
SERIAL, IDENTITY, AUTO_INCREMENT, VARCHAR2, NVARCHAR, NUMBER
Mapped to the portable logical types
Warned, not silently dropped
Check constraints, partitioning, storage clauses, triggers, generated columns
The design document cannot express these
01 / IMPORTER

Read the schema you already have

Point the importer at existing DDL and it reconstructs a design document from it. Dialect-specific type spellings are folded into the portable logical types, so an imported Postgres schema can be previewed against SQL Server without hand-editing every column.

The warnings are the important part. A tool that quietly discards your check constraints and partitioning while reporting success has told you a comfortable lie; Qrly lists what it could not represent so you know what the design does not carry.

02 / AI

The assistant proposes a design, never SQL

The AI assistant returns a whole design document. It does not return statements, and there is no path by which its output reaches the database as text. The proposal is rebuilt field by field through the same DDL generator that compiles a hand-authored design.

That rebuild is the trust boundary. Model output is treated as structured data to be validated against the same rules as human input — the identifier regex, the default-expression allow-list, the referential-action allow-list — rather than as instructions to be run.

03 / CAPS

Bounded proposals

An AI-proposed design is capped at 60 tables, and at 200 columns, indexes and foreign keys per table. A model asked for "a data warehouse" cannot return something arbitrarily large that somebody then applies without reading it.

And it arrives as a draft, subject to the same preview and the same cascade as anything else. Nothing about the AI path shortens the route to execution.

04 / DEFENCES

Three allow-lists, no free text

Identifiers must match an identifier regex. Column defaults are restricted to NULL, TRUE, FALSE, CURRENT_TIMESTAMP, CURRENT_DATE, CURRENT_TIME, NOW(), a numeric literal or a simple quoted string. Referential actions are restricted to the five standard actions.

The point of an allow-list rather than an escaping routine is that it fails closed. An unrecognised default expression is rejected, not escaped and hoped for — and there is no field anywhere on the path where free-form SQL text is carried through to the target.

Is Constructions just an open SQL console?

No. You cannot type arbitrary DDL and have Qrly run it. You build a structured design document — schemas, tables, columns with portable logical types, indexes and foreign keys — and Qrly compiles that document into dialect-specific statements. Everything that reaches the database has been generated by the DDL generator from validated fields.

Constructions is also the only path in the product that changes the shape of a customer database: elsewhere the connection pool hands out read-only connections, native SQL passes the sanitizer and every parameter is bound.

What must be enabled before any DDL actually runs?

Four things, all of them. The tenant must have ddl_enabled, the organisation must have ddl_enabled and the connection must have ddl_enabled — all three default to off — and the caller must hold FULL data access on the target connection.

The policy service reports the outermost blocking tier, so an administrator is told which level to fix rather than guessing, and a policy-blocked banner appears in the UI when DDL is disabled.

Is applying a Construction transactional?

No, and Qrly does not pretend otherwise. Statements run one at a time in auto-commit, because DDL commits implicitly on most engines and wrapping it in a transaction would be a false promise.

Execution stops at the first failure, and the run records the verbatim SQL, statements total and statements run, status, error, duration and who triggered it — so you know exactly how far it got and exactly what reached the database.

Can I import a schema that already exists?

Yes. The SQL importer reads existing DDL back into a design document: CREATE SCHEMA, CREATE TABLE with inline and table-level primary key, unique and foreign key constraints, CREATE [UNIQUE] INDEX, and ALTER TABLE … ADD FOREIGN KEY.

It folds dialect spellings such as SERIAL, IDENTITY, AUTO_INCREMENT, VARCHAR2, NVARCHAR and NUMBER into portable logical types, and warns on features the design document cannot express — check constraints, partitioning, storage clauses, triggers and generated columns. It caps at 200 tables and 2,000,000 characters.

Does the AI assistant write SQL against my database?

No. The assistant proposes a whole design document, never SQL. That proposal is rebuilt field by field through the same DDL generator that compiles a hand-authored design, which is the trust boundary: model output is treated as structured data to be validated, not as statements to be executed.

It is capped at 60 tables and 200 columns, indexes and foreign keys per table, and the result is still a draft you preview before applying.

How do you prevent injection through the schema designer?

Three allow-lists. Identifiers must match an identifier regex. Column default expressions are restricted to an allow-list — NULL, TRUE, FALSE, CURRENT_TIMESTAMP, CURRENT_DATE, CURRENT_TIME, NOW(), a numeric literal or a simple quoted string. Referential actions are restricted to CASCADE, SET NULL, SET DEFAULT, RESTRICT and NO ACTION.

Anything outside those sets is rejected before compilation, so there is no free-text field on the path to the database.

Schema authoring your DBA can actually approve

Default off at three levels, full data access required, every statement previewed and every run recorded verbatim.