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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +29 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +34 -0
- data/.ruby-version +1 -1
- data/.travis.yml +16 -2
- data/CHANGELOG.md +10 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +94 -1
- data/Guardfile +42 -0
- data/README.md +23 -4
- data/Rakefile +3 -3
- data/arel_toolkit.gemspec +32 -15
- data/bin/console +4 -7
- data/lib/arel/extensions.rb +71 -0
- data/lib/arel/extensions/absolute.rb +10 -0
- data/lib/arel/extensions/all.rb +23 -0
- data/lib/arel/extensions/any.rb +23 -0
- data/lib/arel/extensions/array.rb +29 -0
- data/lib/arel/extensions/array_subselect.rb +23 -0
- data/lib/arel/extensions/between_symmetric.rb +23 -0
- data/lib/arel/extensions/bit_string.rb +27 -0
- data/lib/arel/extensions/bitwise_xor.rb +10 -0
- data/lib/arel/extensions/coalesce.rb +9 -0
- data/lib/arel/extensions/conflict.rb +47 -0
- data/lib/arel/extensions/contained_by.rb +10 -0
- data/lib/arel/extensions/contains.rb +10 -0
- data/lib/arel/extensions/cross_join.rb +21 -0
- data/lib/arel/extensions/cube_root.rb +10 -0
- data/lib/arel/extensions/current_catalog.rb +20 -0
- data/lib/arel/extensions/current_date.rb +20 -0
- data/lib/arel/extensions/current_of_expression.rb +29 -0
- data/lib/arel/extensions/current_role.rb +20 -0
- data/lib/arel/extensions/current_schema.rb +20 -0
- data/lib/arel/extensions/current_time.rb +22 -0
- data/lib/arel/extensions/current_timestamp.rb +22 -0
- data/lib/arel/extensions/current_user.rb +20 -0
- data/lib/arel/extensions/default_values.rb +21 -0
- data/lib/arel/extensions/delete_statement.rb +49 -0
- data/lib/arel/extensions/distinct_from.rb +22 -0
- data/lib/arel/extensions/equality.rb +30 -0
- data/lib/arel/extensions/except_all.rb +21 -0
- data/lib/arel/extensions/exponentiation.rb +10 -0
- data/lib/arel/extensions/factorial.rb +33 -0
- data/lib/arel/extensions/function.rb +68 -0
- data/lib/arel/extensions/generate_series.rb +9 -0
- data/lib/arel/extensions/greatest.rb +9 -0
- data/lib/arel/extensions/indirection.rb +32 -0
- data/lib/arel/extensions/infer.rb +35 -0
- data/lib/arel/extensions/insert_statement.rb +69 -0
- data/lib/arel/extensions/intersect_all.rb +21 -0
- data/lib/arel/extensions/lateral.rb +34 -0
- data/lib/arel/extensions/least.rb +9 -0
- data/lib/arel/extensions/local_time.rb +22 -0
- data/lib/arel/extensions/local_timestamp.rb +22 -0
- data/lib/arel/extensions/modulo.rb +10 -0
- data/lib/arel/extensions/named_function.rb +15 -0
- data/lib/arel/extensions/natural_join.rb +21 -0
- data/lib/arel/extensions/not_between.rb +22 -0
- data/lib/arel/extensions/not_between_symmetric.rb +23 -0
- data/lib/arel/extensions/not_distinct_from.rb +22 -0
- data/lib/arel/extensions/not_equal.rb +30 -0
- data/lib/arel/extensions/not_similar.rb +29 -0
- data/lib/arel/extensions/null_if.rb +24 -0
- data/lib/arel/extensions/ordering.rb +47 -0
- data/lib/arel/extensions/overlap.rb +10 -0
- data/lib/arel/extensions/range_function.rb +23 -0
- data/lib/arel/extensions/rank.rb +9 -0
- data/lib/arel/extensions/row.rb +30 -0
- data/lib/arel/extensions/select_statement.rb +26 -0
- data/lib/arel/extensions/session_user.rb +20 -0
- data/lib/arel/extensions/set_to_default.rb +21 -0
- data/lib/arel/extensions/similar.rb +32 -0
- data/lib/arel/extensions/square_root.rb +10 -0
- data/lib/arel/extensions/table.rb +49 -0
- data/lib/arel/extensions/time_with_precision.rb +13 -0
- data/lib/arel/extensions/type_cast.rb +30 -0
- data/lib/arel/extensions/unknown.rb +20 -0
- data/lib/arel/extensions/update_statement.rb +63 -0
- data/lib/arel/extensions/user.rb +20 -0
- data/lib/arel/extensions/with_ordinality.rb +22 -0
- data/lib/arel/sql_to_arel.rb +8 -0
- data/lib/arel/sql_to_arel/frame_options.rb +110 -0
- data/lib/arel/sql_to_arel/pg_query_visitor.rb +1005 -0
- data/lib/arel/sql_to_arel/unbound_column_reference.rb +5 -0
- data/lib/arel_toolkit.rb +4 -3
- data/lib/arel_toolkit/version.rb +1 -1
- 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,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,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,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
|