activerecord-sqlserver-adapter 6.0.1 → 6.1.1.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/.github/workflows/ci.yml +26 -0
- data/CHANGELOG.md +29 -46
- data/README.md +32 -3
- data/RUNNING_UNIT_TESTS.md +1 -1
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +5 -10
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +9 -2
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +28 -16
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +8 -7
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -1
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -3
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +31 -9
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +36 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +0 -1
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +2 -1
- data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +100 -69
- data/lib/active_record/connection_adapters/sqlserver_column.rb +75 -19
- data/lib/active_record/sqlserver_base.rb +9 -15
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +17 -14
- data/lib/arel/visitors/sqlserver.rb +125 -40
- data/test/cases/adapter_test_sqlserver.rb +50 -16
- data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
- data/test/cases/coerced_tests.rb +611 -78
- data/test/cases/column_test_sqlserver.rb +9 -2
- data/test/cases/disconnected_test_sqlserver.rb +39 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +9 -0
- data/test/cases/fetch_test_sqlserver.rb +18 -0
- data/test/cases/in_clause_test_sqlserver.rb +27 -0
- data/test/cases/lateral_test_sqlserver.rb +35 -0
- data/test/cases/migration_test_sqlserver.rb +51 -0
- data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
- data/test/cases/order_test_sqlserver.rb +7 -0
- data/test/cases/primary_keys_test_sqlserver.rb +103 -0
- data/test/cases/rake_test_sqlserver.rb +38 -2
- data/test/cases/schema_dumper_test_sqlserver.rb +14 -3
- data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
- data/test/models/sqlserver/composite_pk.rb +9 -0
- data/test/models/sqlserver/sst_string_collation.rb +3 -0
- data/test/schema/sqlserver_specific_schema.rb +25 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
- data/test/support/sql_counter_sqlserver.rb +14 -12
- metadata +29 -9
- data/.travis.yml +0 -23
- data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +0 -28
@@ -2,31 +2,87 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
super
|
9
|
-
end
|
5
|
+
module SQLServer
|
6
|
+
class Column < ConnectionAdapters::Column
|
7
|
+
delegate :is_identity, :is_primary, :table_name, :ordinal_position, to: :sql_type_metadata
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
def initialize(*, is_identity: nil, is_primary: nil, table_name: nil, ordinal_position: nil, **)
|
10
|
+
super
|
11
|
+
@is_identity = is_identity
|
12
|
+
@is_primary = is_primary
|
13
|
+
@table_name = table_name
|
14
|
+
@ordinal_position = ordinal_position
|
15
|
+
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
def is_identity?
|
18
|
+
is_identity
|
19
|
+
end
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
def is_primary?
|
22
|
+
is_primary
|
23
|
+
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
25
|
+
def is_utf8?
|
26
|
+
sql_type =~ /nvarchar|ntext|nchar/i
|
27
|
+
end
|
28
|
+
|
29
|
+
def case_sensitive?
|
30
|
+
collation && collation.match(/_CS/)
|
31
|
+
end
|
26
32
|
|
27
|
-
|
28
|
-
|
33
|
+
def init_with(coder)
|
34
|
+
@is_identity = coder["is_identity"]
|
35
|
+
@is_primary = coder["is_primary"]
|
36
|
+
@table_name = coder["table_name"]
|
37
|
+
@ordinal_position = coder["ordinal_position"]
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
def encode_with(coder)
|
42
|
+
coder["is_identity"] = @is_identity
|
43
|
+
coder["is_primary"] = @is_primary
|
44
|
+
coder["table_name"] = @table_name
|
45
|
+
coder["ordinal_position"] = @ordinal_position
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
def ==(other)
|
50
|
+
other.is_a?(Column) &&
|
51
|
+
super &&
|
52
|
+
is_identity? == other.is_identity? &&
|
53
|
+
is_primary? == other.is_primary? &&
|
54
|
+
table_name == other.table_name &&
|
55
|
+
ordinal_position == other.ordinal_position
|
56
|
+
end
|
57
|
+
alias :eql? :==
|
58
|
+
|
59
|
+
def hash
|
60
|
+
Column.hash ^
|
61
|
+
super.hash ^
|
62
|
+
is_identity?.hash ^
|
63
|
+
is_primary?.hash ^
|
64
|
+
table_name.hash ^
|
65
|
+
ordinal_position.hash
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# In the Rails version of this method there is an assumption that the `default` value will always be a
|
71
|
+
# `String` class, which must be true for the MySQL/PostgreSQL/SQLite adapters. However, in the SQL Server
|
72
|
+
# adapter the `default` value can also be Boolean/Date/Time/etc. Changed the implementation of this method
|
73
|
+
# to handle non-String `default` objects.
|
74
|
+
def deduplicated
|
75
|
+
@name = -name
|
76
|
+
@sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata
|
77
|
+
@default = (default.is_a?(String) ? -default : default.dup.freeze) if default
|
78
|
+
@default_function = -default_function if default_function
|
79
|
+
@collation = -collation if collation
|
80
|
+
@comment = -comment if comment
|
81
|
+
freeze
|
82
|
+
end
|
29
83
|
end
|
84
|
+
|
85
|
+
SQLServerColumn = SQLServer::Column
|
30
86
|
end
|
31
87
|
end
|
32
88
|
end
|
@@ -4,21 +4,15 @@ module ActiveRecord
|
|
4
4
|
module ConnectionHandling
|
5
5
|
def sqlserver_connection(config) #:nodoc:
|
6
6
|
config = config.symbolize_keys
|
7
|
-
config.reverse_merge!
|
8
|
-
mode = config[:mode].to_s.downcase.underscore.to_sym
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
rescue TinyTds::Error => e
|
17
|
-
if e.message.match(/database .* does not exist/i)
|
18
|
-
raise ActiveRecord::NoDatabaseError
|
19
|
-
else
|
20
|
-
raise
|
21
|
-
end
|
7
|
+
config.reverse_merge!(mode: :dblib)
|
8
|
+
config[:mode] = config[:mode].to_s.downcase.underscore.to_sym
|
9
|
+
|
10
|
+
ConnectionAdapters::SQLServerAdapter.new(
|
11
|
+
ConnectionAdapters::SQLServerAdapter.new_client(config),
|
12
|
+
logger,
|
13
|
+
nil,
|
14
|
+
config
|
15
|
+
)
|
22
16
|
end
|
23
17
|
end
|
24
18
|
end
|
@@ -13,13 +13,18 @@ module ActiveRecord
|
|
13
13
|
delegate :connection, :establish_connection, :clear_active_connections!,
|
14
14
|
to: ActiveRecord::Base
|
15
15
|
|
16
|
+
def self.using_database_configurations?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
16
20
|
def initialize(configuration)
|
17
21
|
@configuration = configuration
|
22
|
+
@configuration_hash = @configuration.configuration_hash
|
18
23
|
end
|
19
24
|
|
20
25
|
def create(master_established = false)
|
21
26
|
establish_master_connection unless master_established
|
22
|
-
connection.create_database configuration
|
27
|
+
connection.create_database configuration.database, configuration_hash.merge(collation: default_collation)
|
23
28
|
establish_connection configuration
|
24
29
|
rescue ActiveRecord::StatementInvalid => e
|
25
30
|
if /database .* already exists/i === e.message
|
@@ -31,7 +36,7 @@ module ActiveRecord
|
|
31
36
|
|
32
37
|
def drop
|
33
38
|
establish_master_connection
|
34
|
-
connection.drop_database configuration
|
39
|
+
connection.drop_database configuration.database
|
35
40
|
end
|
36
41
|
|
37
42
|
def charset
|
@@ -49,14 +54,14 @@ module ActiveRecord
|
|
49
54
|
end
|
50
55
|
|
51
56
|
def structure_dump(filename, extra_flags)
|
52
|
-
server_arg = "-S #{Shellwords.escape(
|
53
|
-
server_arg += ":#{Shellwords.escape(
|
57
|
+
server_arg = "-S #{Shellwords.escape(configuration_hash[:host])}"
|
58
|
+
server_arg += ":#{Shellwords.escape(configuration_hash[:port])}" if configuration_hash[:port]
|
54
59
|
command = [
|
55
60
|
"defncopy-ttds",
|
56
61
|
server_arg,
|
57
|
-
"-D #{Shellwords.escape(
|
58
|
-
"-U #{Shellwords.escape(
|
59
|
-
"-P #{Shellwords.escape(
|
62
|
+
"-D #{Shellwords.escape(configuration_hash[:database])}",
|
63
|
+
"-U #{Shellwords.escape(configuration_hash[:username])}",
|
64
|
+
"-P #{Shellwords.escape(configuration_hash[:password])}",
|
60
65
|
"-o #{Shellwords.escape(filename)}",
|
61
66
|
]
|
62
67
|
table_args = connection.tables.map { |t| Shellwords.escape(t) }
|
@@ -80,16 +85,14 @@ module ActiveRecord
|
|
80
85
|
|
81
86
|
private
|
82
87
|
|
83
|
-
|
84
|
-
@configuration
|
85
|
-
end
|
88
|
+
attr_reader :configuration, :configuration_hash
|
86
89
|
|
87
90
|
def default_collation
|
88
|
-
|
91
|
+
configuration_hash[:collation] || DEFAULT_COLLATION
|
89
92
|
end
|
90
93
|
|
91
94
|
def establish_master_connection
|
92
|
-
establish_connection
|
95
|
+
establish_connection configuration_hash.merge(database: "master")
|
93
96
|
end
|
94
97
|
end
|
95
98
|
|
@@ -110,9 +113,9 @@ module ActiveRecord
|
|
110
113
|
end
|
111
114
|
|
112
115
|
def configuration_host_ip(configuration)
|
113
|
-
return nil unless configuration
|
116
|
+
return nil unless configuration.host
|
114
117
|
|
115
|
-
Socket::getaddrinfo(configuration
|
118
|
+
Socket::getaddrinfo(configuration.host, "echo", Socket::AF_INET)[0][3]
|
116
119
|
end
|
117
120
|
|
118
121
|
def local_ipaddr?(host_ip)
|
@@ -11,13 +11,14 @@ module Arel
|
|
11
11
|
|
12
12
|
private
|
13
13
|
|
14
|
-
# SQLServer ToSql/Visitor (
|
14
|
+
# SQLServer ToSql/Visitor (Overrides)
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
BIND_BLOCK = proc { |i| "@#{i - 1}" }
|
17
|
+
private_constant :BIND_BLOCK
|
18
|
+
|
19
|
+
def bind_block; BIND_BLOCK; end
|
19
20
|
|
20
|
-
def visit_Arel_Nodes_Bin
|
21
|
+
def visit_Arel_Nodes_Bin(o, collector)
|
21
22
|
visit o.expr, collector
|
22
23
|
collector << " #{ActiveRecord::ConnectionAdapters::SQLServerAdapter.cs_equality_operator} "
|
23
24
|
end
|
@@ -28,26 +29,26 @@ module Arel
|
|
28
29
|
visit o.right, collector
|
29
30
|
end
|
30
31
|
|
31
|
-
def visit_Arel_Nodes_UpdateStatement(o,
|
32
|
+
def visit_Arel_Nodes_UpdateStatement(o, collector)
|
32
33
|
if o.orders.any? && o.limit.nil?
|
33
34
|
o.limit = Nodes::Limit.new(9_223_372_036_854_775_807)
|
34
35
|
end
|
35
36
|
super
|
36
37
|
end
|
37
38
|
|
38
|
-
def visit_Arel_Nodes_Lock
|
39
|
+
def visit_Arel_Nodes_Lock(o, collector)
|
39
40
|
o.expr = Arel.sql("WITH(UPDLOCK)") if o.expr.to_s =~ /FOR UPDATE/
|
40
41
|
collector << " "
|
41
42
|
visit o.expr, collector
|
42
43
|
end
|
43
44
|
|
44
|
-
def visit_Arel_Nodes_Offset
|
45
|
+
def visit_Arel_Nodes_Offset(o, collector)
|
45
46
|
collector << OFFSET
|
46
47
|
visit o.expr, collector
|
47
48
|
collector << ROWS
|
48
49
|
end
|
49
50
|
|
50
|
-
def visit_Arel_Nodes_Limit
|
51
|
+
def visit_Arel_Nodes_Limit(o, collector)
|
51
52
|
if node_value(o) == 0
|
52
53
|
collector << FETCH0
|
53
54
|
collector << ROWS_ONLY
|
@@ -63,7 +64,38 @@ module Arel
|
|
63
64
|
super
|
64
65
|
end
|
65
66
|
|
66
|
-
def
|
67
|
+
def visit_Arel_Nodes_HomogeneousIn(o, collector)
|
68
|
+
collector.preparable = false
|
69
|
+
|
70
|
+
collector << quote_table_name(o.table_name) << "." << quote_column_name(o.column_name)
|
71
|
+
|
72
|
+
if o.type == :in
|
73
|
+
collector << " IN ("
|
74
|
+
else
|
75
|
+
collector << " NOT IN ("
|
76
|
+
end
|
77
|
+
|
78
|
+
values = o.casted_values
|
79
|
+
|
80
|
+
if values.empty?
|
81
|
+
collector << @connection.quote(nil)
|
82
|
+
elsif @connection.prepared_statements
|
83
|
+
# Monkey-patch start. Add query attribute bindings rather than just values.
|
84
|
+
column_name = o.column_name
|
85
|
+
column_type = o.attribute.relation.type_for_attribute(o.column_name)
|
86
|
+
attrs = values.map { |value| ActiveRecord::Relation::QueryAttribute.new(column_name, value, column_type) }
|
87
|
+
|
88
|
+
collector.add_binds(attrs, &bind_block)
|
89
|
+
# Monkey-patch end.
|
90
|
+
else
|
91
|
+
collector.add_binds(values, &bind_block)
|
92
|
+
end
|
93
|
+
|
94
|
+
collector << ")"
|
95
|
+
collector
|
96
|
+
end
|
97
|
+
|
98
|
+
def visit_Arel_Nodes_SelectStatement(o, collector)
|
67
99
|
@select_statement = o
|
68
100
|
distinct_One_As_One_Is_So_Not_Fetch o
|
69
101
|
if o.with
|
@@ -80,7 +112,17 @@ module Arel
|
|
80
112
|
@select_statement = nil
|
81
113
|
end
|
82
114
|
|
83
|
-
def
|
115
|
+
def visit_Arel_Nodes_SelectCore(o, collector)
|
116
|
+
collector = super
|
117
|
+
maybe_visit o.optimizer_hints, collector
|
118
|
+
end
|
119
|
+
|
120
|
+
def visit_Arel_Nodes_OptimizerHints(o, collector)
|
121
|
+
hints = o.expr.map { |v| sanitize_as_option_clause(v) }.join(", ")
|
122
|
+
collector << "OPTION (#{hints})"
|
123
|
+
end
|
124
|
+
|
125
|
+
def visit_Arel_Table(o, collector)
|
84
126
|
# Apparently, o.engine.connection can actually be a different adapter
|
85
127
|
# than sqlserver. Can be removed if fixed in ActiveRecord. See:
|
86
128
|
# github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450
|
@@ -102,7 +144,7 @@ module Arel
|
|
102
144
|
end
|
103
145
|
end
|
104
146
|
|
105
|
-
def visit_Arel_Nodes_JoinSource
|
147
|
+
def visit_Arel_Nodes_JoinSource(o, collector)
|
106
148
|
if o.left
|
107
149
|
collector = visit o.left, collector
|
108
150
|
collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector
|
@@ -114,39 +156,53 @@ module Arel
|
|
114
156
|
collector
|
115
157
|
end
|
116
158
|
|
117
|
-
def visit_Arel_Nodes_InnerJoin
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
if o.right
|
122
|
-
collector << " "
|
123
|
-
visit(o.right, collector)
|
159
|
+
def visit_Arel_Nodes_InnerJoin(o, collector)
|
160
|
+
if o.left.is_a?(Arel::Nodes::As) && o.left.left.is_a?(Arel::Nodes::Lateral)
|
161
|
+
collector << "CROSS "
|
162
|
+
visit o.left, collector
|
124
163
|
else
|
125
|
-
collector
|
164
|
+
collector << "INNER JOIN "
|
165
|
+
collector = visit o.left, collector
|
166
|
+
collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true
|
167
|
+
if o.right
|
168
|
+
collector << " "
|
169
|
+
visit(o.right, collector)
|
170
|
+
else
|
171
|
+
collector
|
172
|
+
end
|
126
173
|
end
|
127
174
|
end
|
128
175
|
|
129
|
-
def visit_Arel_Nodes_OuterJoin
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
176
|
+
def visit_Arel_Nodes_OuterJoin(o, collector)
|
177
|
+
if o.left.is_a?(Arel::Nodes::As) && o.left.left.is_a?(Arel::Nodes::Lateral)
|
178
|
+
collector << "OUTER "
|
179
|
+
visit o.left, collector
|
180
|
+
else
|
181
|
+
collector << "LEFT OUTER JOIN "
|
182
|
+
collector = visit o.left, collector
|
183
|
+
collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true
|
184
|
+
collector << " "
|
185
|
+
visit o.right, collector
|
186
|
+
end
|
135
187
|
end
|
136
188
|
|
137
|
-
def
|
138
|
-
if Array === right
|
139
|
-
right.each { |node| remove_invalid_ordering_from_select_statement(node) }
|
189
|
+
def visit_Arel_Nodes_In(o, collector)
|
190
|
+
if Array === o.right
|
191
|
+
o.right.each { |node| remove_invalid_ordering_from_select_statement(node) }
|
140
192
|
else
|
141
|
-
remove_invalid_ordering_from_select_statement(right)
|
193
|
+
remove_invalid_ordering_from_select_statement(o.right)
|
142
194
|
end
|
143
195
|
|
144
196
|
super
|
145
197
|
end
|
146
198
|
|
199
|
+
def collect_optimizer_hints(o, collector)
|
200
|
+
collector
|
201
|
+
end
|
202
|
+
|
147
203
|
# SQLServer ToSql/Visitor (Additions)
|
148
204
|
|
149
|
-
def visit_Arel_Nodes_SelectStatement_SQLServer_Lock
|
205
|
+
def visit_Arel_Nodes_SelectStatement_SQLServer_Lock(collector, options = {})
|
150
206
|
if select_statement_lock?
|
151
207
|
collector = visit @select_statement.lock, collector
|
152
208
|
collector << " " if options[:space]
|
@@ -154,7 +210,7 @@ module Arel
|
|
154
210
|
collector
|
155
211
|
end
|
156
212
|
|
157
|
-
def visit_Orders_And_Let_Fetch_Happen
|
213
|
+
def visit_Orders_And_Let_Fetch_Happen(o, collector)
|
158
214
|
make_Fetch_Possible_And_Deterministic o
|
159
215
|
unless o.orders.empty?
|
160
216
|
collector << " ORDER BY "
|
@@ -167,13 +223,25 @@ module Arel
|
|
167
223
|
collector
|
168
224
|
end
|
169
225
|
|
170
|
-
def visit_Make_Fetch_Happen
|
226
|
+
def visit_Make_Fetch_Happen(o, collector)
|
171
227
|
o.offset = Nodes::Offset.new(0) if o.limit && !o.offset
|
172
228
|
collector = visit o.offset, collector if o.offset
|
173
229
|
collector = visit o.limit, collector if o.limit
|
174
230
|
collector
|
175
231
|
end
|
176
232
|
|
233
|
+
def visit_Arel_Nodes_Lateral(o, collector)
|
234
|
+
collector << "APPLY"
|
235
|
+
collector << " "
|
236
|
+
if o.expr.is_a?(Arel::Nodes::SelectStatement)
|
237
|
+
collector << "("
|
238
|
+
visit(o.expr, collector)
|
239
|
+
collector << ")"
|
240
|
+
else
|
241
|
+
visit(o.expr, collector)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
177
245
|
# SQLServer Helpers
|
178
246
|
|
179
247
|
def node_value(node)
|
@@ -190,7 +258,7 @@ module Arel
|
|
190
258
|
@select_statement && @select_statement.lock
|
191
259
|
end
|
192
260
|
|
193
|
-
def make_Fetch_Possible_And_Deterministic
|
261
|
+
def make_Fetch_Possible_And_Deterministic(o)
|
194
262
|
return if o.limit.nil? && o.offset.nil?
|
195
263
|
|
196
264
|
t = table_From_Statement o
|
@@ -203,7 +271,7 @@ module Arel
|
|
203
271
|
end
|
204
272
|
end
|
205
273
|
|
206
|
-
def distinct_One_As_One_Is_So_Not_Fetch
|
274
|
+
def distinct_One_As_One_Is_So_Not_Fetch(o)
|
207
275
|
core = o.cores.first
|
208
276
|
distinct = Nodes::Distinct === core.set_quantifier
|
209
277
|
oneasone = core.projections.all? { |x| x == ActiveRecord::FinderMethods::ONE_AS_ONE }
|
@@ -214,7 +282,7 @@ module Arel
|
|
214
282
|
end
|
215
283
|
end
|
216
284
|
|
217
|
-
def table_From_Statement
|
285
|
+
def table_From_Statement(o)
|
218
286
|
core = o.cores.first
|
219
287
|
if Arel::Table === core.from
|
220
288
|
core.from
|
@@ -225,15 +293,28 @@ module Arel
|
|
225
293
|
end
|
226
294
|
end
|
227
295
|
|
228
|
-
def primary_Key_From_Table
|
296
|
+
def primary_Key_From_Table(t)
|
229
297
|
return unless t
|
230
298
|
|
231
|
-
|
232
|
-
|
299
|
+
primary_keys = @connection.schema_cache.primary_keys(t.name)
|
300
|
+
column_name = nil
|
301
|
+
|
302
|
+
case primary_keys
|
303
|
+
when NilClass
|
304
|
+
column_name = @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
|
305
|
+
when String
|
306
|
+
column_name = primary_keys
|
307
|
+
when Array
|
308
|
+
candidate_columns = @connection.schema_cache.columns_hash(t.name).slice(*primary_keys).values
|
309
|
+
candidate_column = candidate_columns.find(&:is_identity?)
|
310
|
+
candidate_column ||= candidate_columns.first
|
311
|
+
column_name = candidate_column.try(:name)
|
312
|
+
end
|
313
|
+
|
233
314
|
column_name ? t[column_name] : nil
|
234
315
|
end
|
235
316
|
|
236
|
-
def remote_server_table_name
|
317
|
+
def remote_server_table_name(o)
|
237
318
|
ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers(
|
238
319
|
"#{o.class.engine.connection.database_prefix}#{o.name}"
|
239
320
|
).quoted
|
@@ -247,6 +328,10 @@ module Arel
|
|
247
328
|
|
248
329
|
node.orders = [] unless node.offset || node.limit
|
249
330
|
end
|
331
|
+
|
332
|
+
def sanitize_as_option_clause(value)
|
333
|
+
value.gsub(%r{OPTION \s* \( (.+) \)}xi, "\\1")
|
334
|
+
end
|
250
335
|
end
|
251
336
|
end
|
252
337
|
end
|