arel_toolkit 0.3.0 → 0.4.0

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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +3 -0
  3. data/.gitignore +4 -1
  4. data/.rubocop.yml +13 -5
  5. data/.travis.yml +7 -2
  6. data/Appraisals +9 -0
  7. data/CHANGELOG.md +19 -0
  8. data/Gemfile +5 -0
  9. data/Gemfile.lock +22 -5
  10. data/README.md +59 -18
  11. data/arel_toolkit.gemspec +5 -1
  12. data/gemfiles/.bundle/config +2 -0
  13. data/gemfiles/arel_gems.gemfile +10 -0
  14. data/gemfiles/arel_gems.gemfile.lock +274 -0
  15. data/gemfiles/default.gemfile +5 -0
  16. data/gemfiles/default.gemfile.lock +198 -0
  17. data/lib/arel/enhance.rb +16 -0
  18. data/lib/arel/enhance/context_enhancer/arel_table.rb +75 -0
  19. data/lib/arel/enhance/node.rb +189 -0
  20. data/lib/arel/enhance/path.rb +38 -0
  21. data/lib/arel/enhance/path_node.rb +26 -0
  22. data/lib/arel/enhance/query.rb +36 -0
  23. data/lib/arel/enhance/visitor.rb +81 -0
  24. data/lib/arel/extensions.rb +24 -4
  25. data/lib/arel/extensions/active_record_type_caster_map.rb +7 -0
  26. data/lib/arel/extensions/array.rb +2 -9
  27. data/lib/arel/extensions/at_time_zone.rb +10 -3
  28. data/lib/arel/extensions/binary.rb +7 -0
  29. data/lib/arel/extensions/bit_string.rb +2 -9
  30. data/lib/arel/extensions/case.rb +17 -0
  31. data/lib/arel/extensions/conflict.rb +9 -0
  32. data/lib/arel/extensions/contains.rb +27 -5
  33. data/lib/arel/extensions/current_catalog.rb +4 -0
  34. data/lib/arel/extensions/current_date.rb +4 -0
  35. data/lib/arel/extensions/current_of_expression.rb +2 -9
  36. data/lib/arel/extensions/current_role.rb +4 -0
  37. data/lib/arel/extensions/current_row.rb +7 -0
  38. data/lib/arel/extensions/current_schema.rb +4 -0
  39. data/lib/arel/extensions/current_user.rb +4 -0
  40. data/lib/arel/extensions/dealocate.rb +31 -0
  41. data/lib/arel/extensions/default_values.rb +4 -0
  42. data/lib/arel/extensions/delete_manager.rb +22 -6
  43. data/lib/arel/extensions/delete_statement.rb +26 -9
  44. data/lib/arel/extensions/dot.rb +11 -0
  45. data/lib/arel/extensions/extract_from.rb +3 -10
  46. data/lib/arel/extensions/factorial.rb +10 -2
  47. data/lib/arel/extensions/false.rb +7 -0
  48. data/lib/arel/extensions/function.rb +42 -13
  49. data/lib/arel/extensions/indirection.rb +3 -12
  50. data/lib/arel/extensions/infer.rb +6 -6
  51. data/lib/arel/extensions/infix_operation.rb +17 -0
  52. data/lib/arel/extensions/insert_manager.rb +19 -3
  53. data/lib/arel/extensions/insert_statement.rb +30 -11
  54. data/lib/arel/extensions/into.rb +21 -0
  55. data/lib/arel/extensions/named_argument.rb +3 -8
  56. data/lib/arel/extensions/named_function.rb +7 -0
  57. data/lib/arel/extensions/ordering.rb +21 -6
  58. data/lib/arel/extensions/overlaps.rb +9 -0
  59. data/lib/arel/extensions/overlay.rb +9 -0
  60. data/lib/arel/extensions/position.rb +3 -8
  61. data/lib/arel/extensions/prepare.rb +39 -0
  62. data/lib/arel/extensions/row.rb +3 -8
  63. data/lib/arel/extensions/select_core.rb +58 -0
  64. data/lib/arel/extensions/select_manager.rb +22 -6
  65. data/lib/arel/extensions/select_statement.rb +31 -9
  66. data/lib/arel/extensions/session_user.rb +4 -0
  67. data/lib/arel/extensions/set_to_default.rb +4 -0
  68. data/lib/arel/extensions/substring.rb +8 -0
  69. data/lib/arel/extensions/table.rb +43 -10
  70. data/lib/arel/extensions/time_with_precision.rb +6 -0
  71. data/lib/arel/extensions/to_sql.rb +27 -0
  72. data/lib/arel/extensions/transaction.rb +3 -8
  73. data/lib/arel/extensions/tree_manager.rb +10 -0
  74. data/lib/arel/extensions/trim.rb +8 -0
  75. data/lib/arel/extensions/true.rb +7 -0
  76. data/lib/arel/extensions/type_cast.rb +7 -0
  77. data/lib/arel/extensions/unary.rb +7 -0
  78. data/lib/arel/extensions/unary_operation.rb +16 -0
  79. data/lib/arel/extensions/unknown.rb +4 -0
  80. data/lib/arel/extensions/update_manager.rb +22 -6
  81. data/lib/arel/extensions/update_statement.rb +27 -10
  82. data/lib/arel/extensions/user.rb +4 -0
  83. data/lib/arel/extensions/values_list.rb +15 -0
  84. data/lib/arel/extensions/variable_set.rb +9 -0
  85. data/lib/arel/extensions/variable_show.rb +3 -8
  86. data/lib/arel/middleware/chain.rb +1 -5
  87. data/lib/arel/middleware/railtie.rb +10 -0
  88. data/lib/arel/sql_to_arel.rb +6 -3
  89. data/lib/arel/sql_to_arel/pg_query_visitor.rb +43 -15
  90. data/lib/arel/sql_to_arel/pg_query_visitor/frame_options.rb +1 -1
  91. data/lib/arel/sql_to_arel/result.rb +0 -4
  92. data/lib/arel/transformer.rb +7 -0
  93. data/lib/arel/transformer/add_schema_to_table.rb +26 -0
  94. data/lib/arel/transformer/remove_active_record_info.rb +42 -0
  95. data/lib/arel_toolkit.rb +8 -1
  96. data/lib/arel_toolkit/version.rb +1 -1
  97. metadata +81 -8
  98. data/lib/arel/extensions/unbound_column_reference.rb +0 -5
  99. data/lib/arel/sql_formatter.rb +0 -59
@@ -10,4 +10,10 @@ module Arel
10
10
  end
11
11
  end
12
12
  end
13
+
14
+ module Visitors
15
+ class Dot
16
+ alias visit_Arel_Nodes_TimeWithPrecision terminal
17
+ end
18
+ end
13
19
  end
@@ -0,0 +1,27 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Visitors
6
+ class ToSql
7
+ def visit_Arel_Attributes_Attribute(o, collector)
8
+ if o.relation
9
+ join_name = o.relation.table_alias || o.relation.name
10
+ collector << "#{quote_table_name join_name}.#{quote_column_name o.name}"
11
+ else
12
+ visit_Arel_Nodes_UnqualifiedColumn o, collector
13
+ end
14
+ end
15
+
16
+ alias visit_Arel_Attributes_Integer visit_Arel_Attributes_Attribute
17
+ alias visit_Arel_Attributes_Float visit_Arel_Attributes_Attribute
18
+ alias visit_Arel_Attributes_Decimal visit_Arel_Attributes_Attribute
19
+ alias visit_Arel_Attributes_String visit_Arel_Attributes_Attribute
20
+ alias visit_Arel_Attributes_Time visit_Arel_Attributes_Attribute
21
+ alias visit_Arel_Attributes_Boolean visit_Arel_Attributes_Attribute
22
+ end
23
+ end
24
+ end
25
+
26
+ # rubocop:enable Naming/MethodName
27
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -6,14 +6,9 @@
6
6
  module Arel
7
7
  module Nodes
8
8
  # https://www.postgresql.org/docs/8.3/tutorial-transactions.html
9
- class Transaction < Arel::Nodes::Node
10
- attr_reader :type
11
- attr_reader :options
12
-
13
- def initialize(type, options)
14
- @type = type
15
- @options = options
16
- end
9
+ class Transaction < Arel::Nodes::Binary
10
+ alias type left
11
+ alias options right
17
12
  end
18
13
  end
19
14
 
@@ -0,0 +1,10 @@
1
+ module Arel
2
+ class TreeManager
3
+ # Iterate through AST, nodes will be yielded depth-first
4
+ def each(&block)
5
+ return enum_for(:each) unless block_given?
6
+
7
+ ::Arel::Visitors::DepthFirst.new(block).accept ast
8
+ end
9
+ end
10
+ end
@@ -29,6 +29,14 @@ module Arel
29
29
  collector << ')'
30
30
  end
31
31
  end
32
+
33
+ class Dot
34
+ def visit_Arel_Nodes_Trim(o)
35
+ visit_edge o, 'type'
36
+ visit_edge o, 'substring'
37
+ visit_edge o, 'string'
38
+ end
39
+ end
32
40
  end
33
41
  end
34
42
 
@@ -0,0 +1,7 @@
1
+ module Arel
2
+ module Visitors
3
+ class Dot
4
+ alias visit_Arel_Nodes_True terminal
5
+ end
6
+ end
7
+ end
@@ -27,6 +27,13 @@ module Arel
27
27
  collector << o.type_name
28
28
  end
29
29
  end
30
+
31
+ class Dot
32
+ def visit_Arel_Nodes_TypeCast(o)
33
+ visit_edge(o, 'arg')
34
+ visit_edge(o, 'type_name')
35
+ end
36
+ end
30
37
  end
31
38
  end
32
39
 
@@ -0,0 +1,7 @@
1
+ module Arel
2
+ module Visitors
3
+ class Dot
4
+ alias visit_Arel_Nodes_Unary unary
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,16 @@
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_UnaryOperation(o)
8
+ visit_edge o, 'operator'
9
+ visit_edge o, 'expr'
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ # rubocop:enable Naming/MethodName
16
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -13,6 +13,10 @@ module Arel
13
13
  collector << 'UNKNOWN'
14
14
  end
15
15
  end
16
+
17
+ class Dot
18
+ alias visit_Arel_Nodes_Unknown terminal
19
+ end
16
20
  end
17
21
  end
18
22
 
@@ -1,9 +1,25 @@
1
- Arel::UpdateManager.class_eval do
2
- def ==(other)
3
- @ast == other.ast && @ctx == other.ctx
4
- end
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ class UpdateManager < Arel::TreeManager
6
+ def ==(other)
7
+ other.is_a?(self.class) && @ast == other.ast && @ctx == other.ctx
8
+ end
9
+
10
+ protected
5
11
 
6
- protected
12
+ attr_reader :ctx
13
+ end
7
14
 
8
- attr_reader :ctx
15
+ module Visitors
16
+ class Dot
17
+ def visit_Arel_UpdateManager(o)
18
+ visit_edge o, 'ast'
19
+ end
20
+ end
21
+ end
9
22
  end
23
+
24
+ # rubocop:enable Naming/MethodName
25
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -3,19 +3,22 @@
3
3
 
4
4
  module Arel
5
5
  module Nodes
6
- # https://www.postgresql.org/docs/10/sql-update.html
7
- Arel::Nodes::UpdateStatement.class_eval do
8
- attr_accessor :with
9
- attr_accessor :froms
10
- attr_accessor :returning
6
+ class UpdateStatement
7
+ module UpdateStatementExtension
8
+ # https://www.postgresql.org/docs/10/sql-update.html
9
+ attr_accessor :with
10
+ attr_accessor :froms
11
+ attr_accessor :returning
11
12
 
12
- alias_method :old_initialize, :initialize
13
- def initialize
14
- old_initialize
13
+ def initialize
14
+ super
15
15
 
16
- @froms = []
17
- @returning = []
16
+ @froms = []
17
+ @returning = []
18
+ end
18
19
  end
20
+
21
+ prepend UpdateStatementExtension
19
22
  end
20
23
  end
21
24
 
@@ -64,6 +67,20 @@ module Arel
64
67
  # rubocop:enable Metrics/CyclomaticComplexity
65
68
  # rubocop:enable Metrics/PerceivedComplexity
66
69
  end
70
+
71
+ class Dot
72
+ module UpdateStatementExtension
73
+ def visit_Arel_Nodes_UpdateStatement(o)
74
+ super
75
+
76
+ visit_edge o, 'with'
77
+ visit_edge o, 'froms'
78
+ visit_edge o, 'returning'
79
+ end
80
+ end
81
+
82
+ prepend UpdateStatementExtension
83
+ end
67
84
  end
68
85
  end
69
86
 
@@ -13,6 +13,10 @@ module Arel
13
13
  collector << 'user'
14
14
  end
15
15
  end
16
+
17
+ class Dot
18
+ alias visit_Arel_Nodes_User terminal
19
+ end
16
20
  end
17
21
  end
18
22
 
@@ -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_ValuesList(o)
8
+ visit_edge o, 'rows'
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ # rubocop:enable Naming/MethodName
15
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -39,6 +39,15 @@ module Arel
39
39
  end
40
40
  end
41
41
  end
42
+
43
+ class Dot
44
+ def visit_Arel_Nodes_VariableSet(o)
45
+ visit_edge o, 'type'
46
+ visit_edge o, 'args'
47
+ visit_edge o, 'name'
48
+ visit_edge o, 'local'
49
+ end
50
+ end
42
51
  end
43
52
  end
44
53
 
@@ -4,12 +4,7 @@
4
4
  module Arel
5
5
  module Nodes
6
6
  # https://www.postgresql.org/docs/9.1/sql-show.html
7
- class VariableShow < Arel::Nodes::Node
8
- attr_reader :name
9
-
10
- def initialize(name)
11
- @name = name
12
- end
7
+ class VariableShow < Arel::Nodes::Unary
13
8
  end
14
9
  end
15
10
 
@@ -17,10 +12,10 @@ module Arel
17
12
  class ToSql
18
13
  def visit_Arel_Nodes_VariableShow(o, collector)
19
14
  collector << 'SHOW '
20
- collector << if o.name == 'timezone'
15
+ collector << if o.expr == 'timezone'
21
16
  'TIME ZONE'
22
17
  else
23
- o.name
18
+ o.expr
24
19
  end
25
20
  end
26
21
  end
@@ -14,7 +14,7 @@ module Arel
14
14
 
15
15
  internal_middleware.each do |middleware_item|
16
16
  result = result.map do |arel|
17
- middleware_item.call(arel, updated_context)
17
+ middleware_item.call(arel, updated_context.dup)
18
18
  end
19
19
  end
20
20
 
@@ -88,10 +88,6 @@ module Arel
88
88
  ensure
89
89
  Arel::Middleware.current_chain = previous_chain
90
90
  end
91
-
92
- def current_chain
93
- Arel::Middleware.current_chain
94
- end
95
91
  end
96
92
  end
97
93
  end
@@ -1,5 +1,15 @@
1
1
  module Arel
2
2
  module Middleware
3
+ if defined? Rails::Railtie
4
+ class Railtie < Rails::Railtie
5
+ initializer 'arel.middleware.insert' do
6
+ ActiveSupport.on_load :active_record do
7
+ Arel::Middleware::Railtie.insert_postgresql
8
+ end
9
+ end
10
+ end
11
+ end
12
+
3
13
  class Railtie
4
14
  def self.insert_postgresql
5
15
  ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(
@@ -1,8 +1,11 @@
1
- require 'arel/sql_to_arel/result'
2
- require 'arel/sql_to_arel/error'
3
- require 'arel/sql_to_arel/pg_query_visitor'
1
+ require_relative './sql_to_arel/result'
2
+ require_relative './sql_to_arel/error'
3
+ require_relative './sql_to_arel/pg_query_visitor'
4
4
 
5
5
  module Arel
6
+ module SqlToArel
7
+ end
8
+
6
9
  def self.sql_to_arel(sql, binds: [])
7
10
  SqlToArel::PgQueryVisitor.new.accept(sql, binds)
8
11
  end
@@ -278,17 +278,21 @@ module Arel
278
278
  end
279
279
 
280
280
  def visit_ColumnRef(fields:)
281
- visited_fields = visit(fields)
281
+ case fields.length
282
+ when 1
283
+ visited_field = visit(fields[0], :operator)
284
+ Arel::Nodes::UnqualifiedColumn.new Arel::Attribute.new(nil, visited_field)
282
285
 
283
- if fields.length == 2
286
+ when 2
284
287
  table_reference, column_reference = fields
285
288
  table_reference = visit(table_reference, :operator)
286
289
  table = Arel::Table.new(table_reference)
287
290
 
288
291
  column_reference = visit(column_reference, :operator)
289
292
  table[column_reference]
293
+
290
294
  else
291
- Arel::Nodes::UnboundColumnReference.new visited_fields.join('.')
295
+ raise 'Can\'t deal with column refs that have more than 2 fields'
292
296
  end
293
297
  end
294
298
 
@@ -423,11 +427,20 @@ module Arel
423
427
  [Arel::Nodes::Overlaps.new(start1, end1, start2, end2)]
424
428
 
425
429
  else
426
- if function_names.length > 1
427
- boom "Don't know how to handle function names `#{function_names}`"
428
- end
430
+ case function_names.length
431
+ when 2
432
+ if function_names.first == PG_CATALOG
433
+ boom "Missing postgres function `#{function_names.last}`"
434
+ end
429
435
 
430
- Arel::Nodes::NamedFunction.new(function_names.first, args)
436
+ func = Arel::Nodes::NamedFunction.new(function_names.last, args)
437
+ func.schema_name = function_names.first
438
+ func
439
+ when 1
440
+ Arel::Nodes::NamedFunction.new(function_names.first, args)
441
+ else
442
+ boom "Don't know how to handle function names length `#{function_names.length}`"
443
+ end
431
444
  end
432
445
 
433
446
  func.distinct = (agg_distinct.nil? ? false : true) unless func.is_a?(::Array)
@@ -444,17 +457,16 @@ module Arel
444
457
  end
445
458
 
446
459
  def visit_InferClause(conname: nil, index_elems: nil)
447
- infer = Arel::Nodes::Infer.new
448
- infer.name = Arel.sql(conname) if conname
449
- infer.indexes = visit(index_elems) if index_elems
450
- infer
460
+ left = Arel.sql(conname) if conname
461
+ right = visit(index_elems) if index_elems
462
+ Arel::Nodes::Infer.new left, right
451
463
  end
452
464
 
453
465
  def visit_IndexElem(name:, ordering:, nulls_ordering:)
454
466
  boom "Unknown ordering `#{ordering}`" unless ordering.zero?
455
467
  boom "Unknown nulls ordering `#{ordering}`" unless nulls_ordering.zero?
456
468
 
457
- Arel.sql(name)
469
+ Arel.sql visit_String(str: name)
458
470
  end
459
471
 
460
472
  def visit_InsertStmt(
@@ -494,6 +506,12 @@ module Arel
494
506
  ival
495
507
  end
496
508
 
509
+ def visit_IntoClause(rel:, on_commit:)
510
+ raise "Unknown on_commit `#{on_commit}`" unless on_commit.zero?
511
+
512
+ Arel::Nodes::Into.new(visit(rel))
513
+ end
514
+
497
515
  def visit_JoinExpr(jointype:, is_natural: nil, larg:, rarg:, quals: nil)
498
516
  join_class = case jointype
499
517
  when 0
@@ -531,12 +549,12 @@ module Arel
531
549
  1 => 'FOR KEY SHARE',
532
550
  2 => 'FOR SHARE',
533
551
  3 => 'FOR NO KEY UPDATE',
534
- 4 => 'FOR UPDATE'
552
+ 4 => 'FOR UPDATE',
535
553
  }.fetch(strength)
536
554
  wait_policy_clause = {
537
555
  0 => '',
538
556
  1 => ' SKIP LOCKED',
539
- 2 => ' NOWAIT'
557
+ 2 => ' NOWAIT',
540
558
  }.fetch(wait_policy)
541
559
 
542
560
  Arel::Nodes::Lock.new Arel.sql("#{strength_clause}#{wait_policy_clause}")
@@ -669,6 +687,7 @@ module Arel
669
687
  op:,
670
688
  window_clause: nil,
671
689
  values_lists: nil,
690
+ into_clause: nil,
672
691
  all: nil,
673
692
  larg: nil,
674
693
  rarg: nil
@@ -702,6 +721,7 @@ module Arel
702
721
  select_core.groups = visit(group_clause) if group_clause
703
722
  select_core.havings = [visit(having_clause)] if having_clause
704
723
  select_core.windows = visit(window_clause) if window_clause
724
+ select_core.into = visit(into_clause) if into_clause
705
725
 
706
726
  if distinct_clause == [nil]
707
727
  select_core.set_quantifier = Arel::Nodes::Distinct.new
@@ -726,7 +746,7 @@ module Arel
726
746
  value
727
747
  when Integer
728
748
  Arel.sql(value.to_s)
729
- when Arel::Nodes::TypeCast
749
+ when Arel::Nodes::TypeCast, Arel::Nodes::UnqualifiedColumn
730
750
  Arel.sql(value.to_sql)
731
751
  when Arel::Nodes::BindParam
732
752
  value
@@ -1077,6 +1097,14 @@ module Arel
1077
1097
  end
1078
1098
  end
1079
1099
 
1100
+ def visit_DeallocateStmt(name: nil)
1101
+ Arel::Nodes::Dealocate.new name
1102
+ end
1103
+
1104
+ def visit_PrepareStmt(name:, argtypes: nil, query:)
1105
+ Arel::Nodes::Prepare.new name, argtypes && visit(argtypes), visit(query)
1106
+ end
1107
+
1080
1108
  def visit(attribute, context = nil)
1081
1109
  return attribute.map { |attr| visit(attr, context) } if attribute.is_a? Array
1082
1110