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.
- checksums.yaml +7 -0
- data/.gitignore +35 -0
- data/.travis.yml +100 -0
- data/.yardopts +4 -0
- data/CONTRIBUTING.md +50 -0
- data/Gemfile +92 -0
- data/History.md +1191 -0
- data/LICENSE.txt +26 -0
- data/README.md +240 -0
- data/RUNNING_TESTS.md +127 -0
- data/Rakefile +336 -0
- data/Rakefile.jdbc +20 -0
- data/activerecord-jdbc-adapter.gemspec +55 -0
- data/activerecord-jdbc-alt-adapter.gemspec +56 -0
- data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
- data/lib/activerecord-jdbc-adapter.rb +1 -0
- data/lib/arel/visitors/compat.rb +60 -0
- data/lib/arel/visitors/db2.rb +137 -0
- data/lib/arel/visitors/derby.rb +112 -0
- data/lib/arel/visitors/firebird.rb +79 -0
- data/lib/arel/visitors/h2.rb +25 -0
- data/lib/arel/visitors/hsqldb.rb +32 -0
- data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
- data/lib/arel/visitors/sql_server.rb +225 -0
- data/lib/arel/visitors/sql_server/ng42.rb +294 -0
- data/lib/arel/visitors/sqlserver.rb +214 -0
- data/lib/arjdbc.rb +19 -0
- data/lib/arjdbc/abstract/connection_management.rb +35 -0
- data/lib/arjdbc/abstract/core.rb +74 -0
- data/lib/arjdbc/abstract/database_statements.rb +64 -0
- data/lib/arjdbc/abstract/statement_cache.rb +58 -0
- data/lib/arjdbc/abstract/transaction_support.rb +86 -0
- data/lib/arjdbc/db2.rb +4 -0
- data/lib/arjdbc/db2/adapter.rb +789 -0
- data/lib/arjdbc/db2/as400.rb +130 -0
- data/lib/arjdbc/db2/column.rb +167 -0
- data/lib/arjdbc/db2/connection_methods.rb +44 -0
- data/lib/arjdbc/derby.rb +3 -0
- data/lib/arjdbc/derby/active_record_patch.rb +13 -0
- data/lib/arjdbc/derby/adapter.rb +540 -0
- data/lib/arjdbc/derby/connection_methods.rb +20 -0
- data/lib/arjdbc/derby/schema_creation.rb +15 -0
- data/lib/arjdbc/discover.rb +104 -0
- data/lib/arjdbc/firebird.rb +4 -0
- data/lib/arjdbc/firebird/adapter.rb +434 -0
- data/lib/arjdbc/firebird/connection_methods.rb +23 -0
- data/lib/arjdbc/h2.rb +3 -0
- data/lib/arjdbc/h2/adapter.rb +303 -0
- data/lib/arjdbc/h2/connection_methods.rb +27 -0
- data/lib/arjdbc/hsqldb.rb +3 -0
- data/lib/arjdbc/hsqldb/adapter.rb +297 -0
- data/lib/arjdbc/hsqldb/connection_methods.rb +28 -0
- data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
- data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
- data/lib/arjdbc/informix.rb +5 -0
- data/lib/arjdbc/informix/adapter.rb +162 -0
- data/lib/arjdbc/informix/connection_methods.rb +9 -0
- data/lib/arjdbc/jdbc.rb +59 -0
- data/lib/arjdbc/jdbc/adapter.rb +475 -0
- data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
- data/lib/arjdbc/jdbc/base_ext.rb +15 -0
- data/lib/arjdbc/jdbc/callbacks.rb +53 -0
- data/lib/arjdbc/jdbc/column.rb +97 -0
- data/lib/arjdbc/jdbc/connection.rb +14 -0
- data/lib/arjdbc/jdbc/connection_methods.rb +37 -0
- data/lib/arjdbc/jdbc/error.rb +65 -0
- data/lib/arjdbc/jdbc/extension.rb +59 -0
- data/lib/arjdbc/jdbc/java.rb +13 -0
- data/lib/arjdbc/jdbc/railtie.rb +2 -0
- data/lib/arjdbc/jdbc/rake_tasks.rb +3 -0
- data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
- data/lib/arjdbc/jdbc/type_cast.rb +166 -0
- data/lib/arjdbc/jdbc/type_converter.rb +142 -0
- data/lib/arjdbc/mssql.rb +7 -0
- data/lib/arjdbc/mssql/adapter.rb +384 -0
- data/lib/arjdbc/mssql/column.rb +29 -0
- data/lib/arjdbc/mssql/connection_methods.rb +79 -0
- data/lib/arjdbc/mssql/database_statements.rb +134 -0
- data/lib/arjdbc/mssql/errors.rb +6 -0
- data/lib/arjdbc/mssql/explain_support.rb +129 -0
- data/lib/arjdbc/mssql/extensions.rb +36 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
- data/lib/arjdbc/mssql/lock_methods.rb +77 -0
- data/lib/arjdbc/mssql/old_adapter.rb +804 -0
- data/lib/arjdbc/mssql/old_column.rb +200 -0
- data/lib/arjdbc/mssql/quoting.rb +101 -0
- data/lib/arjdbc/mssql/schema_creation.rb +31 -0
- data/lib/arjdbc/mssql/schema_definitions.rb +74 -0
- data/lib/arjdbc/mssql/schema_statements.rb +329 -0
- data/lib/arjdbc/mssql/transaction.rb +69 -0
- data/lib/arjdbc/mssql/types.rb +52 -0
- data/lib/arjdbc/mssql/types/binary_types.rb +33 -0
- data/lib/arjdbc/mssql/types/date_and_time_types.rb +134 -0
- data/lib/arjdbc/mssql/types/deprecated_types.rb +40 -0
- data/lib/arjdbc/mssql/types/numeric_types.rb +71 -0
- data/lib/arjdbc/mssql/types/string_types.rb +56 -0
- data/lib/arjdbc/mssql/utils.rb +66 -0
- data/lib/arjdbc/mysql.rb +3 -0
- data/lib/arjdbc/mysql/adapter.rb +140 -0
- data/lib/arjdbc/mysql/connection_methods.rb +166 -0
- data/lib/arjdbc/oracle/adapter.rb +863 -0
- data/lib/arjdbc/postgresql.rb +3 -0
- data/lib/arjdbc/postgresql/adapter.rb +687 -0
- data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
- data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
- data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
- data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
- data/lib/arjdbc/postgresql/column.rb +51 -0
- data/lib/arjdbc/postgresql/connection_methods.rb +67 -0
- data/lib/arjdbc/postgresql/name.rb +24 -0
- data/lib/arjdbc/postgresql/oid_types.rb +266 -0
- data/lib/arjdbc/railtie.rb +11 -0
- data/lib/arjdbc/sqlite3.rb +3 -0
- data/lib/arjdbc/sqlite3/adapter.rb +678 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +59 -0
- data/lib/arjdbc/sybase.rb +2 -0
- data/lib/arjdbc/sybase/adapter.rb +47 -0
- data/lib/arjdbc/tasks.rb +13 -0
- data/lib/arjdbc/tasks/database_tasks.rb +31 -0
- data/lib/arjdbc/tasks/databases.rake +48 -0
- data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
- data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
- data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
- data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
- data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
- data/lib/arjdbc/util/quoted_cache.rb +60 -0
- data/lib/arjdbc/util/serialized_attributes.rb +98 -0
- data/lib/arjdbc/util/table_copier.rb +110 -0
- data/lib/arjdbc/version.rb +3 -0
- data/lib/generators/jdbc/USAGE +9 -0
- data/lib/generators/jdbc/jdbc_generator.rb +17 -0
- data/lib/jdbc_adapter.rb +2 -0
- data/lib/jdbc_adapter/rake_tasks.rb +4 -0
- data/lib/jdbc_adapter/version.rb +4 -0
- data/pom.xml +114 -0
- data/rails_generators/jdbc_generator.rb +15 -0
- data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
- data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
- data/rakelib/01-tomcat.rake +51 -0
- data/rakelib/02-test.rake +132 -0
- data/rakelib/bundler_ext.rb +11 -0
- data/rakelib/db.rake +75 -0
- data/rakelib/rails.rake +223 -0
- data/src/java/arjdbc/ArJdbcModule.java +276 -0
- data/src/java/arjdbc/db2/DB2Module.java +76 -0
- data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +126 -0
- data/src/java/arjdbc/derby/DerbyModule.java +178 -0
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +152 -0
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +174 -0
- data/src/java/arjdbc/h2/H2Module.java +50 -0
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +85 -0
- data/src/java/arjdbc/hsqldb/HSQLDBModule.java +73 -0
- data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +75 -0
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
- data/src/java/arjdbc/jdbc/Callable.java +44 -0
- data/src/java/arjdbc/jdbc/ConnectionFactory.java +45 -0
- data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
- data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
- data/src/java/arjdbc/jdbc/DriverWrapper.java +119 -0
- data/src/java/arjdbc/jdbc/JdbcResult.java +130 -0
- data/src/java/arjdbc/jdbc/RubyConnectionFactory.java +61 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3979 -0
- data/src/java/arjdbc/mssql/MSSQLModule.java +90 -0
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +508 -0
- data/src/java/arjdbc/mysql/MySQLModule.java +152 -0
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +294 -0
- data/src/java/arjdbc/oracle/OracleModule.java +80 -0
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +455 -0
- data/src/java/arjdbc/postgresql/ByteaUtils.java +157 -0
- data/src/java/arjdbc/postgresql/PgDateTimeUtils.java +52 -0
- data/src/java/arjdbc/postgresql/PostgreSQLModule.java +77 -0
- data/src/java/arjdbc/postgresql/PostgreSQLResult.java +192 -0
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +948 -0
- data/src/java/arjdbc/sqlite3/SQLite3Module.java +73 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +525 -0
- data/src/java/arjdbc/util/CallResultSet.java +826 -0
- data/src/java/arjdbc/util/DateTimeUtils.java +699 -0
- data/src/java/arjdbc/util/ObjectSupport.java +65 -0
- data/src/java/arjdbc/util/QuotingUtils.java +137 -0
- data/src/java/arjdbc/util/StringCache.java +63 -0
- data/src/java/arjdbc/util/StringHelper.java +145 -0
- 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
|