arel_toolkit 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +29 -0
  3. data/.gitignore +5 -0
  4. data/.rubocop.yml +34 -0
  5. data/.ruby-version +1 -1
  6. data/.travis.yml +16 -2
  7. data/CHANGELOG.md +10 -0
  8. data/Gemfile +1 -1
  9. data/Gemfile.lock +94 -1
  10. data/Guardfile +42 -0
  11. data/README.md +23 -4
  12. data/Rakefile +3 -3
  13. data/arel_toolkit.gemspec +32 -15
  14. data/bin/console +4 -7
  15. data/lib/arel/extensions.rb +71 -0
  16. data/lib/arel/extensions/absolute.rb +10 -0
  17. data/lib/arel/extensions/all.rb +23 -0
  18. data/lib/arel/extensions/any.rb +23 -0
  19. data/lib/arel/extensions/array.rb +29 -0
  20. data/lib/arel/extensions/array_subselect.rb +23 -0
  21. data/lib/arel/extensions/between_symmetric.rb +23 -0
  22. data/lib/arel/extensions/bit_string.rb +27 -0
  23. data/lib/arel/extensions/bitwise_xor.rb +10 -0
  24. data/lib/arel/extensions/coalesce.rb +9 -0
  25. data/lib/arel/extensions/conflict.rb +47 -0
  26. data/lib/arel/extensions/contained_by.rb +10 -0
  27. data/lib/arel/extensions/contains.rb +10 -0
  28. data/lib/arel/extensions/cross_join.rb +21 -0
  29. data/lib/arel/extensions/cube_root.rb +10 -0
  30. data/lib/arel/extensions/current_catalog.rb +20 -0
  31. data/lib/arel/extensions/current_date.rb +20 -0
  32. data/lib/arel/extensions/current_of_expression.rb +29 -0
  33. data/lib/arel/extensions/current_role.rb +20 -0
  34. data/lib/arel/extensions/current_schema.rb +20 -0
  35. data/lib/arel/extensions/current_time.rb +22 -0
  36. data/lib/arel/extensions/current_timestamp.rb +22 -0
  37. data/lib/arel/extensions/current_user.rb +20 -0
  38. data/lib/arel/extensions/default_values.rb +21 -0
  39. data/lib/arel/extensions/delete_statement.rb +49 -0
  40. data/lib/arel/extensions/distinct_from.rb +22 -0
  41. data/lib/arel/extensions/equality.rb +30 -0
  42. data/lib/arel/extensions/except_all.rb +21 -0
  43. data/lib/arel/extensions/exponentiation.rb +10 -0
  44. data/lib/arel/extensions/factorial.rb +33 -0
  45. data/lib/arel/extensions/function.rb +68 -0
  46. data/lib/arel/extensions/generate_series.rb +9 -0
  47. data/lib/arel/extensions/greatest.rb +9 -0
  48. data/lib/arel/extensions/indirection.rb +32 -0
  49. data/lib/arel/extensions/infer.rb +35 -0
  50. data/lib/arel/extensions/insert_statement.rb +69 -0
  51. data/lib/arel/extensions/intersect_all.rb +21 -0
  52. data/lib/arel/extensions/lateral.rb +34 -0
  53. data/lib/arel/extensions/least.rb +9 -0
  54. data/lib/arel/extensions/local_time.rb +22 -0
  55. data/lib/arel/extensions/local_timestamp.rb +22 -0
  56. data/lib/arel/extensions/modulo.rb +10 -0
  57. data/lib/arel/extensions/named_function.rb +15 -0
  58. data/lib/arel/extensions/natural_join.rb +21 -0
  59. data/lib/arel/extensions/not_between.rb +22 -0
  60. data/lib/arel/extensions/not_between_symmetric.rb +23 -0
  61. data/lib/arel/extensions/not_distinct_from.rb +22 -0
  62. data/lib/arel/extensions/not_equal.rb +30 -0
  63. data/lib/arel/extensions/not_similar.rb +29 -0
  64. data/lib/arel/extensions/null_if.rb +24 -0
  65. data/lib/arel/extensions/ordering.rb +47 -0
  66. data/lib/arel/extensions/overlap.rb +10 -0
  67. data/lib/arel/extensions/range_function.rb +23 -0
  68. data/lib/arel/extensions/rank.rb +9 -0
  69. data/lib/arel/extensions/row.rb +30 -0
  70. data/lib/arel/extensions/select_statement.rb +26 -0
  71. data/lib/arel/extensions/session_user.rb +20 -0
  72. data/lib/arel/extensions/set_to_default.rb +21 -0
  73. data/lib/arel/extensions/similar.rb +32 -0
  74. data/lib/arel/extensions/square_root.rb +10 -0
  75. data/lib/arel/extensions/table.rb +49 -0
  76. data/lib/arel/extensions/time_with_precision.rb +13 -0
  77. data/lib/arel/extensions/type_cast.rb +30 -0
  78. data/lib/arel/extensions/unknown.rb +20 -0
  79. data/lib/arel/extensions/update_statement.rb +63 -0
  80. data/lib/arel/extensions/user.rb +20 -0
  81. data/lib/arel/extensions/with_ordinality.rb +22 -0
  82. data/lib/arel/sql_to_arel.rb +8 -0
  83. data/lib/arel/sql_to_arel/frame_options.rb +110 -0
  84. data/lib/arel/sql_to_arel/pg_query_visitor.rb +1005 -0
  85. data/lib/arel/sql_to_arel/unbound_column_reference.rb +5 -0
  86. data/lib/arel_toolkit.rb +4 -3
  87. data/lib/arel_toolkit/version.rb +1 -1
  88. metadata +250 -4
@@ -0,0 +1,26 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Nodes
6
+ Arel::Nodes::SelectStatement.class_eval do
7
+ # For INSERT statements
8
+ attr_accessor :values_lists
9
+ attr_accessor :union
10
+ attr_writer :cores
11
+ end
12
+ end
13
+
14
+ module Visitors
15
+ class ToSql
16
+ alias old_visit_Nodes_SelectStatement visit_Arel_Nodes_SelectStatement
17
+ def visit_Arel_Nodes_SelectStatement(o, collector)
18
+ visit(o.union, collector) if o.union
19
+ old_visit_Nodes_SelectStatement(o, collector)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ # rubocop:enable Naming/MethodName
26
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -0,0 +1,20 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Nodes
6
+ class SessionUser < Arel::Nodes::Node
7
+ end
8
+ end
9
+
10
+ module Visitors
11
+ class ToSql
12
+ def visit_Arel_Nodes_SessionUser(_o, collector)
13
+ collector << 'session_user'
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ # rubocop:enable Naming/MethodName
20
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -0,0 +1,21 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Nodes
6
+ # https://www.postgresql.org/docs/9.5/sql-insert.html
7
+ class SetToDefault < Arel::Nodes::Node
8
+ end
9
+ end
10
+
11
+ module Visitors
12
+ class ToSql
13
+ def visit_Arel_Nodes_SetToDefault(_o, collector)
14
+ collector << 'DEFAULT'
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ # rubocop:enable Naming/MethodName
21
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -0,0 +1,32 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Nodes
6
+ # Postgres: https://www.postgresql.org/docs/9/functions-matching.html
7
+ class Similar < Arel::Nodes::Matches
8
+ def initialize(left, right, escape = nil)
9
+ super(left, right, escape, false)
10
+ end
11
+ end
12
+ end
13
+
14
+ module Visitors
15
+ class ToSql
16
+ def visit_Arel_Nodes_Similar(o, collector)
17
+ visit o.left, collector
18
+ collector << ' SIMILAR TO '
19
+ visit o.right, collector
20
+ if o.escape
21
+ collector << ' ESCAPE '
22
+ visit o.escape, collector
23
+ else
24
+ collector
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ # rubocop:enable Naming/MethodName
32
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -0,0 +1,10 @@
1
+ module Arel
2
+ module Nodes
3
+ # https://www.postgresql.org/docs/9.4/functions-math.html
4
+ class SquareRoot < Arel::Nodes::UnaryOperation
5
+ def initialize(operand)
6
+ super('|/', operand)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,49 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+ # rubocop:disable Metrics/ParameterLists
4
+
5
+ module Arel
6
+ module Nodes
7
+ Arel::Table.class_eval do
8
+ # postgres only: https://www.postgresql.org/docs/9.5/sql-select.html
9
+ attr_accessor :only
10
+ # postgres only: https://www.postgresql.org/docs/9.5/ddl-schemas.html
11
+ attr_accessor :schema_name
12
+ # postgres only: https://www.postgresql.org/docs/9.1/catalog-pg-class.html
13
+ attr_accessor :relpersistence
14
+
15
+ alias_method :old_initialize, :initialize
16
+ def initialize(
17
+ name,
18
+ as: nil,
19
+ type_caster: nil,
20
+ only: false,
21
+ schema_name: nil,
22
+ relpersistence: 'p'
23
+ )
24
+ @only = only
25
+ @schema_name = schema_name
26
+ @relpersistence = relpersistence
27
+
28
+ old_initialize(name, as: as, type_caster: type_caster)
29
+ end
30
+ end
31
+ end
32
+
33
+ module Visitors
34
+ class ToSql
35
+ alias old_visit_Arel_Table visit_Arel_Table
36
+ def visit_Arel_Table(o, collector)
37
+ collector << 'ONLY ' if o.only
38
+
39
+ collector << "\"#{o.schema_name}\"." if o.schema_name
40
+
41
+ old_visit_Arel_Table(o, collector)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ # rubocop:enable Naming/MethodName
48
+ # rubocop:enable Naming/UncommunicativeMethodParamName
49
+ # rubocop:enable Metrics/ParameterLists
@@ -0,0 +1,13 @@
1
+ module Arel
2
+ module Nodes
3
+ class TimeWithPrecision < Arel::Nodes::Node
4
+ attr_reader :precision
5
+
6
+ def initialize(precision: nil)
7
+ super()
8
+
9
+ @precision = precision
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,30 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Nodes
6
+ # Postgres: https://www.postgresql.org/docs/9.1/sql-expressions.html
7
+ class TypeCast < Arel::Nodes::Node
8
+ attr_reader :arg
9
+ attr_reader :type_name
10
+
11
+ def initialize(arg, type_name)
12
+ @arg = arg
13
+ @type_name = type_name
14
+ end
15
+ end
16
+ end
17
+
18
+ module Visitors
19
+ class ToSql
20
+ def visit_Arel_Nodes_TypeCast(o, collector)
21
+ visit o.arg, collector
22
+ collector << '::'
23
+ collector << o.type_name
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ # rubocop:enable Naming/MethodName
30
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -0,0 +1,20 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Nodes
6
+ class Unknown < Arel::Nodes::Node
7
+ end
8
+ end
9
+
10
+ module Visitors
11
+ class ToSql
12
+ def visit_Arel_Nodes_Unknown(_o, collector)
13
+ collector << 'UNKNOWN'
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ # rubocop:enable Naming/MethodName
20
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -0,0 +1,63 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
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
11
+ end
12
+ end
13
+
14
+ module Visitors
15
+ class ToSql
16
+ # rubocop:disable Metrics/CyclomaticComplexity
17
+ # rubocop:disable Metrics/AbcSize
18
+ # rubocop:disable Metrics/PerceivedComplexity
19
+ def visit_Arel_Nodes_UpdateStatement(o, collector)
20
+ if o.with
21
+ collector = visit o.with, collector
22
+ collector << SPACE
23
+ end
24
+
25
+ wheres = if o.orders.empty? && o.limit.nil?
26
+ o.wheres
27
+ else
28
+ [Nodes::In.new(o.key, [build_subselect(o.key, o)])]
29
+ end
30
+
31
+ collector << 'UPDATE '
32
+ collector = visit o.relation, collector
33
+ unless o.values.empty?
34
+ collector << ' SET '
35
+ collector = inject_join o.values, collector, ', '
36
+ end
37
+
38
+ unless o.froms.empty?
39
+ collector << ' FROM '
40
+ collector = inject_join o.froms, collector, ', '
41
+ end
42
+
43
+ unless wheres.empty?
44
+ collector << ' WHERE '
45
+ collector = inject_join wheres, collector, ' AND '
46
+ end
47
+
48
+ unless o.returning.empty?
49
+ collector << ' RETURNING '
50
+ collector = inject_join o.returning, collector, ', '
51
+ end
52
+
53
+ collector
54
+ end
55
+ # rubocop:enable Metrics/AbcSize
56
+ # rubocop:enable Metrics/CyclomaticComplexity
57
+ # rubocop:enable Metrics/PerceivedComplexity
58
+ end
59
+ end
60
+ end
61
+
62
+ # rubocop:enable Naming/MethodName
63
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -0,0 +1,20 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Nodes
6
+ class User < Arel::Nodes::Node
7
+ end
8
+ end
9
+
10
+ module Visitors
11
+ class ToSql
12
+ def visit_Arel_Nodes_User(_o, collector)
13
+ collector << 'user'
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ # rubocop:enable Naming/MethodName
20
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -0,0 +1,22 @@
1
+ # rubocop:disable Naming/MethodName
2
+ # rubocop:disable Naming/UncommunicativeMethodParamName
3
+
4
+ module Arel
5
+ module Nodes
6
+ # Postgres: https://paquier.xyz/postgresql-2/postgres-9-4-feature-highlight-with-ordinality/
7
+ class WithOrdinality < Arel::Nodes::Unary
8
+ end
9
+ end
10
+
11
+ module Visitors
12
+ class ToSql
13
+ def visit_Arel_Nodes_WithOrdinality(o, collector)
14
+ visit o.expr, collector
15
+ collector << ' WITH ORDINALITY'
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ # rubocop:enable Naming/MethodName
22
+ # rubocop:enable Naming/UncommunicativeMethodParamName
@@ -0,0 +1,8 @@
1
+ require 'arel/sql_to_arel/pg_query_visitor'
2
+ require 'arel/sql_to_arel/unbound_column_reference'
3
+
4
+ module Arel
5
+ def self.sql_to_arel(sql)
6
+ SqlToArel::PgQueryVisitor.new.accept(sql)
7
+ end
8
+ end
@@ -0,0 +1,110 @@
1
+ module Arel
2
+ module SqlToArel
3
+ class FrameOptions
4
+ class << self
5
+ def arel(frame_options, start_offset, end_offset)
6
+ frame_option_names = calculate_frame_option_names(frame_options)
7
+ return unless frame_option_names.include?('FRAMEOPTION_NONDEFAULT')
8
+
9
+ range_klass = if frame_option_names.include?('FRAMEOPTION_RANGE')
10
+ Arel::Nodes::Range
11
+ else
12
+ Arel::Nodes::Rows
13
+ end
14
+
15
+ start_node = calculate_frame_node(
16
+ 'FRAMEOPTION_START_',
17
+ frame_option_names,
18
+ start_offset,
19
+ )
20
+ end_node = calculate_frame_node(
21
+ 'FRAMEOPTION_END_',
22
+ frame_option_names,
23
+ end_offset,
24
+ )
25
+
26
+ if frame_option_names.include?('FRAMEOPTION_BETWEEN')
27
+ Arel::Nodes::Between.new(
28
+ range_klass.new,
29
+ Arel::Nodes::And.new([start_node, end_node]),
30
+ )
31
+ else
32
+ range_klass.new start_node
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ # always NONDEFAULT
39
+ # RANGE or ROWS
40
+ # mandatory BETWEEN
41
+ # RANGE only unbounded
42
+ # ROWS all
43
+ # https://github.com/postgres/postgres/blob/REL_10_1/src/include/nodes/parsenodes.h
44
+ FRAMEOPTIONS = {
45
+ 'FRAMEOPTION_NONDEFAULT' => 0x00001,
46
+ 'FRAMEOPTION_RANGE' => 0x00002,
47
+ 'FRAMEOPTION_ROWS' => 0x00004,
48
+ 'FRAMEOPTION_BETWEEN' => 0x00008,
49
+ 'FRAMEOPTION_START_UNBOUNDED_PRECEDING' => 0x00010,
50
+ 'FRAMEOPTION_END_UNBOUNDED_PRECEDING' => 0x00020,
51
+ 'FRAMEOPTION_START_UNBOUNDED_FOLLOWING' => 0x00040,
52
+ 'FRAMEOPTION_END_UNBOUNDED_FOLLOWING' => 0x00080,
53
+ 'FRAMEOPTION_START_CURRENT_ROW' => 0x00100,
54
+ 'FRAMEOPTION_END_CURRENT_ROW' => 0x00200,
55
+ 'FRAMEOPTION_START_VALUE_PRECEDING' => 0x00400,
56
+ 'FRAMEOPTION_END_VALUE_PRECEDING' => 0x00800,
57
+ 'FRAMEOPTION_START_VALUE_FOLLOWING' => 0x01000,
58
+ 'FRAMEOPTION_END_VALUE_FOLLOWING' => 0x02000
59
+ }.freeze
60
+
61
+ def biggest_detractable_number(number, candidates)
62
+ high_to_low_candidates = candidates.sort { |a, b| b <=> a }
63
+ high_to_low_candidates.find do |candidate|
64
+ number - candidate >= 0
65
+ end
66
+ end
67
+
68
+ def calculate_frame_option_names(frame_options, names = [])
69
+ return names if frame_options.zero?
70
+
71
+ number = biggest_detractable_number(frame_options, FRAMEOPTIONS.values)
72
+ name = FRAMEOPTIONS.key(number)
73
+ calculate_frame_option_names(
74
+ frame_options - number, names + [name]
75
+ )
76
+ end
77
+
78
+ def calculate_frame_node(pattern, frame_option_names, offset)
79
+ node_name = frame_option_names.select { |n| n.start_with?(pattern) }
80
+ raise "Don't know how to handle multiple nodes" if node_name.length > 1
81
+
82
+ node_name = node_name.first.gsub(/FRAMEOPTION_(START|END)_/, '')
83
+ name_to_node(node_name, offset)
84
+ end
85
+
86
+ def name_to_node(node_name, offset)
87
+ case node_name
88
+ when 'UNBOUNDED_PRECEDING'
89
+ Arel::Nodes::Preceding.new
90
+
91
+ when 'UNBOUNDED_FOLLOWING'
92
+ Arel::Nodes::Following.new
93
+
94
+ when 'CURRENT_ROW'
95
+ Arel::Nodes::CurrentRow.new
96
+
97
+ when 'VALUE_PRECEDING'
98
+ Arel::Nodes::Preceding.new offset
99
+
100
+ when 'VALUE_FOLLOWING'
101
+ Arel::Nodes::Following.new offset
102
+
103
+ else
104
+ raise "Unknown start / end frame node `#{node_name}`"
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end