|
208 | 208 | import static io.trino.testing.TestingAccessControlManager.TestingPrivilegeType.SELECT_COLUMN; |
209 | 209 | import static io.trino.testing.TestingAccessControlManager.privilege; |
210 | 210 | import static io.trino.testing.TestingEventListenerManager.emptyEventListenerManager; |
| 211 | +import static io.trino.testing.TestingMetadata.STALE_MV_STALENESS; |
211 | 212 | import static io.trino.testing.TestingSession.testSessionBuilder; |
212 | 213 | import static io.trino.testing.TransactionBuilder.transaction; |
213 | 214 | import static io.trino.testing.assertions.TrinoExceptionAssert.assertTrinoExceptionThrownBy; |
@@ -5797,6 +5798,22 @@ public void testRowPatternCountFunction() |
5797 | 5798 | public void testAnalyzeFreshMaterializedView() |
5798 | 5799 | { |
5799 | 5800 | analyze("SELECT * FROM fresh_materialized_view"); |
| 5801 | + analyze("SELECT * FROM fresh_materialized_view_non_existent_table"); |
| 5802 | + assertFails("REFRESH MATERIALIZED VIEW fresh_materialized_view_non_existent_table") |
| 5803 | + .hasErrorCode(TABLE_NOT_FOUND) |
| 5804 | + .hasMessage("line 1:18: Table 'tpch.s1.non_existent_table' does not exist"); |
| 5805 | + } |
| 5806 | + |
| 5807 | + @Test |
| 5808 | + public void testAnalyzeStaleMaterializedView() |
| 5809 | + { |
| 5810 | + analyze("SELECT * FROM stale_materialized_view"); |
| 5811 | + assertFails("SELECT * FROM stale_materialized_view_non_existent_table") |
| 5812 | + .hasErrorCode(INVALID_VIEW) |
| 5813 | + .hasMessage("line 1:15: Failed analyzing stored view 'tpch.s1.stale_materialized_view_non_existent_table': line 1:18: Table 'tpch.s1.non_existent_table' does not exist"); |
| 5814 | + assertFails("REFRESH MATERIALIZED VIEW stale_materialized_view_non_existent_table") |
| 5815 | + .hasErrorCode(TABLE_NOT_FOUND) |
| 5816 | + .hasMessage("line 1:18: Table 'tpch.s1.non_existent_table' does not exist"); |
5800 | 5817 | } |
5801 | 5818 |
|
5802 | 5819 | @Test |
@@ -5832,6 +5849,23 @@ public void testAnalyzeMaterializedViewWithAccessControl() |
5832 | 5849 | accessControlManager) |
5833 | 5850 | .hasErrorCode(PERMISSION_DENIED) |
5834 | 5851 | .hasMessage("Access Denied: Cannot select from columns [a, b] in table or view tpch.s1.fresh_materialized_view"); |
| 5852 | + accessControlManager.reset(); |
| 5853 | + |
| 5854 | + // Deny access to the table referenced by the underlying query |
| 5855 | + accessControlManager.denyIdentityTable((_, table) -> !"t1".equals(table)); |
| 5856 | + // When an MV is fresh or within the grace period, analysis of the underlying query is not performed, |
| 5857 | + // so access control checks on the tables referenced by the query are not executed. |
| 5858 | + analyze(CLIENT_SESSION, "SELECT * FROM fresh_materialized_view", accessControlManager); |
| 5859 | + assertFails(CLIENT_SESSION, "REFRESH MATERIALIZED VIEW fresh_materialized_view", accessControlManager) |
| 5860 | + .hasErrorCode(PERMISSION_DENIED) |
| 5861 | + .hasMessage("Access Denied: Cannot select from columns [a, b] in table or view tpch.s1.t1"); |
| 5862 | + // Now we check that when the MV is stale, access to the underlying tables is checked. |
| 5863 | + assertFails(CLIENT_SESSION, "SELECT * FROM stale_materialized_view", accessControlManager) |
| 5864 | + .hasErrorCode(PERMISSION_DENIED) |
| 5865 | + .hasMessage("Access Denied: View owner does not have sufficient privileges: View owner 'some user' cannot create view that selects from tpch.s1.t1"); |
| 5866 | + assertFails(CLIENT_SESSION, "REFRESH MATERIALIZED VIEW stale_materialized_view", accessControlManager) |
| 5867 | + .hasErrorCode(PERMISSION_DENIED) |
| 5868 | + .hasMessage("Access Denied: Cannot select from columns [a, b] in table or view tpch.s1.t1"); |
5835 | 5869 | } |
5836 | 5870 |
|
5837 | 5871 | @Test |
@@ -8074,27 +8108,70 @@ public void setup() |
8074 | 8108 | ImmutableList.of(new ColumnMetadata("a", BIGINT))), |
8075 | 8109 | FAIL)); |
8076 | 8110 |
|
| 8111 | + MaterializedViewDefinition materializedView = new MaterializedViewDefinition( |
| 8112 | + "SELECT a, b FROM t1", |
| 8113 | + Optional.of(TPCH_CATALOG), |
| 8114 | + Optional.of("s1"), |
| 8115 | + ImmutableList.of(new ViewColumn("a", BIGINT.getTypeId(), Optional.empty()), new ViewColumn("b", BIGINT.getTypeId(), Optional.empty())), |
| 8116 | + Optional.of(STALE_MV_STALENESS.minusHours(1)), // Minus 1 hour to make MVs not marked as fresh become stale immediately. This doesn’t affect those marked as fresh. |
| 8117 | + Optional.empty(), |
| 8118 | + Optional.empty(), |
| 8119 | + Identity.ofUser("some user"), |
| 8120 | + ImmutableList.of(), |
| 8121 | + // t3 has a, b column and hidden column x |
| 8122 | + Optional.of(new CatalogSchemaTableName(TPCH_CATALOG, "s1", "t3"))); |
| 8123 | + |
8077 | 8124 | QualifiedObjectName freshMaterializedView = new QualifiedObjectName(TPCH_CATALOG, "s1", "fresh_materialized_view"); |
8078 | 8125 | inSetupTransaction(session -> metadata.createMaterializedView( |
8079 | 8126 | session, |
8080 | 8127 | freshMaterializedView, |
8081 | | - new MaterializedViewDefinition( |
8082 | | - "SELECT a, b FROM t1", |
8083 | | - Optional.of(TPCH_CATALOG), |
8084 | | - Optional.of("s1"), |
8085 | | - ImmutableList.of(new ViewColumn("a", BIGINT.getTypeId(), Optional.empty()), new ViewColumn("b", BIGINT.getTypeId(), Optional.empty())), |
8086 | | - Optional.empty(), |
8087 | | - Optional.empty(), |
8088 | | - Optional.empty(), |
8089 | | - Identity.ofUser("some user"), |
8090 | | - ImmutableList.of(), |
8091 | | - // t3 has a, b column and hidden column x |
8092 | | - Optional.of(new CatalogSchemaTableName(TPCH_CATALOG, "s1", "t3"))), |
| 8128 | + materializedView, |
8093 | 8129 | ImmutableMap.of(), |
8094 | 8130 | false, |
8095 | 8131 | false)); |
8096 | 8132 | testingConnectorMetadata.markMaterializedViewIsFresh(freshMaterializedView.asSchemaTableName()); |
8097 | 8133 |
|
| 8134 | + QualifiedObjectName staleMaterializedView = new QualifiedObjectName(TPCH_CATALOG, "s1", "stale_materialized_view"); |
| 8135 | + inSetupTransaction(session -> metadata.createMaterializedView( |
| 8136 | + session, |
| 8137 | + staleMaterializedView, |
| 8138 | + materializedView, |
| 8139 | + ImmutableMap.of(), |
| 8140 | + false, |
| 8141 | + false)); |
| 8142 | + |
| 8143 | + MaterializedViewDefinition materializedViewNonExistentTable = new MaterializedViewDefinition( |
| 8144 | + "SELECT a, b FROM non_existent_table", |
| 8145 | + Optional.of(TPCH_CATALOG), |
| 8146 | + Optional.of("s1"), |
| 8147 | + ImmutableList.of(new ViewColumn("a", BIGINT.getTypeId(), Optional.empty()), new ViewColumn("b", BIGINT.getTypeId(), Optional.empty())), |
| 8148 | + Optional.of(STALE_MV_STALENESS.minusHours(1)), // Minus 1 hour to make MVs not marked as fresh become stale immediately. This doesn’t affect those marked as fresh. |
| 8149 | + Optional.empty(), |
| 8150 | + Optional.empty(), |
| 8151 | + Identity.ofUser("some user"), |
| 8152 | + ImmutableList.of(), |
| 8153 | + // t3 has a, b column and hidden column x |
| 8154 | + Optional.of(new CatalogSchemaTableName(TPCH_CATALOG, "s1", "t3"))); |
| 8155 | + |
| 8156 | + QualifiedObjectName freshMaterializedViewNonExistentTable = new QualifiedObjectName(TPCH_CATALOG, "s1", "fresh_materialized_view_non_existent_table"); |
| 8157 | + inSetupTransaction(session -> metadata.createMaterializedView( |
| 8158 | + session, |
| 8159 | + freshMaterializedViewNonExistentTable, |
| 8160 | + materializedViewNonExistentTable, |
| 8161 | + ImmutableMap.of(), |
| 8162 | + false, |
| 8163 | + false)); |
| 8164 | + testingConnectorMetadata.markMaterializedViewIsFresh(freshMaterializedViewNonExistentTable.asSchemaTableName()); |
| 8165 | + |
| 8166 | + QualifiedObjectName staleMaterializedViewNonExistentTable = new QualifiedObjectName(TPCH_CATALOG, "s1", "stale_materialized_view_non_existent_table"); |
| 8167 | + inSetupTransaction(session -> metadata.createMaterializedView( |
| 8168 | + session, |
| 8169 | + staleMaterializedViewNonExistentTable, |
| 8170 | + materializedViewNonExistentTable, |
| 8171 | + ImmutableMap.of(), |
| 8172 | + false, |
| 8173 | + false)); |
| 8174 | + |
8098 | 8175 | QualifiedObjectName freshMaterializedViewMismatchedColumnCount = new QualifiedObjectName(TPCH_CATALOG, "s1", "fresh_materialized_view_mismatched_column_count"); |
8099 | 8176 | inSetupTransaction(session -> metadata.createMaterializedView( |
8100 | 8177 | session, |
|
0 commit comments