arel_toolkit 0.2.0 → 0.3.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/.travis.yml +8 -0
  4. data/CHANGELOG.md +56 -7
  5. data/Gemfile.lock +54 -1
  6. data/Guardfile +19 -12
  7. data/README.md +56 -2
  8. data/Rakefile +8 -0
  9. data/arel_toolkit.gemspec +6 -0
  10. data/bin/console +1 -0
  11. data/lib/arel/extensions/assignment.rb +22 -0
  12. data/lib/arel/extensions/at_time_zone.rb +30 -0
  13. data/lib/arel/extensions/contained_within_equals.rb +10 -0
  14. data/lib/arel/extensions/contains.rb +2 -2
  15. data/lib/arel/extensions/contains_equals.rb +10 -0
  16. data/lib/arel/extensions/delete_manager.rb +9 -0
  17. data/lib/arel/extensions/delete_statement.rb +7 -0
  18. data/lib/arel/extensions/distinct_from.rb +3 -16
  19. data/lib/arel/extensions/equality.rb +2 -4
  20. data/lib/arel/extensions/extract_from.rb +32 -0
  21. data/lib/arel/extensions/insert_manager.rb +5 -0
  22. data/lib/arel/extensions/insert_statement.rb +10 -3
  23. data/lib/arel/extensions/json_get_field.rb +10 -0
  24. data/lib/arel/extensions/json_get_object.rb +10 -0
  25. data/lib/arel/extensions/json_path_get_field.rb +10 -0
  26. data/lib/arel/extensions/json_path_get_object.rb +10 -0
  27. data/lib/arel/extensions/jsonb_all_key_exists.rb +10 -0
  28. data/lib/arel/extensions/jsonb_any_key_exists.rb +10 -0
  29. data/lib/arel/extensions/jsonb_key_exists.rb +10 -0
  30. data/lib/arel/extensions/named_argument.rb +29 -0
  31. data/lib/arel/extensions/not_distinct_from.rb +3 -16
  32. data/lib/arel/extensions/not_equal.rb +2 -4
  33. data/lib/arel/extensions/overlap.rb +1 -1
  34. data/lib/arel/extensions/overlaps.rb +40 -0
  35. data/lib/arel/extensions/overlay.rb +44 -0
  36. data/lib/arel/extensions/position.rb +32 -0
  37. data/lib/arel/extensions/select_manager.rb +9 -0
  38. data/lib/arel/extensions/substring.rb +38 -0
  39. data/lib/arel/extensions/transaction.rb +50 -0
  40. data/lib/arel/extensions/trim.rb +36 -0
  41. data/lib/arel/extensions/type_cast.rb +4 -0
  42. data/lib/arel/{sql_to_arel → extensions}/unbound_column_reference.rb +1 -1
  43. data/lib/arel/extensions/update_manager.rb +9 -0
  44. data/lib/arel/extensions/update_statement.rb +8 -0
  45. data/lib/arel/extensions/variable_set.rb +46 -0
  46. data/lib/arel/extensions/variable_show.rb +31 -0
  47. data/lib/arel/extensions.rb +26 -0
  48. data/lib/arel/middleware/chain.rb +97 -0
  49. data/lib/arel/middleware/postgresql_adapter.rb +26 -0
  50. data/lib/arel/middleware/railtie.rb +11 -0
  51. data/lib/arel/middleware.rb +23 -0
  52. data/lib/arel/sql_formatter.rb +59 -0
  53. data/lib/arel/sql_to_arel/error.rb +6 -0
  54. data/lib/arel/sql_to_arel/pg_query_visitor/frame_options.rb +112 -0
  55. data/lib/arel/sql_to_arel/pg_query_visitor.rb +271 -52
  56. data/lib/arel/sql_to_arel/result.rb +17 -0
  57. data/lib/arel/sql_to_arel.rb +4 -3
  58. data/lib/arel_toolkit/version.rb +1 -1
  59. data/lib/arel_toolkit.rb +2 -0
  60. metadata +120 -4
  61. data/lib/arel/sql_to_arel/frame_options.rb +0 -110
@@ -6,7 +6,7 @@
6
6
  # rubocop:disable Metrics/ParameterLists
7
7
 
8
8
  require 'pg_query'
9
- require_relative './frame_options'
9
+ require_relative './pg_query_visitor/frame_options'
10
10
 
11
11
  module Arel
12
12
  module SqlToArel
@@ -15,13 +15,21 @@ module Arel
15
15
  MIN_MAX_EXPR = 'MinMaxExpr'.freeze
16
16
 
17
17
  attr_reader :object
18
+ attr_reader :binds
19
+ attr_reader :sql
18
20
 
19
- def accept(sql)
21
+ def accept(sql, binds = [])
20
22
  tree = PgQuery.parse(sql).tree
21
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/33' if tree.length > 1
22
23
 
23
- @object = tree.first
24
- visit object, :top
24
+ @object = tree
25
+ @binds = binds
26
+ @sql = sql
27
+
28
+ Result.new visit(object, :top)
29
+ rescue ::StandardError => e
30
+ raise e.class, e.message, e.backtrace if e.is_a?(Arel::SqlToArel::Error)
31
+
32
+ boom e.message, e.backtrace
25
33
  end
26
34
 
27
35
  private
@@ -72,7 +80,7 @@ module Arel
72
80
  Arel::Nodes::NullIf.new(left, right)
73
81
 
74
82
  when PgQuery::AEXPR_OF
75
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/34'
83
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/34'
76
84
 
77
85
  when PgQuery::AEXPR_IN
78
86
  left = visit(lexpr)
@@ -91,7 +99,7 @@ module Arel
91
99
  escape = nil
92
100
 
93
101
  if right.is_a?(Array)
94
- raise "Don't know how to handle length `#{right.length}`" if right.length != 2
102
+ boom "Don't know how to handle length `#{right.length}`" if right.length != 2
95
103
 
96
104
  right, escape = right
97
105
  end
@@ -110,7 +118,7 @@ module Arel
110
118
  escape = nil
111
119
 
112
120
  if right.is_a?(Array)
113
- raise "Don't know how to handle length `#{right.length}`" if right.length != 2
121
+ boom "Don't know how to handle length `#{right.length}`" if right.length != 2
114
122
 
115
123
  right, escape = right
116
124
  end
@@ -129,7 +137,7 @@ module Arel
129
137
  escape = nil
130
138
 
131
139
  if right.is_a?(Array)
132
- raise "Don't know how to handle length `#{right.length}`" if right.length != 2
140
+ boom "Don't know how to handle length `#{right.length}`" if right.length != 2
133
141
 
134
142
  right, escape = right
135
143
  end
@@ -164,10 +172,10 @@ module Arel
164
172
  Arel::Nodes::NotBetweenSymmetric.new left, Arel::Nodes::And.new(right)
165
173
 
166
174
  when PgQuery::AEXPR_PAREN
167
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/35'
175
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/35'
168
176
 
169
177
  else
170
- raise "Unknown Expr type `#{kind}`"
178
+ boom "Unknown Expr type `#{kind}`"
171
179
  end
172
180
  end
173
181
 
@@ -176,7 +184,7 @@ module Arel
176
184
  end
177
185
 
178
186
  def visit_A_Indirection(arg:, indirection:)
179
- Arel::Nodes::Indirection.new(visit(arg, :operator), visit(indirection, :operator))
187
+ Arel::Nodes::Indirection.new(visit(arg), visit(indirection, :operator))
180
188
  end
181
189
 
182
190
  def visit_A_Star
@@ -205,7 +213,7 @@ module Arel
205
213
  Arel::Nodes::Not.new(args)
206
214
 
207
215
  else
208
- raise "? Boolop -> #{boolop}"
216
+ boom "? Boolop -> #{boolop}"
209
217
  end
210
218
 
211
219
  if context
@@ -238,7 +246,7 @@ module Arel
238
246
  Arel::Nodes::NotEqual.new(arg, Arel::Nodes::Unknown.new)
239
247
 
240
248
  else
241
- raise '?'
249
+ boom '?'
242
250
  end
243
251
  end
244
252
 
@@ -269,8 +277,19 @@ module Arel
269
277
  Arel::Nodes::Coalesce.new args
270
278
  end
271
279
 
272
- def visit_ColumnRef(context = nil, fields:)
273
- UnboundColumnReference.new visit(fields, context).join('.')
280
+ def visit_ColumnRef(fields:)
281
+ visited_fields = visit(fields)
282
+
283
+ if fields.length == 2
284
+ table_reference, column_reference = fields
285
+ table_reference = visit(table_reference, :operator)
286
+ table = Arel::Table.new(table_reference)
287
+
288
+ column_reference = visit(column_reference, :operator)
289
+ table[column_reference]
290
+ else
291
+ Arel::Nodes::UnboundColumnReference.new visited_fields.join('.')
292
+ end
274
293
  end
275
294
 
276
295
  def visit_CommonTableExpr(ctename:, ctequery:)
@@ -283,6 +302,15 @@ module Arel
283
302
  Arel::Nodes::CurrentOfExpression.new(cursor_name)
284
303
  end
285
304
 
305
+ def visit_DefElem(defname:, arg:, defaction:)
306
+ case defname
307
+ when 'savepoint_name'
308
+ visit(arg)
309
+ else
310
+ boom "Unknown defname `#{defname}` with defaction `#{defaction}`"
311
+ end
312
+ end
313
+
286
314
  def visit_DeleteStmt(
287
315
  relation:,
288
316
  using_clause: nil,
@@ -356,8 +384,48 @@ module Arel
356
384
  when [PG_CATALOG, 'similar_escape']
357
385
  args
358
386
 
387
+ when [PG_CATALOG, 'date_part']
388
+ field, expression = args
389
+ [Arel::Nodes::ExtractFrom.new(expression, field)]
390
+
391
+ when [PG_CATALOG, 'timezone']
392
+ timezone, expression = args
393
+
394
+ [Arel::Nodes::AtTimeZone.new(maybe_add_grouping(expression), timezone)]
395
+
396
+ # https://www.postgresql.org/docs/10/functions-string.html
397
+ when [PG_CATALOG, 'position']
398
+ string, substring = args
399
+ [Arel::Nodes::Position.new(substring, string)]
400
+
401
+ when [PG_CATALOG, 'overlay']
402
+ string, substring, start, length = args
403
+ [Arel::Nodes::Overlay.new(string, substring, start, length)]
404
+
405
+ when [PG_CATALOG, 'ltrim']
406
+ string, substring = args
407
+ [Arel::Nodes::Trim.new('leading', substring, string)]
408
+
409
+ when [PG_CATALOG, 'rtrim']
410
+ string, substring = args
411
+ [Arel::Nodes::Trim.new('trailing', substring, string)]
412
+
413
+ when [PG_CATALOG, 'btrim']
414
+ string, substring = args
415
+ [Arel::Nodes::Trim.new('both', substring, string)]
416
+
417
+ when [PG_CATALOG, 'substring']
418
+ string, pattern, escape = args
419
+ [Arel::Nodes::Substring.new(string, pattern, escape)]
420
+
421
+ when [PG_CATALOG, 'overlaps']
422
+ start1, end1, start2, end2 = args
423
+ [Arel::Nodes::Overlaps.new(start1, end1, start2, end2)]
424
+
359
425
  else
360
- raise "Don't know how to handle `#{function_names}`" if function_names.length > 1
426
+ if function_names.length > 1
427
+ boom "Don't know how to handle function names `#{function_names}`"
428
+ end
361
429
 
362
430
  Arel::Nodes::NamedFunction.new(function_names.first, args)
363
431
  end
@@ -383,8 +451,8 @@ module Arel
383
451
  end
384
452
 
385
453
  def visit_IndexElem(name:, ordering:, nulls_ordering:)
386
- raise "Unknown ordering `#{ordering}`" unless ordering.zero?
387
- raise "Unknown nulls ordering `#{ordering}`" unless nulls_ordering.zero?
454
+ boom "Unknown ordering `#{ordering}`" unless ordering.zero?
455
+ boom "Unknown nulls ordering `#{ordering}`" unless nulls_ordering.zero?
388
456
 
389
457
  Arel.sql(name)
390
458
  end
@@ -418,7 +486,7 @@ module Arel
418
486
  end
419
487
 
420
488
  insert_statement.returning = visit(returning_list, :select)
421
- insert_statement.on_conflict = visit(on_conflict_clause) if on_conflict_clause
489
+ insert_statement.conflict = visit(on_conflict_clause) if on_conflict_clause
422
490
  insert_manager
423
491
  end
424
492
 
@@ -481,10 +549,17 @@ module Arel
481
549
  when 1
482
550
  Arel::Nodes::Least.new visit(args)
483
551
  else
484
- raise "Unknown Op -> #{op}"
552
+ boom "Unknown Op -> #{op}"
485
553
  end
486
554
  end
487
555
 
556
+ def visit_NamedArgExpr(arg:, name:, argnumber:)
557
+ arg = visit(arg)
558
+ boom '' unless argnumber == -1
559
+
560
+ Arel::Nodes::NamedArgument.new(name, arg)
561
+ end
562
+
488
563
  def visit_Null(**_)
489
564
  Arel.sql 'NULL'
490
565
  end
@@ -509,16 +584,18 @@ module Arel
509
584
  conflict
510
585
  end
511
586
 
512
- def visit_ParamRef(_args)
513
- Arel::Nodes::BindParam.new(nil)
587
+ def visit_ParamRef(number:)
588
+ value = (binds[number - 1] unless binds.empty?)
589
+
590
+ Arel::Nodes::BindParam.new(value)
514
591
  end
515
592
 
516
593
  def visit_RangeFunction(is_rowsfrom:, functions:, lateral: false, ordinality: false)
517
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/36' unless is_rowsfrom == true
594
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/36' unless is_rowsfrom == true
518
595
 
519
596
  functions = functions.map do |function_array|
520
597
  function, empty_value = function_array
521
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/37' unless empty_value.nil?
598
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/37' unless empty_value.nil?
522
599
 
523
600
  visit(function)
524
601
  end
@@ -545,8 +622,8 @@ module Arel
545
622
  )
546
623
  end
547
624
 
548
- def visit_RawStmt(context, stmt:)
549
- visit(stmt, context)
625
+ def visit_RawStmt(context, **args)
626
+ visit(args.fetch(:stmt), context)
550
627
  end
551
628
 
552
629
  def visit_ResTarget(context, val: nil, name: nil)
@@ -562,12 +639,13 @@ module Arel
562
639
  when :insert
563
640
  name
564
641
  when :update
565
- Arel::Nodes::Equality.new(
566
- Arel.sql(visit_String(str: name)),
567
- visit(val),
568
- )
642
+ relation = nil
643
+ column = Arel::Attribute.new(relation, name)
644
+ value = visit(val)
645
+
646
+ Nodes::Assignment.new(Nodes::UnqualifiedColumn.new(column), value)
569
647
  else
570
- raise "Unknown context `#{context}`"
648
+ boom "Unknown context `#{context}`"
571
649
  end
572
650
  end
573
651
 
@@ -600,11 +678,27 @@ module Arel
600
678
  select_statement = select_manager.ast
601
679
 
602
680
  froms, join_sources = generate_sources(from_clause)
681
+ if froms
682
+ froms = froms.first if froms.length == 1
683
+ select_core.froms = froms
684
+ end
685
+
603
686
  select_core.from = froms if froms
604
687
  select_core.source.right = join_sources
605
688
 
606
689
  select_core.projections = visit(target_list, :select) if target_list
607
- select_core.wheres = [visit(where_clause)] if where_clause
690
+
691
+ if where_clause
692
+ where_clause = visit(where_clause)
693
+ where_clause = if where_clause.is_a?(Arel::Nodes::And)
694
+ where_clause
695
+ else
696
+ Arel::Nodes::And.new([where_clause])
697
+ end
698
+
699
+ select_core.wheres = [where_clause]
700
+ end
701
+
608
702
  select_core.groups = visit(group_clause) if group_clause
609
703
  select_core.havings = [visit(having_clause)] if having_clause
610
704
  select_core.windows = visit(window_clause) if window_clause
@@ -616,7 +710,7 @@ module Arel
616
710
  elsif distinct_clause.nil?
617
711
  select_core.set_quantifier = nil
618
712
  else
619
- raise "Unknown distinct clause `#{distinct_clause}`"
713
+ boom "Unknown distinct clause `#{distinct_clause}`"
620
714
  end
621
715
 
622
716
  select_statement.limit = ::Arel::Nodes::Limit.new visit(limit_count) if limit_count
@@ -636,8 +730,10 @@ module Arel
636
730
  Arel.sql(value.to_sql)
637
731
  when Arel::Nodes::BindParam
638
732
  value
733
+ when Arel::Nodes::Quoted
734
+ value.value
639
735
  else
640
- raise "Unknown value `#{value}`"
736
+ boom "Unknown value `#{value}`"
641
737
  end
642
738
  end
643
739
  end
@@ -667,7 +763,7 @@ module Arel
667
763
  end
668
764
  else
669
765
  # https://www.postgresql.org/docs/10/queries-union.html
670
- raise "Unknown combining queries op `#{op}`"
766
+ boom "Unknown combining queries op `#{op}`"
671
767
  end
672
768
 
673
769
  unless union.nil?
@@ -723,7 +819,7 @@ module Arel
723
819
  when :operator
724
820
  str
725
821
  when :const
726
- Arel.sql "'#{str}'"
822
+ Arel::Nodes.build_quoted str
727
823
  else
728
824
  "\"#{str}\""
729
825
  end
@@ -735,7 +831,7 @@ module Arel
735
831
  operator = if oper_name
736
832
  operator = visit(oper_name, :operator)
737
833
  if operator.length > 1
738
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/39'
834
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/39'
739
835
  end
740
836
 
741
837
  operator.first
@@ -744,24 +840,51 @@ module Arel
744
840
  generate_sublink(sub_link_type, subselect, testexpr, operator)
745
841
  end
746
842
 
843
+ def visit_TransactionStmt(kind:, options: nil)
844
+ Arel::Nodes::Transaction.new(
845
+ kind,
846
+ (visit(options) if options),
847
+ )
848
+ end
849
+
747
850
  def visit_TypeCast(arg:, type_name:)
748
851
  arg = visit(arg)
749
852
  type_name = visit(type_name)
750
853
 
751
- Arel::Nodes::TypeCast.new(arg, type_name)
854
+ Arel::Nodes::TypeCast.new(maybe_add_grouping(arg), type_name)
752
855
  end
753
856
 
754
- def visit_TypeName(names:, typemod:)
857
+ def visit_TypeName(names:, typemod:, array_bounds: [])
858
+ array_bounds = visit(array_bounds)
859
+
755
860
  names = names.map do |name|
756
861
  visit(name, :operator)
757
862
  end
758
863
 
759
864
  names = names.reject { |name| name == PG_CATALOG }
760
865
 
761
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/40' if typemod != -1
762
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/41' if names.length > 1
866
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/40' if typemod != -1
867
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/41' if names.length > 1
868
+ if array_bounds != [] && array_bounds != [-1]
869
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/86'
870
+ end
763
871
 
764
- names.first
872
+ type_name = names.first
873
+ type_name = case type_name
874
+ when 'int4'
875
+ 'integer'
876
+ when 'float4'
877
+ 'real'
878
+ when 'float8'
879
+ 'double precision'
880
+ when 'timestamptz'
881
+ 'timestamp with time zone'
882
+ else
883
+ type_name
884
+ end
885
+
886
+ type_name << '[]' if array_bounds == [-1]
887
+ type_name
765
888
  end
766
889
 
767
890
  def visit_UpdateStmt(
@@ -786,6 +909,19 @@ module Arel
786
909
  update_manager
787
910
  end
788
911
 
912
+ def visit_VariableSetStmt(kind:, name:, args: [], is_local: false)
913
+ Arel::Nodes::VariableSet.new(
914
+ kind,
915
+ visit(args),
916
+ name,
917
+ is_local,
918
+ )
919
+ end
920
+
921
+ def visit_VariableShowStmt(name:)
922
+ Arel::Nodes::VariableShow.new(name)
923
+ end
924
+
789
925
  def visit_WindowDef(
790
926
  partition_clause: [],
791
927
  order_clause: [],
@@ -819,13 +955,20 @@ module Arel
819
955
  end
820
956
 
821
957
  def generate_operator(left, right, operator)
958
+ left = maybe_add_grouping(left)
959
+ right = maybe_add_grouping(right)
960
+
822
961
  case operator
823
962
 
824
963
  # https://www.postgresql.org/docs/10/functions-math.html
825
964
  when '+'
826
965
  Arel::Nodes::Addition.new(left, right)
827
966
  when '-'
828
- Arel::Nodes::Subtraction.new(left, right)
967
+ if left.nil?
968
+ Arel::Nodes::UnaryOperation.new(:'-', right)
969
+ else
970
+ Arel::Nodes::Subtraction.new(left, right)
971
+ end
829
972
  when '*'
830
973
  Arel::Nodes::Multiplication.new(left, right)
831
974
  when '/'
@@ -849,9 +992,17 @@ module Arel
849
992
  when '|'
850
993
  Arel::Nodes::BitwiseOr.new(left, right)
851
994
  when '#'
852
- Arel::Nodes::BitwiseXor.new(left, right)
995
+ if left.nil?
996
+ Arel::Nodes::UnaryOperation.new(:'#', right)
997
+ else
998
+ Arel::Nodes::BitwiseXor.new(left, right)
999
+ end
853
1000
  when '~'
854
- Arel::Nodes::BitwiseNot.new(right)
1001
+ if left.nil?
1002
+ Arel::Nodes::BitwiseNot.new(right)
1003
+ else
1004
+ Arel::Nodes::Regexp.new(left, right, true)
1005
+ end
855
1006
  when '<<'
856
1007
  Arel::Nodes::BitwiseShiftLeft.new(left, right)
857
1008
  when '>>'
@@ -881,8 +1032,48 @@ module Arel
881
1032
  when '||'
882
1033
  Arel::Nodes::Concat.new(left, right)
883
1034
 
1035
+ # https://www.postgresql.org/docs/9.3/functions-net.html
1036
+ when '<<='
1037
+ Arel::Nodes::ContainedWithinEquals.new(left, right)
1038
+ when '>>='
1039
+ Arel::Nodes::ContainsEquals.new(left, right)
1040
+
1041
+ # https://www.postgresql.org/docs/9.4/functions-json.html
1042
+ when '->'
1043
+ Arel::Nodes::JsonGetObject.new(left, right)
1044
+ when '->>'
1045
+ Arel::Nodes::JsonGetField.new(left, right)
1046
+ when '#>'
1047
+ Arel::Nodes::JsonPathGetObject.new(left, right)
1048
+ when '#>>'
1049
+ Arel::Nodes::JsonPathGetField.new(left, right)
1050
+
1051
+ # https://www.postgresql.org/docs/9.4/functions-json.html#FUNCTIONS-JSONB-OP-TABLE
1052
+ when '?'
1053
+ Arel::Nodes::JsonbKeyExists.new(left, right)
1054
+ when '?|'
1055
+ if left.nil?
1056
+ Arel::Nodes::UnaryOperation.new(:'?|', right)
1057
+ else
1058
+ Arel::Nodes::JsonbAnyKeyExists.new(left, right)
1059
+ end
1060
+ when '?&'
1061
+ Arel::Nodes::JsonbAllKeyExists.new(left, right)
1062
+
1063
+ # https://www.postgresql.org/docs/9.3/functions-matching.html#FUNCTIONS-POSIX-TABLE
1064
+ when '~*'
1065
+ Arel::Nodes::Regexp.new(left, right, false)
1066
+ when '!~'
1067
+ Arel::Nodes::NotRegexp.new(left, right, true)
1068
+ when '!~*'
1069
+ Arel::Nodes::NotRegexp.new(left, right, false)
1070
+
884
1071
  else
885
- raise "Unknown operator `#{operator}`"
1072
+ if left.nil?
1073
+ Arel::Nodes::UnaryOperation.new(operator, right)
1074
+ else
1075
+ Arel::Nodes::InfixOperation.new(operator, left, right)
1076
+ end
886
1077
  end
887
1078
  end
888
1079
 
@@ -972,27 +1163,55 @@ module Arel
972
1163
  generate_operator(testexpr, Arel::Nodes::All.new(subselect), operator)
973
1164
 
974
1165
  when PgQuery::SUBLINK_TYPE_ANY
975
- generate_operator(testexpr, Arel::Nodes::Any.new(subselect), operator)
1166
+ if operator.nil?
1167
+ Arel::Nodes::In.new(testexpr, subselect)
1168
+ else
1169
+ generate_operator(testexpr, Arel::Nodes::Any.new(subselect), operator)
1170
+ end
976
1171
 
977
1172
  when PgQuery::SUBLINK_TYPE_ROWCOMPARE
978
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/42'
1173
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/42'
979
1174
 
980
1175
  when PgQuery::SUBLINK_TYPE_EXPR
981
1176
  Arel::Nodes::Grouping.new(subselect)
982
1177
 
983
1178
  when PgQuery::SUBLINK_TYPE_MULTIEXPR
984
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/43'
1179
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/43'
985
1180
 
986
1181
  when PgQuery::SUBLINK_TYPE_ARRAY
987
1182
  Arel::Nodes::ArraySubselect.new(subselect)
988
1183
 
989
1184
  when PgQuery::SUBLINK_TYPE_CTE
990
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/44'
1185
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/44'
991
1186
 
992
1187
  else
993
- raise "Unknown sublinktype: #{type}"
1188
+ boom "Unknown sublinktype: #{type}"
994
1189
  end
995
1190
  end
1191
+
1192
+ def maybe_add_grouping(node)
1193
+ case node
1194
+ when Arel::Nodes::Binary
1195
+ Arel::Nodes::Grouping.new(node)
1196
+ else
1197
+ node
1198
+ end
1199
+ end
1200
+
1201
+ def boom(message, backtrace = nil)
1202
+ new_message = <<~STRING
1203
+
1204
+
1205
+ SQL: #{sql}
1206
+ BINDS: #{binds}
1207
+ message: #{message}
1208
+
1209
+ STRING
1210
+
1211
+ raise(Arel::SqlToArel::Error, new_message, backtrace) if backtrace
1212
+
1213
+ raise Arel::SqlToArel::Error, new_message
1214
+ end
996
1215
  end
997
1216
  end
998
1217
  end
@@ -0,0 +1,17 @@
1
+ module Arel
2
+ module SqlToArel
3
+ class Result < Array
4
+ def to_sql
5
+ map(&:to_sql).join('; ')
6
+ end
7
+
8
+ def to_formatted_sql
9
+ map(&:to_formatted_sql).join('; ')
10
+ end
11
+
12
+ def map(&block)
13
+ Result.new super
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,8 +1,9 @@
1
+ require 'arel/sql_to_arel/result'
2
+ require 'arel/sql_to_arel/error'
1
3
  require 'arel/sql_to_arel/pg_query_visitor'
2
- require 'arel/sql_to_arel/unbound_column_reference'
3
4
 
4
5
  module Arel
5
- def self.sql_to_arel(sql)
6
- SqlToArel::PgQueryVisitor.new.accept(sql)
6
+ def self.sql_to_arel(sql, binds: [])
7
+ SqlToArel::PgQueryVisitor.new.accept(sql, binds)
7
8
  end
8
9
  end
@@ -1,3 +1,3 @@
1
1
  module ArelToolkit
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
data/lib/arel_toolkit.rb CHANGED
@@ -2,6 +2,8 @@ require 'arel_toolkit/version'
2
2
  require 'arel'
3
3
  require 'arel/extensions'
4
4
  require 'arel/sql_to_arel'
5
+ require 'arel/middleware'
6
+ require 'arel/sql_formatter'
5
7
 
6
8
  module ArelToolkit
7
9
  end