module Sequel::SQLite::DatasetMethods
Constants
- CONSTANT_MAP
- EXTRACT_MAP
- INSERT_CONFLICT_RESOLUTIONS
-
The allowed values for
insert_conflict
Public Instance Methods
Source
# File lib/sequel/adapters/shared/sqlite.rb 598 def cast_sql_append(sql, expr, type) 599 if type == Time or type == DateTime 600 sql << "datetime(" 601 literal_append(sql, expr) 602 sql << ')' 603 elsif type == Date 604 sql << "date(" 605 literal_append(sql, expr) 606 sql << ')' 607 else 608 super 609 end 610 end
Source
# File lib/sequel/adapters/shared/sqlite.rb 614 def complex_expression_sql_append(sql, op, args) 615 case op 616 when :"NOT LIKE", :"NOT ILIKE" 617 sql << 'NOT ' 618 complex_expression_sql_append(sql, (op == :"NOT ILIKE" ? :ILIKE : :LIKE), args) 619 when :^ 620 complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.lit(["((~(", " & ", ")) & (", " | ", "))"], a, b, a, b)} 621 when :** 622 unless (exp = args[1]).is_a?(Integer) 623 raise(Sequel::Error, "can only emulate exponentiation on SQLite if exponent is an integer, given #{exp.inspect}") 624 end 625 case exp 626 when 0 627 sql << '1' 628 else 629 sql << '(' 630 arg = args[0] 631 if exp < 0 632 invert = true 633 exp = exp.abs 634 sql << '(1.0 / (' 635 end 636 (exp - 1).times do 637 literal_append(sql, arg) 638 sql << " * " 639 end 640 literal_append(sql, arg) 641 sql << ')' 642 if invert 643 sql << "))" 644 end 645 end 646 when :extract 647 part = args[0] 648 raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part] 649 sql << "CAST(strftime(" << format << ', ' 650 literal_append(sql, args[1]) 651 sql << ') AS ' << (part == :second ? 'NUMERIC' : 'INTEGER') << ')' 652 else 653 super 654 end 655 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.
Source
# File lib/sequel/adapters/shared/sqlite.rb 659 def constant_sql_append(sql, constant) 660 if (c = CONSTANT_MAP[constant]) && !db.current_timestamp_utc 661 sql << c 662 else 663 super 664 end 665 end
SQLite has CURRENT_TIMESTAMP and related constants in UTC instead of in localtime, so convert those constants to local time.
Source
# File lib/sequel/adapters/shared/sqlite.rb 670 def delete(&block) 671 @opts[:where] ? super : where(1=>1).delete(&block) 672 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.
Source
# File lib/sequel/adapters/shared/sqlite.rb 675 def empty? 676 return false if @opts[:values] 677 super 678 end
Always return false when using VALUES
Source
# File lib/sequel/adapters/shared/sqlite.rb 683 def explain(opts=nil) 684 # Load the PrettyTable class, needed for explain output 685 Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable) 686 687 keyword = (opts && opts[:query_plan]) ? "EXPLAIN QUERY PLAN" : "EXPLAIN" 688 ds = db.send(:metadata_dataset).clone(:sql=>"#{keyword} #{select_sql}".freeze) 689 rows = ds.all 690 Sequel::PrettyTable.string(rows, ds.columns) 691 end
Return a string specifying a query explanation for a SELECT of the current dataset. Options:
- :query_plan
-
Use EXPLAIN QUERY PLAN instead of EXPLAIN if true.
Source
# File lib/sequel/adapters/shared/sqlite.rb 694 def having(*cond) 695 raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") if !@opts[:group] && db.sqlite_version < 33900 696 super 697 end
HAVING requires GROUP BY on SQLite
Source
# File lib/sequel/adapters/shared/sqlite.rb 772 def insert_conflict(opts = :ignore) 773 case opts 774 when Symbol, String 775 unless INSERT_CONFLICT_RESOLUTIONS.include?(opts.to_s.upcase) 776 raise Error, "Invalid symbol or string passed to Dataset#insert_conflict: #{opts.inspect}. The allowed values are: :rollback, :abort, :fail, :ignore, or :replace" 777 end 778 clone(:insert_conflict => opts) 779 when Hash 780 clone(:insert_on_conflict => opts) 781 else 782 raise Error, "Invalid value passed to Dataset#insert_conflict: #{opts.inspect}, should use a symbol or a hash" 783 end 784 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 algorithms: 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)
Source
# File lib/sequel/adapters/shared/sqlite.rb 791 def insert_ignore 792 insert_conflict(:ignore) 793 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)
Source
# File lib/sequel/adapters/shared/sqlite.rb 701 def insert_select(*values) 702 return unless supports_insert_select? 703 # Handle case where query does not return a row 704 server?(:default).with_sql_first(insert_select_sql(*values)) || false 705 end
Support insert select for associations, so that the model code can use returning instead of a separate query.
Source
# File lib/sequel/adapters/shared/sqlite.rb 709 def insert_select_sql(*values) 710 ds = opts[:returning] ? self : returning 711 ds.insert_sql(*values) 712 end
The SQL to use for an insert_select, adds a RETURNING clause to the insert unless the RETURNING clause is already present.
Source
# File lib/sequel/adapters/shared/sqlite.rb 715 def quoted_identifier_append(sql, c) 716 sql << '`' << c.to_s.gsub('`', '``') << '`' 717 end
SQLite uses the nonstandard ‘ (backtick) for quoting identifiers.
Source
# File lib/sequel/adapters/shared/sqlite.rb 796 def returning(*values) 797 return super if values.empty? 798 raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert) 799 clone(:returning=>_returning_values(values).freeze) 800 end
Automatically add aliases to RETURNING values to work around SQLite bug.
Source
# File lib/sequel/adapters/shared/sqlite.rb 723 def select(*cols) 724 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)}) 725 super(*cols.map{|c| alias_qualified_column(c)}) 726 else 727 super 728 end 729 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.
Source
# File lib/sequel/adapters/shared/sqlite.rb 803 def supports_cte?(type=:select) 804 db.sqlite_version >= 30803 805 end
SQLite 3.8.3+ supports common table expressions.
Source
# File lib/sequel/adapters/shared/sqlite.rb 808 def supports_cte_in_subqueries? 809 supports_cte? 810 end
SQLite supports CTEs in subqueries if it supports CTEs.
Source
# File lib/sequel/adapters/shared/sqlite.rb 818 def supports_deleting_joins? 819 false 820 end
SQLite does not support deleting from a joined dataset
Source
# File lib/sequel/adapters/shared/sqlite.rb 813 def supports_derived_column_lists? 814 false 815 end
SQLite does not support table aliases with column aliases
Source
# File lib/sequel/adapters/shared/sqlite.rb 823 def supports_intersect_except_all? 824 false 825 end
SQLite does not support INTERSECT ALL or EXCEPT ALL
Source
# File lib/sequel/adapters/shared/sqlite.rb 828 def supports_is_true? 829 false 830 end
SQLite does not support IS TRUE
Source
# File lib/sequel/adapters/shared/sqlite.rb 833 def supports_modifying_joins? 834 db.sqlite_version >= 33300 835 end
SQLite 3.33.0 supports modifying joined datasets
Source
# File lib/sequel/adapters/shared/sqlite.rb 838 def supports_multiple_column_in? 839 false 840 end
SQLite does not support multiple columns for the IN/NOT IN operators
Source
# File lib/sequel/adapters/shared/sqlite.rb 843 def supports_returning?(_) 844 db.sqlite_version >= 33500 845 end
SQLite 3.35.0 supports RETURNING on INSERT/UPDATE/DELETE.
Source
# File lib/sequel/adapters/shared/sqlite.rb 850 def supports_timestamp_timezones? 851 db.use_timestamp_timezones? 852 end
Source
# File lib/sequel/adapters/shared/sqlite.rb 855 def supports_where_true? 856 false 857 end
SQLite cannot use WHERE ‘t’.
Source
# File lib/sequel/adapters/shared/sqlite.rb 860 def supports_window_clause? 861 db.sqlite_version >= 32800 862 end
SQLite 3.28+ supports the WINDOW clause.
Source
# File lib/sequel/adapters/shared/sqlite.rb 873 def supports_window_function_frame_option?(option) 874 db.sqlite_version >= 32800 ? true : super 875 end
Source
# File lib/sequel/adapters/shared/sqlite.rb 868 def supports_window_functions? 869 db.sqlite_version >= 32600 870 end
Private Instance Methods
Source
# File lib/sequel/adapters/shared/sqlite.rb 880 def _returning_values(values) 881 values.map do |v| 882 case v 883 when Symbol 884 _, c, a = split_symbol(v) 885 a ? v : Sequel.as(v, c) 886 when SQL::Identifier, SQL::QualifiedIdentifier 887 Sequel.as(v, unqualified_column_for(v)) 888 else 889 v 890 end 891 end 892 end
Add aliases to symbols and identifiers to work around SQLite bug.
Source
# File lib/sequel/adapters/shared/sqlite.rb 1052 def _truncate_sql(table) 1053 "DELETE FROM #{table}" 1054 end
SQLite treats a DELETE with no WHERE clause as a TRUNCATE
Source
# File lib/sequel/adapters/shared/sqlite.rb 895 def aggregate_dataset_use_from_self? 896 super || @opts[:values] 897 end
Use from_self for aggregate dataset using VALUES.
Source
# File lib/sequel/adapters/shared/sqlite.rb 908 def alias_qualified_column(col) 909 case col 910 when Symbol 911 t, c, a = split_symbol(col) 912 if t && !a 913 alias_qualified_column(SQL::QualifiedIdentifier.new(t, c)) 914 else 915 col 916 end 917 when SQL::QualifiedIdentifier 918 SQL::AliasedExpression.new(col, col.column) 919 else 920 col 921 end 922 end
If col is a qualified column, alias it to the same as the column name
Source
# File lib/sequel/adapters/shared/sqlite.rb 900 def as_sql_append(sql, aliaz, column_aliases=nil) 901 raise Error, "sqlite does not support derived column lists" if column_aliases 902 aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier) 903 sql << ' AS ' 904 literal_append(sql, aliaz.to_s) 905 end
SQLite uses string literals instead of identifiers in AS clauses.
Source
# File lib/sequel/adapters/shared/sqlite.rb 925 def check_insert_allowed! 926 raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group] 927 raise(InvalidOperation, "Joined datasets cannot be modified") if joined_dataset? 928 end
Raise an InvalidOperation exception if insert is not allowed for this dataset.
Source
# File lib/sequel/adapters/shared/sqlite.rb 932 def default_import_slice 933 500 934 end
SQLite supports a maximum of 500 rows in a VALUES clause.
Source
# File lib/sequel/adapters/shared/sqlite.rb 937 def default_timestamp_format 938 db.use_timestamp_timezones? ? "'%Y-%m-%d %H:%M:%S.%6N%z'" : super 939 end
The strftime format to use when literalizing the time.
Source
# File lib/sequel/adapters/shared/sqlite.rb 942 def identifier_list(columns) 943 columns.map{|i| quote_identifier(i)}.join(', ') 944 end
SQL fragment specifying a list of identifiers
Source
# File lib/sequel/adapters/shared/sqlite.rb 947 def insert_conflict_sql(sql) 948 if resolution = @opts[:insert_conflict] 949 sql << " OR " << resolution.to_s.upcase 950 end 951 end
Add OR clauses to SQLite INSERT statements
Source
# File lib/sequel/adapters/shared/sqlite.rb 954 def insert_on_conflict_sql(sql) 955 if opts = @opts[:insert_on_conflict] 956 sql << " ON CONFLICT" 957 958 if target = opts[:constraint] 959 sql << " ON CONSTRAINT " 960 identifier_append(sql, target) 961 elsif target = opts[:target] 962 sql << ' ' 963 identifier_append(sql, Array(target)) 964 if conflict_where = opts[:conflict_where] 965 sql << " WHERE " 966 literal_append(sql, conflict_where) 967 end 968 end 969 970 if values = opts[:update] 971 sql << " DO UPDATE SET " 972 update_sql_values_hash(sql, values) 973 if update_where = opts[:update_where] 974 sql << " WHERE " 975 literal_append(sql, update_where) 976 end 977 else 978 sql << " DO NOTHING" 979 end 980 end 981 end
Add ON CONFLICT clause if it should be used
Source
# File lib/sequel/adapters/shared/sqlite.rb 984 def literal_blob_append(sql, v) 985 sql << "X'" << v.unpack("H*").first << "'" 986 end
SQLite uses a preceding X for hex escaping strings
Source
# File lib/sequel/adapters/shared/sqlite.rb 989 def literal_false 990 @db.integer_booleans ? '0' : "'f'" 991 end
Respect the database integer_booleans setting, using 0 or ‘f’.
Source
# File lib/sequel/adapters/shared/sqlite.rb 994 def literal_true 995 @db.integer_booleans ? '1' : "'t'" 996 end
Respect the database integer_booleans setting, using 1 or ‘t’.
Source
# File lib/sequel/adapters/shared/sqlite.rb 1000 def multi_insert_sql_strategy 1001 db.sqlite_version >= 30711 ? :values : :union 1002 end
SQLite only supporting multiple rows in the VALUES clause starting in 3.7.11. On older versions, fallback to using a UNION.
Source
# File lib/sequel/adapters/shared/sqlite.rb 1005 def native_function_name(emulated_function) 1006 if emulated_function == :char_length 1007 'length' 1008 else 1009 super 1010 end 1011 end
Emulate the char_length function with length
Source
# File lib/sequel/adapters/shared/sqlite.rb 1014 def requires_emulating_nulls_first? 1015 db.sqlite_version < 33000 1016 end
SQLite supports NULLS FIRST/LAST natively in 3.30+.
Source
# File lib/sequel/adapters/shared/sqlite.rb 1021 def select_lock_sql(sql) 1022 super unless @opts[:lock] == :update 1023 end
SQLite does not support FOR UPDATE, but silently ignore it instead of raising an error for compatibility with other databases.
Source
# File lib/sequel/adapters/shared/sqlite.rb 1025 def select_only_offset_sql(sql) 1026 sql << " LIMIT -1 OFFSET " 1027 literal_append(sql, @opts[:offset]) 1028 end
Source
# File lib/sequel/adapters/shared/sqlite.rb 1031 def select_values_sql(sql) 1032 sql << "VALUES " 1033 expression_list_append(sql, opts[:values]) 1034 end
Support VALUES clause instead of the SELECT clause to return rows.
Source
# File lib/sequel/adapters/shared/sqlite.rb 1037 def supports_cte_in_compounds? 1038 false 1039 end
SQLite does not support CTEs directly inside UNION/INTERSECT/EXCEPT.
Source
# File lib/sequel/adapters/shared/sqlite.rb 1042 def supports_filtered_aggregates? 1043 db.sqlite_version >= 33000 1044 end
SQLite 3.30 supports the FILTER clause for aggregate functions.
Source
# File lib/sequel/adapters/shared/sqlite.rb 1047 def supports_quoted_function_names? 1048 true 1049 end
SQLite supports quoted function names.
Source
# File lib/sequel/adapters/shared/sqlite.rb 1057 def update_from_sql(sql) 1058 if(from = @opts[:from][1..-1]).empty? 1059 raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join] 1060 else 1061 sql << ' FROM ' 1062 source_list_append(sql, from) 1063 select_join_sql(sql) 1064 end 1065 end
Use FROM to specify additional tables in an update query
Source
# File lib/sequel/adapters/shared/sqlite.rb 1068 def update_table_sql(sql) 1069 sql << ' ' 1070 source_list_append(sql, @opts[:from][0..0]) 1071 end
Only include the primary table in the main update clause