activerecord-jdbc-adapter 1.0.3-java → 50.1-java

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 (268) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.travis.yml +79 -0
  4. data/.yardopts +4 -0
  5. data/CONTRIBUTING.md +50 -0
  6. data/Gemfile +91 -0
  7. data/History.md +1191 -0
  8. data/LICENSE.txt +22 -17
  9. data/README.md +169 -0
  10. data/RUNNING_TESTS.md +127 -0
  11. data/Rakefile +294 -5
  12. data/Rakefile.jdbc +20 -0
  13. data/activerecord-jdbc-adapter.gemspec +55 -0
  14. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  15. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  16. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  17. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  18. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  19. data/lib/activerecord-jdbc-adapter.rb +0 -5
  20. data/lib/arel/visitors/compat.rb +60 -0
  21. data/lib/arel/visitors/db2.rb +128 -6
  22. data/lib/arel/visitors/derby.rb +103 -10
  23. data/lib/arel/visitors/firebird.rb +79 -0
  24. data/lib/arel/visitors/h2.rb +25 -0
  25. data/lib/arel/visitors/hsqldb.rb +18 -10
  26. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  27. data/lib/arel/visitors/sql_server.rb +225 -0
  28. data/lib/arel/visitors/sql_server/ng42.rb +293 -0
  29. data/lib/arjdbc.rb +11 -21
  30. data/lib/arjdbc/abstract/connection_management.rb +35 -0
  31. data/lib/arjdbc/abstract/core.rb +64 -0
  32. data/lib/arjdbc/abstract/database_statements.rb +64 -0
  33. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  34. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  35. data/lib/arjdbc/db2.rb +3 -1
  36. data/lib/arjdbc/db2/adapter.rb +630 -250
  37. data/lib/arjdbc/db2/as400.rb +130 -0
  38. data/lib/arjdbc/db2/column.rb +167 -0
  39. data/lib/arjdbc/db2/connection_methods.rb +44 -0
  40. data/lib/arjdbc/derby.rb +1 -5
  41. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  42. data/lib/arjdbc/derby/adapter.rb +409 -217
  43. data/lib/arjdbc/derby/connection_methods.rb +16 -14
  44. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  45. data/lib/arjdbc/discover.rb +62 -50
  46. data/lib/arjdbc/firebird.rb +3 -1
  47. data/lib/arjdbc/firebird/adapter.rb +365 -62
  48. data/lib/arjdbc/firebird/connection_methods.rb +23 -0
  49. data/lib/arjdbc/h2.rb +2 -3
  50. data/lib/arjdbc/h2/adapter.rb +273 -6
  51. data/lib/arjdbc/h2/connection_methods.rb +23 -8
  52. data/lib/arjdbc/hsqldb.rb +2 -3
  53. data/lib/arjdbc/hsqldb/adapter.rb +204 -77
  54. data/lib/arjdbc/hsqldb/connection_methods.rb +24 -10
  55. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  56. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  57. data/lib/arjdbc/informix.rb +4 -2
  58. data/lib/arjdbc/informix/adapter.rb +78 -54
  59. data/lib/arjdbc/informix/connection_methods.rb +8 -9
  60. data/lib/arjdbc/jdbc.rb +59 -2
  61. data/lib/arjdbc/jdbc/adapter.rb +356 -166
  62. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  63. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  64. data/lib/arjdbc/jdbc/base_ext.rb +15 -0
  65. data/lib/arjdbc/jdbc/callbacks.rb +27 -18
  66. data/lib/arjdbc/jdbc/column.rb +79 -20
  67. data/lib/arjdbc/jdbc/connection.rb +5 -119
  68. data/lib/arjdbc/jdbc/connection_methods.rb +32 -4
  69. data/lib/arjdbc/jdbc/error.rb +65 -0
  70. data/lib/arjdbc/jdbc/extension.rb +41 -29
  71. data/lib/arjdbc/jdbc/java.rb +5 -6
  72. data/lib/arjdbc/jdbc/jdbc.rake +3 -126
  73. data/lib/arjdbc/jdbc/railtie.rb +2 -9
  74. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -10
  75. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
  76. data/lib/arjdbc/jdbc/type_cast.rb +166 -0
  77. data/lib/arjdbc/jdbc/type_converter.rb +35 -19
  78. data/lib/arjdbc/mssql.rb +6 -3
  79. data/lib/arjdbc/mssql/adapter.rb +630 -298
  80. data/lib/arjdbc/mssql/column.rb +200 -0
  81. data/lib/arjdbc/mssql/connection_methods.rb +66 -17
  82. data/lib/arjdbc/mssql/explain_support.rb +99 -0
  83. data/lib/arjdbc/mssql/limit_helpers.rb +189 -50
  84. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  85. data/lib/arjdbc/mssql/types.rb +343 -0
  86. data/lib/arjdbc/mssql/utils.rb +82 -0
  87. data/lib/arjdbc/mysql.rb +2 -3
  88. data/lib/arjdbc/mysql/adapter.rb +86 -356
  89. data/lib/arjdbc/mysql/connection_methods.rb +159 -23
  90. data/lib/arjdbc/oracle/adapter.rb +714 -263
  91. data/lib/arjdbc/postgresql.rb +2 -3
  92. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +24 -0
  93. data/lib/arjdbc/postgresql/adapter.rb +570 -400
  94. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  95. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  96. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  97. data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
  98. data/lib/arjdbc/postgresql/column.rb +51 -0
  99. data/lib/arjdbc/postgresql/connection_methods.rb +57 -18
  100. data/lib/arjdbc/postgresql/name.rb +24 -0
  101. data/lib/arjdbc/postgresql/oid_types.rb +192 -0
  102. data/lib/arjdbc/railtie.rb +11 -0
  103. data/lib/arjdbc/sqlite3.rb +2 -3
  104. data/lib/arjdbc/sqlite3/adapter.rb +518 -198
  105. data/lib/arjdbc/sqlite3/connection_methods.rb +49 -24
  106. data/lib/arjdbc/sybase.rb +2 -2
  107. data/lib/arjdbc/sybase/adapter.rb +7 -6
  108. data/lib/arjdbc/tasks.rb +13 -0
  109. data/lib/arjdbc/tasks/database_tasks.rb +52 -0
  110. data/lib/arjdbc/tasks/databases.rake +91 -0
  111. data/lib/arjdbc/tasks/databases3.rake +215 -0
  112. data/lib/arjdbc/tasks/databases4.rake +39 -0
  113. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  114. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  115. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  116. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  117. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  118. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  119. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  120. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  121. data/lib/arjdbc/util/table_copier.rb +110 -0
  122. data/lib/arjdbc/version.rb +1 -6
  123. data/lib/generators/jdbc/USAGE +9 -0
  124. data/lib/generators/jdbc/jdbc_generator.rb +8 -0
  125. data/lib/jdbc_adapter.rb +1 -1
  126. data/lib/jdbc_adapter/rake_tasks.rb +3 -2
  127. data/lib/jdbc_adapter/version.rb +2 -1
  128. data/pom.xml +114 -0
  129. data/rails_generators/jdbc_generator.rb +1 -1
  130. data/rails_generators/templates/config/initializers/jdbc.rb +8 -5
  131. data/rails_generators/templates/lib/tasks/jdbc.rake +7 -4
  132. data/rakelib/01-tomcat.rake +51 -0
  133. data/rakelib/02-test.rake +132 -0
  134. data/rakelib/bundler_ext.rb +11 -0
  135. data/rakelib/compile.rake +67 -22
  136. data/rakelib/db.rake +61 -0
  137. data/rakelib/rails.rake +204 -29
  138. data/src/java/arjdbc/ArJdbcModule.java +286 -0
  139. data/src/java/arjdbc/db2/DB2Module.java +76 -0
  140. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +126 -0
  141. data/src/java/arjdbc/derby/DerbyModule.java +99 -243
  142. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +152 -0
  143. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +174 -0
  144. data/src/java/arjdbc/{jdbc/JdbcConnectionFactory.java → h2/H2Module.java} +20 -6
  145. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +27 -12
  146. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +73 -0
  147. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +7 -6
  148. data/src/java/arjdbc/jdbc/AdapterJavaService.java +7 -29
  149. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  150. data/src/java/arjdbc/jdbc/ConnectionFactory.java +132 -0
  151. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +157 -0
  152. data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
  153. data/src/java/arjdbc/jdbc/DriverWrapper.java +119 -0
  154. data/src/java/arjdbc/jdbc/JdbcResult.java +130 -0
  155. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3622 -948
  156. data/src/java/arjdbc/mssql/MSSQLModule.java +90 -0
  157. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +181 -0
  158. data/src/java/arjdbc/mysql/MySQLModule.java +99 -81
  159. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +294 -0
  160. data/src/java/arjdbc/oracle/OracleModule.java +80 -0
  161. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +387 -17
  162. data/src/java/arjdbc/postgresql/ByteaUtils.java +157 -0
  163. data/src/java/arjdbc/postgresql/PgResultSetMetaDataWrapper.java +23 -0
  164. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +77 -0
  165. data/src/java/arjdbc/postgresql/PostgreSQLResult.java +184 -0
  166. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +952 -0
  167. data/src/java/arjdbc/sqlite3/SQLite3Module.java +73 -0
  168. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +525 -0
  169. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  170. data/src/java/arjdbc/util/DateTimeUtils.java +580 -0
  171. data/src/java/arjdbc/util/ObjectSupport.java +65 -0
  172. data/src/java/arjdbc/util/QuotingUtils.java +138 -0
  173. data/src/java/arjdbc/util/StringCache.java +63 -0
  174. data/src/java/arjdbc/util/StringHelper.java +159 -0
  175. metadata +245 -268
  176. data/History.txt +0 -369
  177. data/Manifest.txt +0 -180
  178. data/README.txt +0 -181
  179. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -1
  180. data/lib/arel/engines/sql/compilers/db2_compiler.rb +0 -9
  181. data/lib/arel/engines/sql/compilers/derby_compiler.rb +0 -6
  182. data/lib/arel/engines/sql/compilers/h2_compiler.rb +0 -6
  183. data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +0 -15
  184. data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +0 -6
  185. data/lib/arel/engines/sql/compilers/mssql_compiler.rb +0 -46
  186. data/lib/arel/visitors/mssql.rb +0 -44
  187. data/lib/arjdbc/jdbc/compatibility.rb +0 -51
  188. data/lib/arjdbc/jdbc/core_ext.rb +0 -24
  189. data/lib/arjdbc/jdbc/discover.rb +0 -18
  190. data/lib/arjdbc/jdbc/driver.rb +0 -44
  191. data/lib/arjdbc/jdbc/missing_functionality_helper.rb +0 -87
  192. data/lib/arjdbc/jdbc/quoted_primary_key.rb +0 -28
  193. data/lib/arjdbc/jdbc/require_driver.rb +0 -16
  194. data/lib/arjdbc/mimer.rb +0 -2
  195. data/lib/arjdbc/mimer/adapter.rb +0 -142
  196. data/lib/arjdbc/mssql/tsql_helper.rb +0 -61
  197. data/lib/arjdbc/oracle.rb +0 -3
  198. data/lib/arjdbc/oracle/connection_methods.rb +0 -11
  199. data/lib/pg.rb +0 -4
  200. data/rakelib/package.rake +0 -92
  201. data/rakelib/test.rake +0 -81
  202. data/src/java/arjdbc/jdbc/SQLBlock.java +0 -48
  203. data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +0 -127
  204. data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +0 -57
  205. data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +0 -64
  206. data/test/abstract_db_create.rb +0 -117
  207. data/test/activerecord/connection_adapters/type_conversion_test.rb +0 -31
  208. data/test/activerecord/connections/native_jdbc_mysql/connection.rb +0 -25
  209. data/test/db/db2.rb +0 -11
  210. data/test/db/derby.rb +0 -12
  211. data/test/db/h2.rb +0 -11
  212. data/test/db/hsqldb.rb +0 -13
  213. data/test/db/informix.rb +0 -11
  214. data/test/db/jdbc.rb +0 -11
  215. data/test/db/jndi_config.rb +0 -40
  216. data/test/db/logger.rb +0 -3
  217. data/test/db/mssql.rb +0 -9
  218. data/test/db/mysql.rb +0 -10
  219. data/test/db/oracle.rb +0 -34
  220. data/test/db/postgres.rb +0 -9
  221. data/test/db/sqlite3.rb +0 -11
  222. data/test/db2_simple_test.rb +0 -66
  223. data/test/derby_migration_test.rb +0 -68
  224. data/test/derby_multibyte_test.rb +0 -12
  225. data/test/derby_simple_test.rb +0 -99
  226. data/test/generic_jdbc_connection_test.rb +0 -29
  227. data/test/h2_simple_test.rb +0 -41
  228. data/test/has_many_through.rb +0 -79
  229. data/test/helper.rb +0 -5
  230. data/test/hsqldb_simple_test.rb +0 -6
  231. data/test/informix_simple_test.rb +0 -48
  232. data/test/jdbc_common.rb +0 -25
  233. data/test/jndi_callbacks_test.rb +0 -40
  234. data/test/jndi_test.rb +0 -25
  235. data/test/manualTestDatabase.rb +0 -191
  236. data/test/models/add_not_null_column_to_table.rb +0 -12
  237. data/test/models/auto_id.rb +0 -18
  238. data/test/models/data_types.rb +0 -28
  239. data/test/models/entry.rb +0 -43
  240. data/test/models/mixed_case.rb +0 -25
  241. data/test/models/reserved_word.rb +0 -18
  242. data/test/models/string_id.rb +0 -18
  243. data/test/models/validates_uniqueness_of_string.rb +0 -19
  244. data/test/mssql_db_create_test.rb +0 -26
  245. data/test/mssql_identity_insert_test.rb +0 -19
  246. data/test/mssql_legacy_types_test.rb +0 -58
  247. data/test/mssql_limit_offset_test.rb +0 -136
  248. data/test/mssql_multibyte_test.rb +0 -18
  249. data/test/mssql_simple_test.rb +0 -55
  250. data/test/mysql_db_create_test.rb +0 -27
  251. data/test/mysql_info_test.rb +0 -113
  252. data/test/mysql_multibyte_test.rb +0 -10
  253. data/test/mysql_nonstandard_primary_key_test.rb +0 -42
  254. data/test/mysql_simple_test.rb +0 -49
  255. data/test/oracle_simple_test.rb +0 -18
  256. data/test/oracle_specific_test.rb +0 -83
  257. data/test/pick_rails_version.rb +0 -3
  258. data/test/postgres_db_create_test.rb +0 -32
  259. data/test/postgres_drop_db_test.rb +0 -16
  260. data/test/postgres_mixed_case_test.rb +0 -29
  261. data/test/postgres_nonseq_pkey_test.rb +0 -38
  262. data/test/postgres_reserved_test.rb +0 -22
  263. data/test/postgres_schema_search_path_test.rb +0 -44
  264. data/test/postgres_simple_test.rb +0 -51
  265. data/test/postgres_table_alias_length_test.rb +0 -15
  266. data/test/simple.rb +0 -546
  267. data/test/sqlite3_simple_test.rb +0 -233
  268. data/test/sybase_jtds_simple_test.rb +0 -28
@@ -0,0 +1,200 @@
1
+ module ArJdbc
2
+ module MSSQL
3
+
4
+ # @see ActiveRecord::ConnectionAdapters::JdbcColumn#column_types
5
+ def self.column_selector
6
+ [ /sqlserver|tds|Microsoft SQL/i, lambda { |config, column| column.extend(Column) } ]
7
+ end
8
+
9
+ # @see ActiveRecord::ConnectionAdapters::JdbcColumn
10
+ module Column
11
+
12
+ def self.included(base)
13
+ # NOTE: assumes a standalone MSSQLColumn class
14
+ class << base; include Cast; end
15
+ end
16
+
17
+ include LockMethods unless AR42
18
+
19
+ # @override
20
+ def simplified_type(field_type)
21
+ case field_type
22
+ when /int|bigint|smallint|tinyint/i then :integer
23
+ when /numeric/i then (@scale.nil? || @scale == 0) ? :integer : :decimal
24
+ when /float|double|money|real|smallmoney/i then :decimal
25
+ when /datetime|smalldatetime/i then :datetime
26
+ when /timestamp/i then :timestamp
27
+ when /time/i then :time
28
+ when /date/i then :date
29
+ when /text|ntext|xml/i then :text
30
+ when /binary|image|varbinary/i then :binary
31
+ when /char|nchar|nvarchar|string|varchar/i then (@limit == 1073741823 ? (@limit = nil; :text) : :string)
32
+ when /bit/i then :boolean
33
+ when /uniqueidentifier/i then :string
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ # @override
40
+ def default_value(value)
41
+ return $1 if value =~ /^\(N?'(.*)'\)$/ || value =~ /^\(\(?(.*?)\)?\)$/
42
+ value
43
+ end
44
+
45
+ # @override
46
+ def type_cast(value)
47
+ return nil if value.nil?
48
+ case type
49
+ when :integer then ( value.is_a?(String) ? unquote(value) : (value || 0) ).to_i
50
+ when :primary_key then value.respond_to?(:to_i) ? value.to_i : ((value && 1) || 0)
51
+ when :decimal then self.class.value_to_decimal(unquote(value))
52
+ when :date then self.class.string_to_date(value)
53
+ when :datetime then self.class.string_to_time(value)
54
+ when :timestamp then self.class.string_to_time(value)
55
+ when :time then self.class.string_to_dummy_time(value)
56
+ when :boolean then value == true || (value =~ /^t(rue)?$/i) == 0 || unquote(value) == '1'
57
+ when :binary then unquote(value)
58
+ else value
59
+ end
60
+ end
61
+
62
+ # @override
63
+ def extract_limit(sql_type)
64
+ case sql_type
65
+ when /^smallint/i
66
+ 2
67
+ when /^int/i
68
+ 4
69
+ when /^bigint/i
70
+ 8
71
+ when /\(max\)/, /decimal/, /numeric/
72
+ nil
73
+ when /text|ntext|xml|binary|image|varbinary|bit/
74
+ nil
75
+ else
76
+ super
77
+ end
78
+ end
79
+
80
+ # #primary replacement that works on 4.2 as well
81
+ # #columns will set @primary even when on AR 4.2
82
+ def primary?; @primary end
83
+ alias_method :is_primary, :primary?
84
+
85
+ def identity?
86
+ !! sql_type.downcase.index('identity')
87
+ end
88
+ # @deprecated
89
+ alias_method :identity, :identity?
90
+ alias_method :is_identity, :identity?
91
+
92
+ # NOTE: these do not handle = equality as expected
93
+ # see {#repair_special_columns}
94
+ # (TEXT, NTEXT, and IMAGE data types are deprecated)
95
+ # @private
96
+ def special?
97
+ unless defined? @special # /text|ntext|image|xml/i
98
+ sql_type = @sql_type.downcase
99
+ @special = !! ( sql_type.index('text') || sql_type.index('image') || sql_type.index('xml') )
100
+ end
101
+ @special
102
+ end
103
+ # @deprecated
104
+ alias_method :special, :special?
105
+ alias_method :is_special, :special?
106
+
107
+ def is_utf8?
108
+ !!( sql_type =~ /nvarchar|ntext|nchar/i )
109
+ end
110
+
111
+ private
112
+
113
+ def unquote(value)
114
+ value.to_s.sub(/\A\([\(\']?/, "").sub(/[\'\)]?\)\Z/, "")
115
+ end
116
+
117
+ # @deprecated no longer used
118
+ def cast_to_time(value)
119
+ return value if value.is_a?(Time)
120
+ DateTime.parse(value).to_time rescue nil
121
+ end
122
+
123
+ # @deprecated no longer used
124
+ def cast_to_date(value)
125
+ return value if value.is_a?(Date)
126
+ return Date.parse(value) rescue nil
127
+ end
128
+
129
+ # @deprecated no longer used
130
+ def cast_to_datetime(value)
131
+ if value.is_a?(Time)
132
+ if value.year != 0 and value.month != 0 and value.day != 0
133
+ return value
134
+ else
135
+ return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
136
+ end
137
+ end
138
+ if value.is_a?(DateTime)
139
+ begin
140
+ # Attempt to convert back to a Time, but it could fail for dates significantly in the past/future.
141
+ return Time.mktime(value.year, value.mon, value.day, value.hour, value.min, value.sec)
142
+ rescue ArgumentError
143
+ return value
144
+ end
145
+ end
146
+
147
+ return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
148
+
149
+ return value.is_a?(Date) ? value : nil
150
+ end
151
+
152
+ module Cast
153
+
154
+ def string_to_date(value)
155
+ return value unless value.is_a?(String)
156
+ return nil if value.empty?
157
+
158
+ date = fast_string_to_date(value)
159
+ date ? date : Date.parse(value) rescue nil
160
+ end
161
+
162
+ def string_to_time(value)
163
+ return value unless value.is_a?(String)
164
+ return nil if value.empty?
165
+
166
+ fast_string_to_time(value) || DateTime.parse(value).to_time rescue nil
167
+ end
168
+
169
+ ISO_TIME = /\A(\d\d)\:(\d\d)\:(\d\d)(\.\d+)?\z/
170
+
171
+ def string_to_dummy_time(value)
172
+ return value unless value.is_a?(String)
173
+ return nil if value.empty?
174
+
175
+ if value =~ ISO_TIME # "12:34:56.1234560"
176
+ microsec = ($4.to_f * 1_000_000).round.to_i
177
+ new_time 2000, 1, 1, $1.to_i, $2.to_i, $3.to_i, microsec
178
+ else
179
+ super(value)
180
+ end
181
+ end
182
+
183
+ def string_to_binary(value)
184
+ # this will only allow the adapter to insert binary data with a length
185
+ # of 7K or less because of a SQL Server statement length policy ...
186
+ "0x#{value.unpack("H*")}" # "0x#{value.unpack("H*")[0]}"
187
+ end
188
+
189
+ def binary_to_string(value)
190
+ if value.respond_to?(:force_encoding) && value.encoding != Encoding::ASCII_8BIT
191
+ value = value.force_encoding(Encoding::ASCII_8BIT)
192
+ end
193
+ value =~ /[^[:xdigit:]]/ ? value : [value].pack('H*')
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+ end
200
+ end
@@ -1,30 +1,79 @@
1
- class ActiveRecord::Base
2
- class << self
3
- def mssql_connection(config)
4
- require "arjdbc/mssql"
5
- config[:host] ||= "localhost"
6
- config[:port] ||= 1433
7
- config[:driver] ||= "net.sourceforge.jtds.jdbc.Driver"
1
+ ArJdbc::ConnectionMethods.module_eval do
8
2
 
9
- url = "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
3
+ # Default connection method for MS-SQL adapter (`adapter: mssql`),
4
+ # uses the (open-source) jTDS driver.
5
+ # If you'd like to use the "official" MS's SQL-JDBC driver, it's preferable
6
+ # to use the {#sqlserver_connection} method (set `adapter: sqlserver`).
7
+ def mssql_connection(config)
8
+ # NOTE: this detection ain't perfect and is only meant as a temporary hack
9
+ # users will get a deprecation eventually to use `adapter: sqlserver` ...
10
+ if config[:driver] =~ /SQLServerDriver$/ || config[:url] =~ /^jdbc:sqlserver:/
11
+ return sqlserver_connection(config)
12
+ end
13
+
14
+ config[:adapter_spec] ||= ::ArJdbc::MSSQL
15
+ config[:adapter_class] = ActiveRecord::ConnectionAdapters::MSSQLAdapter unless config.key?(:adapter_class)
10
16
 
17
+ return jndi_connection(config) if jndi_config?(config)
18
+
19
+ begin
20
+ require 'jdbc/jtds'
21
+ # NOTE: the adapter has only support for working with the
22
+ # open-source jTDS driver (won't work with MS's driver) !
23
+ ::Jdbc::JTDS.load_driver(:require) if defined?(::Jdbc::JTDS.load_driver)
24
+ rescue LoadError => e # assuming driver.jar is on the class-path
25
+ raise e unless e.message.to_s.index('no such file to load')
26
+ end
27
+
28
+ config[:host] ||= 'localhost'
29
+ config[:port] ||= 1433
30
+ config[:driver] ||= defined?(::Jdbc::JTDS.driver_name) ? ::Jdbc::JTDS.driver_name : 'net.sourceforge.jtds.jdbc.Driver'
31
+ config[:connection_alive_sql] ||= 'SELECT 1'
32
+
33
+ config[:url] ||= begin
34
+ url = "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
11
35
  # Instance is often a preferrable alternative to port when dynamic ports are used.
12
36
  # If instance is specified then port is essentially ignored.
13
37
  url << ";instance=#{config[:instance]}" if config[:instance]
14
-
15
38
  # This will enable windows domain-based authentication and will require the JTDS native libraries be available.
16
39
  url << ";domain=#{config[:domain]}" if config[:domain]
17
-
18
40
  # AppName is shown in sql server as additional information against the connection.
19
41
  url << ";appname=#{config[:appname]}" if config[:appname]
20
- config[:url] ||= url
42
+ url
43
+ end
21
44
 
22
- if !config[:domain]
23
- config[:username] ||= "sa"
24
- config[:password] ||= ""
25
- end
26
- jdbc_connection(config)
45
+ unless config[:domain]
46
+ config[:username] ||= 'sa'
47
+ config[:password] ||= ''
27
48
  end
28
- alias_method :jdbcmssql_connection, :mssql_connection
49
+ jdbc_connection(config)
29
50
  end
51
+ alias_method :jdbcmssql_connection, :mssql_connection
52
+
53
+ # @note Assumes SQLServer SQL-JDBC driver on the class-path.
54
+ def sqlserver_connection(config)
55
+ config[:adapter_spec] ||= ::ArJdbc::MSSQL
56
+ config[:adapter_class] = ActiveRecord::ConnectionAdapters::MSSQLAdapter unless config.key?(:adapter_class)
57
+
58
+ return jndi_connection(config) if jndi_config?(config)
59
+
60
+ config[:host] ||= 'localhost'
61
+ config[:driver] ||= 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
62
+ config[:connection_alive_sql] ||= 'SELECT 1'
63
+
64
+ config[:url] ||= begin
65
+ url = "jdbc:sqlserver://#{config[:host]}"
66
+ url << ( config[:port] ? ":#{config[:port]};" : ';' )
67
+ url << "databaseName=#{config[:database]};" if config[:database]
68
+ url << "instanceName=#{config[:instance]};" if config[:instance]
69
+ app = config[:appname] || config[:application]
70
+ url << "applicationName=#{app};" if app
71
+ isc = config[:integrated_security] # Win only - needs sqljdbc_auth.dll
72
+ url << "integratedSecurity=#{isc};" unless isc.nil?
73
+ url
74
+ end
75
+ jdbc_connection(config)
76
+ end
77
+ alias_method :jdbcsqlserver_connection, :sqlserver_connection
78
+
30
79
  end
@@ -0,0 +1,99 @@
1
+ require 'active_support/core_ext/string'
2
+
3
+ module ArJdbc
4
+ module MSSQL
5
+ module ExplainSupport
6
+
7
+ DISABLED = Java::JavaLang::Boolean.getBoolean('arjdbc.mssql.explain_support.disabled')
8
+
9
+ def supports_explain?; ! DISABLED; end
10
+
11
+ def explain(arel, binds = [])
12
+ return if DISABLED
13
+ sql = to_sql(arel, binds)
14
+ result = with_showplan_on { exec_query(sql, 'EXPLAIN', binds) }
15
+ PrinterTable.new(result).pp
16
+ end
17
+
18
+ protected
19
+
20
+ def with_showplan_on
21
+ set_showplan_option(true)
22
+ yield
23
+ ensure
24
+ set_showplan_option(false)
25
+ end
26
+
27
+ def set_showplan_option(enable = true)
28
+ option = 'SHOWPLAN_TEXT'
29
+ execute "SET #{option} #{enable ? 'ON' : 'OFF'}"
30
+ rescue Exception => e
31
+ raise ActiveRecord::ActiveRecordError, "#{option} could not be turned" +
32
+ " #{enable ? 'ON' : 'OFF'} (check SHOWPLAN permissions) due : #{e.inspect}"
33
+ end
34
+
35
+ # @private
36
+ class PrinterTable
37
+
38
+ cattr_accessor :max_column_width, :cell_padding
39
+ self.max_column_width = 50
40
+ self.cell_padding = 1
41
+
42
+ attr_reader :result
43
+
44
+ def initialize(result)
45
+ @result = result
46
+ end
47
+
48
+ def pp
49
+ @widths = compute_column_widths
50
+ @separator = build_separator
51
+ pp = []
52
+ pp << @separator
53
+ pp << build_cells(result.columns)
54
+ pp << @separator
55
+ result.rows.each do |row|
56
+ pp << build_cells(row)
57
+ end
58
+ pp << @separator
59
+ pp.join("\n") << "\n"
60
+ end
61
+
62
+ private
63
+
64
+ def compute_column_widths
65
+ [].tap do |computed_widths|
66
+ result.columns.each_with_index do |column, i|
67
+ cells_in_column = [column] + result.rows.map { |r| cast_item(r[i]) }
68
+ computed_width = cells_in_column.map(&:length).max
69
+ final_width = computed_width > max_column_width ? max_column_width : computed_width
70
+ computed_widths << final_width
71
+ end
72
+ end
73
+ end
74
+
75
+ def build_separator
76
+ '+' << @widths.map {|w| '-' * (w + (cell_padding * 2))}.join('+') << '+'
77
+ end
78
+
79
+ def build_cells(items)
80
+ cells = []
81
+ items.each_with_index do |item, i|
82
+ cells << cast_item(item).ljust(@widths[i])
83
+ end
84
+ "| #{cells.join(' | ')} |"
85
+ end
86
+
87
+ def cast_item(item)
88
+ case item
89
+ when NilClass then 'NULL'
90
+ when Float then item.to_s.to(9)
91
+ else item.to_s.truncate(max_column_width)
92
+ end
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+ end
99
+ end
@@ -1,84 +1,206 @@
1
- module ::ArJdbc
2
- module MsSQL
1
+ module ArJdbc
2
+ module MSSQL
3
3
  module LimitHelpers
4
- module_function
5
- def get_table_name(sql)
6
- if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
7
- $1
8
- elsif sql =~ /from\s+([^\(\s,]+)\s*/i
9
- $1
10
- else
11
- nil
4
+
5
+ # @private
6
+ FIND_SELECT = /\b(SELECT(\s+DISTINCT)?)\b(.*)/mi
7
+ # @private
8
+ FIND_AGGREGATE_FUNCTION = /(AVG|COUNT|COUNT_BIG|MAX|MIN|SUM|STDDEV|STDEVP|VAR|VARP)\(/i
9
+
10
+ # @private
11
+ module SqlServerReplaceLimitOffset
12
+
13
+ GROUP_BY = 'GROUP BY'
14
+ ORDER_BY = 'ORDER BY'
15
+
16
+ module_function
17
+
18
+ def replace_limit_offset!(sql, limit, offset, order)
19
+ offset ||= 0
20
+
21
+ if match = FIND_SELECT.match(sql)
22
+ select, distinct, rest_of_query = match[1], match[2], match[3]
23
+ rest_of_query.strip!
24
+ end
25
+ rest_of_query[0] = '*' if rest_of_query[0...1] == '1' && rest_of_query !~ /1 AS/i
26
+ if rest_of_query[0...1] == '*'
27
+ from_table = Utils.get_table_name(rest_of_query, true)
28
+ rest_of_query = "#{from_table}.#{rest_of_query}"
29
+ end
30
+
31
+ # Ensure correct queries if the rest_of_query contains a 'GROUP BY'. Otherwise the following error occurs:
32
+ # ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: Column 'users.id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
33
+ # SELECT t.* FROM ( SELECT ROW_NUMBER() OVER(ORDER BY users.id) AS _row_num, [users].[lft], COUNT([users].[lft]) FROM [users] GROUP BY [users].[lft] HAVING COUNT([users].[lft]) > 1 ) AS t WHERE t._row_num BETWEEN 1 AND 1
34
+ if i = ( rest_of_query.rindex(GROUP_BY) || rest_of_query.rindex('group by') )
35
+ # Do not catch 'GROUP BY' statements from sub-selects, indicated
36
+ # by more closing than opening brackets after the last group by.
37
+ rest_after_last_group_by = rest_of_query[i..-1]
38
+ opening_brackets_count = rest_after_last_group_by.count('(')
39
+ closing_brackets_count = rest_after_last_group_by.count(')')
40
+
41
+ if opening_brackets_count == closing_brackets_count
42
+ order_start = order.strip[0, 8]; order_start.upcase!
43
+ if order_start == ORDER_BY && order.match(FIND_AGGREGATE_FUNCTION)
44
+ # do nothing
45
+ elsif order.count(',') == 0
46
+ order.gsub!(/ORDER +BY +([^\s]+)(\s+ASC|\s+DESC)?/i, 'ORDER BY MIN(\1)\2')
47
+ else
48
+ raise("can not handle multiple order conditions (#{order.inspect}) in #{sql.inspect}")
49
+ end
50
+ end
51
+ end
52
+
53
+ if distinct # select =~ /DISTINCT/i
54
+ order = order.gsub(/(\[[a-z0-9_]+\]|[a-z0-9_]+)\./, 't.')
55
+ new_sql = "SELECT t.* FROM "
56
+ new_sql << "( SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, t.* FROM (#{select} #{rest_of_query}) AS t ) AS t"
57
+ append_limit_row_num_clause(new_sql, limit, offset)
58
+ else
59
+ select_columns_before_from = rest_of_query.gsub(/FROM.*/, '').strip
60
+ only_one_column = !select_columns_before_from.include?(',')
61
+ only_one_id_column = only_one_column && (select_columns_before_from.ends_with?('.id') || select_columns_before_from.ends_with?('.[id]'))
62
+
63
+ if only_one_id_column
64
+ # If there's only one id column a subquery will be created which only contains this column
65
+ new_sql = "#{select} t.id FROM "
66
+ else
67
+ # All selected columns are used
68
+ new_sql = "#{select} t.* FROM "
69
+ end
70
+ new_sql << "( SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, #{rest_of_query} ) AS t"
71
+ append_limit_row_num_clause(new_sql, limit, offset)
72
+ end
73
+
74
+ sql.replace new_sql
75
+ end
76
+
77
+ def append_limit_row_num_clause(sql, limit, offset)
78
+ if limit
79
+ start_row = offset + 1; end_row = offset + limit.to_i
80
+ sql << " WHERE t._row_num BETWEEN #{start_row} AND #{end_row}"
81
+ else
82
+ sql << " WHERE t._row_num > #{offset}"
83
+ end
12
84
  end
85
+
13
86
  end
14
87
 
88
+ # @private
15
89
  module SqlServer2000ReplaceLimitOffset
90
+
16
91
  module_function
92
+
17
93
  def replace_limit_offset!(sql, limit, offset, order)
18
94
  if limit
19
95
  offset ||= 0
20
96
  start_row = offset + 1
21
97
  end_row = offset + limit.to_i
22
- find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
23
- whole, select, rest_of_query = find_select.match(sql).to_a
98
+
99
+ if match = FIND_SELECT.match(sql)
100
+ select, distinct, rest_of_query = match[1], match[2], match[3]
101
+ end
102
+ #need the table name for avoiding amiguity
103
+ table_name = Utils.get_table_name(sql, true)
104
+ primary_key = get_primary_key(order, table_name)
105
+
106
+ #I am not sure this will cover all bases. but all the tests pass
107
+ if order[/ORDER/].nil?
108
+ new_order = "ORDER BY #{order}, [#{table_name}].[#{primary_key}]" if order.index("#{table_name}.#{primary_key}").nil?
109
+ else
110
+ new_order ||= order
111
+ end
112
+
24
113
  if (start_row == 1) && (end_row ==1)
25
- new_sql = "#{select} TOP 1 #{rest_of_query}"
114
+ new_sql = "#{select} TOP 1 #{rest_of_query} #{new_order}"
26
115
  sql.replace(new_sql)
27
116
  else
28
- #UGLY
29
- #KLUDGY?
30
- #removing out stuff before the FROM...
31
- rest = rest_of_query[/FROM/i=~ rest_of_query.. -1]
32
- #need the table name for avoiding amiguity
33
- table_name = LimitHelpers.get_table_name(sql)
34
- #I am not sure this will cover all bases. but all the tests pass
35
- new_order = "ORDER BY #{order}, #{table_name}.id" if order.index("#{table_name}.id").nil?
36
- new_order ||= order
117
+ # We are in deep trouble here. SQL Server does not have any kind of OFFSET build in.
118
+ # Only remaining solution is adding a where condition to be sure that the ID is not in SELECT TOP OFFSET FROM SAME_QUERY.
119
+ # To do so we need to extract each part of the query to insert our additional condition in the right place.
120
+ query_without_select = rest_of_query[/FROM/i=~ rest_of_query.. -1]
121
+ additional_condition = "#{table_name}.#{primary_key} NOT IN (#{select} TOP #{offset} #{table_name}.#{primary_key} #{query_without_select} #{new_order})"
122
+
123
+ # Extract the different parts of the query
124
+ having, group_by, where, from, selection = split_sql(rest_of_query, /having/i, /group by/i, /where/i, /from/i)
37
125
 
38
- if (rest_of_query.match(/WHERE/).nil?)
39
- new_sql = "#{select} TOP #{limit} #{rest_of_query} WHERE #{table_name}.id NOT IN (#{select} TOP #{offset} #{table_name}.id #{rest} #{new_order}) #{order} "
126
+ # Update the where part to add our additional condition
127
+ if where.blank?
128
+ where = "WHERE #{additional_condition}"
40
129
  else
41
- new_sql = "#{select} TOP #{limit} #{rest_of_query} AND #{table_name}.id NOT IN (#{select} TOP #{offset} #{table_name}.id #{rest} #{new_order}) #{order} "
130
+ where = "#{where} AND #{additional_condition}"
42
131
  end
43
132
 
44
- sql.replace(new_sql)
133
+ # Replace the query to be our new customized query
134
+ sql.replace("#{select} TOP #{limit} #{selection} #{from} #{where} #{group_by} #{having} #{new_order}")
45
135
  end
46
136
  end
47
137
  sql
48
138
  end
49
- end
50
139
 
51
- module SqlServer2000AddLimitOffset
52
- def add_limit_offset!(sql, options)
53
- if options[:limit]
54
- order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
55
- sql.sub!(/ ORDER BY.*$/i, '')
56
- SqlServerReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
140
+ # Split the rest_of_query into chunks based on regexs (applied from end of string to the beginning)
141
+ # The result is an array of regexs.size+1 elements (the last one being the remaining once everything was chopped away)
142
+ def split_sql(rest_of_query, *regexs)
143
+ results = Array.new
144
+
145
+ regexs.each do |regex|
146
+ if position = (regex =~ rest_of_query)
147
+ # Extract the matched string and chop the rest_of_query
148
+ matched = rest_of_query[position..-1]
149
+ rest_of_query = rest_of_query[0...position]
150
+ else
151
+ matched = nil
152
+ end
153
+
154
+ results << matched
155
+ end
156
+ results << rest_of_query
157
+
158
+ results
159
+ end
160
+
161
+ def get_primary_key(order, table_name) # table_name might be quoted
162
+ if order =~ /(\w*id\w*)/i
163
+ $1
164
+ else
165
+ unquoted_name = Utils.unquote_table_name(table_name)
166
+ model = descendants.find { |m| m.table_name == table_name || m.table_name == unquoted_name }
167
+ model ? model.primary_key : 'id'
57
168
  end
58
169
  end
170
+
171
+ private
172
+
173
+ if ActiveRecord::VERSION::MAJOR >= 3
174
+ def descendants; ::ActiveRecord::Base.descendants; end
175
+ else
176
+ def descendants; ::ActiveRecord::Base.send(:subclasses) end
177
+ end
178
+
59
179
  end
60
180
 
61
- module SqlServerReplaceLimitOffset
62
- module_function
63
- def replace_limit_offset!(sql, limit, offset, order)
64
- if limit
65
- offset ||= 0
66
- start_row = offset + 1
67
- end_row = offset + limit.to_i
68
- find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
69
- whole, select, rest_of_query = find_select.match(sql).to_a
70
- if rest_of_query.strip!.first == '*'
71
- from_table = /.*FROM\s*\b(\w*)\b/i.match(rest_of_query).to_a[1]
72
- end
73
- new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, #{from_table + '.' if from_table}#{rest_of_query}"
74
- new_sql << ") AS t WHERE t._row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
75
- sql.replace(new_sql)
181
+ private
182
+
183
+ if ::ActiveRecord::VERSION::MAJOR < 3
184
+
185
+ def setup_limit_offset!(version = nil)
186
+ if version.to_s == '2000' || sqlserver_2000?
187
+ extend SqlServer2000AddLimitOffset
188
+ else
189
+ extend SqlServerAddLimitOffset
76
190
  end
77
- sql
78
191
  end
192
+
193
+ else
194
+
195
+ def setup_limit_offset!(version = nil); end
196
+
79
197
  end
80
198
 
199
+ # @private
81
200
  module SqlServerAddLimitOffset
201
+
202
+ # @note Only needed with (non-AREL) ActiveRecord **2.3**.
203
+ # @see Arel::Visitors::SQLServer
82
204
  def add_limit_offset!(sql, options)
83
205
  if options[:limit]
84
206
  order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
@@ -86,7 +208,24 @@ module ::ArJdbc
86
208
  SqlServerReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
87
209
  end
88
210
  end
89
- end
211
+
212
+ end if ::ActiveRecord::VERSION::MAJOR < 3
213
+
214
+ # @private
215
+ module SqlServer2000AddLimitOffset
216
+
217
+ # @note Only needed with (non-AREL) ActiveRecord **2.3**.
218
+ # @see Arel::Visitors::SQLServer
219
+ def add_limit_offset!(sql, options)
220
+ if options[:limit]
221
+ order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
222
+ sql.sub!(/ ORDER BY.*$/i, '')
223
+ SqlServer2000ReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
224
+ end
225
+ end
226
+
227
+ end if ::ActiveRecord::VERSION::MAJOR < 3
228
+
90
229
  end
91
230
  end
92
231
  end