class Sequel::SQLite::Database

Attributes

conversion_procs[R]

The conversion procs to use for this database

Public Class Methods

new(opts = OPTS) click to toggle source
Calls superclass method Sequel::Database.new
# File lib/sequel/adapters/sqlite.rb, line 100
def initialize(opts = OPTS)
  super
  @allow_regexp = typecast_value_boolean(opts[:setup_regexp_function])
end

Public Instance Methods

allow_regexp?() click to toggle source

Whether this Database instance is setup to allow regexp matching. True if the :setup_regexp_function option was passed when creating the Database.

# File lib/sequel/adapters/sqlite.rb, line 153
def allow_regexp?
  @allow_regexp
end
connect(server) click to toggle source

Connect to the database. Since SQLite is a file based database, available options are limited:

:database

database name (filename or ':memory:' or file: URI)

:readonly

open database in read-only mode; useful for reading static data that you do not want to modify

:timeout

how long to wait for the database to be available if it is locked, given in milliseconds (default is 5000)

:setup_regexp_function

enable use of Regexp objects with SQL

'REGEXP' operator. If the value is :cached or "cached",
caches the generated regexps, which can result in a memory
leak if dynamic regexps are used.  If the value is a Proc,
it will be called with a string for the regexp and a string
for the value to compare, and should return whether the regexp
matches.
:regexp_function_cache

If setting setup_regexp_function to cached, this

determines the cache to use.  It should either be a proc or a class, and it
defaults to +Hash+. You can use +ObjectSpace::WeakKeyMap+ on Ruby 3.3+ to
have the VM automatically remove regexps from the cache after they
are no longer used.
# File lib/sequel/adapters/sqlite.rb, line 125
def connect(server)
  opts = server_opts(server)
  opts[:database] = ':memory:' if blank_object?(opts[:database])
  sqlite3_opts = {}
  sqlite3_opts[:readonly] = typecast_value_boolean(opts[:readonly]) if opts.has_key?(:readonly)
  db = ::SQLite3::Database.new(opts[:database].to_s, sqlite3_opts)
  db.busy_timeout(typecast_value_integer(opts.fetch(:timeout, 5000)))

  if USE_EXTENDED_RESULT_CODES
    db.extended_result_codes = true
  end
  
  connection_pragmas.each{|s| log_connection_yield(s, db){db.execute_batch(s)}}

  if typecast_value_boolean(opts[:setup_regexp_function])
    setup_regexp_function(db, opts[:setup_regexp_function])
  end
  
  class << db
    attr_reader :prepared_statements
  end
  db.instance_variable_set(:@prepared_statements, {})
  
  db
end
disconnect_connection(c) click to toggle source

Disconnect given connections from the database.

# File lib/sequel/adapters/sqlite.rb, line 158
def disconnect_connection(c)
  c.prepared_statements.each_value{|v| v.first.close}
  c.close
end
execute(sql, opts=OPTS, &block) click to toggle source

Run the given SQL with the given arguments and yield each row.

# File lib/sequel/adapters/sqlite.rb, line 164
def execute(sql, opts=OPTS, &block)
  _execute(:select, sql, opts, &block)
end
execute_ddl(sql, opts=OPTS) click to toggle source

Drop any prepared statements on the connection when executing DDL. This is because prepared statements lock the table in such a way that you can't drop or alter the table while a prepared statement that references it still exists.

Calls superclass method Sequel::Database#execute_ddl
# File lib/sequel/adapters/sqlite.rb, line 176
def execute_ddl(sql, opts=OPTS)
  synchronize(opts[:server]) do |conn|
    conn.prepared_statements.values.each{|cps, s| cps.close}
    conn.prepared_statements.clear
    super
  end
end
execute_dui(sql, opts=OPTS) click to toggle source

Run the given SQL with the given arguments and return the number of changed rows.

# File lib/sequel/adapters/sqlite.rb, line 169
def execute_dui(sql, opts=OPTS)
  _execute(:update, sql, opts)
end
execute_insert(sql, opts=OPTS) click to toggle source
# File lib/sequel/adapters/sqlite.rb, line 184
def execute_insert(sql, opts=OPTS)
  _execute(:insert, sql, opts)
end
freeze() click to toggle source
Calls superclass method Sequel::SQLite::DatabaseMethods#freeze
# File lib/sequel/adapters/sqlite.rb, line 188
def freeze
  @conversion_procs.freeze
  super
end
to_application_timestamp(s) click to toggle source

Handle Integer and Float arguments, since SQLite can store timestamps as integers and floats.

# File lib/sequel/adapters/sqlite.rb, line 194
def to_application_timestamp(s)
  case s
  when String
    super
  when Integer
    super(Time.at(s).to_s)
  when Float
    super(DateTime.jd(s).to_s)
  else
    raise Sequel::Error, "unhandled type when converting to : #{s.inspect} (#{s.class.inspect})"
  end
end

Private Instance Methods

_execute(type, sql, opts, &block) click to toggle source

Yield an available connection. Rescue any SQLite3::Exceptions and turn them into DatabaseErrors.

# File lib/sequel/adapters/sqlite.rb, line 246
def _execute(type, sql, opts, &block)
  synchronize(opts[:server]) do |conn|
    return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
    log_args = opts[:arguments]
    args = {}
    opts.fetch(:arguments, OPTS).each{|k, v| args[k] = prepared_statement_argument(v)}
    case type
    when :select
      log_connection_yield(sql, conn, log_args){conn.query(sql, args, &block)}
    when :insert
      log_connection_yield(sql, conn, log_args){conn.execute(sql, args)}
      conn.last_insert_row_id
    when :update
      log_connection_yield(sql, conn, log_args){conn.execute_batch(sql, args)}
      conn.changes
    end
  end
rescue SQLite3::Exception => e
  raise_error(e)
end
adapter_initialize() click to toggle source
# File lib/sequel/adapters/sqlite.rb, line 209
def adapter_initialize
  @conversion_procs = SQLITE_TYPES.dup
  @conversion_procs['datetime'] = @conversion_procs['timestamp'] = method(:to_application_timestamp)
  set_integer_booleans
end
connection_pool_default_options() click to toggle source

The SQLite adapter does not need the pool to convert exceptions. Also, force the max connections to 1 if a memory database is being used, as otherwise each connection gets a separate database.

# File lib/sequel/adapters/sqlite.rb, line 270
def connection_pool_default_options
  o = super.dup
  # Default to only a single connection if a memory database is used,
  # because otherwise each connection will get a separate database
  o[:max_connections] = 1 if @opts[:database] == ':memory:' || blank_object?(@opts[:database])
  o
end
database_error_classes() click to toggle source

SQLite3 raises ArgumentError in addition to SQLite3::Exception in some cases, such as operations on a closed database.

# File lib/sequel/adapters/sqlite.rb, line 335
def database_error_classes
  [SQLite3::Exception, ArgumentError]
end
dataset_class_default() click to toggle source
# File lib/sequel/adapters/sqlite.rb, line 339
def dataset_class_default
  Dataset
end
execute_prepared_statement(conn, type, name, opts, &block) click to toggle source

Execute a prepared statement on the database using the given name.

# File lib/sequel/adapters/sqlite.rb, line 296
def execute_prepared_statement(conn, type, name, opts, &block)
  ps = prepared_statement(name)
  sql = ps.prepared_sql
  args = opts[:arguments]
  ps_args = {}
  args.each{|k, v| ps_args[k] = prepared_statement_argument(v)}
  if cpsa = conn.prepared_statements[name]
    cps, cps_sql = cpsa
    if cps_sql != sql
      cps.close
      cps = nil
    end
  end
  unless cps
    cps = log_connection_yield("PREPARE #{name}: #{sql}", conn){conn.prepare(sql)}
    conn.prepared_statements[name] = [cps, sql]
  end
  log_sql = String.new
  log_sql << "EXECUTE #{name}"
  if ps.log_sql
    log_sql << " ("
    log_sql << sql
    log_sql << ")"
  end
  if block
    log_connection_yield(log_sql, conn, args){cps.execute(ps_args, &block)}
  else
    log_connection_yield(log_sql, conn, args){cps.execute!(ps_args){|r|}}
    case type
    when :insert
      conn.last_insert_row_id
    when :update
      conn.changes
    end
  end
end
prepared_statement_argument(arg) click to toggle source
# File lib/sequel/adapters/sqlite.rb, line 278
def prepared_statement_argument(arg)
  case arg
  when Date, DateTime, Time
    literal(arg)[1...-1]
  when SQL::Blob
    arg.to_blob
  when true, false
    if integer_booleans
      arg ? 1 : 0
    else
      literal(arg)[1...-1]
    end
  else
    arg
  end
end
setup_regexp_function(db, how) click to toggle source
# File lib/sequel/adapters/sqlite.rb, line 215
def setup_regexp_function(db, how)
  case how
  when Proc
    # nothing
  when :cached, "cached"
    cache = @opts[:regexp_function_cache] || Hash
    cache = cache.is_a?(Proc) ? cache.call : cache.new
    how = if RUBY_VERSION >= '2.4'
      lambda do |regexp_str, str|
        (cache[regexp_str] ||= Regexp.new(regexp_str)).match?(str)
      end
    else
      lambda do |regexp_str, str|
        (cache[regexp_str] ||= Regexp.new(regexp_str)).match(str)
      end
    end
  else
    how = if RUBY_VERSION >= '2.4'
      lambda{|regexp_str, str| Regexp.new(regexp_str).match?(str)}
    else
      lambda{|regexp_str, str| Regexp.new(regexp_str).match(str)}
    end
  end

  db.create_function("regexp", 2) do |func, regexp_str, str|
    func.result = how.call(regexp_str, str) ? 1 : 0
  end
end
sqlite_error_code(exception) click to toggle source

Support SQLite exception codes if ruby-sqlite3 supports them.

# File lib/sequel/adapters/sqlite.rb, line 345
def sqlite_error_code(exception)
  exception.code if exception.respond_to?(:code)
end