activerecord-sqlserver-adapter 4.1.8 → 4.2.0.pre
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 +15 -0
- data/CHANGELOG.md +60 -0
- data/Gemfile +45 -0
- data/Guardfile +29 -0
- data/MIT-LICENSE +5 -5
- data/README.md +193 -0
- data/RUNNING_UNIT_TESTS.md +95 -0
- data/Rakefile +48 -0
- data/activerecord-sqlserver-adapter.gemspec +28 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +5 -15
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +6 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +9 -3
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +130 -151
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +0 -25
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +39 -78
- data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +71 -47
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +14 -30
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +112 -108
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +52 -7
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +52 -0
- data/lib/active_record/connection_adapters/sqlserver/type.rb +46 -0
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/castable.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb +39 -0
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +14 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +37 -0
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/quoter.rb +32 -0
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +24 -0
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +59 -0
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +23 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +118 -12
- data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +133 -198
- data/lib/active_record/connection_adapters/sqlserver_column.rb +15 -86
- data/lib/active_record/sqlserver_base.rb +2 -0
- data/lib/arel/visitors/sqlserver.rb +120 -393
- data/lib/{arel/arel_sqlserver.rb → arel_sqlserver.rb} +1 -3
- data/test/cases/adapter_test_sqlserver.rb +420 -0
- data/test/cases/coerced_tests.rb +642 -0
- data/test/cases/column_test_sqlserver.rb +703 -0
- data/test/cases/connection_test_sqlserver.rb +216 -0
- data/test/cases/database_statements_test_sqlserver.rb +57 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +38 -0
- data/test/cases/helper_sqlserver.rb +36 -0
- data/test/cases/migration_test_sqlserver.rb +66 -0
- data/test/cases/order_test_sqlserver.rb +147 -0
- data/test/cases/pessimistic_locking_test_sqlserver.rb +90 -0
- data/test/cases/schema_dumper_test_sqlserver.rb +175 -0
- data/test/cases/schema_test_sqlserver.rb +54 -0
- data/test/cases/scratchpad_test_sqlserver.rb +9 -0
- data/test/cases/showplan_test_sqlserver.rb +65 -0
- data/test/cases/specific_schema_test_sqlserver.rb +118 -0
- data/test/cases/transaction_test_sqlserver.rb +61 -0
- data/test/cases/utils_test_sqlserver.rb +91 -0
- data/test/cases/uuid_test_sqlserver.rb +41 -0
- data/test/config.yml +35 -0
- data/test/fixtures/1px.gif +0 -0
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
- data/test/models/sqlserver/customers_view.rb +3 -0
- data/test/models/sqlserver/datatype.rb +3 -0
- data/test/models/sqlserver/datatype_migration.rb +3 -0
- data/test/models/sqlserver/dollar_table_name.rb +3 -0
- data/test/models/sqlserver/edge_schema.rb +13 -0
- data/test/models/sqlserver/fk_has_fk.rb +3 -0
- data/test/models/sqlserver/fk_has_pk.rb +3 -0
- data/test/models/sqlserver/natural_pk_data.rb +4 -0
- data/test/models/sqlserver/natural_pk_int_data.rb +3 -0
- data/test/models/sqlserver/no_pk_data.rb +3 -0
- data/test/models/sqlserver/quoted_table.rb +7 -0
- data/test/models/sqlserver/quoted_view_1.rb +3 -0
- data/test/models/sqlserver/quoted_view_2.rb +3 -0
- data/test/models/sqlserver/string_default.rb +3 -0
- data/test/models/sqlserver/string_defaults_big_view.rb +3 -0
- data/test/models/sqlserver/string_defaults_view.rb +3 -0
- data/test/models/sqlserver/tinyint_pk.rb +3 -0
- data/test/models/sqlserver/upper.rb +3 -0
- data/test/models/sqlserver/uppered.rb +3 -0
- data/test/models/sqlserver/uuid.rb +3 -0
- data/test/schema/datatypes/2012.sql +64 -0
- data/test/schema/sqlserver_specific_schema.rb +181 -0
- data/test/support/coerceable_test_sqlserver.rb +45 -0
- data/test/support/load_schema_sqlserver.rb +29 -0
- data/test/support/minitest_sqlserver.rb +1 -0
- data/test/support/paths_sqlserver.rb +48 -0
- data/test/support/rake_helpers.rb +41 -0
- data/test/support/sql_counter_sqlserver.rb +32 -0
- metadata +271 -21
- data/CHANGELOG +0 -39
- data/VERSION +0 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +0 -17
- data/lib/active_record/sqlserver_test_case.rb +0 -17
- data/lib/arel/nodes_sqlserver.rb +0 -14
- data/lib/arel/select_manager_sqlserver.rb +0 -62
|
@@ -1,32 +1,7 @@
|
|
|
1
1
|
module ActiveRecord
|
|
2
|
-
class LostConnection < WrappedDatabaseException
|
|
3
|
-
end
|
|
4
2
|
|
|
5
3
|
class DeadlockVictim < WrappedDatabaseException
|
|
6
4
|
end
|
|
7
5
|
|
|
8
|
-
module ConnectionAdapters
|
|
9
|
-
module Sqlserver
|
|
10
|
-
module Errors
|
|
11
|
-
LOST_CONNECTION_EXCEPTIONS = {
|
|
12
|
-
dblib: ['TinyTds::Error'],
|
|
13
|
-
odbc: ['ODBC::Error']
|
|
14
|
-
}.freeze
|
|
15
|
-
|
|
16
|
-
LOST_CONNECTION_MESSAGES = {
|
|
17
|
-
dblib: [/closed connection/, /dead or not enabled/, /server failed/i],
|
|
18
|
-
odbc: [/link failure/, /server failed/, /connection was already closed/, /invalid handle/i]
|
|
19
|
-
}.freeze
|
|
20
6
|
|
|
21
|
-
def lost_connection_exceptions
|
|
22
|
-
exceptions = LOST_CONNECTION_EXCEPTIONS[@connection_options[:mode]]
|
|
23
|
-
@lost_connection_exceptions ||= exceptions ? exceptions.map { |e| e.constantize rescue nil }.compact : []
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def lost_connection_messages
|
|
27
|
-
LOST_CONNECTION_MESSAGES[@connection_options[:mode]]
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
7
|
end
|
|
@@ -1,73 +1,25 @@
|
|
|
1
1
|
module ActiveRecord
|
|
2
2
|
module ConnectionAdapters
|
|
3
|
-
module
|
|
3
|
+
module SQLServer
|
|
4
4
|
module Quoting
|
|
5
|
-
QUOTED_TRUE, QUOTED_FALSE = '1', '0'
|
|
6
|
-
QUOTED_STRING_PREFIX = 'N'
|
|
7
|
-
|
|
8
|
-
def quote(value, column = nil)
|
|
9
|
-
case value
|
|
10
|
-
when String, ActiveSupport::Multibyte::Chars
|
|
11
|
-
if column && column.type == :integer && value.blank?
|
|
12
|
-
value.to_i.to_s
|
|
13
|
-
elsif column && column.type == :binary
|
|
14
|
-
column.class.string_to_binary(value)
|
|
15
|
-
elsif column && [:uuid, :uniqueidentifier].include?(column.type)
|
|
16
|
-
"'#{quote_string(value)}'"
|
|
17
|
-
elsif value.is_utf8? || (column && column.type == :string)
|
|
18
|
-
"#{quoted_string_prefix}'#{quote_string(value)}'"
|
|
19
|
-
else
|
|
20
|
-
super
|
|
21
|
-
end
|
|
22
|
-
when Date, Time
|
|
23
|
-
if column && column.sql_type == 'datetime'
|
|
24
|
-
"'#{quoted_datetime(value)}'"
|
|
25
|
-
elsif column && (column.sql_type == 'datetimeoffset' || column.sql_type == 'time')
|
|
26
|
-
"'#{quoted_full_iso8601(value)}'"
|
|
27
|
-
else
|
|
28
|
-
super
|
|
29
|
-
end
|
|
30
|
-
when nil
|
|
31
|
-
column.respond_to?(:sql_type) && column.sql_type == 'timestamp' ? 'DEFAULT' : super
|
|
32
|
-
else
|
|
33
|
-
super
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
5
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
6
|
+
QUOTED_TRUE = '1'
|
|
7
|
+
QUOTED_FALSE = '0'
|
|
8
|
+
QUOTED_STRING_PREFIX = 'N'
|
|
40
9
|
|
|
41
|
-
def quote_string(
|
|
42
|
-
|
|
10
|
+
def quote_string(s)
|
|
11
|
+
SQLServer::Utils.quote_string(s)
|
|
43
12
|
end
|
|
44
13
|
|
|
45
14
|
def quote_column_name(name)
|
|
46
|
-
|
|
15
|
+
SQLServer::Utils.extract_identifiers(name).quoted
|
|
47
16
|
end
|
|
48
17
|
|
|
49
|
-
def quote_table_name(name)
|
|
50
|
-
quote_column_name(name)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def quote_database_name(name)
|
|
54
|
-
schema_cache.quote_name(name, false)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Does not quote function default values for UUID columns
|
|
58
18
|
def quote_default_value(value, column)
|
|
59
19
|
if column.type == :uuid && value =~ /\(\)/
|
|
60
20
|
value
|
|
61
21
|
else
|
|
62
|
-
quote(value)
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def substitute_at(column, index)
|
|
67
|
-
if column.respond_to?(:sql_type) && column.sql_type == 'timestamp'
|
|
68
|
-
nil
|
|
69
|
-
else
|
|
70
|
-
Arel::Nodes::BindParam.new "@#{index}"
|
|
22
|
+
quote(value, column)
|
|
71
23
|
end
|
|
72
24
|
end
|
|
73
25
|
|
|
@@ -75,47 +27,56 @@ module ActiveRecord
|
|
|
75
27
|
QUOTED_TRUE
|
|
76
28
|
end
|
|
77
29
|
|
|
30
|
+
def unquoted_true
|
|
31
|
+
1
|
|
32
|
+
end
|
|
33
|
+
|
|
78
34
|
def quoted_false
|
|
79
35
|
QUOTED_FALSE
|
|
80
36
|
end
|
|
81
37
|
|
|
82
|
-
def
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
38
|
+
def unquoted_false
|
|
39
|
+
0
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def quoted_date(value)
|
|
43
|
+
SQLServer::Utils.with_sqlserver_db_date_formats do
|
|
44
|
+
if value.acts_like?(:time) && value.respond_to?(:usec)
|
|
45
|
+
precision = (BigDecimal(value.usec.to_s) / 1_000_000).round(3).to_s.split('.').last
|
|
46
|
+
"#{super}.#{precision}"
|
|
47
|
+
elsif value.acts_like?(:date)
|
|
48
|
+
value.to_s(:_sqlserver_dateformat)
|
|
87
49
|
else
|
|
88
|
-
|
|
50
|
+
super
|
|
89
51
|
end
|
|
90
|
-
else
|
|
91
|
-
quoted_date(value)
|
|
92
52
|
end
|
|
93
53
|
end
|
|
94
54
|
|
|
95
|
-
def quoted_full_iso8601(value)
|
|
96
|
-
if value.acts_like?(:time)
|
|
97
|
-
value.is_a?(Date) ? quoted_value_acts_like_time_filter(value).to_time.xmlschema.to(18) : quoted_value_acts_like_time_filter(value).iso8601(7).to(22)
|
|
98
|
-
else
|
|
99
|
-
quoted_date(value)
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
55
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def _quote(value)
|
|
59
|
+
case value
|
|
60
|
+
when Type::Binary::Data
|
|
61
|
+
"0x#{value.hex}"
|
|
62
|
+
when SQLServer::Type::Quoter
|
|
63
|
+
value.quote_ss_value
|
|
64
|
+
when String, ActiveSupport::Multibyte::Chars
|
|
65
|
+
if value.is_utf8?
|
|
66
|
+
"#{QUOTED_STRING_PREFIX}#{super}"
|
|
67
|
+
else
|
|
68
|
+
super
|
|
69
|
+
end
|
|
108
70
|
else
|
|
109
71
|
super
|
|
110
72
|
end
|
|
111
73
|
end
|
|
112
74
|
|
|
113
|
-
protected
|
|
114
|
-
|
|
115
75
|
def quoted_value_acts_like_time_filter(value)
|
|
116
76
|
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
|
|
117
77
|
value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
|
|
118
78
|
end
|
|
79
|
+
|
|
119
80
|
end
|
|
120
81
|
end
|
|
121
82
|
end
|
|
@@ -1,89 +1,113 @@
|
|
|
1
1
|
module ActiveRecord
|
|
2
2
|
module ConnectionAdapters
|
|
3
|
-
module
|
|
3
|
+
module SQLServer
|
|
4
4
|
class SchemaCache < ActiveRecord::ConnectionAdapters::SchemaCache
|
|
5
|
-
attr_reader :view_information
|
|
6
5
|
|
|
7
6
|
def initialize(conn)
|
|
8
7
|
super
|
|
9
|
-
@
|
|
10
|
-
@view_names = nil
|
|
8
|
+
@views = {}
|
|
11
9
|
@view_information = {}
|
|
12
|
-
@quoted_names = {}
|
|
13
10
|
end
|
|
14
11
|
|
|
15
12
|
# Superclass Overrides
|
|
16
13
|
|
|
14
|
+
def primary_keys(table_name)
|
|
15
|
+
name = key(table_name)
|
|
16
|
+
@primary_keys[name] ||= table_exists?(table_name) ? connection.primary_key(table_name) : nil
|
|
17
|
+
end
|
|
18
|
+
|
|
17
19
|
def table_exists?(table_name)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return @tables[
|
|
21
|
-
@tables[
|
|
20
|
+
name = key(table_name)
|
|
21
|
+
prepare_tables_and_views
|
|
22
|
+
return @tables[name] if @tables.key? name
|
|
23
|
+
table_exists = @tables[name] = connection.table_exists?(table_name)
|
|
24
|
+
table_exists || view_exists?(table_name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def tables(name)
|
|
28
|
+
super(key(name))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def columns(table_name)
|
|
32
|
+
name = key(table_name)
|
|
33
|
+
@columns[name] ||= connection.columns(table_name)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def columns_hash(table_name)
|
|
37
|
+
name = key(table_name)
|
|
38
|
+
@columns_hash[name] ||= Hash[columns(table_name).map { |col|
|
|
39
|
+
[col.name, col]
|
|
40
|
+
}]
|
|
22
41
|
end
|
|
23
42
|
|
|
24
43
|
def clear!
|
|
25
44
|
super
|
|
26
|
-
@
|
|
27
|
-
@view_names = nil
|
|
45
|
+
@views.clear
|
|
28
46
|
@view_information.clear
|
|
29
|
-
@quoted_names.clear
|
|
30
47
|
end
|
|
31
48
|
|
|
32
|
-
def
|
|
33
|
-
|
|
34
|
-
super(key)
|
|
35
|
-
super(table_name)
|
|
36
|
-
# SQL Server Specific
|
|
37
|
-
if @table_names
|
|
38
|
-
@table_names.delete key
|
|
39
|
-
@table_names.delete table_name
|
|
40
|
-
end
|
|
41
|
-
if @view_names
|
|
42
|
-
@view_names.delete key
|
|
43
|
-
@view_names.delete table_name
|
|
44
|
-
end
|
|
45
|
-
@view_information.delete key
|
|
49
|
+
def size
|
|
50
|
+
super + [@views, @view_information].map{ |x| x.size }.inject(:+)
|
|
46
51
|
end
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
def clear_table_cache!(table_name)
|
|
54
|
+
name = key(table_name)
|
|
55
|
+
@columns.delete name
|
|
56
|
+
@columns_hash.delete name
|
|
57
|
+
@primary_keys.delete name
|
|
58
|
+
@tables.delete name
|
|
59
|
+
@views.delete name
|
|
60
|
+
@view_information.delete name
|
|
61
|
+
end
|
|
49
62
|
|
|
50
|
-
def
|
|
51
|
-
@
|
|
63
|
+
def marshal_dump
|
|
64
|
+
super + [@views, @view_information]
|
|
52
65
|
end
|
|
53
66
|
|
|
54
|
-
def
|
|
55
|
-
@
|
|
67
|
+
def marshal_load(array)
|
|
68
|
+
@views, @view_information = array[-2..-1]
|
|
69
|
+
super(array[0..-3])
|
|
56
70
|
end
|
|
57
71
|
|
|
72
|
+
# SQL Server Specific
|
|
73
|
+
|
|
58
74
|
def view_exists?(table_name)
|
|
59
|
-
|
|
75
|
+
name = key(table_name)
|
|
76
|
+
prepare_tables_and_views
|
|
77
|
+
return @views[name] if @views.key? name
|
|
78
|
+
@views[name] = connection.views.include?(table_name)
|
|
60
79
|
end
|
|
61
80
|
|
|
62
81
|
def view_information(table_name)
|
|
63
|
-
|
|
64
|
-
return @view_information[
|
|
65
|
-
@view_information[
|
|
82
|
+
name = key(table_name)
|
|
83
|
+
return @view_information[name] if @view_information.key? name
|
|
84
|
+
@view_information[name] = connection.send(:view_information, table_name)
|
|
66
85
|
end
|
|
67
86
|
|
|
68
|
-
def quote_name(name, split_on_dots = true)
|
|
69
|
-
return @quoted_names[name] if @quoted_names.key? name
|
|
70
87
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def identifier(table_name)
|
|
91
|
+
SQLServer::Utils.extract_identifiers(table_name)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def key(table_name)
|
|
95
|
+
identifier(table_name).quoted
|
|
76
96
|
end
|
|
77
97
|
|
|
78
|
-
|
|
98
|
+
def prepare_tables_and_views
|
|
99
|
+
prepare_views if @views.empty?
|
|
100
|
+
prepare_tables if @tables.empty?
|
|
101
|
+
end
|
|
79
102
|
|
|
80
|
-
def
|
|
81
|
-
|
|
103
|
+
def prepare_tables
|
|
104
|
+
connection.tables.each { |table| @tables[key(table)] = true }
|
|
82
105
|
end
|
|
83
106
|
|
|
84
|
-
def
|
|
85
|
-
|
|
107
|
+
def prepare_views
|
|
108
|
+
connection.views.each { |view| @views[key(view)] = true }
|
|
86
109
|
end
|
|
110
|
+
|
|
87
111
|
end
|
|
88
112
|
end
|
|
89
113
|
end
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
module ActiveRecord
|
|
2
2
|
module ConnectionAdapters
|
|
3
|
-
module
|
|
3
|
+
module SQLServer
|
|
4
4
|
class SchemaCreation < AbstractAdapter::SchemaCreation
|
|
5
|
+
|
|
5
6
|
private
|
|
6
7
|
|
|
7
8
|
def visit_ColumnDefinition(o)
|
|
@@ -13,43 +14,26 @@ module ActiveRecord
|
|
|
13
14
|
sql
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
def
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
def visit_TableDefinition(o)
|
|
18
|
+
if o.as
|
|
19
|
+
table_name = quote_table_name(o.temporary ? "##{o.name}" : o.name)
|
|
20
|
+
projections, source = @conn.to_sql(o.as).match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures
|
|
21
|
+
select_into = "SELECT #{projections} INTO #{table_name} FROM #{source}"
|
|
21
22
|
else
|
|
23
|
+
o.instance_variable_set :@as, nil
|
|
22
24
|
super
|
|
23
25
|
end
|
|
24
26
|
end
|
|
25
27
|
|
|
26
|
-
def
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if o.as.is_a?(ActiveRecord::Relation)
|
|
31
|
-
select = o.as.to_sql
|
|
32
|
-
elsif o.as.is_a?(String)
|
|
33
|
-
select = o.as
|
|
34
|
-
else
|
|
35
|
-
raise 'Only able to generate a table from a SELECT statement passed as a String or ActiveRecord::Relation'
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
create_sql = 'SELECT * INTO '
|
|
39
|
-
create_sql << quoted_name
|
|
40
|
-
create_sql << 'FROM ('
|
|
41
|
-
create_sql << select
|
|
42
|
-
create_sql << ') AS __sq'
|
|
43
|
-
|
|
28
|
+
def add_column_options!(sql, options)
|
|
29
|
+
column = options.fetch(:column) { return super }
|
|
30
|
+
if (column.type == :uuid || column.type == :uniqueidentifier) && options[:default] =~ /\(\)/
|
|
31
|
+
sql << " DEFAULT #{options.delete(:default)}"
|
|
44
32
|
else
|
|
45
|
-
|
|
46
|
-
create_sql << quoted_name
|
|
47
|
-
create_sql << "(#{o.columns.map { |c| accept c }.join(', ')}) "
|
|
48
|
-
create_sql << "#{o.options}"
|
|
33
|
+
super
|
|
49
34
|
end
|
|
50
|
-
|
|
51
|
-
create_sql
|
|
52
35
|
end
|
|
36
|
+
|
|
53
37
|
end
|
|
54
38
|
end
|
|
55
39
|
end
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
module ActiveRecord
|
|
2
2
|
module ConnectionAdapters
|
|
3
|
-
module
|
|
3
|
+
module SQLServer
|
|
4
4
|
module SchemaStatements
|
|
5
|
+
|
|
5
6
|
def native_database_types
|
|
6
7
|
@native_database_types ||= initialize_native_database_types.freeze
|
|
7
8
|
end
|
|
@@ -12,7 +13,7 @@ module ActiveRecord
|
|
|
12
13
|
|
|
13
14
|
def table_exists?(table_name)
|
|
14
15
|
return false if table_name.blank?
|
|
15
|
-
unquoted_table_name = Utils.
|
|
16
|
+
unquoted_table_name = SQLServer::Utils.extract_identifiers(table_name).object
|
|
16
17
|
super || tables.include?(unquoted_table_name) || views.include?(unquoted_table_name)
|
|
17
18
|
end
|
|
18
19
|
|
|
@@ -31,13 +32,12 @@ module ActiveRecord
|
|
|
31
32
|
else
|
|
32
33
|
name = index[:index_name]
|
|
33
34
|
unique = index[:index_description] =~ /unique/
|
|
34
|
-
where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}")
|
|
35
35
|
columns = index[:index_keys].split(',').map do |column|
|
|
36
36
|
column.strip!
|
|
37
37
|
column.gsub! '(-)', '' if column.ends_with?('(-)')
|
|
38
38
|
column
|
|
39
39
|
end
|
|
40
|
-
indexes << IndexDefinition.new(table_name, name, unique, columns
|
|
40
|
+
indexes << IndexDefinition.new(table_name, name, unique, columns)
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
end
|
|
@@ -45,18 +45,14 @@ module ActiveRecord
|
|
|
45
45
|
def columns(table_name, _name = nil)
|
|
46
46
|
return [] if table_name.blank?
|
|
47
47
|
column_definitions(table_name).map do |ci|
|
|
48
|
-
sqlserver_options = ci.
|
|
49
|
-
|
|
48
|
+
sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :default_function, :table_name
|
|
49
|
+
cast_type = lookup_cast_type(ci[:type])
|
|
50
|
+
new_column ci[:name], ci[:default_value], cast_type, ci[:type], ci[:null], sqlserver_options
|
|
50
51
|
end
|
|
51
52
|
end
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
# DISTINCT ON () like Posgres, or FIRST_VALUE() like Oracle (at least before SQL Server 2012). Because
|
|
56
|
-
# of these facts, we don't actually add any extra columns for distinct, but instead have to create
|
|
57
|
-
# a subquery with ROW_NUMBER() and DENSE_RANK() in our monkey-patches to Arel.
|
|
58
|
-
def columns_for_distinct(columns, _orders) #:nodoc:
|
|
59
|
-
columns
|
|
54
|
+
def new_column(name, default, cast_type, sql_type = nil, null = true, sqlserver_options={})
|
|
55
|
+
SQLServerColumn.new name, default, cast_type, sql_type, null, sqlserver_options
|
|
60
56
|
end
|
|
61
57
|
|
|
62
58
|
def rename_table(table_name, new_name)
|
|
@@ -64,7 +60,7 @@ module ActiveRecord
|
|
|
64
60
|
rename_table_indexes(table_name, new_name)
|
|
65
61
|
end
|
|
66
62
|
|
|
67
|
-
def remove_column(table_name, column_name,
|
|
63
|
+
def remove_column(table_name, column_name, _type = nil)
|
|
68
64
|
raise ArgumentError.new('You must specify at least one column name. Example: remove_column(:people, :first_name)') if column_name.is_a? Array
|
|
69
65
|
remove_check_constraints(table_name, column_name)
|
|
70
66
|
remove_default_constraint(table_name, column_name)
|
|
@@ -76,7 +72,6 @@ module ActiveRecord
|
|
|
76
72
|
sql_commands = []
|
|
77
73
|
indexes = []
|
|
78
74
|
column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s }
|
|
79
|
-
|
|
80
75
|
if options_include_default?(options) || (column_object && column_object.type != type.to_sym)
|
|
81
76
|
remove_default_constraint(table_name, column_name)
|
|
82
77
|
indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) }
|
|
@@ -88,7 +83,6 @@ module ActiveRecord
|
|
|
88
83
|
if options_include_default?(options)
|
|
89
84
|
sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_value(options[:default], column_object)} FOR #{quote_column_name(column_name)}"
|
|
90
85
|
end
|
|
91
|
-
|
|
92
86
|
# Add any removed indexes back
|
|
93
87
|
indexes.each do |index|
|
|
94
88
|
sql_commands << "CREATE INDEX #{quote_table_name(index.name)} ON #{quote_table_name(table_name)} (#{index.columns.map { |c| quote_column_name(c) }.join(', ')})"
|
|
@@ -106,13 +100,16 @@ module ActiveRecord
|
|
|
106
100
|
def rename_column(table_name, column_name, new_column_name)
|
|
107
101
|
schema_cache.clear_table_cache!(table_name)
|
|
108
102
|
detect_column_for! table_name, column_name
|
|
109
|
-
|
|
103
|
+
identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{column_name}")
|
|
104
|
+
execute_procedure :sp_rename, identifier.quoted, new_column_name, 'COLUMN'
|
|
110
105
|
rename_column_indexes(table_name, column_name, new_column_name)
|
|
111
106
|
schema_cache.clear_table_cache!(table_name)
|
|
112
107
|
end
|
|
113
108
|
|
|
114
109
|
def rename_index(table_name, old_name, new_name)
|
|
115
|
-
|
|
110
|
+
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters" if new_name.length > allowed_index_name_length
|
|
111
|
+
identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{old_name}")
|
|
112
|
+
execute_procedure :sp_rename, identifier.quoted, new_name, 'INDEX'
|
|
116
113
|
end
|
|
117
114
|
|
|
118
115
|
def remove_index!(table_name, index_name)
|
|
@@ -135,6 +132,15 @@ module ActiveRecord
|
|
|
135
132
|
end
|
|
136
133
|
end
|
|
137
134
|
|
|
135
|
+
def columns_for_distinct(columns, orders)
|
|
136
|
+
order_columns = orders.reject(&:blank?).map{ |s|
|
|
137
|
+
s = s.to_sql unless s.is_a?(String)
|
|
138
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, '')
|
|
139
|
+
.gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
|
|
140
|
+
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
|
141
|
+
[super, *order_columns].join(', ')
|
|
142
|
+
end
|
|
143
|
+
|
|
138
144
|
def change_column_null(table_name, column_name, allow_null, default = nil)
|
|
139
145
|
column = detect_column_for! table_name, column_name
|
|
140
146
|
if !allow_null.nil? && allow_null == false && !default.nil?
|
|
@@ -151,6 +157,7 @@ module ActiveRecord
|
|
|
151
157
|
tables('VIEW')
|
|
152
158
|
end
|
|
153
159
|
|
|
160
|
+
|
|
154
161
|
protected
|
|
155
162
|
|
|
156
163
|
# === SQLServer Specific ======================================== #
|
|
@@ -158,34 +165,39 @@ module ActiveRecord
|
|
|
158
165
|
def initialize_native_database_types
|
|
159
166
|
{
|
|
160
167
|
primary_key: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY',
|
|
161
|
-
string: { name: native_string_database_type, limit: 255 },
|
|
162
|
-
text: { name: native_text_database_type },
|
|
163
168
|
integer: { name: 'int', limit: 4 },
|
|
164
|
-
|
|
169
|
+
bigint: { name: 'bigint' },
|
|
170
|
+
boolean: { name: 'bit' },
|
|
165
171
|
decimal: { name: 'decimal' },
|
|
172
|
+
money: { name: 'money' },
|
|
173
|
+
smallmoney: { name: 'smallmoney' },
|
|
174
|
+
float: { name: 'float' },
|
|
175
|
+
real: { name: 'real' },
|
|
176
|
+
date: { name: 'date' },
|
|
166
177
|
datetime: { name: 'datetime' },
|
|
167
178
|
timestamp: { name: 'datetime' },
|
|
168
|
-
time: { name:
|
|
169
|
-
date: { name: native_date_database_type },
|
|
170
|
-
binary: { name: native_binary_database_type },
|
|
171
|
-
boolean: { name: 'bit' },
|
|
172
|
-
uuid: { name: 'uniqueidentifier' },
|
|
173
|
-
# These are custom types that may move somewhere else for good schema_dumper.rb hacking to output them.
|
|
179
|
+
time: { name: 'time' },
|
|
174
180
|
char: { name: 'char' },
|
|
181
|
+
varchar: { name: 'varchar', limit: 8000 },
|
|
175
182
|
varchar_max: { name: 'varchar(max)' },
|
|
183
|
+
text_basic: { name: 'text' },
|
|
176
184
|
nchar: { name: 'nchar' },
|
|
177
|
-
|
|
178
|
-
|
|
185
|
+
string: { name: 'nvarchar', limit: 4000 },
|
|
186
|
+
text: { name: 'nvarchar(max)' },
|
|
179
187
|
ntext: { name: 'ntext' },
|
|
188
|
+
binary_basic: { name: 'binary' },
|
|
189
|
+
varbinary: { name: 'varbinary', limit: 8000 },
|
|
190
|
+
binary: { name: 'varbinary(max)' },
|
|
191
|
+
uuid: { name: 'uniqueidentifier' },
|
|
180
192
|
ss_timestamp: { name: 'timestamp' }
|
|
181
193
|
}
|
|
182
194
|
end
|
|
183
195
|
|
|
184
196
|
def column_definitions(table_name)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
identifier = SQLServer::Utils.extract_identifiers(table_name)
|
|
198
|
+
database = "#{identifier.database_quoted}." if identifier.database_quoted
|
|
199
|
+
view_exists = schema_cache.view_exists?(table_name)
|
|
200
|
+
view_tblnm = table_name_or_views_table_name(table_name) if view_exists
|
|
189
201
|
sql = %{
|
|
190
202
|
SELECT DISTINCT
|
|
191
203
|
#{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name,
|
|
@@ -194,10 +206,11 @@ module ActiveRecord
|
|
|
194
206
|
columns.COLUMN_DEFAULT AS default_value,
|
|
195
207
|
columns.NUMERIC_SCALE AS numeric_scale,
|
|
196
208
|
columns.NUMERIC_PRECISION AS numeric_precision,
|
|
209
|
+
columns.DATETIME_PRECISION AS datetime_precision,
|
|
197
210
|
columns.ordinal_position,
|
|
198
211
|
CASE
|
|
199
212
|
WHEN columns.DATA_TYPE IN ('nchar','nvarchar') THEN columns.CHARACTER_MAXIMUM_LENGTH
|
|
200
|
-
ELSE COL_LENGTH('#{
|
|
213
|
+
ELSE COL_LENGTH('#{database}'+columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME)
|
|
201
214
|
END AS [length],
|
|
202
215
|
CASE
|
|
203
216
|
WHEN columns.IS_NULLABLE = 'YES' THEN 1
|
|
@@ -208,63 +221,80 @@ module ActiveRecord
|
|
|
208
221
|
ELSE NULL
|
|
209
222
|
END AS [is_primary],
|
|
210
223
|
c.is_identity AS [is_identity]
|
|
211
|
-
FROM #{
|
|
212
|
-
LEFT OUTER JOIN #{
|
|
224
|
+
FROM #{database}INFORMATION_SCHEMA.COLUMNS columns
|
|
225
|
+
LEFT OUTER JOIN #{database}INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
|
|
213
226
|
ON TC.TABLE_NAME = columns.TABLE_NAME
|
|
214
227
|
AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
|
|
215
|
-
LEFT OUTER JOIN #{
|
|
228
|
+
LEFT OUTER JOIN #{database}INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU
|
|
216
229
|
ON KCU.COLUMN_NAME = columns.COLUMN_NAME
|
|
217
230
|
AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
|
|
218
231
|
AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG
|
|
219
232
|
AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA
|
|
220
|
-
INNER JOIN #{
|
|
233
|
+
INNER JOIN #{identifier.database_quoted}.sys.schemas AS s
|
|
221
234
|
ON s.name = columns.TABLE_SCHEMA
|
|
222
235
|
AND s.schema_id = s.schema_id
|
|
223
|
-
INNER JOIN #{
|
|
236
|
+
INNER JOIN #{identifier.database_quoted}.sys.objects AS o
|
|
224
237
|
ON s.schema_id = o.schema_id
|
|
225
238
|
AND o.is_ms_shipped = 0
|
|
226
239
|
AND o.type IN ('U', 'V')
|
|
227
240
|
AND o.name = columns.TABLE_NAME
|
|
228
|
-
INNER JOIN #{
|
|
241
|
+
INNER JOIN #{identifier.database_quoted}.sys.columns AS c
|
|
229
242
|
ON o.object_id = c.object_id
|
|
230
243
|
AND c.name = columns.COLUMN_NAME
|
|
231
244
|
WHERE columns.TABLE_NAME = @0
|
|
232
|
-
AND columns.TABLE_SCHEMA = #{
|
|
245
|
+
AND columns.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : '@1'}
|
|
233
246
|
ORDER BY columns.ordinal_position
|
|
234
247
|
}.gsub(/[ \t\r\n]+/, ' ')
|
|
235
|
-
binds = [[
|
|
236
|
-
binds << [
|
|
237
|
-
results =
|
|
248
|
+
binds = [[info_schema_table_name_column, identifier.object]]
|
|
249
|
+
binds << [info_schema_table_schema_column, identifier.schema] unless identifier.schema.blank?
|
|
250
|
+
results = sp_executesql(sql, 'SCHEMA', binds)
|
|
238
251
|
results.map do |ci|
|
|
239
252
|
ci = ci.symbolize_keys
|
|
253
|
+
ci[:_type] = ci[:type]
|
|
254
|
+
ci[:table_name] = view_tblnm || table_name
|
|
240
255
|
ci[:type] = case ci[:type]
|
|
241
256
|
when /^bit|image|text|ntext|datetime$/
|
|
242
257
|
ci[:type]
|
|
258
|
+
when /^time$/i
|
|
259
|
+
"#{ci[:type]}(#{ci[:datetime_precision]})"
|
|
243
260
|
when /^numeric|decimal$/i
|
|
244
261
|
"#{ci[:type]}(#{ci[:numeric_precision]},#{ci[:numeric_scale]})"
|
|
245
262
|
when /^float|real$/i
|
|
246
|
-
"#{ci[:type]}
|
|
247
|
-
when /^char|nchar|varchar|nvarchar|varbinary|bigint|int|smallint$/
|
|
263
|
+
"#{ci[:type]}"
|
|
264
|
+
when /^char|nchar|varchar|nvarchar|binary|varbinary|bigint|int|smallint$/
|
|
248
265
|
ci[:length].to_i == -1 ? "#{ci[:type]}(max)" : "#{ci[:type]}(#{ci[:length]})"
|
|
249
266
|
else
|
|
250
267
|
ci[:type]
|
|
251
268
|
end
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
269
|
+
ci[:default_value],
|
|
270
|
+
ci[:default_function] = begin
|
|
271
|
+
default = ci[:default_value]
|
|
272
|
+
if default.nil? && view_exists
|
|
273
|
+
default = select_value "
|
|
274
|
+
SELECT c.COLUMN_DEFAULT
|
|
275
|
+
FROM #{database}INFORMATION_SCHEMA.COLUMNS c
|
|
276
|
+
WHERE c.TABLE_NAME = '#{view_tblnm}'
|
|
277
|
+
AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}'".squish, 'SCHEMA'
|
|
278
|
+
end
|
|
279
|
+
case default
|
|
280
|
+
when nil
|
|
281
|
+
[nil, nil]
|
|
282
|
+
when /\A\((\w+\(\))\)\Z/
|
|
283
|
+
default_function = Regexp.last_match[1]
|
|
284
|
+
[nil, default_function]
|
|
285
|
+
when /\A\(N'(.*)'\)\Z/m
|
|
286
|
+
string_literal = SQLServer::Utils.unquote_string(Regexp.last_match[1])
|
|
287
|
+
[string_literal, nil]
|
|
288
|
+
else
|
|
289
|
+
type = case ci[:type]
|
|
290
|
+
when /smallint|int|bigint/ then ci[:_type]
|
|
291
|
+
else ci[:type]
|
|
292
|
+
end
|
|
293
|
+
value = default.match(/\A\((.*)\)\Z/m)[1]
|
|
294
|
+
value = select_value "SELECT CAST(#{value} AS #{type}) AS value", 'SCHEMA'
|
|
295
|
+
[value, nil]
|
|
296
|
+
end
|
|
257
297
|
end
|
|
258
|
-
ci[:default_value] = case ci[:default_value]
|
|
259
|
-
when nil, '(null)', '(NULL)'
|
|
260
|
-
nil
|
|
261
|
-
when /\A\((\w+\(\))\)\Z/
|
|
262
|
-
ci[:default_function] = Regexp.last_match[1]
|
|
263
|
-
nil
|
|
264
|
-
else
|
|
265
|
-
match_data = ci[:default_value].match(/\A\(+N?'?(.*?)'?\)+\Z/m)
|
|
266
|
-
match_data ? match_data[1].gsub("''", "'") : nil
|
|
267
|
-
end
|
|
268
298
|
ci[:null] = ci[:is_nullable].to_i == 1
|
|
269
299
|
ci.delete(:is_nullable)
|
|
270
300
|
ci[:is_primary] = ci[:is_primary].to_i == 1
|
|
@@ -273,6 +303,14 @@ module ActiveRecord
|
|
|
273
303
|
end
|
|
274
304
|
end
|
|
275
305
|
|
|
306
|
+
def info_schema_table_name_column
|
|
307
|
+
@info_schema_table_name_column ||= new_column 'table_name', nil, lookup_cast_type('nvarchar(128)'), 'nvarchar(128)', true
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def info_schema_table_schema_column
|
|
311
|
+
@info_schema_table_schema_column ||= new_column 'table_schema', nil, lookup_cast_type('nvarchar(128)'), 'nvarchar(128)', true
|
|
312
|
+
end
|
|
313
|
+
|
|
276
314
|
def remove_check_constraints(table_name, column_name)
|
|
277
315
|
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'
|
|
278
316
|
constraints.each do |constraint|
|
|
@@ -298,13 +336,14 @@ module ActiveRecord
|
|
|
298
336
|
# === SQLServer Specific (Misc Helpers) ========================= #
|
|
299
337
|
|
|
300
338
|
def get_table_name(sql)
|
|
301
|
-
if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
|
|
339
|
+
tn = if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
|
|
302
340
|
Regexp.last_match[3] || Regexp.last_match[4]
|
|
303
341
|
elsif sql =~ /FROM\s+([^\(\s]+)\s*/i
|
|
304
342
|
Regexp.last_match[1]
|
|
305
343
|
else
|
|
306
344
|
nil
|
|
307
345
|
end
|
|
346
|
+
SQLServer::Utils.extract_identifiers(tn).object
|
|
308
347
|
end
|
|
309
348
|
|
|
310
349
|
def default_constraint_name(table_name, column_name)
|
|
@@ -330,25 +369,24 @@ module ActiveRecord
|
|
|
330
369
|
end
|
|
331
370
|
|
|
332
371
|
def view_information(table_name)
|
|
333
|
-
|
|
334
|
-
view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{
|
|
372
|
+
identifier = SQLServer::Utils.extract_identifiers(table_name)
|
|
373
|
+
view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{identifier.object}'", 'SCHEMA'
|
|
335
374
|
if view_info
|
|
336
375
|
view_info = view_info.with_indifferent_access
|
|
337
376
|
if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
|
|
338
377
|
view_info[:VIEW_DEFINITION] = begin
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
378
|
+
select_values("EXEC sp_helptext #{identifier.object_quoted}", 'SCHEMA').join
|
|
379
|
+
rescue
|
|
380
|
+
warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
|
|
381
|
+
nil
|
|
382
|
+
end
|
|
344
383
|
end
|
|
345
384
|
end
|
|
346
385
|
view_info
|
|
347
386
|
end
|
|
348
387
|
|
|
349
388
|
def table_name_or_views_table_name(table_name)
|
|
350
|
-
|
|
351
|
-
schema_cache.view_names.include?(unquoted_table_name) ? view_table_name(unquoted_table_name) : unquoted_table_name
|
|
389
|
+
schema_cache.view_exists?(table_name) ? view_table_name(table_name) : table_name
|
|
352
390
|
end
|
|
353
391
|
|
|
354
392
|
def views_real_column_name(table_name, column_name)
|
|
@@ -373,51 +411,17 @@ module ActiveRecord
|
|
|
373
411
|
!(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil?
|
|
374
412
|
end
|
|
375
413
|
|
|
376
|
-
def strip_ident_from_update(sql)
|
|
377
|
-
# We can't update Identiy columns in sqlserver. So, strip out the id from the update.
|
|
378
|
-
# There has to be a better way to handle this, but this'll do for now.
|
|
379
|
-
table_name = get_table_name(sql)
|
|
380
|
-
id_column = identity_column(table_name)
|
|
381
|
-
|
|
382
|
-
if id_column
|
|
383
|
-
regex_col_name = Regexp.quote(quote_column_name(id_column.name))
|
|
384
|
-
if sql =~ /, #{regex_col_name} = @?[0-9]*/
|
|
385
|
-
sql = sql.gsub(/, #{regex_col_name} = @?[0-9]*/, '')
|
|
386
|
-
elsif sql =~ /\s#{regex_col_name} = @?[0-9]*,/
|
|
387
|
-
sql = sql.gsub(/\s#{regex_col_name} = @?[0-9]*,/, '')
|
|
388
|
-
end
|
|
389
|
-
end
|
|
390
|
-
sql
|
|
391
|
-
end
|
|
392
|
-
|
|
393
|
-
def update_sql?(sql)
|
|
394
|
-
!(sql =~ /^\s*(UPDATE|EXEC sp_executesql N'UPDATE)/i).nil?
|
|
395
|
-
end
|
|
396
|
-
|
|
397
|
-
def with_identity_insert_enabled(table_name)
|
|
398
|
-
table_name = quote_table_name(table_name_or_views_table_name(table_name))
|
|
399
|
-
set_identity_insert(table_name, true)
|
|
400
|
-
yield
|
|
401
|
-
ensure
|
|
402
|
-
set_identity_insert(table_name, false)
|
|
403
|
-
end
|
|
404
|
-
|
|
405
|
-
def set_identity_insert(table_name, enable = true)
|
|
406
|
-
sql = "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
|
|
407
|
-
do_execute sql, 'SCHEMA'
|
|
408
|
-
rescue Exception
|
|
409
|
-
raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
|
|
410
|
-
end
|
|
411
|
-
|
|
412
414
|
def identity_column(table_name)
|
|
413
415
|
schema_cache.columns(table_name).find(&:is_identity?)
|
|
414
416
|
end
|
|
415
417
|
|
|
418
|
+
|
|
416
419
|
private
|
|
417
420
|
|
|
418
421
|
def create_table_definition(name, temporary, options, as = nil)
|
|
419
|
-
TableDefinition.new native_database_types, name, temporary, options, as
|
|
422
|
+
SQLServer::TableDefinition.new native_database_types, name, temporary, options, as
|
|
420
423
|
end
|
|
424
|
+
|
|
421
425
|
end
|
|
422
426
|
end
|
|
423
427
|
end
|