edgedb / edgedb

A graph-relational database with declarative schema, built-in migration system, and a next-generation query language
https://edgedb.com
Apache License 2.0
13.08k stars 400 forks source link

`InternalServerError` when trying to coalesce with an existing link property value #7356

Closed raddevon closed 5 months ago

raddevon commented 5 months ago

Steps to Reproduce:

  1. Migrate the schema
  2. Add a person (insert Person {name := 'Doc'};)
  3. Add a queue with that person at position 1 (insert Queue {people := (select Person {@position := [1]})};)
  4. Add that person again to the queue by adding another position to their link property array (update Queue set {people += (select .people {@position := (@position ?? []) ++ [2]} filter .name = 'Doc')};

Schema:

module default {
  type Person {
    required name: str;
  }
  type Queue {
    multi people: Person {
      position: array<int64>;
    }
  }
}

A single person may have multiple positions in a queue. Imagine a presale for event tickets that allows each person to purchase multiple tickets, but they must obtain a new position in the queue for each ticket. (I never said it was a well-designed event presale 😅) The goal here is to come up with a query that, without knowing whether a given person is already in the queue, could either add them to it with the desired position or add the desired new position to their existing position link property array. My idea was to coalesce the existing link property value with an empty set and concatenate an array with the new value with the result. That ended in an ISE:

edgedb error: InternalServerError: AssertionError:
  Hint: This is most likely a bug in EdgeDB. Please consider opening an issue ticket at https://github.com/edgedb/edgedb/issues/new?template=bug_report.md
  Server traceback:
      Traceback (most recent call last):
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/server/compiler_pool/worker.py", line 178, in compile
          units, cstate = COMPILER.compile_request(
                          ^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/server/compiler/compiler.py", line 858, in compile_request
          units, cstate = self.compile(
                          ^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/server/compiler/compiler.py", line 938, in compile
          unit_group = compile(ctx=ctx, source=source)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/server/compiler/compiler.py", line 2589, in compile
          return _try_compile(ctx=ctx, source=source)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/server/compiler/compiler.py", line 2667, in _try_compile
          comp, capabilities = _compile_dispatch_ql(
                               ^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/server/compiler/compiler.py", line 2562, in _compile_dispatch_ql
          query = _compile_ql_query(
                  ^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/server/compiler/compiler.py", line 1870, in _compile_ql_query
          ir = qlcompiler.compile_ast_to_ir(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/__init__.py", line 192, in wrapper
          return func(*args, **kwargs)
                 ^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/__init__.py", line 283, in compile_ast_to_ir
          ir_set = dispatch_mod.compile(tree, ctx=ctx)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/stmt.py", line 655, in compile_UpdateQuery
          stmt.subject = compile_query_subject(
                         ^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/stmt.py", line 1530, in compile_query_subject
          view_scls, set = viewgen.process_view(
                           ^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/viewgen.py", line 167, in process_view
          view_scls, ir = _process_view(
                          ^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/viewgen.py", line 408, in _process_view
          pointer, ptr_set = _normalize_view_ptr_expr(
                             ^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/viewgen.py", line 1583, in _normalize_view_ptr_expr
          irexpr, sub_view_rptr = _compile_qlexpr(
                                  ^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/viewgen.py", line 1322, in _compile_qlexpr
          irexpr = dispatch.compile(qlexpr, ctx=shape_expr_ctx)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/stmt.py", line 138, in compile_SelectQuery
          stmt.result = compile_result_clause(
                        ^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/stmt.py", line 1391, in compile_result_clause
          ir_result = compile_query_subject(
                      ^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/stmt.py", line 1530, in compile_query_subject
          view_scls, set = viewgen.process_view(
                           ^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/viewgen.py", line 167, in process_view
          view_scls, ir = _process_view(
                          ^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/viewgen.py", line 408, in _process_view
          pointer, ptr_set = _normalize_view_ptr_expr(
                             ^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/viewgen.py", line 1583, in _normalize_view_ptr_expr
          irexpr, sub_view_rptr = _compile_qlexpr(
                                  ^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/viewgen.py", line 1322, in _compile_qlexpr
          irexpr = dispatch.compile(qlexpr, ctx=shape_expr_ctx)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/stmt.py", line 138, in compile_SelectQuery
          stmt.result = compile_result_clause(
                        ^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/stmt.py", line 1387, in compile_result_clause
          expr = dispatch.compile(result_expr, ctx=ectx)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/expr.py", line 136, in compile_BinOp
          op_node = func.compile_operator(
                    ^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/func.py", line 381, in compile_operator
          arg_ir = polyres.compile_arg(
                   ^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/polyres.py", line 625, in compile_arg
          arg_ir = dispatch.compile(arg_ql, ctx=argctx)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/expr.py", line 136, in compile_BinOp
          op_node = func.compile_operator(
                    ^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/func.py", line 381, in compile_operator
          arg_ir = polyres.compile_arg(
                   ^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/polyres.py", line 625, in compile_arg
          arg_ir = dispatch.compile(arg_ql, ctx=argctx)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/stmt.py", line 138, in compile_SelectQuery
          stmt.result = compile_result_clause(
                        ^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/stmt.py", line 1377, in compile_result_clause
          expr = setgen.new_array_set(
                 ^^^^^^^^^^^^^^^^^^^^^
        File "/Users/raddevon/Library/Application Support/edgedb/portable/5.3/lib/python3.12/site-packages/edb/edgeql/compiler/setgen.py", line 279, in new_array_set
          assert stype.is_array()
      AssertionError
dnwpark commented 5 months ago

@raddevon The issue was with the [], please explicitly type your empty arrays for now.

brassel commented 5 months ago

I have found the following workaround for this issue. In order to either add Doc to the queue at position 2 or add position 2 to his positions if he is already in the queue (clone the Doc!), you can do this:

with 
doc := assert_single((
  select Person
  filter .name = 'Doc'
)),
queue := (
  select Queue {
    id,
    doc_position := (
      select .people {
        ps := (@position, Queue.id)
      }
      filter .id = doc.id 
    )
  }
  filter .id = <uuid>$queue_id
),
ps := assert_single(queue.doc_position.ps.0) ?? [],
result := (update queue
set {
  people += (
    select doc {
      @position := ps ++ [2]
    }
  )
})
select assert_single(result)

I think it is rather noisy so if you have a better solution, please post! Also I do not really understand why I get additional results from other Queues if I do not add the Queue.id to the tuple (and later throw it away by only using .0). If someone could explain this, that would also be great!