og 0.31.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/doc/{AUTHORS → CONTRIBUTORS} +26 -10
- data/doc/LICENSE +2 -3
- data/doc/RELEASES +56 -7
- data/doc/tutorial.txt +15 -15
- data/lib/glue/cacheable.rb +2 -5
- data/lib/glue/hierarchical.rb +1 -4
- data/lib/glue/optimistic_locking.rb +0 -2
- data/lib/glue/orderable.rb +79 -75
- data/lib/glue/revisable.rb +19 -24
- data/lib/glue/searchable.rb +0 -2
- data/lib/glue/taggable.rb +31 -29
- data/lib/glue/timestamped.rb +4 -2
- data/lib/og.rb +50 -29
- data/lib/og/adapter.rb +19 -0
- data/lib/og/adapter/mysql.rb +212 -0
- data/lib/og/adapter/mysql/override.rb +34 -0
- data/lib/og/adapter/mysql/script.rb +15 -0
- data/lib/og/adapter/mysql/utils.rb +40 -0
- data/lib/og/adapter/postgresql.rb +231 -0
- data/lib/og/adapter/postgresql/override.rb +117 -0
- data/lib/og/adapter/postgresql/script.rb +15 -0
- data/lib/og/adapter/postgresql/utils.rb +35 -0
- data/lib/og/adapter/sqlite.rb +132 -0
- data/lib/og/adapter/sqlite/override.rb +33 -0
- data/lib/og/adapter/sqlite/script.rb +15 -0
- data/lib/og/collection.rb +35 -7
- data/lib/og/{evolution.rb → dump.rb} +4 -5
- data/lib/og/entity.rb +102 -173
- data/lib/og/entity/clone.rb +119 -0
- data/lib/og/errors.rb +0 -2
- data/lib/og/manager.rb +85 -37
- data/lib/og/relation.rb +52 -34
- data/lib/og/relation/belongs_to.rb +0 -2
- data/lib/og/relation/has_many.rb +27 -4
- data/lib/og/relation/joins_many.rb +41 -14
- data/lib/og/relation/many_to_many.rb +10 -0
- data/lib/og/relation/refers_to.rb +22 -5
- data/lib/og/store.rb +80 -86
- data/lib/og/store/sql.rb +710 -713
- data/lib/og/store/sql/evolution.rb +119 -0
- data/lib/og/store/sql/join.rb +155 -0
- data/lib/og/store/sql/utils.rb +149 -0
- data/lib/og/test/assertions.rb +1 -3
- data/lib/og/test/testcase.rb +0 -2
- data/lib/og/types.rb +2 -5
- data/lib/og/validation.rb +6 -9
- data/test/{og/mixin → glue}/tc_hierarchical.rb +3 -13
- data/test/glue/tc_og_paginate.rb +47 -0
- data/test/{og/mixin → glue}/tc_optimistic_locking.rb +2 -12
- data/test/{og/mixin → glue}/tc_orderable.rb +15 -23
- data/test/glue/tc_orderable2.rb +47 -0
- data/test/glue/tc_revisable.rb +3 -3
- data/test/{og/mixin → glue}/tc_taggable.rb +20 -10
- data/test/{og/mixin → glue}/tc_timestamped.rb +2 -12
- data/test/glue/tc_webfile.rb +36 -0
- data/test/og/CONFIG.rb +8 -11
- data/test/og/multi_validations_model.rb +14 -0
- data/test/og/store/tc_filesys.rb +3 -1
- data/test/og/store/tc_kirby.rb +16 -13
- data/test/og/store/tc_sti.rb +11 -11
- data/test/og/store/tc_sti2.rb +79 -0
- data/test/og/tc_build.rb +41 -0
- data/test/og/tc_cacheable.rb +3 -2
- data/test/og/tc_has_many.rb +96 -0
- data/test/og/tc_inheritance.rb +6 -4
- data/test/og/tc_joins_many.rb +93 -0
- data/test/og/tc_multi_validations.rb +5 -7
- data/test/og/tc_multiple.rb +7 -6
- data/test/og/tc_override.rb +13 -7
- data/test/og/tc_primary_key.rb +30 -0
- data/test/og/tc_relation.rb +8 -14
- data/test/og/tc_reldelete.rb +163 -0
- data/test/og/tc_reverse.rb +17 -14
- data/test/og/tc_scoped.rb +3 -11
- data/test/og/tc_setup.rb +13 -11
- data/test/og/tc_store.rb +21 -28
- data/test/og/tc_validation2.rb +2 -2
- data/test/og/tc_validation_loop.rb +17 -15
- metadata +109 -103
- data/INSTALL +0 -91
- data/ProjectInfo +0 -51
- data/README +0 -177
- data/doc/config.txt +0 -28
- data/examples/README +0 -23
- data/examples/mysql_to_psql.rb +0 -71
- data/examples/run.rb +0 -271
- data/lib/glue/tree.rb +0 -218
- data/lib/og/store/alpha/filesys.rb +0 -110
- data/lib/og/store/alpha/memory.rb +0 -295
- data/lib/og/store/alpha/sqlserver.rb +0 -256
- data/lib/og/store/kirby.rb +0 -490
- data/lib/og/store/mysql.rb +0 -415
- data/lib/og/store/psql.rb +0 -875
- data/lib/og/store/sqlite.rb +0 -348
- data/lib/og/store/sqlite2.rb +0 -241
- data/setup.rb +0 -1585
- data/test/og/tc_sti_find.rb +0 -35
data/lib/og/store/sqlite.rb
DELETED
@@ -1,348 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require 'sqlite3'
|
3
|
-
rescue Object => ex
|
4
|
-
Logger.error 'Ruby-Sqlite3 bindings are not installed!'
|
5
|
-
Logger.error ex
|
6
|
-
end
|
7
|
-
|
8
|
-
require 'fileutils'
|
9
|
-
require 'set'
|
10
|
-
|
11
|
-
require 'og/store/sql'
|
12
|
-
|
13
|
-
#--
|
14
|
-
# Customize the standard Sqlite3 resultset to make
|
15
|
-
# more compatible with Og.
|
16
|
-
#++
|
17
|
-
|
18
|
-
class SQLite3::ResultSet # :nodoc: all
|
19
|
-
alias_method :blank?, :eof?
|
20
|
-
|
21
|
-
def each_row
|
22
|
-
each do |row|
|
23
|
-
yield(row, 0)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def first_value
|
28
|
-
val = self.next[0]
|
29
|
-
close
|
30
|
-
return val
|
31
|
-
end
|
32
|
-
|
33
|
-
alias_method :fields, :columns
|
34
|
-
end
|
35
|
-
|
36
|
-
module Og
|
37
|
-
|
38
|
-
# A Store that persists objects into an Sqlite3 database.
|
39
|
-
# To read documentation about the methods, consult the documentation
|
40
|
-
# for SqlStore and Store.
|
41
|
-
|
42
|
-
class SqliteStore < SqlStore
|
43
|
-
|
44
|
-
# Override if needed.
|
45
|
-
|
46
|
-
def self.db_filename(options)
|
47
|
-
options[:name] ||= 'data'
|
48
|
-
"#{options[:name]}.db"
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.destroy(options)
|
52
|
-
begin
|
53
|
-
FileUtils.rm(db_filename(options))
|
54
|
-
super
|
55
|
-
rescue Object
|
56
|
-
Logger.info "Cannot drop '#{options[:name]}'!"
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
# Initialize the Sqlite store.
|
61
|
-
# This store provides a default name.
|
62
|
-
|
63
|
-
def initialize(options)
|
64
|
-
super
|
65
|
-
@conn = SQLite3::Database.new(self.class.db_filename(options))
|
66
|
-
end
|
67
|
-
|
68
|
-
def close
|
69
|
-
@conn.close
|
70
|
-
super
|
71
|
-
end
|
72
|
-
|
73
|
-
def enchant(klass, manager)
|
74
|
-
if klass.ann.self.primary_key.symbol == :oid
|
75
|
-
unless klass.properties.include? :oid
|
76
|
-
klass.property :oid, Fixnum, :sql => 'integer PRIMARY KEY'
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
super
|
81
|
-
end
|
82
|
-
|
83
|
-
def query(sql)
|
84
|
-
Logger.debug sql if $DBG
|
85
|
-
return @conn.query(sql)
|
86
|
-
rescue Exception => ex
|
87
|
-
handle_sql_exception(ex, sql)
|
88
|
-
end
|
89
|
-
|
90
|
-
def exec(sql)
|
91
|
-
Logger.debug sql if $DBG
|
92
|
-
@conn.query(sql).close
|
93
|
-
rescue => ex
|
94
|
-
handle_sql_exception(ex, sql)
|
95
|
-
end
|
96
|
-
|
97
|
-
def start
|
98
|
-
@conn.transaction if @transaction_nesting < 1
|
99
|
-
@transaction_nesting += 1
|
100
|
-
end
|
101
|
-
|
102
|
-
def commit
|
103
|
-
@transaction_nesting -= 1
|
104
|
-
@conn.commit if @transaction_nesting < 1
|
105
|
-
end
|
106
|
-
|
107
|
-
def rollback
|
108
|
-
@transaction_nesting -= 1
|
109
|
-
@conn.rollback if @transaction_nesting < 1
|
110
|
-
end
|
111
|
-
|
112
|
-
def last_insert_rowid
|
113
|
-
conn.query("SELECT last_insert_rowid()").first_value.to_i
|
114
|
-
end
|
115
|
-
|
116
|
-
def sql_update(sql)
|
117
|
-
exec(sql)
|
118
|
-
@conn.changes
|
119
|
-
end
|
120
|
-
|
121
|
-
def list_tables
|
122
|
-
rset = @conn.query("select name from sqlite_master where type = 'table'")
|
123
|
-
rset.map { |r| r[0] }
|
124
|
-
end
|
125
|
-
|
126
|
-
def list_fields(table)
|
127
|
-
rset = @conn.query("pragma table_info(#{table})")
|
128
|
-
columns = rset.columns
|
129
|
-
rows = rset.entries
|
130
|
-
idx = columns.index('name')
|
131
|
-
rows.map { |r| r[idx] }
|
132
|
-
end
|
133
|
-
|
134
|
-
# Deserialize one object from the ResultSet.
|
135
|
-
|
136
|
-
def read_one(res, klass, options = nil)
|
137
|
-
return nil if res.blank?
|
138
|
-
|
139
|
-
if options and join_relations = options[:include]
|
140
|
-
join_relations = [join_relations].flatten.collect do |n|
|
141
|
-
klass.relation(n)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
res_row = res.next
|
146
|
-
|
147
|
-
# causes STI classes to come back as the correct child class
|
148
|
-
# if accessed from the superclass.
|
149
|
-
|
150
|
-
klass = Og::Entity::entity_from_string(res_row[0]) if klass.schema_inheritance?
|
151
|
-
obj = klass.og_allocate(res_row, 0)
|
152
|
-
|
153
|
-
if options and options[:select]
|
154
|
-
read_row(obj, res, res_row, 0)
|
155
|
-
else
|
156
|
-
obj.og_read(res_row)
|
157
|
-
read_join_relations(obj, res_row, 0, join_relations) if join_relations
|
158
|
-
end
|
159
|
-
|
160
|
-
return obj
|
161
|
-
|
162
|
-
ensure
|
163
|
-
res.close
|
164
|
-
end
|
165
|
-
|
166
|
-
private
|
167
|
-
|
168
|
-
def property_to_field(klass, p)
|
169
|
-
field = []
|
170
|
-
|
171
|
-
klass.index(p.symbol) if p.index
|
172
|
-
|
173
|
-
field = field_for_property(p)
|
174
|
-
|
175
|
-
if p.sql
|
176
|
-
field << " #{p.sql}"
|
177
|
-
else
|
178
|
-
field << " #{type_for_class(p.klass)}"
|
179
|
-
field << " UNIQUE" if p.unique
|
180
|
-
field << " DEFAULT #{p.default.inspect} NOT NULL" if p.default
|
181
|
-
field << " #{p.extra_sql}" if p.extra_sql
|
182
|
-
end
|
183
|
-
|
184
|
-
field
|
185
|
-
end
|
186
|
-
|
187
|
-
def create_table_ddl(klass, type = nil, table_name = nil)
|
188
|
-
fields = fields_for_class(klass)
|
189
|
-
type ||= ''
|
190
|
-
table_name ||= klass::OGTABLE
|
191
|
-
|
192
|
-
sql = "CREATE #{type} TABLE #{table_name} (#{fields.join(', ')}"
|
193
|
-
|
194
|
-
# Create table constraints.
|
195
|
-
|
196
|
-
if constraints = klass.ann.self[:sql_constraint]
|
197
|
-
sql << ", #{constraints.join(', ')}"
|
198
|
-
end
|
199
|
-
|
200
|
-
sql << ")"
|
201
|
-
|
202
|
-
# Create indices.
|
203
|
-
|
204
|
-
if indices = klass.ann.self[:index]
|
205
|
-
for data in indices
|
206
|
-
idx, options = *data
|
207
|
-
idx = idx.to_s
|
208
|
-
pre_sql, post_sql = options[:pre], options[:post]
|
209
|
-
idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
|
210
|
-
sql << " CREATE #{pre_sql} INDEX #{klass::OGTABLE}_#{idxname}_idx #{post_sql} ON #{klass::OGTABLE} (#{idx});"
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
sql
|
215
|
-
end
|
216
|
-
|
217
|
-
def create_table(klass)
|
218
|
-
unless list_tables.include?(klass::OGTABLE)
|
219
|
-
sql = create_table_ddl(klass)
|
220
|
-
|
221
|
-
@conn.query(sql).close
|
222
|
-
Logger.info "Created table '#{klass::OGTABLE}'."
|
223
|
-
else
|
224
|
-
Logger.debug "Table '#{klass::OGTABLE}' already exists" if $DBG
|
225
|
-
|
226
|
-
actual_fields = Set.new(list_fields(klass::OGTABLE))
|
227
|
-
defined_fields = Set.new(fields_for_class(klass).map { |f| f[0, f.index(' ')] } )
|
228
|
-
|
229
|
-
unless defined_fields == actual_fields
|
230
|
-
Logger.debug "Field mismatch in '#{klass::OGTABLE}'. Attempting to correct..." if $DBG
|
231
|
-
|
232
|
-
fields_to_add = defined_fields - actual_fields
|
233
|
-
fields_to_delete = actual_fields - defined_fields
|
234
|
-
|
235
|
-
fields_to_add.each do |field|
|
236
|
-
if @options[:evolve_schema] == true
|
237
|
-
Logger.debug "Adding field '#{field}' to '#{klass::OGTABLE}'" if $DBG
|
238
|
-
sql = "ALTER TABLE #{klass::OGTABLE} ADD COLUMN #{property_to_field(klass, klass.properties[field.to_sym])}"
|
239
|
-
@conn.query(sql)
|
240
|
-
@conn.query("VACUUM #{klass::OGTABLE}")
|
241
|
-
else
|
242
|
-
Logger.warn "Table '#{klass::OGTABLE} is missing field '#{field}' and :evolve_schema is not set to true or :cautious!"
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
# Sqlite3 does _NOT_ support removing columns via alter table. You have to drop the table.
|
247
|
-
|
248
|
-
unless fields_to_delete.empty?
|
249
|
-
if (@options[:evolve_schema] == true) and (@options[:evolve_schema_cautious] == false)
|
250
|
-
Logger.warn "Removing obsolete fields '#{fields_to_delete.to_a.join(', ')}' from '#{klass::OGTABLE}'!"
|
251
|
-
|
252
|
-
field_list = klass.properties.values.map { |f| field_for_property(f) }.join(', ')
|
253
|
-
backup_table = "#{klass::OGTABLE}_backup"
|
254
|
-
|
255
|
-
sql_transaction = [
|
256
|
-
"BEGIN TRANSACTION",
|
257
|
-
"#{create_table_ddl(klass, 'temporary', backup_table)}",
|
258
|
-
"INSERT INTO #{backup_table} SELECT #{field_list} FROM #{klass::OGTABLE}",
|
259
|
-
"DROP TABLE #{klass::OGTABLE}",
|
260
|
-
"#{create_table_ddl(klass)}",
|
261
|
-
"INSERT INTO #{klass::OGTABLE} SELECT #{field_list} FROM #{backup_table}",
|
262
|
-
"DROP TABLE #{backup_table}",
|
263
|
-
"COMMIT"
|
264
|
-
]
|
265
|
-
|
266
|
-
sql_transaction.each do |s|
|
267
|
-
Logger.debug s if $DBG
|
268
|
-
@conn.query(s)
|
269
|
-
end
|
270
|
-
else
|
271
|
-
Logger.warn "Table '#{klass::OGTABLE}' contains obsolete fields '#{fields_to_delete.to_a.join(', ')}' " +
|
272
|
-
"and :evolve_schema is not set to true or is in cautious mode!"
|
273
|
-
end
|
274
|
-
end
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
# Create join tables if needed. Join tables are used in
|
279
|
-
# 'many_to_many' relations.
|
280
|
-
|
281
|
-
if join_tables = klass.ann.self[:join_tables]
|
282
|
-
for info in join_tables
|
283
|
-
begin
|
284
|
-
create_join_table_sql(info).each do |sql|
|
285
|
-
@conn.query(sql).close
|
286
|
-
end
|
287
|
-
Logger.debug "Created jointable '#{info[:table]}'." if $DBG
|
288
|
-
rescue Object => ex
|
289
|
-
# gmosx: any idea how to better test this?
|
290
|
-
if ex.to_s =~ /table .* already exists/i
|
291
|
-
Logger.debug 'Join table already exists' if $DBG
|
292
|
-
else
|
293
|
-
raise
|
294
|
-
end
|
295
|
-
end
|
296
|
-
end
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
def create_field_map(klass)
|
301
|
-
res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
|
302
|
-
map = {}
|
303
|
-
|
304
|
-
fields = res.columns
|
305
|
-
|
306
|
-
# Check if the field should be ignored.
|
307
|
-
ignore = klass.ann[:self][:ignore_field] || klass.ann[:self][:ignore_fields] || klass.ann[:self][:ignore_columns]
|
308
|
-
|
309
|
-
res.fields.size.times do |i|
|
310
|
-
field_name = fields[i].to_sym
|
311
|
-
unless (ignore and ignore.include?(field_name))
|
312
|
-
map[field_name] = i
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
return map
|
317
|
-
ensure
|
318
|
-
res.close if res
|
319
|
-
end
|
320
|
-
|
321
|
-
def eval_og_insert(klass)
|
322
|
-
pk = klass.primary_key.symbol
|
323
|
-
props = klass.properties.values.dup
|
324
|
-
values = props.collect { |p| write_prop(p) }.join(',')
|
325
|
-
|
326
|
-
if klass.schema_inheritance?
|
327
|
-
props << Property.new(:symbol => :ogtype, :klass => String)
|
328
|
-
values << ", '#{klass}'"
|
329
|
-
end
|
330
|
-
|
331
|
-
sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| field_for_property(p)}.join(',')}) VALUES (#{values})"
|
332
|
-
|
333
|
-
klass.class_eval %{
|
334
|
-
def og_insert(store)
|
335
|
-
#{::Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
|
336
|
-
store.query("#{sql}").close
|
337
|
-
@#{pk} = store.last_insert_rowid
|
338
|
-
#{::Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
|
339
|
-
end
|
340
|
-
}
|
341
|
-
end
|
342
|
-
|
343
|
-
end
|
344
|
-
|
345
|
-
end
|
346
|
-
|
347
|
-
# * George Moschovitis <gm@navel.gr>
|
348
|
-
# * Ghislain Mary
|
data/lib/og/store/sqlite2.rb
DELETED
@@ -1,241 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# TODO: extend from sqlite.rb to avoid code duplication.
|
3
|
-
#++
|
4
|
-
|
5
|
-
begin
|
6
|
-
require 'sqlite'
|
7
|
-
rescue Object => ex
|
8
|
-
Logger.error 'Ruby-Sqlite bindings are not installed!'
|
9
|
-
Logger.error ex
|
10
|
-
end
|
11
|
-
|
12
|
-
require 'fileutils'
|
13
|
-
|
14
|
-
require 'og/store/sql'
|
15
|
-
|
16
|
-
#--
|
17
|
-
# Customize the standard Sqlite2 resultset to make
|
18
|
-
# more compatible with Og.
|
19
|
-
#++
|
20
|
-
|
21
|
-
class SQLite::ResultSet # :nodoc: all
|
22
|
-
alias_method :blank?, :eof?
|
23
|
-
|
24
|
-
def each_row
|
25
|
-
each do |row|
|
26
|
-
yield(row, 0)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def first_value
|
31
|
-
val = self.next[0]
|
32
|
-
close
|
33
|
-
return val
|
34
|
-
end
|
35
|
-
|
36
|
-
alias_method :fields, :columns
|
37
|
-
end
|
38
|
-
|
39
|
-
module Og
|
40
|
-
|
41
|
-
# A Store that persists objects into an Sqlite2 database.
|
42
|
-
# To read documentation about the methods, consult the documentation
|
43
|
-
# for SqlStore and Store.
|
44
|
-
|
45
|
-
class Sqlite2Store < SqlStore
|
46
|
-
|
47
|
-
# Override if needed.
|
48
|
-
|
49
|
-
def self.db_filename(options)
|
50
|
-
options[:name] ||= 'data'
|
51
|
-
"#{options[:name]}.db"
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.destroy(options)
|
55
|
-
begin
|
56
|
-
FileUtils.rm(db_filename(options))
|
57
|
-
super
|
58
|
-
rescue Object
|
59
|
-
Logger.info "Cannot drop '#{options[:name]}'!"
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Initialize the Sqlite store.
|
64
|
-
# This store provides a default name.
|
65
|
-
|
66
|
-
def initialize(options)
|
67
|
-
super
|
68
|
-
@conn = SQLite::Database.new(self.class.db_filename(options))
|
69
|
-
end
|
70
|
-
|
71
|
-
def close
|
72
|
-
@conn.close
|
73
|
-
super
|
74
|
-
end
|
75
|
-
|
76
|
-
def enchant(klass, manager)
|
77
|
-
if klass.ann.self.primary_key.symbol == :oid
|
78
|
-
unless klass.properties.include? :oid
|
79
|
-
klass.property :oid, Fixnum, :sql => 'integer PRIMARY KEY'
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
super
|
84
|
-
end
|
85
|
-
|
86
|
-
def query(sql)
|
87
|
-
Logger.debug sql if $DBG
|
88
|
-
return @conn.query(sql)
|
89
|
-
rescue Exception => ex
|
90
|
-
handle_sql_exception(ex, sql)
|
91
|
-
end
|
92
|
-
|
93
|
-
def exec(sql)
|
94
|
-
Logger.debug sql if $DBG
|
95
|
-
@conn.query(sql).close
|
96
|
-
rescue => ex
|
97
|
-
handle_sql_exception(ex, sql)
|
98
|
-
end
|
99
|
-
|
100
|
-
def start
|
101
|
-
@conn.transaction if @transaction_nesting < 1
|
102
|
-
@transaction_nesting += 1
|
103
|
-
end
|
104
|
-
|
105
|
-
def commit
|
106
|
-
@transaction_nesting -= 1
|
107
|
-
@conn.commit if @transaction_nesting < 1
|
108
|
-
end
|
109
|
-
|
110
|
-
def rollback
|
111
|
-
@transaction_nesting -= 1
|
112
|
-
@conn.rollback if @transaction_nesting < 1
|
113
|
-
end
|
114
|
-
|
115
|
-
def last_insert_rowid
|
116
|
-
conn.query("SELECT last_insert_rowid()").first_value.to_i
|
117
|
-
end
|
118
|
-
|
119
|
-
def sql_update(sql)
|
120
|
-
exec(sql)
|
121
|
-
@conn.changes
|
122
|
-
end
|
123
|
-
|
124
|
-
private
|
125
|
-
|
126
|
-
def create_table(klass)
|
127
|
-
fields = fields_for_class(klass)
|
128
|
-
|
129
|
-
sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
|
130
|
-
|
131
|
-
# Create table constraints.
|
132
|
-
|
133
|
-
if constraints = klass.ann.self[:sql_constraint]
|
134
|
-
sql << ", #{constraints.join(', ')}"
|
135
|
-
end
|
136
|
-
|
137
|
-
sql << ");"
|
138
|
-
|
139
|
-
# Create indices.
|
140
|
-
|
141
|
-
if indices = klass.ann.self[:index]
|
142
|
-
for data in indices
|
143
|
-
idx, options = *data
|
144
|
-
idx = idx.to_s
|
145
|
-
pre_sql, post_sql = options[:pre], options[:post]
|
146
|
-
idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
|
147
|
-
sql << " CREATE #{pre_sql} INDEX #{klass::OGTABLE}_#{idxname}_idx #{post_sql} ON #{klass::OGTABLE} (#{idx});"
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
begin
|
152
|
-
@conn.query(sql).close
|
153
|
-
Logger.info "Created table '#{klass::OGTABLE}'."
|
154
|
-
rescue Object => ex
|
155
|
-
# gmosx: any idea how to better test this?
|
156
|
-
if ex.to_s =~ /table .* already exists/i
|
157
|
-
Logger.debug 'Table already exists' if $DBG
|
158
|
-
return
|
159
|
-
else
|
160
|
-
raise
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
# Create join tables if needed. Join tables are used in
|
165
|
-
# 'many_to_many' relations.
|
166
|
-
|
167
|
-
if join_tables = klass.ann.self[:join_tables]
|
168
|
-
for info in join_tables
|
169
|
-
begin
|
170
|
-
create_join_table_sql(info).each do |sql|
|
171
|
-
@conn.query(sql).close
|
172
|
-
end
|
173
|
-
Logger.debug "Created jointable '#{info[:table]}'." if $DBG
|
174
|
-
rescue Object => ex
|
175
|
-
# gmosx: any idea how to better test this?
|
176
|
-
if ex.to_s =~ /table .* already exists/i
|
177
|
-
Logger.debug 'Join table already exists' if $DBG
|
178
|
-
else
|
179
|
-
raise
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
def create_field_map(klass)
|
187
|
-
res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
|
188
|
-
map = {}
|
189
|
-
|
190
|
-
fields = res.columns
|
191
|
-
|
192
|
-
# Check if the field should be ignored.
|
193
|
-
ignore = klass.ann[:self][:ignore_field] || klass.ann[:self][:ignore_fields] || klass.ann[:self][:ignore_columns]
|
194
|
-
|
195
|
-
res.fields.size.times do |i|
|
196
|
-
field_name = fields[i].to_sym
|
197
|
-
unless (ignore and ignore.include?(field_name))
|
198
|
-
map[field_name] = i
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
return map
|
203
|
-
ensure
|
204
|
-
res.close if res
|
205
|
-
end
|
206
|
-
|
207
|
-
def eval_og_insert(klass)
|
208
|
-
pk = klass.primary_key.symbol
|
209
|
-
props = klass.properties.values.dup
|
210
|
-
values = props.collect { |p| write_prop(p) }.join(',')
|
211
|
-
|
212
|
-
if klass.schema_inheritance?
|
213
|
-
props << Property.new(:symbol => :ogtype, :klass => String)
|
214
|
-
values << ", '#{klass}'"
|
215
|
-
end
|
216
|
-
|
217
|
-
sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| field_for_property(p)}.join(',')}) VALUES (#{values})"
|
218
|
-
|
219
|
-
klass.class_eval %{
|
220
|
-
def og_insert(store)
|
221
|
-
#{::Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
|
222
|
-
store.query("#{sql}").close
|
223
|
-
@#{pk} = store.last_insert_rowid
|
224
|
-
#{::Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
|
225
|
-
end
|
226
|
-
}
|
227
|
-
end
|
228
|
-
|
229
|
-
end
|
230
|
-
|
231
|
-
end
|
232
|
-
|
233
|
-
# * George Moschovitis <gm@navel.gr>
|
234
|
-
# * Ghislain Mary
|
235
|
-
# * Mitchell Foral
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|