activerecord-jdbc-alt-adapter 50.3.0-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 (198) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.travis.yml +100 -0
  4. data/.yardopts +4 -0
  5. data/CONTRIBUTING.md +50 -0
  6. data/Gemfile +92 -0
  7. data/History.md +1191 -0
  8. data/LICENSE.txt +26 -0
  9. data/README.md +240 -0
  10. data/RUNNING_TESTS.md +127 -0
  11. data/Rakefile +336 -0
  12. data/Rakefile.jdbc +20 -0
  13. data/activerecord-jdbc-adapter.gemspec +55 -0
  14. data/activerecord-jdbc-alt-adapter.gemspec +56 -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/postgresql_adapter.rb +1 -0
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
  30. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  31. data/lib/activerecord-jdbc-adapter.rb +1 -0
  32. data/lib/arel/visitors/compat.rb +60 -0
  33. data/lib/arel/visitors/db2.rb +137 -0
  34. data/lib/arel/visitors/derby.rb +112 -0
  35. data/lib/arel/visitors/firebird.rb +79 -0
  36. data/lib/arel/visitors/h2.rb +25 -0
  37. data/lib/arel/visitors/hsqldb.rb +32 -0
  38. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  39. data/lib/arel/visitors/sql_server.rb +225 -0
  40. data/lib/arel/visitors/sql_server/ng42.rb +294 -0
  41. data/lib/arel/visitors/sqlserver.rb +214 -0
  42. data/lib/arjdbc.rb +19 -0
  43. data/lib/arjdbc/abstract/connection_management.rb +35 -0
  44. data/lib/arjdbc/abstract/core.rb +74 -0
  45. data/lib/arjdbc/abstract/database_statements.rb +64 -0
  46. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  47. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  48. data/lib/arjdbc/db2.rb +4 -0
  49. data/lib/arjdbc/db2/adapter.rb +789 -0
  50. data/lib/arjdbc/db2/as400.rb +130 -0
  51. data/lib/arjdbc/db2/column.rb +167 -0
  52. data/lib/arjdbc/db2/connection_methods.rb +44 -0
  53. data/lib/arjdbc/derby.rb +3 -0
  54. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  55. data/lib/arjdbc/derby/adapter.rb +540 -0
  56. data/lib/arjdbc/derby/connection_methods.rb +20 -0
  57. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  58. data/lib/arjdbc/discover.rb +104 -0
  59. data/lib/arjdbc/firebird.rb +4 -0
  60. data/lib/arjdbc/firebird/adapter.rb +434 -0
  61. data/lib/arjdbc/firebird/connection_methods.rb +23 -0
  62. data/lib/arjdbc/h2.rb +3 -0
  63. data/lib/arjdbc/h2/adapter.rb +303 -0
  64. data/lib/arjdbc/h2/connection_methods.rb +27 -0
  65. data/lib/arjdbc/hsqldb.rb +3 -0
  66. data/lib/arjdbc/hsqldb/adapter.rb +297 -0
  67. data/lib/arjdbc/hsqldb/connection_methods.rb +28 -0
  68. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  69. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  70. data/lib/arjdbc/informix.rb +5 -0
  71. data/lib/arjdbc/informix/adapter.rb +162 -0
  72. data/lib/arjdbc/informix/connection_methods.rb +9 -0
  73. data/lib/arjdbc/jdbc.rb +59 -0
  74. data/lib/arjdbc/jdbc/adapter.rb +475 -0
  75. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  76. data/lib/arjdbc/jdbc/base_ext.rb +15 -0
  77. data/lib/arjdbc/jdbc/callbacks.rb +53 -0
  78. data/lib/arjdbc/jdbc/column.rb +97 -0
  79. data/lib/arjdbc/jdbc/connection.rb +14 -0
  80. data/lib/arjdbc/jdbc/connection_methods.rb +37 -0
  81. data/lib/arjdbc/jdbc/error.rb +65 -0
  82. data/lib/arjdbc/jdbc/extension.rb +59 -0
  83. data/lib/arjdbc/jdbc/java.rb +13 -0
  84. data/lib/arjdbc/jdbc/railtie.rb +2 -0
  85. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -0
  86. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
  87. data/lib/arjdbc/jdbc/type_cast.rb +166 -0
  88. data/lib/arjdbc/jdbc/type_converter.rb +142 -0
  89. data/lib/arjdbc/mssql.rb +7 -0
  90. data/lib/arjdbc/mssql/adapter.rb +384 -0
  91. data/lib/arjdbc/mssql/column.rb +29 -0
  92. data/lib/arjdbc/mssql/connection_methods.rb +79 -0
  93. data/lib/arjdbc/mssql/database_statements.rb +134 -0
  94. data/lib/arjdbc/mssql/errors.rb +6 -0
  95. data/lib/arjdbc/mssql/explain_support.rb +129 -0
  96. data/lib/arjdbc/mssql/extensions.rb +36 -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/old_adapter.rb +804 -0
  100. data/lib/arjdbc/mssql/old_column.rb +200 -0
  101. data/lib/arjdbc/mssql/quoting.rb +101 -0
  102. data/lib/arjdbc/mssql/schema_creation.rb +31 -0
  103. data/lib/arjdbc/mssql/schema_definitions.rb +74 -0
  104. data/lib/arjdbc/mssql/schema_statements.rb +329 -0
  105. data/lib/arjdbc/mssql/transaction.rb +69 -0
  106. data/lib/arjdbc/mssql/types.rb +52 -0
  107. data/lib/arjdbc/mssql/types/binary_types.rb +33 -0
  108. data/lib/arjdbc/mssql/types/date_and_time_types.rb +134 -0
  109. data/lib/arjdbc/mssql/types/deprecated_types.rb +40 -0
  110. data/lib/arjdbc/mssql/types/numeric_types.rb +71 -0
  111. data/lib/arjdbc/mssql/types/string_types.rb +56 -0
  112. data/lib/arjdbc/mssql/utils.rb +66 -0
  113. data/lib/arjdbc/mysql.rb +3 -0
  114. data/lib/arjdbc/mysql/adapter.rb +140 -0
  115. data/lib/arjdbc/mysql/connection_methods.rb +166 -0
  116. data/lib/arjdbc/oracle/adapter.rb +863 -0
  117. data/lib/arjdbc/postgresql.rb +3 -0
  118. data/lib/arjdbc/postgresql/adapter.rb +687 -0
  119. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  120. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  121. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  122. data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
  123. data/lib/arjdbc/postgresql/column.rb +51 -0
  124. data/lib/arjdbc/postgresql/connection_methods.rb +67 -0
  125. data/lib/arjdbc/postgresql/name.rb +24 -0
  126. data/lib/arjdbc/postgresql/oid_types.rb +266 -0
  127. data/lib/arjdbc/railtie.rb +11 -0
  128. data/lib/arjdbc/sqlite3.rb +3 -0
  129. data/lib/arjdbc/sqlite3/adapter.rb +678 -0
  130. data/lib/arjdbc/sqlite3/connection_methods.rb +59 -0
  131. data/lib/arjdbc/sybase.rb +2 -0
  132. data/lib/arjdbc/sybase/adapter.rb +47 -0
  133. data/lib/arjdbc/tasks.rb +13 -0
  134. data/lib/arjdbc/tasks/database_tasks.rb +31 -0
  135. data/lib/arjdbc/tasks/databases.rake +48 -0
  136. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  137. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  138. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  139. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  140. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  141. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -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 +3 -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 +132 -0
  157. data/rakelib/bundler_ext.rb +11 -0
  158. data/rakelib/db.rake +75 -0
  159. data/rakelib/rails.rake +223 -0
  160. data/src/java/arjdbc/ArJdbcModule.java +276 -0
  161. data/src/java/arjdbc/db2/DB2Module.java +76 -0
  162. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +126 -0
  163. data/src/java/arjdbc/derby/DerbyModule.java +178 -0
  164. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +152 -0
  165. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +174 -0
  166. data/src/java/arjdbc/h2/H2Module.java +50 -0
  167. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +85 -0
  168. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +73 -0
  169. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +75 -0
  170. data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
  171. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  172. data/src/java/arjdbc/jdbc/ConnectionFactory.java +45 -0
  173. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
  174. data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
  175. data/src/java/arjdbc/jdbc/DriverWrapper.java +119 -0
  176. data/src/java/arjdbc/jdbc/JdbcResult.java +130 -0
  177. data/src/java/arjdbc/jdbc/RubyConnectionFactory.java +61 -0
  178. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3979 -0
  179. data/src/java/arjdbc/mssql/MSSQLModule.java +90 -0
  180. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +508 -0
  181. data/src/java/arjdbc/mysql/MySQLModule.java +152 -0
  182. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +294 -0
  183. data/src/java/arjdbc/oracle/OracleModule.java +80 -0
  184. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +455 -0
  185. data/src/java/arjdbc/postgresql/ByteaUtils.java +157 -0
  186. data/src/java/arjdbc/postgresql/PgDateTimeUtils.java +52 -0
  187. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +77 -0
  188. data/src/java/arjdbc/postgresql/PostgreSQLResult.java +192 -0
  189. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +948 -0
  190. data/src/java/arjdbc/sqlite3/SQLite3Module.java +73 -0
  191. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +525 -0
  192. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  193. data/src/java/arjdbc/util/DateTimeUtils.java +699 -0
  194. data/src/java/arjdbc/util/ObjectSupport.java +65 -0
  195. data/src/java/arjdbc/util/QuotingUtils.java +137 -0
  196. data/src/java/arjdbc/util/StringCache.java +63 -0
  197. data/src/java/arjdbc/util/StringHelper.java +145 -0
  198. metadata +269 -0
@@ -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
@@ -0,0 +1,101 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MSSQL
4
+ module Quoting
5
+ # Quote date/time values for use in SQL input, includes microseconds
6
+ # with three digits only if the value is a Time responding to usec.
7
+ # The JDBC drivers does not work with 6 digits microseconds
8
+ def quoted_date(value)
9
+ if value.acts_like?(:time)
10
+ value = time_with_db_timezone(value)
11
+ end
12
+
13
+ result = value.to_s(:db)
14
+
15
+ if value.respond_to?(:usec) && value.usec > 0
16
+ "#{result}.#{sprintf("%06d", value.usec)}"
17
+ else
18
+ result
19
+ end
20
+ end
21
+
22
+ # Quotes strings for use in SQL input.
23
+ def quote_string(s)
24
+ s.to_s.gsub(/\'/, "''")
25
+ end
26
+
27
+ # Does not quote function default values for UUID columns
28
+ def quote_default_expression(value, column)
29
+ cast_type = lookup_cast_type(column.sql_type)
30
+ if cast_type.type == :uuid && value =~ /\(\)/
31
+ value
32
+ else
33
+ super
34
+ end
35
+ end
36
+
37
+ def quoted_true
38
+ 1
39
+ end
40
+
41
+ def quoted_false
42
+ 0
43
+ end
44
+
45
+ # @override
46
+ def quoted_time(value)
47
+ if value.acts_like?(:time)
48
+ tz_value = time_with_db_timezone(value)
49
+ usec = value.respond_to?(:usec) ? value.usec : 0
50
+ sprintf('%02d:%02d:%02d.%06d', tz_value.hour, tz_value.min, tz_value.sec, usec)
51
+ else
52
+ quoted_date(value)
53
+ end
54
+ end
55
+
56
+ # @private
57
+ # @see #quote in old adapter
58
+ BLOB_VALUE_MARKER = "''"
59
+
60
+ private
61
+
62
+ def time_with_db_timezone(value)
63
+ zone_conv_method = if ActiveRecord::Base.default_timezone == :utc
64
+ :getutc
65
+ else
66
+ :getlocal
67
+ end
68
+
69
+ if value.respond_to?(zone_conv_method)
70
+ value = value.send(zone_conv_method)
71
+ else
72
+ value
73
+ end
74
+ end
75
+
76
+ # @override
77
+ # FIXME: it need to be improved to handle other custom types.
78
+ # Also check if it's possible insert integer into a NVARCHAR
79
+ def _quote(value)
80
+ case value
81
+ when ActiveRecord::Type::Binary::Data
82
+ "0x#{value.hex}"
83
+ # when SomeOtherBinaryData then BLOB_VALUE_MARKER
84
+ # when SomeOtherData then "yyy"
85
+ when String, ActiveSupport::Multibyte::Chars
86
+ "N'#{quote_string(value)}'"
87
+ # when OnlyTimeType then "'#{quoted_time(value)}'"
88
+ when Date, Time
89
+ "'#{quoted_date(value)}'"
90
+ when TrueClass
91
+ quoted_true
92
+ when FalseClass
93
+ quoted_false
94
+ else
95
+ super
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,31 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MSSQL
4
+ class SchemaCreation < AbstractAdapter::SchemaCreation
5
+ private
6
+
7
+ def visit_TableDefinition(o)
8
+ if o.as
9
+ table_name = quote_table_name(o.temporary ? "##{o.name}" : o.name)
10
+ projections, source = @conn.to_sql(o.as).match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures
11
+ select_into = "SELECT #{projections} INTO #{table_name} FROM #{source}"
12
+ else
13
+ o.instance_variable_set :@as, nil
14
+ super
15
+ end
16
+ end
17
+
18
+ # There is no RESTRICT in MSSQL but it has NO ACTION which behave
19
+ # same as RESTRICT, added this behave according rails api.
20
+ def action_sql(action, dependency)
21
+ case dependency
22
+ when :restrict then "ON #{action} NO ACTION"
23
+ else
24
+ super
25
+ end
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,74 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MSSQL
4
+ module ColumnMethods
5
+ # datetime with seconds always zero (:00) and without fractional seconds
6
+ def smalldatetime(*args, **options)
7
+ args.each { |name| column(name, :smalldatetime, options) }
8
+ end
9
+
10
+ # this is the old sql server datetime type, the precision is as follow
11
+ # xx1, xx3, and xx7
12
+ def datetime_basic(*args, **options)
13
+ args.each { |name| column(name, :datetime_basic, options) }
14
+ end
15
+
16
+ def real(*args, **options)
17
+ args.each { |name| column(name, :real, options) }
18
+ end
19
+
20
+ def money(*args, **options)
21
+ args.each { |name| column(name, :money, options) }
22
+ end
23
+
24
+ def smallmoney(*args, **options)
25
+ args.each { |name| column(name, :smallmoney, options) }
26
+ end
27
+
28
+ def char(*args, **options)
29
+ args.each { |name| column(name, :char, options) }
30
+ end
31
+
32
+ def varchar(*args, **options)
33
+ args.each { |name| column(name, :varchar, options) }
34
+ end
35
+
36
+ def varchar_max(*args, **options)
37
+ args.each { |name| column(name, :varchar_max, options) }
38
+ end
39
+
40
+ def text_basic(*args, **options)
41
+ args.each { |name| column(name, :text_basic, options) }
42
+ end
43
+
44
+ def nchar(*args, **options)
45
+ args.each { |name| column(name, :nchar, options) }
46
+ end
47
+
48
+ def ntext(*args, **options)
49
+ args.each { |name| column(name, :ntext, options) }
50
+ end
51
+
52
+ def binary_basic(*args, **options)
53
+ args.each { |name| column(name, :binary_basic, options) }
54
+ end
55
+
56
+ def varbinary(*args, **options)
57
+ args.each { |name| column(name, :varbinary, options) }
58
+ end
59
+
60
+ def uuid(*args, **options)
61
+ args.each { |name| column(name, :uniqueidentifier, options) }
62
+ end
63
+ end
64
+
65
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
66
+ include ColumnMethods
67
+ end
68
+
69
+ class Table < ActiveRecord::ConnectionAdapters::Table
70
+ include ColumnMethods
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,329 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MSSQL
4
+ module SchemaStatements
5
+
6
+ NATIVE_DATABASE_TYPES = {
7
+ # Logical Rails types to SQL Server types
8
+ primary_key: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY',
9
+ integer: { name: 'int', limit: 4 },
10
+ boolean: { name: 'bit' },
11
+ decimal: { name: 'decimal' },
12
+ float: { name: 'float' },
13
+ date: { name: 'date' },
14
+ time: { name: 'time' },
15
+ datetime: { name: 'datetime2' },
16
+ string: { name: 'nvarchar', limit: 4000 },
17
+ text: { name: 'nvarchar(max)' },
18
+ binary: { name: 'varbinary(max)' },
19
+ # Other types or SQL Server specific
20
+ bigint: { name: 'bigint' },
21
+ smalldatetime: { name: 'smalldatetime' },
22
+ datetime_basic: { name: 'datetime' },
23
+ timestamp: { name: 'datetime' },
24
+ real: { name: 'real' },
25
+ money: { name: 'money' },
26
+ smallmoney: { name: 'smallmoney' },
27
+ char: { name: 'char' },
28
+ nchar: { name: 'nchar' },
29
+ varchar: { name: 'varchar', limit: 8000 },
30
+ varchar_max: { name: 'varchar(max)' },
31
+ uuid: { name: 'uniqueidentifier' },
32
+ binary_basic: { name: 'binary' },
33
+ varbinary: { name: 'varbinary', limit: 8000 },
34
+ # Deprecated SQL Server types
35
+ image: { name: 'image' },
36
+ ntext: { name: 'ntext' },
37
+ text_basic: { name: 'text' }
38
+ }.freeze
39
+
40
+ def native_database_types
41
+ NATIVE_DATABASE_TYPES
42
+ end
43
+
44
+ # Returns an array of table names defined in the database.
45
+ def tables(name = nil)
46
+ if name
47
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
48
+ Passing arguments to #tables is deprecated without replacement.
49
+ MSG
50
+ end
51
+
52
+ @connection.tables(nil, name)
53
+ end
54
+
55
+ # Returns an array of Column objects for the table specified by +table_name+.
56
+ # See the concrete implementation for details on the expected parameter values.
57
+ # NOTE: This is ready, all implemented in the java part of adapter,
58
+ # it uses MSSQLColumn, SqlTypeMetadata, etc.
59
+ def columns(table_name)
60
+ @connection.columns(table_name)
61
+ rescue => e
62
+ raise translate_exception_class(e, nil)
63
+ end
64
+
65
+ # Returns an array of view names defined in the database.
66
+ # (to be implemented)
67
+ def views
68
+ []
69
+ end
70
+
71
+ def table_exists?(table_name)
72
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
73
+ #table_exists? currently checks both tables and views.
74
+ This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
75
+ Use #data_source_exists? instead.
76
+ MSG
77
+
78
+ tables.include?(table_name.to_s)
79
+ end
80
+
81
+ # Returns an array of indexes for the given table.
82
+ def indexes(table_name, name = nil)
83
+ @connection.indexes(table_name, name)
84
+ end
85
+
86
+ def primary_keys(table_name)
87
+ @connection.primary_keys(table_name)
88
+ end
89
+
90
+ def foreign_keys(table_name)
91
+ @connection.foreign_keys(table_name)
92
+ end
93
+
94
+ def charset
95
+ select_value "SELECT SqlCharSetName = CAST(SERVERPROPERTY('SqlCharSetName') AS NVARCHAR(128))"
96
+ end
97
+
98
+ def collation
99
+ select_value "SELECT Collation = CAST(SERVERPROPERTY('Collation') AS NVARCHAR(128))"
100
+ end
101
+
102
+ def current_database
103
+ select_value 'SELECT DB_NAME()'
104
+ end
105
+
106
+ def use_database(database = nil)
107
+ database ||= config[:database]
108
+ execute "USE #{quote_database_name(database)}" unless database.blank?
109
+ end
110
+
111
+ def drop_database(name)
112
+ current_db = current_database
113
+ use_database('master') if current_db.to_s == name
114
+ # Only SQL Server 2016 onwards:
115
+ # execute "DROP DATABASE IF EXISTS #{quote_database_name(name)}"
116
+ execute "IF EXISTS(SELECT name FROM sys.databases WHERE name='#{name}') DROP DATABASE #{quote_database_name(name)}"
117
+ end
118
+
119
+ def create_database(name, options = {})
120
+ execute "CREATE DATABASE #{quote_database_name(name)}"
121
+ end
122
+
123
+ def recreate_database(name, options = {})
124
+ drop_database(name)
125
+ create_database(name, options)
126
+ end
127
+
128
+ def remove_column(table_name, column_name, type = nil, options = {})
129
+ raise ArgumentError.new('You must specify at least one column name. Example: remove_column(:people, :first_name)') if column_name.is_a? Array
130
+ remove_check_constraints(table_name, column_name)
131
+ remove_default_constraint(table_name, column_name)
132
+ remove_indexes(table_name, column_name)
133
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
134
+ end
135
+
136
+ def drop_table(table_name, options = {})
137
+ # mssql cannot recreate referenced table with force: :cascade
138
+ # https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-table-transact-sql?view=sql-server-2017
139
+ if options[:force] == :cascade
140
+ execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata|
141
+ fktable = fkdata['FKTABLE_NAME']
142
+ fkcolmn = fkdata['FKCOLUMN_NAME']
143
+ pktable = fkdata['PKTABLE_NAME']
144
+ pkcolmn = fkdata['PKCOLUMN_NAME']
145
+ remove_foreign_key(fktable, name: fkdata['FK_NAME'])
146
+ execute("DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )")
147
+ end
148
+ end
149
+
150
+ if options[:if_exists] && @mssql_major_version < 13
151
+ # this is for sql server 2012 and 2014
152
+ execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}"
153
+ else
154
+ # For sql server 2016 onwards
155
+ super
156
+ end
157
+ end
158
+
159
+ def rename_table(table_name, new_table_name)
160
+ execute "EXEC sp_rename '#{table_name}', '#{new_table_name}'"
161
+ rename_table_indexes(table_name, new_table_name)
162
+ end
163
+
164
+ # This is the same as the abstract method
165
+ def quote_table_name(name)
166
+ quote_column_name(name)
167
+ end
168
+
169
+ # This overrides the abstract method to be specific to SQL Server.
170
+ def quote_column_name(name)
171
+ name = name.to_s.split('.')
172
+ name.map! { |n| quote_name_part(n) } # "[#{name}]"
173
+ name.join('.')
174
+ end
175
+
176
+ def quote_database_name(name)
177
+ quote_name_part(name.to_s)
178
+ end
179
+
180
+ # @private these cannot specify a limit
181
+ NO_LIMIT_TYPES = %w(text binary boolean date)
182
+
183
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
184
+ type_s = type.to_s
185
+ # MSSQL's NVARCHAR(n | max) column supports either a number between 1 and
186
+ # 4000, or the word "MAX", which corresponds to 2**30-1 UCS-2 characters.
187
+ #
188
+ # It does not accept NVARCHAR(1073741823) here, so we have to change it
189
+ # to NVARCHAR(MAX), even though they are logically equivalent.
190
+ #
191
+ # See: http://msdn.microsoft.com/en-us/library/ms186939.aspx
192
+ #
193
+ if type_s == 'string' && limit == 1073741823
194
+ 'NVARCHAR(MAX)'
195
+ elsif NO_LIMIT_TYPES.include?(type_s)
196
+ super(type)
197
+ elsif type_s == 'integer' || type_s == 'int'
198
+ if limit.nil? || limit == 4
199
+ 'int'
200
+ elsif limit == 2
201
+ 'smallint'
202
+ elsif limit == 1
203
+ 'tinyint'
204
+ else
205
+ 'bigint'
206
+ end
207
+ elsif type_s == 'uniqueidentifier'
208
+ type_s
209
+ else
210
+ super
211
+ end
212
+ end
213
+
214
+ # SQL Server requires the ORDER BY columns in the select
215
+ # list for distinct queries, and requires that the ORDER BY
216
+ # include the distinct column.
217
+ def columns_for_distinct(columns, orders) #:nodoc:
218
+ order_columns = orders.reject(&:blank?).map{ |s|
219
+ # Convert Arel node to string
220
+ s = s.to_sql unless s.is_a?(String)
221
+ # Remove any ASC/DESC modifiers
222
+ s.gsub(/\s+(?:ASC|DESC)\b/i, '')
223
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
224
+ }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
225
+
226
+ [super, *order_columns].join(', ')
227
+ end
228
+
229
+ def rename_column(table_name, column_name, new_column_name)
230
+ # The below line checks if column exists otherwise raise activerecord
231
+ # default exception for this case.
232
+ _column = column_for(table_name, column_name)
233
+
234
+ execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
235
+ rename_column_indexes(table_name, column_name, new_column_name)
236
+ end
237
+
238
+ def change_column_default(table_name, column_name, default_or_changes)
239
+ remove_default_constraint(table_name, column_name)
240
+
241
+ default = extract_new_default_value(default_or_changes)
242
+ unless default.nil?
243
+ column = columns(table_name).find { |c| c.name.to_s == column_name.to_s }
244
+ result = execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote_default_expression(default, column)} FOR #{quote_column_name(column_name)}"
245
+ result
246
+ end
247
+ end
248
+
249
+ def change_column(table_name, column_name, type, options = {})
250
+ column = columns(table_name).find { |c| c.name.to_s == column_name.to_s }
251
+
252
+ indexes = []
253
+ if options_include_default?(options) || (column && column.type != type.to_sym)
254
+ remove_default_constraint(table_name, column_name)
255
+ indexes = indexes(table_name).select{ |index| index.columns.include?(column_name.to_s) }
256
+ remove_indexes(table_name, column_name)
257
+ end
258
+
259
+ if !options[:null].nil? && options[:null] == false && !options[:default].nil?
260
+ execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column)} WHERE #{quote_column_name(column_name)} IS NULL"
261
+ end
262
+
263
+ change_column_type(table_name, column_name, type, options)
264
+ change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
265
+
266
+ # add any removed indexes back
267
+ indexes.each do |index|
268
+ index_columns = index.columns.map { |c| quote_column_name(c) }.join(', ')
269
+ execute "CREATE INDEX #{quote_table_name(index.name)} ON #{quote_table_name(table_name)} (#{index_columns})"
270
+ end
271
+ end
272
+
273
+ def change_column_null(table_name, column_name, null, default = nil)
274
+ column = column_for(table_name, column_name)
275
+ quoted_table = quote_table_name(table_name)
276
+ quoted_column = quote_column_name(column_name)
277
+ quoted_default = quote(default)
278
+ unless null || default.nil?
279
+ execute("UPDATE #{quoted_table} SET #{quoted_column}=#{quoted_default} WHERE #{quoted_column} IS NULL")
280
+ end
281
+ sql_alter = [
282
+ "ALTER TABLE #{quoted_table}",
283
+ "ALTER COLUMN #{quoted_column} #{type_to_sql column.type, column.limit, column.precision, column.scale}",
284
+ (' NOT NULL' unless null)
285
+ ]
286
+
287
+ execute(sql_alter.join(' '))
288
+ end
289
+
290
+ private
291
+
292
+ def change_column_type(table_name, column_name, type, options = {})
293
+ sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
294
+ sql << (options[:null] ? " NULL" : " NOT NULL") if options.has_key?(:null)
295
+ result = execute(sql)
296
+ result
297
+ end
298
+
299
+ # Implements the quoting style for SQL Server
300
+ def quote_name_part(part)
301
+ part =~ /^\[.*\]$/ ? part : "[#{part.gsub(']', ']]')}]"
302
+ end
303
+
304
+ def remove_check_constraints(table_name, column_name)
305
+ constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", 'SCHEMA'
306
+ constraints.each do |constraint|
307
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}"
308
+ end
309
+ end
310
+
311
+ def remove_default_constraint(table_name, column_name)
312
+ # If their are foreign keys in this table, we could still get back a 2D array, so flatten just in case.
313
+ execute_procedure(:sp_helpconstraint, table_name, 'nomsg').flatten.select do |row|
314
+ row['constraint_type'] == "DEFAULT on column #{column_name}"
315
+ end.each do |row|
316
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{row['constraint_name']}"
317
+ end
318
+ end
319
+
320
+ def remove_indexes(table_name, column_name)
321
+ indexes(table_name).select { |index| index.columns.include?(column_name.to_s) }.each do |index|
322
+ remove_index(table_name, name: index.name)
323
+ end
324
+ end
325
+
326
+ end
327
+ end
328
+ end
329
+ end