razeri-plugin-contract 0.21.0

The dialect contract Razeri plugins are written against — rendering traits, data types, platform identity
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
// Copyright (C) 2026 Gabriel Shea
// Licensed under either of the Apache License, Version 2.0 or the MIT
// license, at your option.

//! **The one derivation of a surrogate key**, composed from the vocabulary a
//! dialect states.
//!
//! The key is **XXH64, seed 0, of the UTF-8 bytes of the value as the table
//! stores it**, read as a signed 64-bit integer. Nothing trims, lowercases or
//! replaces anything on the way in: a column that wants its value cleaned says
//! so in the design, the load that writes the table cleans it, and the key is
//! the hash of what the table then holds.
//!
//! [`SidSpelling`] says how one engine joins strings, casts a value to text,
//! writes a replacement and spells a 64-bit hash. This module turns those
//! answers into the expression a warehouse runs:
//!
//! ```text
//! guard( hash( part₁ ) )                                  one column
//! guard( hash( escape(part₁) ‖ '|' ‖ … ‖ escape(partₙ) ) )  two or more
//! ```
//!
//! It is published so that a dialect author can render their own key
//! expression and run it. Without it, *"the same key on every engine"* is a
//! claim the person making it cannot check — and a key that differs by one
//! character keys a warehouse to numbers nothing else reproduces.
//!
//! # What a dialect decides, and what it does not
//!
//! The **vocabulary** is the dialect's, and it is the only thing that varies.
//! The **derivation** is fixed: there is no table of engine names here and no
//! way to add one, so a third party spells their own engine's key by stating a
//! [`SidSpelling`] and nothing else.
//!
//! # The parts of the derivation
//!
//! - each **part** is the key column cast to text, and nothing more;
//! - a key of **one** column is hashed as that text, with nothing done to it;
//! - a key of **two or more** columns **escapes** each part — every `\`
//!   doubled, then a `\` put before every `|` ([`escape_part`]) — and joins
//!   the parts with the literal `|`. Without the escape `('a|b', 'c')` and
//!   `('a', 'b|c')` are one text and one key. A part holding neither character
//!   is its own text, so its key is the number it always was;
//! - **guard** is the [`NULL_SID`] sentinel, and it fires when **any** part is
//!   NULL: any NULL component keys the whole member to the sentinel;
//! - **hash** is either this engine's own spelling or a call to the
//!   [`HELPER_FUNCTION_NAME`] function, whose body is that same spelling over
//!   its argument as given. The two sites are one derivation — see
//!   [`HashSite`].

/// **The sentinel a member with any NULL key part is keyed to**, `(-2^63) + 2`.
///
/// One number, on every engine, for one reason: a NULL natural key is an
/// absence rather than a value, and two absences are not the same member. It
/// sits two above the minimum so that a warehouse can still reserve the
/// minimum itself.
pub const NULL_SID: i64 = -9_223_372_036_854_775_806_i64;

/// **The name of the surrogate-key function — the only one.**
///
/// One public name everywhere, whoever defines it. On engines where the hash is
/// an ordinary SQL expression, Razeri emits a function of this name whose body
/// is the derivation below. On engines where the hash lives outside SQL — a
/// compiled library, an assembly, a registered handler — the dialect's
/// prerequisites install an artifact under this same name and
/// [`SidSpelling::hash`] names it directly, so nothing emits a second
/// definition.
///
/// A second name is a second contract. There is no alias and no transitional
/// spelling.
pub const HELPER_FUNCTION_NAME: &str = "generate_sid";

use crate::ExpressionRenderer;
use crate::sid_spelling::{Concat, SidSpelling, TextCast, Trim};

/// Where the hash itself is spelled.
#[derive(Debug, Clone, Copy)]
pub enum HashSite<'a> {
    /// Inline this engine's own hash spelling, from [`SidSpelling::hash`].
    Native,
    /// Inline this engine's own hash spelling, in the schema Razeri installed
    /// it in — [`SidPrerequisiteHome::Schema`](crate::SidPrerequisiteHome).
    ///
    /// The schema, a dot, and then [`SidSpelling::hash`] exactly as [`Native`]
    /// writes it. So a dialect that names a home owes a `hash` whose opening
    /// text BEGINS with the function's own name: `razeri_xxh64(` qualifies to
    /// `razeri.razeri_xxh64(`, while a spelling that opened with a cast —
    /// `CAST(xxHash64(` — would put the schema in front of the cast and name
    /// nothing. Every engine that opens with a cast has a server-global
    /// function and names no home, which is why the two facts have never met.
    ///
    /// [`Native`]: Self::Native
    NativeIn(&'a str),
    /// Call the [`HELPER_FUNCTION_NAME`] function by this — possibly
    /// schema-qualified — name. Its body is the [`HashSite::Native`] spelling,
    /// so the two sites are the same derivation.
    ///
    /// **The qualifier is the function's home, not the caller's schema.** One
    /// function is created per deployment unit, so a call from any other schema
    /// of that unit has to name the schema that has it. A caller that qualifies
    /// with its own schema names a function nothing created.
    Helper(&'a str),
}

/// **`TRIM(expr)` in this engine's spelling** — the space around a value
/// taken off, and nothing else.
///
/// Public because taking the space off a value the source wrote is asked in
/// more than one place — reading an amount the source spelled its own way,
/// and a column's `trim` cleaning step — and two spellings of `TRIM` would be
/// two answers to one question. The key itself trims nothing.
///
/// Parameters:
/// - `spelling`: this engine's vocabulary;
/// - `expr`: an already-rendered value expression.
#[must_use]
pub fn normalize_trim(spelling: SidSpelling, expr: &str) -> String {
    match spelling.trim {
        Trim::Function(name) => format!("{name}({expr})"),
        Trim::LtrimRtrim => format!("LTRIM(RTRIM({expr}))"),
        Trim::FunctionWithSpaceSet(name) => format!("{name}({expr}, ' ')"),
    }
}

/// **`CAST(expr AS text)` in this engine's spelling**, total over NULL.
///
/// Public for the reason [`normalize_trim`] is public: turning a value into
/// text is not something only the key derivation does. A caller that has to
/// read a value back out of an engine as text — because the transport it
/// arrives over encodes the engine's own types in a form the same engine will
/// not accept back — needs this engine's cast and not a second opinion about
/// it. Two spellings of one cast are two answers to one question.
///
/// Parameters:
/// - `spelling`: this engine's vocabulary;
/// - `expr`: an already-rendered value expression.
#[must_use]
pub fn cast_to_text(spelling: SidSpelling, expr: &str) -> String {
    match spelling.cast_type {
        TextCast::Direct(ty) => format!("CAST({expr} AS {ty})"),
        TextCast::ClickHouseNullTolerant(ty) => {
            format!("assumeNotNull(CAST({expr} AS Nullable({ty})))")
        }
    }
}

/// **How an engine writes a backslash between quotes** — the one thing about
/// its string literals the escape inside a composite key has to know.
///
/// The escape writes `'\'`, `'\\'` and `'\|'` as literals, and engines
/// disagree about what those are. Standard SQL reads a backslash in a literal
/// as itself; some engines read it as the start of an escape, so one
/// backslash is written as two there. A literal written the wrong way is a
/// different string, or no string at all, and the key it makes is a number
/// no other engine computes.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Backslash {
    /// `'\'` is one backslash: standard SQL, and the default.
    AsWritten,
    /// `'\\'` is one backslash: the engine reads a backslash in a literal
    /// as an escape.
    Doubled,
}

impl Backslash {
    /// **This engine's answer, read from the engine's own words.**
    ///
    /// The one place every dialect already says how it writes a backslash
    /// between quotes is the list of words it reads as a missing value in a
    /// file ([`ExpressionRenderer::render_missing_value`]): an engine whose
    /// literals read a backslash as an escape writes each one twice there, so
    /// that a word holding one stays that word. So the dialect is asked for a
    /// word that is one backslash, and the answer is read off what it writes.
    /// A dialect that writes the word as standard SQL does is
    /// [`Backslash::AsWritten`].
    ///
    /// Parameters:
    /// - `dialect`: the engine the key is written for.
    #[must_use]
    pub fn of(dialect: &dyn ExpressionRenderer) -> Self {
        if dialect.render_missing_value("v", &["\\"]).contains("'\\\\'") {
            Self::Doubled
        } else {
            Self::AsWritten
        }
    }

    /// **`text` as a string literal on this engine**: between single quotes,
    /// each backslash written the way this engine reads one, behind `prefix`,
    /// the engine's Unicode-literal prefix
    /// ([`Lower::pattern_literal_prefix`](crate::Lower::pattern_literal_prefix)).
    /// A quote in `text` is doubled.
    ///
    /// Parameters:
    /// - `prefix`: what this engine puts in front of a Unicode literal.
    /// - `text`: the literal's characters.
    #[must_use]
    pub fn literal(self, prefix: &str, text: &str) -> String {
        let quoted = text.replace('\'', "''");
        match self {
            Self::AsWritten => format!("{prefix}'{quoted}'"),
            Self::Doubled => format!("{prefix}'{}'", quoted.replace('\\', "\\\\")),
        }
    }
}

/// **One part of a composite key, escaped in this engine's words**: every
/// `\` doubled, then a `\` put before every `|` — two replacements, the
/// backslash first, so the second one's own backslashes are not doubled
/// again. The subject is pinned to the engine's binary collation where its
/// key spelling names one
/// ([`Lower::match_collation`](crate::Lower::match_collation)), so the
/// replacement matches by the character.
///
/// Parameters:
/// - `part`: the part, already cast to text.
/// - `spelling`: this engine's vocabulary — its `REPLACE`, its literal prefix
///   and its binary collation.
/// - `backslash`: how this engine writes a backslash between quotes.
#[must_use]
pub fn escape_part(part: &str, spelling: SidSpelling, backslash: Backslash) -> String {
    let words = spelling.lower;
    let literal = |text: &str| backslash.literal(words.pattern_literal_prefix, text);
    let subject = match words.match_collation {
        Some(collation) => format!("{part} COLLATE {collation}"),
        None => part.to_owned(),
    };
    let doubled = format!("{}({subject}, {}, {})", words.replace, literal("\\"), literal("\\\\"));
    format!("{}({doubled}, {}, {})", words.replace, literal("|"), literal("\\|"))
}

/// **The join, written for an engine whose `CONCAT` takes exactly two
/// arguments**: the parts and the separators between them, folded left to
/// right, one call per pair. The same items in the same order as the
/// variadic call, so the joined string is the same string.
///
/// An empty key renders the bare call, as the variadic form does.
fn nest_pairwise(name: &str, parts: &[String]) -> String {
    let mut rest = parts.iter();
    let Some(first) = rest.next() else { return format!("{name}()") };
    rest.fold(first.clone(), |left, next| format!("{name}({name}({left}, '|'), {next})"))
}

/// **The derivation**: one part hashed as given; two or more parts each
/// escaped ([`escape_part`]), joined on `'|'`, and hashed.
///
/// `text_args` are already-rendered value expressions, already cast to text
/// where the caller needs it — a column reference inside a load statement, a
/// parameter name inside the key function's own body. Callers name the value
/// differently, so composing the name here would mean knowing which caller was
/// asking.
///
/// The result carries **no NULL guard**; [`guard_null_members`] wraps this.
///
/// Parameters:
/// - `text_args`: the key's parts, already cast to text;
/// - `spelling`: this engine's vocabulary;
/// - `backslash`: how this engine writes a backslash between quotes
///   ([`Backslash::of`]);
/// - `site`: whether the hash is inlined or reached through the function.
#[must_use]
pub fn sid_derivation(
    text_args: &[String],
    spelling: SidSpelling,
    backslash: Backslash,
    site: HashSite<'_>,
) -> String {
    let joined = if text_args.len() == 1 {
        text_args[0].clone()
    } else {
        let parts: Vec<String> =
            text_args.iter().map(|part| escape_part(part, spelling, backslash)).collect();
        match spelling.concat {
            Concat::Function(name) => format!("{name}({})", parts.join(", '|', ")),
            Concat::FunctionBinary(name) => nest_pairwise(name, &parts),
            Concat::Plus => parts.join(" + '|' + "),
            Concat::Operator(operator) => parts.join(&format!(" {operator} '|' {operator} ")),
        }
    };
    match site {
        HashSite::Native => format!("{}{joined}{}", spelling.hash.0, spelling.hash.1),
        HashSite::NativeIn(home) => {
            format!("{home}.{}{joined}{}", spelling.hash.0, spelling.hash.1)
        }
        HashSite::Helper(name) => format!("{name}({joined})"),
    }
}

/// **The [`NULL_SID`] sentinel, wrapped around the derivation.**
///
/// `raw_args` are the arguments **before** any cast — a cast preserves
/// NULL-ness, and `CAST(x AS TEXT) IS NULL` only makes the guard harder to
/// read.
///
/// **Any NULL component of a composite keys the whole member to the sentinel.**
/// That closes a collision as well as an omission: on several engines
/// `CONCAT('x', '|', NULL)` is `'x|'`, byte-identical to
/// `CONCAT('x', '|', '')`, so `('boston', NULL)` and `('boston', '')` would be
/// one member. Testing the components before they are joined is the only place
/// the distinction still exists.
///
/// The guard is a `CASE WHEN` rather than a `COALESCE` because it tests the
/// **input**. A `COALESCE` over the hash's *output* reaches the sentinel only
/// where the hash propagates NULL, and on at least one engine the hash of NULL
/// is a perfectly ordinary number.
///
/// An empty key renders no guard rather than an empty one.
///
/// Parameters:
/// - `raw_args`: the key's parts as the caller wrote them, before any cast;
/// - `derivation`: the expression [`sid_derivation`] produced.
#[must_use]
pub fn guard_null_members(raw_args: &[String], derivation: String) -> String {
    if raw_args.is_empty() {
        return derivation;
    }
    let test = raw_args.iter().map(|arg| format!("{arg} IS NULL")).collect::<Vec<_>>().join(" OR ");
    format!("CASE WHEN {test} THEN {NULL_SID} ELSE {derivation} END")
}

/// **The surrogate key as one expression**: cast, derive, guard.
///
/// This is the whole composition, and it is what every producer of a key
/// expression emits — the load that keys a dimension, the body of the
/// per-entity key function, the formula a design can name. Rendering it for
/// your own engine and running it is how you check that your
/// [`SidSpelling`] reaches the number every other engine reaches.
///
/// ```
/// use razeri_plugin_contract::{ANSI_SID_SPELLING, sid};
///
/// let key = sid::sid_expression(
///     ANSI_SID_SPELLING,
///     sid::Backslash::AsWritten,
///     None,
///     &["t.city".to_owned()],
/// );
/// assert!(key.starts_with("CASE WHEN t.city IS NULL THEN "));
/// assert!(key.contains("generate_sid(CAST(t.city AS TEXT))"));
/// ```
///
/// # `helper_home` is the key function's schema, never the caller's
///
/// The only thing a schema does here is qualify the call to
/// [`HELPER_FUNCTION_NAME`] on the engines whose
/// [`SidSpelling::through_helper`] is true. Where the key function is an
/// installed artifact instead, its own name is rendered and this argument is
/// ignored entirely — on at least one engine a compiled function lives server
/// globally and a qualified call to it does not resolve at all.
///
/// So the schema this wants is the one the function was **created** in, which
/// is a property of the deployment unit rather than of whichever entity is
/// being keyed. `None` renders the call unqualified, which is correct wherever
/// nothing is qualified: an engine without schemas, or a design that names no
/// schema.
///
/// Parameters:
/// - `spelling`: this engine's vocabulary;
/// - `backslash`: how this engine writes a backslash between quotes
///   ([`Backslash::of`]);
/// - `helper_home`: the schema the key function was created in, where the call
///   needs qualifying;
/// - `parts`: the key's parts as raw value expressions, in order.
#[must_use]
pub fn sid_expression(
    spelling: SidSpelling,
    backslash: Backslash,
    helper_home: Option<&str>,
    parts: &[String],
) -> String {
    let qualified = helper_home
        .map_or_else(|| HELPER_FUNCTION_NAME.to_owned(), |s| format!("{s}.{HELPER_FUNCTION_NAME}"));
    sid_expression_through(spelling, backslash, &qualified, parts)
}

/// **The same expression, with the helper's name already spelled by the
/// target.**
///
/// [`sid_expression`] writes the helper's name the way most engines read it —
/// the schema, a dot, the name, all bare. That is a name on every engine that
/// leaves an unquoted word alone and a *different* name on one that folds it,
/// where the function was created under the spelling that engine's own DDL
/// wrote. A caller holding the dialect spells the name first and hands it in
/// here, so the call and the `CREATE FUNCTION` say the same word.
///
/// Parameters:
/// - `spelling`: this engine's vocabulary;
/// - `backslash`: how this engine writes a backslash between quotes;
/// - `helper`: the key helper's name, qualified and spelled;
/// - `parts`: the key's parts as raw value expressions, in order.
#[must_use]
pub fn sid_expression_through(
    spelling: SidSpelling,
    backslash: Backslash,
    helper: &str,
    parts: &[String],
) -> String {
    let cast: Vec<String> = parts.iter().map(|arg| cast_to_text(spelling, arg)).collect();
    let site = if spelling.through_helper { HashSite::Helper(helper) } else { HashSite::Native };
    guard_null_members(parts, sid_derivation(&cast, spelling, backslash, site))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::sid_spelling::{ANSI_SID_SPELLING, Lower};

    /// The methods a test renderer has to answer, and nothing else.
    macro_rules! for_test_renderer {
        ($name:ident) => {
            impl crate::SqlRenderer for $name {
                fn render_data_type(&self, _: &crate::DataType) -> String {
                    String::new()
                }
                fn quote_identifier(&self, name: &str) -> String {
                    name.to_owned()
                }
                fn qualify_name(&self, _: Option<&str>, name: &str) -> String {
                    name.to_owned()
                }
                fn render_current_timestamp(&self) -> String {
                    String::new()
                }
            }
        };
    }

    /// A vocabulary for the composition tests: a fixture, not an engine.
    const HELPER_ROUTED: SidSpelling =
        SidSpelling { platform: "test", hash: ("pg_xxhash64(", ")"), ..ANSI_SID_SPELLING };

    /// Standard SQL's backslash, for the composition tests.
    const STD: Backslash = Backslash::AsWritten;

    /// `a` and `b`, each escaped as standard SQL writes it.
    const A_B: &str = "REPLACE(REPLACE(a, '\\', '\\\\'), '|', '\\|'), '|', \
                       REPLACE(REPLACE(b, '\\', '\\\\'), '|', '\\|')";

    /// **Nothing is done to a one-column key before it is hashed**: no
    /// `TRIM`, no `LOWER`, no `REPLACE` — the cast and the hash, and nothing
    /// between.
    #[test]
    fn a_part_is_hashed_as_it_is_cast() {
        assert_eq!(
            sid_expression(HELPER_ROUTED, STD, None, &["x".to_owned()]),
            format!("CASE WHEN x IS NULL THEN {NULL_SID} ELSE generate_sid(CAST(x AS TEXT)) END")
        );
        let native = SidSpelling { through_helper: false, ..HELPER_ROUTED };
        for spelling in [HELPER_ROUTED, native] {
            for backslash in [STD, Backslash::Doubled] {
                let rendered = sid_expression(spelling, backslash, Some("acme"), &["x".to_owned()]);
                for fold in ["TRIM(", "LOWER(", "REPLACE("] {
                    assert!(!rendered.contains(fold), "{fold} in {rendered}");
                }
            }
        }
    }

    /// **Each part of a composite is escaped before the join**: every `\`
    /// doubled, then a `\` put before every `|`, in this engine's `REPLACE`,
    /// its literal prefix, its binary collation and its way of writing a
    /// backslash.
    #[test]
    fn a_composite_escapes_each_part_before_the_join() {
        let native = SidSpelling { through_helper: false, ..HELPER_ROUTED };
        assert_eq!(
            sid_derivation(&["a".to_owned(), "b".to_owned()], native, STD, HashSite::Native),
            format!("pg_xxhash64(CONCAT({A_B}))")
        );
        assert_eq!(
            escape_part("a", ANSI_SID_SPELLING, Backslash::Doubled),
            "REPLACE(REPLACE(a, '\\\\', '\\\\\\\\'), '|', '\\\\|')"
        );
        let tsql_like = SidSpelling {
            lower: Lower {
                function: "LOWER",
                replace: "REPLACE",
                pattern_literal_prefix: "N",
                match_collation: Some("Latin1_General_100_BIN2"),
            },
            ..ANSI_SID_SPELLING
        };
        assert_eq!(
            escape_part("a", tsql_like, STD),
            "REPLACE(REPLACE(a COLLATE Latin1_General_100_BIN2, N'\\', N'\\\\'), N'|', N'\\|')"
        );
        let clickhouse_like = SidSpelling {
            lower: Lower { replace: "replaceAll", ..ANSI_SID_SPELLING.lower },
            ..ANSI_SID_SPELLING
        };
        assert!(
            escape_part("a", clickhouse_like, Backslash::Doubled)
                .starts_with("replaceAll(replaceAll(a, "),
            "the engine's own replacement function"
        );
    }

    /// **An engine's backslash is read from its own missing-value words.**
    #[test]
    fn the_backslash_is_read_from_the_dialects_own_literal() {
        struct Escaping;
        struct Standard;
        for_test_renderer!(Escaping);
        for_test_renderer!(Standard);
        impl ExpressionRenderer for Standard {}
        impl ExpressionRenderer for Escaping {
            fn render_missing_value(&self, text: &str, words: &[&str]) -> String {
                let listed: Vec<String> =
                    words.iter().map(|w| format!("'{}'", w.replace('\\', "\\\\"))).collect();
                format!("CASE WHEN {text} IN ({}) THEN NULL ELSE {text} END", listed.join(", "))
            }
        }
        assert_eq!(Backslash::of(&Escaping), Backslash::Doubled);
        assert_eq!(Backslash::of(&Standard), Backslash::AsWritten);
        assert_eq!(Backslash::Doubled.literal("", "\\|"), "'\\\\|'");
        assert_eq!(Backslash::AsWritten.literal("N", "\\|"), "N'\\|'");
        assert_eq!(Backslash::AsWritten.literal("", "O'Hare"), "'O''Hare'");
    }

    /// **The call through the key function and the inlined spelling are one
    /// derivation** over the same parts.
    #[test]
    fn the_helper_site_and_the_native_site_hash_the_same_text() {
        let args = ["a".to_owned(), "b".to_owned()];
        assert_eq!(
            sid_derivation(&args, HELPER_ROUTED, STD, HashSite::Native),
            format!("pg_xxhash64(CONCAT({A_B}))")
        );
        assert_eq!(
            sid_derivation(&args, HELPER_ROUTED, STD, HashSite::Helper(HELPER_FUNCTION_NAME)),
            format!("generate_sid(CONCAT({A_B}))")
        );
        assert_eq!(
            sid_derivation(&args, HELPER_ROUTED, STD, HashSite::NativeIn("razeri")),
            format!("razeri.pg_xxhash64(CONCAT({A_B}))")
        );
    }

    /// **The four join forms are four spellings of one string**: the same
    /// items, in the same order, with the same separators.
    #[test]
    fn the_four_join_forms_join_the_same_items() {
        let three = ["a".to_owned(), "b".to_owned(), "c".to_owned()];
        let with = |concat| {
            sid_derivation(&three, SidSpelling { concat, ..HELPER_ROUTED }, STD, HashSite::Native)
        };
        let [a, b, c] = three.clone().map(|part| escape_part(&part, HELPER_ROUTED, STD));
        assert_eq!(
            with(Concat::Function("CONCAT")),
            format!("pg_xxhash64(CONCAT({a}, '|', {b}, '|', {c}))")
        );
        assert_eq!(
            with(Concat::FunctionBinary("CONCAT")),
            format!("pg_xxhash64(CONCAT(CONCAT(CONCAT(CONCAT({a}, '|'), {b}), '|'), {c}))")
        );
        assert_eq!(with(Concat::Plus), format!("pg_xxhash64({a} + '|' + {b} + '|' + {c})"));
        assert_eq!(
            with(Concat::Operator("||")),
            format!("pg_xxhash64({a} || '|' || {b} || '|' || {c})")
        );
        let one = ["a".to_owned()];
        assert_eq!(
            sid_derivation(
                &one,
                SidSpelling { concat: Concat::Plus, ..HELPER_ROUTED },
                STD,
                HashSite::Native
            ),
            "pg_xxhash64(a)",
            "a one-part key is neither escaped nor joined"
        );
        assert_eq!(
            sid_derivation(
                &[],
                SidSpelling { concat: Concat::FunctionBinary("CONCAT"), ..HELPER_ROUTED },
                STD,
                HashSite::Native
            ),
            "pg_xxhash64(CONCAT())"
        );
    }

    /// **Any NULL part keys the whole member to the sentinel**, the guard
    /// testing the parts before the cast; an empty key renders no guard.
    #[test]
    fn any_null_part_reaches_the_sentinel() {
        let composite = sid_expression(HELPER_ROUTED, STD, None, &["a".to_owned(), "b".to_owned()]);
        assert!(composite.starts_with("CASE WHEN a IS NULL OR b IS NULL THEN "), "{composite}");
        assert!(!sid_expression(HELPER_ROUTED, STD, None, &[]).contains("CASE WHEN"));
    }

    /// **A spelling that does not route through the key function ignores the
    /// schema.** An engine whose key function is an installed artifact
    /// renders a bare call: the artifact is registered where that engine
    /// registers artifacts, not in a schema Razeri created.
    #[test]
    fn a_spelling_that_inlines_its_artifact_ignores_the_schema_it_is_passed() {
        let external =
            SidSpelling { hash: ("generate_sid(", ")"), through_helper: false, ..HELPER_ROUTED };
        let qualified = sid_expression(external, STD, Some("acme"), &["x".to_owned()]);
        let bare = sid_expression(external, STD, None, &["x".to_owned()]);
        assert_eq!(qualified, bare);
        assert!(!qualified.contains("acme."), "the emitted call qualifies an installed artifact");
    }

    /// **An engine that joins with an operator gets one expression**, the
    /// operator between each part and the separator, left to right; a
    /// one-part key is not joined on this form either.
    #[test]
    fn an_operator_join_writes_the_operator_between_each_part_and_the_separator() {
        let spelling = SidSpelling { concat: Concat::Operator("||"), ..HELPER_ROUTED };
        let three = ["a".to_owned(), "b".to_owned(), "c".to_owned()];
        let [a, b, c] = three.clone().map(|part| escape_part(&part, spelling, STD));
        assert_eq!(
            sid_derivation(&three, spelling, STD, HashSite::Native),
            format!("pg_xxhash64({a} || '|' || {b} || '|' || {c})")
        );
        assert_eq!(
            sid_derivation(&["a".to_owned()], spelling, STD, HashSite::Native),
            "pg_xxhash64(a)"
        );
    }

    /// The sentinel is two above the smallest 64-bit integer, and it is spelled
    /// once.
    #[test]
    fn the_sentinel_is_two_above_the_smallest_integer() {
        assert_eq!(NULL_SID, i64::MIN + 2);
    }
}