arel_toolkit 0.1.0 → 0.2.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 (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