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
//! What a live database differs from a declaration by (issue #153).
//!
//! `cratefield_tables::diff` compares two declarations. This compares a
//! declaration against a database that already exists, which is a
//! different question with two traps in it — both of which produce
//! *false* reports, and a false "rewrite" is worse than no report at all
//! because it teaches an operator to skip the next one.
//!
//! # A database holds more than one declaration's tables
//!
//! [`schema`](crate::schema) reads the whole catalog. A venture's
//! `[tables]` section is a subset of it: the module crates own tables
//! too, and so does anything an operator made by hand. Diffing the two
//! whole schemas would report every module table as *"table is gone"* —
//! alarming, and wrong. **A table the declaration does not name is not
//! the declaration's to remove**, so drift narrows the live side to the
//! tables the declaration actually names.
//!
//! # A catalog cannot see most of a declaration
//!
//! A column's storage type is not its declared kind. `text`, `uuid`,
//! `json`, `enum` and `timestamp` are all `TEXT`, and `boolean` is
//! `INTEGER` on SQLite — so reading the catalog back gives `text` for
//! five kinds and `integer` for two. Comparing that against the
//! declaration directly reports a type rewrite for every uuid, json, enum
//! and timestamp column in the venture.
//!
//! The same applies to everything the declaration knows and the catalog
//! does not: length and numeric bounds, formats, enum members, defaults.
//! A declared `max_len = 200` against a catalog that reports no bound
//! reads as *"bounds narrow"* — a rewrite, for a column that is exactly
//! what was asked for.
//!
//! So drift does not compare against the declaration. It compares against
//! a **projection** of it: the declaration reduced to what a catalog read
//! could show. Everything the projection drops is something this
//! comparison cannot speak about, and [`unseen`] says what that is in one
//! place rather than leaving it to be discovered.
//!
//! The cost of the projection is stated rather than hidden: a change from
//! `text` to `uuid` is **invisible** here, because both are `TEXT` and the
//! database genuinely does not know the difference. `cratefield_tables::diff`
//! between two declarations does see it; this cannot, and saying "no
//! drift" about it is the honest answer to the question actually asked.
use ;
use ;
/// What a comparison against a live catalog cannot see, for a caller that
/// wants to say so alongside the report.
///
/// Each of these is a property the declaration carries and the database
/// does not record in a form this crate reads. They are not "no drift" —
/// they are "not asked".
pub const UNSEEN: & = &;
/// The same list as a sentence, for a report's footer.
/// Every difference between `declared` and the database behind `db`, in
/// `cratefield_tables`' expand/contract/rewrite vocabulary.
///
/// Only the tables `declared` names are looked at, and only the
/// properties a catalog records are compared — see the module docs and
/// [`unseen`].
///
/// # Errors
///
/// [`DbError`] when the catalog cannot be read.
pub async
/// The comparison itself, separated from the read so it can be driven
/// over a catalog captured earlier — and so the two traps above are
/// testable without a database.
/// The declaration reduced to what a catalog read could show.
///
/// Everything dropped here is in [`UNSEEN`]. Keeping the two in step is
/// the point of `the_projection_drops_exactly_what_unseen_lists`.
/// The kind a declared field reads back as, once it has been through a
/// database's own catalog.
///
/// Derived from the column types `cratefield-tables` renders and the
/// types [`schema`](crate::schema) maps them back to. Where the two
/// engines disagree — `boolean` is `INTEGER` on SQLite and `BOOLEAN` on
/// Postgres — this answers with the *lossy* one, because reporting no
/// drift on a real difference is the failure this whole module is
/// avoiding, and the alternative is a false rewrite on every boolean
/// column in every SQLite venture.