Skip to content

Commit 4b97748

Browse files
authored
Merge pull request #22787 from owen-mc/docs/db-upgrade-new-entities
Docs: Document `QlBuiltins::NewEntity` for db upgrade/downgrade scripts
2 parents 7218865 + 7952f3c commit 4b97748

1 file changed

Lines changed: 59 additions & 0 deletions

File tree

‎docs/prepare-db-upgrade-downgrade.md‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,65 @@ relation1.rel: run upgrade.qlo predicate1
101101
relation2.rel: run upgrade.qlo predicate2
102102
```
103103

104+
### Creating new database entities
105+
106+
An upgrade or downgrade query can create database entities that do not exist in
107+
the source database by using the compiler-provided `QlBuiltins::NewEntity`
108+
module. Define a `newtype` whose values uniquely identify the entities to
109+
create, and instantiate `NewEntity` with that type:
110+
111+
```ql
112+
class Element extends @element {
113+
string toString() { none() }
114+
}
115+
116+
// Create one wrapper for each existing element.
117+
newtype TAddedElement = TWrapper(Element element)
118+
119+
module Fresh = QlBuiltins::NewEntity<TAddedElement>;
120+
```
121+
122+
`Fresh::map` maps each value of `TAddedElement` to a distinct new entity ID.
123+
Using an algebraic data type with multiple constructors allows several distinct
124+
entities to be created for the same source entity.
125+
126+
The query is compiled against the source schema, so its static QL types cannot
127+
refer to an entity type that exists only in the target schema. An output column
128+
that contains only fresh IDs can use `Fresh::EntityId` directly:
129+
130+
```ql
131+
query predicate new_wrappers(Fresh::EntityId wrapper, Element element) {
132+
wrapper = Fresh::map(TWrapper(element))
133+
}
134+
```
135+
136+
Run the predicate to populate a relation in the target schema:
137+
138+
```properties
139+
wrappers.rel: run upgrade.qlo new_wrappers
140+
```
141+
142+
If an output column can contain both existing source IDs and fresh IDs, define a
143+
union of the source-schema type and `Fresh::EntityId`, then use a class extending
144+
that union as the column's static QL type:
145+
146+
```ql
147+
class TExistingOrFreshElement = @element or Fresh::EntityId;
148+
149+
class ExistingOrFreshElement extends TExistingOrFreshElement {
150+
string toString() { none() }
151+
}
152+
```
153+
154+
These QL types only describe values while evaluating the transformation against
155+
the source schema. The corresponding column type in the target database schema
156+
determines the database type assigned to each fresh ID. Emit every target
157+
relation needed to describe the new entity, including relationships such as its
158+
parent, location, or type. When rewriting an existing relation, its output
159+
predicate will usually need to preserve all existing rows as well as add rows
160+
containing the fresh IDs. Reuse the same `Fresh::map(...)` expression in each
161+
predicate that refers to a particular new entity.
162+
104163
### Testing your scripts
105164

106165
Although we have some automated testing of the scripts (e.g. to test that you can upgrade databases all the way from an initial dbscheme to the newest, and back), it's essential that you apply some more rigorous testing for any non-trivial upgrade or downgrade. You might do so as follows:

0 commit comments

Comments
 (0)