lib/litedb.rb in litedb-0.1.0 vs lib/litedb.rb in litedb-0.2.0
- old
+ new
@@ -1,8 +1,100 @@
# frozen_string_literal: true
require_relative "litedb/version"
-module Litedb
+require "sqlite3"
+require "litescheduler"
+
+class Litedb < SQLite3::Database
class Error < StandardError; end
- # Your code goes here...
+
+ DEFAULT_FILE = ":memory:"
+ DEFAULT_OPTIONS = {
+ synchronous: :NORMAL,
+ mmap_size: 32 * 1024, # 32 kilobytes
+ journal_size_limit: 64 * 1024 * 1024 # 64 megabytes
+ }.freeze
+
+ attr_reader :config
+
+ def initialize(file = nil, options = {}, zvfs = nil)
+ @scheduler = Litescheduler.instance
+ file ||= DEFAULT_FILE
+ options = options.transform_keys { |key|
+ begin
+ key.to_sym
+ rescue
+ key
+ end
+ }
+ @config = DEFAULT_OPTIONS.merge(options)
+
+ super(file, @config, zvfs)
+
+ set_pragmas
+ run_migrations
+ prepare_statements
+ end
+
+ def run_statement(statement, *args)
+ statements[statement].execute!(*args)
+ end
+
+ private
+
+ def set_pragmas
+ # set a custom busy handler to override the `busy_timeout`
+ # this ensures we either switch to another execution context or try again to connect
+ # https://www.sqlite.org/pragma.html#pragma_busy_timeout
+ busy_handler { @scheduler.switch || sleep(rand * 0.001) }
+ # Journal mode WAL allows for greater concurrency (many readers + one writer)
+ # https://www.sqlite.org/pragma.html#pragma_journal_mode
+ self.journal_mode = :WAL
+ # level of database durability
+ # 2 = "FULL" (sync on every write)
+ # 1 = "NORMAL" (sync every 1000 written pages)
+ # 0 = "OFF" (don't sync)
+ # https://www.sqlite.org/pragma.html#pragma_synchronous
+ self.synchronous = @config[:synchronous]
+ # set the global memory map so all processes can share data
+ # https://www.sqlite.org/pragma.html#pragma_mmap_size
+ # https://www.sqlite.org/mmap.html
+ self.mmap_size = @config[:mmap_size]
+ # impose a limit on the WAL file to prevent unlimited growth (with a negative impact on read performance as well)
+ # https://www.sqlite.org/pragma.html#pragma_journal_size_limit
+ self.journal_size_limit = @config[:journal_size_limit]
+ end
+
+ def run_migrations
+ return if @config[:migrations].nil?
+
+ migrations = if @config[:migrations].is_a?(Hash)
+ @config[:migrations].values
+ elsif @config[:migrations].is_a?(Array)
+ @config[:migrations]
+ else
+ raise Error.new("`migrations` option must be either a Hash or an Array")
+ end
+
+ transaction(:immediate) do
+ migrations.each do |sql|
+ execute(clean_sql(sql))
+ end
+ end
+ end
+
+ def prepare_statements
+ instance_variable_set(:@statements, {})
+ self.class.attr_reader(:statements) unless respond_to?(:statements)
+
+ return if @config[:statements].nil?
+
+ @config[:statements].each do |key, sql|
+ statements[key.to_sym] = prepare(clean_sql(sql))
+ end
+ end
+
+ def clean_sql(sql)
+ sql.gsub(/[[:space:]]+/, " ").strip
+ end
end