og 0.31.0 → 0.40.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|