activerecord 5.2.2.1 → 6.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +734 -508
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +20 -15
- data/lib/active_record/associations/association.rb +61 -20
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +16 -28
- data/lib/active_record/associations/collection_proxy.rb +19 -48
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +3 -10
- data/lib/active_record/associations/has_many_through_association.rb +20 -25
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +28 -28
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +40 -32
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +111 -40
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +22 -8
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +5 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +137 -26
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +114 -130
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +26 -11
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +135 -56
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +189 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +151 -198
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +53 -43
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +7 -11
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -77
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +164 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
- data/lib/active_record/connection_handling.rb +155 -26
- data/lib/active_record/core.rb +104 -59
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +37 -7
- data/lib/active_record/errors.rb +30 -16
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +145 -472
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +91 -64
- data/lib/active_record/model_schema.rb +33 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +231 -25
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +33 -22
- data/lib/active_record/railtie.rb +80 -43
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +199 -46
- data/lib/active_record/reflection.rb +42 -44
- data/lib/active_record/relation.rb +311 -80
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +67 -57
- data/lib/active_record/relation/delegation.rb +26 -43
- data/lib/active_record/relation/finder_methods.rb +28 -28
- data/lib/active_record/relation/merger.rb +17 -23
- data/lib/active_record/relation/predicate_builder.rb +18 -15
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +17 -10
- data/lib/active_record/relation/query_methods.rb +247 -73
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +20 -15
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +194 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +225 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +57 -66
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +58 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +111 -26
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Visitors
|
5
|
+
class IBM_DB < Arel::Visitors::ToSql
|
6
|
+
private
|
7
|
+
def visit_Arel_Nodes_SelectCore(o, collector)
|
8
|
+
collector = super
|
9
|
+
maybe_visit o.optimizer_hints, collector
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_Arel_Nodes_OptimizerHints(o, collector)
|
13
|
+
hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join
|
14
|
+
collector << "/* <OPTGUIDELINES>#{hints}</OPTGUIDELINES> */"
|
15
|
+
end
|
16
|
+
|
17
|
+
def visit_Arel_Nodes_Limit(o, collector)
|
18
|
+
collector << "FETCH FIRST "
|
19
|
+
collector = visit o.expr, collector
|
20
|
+
collector << " ROWS ONLY"
|
21
|
+
end
|
22
|
+
|
23
|
+
def is_distinct_from(o, collector)
|
24
|
+
collector << "DECODE("
|
25
|
+
collector = visit [o.left, o.right, 0, 1], collector
|
26
|
+
collector << ")"
|
27
|
+
end
|
28
|
+
|
29
|
+
def collect_optimizer_hints(o, collector)
|
30
|
+
collector
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Visitors
|
5
|
+
class Informix < Arel::Visitors::ToSql
|
6
|
+
private
|
7
|
+
def visit_Arel_Nodes_SelectStatement(o, collector)
|
8
|
+
collector << "SELECT "
|
9
|
+
collector = maybe_visit o.offset, collector
|
10
|
+
collector = maybe_visit o.limit, collector
|
11
|
+
collector = o.cores.inject(collector) { |c, x|
|
12
|
+
visit_Arel_Nodes_SelectCore x, c
|
13
|
+
}
|
14
|
+
if o.orders.any?
|
15
|
+
collector << "ORDER BY "
|
16
|
+
collector = inject_join o.orders, collector, ", "
|
17
|
+
end
|
18
|
+
maybe_visit o.lock, collector
|
19
|
+
end
|
20
|
+
|
21
|
+
def visit_Arel_Nodes_SelectCore(o, collector)
|
22
|
+
collector = inject_join o.projections, collector, ", "
|
23
|
+
if o.source && !o.source.empty?
|
24
|
+
collector << " FROM "
|
25
|
+
collector = visit o.source, collector
|
26
|
+
end
|
27
|
+
|
28
|
+
if o.wheres.any?
|
29
|
+
collector << " WHERE "
|
30
|
+
collector = inject_join o.wheres, collector, " AND "
|
31
|
+
end
|
32
|
+
|
33
|
+
if o.groups.any?
|
34
|
+
collector << "GROUP BY "
|
35
|
+
collector = inject_join o.groups, collector, ", "
|
36
|
+
end
|
37
|
+
|
38
|
+
if o.havings.any?
|
39
|
+
collector << " HAVING "
|
40
|
+
collector = inject_join o.havings, collector, " AND "
|
41
|
+
end
|
42
|
+
collector
|
43
|
+
end
|
44
|
+
|
45
|
+
def visit_Arel_Nodes_OptimizerHints(o, collector)
|
46
|
+
hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(", ")
|
47
|
+
collector << "/*+ #{hints} */"
|
48
|
+
end
|
49
|
+
|
50
|
+
def visit_Arel_Nodes_Offset(o, collector)
|
51
|
+
collector << "SKIP "
|
52
|
+
visit o.expr, collector
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit_Arel_Nodes_Limit(o, collector)
|
56
|
+
collector << "FIRST "
|
57
|
+
visit o.expr, collector
|
58
|
+
collector << " "
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Visitors
|
5
|
+
class MSSQL < Arel::Visitors::ToSql
|
6
|
+
RowNumber = Struct.new :children
|
7
|
+
|
8
|
+
def initialize(*)
|
9
|
+
@primary_keys = {}
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
|
16
|
+
right = o.right
|
17
|
+
|
18
|
+
if right.nil?
|
19
|
+
collector = visit o.left, collector
|
20
|
+
collector << " IS NULL"
|
21
|
+
else
|
22
|
+
collector << "EXISTS (VALUES ("
|
23
|
+
collector = visit o.left, collector
|
24
|
+
collector << ") INTERSECT VALUES ("
|
25
|
+
collector = visit right, collector
|
26
|
+
collector << "))"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def visit_Arel_Nodes_IsDistinctFrom(o, collector)
|
31
|
+
if o.right.nil?
|
32
|
+
collector = visit o.left, collector
|
33
|
+
collector << " IS NOT NULL"
|
34
|
+
else
|
35
|
+
collector << "NOT "
|
36
|
+
visit_Arel_Nodes_IsNotDistinctFrom o, collector
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def visit_Arel_Visitors_MSSQL_RowNumber(o, collector)
|
41
|
+
collector << "ROW_NUMBER() OVER (ORDER BY "
|
42
|
+
inject_join(o.children, collector, ", ") << ") as _row_num"
|
43
|
+
end
|
44
|
+
|
45
|
+
def visit_Arel_Nodes_SelectStatement(o, collector)
|
46
|
+
if !o.limit && !o.offset
|
47
|
+
return super
|
48
|
+
end
|
49
|
+
|
50
|
+
is_select_count = false
|
51
|
+
o.cores.each { |x|
|
52
|
+
core_order_by = row_num_literal determine_order_by(o.orders, x)
|
53
|
+
if select_count? x
|
54
|
+
x.projections = [core_order_by]
|
55
|
+
is_select_count = true
|
56
|
+
else
|
57
|
+
x.projections << core_order_by
|
58
|
+
end
|
59
|
+
}
|
60
|
+
|
61
|
+
if is_select_count
|
62
|
+
# fixme count distinct wouldn't work with limit or offset
|
63
|
+
collector << "SELECT COUNT(1) as count_id FROM ("
|
64
|
+
end
|
65
|
+
|
66
|
+
collector << "SELECT _t.* FROM ("
|
67
|
+
collector = o.cores.inject(collector) { |c, x|
|
68
|
+
visit_Arel_Nodes_SelectCore x, c
|
69
|
+
}
|
70
|
+
collector << ") as _t WHERE #{get_offset_limit_clause(o)}"
|
71
|
+
|
72
|
+
if is_select_count
|
73
|
+
collector << ") AS subquery"
|
74
|
+
else
|
75
|
+
collector
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def visit_Arel_Nodes_SelectCore(o, collector)
|
80
|
+
collector = super
|
81
|
+
maybe_visit o.optimizer_hints, collector
|
82
|
+
end
|
83
|
+
|
84
|
+
def visit_Arel_Nodes_OptimizerHints(o, collector)
|
85
|
+
hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(", ")
|
86
|
+
collector << "OPTION (#{hints})"
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_offset_limit_clause(o)
|
90
|
+
first_row = o.offset ? o.offset.expr.to_i + 1 : 1
|
91
|
+
last_row = o.limit ? o.limit.expr.to_i - 1 + first_row : nil
|
92
|
+
if last_row
|
93
|
+
" _row_num BETWEEN #{first_row} AND #{last_row}"
|
94
|
+
else
|
95
|
+
" _row_num >= #{first_row}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def visit_Arel_Nodes_DeleteStatement(o, collector)
|
100
|
+
collector << "DELETE "
|
101
|
+
if o.limit
|
102
|
+
collector << "TOP ("
|
103
|
+
visit o.limit.expr, collector
|
104
|
+
collector << ") "
|
105
|
+
end
|
106
|
+
collector << "FROM "
|
107
|
+
collector = visit o.relation, collector
|
108
|
+
if o.wheres.any?
|
109
|
+
collector << " WHERE "
|
110
|
+
inject_join o.wheres, collector, " AND "
|
111
|
+
else
|
112
|
+
collector
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def collect_optimizer_hints(o, collector)
|
117
|
+
collector
|
118
|
+
end
|
119
|
+
|
120
|
+
def determine_order_by(orders, x)
|
121
|
+
if orders.any?
|
122
|
+
orders
|
123
|
+
elsif x.groups.any?
|
124
|
+
x.groups
|
125
|
+
else
|
126
|
+
pk = find_left_table_pk(x.froms)
|
127
|
+
pk ? [pk] : []
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def row_num_literal(order_by)
|
132
|
+
RowNumber.new order_by
|
133
|
+
end
|
134
|
+
|
135
|
+
def select_count?(x)
|
136
|
+
x.projections.length == 1 && Arel::Nodes::Count === x.projections.first
|
137
|
+
end
|
138
|
+
|
139
|
+
# FIXME raise exception of there is no pk?
|
140
|
+
def find_left_table_pk(o)
|
141
|
+
if o.kind_of?(Arel::Nodes::Join)
|
142
|
+
find_left_table_pk(o.left)
|
143
|
+
elsif o.instance_of?(Arel::Table)
|
144
|
+
find_primary_key(o)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def find_primary_key(o)
|
149
|
+
@primary_keys[o.name] ||= begin
|
150
|
+
primary_key_name = @connection.primary_key(o.name)
|
151
|
+
# some tables might be without primary key
|
152
|
+
primary_key_name && o[primary_key_name]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Visitors
|
5
|
+
class MySQL < Arel::Visitors::ToSql
|
6
|
+
private
|
7
|
+
def visit_Arel_Nodes_Bin(o, collector)
|
8
|
+
collector << "BINARY "
|
9
|
+
visit o.expr, collector
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_Arel_Nodes_UnqualifiedColumn(o, collector)
|
13
|
+
visit o.expr, collector
|
14
|
+
end
|
15
|
+
|
16
|
+
###
|
17
|
+
# :'(
|
18
|
+
# http://dev.mysql.com/doc/refman/5.0/en/select.html#id3482214
|
19
|
+
def visit_Arel_Nodes_SelectStatement(o, collector)
|
20
|
+
if o.offset && !o.limit
|
21
|
+
o.limit = Arel::Nodes::Limit.new(18446744073709551615)
|
22
|
+
end
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def visit_Arel_Nodes_SelectCore(o, collector)
|
27
|
+
o.froms ||= Arel.sql("DUAL")
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def visit_Arel_Nodes_Concat(o, collector)
|
32
|
+
collector << " CONCAT("
|
33
|
+
visit o.left, collector
|
34
|
+
collector << ", "
|
35
|
+
visit o.right, collector
|
36
|
+
collector << ") "
|
37
|
+
collector
|
38
|
+
end
|
39
|
+
|
40
|
+
def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
|
41
|
+
collector = visit o.left, collector
|
42
|
+
collector << " <=> "
|
43
|
+
visit o.right, collector
|
44
|
+
end
|
45
|
+
|
46
|
+
def visit_Arel_Nodes_IsDistinctFrom(o, collector)
|
47
|
+
collector << "NOT "
|
48
|
+
visit_Arel_Nodes_IsNotDistinctFrom o, collector
|
49
|
+
end
|
50
|
+
|
51
|
+
# In the simple case, MySQL allows us to place JOINs directly into the UPDATE
|
52
|
+
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
53
|
+
# these, we must use a subquery.
|
54
|
+
def prepare_update_statement(o)
|
55
|
+
if o.offset || has_join_sources?(o) && has_limit_or_offset_or_orders?(o)
|
56
|
+
super
|
57
|
+
else
|
58
|
+
o
|
59
|
+
end
|
60
|
+
end
|
61
|
+
alias :prepare_delete_statement :prepare_update_statement
|
62
|
+
|
63
|
+
# MySQL is too stupid to create a temporary table for use subquery, so we have
|
64
|
+
# to give it some prompting in the form of a subsubquery.
|
65
|
+
def build_subselect(key, o)
|
66
|
+
subselect = super
|
67
|
+
|
68
|
+
# Materialize subquery by adding distinct
|
69
|
+
# to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
|
70
|
+
unless has_limit_or_offset_or_orders?(subselect)
|
71
|
+
core = subselect.cores.last
|
72
|
+
core.set_quantifier = Arel::Nodes::Distinct.new
|
73
|
+
end
|
74
|
+
|
75
|
+
Nodes::SelectStatement.new.tap do |stmt|
|
76
|
+
core = stmt.cores.last
|
77
|
+
core.froms = Nodes::Grouping.new(subselect).as("__active_record_temp")
|
78
|
+
core.projections = [Arel.sql(quote_column_name(key.name))]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Visitors
|
5
|
+
class Oracle < Arel::Visitors::ToSql
|
6
|
+
private
|
7
|
+
|
8
|
+
def visit_Arel_Nodes_SelectStatement(o, collector)
|
9
|
+
o = order_hacks(o)
|
10
|
+
|
11
|
+
# if need to select first records without ORDER BY and GROUP BY and without DISTINCT
|
12
|
+
# then can use simple ROWNUM in WHERE clause
|
13
|
+
if o.limit && o.orders.empty? && o.cores.first.groups.empty? && !o.offset && o.cores.first.set_quantifier.class.to_s !~ /Distinct/
|
14
|
+
o.cores.last.wheres.push Nodes::LessThanOrEqual.new(
|
15
|
+
Nodes::SqlLiteral.new("ROWNUM"), o.limit.expr
|
16
|
+
)
|
17
|
+
return super
|
18
|
+
end
|
19
|
+
|
20
|
+
if o.limit && o.offset
|
21
|
+
o = o.dup
|
22
|
+
limit = o.limit.expr
|
23
|
+
offset = o.offset
|
24
|
+
o.offset = nil
|
25
|
+
collector << "
|
26
|
+
SELECT * FROM (
|
27
|
+
SELECT raw_sql_.*, rownum raw_rnum_
|
28
|
+
FROM ("
|
29
|
+
|
30
|
+
collector = super(o, collector)
|
31
|
+
|
32
|
+
if offset.expr.is_a? Nodes::BindParam
|
33
|
+
collector << ") raw_sql_ WHERE rownum <= ("
|
34
|
+
collector = visit offset.expr, collector
|
35
|
+
collector << " + "
|
36
|
+
collector = visit limit, collector
|
37
|
+
collector << ") ) WHERE raw_rnum_ > "
|
38
|
+
collector = visit offset.expr, collector
|
39
|
+
return collector
|
40
|
+
else
|
41
|
+
collector << ") raw_sql_
|
42
|
+
WHERE rownum <= #{offset.expr.to_i + limit}
|
43
|
+
)
|
44
|
+
WHERE "
|
45
|
+
return visit(offset, collector)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if o.limit
|
50
|
+
o = o.dup
|
51
|
+
limit = o.limit.expr
|
52
|
+
collector << "SELECT * FROM ("
|
53
|
+
collector = super(o, collector)
|
54
|
+
collector << ") WHERE ROWNUM <= "
|
55
|
+
return visit limit, collector
|
56
|
+
end
|
57
|
+
|
58
|
+
if o.offset
|
59
|
+
o = o.dup
|
60
|
+
offset = o.offset
|
61
|
+
o.offset = nil
|
62
|
+
collector << "SELECT * FROM (
|
63
|
+
SELECT raw_sql_.*, rownum raw_rnum_
|
64
|
+
FROM ("
|
65
|
+
collector = super(o, collector)
|
66
|
+
collector << ") raw_sql_
|
67
|
+
)
|
68
|
+
WHERE "
|
69
|
+
return visit offset, collector
|
70
|
+
end
|
71
|
+
|
72
|
+
super
|
73
|
+
end
|
74
|
+
|
75
|
+
def visit_Arel_Nodes_Limit(o, collector)
|
76
|
+
collector
|
77
|
+
end
|
78
|
+
|
79
|
+
def visit_Arel_Nodes_Offset(o, collector)
|
80
|
+
collector << "raw_rnum_ > "
|
81
|
+
visit o.expr, collector
|
82
|
+
end
|
83
|
+
|
84
|
+
def visit_Arel_Nodes_Except(o, collector)
|
85
|
+
collector << "( "
|
86
|
+
collector = infix_value o, collector, " MINUS "
|
87
|
+
collector << " )"
|
88
|
+
end
|
89
|
+
|
90
|
+
def visit_Arel_Nodes_UpdateStatement(o, collector)
|
91
|
+
# Oracle does not allow ORDER BY/LIMIT in UPDATEs.
|
92
|
+
if o.orders.any? && o.limit.nil?
|
93
|
+
# However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided,
|
94
|
+
# otherwise let the user deal with the error
|
95
|
+
o = o.dup
|
96
|
+
o.orders = []
|
97
|
+
end
|
98
|
+
|
99
|
+
super
|
100
|
+
end
|
101
|
+
|
102
|
+
###
|
103
|
+
# Hacks for the order clauses specific to Oracle
|
104
|
+
def order_hacks(o)
|
105
|
+
return o if o.orders.empty?
|
106
|
+
return o unless o.cores.any? do |core|
|
107
|
+
core.projections.any? do |projection|
|
108
|
+
/FIRST_VALUE/ === projection
|
109
|
+
end
|
110
|
+
end
|
111
|
+
# Previous version with join and split broke ORDER BY clause
|
112
|
+
# if it contained functions with several arguments (separated by ',').
|
113
|
+
#
|
114
|
+
# orders = o.orders.map { |x| visit x }.join(', ').split(',')
|
115
|
+
orders = o.orders.map do |x|
|
116
|
+
string = visit(x, Arel::Collectors::SQLString.new).value
|
117
|
+
if string.include?(",")
|
118
|
+
split_order_string(string)
|
119
|
+
else
|
120
|
+
string
|
121
|
+
end
|
122
|
+
end.flatten
|
123
|
+
o.orders = []
|
124
|
+
orders.each_with_index do |order, i|
|
125
|
+
o.orders <<
|
126
|
+
Nodes::SqlLiteral.new("alias_#{i}__#{' DESC' if /\bdesc$/i === order}")
|
127
|
+
end
|
128
|
+
o
|
129
|
+
end
|
130
|
+
|
131
|
+
# Split string by commas but count opening and closing brackets
|
132
|
+
# and ignore commas inside brackets.
|
133
|
+
def split_order_string(string)
|
134
|
+
array = []
|
135
|
+
i = 0
|
136
|
+
string.split(",").each do |part|
|
137
|
+
if array[i]
|
138
|
+
array[i] << "," << part
|
139
|
+
else
|
140
|
+
# to ensure that array[i] will be String and not Arel::Nodes::SqlLiteral
|
141
|
+
array[i] = part.to_s
|
142
|
+
end
|
143
|
+
i += 1 if array[i].count("(") == array[i].count(")")
|
144
|
+
end
|
145
|
+
array
|
146
|
+
end
|
147
|
+
|
148
|
+
def visit_Arel_Nodes_BindParam(o, collector)
|
149
|
+
collector.add_bind(o.value) { |i| ":a#{i}" }
|
150
|
+
end
|
151
|
+
|
152
|
+
def is_distinct_from(o, collector)
|
153
|
+
collector << "DECODE("
|
154
|
+
collector = visit [o.left, o.right, 0, 1], collector
|
155
|
+
collector << ")"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|