Class: Tina4::Adapters::Sqlite3Adapter

Inherits:
Object
  • Object
show all
Defined in:
lib/tina4/database/sqlite3_adapter.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection_string = nil) ⇒ Sqlite3Adapter

Returns a new instance of Sqlite3Adapter.



8
9
10
11
12
13
# File 'lib/tina4/database/sqlite3_adapter.rb', line 8

def initialize(connection_string = nil)
  @connection = nil
  @db_path = nil
  @in_transaction = false
  connect(connection_string) if connection_string
end

Instance Attribute Details

#connectionObject (readonly)

Returns the value of attribute connection.



6
7
8
# File 'lib/tina4/database/sqlite3_adapter.rb', line 6

def connection
  @connection
end

#db_pathObject (readonly)

Returns the value of attribute db_path.



6
7
8
# File 'lib/tina4/database/sqlite3_adapter.rb', line 6

def db_path
  @db_path
end

Class Method Details

.resolve_path(connection_string) ⇒ Object

Resolve a SQLite URL / path against the project root (cwd). Convention matches Tina4::Drivers::SqliteDriver.resolve_path —three slashes = relative, four = absolute, drive letter = Windows abs.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/tina4/database/sqlite3_adapter.rb', line 28

def self.resolve_path(connection_string)
  return ":memory:" if connection_string == "sqlite::memory:" || connection_string == "sqlite:///:memory:"

  raw = connection_string.to_s
    .sub(/^sqlite3?:\/\/\//, "")
    .sub(/^sqlite3?:\/\//, "")
    .sub(/^sqlite3?:/, "")
  return ":memory:" if raw == ":memory:"

  is_windows_abs = raw.match?(/^[A-Za-z]:[\/\\]/)
  is_unix_abs    = raw.start_with?("/")

  if is_windows_abs || is_unix_abs
    raw
  else
    resolved = File.join(Dir.pwd, raw)
    parent = File.dirname(resolved)
    require "fileutils"
    FileUtils.mkdir_p(parent) unless File.directory?(parent)
    resolved
  end
end

Instance Method Details

#apply_limit(sql, limit, offset = 0) ⇒ Object



152
153
154
# File 'lib/tina4/database/sqlite3_adapter.rb', line 152

def apply_limit(sql, limit, offset = 0)
  "#{sql} LIMIT #{limit} OFFSET #{offset}"
end

#begin_transactionObject

Transaction support



116
117
118
119
120
# File 'lib/tina4/database/sqlite3_adapter.rb', line 116

def begin_transaction
  return if @in_transaction
  @connection.execute("BEGIN TRANSACTION")
  @in_transaction = true
end

#closeObject



51
52
53
54
# File 'lib/tina4/database/sqlite3_adapter.rb', line 51

def close
  @connection.close if @connection
  @connection = nil
end

#columns(table) ⇒ Object

Get column metadata for a table



92
93
94
95
96
97
98
99
100
101
102
# File 'lib/tina4/database/sqlite3_adapter.rb', line 92

def columns(table)
  query("PRAGMA table_info(#{table})").map do |r|
    {
      name: r[:name],
      type: r[:type],
      nullable: r[:notnull] == 0,
      default: r[:dflt_value],
      primary_key: r[:pk] == 1
    }
  end
end

#commitObject



122
123
124
125
126
# File 'lib/tina4/database/sqlite3_adapter.rb', line 122

def commit
  return unless @in_transaction
  @connection.execute("COMMIT")
  @in_transaction = false
end

#connect(connection_string) ⇒ Object



15
16
17
18
19
20
21
22
23
# File 'lib/tina4/database/sqlite3_adapter.rb', line 15

def connect(connection_string)
  require "sqlite3"
  @db_path = self.class.resolve_path(connection_string)
  @connection = SQLite3::Database.new(@db_path)
  @connection.results_as_hash = true
  @connection.execute("PRAGMA journal_mode=WAL")
  @connection.execute("PRAGMA foreign_keys=ON")
  self
end

#connected?Boolean

Returns:

  • (Boolean)


56
57
58
# File 'lib/tina4/database/sqlite3_adapter.rb', line 56

def connected?
  !@connection.nil? && !@connection.closed?
end

#exec(sql, params = []) ⇒ Object

Execute DDL or DML without returning rows



77
78
79
80
# File 'lib/tina4/database/sqlite3_adapter.rb', line 77

def exec(sql, params = [])
  @connection.execute(sql, params)
  { affected_rows: @connection.changes }
end

#fetch(sql, limit = 100, offset = nil) ⇒ Object

Paginated fetch



67
68
69
70
71
72
73
74
# File 'lib/tina4/database/sqlite3_adapter.rb', line 67

def fetch(sql, limit = 100, offset = nil)
  effective_sql = sql
  if limit
    effective_sql = "#{sql} LIMIT #{limit}"
    effective_sql += " OFFSET #{offset}" if offset && offset > 0
  end
  query(effective_sql)
end

#last_insert_idObject

Get last inserted row id



111
112
113
# File 'lib/tina4/database/sqlite3_adapter.rb', line 111

def last_insert_id
  @connection.last_insert_row_id
end

#placeholderObject

Convenience: placeholder for parameterized queries



144
145
146
# File 'lib/tina4/database/sqlite3_adapter.rb', line 144

def placeholder
  "?"
end

#placeholders(count) ⇒ Object



148
149
150
# File 'lib/tina4/database/sqlite3_adapter.rb', line 148

def placeholders(count)
  (["?"] * count).join(", ")
end

#query(sql, params = []) ⇒ Object

Execute a query and return rows as array of symbol-keyed hashes



61
62
63
64
# File 'lib/tina4/database/sqlite3_adapter.rb', line 61

def query(sql, params = [])
  results = @connection.execute(sql, params)
  results.map { |row| symbolize_keys(row) }
end

#rollbackObject



128
129
130
131
132
# File 'lib/tina4/database/sqlite3_adapter.rb', line 128

def rollback
  return unless @in_transaction
  @connection.execute("ROLLBACK")
  @in_transaction = false
end

#table_exists?(table) ⇒ Boolean

Check if a table exists

Returns:

  • (Boolean)


83
84
85
86
87
88
89
# File 'lib/tina4/database/sqlite3_adapter.rb', line 83

def table_exists?(table)
  rows = query(
    "SELECT name FROM sqlite_master WHERE type='table' AND name = ?",
    [table.to_s]
  )
  !rows.empty?
end

#tablesObject

List all user tables



105
106
107
108
# File 'lib/tina4/database/sqlite3_adapter.rb', line 105

def tables
  rows = query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")
  rows.map { |r| r[:name] }
end

#transactionObject



134
135
136
137
138
139
140
141
# File 'lib/tina4/database/sqlite3_adapter.rb', line 134

def transaction
  begin_transaction
  yield self
  commit
rescue => e
  rollback
  raise e
end