module Sequel::SQLite::DatasetMethods
Constants
- CONSTANT_MAP
- EXTRACT_MAP
- INSERT_CONFLICT_RESOLUTIONS
The allowed values for
insert_conflict
Public Instance Methods
# File lib/sequel/adapters/shared/sqlite.rb 583 def cast_sql_append(sql, expr, type) 584 if type == Time or type == DateTime 585 sql << "datetime(" 586 literal_append(sql, expr) 587 sql << ')' 588 elsif type == Date 589 sql << "date(" 590 literal_append(sql, expr) 591 sql << ')' 592 else 593 super 594 end 595 end
SQLite
doesn’t support a NOT LIKE b, you need to use NOT (a LIKE b). It doesn’t support xor, power, or the extract function natively, so those have to be emulated.
# File lib/sequel/adapters/shared/sqlite.rb 599 def complex_expression_sql_append(sql, op, args) 600 case op 601 when :"NOT LIKE", :"NOT ILIKE" 602 sql << 'NOT ' 603 complex_expression_sql_append(sql, (op == :"NOT ILIKE" ? :ILIKE : :LIKE), args) 604 when :^ 605 complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.lit(["((~(", " & ", ")) & (", " | ", "))"], a, b, a, b)} 606 when :** 607 unless (exp = args[1]).is_a?(Integer) 608 raise(Sequel::Error, "can only emulate exponentiation on SQLite if exponent is an integer, given #{exp.inspect}") 609 end 610 case exp 611 when 0 612 sql << '1' 613 else 614 sql << '(' 615 arg = args[0] 616 if exp < 0 617 invert = true 618 exp = exp.abs 619 sql << '(1.0 / (' 620 end 621 (exp - 1).times do 622 literal_append(sql, arg) 623 sql << " * " 624 end 625 literal_append(sql, arg) 626 sql << ')' 627 if invert 628 sql << "))" 629 end 630 end 631 when :extract 632 part = args[0] 633 raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part] 634 sql << "CAST(strftime(" << format << ', ' 635 literal_append(sql, args[1]) 636 sql << ') AS ' << (part == :second ? 'NUMERIC' : 'INTEGER') << ')' 637 else 638 super 639 end 640 end
SQLite
has CURRENT_TIMESTAMP and related constants in UTC instead of in localtime, so convert those constants to local time.
# File lib/sequel/adapters/shared/sqlite.rb 644 def constant_sql_append(sql, constant) 645 if (c = CONSTANT_MAP[constant]) && !db.current_timestamp_utc 646 sql << c 647 else 648 super 649 end 650 end
SQLite
performs a TRUNCATE style DELETE if no filter is specified. Since we want to always return the count of records, add a condition that is always true and then delete.
# File lib/sequel/adapters/shared/sqlite.rb 655 def delete(&block) 656 @opts[:where] ? super : where(1=>1).delete(&block) 657 end
Always return false when using VALUES
# File lib/sequel/adapters/shared/sqlite.rb 660 def empty? 661 return false if @opts[:values] 662 super 663 end
Return an array of strings specifying a query explanation for a SELECT of the current dataset. Currently, the options are ignored, but it accepts options to be compatible with other adapters.
# File lib/sequel/adapters/shared/sqlite.rb 668 def explain(opts=nil) 669 # Load the PrettyTable class, needed for explain output 670 Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable) 671 672 ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}") 673 rows = ds.all 674 Sequel::PrettyTable.string(rows, ds.columns) 675 end
HAVING requires GROUP BY on SQLite
# File lib/sequel/adapters/shared/sqlite.rb 678 def having(*cond) 679 raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") if !@opts[:group] && db.sqlite_version < 33900 680 super 681 end
Handle uniqueness violations when inserting, by using a specified resolution algorithm. With no options, uses INSERT OR REPLACE. SQLite
supports the following conflict resolution algoriths: ROLLBACK, ABORT, FAIL, IGNORE and REPLACE.
On SQLite
3.24.0+, you can pass a hash to use an ON CONFLICT clause. With out :update option, uses ON CONFLICT DO NOTHING. Options:
- :conflict_where
-
The index filter, when using a partial index to determine uniqueness.
- :target
-
The column name or expression to handle uniqueness violations on.
- :update
-
A hash of columns and values to set. Uses ON CONFLICT DO UPDATE.
- :update_where
-
A WHERE condition to use for the update.
Examples:
DB[:table].insert_conflict.insert(a: 1, b: 2) # INSERT OR IGNORE INTO TABLE (a, b) VALUES (1, 2) DB[:table].insert_conflict(:replace).insert(a: 1, b: 2) # INSERT OR REPLACE INTO TABLE (a, b) VALUES (1, 2) DB[:table].insert_conflict({}).insert(a: 1, b: 2) # INSERT INTO TABLE (a, b) VALUES (1, 2) # ON CONFLICT DO NOTHING DB[:table].insert_conflict(target: :a).insert(a: 1, b: 2) # INSERT INTO TABLE (a, b) VALUES (1, 2) # ON CONFLICT (a) DO NOTHING DB[:table].insert_conflict(target: :a, conflict_where: {c: true}).insert(a: 1, b: 2) # INSERT INTO TABLE (a, b) VALUES (1, 2) # ON CONFLICT (a) WHERE (c IS TRUE) DO NOTHING DB[:table].insert_conflict(target: :a, update: {b: Sequel[:excluded][:b]}).insert(a: 1, b: 2) # INSERT INTO TABLE (a, b) VALUES (1, 2) # ON CONFLICT (a) DO UPDATE SET b = excluded.b DB[:table].insert_conflict(target: :a, update: {b: Sequel[:excluded][:b]}, update_where: {Sequel[:table][:status_id] => 1}).insert(a: 1, b: 2) # INSERT INTO TABLE (a, b) VALUES (1, 2) # ON CONFLICT (a) DO UPDATE SET b = excluded.b WHERE (table.status_id = 1)
# File lib/sequel/adapters/shared/sqlite.rb 756 def insert_conflict(opts = :ignore) 757 case opts 758 when Symbol, String 759 unless INSERT_CONFLICT_RESOLUTIONS.include?(opts.to_s.upcase) 760 raise Error, "Invalid symbol or string passed to Dataset#insert_conflict: #{opts.inspect}. The allowed values are: :rollback, :abort, :fail, :ignore, or :replace" 761 end 762 clone(:insert_conflict => opts) 763 when Hash 764 clone(:insert_on_conflict => opts) 765 else 766 raise Error, "Invalid value passed to Dataset#insert_conflict: #{opts.inspect}, should use a symbol or a hash" 767 end 768 end
Ignore uniqueness/exclusion violations when inserting, using INSERT OR IGNORE. Exists mostly for compatibility to MySQL’s insert_ignore. Example:
DB[:table].insert_ignore.insert(a: 1, b: 2) # INSERT OR IGNORE INTO TABLE (a, b) VALUES (1, 2)
# File lib/sequel/adapters/shared/sqlite.rb 775 def insert_ignore 776 insert_conflict(:ignore) 777 end
Support insert select for associations, so that the model code can use returning instead of a separate query.
# File lib/sequel/adapters/shared/sqlite.rb 685 def insert_select(*values) 686 return unless supports_insert_select? 687 # Handle case where query does not return a row 688 server?(:default).with_sql_first(insert_select_sql(*values)) || false 689 end
The SQL
to use for an insert_select
, adds a RETURNING clause to the insert unless the RETURNING clause is already present.
# File lib/sequel/adapters/shared/sqlite.rb 693 def insert_select_sql(*values) 694 ds = opts[:returning] ? self : returning 695 ds.insert_sql(*values) 696 end
SQLite
uses the nonstandard ‘ (backtick) for quoting identifiers.
# File lib/sequel/adapters/shared/sqlite.rb 699 def quoted_identifier_append(sql, c) 700 sql << '`' << c.to_s.gsub('`', '``') << '`' 701 end
Automatically add aliases to RETURNING values to work around SQLite
bug.
# File lib/sequel/adapters/shared/sqlite.rb 780 def returning(*values) 781 return super if values.empty? 782 raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert) 783 clone(:returning=>_returning_values(values).freeze) 784 end
When a qualified column is selected on SQLite
and the qualifier is a subselect, the column name used is the full qualified name (including the qualifier) instead of just the column name. To get correct column names, you must use an alias.
# File lib/sequel/adapters/shared/sqlite.rb 707 def select(*cols) 708 if ((f = @opts[:from]) && f.any?{|t| t.is_a?(Dataset) || (t.is_a?(SQL::AliasedExpression) && t.expression.is_a?(Dataset))}) || ((j = @opts[:join]) && j.any?{|t| t.table.is_a?(Dataset)}) 709 super(*cols.map{|c| alias_qualified_column(c)}) 710 else 711 super 712 end 713 end
SQLite
3.8.3+ supports common table expressions.
# File lib/sequel/adapters/shared/sqlite.rb 787 def supports_cte?(type=:select) 788 db.sqlite_version >= 30803 789 end
SQLite
supports CTEs in subqueries if it supports CTEs.
# File lib/sequel/adapters/shared/sqlite.rb 792 def supports_cte_in_subqueries? 793 supports_cte? 794 end
SQLite
does not support deleting from a joined dataset
# File lib/sequel/adapters/shared/sqlite.rb 802 def supports_deleting_joins? 803 false 804 end
SQLite
does not support table aliases with column aliases
# File lib/sequel/adapters/shared/sqlite.rb 797 def supports_derived_column_lists? 798 false 799 end
SQLite
does not support INTERSECT ALL or EXCEPT ALL
# File lib/sequel/adapters/shared/sqlite.rb 807 def supports_intersect_except_all? 808 false 809 end
SQLite
does not support IS TRUE
# File lib/sequel/adapters/shared/sqlite.rb 812 def supports_is_true? 813 false 814 end
SQLite
3.33.0 supports modifying joined datasets
# File lib/sequel/adapters/shared/sqlite.rb 817 def supports_modifying_joins? 818 db.sqlite_version >= 33300 819 end
SQLite
does not support multiple columns for the IN/NOT IN operators
# File lib/sequel/adapters/shared/sqlite.rb 822 def supports_multiple_column_in? 823 false 824 end
SQLite
3.35.0 supports RETURNING on INSERT/UPDATE/DELETE.
# File lib/sequel/adapters/shared/sqlite.rb 827 def supports_returning?(_) 828 db.sqlite_version >= 33500 829 end
SQLite
supports timezones in literal timestamps, since it stores them as text. But using timezones in timestamps breaks SQLite
datetime functions, so we allow the user to override the default per database.
# File lib/sequel/adapters/shared/sqlite.rb 834 def supports_timestamp_timezones? 835 db.use_timestamp_timezones? 836 end
SQLite
cannot use WHERE ‘t’.
# File lib/sequel/adapters/shared/sqlite.rb 839 def supports_where_true? 840 false 841 end
SQLite
3.28+ supports the WINDOW clause.
# File lib/sequel/adapters/shared/sqlite.rb 844 def supports_window_clause? 845 db.sqlite_version >= 32800 846 end
SQLite
3.25+ supports window functions. However, support is only enabled on SQLite
3.26.0+ because internal Sequel
usage of window functions to implement eager loading of limited associations triggers an SQLite
crash bug in versions 3.25.0-3.25.3.
# File lib/sequel/adapters/shared/sqlite.rb 852 def supports_window_functions? 853 db.sqlite_version >= 32600 854 end
Private Instance Methods
Add aliases to symbols and identifiers to work around SQLite
bug.
# File lib/sequel/adapters/shared/sqlite.rb 864 def _returning_values(values) 865 values.map do |v| 866 case v 867 when Symbol 868 _, c, a = split_symbol(v) 869 a ? v : Sequel.as(v, c) 870 when SQL::Identifier, SQL::QualifiedIdentifier 871 Sequel.as(v, unqualified_column_for(v)) 872 else 873 v 874 end 875 end 876 end
SQLite
treats a DELETE with no WHERE clause as a TRUNCATE
# File lib/sequel/adapters/shared/sqlite.rb 1036 def _truncate_sql(table) 1037 "DELETE FROM #{table}" 1038 end
Use from_self for aggregate dataset using VALUES.
# File lib/sequel/adapters/shared/sqlite.rb 879 def aggreate_dataset_use_from_self? 880 super || @opts[:values] 881 end
If col is a qualified column, alias it to the same as the column name
# File lib/sequel/adapters/shared/sqlite.rb 892 def alias_qualified_column(col) 893 case col 894 when Symbol 895 t, c, a = split_symbol(col) 896 if t && !a 897 alias_qualified_column(SQL::QualifiedIdentifier.new(t, c)) 898 else 899 col 900 end 901 when SQL::QualifiedIdentifier 902 SQL::AliasedExpression.new(col, col.column) 903 else 904 col 905 end 906 end
SQLite
uses string literals instead of identifiers in AS clauses.
# File lib/sequel/adapters/shared/sqlite.rb 884 def as_sql_append(sql, aliaz, column_aliases=nil) 885 raise Error, "sqlite does not support derived column lists" if column_aliases 886 aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier) 887 sql << ' AS ' 888 literal_append(sql, aliaz.to_s) 889 end
Raise an InvalidOperation exception if insert is not allowed for this dataset.
# File lib/sequel/adapters/shared/sqlite.rb 909 def check_insert_allowed! 910 raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group] 911 raise(InvalidOperation, "Joined datasets cannot be modified") if joined_dataset? 912 end
SQLite
supports a maximum of 500 rows in a VALUES clause.
# File lib/sequel/adapters/shared/sqlite.rb 916 def default_import_slice 917 500 918 end
The strftime format to use when literalizing the time.
# File lib/sequel/adapters/shared/sqlite.rb 921 def default_timestamp_format 922 db.use_timestamp_timezones? ? "'%Y-%m-%d %H:%M:%S.%6N%z'" : super 923 end
SQL
fragment specifying a list of identifiers
# File lib/sequel/adapters/shared/sqlite.rb 926 def identifier_list(columns) 927 columns.map{|i| quote_identifier(i)}.join(', ') 928 end
Add OR clauses to SQLite
INSERT statements
# File lib/sequel/adapters/shared/sqlite.rb 931 def insert_conflict_sql(sql) 932 if resolution = @opts[:insert_conflict] 933 sql << " OR " << resolution.to_s.upcase 934 end 935 end
Add ON CONFLICT clause if it should be used
# File lib/sequel/adapters/shared/sqlite.rb 938 def insert_on_conflict_sql(sql) 939 if opts = @opts[:insert_on_conflict] 940 sql << " ON CONFLICT" 941 942 if target = opts[:constraint] 943 sql << " ON CONSTRAINT " 944 identifier_append(sql, target) 945 elsif target = opts[:target] 946 sql << ' ' 947 identifier_append(sql, Array(target)) 948 if conflict_where = opts[:conflict_where] 949 sql << " WHERE " 950 literal_append(sql, conflict_where) 951 end 952 end 953 954 if values = opts[:update] 955 sql << " DO UPDATE SET " 956 update_sql_values_hash(sql, values) 957 if update_where = opts[:update_where] 958 sql << " WHERE " 959 literal_append(sql, update_where) 960 end 961 else 962 sql << " DO NOTHING" 963 end 964 end 965 end
SQLite
uses a preceding X for hex escaping strings
# File lib/sequel/adapters/shared/sqlite.rb 968 def literal_blob_append(sql, v) 969 sql << "X'" << v.unpack("H*").first << "'" 970 end
Respect the database integer_booleans setting, using 0 or ‘f’.
# File lib/sequel/adapters/shared/sqlite.rb 973 def literal_false 974 @db.integer_booleans ? '0' : "'f'" 975 end
Respect the database integer_booleans setting, using 1 or ‘t’.
# File lib/sequel/adapters/shared/sqlite.rb 978 def literal_true 979 @db.integer_booleans ? '1' : "'t'" 980 end
SQLite
only supporting multiple rows in the VALUES clause starting in 3.7.11. On older versions, fallback to using a UNION.
# File lib/sequel/adapters/shared/sqlite.rb 984 def multi_insert_sql_strategy 985 db.sqlite_version >= 30711 ? :values : :union 986 end
Emulate the char_length function with length
# File lib/sequel/adapters/shared/sqlite.rb 989 def native_function_name(emulated_function) 990 if emulated_function == :char_length 991 'length' 992 else 993 super 994 end 995 end
SQLite
supports NULLS FIRST/LAST natively in 3.30+.
# File lib/sequel/adapters/shared/sqlite.rb 998 def requires_emulating_nulls_first? 999 db.sqlite_version < 33000 1000 end
SQLite
does not support FOR UPDATE, but silently ignore it instead of raising an error for compatibility with other databases.
# File lib/sequel/adapters/shared/sqlite.rb 1005 def select_lock_sql(sql) 1006 super unless @opts[:lock] == :update 1007 end
# File lib/sequel/adapters/shared/sqlite.rb 1009 def select_only_offset_sql(sql) 1010 sql << " LIMIT -1 OFFSET " 1011 literal_append(sql, @opts[:offset]) 1012 end
Support VALUES clause instead of the SELECT clause to return rows.
# File lib/sequel/adapters/shared/sqlite.rb 1015 def select_values_sql(sql) 1016 sql << "VALUES " 1017 expression_list_append(sql, opts[:values]) 1018 end
SQLite
does not support CTEs directly inside UNION/INTERSECT/EXCEPT.
# File lib/sequel/adapters/shared/sqlite.rb 1021 def supports_cte_in_compounds? 1022 false 1023 end
SQLite
3.30 supports the FILTER clause for aggregate functions.
# File lib/sequel/adapters/shared/sqlite.rb 1026 def supports_filtered_aggregates? 1027 db.sqlite_version >= 33000 1028 end
SQLite
supports quoted function names.
# File lib/sequel/adapters/shared/sqlite.rb 1031 def supports_quoted_function_names? 1032 true 1033 end
Use FROM to specify additional tables in an update query
# File lib/sequel/adapters/shared/sqlite.rb 1041 def update_from_sql(sql) 1042 if(from = @opts[:from][1..-1]).empty? 1043 raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join] 1044 else 1045 sql << ' FROM ' 1046 source_list_append(sql, from) 1047 select_join_sql(sql) 1048 end 1049 end
Only include the primary table in the main update clause
# File lib/sequel/adapters/shared/sqlite.rb 1052 def update_table_sql(sql) 1053 sql << ' ' 1054 source_list_append(sql, @opts[:from][0..0]) 1055 end