viking-sequel 3.10.0
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.
- data/CHANGELOG +3134 -0
- data/COPYING +19 -0
- data/README.rdoc +723 -0
- data/Rakefile +193 -0
- data/bin/sequel +196 -0
- data/doc/advanced_associations.rdoc +644 -0
- data/doc/cheat_sheet.rdoc +218 -0
- data/doc/dataset_basics.rdoc +106 -0
- data/doc/dataset_filtering.rdoc +158 -0
- data/doc/opening_databases.rdoc +296 -0
- data/doc/prepared_statements.rdoc +104 -0
- data/doc/reflection.rdoc +84 -0
- data/doc/release_notes/1.0.txt +38 -0
- data/doc/release_notes/1.1.txt +143 -0
- data/doc/release_notes/1.3.txt +101 -0
- data/doc/release_notes/1.4.0.txt +53 -0
- data/doc/release_notes/1.5.0.txt +155 -0
- data/doc/release_notes/2.0.0.txt +298 -0
- data/doc/release_notes/2.1.0.txt +271 -0
- data/doc/release_notes/2.10.0.txt +328 -0
- data/doc/release_notes/2.11.0.txt +215 -0
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/release_notes/2.2.0.txt +253 -0
- data/doc/release_notes/2.3.0.txt +88 -0
- data/doc/release_notes/2.4.0.txt +106 -0
- data/doc/release_notes/2.5.0.txt +137 -0
- data/doc/release_notes/2.6.0.txt +157 -0
- data/doc/release_notes/2.7.0.txt +166 -0
- data/doc/release_notes/2.8.0.txt +171 -0
- data/doc/release_notes/2.9.0.txt +97 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/doc/release_notes/3.10.0.txt +286 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/release_notes/3.3.0.txt +192 -0
- data/doc/release_notes/3.4.0.txt +325 -0
- data/doc/release_notes/3.5.0.txt +510 -0
- data/doc/release_notes/3.6.0.txt +366 -0
- data/doc/release_notes/3.7.0.txt +179 -0
- data/doc/release_notes/3.8.0.txt +151 -0
- data/doc/release_notes/3.9.0.txt +233 -0
- data/doc/schema.rdoc +36 -0
- data/doc/sharding.rdoc +113 -0
- data/doc/virtual_rows.rdoc +205 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +90 -0
- data/lib/sequel/adapters/ado/mssql.rb +30 -0
- data/lib/sequel/adapters/amalgalite.rb +176 -0
- data/lib/sequel/adapters/db2.rb +139 -0
- data/lib/sequel/adapters/dbi.rb +113 -0
- data/lib/sequel/adapters/do.rb +188 -0
- data/lib/sequel/adapters/do/mysql.rb +49 -0
- data/lib/sequel/adapters/do/postgres.rb +91 -0
- data/lib/sequel/adapters/do/sqlite.rb +40 -0
- data/lib/sequel/adapters/firebird.rb +283 -0
- data/lib/sequel/adapters/informix.rb +77 -0
- data/lib/sequel/adapters/jdbc.rb +587 -0
- data/lib/sequel/adapters/jdbc/as400.rb +58 -0
- data/lib/sequel/adapters/jdbc/h2.rb +133 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
- data/lib/sequel/adapters/mysql.rb +421 -0
- data/lib/sequel/adapters/odbc.rb +143 -0
- data/lib/sequel/adapters/odbc/mssql.rb +42 -0
- data/lib/sequel/adapters/openbase.rb +64 -0
- data/lib/sequel/adapters/oracle.rb +131 -0
- data/lib/sequel/adapters/postgres.rb +504 -0
- data/lib/sequel/adapters/shared/mssql.rb +490 -0
- data/lib/sequel/adapters/shared/mysql.rb +498 -0
- data/lib/sequel/adapters/shared/oracle.rb +195 -0
- data/lib/sequel/adapters/shared/postgres.rb +830 -0
- data/lib/sequel/adapters/shared/progress.rb +44 -0
- data/lib/sequel/adapters/shared/sqlite.rb +389 -0
- data/lib/sequel/adapters/sqlite.rb +224 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
- data/lib/sequel/connection_pool.rb +99 -0
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +293 -0
- data/lib/sequel/core_sql.rb +241 -0
- data/lib/sequel/database.rb +1079 -0
- data/lib/sequel/database/schema_generator.rb +327 -0
- data/lib/sequel/database/schema_methods.rb +203 -0
- data/lib/sequel/database/schema_sql.rb +320 -0
- data/lib/sequel/dataset.rb +32 -0
- data/lib/sequel/dataset/actions.rb +441 -0
- data/lib/sequel/dataset/features.rb +86 -0
- data/lib/sequel/dataset/graph.rb +254 -0
- data/lib/sequel/dataset/misc.rb +119 -0
- data/lib/sequel/dataset/mutation.rb +64 -0
- data/lib/sequel/dataset/prepared_statements.rb +227 -0
- data/lib/sequel/dataset/query.rb +709 -0
- data/lib/sequel/dataset/sql.rb +996 -0
- data/lib/sequel/exceptions.rb +51 -0
- data/lib/sequel/extensions/blank.rb +43 -0
- data/lib/sequel/extensions/inflector.rb +242 -0
- data/lib/sequel/extensions/looser_typecasting.rb +21 -0
- data/lib/sequel/extensions/migration.rb +239 -0
- data/lib/sequel/extensions/named_timezones.rb +61 -0
- data/lib/sequel/extensions/pagination.rb +100 -0
- data/lib/sequel/extensions/pretty_table.rb +82 -0
- data/lib/sequel/extensions/query.rb +52 -0
- data/lib/sequel/extensions/schema_dumper.rb +271 -0
- data/lib/sequel/extensions/sql_expr.rb +122 -0
- data/lib/sequel/extensions/string_date_time.rb +46 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
- data/lib/sequel/metaprogramming.rb +9 -0
- data/lib/sequel/model.rb +120 -0
- data/lib/sequel/model/associations.rb +1514 -0
- data/lib/sequel/model/base.rb +1069 -0
- data/lib/sequel/model/default_inflections.rb +45 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/sequel/model/exceptions.rb +21 -0
- data/lib/sequel/model/inflections.rb +162 -0
- data/lib/sequel/model/plugins.rb +70 -0
- data/lib/sequel/plugins/active_model.rb +59 -0
- data/lib/sequel/plugins/association_dependencies.rb +103 -0
- data/lib/sequel/plugins/association_proxies.rb +41 -0
- data/lib/sequel/plugins/boolean_readers.rb +53 -0
- data/lib/sequel/plugins/caching.rb +141 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
- data/lib/sequel/plugins/composition.rb +138 -0
- data/lib/sequel/plugins/force_encoding.rb +72 -0
- data/lib/sequel/plugins/hook_class_methods.rb +126 -0
- data/lib/sequel/plugins/identity_map.rb +116 -0
- data/lib/sequel/plugins/instance_filters.rb +98 -0
- data/lib/sequel/plugins/instance_hooks.rb +57 -0
- data/lib/sequel/plugins/lazy_attributes.rb +77 -0
- data/lib/sequel/plugins/many_through_many.rb +208 -0
- data/lib/sequel/plugins/nested_attributes.rb +206 -0
- data/lib/sequel/plugins/optimistic_locking.rb +81 -0
- data/lib/sequel/plugins/rcte_tree.rb +281 -0
- data/lib/sequel/plugins/schema.rb +66 -0
- data/lib/sequel/plugins/serialization.rb +166 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
- data/lib/sequel/plugins/subclasses.rb +45 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/timestamps.rb +87 -0
- data/lib/sequel/plugins/touch.rb +118 -0
- data/lib/sequel/plugins/typecast_on_load.rb +72 -0
- data/lib/sequel/plugins/validation_class_methods.rb +405 -0
- data/lib/sequel/plugins/validation_helpers.rb +223 -0
- data/lib/sequel/sql.rb +1020 -0
- data/lib/sequel/timezones.rb +161 -0
- data/lib/sequel/version.rb +12 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/firebird_spec.rb +407 -0
- data/spec/adapters/informix_spec.rb +97 -0
- data/spec/adapters/mssql_spec.rb +403 -0
- data/spec/adapters/mysql_spec.rb +1019 -0
- data/spec/adapters/oracle_spec.rb +286 -0
- data/spec/adapters/postgres_spec.rb +969 -0
- data/spec/adapters/spec_helper.rb +51 -0
- data/spec/adapters/sqlite_spec.rb +432 -0
- data/spec/core/connection_pool_spec.rb +808 -0
- data/spec/core/core_sql_spec.rb +417 -0
- data/spec/core/database_spec.rb +1662 -0
- data/spec/core/dataset_spec.rb +3827 -0
- data/spec/core/expression_filters_spec.rb +595 -0
- data/spec/core/object_graph_spec.rb +296 -0
- data/spec/core/schema_generator_spec.rb +159 -0
- data/spec/core/schema_spec.rb +830 -0
- data/spec/core/spec_helper.rb +56 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/active_model_spec.rb +76 -0
- data/spec/extensions/association_dependencies_spec.rb +127 -0
- data/spec/extensions/association_proxies_spec.rb +50 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/boolean_readers_spec.rb +92 -0
- data/spec/extensions/caching_spec.rb +250 -0
- data/spec/extensions/class_table_inheritance_spec.rb +252 -0
- data/spec/extensions/composition_spec.rb +194 -0
- data/spec/extensions/force_encoding_spec.rb +117 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/identity_map_spec.rb +202 -0
- data/spec/extensions/inflector_spec.rb +181 -0
- data/spec/extensions/instance_filters_spec.rb +55 -0
- data/spec/extensions/instance_hooks_spec.rb +133 -0
- data/spec/extensions/lazy_attributes_spec.rb +153 -0
- data/spec/extensions/looser_typecasting_spec.rb +39 -0
- data/spec/extensions/many_through_many_spec.rb +884 -0
- data/spec/extensions/migration_spec.rb +332 -0
- data/spec/extensions/named_timezones_spec.rb +72 -0
- data/spec/extensions/nested_attributes_spec.rb +396 -0
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/rcte_tree_spec.rb +205 -0
- data/spec/extensions/schema_dumper_spec.rb +357 -0
- data/spec/extensions/schema_spec.rb +127 -0
- data/spec/extensions/serialization_spec.rb +209 -0
- data/spec/extensions/single_table_inheritance_spec.rb +96 -0
- data/spec/extensions/spec_helper.rb +91 -0
- data/spec/extensions/sql_expr_spec.rb +89 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/subclasses_spec.rb +52 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/thread_local_timezones_spec.rb +45 -0
- data/spec/extensions/timestamps_spec.rb +150 -0
- data/spec/extensions/touch_spec.rb +155 -0
- data/spec/extensions/typecast_on_load_spec.rb +69 -0
- data/spec/extensions/validation_class_methods_spec.rb +984 -0
- data/spec/extensions/validation_helpers_spec.rb +438 -0
- data/spec/integration/associations_test.rb +281 -0
- data/spec/integration/database_test.rb +26 -0
- data/spec/integration/dataset_test.rb +963 -0
- data/spec/integration/eager_loader_test.rb +734 -0
- data/spec/integration/model_test.rb +130 -0
- data/spec/integration/plugin_test.rb +814 -0
- data/spec/integration/prepared_statement_test.rb +213 -0
- data/spec/integration/schema_test.rb +361 -0
- data/spec/integration/spec_helper.rb +73 -0
- data/spec/integration/timezone_test.rb +55 -0
- data/spec/integration/transaction_test.rb +122 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +175 -0
- data/spec/model/associations_spec.rb +2633 -0
- data/spec/model/base_spec.rb +418 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1391 -0
- data/spec/model/hooks_spec.rb +240 -0
- data/spec/model/inflector_spec.rb +26 -0
- data/spec/model/model_spec.rb +593 -0
- data/spec/model/plugins_spec.rb +236 -0
- data/spec/model/record_spec.rb +1500 -0
- data/spec/model/spec_helper.rb +97 -0
- data/spec/model/validations_spec.rb +153 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +346 -0
@@ -0,0 +1,490 @@
|
|
1
|
+
module Sequel
|
2
|
+
Dataset::NON_SQL_OPTIONS << :disable_insert_output
|
3
|
+
module MSSQL
|
4
|
+
module DatabaseMethods
|
5
|
+
AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
|
6
|
+
SERVER_VERSION_RE = /^(\d+)\.(\d+)\.(\d+)/.freeze
|
7
|
+
SERVER_VERSION_SQL = "SELECT CAST(SERVERPROPERTY('ProductVersion') AS varchar)".freeze
|
8
|
+
SQL_BEGIN = "BEGIN TRANSACTION".freeze
|
9
|
+
SQL_COMMIT = "COMMIT TRANSACTION".freeze
|
10
|
+
SQL_ROLLBACK = "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION".freeze
|
11
|
+
SQL_ROLLBACK_TO_SAVEPOINT = 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION autopoint_%d'.freeze
|
12
|
+
SQL_SAVEPOINT = 'SAVE TRANSACTION autopoint_%d'.freeze
|
13
|
+
TEMPORARY = "#".freeze
|
14
|
+
|
15
|
+
# Microsoft SQL Server uses the :mssql type.
|
16
|
+
def database_type
|
17
|
+
:mssql
|
18
|
+
end
|
19
|
+
|
20
|
+
# The version of the MSSQL server, as an integer (e.g. 10001600 for
|
21
|
+
# SQL Server 2008 Express).
|
22
|
+
def server_version(server=nil)
|
23
|
+
return @server_version if @server_version
|
24
|
+
@server_version = synchronize(server) do |conn|
|
25
|
+
(conn.server_version rescue nil) if conn.respond_to?(:server_version)
|
26
|
+
end
|
27
|
+
unless @server_version
|
28
|
+
m = SERVER_VERSION_RE.match(fetch(SERVER_VERSION_SQL).single_value.to_s)
|
29
|
+
@server_version = (m[1].to_i * 1000000) + (m[2].to_i * 10000) + m[3].to_i
|
30
|
+
end
|
31
|
+
@server_version
|
32
|
+
end
|
33
|
+
|
34
|
+
# MSSQL supports savepoints, though it doesn't support committing/releasing them savepoint
|
35
|
+
def supports_savepoints?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
# Microsoft SQL Server supports using the INFORMATION_SCHEMA to get
|
40
|
+
# information on tables.
|
41
|
+
def tables(opts={})
|
42
|
+
m = output_identifier_meth
|
43
|
+
metadata_dataset.from(:information_schema__tables___t).
|
44
|
+
select(:table_name).
|
45
|
+
filter(:table_type=>'BASE TABLE', :table_schema=>(opts[:schema]||default_schema||'dbo').to_s).
|
46
|
+
map{|x| m.call(x[:table_name])}
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# MSSQL uses the IDENTITY(1,1) column for autoincrementing columns.
|
52
|
+
def auto_increment_sql
|
53
|
+
AUTO_INCREMENT
|
54
|
+
end
|
55
|
+
|
56
|
+
# MSSQL specific syntax for altering tables.
|
57
|
+
def alter_table_sql(table, op)
|
58
|
+
case op[:op]
|
59
|
+
when :add_column
|
60
|
+
"ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}"
|
61
|
+
when :rename_column
|
62
|
+
"SP_RENAME #{literal("#{quote_schema_table(table)}.#{quote_identifier(op[:name])}")}, #{literal(op[:new_name].to_s)}, 'COLUMN'"
|
63
|
+
when :set_column_type
|
64
|
+
"ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} #{type_literal(op)}"
|
65
|
+
when :set_column_null
|
66
|
+
sch = schema(table).find{|k,v| k.to_s == op[:name].to_s}.last
|
67
|
+
type = sch[:db_type]
|
68
|
+
if [:string, :decimal].include?(sch[:type]) and size = (sch[:max_chars] || sch[:column_size])
|
69
|
+
type += "(#{size}#{", #{sch[:scale]}" if sch[:scale] && sch[:scale].to_i > 0})"
|
70
|
+
end
|
71
|
+
"ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} #{type_literal(:type=>type)} #{'NOT ' unless op[:null]}NULL"
|
72
|
+
when :set_column_default
|
73
|
+
"ALTER TABLE #{quote_schema_table(table)} ADD CONSTRAINT #{quote_identifier("sequel_#{table}_#{op[:name]}_def")} DEFAULT #{literal(op[:default])} FOR #{quote_identifier(op[:name])}"
|
74
|
+
else
|
75
|
+
super(table, op)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# SQL to start a new savepoint
|
80
|
+
def begin_savepoint_sql(depth)
|
81
|
+
SQL_SAVEPOINT % depth
|
82
|
+
end
|
83
|
+
|
84
|
+
# SQL to BEGIN a transaction.
|
85
|
+
def begin_transaction_sql
|
86
|
+
SQL_BEGIN
|
87
|
+
end
|
88
|
+
|
89
|
+
# Commit the active transaction on the connection, does not commit/release
|
90
|
+
# savepoints.
|
91
|
+
def commit_transaction(conn)
|
92
|
+
log_connection_execute(conn, commit_transaction_sql) unless Thread.current[:sequel_transaction_depth] > 1
|
93
|
+
end
|
94
|
+
|
95
|
+
# SQL to COMMIT a transaction.
|
96
|
+
def commit_transaction_sql
|
97
|
+
SQL_COMMIT
|
98
|
+
end
|
99
|
+
|
100
|
+
# The SQL to drop an index for the table.
|
101
|
+
def drop_index_sql(table, op)
|
102
|
+
"DROP INDEX #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))} ON #{quote_schema_table(table)}"
|
103
|
+
end
|
104
|
+
|
105
|
+
# Always quote identifiers in the metadata_dataset, so schema parsing works.
|
106
|
+
def metadata_dataset
|
107
|
+
ds = super
|
108
|
+
ds.quote_identifiers = true
|
109
|
+
ds
|
110
|
+
end
|
111
|
+
|
112
|
+
# Use SP_RENAME to rename the table
|
113
|
+
def rename_table_sql(name, new_name)
|
114
|
+
"SP_RENAME #{quote_schema_table(name)}, #{quote_schema_table(new_name)}"
|
115
|
+
end
|
116
|
+
|
117
|
+
# SQL to rollback to a savepoint
|
118
|
+
def rollback_savepoint_sql(depth)
|
119
|
+
SQL_ROLLBACK_TO_SAVEPOINT % depth
|
120
|
+
end
|
121
|
+
|
122
|
+
# SQL to ROLLBACK a transaction.
|
123
|
+
def rollback_transaction_sql
|
124
|
+
SQL_ROLLBACK
|
125
|
+
end
|
126
|
+
|
127
|
+
# MSSQL uses the INFORMATION_SCHEMA to hold column information. This method does
|
128
|
+
# not support the parsing of primary key information.
|
129
|
+
def schema_parse_table(table_name, opts)
|
130
|
+
m = output_identifier_meth
|
131
|
+
m2 = input_identifier_meth
|
132
|
+
ds = metadata_dataset.from(:information_schema__tables___t).
|
133
|
+
join(:information_schema__columns___c, :table_catalog=>:table_catalog,
|
134
|
+
:table_schema => :table_schema, :table_name => :table_name).
|
135
|
+
select(:column_name___column, :data_type___db_type, :character_maximum_length___max_chars, :column_default___default, :is_nullable___allow_null, :numeric_precision___column_size, :numeric_scale___scale).
|
136
|
+
filter(:c__table_name=>m2.call(table_name.to_s))
|
137
|
+
if schema = opts[:schema] || default_schema
|
138
|
+
ds.filter!(:c__table_schema=>schema)
|
139
|
+
end
|
140
|
+
ds.map do |row|
|
141
|
+
row[:allow_null] = row[:allow_null] == 'YES' ? true : false
|
142
|
+
row[:default] = nil if blank_object?(row[:default])
|
143
|
+
row[:type] = schema_column_type(row[:db_type])
|
144
|
+
[m.call(row.delete(:column)), row]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# SQL fragment for marking a table as temporary
|
149
|
+
def temporary_table_sql
|
150
|
+
TEMPORARY
|
151
|
+
end
|
152
|
+
|
153
|
+
# MSSQL has both datetime and timestamp classes, most people are going
|
154
|
+
# to want datetime
|
155
|
+
def type_literal_generic_datetime(column)
|
156
|
+
:datetime
|
157
|
+
end
|
158
|
+
|
159
|
+
# MSSQL has both datetime and timestamp classes, most people are going
|
160
|
+
# to want datetime
|
161
|
+
def type_literal_generic_time(column)
|
162
|
+
column[:only_time] ? :time : :datetime
|
163
|
+
end
|
164
|
+
|
165
|
+
# MSSQL doesn't have a true boolean class, so it uses bit
|
166
|
+
def type_literal_generic_trueclass(column)
|
167
|
+
:bit
|
168
|
+
end
|
169
|
+
|
170
|
+
# MSSQL uses image type for blobs
|
171
|
+
def type_literal_generic_file(column)
|
172
|
+
:image
|
173
|
+
end
|
174
|
+
|
175
|
+
# support for clustered index type
|
176
|
+
def index_definition_sql(table_name, index)
|
177
|
+
index_name = index[:name] || default_index_name(table_name, index[:columns])
|
178
|
+
clustered = index[:type] == :clustered
|
179
|
+
if index[:where]
|
180
|
+
raise Error, "Partial indexes are not supported for this database"
|
181
|
+
else
|
182
|
+
"CREATE #{'UNIQUE ' if index[:unique]}#{'CLUSTERED ' if clustered}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
module DatasetMethods
|
188
|
+
BOOL_TRUE = '1'.freeze
|
189
|
+
BOOL_FALSE = '0'.freeze
|
190
|
+
COMMA_SEPARATOR = ', '.freeze
|
191
|
+
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'with from output from2 where')
|
192
|
+
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'with into columns output values')
|
193
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with limit distinct columns into from lock join where group having order compounds')
|
194
|
+
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'with table set output from where')
|
195
|
+
NOLOCK = ' WITH (NOLOCK)'.freeze
|
196
|
+
UPDLOCK = ' WITH (UPDLOCK)'.freeze
|
197
|
+
WILDCARD = LiteralString.new('*').freeze
|
198
|
+
CONSTANT_MAP = {:CURRENT_DATE=>'CAST(CURRENT_TIMESTAMP AS DATE)'.freeze, :CURRENT_TIME=>'CAST(CURRENT_TIMESTAMP AS TIME)'.freeze}
|
199
|
+
|
200
|
+
# MSSQL uses + for string concatenation, and LIKE is case insensitive by default.
|
201
|
+
def complex_expression_sql(op, args)
|
202
|
+
case op
|
203
|
+
when :'||'
|
204
|
+
super(:+, args)
|
205
|
+
when :ILIKE
|
206
|
+
super(:LIKE, args)
|
207
|
+
when :"NOT ILIKE"
|
208
|
+
super(:"NOT LIKE", args)
|
209
|
+
else
|
210
|
+
super(op, args)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# MSSQL doesn't support the SQL standard CURRENT_DATE or CURRENT_TIME
|
215
|
+
def constant_sql(constant)
|
216
|
+
CONSTANT_MAP[constant] || super
|
217
|
+
end
|
218
|
+
|
219
|
+
# Disable the use of INSERT OUTPUT
|
220
|
+
def disable_insert_output
|
221
|
+
clone(:disable_insert_output=>true)
|
222
|
+
end
|
223
|
+
|
224
|
+
# Disable the use of INSERT OUTPUT, modifying the receiver
|
225
|
+
def disable_insert_output!
|
226
|
+
mutation_method(:disable_insert_output)
|
227
|
+
end
|
228
|
+
|
229
|
+
# When returning all rows, if an offset is used, delete the row_number column
|
230
|
+
# before yielding the row.
|
231
|
+
def fetch_rows(sql, &block)
|
232
|
+
@opts[:offset] ? super(sql){|r| r.delete(row_number_column); yield r} : super(sql, &block)
|
233
|
+
end
|
234
|
+
|
235
|
+
# MSSQL uses the CONTAINS keyword for full text search
|
236
|
+
def full_text_search(cols, terms, opts = {})
|
237
|
+
filter("CONTAINS (#{literal(cols)}, #{literal(terms)})")
|
238
|
+
end
|
239
|
+
|
240
|
+
# Use the OUTPUT clause to get the value of all columns for the newly inserted record.
|
241
|
+
def insert_select(*values)
|
242
|
+
return unless supports_output_clause?
|
243
|
+
naked.clone(default_server_opts(:sql=>output(nil, [:inserted.*]).insert_sql(*values))).single_record unless opts[:disable_insert_output]
|
244
|
+
end
|
245
|
+
|
246
|
+
# Specify a table for a SELECT ... INTO query.
|
247
|
+
def into(table)
|
248
|
+
clone(:into => table)
|
249
|
+
end
|
250
|
+
|
251
|
+
# MSSQL uses a UNION ALL statement to insert multiple values at once.
|
252
|
+
def multi_insert_sql(columns, values)
|
253
|
+
[insert_sql(columns, LiteralString.new(values.map {|r| "SELECT #{expression_list(r)}" }.join(" UNION ALL ")))]
|
254
|
+
end
|
255
|
+
|
256
|
+
# Allows you to do a dirty read of uncommitted data using WITH (NOLOCK).
|
257
|
+
def nolock
|
258
|
+
lock_style(:dirty)
|
259
|
+
end
|
260
|
+
|
261
|
+
# Include an OUTPUT clause in the eventual INSERT, UPDATE, or DELETE query.
|
262
|
+
#
|
263
|
+
# The first argument is the table to output into, and the second argument
|
264
|
+
# is either an Array of column values to select, or a Hash which maps output
|
265
|
+
# column names to selected values, in the style of #insert or #update.
|
266
|
+
#
|
267
|
+
# Output into a returned result set is not currently supported.
|
268
|
+
#
|
269
|
+
# Examples:
|
270
|
+
#
|
271
|
+
# dataset.output(:output_table, [:deleted__id, :deleted__name])
|
272
|
+
# dataset.output(:output_table, :id => :inserted__id, :name => :inserted__name)
|
273
|
+
def output(into, values)
|
274
|
+
raise(Error, "SQL Server versions 2000 and earlier do not support the OUTPUT clause") unless supports_output_clause?
|
275
|
+
output = {}
|
276
|
+
case values
|
277
|
+
when Hash
|
278
|
+
output[:column_list], output[:select_list] = values.keys, values.values
|
279
|
+
when Array
|
280
|
+
output[:select_list] = values
|
281
|
+
end
|
282
|
+
output[:into] = into
|
283
|
+
clone({:output => output})
|
284
|
+
end
|
285
|
+
|
286
|
+
# An output method that modifies the receiver.
|
287
|
+
def output!(into, values)
|
288
|
+
mutation_method(:output, into, values)
|
289
|
+
end
|
290
|
+
|
291
|
+
# MSSQL uses [] to quote identifiers
|
292
|
+
def quoted_identifier(name)
|
293
|
+
"[#{name}]"
|
294
|
+
end
|
295
|
+
|
296
|
+
# MSSQL Requires the use of the ROW_NUMBER window function to emulate
|
297
|
+
# an offset. This implementation requires MSSQL 2005 or greater (offset
|
298
|
+
# can't be emulated well in MSSQL 2000).
|
299
|
+
#
|
300
|
+
# The implementation is ugly, cloning the current dataset and modifying
|
301
|
+
# the clone to add a ROW_NUMBER window function (and some other things),
|
302
|
+
# then using the modified clone in a subselect which is selected from.
|
303
|
+
#
|
304
|
+
# If offset is used, an order must be provided, because the use of ROW_NUMBER
|
305
|
+
# requires an order.
|
306
|
+
def select_sql
|
307
|
+
return super unless o = @opts[:offset]
|
308
|
+
raise(Error, 'MSSQL requires an order be provided if using an offset') unless order = @opts[:order]
|
309
|
+
dsa1 = dataset_alias(1)
|
310
|
+
rn = row_number_column
|
311
|
+
subselect_sql(unlimited.
|
312
|
+
unordered.
|
313
|
+
select_append{ROW_NUMBER(:over, :order=>order){}.as(rn)}.
|
314
|
+
from_self(:alias=>dsa1).
|
315
|
+
limit(@opts[:limit]).
|
316
|
+
where(SQL::Identifier.new(rn) > o))
|
317
|
+
end
|
318
|
+
|
319
|
+
# The version of the database server.
|
320
|
+
def server_version
|
321
|
+
db.server_version(@opts[:server])
|
322
|
+
end
|
323
|
+
|
324
|
+
# Microsoft SQL Server does not support INTERSECT or EXCEPT
|
325
|
+
def supports_intersect_except?
|
326
|
+
false
|
327
|
+
end
|
328
|
+
|
329
|
+
# MSSQL does not support IS TRUE
|
330
|
+
def supports_is_true?
|
331
|
+
false
|
332
|
+
end
|
333
|
+
|
334
|
+
# MSSQL doesn't support JOIN USING
|
335
|
+
def supports_join_using?
|
336
|
+
false
|
337
|
+
end
|
338
|
+
|
339
|
+
# MSSQL 2005+ supports modifying joined datasets
|
340
|
+
def supports_modifying_joins?
|
341
|
+
true
|
342
|
+
end
|
343
|
+
|
344
|
+
# MSSQL does not support multiple columns for the IN/NOT IN operators
|
345
|
+
def supports_multiple_column_in?
|
346
|
+
false
|
347
|
+
end
|
348
|
+
|
349
|
+
# Only 2005+ supports the output clause.
|
350
|
+
def supports_output_clause?
|
351
|
+
server_version >= 9000000
|
352
|
+
end
|
353
|
+
|
354
|
+
# MSSQL 2005+ supports window functions
|
355
|
+
def supports_window_functions?
|
356
|
+
true
|
357
|
+
end
|
358
|
+
|
359
|
+
private
|
360
|
+
|
361
|
+
# MSSQL supports the OUTPUT clause for DELETE statements.
|
362
|
+
# It also allows prepending a WITH clause.
|
363
|
+
def delete_clause_methods
|
364
|
+
DELETE_CLAUSE_METHODS
|
365
|
+
end
|
366
|
+
|
367
|
+
# Only include the primary table in the main delete clause
|
368
|
+
def delete_from_sql(sql)
|
369
|
+
sql << " FROM #{source_list(@opts[:from][0..0])}"
|
370
|
+
end
|
371
|
+
|
372
|
+
# MSSQL supports FROM clauses in DELETE and UPDATE statements.
|
373
|
+
def delete_from2_sql(sql)
|
374
|
+
if joined_dataset?
|
375
|
+
select_from_sql(sql)
|
376
|
+
select_join_sql(sql)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
alias update_from_sql delete_from2_sql
|
380
|
+
|
381
|
+
# Handle the with clause for delete, insert, and update statements
|
382
|
+
# to be the same as the insert statement.
|
383
|
+
def delete_with_sql(sql)
|
384
|
+
select_with_sql(sql)
|
385
|
+
end
|
386
|
+
alias insert_with_sql delete_with_sql
|
387
|
+
alias update_with_sql delete_with_sql
|
388
|
+
|
389
|
+
# MSSQL raises an error if you try to provide more than 3 decimal places
|
390
|
+
# for a fractional timestamp. This probably doesn't work for smalldatetime
|
391
|
+
# fields.
|
392
|
+
def format_timestamp_usec(usec)
|
393
|
+
sprintf(".%03d", usec/1000)
|
394
|
+
end
|
395
|
+
|
396
|
+
# MSSQL supports the OUTPUT clause for INSERT statements.
|
397
|
+
# It also allows prepending a WITH clause.
|
398
|
+
def insert_clause_methods
|
399
|
+
INSERT_CLAUSE_METHODS
|
400
|
+
end
|
401
|
+
|
402
|
+
# MSSQL uses a literal hexidecimal number for blob strings
|
403
|
+
def literal_blob(v)
|
404
|
+
blob = '0x'
|
405
|
+
v.each_byte{|x| blob << sprintf('%02x', x)}
|
406
|
+
blob
|
407
|
+
end
|
408
|
+
|
409
|
+
# Use unicode string syntax for all strings. Don't double backslashes.
|
410
|
+
def literal_string(v)
|
411
|
+
"N'#{v.gsub(/'/, "''")}'"
|
412
|
+
end
|
413
|
+
|
414
|
+
# Use 0 for false on MSSQL
|
415
|
+
def literal_false
|
416
|
+
BOOL_FALSE
|
417
|
+
end
|
418
|
+
|
419
|
+
# Use 1 for true on MSSQL
|
420
|
+
def literal_true
|
421
|
+
BOOL_TRUE
|
422
|
+
end
|
423
|
+
|
424
|
+
# The alias to use for the row_number column when emulating OFFSET
|
425
|
+
def row_number_column
|
426
|
+
:x_sequel_row_number_x
|
427
|
+
end
|
428
|
+
|
429
|
+
# MSSQL adds the limit before the columns
|
430
|
+
def select_clause_methods
|
431
|
+
SELECT_CLAUSE_METHODS
|
432
|
+
end
|
433
|
+
|
434
|
+
def select_into_sql(sql)
|
435
|
+
sql << " INTO #{table_ref(@opts[:into])}" if @opts[:into]
|
436
|
+
end
|
437
|
+
|
438
|
+
# MSSQL uses TOP N for limit. For MSSQL 2005+ TOP (N) is used
|
439
|
+
# to allow the limit to be a bound variable.
|
440
|
+
def select_limit_sql(sql)
|
441
|
+
if l = @opts[:limit]
|
442
|
+
l = literal(l)
|
443
|
+
l = "(#{l})" if server_version >= 9000000
|
444
|
+
sql << " TOP #{l}"
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
# Support different types of locking styles
|
449
|
+
def select_lock_sql(sql)
|
450
|
+
case @opts[:lock]
|
451
|
+
when :update
|
452
|
+
sql << UPDLOCK
|
453
|
+
when :dirty
|
454
|
+
sql << NOLOCK
|
455
|
+
else
|
456
|
+
super
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
# SQL fragment for MSSQL's OUTPUT clause.
|
461
|
+
def output_sql(sql)
|
462
|
+
return unless supports_output_clause?
|
463
|
+
return unless output = @opts[:output]
|
464
|
+
sql << " OUTPUT #{column_list(output[:select_list])}"
|
465
|
+
if into = output[:into]
|
466
|
+
sql << " INTO #{table_ref(into)}"
|
467
|
+
if column_list = output[:column_list]
|
468
|
+
cl = []
|
469
|
+
column_list.each { |k, v| cl << literal(String === k ? k.to_sym : k) }
|
470
|
+
sql << " (#{cl.join(COMMA_SEPARATOR)})"
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|
474
|
+
alias delete_output_sql output_sql
|
475
|
+
alias update_output_sql output_sql
|
476
|
+
alias insert_output_sql output_sql
|
477
|
+
|
478
|
+
# MSSQL supports the OUTPUT clause for UPDATE statements.
|
479
|
+
# It also allows prepending a WITH clause.
|
480
|
+
def update_clause_methods
|
481
|
+
UPDATE_CLAUSE_METHODS
|
482
|
+
end
|
483
|
+
|
484
|
+
# Only include the primary table in the main update clause
|
485
|
+
def update_table_sql(sql)
|
486
|
+
sql << " #{source_list(@opts[:from][0..0])}"
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end
|
490
|
+
end
|