razeri-plugin-contract 0.8.0

The dialect contract Razeri plugins are written against — rendering traits, data types, platform identity
Documentation

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_procedure takes the companion table a load sets rows aside in, so one call to a retention procedure reaches both tables.
  • ExpressionRenderer::render_parse_datetime renders 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_encoding reads 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_query asks a running warehouse what it holds, one row per column. Leave it alone and you get the standard's information_schema, which most engines carry; answer None where yours has no catalog to read.
  • SqlRenderer::render_catalog_object_times_query asks when each object was created. Leave it alone and the answer is None, which is the truthful one on every engine that records no such moment.
  • SqlRenderer::catalog_system_schemas names the schemas a scan of a whole warehouse passes over. Leave it alone and it is information_schema alone; add the others your engine keeps its own bookkeeping in.
  • SqlRenderer::catalog_scope_predicate is the WHERE clause 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_number renders 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_body says 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 by IF and closed by END IF is 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.

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.