activerecord-sqlserver-adapter 6.0.0.rc1 → 6.1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +26 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +29 -0
  5. data/CHANGELOG.md +18 -23
  6. data/Gemfile +11 -5
  7. data/Guardfile +9 -8
  8. data/README.md +32 -3
  9. data/RUNNING_UNIT_TESTS.md +1 -1
  10. data/Rakefile +12 -16
  11. data/VERSION +1 -1
  12. data/activerecord-sqlserver-adapter.gemspec +4 -4
  13. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +0 -4
  14. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +1 -4
  15. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +3 -13
  16. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +8 -5
  17. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +2 -3
  18. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +2 -3
  19. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -4
  20. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +58 -43
  21. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +7 -12
  22. data/lib/active_record/connection_adapters/sqlserver/errors.rb +0 -3
  23. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +15 -15
  24. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -3
  25. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +16 -10
  26. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +131 -105
  27. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +6 -8
  28. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +2 -2
  29. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +1 -1
  30. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +25 -7
  31. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +1 -5
  32. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +6 -10
  33. data/lib/active_record/connection_adapters/sqlserver/type.rb +36 -35
  34. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +0 -2
  35. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +0 -2
  36. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +0 -2
  37. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +2 -2
  38. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +0 -2
  39. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +2 -3
  40. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +2 -3
  41. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +0 -2
  42. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +0 -2
  43. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +0 -2
  44. data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
  45. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +0 -2
  46. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +0 -2
  47. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +0 -1
  48. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +0 -2
  49. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +0 -2
  50. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +0 -2
  51. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +0 -2
  52. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +0 -2
  53. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +0 -2
  54. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +0 -2
  55. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +2 -3
  56. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +6 -9
  57. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +0 -2
  58. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +0 -2
  59. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +1 -3
  60. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +0 -2
  61. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +0 -2
  62. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +0 -2
  63. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +0 -2
  64. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +1 -2
  65. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +1 -3
  66. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +0 -2
  67. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +1 -3
  68. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +0 -2
  69. data/lib/active_record/connection_adapters/sqlserver/utils.rb +8 -11
  70. data/lib/active_record/connection_adapters/sqlserver/version.rb +0 -2
  71. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +170 -136
  72. data/lib/active_record/connection_adapters/sqlserver_column.rb +16 -1
  73. data/lib/active_record/sqlserver_base.rb +9 -15
  74. data/lib/active_record/tasks/sqlserver_database_tasks.rb +36 -39
  75. data/lib/activerecord-sqlserver-adapter.rb +1 -1
  76. data/lib/arel/visitors/sqlserver.rb +126 -50
  77. data/lib/arel_sqlserver.rb +2 -2
  78. data/test/cases/adapter_test_sqlserver.rb +203 -190
  79. data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
  80. data/test/cases/change_column_null_test_sqlserver.rb +12 -12
  81. data/test/cases/coerced_tests.rb +656 -318
  82. data/test/cases/column_test_sqlserver.rb +285 -284
  83. data/test/cases/connection_test_sqlserver.rb +15 -20
  84. data/test/cases/disconnected_test_sqlserver.rb +39 -0
  85. data/test/cases/execute_procedure_test_sqlserver.rb +26 -19
  86. data/test/cases/fetch_test_sqlserver.rb +14 -22
  87. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +12 -18
  88. data/test/cases/helper_sqlserver.rb +13 -15
  89. data/test/cases/in_clause_test_sqlserver.rb +36 -9
  90. data/test/cases/index_test_sqlserver.rb +13 -15
  91. data/test/cases/json_test_sqlserver.rb +23 -25
  92. data/test/cases/lateral_test_sqlserver.rb +35 -0
  93. data/test/cases/migration_test_sqlserver.rb +71 -26
  94. data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
  95. data/test/cases/order_test_sqlserver.rb +57 -53
  96. data/test/cases/pessimistic_locking_test_sqlserver.rb +25 -33
  97. data/test/cases/primary_keys_test_sqlserver.rb +103 -0
  98. data/test/cases/rake_test_sqlserver.rb +33 -46
  99. data/test/cases/schema_dumper_test_sqlserver.rb +121 -108
  100. data/test/cases/schema_test_sqlserver.rb +18 -26
  101. data/test/cases/scratchpad_test_sqlserver.rb +2 -4
  102. data/test/cases/showplan_test_sqlserver.rb +24 -33
  103. data/test/cases/specific_schema_test_sqlserver.rb +66 -65
  104. data/test/cases/transaction_test_sqlserver.rb +16 -19
  105. data/test/cases/trigger_test_sqlserver.rb +12 -12
  106. data/test/cases/utils_test_sqlserver.rb +68 -70
  107. data/test/cases/uuid_test_sqlserver.rb +11 -13
  108. data/test/debug.rb +6 -6
  109. data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
  110. data/test/migrations/create_clients_and_change_column_null.rb +1 -1
  111. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +2 -4
  112. data/test/models/sqlserver/booking.rb +1 -1
  113. data/test/models/sqlserver/customers_view.rb +1 -1
  114. data/test/models/sqlserver/dollar_table_name.rb +1 -1
  115. data/test/models/sqlserver/edge_schema.rb +1 -3
  116. data/test/models/sqlserver/fk_has_fk.rb +1 -1
  117. data/test/models/sqlserver/fk_has_pk.rb +1 -1
  118. data/test/models/sqlserver/natural_pk_data.rb +2 -2
  119. data/test/models/sqlserver/natural_pk_int_data.rb +1 -1
  120. data/test/models/sqlserver/no_pk_data.rb +1 -1
  121. data/test/models/sqlserver/object_default.rb +1 -1
  122. data/test/models/sqlserver/quoted_table.rb +2 -2
  123. data/test/models/sqlserver/quoted_view_1.rb +1 -1
  124. data/test/models/sqlserver/quoted_view_2.rb +1 -1
  125. data/test/models/sqlserver/sst_memory.rb +1 -1
  126. data/test/models/sqlserver/sst_string_collation.rb +3 -0
  127. data/test/models/sqlserver/string_default.rb +1 -1
  128. data/test/models/sqlserver/string_defaults_big_view.rb +1 -1
  129. data/test/models/sqlserver/string_defaults_view.rb +1 -1
  130. data/test/models/sqlserver/tinyint_pk.rb +1 -1
  131. data/test/models/sqlserver/trigger.rb +2 -2
  132. data/test/models/sqlserver/trigger_history.rb +1 -1
  133. data/test/models/sqlserver/upper.rb +1 -1
  134. data/test/models/sqlserver/uppered.rb +1 -1
  135. data/test/models/sqlserver/uuid.rb +1 -1
  136. data/test/schema/sqlserver_specific_schema.rb +36 -21
  137. data/test/support/coerceable_test_sqlserver.rb +1 -4
  138. data/test/support/connection_reflection.rb +1 -2
  139. data/test/support/core_ext/query_cache.rb +1 -1
  140. data/test/support/load_schema_sqlserver.rb +3 -5
  141. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
  142. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
  143. data/test/support/minitest_sqlserver.rb +1 -1
  144. data/test/support/paths_sqlserver.rb +9 -11
  145. data/test/support/rake_helpers.rb +12 -10
  146. data/test/support/sql_counter_sqlserver.rb +14 -16
  147. data/test/support/test_in_memory_oltp.rb +7 -7
  148. metadata +31 -11
  149. data/.travis.yml +0 -23
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLServer
6
+ module Type
7
+ class DecimalWithoutScale < ActiveRecord::Type::DecimalWithoutScale
8
+ def sqlserver_type
9
+ "decimal".yield_self do |type|
10
+ type += "(#{precision.to_i},0)" if precision
11
+ type
12
+ end
13
+ end
14
+
15
+ def type_cast_for_schema(value)
16
+ value.is_a?(BigDecimal) ? value.to_s : value.inspect
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class Float < ActiveRecord::Type::Float
8
-
9
8
  def type
10
9
  :float
11
10
  end
@@ -13,7 +12,6 @@ module ActiveRecord
13
12
  def sqlserver_type
14
13
  "float"
15
14
  end
16
-
17
15
  end
18
16
  end
19
17
  end
@@ -5,11 +5,9 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class Integer < ActiveRecord::Type::Integer
8
-
9
8
  def sqlserver_type
10
9
  "int"
11
10
  end
12
-
13
11
  end
14
12
  end
15
13
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class Json < ActiveRecord::Type::Json
8
-
9
8
  end
10
9
  end
11
10
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class Money < Decimal
8
-
9
8
  def initialize(**args)
10
9
  super
11
10
  @precision = 19
@@ -19,7 +18,6 @@ module ActiveRecord
19
18
  def sqlserver_type
20
19
  "money"
21
20
  end
22
-
23
21
  end
24
22
  end
25
23
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class Real < Float
8
-
9
8
  def type
10
9
  :real
11
10
  end
@@ -13,7 +12,6 @@ module ActiveRecord
13
12
  def sqlserver_type
14
13
  "real"
15
14
  end
16
-
17
15
  end
18
16
  end
19
17
  end
@@ -5,11 +5,9 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class SmallInteger < Integer
8
-
9
8
  def sqlserver_type
10
9
  "smallint"
11
10
  end
12
-
13
11
  end
14
12
  end
15
13
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class SmallMoney < Money
8
-
9
8
  def initialize(**args)
10
9
  super
11
10
  @precision = 10
@@ -19,7 +18,6 @@ module ActiveRecord
19
18
  def sqlserver_type
20
19
  "smallmoney"
21
20
  end
22
-
23
21
  end
24
22
  end
25
23
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class SmallDateTime < DateTime
8
-
9
8
  def type
10
9
  :smalldatetime
11
10
  end
@@ -23,7 +22,6 @@ module ActiveRecord
23
22
  def apply_seconds_precision(value)
24
23
  value.change usec: 0 if value
25
24
  end
26
-
27
25
  end
28
26
  end
29
27
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class String < ActiveRecord::Type::String
8
-
9
8
  def changed_in_place?(raw_old_value, new_value)
10
9
  if raw_old_value.is_a?(Data)
11
10
  raw_old_value.value != new_value
@@ -13,7 +12,6 @@ module ActiveRecord
13
12
  super
14
13
  end
15
14
  end
16
-
17
15
  end
18
16
  end
19
17
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class Text < VarcharMax
8
-
9
8
  def type
10
9
  :text_basic
11
10
  end
@@ -13,7 +12,6 @@ module ActiveRecord
13
12
  def sqlserver_type
14
13
  "text"
15
14
  end
16
-
17
15
  end
18
16
  end
19
17
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class Time < ActiveRecord::Type::Time
8
-
9
8
  include TimeValueFractional2
10
9
 
11
10
  def serialize(value)
@@ -13,7 +12,7 @@ module ActiveRecord
13
12
  return value unless value.acts_like?(:time)
14
13
 
15
14
  time = "#{value.to_s(:_sqlserver_time)}.#{quote_fractional(value)}"
16
-
15
+
17
16
  Data.new time, self
18
17
  end
19
18
 
@@ -38,6 +37,7 @@ module ActiveRecord
38
37
  def cast_value(value)
39
38
  value = super
40
39
  return if value.blank?
40
+
41
41
  value = value.change year: 2000, month: 01, day: 01
42
42
  apply_seconds_precision(value)
43
43
  end
@@ -45,7 +45,6 @@ module ActiveRecord
45
45
  def fractional_scale
46
46
  precision
47
47
  end
48
-
49
48
  end
50
49
  end
51
50
  end
@@ -4,18 +4,18 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLServer
6
6
  module Type
7
-
8
7
  module TimeValueFractional
9
-
10
8
  private
11
9
 
12
10
  def apply_seconds_precision(value)
13
11
  return value if !value.respond_to?(fractional_property) || value.send(fractional_property).zero?
12
+
14
13
  value.change fractional_property => seconds_precision(value)
15
14
  end
16
15
 
17
16
  def seconds_precision(value)
18
17
  return 0 if fractional_scale == 0
18
+
19
19
  seconds = value.send(fractional_property).to_f / fractional_operator.to_f
20
20
  seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).round(fractional_scale)
21
21
  (seconds * fractional_operator).round(0).to_i
@@ -23,9 +23,10 @@ module ActiveRecord
23
23
 
24
24
  def quote_fractional(value)
25
25
  return 0 if fractional_scale == 0
26
+
26
27
  frac_seconds = seconds_precision(value)
27
28
  seconds = (frac_seconds.to_f / fractional_operator.to_f).round(fractional_scale)
28
- seconds.to_d.to_s.split('.').last.to(fractional_scale-1)
29
+ seconds.to_d.to_s.split(".").last.to(fractional_scale - 1)
29
30
  end
30
31
 
31
32
  def fractional_property
@@ -37,7 +38,7 @@ module ActiveRecord
37
38
  end
38
39
 
39
40
  def fractional_operator
40
- 10 ** fractional_digits
41
+ 10**fractional_digits
41
42
  end
42
43
 
43
44
  def fractional_precision
@@ -47,11 +48,9 @@ module ActiveRecord
47
48
  def fractional_scale
48
49
  3
49
50
  end
50
-
51
51
  end
52
52
 
53
53
  module TimeValueFractional2
54
-
55
54
  include TimeValueFractional
56
55
 
57
56
  private
@@ -82,11 +81,9 @@ module ActiveRecord
82
81
  end
83
82
 
84
83
  def fractional_scale_max
85
- ('9' * fractional_scale) + ('0' * (fractional_digits - fractional_scale))
84
+ ("9" * fractional_scale) + ("0" * (fractional_digits - fractional_scale))
86
85
  end
87
-
88
86
  end
89
-
90
87
  end
91
88
  end
92
89
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class Timestamp < Binary
8
-
9
8
  def type
10
9
  :ss_timestamp
11
10
  end
@@ -13,7 +12,6 @@ module ActiveRecord
13
12
  def sqlserver_type
14
13
  "timestamp"
15
14
  end
16
-
17
15
  end
18
16
  end
19
17
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class TinyInteger < Integer
8
-
9
8
  def sqlserver_type
10
9
  "tinyint"
11
10
  end
@@ -19,7 +18,6 @@ module ActiveRecord
19
18
  def min_value
20
19
  0
21
20
  end
22
-
23
21
  end
24
22
  end
25
23
  end
@@ -5,18 +5,16 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class UnicodeChar < UnicodeString
8
-
9
8
  def type
10
9
  :nchar
11
10
  end
12
11
 
13
12
  def sqlserver_type
14
- 'nchar'.yield_self do |type|
13
+ "nchar".yield_self do |type|
15
14
  type += "(#{limit})" if limit
16
15
  type
17
16
  end
18
17
  end
19
-
20
18
  end
21
19
  end
22
20
  end
@@ -5,8 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class UnicodeString < String
8
-
9
-
10
8
  end
11
9
  end
12
10
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class UnicodeText < UnicodeVarcharMax
8
-
9
8
  def type
10
9
  :ntext
11
10
  end
@@ -13,7 +12,6 @@ module ActiveRecord
13
12
  def sqlserver_type
14
13
  "ntext"
15
14
  end
16
-
17
15
  end
18
16
  end
19
17
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class UnicodeVarchar < UnicodeChar
8
-
9
8
  def initialize(**args)
10
9
  super
11
10
  @limit = 4000 if @limit.to_i == 0
@@ -21,7 +20,6 @@ module ActiveRecord
21
20
  type
22
21
  end
23
22
  end
24
-
25
23
  end
26
24
  end
27
25
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class UnicodeVarcharMax < UnicodeVarchar
8
-
9
8
  def initialize(**args)
10
9
  super
11
10
  @limit = 2_147_483_647
@@ -18,7 +17,6 @@ module ActiveRecord
18
17
  def sqlserver_type
19
18
  "nvarchar(max)"
20
19
  end
21
-
22
20
  end
23
21
  end
24
22
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class Uuid < String
8
-
9
8
  ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x
10
9
 
11
10
  alias_method :serialize, :deserialize
@@ -20,6 +19,7 @@ module ActiveRecord
20
19
 
21
20
  def serialize(value)
22
21
  return unless value
22
+
23
23
  Data.new super, self
24
24
  end
25
25
 
@@ -30,7 +30,6 @@ module ActiveRecord
30
30
  def quoted(value)
31
31
  Utils.quote_string_single(value) if value
32
32
  end
33
-
34
33
  end
35
34
  end
36
35
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class Varbinary < Binary
8
-
9
8
  def initialize(**args)
10
9
  super
11
10
  @limit = 8000 if @limit.to_i == 0
@@ -16,12 +15,11 @@ module ActiveRecord
16
15
  end
17
16
 
18
17
  def sqlserver_type
19
- 'varbinary'.yield_self do |type|
18
+ "varbinary".yield_self do |type|
20
19
  type += "(#{limit})" if limit
21
20
  type
22
21
  end
23
22
  end
24
-
25
23
  end
26
24
  end
27
25
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module Type
7
7
  class VarbinaryMax < Varbinary
8
-
9
8
  def initialize(**args)
10
9
  super
11
10
  @limit = 2_147_483_647
@@ -18,7 +17,6 @@ module ActiveRecord
18
17
  def sqlserver_type
19
18
  "varbinary(max)"
20
19
  end
21
-
22
20
  end
23
21
  end
24
22
  end