Skip to main content

razeri_plugin_contract/
lib.rs

1// Copyright (C) 2026 Gabriel Shea
2// Licensed under either of the Apache License, Version 2.0 or the MIT
3// license, at your option.
4
5#![warn(missing_docs)]
6
7//! # razeri-plugin-contract
8//!
9//! **The contract a Razeri dialect plugin is written against.**
10//!
11//! This crate and `razeri-plugin-api` are the two open crates in Razeri's
12//! workspace. Everything a plugin author needs in order to *describe* and
13//! *render* a database platform lives here; nothing else does. It depends on
14//! `serde` and on nothing else at all, which is what lets a plugin be built,
15//! published and consumed outside this repository.
16//!
17//! ## What is in here
18//!
19//! **The rendering hierarchy** — ten traits, layered so that a new dialect
20//! implements four required methods and inherits ANSI-standard behaviour for
21//! the rest:
22//!
23//! 1. [`SqlRenderer`] — core SQL rendering (the four required methods)
24//! 2. [`ExpressionRenderer`] — expressions, with ANSI defaults
25//! 3. [`DdlRenderer`] — DDL, with ANSI defaults
26//! 4. [`DmlRenderer`] — DML, with ANSI defaults
27//! 5. [`ProcedureRenderer`] — stored procedures and functions
28//! 6. [`FormulaRenderer`] — formulas, with ANSI defaults
29//! 7. [`TaskRenderer`] — scheduled tasks
30//! 8. [`ReplicationRenderer`] — data extraction, loading and key-function deployment
31//! 9. [`StorageRenderer`] — partitioning, compression and index strategy
32//! 10. [`DialectCapabilities`] — what the platform's SQL can and cannot do
33//!
34//! **The vocabulary those traits speak**: [`DataType`] and [`DataTypeRef`], the
35//! [`storage_strategy::StoragePlan`] a [`StorageRenderer`] translates, the
36//! [`StoragePlatform`] a storage plugin names, and [`DialectFamily`], the
37//! platform's own identity.
38//!
39//! **The spelling a source gives a day** — [`date_format`]. One import
40//! arrives with `1/1/2015` in one column and `2015-01-01T00:00:00Z` in the
41//! next; the design states the spelling, this crate reads it once into a list
42//! of directives, and each engine translates that list into its own pattern
43//! language through [`ExpressionRenderer::render_parse_datetime`]. A directive
44//! an engine cannot spell is refused by name.
45//!
46//! **The way a source writes an amount** — [`number_format`]. A file writes a
47//! price `$2,899.99 `, where the symbol, the thousands mark and the space
48//! around it are not part of the number. The design states the picture, this
49//! crate reads it once into the marks that are not part of the value, and each
50//! engine takes those marks out and converts what is left through
51//! [`ExpressionRenderer::render_parse_number`]. An engine that states nothing
52//! is refused by name rather than guessed for.
53//!
54//! **The questions asked of a warehouse that is already there** —
55//! [`SqlRenderer::render_catalog_columns_query`] and
56//! [`SqlRenderer::render_catalog_object_times_query`]. A design says what
57//! would be built; how much of a running warehouse was built is a question
58//! only that warehouse answers, so each engine spells the two queries that
59//! ask it and names the schemas a whole-warehouse scan passes over through
60//! [`SqlRenderer::catalog_system_schemas`]. `None` is a complete answer to
61//! either query: an engine with no catalog to read is named rather than
62//! reported as an empty warehouse.
63//!
64//! **The fan-out an engine states** — [`flatten`]. One row holding an array
65//! becomes one row per element, and every engine spells that differently, so
66//! the dialect answers with the whole shape rather than filling in a template.
67//!
68//! **The decisions a dialect states rather than a core crate deciding for it**,
69//! starting with [`SidSpelling`] — how this engine spells a surrogate key —
70//! and continuing with [`ProcedureForm`] and [`ScalarFunctionForm`], the
71//! routine grammars it speaks, [`MergeShape`], how it upserts, and
72//! [`EvolutionAnswers`], what it keeps when a table that already holds rows
73//! changes shape.
74//!
75//! **The surrogate key's composition** — [`sid`] turns a [`SidSpelling`] into
76//! the key expression an engine runs. It is here rather than in the product
77//! because a dialect author who cannot render their own key expression cannot
78//! run it either, and *"the same key on every engine"* is then a claim nobody
79//! outside this repository can check.
80//!
81//! **The named routine grammars, spelled** — [`routine_render`]. A dialect
82//! whose engine has stored procedures in a grammar of its own renders the whole
83//! `CREATE` itself, and *"PL/pgSQL with one tweak"* used to mean writing the
84//! whole thing from nothing. These three functions write out the grammar a
85//! dialect is nearly the same as, so it can start there and say what differs.
86//!
87//! ## What is deliberately not in here
88//!
89//! Razeri's design model, and the renderers that turn it into Razeri's own
90//! output. A plugin says what its engine spells; Razeri composes. That division
91//! is why one contract crate can be open while the product is not.
92//!
93//! The routine grammars are the one thing on both sides of that line, and they
94//! are here because they belong to the engines rather than to Razeri's design: a
95//! plugin author who cannot read the grammar next to theirs has to guess at it.
96//!
97//! ## How this crate is versioned
98//!
99//! While the major version is zero:
100//!
101//! - **a change to a trait's method set bumps the minor version** — a method
102//!   added, removed or renamed, whether or not it carries a default body;
103//! - **documentation and fixes bump the patch version.**
104//!
105//! The rule is about who decides when a plugin's behaviour changes. Cargo
106//! flows a patch release to every plugin pinned at the minor the next time it
107//! builds, and flows a minor release to nobody. A defaulted method costs an
108//! existing plugin no line of code, and it still changes what that plugin
109//! *says*: an engine that has not implemented the new method now answers the
110//! default, and its users meet whatever Razeri does with that answer — a
111//! refusal they had never seen, arriving because somebody else released. So a
112//! new method is opted into, and the plugin author chooses the day.
113//!
114//! This is stricter than semantic versioning requires of a zero-major crate,
115//! deliberately: the looser reading saves a version number and spends a
116//! plugin author's afternoon.
117//!
118//! ### What 0.5.0 adds to the method set
119//!
120//! - [`StorageRenderer::render_partition_retain_procedure`] takes the
121//!   companion table a load sets rows aside in, so one call to a retention
122//!   procedure reaches both tables.
123//! - [`ExpressionRenderer::render_parse_datetime`] renders the parse of a
124//!   value the source spells its own way into a date or a timestamp.
125//! - [`SqlRenderer::delimited_file_relation_in_encoding`] reads a delimited
126//!   file whose bytes are not UTF-8, where the engine states a way to.
127//!
128//! ### What 0.6.0 adds to the method set
129//!
130//! - [`SqlRenderer::render_catalog_columns_query`] asks a running warehouse
131//!   what it holds, one row per column.
132//! - [`SqlRenderer::render_catalog_object_times_query`] asks when each object
133//!   was created, where the engine records it at all.
134//! - [`SqlRenderer::catalog_system_schemas`] names the schemas a
135//!   whole-warehouse scan passes over, and
136//!   [`SqlRenderer::catalog_scope_predicate`] is the `WHERE` clause both
137//!   catalog queries share.
138//! - [`ExpressionRenderer::render_parse_number`] renders the reading of a
139//!   value the source writes its own way into a number.
140//! - [`StorageRenderer::slice_replacement_is_procedure_body`] says whether the
141//!   slice replacement this engine rendered runs on its own or only inside a
142//!   procedure. Left alone, the shape of the answer is read instead.
143//!
144//! Beside the method set, [`sid::normalize_trim`] is now public: the `TRIM` in
145//! an engine's own spelling, which both the canonical normalization and the
146//! reading of a written amount start from.
147
148/// This crate's version, as a plugin was compiled against it.
149///
150/// A plugin loaded from a directory is refused unless it was built against
151/// the same version, from the same source, as the razeri that loads it.
152pub const VERSION: &str = env!("CARGO_PKG_VERSION");
153
154/// A fingerprint of this crate's source. Two builds carrying the same version
155/// over different source have different fingerprints.
156pub const SOURCE_FINGERPRINT: u64 =
157    match u64::from_str_radix(env!("RAZERI_PLUGIN_CONTRACT_SOURCE"), 16) {
158        Ok(fingerprint) => fingerprint,
159        Err(_) => panic!("the build script writes the fingerprint in hexadecimal"),
160    };
161
162pub mod agnostic;
163pub mod capabilities;
164pub mod constraint_enforcement;
165pub mod data_type;
166pub mod date_format;
167pub mod dialect_family;
168pub mod evolution;
169pub mod flatten;
170pub mod formula_renderer;
171pub mod merge_shape;
172pub mod number_format;
173pub mod procedure_log_level;
174pub mod renderer;
175pub mod replication;
176pub mod routine_form;
177pub mod routine_render;
178pub mod routine_spec;
179pub mod sid;
180pub mod sid_spelling;
181pub mod storage_platform;
182pub mod storage_renderer;
183pub mod storage_strategy;
184pub mod task_renderer;
185pub mod value_text;
186
187pub use agnostic::*;
188pub use capabilities::*;
189pub use constraint_enforcement::*;
190pub use data_type::{DataType, DataTypeRef};
191pub use date_format::{DateFormat, DateFormatError, FormatDirective, FormatPart};
192pub use dialect_family::DialectFamily;
193pub use evolution::*;
194pub use flatten::{Flatten, FlattenField, FlattenRequest};
195pub use formula_renderer::*;
196pub use merge_shape::*;
197pub use number_format::{Negatives, NumberFormat, NumberFormatError};
198pub use procedure_log_level::ProcedureLogLevel;
199pub use renderer::*;
200pub use replication::*;
201pub use routine_form::*;
202pub use routine_render::{
203    render_procedure_as, render_scalar_function_as, render_table_function_as,
204};
205pub use routine_spec::*;
206pub use sid::sid_expression;
207pub use sid_spelling::*;
208pub use storage_platform::StoragePlatform;
209pub use storage_renderer::*;
210pub use task_renderer::*;
211pub use value_text::{
212    CanonicalText, DOUBLE_DIGITS, FLOAT_TEXT_FUNCTION, FloatArithmetic, FloatParts, SINGLE_DIGITS,
213    SINGLE_FLOAT_TEXT_FUNCTION, TextFunctions, UTC_OFFSET, ValueTextFunctions, computed_float_text,
214    float_text, float_text_function, in_short_lines, rtrim_fraction, signed_float_text, truth_text,
215};