razeri-plugin-contract
The contract a Razeri dialect plugin is written against.
This crate and razeri-plugin-api are the two open crates in Razeri's workspace. Everything a plugin author needs in order to describe and render a database platform lives here; nothing else does. It depends on serde and on nothing else at all, which is what lets a plugin be built, published and consumed outside Razeri's repository.
What is in here
The rendering hierarchy — ten traits, layered so that a new dialect implements four required methods and inherits ANSI-standard behaviour for the rest:
| Trait | What it renders |
|---|---|
SqlRenderer |
Core SQL — the four required methods |
ExpressionRenderer |
Expressions, with ANSI defaults |
DdlRenderer |
DDL, with ANSI defaults |
DmlRenderer |
DML, with ANSI defaults |
ProcedureRenderer |
Stored procedures and functions |
FormulaRenderer |
Formulas, with ANSI defaults |
TaskRenderer |
Scheduled tasks |
ReplicationRenderer |
Data extraction, loading and key-function deployment |
StorageRenderer |
Partitioning, compression and index strategy |
DialectCapabilities |
What the platform's SQL can and cannot do |
The vocabulary those traits speak: DataType and DataTypeRef, the StoragePlan a StorageRenderer translates, the StoragePlatform a storage plugin names, and DialectFamily, the platform's own identity.
The decisions a dialect states rather than a core crate deciding for it — starting with SidSpelling, how this engine spells a surrogate key, and continuing with ProcedureForm, ScalarFunctionForm and TableFunctionForm, the routine grammars it speaks. Each of the three carries a Custom arm for a grammar Razeri does not know: your dialect names it and renders the routine itself, from the RoutineSpec Razeri hands over.
The spelling a source gives a day — date_format. One import arrives writing 1/1/2015 in one column and 2015-01-01T00:00:00Z in the next, and no two engines read either of them the same way. The design states the spelling, this crate reads it once into a list of directives, and ExpressionRenderer::render_parse_datetime is where your dialect translates that list into whatever pattern language your engine has. A directive your engine cannot say is a refusal naming it, never a guess.
The way a source writes an amount — number_format. The same file writes a price $2,899.99 , and the symbol, the thousands mark and the trailing space are not part of the number. The design states the picture, this crate reads it once into the marks that are not part of the value, and ExpressionRenderer::render_parse_number is where your dialect converts what is left. Three shared shapes are here for the three kinds of engine: one with a conversion that answers nothing, one that needs a shape guard in front of one that does not, and one with neither.
The questions asked of a warehouse that is already there — SqlRenderer::render_catalog_columns_query and render_catalog_object_times_query. Razeri can read a design and say what it would build; how much of a running warehouse it actually built is a question only that warehouse answers. So your dialect spells the two queries that ask it — one row per column, and the moment each object was created where your engine records one — and catalog_system_schemas names the schemas a scan of the whole warehouse passes over. The standard's information_schema is what the first one answers by default, and None is a complete answer to either: an engine that keeps no catalog Razeri can read is named, not reported as an empty warehouse.
The fan-out an engine states — flatten. One row holding an array becomes one row per element, and no two engine families spell that the same way, so the dialect's render_flatten answers with the whole shape — the FROM item, the ordinal and one expression per field — rather than filling in a template. The ordinal it returns counts from one on every engine, because the element's surrogate key is derived from its position.
The surrogate key's composition — sid::sid_expression turns a SidSpelling into the key expression an engine runs. It is here because a dialect author who cannot render their own key expression cannot run it either, and "the same key on every engine" is then a claim nobody outside Razeri's repository can check.
AnsiDialect is a complete worked implementation of the required methods, and render_data_type_ansi is the ANSI type mapping to start from.
How this crate is versioned
While the major version is zero:
- a change to a trait's method set bumps the minor version — a method added, removed or renamed, whether or not it carries a default body;
- documentation and fixes bump the patch version.
The rule is about who decides when a plugin's behaviour changes. Cargo flows a patch release to every plugin pinned at the minor the next time it builds, and flows a minor release to nobody. A defaulted method costs an existing plugin no line of code, and it still changes what that plugin says: an engine that has not implemented the new method now answers the default, and its users meet whatever Razeri does with that answer — a refusal they had never seen, arriving because somebody else released. So a new method is opted into, and the plugin author chooses the day.
This is stricter than semantic versioning requires of a zero-major crate, deliberately: the looser reading saves a version number and spends a plugin author's afternoon.
What 0.5.0 adds to the method set
StorageRenderer::render_partition_retain_proceduretakes the companion table a load sets rows aside in, so one call to a retention procedure reaches both tables.ExpressionRenderer::render_parse_datetimerenders the parse of a value the source spells its own way into a date or a timestamp. Leave it alone and your engine states nothing: a design naming a spelling is refused, with your engine named.SqlRenderer::delimited_file_relation_in_encodingreads a delimited file whose bytes are not UTF-8. Leave it alone and a source naming a fallback encoding is declined, with your engine named, rather than read as some other language.
What 0.6.0 adds to the method set
SqlRenderer::render_catalog_columns_queryasks a running warehouse what it holds, one row per column. Leave it alone and you get the standard'sinformation_schema, which most engines carry; answerNonewhere yours has no catalog to read.SqlRenderer::render_catalog_object_times_queryasks when each object was created. Leave it alone and the answer isNone, which is the truthful one on every engine that records no such moment.SqlRenderer::catalog_system_schemasnames the schemas a scan of a whole warehouse passes over. Leave it alone and it isinformation_schemaalone; add the others your engine keeps its own bookkeeping in.SqlRenderer::catalog_scope_predicateis theWHEREclause both catalog queries share — one named schema, or every schema that is not your engine's own. Leave it alone unless your catalog wants its scope written differently.ExpressionRenderer::render_parse_numberrenders the reading of a value the source writes its own way into a number. Leave it alone and your engine states nothing: a number column naming a picture is refused, with your engine named.StorageRenderer::slice_replacement_is_procedure_bodysays whether the slice replacement you rendered runs on its own or only inside a procedure. Leave it alone and Razeri reads the shape of your answer — a block opened byIFand closed byEND IFis taken for a procedure body — which is a guess your engine can settle.
Beside the method set, sid::normalize_trim is now public: the TRIM in your engine's own spelling, which is where reading a written amount starts and where the canonical normalization starts, so the two cannot drift apart.
Stability
This crate and razeri-plugin-api follow semantic versioning while the
major version is zero, held to the stricter reading above. A trait gains a
method only with a default body, so an existing plugin keeps compiling
across a minor release without changing a line. A method that leaves does so
as #[deprecated] for one minor version before it is removed, so a plugin
sees the warning before the removal, not after.
What is deliberately not in here
Razeri's design model and every renderer that produces Razeri's own output. A plugin says what its engine spells; Razeri composes.
License
Dual-licensed under MIT or Apache 2.0, at your option. Plugins belong to their authors.
Security
Report a vulnerability through GitHub's private vulnerability reporting on the Razeri repository — see its security policy.