neo-activerecord-jdbc-adapter 5.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.travis.yml +438 -0
  4. data/.yardopts +4 -0
  5. data/Appraisals +41 -0
  6. data/CONTRIBUTING.md +44 -0
  7. data/Gemfile +62 -0
  8. data/History.md +1191 -0
  9. data/LICENSE.txt +25 -0
  10. data/README.md +266 -0
  11. data/RUNNING_TESTS.md +88 -0
  12. data/Rakefile +100 -0
  13. data/Rakefile.jdbc +20 -0
  14. data/activerecord-jdbc-adapter.gemspec +42 -0
  15. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  16. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  17. data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
  18. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  19. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
  20. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
  21. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  22. data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
  23. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  24. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  25. data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
  26. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
  28. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
  31. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  32. data/lib/activerecord-jdbc-adapter.rb +1 -0
  33. data/lib/arel/visitors/compat.rb +60 -0
  34. data/lib/arel/visitors/db2.rb +137 -0
  35. data/lib/arel/visitors/derby.rb +112 -0
  36. data/lib/arel/visitors/firebird.rb +79 -0
  37. data/lib/arel/visitors/h2.rb +25 -0
  38. data/lib/arel/visitors/hsqldb.rb +32 -0
  39. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  40. data/lib/arel/visitors/sql_server.rb +225 -0
  41. data/lib/arel/visitors/sql_server/ng42.rb +293 -0
  42. data/lib/arjdbc.rb +19 -0
  43. data/lib/arjdbc/abstract/database_statements.rb +92 -0
  44. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  45. data/lib/arjdbc/common_jdbc_methods.rb +13 -0
  46. data/lib/arjdbc/db2.rb +4 -0
  47. data/lib/arjdbc/db2/adapter.rb +789 -0
  48. data/lib/arjdbc/db2/as400.rb +130 -0
  49. data/lib/arjdbc/db2/column.rb +167 -0
  50. data/lib/arjdbc/db2/connection_methods.rb +44 -0
  51. data/lib/arjdbc/derby.rb +3 -0
  52. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  53. data/lib/arjdbc/derby/adapter.rb +556 -0
  54. data/lib/arjdbc/derby/connection_methods.rb +20 -0
  55. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  56. data/lib/arjdbc/discover.rb +115 -0
  57. data/lib/arjdbc/firebird.rb +4 -0
  58. data/lib/arjdbc/firebird/adapter.rb +434 -0
  59. data/lib/arjdbc/firebird/connection_methods.rb +23 -0
  60. data/lib/arjdbc/h2.rb +3 -0
  61. data/lib/arjdbc/h2/adapter.rb +303 -0
  62. data/lib/arjdbc/h2/connection_methods.rb +27 -0
  63. data/lib/arjdbc/hsqldb.rb +3 -0
  64. data/lib/arjdbc/hsqldb/adapter.rb +297 -0
  65. data/lib/arjdbc/hsqldb/connection_methods.rb +28 -0
  66. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  67. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  68. data/lib/arjdbc/informix.rb +5 -0
  69. data/lib/arjdbc/informix/adapter.rb +162 -0
  70. data/lib/arjdbc/informix/connection_methods.rb +9 -0
  71. data/lib/arjdbc/jdbc.rb +59 -0
  72. data/lib/arjdbc/jdbc/adapter.rb +899 -0
  73. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  74. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  75. data/lib/arjdbc/jdbc/base_ext.rb +44 -0
  76. data/lib/arjdbc/jdbc/callbacks.rb +51 -0
  77. data/lib/arjdbc/jdbc/column.rb +97 -0
  78. data/lib/arjdbc/jdbc/connection.rb +133 -0
  79. data/lib/arjdbc/jdbc/connection_methods.rb +36 -0
  80. data/lib/arjdbc/jdbc/driver.rb +43 -0
  81. data/lib/arjdbc/jdbc/extension.rb +59 -0
  82. data/lib/arjdbc/jdbc/java.rb +15 -0
  83. data/lib/arjdbc/jdbc/jdbc.rake +4 -0
  84. data/lib/arjdbc/jdbc/quoted_primary_key.rb +28 -0
  85. data/lib/arjdbc/jdbc/railtie.rb +2 -0
  86. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -0
  87. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
  88. data/lib/arjdbc/jdbc/type_cast.rb +166 -0
  89. data/lib/arjdbc/jdbc/type_converter.rb +142 -0
  90. data/lib/arjdbc/mimer.rb +3 -0
  91. data/lib/arjdbc/mimer/adapter.rb +142 -0
  92. data/lib/arjdbc/mssql.rb +7 -0
  93. data/lib/arjdbc/mssql/adapter.rb +808 -0
  94. data/lib/arjdbc/mssql/column.rb +200 -0
  95. data/lib/arjdbc/mssql/connection_methods.rb +79 -0
  96. data/lib/arjdbc/mssql/explain_support.rb +99 -0
  97. data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
  98. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  99. data/lib/arjdbc/mssql/types.rb +343 -0
  100. data/lib/arjdbc/mssql/utils.rb +82 -0
  101. data/lib/arjdbc/mysql.rb +3 -0
  102. data/lib/arjdbc/mysql/adapter.rb +1006 -0
  103. data/lib/arjdbc/mysql/bulk_change_table.rb +150 -0
  104. data/lib/arjdbc/mysql/column.rb +162 -0
  105. data/lib/arjdbc/mysql/connection_methods.rb +145 -0
  106. data/lib/arjdbc/mysql/explain_support.rb +82 -0
  107. data/lib/arjdbc/mysql/schema_creation.rb +58 -0
  108. data/lib/arjdbc/oracle.rb +4 -0
  109. data/lib/arjdbc/oracle/adapter.rb +952 -0
  110. data/lib/arjdbc/oracle/column.rb +126 -0
  111. data/lib/arjdbc/oracle/connection_methods.rb +21 -0
  112. data/lib/arjdbc/postgresql.rb +3 -0
  113. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
  114. data/lib/arjdbc/postgresql/adapter.rb +825 -0
  115. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  116. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  117. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  118. data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
  119. data/lib/arjdbc/postgresql/column.rb +51 -0
  120. data/lib/arjdbc/postgresql/connection_methods.rb +54 -0
  121. data/lib/arjdbc/postgresql/name.rb +24 -0
  122. data/lib/arjdbc/postgresql/oid_types.rb +178 -0
  123. data/lib/arjdbc/railtie.rb +11 -0
  124. data/lib/arjdbc/sqlite3.rb +3 -0
  125. data/lib/arjdbc/sqlite3/adapter.rb +703 -0
  126. data/lib/arjdbc/sqlite3/connection_methods.rb +40 -0
  127. data/lib/arjdbc/sybase.rb +2 -0
  128. data/lib/arjdbc/sybase/adapter.rb +47 -0
  129. data/lib/arjdbc/tasks.rb +13 -0
  130. data/lib/arjdbc/tasks/database_tasks.rb +54 -0
  131. data/lib/arjdbc/tasks/databases.rake +91 -0
  132. data/lib/arjdbc/tasks/databases3.rake +215 -0
  133. data/lib/arjdbc/tasks/databases4.rake +39 -0
  134. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  135. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  136. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  137. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  138. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  139. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  140. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
  141. data/lib/arjdbc/tasks/oracle_database_tasks.rb +65 -0
  142. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  143. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  144. data/lib/arjdbc/util/table_copier.rb +110 -0
  145. data/lib/arjdbc/version.rb +8 -0
  146. data/lib/generators/jdbc/USAGE +9 -0
  147. data/lib/generators/jdbc/jdbc_generator.rb +17 -0
  148. data/lib/jdbc_adapter.rb +2 -0
  149. data/lib/jdbc_adapter/rake_tasks.rb +4 -0
  150. data/lib/jdbc_adapter/version.rb +4 -0
  151. data/pom.xml +114 -0
  152. data/rails_generators/jdbc_generator.rb +15 -0
  153. data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
  154. data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
  155. data/rakelib/01-tomcat.rake +51 -0
  156. data/rakelib/02-test.rake +121 -0
  157. data/rakelib/bundler_ext.rb +11 -0
  158. data/rakelib/compile.rake +62 -0
  159. data/rakelib/db.rake +58 -0
  160. data/rakelib/rails.rake +75 -0
  161. data/src/java/arjdbc/ArJdbcModule.java +178 -0
  162. data/src/java/arjdbc/db2/DB2Module.java +71 -0
  163. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +142 -0
  164. data/src/java/arjdbc/derby/DerbyModule.java +179 -0
  165. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +164 -0
  166. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
  167. data/src/java/arjdbc/h2/H2Module.java +44 -0
  168. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +67 -0
  169. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +68 -0
  170. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +75 -0
  171. data/src/java/arjdbc/jdbc/AdapterJavaService.java +45 -0
  172. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  173. data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +45 -0
  174. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3616 -0
  175. data/src/java/arjdbc/jdbc/SQLBlock.java +54 -0
  176. data/src/java/arjdbc/mssql/MSSQLModule.java +102 -0
  177. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +195 -0
  178. data/src/java/arjdbc/mysql/MySQLModule.java +147 -0
  179. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +397 -0
  180. data/src/java/arjdbc/oracle/OracleModule.java +75 -0
  181. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +465 -0
  182. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +752 -0
  183. data/src/java/arjdbc/sqlite3/SQLite3Module.java +78 -0
  184. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +351 -0
  185. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  186. data/src/java/arjdbc/util/QuotingUtils.java +111 -0
  187. metadata +255 -0
@@ -0,0 +1,25 @@
1
+ require 'arel/visitors/compat'
2
+ require 'arel/visitors/hsqldb'
3
+
4
+ module Arel
5
+ module Visitors
6
+ class H2 < Arel::Visitors::HSQLDB
7
+ def visit_Arel_Nodes_SelectStatement(o, *)
8
+ o.limit ||= Arel::Nodes::Limit.new(-1) if o.offset
9
+ super
10
+ end if ArJdbc::AR42
11
+
12
+ def limit_offset sql, o
13
+ offset = o.offset || 0
14
+ offset = offset.expr unless (offset.nil? || offset == 0)
15
+ if limit = o.limit
16
+ "SELECT LIMIT #{offset} #{limit_for(limit)} #{sql[7..-1]}"
17
+ elsif offset > 0
18
+ "SELECT LIMIT #{offset} -1 #{sql[7..-1]}" # removes "SELECT "
19
+ else
20
+ sql
21
+ end
22
+ end unless ArJdbc::AR42
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ require 'arel/visitors/compat'
2
+
3
+ module Arel
4
+ module Visitors
5
+ class HSQLDB < Arel::Visitors::ToSql
6
+ def visit_Arel_Nodes_SelectStatement(o, *)
7
+ o.limit ||= Arel::Nodes::Limit.new(0) if o.offset
8
+ super
9
+ end if ArJdbc::AR42
10
+
11
+ def visit_Arel_Nodes_SelectStatement o, a = nil
12
+ sql = limit_offset(o.cores.map { |x| do_visit_select_core x, a }.join, o)
13
+ sql << " ORDER BY #{o.orders.map { |x| do_visit x, a }.join(', ')}" unless o.orders.empty?
14
+ sql
15
+ end unless ArJdbc::AR42
16
+
17
+ private
18
+
19
+ def limit_offset sql, o
20
+ offset = o.offset || 0
21
+ offset = offset.expr unless (offset.nil? || offset == 0)
22
+ if limit = o.limit
23
+ "SELECT LIMIT #{offset} #{limit_for(limit)} #{sql[7..-1]}"
24
+ elsif offset > 0
25
+ "SELECT LIMIT #{offset} 0 #{sql[7..-1]}" # removes "SELECT "
26
+ else
27
+ sql
28
+ end
29
+ end unless ArJdbc::AR42
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,6 @@
1
+ require 'arel/visitors/compat'
2
+
3
+ class Arel::Visitors::PostgreSQL
4
+ # AREL converts bind argument markers "?" to "$n" for PG, but JDBC wants "?".
5
+ remove_method :visit_Arel_Nodes_BindParam if ArJdbc::AR42
6
+ end
@@ -0,0 +1,225 @@
1
+ require 'arel/visitors/compat'
2
+
3
+ module Arel
4
+ module Visitors
5
+ ToSql.class_eval do
6
+ alias_method :_visit_Arel_Nodes_SelectStatement, :visit_Arel_Nodes_SelectStatement
7
+ end
8
+ # @note AREL set's up `Arel::Visitors::MSSQL` but its not usable as is ...
9
+ # @private
10
+ class SQLServer < const_defined?(:MSSQL) ? MSSQL : ToSql
11
+
12
+ private
13
+
14
+ def visit_Arel_Nodes_SelectStatement(*args) # [o] AR <= 4.0 [o, a] on 4.1
15
+ o, a = args.first, args.last
16
+
17
+ return _visit_Arel_Nodes_SelectStatement(*args) if ! o.limit && ! o.offset
18
+
19
+ unless o.orders.empty?
20
+ select_order_by = do_visit_columns o.orders, a, 'ORDER BY '
21
+ end
22
+
23
+ select_count = false; sql = ''
24
+ o.cores.each do |x|
25
+ x = x.dup
26
+ core_order_by = select_order_by || determine_order_by(x, a)
27
+ if select_count? x
28
+ x.projections = [
29
+ Arel::Nodes::SqlLiteral.new(core_order_by ? over_row_num(core_order_by) : '*')
30
+ ]
31
+ select_count = true
32
+ else
33
+ # NOTE: this should really be added here and we should built the
34
+ # wrapping SQL but than #replace_limit_offset! assumes it does that
35
+ # ... MS-SQL adapter code seems to be 'hacked' by a lot of people
36
+ #x.projections << Arel::Nodes::SqlLiteral.new over_row_num(order_by)
37
+ end
38
+ sql << do_visit_select_core(x, a)
39
+ end
40
+
41
+ #sql = "SELECT _t.* FROM (#{sql}) as _t WHERE #{get_offset_limit_clause(o)}"
42
+ select_order_by ||= "ORDER BY #{@connection.determine_order_clause(sql)}"
43
+ replace_limit_offset!(sql, limit_for(o.limit), o.offset && o.offset.value.to_i, select_order_by)
44
+
45
+ sql = "SELECT COUNT(*) AS count_id FROM (#{sql}) AS subquery" if select_count
46
+
47
+ add_lock!(sql, :lock => o.lock && true)
48
+
49
+ sql
50
+ end unless ArJdbc::AR42
51
+
52
+ # @private
53
+ MAX_LIMIT_VALUE = 9_223_372_036_854_775_807
54
+
55
+ def visit_Arel_Nodes_UpdateStatement(*args) # [o] AR <= 4.0 [o, a] on 4.1
56
+ o = args.first
57
+ if o.orders.any? && o.limit.nil?
58
+ o.limit = Nodes::Limit.new(MAX_LIMIT_VALUE)
59
+ end
60
+ super
61
+ end
62
+
63
+ def visit_Arel_Nodes_Lock o, a = nil
64
+ # MS-SQL doesn't support "SELECT...FOR UPDATE". Instead, it needs
65
+ # WITH(ROWLOCK,UPDLOCK) specified after each table in the FROM clause.
66
+ #
67
+ # we return nothing here and add the appropriate stuff with #add_lock!
68
+ #do_visit o.expr, a
69
+ end unless ArJdbc::AR42
70
+
71
+ def visit_Arel_Nodes_Top o, a = nil
72
+ # `top` wouldn't really work here:
73
+ # User.select("distinct first_name").limit(10)
74
+ # would generate "select top 10 distinct first_name from users",
75
+ # which is invalid should be "select distinct top 10 first_name ..."
76
+ a || ''
77
+ end
78
+
79
+ def visit_Arel_Nodes_Limit o, a = nil
80
+ "TOP (#{do_visit o.expr, a})"
81
+ end unless ArJdbc::AR42
82
+
83
+ def visit_Arel_Nodes_Ordering o, a = nil
84
+ expr = do_visit o.expr, a
85
+ if o.respond_to?(:direction)
86
+ "#{expr} #{o.ascending? ? 'ASC' : 'DESC'}"
87
+ else
88
+ expr
89
+ end
90
+ end unless ArJdbc::AR42
91
+
92
+ def visit_Arel_Nodes_Bin o, a = nil
93
+ expr = o.expr; sql = do_visit expr, a
94
+ if expr.respond_to?(:val) && expr.val.is_a?(Numeric)
95
+ sql
96
+ else
97
+ sql << " #{::ArJdbc::MSSQL.cs_equality_operator} "
98
+ sql
99
+ end
100
+ end unless ArJdbc::AR42
101
+
102
+ private
103
+
104
+ def self.possibly_private_method_defined?(name)
105
+ private_method_defined?(name) || method_defined?(name)
106
+ end
107
+
108
+ def select_count? x
109
+ x.projections.length == 1 && Arel::Nodes::Count === x.projections.first
110
+ end unless possibly_private_method_defined? :select_count?
111
+
112
+ def determine_order_by x, a
113
+ unless x.groups.empty?
114
+ do_visit_columns x.groups, a, 'ORDER BY '
115
+ else
116
+ table_pk = find_left_table_pk(x)
117
+ table_pk && "ORDER BY #{table_pk}"
118
+ end
119
+ end
120
+
121
+ def find_left_table_pk o
122
+ primary_key_from_table table_from_select_core(o)
123
+ end
124
+
125
+ def do_visit_columns(colls, a, sql)
126
+ last = colls.size - 1
127
+ colls.each_with_index do |x, i|
128
+ sql << do_visit(x, a); sql << ', ' unless i == last
129
+ end
130
+ sql
131
+ end
132
+
133
+ def do_visit_columns(colls, a, sql)
134
+ prefix = sql
135
+ sql = Arel::Collectors::PlainString.new
136
+ sql << prefix if prefix
137
+
138
+ last = colls.size - 1
139
+ colls.each_with_index do |x, i|
140
+ visit(x, sql); sql << ', ' unless i == last
141
+ end
142
+ sql.value
143
+ end if ArJdbc::AR42
144
+
145
+ def do_visit_columns(colls, a, sql)
146
+ non_simple_order = /\sASC|\sDESC|\sCASE|\sCOLLATE|[\.,\[\(]/i # MIN(width)
147
+
148
+ last = colls.size - 1
149
+ colls.each_with_index do |x, i|
150
+ coll = do_visit(x, a)
151
+
152
+ if coll !~ non_simple_order && coll.to_i == 0
153
+ sql << @connection.quote_column_name(coll)
154
+ else
155
+ sql << coll
156
+ end
157
+
158
+ sql << ', ' unless i == last
159
+ end
160
+ sql
161
+ end if Arel::VERSION < '4.0.0'
162
+
163
+ def over_row_num order_by
164
+ "ROW_NUMBER() OVER (#{order_by}) as _row_num"
165
+ end # unless possibly_private_method_defined? :row_num_literal
166
+
167
+ def table_from_select_core core
168
+ if Arel::Table === core.from
169
+ core.from
170
+ elsif Arel::Nodes::SqlLiteral === core.from
171
+ Arel::Table.new(core.from, @engine)
172
+ elsif Arel::Nodes::JoinSource === core.source
173
+ Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left
174
+ end
175
+ end
176
+
177
+ def table_from_select_core core
178
+ table_finder = lambda do |x|
179
+ case x
180
+ when Arel::Table
181
+ x
182
+ when Arel::Nodes::SqlLiteral
183
+ Arel::Table.new(x, @engine)
184
+ when Arel::Nodes::Join
185
+ table_finder.call(x.left)
186
+ end
187
+ end
188
+ table_finder.call(core.froms)
189
+ end if ActiveRecord::VERSION::STRING < '3.2'
190
+
191
+ def primary_key_from_table t
192
+ return unless t
193
+ return t.primary_key if t.primary_key
194
+
195
+ engine = t.engine
196
+ if engine_pk = engine.primary_key
197
+ pk = engine.arel_table[engine_pk]
198
+ return pk if pk
199
+ end
200
+
201
+ pk = (@primary_keys ||= {}).fetch(table_name = engine.table_name) do
202
+ pk_name = @connection.primary_key(table_name)
203
+ # some tables might be without primary key
204
+ @primary_keys[table_name] = pk_name && t[pk_name]
205
+ end
206
+ return pk if pk
207
+
208
+ column_name = engine.columns.first.try(:name)
209
+ column_name && t[column_name]
210
+ end
211
+
212
+ include ArJdbc::MSSQL::LockMethods
213
+
214
+ include ArJdbc::MSSQL::LimitHelpers::SqlServerReplaceLimitOffset
215
+
216
+ end
217
+
218
+ class SQLServer2000 < SQLServer
219
+ include ArJdbc::MSSQL::LimitHelpers::SqlServer2000ReplaceLimitOffset
220
+ end
221
+
222
+ load 'arel/visitors/sql_server/ng42.rb' if ArJdbc::AR42
223
+
224
+ end
225
+ end
@@ -0,0 +1,293 @@
1
+ module Arel
2
+ module Visitors
3
+ class SQLServerNG < 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
+ private
12
+
13
+ # SQLServer ToSql/Visitor (Overides)
14
+
15
+ #def visit_Arel_Nodes_BindParam o, collector
16
+ # collector.add_bind(o) { |i| "@#{i-1}" }
17
+ #end
18
+
19
+ def visit_Arel_Nodes_Bin o, collector
20
+ visit o.expr, collector
21
+ if o.expr.val.is_a? Numeric
22
+ collector
23
+ else
24
+ collector << " #{::ArJdbc::MSSQL.cs_equality_operator} "
25
+ end
26
+ end
27
+
28
+ def visit_Arel_Nodes_UpdateStatement(o, a)
29
+ if o.orders.any? && o.limit.nil?
30
+ o.limit = Nodes::Limit.new(9_223_372_036_854_775_807)
31
+ end
32
+ super
33
+ end
34
+
35
+ def visit_Arel_Nodes_Lock o, collector
36
+ o.expr = Arel.sql('WITH(UPDLOCK)') if o.expr.to_s =~ /FOR UPDATE/
37
+ collector << SPACE
38
+ visit o.expr, collector
39
+ end
40
+
41
+ def visit_Arel_Nodes_Offset o, collector
42
+ collector << OFFSET
43
+ visit o.expr, collector
44
+ collector << ROWS
45
+ end
46
+
47
+ def visit_Arel_Nodes_Limit o, collector
48
+ if node_value(o) == 0
49
+ collector << FETCH0
50
+ collector << ROWS_ONLY
51
+ else
52
+ collector << FETCH
53
+ visit o.expr, collector
54
+ collector << ROWS_ONLY
55
+ end
56
+ end
57
+
58
+ def visit_Arel_Nodes_SelectStatement o, collector
59
+ distinct_One_As_One_Is_So_Not_Fetch o
60
+
61
+ set_select_statement_lock o.lock
62
+
63
+ if o.with
64
+ collector = visit o.with, collector
65
+ collector << SPACE
66
+ end
67
+
68
+ return _visit_Arel_Nodes_SelectStatement(o, collector) if ! o.limit && ! o.offset
69
+
70
+ # collector = o.cores.inject(collector) { |c,x|
71
+ # visit_Arel_Nodes_SelectCore(x, c)
72
+ # }
73
+
74
+ unless o.orders.empty?
75
+ select_order_by = do_visit_columns o.orders, collector, 'ORDER BY '
76
+ end
77
+
78
+ select_count = false
79
+ collector = o.cores.inject(collector) do |c, x|
80
+ unless core_order_by = select_order_by
81
+ core_order_by = generate_order_by determine_order_by(o, x)
82
+ end
83
+
84
+ if select_count? x
85
+ x.projections = [ Arel::Nodes::SqlLiteral.new(over_row_num(core_order_by)) ]
86
+ select_count = true
87
+ else
88
+ # NOTE: this should really be added here and we should built the
89
+ # wrapping SQL but than #replace_limit_offset! assumes it does that
90
+ # ... MS-SQL adapter code seems to be 'hacked' by a lot of people
91
+ #x.projections << Arel::Nodes::SqlLiteral.new(over_row_num(select_order_by))
92
+ end if core_order_by
93
+ visit_Arel_Nodes_SelectCore(x, c)
94
+ end
95
+ # END collector = o.cores.inject(collector) { |c,x|
96
+
97
+ # collector = visit_Orders_And_Let_Fetch_Happen o, collector
98
+ # collector = visit_Make_Fetch_Happen o, collector
99
+ # collector # __method__ END
100
+
101
+ self.class.collector_proxy(collector) do |sql|
102
+ select_order_by ||= "ORDER BY #{@connection.determine_order_clause(sql)}"
103
+ replace_limit_offset!(sql, limit_for(o.limit), o.offset && o.offset.value.to_i, select_order_by)
104
+ sql = "SELECT COUNT(*) AS count_id FROM (#{sql}) AS subquery" if select_count
105
+ sql
106
+ end
107
+
108
+ ensure
109
+ set_select_statement_lock nil
110
+ end
111
+
112
+ def visit_Arel_Nodes_JoinSource o, collector
113
+ if o.left
114
+ collector = visit o.left, collector
115
+ collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector
116
+ end
117
+ if o.right.any?
118
+ collector << " " if o.left
119
+ collector = inject_join o.right, collector, ' '
120
+ end
121
+ collector
122
+ end
123
+
124
+ def visit_Arel_Nodes_OuterJoin o, collector
125
+ collector << "LEFT OUTER JOIN "
126
+ collector = visit o.left, collector
127
+ collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true
128
+ collector << " "
129
+ visit o.right, collector
130
+ end
131
+
132
+ # SQLServer ToSql/Visitor (Additions)
133
+
134
+ def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {}
135
+ if lock = select_statement_lock
136
+ collector = visit lock, collector
137
+ collector << SPACE if options[:space]
138
+ end
139
+ collector
140
+ end
141
+
142
+ def visit_Orders_And_Let_Fetch_Happen o, collector
143
+ make_Fetch_Possible_And_Deterministic o
144
+ unless o.orders.empty?
145
+ collector << SPACE
146
+ collector << ORDER_BY
147
+ len = o.orders.length - 1
148
+ o.orders.each_with_index { |x, i|
149
+ collector = visit(x, collector)
150
+ collector << COMMA unless len == i
151
+ }
152
+ end
153
+ collector
154
+ end
155
+
156
+ def visit_Make_Fetch_Happen o, collector
157
+ o.offset = Nodes::Offset.new(0) if o.limit && !o.offset
158
+ collector = visit o.offset, collector if o.offset
159
+ collector = visit o.limit, collector if o.limit
160
+ collector
161
+ end
162
+
163
+ # SQLServer Helpers
164
+
165
+ # attr_reader :select_statement_lock
166
+ def select_statement_lock
167
+ Thread.current[:'Arel::Visitors::SQLServerNG.select_statement_lock']
168
+ end
169
+
170
+ def set_select_statement_lock(lock) # @select_statement_lock = lock
171
+ Thread.current[:'Arel::Visitors::SQLServerNG.select_statement_lock'] = lock
172
+ end
173
+
174
+ def make_Fetch_Possible_And_Deterministic o
175
+ return if o.limit.nil? && o.offset.nil?
176
+ if o.orders.empty? # ORDER BY mandatory with OFFSET FETCH clause
177
+ t = table_From_Statement o
178
+ pk = primary_Key_From_Table t
179
+ return unless pk
180
+ # Prefer deterministic vs a simple `(SELECT NULL)` expr.
181
+ o.orders = [ pk.asc ]
182
+ end
183
+ end
184
+
185
+ def distinct_One_As_One_Is_So_Not_Fetch o
186
+ core = o.cores.first
187
+ distinct = Nodes::Distinct === core.set_quantifier
188
+ oneasone = core.projections.all? { |x| x == ActiveRecord::FinderMethods::ONE_AS_ONE }
189
+ limitone = node_value(o.limit) == 1
190
+ if distinct && oneasone && limitone && !o.offset
191
+ core.projections = [Arel.sql("TOP(1) 1 AS [one]")]
192
+ o.limit = nil
193
+ end
194
+ end
195
+
196
+ def table_From_Statement o
197
+ core = o.cores.first
198
+ if Arel::Table === core.from
199
+ core.from
200
+ elsif Arel::Nodes::SqlLiteral === core.from
201
+ Arel::Table.new(core.from)
202
+ elsif Arel::Nodes::JoinSource === core.source
203
+ Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left
204
+ end
205
+ end
206
+
207
+ def primary_Key_From_Table t
208
+ return unless t
209
+ return t.primary_key if t.primary_key
210
+ if engine_pk = t.engine.primary_key
211
+ pk = t.engine.arel_table[engine_pk]
212
+ return pk if pk
213
+ end
214
+ pk = t.engine.connection.schema_cache.primary_keys(t.engine.table_name)
215
+ return pk if pk
216
+ column_name = t.engine.columns.first.try(:name)
217
+ column_name ? t[column_name] : nil
218
+ end
219
+
220
+ def determine_order_by o, x
221
+ if o.orders.any?
222
+ o.orders
223
+ elsif x.groups.any?
224
+ x.groups
225
+ else
226
+ pk = find_left_table_pk(x)
227
+ pk ? [ pk ] : nil # []
228
+ end
229
+ end
230
+
231
+ def generate_order_by orders
232
+ do_visit_columns orders, nil, 'ORDER BY '
233
+ end
234
+
235
+ SQLString = ActiveRecord::ConnectionAdapters::AbstractAdapter::SQLString
236
+ # BindCollector = ActiveRecord::ConnectionAdapters::AbstractAdapter::BindCollector
237
+
238
+ def self.collector_proxy(collector, &block)
239
+ if collector.is_a?(SQLString)
240
+ return SQLStringProxy.new(collector, block)
241
+ end
242
+ BindCollectorProxy.new(collector, block)
243
+ end
244
+
245
+ class BindCollectorProxy < ActiveRecord::ConnectionAdapters::AbstractAdapter::BindCollector
246
+
247
+ def initialize(collector, block); @delegate = collector; @block = block end
248
+
249
+ def << str; @delegate << str; self end
250
+
251
+ def add_bind bind; @delegate.add_bind bind; self end
252
+
253
+ def value; @delegate.value; end
254
+
255
+ #def substitute_binds bvs; @delegate.substitute_binds(bvs); self end
256
+
257
+ def compile(bvs, conn)
258
+ _yield_str @delegate.compile(bvs, conn)
259
+ end
260
+
261
+ private
262
+
263
+ def method_missing(name, *args, &block); @delegate.send(name, args, &block) end
264
+
265
+ def _yield_str(str); @block ? @block.call(str) : str end
266
+
267
+ end
268
+
269
+ class SQLStringProxy < ActiveRecord::ConnectionAdapters::AbstractAdapter::SQLString
270
+
271
+ def initialize(collector, block); @delegate = collector; @block = block end
272
+
273
+ def << str; @delegate << str; self end
274
+
275
+ def add_bind bind; @delegate.add_bind bind; self end
276
+
277
+ def compile(bvs, conn)
278
+ _yield_str @delegate.compile(bvs, conn)
279
+ end
280
+
281
+ private
282
+
283
+ def method_missing(name, *args, &block); @delegate.send(name, args, &block) end
284
+
285
+ def _yield_str(str); @block ? @block.call(str) : str end
286
+
287
+ end
288
+
289
+ end
290
+ end
291
+ end
292
+
293
+ Arel::Visitors::VISITORS['mssql'] = Arel::Visitors::VISITORS['sqlserver'] = Arel::Visitors::SQLServerNG