arel_toolkit 0.4.0 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/coverage.yml +48 -0
  3. data/.github/workflows/test.yml +65 -0
  4. data/.gitignore +6 -1
  5. data/Appraisals +4 -0
  6. data/CHANGELOG.md +87 -7
  7. data/Gemfile.lock +50 -39
  8. data/Guardfile +4 -0
  9. data/README.md +25 -11
  10. data/Rakefile +11 -1
  11. data/arel_toolkit.gemspec +10 -5
  12. data/benchmark.rb +54 -0
  13. data/ext/pg_result_init/extconf.rb +52 -0
  14. data/ext/pg_result_init/pg_result_init.c +138 -0
  15. data/ext/pg_result_init/pg_result_init.h +6 -0
  16. data/gemfiles/active_record_6.gemfile +7 -0
  17. data/gemfiles/active_record_6.gemfile.lock +210 -0
  18. data/gemfiles/arel_gems.gemfile.lock +28 -18
  19. data/gemfiles/default.gemfile.lock +30 -20
  20. data/lib/arel/enhance.rb +1 -0
  21. data/lib/arel/enhance/context_enhancer/arel_table.rb +18 -1
  22. data/lib/arel/enhance/node.rb +71 -28
  23. data/lib/arel/enhance/query.rb +2 -0
  24. data/lib/arel/enhance/query_methods.rb +23 -0
  25. data/lib/arel/enhance/visitor.rb +19 -3
  26. data/lib/arel/extensions.rb +8 -2
  27. data/lib/arel/extensions/active_model_attribute_with_cast_value.rb +22 -0
  28. data/lib/arel/extensions/active_record_relation_query_attribute.rb +22 -0
  29. data/lib/arel/extensions/active_record_type_caster_connection.rb +7 -0
  30. data/lib/arel/extensions/attributes_attribute.rb +47 -0
  31. data/lib/arel/extensions/bind_param.rb +15 -0
  32. data/lib/arel/extensions/coalesce.rb +17 -3
  33. data/lib/arel/extensions/delete_statement.rb +20 -15
  34. data/lib/arel/extensions/exists.rb +59 -0
  35. data/lib/arel/extensions/function.rb +3 -2
  36. data/lib/arel/extensions/greatest.rb +17 -3
  37. data/lib/arel/extensions/infer.rb +1 -1
  38. data/lib/arel/extensions/insert_statement.rb +3 -3
  39. data/lib/arel/extensions/least.rb +17 -3
  40. data/lib/arel/extensions/node.rb +10 -0
  41. data/lib/arel/extensions/range_function.rb +10 -2
  42. data/lib/arel/extensions/select_core.rb +22 -7
  43. data/lib/arel/extensions/top.rb +8 -0
  44. data/lib/arel/extensions/tree_manager.rb +5 -0
  45. data/lib/arel/extensions/update_statement.rb +9 -23
  46. data/lib/arel/middleware.rb +5 -1
  47. data/lib/arel/middleware/active_record_extension.rb +13 -0
  48. data/lib/arel/middleware/cache_accessor.rb +35 -0
  49. data/lib/arel/middleware/chain.rb +110 -31
  50. data/lib/arel/middleware/database_executor.rb +77 -0
  51. data/lib/arel/middleware/no_op_cache.rb +9 -0
  52. data/lib/arel/middleware/postgresql_adapter.rb +41 -5
  53. data/lib/arel/middleware/railtie.rb +6 -2
  54. data/lib/arel/middleware/result.rb +170 -0
  55. data/lib/arel/middleware/to_sql_executor.rb +15 -0
  56. data/lib/arel/middleware/to_sql_middleware.rb +33 -0
  57. data/lib/arel/sql_to_arel/pg_query_visitor.rb +34 -33
  58. data/lib/arel/sql_to_arel/result.rb +19 -2
  59. data/lib/arel/transformer.rb +2 -1
  60. data/lib/arel/transformer/prefix_schema_name.rb +183 -0
  61. data/lib/arel/transformer/remove_active_record_info.rb +2 -4
  62. data/lib/arel/transformer/replace_table_with_subquery.rb +31 -0
  63. data/lib/arel_toolkit.rb +7 -1
  64. data/lib/arel_toolkit/version.rb +1 -1
  65. metadata +101 -37
  66. data/.travis.yml +0 -34
  67. data/lib/arel/extensions/generate_series.rb +0 -9
  68. data/lib/arel/extensions/rank.rb +0 -9
  69. data/lib/arel/transformer/add_schema_to_table.rb +0 -26
@@ -27,6 +27,8 @@ module Arel
27
27
  matches? object_attribute_value, test_value
28
28
  end
29
29
  end
30
+ when Arel::Enhance::QueryMethods::QueryMethod
31
+ test.matches?(object)
30
32
  else
31
33
  object == test
32
34
  end
@@ -0,0 +1,23 @@
1
+ module Arel
2
+ module Enhance
3
+ module QueryMethods
4
+ class QueryMethod
5
+ attr_reader :subject
6
+
7
+ def initialize(subject)
8
+ @subject = subject
9
+ end
10
+ end
11
+
12
+ class Ancestors < QueryMethod
13
+ def matches?(other)
14
+ other <= subject
15
+ end
16
+ end
17
+
18
+ def self.in_ancestors?(object)
19
+ Ancestors.new(object)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -40,8 +40,10 @@ module Arel
40
40
  end
41
41
  alias visit_Arel_Nodes_And nary
42
42
 
43
- def visit_Hash(_object)
44
- raise 'Hash is not supported'
43
+ def visit_Hash(object)
44
+ object.each do |key, child|
45
+ process_node(child, Arel::Enhance::PathNode.new([:[], key], key))
46
+ end
45
47
  end
46
48
 
47
49
  def visit_Array(object)
@@ -61,9 +63,23 @@ module Arel
61
63
  end
62
64
  end
63
65
 
66
+ # rubocop:disable Metrics/AbcSize
67
+ # arel/lib/arel/visitors/visitor.rb:29
64
68
  def visit(object)
65
- Arel::Visitors::Visitor.instance_method(:visit).bind(self).call(object)
69
+ dispatch_method = dispatch[object.class]
70
+ send dispatch_method, object
71
+ rescue NoMethodError => e
72
+ raise e if respond_to?(dispatch_method, true)
73
+
74
+ superklass = object.class.ancestors.find do |klass|
75
+ respond_to?(dispatch[klass], true)
76
+ end
77
+ raise(TypeError, "Cannot visit #{object.class}") unless superklass
78
+
79
+ dispatch[object.class] = dispatch[superklass]
80
+ retry
66
81
  end
82
+ # rubocop:enable Metrics/AbcSize
67
83
 
68
84
  def current_node
69
85
  @node_stack.last
@@ -21,6 +21,7 @@ require 'arel/extensions/lateral'
21
21
  require 'arel/extensions/range_function'
22
22
  require 'arel/extensions/with_ordinality'
23
23
  require 'arel/extensions/table'
24
+ require 'arel/extensions/attributes_attribute'
24
25
  require 'arel/extensions/row'
25
26
  require 'arel/extensions/ordering'
26
27
  require 'arel/extensions/all'
@@ -63,8 +64,6 @@ require 'arel/extensions/current_of_expression'
63
64
  require 'arel/extensions/delete_statement'
64
65
  require 'arel/extensions/least'
65
66
  require 'arel/extensions/greatest'
66
- require 'arel/extensions/generate_series'
67
- require 'arel/extensions/rank'
68
67
  require 'arel/extensions/coalesce'
69
68
  require 'arel/extensions/not_equal'
70
69
  require 'arel/extensions/equality'
@@ -110,6 +109,13 @@ require 'arel/extensions/to_sql'
110
109
  require 'arel/extensions/prepare'
111
110
  require 'arel/extensions/dealocate'
112
111
  require 'arel/extensions/active_record_type_caster_map'
112
+ require 'arel/extensions/active_record_type_caster_connection'
113
+ require 'arel/extensions/active_record_relation_query_attribute'
114
+ require 'arel/extensions/active_model_attribute_with_cast_value'
115
+ require 'arel/extensions/exists'
116
+ require 'arel/extensions/bind_param'
117
+ require 'arel/extensions/node'
118
+ require 'arel/extensions/top'
113
119
 
114
120
  module Arel
115
121
  module Extensions
@@ -0,0 +1,22 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Visitors
6
+ class Dot
7
+ def visit_ActiveModel_Attribute_WithCastValue(o)
8
+ visit_edge o, 'name'
9
+ visit_edge o, 'value_before_type_cast'
10
+ end
11
+ end
12
+
13
+ class ToSql
14
+ def visit_ActiveModel_Attribute_WithCastValue(_o, collector)
15
+ collector
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ # rubocop:enable Naming/MethodName
22
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -0,0 +1,22 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Visitors
6
+ class Dot
7
+ def visit_ActiveRecord_Relation_QueryAttribute(o)
8
+ visit_edge o, 'name'
9
+ visit_edge o, 'value_before_type_cast'
10
+ end
11
+ end
12
+
13
+ class ToSql
14
+ def visit_ActiveRecord_Relation_QueryAttribute(_o, collector)
15
+ collector
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ # rubocop:enable Naming/MethodName
22
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -0,0 +1,7 @@
1
+ module Arel
2
+ module Visitors
3
+ class Dot
4
+ alias visit_ActiveRecord_TypeCaster_Connection terminal
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,47 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Attributes
6
+ class Attribute
7
+ module AttributeExtension
8
+ # postgres only: https://www.postgresql.org/docs/10/ddl-schemas.html
9
+ attr_accessor :schema_name
10
+ attr_accessor :database
11
+ end
12
+
13
+ prepend AttributeExtension
14
+ end
15
+ end
16
+
17
+ module Visitors
18
+ class ToSql
19
+ module AttributesAttributeExtension
20
+ def visit_Arel_Attributes_Attribute(o, collector)
21
+ collector << "#{quote_table_name(o.database)}." if o.database
22
+ collector << "#{quote_table_name(o.schema_name)}." if o.schema_name
23
+
24
+ super
25
+ end
26
+ end
27
+
28
+ prepend AttributesAttributeExtension
29
+ end
30
+
31
+ class Dot
32
+ module AttributesAttributeExtension
33
+ def visit_Arel_Attributes_Attribute(o)
34
+ super
35
+
36
+ visit_edge o, 'schema_name'
37
+ visit_edge o, 'database'
38
+ end
39
+ end
40
+
41
+ prepend AttributesAttributeExtension
42
+ end
43
+ end
44
+ end
45
+
46
+ # rubocop:enable Naming/MethodName
47
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -0,0 +1,15 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Visitors
6
+ class Dot
7
+ def visit_Arel_Nodes_BindParam(o)
8
+ visit_edge o, 'value'
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ # rubocop:enable Naming/MethodName
15
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -1,9 +1,23 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
1
4
  module Arel
2
5
  module Nodes
3
- class Coalesce < Arel::Nodes::NamedFunction
4
- def initialize(args)
5
- super 'COALESCE', args
6
+ # https://www.postgresql.org/docs/10/functions-conditional.html
7
+ class Coalesce < Arel::Nodes::Unary
8
+ end
9
+ end
10
+
11
+ module Visitors
12
+ class ToSql
13
+ def visit_Arel_Nodes_Coalesce(o, collector)
14
+ collector << 'COALESCE('
15
+ collector = inject_join(o.expr, collector, ', ')
16
+ collector << ')'
6
17
  end
7
18
  end
8
19
  end
9
20
  end
21
+
22
+ # rubocop:enable Naming/MethodName
23
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -9,11 +9,14 @@ module Arel
9
9
  attr_accessor :using
10
10
  attr_accessor :with
11
11
  attr_accessor :returning
12
+ attr_accessor :orders
12
13
 
13
14
  def initialize(relation = nil, wheres = [])
14
15
  super
15
16
 
16
17
  @returning = []
18
+ @orders = []
19
+ @using = []
17
20
  end
18
21
  end
19
22
 
@@ -27,27 +30,29 @@ module Arel
27
30
  def visit_Arel_Nodes_DeleteStatement(o, collector)
28
31
  if o.with
29
32
  collector = visit o.with, collector
30
- collector << SPACE
33
+ collector << ' '
31
34
  end
32
35
 
33
- collector << 'DELETE FROM '
34
- collector = visit o.relation, collector
35
-
36
- if o.using
37
- collector << ' USING '
38
- collector = inject_join o.using, collector, ', '
39
- end
36
+ if Gem.loaded_specs['activerecord'].version >= Gem::Version.new('6.0.0')
37
+ o = prepare_delete_statement(o)
40
38
 
41
- if o.wheres.any?
42
- collector << WHERE
43
- collector = inject_join o.wheres, collector, AND
39
+ if has_join_sources?(o)
40
+ collector << 'DELETE '
41
+ visit o.relation.left, collector
42
+ collector << ' FROM '
43
+ else
44
+ collector << 'DELETE FROM '
45
+ end
46
+ else
47
+ collector << 'DELETE FROM '
44
48
  end
45
49
 
46
- unless o.returning.empty?
47
- collector << ' RETURNING '
48
- collector = inject_join o.returning, collector, ', '
49
- end
50
+ collector = visit o.relation, collector
50
51
 
52
+ collect_nodes_for o.using, collector, ' USING ', ', '
53
+ collect_nodes_for o.wheres, collector, ' WHERE ', ' AND '
54
+ collect_nodes_for o.returning, collector, ' RETURNING ', ', '
55
+ collect_nodes_for o.orders, collector, ' ORDER BY '
51
56
  maybe_visit o.limit, collector
52
57
  end
53
58
  # rubocop:enable Metrics/AbcSize
@@ -0,0 +1,59 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Nodes
6
+ # This is a copy of https://github.com/rails/arel/blob/v9.0.0/lib/arel/nodes/function.rb
7
+ # Only difference is the superclass, because EXISTS is not a function but a subquery expression.
8
+ # Semantic meaning is important when transforming the Arel using the enhanced AST,
9
+ # because EXISTS cannot be processed as a function. For example it does not have a schema
10
+ # like a normal function.
11
+ #
12
+ # To change the superclass we're removing the existing Exists class `Arel::Nodes::Exists`
13
+ # and recreating it extending from `Arel::Nodes::Unary`.
14
+ remove_const(:Exists)
15
+
16
+ # https://www.postgresql.org/docs/10/functions-subquery.html
17
+ class Exists < Arel::Nodes::Unary
18
+ include Arel::Predications
19
+ include Arel::WindowPredications
20
+ include Arel::OrderPredications
21
+ attr_accessor :expressions, :alias, :distinct
22
+
23
+ def initialize(expr, aliaz = nil)
24
+ @expressions = expr
25
+ @alias = aliaz && SqlLiteral.new(aliaz)
26
+ @distinct = false
27
+ end
28
+
29
+ def as(aliaz)
30
+ self.alias = SqlLiteral.new(aliaz)
31
+ self
32
+ end
33
+
34
+ def hash
35
+ [@expressions, @alias, @distinct].hash
36
+ end
37
+
38
+ def eql?(other)
39
+ self.class == other.class &&
40
+ expressions == other.expressions &&
41
+ self.alias == other.alias &&
42
+ distinct == other.distinct
43
+ end
44
+ alias == eql?
45
+ end
46
+ end
47
+
48
+ module Visitors
49
+ class Dot
50
+ def visit_Arel_Nodes_Exists(o)
51
+ visit_edge o, 'expressions'
52
+ visit_edge o, 'alias'
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ # rubocop:enable Naming/MethodName
59
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -46,7 +46,7 @@ module Arel
46
46
  end
47
47
 
48
48
  if o.orders.any?
49
- collector << SPACE unless o.within_group
49
+ collector << ' ' unless o.within_group
50
50
  collector << 'ORDER BY '
51
51
  collector = inject_join o.orders, collector, ', '
52
52
  end
@@ -80,13 +80,14 @@ module Arel
80
80
  visit_edge o, 'filter'
81
81
  visit_edge o, 'within_group'
82
82
  visit_edge o, 'variardic'
83
+ visit_edge o, 'schema_name'
83
84
  end
84
85
 
85
- alias visit_Arel_Nodes_Exists function
86
86
  alias visit_Arel_Nodes_Min function
87
87
  alias visit_Arel_Nodes_Max function
88
88
  alias visit_Arel_Nodes_Avg function
89
89
  alias visit_Arel_Nodes_Sum function
90
+ alias visit_Arel_Nodes_Count function
90
91
  end
91
92
 
92
93
  prepend FunctionExtension
@@ -1,9 +1,23 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
1
4
  module Arel
2
5
  module Nodes
3
- class Greatest < Arel::Nodes::NamedFunction
4
- def initialize(args)
5
- super 'GREATEST', args
6
+ # https://www.postgresql.org/docs/10/functions-conditional.html
7
+ class Greatest < Arel::Nodes::Unary
8
+ end
9
+ end
10
+
11
+ module Visitors
12
+ class ToSql
13
+ def visit_Arel_Nodes_Greatest(o, collector)
14
+ collector << 'GREATEST('
15
+ collector = inject_join(o.expr, collector, ', ')
16
+ collector << ')'
6
17
  end
7
18
  end
8
19
  end
9
20
  end
21
+
22
+ # rubocop:enable Naming/MethodName
23
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -16,7 +16,7 @@ module Arel
16
16
  if o.name
17
17
  collector << 'ON CONSTRAINT '
18
18
  collector << o.left
19
- collector << SPACE
19
+ collector << ' '
20
20
  end
21
21
 
22
22
  if o.right
@@ -5,7 +5,7 @@ module Arel
5
5
  module Nodes
6
6
  class InsertStatement
7
7
  # https://www.postgresql.org/docs/9.5/sql-insert.html
8
- module InsertStatementExtensions
8
+ module InsertStatementExtension
9
9
  attr_accessor :with
10
10
  attr_accessor :conflict
11
11
  attr_accessor :override
@@ -18,7 +18,7 @@ module Arel
18
18
  end
19
19
  end
20
20
 
21
- prepend(InsertStatementExtensions)
21
+ prepend(InsertStatementExtension)
22
22
  end
23
23
  end
24
24
 
@@ -30,7 +30,7 @@ module Arel
30
30
  def visit_Arel_Nodes_InsertStatement(o, collector)
31
31
  if o.with
32
32
  collector = visit o.with, collector
33
- collector << SPACE
33
+ collector << ' '
34
34
  end
35
35
 
36
36
  collector << 'INSERT INTO '