require "sqlite3/errors" module SQLite3 # This module is intended for inclusion solely by the Database class. It # defines convenience methods for the various pragmas supported by SQLite3. # # For a detailed description of these pragmas, see the SQLite3 documentation # at http://sqlite.org/pragma.html. module Pragmas # Returns +true+ or +false+ depending on the value of the named pragma. def get_boolean_pragma(name) get_first_value("PRAGMA #{name}") != 0 end # Sets the given pragma to the given boolean value. The value itself # may be +true+ or +false+, or any other commonly used string or # integer that represents truth. def set_boolean_pragma(name, mode) case mode when String case mode.downcase when "on", "yes", "true", "y", "t" then mode = "'ON'" when "off", "no", "false", "n", "f" then mode = "'OFF'" else raise SQLite3::Exception, "unrecognized pragma parameter #{mode.inspect}" end when true, 1 mode = "ON" when false, 0, nil mode = "OFF" else raise SQLite3::Exception, "unrecognized pragma parameter #{mode.inspect}" end execute("PRAGMA #{name}=#{mode}") end # Requests the given pragma (and parameters), and if the block is given, # each row of the result set will be yielded to it. Otherwise, the results # are returned as an array. def get_query_pragma(name, *params, &block) # :yields: row if params.empty? execute("PRAGMA #{name}", &block) else args = "'" + params.join("','") + "'" execute("PRAGMA #{name}( #{args} )", &block) end end # Return the value of the given pragma. def get_enum_pragma(name) get_first_value("PRAGMA #{name}") end # Set the value of the given pragma to +mode+. The +mode+ parameter must # conform to one of the values in the given +enum+ array. Each entry in # the array is another array comprised of elements in the enumeration that # have duplicate values. See #synchronous, #default_synchronous, # #temp_store, and #default_temp_store for usage examples. def set_enum_pragma(name, mode, enums) match = enums.find { |p| p.find { |i| i.to_s.downcase == mode.to_s.downcase } } unless match raise SQLite3::Exception, "unrecognized #{name} #{mode.inspect}" end execute("PRAGMA #{name}='#{match.first.upcase}'") end # Returns the value of the given pragma as an integer. def get_int_pragma(name) get_first_value("PRAGMA #{name}").to_i end # Set the value of the given pragma to the integer value of the +value+ # parameter. def set_int_pragma(name, value) execute("PRAGMA #{name}=#{value.to_i}") end # The enumeration of valid synchronous modes. SYNCHRONOUS_MODES = [["full", 2], ["normal", 1], ["off", 0]] # The enumeration of valid temp store modes. TEMP_STORE_MODES = [["default", 0], ["file", 1], ["memory", 2]] # The enumeration of valid auto vacuum modes. AUTO_VACUUM_MODES = [["none", 0], ["full", 1], ["incremental", 2]] # The list of valid journaling modes. JOURNAL_MODES = [["delete"], ["truncate"], ["persist"], ["memory"], ["wal"], ["off"]] # The list of valid locking modes. LOCKING_MODES = [["normal"], ["exclusive"]] # The list of valid encodings. ENCODINGS = [["utf-8"], ["utf-16"], ["utf-16le"], ["utf-16be"]] # The list of valid WAL checkpoints. WAL_CHECKPOINTS = [["passive"], ["full"], ["restart"], ["truncate"]] def application_id get_int_pragma "application_id" end def application_id=(integer) set_int_pragma "application_id", integer end def auto_vacuum get_enum_pragma "auto_vacuum" end def auto_vacuum=(mode) set_enum_pragma "auto_vacuum", mode, AUTO_VACUUM_MODES end def automatic_index get_boolean_pragma "automatic_index" end def automatic_index=(mode) set_boolean_pragma "automatic_index", mode end def busy_timeout get_int_pragma "busy_timeout" end def busy_timeout=(milliseconds) set_int_pragma "busy_timeout", milliseconds end def cache_size get_int_pragma "cache_size" end def cache_size=(size) set_int_pragma "cache_size", size end def cache_spill get_boolean_pragma "cache_spill" end def cache_spill=(mode) set_boolean_pragma "cache_spill", mode end def case_sensitive_like=(mode) set_boolean_pragma "case_sensitive_like", mode end def cell_size_check get_boolean_pragma "cell_size_check" end def cell_size_check=(mode) set_boolean_pragma "cell_size_check", mode end def checkpoint_fullfsync get_boolean_pragma "checkpoint_fullfsync" end def checkpoint_fullfsync=(mode) set_boolean_pragma "checkpoint_fullfsync", mode end def collation_list(&block) # :yields: row get_query_pragma "collation_list", &block end def compile_options(&block) # :yields: row get_query_pragma "compile_options", &block end def count_changes get_boolean_pragma "count_changes" end def count_changes=(mode) set_boolean_pragma "count_changes", mode end def data_version get_int_pragma "data_version" end def database_list(&block) # :yields: row get_query_pragma "database_list", &block end def default_cache_size get_int_pragma "default_cache_size" end def default_cache_size=(size) set_int_pragma "default_cache_size", size end def default_synchronous get_enum_pragma "default_synchronous" end def default_synchronous=(mode) set_enum_pragma "default_synchronous", mode, SYNCHRONOUS_MODES end def default_temp_store get_enum_pragma "default_temp_store" end def default_temp_store=(mode) set_enum_pragma "default_temp_store", mode, TEMP_STORE_MODES end def defer_foreign_keys get_boolean_pragma "defer_foreign_keys" end def defer_foreign_keys=(mode) set_boolean_pragma "defer_foreign_keys", mode end def encoding get_enum_pragma "encoding" end def encoding=(mode) set_enum_pragma "encoding", mode, ENCODINGS end def foreign_key_check(*table, &block) # :yields: row get_query_pragma "foreign_key_check", *table, &block end def foreign_key_list(table, &block) # :yields: row get_query_pragma "foreign_key_list", table, &block end def foreign_keys get_boolean_pragma "foreign_keys" end def foreign_keys=(mode) set_boolean_pragma "foreign_keys", mode end def freelist_count get_int_pragma "freelist_count" end def full_column_names get_boolean_pragma "full_column_names" end def full_column_names=(mode) set_boolean_pragma "full_column_names", mode end def fullfsync get_boolean_pragma "fullfsync" end def fullfsync=(mode) set_boolean_pragma "fullfsync", mode end def ignore_check_constraints=(mode) set_boolean_pragma "ignore_check_constraints", mode end def incremental_vacuum(pages, &block) # :yields: row get_query_pragma "incremental_vacuum", pages, &block end def index_info(index, &block) # :yields: row get_query_pragma "index_info", index, &block end def index_list(table, &block) # :yields: row get_query_pragma "index_list", table, &block end def index_xinfo(index, &block) # :yields: row get_query_pragma "index_xinfo", index, &block end def integrity_check(*num_errors, &block) # :yields: row get_query_pragma "integrity_check", *num_errors, &block end def journal_mode get_enum_pragma "journal_mode" end def journal_mode=(mode) set_enum_pragma "journal_mode", mode, JOURNAL_MODES end def journal_size_limit get_int_pragma "journal_size_limit" end def journal_size_limit=(size) set_int_pragma "journal_size_limit", size end def legacy_file_format get_boolean_pragma "legacy_file_format" end def legacy_file_format=(mode) set_boolean_pragma "legacy_file_format", mode end def locking_mode get_enum_pragma "locking_mode" end def locking_mode=(mode) set_enum_pragma "locking_mode", mode, LOCKING_MODES end def max_page_count get_int_pragma "max_page_count" end def max_page_count=(size) set_int_pragma "max_page_count", size end def mmap_size get_int_pragma "mmap_size" end def mmap_size=(size) set_int_pragma "mmap_size", size end # Attempt to optimize the database. # # To customize the optimization options, pass +bitmask+ with a combination # of the Constants::Optimize masks. # # See https://www.sqlite.org/pragma.html#pragma_optimize for more information. def optimize(bitmask = nil) if bitmask set_int_pragma "optimize", bitmask else execute("PRAGMA optimize") end end def page_count get_int_pragma "page_count" end def page_size get_int_pragma "page_size" end def page_size=(size) set_int_pragma "page_size", size end def parser_trace=(mode) set_boolean_pragma "parser_trace", mode end def query_only get_boolean_pragma "query_only" end def query_only=(mode) set_boolean_pragma "query_only", mode end def quick_check(*num_errors, &block) # :yields: row get_query_pragma "quick_check", *num_errors, &block end def read_uncommitted get_boolean_pragma "read_uncommitted" end def read_uncommitted=(mode) set_boolean_pragma "read_uncommitted", mode end def recursive_triggers get_boolean_pragma "recursive_triggers" end def recursive_triggers=(mode) set_boolean_pragma "recursive_triggers", mode end def reverse_unordered_selects get_boolean_pragma "reverse_unordered_selects" end def reverse_unordered_selects=(mode) set_boolean_pragma "reverse_unordered_selects", mode end def schema_cookie get_int_pragma "schema_cookie" end def schema_cookie=(cookie) set_int_pragma "schema_cookie", cookie end def schema_version get_int_pragma "schema_version" end def schema_version=(version) set_int_pragma "schema_version", version end def secure_delete get_boolean_pragma "secure_delete" end def secure_delete=(mode) set_boolean_pragma "secure_delete", mode end def short_column_names get_boolean_pragma "short_column_names" end def short_column_names=(mode) set_boolean_pragma "short_column_names", mode end def shrink_memory execute("PRAGMA shrink_memory") end def soft_heap_limit get_int_pragma "soft_heap_limit" end def soft_heap_limit=(mode) set_int_pragma "soft_heap_limit", mode end def stats(&block) # :yields: row get_query_pragma "stats", &block end def synchronous get_enum_pragma "synchronous" end def synchronous=(mode) set_enum_pragma "synchronous", mode, SYNCHRONOUS_MODES end def temp_store get_enum_pragma "temp_store" end def temp_store=(mode) set_enum_pragma "temp_store", mode, TEMP_STORE_MODES end def threads get_int_pragma "threads" end def threads=(count) set_int_pragma "threads", count end def user_cookie get_int_pragma "user_cookie" end def user_cookie=(cookie) set_int_pragma "user_cookie", cookie end def user_version get_int_pragma "user_version" end def user_version=(version) set_int_pragma "user_version", version end def vdbe_addoptrace=(mode) set_boolean_pragma "vdbe_addoptrace", mode end def vdbe_debug=(mode) set_boolean_pragma "vdbe_debug", mode end def vdbe_listing=(mode) set_boolean_pragma "vdbe_listing", mode end def vdbe_trace get_boolean_pragma "vdbe_trace" end def vdbe_trace=(mode) set_boolean_pragma "vdbe_trace", mode end def wal_autocheckpoint get_int_pragma "wal_autocheckpoint" end def wal_autocheckpoint=(mode) set_int_pragma "wal_autocheckpoint", mode end def wal_checkpoint get_enum_pragma "wal_checkpoint" end def wal_checkpoint=(mode) set_enum_pragma "wal_checkpoint", mode, WAL_CHECKPOINTS end def writable_schema=(mode) set_boolean_pragma "writable_schema", mode end ### # Returns information about +table+. Yields each row of table information # if a block is provided. def table_info table stmt = prepare "PRAGMA table_info(#{table})" columns = stmt.columns needs_tweak_default = version_compare(SQLite3.libversion.to_s, "3.3.7") > 0 result = [] unless block_given? stmt.each do |row| new_row = columns.zip(row).to_h tweak_default(new_row) if needs_tweak_default # Ensure the type value is downcased. On Mac and Windows # platforms this value is now being returned as all upper # case. if new_row["type"] new_row["type"] = new_row["type"].downcase end if block_given? yield new_row else result << new_row end end stmt.close result end private # Compares two version strings def version_compare(v1, v2) v1 = v1.split(".").map { |i| i.to_i } v2 = v2.split(".").map { |i| i.to_i } parts = [v1.length, v2.length].max v1.push 0 while v1.length < parts v2.push 0 while v2.length < parts v1.zip(v2).each do |a, b| return -1 if a < b return 1 if a > b end 0 end # Since SQLite 3.3.8, the table_info pragma has returned the default # value of the row as a quoted SQL value. This method essentially # unquotes those values. def tweak_default(hash) case hash["dflt_value"] when /^null$/i hash["dflt_value"] = nil when /^'(.*)'$/m hash["dflt_value"] = $1.gsub("''", "'") when /^"(.*)"$/m hash["dflt_value"] = $1.gsub('""', '"') end end end end