lib/btrieve/btrieve_table.rb in beezwax-0.5.2 vs lib/btrieve/btrieve_table.rb in beezwax-0.6.0

- old
+ new

@@ -1,80 +1,54 @@ require 'digest/md5' +# Represents a single btrieve table for a particular BTR database. class BtrieveTable include Btrieve - attr_reader :tablename, :session, :pos_buffer, :schema + attr_reader :tablename, :pos_buffer, :schema attr_accessor :primary_key - def initialize(tablename, session, schema=nil) + # Initializes a btrieve table. + def initialize(tablename, schema=nil) @tablename = tablename - @session = session @pos_buffer = Btrieve.create_string_buffer(POS_BLOCK_SIZE) - @schema = schema ? schema : @session.btrieve_schema.load_schema(@tablename) + @schema = schema ? schema : session.btrieve_schema.get_table_schema(@tablename) end - def open(mode = NORMAL_MODE) - btr_op(@session, OPEN, @pos_buffer, NULL_BUFFER, @schema[:filename], mode) + # Opens this btrieve table in preparation for some BTR operation(s). + # If a block is passed to the call, the table is closed automatically. + # If a block is NOT passed to the call, the client code has to make sure to close the file. + def open(mode=NORMAL_MODE, &block) + file_unc="//#{session.host}/#{session.data_share}/#{@schema[:filename]}" + btr_op(OPEN, @pos_buffer, NULL_BUFFER, file_unc, mode) + if block_given? + yield + close() + end end - def mappings() - @session.mappings[@tablename] ||= {} - end - - def add_mapping(attr_name, mapping_name, transform) - mappings[mapping_name.to_sym]={:column=>attr_name.to_sym,:transform=>transform.to_sym} - end - - def relations() - @session.relations[@tablename] ||= {} - end - - def add_relation(name, target_table, index, join) - relations[name.to_sym]={:target=>target_table.to_sym,:index=>index,:join=>join} - end - - def find(match, index_number) - unique=schema[:index_flags].map{|f|f&1==1}[index_number] - result=nil - if(unique) - result=find_in_unique_index(match, index_numer) + # Finds an array of records in this table, using the specified index number and match hash. + # If a block is passed in, the entries of this set are passed into this block one by one. Otherwise, + # the set is returned to the caller. + def find(match, index_number, &block) + allows_duplicates=schema[:index_flags].map{|f|f&1==1;}[index_number] + set=nil + if(allows_duplicates) + set=find_in_index(match, index_number) else - result=find_in_index(match, index_numer) + rec=find_in_unique_index(match, index_number) + set=rec.nil? ? [] : [rec] end - result - end - - def find_in_unique_index(match, index_number=nil) - finder(match, index_number) do |key_buffer, index| - btr_record = BtrieveRecord.new(self) - batch(){ btr_op(@session, GET_EQUAL, @pos_buffer, btr_record.data_buffer, key_buffer, index) } - btr_record + if(block_given?) + set.each{|record| yield record} + else + set end end - def find_in_index(match, index_number=nil) - finder(match, index_number) do |key_buffer, index| - absolute_match = match.keys.inject({}){|vals, key| vals[key] = match[key] if(match[key] != nil); vals} - absolute_match_keys = absolute_match.keys - result = [] - batch() do - btr_record = BtrieveRecord.new(self) - ops_result = btr_op(@session, GET_GREATER_THAN_OR_EQUAL, @pos_buffer, btr_record.data_buffer, key_buffer, index, [OK, EOF]) - if OK==ops_result - result << btr_record - while(true) - btr_record = BtrieveRecord.new(self) - op_result = btr_op(@session, GET_NEXT, @pos_buffer, btr_record.data_buffer, key_buffer, index_number, [OK, EOF, 21]) - break unless btr_record.get_attributes(absolute_match_keys)==absolute_match and op_result!=EOF and op_result!=21 - result << btr_record - end - end - end - result - end - end - + # Finds an array of records in this table, using the specified index number, a key and a range of values. + # If a block is passed in, the entries of this set are passed into this block one by one. Otherwise, + # the set is returned to the caller. def find_in_range(index_num, key, range, &block) index = @schema[:indices][index_num] columns = @schema[:columns] index_values = index.inject([]){|vals,akey|vals << (akey == key ? range.first : nil); vals} key_packer = index.inject("") do |packer, akey| @@ -82,62 +56,61 @@ nil_packer = 'x'*columns[key][:size] packer << (akey == key ? value_packer : nil_packer) packer end key_buffer = index_values.pack(key_packer) - btr_record = BtrieveRecord.new(self) + records = [] batch do - btr_op(@session, GET_GREATER_THAN_OR_EQUAL, @pos_buffer, btr_record.data_buffer, key_buffer, index_num, [OK, EOF]) - while(range.include?(btr_record[key])) - yield btr_record + btr_op(GET_GREATER_THAN_OR_EQUAL, @pos_buffer, btr_record.data_buffer, key_buffer, index_num, [OK, EOF]) + while(range.include?(btr_record[key])) + if(!block.nil?) + yield btr_record + else + records << btr_record + end btr_record = BtrieveRecord.new(self) - btr_op(@session, GET_NEXT, @pos_buffer, btr_record.data_buffer, key_buffer, index_num, [OK, EOF]) + btr_op(GET_NEXT, @pos_buffer, btr_record.data_buffer, key_buffer, index_num, [OK, EOF]) end end + if(block.nil?) + records + end end + # Closes this btrieve table to conclude a sequence of BTR operation(s). def close - btr_op(@session, CLOSE, @pos_buffer, NULL_BUFFER, NULL_BUFFER, NULL_KEY) + btr_op(CLOSE, @pos_buffer, NULL_BUFFER, NULL_BUFFER, NULL_KEY) end - def each_record(mode = READ_ONLY_MODE, &block) + # Loops over the records of this BTR table and passes each record to the block passed in. + def each_record(mode=NORMAL_MODE, &block) batch(mode) do while(record = step_next) do yield record end end end - def batch(mode = READ_ONLY_MODE, &block) + # Wraps a sequence of BTR operations between a pair of open and close statements for this table. + # DEPRECATED - Use the "open" method instead and pass a block to it. + def batch(mode=NORMAL_MODE, &block) open(mode) yield close end + # Returns this table's version as a MD5 Digest value. def version() Digest::MD5.hexdigest(BtrieveSchema.sort(schema[:columns]).inject([]){|array,column|array << BtrieveSchema.readable_type(column);array}.to_s) end - - #def self.primary_key_match(pk) - # vals=pk.split('.') - # {PK_KEYS[0]=>vals[0].to_i,PK_KEYS[1]=>vals[1].to_i,PK_KEYS[2]=>vals[2],PK_KEYS[3]=>vals[3].to_i,PK_KEYS[4]=>vals[4].to_i} - #end - - #def self.tablename_to_url(tablename) - # tablename.gsub(' & ','.and.').gsub('a/c ', 'ac.').gsub('es - o','es.o').gsub(' ','.') - #end - - #def self.url_to_tablename(url_tablename) - # url_tablename.gsub('.and.', ' & ').gsub('ac.', 'a/c ').gsub('es.o','es - o').gsub('.',' ') - #end private def step_next btr_record = BtrieveRecord.new(self) - result = btr_op(@session, STEP_NEXT, @pos_buffer, btr_record.data_buffer, NULL_BUFFER, NULL_KEY, [OK, EOF]) + result = btr_op(STEP_NEXT, @pos_buffer, btr_record.data_buffer, NULL_BUFFER, NULL_KEY, [OK, EOF]) return false if(result == EOF) btr_record end def find_matching_index(match) @@ -156,10 +129,11 @@ key_components = index.inject([]) do |vals, key| datatype = BtrieveSchema.lookup(columns[key][:datatype], columns[key][:size])[:name] value = match[key] value = value.to_f if FLOAT_TYPES.include?(datatype) value = value.to_i if INTEGER_TYPES.include?(datatype) + value = value.ljust(columns[key][:size], ' ') if datatype=='CHAR' vals << value vals end key_packer = index.inject("") do |packer, key| datatype = BtrieveSchema.lookup(columns[key][:datatype], columns[key][:size]) @@ -171,7 +145,43 @@ packer end key_buffer = key_components.pack(key_packer) yield(key_buffer, index_number) end + + def find_in_unique_index(match, index_number=nil) + finder(match, index_number) do |key_buffer, index| + btr_record = BtrieveRecord.new(self) + batch(){ btr_op(GET_EQUAL, @pos_buffer, btr_record.data_buffer, key_buffer, index, [KEY_NOT_FOUND, OK, EOF]) } + btr_record.nil? ? nil : btr_record + end + end + + def find_in_index(match, index_number=nil) + finder(match, index_number) do |org_key_buffer, index| + absolute_match = match.keys.inject({}){|vals, key| vals[key] = match[key] if(match[key] != nil); vals} + absolute_match_keys = absolute_match.keys + result = [] + batch do + key_buffer="#{org_key_buffer}" + ops_result = btr_op(GET_EQUAL_KEY, @pos_buffer, NULL_BUFFER, key_buffer, index, [OK, EOF, KEY_NOT_FOUND]) + ops=GET_EQUAL + while(ops_result == OK) + btr_record = BtrieveRecord.new(self) + ops_result = btr_op(ops, @pos_buffer, btr_record.data_buffer, key_buffer, index, [OK, EOF]) + break if(key_buffer!=org_key_buffer) + ops=GET_NEXT + result << btr_record if(ops_result == OK) + end + end + result + end + end + + def session + s=BtrieveSession.get_session + raise "Cannot manipulate a BtrieveTable when no BtrieveSession exists." if s.nil? + s + end + end