Repository: edgedb/edgedb

Issue:
Migration fails with error edgedb error: InternalServerError: there is no parameter $1
<!-- Please search existing issues to avoid creating duplicates. -->
- EdgeDB Version:  Version                │ 2.4+9f3da78
- OS Version: MacOS Monterey 12.5.1

Steps to Reproduce:

1. Add a migration using a filter expr such as SELECT X LIMIT 1
2. Run the migration

<!-- If the issue is about a query error, please also provide your schema -->
Schema:



Discussion:
Could you give a specific example? I don't fully follow.

On Wed, Oct 19, 2022, 07:32 Ravin Thambapillai ***@***.***>
wrote:

>
>    - EdgeDB Version: Version │ 2.4+9f3da78
>    - OS Version: MacOS Monterey 12.5.1
>
> Steps to Reproduce:
>
>    1. Add a migration using a filter expr such as SELECT X LIMIT 1
>    2. Run the migration
>
> Schema:
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/edgedb/edgedb/issues/4551>, or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AACTC7PUTM3YDAQEF2WMV4LWEABAZANCNFSM6AAAAAARJGCJWQ>
> .
> You are receiving this because you are subscribed to this thread.Message
> ID: ***@***.***>
>

Absolutely: here is my migration file:

```
CREATE MIGRATION m1itj65yetbsc7c2u6hfnrnk2zxlmmmwoxz2ekecpljcwp7fxvqlbq
    ONTO m1gim6kumzro3uebw63ckbda6aivsejppsfnx754ggxvuveiudivfq
{
  CREATE GLOBAL default::current_user -> std::uuid;
  ALTER TYPE default::Document {
      CREATE REQUIRED LINK author -> default::User {
          SET REQUIRED USING (SELECT
              default::User 
          LIMIT
              1
          );
      };
      EXTENDING default::SignalSubject LAST;
  };
  ALTER TYPE default::Document {
      CREATE ACCESS POLICY allow_insert
          ALLOW INSERT USING (true);
      CREATE MULTI LINK viewers -> default::User;
      CREATE ACCESS POLICY allow_select_for_viewer
          ALLOW SELECT USING (((GLOBAL default::current_user IN .viewers.id) ?? false));
      CREATE MULTI LINK editors -> default::User;
      CREATE ACCESS POLICY allow_select_or_update_for_editor
          ALLOW SELECT, UPDATE USING (((GLOBAL default::current_user IN .editors.id) ?? false));
      CREATE ACCESS POLICY author_has_full_access
          ALLOW ALL USING ((GLOBAL default::current_user ?= .author.id));
      CREATE MULTI LINK owners -> default::User;
      CREATE ACCESS POLICY owner_has_full_access
          ALLOW ALL USING (((GLOBAL default::current_user IN .owners.id) ?? false));
      CREATE CONSTRAINT std::exclusive ON (.author);
  };
  CREATE TYPE default::DocumentAcl {
      CREATE REQUIRED LINK document -> default::Document;
      CREATE REQUIRED LINK sharedBy -> default::User;
      CREATE REQUIRED MULTI LINK user -> default::User;
      CREATE REQUIRED PROPERTY accessRight -> std::str;
      CREATE REQUIRED PROPERTY createdDatetime -> std::datetime {
          SET default := (std::datetime_current());
          SET readonly := true;
      };
  };
  ALTER TYPE default::Document {
      CREATE MULTI LINK acls := (.<document[IS default::DocumentAcl]);
  };
  ALTER TYPE default::DocumentVersion {
      CREATE ACCESS POLICY allow_select_for_viewer
          ALLOW SELECT USING (((GLOBAL default::current_user IN .parentDocument.viewers.id) ?? false));
      CREATE ACCESS POLICY allow_select_or_update_for_editor
          ALLOW SELECT, UPDATE USING (((GLOBAL default::current_user IN .parentDocument.editors.id) ?? false));
      CREATE ACCESS POLICY author_has_full_access
          ALLOW ALL USING ((GLOBAL default::current_user ?= .parentDocument.author.id));
      CREATE ACCESS POLICY owner_has_full_access
          ALLOW ALL USING (((GLOBAL default::current_user IN .parentDocument.owners.id) ?? false));
      CREATE ACCESS POLICY allow_insert
          ALLOW INSERT USING (true);
  };
  ALTER TYPE default::StrategyNode {
      ALTER LINK documentVersion {
          SET REQUIRED USING (SELECT
              default::DocumentVersion 
          LIMIT
              1
          );
      };
      CREATE ACCESS POLICY allow_select_for_viewer
          ALLOW SELECT USING (((GLOBAL default::current_user IN .documentVersion.parentDocument.viewers.id) ?? false));
      CREATE ACCESS POLICY allow_select_or_update_for_editor
          ALLOW SELECT, UPDATE USING (((GLOBAL default::current_user IN .documentVersion.parentDocument.editors.id) ?? false));
      CREATE ACCESS POLICY author_has_full_access
          ALLOW ALL USING ((GLOBAL default::current_user ?= .documentVersion.parentDocument.author.id));
      CREATE ACCESS POLICY owner_has_full_access
          ALLOW ALL USING (((GLOBAL default::current_user IN .documentVersion.parentDocument.owners.id) ?? false));
      CREATE ACCESS POLICY allow_insert
          ALLOW INSERT USING (true);
  };
  ALTER TYPE default::User {
      CREATE LINK acl := (.<user[IS default::DocumentAcl]);
      CREATE MULTI LINK authored := (.<author[IS default::Document]);
      CREATE MULTI LINK canEdit := (.<editors[IS default::Document]);
      CREATE MULTI LINK canView := (.<viewers[IS default::Document]);
  };
};
```

And here is my schema:

`module default {
    global current_user -> uuid;

    type ActivitySource {
        required link strategyNode -> StrategyNode;
        property sourceName -> str;
        property sourceUrl -> str;
        property timeLastPulled -> datetime {
            default := datetime_current();
        }

        constraint exclusive on ((.sourceUrl, .strategyNode));
    }

    type DashboardConfig {
        required link strategyNode -> StrategyNode;
        required property dashboardSource -> str;
        multi link metrics -> Metric;
    }

    type Document extending SignalSubject {
        property unpublishedVersion -> json; # May eventually be some collaborative editing supportive representation, just a serialized document for now. #TODO make required
        link mostRecentPublished := (
            select .<parentDocument[is DocumentVersion] order by .createdDatetime desc limit 1
        );
        multi link history := (
            select .<parentDocument[is DocumentVersion] order by .createdDatetime desc
        );
        required link author -> User;
        multi link acls := .<document[is DocumentAcl];
        multi link viewers -> User;
        multi link editors -> User;
        multi link owners -> User;        

        constraint exclusive on (.author); 
        access policy author_has_full_access
            allow all
            using (global current_user ?= .author.id);

        access policy owner_has_full_access
            allow all
            using ((global current_user in .owners.id) ?? false);


        access policy allow_select_or_update_for_editor
            allow select, update, update read, update write
            using ((global current_user in .editors.id) ?? false);

        access policy allow_select_for_viewer
            allow select
            using ((global current_user in .viewers.id) ?? false);

        access policy allow_insert
            allow insert
            using (true);
    }

    type DocumentVersion {
        required link parentDocument -> Document;
        required property content -> json;
        required property createdDatetime -> datetime {
            default := datetime_current();
            readonly := true;
        }
        index on ((.parentDocument, .createdDatetime));
        access policy author_has_full_access
            allow all
            using (global current_user ?= .parentDocument.author.id);

        access policy owner_has_full_access
            allow all
            using ((global current_user in .parentDocument.owners.id) ?? false);

        access policy allow_select_or_update_for_editor
            allow select, update, update read, update write
            using ((global current_user in .parentDocument.editors.id) ?? false);

        access policy allow_select_for_viewer
            allow select
            using ((global current_user in .parentDocument.viewers.id) ?? false);

        access policy allow_insert
            allow insert
            using (true);        
    }

    type Event extending SignalSubject {
        required property eventDatetime -> datetime {
            default := datetime_current();
        }
        required property name -> str;
        property type_ -> str;
        required link author -> User;
        multi link strategyNodes -> StrategyNode;
    }


    type Metric extending SignalSubject {
        property name -> str {
            annotation description := "Optional, likely to be exactly the same as the SN and someday maybe a computed property, but may have an idiosyncratic name"
        }
        property type_ -> str;
        multi link strategyNodes -> StrategyNode {
            property target_ -> float64; # Unused. Will be used instead of the target object when link properties are better supported
        }
        required property sourceMetadata -> json;
        multi link metricTargets := .<metric[is MetricTarget];
        link generatingQuestion := .<generatedMetric[is Question];
        link predictingQuestion := .<predictedMetric[is Question];
    }

    type MetricTarget extending SignalSubject {
        property name -> str;
        property targetValue -> float64;
        required link author -> User; 
        required link strategyNode -> StrategyNode;
        required link metric -> Metric;
        property score -> float64;
        property scoringFunction -> json;
        property targetCompletionDate -> datetime;
    }

    abstract type Principal extending SignalSubject {
        required property name -> str;
        link parentTeam -> Team;
    }

    type Question {
        required property type_ -> str;
        required property prompt -> str;
        link predictedMetric -> Metric;
        link generatedMetric -> Metric;
        multi link positions := .<question[is QuestionPosition];
        multi link responses := .<question[is Signal];
    }

    type QuestionPosition {
        required property position -> int32;
        required link question -> Question;
        required link survey -> Survey;
    }


    type Signal extending SignalSubject { # Extends SignalSubject to enable conversation-style signal
        required property content -> json;
        required property type_ -> str;
        link subject -> SignalSubject;
        link strategyNode ->StrategyNode;
        link survey -> Survey;        
        link question -> Question;

        required link author -> User; # TODO make required when we have a auth and users live
        multi link recipients -> Principal; # TODO new recipients permissions model

        required property createdDatetime -> datetime {
            default := datetime_current();
            #readonly := true;
        }
        property modifiedDatetime -> datetime;
        constraint exclusive on ((.subject, .author, .createdDatetime, .type_))
    }

    abstract type SignalSubject {
        multi link signals := .<subject[is Signal];
    }

    type StrategyNode extending SignalSubject {
        link parentNode -> StrategyNode;
        multi link childNodes := .<parentNode[is StrategyNode];

        required link documentVersion -> DocumentVersion; # Latest available document version for this node. TODO In the future will likely be required

        required property nodeType -> str; # This may eventually reference a node type as data with ontology customization
        required property title -> str;
        required property tiptapId -> str {
            constraint exclusive;
        };
        property metadata -> json;

        multi link metrics := .<strategyNodes[is Metric];

        multi link responsibleParties -> User;
        multi link defaultSignalViewers -> Principal;        
        multi link subscribers -> User;
        property status -> str;
        link team -> Team;
        access policy author_has_full_access
            allow all
            using (global current_user ?= .documentVersion.parentDocument.author.id);

        access policy owner_has_full_access
            allow all
            using ((global current_user in .documentVersion.parentDocument.owners.id) ?? false);

        access policy allow_select_or_update_for_editor
            allow select, update, update read, update write
            using ((global current_user in .documentVersion.parentDocument.editors.id) ?? false);

        access policy allow_select_for_viewer
            allow select
            using ((global current_user in .documentVersion.parentDocument.viewers.id) ?? false);

        access policy allow_insert
            allow insert
            using (true);

        index on (.tiptapId);

    }

    type StrategyNodeEdit extending SignalSubject {
        required link node -> StrategyNode;
        property fromStatus -> str;
        property toStatus -> str;
        property fromTitle -> str;
        property toTitle -> str;
        property fromType -> str;
        property toType -> str;
        property oldNode -> json;
        property newNode -> json;
        required property editedFields -> array<str>;
        property reason -> str;
        multi link motivatingSignal -> Signal;
        required link author -> User;
        required property createdDatetime -> datetime {
            default := datetime_current();
            #readonly := true;
        }
        # access policy author_has_full_access
        #     allow all
        #     using (global current_user ?= .documentVersion.parentDocument.author.id);

        # access policy owner_has_full_access
        #     allow all
        #     using ((global current_user in .documentVersionparentDocument.owners.id) ?? false);

        # access policy allow_select_or_update_for_editor
        #     allow select, update, update read, update write
        #     using ((global current_user in .documentVersion.parentDocument.editors.id) ?? false);

        # access policy allow_select_for_viewer
        #     allow select
        #     using ((global current_user in .documentVersion.parentDocument.viewers.id) ?? false);

        # access policy allow_insert
        #     allow insert
        #     using (true);        
    }

    type Survey {
        required property name -> str;
        required property isRecurring -> str;
        property reason -> str;
        multi link recipients -> User;
        required multi link questions -> Question {
            property position -> int64; # Unused. Will be used instead of position object when link properties are better supported
        }
        required property subjectTypes -> array<str>;
        required property subjects -> array<str>; # TO DO - should probably be a multi link to strategy node but want to require the prop for now since we are not yet populating strategy node
        required link strategyNode -> StrategyNode;
        required link author -> User;
    }


    type Team extending Principal {
        required multi link leads -> User;  # TODO should probably be a property on the link
        required multi link members -> User;
        multi link subTeams -> Team;
        multi link subscribers -> User;
    }

    type User extending Principal {
        required property email -> str;
        required property provider -> str;
        property given_name -> str;
        property family_name -> str;
        property picture -> str;
        property email_verified -> bool;
        property locale -> str;
        property provider_id -> str;
        link acl := .<user[is DocumentAcl];
        multi link canView := .<viewers[is Document];
        multi link canEdit := .<editors[is Document];
        multi link authored := .<author[is Document];

        constraint exclusive on ((.email, .provider));
    }

}`

Anytime I run the the migration, I get the following error:

<img width="1167" alt="Screenshot 2022-10-19 at 11 15 34 AM" src="https://user-images.githubusercontent.com/1099475/196732763-eca76204-ff67-40ee-a8c1-c64e55c7bf9a.png">


I updated the title to make it a little clearer what I meant, and put a little context here as well: https://discord.com/channels/841451783728529451/1032297137637175349
We strongly suspect that this issue was introduced by the use of access_policies
Yes, it is caused by the interaction of access policies and `SET REQUIRED USING` and access policies: we are trying to apply the access policies while evaluating `(SELECT default::DocumentVersion LIMIT 1)`, and this is failing because the global variable cannot be accessed in that context.

As a workaround, it should work to comment out the access policies when making the link required, and then adding them back in in another migration.

This will be fixed in 2.5.

Resolving patch:
diff --git a/edb/pgsql/delta.py b/edb/pgsql/delta.py
--- a/edb/pgsql/delta.py
+++ b/edb/pgsql/delta.py
@@ -4123,6 +4123,7 @@ def _compile_conversion_expr(
                 context,
                 conv_expr,
                 target_as_singleton=target_as_singleton,
+                no_query_rewrites=True,
             )
             ir = conv_expr.irast
 
@@ -4145,6 +4146,7 @@ def _compile_conversion_expr(
                     schema=orig_schema,
                 ),
                 target_as_singleton=target_as_singleton,
+                no_query_rewrites=True,
             )
 
             ir = conv_expr.irast
diff --git a/edb/schema/delta.py b/edb/schema/delta.py
--- a/edb/schema/delta.py
+++ b/edb/schema/delta.py
@@ -1590,6 +1590,7 @@ def apply(
                 schema,
                 options=qlcompiler.CompilerOptions(
                     modaliases=context.modaliases,
+                    apply_query_rewrites=False,
                 )
             )
         return schema
diff --git a/edb/schema/pointers.py b/edb/schema/pointers.py
--- a/edb/schema/pointers.py
+++ b/edb/schema/pointers.py
@@ -1315,6 +1315,7 @@ def _compile_expr(
         singleton_result_expected: bool = False,
         target_as_singleton: bool = False,
         expr_description: Optional[str] = None,
+        no_query_rewrites: bool = False,
     ) -> s_expr.Expression:
         singletons: List[Union[s_types.Type, Pointer]] = []
 
@@ -1369,7 +1370,9 @@ def _compile_expr(
                 anchors={qlast.Source().name: source},
                 path_prefix_anchor=qlast.Source().name,
                 singletons=singletons,
-                apply_query_rewrites=not context.stdmode,
+                apply_query_rewrites=(
+                    not context.stdmode and not no_query_rewrites
+                ),
                 track_schema_ref_exprs=track_schema_ref_exprs,
                 in_ddl_context_name=in_ddl_context_name,
             ),
@@ -2221,6 +2224,7 @@ def _alter_begin(
                     expr=self.cast_expr,
                     target_as_singleton=True,
                     singleton_result_expected=True,
+                    no_query_rewrites=True,
                     expr_description=(
                         f'the USING clause for the alteration of {vn}'
                     ),
@@ -2400,6 +2404,7 @@ def _alter_begin(
                     expr=self.conv_expr,
                     target_as_singleton=False,
                     singleton_result_expected=True,
+                    no_query_rewrites=True,
                     expr_description=(
                         f'the USING clause for the alteration of {vn}'
                     ),
@@ -2614,6 +2619,7 @@ def _alter_begin(
                     expr=self.fill_expr,
                     target_as_singleton=True,
                     singleton_result_expected=new_card.is_single(),
+                    no_query_rewrites=True,
                     expr_description=(
                         f'the USING clause for the alteration of {vn}'
                     ),


Test patch:
diff --git a/tests/test_edgeql_ddl.py b/tests/test_edgeql_ddl.py
--- a/tests/test_edgeql_ddl.py
+++ b/tests/test_edgeql_ddl.py
@@ -5801,6 +5801,38 @@ async def test_edgeql_ddl_policies_03(self):
                   SET default := "!!!";
             """)
 
+    async def test_edgeql_ddl_policies_04(self):
+        await self.con.execute("""
+            create global current_user -> uuid;
+
+            create type User {
+                create access policy ins allow insert;
+                create access policy sel allow select
+                  using (.id ?= global current_user);
+            };
+            create type User2 extending User;
+
+            create type Obj {
+                create optional multi link user -> User;
+            };
+        """)
+
+        await self.con.execute("""
+            alter type Obj {
+                alter link user set required using (select User limit 1);
+            };
+        """)
+        await self.con.execute("""
+            alter type Obj {
+                alter link user set single using (select User limit 1);
+            };
+        """)
+        await self.con.execute("""
+            alter type Obj {
+                alter link user set type User2 using (select User2 limit 1);
+            };
+        """)
+
     # A big collection of tests to make sure that functions get
     # updated when access policies change
     async def test_edgeql_ddl_func_policies_01(self):
@@ -12015,6 +12047,29 @@ async def test_edgeql_ddl_create_migration_01(self):
             }]
         )
 
+    async def test_edgeql_ddl_create_migration_02(self):
+        await self.con.execute('''
+CREATE MIGRATION m1kmv2mcizpj2twxlxxerkgngr2fkto7wnjd6uig3aa3x67dykvspq
+    ONTO initial
+{
+  CREATE GLOBAL default::foo -> std::bool;
+  CREATE TYPE default::Foo {
+      CREATE ACCESS POLICY foo
+          ALLOW ALL USING ((GLOBAL default::foo ?? true));
+  };
+};
+        ''')
+
+        await self.con.execute('''
+CREATE MIGRATION m14i24uhm6przo3bpl2lqndphuomfrtq3qdjaqdg6fza7h6m7tlbra
+    ONTO m1kmv2mcizpj2twxlxxerkgngr2fkto7wnjd6uig3aa3x67dykvspq
+{
+  CREATE TYPE default::X;
+
+  INSERT Foo;
+};
+        ''')
+
     async def test_edgeql_ddl_naked_backlink_in_computable(self):
         await self.con.execute('''
             CREATE TYPE User {