sequel 3.21.0 → 3.41.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 +1131 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +106 -65
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +68 -52
- data/doc/advanced_associations.rdoc +201 -75
- data/doc/association_basics.rdoc +327 -79
- data/doc/bin_sequel.rdoc +144 -0
- data/doc/cheat_sheet.rdoc +21 -21
- data/doc/core_extensions.rdoc +374 -0
- data/doc/dataset_basics.rdoc +10 -10
- data/doc/dataset_filtering.rdoc +54 -42
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +89 -557
- data/doc/model_hooks.rdoc +80 -29
- data/doc/object_model.rdoc +576 -0
- data/doc/opening_databases.rdoc +121 -53
- data/doc/prepared_statements.rdoc +41 -15
- data/doc/querying.rdoc +137 -56
- data/doc/reflection.rdoc +36 -10
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/release_notes/3.33.0.txt +157 -0
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/release_notes/3.37.0.txt +338 -0
- data/doc/release_notes/3.38.0.txt +234 -0
- data/doc/release_notes/3.39.0.txt +237 -0
- data/doc/release_notes/3.40.0.txt +73 -0
- data/doc/release_notes/3.41.0.txt +155 -0
- data/doc/schema_modification.rdoc +585 -0
- data/doc/sharding.rdoc +99 -8
- data/doc/sql.rdoc +154 -112
- data/doc/testing.rdoc +169 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +78 -43
- data/lib/sequel/adapters/ado/access.rb +334 -0
- data/lib/sequel/adapters/ado/mssql.rb +18 -12
- data/lib/sequel/adapters/ado.rb +69 -21
- data/lib/sequel/adapters/amalgalite.rb +17 -14
- data/lib/sequel/adapters/cubrid.rb +143 -0
- data/lib/sequel/adapters/db2.rb +178 -85
- data/lib/sequel/adapters/dbi.rb +19 -21
- data/lib/sequel/adapters/do/mysql.rb +17 -11
- data/lib/sequel/adapters/do/postgres.rb +2 -61
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +20 -39
- data/lib/sequel/adapters/firebird.rb +33 -210
- data/lib/sequel/adapters/ibmdb.rb +452 -0
- data/lib/sequel/adapters/informix.rb +6 -29
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
- data/lib/sequel/adapters/jdbc/db2.rb +78 -0
- data/lib/sequel/adapters/jdbc/derby.rb +325 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
- data/lib/sequel/adapters/jdbc/h2.rb +68 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +16 -28
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
- data/lib/sequel/adapters/jdbc/sqlserver.rb +72 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +308 -114
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +130 -230
- data/lib/sequel/adapters/mysql2.rb +64 -63
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- data/lib/sequel/adapters/odbc.rb +29 -10
- data/lib/sequel/adapters/openbase.rb +7 -11
- data/lib/sequel/adapters/oracle.rb +364 -57
- data/lib/sequel/adapters/postgres.rb +379 -145
- data/lib/sequel/adapters/shared/access.rb +236 -11
- data/lib/sequel/adapters/shared/cubrid.rb +216 -0
- data/lib/sequel/adapters/shared/db2.rb +353 -0
- data/lib/sequel/adapters/shared/firebird.rb +231 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +412 -116
- data/lib/sequel/adapters/shared/mysql.rb +396 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
- data/lib/sequel/adapters/shared/oracle.rb +262 -45
- data/lib/sequel/adapters/shared/postgres.rb +688 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +317 -96
- data/lib/sequel/adapters/sqlite.rb +109 -46
- data/lib/sequel/adapters/swift/mysql.rb +10 -12
- data/lib/sequel/adapters/swift/postgres.rb +13 -79
- data/lib/sequel/adapters/swift/sqlite.rb +9 -6
- data/lib/sequel/adapters/swift.rb +31 -27
- data/lib/sequel/adapters/tinytds.rb +177 -25
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
- data/lib/sequel/adapters/utils/pg_types.rb +81 -0
- data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +194 -0
- data/lib/sequel/connection_pool/sharded_single.rb +23 -13
- data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
- data/lib/sequel/connection_pool/single.rb +11 -3
- data/lib/sequel/connection_pool/threaded.rb +69 -20
- data/lib/sequel/connection_pool.rb +20 -18
- data/lib/sequel/core.rb +143 -11
- data/lib/sequel/database/connecting.rb +66 -29
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +63 -2
- data/lib/sequel/database/logging.rb +7 -2
- data/lib/sequel/database/misc.rb +192 -51
- data/lib/sequel/database/query.rb +299 -114
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +310 -69
- data/lib/sequel/dataset/actions.rb +344 -90
- data/lib/sequel/dataset/features.rb +101 -8
- data/lib/sequel/dataset/graph.rb +42 -15
- data/lib/sequel/dataset/misc.rb +52 -25
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +322 -68
- data/lib/sequel/dataset/sql.rb +716 -293
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +83 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
- data/lib/sequel/extensions/blank.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +72 -0
- data/lib/sequel/extensions/connection_validator.rb +109 -0
- data/lib/sequel/extensions/constraint_validations.rb +451 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
- data/lib/sequel/extensions/eval_inspect.rb +173 -0
- data/lib/sequel/extensions/inflector.rb +4 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -4
- data/lib/sequel/extensions/migration.rb +100 -11
- data/lib/sequel/extensions/named_timezones.rb +9 -0
- data/lib/sequel/extensions/null_dataset.rb +94 -0
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pg_array.rb +539 -0
- data/lib/sequel/extensions/pg_array_ops.rb +263 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
- data/lib/sequel/extensions/pg_hstore.rb +334 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
- data/lib/sequel/extensions/pg_inet.rb +113 -0
- data/lib/sequel/extensions/pg_interval.rb +191 -0
- data/lib/sequel/extensions/pg_json.rb +212 -0
- data/lib/sequel/extensions/pg_range.rb +513 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +576 -0
- data/lib/sequel/extensions/pg_row_ops.rb +182 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
- data/lib/sequel/extensions/pretty_table.rb +17 -72
- data/lib/sequel/extensions/query.rb +8 -4
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +282 -76
- data/lib/sequel/extensions/select_remove.rb +39 -0
- data/lib/sequel/extensions/server_block.rb +140 -0
- data/lib/sequel/extensions/split_array_nil.rb +65 -0
- data/lib/sequel/extensions/sql_expr.rb +8 -110
- data/lib/sequel/extensions/string_date_time.rb +4 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
- data/lib/sequel/extensions/to_dot.rb +99 -83
- data/lib/sequel/model/associations.rb +1263 -409
- data/lib/sequel/model/base.rb +624 -171
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +24 -2
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model.rb +30 -11
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +137 -16
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +44 -12
- data/lib/sequel/plugins/constraint_validations.rb +198 -0
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +72 -0
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +134 -15
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +77 -6
- data/lib/sequel/plugins/list.rb +13 -2
- data/lib/sequel/plugins/many_through_many.rb +103 -51
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +150 -66
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/pg_row.rb +121 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
- data/lib/sequel/plugins/prepared_statements.rb +168 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +31 -17
- data/lib/sequel/plugins/schema.rb +8 -3
- data/lib/sequel/plugins/serialization.rb +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +57 -12
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/subclasses.rb +29 -3
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/touch.rb +18 -6
- data/lib/sequel/plugins/tree.rb +3 -3
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +2 -1
- data/lib/sequel/plugins/validation_helpers.rb +57 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +721 -141
- data/lib/sequel/timezones.rb +67 -40
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/firebird_spec.rb +53 -39
- data/spec/adapters/mssql_spec.rb +261 -66
- data/spec/adapters/mysql_spec.rb +597 -352
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +2103 -341
- data/spec/adapters/spec_helper.rb +16 -6
- data/spec/adapters/sqlite_spec.rb +237 -76
- data/spec/core/connection_pool_spec.rb +270 -141
- data/spec/core/database_spec.rb +980 -524
- data/spec/core/dataset_spec.rb +1658 -1243
- data/spec/core/expression_filters_spec.rb +741 -334
- data/spec/core/mock_adapter_spec.rb +453 -0
- data/spec/core/object_graph_spec.rb +88 -129
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +556 -53
- data/spec/core/spec_helper.rb +7 -48
- data/spec/core_extensions_spec.rb +626 -0
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/arbitrary_servers_spec.rb +110 -0
- data/spec/extensions/association_autoreloading_spec.rb +18 -10
- data/spec/extensions/association_dependencies_spec.rb +15 -25
- data/spec/extensions/association_pks_spec.rb +227 -33
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/boolean_readers_spec.rb +25 -25
- data/spec/extensions/caching_spec.rb +47 -51
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/connection_validator_spec.rb +118 -0
- data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
- data/spec/extensions/constraint_validations_spec.rb +316 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +88 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/eval_inspect_spec.rb +67 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +97 -128
- data/spec/extensions/identity_map_spec.rb +142 -24
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +72 -0
- data/spec/extensions/json_serializer_spec.rb +49 -12
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +49 -40
- data/spec/extensions/looser_typecasting_spec.rb +7 -7
- data/spec/extensions/many_through_many_spec.rb +368 -254
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +140 -35
- data/spec/extensions/named_timezones_spec.rb +29 -11
- data/spec/extensions/nested_attributes_spec.rb +268 -89
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +24 -21
- data/spec/extensions/pg_array_ops_spec.rb +112 -0
- data/spec/extensions/pg_array_spec.rb +324 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
- data/spec/extensions/pg_hstore_spec.rb +200 -0
- data/spec/extensions/pg_inet_spec.rb +47 -0
- data/spec/extensions/pg_interval_spec.rb +72 -0
- data/spec/extensions/pg_json_spec.rb +99 -0
- data/spec/extensions/pg_range_ops_spec.rb +56 -0
- data/spec/extensions/pg_range_spec.rb +395 -0
- data/spec/extensions/pg_row_ops_spec.rb +58 -0
- data/spec/extensions/pg_row_plugin_spec.rb +49 -0
- data/spec/extensions/pg_row_spec.rb +340 -0
- data/spec/extensions/pg_statement_cache_spec.rb +208 -0
- data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +87 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +7 -6
- data/spec/extensions/query_literals_spec.rb +167 -0
- data/spec/extensions/query_spec.rb +9 -3
- data/spec/extensions/rcte_tree_spec.rb +50 -43
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +434 -49
- data/spec/extensions/schema_spec.rb +30 -49
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +86 -48
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/sharding_spec.rb +69 -143
- data/spec/extensions/single_table_inheritance_spec.rb +65 -29
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +34 -67
- data/spec/extensions/split_array_nil_spec.rb +24 -0
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/subclasses_spec.rb +14 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +8 -11
- data/spec/extensions/touch_spec.rb +53 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +9 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +77 -68
- data/spec/extensions/validation_helpers_spec.rb +48 -37
- data/spec/extensions/xml_serializer_spec.rb +29 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
- data/spec/integration/associations_test.rb +659 -21
- data/spec/integration/database_test.rb +25 -5
- data/spec/integration/dataset_test.rb +729 -141
- data/spec/integration/eager_loader_test.rb +38 -54
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +871 -160
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +377 -57
- data/spec/integration/spec_helper.rb +49 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +40 -8
- data/spec/model/association_reflection_spec.rb +207 -8
- data/spec/model/associations_spec.rb +1206 -693
- data/spec/model/base_spec.rb +334 -100
- data/spec/model/dataset_methods_spec.rb +45 -27
- data/spec/model/eager_loading_spec.rb +828 -692
- data/spec/model/hooks_spec.rb +355 -67
- data/spec/model/model_spec.rb +251 -179
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +506 -232
- data/spec/model/spec_helper.rb +16 -77
- data/spec/model/validations_spec.rb +38 -2
- data/spec/rcov.opts +2 -0
- metadata +223 -81
- data/spec/core/core_sql_spec.rb +0 -451
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
Sequel.require %w'emulate_offset_with_row_number split_alter_table', 'adapters/utils/'
|
|
2
|
+
|
|
1
3
|
module Sequel
|
|
2
4
|
Dataset::NON_SQL_OPTIONS << :disable_insert_output
|
|
3
5
|
module MSSQL
|
|
@@ -10,16 +12,92 @@ module Sequel
|
|
|
10
12
|
SQL_ROLLBACK = "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION".freeze
|
|
11
13
|
SQL_ROLLBACK_TO_SAVEPOINT = 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION autopoint_%d'.freeze
|
|
12
14
|
SQL_SAVEPOINT = 'SAVE TRANSACTION autopoint_%d'.freeze
|
|
15
|
+
MSSQL_DEFAULT_RE = /\A(?:\(N?('.*')\)|\(\((-?\d+(?:\.\d+)?)\)\))\z/
|
|
16
|
+
FOREIGN_KEY_ACTION_MAP = {0 => :no_action, 1 => :cascade, 2 => :set_null, 3 => :set_default}.freeze
|
|
17
|
+
|
|
18
|
+
include Sequel::Database::SplitAlterTable
|
|
13
19
|
|
|
20
|
+
# Whether to use N'' to quote strings, which allows unicode characters inside the
|
|
21
|
+
# strings. True by default for compatibility, can be set to false for a possible
|
|
22
|
+
# performance increase. This sets the default for all datasets created from this
|
|
23
|
+
# Database object.
|
|
24
|
+
attr_accessor :mssql_unicode_strings
|
|
25
|
+
|
|
14
26
|
# The types to check for 0 scale to transform :decimal types
|
|
15
27
|
# to :integer.
|
|
16
28
|
DECIMAL_TYPE_RE = /number|numeric|decimal/io
|
|
17
|
-
|
|
29
|
+
|
|
18
30
|
# Microsoft SQL Server uses the :mssql type.
|
|
19
31
|
def database_type
|
|
20
32
|
:mssql
|
|
21
33
|
end
|
|
22
34
|
|
|
35
|
+
# Microsoft SQL Server namespaces indexes per table.
|
|
36
|
+
def global_index_namespace?
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Return foreign key information using the system views, including
|
|
41
|
+
# :name, :on_delete, and :on_update entries in the hashes.
|
|
42
|
+
def foreign_key_list(table, opts={})
|
|
43
|
+
m = output_identifier_meth
|
|
44
|
+
im = input_identifier_meth
|
|
45
|
+
schema, table = schema_and_table(table)
|
|
46
|
+
current_schema = m.call(get(Sequel.function('schema_name')))
|
|
47
|
+
fk_action_map = FOREIGN_KEY_ACTION_MAP
|
|
48
|
+
ds = metadata_dataset.from(:sys__foreign_keys___fk).
|
|
49
|
+
join(:sys__foreign_key_columns___fkc, :constraint_object_id => :object_id).
|
|
50
|
+
join(:sys__all_columns___pc, :object_id => :fkc__parent_object_id, :column_id => :fkc__parent_column_id).
|
|
51
|
+
join(:sys__all_columns___rc, :object_id => :fkc__referenced_object_id, :column_id => :fkc__referenced_column_id).
|
|
52
|
+
where{{object_schema_name(:fk__parent_object_id) => im.call(schema || current_schema)}}.
|
|
53
|
+
where{{object_name(:fk__parent_object_id) => im.call(table)}}.
|
|
54
|
+
select{[:fk__name,
|
|
55
|
+
:fk__delete_referential_action,
|
|
56
|
+
:fk__update_referential_action,
|
|
57
|
+
:pc__name___column,
|
|
58
|
+
:rc__name___referenced_column,
|
|
59
|
+
object_schema_name(:fk__referenced_object_id).as(:schema),
|
|
60
|
+
object_name(:fk__referenced_object_id).as(:table)]}.
|
|
61
|
+
order(:name, :fkc__constraint_column_id)
|
|
62
|
+
h = {}
|
|
63
|
+
ds.each do |row|
|
|
64
|
+
if r = h[row[:name]]
|
|
65
|
+
r[:columns] << m.call(row[:column])
|
|
66
|
+
r[:key] << m.call(row[:referenced_column])
|
|
67
|
+
else
|
|
68
|
+
referenced_schema = m.call(row[:schema])
|
|
69
|
+
referenced_table = m.call(row[:table])
|
|
70
|
+
h[row[:name]] = { :name => m.call(row[:name]),
|
|
71
|
+
:table => (referenced_schema == current_schema) ? referenced_table : :"#{referenced_schema}__#{referenced_table}",
|
|
72
|
+
:columns => [m.call(row[:column])],
|
|
73
|
+
:key => [m.call(row[:referenced_column])],
|
|
74
|
+
:on_update => fk_action_map[row[:update_referential_action]],
|
|
75
|
+
:on_delete => fk_action_map[row[:delete_referential_action]] }
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
h.values
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Use the system tables to get index information
|
|
82
|
+
def indexes(table, opts={})
|
|
83
|
+
m = output_identifier_meth
|
|
84
|
+
im = input_identifier_meth
|
|
85
|
+
indexes = {}
|
|
86
|
+
metadata_dataset.from(:sys__tables___t).
|
|
87
|
+
join(:sys__indexes___i, :object_id=>:object_id).
|
|
88
|
+
join(:sys__index_columns___ic, :object_id=>:object_id, :index_id=>:index_id).
|
|
89
|
+
join(:sys__columns___c, :object_id=>:object_id, :column_id=>:column_id).
|
|
90
|
+
select(:i__name, :i__is_unique, :c__name___column).
|
|
91
|
+
where{{t__name=>im.call(table)}}.
|
|
92
|
+
where(:i__is_primary_key=>0, :i__is_disabled=>0).
|
|
93
|
+
order(:i__name, :ic__index_column_id).
|
|
94
|
+
each do |r|
|
|
95
|
+
index = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>(r[:is_unique] && r[:is_unique]!=0)}
|
|
96
|
+
index[:columns] << m.call(r[:column])
|
|
97
|
+
end
|
|
98
|
+
indexes
|
|
99
|
+
end
|
|
100
|
+
|
|
23
101
|
# The version of the MSSQL server, as an integer (e.g. 10001600 for
|
|
24
102
|
# SQL Server 2008 Express).
|
|
25
103
|
def server_version(server=nil)
|
|
@@ -44,18 +122,33 @@ module Sequel
|
|
|
44
122
|
true
|
|
45
123
|
end
|
|
46
124
|
|
|
125
|
+
# MSSQL supports transaction DDL statements.
|
|
126
|
+
def supports_transactional_ddl?
|
|
127
|
+
true
|
|
128
|
+
end
|
|
129
|
+
|
|
47
130
|
# Microsoft SQL Server supports using the INFORMATION_SCHEMA to get
|
|
48
131
|
# information on tables.
|
|
49
132
|
def tables(opts={})
|
|
50
|
-
|
|
51
|
-
metadata_dataset.from(:information_schema__tables___t).
|
|
52
|
-
select(:table_name).
|
|
53
|
-
filter(:table_type=>'BASE TABLE', :table_schema=>(opts[:schema]||default_schema||'dbo').to_s).
|
|
54
|
-
map{|x| m.call(x[:table_name])}
|
|
133
|
+
information_schema_tables('BASE TABLE', opts)
|
|
55
134
|
end
|
|
56
135
|
|
|
136
|
+
# Microsoft SQL Server supports using the INFORMATION_SCHEMA to get
|
|
137
|
+
# information on views.
|
|
138
|
+
def views(opts={})
|
|
139
|
+
information_schema_tables('VIEW', opts)
|
|
140
|
+
end
|
|
141
|
+
|
|
57
142
|
private
|
|
58
143
|
|
|
144
|
+
# Add dropping of the default constraint to the list of SQL queries.
|
|
145
|
+
# This is necessary before dropping the column or changing its type.
|
|
146
|
+
def add_drop_default_constraint_sql(sqls, table, column)
|
|
147
|
+
if constraint = default_constraint_name(table, column)
|
|
148
|
+
sqls << "ALTER TABLE #{quote_schema_table(table)} DROP CONSTRAINT #{constraint}"
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
59
152
|
# MSSQL uses the IDENTITY(1,1) column for autoincrementing columns.
|
|
60
153
|
def auto_increment_sql
|
|
61
154
|
AUTO_INCREMENT
|
|
@@ -66,10 +159,26 @@ module Sequel
|
|
|
66
159
|
case op[:op]
|
|
67
160
|
when :add_column
|
|
68
161
|
"ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}"
|
|
162
|
+
when :drop_column
|
|
163
|
+
sqls = []
|
|
164
|
+
add_drop_default_constraint_sql(sqls, table, op[:name])
|
|
165
|
+
sqls << super
|
|
69
166
|
when :rename_column
|
|
70
167
|
"sp_rename #{literal("#{quote_schema_table(table)}.#{quote_identifier(op[:name])}")}, #{literal(op[:new_name].to_s)}, 'COLUMN'"
|
|
71
168
|
when :set_column_type
|
|
72
|
-
|
|
169
|
+
sqls = []
|
|
170
|
+
if sch = schema(table)
|
|
171
|
+
if cs = sch.each{|k, v| break v if k == op[:name]; nil}
|
|
172
|
+
cs = cs.dup
|
|
173
|
+
add_drop_default_constraint_sql(sqls, table, op[:name])
|
|
174
|
+
cs[:default] = cs[:ruby_default]
|
|
175
|
+
op = cs.merge!(op)
|
|
176
|
+
default = op.delete(:default)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
sqls << "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{column_definition_sql(op)}"
|
|
180
|
+
sqls << alter_table_sql(table, op.merge(:op=>:set_column_default, :default=>default)) if default
|
|
181
|
+
sqls
|
|
73
182
|
when :set_column_null
|
|
74
183
|
sch = schema(table).find{|k,v| k.to_s == op[:name].to_s}.last
|
|
75
184
|
type = sch[:db_type]
|
|
@@ -94,10 +203,18 @@ module Sequel
|
|
|
94
203
|
SQL_BEGIN
|
|
95
204
|
end
|
|
96
205
|
|
|
206
|
+
# Handle MSSQL specific default format.
|
|
207
|
+
def column_schema_normalize_default(default, type)
|
|
208
|
+
if m = MSSQL_DEFAULT_RE.match(default)
|
|
209
|
+
default = m[1] || m[2]
|
|
210
|
+
end
|
|
211
|
+
super(default, type)
|
|
212
|
+
end
|
|
213
|
+
|
|
97
214
|
# Commit the active transaction on the connection, does not commit/release
|
|
98
215
|
# savepoints.
|
|
99
216
|
def commit_transaction(conn, opts={})
|
|
100
|
-
log_connection_execute(conn, commit_transaction_sql) unless
|
|
217
|
+
log_connection_execute(conn, commit_transaction_sql) unless _trans(conn)[:savepoint_level] > 1
|
|
101
218
|
end
|
|
102
219
|
|
|
103
220
|
# SQL to COMMIT a transaction.
|
|
@@ -108,15 +225,55 @@ module Sequel
|
|
|
108
225
|
# MSSQL uses the name of the table to decide the difference between
|
|
109
226
|
# a regular and temporary table, with temporary table names starting with
|
|
110
227
|
# a #.
|
|
111
|
-
def
|
|
112
|
-
"CREATE TABLE #{quote_schema_table(options[:temp] ? "##{name}" : name)}
|
|
228
|
+
def create_table_prefix_sql(name, options)
|
|
229
|
+
"CREATE TABLE #{quote_schema_table(options[:temp] ? "##{name}" : name)}"
|
|
113
230
|
end
|
|
114
231
|
|
|
232
|
+
# MSSQL doesn't support CREATE TABLE AS, it only supports SELECT INTO.
|
|
233
|
+
# Emulating CREATE TABLE AS using SELECT INTO is only possible if a dataset
|
|
234
|
+
# is given as the argument, it can't work with a string, so raise an
|
|
235
|
+
# Error if a string is given.
|
|
236
|
+
def create_table_as(name, ds, options)
|
|
237
|
+
raise(Error, "must provide dataset instance as value of create_table :as option on MSSQL") unless ds.is_a?(Sequel::Dataset)
|
|
238
|
+
run(ds.into(name).sql)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# The name of the constraint for setting the default value on the table and column.
|
|
242
|
+
# The SQL used to select default constraints utilizes MSSQL catalog views which were introduced in 2005.
|
|
243
|
+
# This method intentionally does not support MSSQL 2000.
|
|
244
|
+
def default_constraint_name(table, column_name)
|
|
245
|
+
if server_version >= 9000000
|
|
246
|
+
table_name = schema_and_table(table).compact.join('.')
|
|
247
|
+
self[:sys__default_constraints].
|
|
248
|
+
where{{:parent_object_id => Sequel::SQL::Function.new(:object_id, table_name), col_name(:parent_object_id, :parent_column_id) => column_name.to_s}}.
|
|
249
|
+
get(:name)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
115
253
|
# The SQL to drop an index for the table.
|
|
116
254
|
def drop_index_sql(table, op)
|
|
117
255
|
"DROP INDEX #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))} ON #{quote_schema_table(table)}"
|
|
118
256
|
end
|
|
119
257
|
|
|
258
|
+
# support for clustered index type
|
|
259
|
+
def index_definition_sql(table_name, index)
|
|
260
|
+
index_name = index[:name] || default_index_name(table_name, index[:columns])
|
|
261
|
+
if index[:type] == :full_text
|
|
262
|
+
"CREATE FULLTEXT INDEX ON #{quote_schema_table(table_name)} #{literal(index[:columns])} KEY INDEX #{literal(index[:key_index])}"
|
|
263
|
+
else
|
|
264
|
+
"CREATE #{'UNIQUE ' if index[:unique]}#{'CLUSTERED ' if index[:type] == :clustered}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}#{" INCLUDE #{literal(index[:include])}" if index[:include]}#{" WHERE #{filter_expr(index[:where])}" if index[:where]}"
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Backbone of the tables and views support.
|
|
269
|
+
def information_schema_tables(type, opts)
|
|
270
|
+
m = output_identifier_meth
|
|
271
|
+
metadata_dataset.from(:information_schema__tables___t).
|
|
272
|
+
select(:table_name).
|
|
273
|
+
filter(:table_type=>type, :table_schema=>(opts[:schema]||default_schema||'dbo').to_s).
|
|
274
|
+
map{|x| m.call(x[:table_name])}
|
|
275
|
+
end
|
|
276
|
+
|
|
120
277
|
# Always quote identifiers in the metadata_dataset, so schema parsing works.
|
|
121
278
|
def metadata_dataset
|
|
122
279
|
ds = super
|
|
@@ -139,20 +296,41 @@ module Sequel
|
|
|
139
296
|
SQL_ROLLBACK
|
|
140
297
|
end
|
|
141
298
|
|
|
142
|
-
# MSSQL
|
|
143
|
-
|
|
299
|
+
# The closest MSSQL equivalent of a boolean datatype is the bit type.
|
|
300
|
+
def schema_column_type(db_type)
|
|
301
|
+
case db_type
|
|
302
|
+
when /\A(bit)\z/io
|
|
303
|
+
:boolean
|
|
304
|
+
else
|
|
305
|
+
super
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# MSSQL uses the INFORMATION_SCHEMA to hold column information, and
|
|
310
|
+
# parses primary key information from the sysindexes, sysindexkeys,
|
|
311
|
+
# and syscolumns system tables.
|
|
144
312
|
def schema_parse_table(table_name, opts)
|
|
145
|
-
m = output_identifier_meth
|
|
146
|
-
m2 = input_identifier_meth
|
|
313
|
+
m = output_identifier_meth(opts[:dataset])
|
|
314
|
+
m2 = input_identifier_meth(opts[:dataset])
|
|
315
|
+
tn = m2.call(table_name.to_s)
|
|
316
|
+
table_id = get{object_id(tn)}
|
|
317
|
+
pk_index_id = metadata_dataset.from(:sysindexes).
|
|
318
|
+
where(:id=>table_id, :indid=>1..254){{(status & 2048)=>2048}}.
|
|
319
|
+
get(:indid)
|
|
320
|
+
pk_cols = metadata_dataset.from(:sysindexkeys___sik).
|
|
321
|
+
join(:syscolumns___sc, :id=>:id, :colid=>:colid).
|
|
322
|
+
where(:sik__id=>table_id, :sik__indid=>pk_index_id).
|
|
323
|
+
select_order_map(:sc__name)
|
|
147
324
|
ds = metadata_dataset.from(:information_schema__tables___t).
|
|
148
325
|
join(:information_schema__columns___c, :table_catalog=>:table_catalog,
|
|
149
326
|
:table_schema => :table_schema, :table_name => :table_name).
|
|
150
327
|
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).
|
|
151
|
-
filter(:c__table_name=>
|
|
328
|
+
filter(:c__table_name=>tn)
|
|
152
329
|
if schema = opts[:schema] || default_schema
|
|
153
330
|
ds.filter!(:c__table_schema=>schema)
|
|
154
331
|
end
|
|
155
332
|
ds.map do |row|
|
|
333
|
+
row[:primary_key] = pk_cols.include?(row[:column])
|
|
156
334
|
row[:allow_null] = row[:allow_null] == 'YES' ? true : false
|
|
157
335
|
row[:default] = nil if blank_object?(row[:default])
|
|
158
336
|
row[:type] = if row[:db_type] =~ DECIMAL_TYPE_RE && row[:scale] == 0
|
|
@@ -163,6 +341,11 @@ module Sequel
|
|
|
163
341
|
[m.call(row.delete(:column)), row]
|
|
164
342
|
end
|
|
165
343
|
end
|
|
344
|
+
|
|
345
|
+
# Set the mssql_unicode_strings settings from the given options.
|
|
346
|
+
def set_mssql_unicode_strings
|
|
347
|
+
@mssql_unicode_strings = typecast_value_boolean(@opts.fetch(:mssql_unicode_strings, true))
|
|
348
|
+
end
|
|
166
349
|
|
|
167
350
|
# MSSQL has both datetime and timestamp classes, most people are going
|
|
168
351
|
# to want datetime
|
|
@@ -185,53 +368,100 @@ module Sequel
|
|
|
185
368
|
def type_literal_generic_file(column)
|
|
186
369
|
:'varbinary(max)'
|
|
187
370
|
end
|
|
188
|
-
|
|
189
|
-
# support for clustered index type
|
|
190
|
-
def index_definition_sql(table_name, index)
|
|
191
|
-
index_name = index[:name] || default_index_name(table_name, index[:columns])
|
|
192
|
-
clustered = index[:type] == :clustered
|
|
193
|
-
if index[:where]
|
|
194
|
-
raise Error, "Partial indexes are not supported for this database"
|
|
195
|
-
else
|
|
196
|
-
"CREATE #{'UNIQUE ' if index[:unique]}#{'CLUSTERED ' if clustered}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}"
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
371
|
end
|
|
200
372
|
|
|
201
373
|
module DatasetMethods
|
|
374
|
+
include EmulateOffsetWithRowNumber
|
|
375
|
+
|
|
202
376
|
BOOL_TRUE = '1'.freeze
|
|
203
377
|
BOOL_FALSE = '0'.freeze
|
|
204
378
|
COMMA_SEPARATOR = ', '.freeze
|
|
205
|
-
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'with from output from2 where')
|
|
206
|
-
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'with into columns output values')
|
|
207
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with distinct limit columns into from lock join where group having order compounds')
|
|
208
|
-
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'with table set output from where')
|
|
379
|
+
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'with delete from output from2 where')
|
|
380
|
+
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'with insert into columns output values')
|
|
381
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct limit columns into from lock join where group having order compounds')
|
|
382
|
+
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'with update limit table set output from where')
|
|
209
383
|
NOLOCK = ' WITH (NOLOCK)'.freeze
|
|
210
384
|
UPDLOCK = ' WITH (UPDLOCK)'.freeze
|
|
211
385
|
WILDCARD = LiteralString.new('*').freeze
|
|
212
386
|
CONSTANT_MAP = {:CURRENT_DATE=>'CAST(CURRENT_TIMESTAMP AS DATE)'.freeze, :CURRENT_TIME=>'CAST(CURRENT_TIMESTAMP AS TIME)'.freeze}
|
|
387
|
+
EXTRACT_MAP = {:year=>"yy", :month=>"m", :day=>"d", :hour=>"hh", :minute=>"n", :second=>"s"}
|
|
388
|
+
BRACKET_CLOSE = Dataset::BRACKET_CLOSE
|
|
389
|
+
BRACKET_OPEN = Dataset::BRACKET_OPEN
|
|
390
|
+
COMMA = Dataset::COMMA
|
|
391
|
+
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
392
|
+
PAREN_SPACE_OPEN = Dataset::PAREN_SPACE_OPEN
|
|
393
|
+
SPACE = Dataset::SPACE
|
|
394
|
+
FROM = Dataset::FROM
|
|
395
|
+
APOS = Dataset::APOS
|
|
396
|
+
APOS_RE = Dataset::APOS_RE
|
|
397
|
+
DOUBLE_APOS = Dataset::DOUBLE_APOS
|
|
398
|
+
INTO = Dataset::INTO
|
|
399
|
+
DATEPART_SECOND_OPEN = "CAST((datepart(".freeze
|
|
400
|
+
DATEPART_SECOND_MIDDLE = ') + datepart(ns, '.freeze
|
|
401
|
+
DATEPART_SECOND_CLOSE = ")/1000000000.0) AS double precision)".freeze
|
|
402
|
+
DATEPART_OPEN = "datepart(".freeze
|
|
403
|
+
UNION_ALL = ' UNION ALL '.freeze
|
|
404
|
+
SELECT_SPACE = 'SELECT '.freeze
|
|
405
|
+
TIMESTAMP_USEC_FORMAT = ".%03d".freeze
|
|
406
|
+
OUTPUT_INSERTED = " OUTPUT INSERTED.*".freeze
|
|
407
|
+
HEX_START = '0x'.freeze
|
|
408
|
+
UNICODE_STRING_START = "N'".freeze
|
|
409
|
+
BACKSLASH_CRLF_RE = /\\((?:\r\n)|\n)/.freeze
|
|
410
|
+
BACKSLASH_CRLF_REPLACE = '\\\\\\\\\\1\\1'.freeze
|
|
411
|
+
TOP_PAREN = " TOP (".freeze
|
|
412
|
+
TOP = " TOP ".freeze
|
|
413
|
+
OUTPUT = " OUTPUT ".freeze
|
|
414
|
+
HSTAR = "H*".freeze
|
|
415
|
+
CASE_SENSITIVE_COLLATION = 'Latin1_General_CS_AS'.freeze
|
|
416
|
+
CASE_INSENSITIVE_COLLATION = 'Latin1_General_CI_AS'.freeze
|
|
417
|
+
DEFAULT_TIMESTAMP_FORMAT = "'%Y-%m-%dT%H:%M:%S%N%z'".freeze
|
|
418
|
+
FORMAT_DATE = "'%Y%m%d'".freeze
|
|
419
|
+
|
|
420
|
+
# Allow overriding of the mssql_unicode_strings option at the dataset level.
|
|
421
|
+
attr_accessor :mssql_unicode_strings
|
|
422
|
+
|
|
423
|
+
# Copy the mssql_unicode_strings option from the +db+ object.
|
|
424
|
+
def initialize(db, opts={})
|
|
425
|
+
super
|
|
426
|
+
@mssql_unicode_strings = db.mssql_unicode_strings
|
|
427
|
+
end
|
|
213
428
|
|
|
214
429
|
# MSSQL uses + for string concatenation, and LIKE is case insensitive by default.
|
|
215
|
-
def
|
|
430
|
+
def complex_expression_sql_append(sql, op, args)
|
|
216
431
|
case op
|
|
217
432
|
when :'||'
|
|
218
|
-
super(:+, args)
|
|
219
|
-
when :
|
|
220
|
-
super(
|
|
221
|
-
when :"NOT ILIKE"
|
|
222
|
-
super(:"NOT LIKE", args)
|
|
433
|
+
super(sql, :+, args)
|
|
434
|
+
when :LIKE, :"NOT LIKE"
|
|
435
|
+
super(sql, op, args.map{|a| LiteralString.new("(#{literal(a)} COLLATE #{CASE_SENSITIVE_COLLATION})")})
|
|
436
|
+
when :ILIKE, :"NOT ILIKE"
|
|
437
|
+
super(sql, (op == :ILIKE ? :LIKE : :"NOT LIKE"), args.map{|a| LiteralString.new("(#{literal(a)} COLLATE #{CASE_INSENSITIVE_COLLATION})")})
|
|
223
438
|
when :<<
|
|
224
|
-
"(#{literal(
|
|
439
|
+
sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * POWER(2, #{literal(b)}))"}
|
|
225
440
|
when :>>
|
|
226
|
-
"(#{literal(
|
|
441
|
+
sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / POWER(2, #{literal(b)}))"}
|
|
442
|
+
when :extract
|
|
443
|
+
part = args.at(0)
|
|
444
|
+
raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part]
|
|
445
|
+
if part == :second
|
|
446
|
+
expr = literal(args.at(1))
|
|
447
|
+
sql << DATEPART_SECOND_OPEN << format.to_s << COMMA << expr << DATEPART_SECOND_MIDDLE << expr << DATEPART_SECOND_CLOSE
|
|
448
|
+
else
|
|
449
|
+
sql << DATEPART_OPEN << format.to_s << COMMA
|
|
450
|
+
literal_append(sql, args.at(1))
|
|
451
|
+
sql << PAREN_CLOSE
|
|
452
|
+
end
|
|
227
453
|
else
|
|
228
|
-
super
|
|
454
|
+
super
|
|
229
455
|
end
|
|
230
456
|
end
|
|
231
457
|
|
|
232
458
|
# MSSQL doesn't support the SQL standard CURRENT_DATE or CURRENT_TIME
|
|
233
|
-
def
|
|
234
|
-
CONSTANT_MAP[constant]
|
|
459
|
+
def constant_sql_append(sql, constant)
|
|
460
|
+
if c = CONSTANT_MAP[constant]
|
|
461
|
+
sql << c
|
|
462
|
+
else
|
|
463
|
+
super
|
|
464
|
+
end
|
|
235
465
|
end
|
|
236
466
|
|
|
237
467
|
# Disable the use of INSERT OUTPUT
|
|
@@ -244,21 +474,31 @@ module Sequel
|
|
|
244
474
|
mutation_method(:disable_insert_output)
|
|
245
475
|
end
|
|
246
476
|
|
|
247
|
-
#
|
|
248
|
-
#
|
|
249
|
-
|
|
250
|
-
|
|
477
|
+
# There is no function on Microsoft SQL Server that does character length
|
|
478
|
+
# and respects trailing spaces (datalength respects trailing spaces, but
|
|
479
|
+
# counts bytes instead of characters). Use a hack to work around the
|
|
480
|
+
# trailing spaces issue.
|
|
481
|
+
def emulated_function_sql_append(sql, f)
|
|
482
|
+
case f.f
|
|
483
|
+
when :char_length
|
|
484
|
+
literal_append(sql, SQL::Function.new(:len, Sequel.join([f.args.first, 'x'])) - 1)
|
|
485
|
+
when :trim
|
|
486
|
+
literal_append(sql, SQL::Function.new(:ltrim, SQL::Function.new(:rtrim, f.args.first)))
|
|
487
|
+
else
|
|
488
|
+
super
|
|
489
|
+
end
|
|
251
490
|
end
|
|
252
491
|
|
|
253
492
|
# MSSQL uses the CONTAINS keyword for full text search
|
|
254
493
|
def full_text_search(cols, terms, opts = {})
|
|
255
|
-
|
|
494
|
+
terms = "\"#{terms.join('" OR "')}\"" if terms.is_a?(Array)
|
|
495
|
+
filter("CONTAINS (?, ?)", cols, terms)
|
|
256
496
|
end
|
|
257
497
|
|
|
258
498
|
# Use the OUTPUT clause to get the value of all columns for the newly inserted record.
|
|
259
499
|
def insert_select(*values)
|
|
260
|
-
return unless
|
|
261
|
-
naked.clone(default_server_opts(:sql=>output(nil, [:inserted
|
|
500
|
+
return unless supports_insert_select?
|
|
501
|
+
naked.clone(default_server_opts(:sql=>output(nil, [SQL::ColumnAll.new(:inserted)]).insert_sql(*values))).single_record
|
|
262
502
|
end
|
|
263
503
|
|
|
264
504
|
# Specify a table for a SELECT ... INTO query.
|
|
@@ -266,17 +506,18 @@ module Sequel
|
|
|
266
506
|
clone(:into => table)
|
|
267
507
|
end
|
|
268
508
|
|
|
269
|
-
# SQL Server does not support CTEs on subqueries, so move any CTEs
|
|
270
|
-
# on joined datasets to the top level. The user is responsible for
|
|
271
|
-
# resolving any name clashes this may cause.
|
|
272
|
-
def join_table(type, table, expr=nil, table_alias={}, &block)
|
|
273
|
-
return super unless Dataset === table && table.opts[:with]
|
|
274
|
-
clone(:with => (opts[:with] || []) + table.opts[:with]).join_table(type, table.clone(:with => nil), expr, table_alias, &block)
|
|
275
|
-
end
|
|
276
|
-
|
|
277
509
|
# MSSQL uses a UNION ALL statement to insert multiple values at once.
|
|
278
510
|
def multi_insert_sql(columns, values)
|
|
279
|
-
|
|
511
|
+
c = false
|
|
512
|
+
sql = LiteralString.new('')
|
|
513
|
+
u = UNION_ALL
|
|
514
|
+
values.each do |v|
|
|
515
|
+
sql << u if c
|
|
516
|
+
sql << SELECT_SPACE
|
|
517
|
+
expression_list_append(sql, v)
|
|
518
|
+
c ||= true
|
|
519
|
+
end
|
|
520
|
+
[insert_sql(columns, sql)]
|
|
280
521
|
end
|
|
281
522
|
|
|
282
523
|
# Allows you to do a dirty read of uncommitted data using WITH (NOLOCK).
|
|
@@ -314,39 +555,32 @@ module Sequel
|
|
|
314
555
|
mutation_method(:output, into, values)
|
|
315
556
|
end
|
|
316
557
|
|
|
317
|
-
# MSSQL uses [] to quote identifiers
|
|
318
|
-
|
|
319
|
-
|
|
558
|
+
# MSSQL uses [] to quote identifiers. MSSQL does not support
|
|
559
|
+
# escaping of ], so you cannot use that character in an identifier.
|
|
560
|
+
def quoted_identifier_append(sql, name)
|
|
561
|
+
sql << BRACKET_OPEN << name.to_s << BRACKET_CLOSE
|
|
320
562
|
end
|
|
321
563
|
|
|
322
|
-
# MSSQL Requires the use of the ROW_NUMBER window function to emulate
|
|
323
|
-
# an offset. This implementation requires MSSQL 2005 or greater (offset
|
|
324
|
-
# can't be emulated well in MSSQL 2000).
|
|
325
|
-
#
|
|
326
|
-
# The implementation is ugly, cloning the current dataset and modifying
|
|
327
|
-
# the clone to add a ROW_NUMBER window function (and some other things),
|
|
328
|
-
# then using the modified clone in a subselect which is selected from.
|
|
329
|
-
#
|
|
330
|
-
# If offset is used, an order must be provided, because the use of ROW_NUMBER
|
|
331
|
-
# requires an order.
|
|
332
|
-
def select_sql
|
|
333
|
-
return super unless o = @opts[:offset]
|
|
334
|
-
raise(Error, 'MSSQL requires an order be provided if using an offset') unless order = @opts[:order]
|
|
335
|
-
dsa1 = dataset_alias(1)
|
|
336
|
-
rn = row_number_column
|
|
337
|
-
subselect_sql(unlimited.
|
|
338
|
-
unordered.
|
|
339
|
-
select_append{ROW_NUMBER(:over, :order=>order){}.as(rn)}.
|
|
340
|
-
from_self(:alias=>dsa1).
|
|
341
|
-
limit(@opts[:limit]).
|
|
342
|
-
where(SQL::Identifier.new(rn) > o))
|
|
343
|
-
end
|
|
344
|
-
|
|
345
564
|
# The version of the database server.
|
|
346
565
|
def server_version
|
|
347
566
|
db.server_version(@opts[:server])
|
|
348
567
|
end
|
|
349
568
|
|
|
569
|
+
# MSSQL 2005+ supports GROUP BY CUBE.
|
|
570
|
+
def supports_group_cube?
|
|
571
|
+
is_2005_or_later?
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
# MSSQL 2005+ supports GROUP BY ROLLUP
|
|
575
|
+
def supports_group_rollup?
|
|
576
|
+
is_2005_or_later?
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
# MSSQL supports insert_select via the OUTPUT clause.
|
|
580
|
+
def supports_insert_select?
|
|
581
|
+
supports_output_clause? && !opts[:disable_insert_output]
|
|
582
|
+
end
|
|
583
|
+
|
|
350
584
|
# MSSQL 2005+ supports INTERSECT and EXCEPT
|
|
351
585
|
def supports_intersect_except?
|
|
352
586
|
is_2005_or_later?
|
|
@@ -381,18 +615,55 @@ module Sequel
|
|
|
381
615
|
def supports_window_functions?
|
|
382
616
|
true
|
|
383
617
|
end
|
|
618
|
+
|
|
619
|
+
# MSSQL cannot use WHERE 1.
|
|
620
|
+
def supports_where_true?
|
|
621
|
+
false
|
|
622
|
+
end
|
|
384
623
|
|
|
385
624
|
protected
|
|
625
|
+
|
|
626
|
+
# If returned primary keys are requested, use OUTPUT unless already set on the
|
|
627
|
+
# dataset. If OUTPUT is already set, use existing returning values. If OUTPUT
|
|
628
|
+
# is only set to return a single columns, return an array of just that column.
|
|
629
|
+
# Otherwise, return an array of hashes.
|
|
630
|
+
def _import(columns, values, opts={})
|
|
631
|
+
if opts[:return] == :primary_key && !@opts[:output]
|
|
632
|
+
output(nil, [SQL::QualifiedIdentifier.new(:inserted, first_primary_key)])._import(columns, values, opts)
|
|
633
|
+
elsif @opts[:output]
|
|
634
|
+
statements = multi_insert_sql(columns, values)
|
|
635
|
+
@db.transaction(opts.merge(:server=>@opts[:server])) do
|
|
636
|
+
statements.map{|st| with_sql(st)}
|
|
637
|
+
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
|
638
|
+
else
|
|
639
|
+
super
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
386
643
|
# MSSQL does not allow ordering in sub-clauses unless 'top' (limit) is specified
|
|
387
644
|
def aggregate_dataset
|
|
388
645
|
(options_overlap(Sequel::Dataset::COUNT_FROM_SELF_OPTS) && !options_overlap([:limit])) ? unordered.from_self : super
|
|
389
646
|
end
|
|
390
647
|
|
|
391
648
|
private
|
|
649
|
+
|
|
650
|
+
# Whether we are using SQL Server 2005 or later.
|
|
392
651
|
def is_2005_or_later?
|
|
393
652
|
server_version >= 9000000
|
|
394
653
|
end
|
|
395
654
|
|
|
655
|
+
# Whether we are using SQL Server 2008 or later.
|
|
656
|
+
def is_2008_or_later?
|
|
657
|
+
server_version >= 10000000
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
# Use strict ISO-8601 format with T between date and time,
|
|
661
|
+
# since that is the format that is multilanguage and not
|
|
662
|
+
# DATEFORMAT dependent.
|
|
663
|
+
def default_timestamp_format
|
|
664
|
+
DEFAULT_TIMESTAMP_FORMAT
|
|
665
|
+
end
|
|
666
|
+
|
|
396
667
|
# MSSQL supports the OUTPUT clause for DELETE statements.
|
|
397
668
|
# It also allows prepending a WITH clause.
|
|
398
669
|
def delete_clause_methods
|
|
@@ -401,7 +672,8 @@ module Sequel
|
|
|
401
672
|
|
|
402
673
|
# Only include the primary table in the main delete clause
|
|
403
674
|
def delete_from_sql(sql)
|
|
404
|
-
sql <<
|
|
675
|
+
sql << FROM
|
|
676
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
405
677
|
end
|
|
406
678
|
|
|
407
679
|
# MSSQL supports FROM clauses in DELETE and UPDATE statements.
|
|
@@ -413,19 +685,17 @@ module Sequel
|
|
|
413
685
|
end
|
|
414
686
|
alias update_from_sql delete_from2_sql
|
|
415
687
|
|
|
416
|
-
#
|
|
417
|
-
#
|
|
418
|
-
def
|
|
419
|
-
|
|
688
|
+
# Return the first primary key for the current table. If this table has
|
|
689
|
+
# multiple primary keys, this will only return one of them. Used by #_import.
|
|
690
|
+
def first_primary_key
|
|
691
|
+
@db.schema(self).map{|k, v| k if v[:primary_key] == true}.compact.first
|
|
420
692
|
end
|
|
421
|
-
|
|
422
|
-
alias update_with_sql delete_with_sql
|
|
423
|
-
|
|
693
|
+
|
|
424
694
|
# MSSQL raises an error if you try to provide more than 3 decimal places
|
|
425
695
|
# for a fractional timestamp. This probably doesn't work for smalldatetime
|
|
426
696
|
# fields.
|
|
427
697
|
def format_timestamp_usec(usec)
|
|
428
|
-
sprintf(
|
|
698
|
+
sprintf(TIMESTAMP_USEC_FORMAT, usec/1000)
|
|
429
699
|
end
|
|
430
700
|
|
|
431
701
|
# MSSQL supports the OUTPUT clause for INSERT statements.
|
|
@@ -434,51 +704,71 @@ module Sequel
|
|
|
434
704
|
INSERT_CLAUSE_METHODS
|
|
435
705
|
end
|
|
436
706
|
|
|
707
|
+
# Use OUTPUT INSERTED.* to return all columns of the inserted row,
|
|
708
|
+
# for use with the prepared statement code.
|
|
709
|
+
def insert_output_sql(sql)
|
|
710
|
+
if @opts.has_key?(:returning)
|
|
711
|
+
sql << OUTPUT_INSERTED
|
|
712
|
+
else
|
|
713
|
+
output_sql(sql)
|
|
714
|
+
end
|
|
715
|
+
end
|
|
716
|
+
|
|
437
717
|
# MSSQL uses a literal hexidecimal number for blob strings
|
|
438
|
-
def
|
|
439
|
-
|
|
440
|
-
v.each_byte{|x| blob << sprintf('%02x', x)}
|
|
441
|
-
blob
|
|
718
|
+
def literal_blob_append(sql, v)
|
|
719
|
+
sql << HEX_START << v.unpack(HSTAR).first
|
|
442
720
|
end
|
|
443
721
|
|
|
444
|
-
# Use
|
|
445
|
-
|
|
446
|
-
|
|
722
|
+
# Use YYYYmmdd format, since that's the only want that is
|
|
723
|
+
# multilanguage and not DATEFORMAT dependent.
|
|
724
|
+
def literal_date(v)
|
|
725
|
+
v.strftime(FORMAT_DATE)
|
|
447
726
|
end
|
|
448
|
-
|
|
727
|
+
|
|
449
728
|
# Use 0 for false on MSSQL
|
|
450
729
|
def literal_false
|
|
451
730
|
BOOL_FALSE
|
|
452
731
|
end
|
|
453
732
|
|
|
733
|
+
# Optionally use unicode string syntax for all strings. Don't double
|
|
734
|
+
# backslashes.
|
|
735
|
+
def literal_string_append(sql, v)
|
|
736
|
+
sql << (mssql_unicode_strings ? UNICODE_STRING_START : APOS)
|
|
737
|
+
sql << v.gsub(APOS_RE, DOUBLE_APOS).gsub(BACKSLASH_CRLF_RE, BACKSLASH_CRLF_REPLACE) << APOS
|
|
738
|
+
end
|
|
739
|
+
|
|
454
740
|
# Use 1 for true on MSSQL
|
|
455
741
|
def literal_true
|
|
456
742
|
BOOL_TRUE
|
|
457
743
|
end
|
|
458
744
|
|
|
459
|
-
# The alias to use for the row_number column when emulating OFFSET
|
|
460
|
-
def row_number_column
|
|
461
|
-
:x_sequel_row_number_x
|
|
462
|
-
end
|
|
463
|
-
|
|
464
745
|
# MSSQL adds the limit before the columns
|
|
465
746
|
def select_clause_methods
|
|
466
747
|
SELECT_CLAUSE_METHODS
|
|
467
748
|
end
|
|
468
749
|
|
|
469
750
|
def select_into_sql(sql)
|
|
470
|
-
|
|
751
|
+
if i = @opts[:into]
|
|
752
|
+
sql << INTO
|
|
753
|
+
identifier_append(sql, i)
|
|
754
|
+
end
|
|
471
755
|
end
|
|
472
756
|
|
|
473
757
|
# MSSQL uses TOP N for limit. For MSSQL 2005+ TOP (N) is used
|
|
474
758
|
# to allow the limit to be a bound variable.
|
|
475
759
|
def select_limit_sql(sql)
|
|
476
760
|
if l = @opts[:limit]
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
761
|
+
if is_2005_or_later?
|
|
762
|
+
sql << TOP_PAREN
|
|
763
|
+
literal_append(sql, l)
|
|
764
|
+
sql << PAREN_CLOSE
|
|
765
|
+
else
|
|
766
|
+
sql << TOP
|
|
767
|
+
literal_append(sql, l)
|
|
768
|
+
end
|
|
480
769
|
end
|
|
481
770
|
end
|
|
771
|
+
alias update_limit_sql select_limit_sql
|
|
482
772
|
|
|
483
773
|
# Support different types of locking styles
|
|
484
774
|
def select_lock_sql(sql)
|
|
@@ -496,19 +786,20 @@ module Sequel
|
|
|
496
786
|
def output_sql(sql)
|
|
497
787
|
return unless supports_output_clause?
|
|
498
788
|
return unless output = @opts[:output]
|
|
499
|
-
sql <<
|
|
789
|
+
sql << OUTPUT
|
|
790
|
+
column_list_append(sql, output[:select_list])
|
|
500
791
|
if into = output[:into]
|
|
501
|
-
sql <<
|
|
792
|
+
sql << INTO
|
|
793
|
+
identifier_append(sql, into)
|
|
502
794
|
if column_list = output[:column_list]
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
sql <<
|
|
795
|
+
sql << PAREN_SPACE_OPEN
|
|
796
|
+
source_list_append(sql, column_list)
|
|
797
|
+
sql << PAREN_CLOSE
|
|
506
798
|
end
|
|
507
799
|
end
|
|
508
800
|
end
|
|
509
801
|
alias delete_output_sql output_sql
|
|
510
802
|
alias update_output_sql output_sql
|
|
511
|
-
alias insert_output_sql output_sql
|
|
512
803
|
|
|
513
804
|
# MSSQL supports the OUTPUT clause for UPDATE statements.
|
|
514
805
|
# It also allows prepending a WITH clause.
|
|
@@ -518,7 +809,12 @@ module Sequel
|
|
|
518
809
|
|
|
519
810
|
# Only include the primary table in the main update clause
|
|
520
811
|
def update_table_sql(sql)
|
|
521
|
-
sql <<
|
|
812
|
+
sql << SPACE
|
|
813
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
814
|
+
end
|
|
815
|
+
|
|
816
|
+
def uses_with_rollup?
|
|
817
|
+
!is_2008_or_later?
|
|
522
818
|
end
|
|
523
819
|
end
|
|
524
820
|
end
|