arel_toolkit 0.3.0 → 0.4.4
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 +3 -0
- data/.github/workflows/develop.yml +90 -0
- data/.github/workflows/master.yml +67 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +13 -5
- data/Appraisals +13 -0
- data/CHANGELOG.md +94 -5
- data/Gemfile +5 -0
- data/Gemfile.lock +62 -33
- data/Guardfile +4 -0
- data/README.md +67 -23
- data/Rakefile +11 -1
- data/arel_toolkit.gemspec +15 -6
- data/benchmark.rb +54 -0
- data/ext/pg_result_init/extconf.rb +52 -0
- data/ext/pg_result_init/pg_result_init.c +138 -0
- data/ext/pg_result_init/pg_result_init.h +6 -0
- data/gemfiles/active_record_6.gemfile +7 -0
- data/gemfiles/active_record_6.gemfile.lock +210 -0
- data/gemfiles/arel_gems.gemfile +10 -0
- data/gemfiles/arel_gems.gemfile.lock +284 -0
- data/gemfiles/default.gemfile +5 -0
- data/gemfiles/default.gemfile.lock +208 -0
- data/lib/arel/enhance.rb +17 -0
- data/lib/arel/enhance/context_enhancer/arel_table.rb +92 -0
- data/lib/arel/enhance/node.rb +232 -0
- data/lib/arel/enhance/path.rb +38 -0
- data/lib/arel/enhance/path_node.rb +26 -0
- data/lib/arel/enhance/query.rb +38 -0
- data/lib/arel/enhance/query_methods.rb +23 -0
- data/lib/arel/enhance/visitor.rb +97 -0
- data/lib/arel/extensions.rb +32 -6
- data/lib/arel/extensions/active_model_attribute_with_cast_value.rb +22 -0
- data/lib/arel/extensions/active_record_relation_query_attribute.rb +22 -0
- data/lib/arel/extensions/active_record_type_caster_connection.rb +7 -0
- data/lib/arel/extensions/active_record_type_caster_map.rb +7 -0
- data/lib/arel/extensions/array.rb +2 -9
- data/lib/arel/extensions/at_time_zone.rb +10 -3
- data/lib/arel/extensions/attributes_attribute.rb +47 -0
- data/lib/arel/extensions/binary.rb +7 -0
- data/lib/arel/extensions/bind_param.rb +15 -0
- data/lib/arel/extensions/bit_string.rb +2 -9
- data/lib/arel/extensions/case.rb +17 -0
- data/lib/arel/extensions/coalesce.rb +17 -3
- data/lib/arel/extensions/conflict.rb +9 -0
- data/lib/arel/extensions/contains.rb +27 -5
- data/lib/arel/extensions/current_catalog.rb +4 -0
- data/lib/arel/extensions/current_date.rb +4 -0
- data/lib/arel/extensions/current_of_expression.rb +2 -9
- data/lib/arel/extensions/current_role.rb +4 -0
- data/lib/arel/extensions/current_row.rb +7 -0
- data/lib/arel/extensions/current_schema.rb +4 -0
- data/lib/arel/extensions/current_user.rb +4 -0
- data/lib/arel/extensions/dealocate.rb +31 -0
- data/lib/arel/extensions/default_values.rb +4 -0
- data/lib/arel/extensions/delete_manager.rb +22 -6
- data/lib/arel/extensions/delete_statement.rb +46 -24
- data/lib/arel/extensions/dot.rb +11 -0
- data/lib/arel/extensions/exists.rb +59 -0
- data/lib/arel/extensions/extract_from.rb +3 -10
- data/lib/arel/extensions/factorial.rb +10 -2
- data/lib/arel/extensions/false.rb +7 -0
- data/lib/arel/extensions/function.rb +44 -14
- data/lib/arel/extensions/greatest.rb +17 -3
- data/lib/arel/extensions/indirection.rb +3 -12
- data/lib/arel/extensions/infer.rb +7 -7
- data/lib/arel/extensions/infix_operation.rb +17 -0
- data/lib/arel/extensions/insert_manager.rb +19 -3
- data/lib/arel/extensions/insert_statement.rb +31 -12
- data/lib/arel/extensions/into.rb +21 -0
- data/lib/arel/extensions/least.rb +17 -3
- data/lib/arel/extensions/named_argument.rb +3 -8
- data/lib/arel/extensions/named_function.rb +7 -0
- data/lib/arel/extensions/node.rb +10 -0
- data/lib/arel/extensions/ordering.rb +21 -6
- data/lib/arel/extensions/overlaps.rb +9 -0
- data/lib/arel/extensions/overlay.rb +9 -0
- data/lib/arel/extensions/position.rb +3 -8
- data/lib/arel/extensions/prepare.rb +39 -0
- data/lib/arel/extensions/range_function.rb +10 -2
- data/lib/arel/extensions/row.rb +3 -8
- data/lib/arel/extensions/select_core.rb +73 -0
- data/lib/arel/extensions/select_manager.rb +22 -6
- data/lib/arel/extensions/select_statement.rb +31 -9
- data/lib/arel/extensions/session_user.rb +4 -0
- data/lib/arel/extensions/set_to_default.rb +4 -0
- data/lib/arel/extensions/substring.rb +8 -0
- data/lib/arel/extensions/table.rb +43 -10
- data/lib/arel/extensions/time_with_precision.rb +6 -0
- data/lib/arel/extensions/to_sql.rb +27 -0
- data/lib/arel/extensions/top.rb +8 -0
- data/lib/arel/extensions/transaction.rb +3 -8
- data/lib/arel/extensions/tree_manager.rb +15 -0
- data/lib/arel/extensions/trim.rb +8 -0
- data/lib/arel/extensions/true.rb +7 -0
- data/lib/arel/extensions/type_cast.rb +7 -0
- data/lib/arel/extensions/unary.rb +7 -0
- data/lib/arel/extensions/unary_operation.rb +16 -0
- data/lib/arel/extensions/unknown.rb +4 -0
- data/lib/arel/extensions/update_manager.rb +22 -6
- data/lib/arel/extensions/update_statement.rb +36 -33
- data/lib/arel/extensions/user.rb +4 -0
- data/lib/arel/extensions/values_list.rb +15 -0
- data/lib/arel/extensions/variable_set.rb +9 -0
- data/lib/arel/extensions/variable_show.rb +3 -8
- data/lib/arel/middleware.rb +5 -1
- data/lib/arel/middleware/active_record_extension.rb +13 -0
- data/lib/arel/middleware/cache_accessor.rb +35 -0
- data/lib/arel/middleware/chain.rb +108 -33
- data/lib/arel/middleware/database_executor.rb +77 -0
- data/lib/arel/middleware/no_op_cache.rb +9 -0
- data/lib/arel/middleware/postgresql_adapter.rb +41 -5
- data/lib/arel/middleware/railtie.rb +15 -1
- data/lib/arel/middleware/result.rb +170 -0
- data/lib/arel/middleware/to_sql_executor.rb +15 -0
- data/lib/arel/middleware/to_sql_middleware.rb +33 -0
- data/lib/arel/sql_to_arel.rb +6 -3
- data/lib/arel/sql_to_arel/pg_query_visitor.rb +67 -38
- data/lib/arel/sql_to_arel/pg_query_visitor/frame_options.rb +1 -1
- data/lib/arel/sql_to_arel/result.rb +17 -4
- data/lib/arel/transformer.rb +8 -0
- data/lib/arel/transformer/prefix_schema_name.rb +183 -0
- data/lib/arel/transformer/remove_active_record_info.rb +40 -0
- data/lib/arel/transformer/replace_table_with_subquery.rb +31 -0
- data/lib/arel_toolkit.rb +15 -2
- data/lib/arel_toolkit/version.rb +1 -1
- metadata +179 -42
- data/.travis.yml +0 -29
- data/lib/arel/extensions/generate_series.rb +0 -9
- data/lib/arel/extensions/rank.rb +0 -9
- data/lib/arel/extensions/unbound_column_reference.rb +0 -5
- data/lib/arel/sql_formatter.rb +0 -59
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module Arel
|
|
2
|
+
module Middleware
|
|
3
|
+
class DatabaseExecutor
|
|
4
|
+
attr_reader :middleware
|
|
5
|
+
|
|
6
|
+
attr_accessor :index
|
|
7
|
+
attr_reader :context
|
|
8
|
+
attr_reader :final_block
|
|
9
|
+
|
|
10
|
+
def initialize(middleware)
|
|
11
|
+
@middleware = middleware
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run(arel, context, final_block)
|
|
15
|
+
@index = 0
|
|
16
|
+
@context = context
|
|
17
|
+
@final_block = final_block
|
|
18
|
+
|
|
19
|
+
result = call(arel)
|
|
20
|
+
check_return_type result
|
|
21
|
+
result
|
|
22
|
+
ensure
|
|
23
|
+
@index = 0
|
|
24
|
+
@context = nil
|
|
25
|
+
@final_block = nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def call(next_arel)
|
|
29
|
+
check_argument_type next_arel
|
|
30
|
+
|
|
31
|
+
current_middleware = middleware[index]
|
|
32
|
+
|
|
33
|
+
return execute_sql(next_arel) if current_middleware.nil?
|
|
34
|
+
|
|
35
|
+
self.index += 1
|
|
36
|
+
|
|
37
|
+
case current_middleware.method(:call).arity
|
|
38
|
+
when 2
|
|
39
|
+
current_middleware.call(next_arel, self)
|
|
40
|
+
else
|
|
41
|
+
current_middleware.call(next_arel, self, context.dup)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def execute_sql(next_arel)
|
|
48
|
+
sql, binds = next_arel.to_sql_and_binds
|
|
49
|
+
|
|
50
|
+
context[:cache_accessor].write(
|
|
51
|
+
transformed_sql: sql,
|
|
52
|
+
transformed_binds: binds,
|
|
53
|
+
original_sql: context[:original_sql],
|
|
54
|
+
original_binds: context[:original_binds],
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
sql_result = final_block.call(sql, binds)
|
|
58
|
+
|
|
59
|
+
check_return_type sql_result
|
|
60
|
+
sql_result
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def check_argument_type(next_arel)
|
|
64
|
+
return if next_arel.is_a?(Arel::Enhance::Node)
|
|
65
|
+
|
|
66
|
+
raise "Only `Arel::Enhance::Node` is valid for middleware, passed `#{next_arel.class}`"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def check_return_type(return_object)
|
|
70
|
+
return if return_object.is_a?(Arel::Middleware::Result)
|
|
71
|
+
|
|
72
|
+
raise 'Object returned from middleware needs to be wrapped in `Arel::Middleware::Result`' \
|
|
73
|
+
" for object `#{return_object}`"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -8,18 +8,54 @@ module Arel
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def execute(sql, name = nil)
|
|
11
|
-
sql = Arel::Middleware.current_chain.execute(sql)
|
|
12
11
|
super(sql, name)
|
|
13
12
|
end
|
|
14
13
|
|
|
14
|
+
alias parent_execute execute
|
|
15
|
+
|
|
16
|
+
# rubocop:disable Lint/DuplicateMethods
|
|
17
|
+
def execute(sql, name = nil)
|
|
18
|
+
Arel::Middleware.current_chain.execute(sql) do |processed_sql|
|
|
19
|
+
Arel::Middleware::Result.create(
|
|
20
|
+
data: parent_execute(processed_sql, name),
|
|
21
|
+
from: Arel::Middleware::PGResult,
|
|
22
|
+
to: Arel::Middleware::PGResult,
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
# rubocop:enable Lint/DuplicateMethods
|
|
27
|
+
|
|
28
|
+
def query(sql, name = nil)
|
|
29
|
+
Arel::Middleware.current_chain.execute(sql) do |processed_sql|
|
|
30
|
+
# NOTE: we're not calling `super` here, but execute.
|
|
31
|
+
# The `query` super does not return the columns, like the other methods.
|
|
32
|
+
# As we want the result objects to be the same, we call execute instead.
|
|
33
|
+
Arel::Middleware::Result.create(
|
|
34
|
+
data: parent_execute(processed_sql, name),
|
|
35
|
+
from: Arel::Middleware::PGResult,
|
|
36
|
+
to: Arel::Middleware::ArrayResult,
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
15
41
|
def exec_no_cache(sql, name, binds)
|
|
16
|
-
|
|
17
|
-
|
|
42
|
+
Arel::Middleware.current_chain.execute(sql, binds) do |processed_sql, processed_binds|
|
|
43
|
+
Arel::Middleware::Result.create(
|
|
44
|
+
data: super(processed_sql, name, processed_binds),
|
|
45
|
+
from: Arel::Middleware::PGResult,
|
|
46
|
+
to: Arel::Middleware::PGResult,
|
|
47
|
+
)
|
|
48
|
+
end
|
|
18
49
|
end
|
|
19
50
|
|
|
20
51
|
def exec_cache(sql, name, binds)
|
|
21
|
-
|
|
22
|
-
|
|
52
|
+
Arel::Middleware.current_chain.execute(sql, binds) do |processed_sql, processed_binds|
|
|
53
|
+
Arel::Middleware::Result.create(
|
|
54
|
+
data: super(processed_sql, name, processed_binds),
|
|
55
|
+
from: Arel::Middleware::PGResult,
|
|
56
|
+
to: Arel::Middleware::PGResult,
|
|
57
|
+
)
|
|
58
|
+
end
|
|
23
59
|
end
|
|
24
60
|
end
|
|
25
61
|
end
|
|
@@ -1,10 +1,24 @@
|
|
|
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
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
3
13
|
class Railtie
|
|
4
|
-
def self.
|
|
14
|
+
def self.insert
|
|
5
15
|
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(
|
|
6
16
|
Arel::Middleware::PostgreSQLAdapter,
|
|
7
17
|
)
|
|
18
|
+
|
|
19
|
+
ActiveRecord::Base.singleton_class.prepend(
|
|
20
|
+
Arel::Middleware::ActiveRecordExtension,
|
|
21
|
+
)
|
|
8
22
|
end
|
|
9
23
|
end
|
|
10
24
|
end
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
module Arel
|
|
2
|
+
module Middleware
|
|
3
|
+
class Column
|
|
4
|
+
attr_reader :name
|
|
5
|
+
attr_reader :metadata
|
|
6
|
+
|
|
7
|
+
def initialize(name, metadata)
|
|
8
|
+
@name = name
|
|
9
|
+
@metadata = metadata
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Class is very similar to ActiveRecord::Result
|
|
14
|
+
# activerecord/lib/active_record/result.rb
|
|
15
|
+
class Result
|
|
16
|
+
attr_reader :original_data
|
|
17
|
+
|
|
18
|
+
def self.create(from:, to:, data:)
|
|
19
|
+
Result.new from, to, data
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(from_caster, to_caster, original_data)
|
|
23
|
+
@from_caster = from_caster
|
|
24
|
+
@to_caster = to_caster
|
|
25
|
+
@original_data = original_data
|
|
26
|
+
@modified = false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def columns
|
|
30
|
+
@columns ||= column_objects.map(&:name)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def column_objects
|
|
34
|
+
@column_objects ||= from_caster.column_objects(original_data)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def rows
|
|
38
|
+
@rows ||= from_caster.rows(original_data)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def remove_column(column_name)
|
|
42
|
+
column_index = columns.index(column_name)
|
|
43
|
+
raise "Unknown column `#{column_name}`. Existing columns: `#{columns}`" if column_index.nil?
|
|
44
|
+
|
|
45
|
+
@hash_rows = nil
|
|
46
|
+
@columns = nil
|
|
47
|
+
@modified = true
|
|
48
|
+
|
|
49
|
+
column_objects.delete_at(column_index)
|
|
50
|
+
deleted_rows = []
|
|
51
|
+
|
|
52
|
+
rows.map! do |row|
|
|
53
|
+
deleted_rows << row.delete_at(column_index)
|
|
54
|
+
row
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
deleted_rows
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def hash_rows
|
|
61
|
+
@hash_rows ||=
|
|
62
|
+
begin
|
|
63
|
+
rows.map do |row|
|
|
64
|
+
hash = {}
|
|
65
|
+
|
|
66
|
+
index = 0
|
|
67
|
+
length = columns.length
|
|
68
|
+
|
|
69
|
+
while index < length
|
|
70
|
+
hash[columns[index]] = row[index]
|
|
71
|
+
index += 1
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
hash
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def to_casted_result
|
|
80
|
+
to_caster.cast_to(self)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def modified?
|
|
84
|
+
@modified
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
attr_reader :to_caster, :from_caster
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class PGResult
|
|
93
|
+
class << self
|
|
94
|
+
def column_objects(pg_result)
|
|
95
|
+
pg_result.fields.each_with_index.map do |field, index|
|
|
96
|
+
Column.new(
|
|
97
|
+
field,
|
|
98
|
+
tableid: pg_result.ftable(index),
|
|
99
|
+
columnid: pg_result.ftablecol(index),
|
|
100
|
+
format: pg_result.fformat(index),
|
|
101
|
+
typid: pg_result.ftype(index),
|
|
102
|
+
typlen: pg_result.fsize(index),
|
|
103
|
+
atttypmod: pg_result.fmod(index),
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def rows(data)
|
|
109
|
+
data.values
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def cast_to(result)
|
|
113
|
+
return result.original_data unless result.modified?
|
|
114
|
+
|
|
115
|
+
pg_columns = result_to_columns(result)
|
|
116
|
+
conn = ActiveRecord::Base.connection.raw_connection
|
|
117
|
+
new_result = PgResultInit.create(conn, result.original_data, pg_columns, result.rows)
|
|
118
|
+
result.original_data.clear
|
|
119
|
+
new_result
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
def result_to_columns(result)
|
|
125
|
+
result.column_objects.map do |column|
|
|
126
|
+
{
|
|
127
|
+
name: column.name,
|
|
128
|
+
tableid: column.metadata.fetch(:tableid, 0),
|
|
129
|
+
columnid: column.metadata.fetch(:columnid, 0),
|
|
130
|
+
format: column.metadata.fetch(:format, 0),
|
|
131
|
+
typid: column.metadata.fetch(:typid),
|
|
132
|
+
typlen: column.metadata.fetch(:typlen),
|
|
133
|
+
atttypmod: column.metadata.fetch(:atttypmod, -1),
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
class EmptyPGResult < PGResult
|
|
141
|
+
class << self
|
|
142
|
+
def cast_to(_result)
|
|
143
|
+
ActiveRecord::Base.connection.raw_connection.make_empty_pgresult(2)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
class ArrayResult
|
|
149
|
+
def self.cast_to(result)
|
|
150
|
+
result.rows
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
class StringResult
|
|
155
|
+
class << self
|
|
156
|
+
def column_objects(_string)
|
|
157
|
+
[]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def rows(_string)
|
|
161
|
+
[]
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def cast_to(result)
|
|
165
|
+
result.original_data
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Arel
|
|
2
|
+
module Middleware
|
|
3
|
+
class ToSqlExecutor < DatabaseExecutor
|
|
4
|
+
private
|
|
5
|
+
|
|
6
|
+
def execute_sql(next_arel)
|
|
7
|
+
Arel::Middleware::Result.create(
|
|
8
|
+
data: next_arel.to_sql,
|
|
9
|
+
from: Arel::Middleware::StringResult,
|
|
10
|
+
to: Arel::Middleware::EmptyPGResult,
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Arel
|
|
2
|
+
module Middleware
|
|
3
|
+
class ToSqlMiddleware
|
|
4
|
+
attr_reader :sql, :type, :query_class
|
|
5
|
+
|
|
6
|
+
def initialize(type)
|
|
7
|
+
@sql = []
|
|
8
|
+
@type = type
|
|
9
|
+
@query_class = class_from_type
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call(next_arel, next_middleware)
|
|
13
|
+
sql << next_arel.to_sql unless next_arel.query(class: query_class).empty?
|
|
14
|
+
next_middleware.call(next_arel)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def class_from_type
|
|
20
|
+
case type
|
|
21
|
+
when :insert
|
|
22
|
+
Arel::Nodes::InsertStatement
|
|
23
|
+
when :select
|
|
24
|
+
Arel::Nodes::SelectStatement
|
|
25
|
+
when :update
|
|
26
|
+
Arel::Nodes::UpdateStatement
|
|
27
|
+
when :delete
|
|
28
|
+
Arel::Nodes::DeleteStatement
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/arel/sql_to_arel.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
@@ -26,6 +26,9 @@ module Arel
|
|
|
26
26
|
@sql = sql
|
|
27
27
|
|
|
28
28
|
Result.new visit(object, :top)
|
|
29
|
+
rescue ::PgQuery::ParseError => e
|
|
30
|
+
new_error = ::PgQuery::ParseError.new(e.message, __FILE__, __LINE__, -1)
|
|
31
|
+
raise new_error, e.message, e.backtrace
|
|
29
32
|
rescue ::StandardError => e
|
|
30
33
|
raise e.class, e.message, e.backtrace if e.is_a?(Arel::SqlToArel::Error)
|
|
31
34
|
|
|
@@ -192,7 +195,7 @@ module Arel
|
|
|
192
195
|
end
|
|
193
196
|
|
|
194
197
|
def visit_Alias(aliasname:)
|
|
195
|
-
aliasname
|
|
198
|
+
Arel.sql visit_String(nil, str: aliasname)
|
|
196
199
|
end
|
|
197
200
|
|
|
198
201
|
def visit_BitString(str:)
|
|
@@ -278,18 +281,20 @@ module Arel
|
|
|
278
281
|
end
|
|
279
282
|
|
|
280
283
|
def visit_ColumnRef(fields:)
|
|
281
|
-
|
|
284
|
+
fields = fields.reverse
|
|
285
|
+
column = visit(fields[0], :operator)
|
|
286
|
+
table = visit(fields[1], :operator) if fields[1]
|
|
287
|
+
schema_name = visit(fields[2], :operator) if fields[2]
|
|
288
|
+
database = visit(fields[3], :operator) if fields[3]
|
|
282
289
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
290
|
+
table = Arel::Table.new(table) if table
|
|
291
|
+
attribute = Arel::Attribute.new(table, column)
|
|
292
|
+
attribute.schema_name = schema_name
|
|
293
|
+
attribute.database = database
|
|
287
294
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
Arel::Nodes::UnboundColumnReference.new visited_fields.join('.')
|
|
292
|
-
end
|
|
295
|
+
return attribute if table
|
|
296
|
+
|
|
297
|
+
Arel::Nodes::UnqualifiedColumn.new Arel::Attribute.new(nil, column)
|
|
293
298
|
end
|
|
294
299
|
|
|
295
300
|
def visit_CommonTableExpr(ctename:, ctequery:)
|
|
@@ -360,15 +365,9 @@ module Arel
|
|
|
360
365
|
when ['sum']
|
|
361
366
|
Arel::Nodes::Sum.new args
|
|
362
367
|
|
|
363
|
-
when ['rank']
|
|
364
|
-
Arel::Nodes::Rank.new args
|
|
365
|
-
|
|
366
368
|
when ['count']
|
|
367
369
|
Arel::Nodes::Count.new args
|
|
368
370
|
|
|
369
|
-
when ['generate_series']
|
|
370
|
-
Arel::Nodes::GenerateSeries.new args
|
|
371
|
-
|
|
372
371
|
when ['max']
|
|
373
372
|
Arel::Nodes::Max.new args
|
|
374
373
|
|
|
@@ -423,11 +422,16 @@ module Arel
|
|
|
423
422
|
[Arel::Nodes::Overlaps.new(start1, end1, start2, end2)]
|
|
424
423
|
|
|
425
424
|
else
|
|
426
|
-
|
|
427
|
-
|
|
425
|
+
case function_names.length
|
|
426
|
+
when 2
|
|
427
|
+
func = Arel::Nodes::NamedFunction.new(function_names.last, args)
|
|
428
|
+
func.schema_name = function_names.first
|
|
429
|
+
func
|
|
430
|
+
when 1
|
|
431
|
+
Arel::Nodes::NamedFunction.new(function_names.first, args)
|
|
432
|
+
else
|
|
433
|
+
boom "Don't know how to handle function names length `#{function_names.length}`"
|
|
428
434
|
end
|
|
429
|
-
|
|
430
|
-
Arel::Nodes::NamedFunction.new(function_names.first, args)
|
|
431
435
|
end
|
|
432
436
|
|
|
433
437
|
func.distinct = (agg_distinct.nil? ? false : true) unless func.is_a?(::Array)
|
|
@@ -444,17 +448,16 @@ module Arel
|
|
|
444
448
|
end
|
|
445
449
|
|
|
446
450
|
def visit_InferClause(conname: nil, index_elems: nil)
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
infer
|
|
451
|
+
left = Arel.sql(conname) if conname
|
|
452
|
+
right = visit(index_elems) if index_elems
|
|
453
|
+
Arel::Nodes::Infer.new left, right
|
|
451
454
|
end
|
|
452
455
|
|
|
453
456
|
def visit_IndexElem(name:, ordering:, nulls_ordering:)
|
|
454
457
|
boom "Unknown ordering `#{ordering}`" unless ordering.zero?
|
|
455
458
|
boom "Unknown nulls ordering `#{ordering}`" unless nulls_ordering.zero?
|
|
456
459
|
|
|
457
|
-
Arel.sql(name)
|
|
460
|
+
Arel.sql visit_String(str: name)
|
|
458
461
|
end
|
|
459
462
|
|
|
460
463
|
def visit_InsertStmt(
|
|
@@ -494,6 +497,12 @@ module Arel
|
|
|
494
497
|
ival
|
|
495
498
|
end
|
|
496
499
|
|
|
500
|
+
def visit_IntoClause(rel:, on_commit:)
|
|
501
|
+
raise "Unknown on_commit `#{on_commit}`" unless on_commit.zero?
|
|
502
|
+
|
|
503
|
+
Arel::Nodes::Into.new(visit(rel))
|
|
504
|
+
end
|
|
505
|
+
|
|
497
506
|
def visit_JoinExpr(jointype:, is_natural: nil, larg:, rarg:, quals: nil)
|
|
498
507
|
join_class = case jointype
|
|
499
508
|
when 0
|
|
@@ -531,12 +540,12 @@ module Arel
|
|
|
531
540
|
1 => 'FOR KEY SHARE',
|
|
532
541
|
2 => 'FOR SHARE',
|
|
533
542
|
3 => 'FOR NO KEY UPDATE',
|
|
534
|
-
4 => 'FOR UPDATE'
|
|
543
|
+
4 => 'FOR UPDATE',
|
|
535
544
|
}.fetch(strength)
|
|
536
545
|
wait_policy_clause = {
|
|
537
546
|
0 => '',
|
|
538
547
|
1 => ' SKIP LOCKED',
|
|
539
|
-
2 => ' NOWAIT'
|
|
548
|
+
2 => ' NOWAIT',
|
|
540
549
|
}.fetch(wait_policy)
|
|
541
550
|
|
|
542
551
|
Arel::Nodes::Lock.new Arel.sql("#{strength_clause}#{wait_policy_clause}")
|
|
@@ -584,15 +593,19 @@ module Arel
|
|
|
584
593
|
conflict
|
|
585
594
|
end
|
|
586
595
|
|
|
587
|
-
def visit_ParamRef(number:)
|
|
596
|
+
def visit_ParamRef(number: nil)
|
|
588
597
|
value = (binds[number - 1] unless binds.empty?)
|
|
589
598
|
|
|
590
599
|
Arel::Nodes::BindParam.new(value)
|
|
591
600
|
end
|
|
592
601
|
|
|
593
|
-
def visit_RangeFunction(
|
|
594
|
-
|
|
595
|
-
|
|
602
|
+
def visit_RangeFunction(
|
|
603
|
+
is_rowsfrom: nil,
|
|
604
|
+
functions:,
|
|
605
|
+
lateral: false,
|
|
606
|
+
ordinality: false,
|
|
607
|
+
aliaz: nil
|
|
608
|
+
)
|
|
596
609
|
functions = functions.map do |function_array|
|
|
597
610
|
function, empty_value = function_array
|
|
598
611
|
boom 'https://github.com/mvgijssel/arel_toolkit/issues/37' unless empty_value.nil?
|
|
@@ -600,15 +613,16 @@ module Arel
|
|
|
600
613
|
visit(function)
|
|
601
614
|
end
|
|
602
615
|
|
|
603
|
-
node = Arel::Nodes::RangeFunction.new functions
|
|
616
|
+
node = Arel::Nodes::RangeFunction.new functions, is_rowsfrom: is_rowsfrom
|
|
604
617
|
node = lateral ? Arel::Nodes::Lateral.new(node) : node
|
|
605
|
-
ordinality ? Arel::Nodes::WithOrdinality.new(node) : node
|
|
618
|
+
node = ordinality ? Arel::Nodes::WithOrdinality.new(node) : node
|
|
619
|
+
aliaz.nil? ? node : Arel::Nodes::As.new(node, visit(aliaz))
|
|
606
620
|
end
|
|
607
621
|
|
|
608
622
|
def visit_RangeSubselect(aliaz:, subquery:, lateral: false)
|
|
609
623
|
aliaz = visit(aliaz)
|
|
610
624
|
subquery = visit(subquery)
|
|
611
|
-
node = Arel::Nodes::
|
|
625
|
+
node = Arel::Nodes::As.new(Arel::Nodes::Grouping.new(subquery), aliaz)
|
|
612
626
|
lateral ? Arel::Nodes::Lateral.new(node) : node
|
|
613
627
|
end
|
|
614
628
|
|
|
@@ -632,7 +646,8 @@ module Arel
|
|
|
632
646
|
val = visit(val)
|
|
633
647
|
|
|
634
648
|
if name
|
|
635
|
-
|
|
649
|
+
aliaz = visit_Alias(aliasname: name)
|
|
650
|
+
Arel::Nodes::As.new(val, aliaz)
|
|
636
651
|
else
|
|
637
652
|
val
|
|
638
653
|
end
|
|
@@ -669,6 +684,7 @@ module Arel
|
|
|
669
684
|
op:,
|
|
670
685
|
window_clause: nil,
|
|
671
686
|
values_lists: nil,
|
|
687
|
+
into_clause: nil,
|
|
672
688
|
all: nil,
|
|
673
689
|
larg: nil,
|
|
674
690
|
rarg: nil
|
|
@@ -702,6 +718,8 @@ module Arel
|
|
|
702
718
|
select_core.groups = visit(group_clause) if group_clause
|
|
703
719
|
select_core.havings = [visit(having_clause)] if having_clause
|
|
704
720
|
select_core.windows = visit(window_clause) if window_clause
|
|
721
|
+
select_core.into = visit(into_clause) if into_clause
|
|
722
|
+
select_core.top = ::Arel::Nodes::Top.new visit(limit_count) if limit_count
|
|
705
723
|
|
|
706
724
|
if distinct_clause == [nil]
|
|
707
725
|
select_core.set_quantifier = Arel::Nodes::Distinct.new
|
|
@@ -726,7 +744,7 @@ module Arel
|
|
|
726
744
|
value
|
|
727
745
|
when Integer
|
|
728
746
|
Arel.sql(value.to_s)
|
|
729
|
-
when Arel::Nodes::TypeCast
|
|
747
|
+
when Arel::Nodes::TypeCast, Arel::Nodes::UnqualifiedColumn
|
|
730
748
|
Arel.sql(value.to_sql)
|
|
731
749
|
when Arel::Nodes::BindParam
|
|
732
750
|
value
|
|
@@ -930,8 +948,11 @@ module Arel
|
|
|
930
948
|
start_offset: nil,
|
|
931
949
|
end_offset: nil
|
|
932
950
|
)
|
|
933
|
-
|
|
951
|
+
if name.present? && partition_clause.empty? && order_clause.empty?
|
|
952
|
+
return Arel::Nodes::SqlLiteral.new(name)
|
|
953
|
+
end
|
|
934
954
|
|
|
955
|
+
instance = name.nil? ? Arel::Nodes::Window.new : Arel::Nodes::NamedWindow.new(name)
|
|
935
956
|
instance.tap do |window|
|
|
936
957
|
window.orders = visit order_clause
|
|
937
958
|
window.partitions = visit partition_clause
|
|
@@ -1077,6 +1098,14 @@ module Arel
|
|
|
1077
1098
|
end
|
|
1078
1099
|
end
|
|
1079
1100
|
|
|
1101
|
+
def visit_DeallocateStmt(name: nil)
|
|
1102
|
+
Arel::Nodes::Dealocate.new name
|
|
1103
|
+
end
|
|
1104
|
+
|
|
1105
|
+
def visit_PrepareStmt(name:, argtypes: nil, query:)
|
|
1106
|
+
Arel::Nodes::Prepare.new name, argtypes && visit(argtypes), visit(query)
|
|
1107
|
+
end
|
|
1108
|
+
|
|
1080
1109
|
def visit(attribute, context = nil)
|
|
1081
1110
|
return attribute.map { |attr| visit(attr, context) } if attribute.is_a? Array
|
|
1082
1111
|
|