og 0.9.5 → 0.10.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.
@@ -0,0 +1,262 @@
1
+ # * George Moschovitis <gm@navel.gr>
2
+ # (c) 2004-2005 Navel, all rights reserved.
3
+ # $Id: sqlite.rb 259 2005-02-15 08:54:54Z gmosx $
4
+
5
+ require 'sqlite3'
6
+ require 'fileutils'
7
+
8
+ require 'og/adapter'
9
+ require 'og/connection'
10
+ require 'glue/attribute'
11
+
12
+ class Og
13
+
14
+ # The SQLite adapter. This adapter communicates with
15
+ # an SQLite3 rdbms. For extra documentation see
16
+ # lib/og/adapter.rb
17
+
18
+ class SqliteAdapter < Adapter
19
+
20
+ def drop_db(database, user = nil, password = nil)
21
+ begin
22
+ FileUtils.rm("#{database}.db")
23
+ super
24
+ rescue
25
+ Logger.error "Cannot drop '#{database}'!"
26
+ end
27
+ end
28
+ =begin
29
+ def write_prop(p)
30
+ if p.klass.ancestors.include?(Integer)
31
+ return "@#{p.symbol}"
32
+ elsif p.klass.ancestors.include?(Float)
33
+ return "@#{p.symbol}"
34
+ elsif p.klass.ancestors.include?(String)
35
+ return "#{self.class}.escape(@#{p.symbol})"
36
+ elsif p.klass.ancestors.include?(Time)
37
+ return %|#\{@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
38
+ elsif p.klass.ancestors.include?(Date)
39
+ return %|#\{@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : 'NULL'\}|
40
+ elsif p.klass.ancestors.include?(TrueClass)
41
+ return "#\{@#{p.symbol} ? \"'t'\" : 'NULL' \}"
42
+ else
43
+ return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
44
+ end
45
+ end
46
+ =end
47
+ def insert_code(klass, db, pre_cb, post_cb)
48
+ props = props_for_insert(klass)
49
+ values = props.collect { |p| write_prop(p) }.join(',')
50
+
51
+ sql = "INSERT INTO #{klass::DBTABLE} (#{props.collect {|p| p.name}.join(',')}) VALUES (#{values})"
52
+
53
+ %{
54
+ #{pre_cb}
55
+ conn.store.query("#{sql}").close
56
+ @oid = conn.store.last_insert_row_id
57
+ #{post_cb}
58
+ }
59
+ =begin
60
+ props = props_for_insert(klass)
61
+
62
+ placeholders = Array.new(props.size, '?').join(',')
63
+ values = props.collect { |p| write_prop(p) }.join(',')
64
+
65
+ sql = "INSERT INTO #{klass::DBTABLE} (#{props.collect {|p| p.name}.join(',')}) VALUES (#{placeholders})"
66
+ klass.class_eval %{
67
+ cattr_accessor :og_insert_statement
68
+ }
69
+
70
+ klass.og_insert_statement = db.prepare(sql)
71
+
72
+ %{
73
+ #{pre_cb}
74
+ @@og_insert_statement.execute(#{values})
75
+ @oid = conn.store.last_insert_row_id
76
+ #{post_cb}
77
+ }
78
+ =end
79
+ end
80
+
81
+ def new_connection(db)
82
+ return Og::SqliteConnection.new(db)
83
+ end
84
+
85
+ def calc_field_index(klass, db)
86
+ res = db.query "SELECT * FROM #{klass::DBTABLE} LIMIT 1"
87
+ meta = db.managed_classes[klass]
88
+
89
+ columns = res.columns
90
+
91
+ for idx in (0...columns.size)
92
+ meta.field_index[columns[idx]] = idx
93
+ end
94
+
95
+ ensure
96
+ res.close
97
+ end
98
+
99
+ def create_table(klass, db)
100
+ conn = db.get_connection
101
+
102
+ fields = create_fields(klass)
103
+
104
+ sql = "CREATE TABLE #{klass::DBTABLE} (#{fields.join(', ')}"
105
+
106
+ # Create table constrains
107
+
108
+ if klass.__meta and constrains = klass.__meta[:sql_constrain]
109
+ sql << ", #{constrains.join(', ')}"
110
+ end
111
+
112
+ sql << ");"
113
+
114
+ # Create indices
115
+
116
+ if klass.__meta and indices = klass.__meta[:sql_index]
117
+ for data in indices
118
+ idx, options = *data
119
+ idx = idx.to_s
120
+ pre_sql, post_sql = options[:pre], options[:post]
121
+ idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
122
+ sql << " CREATE #{pre_sql} INDEX #{klass::DBTABLE}_#{idxname}_idx #{post_sql} ON #{klass::DBTABLE} (#{idx});"
123
+ end
124
+ end
125
+
126
+ begin
127
+ conn.store.query(sql).close
128
+ Logger.info "Created table '#{klass::DBTABLE}'."
129
+ rescue Exception => ex
130
+ # gmosx: any idea how to better test this?
131
+ if ex.to_s =~ /table .* already exists/i
132
+ Logger.debug "Table already exists" if $DBG
133
+ return
134
+ else
135
+ raise
136
+ end
137
+ end
138
+
139
+ # Create join tables if needed. Join tables are used in
140
+ # 'many_to_many' relations.
141
+
142
+ if klass.__meta and joins = klass.__meta[:sql_join]
143
+ for data in joins
144
+ # the class to join to and some options.
145
+ join_class, options = *data
146
+
147
+ # gmosx: dont use DBTABLE here, perhaps the join class
148
+ # is not managed yet.
149
+ join_table = "#{self.class.join_table(klass, join_class)}"
150
+ join_src = "#{self.class.encode(klass)}_oid"
151
+ join_dst = "#{self.class.encode(join_class)}_oid"
152
+ begin
153
+ conn.store.query("CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )").close
154
+ conn.store.query("CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)").close
155
+ conn.store.query("CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)").close
156
+ rescue Exception => ex
157
+ # gmosx: any idea how to better test this?
158
+ if ex.to_s =~ /table .* already exists/i
159
+ Logger.debug "Join table already exists" if $DBG
160
+ else
161
+ raise
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ ensure
168
+ db.put_connection
169
+ end
170
+
171
+ end
172
+
173
+ # The SQLite connection.
174
+
175
+ class SqliteConnection < Connection
176
+
177
+ def initialize(db)
178
+ @store = SQLite3::Database.new("#{db.config[:database]}.db")
179
+ super
180
+ end
181
+
182
+ def close
183
+ @store.close
184
+ super
185
+ end
186
+
187
+ def prepare(sql)
188
+ @store.prepare(sql)
189
+ end
190
+
191
+ def query(sql)
192
+ Logger.debug sql if $DBG
193
+ begin
194
+ return @store.query(sql)
195
+ rescue => ex
196
+ Logger.error "DB error #{ex}, [#{sql}]"
197
+ Logger.error ex.backtrace.join("\n")
198
+ return nil
199
+ end
200
+ end
201
+
202
+ def exec(sql)
203
+ Logger.debug sql if $DBG
204
+ begin
205
+ @store.query(sql).close
206
+ rescue => ex
207
+ Logger.error "DB error #{ex}, [#{sql}]"
208
+ Logger.error ex.backtrace.join("\n")
209
+ end
210
+ end
211
+
212
+ def start
213
+ @store.transaction
214
+ end
215
+
216
+ def commit
217
+ @store.commit
218
+ end
219
+
220
+ def rollback
221
+ @store.rollback
222
+ end
223
+
224
+ def valid_res?(res)
225
+ return !(res.nil?)
226
+ end
227
+
228
+ def read_one(res, klass)
229
+ return nil unless valid_res?(res)
230
+ row = res.next
231
+ return nil unless row
232
+
233
+ obj = klass.new
234
+ obj.og_read(row)
235
+
236
+ res.close
237
+ return obj
238
+ end
239
+
240
+ def read_all(res, klass)
241
+ return [] unless valid_res?(res)
242
+ objects = []
243
+
244
+ res.each do |row|
245
+ obj = klass.new
246
+ obj.og_read(row)
247
+ objects << obj
248
+ end
249
+
250
+ res.close
251
+ return objects
252
+ end
253
+
254
+ def read_int(res, idx = 0)
255
+ val = res.next[idx].to_i
256
+ res.close
257
+ return val
258
+ end
259
+
260
+ end
261
+
262
+ end
@@ -1,6 +1,6 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: backend.rb 202 2005-01-17 10:44:13Z gmosx $
3
+ # $Id: backend.rb 249 2005-02-04 14:03:00Z gmosx $
4
4
 
5
5
  require 'yaml'
6
6
 
@@ -1,6 +1,6 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: connection.rb 248 2005-01-31 13:38:34Z gmosx $
3
+ # $Id: connection.rb 254 2005-02-10 12:44:05Z gmosx $
4
4
 
5
5
  class Og;
6
6
 
@@ -12,41 +12,138 @@ require 'glue/time'
12
12
  # functionality. A backend specific implementation file (driver)
13
13
  # implements all methods.
14
14
  #
15
- # === Future
15
+ # == Future
16
16
  #
17
17
  # - support caching.
18
18
  # - support prepared statements.
19
19
 
20
20
  class Connection
21
- # The frontend (Og) contains useful strucutres.
22
-
23
- attr_reader :og
24
-
25
- # The backend
21
+
22
+ # The Og database object.
26
23
 
27
24
  attr_reader :db
28
25
 
29
- # If set to true, the select methods deserialize the
30
- # resultset to create entities.
26
+ # The actual connection to the backend store.
27
+
28
+ attr_accessor :store
29
+
30
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
31
+ # :section: Backend connection methods.
32
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
31
33
 
32
- attr_accessor :deserialize
33
-
34
34
  # Initialize a connection to the database.
35
35
 
36
- def initialize(og)
37
- @og = og
38
- @db = @og.config[:backend].new(@og.config)
39
- @deserialize = true
36
+ def initialize(db)
37
+ @db = db
40
38
  Logger.debug "Created DB connection." if $DBG
41
39
  end
42
40
 
43
41
  # Close the connection to the database.
44
42
 
45
- def close()
46
- @db.close()
43
+ def close
44
+ @store.close
47
45
  Logger.debug "Closed DB connection." if $DBG
48
46
  end
49
47
 
48
+ # Create the managed object table. The properties of the
49
+ # object are mapped to the table columns. Additional sql relations
50
+ # and constrains are created (indicices, sequences, etc).
51
+
52
+ def create_table(klass)
53
+ raise 'Not implemented!'
54
+ end
55
+
56
+ # Drop the managed object table.
57
+
58
+ def drop_table(klass)
59
+ exec "DROP TABLE #{klass::DBTABLE}"
60
+ end
61
+
62
+ # Prepare an sql statement.
63
+
64
+ def prepare(sql)
65
+ raise 'Not implemented!'
66
+ end
67
+
68
+ # Execute an SQL query and return the result.
69
+
70
+ def query(sql)
71
+ raise 'Not implemented!'
72
+ end
73
+
74
+ # Execute an SQL query, no result returned.
75
+
76
+ def exec(sql)
77
+ raise 'Not implemented!'
78
+ end
79
+ alias_method :execute, :exec
80
+
81
+ # Start a new transaction.
82
+
83
+ def start
84
+ exec 'START TRANSACTION'
85
+ end
86
+
87
+ # Commit a transaction.
88
+
89
+ def commit
90
+ exec 'COMMIT'
91
+ end
92
+
93
+ # Rollback a transaction.
94
+
95
+ def rollback
96
+ exec 'ROLLBACK'
97
+ end
98
+
99
+ # Transaction helper. In the transaction block use
100
+ # the db pointer to the backend.
101
+
102
+ def transaction(&block)
103
+ begin
104
+ start
105
+ yield(self)
106
+ commit
107
+ rescue => ex
108
+ Logger.error "DB Error: ERROR IN TRANSACTION"
109
+ Logger.error "#{ex}"
110
+ Logger.error "#{ex.backtrace}"
111
+ rollback
112
+ end
113
+ end
114
+
115
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
116
+ # :section: Deserialization methods.
117
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
118
+
119
+ # Is the given resultset valid?
120
+
121
+ def valid_res?(res)
122
+ return !(res.nil?)
123
+ end
124
+
125
+ # Read (deserialize) one row of the resultset.
126
+
127
+ def read_one(res, klass)
128
+ raise 'Not implemented!'
129
+ end
130
+
131
+ # Read (deserialize) all rows of the resultset.
132
+
133
+ def read_all(res, klass)
134
+ raise 'Not implemented!'
135
+ end
136
+
137
+ # Read the first column of the resultset as an Integer.
138
+
139
+ def read_int(res, idx = 0)
140
+ raise 'Not implemented!'
141
+ end
142
+
143
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
144
+ # :section: Managed object methods.
145
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
146
+
50
147
  # Save an object to the database. Insert if this is a new object or
51
148
  # update if this is already stored in the database.
52
149
 
@@ -110,7 +207,7 @@ class Connection
110
207
 
111
208
  def load_by_oid(oid, klass)
112
209
  res = query "SELECT * FROM #{klass::DBTABLE} WHERE oid=#{oid}"
113
- @deserialize? @db.deserialize_one(res, klass) : res
210
+ read_one(res, klass)
114
211
  end
115
212
  alias_method :get_by_oid, :load_by_oid
116
213
 
@@ -118,7 +215,7 @@ class Connection
118
215
 
119
216
  def load_by_name(name, klass)
120
217
  res = query "SELECT * FROM #{klass::DBTABLE} WHERE name='#{name}'"
121
- @deserialize? @db.deserialize_one(res, klass) : res
218
+ read_one(res, klass)
122
219
  end
123
220
  alias_method :get_by_name, :load_by_name
124
221
 
@@ -127,7 +224,7 @@ class Connection
127
224
 
128
225
  def load_all(klass, extrasql = nil)
129
226
  res = query "SELECT * FROM #{klass::DBTABLE} #{extrasql}"
130
- @deserialize? @db.deserialize_all(res, klass) : res
227
+ read_all(res, klass)
131
228
  end
132
229
  alias_method :get_all, :load_all
133
230
 
@@ -139,8 +236,8 @@ class Connection
139
236
  sql = "SELECT * FROM #{klass::DBTABLE} WHERE #{sql}"
140
237
  end
141
238
 
142
- res = @db.safe_query(sql)
143
- @deserialize? @db.deserialize_all(res, klass) : res
239
+ res = query(sql)
240
+ read_all(res, klass)
144
241
  end
145
242
 
146
243
  # Optimized for one result.
@@ -150,8 +247,8 @@ class Connection
150
247
  sql = "SELECT * FROM #{klass::DBTABLE} WHERE #{sql}"
151
248
  end
152
249
 
153
- res = @db.safe_query(sql)
154
- @deserialize? @db.deserialize_one(res, klass) : res
250
+ res = query(sql)
251
+ read_one(res, klass)
155
252
  end
156
253
 
157
254
  # Perform a count query.
@@ -161,9 +258,8 @@ class Connection
161
258
  sql = "SELECT COUNT(*) FROM #{klass::DBTABLE} WHERE #{sql}"
162
259
  end
163
260
 
164
- res = @db.safe_query(sql)
165
-
166
- return @db.get_int(res)
261
+ res = query(sql)
262
+ return read_int(res)
167
263
  end
168
264
 
169
265
  # Delete an object from the database. Allways perform a deep delete.
@@ -199,66 +295,6 @@ class Connection
199
295
  end
200
296
  alias_method :delete!, :delete
201
297
 
202
- # Create the managed object table. The properties of the
203
- # object are mapped to the table columns. Additional sql relations
204
- # and constrains are created (indicices, sequences, etc).
205
-
206
- def create_table(klass)
207
- @db.create_table(klass)
208
- end
209
-
210
- # Drop the managed object table.
211
-
212
- def drop_table(klass)
213
- @db.drop_table(klass)
214
- end
215
-
216
- # Execute an SQL query and return the result
217
-
218
- def query(sql)
219
- @db.safe_query(sql)
220
- end
221
-
222
- # Execute an SQL query, no result returned.
223
-
224
- def exec(sql)
225
- @db.safe_exec(sql)
226
- end
227
-
228
- # Start a new transaction.
229
-
230
- def start
231
- @db.start
232
- end
233
-
234
- # Commit a transaction.
235
-
236
- def commit
237
- @db.commit
238
- end
239
-
240
- # Rollback transaction.
241
-
242
- def rollback
243
- @db.rollback
244
- end
245
-
246
- # Transaction helper. In the transaction block use
247
- # the db pointer to the backend.
248
-
249
- def transaction(&block)
250
- begin
251
- @db.start
252
- yield(@db)
253
- @db.commit
254
- rescue => ex
255
- Logger.error "DB Error: ERROR IN TRANSACTION"
256
- Logger.error #{ex}
257
- Logger.error #{ex.backtrace}
258
- @db.rollback
259
- end
260
- end
261
-
262
298
  end
263
299
 
264
300
  end