activerecord-jdbc-alt-adapter 52.4.0-java → 61.0.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 +4 -4
- data/.gitignore +3 -0
- data/.nvimlog +0 -0
- data/.travis.yml +63 -39
- data/Gemfile +11 -4
- data/README.md +55 -35
- data/Rakefile +1 -1
- data/Rakefile.jdbc +8 -1
- data/activerecord-jdbc-adapter.gemspec +6 -9
- data/activerecord-jdbc-alt-adapter.gemspec +9 -12
- data/lib/arel/visitors/postgresql_jdbc.rb +1 -1
- data/lib/arel/visitors/sqlserver.rb +49 -23
- data/lib/arjdbc/abstract/connection_management.rb +7 -0
- data/lib/arjdbc/abstract/core.rb +17 -23
- data/lib/arjdbc/abstract/database_statements.rb +30 -2
- data/lib/arjdbc/abstract/statement_cache.rb +2 -5
- data/lib/arjdbc/abstract/transaction_support.rb +22 -7
- data/lib/arjdbc/db2/column.rb +0 -39
- data/lib/arjdbc/derby/adapter.rb +1 -20
- data/lib/arjdbc/firebird/adapter.rb +0 -21
- data/lib/arjdbc/h2/adapter.rb +0 -15
- data/lib/arjdbc/hsqldb/adapter.rb +0 -14
- data/lib/arjdbc/informix/adapter.rb +0 -23
- data/lib/arjdbc/jdbc/adapter.rb +3 -1
- data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
- data/lib/arjdbc/jdbc/base_ext.rb +3 -1
- data/lib/arjdbc/jdbc/callbacks.rb +2 -0
- data/lib/arjdbc/jdbc/column.rb +2 -0
- data/lib/arjdbc/jdbc/connection.rb +2 -0
- data/lib/arjdbc/jdbc/connection_methods.rb +2 -0
- data/lib/arjdbc/jdbc/error.rb +2 -0
- data/lib/arjdbc/jdbc/extension.rb +2 -0
- data/lib/arjdbc/jdbc/java.rb +3 -1
- data/lib/arjdbc/jdbc/railtie.rb +3 -1
- data/lib/arjdbc/jdbc/rake_tasks.rb +3 -1
- data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -1
- data/lib/arjdbc/jdbc/type_cast.rb +2 -0
- data/lib/arjdbc/jdbc/type_converter.rb +2 -0
- data/lib/arjdbc/mssql.rb +3 -1
- data/lib/arjdbc/mssql/adapter.rb +114 -36
- data/lib/arjdbc/mssql/column.rb +19 -1
- data/lib/arjdbc/mssql/connection_methods.rb +10 -2
- data/lib/arjdbc/mssql/database_limits.rb +9 -0
- data/lib/arjdbc/mssql/database_statements.rb +44 -6
- data/lib/arjdbc/mssql/errors.rb +2 -0
- data/lib/arjdbc/mssql/explain_support.rb +3 -1
- data/lib/arjdbc/mssql/extensions/attribute_methods.rb +6 -2
- data/lib/arjdbc/mssql/extensions/calculations.rb +2 -0
- data/lib/arjdbc/mssql/quoting.rb +38 -0
- data/lib/arjdbc/mssql/schema_creation.rb +25 -3
- data/lib/arjdbc/mssql/schema_definitions.rb +10 -0
- data/lib/arjdbc/mssql/schema_dumper.rb +2 -0
- data/lib/arjdbc/mssql/schema_statements.rb +92 -22
- data/lib/arjdbc/mssql/transaction.rb +2 -0
- data/lib/arjdbc/mssql/types.rb +2 -0
- data/lib/arjdbc/mssql/types/binary_types.rb +2 -0
- data/lib/arjdbc/mssql/types/date_and_time_types.rb +2 -0
- data/lib/arjdbc/mssql/types/deprecated_types.rb +2 -0
- data/lib/arjdbc/mssql/types/numeric_types.rb +2 -0
- data/lib/arjdbc/mssql/types/string_types.rb +2 -0
- data/lib/arjdbc/mssql/utils.rb +2 -0
- data/lib/arjdbc/mysql/adapter.rb +59 -21
- data/lib/arjdbc/mysql/connection_methods.rb +6 -1
- data/lib/arjdbc/postgresql/adapter.rb +257 -219
- data/lib/arjdbc/postgresql/base/array_decoder.rb +2 -0
- data/lib/arjdbc/postgresql/base/array_encoder.rb +4 -2
- data/lib/arjdbc/postgresql/base/array_parser.rb +4 -2
- data/lib/arjdbc/postgresql/base/pgconn.rb +2 -0
- data/lib/arjdbc/postgresql/column.rb +6 -4
- data/lib/arjdbc/postgresql/connection_methods.rb +1 -0
- data/lib/arjdbc/postgresql/name.rb +2 -0
- data/lib/arjdbc/postgresql/oid_types.rb +7 -4
- data/lib/arjdbc/sqlite3/adapter.rb +266 -221
- data/lib/arjdbc/sqlite3/connection_methods.rb +26 -4
- data/lib/arjdbc/tasks/databases.rake +21 -13
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +126 -25
- data/lib/arjdbc/util/quoted_cache.rb +3 -1
- data/lib/arjdbc/util/serialized_attributes.rb +3 -1
- data/lib/arjdbc/util/table_copier.rb +3 -1
- data/lib/arjdbc/version.rb +3 -1
- data/pom.xml +4 -4
- data/rakelib/01-tomcat.rake +2 -2
- data/rakelib/rails.rake +1 -1
- data/src/java/arjdbc/ArJdbcModule.java +5 -5
- data/src/java/arjdbc/jdbc/DriverWrapper.java +1 -9
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +549 -691
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +88 -0
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +13 -23
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +125 -53
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +97 -103
- data/src/java/arjdbc/util/DateTimeUtils.java +12 -4
- metadata +10 -18
data/lib/arjdbc/mssql/column.rb
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
# MSSQL specific extensions to column definitions in a table.
|
|
4
6
|
class MSSQLColumn < Column
|
|
7
|
+
attr_reader :table_name
|
|
8
|
+
|
|
5
9
|
def initialize(name, raw_default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil)
|
|
10
|
+
@table_name = table_name
|
|
11
|
+
|
|
6
12
|
default = extract_default(raw_default)
|
|
7
13
|
|
|
8
|
-
super(name, default, sql_type_metadata, null,
|
|
14
|
+
super(name, default, sql_type_metadata, null, default_function, collation: collation, comment: comment)
|
|
9
15
|
end
|
|
10
16
|
|
|
11
17
|
def extract_default(value)
|
|
@@ -24,6 +30,18 @@ module ActiveRecord
|
|
|
24
30
|
sql_type.downcase.include? 'identity'
|
|
25
31
|
end
|
|
26
32
|
|
|
33
|
+
def ==(other)
|
|
34
|
+
other.is_a?(MSSQLColumn) &&
|
|
35
|
+
super &&
|
|
36
|
+
table_name == other.table_name
|
|
37
|
+
end
|
|
38
|
+
alias :eql? :==
|
|
39
|
+
|
|
40
|
+
def hash
|
|
41
|
+
MSSQLColumn.hash ^
|
|
42
|
+
super.hash ^
|
|
43
|
+
table_name.hash
|
|
44
|
+
end
|
|
27
45
|
end
|
|
28
46
|
end
|
|
29
47
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
ArJdbc::ConnectionMethods.module_eval do
|
|
2
4
|
|
|
3
5
|
# Default connection method for MS-SQL adapter (`adapter: mssql`),
|
|
@@ -11,6 +13,8 @@ ArJdbc::ConnectionMethods.module_eval do
|
|
|
11
13
|
return sqlserver_connection(config)
|
|
12
14
|
end
|
|
13
15
|
|
|
16
|
+
config = config.deep_dup
|
|
17
|
+
|
|
14
18
|
config[:adapter_spec] ||= ::ArJdbc::MSSQL
|
|
15
19
|
config[:adapter_class] = ActiveRecord::ConnectionAdapters::MSSQLAdapter unless config.key?(:adapter_class)
|
|
16
20
|
|
|
@@ -31,7 +35,8 @@ ArJdbc::ConnectionMethods.module_eval do
|
|
|
31
35
|
config[:connection_alive_sql] ||= 'SELECT 1'
|
|
32
36
|
|
|
33
37
|
config[:url] ||= begin
|
|
34
|
-
url =
|
|
38
|
+
url = ''.dup
|
|
39
|
+
url << "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
|
|
35
40
|
# Instance is often a preferrable alternative to port when dynamic ports are used.
|
|
36
41
|
# If instance is specified then port is essentially ignored.
|
|
37
42
|
url << ";instance=#{config[:instance]}" if config[:instance]
|
|
@@ -52,6 +57,8 @@ ArJdbc::ConnectionMethods.module_eval do
|
|
|
52
57
|
|
|
53
58
|
# @note Assumes SQLServer SQL-JDBC driver on the class-path.
|
|
54
59
|
def sqlserver_connection(config)
|
|
60
|
+
config = config.deep_dup
|
|
61
|
+
|
|
55
62
|
config[:adapter_spec] ||= ::ArJdbc::MSSQL
|
|
56
63
|
config[:adapter_class] = ActiveRecord::ConnectionAdapters::MSSQLAdapter unless config.key?(:adapter_class)
|
|
57
64
|
|
|
@@ -63,7 +70,8 @@ ArJdbc::ConnectionMethods.module_eval do
|
|
|
63
70
|
config[:lock_timeout] ||= 5000
|
|
64
71
|
|
|
65
72
|
config[:url] ||= begin
|
|
66
|
-
url =
|
|
73
|
+
url = ''.dup
|
|
74
|
+
url << "jdbc:sqlserver://#{config[:host]}"
|
|
67
75
|
url << ( config[:port] ? ":#{config[:port]};" : ';' )
|
|
68
76
|
url << "databaseName=#{config[:database]};" if config[:database]
|
|
69
77
|
url << "instanceName=#{config[:instance]};" if config[:instance]
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module MSSQL
|
|
@@ -6,6 +8,13 @@ module ActiveRecord
|
|
|
6
8
|
# Returns the maximum number of elements in an IN (x,y,z) clause.
|
|
7
9
|
# NOTE: Could not find a limit for IN in mssql but 10000 seems to work
|
|
8
10
|
# with the active record tests
|
|
11
|
+
# FIXME: this method was deprecated in rails 6.1, and it seems the only
|
|
12
|
+
# code that used this method was the oracle visitor, the code was moved
|
|
13
|
+
# from rails to the adapter itself.
|
|
14
|
+
# https://github.com/rsim/oracle-enhanced/pull/2008/files
|
|
15
|
+
# related:
|
|
16
|
+
# https://github.com/rails/rails/pull/38946
|
|
17
|
+
# https://github.com/rails/rails/pull/39057
|
|
9
18
|
def in_clause_length
|
|
10
19
|
10_000
|
|
11
20
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module MSSQL
|
|
@@ -23,6 +25,15 @@ module ActiveRecord
|
|
|
23
25
|
end
|
|
24
26
|
alias_method :execute_procedure, :exec_proc # AR-SQLServer-Adapter naming
|
|
25
27
|
|
|
28
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
|
29
|
+
:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback, :with
|
|
30
|
+
) # :nodoc:
|
|
31
|
+
private_constant :READ_QUERY
|
|
32
|
+
|
|
33
|
+
def write_query?(sql) # :nodoc:
|
|
34
|
+
!READ_QUERY.match?(sql)
|
|
35
|
+
end
|
|
36
|
+
|
|
26
37
|
def execute(sql, name = nil)
|
|
27
38
|
# with identity insert on block
|
|
28
39
|
if insert_sql?(sql)
|
|
@@ -52,11 +63,6 @@ module ActiveRecord
|
|
|
52
63
|
end
|
|
53
64
|
end
|
|
54
65
|
|
|
55
|
-
# Implements the truncate method.
|
|
56
|
-
def truncate(table_name, name = nil)
|
|
57
|
-
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
|
58
|
-
end
|
|
59
|
-
|
|
60
66
|
# Not a rails method, own method to test different isolation
|
|
61
67
|
# levels supported by the mssql adapter.
|
|
62
68
|
def supports_transaction_isolation_level?(level)
|
|
@@ -95,8 +101,40 @@ module ActiveRecord
|
|
|
95
101
|
end
|
|
96
102
|
end
|
|
97
103
|
|
|
104
|
+
# Implements the truncate method.
|
|
105
|
+
def truncate(table_name, name = nil)
|
|
106
|
+
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def truncate_tables(*table_names) # :nodoc:
|
|
110
|
+
return if table_names.empty?
|
|
111
|
+
|
|
112
|
+
disable_referential_integrity do
|
|
113
|
+
table_names.each do |table_name|
|
|
114
|
+
mssql_truncate(table_name)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
98
119
|
private
|
|
99
120
|
|
|
121
|
+
# It seems the truncate_tables is mostly used for testing
|
|
122
|
+
# this a workaround to the fact that SQL Server truncate tables
|
|
123
|
+
# referenced by a foreign key, it may not be required to reset
|
|
124
|
+
# the identity column too, more at:
|
|
125
|
+
# https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-ver15
|
|
126
|
+
# TODO: improve is with pure T-SQL, use statements
|
|
127
|
+
# such as TRY CATCH and reset identity with DBCC CHECKIDENT
|
|
128
|
+
def mssql_truncate(table_name)
|
|
129
|
+
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", 'Truncate Tables'
|
|
130
|
+
rescue => e
|
|
131
|
+
if e.message =~ /Cannot truncate table .* because it is being referenced by a FOREIGN KEY constraint/
|
|
132
|
+
execute "DELETE FROM #{quote_table_name(table_name)}", 'Truncate Tables with Delete'
|
|
133
|
+
else
|
|
134
|
+
raise
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
100
138
|
# Overrides method in abstract class, combining the sqls with semicolon
|
|
101
139
|
# affects disable_referential_integrity in mssql specially when multiple
|
|
102
140
|
# tables are involved.
|
|
@@ -127,7 +165,7 @@ module ActiveRecord
|
|
|
127
165
|
end
|
|
128
166
|
|
|
129
167
|
def identity_column_name(table_name)
|
|
130
|
-
for column in columns(table_name)
|
|
168
|
+
for column in schema_cache.columns(table_name)
|
|
131
169
|
return column.name if column.identity?
|
|
132
170
|
end
|
|
133
171
|
nil
|
data/lib/arjdbc/mssql/errors.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_support/core_ext/string'
|
|
2
4
|
|
|
3
5
|
module ActiveRecord
|
|
@@ -108,7 +110,7 @@ module ActiveRecord
|
|
|
108
110
|
end
|
|
109
111
|
|
|
110
112
|
def build_separator
|
|
111
|
-
'+' << @widths.map {|w| '-' * (w + (cell_padding * 2))}.join('+') << '+'
|
|
113
|
+
'+'.dup << @widths.map {|w| '-' * (w + (cell_padding * 2))}.join('+') << '+'
|
|
112
114
|
end
|
|
113
115
|
|
|
114
116
|
def build_cells(items)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# This file contains extensions, overrides, and monkey patches to core parts
|
|
2
4
|
# of active record to allow SQL Server work properly.
|
|
3
5
|
#
|
|
@@ -15,10 +17,12 @@ module ActiveRecord
|
|
|
15
17
|
# this behaviour, even the current comments for that method says that
|
|
16
18
|
# it rejects primary key but it doesn't (maybe a rails bug?)
|
|
17
19
|
def attributes_for_update(attribute_names)
|
|
18
|
-
attribute_names
|
|
20
|
+
attribute_names &= self.class.column_names
|
|
21
|
+
|
|
22
|
+
attribute_names.delete_if do |name|
|
|
19
23
|
# It seems is only required to check if column in identity or not.
|
|
20
24
|
# This allows to update rails custom primary keys
|
|
21
|
-
next true if readonly_attribute?(name)
|
|
25
|
+
next true if self.class.readonly_attribute?(name)
|
|
22
26
|
|
|
23
27
|
column = self.class.columns_hash[name]
|
|
24
28
|
column && column.identity?
|
data/lib/arjdbc/mssql/quoting.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module MSSQL
|
|
@@ -64,6 +66,42 @@ module ActiveRecord
|
|
|
64
66
|
# @see #quote in old adapter
|
|
65
67
|
BLOB_VALUE_MARKER = "''"
|
|
66
68
|
|
|
69
|
+
def column_name_matcher
|
|
70
|
+
COLUMN_NAME
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def column_name_with_order_matcher
|
|
74
|
+
COLUMN_NAME_WITH_ORDER
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
COLUMN_NAME = /
|
|
78
|
+
\A
|
|
79
|
+
(
|
|
80
|
+
(?:
|
|
81
|
+
# \[table_name\].\[column_name\] | function(one or no argument)
|
|
82
|
+
((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\)
|
|
83
|
+
)
|
|
84
|
+
(?:\s+AS\s+(?:\w+|\[\w+\]))?
|
|
85
|
+
)
|
|
86
|
+
(?:\s*,\s*\g<1>)*
|
|
87
|
+
\z
|
|
88
|
+
/ix
|
|
89
|
+
|
|
90
|
+
COLUMN_NAME_WITH_ORDER = /
|
|
91
|
+
\A
|
|
92
|
+
(
|
|
93
|
+
(?:
|
|
94
|
+
# \[table_name\].\[column_name\] | function(one or no argument)
|
|
95
|
+
((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\)
|
|
96
|
+
)
|
|
97
|
+
(?:\s+ASC|\s+DESC)?
|
|
98
|
+
)
|
|
99
|
+
(?:\s*,\s*\g<1>)*
|
|
100
|
+
\z
|
|
101
|
+
/ix
|
|
102
|
+
|
|
103
|
+
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
|
104
|
+
|
|
67
105
|
private
|
|
68
106
|
|
|
69
107
|
def time_with_db_timezone(value)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module MSSQL
|
|
4
|
-
class SchemaCreation <
|
|
6
|
+
class SchemaCreation < SchemaCreation
|
|
5
7
|
private
|
|
6
8
|
|
|
7
9
|
def visit_TableDefinition(o)
|
|
@@ -11,8 +13,28 @@ module ActiveRecord
|
|
|
11
13
|
projections, source = query.match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures
|
|
12
14
|
select_into = "SELECT #{projections} INTO #{table_name} FROM #{source}"
|
|
13
15
|
else
|
|
14
|
-
o.instance_variable_set :@as, nil
|
|
15
|
-
super
|
|
16
|
+
# o.instance_variable_set :@as, nil
|
|
17
|
+
# super
|
|
18
|
+
create_sql = +''
|
|
19
|
+
|
|
20
|
+
create_sql << "IF NOT EXISTS (SELECT 1 FROM sysobjects WHERE name='#{o.name}' and xtype='U') " if o.if_not_exists
|
|
21
|
+
create_sql << "CREATE#{table_modifier_in_create(o)} TABLE "
|
|
22
|
+
create_sql << "#{quote_table_name(o.name)} "
|
|
23
|
+
|
|
24
|
+
statements = o.columns.map { |c| accept c }
|
|
25
|
+
statements << accept(o.primary_keys) if o.primary_keys
|
|
26
|
+
|
|
27
|
+
if supports_indexes_in_create?
|
|
28
|
+
statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if supports_foreign_keys?
|
|
32
|
+
statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
create_sql << "(#{statements.join(', ')})" if statements.present?
|
|
36
|
+
add_table_options!(create_sql, o)
|
|
37
|
+
create_sql
|
|
16
38
|
end
|
|
17
39
|
end
|
|
18
40
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module MSSQL
|
|
@@ -81,6 +83,14 @@ module ActiveRecord
|
|
|
81
83
|
|
|
82
84
|
super
|
|
83
85
|
end
|
|
86
|
+
|
|
87
|
+
def timestamps(**options)
|
|
88
|
+
if !options.key?(:precision) && @conn.supports_datetime_with_precision?
|
|
89
|
+
options[:precision] = 7
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
super
|
|
93
|
+
end
|
|
84
94
|
end
|
|
85
95
|
|
|
86
96
|
class Table < ActiveRecord::ConnectionAdapters::Table
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module MSSQL
|
|
4
|
-
module SchemaStatements
|
|
6
|
+
module SchemaStatements # :nodoc:
|
|
5
7
|
|
|
6
8
|
NATIVE_DATABASE_TYPES = {
|
|
7
9
|
# Logical Rails types to SQL Server types
|
|
@@ -41,23 +43,36 @@ module ActiveRecord
|
|
|
41
43
|
NATIVE_DATABASE_TYPES
|
|
42
44
|
end
|
|
43
45
|
|
|
44
|
-
# Returns an array of Column objects for the table specified by +table_name+.
|
|
45
|
-
# See the concrete implementation for details on the expected parameter values.
|
|
46
|
-
# NOTE: This is ready, all implemented in the java part of adapter,
|
|
47
|
-
# it uses MSSQLColumn, SqlTypeMetadata, etc.
|
|
48
|
-
def columns(table_name)
|
|
49
|
-
@connection.columns(table_name)
|
|
50
|
-
rescue => e
|
|
51
|
-
# raise translate_exception_class(e, nil)
|
|
52
|
-
# FIXME: this breaks one arjdbc test but fixes activerecord tests
|
|
53
|
-
# (table name alias). Also it behaves similarly to the CRuby adapter
|
|
54
|
-
# which returns an empty array too. (postgres throws a exception)
|
|
55
|
-
[]
|
|
56
|
-
end
|
|
57
|
-
|
|
58
46
|
# Returns an array of indexes for the given table.
|
|
59
|
-
def indexes(table_name
|
|
60
|
-
|
|
47
|
+
def indexes(table_name)
|
|
48
|
+
data = select("EXEC sp_helpindex #{quote(table_name)}", "SCHEMA") rescue []
|
|
49
|
+
|
|
50
|
+
data.reduce([]) do |indexes, index|
|
|
51
|
+
index = index.with_indifferent_access
|
|
52
|
+
|
|
53
|
+
if index[:index_description] =~ /primary key/
|
|
54
|
+
indexes
|
|
55
|
+
else
|
|
56
|
+
name = index[:index_name]
|
|
57
|
+
unique = index[:index_description].to_s.match?(/unique/)
|
|
58
|
+
where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}")
|
|
59
|
+
orders = {}
|
|
60
|
+
columns = []
|
|
61
|
+
|
|
62
|
+
index[:index_keys].split(',').each do |column|
|
|
63
|
+
column.strip!
|
|
64
|
+
|
|
65
|
+
if column.ends_with?('(-)')
|
|
66
|
+
column.gsub! '(-)', ''
|
|
67
|
+
orders[column] = :desc
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
columns << column
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
indexes << IndexDefinition.new(table_name, name, unique, columns, where: where, orders: orders)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
61
76
|
end
|
|
62
77
|
|
|
63
78
|
def primary_keys(table_name)
|
|
@@ -94,7 +109,17 @@ module ActiveRecord
|
|
|
94
109
|
end
|
|
95
110
|
|
|
96
111
|
def create_database(name, options = {})
|
|
97
|
-
|
|
112
|
+
edition_options = create_db_edition_options(options)
|
|
113
|
+
|
|
114
|
+
if options[:collation] && edition_options.present?
|
|
115
|
+
execute "CREATE DATABASE #{quote_database_name(name)} COLLATE #{options[:collation]} (#{edition_options.join(', ')})"
|
|
116
|
+
elsif options[:collation]
|
|
117
|
+
execute "CREATE DATABASE #{quote_database_name(name)} COLLATE #{options[:collation]}"
|
|
118
|
+
elsif edition_options.present?
|
|
119
|
+
execute "CREATE DATABASE #{quote_database_name(name)} (#{edition_options.join(', ')})"
|
|
120
|
+
else
|
|
121
|
+
execute "CREATE DATABASE #{quote_database_name(name)}"
|
|
122
|
+
end
|
|
98
123
|
end
|
|
99
124
|
|
|
100
125
|
def recreate_database(name, options = {})
|
|
@@ -104,6 +129,9 @@ module ActiveRecord
|
|
|
104
129
|
|
|
105
130
|
def remove_column(table_name, column_name, type = nil, options = {})
|
|
106
131
|
raise ArgumentError.new('You must specify at least one column name. Example: remove_column(:people, :first_name)') if column_name.is_a? Array
|
|
132
|
+
|
|
133
|
+
return if options[:if_exists] == true && !column_exists?(table_name, column_name)
|
|
134
|
+
|
|
107
135
|
remove_check_constraints(table_name, column_name)
|
|
108
136
|
remove_default_constraint(table_name, column_name)
|
|
109
137
|
remove_indexes(table_name, column_name)
|
|
@@ -124,7 +152,7 @@ module ActiveRecord
|
|
|
124
152
|
end
|
|
125
153
|
end
|
|
126
154
|
|
|
127
|
-
if options[:if_exists] &&
|
|
155
|
+
if options[:if_exists] && mssql_major_version < 13
|
|
128
156
|
# this is for sql server 2012 and 2014
|
|
129
157
|
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}"
|
|
130
158
|
else
|
|
@@ -193,7 +221,7 @@ module ActiveRecord
|
|
|
193
221
|
column_type_sql << "(#{precision})"
|
|
194
222
|
else
|
|
195
223
|
raise(
|
|
196
|
-
|
|
224
|
+
ArgumentError,
|
|
197
225
|
"No #{native[:name]} type has precision of #{precision}. The " \
|
|
198
226
|
'allowed range of precision is from 0 to 7, even though the ' \
|
|
199
227
|
'sql type precision is 7 this adapter will persist up to 6 ' \
|
|
@@ -220,6 +248,14 @@ module ActiveRecord
|
|
|
220
248
|
(order_columns << super).join(', ')
|
|
221
249
|
end
|
|
222
250
|
|
|
251
|
+
def add_timestamps(table_name, options = {})
|
|
252
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
|
253
|
+
options[:precision] = 7
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
super
|
|
257
|
+
end
|
|
258
|
+
|
|
223
259
|
def create_schema_dumper(options)
|
|
224
260
|
MSSQL::SchemaDumper.create(self, options)
|
|
225
261
|
end
|
|
@@ -299,13 +335,45 @@ module ActiveRecord
|
|
|
299
335
|
execute(sql_alter.join(' '))
|
|
300
336
|
end
|
|
301
337
|
|
|
338
|
+
def update_table_definition(table_name, base) #:nodoc:
|
|
339
|
+
MSSQL::Table.new(table_name, base)
|
|
340
|
+
end
|
|
341
|
+
|
|
302
342
|
private
|
|
303
343
|
|
|
344
|
+
def schema_creation
|
|
345
|
+
MSSQL::SchemaCreation.new(self)
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def create_table_definition(*args)
|
|
349
|
+
MSSQL::TableDefinition.new(self, *args)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def new_column_from_field(table_name, field)
|
|
353
|
+
field
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def create_db_edition_options(options = {})
|
|
357
|
+
edition_config = options.select { |k, _v| k.match?('azure') }
|
|
358
|
+
|
|
359
|
+
edition_config.each_with_object([]) do |(key, value), output|
|
|
360
|
+
output << case key
|
|
361
|
+
when :azure_maxsize
|
|
362
|
+
"MAXSIZE = #{value}"
|
|
363
|
+
when :azure_edition
|
|
364
|
+
"EDITION = #{value}"
|
|
365
|
+
when :service_objective
|
|
366
|
+
"SERVICE_OBJECTIVE = #{value}"
|
|
367
|
+
end
|
|
368
|
+
end.compact
|
|
369
|
+
end
|
|
370
|
+
|
|
304
371
|
def data_source_sql(name = nil, type: nil)
|
|
305
372
|
scope = quoted_scope(name, type: type)
|
|
306
373
|
table_name = 'TABLE_NAME'
|
|
307
374
|
|
|
308
|
-
sql =
|
|
375
|
+
sql = ''.dup
|
|
376
|
+
sql << "SELECT #{table_name}"
|
|
309
377
|
sql << ' FROM INFORMATION_SCHEMA.TABLES'
|
|
310
378
|
sql << ' WHERE TABLE_CATALOG = DB_NAME()'
|
|
311
379
|
sql << " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
|
|
@@ -327,7 +395,9 @@ module ActiveRecord
|
|
|
327
395
|
end
|
|
328
396
|
|
|
329
397
|
def change_column_type(table_name, column_name, type, options = {})
|
|
330
|
-
sql =
|
|
398
|
+
sql = ''.dup
|
|
399
|
+
|
|
400
|
+
sql << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}"
|
|
331
401
|
sql << (options[:null] ? " NULL" : " NOT NULL") if options.has_key?(:null)
|
|
332
402
|
result = execute(sql)
|
|
333
403
|
result
|