Skip to content

Commit

Permalink
Add sorting for constraints and indexes when generating code. (#7695)
Browse files Browse the repository at this point in the history
Sorts constraints and indexes by the text of their expressions.

For example, given a schema:
```
type Foo {
    constraint exclusive on ('C');
    constraint exclusive on ('A');
    constraint exclusive on ('D);
    constraint exclusive on ('B');
};
```

The query `describe schema as sdl` will list the constraints sorted alphabetically:
```
module default {
    type Foo {
        constraint std::exclusive on ('A');
        constraint std::exclusive on ('B');
        constraint std::exclusive on ('C');
        constraint std::exclusive on ('D');
    };
};
```
  • Loading branch information
dnwpark authored Aug 29, 2024
1 parent 198a73e commit f661c6d
Show file tree
Hide file tree
Showing 2 changed files with 1,132 additions and 23 deletions.
109 changes: 97 additions & 12 deletions edb/edgeql/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -933,15 +933,84 @@ def _ddl_visit_body(
self.new_lines = 1
self.visit_list(list(items), terminator=';')
elif self.descmode or self.sdlmode:
sort_key = lambda c: (
(c.name.itemclass or '', c.name.name)
if isinstance(c, qlast.ObjectDDL)
else ('', c.name if isinstance(c, qlast.SetField) else '')
)
def sort_desc_or_sdl(
c: qlast.Base,
) -> tuple[str, ...]:
# The sort key is a tuple of parts of the command which will
# be rendered to text.
#
# Commands will be ordered generally as:
# 1. General DDL Operations
# 2. Set Field Operations
# 3. Object DDL Operations
#
# Empty strings are used to achieve this general ordering.
#
# General DDL Operations are sorted by command class name.
# This works because these commands can each appear once per
# body.
# eg. ('', '', '', 'AlterAddInherit')
#
# Set Field Operations are sorted by field name.
# eg. ('', '', 'readonly')
#
# Object DDL Operations are sorted first by itemclass then
# name.
# eg. ('TYPE', 'Foo')
#
# For constraints and indexes, the expression and except
# expression are included.
# eg. ('CONSTRAINT', 'exclusive', '.a', '.b')

if isinstance(c, qlast.ObjectDDL):
if isinstance(c, qlast.ConcreteConstraintOp):
subject_expr = (
self.generate_isolated_text(c.subjectexpr)
if c.subjectexpr is not None else
''
)
except_expr = (
self.generate_isolated_text(c.except_expr)
if c.except_expr is not None else
''
)
return (
typeutils.not_none(c.name.itemclass),
c.name.name,
subject_expr,
except_expr,
)

if isinstance(c, qlast.ConcreteIndexCommand):
expr = (
self.generate_isolated_text(c.expr)
if c.expr is not None else
''
)
except_expr = (
self.generate_isolated_text(c.except_expr)
if c.except_expr is not None else
''
)
return (
typeutils.not_none(c.name.itemclass),
c.name.name,
expr,
except_expr,
)

return (c.name.itemclass or '', c.name.name)

if isinstance(c, qlast.SetField):
return ('', '', c.name)

return ('', '', '', c.__class__.__name__)

if not self.unsorted:
commands = sorted(commands, key=sort_key)
commands = sorted(commands, key=sort_desc_or_sdl)

self.visit_list(list(commands), terminator=';')

else:
self.visit_list(list(commands), terminator=';')

Expand Down Expand Up @@ -2432,15 +2501,24 @@ def visit_Options(self, node: qlast.Options) -> None:
self.write(opt.name)

# SDL nodes
def visit_Schema(self, node: qlast.Schema) -> None:
sdl_codegen = self.__class__(
def copy_generator(self) -> EdgeQLSourceGenerator:
return self.__class__(
indent_with=self.indent_with,
add_line_information=self.add_line_information,
pretty=self.pretty,
unsorted=self.unsorted,
sdlmode=True,
descmode=self.descmode,
limit_ref_classes=self.limit_ref_classes)
limit_ref_classes=self.limit_ref_classes
)

def generate_isolated_text(self, node: qlast.Base) -> str:
sdl_codegen = self.copy_generator()
sdl_codegen.visit(node)
return ''.join(sdl_codegen.result)

def visit_Schema(self, node: qlast.Schema) -> None:
sdl_codegen = self.copy_generator()
sdl_codegen.indentation = self.indentation
sdl_codegen.current_line = self.current_line
sdl_codegen.visit_list(node.declarations, terminator=';')
Expand Down Expand Up @@ -2479,9 +2557,16 @@ def to_source( # type: ignore
_fix_parent_links(node)

return super().to_source(
node, indent_with, add_line_information, pretty,
sdlmode=sdlmode, descmode=descmode, uppercase=uppercase,
unsorted=unsorted, limit_ref_classes=limit_ref_classes)
node,
indent_with,
add_line_information,
pretty,
sdlmode=sdlmode,
descmode=descmode,
uppercase=uppercase,
limit_ref_classes=limit_ref_classes,
unsorted=unsorted,
)


def _fix_parent_links(node: qlast.Base) -> qlast.Base:
Expand Down
Loading

0 comments on commit f661c6d

Please sign in to comment.