Module: EnhancedSQLite3::SupportsVirtualColumns::Adapter

Defined in:
lib/enhanced_sqlite3/supports_virtual_columns.rb

Constant Summary collapse

GENERATED_ALWAYS_AS_REGEX =
/.*"(\w+)".+GENERATED ALWAYS AS \((.+)\) (?:STORED|VIRTUAL)/i

Instance Method Summary collapse

Instance Method Details

#extract_generated_type(field) ⇒ Object



117
118
119
120
121
122
# File 'lib/enhanced_sqlite3/supports_virtual_columns.rb', line 117

def extract_generated_type(field)
  case field["hidden"]
  when 2 then :virtual
  when 3 then :stored
  end
end

#invalid_alter_table_type?(type, options) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
61
62
63
# File 'lib/enhanced_sqlite3/supports_virtual_columns.rb', line 58

def invalid_alter_table_type?(type, options)
  type.to_sym == :primary_key || options[:primary_key] ||
    options[:null] == false && options[:default].nil?
  options[:null] == false && options[:default].nil? ||
    (type.to_sym == :virtual && options[:stored])
end

#new_column_from_field(table_name, field, definitions) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/enhanced_sqlite3/supports_virtual_columns.rb', line 19

def new_column_from_field(table_name, field, definitions)
  default = field["dflt_value"]

   = (field["type"])
  default_value = extract_value_from_default(default)
  generated_type = extract_generated_type(field)

  default_function = if generated_type.present?
    default
  else
    extract_default_function(default_value, default)
  end

  rowid = is_column_the_rowid?(field, definitions) if definitions

  ActiveRecord::ConnectionAdapters::SQLite3::Column.new(
    field["name"],
    default_value,
    ,
    field["notnull"].to_i == 0,
    default_function,
    collation: field["collation"],
    auto_increment: field["auto_increment"],
    rowid: rowid,
    generated_type: generated_type
  )
end

#supports_virtual_columns?Boolean

Returns:

  • (Boolean)


15
16
17
# File 'lib/enhanced_sqlite3/supports_virtual_columns.rb', line 15

def supports_virtual_columns?
  database_version >= "3.31.0"
end

#table_structure(table_name) ⇒ Object Also known as: column_definitions

Raises:

  • (ActiveRecord::StatementInvalid)


47
48
49
50
51
52
53
54
55
# File 'lib/enhanced_sqlite3/supports_virtual_columns.rb', line 47

def table_structure(table_name)
  structure = if supports_virtual_columns?
    internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA")
  else
    internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
  end
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
  table_structure_with_collation(table_name, structure)
end

#table_structure_with_collation(table_name, basic_structure) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/enhanced_sqlite3/supports_virtual_columns.rb', line 66

def table_structure_with_collation(table_name, basic_structure)
  collation_hash = {}
  auto_increments = {}
  generated_columns = {}
  sql = <<~SQL
    SELECT sql FROM
      (SELECT * FROM sqlite_master UNION ALL
       SELECT * FROM sqlite_temp_master)
    WHERE type = 'table' AND name = #{quote(table_name)}
  SQL

  # Result will have following sample string
  # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  #                       "password_digest" varchar COLLATE "NOCASE");
  result = query_value(sql, "SCHEMA")

  if result
    # Splitting with left parentheses and discarding the first part will return all
    # columns separated with comma(,).
    columns_string = result.split("(", 2).last

    columns_string.split(",").each do |column_string|
      # This regex will match the column name and collation type and will save
      # the value in $1 and $2 respectively.
      collation_hash[$1] = $2 if ActiveRecord::ConnectionAdapters::SQLite3Adapter::COLLATE_REGEX =~ column_string
      auto_increments[$1] = true if ActiveRecord::ConnectionAdapters::SQLite3Adapter::PRIMARY_KEY_AUTOINCREMENT_REGEX =~ column_string
      generated_columns[$1] = $2 if GENERATED_ALWAYS_AS_REGEX =~ column_string
    end

    basic_structure.map do |column|
      column_name = column["name"]

      if collation_hash.has_key? column_name
        column["collation"] = collation_hash[column_name]
      end

      if auto_increments.has_key?(column_name)
        column["auto_increment"] = true
      end

      if generated_columns.has_key?(column_name)
        column["dflt_value"] = generated_columns[column_name]
      end

      column
    end
  else
    basic_structure.to_a
  end
end