You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When attempting to follow along with the Easy EdgeDB book, the chapter 18 migrations to add an abstract type (HasMoney) and change an existing type (Person) to extend it now fail.
After answering the migration questions:
Error executing command: EdgeDB could not resolve migration with the provided answers. Please retry with different answers.
I tried to create a minimal reproduction by creating a type and then creating a new migration to create a new abstract type and updating the initial type to extend it, but that did not produce the same result. Instead, I discovered a bug in the migration tool.
EdgeDB Version: 4.1.0+a8fe4d7
EdgeDB CLI Version: 4.5+641a8f3
OS Version: macOS 14.0
Steps to Reproduce:
Migrate to chapter 17 schema (below)
Attempt to migrate to chapter 18 schema (below)
Schemas
Chapter 17
moduledefault {
# Scalar typesscalartype Class extendingenum<Rogue, Mystic, Merchant>;
scalartype LotteryTicket extendingenum<Nothing, WallChicken, ChainWhip, Crucifix, Garlic>;
scalartype Mode extendingenum<Info, Debug>;
scalartype PCNumber extendingsequence;
scalartype Rank extendingenum<Captain, FirstMate, SecondMate, Cook>;
scalartype SleepState extendingenum<Asleep, Awake>;
# Globals and definitionsrequiredglobal tester_mode: Mode {
default:= Mode.Info;
}
global time :=assert_single((select Time));
abstractannotation warning;
# Abstract object typesabstracttype HasNameAndCoffins {
required coffins: int16 {
default:=0;
}
required name: str {
delegatedconstraintexclusive;
constraintmax_len_value(30);
}
}
abstracttype HasNumber {
required number: int16;
}
abstracttype Person {
required name: str {
delegatedconstraintexclusive;
}
multi places_visited: Place;
multi lovers: Person;
is_single :=notexists .lovers;
strength: int16;
first_appearance: cal::local_date;
last_appearance: cal::local_date;
age: int16;
title: str;
degrees: array<str>;
conversational_name := .title ++''++ .name ifexists .title else .name;
pen_name := .name ++', '++array_join(.degrees, ', ') ifexists .degrees else .name;
}
abstracttype Place extending HasNameAndCoffins {
modern_name: str;
multi important_places: Landmark;
}
# Object typestype BookExcerpt {
required date: cal::local_datetime;
required excerpt: str;
indexon (.date);
required author: Person;
}
type Castle extending Place {
doors: array<int16>;
}
type City extending Place {
annotation description :='A place with 50 or more buildings. Anything else is an OtherPlace';
population: int64;
indexon (.name ++': '++<str>.population) {
annotation title :='Lists city name and population for display in Long Library stage';
}
}
type Country extending Place;
type Crewman extending HasNumber, Person {
overloaded name: str {
default:='Crewman '++<str>.number;
}
}
type Event {
required description: str;
required start_time: cal::local_datetime;
required end_time: cal::local_datetime;
requiredmulti place: Place;
requiredmulti people: Person;
location: tuple<float64, float64>;
indexon (.location);
ns_suffix :='_N_'if .location.0>0.0else'_S_';
ew_suffix :='_E'if .location.1>0.0else'_W';
url :=get_url()
++<str>(math::abs(.location.0)) ++ .ns_suffix
++<str>(math::abs(.location.1)) ++ .ew_suffix;
}
type Landmark {
required name: str;
multi context: str;
}
type Lord extending Person {
constraintexpressionon (contains(__subject__.name, 'Lord')) {
errmessage :="All lords need \'Lord\' in their name";
}
}
type MinorVampire extending Person {
former_self: Person;
single master :=assert_single(.<slaves[is Vampire]);
master_name := .master.name;
};
type NPC extending Person {
overloaded age: int16 {
constraintmax_value(120);
}
}
type OtherPlace extending Place {
annotation description :='A place with under 50 buildings - hamlets, small villages, etc.';
annotation warning :='Castles and castle towns do not count! Use the Castle type for that';
}
type Party {
name: str;
members :=.<party[is PC];
}
type PC extending Person {
required class: Class;
required created_at: datetime {
default:=datetime_of_statement();
}
required number: PCNumber {
default:=sequence_next(introspect PCNumber);
}
multi party: Party {
onsourcedeletedeletetargetiforphan;
ontargetdeleteallow;
}
overloadedrequired name: str {
constraintmax_len_value(30);
}
last_updated: datetime {
rewriteinsert, updateusing (datetime_of_statement());
}
bonus_item: LotteryTicket {
rewriteinsert, updateusing (get_ticket());
}
}
type Sailor extending Person {
rank: Rank;
}
type Ship extending HasNameAndCoffins {
multi sailors: Sailor;
multi crew: Crewman;
}
type Time {
required clock: str;
clock_time :=<cal::local_time>.clock;
hour := .clock[0:2];
vampires_are := SleepState.Asleep if<int16>.hour >7and<int16>.hour <19else SleepState.Awake;
}
type Vampire extending Person {
multi slaves: MinorVampire {
onsourcedeletedeletetarget;
property combined_strength := (Vampire.strength + .strength) /2;
}
army_strength :=sum(.slaves@combined_strength);
}
# Aliasesalias AllNames := (
distinct (HasNameAndCoffins.name union
Place.modern_name union
Landmark.name union
Person.name)
);
alias CrewmanInBulgaria := Crewman {
name :='Gospodin '++ .name,
strength := .strength +<int16>1,
original_name := .name,
};
alias GameInfo := (
title := (
en :="Dracula the Immortal",
fr :="Dracula l'immortel",
no :="Dracula den udødelige",
ro :="Dracula, nemuritorul"
),
country :="Norway",
date_published :=2023,
website :="www.draculatheimmortal.com"
);
# Functionsfunctioncan_enter(person_name: str, place: str) ->optionalstrusing (
with
vampire :=assert_single((select Person filter .name = person_name)),
enter_place :=assert_single((select HasNameAndCoffins filter .name = place))
select vampire.name ++' can enter.'if enter_place.coffins >0else vampire.name ++' cannot enter.'
);
functionfight(one: Person, two: Person) ->strusing (
one.name ++' wins!'if (one.strength ??0) > (two.strength ??0)
else two.name ++' wins!'
);
functionfight(people_names: array<str>, opponent: Person) ->strusing (
with
people := (select Person filtercontains(people_names, .name)),
selectarray_join(people_names, ', ') ++' win!'ifsum(people.strength) > (opponent.strength ??0)
else opponent.name ++' wins!'
);
functionget_ticket() -> LotteryTicket
using (
with rnd :=<int16>(random() *10),
select(
LotteryTicket.Nothing if rnd <=6else
LotteryTicket.WallChicken if rnd =7else
LotteryTicket.ChainWhip if rnd =8else
LotteryTicket.Crucifix if rnd =9else
LotteryTicket.Garlic)
);
functionget_url() ->strusing (
<str>'https://geohack.toolforge.org/geohack.php?params='
);
functionvisited(person: str, city: str) ->boolusing (
with person := (select Person filter .name = person),
select city in person.places_visited.name
);
}
Chapter 18
moduledefault {
# Scalar typesscalartype Class extendingenum<Rogue, Mystic, Merchant>;
scalartype LotteryTicket extendingenum<Nothing, WallChicken, ChainWhip, Crucifix, Garlic>;
scalartype Mode extendingenum<Info, Debug>;
scalartype PCNumber extendingsequence;
scalartype Rank extendingenum<Captain, FirstMate, SecondMate, Cook>;
scalartype SleepState extendingenum<Asleep, Awake>;
# Globals and definitionsrequiredglobal tester_mode: Mode {
default:= Mode.Info;
}
global time :=assert_single((select Time));
abstractannotation warning;
# Abstract object typesabstracttype HasNameAndCoffins {
required coffins: int16 {
default:=0;
}
required name: str {
delegatedconstraintexclusive;
constraintmax_len_value(30);
}
}
abstracttype HasNumber {
required number: int16;
}
abstracttype Person extending HasMoney {
required name: str {
delegatedconstraintexclusive;
}
multi places_visited: Place;
multi lovers: Person;
is_single :=notexists .lovers;
strength: int16;
first_appearance: cal::local_date;
last_appearance: cal::local_date;
age: int16;
title: str;
degrees: array<str>;
conversational_name := .title ++''++ .name ifexists .title else .name;
pen_name := .name ++', '++array_join(.degrees, ', ') ifexists .degrees else .name;
}
abstracttype Place extending HasNameAndCoffins {
modern_name: str;
multi important_places: Landmark;
}
# Object typestype BookExcerpt {
required date: cal::local_datetime;
required excerpt: str;
indexon (.date);
required author: Person;
}
type Castle extending Place {
doors: array<int16>;
}
type City extending Place {
annotation description :='A place with 50 or more buildings. Anything else is an OtherPlace';
population: int64;
indexon (.name ++': '++<str>.population) {
annotation title :='Lists city name and population for display in Long Library stage';
}
}
type Country extending Place;
type Crewman extending HasNumber, Person {
overloaded name: str {
default:='Crewman '++<str>.number;
}
}
type Event {
required description: str;
required start_time: cal::local_datetime;
required end_time: cal::local_datetime;
requiredmulti place: Place;
requiredmulti people: Person;
location: tuple<float64, float64>;
indexon (.location);
ns_suffix :='_N_'if .location.0>0.0else'_S_';
ew_suffix :='_E'if .location.1>0.0else'_W';
url :=get_url()
++<str>(math::abs(.location.0)) ++ .ns_suffix
++<str>(math::abs(.location.1)) ++ .ew_suffix;
}
type Landmark {
required name: str;
multi context: str;
}
type Lord extending Person {
constraintexpressionon (contains(__subject__.name, 'Lord')) {
errmessage :="All lords need \'Lord\' in their name";
}
}
type MinorVampire extending Person {
former_self: Person;
single master :=assert_single(.<slaves[is Vampire]);
master_name := .master.name;
};
type NPC extending Person {
overloaded age: int16 {
constraintmax_value(120);
}
}
type OtherPlace extending Place {
annotation description :='A place with under 50 buildings - hamlets, small villages, etc.';
annotation warning :='Castles and castle towns do not count! Use the Castle type for that';
}
type Party {
name: str;
members :=.<party[is PC];
}
type PC extending Person {
required class: Class;
required created_at: datetime {
default:=datetime_of_statement();
}
required number: PCNumber {
default:=sequence_next(introspect PCNumber);
}
multi party: Party {
onsourcedeletedeletetargetiforphan;
ontargetdeleteallow;
}
overloadedrequired name: str {
constraintmax_len_value(30);
}
last_updated: datetime {
rewriteinsert, updateusing (datetime_of_statement());
}
bonus_item: LotteryTicket {
rewriteinsert, updateusing (get_ticket());
}
}
type Sailor extending Person {
rank: Rank;
}
type Ship extending HasNameAndCoffins {
multi sailors: Sailor;
multi crew: Crewman;
}
type Time {
required clock: str;
clock_time :=<cal::local_time>.clock;
hour := .clock[0:2];
vampires_are := SleepState.Asleep if<int16>.hour >7and<int16>.hour <19else SleepState.Awake;
}
type Vampire extending Person {
multi slaves: MinorVampire {
onsourcedeletedeletetarget;
property combined_strength := (Vampire.strength + .strength) /2;
}
army_strength :=sum(.slaves@combined_strength);
}
# Aliasesalias AllNames := (
distinct (HasNameAndCoffins.name union
Place.modern_name union
Landmark.name union
Person.name)
);
alias CrewmanInBulgaria := Crewman {
name :='Gospodin '++ .name,
strength := .strength +<int16>1,
original_name := .name,
};
alias GameInfo := (
title := (
en :="Dracula the Immortal",
fr :="Dracula l'immortel",
no :="Dracula den udødelige",
ro :="Dracula, nemuritorul"
),
country :="Norway",
date_published :=2023,
website :="www.draculatheimmortal.com"
);
# Functionsfunctioncan_enter(person_name: str, place: str) ->optionalstrusing (
with
vampire :=assert_single((select Person filter .name = person_name)),
enter_place :=assert_single((select HasNameAndCoffins filter .name = place))
select vampire.name ++' can enter.'if enter_place.coffins >0else vampire.name ++' cannot enter.'
);
functionfight(one: Person, two: Person) ->strusing (
one.name ++' wins!'if (one.strength ??0) > (two.strength ??0)
else two.name ++' wins!'
);
functionfight(people_names: array<str>, opponent: Person) ->strusing (
with
people := (select Person filtercontains(people_names, .name)),
selectarray_join(people_names, ', ') ++' win!'ifsum(people.strength) > (opponent.strength ??0)
else opponent.name ++' wins!'
);
functionget_ticket() -> LotteryTicket
using (
with rnd :=<int16>(random() *10),
select(
LotteryTicket.Nothing if rnd <=6else
LotteryTicket.WallChicken if rnd =7else
LotteryTicket.ChainWhip if rnd =8else
LotteryTicket.Crucifix if rnd =9else
LotteryTicket.Garlic)
);
functionget_url() ->strusing (
<str>'https://geohack.toolforge.org/geohack.php?params='
);
functionvisited(person: str, city: str) ->boolusing (
with person := (select Person filter .name = person),
select city in person.places_visited.name
);
scalartype Money extendingint64 {
constraintmin_value(0);
}
abstracttype HasMoney {
required pounds: Money {
default:=0;
}
required shillings: Money {
default:=0;
}
required pence: Money {
default:=0;
}
required cents: Money {
default:=0;
}
required dollars: Money {
default:=0;
}
total_pence := .pounds *240+ .shillings *20+ .pence;
total_cents := .dollars *100+ .cents;
approx_wealth_in_pounds :=<int64>.total_pence /240+ .total_cents /800;
}
}
```</details>
The text was updated successfully, but these errors were encountered:
When attempting to follow along with the Easy EdgeDB book, the chapter 18 migrations to add an abstract type (
HasMoney
) and change an existing type (Person
) to extend it now fail.After answering the migration questions:
The user who discovered this started a discussion about it.
I tried to create a minimal reproduction by creating a type and then creating a new migration to create a new abstract type and updating the initial type to extend it, but that did not produce the same result. Instead, I discovered a bug in the migration tool.
Steps to Reproduce:
Schemas
Chapter 17
Chapter 18
The text was updated successfully, but these errors were encountered: