numeric-sum.td 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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. # Note that this test uses an append-only source, so doesn't fully express the
  10. # semantics of summing numeric values because values are non-retractable.
  11. > CREATE TABLE numeric_insertions (a numeric);
  12. > CREATE TABLE numeric_deletions (a numeric);
  13. > CREATE VIEW numeric_values AS
  14. SELECT a FROM numeric_insertions
  15. EXCEPT (SELECT a FROM numeric_deletions);
  16. > CREATE MATERIALIZED VIEW numeric_values_sum AS
  17. SELECT sum(a) AS sum_a FROM numeric_values;
  18. > INSERT INTO numeric_insertions VALUES
  19. ('1.2'), ('2.3'), ('3.4');
  20. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  21. sum_a
  22. ----
  23. 6.9
  24. > INSERT INTO numeric_insertions VALUES ('0.0');
  25. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  26. sum_a
  27. ----
  28. 6.9
  29. > INSERT INTO numeric_insertions VALUES
  30. ('-1.2'), ('-2.3'), ('-3.4');
  31. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  32. sum_a
  33. ----
  34. 0
  35. > INSERT INTO numeric_deletions VALUES
  36. ('1.2'), ('2.3'), ('3.4');
  37. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  38. sum_a
  39. ----
  40. -6.9
  41. > INSERT INTO numeric_deletions VALUES
  42. ('-1.2'), ('-2.3'), ('-3.4');
  43. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  44. sum_a
  45. ----
  46. 0
  47. # sum operation preserves commutativity, even when it appears lost from the
  48. # datum's perspective.
  49. > INSERT INTO numeric_insertions VALUES ('1e38');
  50. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  51. sum_a
  52. ----
  53. 100000000000000000000000000000000000000
  54. > INSERT INTO numeric_insertions VALUES ('9e38');
  55. # When you "fill" >38 digits of precision in the aggregator, signal
  56. # pseudo-overflow with infinity. By returning infinity and preserving the actual
  57. # sum in a larger data type behind the aggregation, we can preserve associativity
  58. # and commutativity by e.g. allowing users to retract values that caused the
  59. # "overflow." We can still continue to aggregate values "behind" this infinity,
  60. # but this is meant to signal to users that they need to start retracting values
  61. # from the aggregation or they risk a panic, which will occur once the
  62. # aggregator's value exceeds its max precision.
  63. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  64. sum_a
  65. ----
  66. Infinity
  67. # Side note that you cannot rescale Infinity
  68. ! SELECT sum_a::numeric(39,1)::text from numeric_values_sum;
  69. contains:numeric field overflow
  70. # Retracting/subtracting values lets you return to a valid state
  71. > INSERT INTO numeric_insertions VALUES ('-9e38');
  72. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  73. sum_a
  74. ----
  75. 100000000000000000000000000000000000000
  76. # Re-enter "overflow" state
  77. > INSERT INTO numeric_deletions VALUES ('-9e38');
  78. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  79. sum_a
  80. ----
  81. Infinity
  82. # If you receive values while the aggregator in this "overflow" state, new
  83. # values still received/tracked.
  84. > INSERT INTO numeric_insertions VALUES ('1e-39');
  85. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  86. sum_a
  87. ----
  88. Infinity
  89. > INSERT INTO numeric_deletions VALUES ('1e38'), ('9e38');
  90. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  91. sum_a
  92. ----
  93. 0.000000000000000000000000000000000000001
  94. # Infinity in this context is signed
  95. > INSERT INTO numeric_insertions VALUES ('-8e38'), ('-7e38');
  96. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  97. sum_a
  98. ----
  99. -Infinity
  100. # Returns to zero
  101. > INSERT INTO numeric_deletions VALUES ('1e-39'), ('-8e38'), ('-7e38');
  102. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  103. sum_a
  104. ----
  105. 0
  106. # Rounded values are still commutative, i.e. rounding is deterministic.
  107. > INSERT INTO numeric_insertions VALUES ('1.23456789e-38'), ('-1.23456789e-38');
  108. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  109. sum_a
  110. ----
  111. 0
  112. # However, sum is not associative from perspective of output
  113. > INSERT INTO numeric_insertions VALUES
  114. ('0.987654321098765432109876543210987654321'),
  115. ('0.87654321098765432109876543210987654321');
  116. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  117. sum_a
  118. ----
  119. 1.86419753208641975320864197532086419753
  120. > INSERT INTO numeric_insertions VALUES
  121. ('-1.86419753208641975320864197532086419753');
  122. # One might expect this to be zero, but there is a remainder from the original
  123. # inputs in the aggregator that isn't visible from the narrower datum.
  124. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  125. sum_a
  126. ----
  127. 0.000000000000000000000000000000000000001
  128. # Test NaN
  129. > INSERT INTO numeric_insertions VALUES ('NaN');
  130. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  131. sum_a
  132. ----
  133. NaN
  134. > INSERT INTO numeric_deletions VALUES ('NaN');
  135. > SELECT sum_a::text AS sum_a FROM numeric_values_sum;
  136. sum_a
  137. ----
  138. 0.000000000000000000000000000000000000001
  139. # Test with specified scale
  140. # - Rescale over sum
  141. > CREATE TABLE numeric_scaled_insertions (a numeric);
  142. > CREATE TABLE numeric_scaled_deletions (a numeric);
  143. > CREATE VIEW numeric_scaled_values AS
  144. SELECT a FROM numeric_scaled_insertions
  145. EXCEPT (SELECT a FROM numeric_scaled_deletions);
  146. > CREATE MATERIALIZED VIEW numeric_scaled_values_sum AS
  147. SELECT sum(a)::numeric(39, 3) AS sum_a FROM numeric_scaled_values;
  148. > INSERT INTO numeric_scaled_insertions VALUES ('1.2'), ('2.3'), ('3.4001');
  149. # Even though the sum is supposed to be rescaled to 3 decimals, the reduction
  150. # before packing the values into a row trims the trailing zeroes. However, the
  151. # rescale still works because the values are rounded to 3 units of scale.
  152. > SELECT sum_a::text AS sum_a FROM numeric_scaled_values_sum;
  153. sum_a
  154. ----
  155. 6.9
  156. # Values < 5e(-scale) round to a version of zero
  157. > INSERT INTO numeric_scaled_insertions VALUES ('0.00009');
  158. > SELECT sum_a::text AS sum_a FROM numeric_scaled_values_sum;
  159. sum_a
  160. ----
  161. 6.9
  162. > INSERT INTO numeric_scaled_insertions VALUES ('0.0005');
  163. > SELECT sum_a::text AS sum_a FROM numeric_scaled_values_sum;
  164. sum_a
  165. ----
  166. 6.901
  167. > INSERT INTO numeric_scaled_insertions VALUES ('1.2345'), ('2.3456'), ('3.4567');
  168. > SELECT sum_a::text AS sum_a FROM numeric_scaled_values_sum;
  169. sum_a
  170. ----
  171. 13.937
  172. # Inputing values that are invalid for the scale generates errors, equivalent to
  173. # overflow
  174. > INSERT INTO numeric_scaled_insertions VALUES ('1e38');
  175. # Note that this error happens inside the view, but outside the aggregation,
  176. # i.e. this is an error caused by a unary function on a scalar value. This
  177. # differs from the class of overflow that generates "Infinity," which occurs
  178. # only in aggregation contexts as a means of preserving commutativity and
  179. # associativity.
  180. ! SELECT sum_a::text AS sum_a FROM numeric_scaled_values_sum;
  181. contains:Evaluation error: numeric field overflow
  182. # Errored state is invertible by reducing aggregated value so it's expressable
  183. # with the provided scale.
  184. > INSERT INTO numeric_scaled_insertions VALUES ('-1e38');
  185. > SELECT sum_a::text AS sum_a FROM numeric_scaled_values_sum;
  186. sum_a
  187. ----
  188. 13.937
  189. # - Rescale values from source
  190. > CREATE TABLE numeric_scaled_input_insertions (a numeric);
  191. > CREATE TABLE numeric_scaled_input_deletions (a numeric);
  192. # Nest the `EXCEPT` in another clause to allow only non-retracted values to get
  193. # rescaled.
  194. > CREATE VIEW numeric_scaled_inputs AS
  195. SELECT a::numeric(38,3) FROM (
  196. SELECT a FROM numeric_scaled_input_insertions
  197. EXCEPT (SELECT a FROM numeric_scaled_input_deletions)
  198. );
  199. > CREATE MATERIALIZED VIEW numeric_scaled_inputs_sum AS
  200. SELECT sum(a) AS sum_a FROM numeric_scaled_inputs;
  201. > INSERT INTO numeric_scaled_input_insertions VALUES ('1.2'), ('2.3'), ('3.4');
  202. > SELECT sum_a::text AS sum_a FROM numeric_scaled_inputs_sum;
  203. sum_a
  204. ----
  205. 6.9
  206. # Inputing values that are invalid for the scale generates errors, equivalent to
  207. # overflow.
  208. > INSERT INTO numeric_scaled_input_insertions VALUES ('1e38');
  209. # Note that this error actually occurs in numeric_scaled_inputs, so is an
  210. # evaluation error, rather than the aggregation returning infinity.
  211. ! SELECT sum_a::text AS sum_a FROM numeric_scaled_inputs_sum;
  212. contains:Evaluation error: numeric field overflow
  213. # However, retracting this values returns us to a good state.
  214. > INSERT INTO numeric_scaled_input_deletions VALUES ('1e38');
  215. > SELECT sum_a::text AS sum_a FROM numeric_scaled_inputs_sum;
  216. sum_a
  217. ----
  218. 6.9