Class: Amalgalite::Schema

Inherits:
Object
  • Object
show all
Defined in:
lib/amalgalite/schema.rb

Overview

An object view of the schema in the SQLite database. If the schema changes after this class is created, it has no knowledge of that.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(db, catalog = 'main', master_table = 'sqlite_master') ⇒ Schema

Create a new instance of Schema



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/amalgalite/schema.rb', line 32

def initialize( db, catalog = 'main', master_table = 'sqlite_master' )
  @db             = db
  @catalog        = catalog
  @schema_version = nil
  @tables         = {}
  @views          = {}
  @master_table   = master_table

  if @master_table == 'sqlite_master' then
    @temp_schema = ::Amalgalite::Schema.new( db, 'temp', 'sqlite_temp_master')
  else
    @temp_schema = nil
  end
  load_schema!
end

Instance Attribute Details

#catalogObject (readonly)

The internal database that this schema is for. Most of the time this will be ‘main’ for the main database. For the temp tables, this will be ‘temp’ and for any attached databsae, this is the name of attached database.



21
22
23
# File 'lib/amalgalite/schema.rb', line 21

def catalog
  @catalog
end

#dbObject (readonly)

The Amalagalite::Database this schema is associated with.



27
28
29
# File 'lib/amalgalite/schema.rb', line 27

def db
  @db
end

#schema_versionObject (readonly)

The schema_version at the time this schema was taken.



24
25
26
# File 'lib/amalgalite/schema.rb', line 24

def schema_version
  @schema_version
end

Instance Method Details

#catalog_master_tableObject



48
49
50
# File 'lib/amalgalite/schema.rb', line 48

def catalog_master_table
  "#{catalog}.#{@master_table}"
end

#current_versionObject



62
63
64
# File 'lib/amalgalite/schema.rb', line 62

def current_version
  @db.first_value_from("PRAGMA #{catalog}.schema_version")
end

#dirty?Boolean

Returns:



56
57
58
59
60
# File 'lib/amalgalite/schema.rb', line 56

def dirty?()
  return true  if (@schema_version != self.current_version)
  return false unless @temp_schema
  return @temp_schema.dirty?
end

#load_columns(table) ⇒ Object

load all the columns for a particular table



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/amalgalite/schema.rb', line 154

def load_columns( table )
  cols = {}
  idx = 0
  @db.execute("PRAGMA #{catalog}.table_info(#{@db.quote(table.name)})") do |row|
    col = Amalgalite::Column.new( catalog,  table.name, row['name'], row['cid'])

    col.default_value       = row['dflt_value']

    col.declared_data_type  = row['type']
    col.not_null_constraint = row['notnull']
    col.primary_key         = row['pk']

    # need to remove leading and trailing ' or " from the default value
    if col.default_value and col.default_value.kind_of?( String ) and ( col.default_value.length >= 2 ) then
      fc = col.default_value[0].chr
      lc = col.default_value[-1].chr
      if fc == lc and ( fc == "'" || fc == '"' ) then
        col.default_value = col.default_value[1..-2]
      end
    end

    unless table.temporary? then
      # get more exact information
      @db.api.( catalog, table.name, col.name ).each_pair do |key, value|
        col.send("#{key}=", value)
      end
    end
    col.schema = self
    cols[col.name] = col
    idx += 1
  end
  return cols
end

#load_indexes(table) ⇒ Object

load all the indexes for a particular table



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/amalgalite/schema.rb', line 123

def load_indexes( table )
  indexes = {}

  @db.prepare("SELECT name, sql FROM #{catalog_master_table} WHERE type ='index' and tbl_name = $name") do |idx_stmt|
    idx_stmt.execute( "$name" => table.name) do |idx_info|
      indexes[idx_info['name']] = Amalgalite::Index.new( idx_info['name'], idx_info['sql'], table )
    end
  end

  @db.execute("PRAGMA index_list( #{@db.quote(table.name)} );") do |idx_list|
    idx = indexes[idx_list['name']]

    # temporary indexes do not show up in the previous list
    if idx.nil? then
      idx = Amalgalite::Index.new( idx_list['name'], nil, table )
      indexes[idx_list['name']] = idx
    end

    idx.sequence_number = idx_list['seq']
    idx.unique          = Boolean.to_bool( idx_list['unique'] )

    @db.execute("PRAGMA index_info( #{@db.quote(idx.name)} );") do |col_info|
      idx.columns << table.columns[col_info['name']]
    end
  end
  return indexes
end

#load_schema!Object

load the schema from the database



68
69
70
71
72
73
74
75
76
# File 'lib/amalgalite/schema.rb', line 68

def load_schema!
  load_tables
  load_views
  if @temp_schema then
    @temp_schema.load_schema!
  end
  @schema_version = self.current_version
  nil
end

#load_table(table_name) ⇒ Object

Load a single table



106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/amalgalite/schema.rb', line 106

def load_table( table_name )
  rows = @db.execute("SELECT tbl_name, sql FROM #{catalog_master_table} WHERE type = 'table' AND tbl_name = ?", table_name)
  table_info = rows.first
  table = nil
  if table_info then
    table = Amalgalite::Table.new( table_info['tbl_name'], table_info['sql'] )
    table.schema = self
    table.columns = load_columns( table )
    table.indexes = load_indexes( table )
    @tables[table.name] = table
  end
  return table
end

#load_tablesObject

load all the tables



94
95
96
97
98
99
100
101
102
# File 'lib/amalgalite/schema.rb', line 94

def load_tables
  @tables = {}
  @db.execute("SELECT tbl_name FROM #{catalog_master_table} WHERE type = 'table' AND name != 'sqlite_sequence'") do |table_info|
    table = load_table( table_info['tbl_name'] )
    table.indexes = load_indexes( table )
    @tables[table.name] = table
  end
  return @tables
end

#load_view(name) ⇒ Object

load a single view



206
207
208
209
210
211
212
# File 'lib/amalgalite/schema.rb', line 206

def load_view( name )
  rows = @db.execute("SELECT name, sql FROM #{catalog_master_table} WHERE type = 'view' AND name = ?", name )
  view_info = rows.first
  view = Amalgalite::View.new( view_info['name'], view_info['sql'] )
  view.schema = self
  return view
end

#load_viewsObject

load all the views for the database



217
218
219
220
221
222
223
# File 'lib/amalgalite/schema.rb', line 217

def load_views
  @db.execute("SELECT name, sql FROM #{catalog_master_table} WHERE type = 'view'") do |view_info|
    view = load_view( view_info['name'] )
    @views[view.name] = view
  end
  return @views
end

#tablesObject

return the tables, reloading if dirty. If there is a temp table and a normal table with the same name, then the temp table is the one that is returned in the hash.



82
83
84
85
86
87
88
89
# File 'lib/amalgalite/schema.rb', line 82

def tables
  load_schema! if dirty?
  t = @tables
  if @temp_schema then
    t = @tables.merge( @temp_schema.tables )
  end
  return t
end

#temporary?Boolean

Returns:



52
53
54
# File 'lib/amalgalite/schema.rb', line 52

def temporary?
  catalog == "temp"
end

#viewsObject

return the views, reloading if dirty

If there is a temp view, and a regular view of the same name, then the temporary view is the one that is returned in the hash.



194
195
196
197
198
199
200
201
# File 'lib/amalgalite/schema.rb', line 194

def views
  reload_schema! if dirty?
  v = @views
  if @temp_schema then
    v = @views.merge( @temp_schema.views )
  end
  return v
end