odba 1.1.3 → 1.1.8

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/lib/odba/marshal.rb CHANGED
@@ -2,8 +2,8 @@
2
2
  #-- Marshal -- odba -- 29.04.2004 -- hwyss@ywesee.com rwaltert@ywesee.com mwalder@ywesee.com
3
3
 
4
4
  module ODBA
5
- # Marshal is a simple extension of ::Marshal. To be able to store our data
6
- # using the DBI-Interface, we need to escape invalid characters from the
5
+ # Marshal is a simple extension of ::Marshal. To be able to store our data
6
+ # using the DBI-Interface, we need to escape invalid characters from the
7
7
  # standard binary dump.
8
8
  module Marshal
9
9
  def Marshal.dump(obj)
@@ -13,6 +13,9 @@ module ODBA
13
13
  def Marshal.load(hexdump)
14
14
  binary = [hexdump].pack('H*')
15
15
  ::Marshal.load(binary)
16
+ rescue => error
17
+ $stderr.puts "#{error}: hexdump is #{hexdump.inspect} #{error.backtrace.join("\n")}"
18
+ Date.new
16
19
  end
17
20
  end
18
21
  end
@@ -45,7 +45,7 @@ module ODBA
45
45
  opts = keys.pop
46
46
  end
47
47
  if(keys.last.is_a?(Class))
48
- origin_klass = keys.pop
48
+ origin_klass = keys.pop
49
49
  resolve = keys.pop
50
50
  resolve_origin = keys.pop
51
51
  elsif(keys.last.is_a?(Symbol))
@@ -58,7 +58,7 @@ module ODBA
58
58
  else
59
59
  resolve = keys.first
60
60
  end
61
- keys.each { |key|
61
+ keys.each { |key|
62
62
  if RUBY_VERSION >= '1.9'
63
63
  key = key.to_sym
64
64
  else
@@ -85,8 +85,8 @@ module ODBA
85
85
  opts.each { |key, val| index_definition.send "#{key}=", val }
86
86
  ODBA.cache.ensure_index_deferred(index_definition)
87
87
  meta_eval {
88
- define_method(search_name) { |*vals|
89
- if(vals.size > 1)
88
+ define_method(search_name) { |*vals|
89
+ if(vals.size > 1)
90
90
  args = {}
91
91
  vals.each_with_index { |val, idx|
92
92
  cond = case val
@@ -95,7 +95,7 @@ module ODBA
95
95
  else
96
96
  'like'
97
97
  end
98
- args.store(keys.at(idx),
98
+ args.store(keys.at(idx),
99
99
  { 'value' => val, 'condition' => cond })
100
100
  }
101
101
  ODBA.cache.retrieve_from_index(index_name, args)
@@ -104,12 +104,12 @@ module ODBA
104
104
  end
105
105
  }
106
106
  define_method(exact_name) { |*vals|
107
- if(vals.size > 1)
107
+ if(vals.size > 1)
108
108
  args = {}
109
109
  vals.each_with_index { |val, idx|
110
110
  args.store(keys.at(idx), val)
111
111
  }
112
- ODBA.cache.retrieve_from_index(index_name, args,
112
+ ODBA.cache.retrieve_from_index(index_name, args,
113
113
  ODBA::Persistable::Exact)
114
114
  else
115
115
  ODBA.cache.retrieve_from_index(index_name, vals.first,
@@ -117,7 +117,7 @@ module ODBA
117
117
  end
118
118
  }
119
119
  define_method(find_name) { |*vals|
120
- if(vals.size > 1)
120
+ if(vals.size > 1)
121
121
  args = {}
122
122
  vals.each_with_index { |val, idx|
123
123
  cond = case val
@@ -126,7 +126,7 @@ module ODBA
126
126
  else
127
127
  'like'
128
128
  end
129
- args.store(keys.at(idx),
129
+ args.store(keys.at(idx),
130
130
  { 'value' => val, 'condition' => cond })
131
131
  }
132
132
  ODBA.cache.retrieve_from_index(index_name, args,
@@ -145,7 +145,7 @@ module ODBA
145
145
  index_definition
146
146
  end
147
147
  def odba_extent
148
- all = ODBA.cache.extent(self)
148
+ all = ODBA.cache.extent(self)
149
149
  if(block_given?)
150
150
  all.each { |instance| yield instance }
151
151
  nil
@@ -154,7 +154,7 @@ module ODBA
154
154
  end
155
155
  end
156
156
  def odba_count
157
- ODBA.cache.count(self)
157
+ ODBA.cache.count(self)
158
158
  end
159
159
  end
160
160
  }
@@ -164,7 +164,7 @@ module ODBA
164
164
  name.gsub(@@sanitize_ptrn, '_')
165
165
  end
166
166
  attr_accessor :odba_name, :odba_prefetch
167
- # Classes which include Persistable may override ODBA_EXCLUDE_VARS to
167
+ # Classes which include Persistable may override ODBA_EXCLUDE_VARS to
168
168
  # prevent data from being stored in the database (e.g. passwords, file
169
169
  # descriptors). Simply redefine: ODBA_EXCLUDE_VARS = ['@foo']
170
170
  ODBA_EXCLUDE_VARS = []
@@ -178,7 +178,7 @@ module ODBA
178
178
  ODBA_PREDEFINE_EXCLUDE_VARS = ['@odba_observers'] # :nodoc:
179
179
  ODBA_PREDEFINE_SERIALIZABLE = ['@odba_target_ids'] # :nodoc:, legacy
180
180
  end
181
- # If you want to prevent Persistables from being disconnected and stored
181
+ # If you want to prevent Persistables from being disconnected and stored
182
182
  # separately (Array and Hash are Persistable by default), redefine:
183
183
  # ODBA_SERIALIZABLE = ['@bar']
184
184
  ODBA_SERIALIZABLE = []
@@ -188,7 +188,7 @@ module ODBA
188
188
  @@odba_id_name = RUBY_VERSION >= '1.9' ? :@odba_id : '@odba_id'
189
189
  def dup # :nodoc:
190
190
  twin = super
191
- ## since twin may not be a Persistable, we need to do some magic here to
191
+ ## since twin may not be a Persistable, we need to do some magic here to
192
192
  # ensure that it does not have the same odba_id
193
193
  twin.instance_variable_set(@@odba_id_name, nil)
194
194
  twin
@@ -221,7 +221,7 @@ module ODBA
221
221
  def odba_delete
222
222
  ODBA.cache.delete(self)
223
223
  end
224
- # Delete _observer_ as an observer on this object.
224
+ # Delete _observer_ as an observer on this object.
225
225
  # It will no longer receive notifications.
226
226
  def odba_delete_observer(observer)
227
227
  @odba_observers.delete(observer) if(@odba_observers)
@@ -251,7 +251,7 @@ module ODBA
251
251
  ODBA_PREDEFINE_EXCLUDE_VARS
252
252
  end
253
253
  if(defined?(self::class::ODBA_EXCLUDE_VARS))
254
- exc += self::class::ODBA_EXCLUDE_VARS
254
+ exc += self::class::ODBA_EXCLUDE_VARS
255
255
  end
256
256
  if RUBY_VERSION >= '1.9'
257
257
  exc.map{|v| v.to_sym}
@@ -259,8 +259,8 @@ module ODBA
259
259
  exc
260
260
  end
261
261
  end
262
- # Returns the odba unique id of this Persistable.
263
- # If no id had been assigned, this is now done.
262
+ # Returns the odba unique id of this Persistable.
263
+ # If no id had been assigned, this is now done.
264
264
  # No attempt is made to store the Persistable in the db.
265
265
  def odba_id
266
266
  @odba_id ||= ODBA.cache.next_id
@@ -269,7 +269,7 @@ module ODBA
269
269
  ODBA.marshaller.dump(odba_isolated_twin)
270
270
  end
271
271
  # Convenience method equivalent to ODBA.cache.store(self)
272
- def odba_isolated_store
272
+ def odba_isolated_store
273
273
  @odba_persistent = true
274
274
  ODBA.cache.store(self)
275
275
  end
@@ -278,7 +278,7 @@ module ODBA
278
278
  def odba_isolated_stub
279
279
  Stub.new(self.odba_id, nil, self)
280
280
  end
281
- # Returns a duplicate of this Persistable, for which all connected
281
+ # Returns a duplicate of this Persistable, for which all connected
282
282
  # Persistables have been replaced by a Stub
283
283
  def odba_isolated_twin
284
284
  # ensure a valid odba_id
@@ -289,11 +289,12 @@ module ODBA
289
289
  twin
290
290
  end
291
291
  # A Persistable instance can be _prefetchable_. This means that the object
292
- # can be loaded at startup by calling ODBA.cache.prefetch, and that it will
293
- # never expire from the Cache. The prefetch status can be controlled per
294
- # instance by setting the instance variable @odba_prefetch, and per class by
292
+ # can be loaded at startup by calling ODBA.cache.prefetch, and that it will
293
+ # never expire from the Cache. The prefetch status can be controlled per
294
+ # instance by setting the instance variable @odba_prefetch, and per class by
295
295
  # overriding the module constant ODBA_PREFETCH
296
296
  def odba_prefetch?
297
+ @odba_prefetch ||= nil
297
298
  @odba_prefetch \
298
299
  || (defined?(self::class::ODBA_PREFETCH) && self::class::ODBA_PREFETCH)
299
300
  end
@@ -301,9 +302,9 @@ module ODBA
301
302
  @odba_indexable \
302
303
  || (defined?(self::class::ODBA_INDEXABLE) && self::class::ODBA_INDEXABLE)
303
304
  end
304
- # Invoke the update method in each currently associated observer
305
+ # Invoke the update method in each currently associated observer
305
306
  # in turn, passing it the given arguments
306
- def odba_notify_observers(*args)
307
+ def odba_notify_observers(*args)
307
308
  odba_observers.each { |obs| obs.odba_update(*args) }
308
309
  end
309
310
  def odba_observers
@@ -313,6 +314,7 @@ module ODBA
313
314
  instance_variables - odba_serializables - odba_exclude_vars
314
315
  end
315
316
  def odba_replace!(obj) # :nodoc:
317
+ @odba_observers ||= []
316
318
  instance_variables.each { |name|
317
319
  instance_variable_set(name, obj.instance_variable_get(name))
318
320
  }
@@ -353,7 +355,7 @@ module ODBA
353
355
  ODBA_PREDEFINE_SERIALIZABLE
354
356
  end
355
357
  if(defined?(self::class::ODBA_SERIALIZABLE))
356
- srs += self::class::ODBA_SERIALIZABLE
358
+ srs += self::class::ODBA_SERIALIZABLE
357
359
  end
358
360
  if RUBY_VERSION >= '1.9'
359
361
  srs.map{|s| s.to_sym}
@@ -369,7 +371,7 @@ module ODBA
369
371
  end
370
372
  # Stores this Persistable and recursively all connected unsaved persistables,
371
373
  # until no more direcly connected unsaved persistables can be found.
372
- # The optional parameter _name_ can be used later to retrieve this
374
+ # The optional parameter _name_ can be used later to retrieve this
373
375
  # Persistable using Cache#fetch_named
374
376
  def odba_store(name = nil)
375
377
  begin
@@ -379,7 +381,7 @@ module ODBA
379
381
  end
380
382
  odba_store_unsaved
381
383
  self
382
- rescue
384
+ rescue
383
385
  @odba_name = old_name
384
386
  raise
385
387
  end
@@ -406,14 +408,14 @@ module ODBA
406
408
  # must not be synchronized because of the following if
407
409
  # statement (if an object has already been replaced by
408
410
  # a stub, it will have the correct id and it
409
- # will be ignored)
411
+ # will be ignored)
410
412
  case var
411
413
  when Stub
412
414
  # no need to make a new stub
413
415
  when Persistable
414
- if(var.odba_id == id)
416
+ if(var.odba_id == id)
415
417
  stub = ODBA::Stub.new(id, self, obj)
416
- instance_variable_set(name, stub)
418
+ instance_variable_set(name, stub)
417
419
  end
418
420
  end
419
421
  }
@@ -465,6 +467,7 @@ module ODBA
465
467
  !@odba_persistent
466
468
  #true
467
469
  else
470
+ @odba_snapshot_level ||= 0
468
471
  @odba_snapshot_level.to_i < snapshot_level
469
472
  end
470
473
  end
@@ -482,7 +485,7 @@ class Array # :nodoc: all
482
485
  def odba_collection
483
486
  coll = []
484
487
  each_with_index { |item, index|
485
- coll.push([index, item])
488
+ coll.push([index, item])
486
489
  }
487
490
  coll
488
491
  end
@@ -491,9 +494,9 @@ class Array # :nodoc: all
491
494
  delete_if { |val| val.eql?(remove_object) }
492
495
  end
493
496
  def odba_prefetch?
494
- super || any? { |item|
497
+ super || any? { |item|
495
498
  item.respond_to?(:odba_prefetch?) \
496
- && item.odba_prefetch?
499
+ && item.odba_prefetch?
497
500
  }
498
501
  end
499
502
  def odba_replace!(obj) # :nodoc:
@@ -505,7 +508,7 @@ class Array # :nodoc: all
505
508
  super
506
509
  end
507
510
  def odba_restore(collection=[])
508
- collection.each { |key, val|
511
+ collection.each { |key, val|
509
512
  self[key] = val
510
513
  }
511
514
  end
@@ -579,7 +582,7 @@ class Hash # :nodoc: all
579
582
  super
580
583
  end
581
584
  def odba_restore(collection=[])
582
- collection.each { |key, val|
585
+ collection.each { |key, val|
583
586
  self[key] = val
584
587
  }
585
588
  end
data/lib/odba/storage.rb CHANGED
@@ -158,11 +158,14 @@ CREATE INDEX IF NOT EXISTS target_id_#{table_name} ON #{table_name}(target_id);
158
158
  end
159
159
  def create_fulltext_index(table_name)
160
160
  self.dbi.do <<-SQL
161
- CREATE TABLE IF NOT EXISTS #{table_name} (
161
+ DROP TABLE IF EXISTS #{table_name};
162
+ SQL
163
+ self.dbi.do <<-SQL
164
+ CREATE TABLE IF NOT EXISTS #{table_name} (
162
165
  origin_id INTEGER,
163
166
  search_term tsvector,
164
167
  target_id INTEGER
165
- );
168
+ ) WITH OIDS ;
166
169
  SQL
167
170
  #index origin_id
168
171
  self.dbi.do <<-SQL
@@ -178,12 +181,15 @@ CREATE INDEX IF NOT EXISTS target_id_#{table_name} ON #{table_name}(target_id);
178
181
  SQL
179
182
  end
180
183
  def create_index(table_name)
184
+ self.dbi.do <<-SQL
185
+ DROP TABLE IF EXISTS #{table_name};
186
+ SQL
181
187
  self.dbi.do <<-SQL
182
188
  CREATE TABLE IF NOT EXISTS #{table_name} (
183
189
  origin_id INTEGER,
184
190
  search_term TEXT,
185
191
  target_id INTEGER
186
- );
192
+ ) WITH OIDS;
187
193
  SQL
188
194
  #index origin_id
189
195
  self.dbi.do <<-SQL
@@ -205,7 +211,7 @@ CREATE INDEX IF NOT EXISTS target_id_#{table_name} ON #{table_name}(target_id);
205
211
  Thread.current[:txn] || @dbi
206
212
  end
207
213
  def drop_index(index_name)
208
- self.dbi.do "DROP TABLE #{index_name}"
214
+ self.dbi.do "DROP TABLE IF EXISTS #{index_name}"
209
215
  end
210
216
  def delete_index_element(index_name, odba_id, id_name)
211
217
  self.dbi.do <<-SQL, odba_id
@@ -301,13 +307,20 @@ CREATE INDEX IF NOT EXISTS target_id_#{table_name} ON #{table_name}(target_id);
301
307
  def generate_dictionary(language)
302
308
  # postgres searches for the dictionary file in the directory share/tsearch_data of it installation location
303
309
  # By default under gentoo, this is /usr/share/postgresql/tsearch_data/
310
+ # Use /usr/local/pgsql-10.1/bin/pg_config --sharedir to get the current value
304
311
  # As we have no way to get the current installation path, we do not check whether the files are present or not
305
312
  file='fulltext'
306
313
  # setup configuration
314
+ self.dbi.do <<-SQL
315
+ DROP TEXT SEARCH DICTIONARY IF EXISTS public.default_#{language};
316
+ SQL
307
317
  self.dbi.do <<-SQL
308
318
  CREATE TEXT SEARCH CONFIGURATION public.default_#{language} ( COPY = pg_catalog.#{language} );
309
319
  SQL
310
320
  # ispell
321
+ self.dbi.do <<-SQL
322
+ DROP TEXT SEARCH DICTIONARY IF EXISTS #{language}_ispell;
323
+ SQL
311
324
  self.dbi.do <<-SQL
312
325
  CREATE TEXT SEARCH DICTIONARY #{language}_ispell (
313
326
  TEMPLATE = ispell,
@@ -460,7 +473,7 @@ CREATE INDEX IF NOT EXISTS target_id_#{table_name} ON #{table_name}(target_id);
460
473
  end
461
474
  self.dbi.select_all(sql, *values)
462
475
  end
463
- def retrieve_from_fulltext_index(index_name, search_term, dict, limit=nil)
476
+ def retrieve_from_fulltext_index(index_name, search_term, limit=nil)
464
477
  ## this combination of gsub statements solves the problem of
465
478
  # properly escaping strings of this form: "(2:1)" into
466
479
  # '\(2\:1\)' (see test_retrieve_from_fulltext_index)
@@ -468,19 +481,19 @@ CREATE INDEX IF NOT EXISTS target_id_#{table_name} ON #{table_name}(target_id);
468
481
  .gsub(/[():]/i, '\\ \\&').gsub(/\s/, '')
469
482
  sql = <<-EOQ
470
483
  SELECT target_id,
471
- max(ts_rank(search_term, to_tsquery(?, ?))) AS relevance
484
+ max(ts_rank(search_term, to_tsquery(?))) AS relevance
472
485
  FROM #{index_name}
473
- WHERE search_term @@ to_tsquery(?, ?)
486
+ WHERE search_term @@ to_tsquery(?)
474
487
  GROUP BY target_id
475
488
  ORDER BY relevance DESC
476
489
  EOQ
477
490
  if(limit)
478
491
  sql << " LIMIT #{limit}"
479
492
  end
480
- self.dbi.select_all(sql, dict, term, dict, term)
493
+ self.dbi.select_all(sql, term, term)
481
494
  rescue DBI::ProgrammingError => e
482
495
  warn("ODBA::Storage.retrieve_from_fulltext_index rescued a DBI::ProgrammingError(#{e.message}). Query:")
483
- warn("self.dbi.select_all(#{sql}, #{dict}, #{term}, #{dict}, #{term})")
496
+ warn("self.dbi.select_all(#{sql}, #{term}, #{term})")
484
497
  warn("returning empty result")
485
498
  []
486
499
  end
@@ -580,20 +593,24 @@ WHERE origin_id = ?
580
593
  SQL
581
594
  end
582
595
  end
583
- def update_fulltext_index(index_name, origin_id, search_term, target_id, dict)
596
+ def update_fulltext_index(index_name, origin_id, search_term, target_id)
584
597
  search_term = search_term.gsub(/\s+/, ' ').strip
585
598
  if(target_id)
586
- self.dbi.do <<-SQL, origin_id, dict, search_term, target_id
599
+ value = <<-SQL, origin_id.to_s, search_term, target_id
587
600
  INSERT INTO #{index_name} (origin_id, search_term, target_id)
588
- VALUES (?, to_tsvector(?, ?), ?)
601
+ VALUES (?, to_tsvector(?), ?)
602
+ SQL
603
+ result = self.dbi.do <<-SQL, origin_id.to_s, search_term, target_id
604
+ INSERT INTO #{index_name} (origin_id, search_term, target_id)
605
+ VALUES (?, to_tsvector(?), ?)
589
606
  SQL
590
607
  else
591
- self.dbi.do <<-SQL, dict, search_term, origin_id
592
- UPDATE #{index_name} SET search_term=to_tsvector(?, ?)
608
+ result = self.dbi.do <<-SQL, search_term, origin_id
609
+ UPDATE #{index_name} SET search_term=to_tsvector(?)
593
610
  WHERE origin_id=?
594
611
  SQL
595
612
  end
596
- end
613
+ end
597
614
  def update_index(index_name, origin_id, search_term, target_id)
598
615
  if(target_id)
599
616
  self.dbi.do <<-SQL, origin_id, search_term, target_id
data/lib/odba/stub.rb CHANGED
@@ -10,7 +10,7 @@ module ODBA
10
10
  def initialize(odba_id, odba_container, receiver)
11
11
  @odba_id = odba_id
12
12
  @odba_container = odba_container
13
- @odba_class = receiver.class unless receiver.nil?
13
+ @odba_class = receiver.class unless receiver.nil?
14
14
  @receiver_loaded = true
15
15
  end
16
16
  def class
@@ -50,16 +50,19 @@ module ODBA
50
50
  @receiver_loaded = true
51
51
  if(@odba_container)
52
52
  @odba_container.odba_replace_stubs(@odba_id, @receiver)
53
- else
53
+ else
54
54
  warn "Potential Memory-Leak: stub for #{@receiver.class}##{@odba_id} was saved without container"
55
55
  end
56
56
  @receiver
57
57
  rescue OdbaError => e
58
- warn "ODBA::Stub was unable to replace #{@odba_class}##{@odba_id} from #{@odba_container.class}:##{@odba_container.odba_id}"
58
+ puts "OdbaError"
59
+ puts caller[0..10].join("\n")
60
+ warn "ODBA::Stub was unable to replace #{@odba_class}##{@odba_id} from #{@odba_container.class}:##{@odba_container.odba_id}. raise OdbaError"
61
+ raise OdbaError
59
62
  end
60
63
  end
61
64
  alias :odba_instance :odba_receiver
62
- # A stub always references a Persistable that has
65
+ # A stub always references a Persistable that has
63
66
  # already been saved.
64
67
  def odba_unsaved?(snapshot_level=nil)
65
68
  false
@@ -78,8 +81,8 @@ module ODBA
78
81
  end
79
82
  end
80
83
  no_override = [
81
- "class", "is_a?", "__id__", "__send__", "inspect",
82
- "eql?", "nil?", "respond_to?", "object_id",
84
+ "class", "is_a?", "__id__", "__send__", "inspect",
85
+ "eql?", "nil?", "respond_to?", "object_id",
83
86
  "instance_variables", "instance_variable_get",
84
87
  "instance_variable_set", "==",
85
88
  ## methods defined in persistable.rb:Object