og 0.9.3 → 0.9.5
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/ChangeLog +64 -0
- data/README.og +2 -2
- data/examples/og/mock_example.rb +1 -4
- data/examples/og/mysql_to_psql.rb +2 -4
- data/lib/glue/cache.rb +32 -34
- data/lib/glue/number.rb +3 -9
- data/lib/glue/time.rb +14 -22
- data/lib/glue/validation.rb +2 -4
- data/lib/og/backend.rb +36 -40
- data/lib/og/backends/psql.rb +7 -7
- data/lib/og/backends/sqlite.rb +383 -0
- data/lib/og/connection.rb +34 -34
- data/lib/og/meta.rb +8 -0
- data/lib/og/version.rb +1 -1
- metadata +3 -2
data/lib/og/backends/psql.rb
CHANGED
@@ -12,7 +12,7 @@ class Og
|
|
12
12
|
# This backend is compatible with Michael Neumann's postgres-pr
|
13
13
|
# pure ruby driver.
|
14
14
|
|
15
|
-
class PsqlBackend <
|
15
|
+
class PsqlBackend < Backend
|
16
16
|
|
17
17
|
# A mapping between Ruby and SQL types.
|
18
18
|
|
@@ -101,15 +101,15 @@ class PsqlBackend < Og::Backend
|
|
101
101
|
elsif p.klass.ancestors.include?(Float)
|
102
102
|
return "#\{@#{p.symbol} || 'NULL'\}"
|
103
103
|
elsif p.klass.ancestors.include?(String)
|
104
|
-
return "'#\{
|
104
|
+
return "'#\{PsqlBackend.escape(@#{p.symbol})\}'"
|
105
105
|
elsif p.klass.ancestors.include?(Time)
|
106
|
-
return %|#\{@#{p.symbol} ? "'#\{
|
106
|
+
return %|#\{@#{p.symbol} ? "'#\{PsqlBackend.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
|
107
107
|
elsif p.klass.ancestors.include?(Date)
|
108
|
-
return %|#\{@#{p.symbol} ? "'#\{
|
108
|
+
return %|#\{@#{p.symbol} ? "'#\{PsqlBackend.date(@#{p.symbol})\}'" : 'NULL'\}|
|
109
109
|
elsif p.klass.ancestors.include?(TrueClass)
|
110
110
|
return "#\{@#{p.symbol} ? \"'t'\" : 'NULL' \}"
|
111
111
|
else
|
112
|
-
return %|#\{@#{p.symbol} ? "'#\{
|
112
|
+
return %|#\{@#{p.symbol} ? "'#\{PsqlBackend.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
@@ -124,9 +124,9 @@ class PsqlBackend < Og::Backend
|
|
124
124
|
elsif p.klass.ancestors.include?(String)
|
125
125
|
return "res.getvalue(tuple, #{idx})"
|
126
126
|
elsif p.klass.ancestors.include?(Time)
|
127
|
-
return "
|
127
|
+
return "PsqlBackend.parse_timestamp(res.getvalue(tuple, #{idx}))"
|
128
128
|
elsif p.klass.ancestors.include?(Date)
|
129
|
-
return "
|
129
|
+
return "PsqlBackend.parse_date(res.getvalue(tuple, #{idx}))"
|
130
130
|
elsif p.klass.ancestors.include?(TrueClass)
|
131
131
|
return %|('t' == res.getvalue(tuple, #{idx}))|
|
132
132
|
else
|
@@ -0,0 +1,383 @@
|
|
1
|
+
# * George Moschovitis <gm@navel.gr>
|
2
|
+
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
+
# $Id$
|
4
|
+
|
5
|
+
require 'sqlite'
|
6
|
+
|
7
|
+
require 'og/backend'
|
8
|
+
|
9
|
+
class Og
|
10
|
+
|
11
|
+
# Implements an SQLite powered backend.
|
12
|
+
|
13
|
+
class SqliteBackend < Backend
|
14
|
+
|
15
|
+
# A mapping between Ruby and SQL types.
|
16
|
+
|
17
|
+
TYPEMAP = {
|
18
|
+
Integer => 'integer',
|
19
|
+
Fixnum => 'integer',
|
20
|
+
Float => 'float',
|
21
|
+
String => 'text',
|
22
|
+
Time => 'timestamp',
|
23
|
+
Date => 'date',
|
24
|
+
TrueClass => 'boolean',
|
25
|
+
Object => 'text',
|
26
|
+
Array => 'text',
|
27
|
+
Hash => 'text'
|
28
|
+
}
|
29
|
+
|
30
|
+
# Intitialize the connection to the RDBMS.
|
31
|
+
|
32
|
+
def initialize(config)
|
33
|
+
begin
|
34
|
+
@conn = SQLite::Database.new(config[:database])
|
35
|
+
rescue => ex
|
36
|
+
# gmosx: any idea how to better test this?
|
37
|
+
if ex.to_s =~ /database .* does not exist/i
|
38
|
+
Logger.info "Database '#{config[:database]}' not found!"
|
39
|
+
SqliteBackend.create_db(config[:database], config[:user])
|
40
|
+
retry
|
41
|
+
end
|
42
|
+
raise
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
47
|
+
# Utilities
|
48
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
49
|
+
|
50
|
+
# Escape an SQL string
|
51
|
+
|
52
|
+
def self.escape(str)
|
53
|
+
return nil unless str
|
54
|
+
return str.gsub( /'/, "''" )
|
55
|
+
end
|
56
|
+
|
57
|
+
# Convert a ruby time to an sql timestamp.
|
58
|
+
# TODO: Optimize this
|
59
|
+
|
60
|
+
def self.timestamp(time = Time.now)
|
61
|
+
return nil unless time
|
62
|
+
return time.strftime("%Y-%m-%d %H:%M:%S")
|
63
|
+
end
|
64
|
+
|
65
|
+
# Output YYY-mm-dd
|
66
|
+
# TODO: Optimize this
|
67
|
+
|
68
|
+
def self.date(date)
|
69
|
+
return nil unless date
|
70
|
+
return "#{date.year}-#{date.month}-#{date.mday}"
|
71
|
+
end
|
72
|
+
|
73
|
+
# Parse sql datetime
|
74
|
+
# TODO: Optimize this
|
75
|
+
|
76
|
+
def self.parse_timestamp(str)
|
77
|
+
return Time.parse(str)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Input YYYY-mm-dd
|
81
|
+
# TODO: Optimize this
|
82
|
+
|
83
|
+
def self.parse_date(str)
|
84
|
+
return nil unless str
|
85
|
+
return Date.strptime(str)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Return an sql string evaluator for the property.
|
89
|
+
# No need to optimize this, used only to precalculate code.
|
90
|
+
# YAML is used to store general Ruby objects to be more
|
91
|
+
# portable.
|
92
|
+
#
|
93
|
+
# FIXME: add extra handling for float.
|
94
|
+
|
95
|
+
def self.write_prop(p)
|
96
|
+
if p.klass.ancestors.include?(Integer)
|
97
|
+
return "#\{@#{p.symbol} || 'NULL'\}"
|
98
|
+
elsif p.klass.ancestors.include?(Float)
|
99
|
+
return "#\{@#{p.symbol} || 'NULL'\}"
|
100
|
+
elsif p.klass.ancestors.include?(String)
|
101
|
+
return "'#\{SqliteBackend.escape(@#{p.symbol})\}'"
|
102
|
+
elsif p.klass.ancestors.include?(Time)
|
103
|
+
return %|#\{@#{p.symbol} ? "'#\{SqliteBackend.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
|
104
|
+
elsif p.klass.ancestors.include?(Date)
|
105
|
+
return %|#\{@#{p.symbol} ? "'#\{SqliteBackend.date(@#{p.symbol})\}'" : 'NULL'\}|
|
106
|
+
elsif p.klass.ancestors.include?(TrueClass)
|
107
|
+
return "#\{@#{p.symbol} ? \"'t'\" : 'NULL' \}"
|
108
|
+
else
|
109
|
+
return %|#\{@#{p.symbol} ? "'#\{SqliteBackend.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Return an evaluator for reading the property.
|
114
|
+
# No need to optimize this, used only to precalculate code.
|
115
|
+
|
116
|
+
def self.read_prop(p, idx)
|
117
|
+
if p.klass.ancestors.include?(Integer)
|
118
|
+
return "res.getvalue(tuple, #{idx}).to_i()"
|
119
|
+
elsif p.klass.ancestors.include?(Float)
|
120
|
+
return "res.getvalue(tuple, #{idx}).to_f()"
|
121
|
+
elsif p.klass.ancestors.include?(String)
|
122
|
+
return "res.getvalue(tuple, #{idx})"
|
123
|
+
elsif p.klass.ancestors.include?(Time)
|
124
|
+
return "PsqlBackend.parse_timestamp(res.getvalue(tuple, #{idx}))"
|
125
|
+
elsif p.klass.ancestors.include?(Date)
|
126
|
+
return "PsqlBackend.parse_date(res.getvalue(tuple, #{idx}))"
|
127
|
+
elsif p.klass.ancestors.include?(TrueClass)
|
128
|
+
return %|('t' == res.getvalue(tuple, #{idx}))|
|
129
|
+
else
|
130
|
+
return "YAML::load(res.getvalue(tuple, #{idx}))"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns the code that actually inserts the object into the
|
135
|
+
# database. Returns the code as String.
|
136
|
+
|
137
|
+
def self.insert_code(klass, sql, pre_cb, post_cb)
|
138
|
+
%{
|
139
|
+
#{pre_cb}
|
140
|
+
res = conn.db.query("SELECT nextval('#{klass::DBSEQ}')")
|
141
|
+
@oid = res.getvalue(0, 0).to_i
|
142
|
+
conn.exec "#{sql}"
|
143
|
+
#{post_cb}
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
# generate the mapping of the database fields to the
|
148
|
+
# object properties.
|
149
|
+
|
150
|
+
def self.calc_field_index(klass, og)
|
151
|
+
res = og.query "SELECT * FROM #{klass::DBTABLE} LIMIT 1"
|
152
|
+
meta = og.managed_classes[klass]
|
153
|
+
|
154
|
+
for field in res.fields
|
155
|
+
meta.field_index[field] = res.fieldnum(field)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Generate the property for oid
|
160
|
+
|
161
|
+
def self.eval_og_oid(klass)
|
162
|
+
klass.class_eval %{
|
163
|
+
prop_accessor :oid, Fixnum, :sql => "integer PRIMARY KEY"
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
168
|
+
# Connection methods.
|
169
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
170
|
+
|
171
|
+
# Create the database.
|
172
|
+
|
173
|
+
def self.create_db(database, user = nil, password = nil)
|
174
|
+
Logger.info "Creating database '#{database}'."
|
175
|
+
`createdb #{database} -U #{user}`
|
176
|
+
end
|
177
|
+
|
178
|
+
# Drop the database.
|
179
|
+
|
180
|
+
def self.drop_db(database, user = nil, password = nil)
|
181
|
+
Logger.info "Dropping database '#{database}'."
|
182
|
+
`dropdb #{database} -U #{user}`
|
183
|
+
end
|
184
|
+
|
185
|
+
# Execute an SQL query and return the result.
|
186
|
+
|
187
|
+
def query(sql)
|
188
|
+
Logger.debug sql if $DBG
|
189
|
+
return @conn.exec(sql)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Execute an SQL query, no result returned.
|
193
|
+
|
194
|
+
def exec(sql)
|
195
|
+
Logger.debug sql if $DBG
|
196
|
+
res = @conn.exec(sql)
|
197
|
+
res.clear()
|
198
|
+
end
|
199
|
+
|
200
|
+
# Execute an SQL query and return the result. Wrapped in a rescue
|
201
|
+
# block.
|
202
|
+
|
203
|
+
def safe_query(sql)
|
204
|
+
Logger.debug sql if $DBG
|
205
|
+
begin
|
206
|
+
return @conn.exec(sql)
|
207
|
+
rescue => ex
|
208
|
+
Logger.error "DB error #{ex}, [#{sql}]"
|
209
|
+
Logger.error ex.backtrace
|
210
|
+
return nil
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Execute an SQL query, no result returned. Wrapped in a rescue
|
215
|
+
# block.
|
216
|
+
|
217
|
+
def safe_exec(sql)
|
218
|
+
Logger.debug sql if $DBG
|
219
|
+
begin
|
220
|
+
res = @conn.exec(sql)
|
221
|
+
res.clear()
|
222
|
+
rescue => ex
|
223
|
+
Logger.error "DB error #{ex}, [#{sql}]"
|
224
|
+
Logger.error ex.backtrace
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Check if it is a valid resultset.
|
229
|
+
|
230
|
+
def valid?(res)
|
231
|
+
return !(res.nil? or 0 == res.num_tuples)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Create the managed object table. The properties of the
|
235
|
+
# object are mapped to the table columns. Additional sql relations
|
236
|
+
# and constrains are created (indicices, sequences, etc).
|
237
|
+
|
238
|
+
def create_table(klass)
|
239
|
+
fields = create_fields(klass, TYPEMAP)
|
240
|
+
|
241
|
+
sql = "CREATE TABLE #{klass::DBTABLE} (#{fields.join(', ')}"
|
242
|
+
|
243
|
+
# Create table constrains
|
244
|
+
|
245
|
+
if klass.__meta and constrains = klass.__meta[:sql_constrain]
|
246
|
+
sql << ", #{constrains.join(', ')}"
|
247
|
+
end
|
248
|
+
|
249
|
+
sql << ") WITHOUT OIDS;"
|
250
|
+
|
251
|
+
# Create indices
|
252
|
+
|
253
|
+
if klass.__meta and indices = klass.__meta[:sql_index]
|
254
|
+
for data in indices
|
255
|
+
idx, options = *data
|
256
|
+
idx = idx.to_s
|
257
|
+
pre_sql, post_sql = options[:pre], options[:post]
|
258
|
+
idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
|
259
|
+
sql << " CREATE #{pre_sql} INDEX #{klass::DBTABLE}_#{idxname}_idx #{post_sql} ON #{klass::DBTABLE} (#{idx});"
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
begin
|
264
|
+
exec(sql)
|
265
|
+
Logger.info "Created table '#{klass::DBTABLE}'."
|
266
|
+
rescue => ex
|
267
|
+
# gmosx: any idea how to better test this?
|
268
|
+
if ex.to_s =~ /relation .* already exists/i
|
269
|
+
Logger.debug "Table already exists" if $DBG
|
270
|
+
else
|
271
|
+
raise
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# create the sequence for this table. Even if the table
|
276
|
+
# uses the oids_seq, attempt to create it. This makes
|
277
|
+
# the system more fault tolerant.
|
278
|
+
|
279
|
+
begin
|
280
|
+
exec "CREATE SEQUENCE #{klass::DBSEQ}"
|
281
|
+
Logger.info "Created sequence '#{klass::DBSEQ}'."
|
282
|
+
rescue => ex
|
283
|
+
# gmosx: any idea how to better test this?
|
284
|
+
if ex.to_s =~ /relation .* already exists/i
|
285
|
+
Logger.debug "Sequence already exists" if $DBG
|
286
|
+
else
|
287
|
+
raise
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# Create join tables if needed. Join tables are used in
|
292
|
+
# 'many_to_many' relations.
|
293
|
+
|
294
|
+
if klass.__meta and joins = klass.__meta[:sql_join]
|
295
|
+
for data in joins
|
296
|
+
# the class to join to and some options.
|
297
|
+
join_class, options = *data
|
298
|
+
|
299
|
+
# gmosx: dont use DBTABLE here, perhaps the join class
|
300
|
+
# is not managed yet.
|
301
|
+
join_table = "#{self.class.join_table(klass, join_class)}"
|
302
|
+
join_src = "#{self.class.encode(klass)}_oid"
|
303
|
+
join_dst = "#{self.class.encode(join_class)}_oid"
|
304
|
+
begin
|
305
|
+
exec "CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )"
|
306
|
+
exec "CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)"
|
307
|
+
exec "CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)"
|
308
|
+
rescue => ex
|
309
|
+
# gmosx: any idea how to better test this?
|
310
|
+
if ex.to_s =~ /relation .* already exists/i
|
311
|
+
Logger.debug "Join table already exists" if $DBG
|
312
|
+
else
|
313
|
+
raise
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
begin
|
320
|
+
exec(sql)
|
321
|
+
Logger.info "Created join table '#{join_table}'."
|
322
|
+
rescue => ex
|
323
|
+
# gmosx: any idea how to better test this?
|
324
|
+
if ex.to_s =~ /relation .* already exists/i
|
325
|
+
Logger.debug "Join table already exists" if $DBG
|
326
|
+
else
|
327
|
+
raise
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
# Drop the managed object table.
|
334
|
+
|
335
|
+
def drop_table(klass)
|
336
|
+
super
|
337
|
+
exec "DROP SEQUENCE #{klass::DBSEQ}"
|
338
|
+
end
|
339
|
+
|
340
|
+
# Deserialize one row of the resultset.
|
341
|
+
|
342
|
+
def deserialize_one(res, klass)
|
343
|
+
return nil unless valid?(res)
|
344
|
+
|
345
|
+
# gmosx: Managed objects should have no params constructor.
|
346
|
+
entity = klass.new()
|
347
|
+
entity.og_deserialize(res, 0)
|
348
|
+
|
349
|
+
# get_join_fields(res, 0, entity, join_fields) if join_fields
|
350
|
+
|
351
|
+
res.clear()
|
352
|
+
return entity
|
353
|
+
end
|
354
|
+
|
355
|
+
# Deserialize all rows of the resultset.
|
356
|
+
|
357
|
+
def deserialize_all(res, klass)
|
358
|
+
return [] unless valid?(res)
|
359
|
+
|
360
|
+
entities = []
|
361
|
+
|
362
|
+
for tuple in (0...res.num_tuples)
|
363
|
+
entity = klass.new()
|
364
|
+
entity.og_deserialize(res, tuple)
|
365
|
+
|
366
|
+
# get_join_fields(res, tuple, entity, join_fields) if join_fields
|
367
|
+
|
368
|
+
entities << entity
|
369
|
+
end
|
370
|
+
|
371
|
+
res.clear()
|
372
|
+
return entities
|
373
|
+
end
|
374
|
+
|
375
|
+
# Return a single integer value from the resultset.
|
376
|
+
|
377
|
+
def get_int(res, idx = 0)
|
378
|
+
return res.getvalue(0, idx).to_i
|
379
|
+
end
|
380
|
+
|
381
|
+
end
|
382
|
+
|
383
|
+
end
|
data/lib/og/connection.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
|
-
|
2
|
-
# George Moschovitis <gm@navel.gr>
|
1
|
+
# * George Moschovitis <gm@navel.gr>
|
3
2
|
# (c) 2004-2005 Navel, all rights reserved.
|
4
3
|
# $Id: connection.rb 248 2005-01-31 13:38:34Z gmosx $
|
5
|
-
#++
|
6
4
|
|
7
5
|
class Og;
|
8
6
|
|
@@ -33,7 +31,7 @@ class Connection
|
|
33
31
|
|
34
32
|
attr_accessor :deserialize
|
35
33
|
|
36
|
-
# Initialize a connection to the database
|
34
|
+
# Initialize a connection to the database.
|
37
35
|
|
38
36
|
def initialize(og)
|
39
37
|
@og = og
|
@@ -42,8 +40,8 @@ class Connection
|
|
42
40
|
Logger.debug "Created DB connection." if $DBG
|
43
41
|
end
|
44
42
|
|
45
|
-
# Close the connection to the database
|
46
|
-
|
43
|
+
# Close the connection to the database.
|
44
|
+
|
47
45
|
def close()
|
48
46
|
@db.close()
|
49
47
|
Logger.debug "Closed DB connection." if $DBG
|
@@ -51,7 +49,7 @@ class Connection
|
|
51
49
|
|
52
50
|
# Save an object to the database. Insert if this is a new object or
|
53
51
|
# update if this is already stored in the database.
|
54
|
-
|
52
|
+
|
55
53
|
def save(obj)
|
56
54
|
if obj.oid
|
57
55
|
# object allready inserted, update!
|
@@ -65,13 +63,13 @@ class Connection
|
|
65
63
|
alias_method :put, :save
|
66
64
|
|
67
65
|
# Force insertion of managed object.
|
68
|
-
|
66
|
+
|
69
67
|
def insert(obj)
|
70
68
|
obj.og_insert(self)
|
71
69
|
end
|
72
70
|
|
73
71
|
# Force update of managed object.
|
74
|
-
|
72
|
+
|
75
73
|
def update(obj)
|
76
74
|
obj.og_update(self)
|
77
75
|
end
|
@@ -81,11 +79,11 @@ class Connection
|
|
81
79
|
# Input:
|
82
80
|
# sql = the sql code to updated the properties.
|
83
81
|
#
|
84
|
-
# WARNING: the object in
|
82
|
+
# WARNING: the object in memory is not updated.
|
85
83
|
#--
|
86
84
|
# TODO: should update the object in memory.
|
87
85
|
#++
|
88
|
-
|
86
|
+
|
89
87
|
def update_properties(update_sql, obj_or_oid, klass = nil)
|
90
88
|
oid = obj_or_oid.to_i
|
91
89
|
klass = obj_or_oid.class unless klass
|
@@ -98,7 +96,7 @@ class Connection
|
|
98
96
|
#
|
99
97
|
# Input:
|
100
98
|
# oid = the object oid, OR the object name.
|
101
|
-
|
99
|
+
|
102
100
|
def load(oid, klass)
|
103
101
|
if oid.to_i > 0 # a valid Fixnum ?
|
104
102
|
load_by_oid(oid, klass)
|
@@ -109,7 +107,7 @@ class Connection
|
|
109
107
|
alias_method :get, :load
|
110
108
|
|
111
109
|
# Load an object by oid.
|
112
|
-
|
110
|
+
|
113
111
|
def load_by_oid(oid, klass)
|
114
112
|
res = query "SELECT * FROM #{klass::DBTABLE} WHERE oid=#{oid}"
|
115
113
|
@deserialize? @db.deserialize_one(res, klass) : res
|
@@ -117,7 +115,7 @@ class Connection
|
|
117
115
|
alias_method :get_by_oid, :load_by_oid
|
118
116
|
|
119
117
|
# Load an object by name.
|
120
|
-
|
118
|
+
|
121
119
|
def load_by_name(name, klass)
|
122
120
|
res = query "SELECT * FROM #{klass::DBTABLE} WHERE name='#{name}'"
|
123
121
|
@deserialize? @db.deserialize_one(res, klass) : res
|
@@ -126,7 +124,7 @@ class Connection
|
|
126
124
|
|
127
125
|
# Load all objects of the given klass.
|
128
126
|
# Used to be called 'collect' in an earlier version.
|
129
|
-
|
127
|
+
|
130
128
|
def load_all(klass, extrasql = nil)
|
131
129
|
res = query "SELECT * FROM #{klass::DBTABLE} #{extrasql}"
|
132
130
|
@deserialize? @db.deserialize_all(res, klass) : res
|
@@ -135,7 +133,7 @@ class Connection
|
|
135
133
|
|
136
134
|
# Perform a standard SQL query to the database. Deserializes the
|
137
135
|
# results.
|
138
|
-
|
136
|
+
|
139
137
|
def select(sql, klass)
|
140
138
|
unless sql =~ /SELECT/i
|
141
139
|
sql = "SELECT * FROM #{klass::DBTABLE} WHERE #{sql}"
|
@@ -146,7 +144,7 @@ class Connection
|
|
146
144
|
end
|
147
145
|
|
148
146
|
# Optimized for one result.
|
149
|
-
|
147
|
+
|
150
148
|
def select_one(sql, klass)
|
151
149
|
unless sql =~ /SELECT/i
|
152
150
|
sql = "SELECT * FROM #{klass::DBTABLE} WHERE #{sql}"
|
@@ -157,7 +155,7 @@ class Connection
|
|
157
155
|
end
|
158
156
|
|
159
157
|
# Perform a count query.
|
160
|
-
|
158
|
+
|
161
159
|
def count(sql, klass = nil)
|
162
160
|
unless sql =~ /SELECT/i
|
163
161
|
sql = "SELECT COUNT(*) FROM #{klass::DBTABLE} WHERE #{sql}"
|
@@ -173,21 +171,23 @@ class Connection
|
|
173
171
|
# No need to optimize here with pregenerated code. Deletes are
|
174
172
|
# not used as much as reads or writes.
|
175
173
|
#
|
176
|
-
#
|
174
|
+
# Input:
|
177
175
|
#
|
178
176
|
# obj_or_oid = Object or oid to delete.
|
179
177
|
# klass = Class of object (can be nil if an object is passed)
|
180
|
-
|
178
|
+
|
181
179
|
def delete(obj_or_oid, klass = nil, cascade = true)
|
182
180
|
oid = obj_or_oid.to_i
|
183
181
|
klass = obj_or_oid.class unless klass
|
184
182
|
|
185
183
|
# this is a class callback!
|
184
|
+
|
186
185
|
if klass.respond_to?(:og_pre_delete)
|
187
186
|
klass.og_pre_delete(self, oid)
|
188
187
|
end
|
189
188
|
|
190
189
|
# TODO: implement this as stored procedure? naaah.
|
190
|
+
|
191
191
|
transaction do |tx|
|
192
192
|
tx.exec "DELETE FROM #{klass::DBTABLE} WHERE oid=#{oid}"
|
193
193
|
if cascade and klass.__meta.include?(:has)
|
@@ -202,60 +202,60 @@ class Connection
|
|
202
202
|
# Create the managed object table. The properties of the
|
203
203
|
# object are mapped to the table columns. Additional sql relations
|
204
204
|
# and constrains are created (indicices, sequences, etc).
|
205
|
-
|
205
|
+
|
206
206
|
def create_table(klass)
|
207
207
|
@db.create_table(klass)
|
208
208
|
end
|
209
209
|
|
210
210
|
# Drop the managed object table.
|
211
|
-
|
211
|
+
|
212
212
|
def drop_table(klass)
|
213
213
|
@db.drop_table(klass)
|
214
214
|
end
|
215
215
|
|
216
216
|
# Execute an SQL query and return the result
|
217
|
-
|
217
|
+
|
218
218
|
def query(sql)
|
219
219
|
@db.safe_query(sql)
|
220
220
|
end
|
221
221
|
|
222
222
|
# Execute an SQL query, no result returned.
|
223
|
-
|
223
|
+
|
224
224
|
def exec(sql)
|
225
225
|
@db.safe_exec(sql)
|
226
226
|
end
|
227
227
|
|
228
228
|
# Start a new transaction.
|
229
|
-
|
229
|
+
|
230
230
|
def start
|
231
|
-
@db.start
|
231
|
+
@db.start
|
232
232
|
end
|
233
233
|
|
234
234
|
# Commit a transaction.
|
235
|
-
|
235
|
+
|
236
236
|
def commit
|
237
|
-
@db.commit
|
237
|
+
@db.commit
|
238
238
|
end
|
239
239
|
|
240
240
|
# Rollback transaction.
|
241
|
-
|
241
|
+
|
242
242
|
def rollback
|
243
|
-
@db.rollback
|
243
|
+
@db.rollback
|
244
244
|
end
|
245
245
|
|
246
246
|
# Transaction helper. In the transaction block use
|
247
247
|
# the db pointer to the backend.
|
248
|
-
|
248
|
+
|
249
249
|
def transaction(&block)
|
250
250
|
begin
|
251
|
-
@db.start
|
251
|
+
@db.start
|
252
252
|
yield(@db)
|
253
|
-
@db.commit
|
253
|
+
@db.commit
|
254
254
|
rescue => ex
|
255
255
|
Logger.error "DB Error: ERROR IN TRANSACTION"
|
256
256
|
Logger.error #{ex}
|
257
257
|
Logger.error #{ex.backtrace}
|
258
|
-
@db.rollback
|
258
|
+
@db.rollback
|
259
259
|
end
|
260
260
|
end
|
261
261
|
|