module Sequel::MSSQL::DatasetMethods
Constants
- CONSTANT_MAP
- EXTRACT_MAP
- LIMIT_ALL
Public Instance Methods
Source
# File lib/sequel/adapters/shared/mssql.rb 604 def complex_expression_sql_append(sql, op, args) 605 case op 606 when :'||' 607 super(sql, :+, args) 608 when :LIKE, :"NOT LIKE" 609 super(sql, op, complex_expression_sql_like_args(args, " COLLATE Latin1_General_CS_AS)")) 610 when :ILIKE, :"NOT ILIKE" 611 super(sql, (op == :ILIKE ? :LIKE : :"NOT LIKE"), complex_expression_sql_like_args(args, " COLLATE Latin1_General_CI_AS)")) 612 when :<<, :>> 613 complex_expression_emulate_append(sql, op, args) 614 when :extract 615 part = args[0] 616 raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part] 617 if part == :second 618 expr = args[1] 619 sql << "CAST((datepart(" << format.to_s << ', ' 620 literal_append(sql, expr) 621 sql << ') + datepart(ns, ' 622 literal_append(sql, expr) 623 sql << ")/1000000000.0) AS double precision)" 624 else 625 sql << "datepart(" << format.to_s << ', ' 626 literal_append(sql, args[1]) 627 sql << ')' 628 end 629 else 630 super 631 end 632 end
Source
Source
# File lib/sequel/adapters/shared/mssql.rb 647 def count(*a, &block) 648 if (@opts[:sql] && a.empty? && !block) 649 naked.to_a.length 650 else 651 super 652 end 653 end
For a dataset with custom SQL, since it may include ORDER BY, you cannot wrap it in a subquery. Load entire query in this case to get the number of rows. In general, you should avoid calling this method on datasets with custom SQL.
Source
# File lib/sequel/adapters/shared/mssql.rb 656 def cross_apply(table) 657 join_table(:cross_apply, table) 658 end
Uses CROSS APPLY to join the given table into the current dataset.
Source
# File lib/sequel/adapters/shared/mssql.rb 661 def disable_insert_output 662 clone(:disable_insert_output=>true) 663 end
Disable the use of INSERT OUTPUT
Source
# File lib/sequel/adapters/shared/mssql.rb 669 def empty? 670 if @opts[:sql] 671 naked.each{return false} 672 true 673 else 674 super 675 end 676 end
For a dataset with custom SQL, since it may include ORDER BY, you cannot wrap it in a subquery. Run query, and if it returns any records, return true. In general, you should avoid calling this method on datasets with custom SQL.
Sequel::EmulateOffsetWithRowNumber#empty?
Source
# File lib/sequel/adapters/shared/mssql.rb 679 def escape_like(string) 680 string.gsub(/[\\%_\[\]]/){|m| "\\#{m}"} 681 end
MSSQL treats [] as a metacharacter in LIKE expressions.
Source
# File lib/sequel/adapters/shared/mssql.rb 684 def full_text_search(cols, terms, opts = OPTS) 685 case terms 686 when Array, Set 687 terms = Sequel.array_or_set_join(terms.map{|term| "\"#{term.to_s.gsub('"', '""')}\""}, " OR ") 688 end 689 where(Sequel.lit("CONTAINS (?, ?)", cols, terms)) 690 end
MSSQL uses the CONTAINS keyword for full text search
Source
# File lib/sequel/adapters/shared/mssql.rb 695 def insert_select(*values) 696 return unless supports_insert_select? 697 with_sql_first(insert_select_sql(*values)) || false 698 end
Insert a record, returning the record inserted, using OUTPUT. Always returns nil without running an INSERT statement if disable_insert_output is used. If the query runs but returns no values, returns false.
Source
# File lib/sequel/adapters/shared/mssql.rb 702 def insert_select_sql(*values) 703 ds = (opts[:output] || opts[:returning]) ? self : output(nil, [SQL::ColumnAll.new(:inserted)]) 704 ds.insert_sql(*values) 705 end
Add OUTPUT clause unless there is already an existing output clause, then return the SQL to insert.
Source
# File lib/sequel/adapters/shared/mssql.rb 708 def into(table) 709 clone(:into => table) 710 end
Specify a table for a SELECT … INTO query.
Source
# File lib/sequel/adapters/shared/mssql.rb 595 def mssql_unicode_strings 596 opts.has_key?(:mssql_unicode_strings) ? opts[:mssql_unicode_strings] : db.mssql_unicode_strings 597 end
Use the database’s mssql_unicode_strings setting if the dataset hasn’t overridden it.
Source
# File lib/sequel/adapters/shared/mssql.rb 713 def nolock 714 cached_lock_style_dataset(:_nolock_ds, :dirty) 715 end
Allows you to do a dirty read of uncommitted data using WITH (NOLOCK).
Source
# File lib/sequel/adapters/shared/mssql.rb 718 def outer_apply(table) 719 join_table(:outer_apply, table) 720 end
Uses OUTER APPLY to join the given table into the current dataset.
Source
# File lib/sequel/adapters/shared/mssql.rb 734 def output(into, values) 735 raise(Error, "SQL Server versions 2000 and earlier do not support the OUTPUT clause") unless supports_output_clause? 736 output = {} 737 case values 738 when Hash 739 output[:column_list], output[:select_list] = values.keys, values.values 740 when Array 741 output[:select_list] = values 742 end 743 output[:into] = into 744 clone(:output => output) 745 end
Include an OUTPUT clause in the eventual INSERT, UPDATE, or DELETE query.
The first argument is the table to output into, and the second argument is either an Array of column values to select, or a Hash which maps output column names to selected values, in the style of insert or update.
Output into a returned result set is not currently supported.
Examples:
dataset.output(:output_table, [Sequel[:deleted][:id], Sequel[:deleted][:name]]) dataset.output(:output_table, id: Sequel[:inserted][:id], name: Sequel[:inserted][:name])
Source
# File lib/sequel/adapters/shared/mssql.rb 748 def quoted_identifier_append(sql, name) 749 sql << '[' << name.to_s.gsub(/\]/, ']]') << ']' 750 end
MSSQL uses [] to quote identifiers.
Source
# File lib/sequel/adapters/shared/mssql.rb 753 def returning(*values) 754 values = values.map do |v| 755 unless r = unqualified_column_for(v) 756 raise(Error, "cannot emulate RETURNING via OUTPUT for value: #{v.inspect}") 757 end 758 r 759 end 760 clone(:returning=>values) 761 end
Emulate RETURNING using the output clause. This only handles values that are simple column references.
Source
# File lib/sequel/adapters/shared/mssql.rb 767 def select_sql 768 if @opts[:offset] 769 raise(Error, "Using with_ties is not supported with an offset on Microsoft SQL Server") if @opts[:limit_with_ties] 770 return order(1).select_sql if is_2012_or_later? && !@opts[:order] 771 end 772 super 773 end
On MSSQL 2012+ add a default order to the current dataset if an offset is used. The default offset emulation using a subquery would be used in the unordered case by default, and that also adds a default order, so it’s better to just avoid the subquery.
Sequel::EmulateOffsetWithRowNumber#select_sql
Source
# File lib/sequel/adapters/shared/mssql.rb 776 def server_version 777 db.server_version(@opts[:server]) 778 end
The version of the database server.
Source
# File lib/sequel/adapters/shared/mssql.rb 780 def supports_cte?(type=:select) 781 is_2005_or_later? 782 end
Source
# File lib/sequel/adapters/shared/mssql.rb 785 def supports_group_cube? 786 is_2005_or_later? 787 end
MSSQL 2005+ supports GROUP BY CUBE.
Source
# File lib/sequel/adapters/shared/mssql.rb 790 def supports_group_rollup? 791 is_2005_or_later? 792 end
MSSQL 2005+ supports GROUP BY ROLLUP
Source
# File lib/sequel/adapters/shared/mssql.rb 795 def supports_grouping_sets? 796 is_2008_or_later? 797 end
MSSQL 2008+ supports GROUPING SETS
Source
# File lib/sequel/adapters/shared/mssql.rb 800 def supports_insert_select? 801 supports_output_clause? && !opts[:disable_insert_output] 802 end
MSSQL supports insert_select via the OUTPUT clause.
Source
# File lib/sequel/adapters/shared/mssql.rb 805 def supports_intersect_except? 806 is_2005_or_later? 807 end
MSSQL 2005+ supports INTERSECT and EXCEPT
Source
# File lib/sequel/adapters/shared/mssql.rb 810 def supports_is_true? 811 false 812 end
MSSQL does not support IS TRUE
Source
# File lib/sequel/adapters/shared/mssql.rb 815 def supports_join_using? 816 false 817 end
MSSQL doesn’t support JOIN USING
Source
# File lib/sequel/adapters/shared/mssql.rb 820 def supports_merge? 821 is_2008_or_later? 822 end
MSSQL 2008+ supports MERGE
Source
# File lib/sequel/adapters/shared/mssql.rb 825 def supports_modifying_joins? 826 is_2005_or_later? 827 end
MSSQL 2005+ supports modifying joined datasets
Source
# File lib/sequel/adapters/shared/mssql.rb 830 def supports_multiple_column_in? 831 false 832 end
MSSQL does not support multiple columns for the IN/NOT IN operators
Source
# File lib/sequel/adapters/shared/mssql.rb 835 def supports_nowait? 836 true 837 end
MSSQL supports NOWAIT.
Source
# File lib/sequel/adapters/shared/mssql.rb 845 def supports_output_clause? 846 is_2005_or_later? 847 end
MSSQL 2005+ supports the OUTPUT clause.
Source
# File lib/sequel/adapters/shared/mssql.rb 850 def supports_returning?(type) 851 supports_insert_select? 852 end
MSSQL 2005+ can emulate RETURNING via the OUTPUT clause.
Source
# File lib/sequel/adapters/shared/mssql.rb 855 def supports_skip_locked? 856 true 857 end
MSSQL uses READPAST to skip locked rows.
Source
# File lib/sequel/adapters/shared/mssql.rb 865 def supports_where_true? 866 false 867 end
MSSQL cannot use WHERE 1.
Source
# File lib/sequel/adapters/shared/mssql.rb 860 def supports_window_functions? 861 true 862 end
MSSQL 2005+ supports window functions
Source
# File lib/sequel/adapters/shared/mssql.rb 600 def with_mssql_unicode_strings(v) 601 clone(:mssql_unicode_strings=>v) 602 end
Return a cloned dataset with the mssql_unicode_strings option set.
Source
# File lib/sequel/adapters/shared/mssql.rb 871 def with_ties 872 clone(:limit_with_ties=>true) 873 end
Use WITH TIES when limiting the result set to also include additional rows matching the last row.
Protected Instance Methods
Source
# File lib/sequel/adapters/shared/mssql.rb 881 def _import(columns, values, opts=OPTS) 882 if opts[:return] == :primary_key && !@opts[:output] 883 output(nil, [SQL::QualifiedIdentifier.new(:inserted, first_primary_key)])._import(columns, values, opts) 884 elsif @opts[:output] 885 # no transaction: our multi_insert_sql_strategy should guarantee 886 # that there's only ever a single statement. 887 sql = multi_insert_sql(columns, values)[0].freeze 888 naked.with_sql(sql).map{|v| v.length == 1 ? v.values.first : v} 889 else 890 super 891 end 892 end
If returned primary keys are requested, use OUTPUT unless already set on the dataset. If OUTPUT is already set, use existing returning values. If OUTPUT is only set to return a single columns, return an array of just that column. Otherwise, return an array of hashes.
Source
# File lib/sequel/adapters/shared/mssql.rb 901 def compound_from_self 902 if @opts[:offset] && !@opts[:limit] && !is_2012_or_later? 903 clone(:limit=>LIMIT_ALL).from_self 904 elsif @opts[:order] && !(@opts[:sql] || @opts[:limit] || @opts[:offset]) 905 unordered 906 else 907 super 908 end 909 end
If the dataset using a order without a limit or offset or custom SQL, remove the order. Compounds on Microsoft SQL Server have undefined order unless the result is specifically ordered. Applying the current order before the compound doesn’t work in all cases, such as when qualified identifiers are used. If you want to ensure a order for a compound dataset, apply the order after all compounds have been added.
Private Instance Methods
Source
# File lib/sequel/adapters/shared/mssql.rb 914 def _merge_when_conditions_sql(sql, data) 915 if data.has_key?(:conditions) 916 sql << " AND " 917 literal_append(sql, _normalize_merge_when_conditions(data[:conditions])) 918 end 919 end
Normalize conditions for MERGE WHEN.
Source
# File lib/sequel/adapters/shared/mssql.rb 937 def _merge_when_sql(sql) 938 super 939 sql << ';' 940 end
MSSQL requires a semicolon at the end of MERGE.
Source
# File lib/sequel/adapters/shared/mssql.rb 923 def _normalize_merge_when_conditions(conditions) 924 case conditions 925 when nil, false 926 {1=>0} 927 when true 928 {1=>1} 929 when Sequel::SQL::DelayedEvaluation 930 Sequel.delay{_normalize_merge_when_conditions(conditions.call(self))} 931 else 932 conditions 933 end 934 end
Handle nil, false, and true MERGE WHEN conditions to avoid non-boolean type error.
Source
# File lib/sequel/adapters/shared/mssql.rb 943 def aggregate_dataset 944 (options_overlap(Sequel::Dataset::COUNT_FROM_SELF_OPTS) && !options_overlap([:limit])) ? unordered.from_self : super 945 end
MSSQL does not allow ordering in sub-clauses unless TOP (limit) is specified
Source
# File lib/sequel/adapters/shared/mssql.rb 948 def check_not_limited!(type) 949 return if @opts[:skip_limit_check] && type != :truncate 950 raise Sequel::InvalidOperation, "Dataset##{type} not supported on ordered, limited datasets" if opts[:order] && opts[:limit] 951 super if type == :truncate || @opts[:offset] 952 end
Allow update and delete for unordered, limited datasets only.
Source
# File lib/sequel/adapters/shared/mssql.rb 970 def complex_expression_sql_like_args(args, collation) 971 if db.like_without_collate 972 args 973 else 974 args.map{|a| Sequel.lit(["(", collation], a)} 975 end 976 end
Determine whether to add the COLLATE for LIKE arguments, based on the Database setting.
Source
# File lib/sequel/adapters/shared/mssql.rb 981 def default_timestamp_format 982 "'%Y-%m-%dT%H:%M:%S.%3N'" 983 end
Use strict ISO-8601 format with T between date and time, since that is the format that is multilanguage and not DATEFORMAT dependent.
Source
# File lib/sequel/adapters/shared/mssql.rb 992 def delete_from2_sql(sql) 993 if joined_dataset? 994 select_from_sql(sql) 995 select_join_sql(sql) 996 end 997 end
MSSQL supports FROM clauses in DELETE and UPDATE statements.
Source
# File lib/sequel/adapters/shared/mssql.rb 986 def delete_from_sql(sql) 987 sql << ' FROM ' 988 source_list_append(sql, @opts[:from][0..0]) 989 end
Only include the primary table in the main delete clause
Source
# File lib/sequel/adapters/shared/mssql.rb 1000 def delete_output_sql(sql) 1001 output_sql(sql, :DELETED) 1002 end
Source
# File lib/sequel/adapters/shared/mssql.rb 1008 def emulate_function?(name) 1009 name == :char_length || name == :trim 1010 end
There is no function on Microsoft SQL Server that does character length and respects trailing spaces (datalength respects trailing spaces, but counts bytes instead of characters). Use a hack to work around the trailing spaces issue.
Source
# File lib/sequel/adapters/shared/mssql.rb 1012 def emulate_function_sql_append(sql, f) 1013 case f.name 1014 when :char_length 1015 literal_append(sql, SQL::Function.new(:len, Sequel.join([f.args.first, 'x'])) - 1) 1016 when :trim 1017 literal_append(sql, SQL::Function.new(:ltrim, SQL::Function.new(:rtrim, f.args.first))) 1018 end 1019 end
Source
# File lib/sequel/adapters/shared/mssql.rb 1022 def emulate_offset_with_row_number? 1023 super && !(is_2012_or_later? && @opts[:order]) 1024 end
Microsoft SQL Server 2012+ has native support for offsets, but only for ordered datasets.
Sequel::EmulateOffsetWithRowNumber#emulate_offset_with_row_number?
Source
# File lib/sequel/adapters/shared/mssql.rb 1028 def first_primary_key 1029 @db.schema(self).map{|k, v| k if v[:primary_key] == true}.compact.first 1030 end
Return the first primary key for the current table. If this table has multiple primary keys, this will only return one of them. Used by _import.
Source
# File lib/sequel/adapters/shared/mssql.rb 1032 def insert_output_sql(sql) 1033 output_sql(sql, :INSERTED) 1034 end
Source
# File lib/sequel/adapters/shared/mssql.rb 955 def is_2005_or_later? 956 server_version >= 9000000 957 end
Whether we are using SQL Server 2005 or later.
Source
# File lib/sequel/adapters/shared/mssql.rb 960 def is_2008_or_later? 961 server_version >= 10000000 962 end
Whether we are using SQL Server 2008 or later.
Source
# File lib/sequel/adapters/shared/mssql.rb 965 def is_2012_or_later? 966 server_version >= 11000000 967 end
Whether we are using SQL Server 2012 or later.
Source
# File lib/sequel/adapters/shared/mssql.rb 1038 def join_type_sql(join_type) 1039 case join_type 1040 when :cross_apply 1041 'CROSS APPLY' 1042 when :outer_apply 1043 'OUTER APPLY' 1044 else 1045 super 1046 end 1047 end
Handle CROSS APPLY and OUTER APPLY JOIN types
Source
# File lib/sequel/adapters/shared/mssql.rb 1050 def literal_blob_append(sql, v) 1051 sql << '0x' << v.unpack("H*").first 1052 end
MSSQL uses a literal hexadecimal number for blob strings
Source
# File lib/sequel/adapters/shared/mssql.rb 1056 def literal_date(v) 1057 v.strftime("'%Y%m%d'") 1058 end
Use YYYYmmdd format, since that’s the only format that is multilanguage and not DATEFORMAT dependent.
Source
# File lib/sequel/adapters/shared/mssql.rb 1061 def literal_false 1062 '0' 1063 end
Use 0 for false on MSSQL
Source
# File lib/sequel/adapters/shared/mssql.rb 1067 def literal_string_append(sql, v) 1068 sql << (mssql_unicode_strings ? "N'" : "'") 1069 sql << v.gsub("'", "''").gsub(/\\((?:\r\n)|\n)/, '\\\\\\\\\\1\\1') << "'" 1070 end
Optionally use unicode string syntax for all strings. Don’t double backslashes.
Source
# File lib/sequel/adapters/shared/mssql.rb 1073 def literal_true 1074 '1' 1075 end
Use 1 for true on MSSQL
Source
# File lib/sequel/adapters/shared/mssql.rb 1079 def multi_insert_sql_strategy 1080 is_2008_or_later? ? :values : :union 1081 end
MSSQL 2008+ supports multiple rows in the VALUES clause, older versions can use UNION.
Source
# File lib/sequel/adapters/shared/mssql.rb 1083 def non_sql_option?(key) 1084 super || key == :disable_insert_output || key == :mssql_unicode_strings 1085 end
Source
# File lib/sequel/adapters/shared/mssql.rb 1184 def output_list_sql(sql, output) 1185 sql << " OUTPUT " 1186 column_list_append(sql, output[:select_list]) 1187 if into = output[:into] 1188 sql << " INTO " 1189 identifier_append(sql, into) 1190 if column_list = output[:column_list] 1191 sql << ' (' 1192 source_list_append(sql, column_list) 1193 sql << ')' 1194 end 1195 end 1196 end
Source
# File lib/sequel/adapters/shared/mssql.rb 1198 def output_returning_sql(sql, type, values) 1199 sql << " OUTPUT " 1200 if values.empty? 1201 literal_append(sql, SQL::ColumnAll.new(type)) 1202 else 1203 values = values.map do |v| 1204 case v 1205 when SQL::AliasedExpression 1206 Sequel.qualify(type, v.expression).as(v.alias) 1207 else 1208 Sequel.qualify(type, v) 1209 end 1210 end 1211 column_list_append(sql, values) 1212 end 1213 end
Source
# File lib/sequel/adapters/shared/mssql.rb 1175 def output_sql(sql, type) 1176 return unless supports_output_clause? 1177 if output = @opts[:output] 1178 output_list_sql(sql, output) 1179 elsif values = @opts[:returning] 1180 output_returning_sql(sql, type, values) 1181 end 1182 end
Source
# File lib/sequel/adapters/shared/mssql.rb 1216 def requires_emulating_nulls_first? 1217 true 1218 end
MSSQL does not natively support NULLS FIRST/LAST.
Source
# File lib/sequel/adapters/shared/mssql.rb 1087 def select_into_sql(sql) 1088 if i = @opts[:into] 1089 sql << " INTO " 1090 identifier_append(sql, i) 1091 end 1092 end
Source
# File lib/sequel/adapters/shared/mssql.rb 1096 def select_limit_sql(sql) 1097 if l = @opts[:limit] 1098 return if is_2012_or_later? && @opts[:order] && @opts[:offset] 1099 shared_limit_sql(sql, l) 1100 end 1101 end
MSSQL 2000 uses TOP N for limit. For MSSQL 2005+ TOP (N) is used to allow the limit to be a bound variable.
Source
# File lib/sequel/adapters/shared/mssql.rb 1130 def select_lock_sql(sql) 1131 lock = @opts[:lock] 1132 skip_locked = @opts[:skip_locked] 1133 nowait = @opts[:nowait] 1134 for_update = lock == :update 1135 dirty = lock == :dirty 1136 lock_hint = for_update || dirty 1137 1138 if lock_hint || skip_locked 1139 sql << " WITH (" 1140 1141 if lock_hint 1142 sql << (for_update ? 'UPDLOCK' : 'NOLOCK') 1143 end 1144 1145 if skip_locked || nowait 1146 sql << ', ' if lock_hint 1147 sql << (skip_locked ? "READPAST" : "NOWAIT") 1148 end 1149 1150 sql << ')' 1151 else 1152 super 1153 end 1154 end
Handle dirty, skip locked, and for update locking
Source
# File lib/sequel/adapters/shared/mssql.rb 1158 def select_order_sql(sql) 1159 super 1160 if is_2012_or_later? && @opts[:order] 1161 if o = @opts[:offset] 1162 sql << " OFFSET " 1163 literal_append(sql, o) 1164 sql << " ROWS" 1165 1166 if l = @opts[:limit] 1167 sql << " FETCH NEXT " 1168 literal_append(sql, l) 1169 sql << " ROWS ONLY" 1170 end 1171 end 1172 end 1173 end
On 2012+ when there is an order with an offset, append the offset (and possible limit) at the end of the order clause.
Source
# File lib/sequel/adapters/shared/mssql.rb 1222 def sqltime_precision 1223 6 1224 end
MSSQL supports 100-nsec precision for time columns, but ruby by default only supports usec precision.
Source
Source
# File lib/sequel/adapters/shared/mssql.rb 1122 def update_limit_sql(sql) 1123 if l = @opts[:limit] 1124 shared_limit_sql(sql, l) 1125 end 1126 end
Source
# File lib/sequel/adapters/shared/mssql.rb 1234 def update_table_sql(sql) 1235 sql << ' ' 1236 source_list_append(sql, @opts[:from][0..0]) 1237 end
Only include the primary table in the main update clause
Source
# File lib/sequel/adapters/shared/mssql.rb 1239 def uses_with_rollup? 1240 !is_2008_or_later? 1241 end