From 3c03cec723beb43ce98978d201a5f0925bc4d0a2 Mon Sep 17 00:00:00 2001 From: dnwpark Date: Tue, 8 Oct 2024 19:48:25 -0400 Subject: [PATCH] Search std for module name when using with x as module. (#7836) - Allows queries such as `with http as net::http select http::Response`. The aliased module `net::http` will resolve to `std::net::http`. - Apply module aliases to function names during normalization, even if no function was found. - This fixes an issue where given the following modules: - `module A { function foo() -> int64 using (1) }` - `module B {}` - `module C { alias query := (with A as B select A::foo()}` - `C::query` would normalize to `select A::foo()` with the expectation that `A::Foo` does not exist. --- edb/edgeql/compiler/normalization.py | 24 + edb/edgeql/tracer.py | 87 ++- edb/schema/schema.py | 90 ++- tests/test_edgeql_expressions.py | 760 ++++++++++++++++++---- tests/test_schema.py | 911 +++++++++++++++++++++++---- 5 files changed, 1565 insertions(+), 307 deletions(-) diff --git a/edb/edgeql/compiler/normalization.py b/edb/edgeql/compiler/normalization.py index 57958ae4798..4ff7dc87865 100644 --- a/edb/edgeql/compiler/normalization.py +++ b/edb/edgeql/compiler/normalization.py @@ -419,6 +419,30 @@ def normalize_FunctionCall( sname = funcs[0].get_shortname(schema) node.func = (sname.module, sname.name) + elif modaliases and isinstance(name, sn.QualName): + # Even if no function was found, apply the modaliases. + # It is possible that a function without the modalias exists but + # we don't want to find that. + # + # Eg. + # module A { + # function foo() -> int64 using (1); + # } + # module B {} + # module default { + # alias query := (with A as module B select A::foo() ); + # } + current_module = ( + modaliases[None] + if modaliases and None in modaliases else + None + ) + _, module = s_schema.apply_module_aliases( + name.module, modaliases, current_module, + ) + if module is not None: + node.func = (module, name.name) + # It's odd we don't find a function, but this will be picked up # by the compiler with a more appropriate error message. diff --git a/edb/edgeql/tracer.py b/edb/edgeql/tracer.py index 0ed3eee79db..24844cf12e7 100644 --- a/edb/edgeql/tracer.py +++ b/edb/edgeql/tracer.py @@ -381,42 +381,63 @@ def resolve_name( """Resolve a name into a fully-qualified one. This takes into account the current module and modaliases. + + This function mostly mirrors schema.FlatSchema._search_with_getter + except: + - If no module and no default module was set, try the current module + - When searching in std, ensure module is not a local module + - If no result found, return a name with the best modname available """ + + def exists(name: sn.QualName) -> bool: + return ( + objects.get(name) is not None + or schema.get(name, default=None, type=so.Object) is not None + ) + module = ref.module + orig_module = module - no_std = declaration - if module and module.startswith('__current__::'): - no_std = True - module = f'{current_module}::{module.removeprefix("__current__::")}' - elif not module: - module = current_module - elif modaliases: - if module: - first, sep, rest = module.partition('::') - else: - first, sep, rest = module, '', '' - - fq_module = modaliases.get(first) - if fq_module is not None: - no_std = True - module = fq_module + sep + rest - - qname = sn.QualName(module=module, name=ref.name) - - # check if there's a name in default module - # that matches - if not no_std and not ( - ref.module and ref.module in local_modules - ) and not ( - objects.get(qname) - or schema.get( - qname, default=None, type=so.Object) is not None - ): - std_name = sn.QualName( - f'std::{ref.module}' if ref.module else 'std', ref.name) - if schema.get(std_name, default=None) is not None: - return std_name - return qname + # Apply module aliases + is_current, module = s_schema.apply_module_aliases( + module, modaliases, current_module, + ) + no_std = declaration or is_current + + # Check if something matches the name + if module is not None: + fqname = sn.QualName(module=module, name=ref.name) + if exists(fqname): + return fqname + + elif orig_module is None: + # Look for name in current module + fqname = sn.QualName(module=current_module, name=ref.name) + if exists(fqname): + return fqname + + # Try something in std if __current__ was not specified + if not no_std: + # If module == None, look in std + if orig_module is None: + mod_name = 'std' + fqname = sn.QualName(mod_name, ref.name) + if exists(fqname): + return fqname + + # Ensure module is not a local module. + # Then try the module as part of std. + if module and module not in local_modules: + mod_name = f'std::{module}' + fqname = sn.QualName(mod_name, ref.name) + if exists(fqname): + return fqname + + # Just pick the best module name available + return sn.QualName( + module=module or orig_module or current_module, + name=ref.name, + ) class TracerContext: diff --git a/edb/schema/schema.py b/edb/schema/schema.py index f8696c4f82b..ce5af340420 100644 --- a/edb/schema/schema.py +++ b/edb/schema/schema.py @@ -1088,6 +1088,14 @@ def _search_with_getter( module_aliases: Optional[Mapping[Optional[str], str]], disallow_module: Optional[Callable[[str], bool]], ) -> Any: + """ + Find something in the schema with a given name. + + This function mostly mirrors edgeql.tracer.resolve_name + except: + - When searching in std, disallow some modules (often the base modules) + - If no result found, return default + """ if isinstance(name, str): name = sn.name_from_string(name) shortname = name.name @@ -1102,39 +1110,30 @@ def _search_with_getter( else: return default - alias_hit = local = False - if module and module.startswith('__current__::'): - local = True - if not module_aliases or None not in module_aliases: - return default - cur_module = module_aliases[None] - module = f'{cur_module}::{module.removeprefix("__current__::")}' - elif module_aliases is not None: - first: Optional[str] - if module: - first, sep, rest = module.partition('::') - else: - first, sep, rest = module, '', '' + # Apply module aliases + current_module = ( + module_aliases[None] + if module_aliases and None in module_aliases else + None + ) + is_current, module = apply_module_aliases( + module, module_aliases, current_module, + ) + if is_current and current_module is None: + return default - fq_module = module_aliases.get(first) - if fq_module is not None: - alias_hit = True - module = fq_module + sep + rest + no_std = is_current + # Check if something matches the name if module is not None: fqname = sn.QualName(module, shortname) result = getter(self, fqname) if result is not None: return result - # Try something in std, but only if there isn't a module clash - if not local and ( - orig_module is None - or ( - not alias_hit and module - ) - ): - # If no module was specified, look in std + # Try something in std if __current__ was not specified + if not no_std: + # If module == None, look in std if orig_module is None: mod_name = 'std' fqname = sn.QualName(mod_name, shortname) @@ -1142,20 +1141,13 @@ def _search_with_getter( if result is not None: return result - # If a module was specified in the name, ensure that no base module - # of the same name exists. - # - # If no module was specified, try the default module name as a part - # of std. The same condition applies. + # Ensure module is not a base module. + # Then try the module as part of std. if module and not ( self.has_module(fmod := module.split('::')[0]) or (disallow_module and disallow_module(fmod)) ): - mod_name = ( - f'std::{module}' - if orig_module is None - else f'std::{orig_module}' - ) + mod_name = f'std::{module}' fqname = sn.QualName(mod_name, shortname) result = getter(self, fqname) if result is not None: @@ -1544,6 +1536,34 @@ def __repr__(self) -> str: f'<{type(self).__name__} gen:{self._generation} at {id(self):#x}>') +def apply_module_aliases( + module: Optional[str], + module_aliases: Optional[Mapping[Optional[str], str]], + current_module: Optional[str], +) -> tuple[bool, Optional[str]]: + is_current = False + if module and module.startswith('__current__::'): + # Replace __current__ with default module + is_current = True + if current_module is not None: + module = f'{current_module}::{module.removeprefix("__current__::")}' + else: + module = None + elif module_aliases is not None: + # Apply modalias + first: Optional[str] + if module: + first, sep, rest = module.partition('::') + else: + first, sep, rest = module, '', '' + + fq_module = module_aliases.get(first) + if fq_module is not None: + module = fq_module + sep + rest + + return is_current, module + + EMPTY_SCHEMA = FlatSchema() diff --git a/tests/test_edgeql_expressions.py b/tests/test_edgeql_expressions.py index ad511a51b84..a87e8075361 100644 --- a/tests/test_edgeql_expressions.py +++ b/tests/test_edgeql_expressions.py @@ -9909,159 +9909,605 @@ async def test_edgeql_cast_to_function_01(self): async def test_edgeql_expr_with_module_01(self): await self.con.execute(f""" create module dummy; + create module A; + create type A::Foo; """) - valid_queries = [ - 'SELECT {} = 1', - 'SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', + NO_ERR = 1 + REF_ERR = 2 + + queries = [] + + with_mod = '' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH MODULE dummy ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH MODULE std ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH MODULE A ' + queries += [ + (NO_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH AAA as MODULE A ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH s as MODULE std ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH std as MODULE A ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), ] + with_mod = 'WITH A as MODULE std ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + ] + + for error, query in queries: + if error == NO_ERR: + await self.con.execute(query) - for query in valid_queries: - await self.con.execute(query) + elif error == REF_ERR: + async with self.assertRaisesRegexTx( + edgedb.errors.InvalidReferenceError, + "Foo' does not exist", + ): + await self.con.execute(query) async def test_edgeql_expr_with_module_02(self): await self.con.execute(f""" create module dummy; - create type default::int64; + create module A; + create function A::abs(x: int64) -> int64 using (x); """) - valid_queries = [ - 'SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', + NO_ERR = 1 + REF_ERR = 2 + + queries = [] + + with_mod = '' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), ] - invalid_queries = [ - 'SELECT {} = 1', - 'SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', + with_mod = 'WITH MODULE dummy ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + ] + with_mod = 'WITH MODULE std ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + ] + with_mod = 'WITH MODULE A ' + queries += [ + (NO_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + (REF_ERR, with_mod + 'SELECT dum::abs(1)'), + ] + with_mod = 'WITH AAA as MODULE A ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + (NO_ERR, with_mod + 'SELECT AAA::abs(1)'), + ] + with_mod = 'WITH s as MODULE std ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + (REF_ERR, with_mod + 'SELECT s::abs(1)'), + ] + with_mod = 'WITH std as MODULE A ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (NO_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + ] + with_mod = 'WITH A as MODULE std ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (REF_ERR, with_mod + 'SELECT A::abs(1)'), ] - for query in valid_queries: - await self.con.execute(query) - - for query in invalid_queries: - async with self.assertRaisesRegexTx( - edgedb.errors.InvalidTypeError, - "operator '=' cannot be applied", - ): + for error, query in queries: + if error == NO_ERR: await self.con.execute(query) + elif error == REF_ERR: + async with self.assertRaisesRegexTx( + edgedb.errors.InvalidReferenceError, + "abs' does not exist", + ): + await self.con.execute(query) + async def test_edgeql_expr_with_module_03(self): await self.con.execute(f""" create module dummy; """) - valid_queries = [ - 'select _test::abs(1)', - 'select std::_test::abs(1)', - 'with module dummy select _test::abs(1)', - 'with module dummy select std::_test::abs(1)', - 'with module _test select abs(1)', - 'with module _test select _test::abs(1)', - 'with module _test select std::_test::abs(1)', - 'with module std select _test::abs(1)', - 'with module std select std::_test::abs(1)', - 'with module std::_test select abs(1)', - 'with module std::_test select _test::abs(1)', - 'with module std::_test select std::_test::abs(1)', + NO_ERR = 1 + REF_ERR = 2 + + queries = [] + + with_mod = '' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), ] - invalid_queries = [ - 'select abs(1)', - 'with module dummy select abs(1)', - 'with module std select abs(1)', + with_mod = 'WITH MODULE dummy ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH MODULE std ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH def as MODULE default ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH s as MODULE std ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH std as MODULE dummy ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), ] - for query in valid_queries: - await self.con.execute(query) - - for query in invalid_queries: - async with self.assertRaisesRegexTx( - edgedb.errors.InvalidReferenceError, - "abs' does not exist", - ): + for error, query in queries: + if error == NO_ERR: await self.con.execute(query) + elif error == REF_ERR: + async with self.assertRaisesRegexTx( + edgedb.errors.InvalidReferenceError, + "int64' does not exist", + ): + await self.con.execute(query) + async def test_edgeql_expr_with_module_04(self): await self.con.execute(f""" create module dummy; - create module _test; + create type default::int64; """) - valid_queries = [ - 'select std::_test::abs(1)', - 'with module dummy select std::_test::abs(1)', - 'with module _test select std::_test::abs(1)', - 'with module std select std::_test::abs(1)', - 'with module std::_test select abs(1)', - 'with module std::_test select std::_test::abs(1)', + NO_ERR = 1 + REF_ERR = 2 + TYPE_ERR = 3 + + queries = [] + + with_mod = '' + queries += [ + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), ] - invalid_queries = [ - 'select abs(1)', - 'select _test::abs(1)', - 'with module dummy select abs(1)', - 'with module dummy select _test::abs(1)', - 'with module _test select abs(1)', - 'with module _test select _test::abs(1)', - 'with module std select abs(1)', - 'with module std select _test::abs(1)', - 'with module std::_test select _test::abs(1)', + with_mod = 'WITH MODULE dummy ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH MODULE std ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH def as MODULE default ' + queries += [ + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH s as MODULE std ' + queries += [ + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH std as MODULE dummy ' + queries += [ + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH std as MODULE default ' + queries += [ + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), ] - for query in valid_queries: - await self.con.execute(query) - - for query in invalid_queries: - async with self.assertRaisesRegexTx( - edgedb.errors.InvalidReferenceError, - "abs' does not exist", - ): + for error, query in queries: + if error == NO_ERR: await self.con.execute(query) + elif error == REF_ERR: + async with self.assertRaisesRegexTx( + edgedb.errors.InvalidReferenceError, + "int64' does not exist", + ): + await self.con.execute(query) + + elif error == TYPE_ERR: + async with self.assertRaisesRegexTx( + edgedb.errors.InvalidTypeError, + "operator '=' cannot be applied", + ): + await self.con.execute(query) + async def test_edgeql_expr_with_module_05(self): + await self.con.execute(f""" + create module dummy; + """) + + NO_ERR = 1 + REF_ERR = 2 + + queries = [] + + with_mod = '' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module dummy ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module _test ' + queries += [ + (NO_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module std ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module std::_test ' + queries += [ + (NO_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with t as module _test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + (NO_ERR, with_mod + 'select t::abs(1)'), + ] + with_mod = 'with s as module std ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + (REF_ERR, with_mod + 'select s::abs(1)'), + ] + with_mod = 'with st as module std::_test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + (NO_ERR, with_mod + 'select st::abs(1)'), + ] + with_mod = 'with std as module _test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (REF_ERR, with_mod + 'select std::_test::abs(1)'), + (NO_ERR, with_mod + 'select std::abs(1)'), + ] + + for error, query in queries: + if error == NO_ERR: + await self.con.execute(query) + + elif error == REF_ERR: + async with self.assertRaisesRegexTx( + edgedb.errors.InvalidReferenceError, + "abs' does not exist", + ): + await self.con.execute(query) + + async def test_edgeql_expr_with_module_06(self): + await self.con.execute(f""" + create module dummy; + create module _test; + """) + + NO_ERR = 1 + REF_ERR = 2 + + queries = [] + + with_mod = '' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module dummy ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module _test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module std ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module std::_test ' + queries += [ + (NO_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with t as module _test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + (REF_ERR, with_mod + 'select t::abs(1)'), + ] + with_mod = 'with s as module std ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + (REF_ERR, with_mod + 'select s::abs(1)'), + ] + with_mod = 'with st as module std::_test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + (NO_ERR, with_mod + 'select st::abs(1)'), + ] + with_mod = 'with std as module _test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (REF_ERR, with_mod + 'select std::_test::abs(1)'), + (REF_ERR, with_mod + 'select std::abs(1)'), + ] + with_mod = 'with std as module std::_test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (REF_ERR, with_mod + 'select std::_test::abs(1)'), + (NO_ERR, with_mod + 'select std::abs(1)'), + ] + + for error, query in queries: + if error == NO_ERR: + await self.con.execute(query) + + elif error == REF_ERR: + async with self.assertRaisesRegexTx( + edgedb.errors.InvalidReferenceError, + "abs' does not exist", + ): + await self.con.execute(query) + + async def test_edgeql_expr_with_module_07(self): await self.con.execute(f""" create module dummy; create module std::test; create scalar type std::test::Foo extending int64; """) - valid_queries = [ - 'select 1', - 'select 1', - 'with module dummy select 1', - 'with module dummy select 1', - 'with module test select 1', - 'with module test select 1', - 'with module test select 1', - 'with module std select 1', - 'with module std select 1', - 'with module std::test select 1', - 'with module std::test select 1', - 'with module std::test select 1', + NO_ERR = 1 + REF_ERR = 2 + + queries = [] + + with_mod = '' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), ] - invalid_queries = [ - 'select 1', - 'with module dummy select 1', - 'with module std select 1', + with_mod = 'with module dummy ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + ] + with_mod = 'with module test ' + queries += [ + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + ] + with_mod = 'with module std ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + ] + with_mod = 'with module std::test ' + queries += [ + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + ] + with_mod = 'with t as module test ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + ] + with_mod = 'with s as module std ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + ] + with_mod = 'with st as module std::test ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + ] + with_mod = 'WITH std as MODULE dummy ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + ] + with_mod = 'WITH std as MODULE test ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), ] - for query in valid_queries: - await self.con.execute(query) - - for query in invalid_queries: - async with self.assertRaisesRegexTx( - edgedb.errors.InvalidReferenceError, - "Foo' does not exist", - ): + for error, query in queries: + if error == NO_ERR: await self.con.execute(query) - async def test_edgeql_expr_with_module_06(self): + elif error == REF_ERR: + async with self.assertRaisesRegexTx( + edgedb.errors.InvalidReferenceError, + "Foo' does not exist", + ): + await self.con.execute(query) + + async def test_edgeql_expr_with_module_08(self): await self.con.execute(f""" create module dummy; create module std::test; @@ -10069,32 +10515,84 @@ async def test_edgeql_expr_with_module_06(self): create module test; """) - valid_queries = [ - 'select 1', - 'with module dummy select 1', - 'with module test select 1', - 'with module std select 1', - 'with module std::test select 1', - 'with module std::test select 1', + NO_ERR = 1 + REF_ERR = 2 + + queries = [] + + with_mod = '' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), ] - invalid_queries = [ - 'select 1', - 'select 1', - 'with module dummy select 1', - 'with module dummy select 1', - 'with module test select 1', - 'with module test select 1', - 'with module std select 1', - 'with module std select 1', - 'with module std::test select 1', + with_mod = 'with module dummy ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + ] + with_mod = 'with module test ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + ] + with_mod = 'with module std ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + ] + with_mod = 'with module std::test ' + queries += [ + (NO_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + ] + with_mod = 'with t as module test ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + ] + with_mod = 'with s as module std ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + ] + with_mod = 'with st as module std::test ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + (NO_ERR, with_mod + 'select 1'), + ] + with_mod = 'WITH std as MODULE dummy ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + ] + with_mod = 'WITH std as MODULE test ' + queries += [ + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), + (REF_ERR, with_mod + 'select 1'), ] - for query in valid_queries: - await self.con.execute(query) - - for query in invalid_queries: - async with self.assertRaisesRegexTx( - edgedb.errors.InvalidReferenceError, - "Foo' does not exist", - ): + for error, query in queries: + if error == NO_ERR: await self.con.execute(query) + + elif error == REF_ERR: + async with self.assertRaisesRegexTx( + edgedb.errors.InvalidReferenceError, + "Foo' does not exist", + ): + await self.con.execute(query) diff --git a/tests/test_schema.py b/tests/test_schema.py index ea2d8d657bd..d0032e79393 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -3123,102 +3123,509 @@ def _check_invalid_queries( def test_schema_with_module_01(self): schema_text = f''' module dummy {{}} + module A {{ + type Foo; + }} ''' - valid_queries = [ - 'SELECT {} = 1', - 'SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', + NO_ERR = 1 + REF_ERR = 2 + + queries = [] + + with_mod = '' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH MODULE dummy ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH MODULE std ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH MODULE A ' + queries += [ + (NO_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), ] - self._check_valid_queries(schema_text, valid_queries) + with_mod = 'WITH AAA as MODULE A ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH s as MODULE std ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH std as MODULE A ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (NO_ERR, with_mod + 'SELECT {}'), + ] + with_mod = 'WITH A as MODULE std ' + queries += [ + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + (REF_ERR, with_mod + 'SELECT {}'), + ] + + self._check_valid_queries( + schema_text, + [query for error, query in queries if error == NO_ERR], + ) + self._check_invalid_queries( + schema_text, + [query for error, query in queries if error == REF_ERR], + errors.InvalidReferenceError, + "Foo' does not exist", + ) def test_schema_with_module_02(self): + schema_text = f''' + module dummy {{}} + module A {{ + function abs(x: int64) -> int64 using (x); + }} + ''' + NO_ERR = 1 + REF_ERR = 2 + + queries = [] + + with_mod = '' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + ] + with_mod = 'WITH MODULE dummy ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + ] + with_mod = 'WITH MODULE std ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + ] + with_mod = 'WITH MODULE A ' + queries += [ + (NO_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + (REF_ERR, with_mod + 'SELECT dum::abs(1)'), + ] + with_mod = 'WITH AAA as MODULE A ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + (NO_ERR, with_mod + 'SELECT AAA::abs(1)'), + ] + with_mod = 'WITH s as MODULE std ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + (REF_ERR, with_mod + 'SELECT s::abs(1)'), + ] + with_mod = 'WITH std as MODULE A ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (NO_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (NO_ERR, with_mod + 'SELECT A::abs(1)'), + ] + with_mod = 'WITH A as MODULE std ' + queries += [ + (REF_ERR, with_mod + 'SELECT abs(1)'), + (REF_ERR, with_mod + 'SELECT std::abs(1)'), + (REF_ERR, with_mod + 'SELECT dummy::abs(1)'), + (REF_ERR, with_mod + 'SELECT A::abs(1)'), + ] + + self._check_valid_queries( + schema_text, + [query for error, query in queries if error == NO_ERR], + ) + self._check_invalid_queries( + schema_text, + [query for error, query in queries if error == REF_ERR], + errors.InvalidReferenceError, + "abs' does not exist", + ) + + def test_schema_with_module_03(self): + schema_text = f''' + module dummy {{}} + ''' + NO_ERR = 1 + REF_ERR = 2 + + queries = [] + + with_mod = '' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH MODULE dummy ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH MODULE std ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH def as MODULE default ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH s as MODULE std ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH std as MODULE dummy ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + + self._check_valid_queries( + schema_text, + [query for error, query in queries if error == NO_ERR], + ) + self._check_invalid_queries( + schema_text, + [query for error, query in queries if error == REF_ERR], + errors.InvalidReferenceError, + "int64' does not exist", + ) + + def test_schema_with_module_04(self): schema_text = f''' module dummy {{}} module default {{ type int64; }} ''' - valid_queries = [ - 'SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', + + NO_ERR = 1 + REF_ERR = 2 + TYPE_ERR = 3 + + queries = [] + + with_mod = '' + queries += [ + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH MODULE dummy ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH MODULE std ' + queries += [ + (NO_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), ] - invalid_queries = [ - 'SELECT {} = 1', - 'SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', + with_mod = 'WITH def as MODULE default ' + queries += [ + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), ] - self._check_valid_queries(schema_text, valid_queries) + with_mod = 'WITH s as MODULE std ' + queries += [ + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (NO_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH std as MODULE dummy ' + queries += [ + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + with_mod = 'WITH std as MODULE default ' + queries += [ + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (TYPE_ERR, with_mod + 'SELECT {} = 1'), + (REF_ERR, with_mod + 'SELECT {} = 1'), + ] + + self._check_valid_queries( + schema_text, + [query for error, query in queries if error == NO_ERR], + ) self._check_invalid_queries( schema_text, - invalid_queries, + [query for error, query in queries if error == REF_ERR], + errors.InvalidReferenceError, + "int64' does not exist", + ) + self._check_invalid_queries( + schema_text, + [query for error, query in queries if error == TYPE_ERR], errors.InvalidTypeError, "operator '=' cannot be applied", ) - def test_schema_with_module_03(self): + def test_schema_with_module_05(self): schema_text = f''' module dummy {{}} ''' - valid_queries = [ - 'select _test::abs(1)', - 'select std::_test::abs(1)', - 'with module dummy select _test::abs(1)', - 'with module dummy select std::_test::abs(1)', - 'with module _test select abs(1)', - 'with module _test select _test::abs(1)', - 'with module _test select std::_test::abs(1)', - 'with module std select _test::abs(1)', - 'with module std select std::_test::abs(1)', - 'with module std::_test select abs(1)', - 'with module std::_test select _test::abs(1)', - 'with module std::_test select std::_test::abs(1)', + + NO_ERR = 1 + REF_ERR = 2 + + queries = [] + + with_mod = '' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module dummy ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module _test ' + queries += [ + (NO_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module std ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module std::_test ' + queries += [ + (NO_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with t as module _test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + (NO_ERR, with_mod + 'select t::abs(1)'), + ] + with_mod = 'with s as module std ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + (REF_ERR, with_mod + 'select s::abs(1)'), ] - invalid_queries = [ - 'select abs(1)', - 'with module dummy select abs(1)', - 'with module std select abs(1)', + with_mod = 'with st as module std::_test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + (NO_ERR, with_mod + 'select st::abs(1)'), ] - self._check_valid_queries(schema_text, valid_queries) + with_mod = 'with std as module _test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (NO_ERR, with_mod + 'select _test::abs(1)'), + (REF_ERR, with_mod + 'select std::_test::abs(1)'), + (NO_ERR, with_mod + 'select std::abs(1)'), + ] + + self._check_valid_queries( + schema_text, + [query for error, query in queries if error == NO_ERR], + ) self._check_invalid_queries( schema_text, - invalid_queries, + [query for error, query in queries if error == REF_ERR], errors.InvalidReferenceError, "abs' does not exist", ) - def test_schema_with_module_04(self): + def test_schema_with_module_06(self): schema_text = f''' module dummy {{}} module _test {{}} ''' - valid_queries = [ - 'select std::_test::abs(1)', - 'with module dummy select std::_test::abs(1)', - 'with module _test select std::_test::abs(1)', - 'with module std select std::_test::abs(1)', - 'with module std::_test select abs(1)', - 'with module std::_test select std::_test::abs(1)', + + NO_ERR = 1 + REF_ERR = 2 + + queries = [] + + with_mod = '' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module dummy ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module _test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module std ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with module std::_test ' + queries += [ + (NO_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + ] + with_mod = 'with t as module _test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + (REF_ERR, with_mod + 'select t::abs(1)'), + ] + with_mod = 'with s as module std ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + (REF_ERR, with_mod + 'select s::abs(1)'), ] - invalid_queries = [ - 'select abs(1)', - 'select _test::abs(1)', - 'with module dummy select abs(1)', - 'with module dummy select _test::abs(1)', - 'with module _test select abs(1)', - 'with module _test select _test::abs(1)', - 'with module std select abs(1)', - 'with module std select _test::abs(1)', - 'with module std::_test select _test::abs(1)', + with_mod = 'with st as module std::_test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (NO_ERR, with_mod + 'select std::_test::abs(1)'), + (NO_ERR, with_mod + 'select st::abs(1)'), ] - self._check_valid_queries(schema_text, valid_queries) + with_mod = 'with std as module _test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (REF_ERR, with_mod + 'select std::_test::abs(1)'), + (REF_ERR, with_mod + 'select std::abs(1)'), + ] + with_mod = 'with std as module std::_test ' + queries += [ + (REF_ERR, with_mod + 'select abs(1)'), + (REF_ERR, with_mod + 'select _test::abs(1)'), + (REF_ERR, with_mod + 'select std::_test::abs(1)'), + (NO_ERR, with_mod + 'select std::abs(1)'), + ] + + self._check_valid_queries( + schema_text, + [query for error, query in queries if error == NO_ERR], + ) self._check_invalid_queries( schema_text, - invalid_queries, + [query for error, query in queries if error == REF_ERR], errors.InvalidReferenceError, "abs' does not exist", ) @@ -11247,17 +11654,174 @@ def test_schema_describe_overload_01(self): def test_schema_describe_with_module_01(self): schema_text = f''' module dummy {{}} + module A {{ + type Foo; + }} ''' - valid_queries = [ - 'SELECT {} = 1', - 'SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', + + queries = [] + + with_mod = '' + queries += [ + with_mod + 'SELECT {}', + ] + with_mod = 'WITH MODULE dummy ' + queries += [ + with_mod + 'SELECT {}', + ] + with_mod = 'WITH MODULE std ' + queries += [ + with_mod + 'SELECT {}', + ] + with_mod = 'WITH MODULE A ' + queries += [ + with_mod + 'SELECT {}', + with_mod + 'SELECT {}', + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + with_mod + 'SELECT {}', + ] + with_mod = 'WITH AAA as MODULE A ' + queries += [ + with_mod + 'SELECT {}', + with_mod + 'SELECT {}', + ] + with_mod = 'WITH s as MODULE std ' + queries += [ + with_mod + 'SELECT {}', + ] + with_mod = 'WITH std as MODULE A ' + queries += [ + with_mod + 'SELECT {}', + with_mod + 'SELECT {}', + ] + with_mod = 'WITH A as MODULE std ' + queries += [] + + normalized = 'SELECT {}' + for query in queries: + self._assert_describe( + schema_text + f''' + module default {{ alias query := ({query}); }} + ''', + + 'describe module default as sdl', + + f''' + alias default::query := ({normalized}); + ''', + explicit_modules=True, + ) + + def test_schema_describe_with_module_02(self): + schema_text = f''' + module dummy {{}} + module A {{ + function abs(x: int64) -> int64 using (x); + }} + ''' + + queries = [] + + with_mod = '' + queries += [ + with_mod + 'SELECT A::abs(1)', + ] + with_mod = 'WITH MODULE dummy ' + queries += [ + with_mod + 'SELECT A::abs(1)', + ] + with_mod = 'WITH MODULE std ' + queries += [ + with_mod + 'SELECT A::abs(1)', + ] + with_mod = 'WITH MODULE A ' + queries += [ + with_mod + 'SELECT abs(1)', + with_mod + 'SELECT A::abs(1)', + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + with_mod + 'SELECT A::abs(1)', + ] + with_mod = 'WITH AAA as MODULE A ' + queries += [ + with_mod + 'SELECT A::abs(1)', + with_mod + 'SELECT AAA::abs(1)', + ] + with_mod = 'WITH s as MODULE std ' + queries += [ + with_mod + 'SELECT A::abs(1)', + ] + with_mod = 'WITH std as MODULE A ' + queries += [ + with_mod + 'SELECT std::abs(1)', + with_mod + 'SELECT A::abs(1)', + ] + with_mod = 'WITH A as MODULE std ' + queries += [] + + normalized = 'SELECT A::abs(1)' + for query in queries: + self._assert_describe( + schema_text + f''' + module default {{ alias query := ({query}); }} + ''', + + 'describe module default as sdl', + + f''' + alias default::query := ({normalized}); + ''', + explicit_modules=True, + ) + + def test_schema_describe_with_module_03(self): + schema_text = f''' + module dummy {{}} + ''' + + queries = [] + + with_mod = '' + queries += [ + with_mod + 'SELECT {} = 1', + with_mod + 'SELECT {} = 1', + ] + with_mod = 'WITH MODULE dummy ' + queries += [ + with_mod + 'SELECT {} = 1', + with_mod + 'SELECT {} = 1', + ] + with_mod = 'WITH MODULE std ' + queries += [ + with_mod + 'SELECT {} = 1', + with_mod + 'SELECT {} = 1', + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + with_mod + 'SELECT {} = 1', + with_mod + 'SELECT {} = 1', + ] + with_mod = 'WITH def as MODULE default ' + queries += [ + with_mod + 'SELECT {} = 1', + with_mod + 'SELECT {} = 1', ] + with_mod = 'WITH s as MODULE std ' + queries += [ + with_mod + 'SELECT {} = 1', + with_mod + 'SELECT {} = 1', + with_mod + 'SELECT {} = 1', + ] + with_mod = 'WITH std as MODULE dummy ' + queries += [ + with_mod + 'SELECT {} = 1', + ] + normalized = 'SELECT ({} = 1)' - for query in valid_queries: + for query in queries: self._assert_describe( schema_text + f''' module default {{ alias query := ({query}); }} @@ -11271,20 +11835,48 @@ def test_schema_describe_with_module_01(self): explicit_modules=True, ) - def test_schema_describe_with_module_02a(self): + def test_schema_describe_with_module_04a(self): schema_text = f''' module dummy {{}} module default {{ type int64; }} ''' - valid_queries = [ - 'SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE dummy SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', - 'WITH MODULE std SELECT {} = 1', + + queries = [] + + with_mod = '' + queries += [ + with_mod + 'SELECT {} = 1', + ] + with_mod = 'WITH MODULE dummy ' + queries += [ + with_mod + 'SELECT {} = 1', + with_mod + 'SELECT {} = 1', ] + with_mod = 'WITH MODULE std ' + queries += [ + with_mod + 'SELECT {} = 1', + with_mod + 'SELECT {} = 1', + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + with_mod + 'SELECT {} = 1', + ] + with_mod = 'WITH def as MODULE default ' + queries += [ + with_mod + 'SELECT {} = 1', + ] + with_mod = 'WITH s as MODULE std ' + queries += [ + with_mod + 'SELECT {} = 1', + with_mod + 'SELECT {} = 1', + ] + with_mod = 'WITH std as MODULE dummy ' + queries += [] + with_mod = 'WITH std as MODULE default ' + queries += [] + normalized = 'SELECT ({} = 1)' - for query in valid_queries: + for query in queries: self._assert_describe( schema_text + f''' module default {{ alias query := ({query}); }} @@ -11299,19 +11891,47 @@ def test_schema_describe_with_module_02a(self): explicit_modules=True, ) - def test_schema_describe_with_module_02b(self): + def test_schema_describe_with_module_04b(self): schema_text = f''' module dummy {{}} module default {{ type int64; }} ''' - valid_queries = [ - 'SELECT {}', - 'SELECT {}', - 'WITH MODULE dummy SELECT {}', - 'WITH MODULE std SELECT {}', + + queries = [] + + with_mod = '' + queries += [ + with_mod + 'SELECT {}', + with_mod + 'SELECT {}', + ] + with_mod = 'WITH MODULE dummy ' + queries += [ + with_mod + 'SELECT {}', + ] + with_mod = 'WITH MODULE std ' + queries += [ + with_mod + 'SELECT {}', + ] + with_mod = 'WITH dum as MODULE dummy ' + queries += [ + with_mod + 'SELECT {}', + ] + with_mod = 'WITH def as MODULE default ' + queries += [ + with_mod + 'SELECT {}', + with_mod + 'SELECT {}', + ] + with_mod = 'WITH s as MODULE std ' + queries += [ + with_mod + 'SELECT {}', ] + with_mod = 'WITH std as MODULE dummy ' + queries += [] + with_mod = 'WITH std as MODULE default ' + queries += [] + normalized = 'SELECT {}' - for query in valid_queries: + for query in queries: self._assert_describe( schema_text + f''' module default {{ alias query := ({query}); }} @@ -11326,26 +11946,65 @@ def test_schema_describe_with_module_02b(self): explicit_modules=True, ) - def test_schema_describe_with_module_03(self): + def test_schema_describe_with_module_05(self): schema_text = f''' module dummy {{}} ''' - valid_queries = [ - 'select _test::abs(1)', - 'select std::_test::abs(1)', - 'with module dummy select _test::abs(1)', - 'with module dummy select std::_test::abs(1)', - 'with module _test select abs(1)', - 'with module _test select _test::abs(1)', - 'with module _test select std::_test::abs(1)', - 'with module std select _test::abs(1)', - 'with module std select std::_test::abs(1)', - 'with module std::_test select abs(1)', - 'with module std::_test select _test::abs(1)', - 'with module std::_test select std::_test::abs(1)', + + queries = [] + + with_mod = '' + queries += [ + with_mod + 'select _test::abs(1)', + with_mod + 'select std::_test::abs(1)', + ] + with_mod = 'with module dummy ' + queries += [ + with_mod + 'select _test::abs(1)', + with_mod + 'select std::_test::abs(1)', + ] + with_mod = 'with module _test ' + queries += [ + with_mod + 'select abs(1)', + with_mod + 'select _test::abs(1)', + with_mod + 'select std::_test::abs(1)', ] + with_mod = 'with module std ' + queries += [ + with_mod + 'select _test::abs(1)', + with_mod + 'select std::_test::abs(1)', + ] + with_mod = 'with module std::_test ' + queries += [ + with_mod + 'select abs(1)', + with_mod + 'select _test::abs(1)', + with_mod + 'select std::_test::abs(1)', + ] + with_mod = 'with t as module _test ' + queries += [ + with_mod + 'select _test::abs(1)', + with_mod + 'select std::_test::abs(1)', + with_mod + 'select t::abs(1)', + ] + with_mod = 'with s as module std ' + queries += [ + with_mod + 'select _test::abs(1)', + with_mod + 'select std::_test::abs(1)', + ] + with_mod = 'with st as module std::_test ' + queries += [ + with_mod + 'select _test::abs(1)', + with_mod + 'select std::_test::abs(1)', + with_mod + 'select st::abs(1)', + ] + with_mod = 'with std as module _test ' + queries += [ + with_mod + 'select _test::abs(1)', + with_mod + 'select std::abs(1)', + ] + normalized = 'SELECT std::_test::abs(1)' - for query in valid_queries: + for query in queries: self._assert_describe( schema_text + f''' module default {{ alias query := ({query}); }} @@ -11359,21 +12018,57 @@ def test_schema_describe_with_module_03(self): explicit_modules=True, ) - def test_schema_describe_with_module_04(self): + def test_schema_describe_with_module_06(self): schema_text = f''' module dummy {{}} module _test {{}} ''' - valid_queries = [ - 'select std::_test::abs(1)', - 'with module dummy select std::_test::abs(1)', - 'with module _test select std::_test::abs(1)', - 'with module std select std::_test::abs(1)', - 'with module std::_test select abs(1)', - 'with module std::_test select std::_test::abs(1)', + + queries = [] + + with_mod = '' + queries += [ + with_mod + 'select std::_test::abs(1)', + ] + with_mod = 'with module dummy ' + queries += [ + with_mod + 'select std::_test::abs(1)', + ] + with_mod = 'with module _test ' + queries += [ + with_mod + 'select std::_test::abs(1)', + ] + with_mod = 'with module std ' + queries += [ + with_mod + 'select std::_test::abs(1)', + ] + with_mod = 'with module std::_test ' + queries += [ + with_mod + 'select abs(1)', + with_mod + 'select std::_test::abs(1)', ] + with_mod = 'with t as module _test ' + queries += [ + with_mod + 'select std::_test::abs(1)', + ] + with_mod = 'with s as module std ' + queries += [ + with_mod + 'select std::_test::abs(1)', + ] + with_mod = 'with st as module std::_test ' + queries += [ + with_mod + 'select std::_test::abs(1)', + with_mod + 'select st::abs(1)', + ] + with_mod = 'with std as module _test ' + queries += [] + with_mod = 'with std as module std::_test ' + queries += [ + with_mod + 'select std::abs(1)', + ] + normalized = 'SELECT std::_test::abs(1)' - for query in valid_queries: + for query in queries: self._assert_describe( schema_text + f''' module default {{ alias query := ({query}); }}