materialized_views.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. # Copyright Materialize, Inc. and contributors. All rights reserved.
  2. #
  3. # Use of this software is governed by the Business Source License
  4. # included in the LICENSE file at the root of this repository.
  5. #
  6. # As of the Change Date specified in that file, in accordance with
  7. # the Business Source License, use of this software will be governed
  8. # by the Apache License, Version 2.0.
  9. import re
  10. from textwrap import dedent
  11. from materialize.checks.actions import Testdrive
  12. from materialize.checks.checks import Check
  13. class MaterializedViews(Check):
  14. def initialize(self) -> Testdrive:
  15. return Testdrive(
  16. dedent(
  17. """
  18. > CREATE TABLE materialized_views_table (f1 STRING);
  19. > INSERT INTO materialized_views_table SELECT 'T1A' || generate_series FROM generate_series(1,10000);
  20. > INSERT INTO materialized_views_table SELECT 'T1B' || generate_series FROM generate_series(1,10000);
  21. # Regression test for database-issues#8032.
  22. > CREATE MATERIALIZED VIEW zero_arity AS SELECT;
  23. """
  24. )
  25. )
  26. def manipulate(self) -> list[Testdrive]:
  27. return [
  28. Testdrive(dedent(s))
  29. for s in [
  30. """
  31. > INSERT INTO materialized_views_table SELECT 'T2A' || generate_series FROM generate_series(1, 10000);
  32. > CREATE MATERIALIZED VIEW materialized_view1 AS SELECT LEFT(f1, 3), COUNT(*) FROM materialized_views_table GROUP BY LEFT(f1, 3);
  33. > DELETE FROM materialized_views_table WHERE LEFT(f1, 3) = 'T1A';
  34. > INSERT INTO materialized_views_table SELECT 'T2B' || generate_series FROM generate_series(1, 10000);
  35. """,
  36. """
  37. > DELETE FROM materialized_views_table WHERE LEFT(f1, 3) = 'T2A';
  38. > CREATE MATERIALIZED VIEW materialized_view2 AS SELECT LEFT(f1, 3), COUNT(*) FROM materialized_views_table GROUP BY LEFT(f1, 3);
  39. > INSERT INTO materialized_views_table SELECT 'T3B' || generate_series FROM generate_series(1, 10000);
  40. """,
  41. ]
  42. ]
  43. def validate(self) -> Testdrive:
  44. return Testdrive(
  45. dedent(
  46. """
  47. > SELECT * FROM materialized_view1
  48. T1B 10000
  49. T2B 10000
  50. T3B 10000
  51. > SELECT * FROM materialized_view2
  52. T1B 10000
  53. T2B 10000
  54. T3B 10000
  55. > SELECT 1, * FROM zero_arity
  56. 1
  57. """
  58. )
  59. )
  60. class MaterializedViewsAssertNotNull(Check):
  61. def initialize(self) -> Testdrive:
  62. return Testdrive(
  63. dedent(
  64. """
  65. > CREATE TABLE not_null_table (x INT, y INT, z INT);
  66. > INSERT INTO not_null_table VALUES (NULL, 2, 3), (4, NULL, 6), (7, 8, NULL);
  67. > CREATE MATERIALIZED VIEW not_null_view1 WITH (ASSERT NOT NULL x) AS SELECT * FROM not_null_table;
  68. """
  69. )
  70. )
  71. def manipulate(self) -> list[Testdrive]:
  72. return [
  73. Testdrive(dedent(s))
  74. for s in [
  75. """
  76. > CREATE MATERIALIZED VIEW not_null_view2 WITH (ASSERT NOT NULL y) AS SELECT * FROM not_null_table;
  77. > INSERT INTO not_null_table VALUES (NULL, 12, 13), (14, NULL, 16), (17, 18, NULL);
  78. """,
  79. """
  80. > CREATE MATERIALIZED VIEW not_null_view3 WITH (ASSERT NOT NULL z) AS SELECT * FROM not_null_table;
  81. > INSERT INTO not_null_table VALUES (NULL, 22, 23), (24, NULL, 26), (27, 28, NULL);
  82. """,
  83. ]
  84. ]
  85. def validate(self) -> Testdrive:
  86. sql = dedent(
  87. """
  88. ! SELECT * FROM not_null_view1
  89. contains: column "x" must not be null
  90. ! SELECT * FROM not_null_view2
  91. contains: column "y" must not be null
  92. ! SELECT * FROM not_null_view3
  93. contains: column "z" must not be null
  94. ! SELECT * FROM not_null_view1 WHERE x IS NOT NULL
  95. contains: column "x" must not be null
  96. ! SELECT * FROM not_null_view2 WHERE y IS NOT NULL
  97. contains: column "y" must not be null
  98. ! SELECT * FROM not_null_view3 WHERE z IS NOT NULL
  99. contains: column "z" must not be null
  100. ! SELECT y FROM not_null_view1
  101. contains: column "x" must not be null
  102. ! SELECT z FROM not_null_view2
  103. contains: column "y" must not be null
  104. ! SELECT x FROM not_null_view3
  105. contains: column "z" must not be null
  106. > DELETE FROM not_null_table WHERE x IS NULL;
  107. > SELECT * FROM not_null_view1
  108. 4 <null> 6
  109. 7 8 <null>
  110. 14 <null> 16
  111. 17 18 <null>
  112. 24 <null> 26
  113. 27 28 <null>
  114. > DELETE FROM not_null_table WHERE y IS NULL;
  115. > SELECT * FROM not_null_view2
  116. 7 8 <null>
  117. 17 18 <null>
  118. 27 28 <null>
  119. > DELETE FROM not_null_table WHERE z IS NULL;
  120. ?[version>=13500] EXPLAIN OPTIMIZED PLAN AS VERBOSE TEXT FOR SELECT * FROM not_null_view1 WHERE x IS NOT NULL
  121. Explained Query:
  122. ReadStorage materialize.public.not_null_view1
  123. Source materialize.public.not_null_view1
  124. Target cluster: quickstart
  125. ?[version<13500] EXPLAIN OPTIMIZED PLAN FOR SELECT * FROM not_null_view1 WHERE x IS NOT NULL
  126. Explained Query:
  127. ReadStorage materialize.public.not_null_view1
  128. Source materialize.public.not_null_view1
  129. Target cluster: quickstart
  130. ?[version>=13500] EXPLAIN OPTIMIZED PLAN AS VERBOSE TEXT FOR SELECT * FROM not_null_view2 WHERE y IS NOT NULL
  131. Explained Query:
  132. ReadStorage materialize.public.not_null_view2
  133. Source materialize.public.not_null_view2
  134. Target cluster: quickstart
  135. ?[version<13500] EXPLAIN OPTIMIZED PLAN FOR SELECT * FROM not_null_view2 WHERE y IS NOT NULL
  136. Explained Query:
  137. ReadStorage materialize.public.not_null_view2
  138. Source materialize.public.not_null_view2
  139. Target cluster: quickstart
  140. ?[version>=13500] EXPLAIN OPTIMIZED PLAN AS VERBOSE TEXT FOR SELECT * FROM not_null_view3 WHERE z IS NOT NULL
  141. Explained Query:
  142. ReadStorage materialize.public.not_null_view3
  143. Source materialize.public.not_null_view3
  144. Target cluster: quickstart
  145. ?[version<13500] EXPLAIN OPTIMIZED PLAN FOR SELECT * FROM not_null_view3 WHERE z IS NOT NULL
  146. Explained Query:
  147. ReadStorage materialize.public.not_null_view3
  148. Source materialize.public.not_null_view3
  149. Target cluster: quickstart
  150. > SELECT * FROM not_null_view3
  151. > INSERT INTO not_null_table VALUES (NULL, 2, 3), (4, NULL, 6), (7, 8, NULL);
  152. > INSERT INTO not_null_table VALUES (NULL, 12, 13), (14, NULL, 16), (17, 18, NULL);
  153. > INSERT INTO not_null_table VALUES (NULL, 22, 23), (24, NULL, 26), (27, 28, NULL);
  154. ! SELECT * FROM not_null_view1
  155. contains: column "x" must not be null
  156. ! SELECT * FROM not_null_view2
  157. contains: column "y" must not be null
  158. ! SELECT * FROM not_null_view3
  159. contains: column "z" must not be null
  160. """
  161. )
  162. return Testdrive(sql)
  163. class MaterializedViewsRefresh(Check):
  164. def initialize(self) -> Testdrive:
  165. return Testdrive(
  166. dedent(
  167. """
  168. > CREATE TABLE refresh_table (x INT);
  169. > INSERT INTO refresh_table VALUES (1);
  170. > CREATE MATERIALIZED VIEW refresh_view_2s_1 WITH (REFRESH EVERY '2 seconds') AS SELECT DISTINCT(x) FROM refresh_table;
  171. > CREATE MATERIALIZED VIEW refresh_view_at_1 WITH (REFRESH AT mz_now()::string::int8) AS SELECT DISTINCT(x) FROM refresh_table;
  172. > CREATE MATERIALIZED VIEW refresh_view_late_1 WITH (REFRESH AT mz_now()::string::int8 + 86400000) AS SELECT DISTINCT(x) FROM refresh_table;
  173. """
  174. )
  175. )
  176. def manipulate(self) -> list[Testdrive]:
  177. return [
  178. Testdrive(dedent(s))
  179. for s in [
  180. """
  181. > INSERT INTO refresh_table VALUES (2);
  182. > CREATE MATERIALIZED VIEW refresh_view_2s_2 WITH (REFRESH EVERY '2 seconds') AS SELECT DISTINCT(x) FROM refresh_table;
  183. > CREATE MATERIALIZED VIEW refresh_view_at_2 WITH (REFRESH AT mz_now()::string::int8) AS SELECT DISTINCT(x) FROM refresh_table;
  184. > CREATE MATERIALIZED VIEW refresh_view_late_2 WITH (REFRESH AT mz_now()::string::int8 + 86400000) AS SELECT DISTINCT(x) FROM refresh_table;
  185. """,
  186. """
  187. > INSERT INTO refresh_table VALUES (3);
  188. > CREATE MATERIALIZED VIEW refresh_view_2s_3 WITH (REFRESH EVERY '2 seconds') AS SELECT DISTINCT(x) FROM refresh_table;
  189. > CREATE MATERIALIZED VIEW refresh_view_at_3 WITH (REFRESH AT mz_now()::string::int8) AS SELECT DISTINCT(x) FROM refresh_table;
  190. > CREATE MATERIALIZED VIEW refresh_view_late_3 WITH (REFRESH AT mz_now()::string::int8 + 86400000) AS SELECT DISTINCT(x) FROM refresh_table;
  191. """,
  192. ]
  193. ]
  194. def validate(self) -> Testdrive:
  195. return Testdrive(
  196. dedent(
  197. """
  198. > INSERT INTO refresh_table VALUES (4);
  199. > SELECT * FROM refresh_view_2s_1
  200. 1
  201. 2
  202. 3
  203. 4
  204. > SELECT * FROM refresh_view_2s_2
  205. 1
  206. 2
  207. 3
  208. 4
  209. > SELECT * FROM refresh_view_2s_3
  210. 1
  211. 2
  212. 3
  213. 4
  214. > SELECT * FROM refresh_view_at_1
  215. 1
  216. > SELECT * FROM refresh_view_at_2
  217. 1
  218. 2
  219. > SELECT * FROM refresh_view_at_3
  220. 1
  221. 2
  222. 3
  223. $ set-regex match=(s\\d+|\\d{13}|[ ]{12}0|u\\d{1,3}|\\(\\d+-\\d\\d-\\d\\d\\s\\d\\d:\\d\\d:\\d\\d\\.\\d\\d\\d\\)|\\(\\d+\\)) replacement=<>
  224. >[version<13900] EXPLAIN TIMESTAMP FOR SELECT * FROM refresh_view_late_1
  225. " query timestamp: <> <>\\n oracle read timestamp: <> <>\\nlargest not in advance of upper: <> <>\\n upper:[<> <>]\\n since:[<> <>]\\n can respond immediately: false\\n timeline: Some(EpochMilliseconds)\\n session wall time: <> <>\\n\\nsource materialize.public.refresh_view_late_1 (<>, storage):\\n read frontier:[<> <>]\\n write frontier:[<> <>]\\n"
  226. >[version>=13900] EXPLAIN TIMESTAMP FOR SELECT * FROM refresh_view_late_1
  227. " query timestamp: <> <>\\n oracle read timestamp: <> <>\\nlargest not in advance of upper: <> <>\\n upper:[<> <>]\\n since:[<> <>]\\n can respond immediately: false\\n timeline: Some(EpochMilliseconds)\\n session wall time: <> <>\\n\\nsource materialize.public.refresh_view_late_1 (<>, storage):\\n read frontier:[<> <>]\\n write frontier:[<> <>]\\n\\nbinding constraints:\\nlower:\\n (StorageInput([User<>])): [<> <>]\\n"
  228. >[version<13900] EXPLAIN TIMESTAMP FOR SELECT * FROM refresh_view_late_2
  229. " query timestamp: <> <>\\n oracle read timestamp: <> <>\\nlargest not in advance of upper: <> <>\\n upper:[<> <>]\\n since:[<> <>]\\n can respond immediately: false\\n timeline: Some(EpochMilliseconds)\\n session wall time: <> <>\\n\\nsource materialize.public.refresh_view_late_2 (<>, storage):\\n read frontier:[<> <>]\\n write frontier:[<> <>]\\n"
  230. >[version>=13900] EXPLAIN TIMESTAMP FOR SELECT * FROM refresh_view_late_2
  231. " query timestamp: <> <>\\n oracle read timestamp: <> <>\\nlargest not in advance of upper: <> <>\\n upper:[<> <>]\\n since:[<> <>]\\n can respond immediately: false\\n timeline: Some(EpochMilliseconds)\\n session wall time: <> <>\\n\\nsource materialize.public.refresh_view_late_2 (<>, storage):\\n read frontier:[<> <>]\\n write frontier:[<> <>]\\n\\nbinding constraints:\\nlower:\\n (StorageInput([User<>])): [<> <>]\\n"
  232. >[version<13900] EXPLAIN TIMESTAMP FOR SELECT * FROM refresh_view_late_3
  233. " query timestamp: <> <>\\n oracle read timestamp: <> <>\\nlargest not in advance of upper: <> <>\\n upper:[<> <>]\\n since:[<> <>]\\n can respond immediately: false\\n timeline: Some(EpochMilliseconds)\\n session wall time: <> <>\\n\\nsource materialize.public.refresh_view_late_3 (<>, storage):\\n read frontier:[<> <>]\\n write frontier:[<> <>]\\n"
  234. >[version>=13900] EXPLAIN TIMESTAMP FOR SELECT * FROM refresh_view_late_3
  235. " query timestamp: <> <>\\n oracle read timestamp: <> <>\\nlargest not in advance of upper: <> <>\\n upper:[<> <>]\\n since:[<> <>]\\n can respond immediately: false\\n timeline: Some(EpochMilliseconds)\\n session wall time: <> <>\\n\\nsource materialize.public.refresh_view_late_3 (<>, storage):\\n read frontier:[<> <>]\\n write frontier:[<> <>]\\n\\nbinding constraints:\\nlower:\\n (StorageInput([User<>])): [<> <>]\\n"
  236. """
  237. )
  238. )
  239. def remove_target_cluster_from_explain(sql: str) -> str:
  240. return re.sub(r"\n\s*Target cluster: \w+\n", "", sql)