activerecord-jdbcsqlserver-adapter 50.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.travis.yml +27 -0
  4. data/CHANGELOG.md +124 -0
  5. data/CODE_OF_CONDUCT.md +31 -0
  6. data/Dockerfile +20 -0
  7. data/Gemfile +77 -0
  8. data/Guardfile +29 -0
  9. data/MIT-LICENSE +20 -0
  10. data/RAILS5-TODO.md +5 -0
  11. data/README.md +93 -0
  12. data/RUNNING_UNIT_TESTS.md +96 -0
  13. data/Rakefile +46 -0
  14. data/VERSION +1 -0
  15. data/activerecord-jdbcsqlserver-adapter.gemspec +21 -0
  16. data/appveyor.yml +39 -0
  17. data/docker-compose.ci.yml +11 -0
  18. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +27 -0
  19. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/date_time.rb +58 -0
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +47 -0
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +4 -0
  23. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
  24. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +362 -0
  25. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +67 -0
  26. data/lib/active_record/connection_adapters/sqlserver/errors.rb +7 -0
  27. data/lib/active_record/connection_adapters/sqlserver/jdbc_overrides.rb +192 -0
  28. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +99 -0
  29. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +34 -0
  30. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +16 -0
  31. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +517 -0
  32. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +66 -0
  33. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +66 -0
  34. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +22 -0
  35. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +20 -0
  36. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +112 -0
  37. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +64 -0
  38. data/lib/active_record/connection_adapters/sqlserver/type.rb +49 -0
  39. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +19 -0
  40. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +21 -0
  41. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +15 -0
  42. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +32 -0
  43. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +30 -0
  44. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +61 -0
  45. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +71 -0
  46. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +17 -0
  47. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +23 -0
  48. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +21 -0
  49. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +19 -0
  50. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +15 -0
  51. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +11 -0
  52. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +25 -0
  53. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +19 -0
  54. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +15 -0
  55. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +25 -0
  56. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +29 -0
  57. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
  58. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +19 -0
  59. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +68 -0
  60. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +93 -0
  61. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +19 -0
  62. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +25 -0
  63. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +21 -0
  64. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +12 -0
  65. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +19 -0
  66. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +26 -0
  67. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +24 -0
  68. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +36 -0
  69. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +26 -0
  70. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +24 -0
  71. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +26 -0
  72. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +24 -0
  73. data/lib/active_record/connection_adapters/sqlserver/utils.rb +146 -0
  74. data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
  75. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +445 -0
  76. data/lib/active_record/connection_adapters/sqlserver_column.rb +28 -0
  77. data/lib/active_record/jdbc_sqlserver_connection_methods.rb +31 -0
  78. data/lib/active_record/sqlserver_base.rb +16 -0
  79. data/lib/active_record/tasks/sqlserver_database_tasks.rb +131 -0
  80. data/lib/activerecord-jdbcsqlserver-adapter.rb +24 -0
  81. data/lib/activerecord-sqlserver-adapter.rb +1 -0
  82. data/lib/arel/visitors/sqlserver.rb +205 -0
  83. data/lib/arel_sqlserver.rb +3 -0
  84. data/test/appveyor/dbsetup.ps1 +27 -0
  85. data/test/appveyor/dbsetup.sql +11 -0
  86. data/test/bin/wait-for.sh +79 -0
  87. data/test/cases/adapter_test_sqlserver.rb +430 -0
  88. data/test/cases/coerced_tests.rb +845 -0
  89. data/test/cases/column_test_sqlserver.rb +812 -0
  90. data/test/cases/connection_test_sqlserver.rb +71 -0
  91. data/test/cases/execute_procedure_test_sqlserver.rb +45 -0
  92. data/test/cases/fetch_test_sqlserver.rb +57 -0
  93. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +76 -0
  94. data/test/cases/helper_sqlserver.rb +44 -0
  95. data/test/cases/index_test_sqlserver.rb +47 -0
  96. data/test/cases/json_test_sqlserver.rb +32 -0
  97. data/test/cases/migration_test_sqlserver.rb +61 -0
  98. data/test/cases/order_test_sqlserver.rb +147 -0
  99. data/test/cases/pessimistic_locking_test_sqlserver.rb +94 -0
  100. data/test/cases/rake_test_sqlserver.rb +169 -0
  101. data/test/cases/schema_dumper_test_sqlserver.rb +234 -0
  102. data/test/cases/schema_test_sqlserver.rb +54 -0
  103. data/test/cases/scratchpad_test_sqlserver.rb +8 -0
  104. data/test/cases/showplan_test_sqlserver.rb +65 -0
  105. data/test/cases/specific_schema_test_sqlserver.rb +180 -0
  106. data/test/cases/transaction_test_sqlserver.rb +91 -0
  107. data/test/cases/utils_test_sqlserver.rb +129 -0
  108. data/test/cases/uuid_test_sqlserver.rb +49 -0
  109. data/test/config.yml +38 -0
  110. data/test/debug.rb +14 -0
  111. data/test/fixtures/1px.gif +0 -0
  112. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
  113. data/test/models/sqlserver/booking.rb +3 -0
  114. data/test/models/sqlserver/customers_view.rb +3 -0
  115. data/test/models/sqlserver/datatype.rb +3 -0
  116. data/test/models/sqlserver/datatype_migration.rb +8 -0
  117. data/test/models/sqlserver/dollar_table_name.rb +3 -0
  118. data/test/models/sqlserver/dot_table_name.rb +3 -0
  119. data/test/models/sqlserver/edge_schema.rb +13 -0
  120. data/test/models/sqlserver/fk_has_fk.rb +3 -0
  121. data/test/models/sqlserver/fk_has_pk.rb +3 -0
  122. data/test/models/sqlserver/natural_pk_data.rb +4 -0
  123. data/test/models/sqlserver/natural_pk_int_data.rb +3 -0
  124. data/test/models/sqlserver/no_pk_data.rb +3 -0
  125. data/test/models/sqlserver/object_default.rb +3 -0
  126. data/test/models/sqlserver/quoted_table.rb +7 -0
  127. data/test/models/sqlserver/quoted_view_1.rb +3 -0
  128. data/test/models/sqlserver/quoted_view_2.rb +3 -0
  129. data/test/models/sqlserver/sst_memory.rb +3 -0
  130. data/test/models/sqlserver/string_default.rb +3 -0
  131. data/test/models/sqlserver/string_defaults_big_view.rb +3 -0
  132. data/test/models/sqlserver/string_defaults_view.rb +3 -0
  133. data/test/models/sqlserver/tinyint_pk.rb +3 -0
  134. data/test/models/sqlserver/upper.rb +3 -0
  135. data/test/models/sqlserver/uppered.rb +3 -0
  136. data/test/models/sqlserver/uuid.rb +3 -0
  137. data/test/schema/datatypes/2012.sql +55 -0
  138. data/test/schema/enable-in-memory-oltp.sql +81 -0
  139. data/test/schema/sqlserver_specific_schema.rb +238 -0
  140. data/test/support/coerceable_test_sqlserver.rb +49 -0
  141. data/test/support/connection_reflection.rb +34 -0
  142. data/test/support/load_schema_sqlserver.rb +29 -0
  143. data/test/support/minitest_sqlserver.rb +1 -0
  144. data/test/support/paths_sqlserver.rb +50 -0
  145. data/test/support/rake_helpers.rb +41 -0
  146. data/test/support/sql_counter_sqlserver.rb +28 -0
  147. data/test/support/test_in_memory_oltp.rb +15 -0
  148. metadata +310 -0
@@ -0,0 +1,28 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class SQLServerColumn < Column
4
+
5
+ def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment = nil, sqlserver_options = {})
6
+ @sqlserver_options = sqlserver_options || {}
7
+ super(name, default, sql_type_metadata, null, table_name, default_function, collation, comment: comment)
8
+ end
9
+
10
+ def is_identity?
11
+ @sqlserver_options[:is_identity]
12
+ end
13
+
14
+ def is_primary?
15
+ @sqlserver_options[:is_primary]
16
+ end
17
+
18
+ def is_utf8?
19
+ sql_type =~ /nvarchar|ntext|nchar/i
20
+ end
21
+
22
+ def case_sensitive?
23
+ collation && collation.match(/_CS/)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ module ActiveRecord
2
+ module ConnectionHandling
3
+
4
+ def sqlserver_connection(config)
5
+ config[:adapter_spec] ||= ::ArJdbc::MSSQL
6
+ config[:mode] ||= :jdbc
7
+
8
+ unless jndi_config?(config)
9
+
10
+ config[:host] ||= 'localhost'
11
+ config[:driver] ||= 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
12
+ config[:connection_alive_sql] ||= 'SELECT 1'
13
+
14
+ config[:url] ||= begin
15
+ url = ["jdbc:sqlserver://#{config[:host]}"]
16
+ url << (config[:port] ? ":#{config[:port]};" : ';')
17
+ url << "databaseName=#{config[:database]};" if config[:database]
18
+ url << "instanceName=#{config[:instance]};" if config[:instance]
19
+ app = config[:appname] || config[:application]
20
+ url << "applicationName=#{app};" if app
21
+ isc = config[:integrated_security] # Win only - needs sqljdbc_auth.dll
22
+ url << "integratedSecurity=#{isc};" unless isc.nil?
23
+ url.join('')
24
+ end
25
+ end
26
+
27
+ ConnectionAdapters::SQLServerAdapter.new(nil, nil, config)
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ module ActiveRecord
2
+ module ConnectionHandling
3
+ def sqlserver_connection(config) #:nodoc:
4
+ config = config.symbolize_keys
5
+ config.reverse_merge! mode: :dblib
6
+ mode = config[:mode].to_s.downcase.underscore.to_sym
7
+ case mode
8
+ when :dblib
9
+ require 'tiny_tds'
10
+ else
11
+ raise ArgumentError, "Unknown connection mode in #{config.inspect}."
12
+ end
13
+ ConnectionAdapters::SQLServerAdapter.new(nil, nil, config.merge(mode: mode))
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,131 @@
1
+ require 'active_record/tasks/database_tasks'
2
+ require 'shellwords'
3
+ require 'ipaddr'
4
+ require 'socket'
5
+
6
+ module ActiveRecord
7
+ module Tasks
8
+
9
+ class SQLServerDatabaseTasks
10
+
11
+ DEFAULT_COLLATION = 'SQL_Latin1_General_CP1_CI_AS'
12
+
13
+ delegate :connection, :establish_connection, :clear_active_connections!,
14
+ to: ActiveRecord::Base
15
+
16
+ def initialize(configuration)
17
+ @configuration = configuration
18
+ end
19
+
20
+ def create(master_established = false)
21
+ establish_master_connection unless master_established
22
+ connection.create_database configuration['database'], configuration.merge('collation' => default_collation)
23
+ establish_connection configuration
24
+ rescue ActiveRecord::StatementInvalid => error
25
+ if /database .* already exists/i === error.message
26
+ raise DatabaseAlreadyExists
27
+ else
28
+ raise
29
+ end
30
+ end
31
+
32
+ def drop
33
+ establish_master_connection
34
+ connection.drop_database configuration['database']
35
+ end
36
+
37
+ def charset
38
+ connection.charset
39
+ end
40
+
41
+ def collation
42
+ connection.collation
43
+ end
44
+
45
+ def purge
46
+ clear_active_connections!
47
+ drop
48
+ create true
49
+ end
50
+
51
+ def structure_dump(filename)
52
+ command = [
53
+ "defncopy",
54
+ "-S #{Shellwords.escape(configuration['host'])}",
55
+ "-D #{Shellwords.escape(configuration['database'])}",
56
+ "-U #{Shellwords.escape(configuration['username'])}",
57
+ "-P #{Shellwords.escape(configuration['password'])}",
58
+ "-o #{Shellwords.escape(filename)}",
59
+ ]
60
+ table_args = connection.tables.map { |t| Shellwords.escape(t) }
61
+ command.concat(table_args)
62
+ view_args = connection.views.map { |v| Shellwords.escape(v) }
63
+ command.concat(view_args)
64
+ raise 'Error dumping database' unless Kernel.system(command.join(' '))
65
+ dump = File.read(filename)
66
+ dump.gsub!(/^USE .*$\nGO\n/, '') # Strip db USE statements
67
+ dump.gsub!(/^GO\n/, '') # Strip db GO statements
68
+ dump.gsub!(/nvarchar\(8000\)/, 'nvarchar(4000)') # Fix nvarchar(8000) column defs
69
+ dump.gsub!(/nvarchar\(-1\)/, 'nvarchar(max)') # Fix nvarchar(-1) column defs
70
+ dump.gsub!(/text\(\d+\)/, 'text') # Fix text(16) column defs
71
+ File.open(filename, "w") { |file| file.puts dump }
72
+ end
73
+
74
+ def structure_load(filename)
75
+ connection.execute File.read(filename)
76
+ end
77
+
78
+
79
+ private
80
+
81
+ def configuration
82
+ @configuration
83
+ end
84
+
85
+ def default_collation
86
+ configuration['collation'] || DEFAULT_COLLATION
87
+ end
88
+
89
+ def establish_master_connection
90
+ establish_connection configuration.merge('database' => 'master')
91
+ end
92
+
93
+ end
94
+
95
+ module DatabaseTasksSQLServer
96
+
97
+ extend ActiveSupport::Concern
98
+
99
+ module ClassMethods
100
+
101
+ LOCAL_IPADDR = [
102
+ IPAddr.new('192.168.0.0/16'),
103
+ IPAddr.new('10.0.0.0/8'),
104
+ IPAddr.new('172.16.0.0/12')
105
+ ]
106
+
107
+ private
108
+
109
+ def local_database?(configuration)
110
+ super || local_ipaddr?(configuration_host_ip(configuration))
111
+ end
112
+
113
+ def configuration_host_ip(configuration)
114
+ return nil unless configuration['host']
115
+ Socket::getaddrinfo(configuration['host'], 'echo', Socket::AF_INET)[0][3]
116
+ end
117
+
118
+ def local_ipaddr?(host_ip)
119
+ return false unless host_ip
120
+ LOCAL_IPADDR.any? { |ip| ip.include?(host_ip) }
121
+ end
122
+
123
+ end
124
+
125
+ end
126
+
127
+ DatabaseTasks.register_task %r{sqlserver}, SQLServerDatabaseTasks
128
+ DatabaseTasks.send :include, DatabaseTasksSQLServer
129
+
130
+ end
131
+ end
@@ -0,0 +1,24 @@
1
+ # Our core date/time overrides to support prepared statements
2
+ require 'active_record/connection_adapters/sqlserver/core_ext/date_time'
3
+
4
+ # Load the jar file for the jdbc driver
5
+ require 'jdbc/mssql'
6
+
7
+ # Standadard arjdbc functionality
8
+ require 'arjdbc/abstract/connection_management'
9
+ require 'arjdbc/abstract/core'
10
+ require 'arjdbc/abstract/database_statements'
11
+ require 'arjdbc/abstract/statement_cache'
12
+ require 'arjdbc/abstract/transaction_support'
13
+ require 'arjdbc/util/quoted_cache'
14
+
15
+ # A module for prepending functionality
16
+ require 'active_record/connection_adapters/sqlserver/jdbc_overrides'
17
+
18
+ # Load the actual adapter
19
+ require_relative './activerecord-sqlserver-adapter'
20
+
21
+ # Load overrides and java specifics
22
+ require 'arjdbc'
23
+ ArJdbc.load_java_part :MSSQL
24
+ require 'active_record/jdbc_sqlserver_connection_methods'
@@ -0,0 +1 @@
1
+ require 'active_record/connection_adapters/sqlserver_adapter'
@@ -0,0 +1,205 @@
1
+ module Arel
2
+ module Visitors
3
+ class SQLServer < Arel::Visitors::ToSql
4
+
5
+ OFFSET = " OFFSET "
6
+ ROWS = " ROWS"
7
+ FETCH = " FETCH NEXT "
8
+ FETCH0 = " FETCH FIRST (SELECT 0) "
9
+ ROWS_ONLY = " ROWS ONLY"
10
+
11
+
12
+ private
13
+
14
+ # SQLServer ToSql/Visitor (Overides)
15
+
16
+ def visit_Arel_Nodes_BindParam o, collector
17
+ collector.add_bind(o) { |i| "@#{i-1}" }
18
+ end unless defined? JRUBY_VERSION # converts bind argument markers "?" to "@n", but JDBC wants "?"
19
+
20
+ def visit_Arel_Nodes_Bin o, collector
21
+ visit o.expr, collector
22
+ collector << " #{ActiveRecord::ConnectionAdapters::SQLServerAdapter.cs_equality_operator} "
23
+ end
24
+
25
+ def visit_Arel_Nodes_UpdateStatement(o, a)
26
+ if o.orders.any? && o.limit.nil?
27
+ o.limit = Nodes::Limit.new(9_223_372_036_854_775_807)
28
+ end
29
+ super
30
+ end
31
+
32
+ def visit_Arel_Nodes_Lock o, collector
33
+ o.expr = Arel.sql('WITH(UPDLOCK)') if o.expr.to_s =~ /FOR UPDATE/
34
+ collector << SPACE
35
+ visit o.expr, collector
36
+ end
37
+
38
+ def visit_Arel_Nodes_Offset o, collector
39
+ collector << OFFSET
40
+ visit o.expr, collector
41
+ collector << ROWS
42
+ end
43
+
44
+ def visit_Arel_Nodes_Limit o, collector
45
+ if node_value(o) == 0
46
+ collector << FETCH0
47
+ collector << ROWS_ONLY
48
+ else
49
+ collector << FETCH
50
+ visit o.expr, collector
51
+ collector << ROWS_ONLY
52
+ end
53
+ end
54
+
55
+ def visit_Arel_Nodes_SelectStatement o, collector
56
+ @select_statement = o
57
+ distinct_One_As_One_Is_So_Not_Fetch o
58
+ if o.with
59
+ collector = visit o.with, collector
60
+ collector << SPACE
61
+ end
62
+ collector = o.cores.inject(collector) { |c,x|
63
+ visit_Arel_Nodes_SelectCore(x, c)
64
+ }
65
+ collector = visit_Orders_And_Let_Fetch_Happen o, collector
66
+ collector = visit_Make_Fetch_Happen o, collector
67
+ collector
68
+ ensure
69
+ @select_statement = nil
70
+ end
71
+
72
+ def visit_Arel_Table o, collector
73
+ # Apparently, o.engine.connection can actually be a different adapter
74
+ # than sqlserver. Can be removed if fixed in ActiveRecord. See:
75
+ # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450
76
+ table_name = begin
77
+ if o.class.engine.connection.respond_to?(:sqlserver?) && o.class.engine.connection.database_prefix_remote_server?
78
+ remote_server_table_name(o)
79
+ else
80
+ quote_table_name(o.name)
81
+ end
82
+ rescue Exception => e
83
+ quote_table_name(o.name)
84
+ end
85
+ if o.table_alias
86
+ collector << "#{table_name} #{quote_table_name o.table_alias}"
87
+ else
88
+ collector << table_name
89
+ end
90
+ end
91
+
92
+ def visit_Arel_Nodes_JoinSource o, collector
93
+ if o.left
94
+ collector = visit o.left, collector
95
+ collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector
96
+ end
97
+ if o.right.any?
98
+ collector << " " if o.left
99
+ collector = inject_join o.right, collector, ' '
100
+ end
101
+ collector
102
+ end
103
+
104
+ def visit_Arel_Nodes_OuterJoin o, collector
105
+ collector << "LEFT OUTER JOIN "
106
+ collector = visit o.left, collector
107
+ collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true
108
+ collector << " "
109
+ visit o.right, collector
110
+ end
111
+
112
+ # SQLServer ToSql/Visitor (Additions)
113
+
114
+ def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {}
115
+ if select_statement_lock?
116
+ collector = visit @select_statement.lock, collector
117
+ collector << SPACE if options[:space]
118
+ end
119
+ collector
120
+ end
121
+
122
+ def visit_Orders_And_Let_Fetch_Happen o, collector
123
+ make_Fetch_Possible_And_Deterministic o
124
+ unless o.orders.empty?
125
+ collector << SPACE
126
+ collector << ORDER_BY
127
+ len = o.orders.length - 1
128
+ o.orders.each_with_index { |x, i|
129
+ collector = visit(x, collector)
130
+ collector << COMMA unless len == i
131
+ }
132
+ end
133
+ collector
134
+ end
135
+
136
+ def visit_Make_Fetch_Happen o, collector
137
+ o.offset = Nodes::Offset.new(0) if o.limit && !o.offset
138
+ collector = visit o.offset, collector if o.offset
139
+ collector = visit o.limit, collector if o.limit
140
+ collector
141
+ end
142
+
143
+ # SQLServer Helpers
144
+
145
+ def node_value(node)
146
+ return nil unless node
147
+ case node.expr
148
+ when NilClass then nil
149
+ when Numeric then node.expr
150
+ when Arel::Nodes::Unary then node.expr.expr
151
+ end
152
+ end
153
+
154
+ def select_statement_lock?
155
+ @select_statement && @select_statement.lock
156
+ end
157
+
158
+ def make_Fetch_Possible_And_Deterministic o
159
+ return if o.limit.nil? && o.offset.nil?
160
+ t = table_From_Statement o
161
+ pk = primary_Key_From_Table t
162
+ return unless pk
163
+ if o.orders.empty?
164
+ # Prefer deterministic vs a simple `(SELECT NULL)` expr.
165
+ o.orders = [pk.asc]
166
+ end
167
+ end
168
+
169
+ def distinct_One_As_One_Is_So_Not_Fetch o
170
+ core = o.cores.first
171
+ distinct = Nodes::Distinct === core.set_quantifier
172
+ oneasone = core.projections.all? { |x| x == ActiveRecord::FinderMethods::ONE_AS_ONE }
173
+ limitone = [nil, 0, 1].include? node_value(o.limit)
174
+ if distinct && oneasone && limitone && !o.offset
175
+ core.projections = [Arel.sql("TOP(1) 1 AS [one]")]
176
+ o.limit = nil
177
+ end
178
+ end
179
+
180
+ def table_From_Statement o
181
+ core = o.cores.first
182
+ if Arel::Table === core.from
183
+ core.from
184
+ elsif Arel::Nodes::SqlLiteral === core.from
185
+ Arel::Table.new(core.from)
186
+ elsif Arel::Nodes::JoinSource === core.source
187
+ Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left
188
+ end
189
+ end
190
+
191
+ def primary_Key_From_Table t
192
+ return unless t
193
+ column_name = schema_cache.primary_keys(t.name) || column_cache(t.name).first.try(:second).try(:name)
194
+ column_name ? t[column_name] : nil
195
+ end
196
+
197
+ def remote_server_table_name o
198
+ ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers(
199
+ "#{o.class.engine.connection.database_prefix}#{o.name}"
200
+ ).quoted
201
+ end
202
+
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,3 @@
1
+ require 'arel'
2
+ require 'arel/visitors/bind_visitor'
3
+ require 'arel/visitors/sqlserver'