|
42 | 42 | import io.trino.spi.function.table.TableFunctionProcessorState; |
43 | 43 | import io.trino.spi.function.table.TableFunctionSplitProcessor; |
44 | 44 | import io.trino.spi.security.ConnectorIdentity; |
| 45 | +import io.trino.spi.security.Identity; |
45 | 46 | import io.trino.sql.tree.ExplainType; |
46 | 47 | import io.trino.testing.AbstractTestQueryFramework; |
47 | 48 | import io.trino.testing.MaterializedRow; |
@@ -1004,6 +1005,47 @@ public void testMaterializedViewWithNonDeterministicFunction() |
1004 | 1005 | assertUpdate("DROP TABLE " + sourceTableName); |
1005 | 1006 | } |
1006 | 1007 |
|
| 1008 | + @Test |
| 1009 | + public void testMaterializedViewWithSessionScopedExpressions() |
| 1010 | + { |
| 1011 | + String sourceTableName = "source_table_" + randomNameSuffix(); |
| 1012 | + assertUpdate("CREATE TABLE " + sourceTableName + " (value INTEGER)"); |
| 1013 | + assertUpdate("INSERT INTO " + sourceTableName + " VALUES 1", 1); |
| 1014 | + |
| 1015 | + // Session-scoped expressions (current_user, current_catalog, current_schema, current_path) |
| 1016 | + // are constants for materialized views (fixed at creation time), so the MV should be FRESH after refresh |
| 1017 | + String mvName = "mv_session_scoped_" + randomNameSuffix(); |
| 1018 | + assertUpdate("CREATE MATERIALIZED VIEW " + mvName + " AS SELECT *, current_user AS created_by FROM " + sourceTableName); |
| 1019 | + |
| 1020 | + assertFreshness(mvName, "STALE"); |
| 1021 | + assertUpdate("REFRESH MATERIALIZED VIEW " + mvName, 1); |
| 1022 | + assertFreshness(mvName, "FRESH"); |
| 1023 | + assertQuery("SELECT created_by FROM " + mvName, "VALUES 'user'"); |
| 1024 | + |
| 1025 | + // A different user querying the MV still sees the original creator's name |
| 1026 | + // because the MV is FRESH and returns cached data |
| 1027 | + Session otherUserSession = Session.builder(getSession()) |
| 1028 | + .setIdentity(Identity.ofUser("other_user")) |
| 1029 | + .build(); |
| 1030 | + assertQuery(otherUserSession, "SELECT created_by FROM " + mvName, "VALUES 'user'"); |
| 1031 | + |
| 1032 | + assertUpdate("INSERT INTO " + sourceTableName + " VALUES 2", 1); |
| 1033 | + assertFreshness(mvName, "STALE"); |
| 1034 | + // Stale MV still serves cached data from storage |
| 1035 | + assertQuery(otherUserSession, "SELECT created_by FROM " + mvName, "VALUES 'user'"); |
| 1036 | + // Refresh by a different user picks up the new row |
| 1037 | + assertUpdate(otherUserSession, "REFRESH MATERIALIZED VIEW " + mvName, 1); |
| 1038 | + assertFreshness(mvName, "FRESH"); |
| 1039 | + // TODO https://github.com/trinodb/trino/issues/28738 session-scoped expressions should resolve using |
| 1040 | + // the MV owner's identity during refresh (like the stale inline path does via analyzeView), but currently |
| 1041 | + // the refresh path resolves them from the refreshing user's session. |
| 1042 | + // When fixed, this should be: VALUES ('user'), ('user') |
| 1043 | + assertQuery("SELECT created_by FROM " + mvName, "VALUES ('user'), ('other_user')"); |
| 1044 | + |
| 1045 | + assertUpdate("DROP MATERIALIZED VIEW " + mvName); |
| 1046 | + assertUpdate("DROP TABLE " + sourceTableName); |
| 1047 | + } |
| 1048 | + |
1007 | 1049 | @Test |
1008 | 1050 | public void testMaterializedViewWithNonDeterministicFunctionAndGracePeriod() |
1009 | 1051 | { |
|
0 commit comments