Class: Oinky::Internal::CursorBase

Inherits:
Object
  • Object
show all
Defined in:
lib/oinky.rb

Overview

The cursor types and implementation methods are different for index cursors and table cursors. However, they operate almost identically. This is the common implementation. Index and Table specialize only slightly.

Direct Known Subclasses

Oinky::Index::Cursor, Table::Cursor

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host) ⇒ CursorBase

Returns a new instance of CursorBase.



524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
# File 'lib/oinky.rb', line 524

def initialize(host)
  if host.is_a? host_class
    @host = host
    @hosthandle = @host.send(:eval, "@handle")
    c = FFI::MemoryPointer.new(:pointer)
    Internal.wrap_oinky(impl(:cursor_new).call(@hosthandle, c))
    @handle = c.read_pointer
    seek_first
  elsif host.is_a? self.class
    c = FFI::MemoryPointer.new(:pointer)
    h, @hosthandle, @host = host.send(:eval, "[@handle, @hosthandle, @host]")
    Internal.wrap_oinky(impl(:cursor_clone).call(@hosthandle, h, c))
    @handle = c.read_pointer
  else
    raise OinkyException.new("Unknown argument type to #{self.class}.new")
  end

  # Cursors are automatically destroyed when the DB is destroyed.
  # If we call cursor deletion after DB deletion, that is equivalent to
  # freeing twice.  It's bad.  But we have no way of controlling the
  # order of finalizer invocation (between cursor and DB) short of
  # referencing the DB in the cursor finalizer.  But that causes the DB
  # to stick around for a whole extra GC cycle.  Generally, that overhead
  # is much worse than leaking the cursor objects for the lifetime of 
  # the DB.  So, we have two options:
  # 1) Keep a reference on the DB object in the cursor finalizer, causing
  #    a full GC-cycle delay in freeing DB resources.
  # 2) Leak cursor objects always, letting them be cleaned up implicitly
  #    when the DB is destroyed.
  # This is a configuration property called Oinky.implicit_cursor_cleanup
  # which defaults to 'true', indicating that we pursue strategy 2.  It
  # is a property of the database object instance which applies to all
  # cursors derived from that instance (table or index).
  # cursors 
  # applies to all cursors created after the property is set.  
  # Changing the global or db-local value dynamically at any time is 
  # safe.  It should generally only be set to 'false' for instances that 
  # will be long-lived, where the aggregate overhead of cursors created 
  # (tens of bytes) might compare to the overhead of the database itself.
  
  unless @host.db.implicit_cursor_cleanup
    ObjectSpace.define_finalizer( self, self.class.finalize(impl(:cursor_free), @handle, {:db=>@host.db}) )
  end
end

Class Method Details

.finalize(m, ptr, refs) ⇒ Object



569
570
571
# File 'lib/oinky.rb', line 569

def self.finalize(m, ptr, refs)
  proc { m.call(ptr); refs[:db] = nil }
end

Instance Method Details

#at_end?Boolean

Returns:

  • (Boolean)


595
596
597
# File 'lib/oinky.rb', line 595

def at_end?
  state_val == 1
end

#before_begin?Boolean

Returns:

  • (Boolean)


598
599
600
# File 'lib/oinky.rb', line 598

def before_begin?
  state_val == -1
end

#cloneObject



601
602
603
# File 'lib/oinky.rb', line 601

def clone
  self.class.new(self)
end

#column_selector_default(cs = nil) ⇒ Object



604
605
606
607
608
# File 'lib/oinky.rb', line 604

def column_selector_default(cs = nil)
  cs = self.table.select_all_columns unless cs
  cs = self.table.select_columns(cs) unless cs.is_a? ColumnSelector
  cs
end

#each_backward(cs = nil) ⇒ Object



651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
# File 'lib/oinky.rb', line 651

def each_backward(cs = nil)
  e = ValuesEnumerator.new {|blk|
    # The cursor may not begin in a valid position
    self.seek_prev unless self.is_valid?
    while self.is_valid?
      blk.yield self.select(cs)
      self.seek_prev
    end
  }
  if block_given?
    e.each{|k| yield k}
  else
    return e
  end
  self
end

#each_forward(cs = nil) ⇒ Object

Move this cursor forward until the end (or until enumeration is stopped)



635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
# File 'lib/oinky.rb', line 635

def each_forward(cs = nil)
  e = ValuesEnumerator.new {|blk|
    # The cursor may not begin in a valid position
    self.seek_next unless self.is_valid?
    while self.is_valid?
      blk.yield self.select(cs)
      self.seek_next
    end
  }
  if block_given?
    e.each{|k| yield k}
  else
    return e
  end
  self
end

#is_valid?Boolean

Returns:

  • (Boolean)


592
593
594
# File 'lib/oinky.rb', line 592

def is_valid?
  state_val == 0
end

#seek_firstObject



573
574
575
576
# File 'lib/oinky.rb', line 573

def seek_first
  Internal.wrap_oinky(impl(:cursor_seek_first).call(@hosthandle, @handle))
  self
end

#seek_lastObject



577
578
579
580
# File 'lib/oinky.rb', line 577

def seek_last
  Internal.wrap_oinky(impl(:cursor_seek_last).call(@hosthandle, @handle))
  self
end

#seek_nextObject



581
582
583
584
# File 'lib/oinky.rb', line 581

def seek_next
  Internal.wrap_oinky(impl(:cursor_seek_next).call(@hosthandle, @handle))
  self
end

#seek_prevObject



585
586
587
588
# File 'lib/oinky.rb', line 585

def seek_prev
  Internal.wrap_oinky(impl(:cursor_seek_prev).call(@hosthandle, @handle))
  self
end

#select(cs = nil) ⇒ Object



609
610
611
612
613
614
615
616
617
618
# File 'lib/oinky.rb', line 609

def select(cs = nil)
  cs = self.column_selector_default(cs)
  vals = self.select_values(cs)
  r = ExHash.new
  cns = cs.cols
  vals.each_with_index{|v,i|
    r[cns[i]] = v
  }
  return r
end

#select_allObject



619
620
621
# File 'lib/oinky.rb', line 619

def select_all
  self.select(nil)
end

#select_all_valuesObject



631
632
633
# File 'lib/oinky.rb', line 631

def select_all_values
  self.select_values(nil)
end

#select_values(cs = nil) ⇒ Object

Like <select>, but just returns values as an array, rather than as a hash



624
625
626
627
628
629
630
# File 'lib/oinky.rb', line 624

def select_values(cs = nil)
  cs = column_selector_default(cs)
  vs = Internal::VariantSet.new(cs.size)
  out_count = FFI::MemoryPointer.new :size_t
  Internal.wrap_oinky(impl(:cursor_get_values).call(@handle, cs.handle, cs.size, vs, out_count))
  (0..out_count.read_int-1).map{|i| vs[i].rb_val }
end

#state_valObject



589
590
591
# File 'lib/oinky.rb', line 589

def state_val
  impl(:cursor_state).call(@hosthandle, @handle)
end