og 0.9.5 → 0.10.0

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