Skip to content

Commit

Permalink
Adding support for read only queries.
Browse files Browse the repository at this point in the history
  • Loading branch information
JEG2 committed Feb 21, 2010
1 parent 6ba308f commit df97d74
Show file tree
Hide file tree
Showing 8 changed files with 581 additions and 84 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.rdoc
Expand Up @@ -5,7 +5,10 @@ Below is a complete listing of changes for each revision of Oklahoma Mixer.
== 0.4.0 == 0.4.0


* Added the read_only?() method * Added the read_only?() method
* Added error handling for table queries * Added error handling for Table Databases queries
* Added support for Table Databases queries on read only databases
* Added support for iteration blocks to Table Databases searches
* Modified Table Databases blocks to yield tuples consistent with the iterators


== 0.3.0 == 0.3.0


Expand Down
2 changes: 0 additions & 2 deletions TODO.rdoc
Expand Up @@ -4,8 +4,6 @@ The following is a list of planned expansions for Oklahoma Mixer in the order I
intend to address them. intend to address them.


1. Resolve minor issues 1. Resolve minor issues
* Support a read only-version of all() without a block
* Make all() yield() like Hash#each (a key-value tuple)
* Speed up counts (fetch keys only to prevent HashMap conversion) * Speed up counts (fetch keys only to prevent HashMap conversion)
* Push :select down into all() call to speed up paginate() * Push :select down into all() call to speed up paginate()
2. Include some higher level abstractions like mixed tables, queues, and shards 2. Include some higher level abstractions like mixed tables, queues, and shards
Expand Down
13 changes: 4 additions & 9 deletions lib/oklahoma_mixer/array_list.rb
Expand Up @@ -11,17 +11,12 @@ def initialize(pointer_or_size)


attr_reader :pointer attr_reader :pointer


def shift include Enumerable
value = C.read_from_func(:shift, @pointer)
block_given? ? yield(value) : value
end


def map def each
values = [ ] (0...C.num(pointer)).each do |i|
while value = shift yield C.read_from_func(:val, :no_free, @pointer, i)
values << yield(value)
end end
values
end end


def push(*values) def push(*values)
Expand Down
10 changes: 7 additions & 3 deletions lib/oklahoma_mixer/array_list/c.rb
Expand Up @@ -12,10 +12,14 @@ module C # :nodoc:
:returns => :pointer :returns => :pointer
func :name => :del, func :name => :del,
:args => :pointer :args => :pointer


func :name => :shift, func :name => :num,
:args => [:pointer, :pointer], :args => :pointer,
:returns => :int
func :name => :val,
:args => [:pointer, :int, :pointer],
:returns => :pointer :returns => :pointer

func :name => :push, func :name => :push,
:args => [:pointer, :pointer, :int] :args => [:pointer, :pointer, :int]
end end
Expand Down
140 changes: 75 additions & 65 deletions lib/oklahoma_mixer/table_database.rb
Expand Up @@ -114,7 +114,7 @@ def each
def all(options = { }, &iterator) def all(options = { }, &iterator)
query(options) do |q| query(options) do |q|
mode = results_mode(options) mode = results_mode(options)
if block_given? if not iterator.nil? and not read_only?
results = self results = self
callback = lambda { |key_pointer, key_size, doc_map, _| callback = lambda { |key_pointer, key_size, doc_map, _|
if mode != :docs if mode != :docs
Expand All @@ -125,9 +125,10 @@ def all(options = { }, &iterator)
doc = map.to_hash { |string| cast_to_encoded_string(string) } doc = map.to_hash { |string| cast_to_encoded_string(string) }
end end
flags = case mode flags = case mode
when :keys then yield(key) when :keys then iterator[key]
when :docs then yield(doc) when :docs then iterator[doc]
else yield(key, doc) when :aoh then iterator[doc.merge!(:primary_key => key)]
else iterator[[key, doc]]
end end
Array(flags).inject(0) { |returned_flags, flag| Array(flags).inject(0) { |returned_flags, flag|
returned_flags | case flag.to_s returned_flags | case flag.to_s
Expand All @@ -144,34 +145,16 @@ def all(options = { }, &iterator)
end end
} }
} }
unless lib.qryproc(q.pointer, callback, nil)
error_code = lib.ecode(@db)
error_message = lib.errmsg(error_code)
fail Error::QueryError,
"#{error_message} (error code #{error_code})"
end
results
else else
results = mode != :hoh ? [ ] : { } query_results(lib.qrysearch(q.pointer), mode, &iterator)
callback = lambda { |key_pointer, key_size, doc_map, _|
if mode == :docs
results << cast_value_out(doc_map, :no_free)
else
key = cast_key_out(key_pointer.get_bytes(0, key_size))
case mode
when :keys
results << key
when :hoh
results[key] = cast_value_out(doc_map, :no_free)
when :aoh
results << cast_value_out(doc_map, :no_free).
merge(:primary_key => key)
else
results << [key, cast_value_out(doc_map, :no_free)]
end
end
0
}
end end
unless lib.qryproc(q.pointer, callback, nil)
error_code = lib.ecode(@db)
error_message = lib.errmsg(error_code)
fail Error::QueryError, "#{error_message} (error code #{error_code})"
end
results
end end
end end


Expand All @@ -196,7 +179,8 @@ def paginate(options)
fail Error::QueryError, ":per_page must be >= 1" if results.per_page < 1 fail Error::QueryError, ":per_page must be >= 1" if results.per_page < 1
results.total_entries = 0 results.total_entries = 0
all( options.merge( :select => :keys_and_docs, all( options.merge( :select => :keys_and_docs,
:limit => nil ) ) { |key, value| :return => :aoa,
:limit => nil ) ) do |key, value|
if results.total_entries >= results.offset and if results.total_entries >= results.offset and
results.size < results.per_page results.size < results.per_page
case mode case mode
Expand All @@ -213,21 +197,21 @@ def paginate(options)
end end
end end
results.total_entries += 1 results.total_entries += 1
} end
results results
end end


def union(q, *queries) def union(q, *queries, &iterator)
search([q] + queries, lib::SEARCHES[:TDBMSUNION]) search([q] + queries, lib::SEARCHES[:TDBMSUNION], &iterator)
end end


def intersection(q, *queries) def intersection(q, *queries, &iterator)
search([q] + queries, lib::SEARCHES[:TDBMSISECT]) search([q] + queries, lib::SEARCHES[:TDBMSISECT], &iterator)
end end
alias_method :isect, :intersection alias_method :isect, :intersection


def difference(q, *queries) def difference(q, *queries, &iterator)
search([q] + queries, lib::SEARCHES[:TDBMSDIFF]) search([q] + queries, lib::SEARCHES[:TDBMSDIFF], &iterator)
end end
alias_method :diff, :difference alias_method :diff, :difference


Expand Down Expand Up @@ -365,42 +349,68 @@ def results_mode(options)
end end
end end


def search(queries, operation) def query_results(results, mode, &iterator)
mode = results_mode(queries.first) keys = ArrayList.new(results)
qs = queries.map { |q| query(q) } if iterator.nil?
keys = ArrayList.new( Utilities.temp_pointer(qs.size) do |pointer| results = mode == :hoh ? { } : [ ]
pointer.write_array_of_pointer(qs.map { |q| q.pointer }) iterator = lambda do |key_and_value|
lib.metasearch(pointer, qs.size, operation) if mode == :hoh
end ) results[key_and_value.first] = key_and_value.last
case mode else
when :keys results << key_and_value
keys.map { |key| cast_key_out(key) } end
when :docs
keys.map { |key| self[cast_key_out(key)] }
when :hoh
results = { }
while key = keys.shift { |k| cast_key_out(k) }
results[key] = self[key]
end end
results
when :aoh
keys.map { |key|
key = cast_key_out(key)
self[key].merge(:primary_key => key)
}
else else
keys.map { |key| results = self
key = cast_key_out(key) end
[key, self[key]] keys.each do |key|
} flags = Array( case mode
when :keys
iterator[cast_key_out(key)]
when :docs
iterator[self[cast_key_out(key)]]
when :aoh
k = cast_key_out(key)
iterator[self[k].merge!(:primary_key => k)]
else
k = cast_key_out(key)
v = self[k]
iterator[[k, v]]
end ).map { |flag| flag.to_s }
if flags.include? "delete"
if read_only?
warn "attempted delete from a read only query"
else
delete(key)
end
elsif v and flags.include? "update"
if read_only?
warn "attempted update from a read only query"
else
self[k] = v
end
end
break if flags.include? "break"
end
results
ensure
keys.free if keys
end

def search(queries, operation, &iterator)
qs = queries.map { |q| query(q) }
Utilities.temp_pointer(qs.size) do |pointer|
pointer.write_array_of_pointer(qs.map { |q| q.pointer })
query_results( lib.metasearch(pointer, qs.size, operation),
results_mode(queries.first),
&iterator)
end end
ensure ensure
if qs if qs
qs.each do |q| qs.each do |q|
q.free q.free
end end
end end
keys.free if keys
end end
end end
end end
3 changes: 3 additions & 0 deletions lib/oklahoma_mixer/table_database/c.rb
Expand Up @@ -90,6 +90,9 @@ module C # :nodoc:
:args => [:pointer, :string, INDEXES], :args => [:pointer, :string, INDEXES],
:returns => :bool :returns => :bool


func :name => :qrysearch,
:args => :pointer,
:returns => :pointer
call :name => :TDBQRYPROC, call :name => :TDBQRYPROC,
:args => [:pointer, :int, :pointer, :pointer], :args => [:pointer, :int, :pointer, :pointer],
:returns => :int :returns => :int
Expand Down

0 comments on commit df97d74

Please sign in to comment.