Class: RDBI::Result

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/rdbi/result.rb

Overview

RDBI::Result encapsulates results from a statement.

Results in RDBI::Result are row-oriented and may be transformable by Result Drivers (RDBI::Result::Driver). They are fetched as a unit or in order.

The RDBI::Result API is deliberately architected to loosely resemble that of IO or File.

Just give me the data!

Have a peek at RDBI::Result#fetch.

Result Counts

Multiple kinds of counts are represented in each result:

  • A count of the results provided

  • A count of the affected rows.

To elaborate, the “affected rows” is a count of rows that were altered by the statement from a DML result such as INSERT or UPDATE. In some cases, statements will both alter rows and yield results, which is why this value is not switched depending on the kind of statement.

Result Drivers

Result drivers are subclasses of RDBI::Result::Driver that take the result as input and yield a transformed input: data structures such a hashes, or even wilder results such as CSV or JSON or YAML. Given the ability to sanely transform row-oriented input, result drivers effectively have the power to do anything.

Accessing result drivers is as easy as using a secondary form of RDBI::Result#fetch or more explicitly with the RDBI::Result#as call.

Defined Under Namespace

Classes: Driver

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(sth, binds, data, schema, type_hash) ⇒ Result

Creates a new RDBI::Result. Please refer to RDBI::Statement#new_execution for instructions on how this is typically used and how the contents are passed to the constructor.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/rdbi/result.rb', line 78

def initialize(sth, binds, data, schema, type_hash)
  @schema         = schema
  @data           = data
  @affected_count = nil  # computed on demand
  @sth            = sth
  @binds          = binds
  @type_hash      = type_hash
  @driver         = RDBI::Result::Driver::Array
  @result_driver  = nil

  configure_rewindable
  configure_driver(@driver)

  RDBI::Util.upon_finalize!(self, @data, :finish)
end

Instance Attribute Details

#bindsObject (readonly)

The binds used in the statement that yielded this Result.



53
54
55
# File 'lib/rdbi/result.rb', line 53

def binds
  @binds
end

#driverObject (readonly)

The RDBI::Result::Driver currently associated with this Result.



47
48
49
# File 'lib/rdbi/result.rb', line 47

def driver
  @driver
end

#rewindable_resultObject (readonly)

See RDBI::Statement#rewindable_result.



56
57
58
# File 'lib/rdbi/result.rb', line 56

def rewindable_result
  @rewindable_result
end

#schemaObject (readonly)

The RDBI::Schema structure associated with this result.



41
42
43
# File 'lib/rdbi/result.rb', line 41

def schema
  @schema
end

#sthObject (readonly)

The RDBI::Statement that yielded this result.



44
45
46
# File 'lib/rdbi/result.rb', line 44

def sth
  @sth
end

#type_hashObject (readonly)

The mapping of types for each positional argument in the Result.



50
51
52
# File 'lib/rdbi/result.rb', line 50

def type_hash
  @type_hash
end

Instance Method Details

#affected_countObject

The count of affected rows by a DML statement (see RDBI::Result main documentation)



102
103
104
# File 'lib/rdbi/result.rb', line 102

def affected_count
  @affected_count ||= @data.affected_count
end

#as(driver_klass, *args) ⇒ Object

:call-seq:

as(String)
as(Symbol)
as(Class)
as([Class, String, or Symbol], *driver_arguments)

Replace the Result Driver. See RDBI::Result’s main docs and RDBI::Result::Driver for more information on Result Drivers. Returns its receiver, permitting method chaining.

You may pass:

  • A Symbol or String which is shorthand for loading from the RDBI::Result::Driver namespace – for example: “CSV” will result in the class RDBI::Result::Driver::CSV.

  • A full class name.

There are no naming requirements; the String/Symbol form is just shorthand for convention.

Any additional arguments will be passed to the driver’s constructor.

This will force a rewind even if rewindable_result is false.



181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/rdbi/result.rb', line 181

def as(driver_klass, *args)

  driver_klass  = RDBI::Util.class_from_class_or_symbol(driver_klass, RDBI::Result::Driver)

  rr = @data.rewindable_result
  @data.rewindable_result = true
  @data.rewind
  @data.rewindable_result = rr
  @driver       = driver_klass
  configure_driver(@driver, *args)
  self
end

#coerce_to_arrayObject

Coerce the underlying result to an array, fetching all values. Same as setting RDBI::Result#rewindable_result.



152
153
154
# File 'lib/rdbi/result.rb', line 152

def coerce_to_array
  @data.coerce_to_array
end

#completeObject Also known as: complete?

:nodoc: FIXME async



60
61
62
# File 'lib/rdbi/result.rb', line 60

def complete
  true
end

#eachObject

Iterator for Enumerable methods. Yields a row at a time as translated by the current driver.



128
129
130
131
132
# File 'lib/rdbi/result.rb', line 128

def each
  while row = fetch(:next_row)
    yield(row)
  end
end

#empty?Boolean

Is this result empty?

Returns:

  • (Boolean)


144
145
146
# File 'lib/rdbi/result.rb', line 144

def empty?
  @data.empty?
end

#fetch(row_count = 1, driver_klass = nil, *args) ⇒ Object Also known as: read

:call-seq:

fetch()
fetch(Integer)
fetch(:first)
fetch(:last)
fetch(:all)
fetch(:rest)
fetch(amount, [Class, String, or Symbol], *driver_arguments)

fetch is the way people will typically interact with this class. It yields some or all of the results depending on the arguments given. Additionally, it can be supplemented with the arguments passed to RDBI::Result#as to one-off select a result driver.

The initial argument can be none or one of many options:

  • An Integer n requests n rows from the result and increments the index.

  • No argument uses an Integer count of 1.

  • :first yields the first row of the result, regardless of the index.

  • :last yields the last row of the result, regardless of the index.

  • :all yields the whole set of rows, regardless of the index.

  • :rest yields all the items that have not been fetched, determined by the index.

  • :first and :last return nil if there are no results. All others will return an empty array.

The index

I bet you’re wondering what that is now, right? Well, the index is essentially a running row count that is altered by certain fetch operations. This makes sequential fetches much simpler.

The index is largely implemented by RDBI::Cursor (and Database Driver subclasses)

Items that do not use the index do not affect it.

Result Drivers will always rewind the index, as this implicates a “point of no return” state change. You may always return to the original driver you were using, but the index position will be lost.

The default result driver is RDBI::Result::Driver::Array.



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/rdbi/result.rb', line 237

def fetch(row_count=1, driver_klass=nil, *args)
  if driver_klass
    as(driver_klass, *args)
  end

  # fetch() has two significantly different return signatures:
  #
  # Returning a single row is nil upon EOF (:first, :last, inside #each)
  # Returning a set of rows is [] upon EOF (all others)

  raw, multiple_rows = case row_count
                       when :first, :last, :next_row
                         [@data.__send__(row_count), false]
                       when :all, :rest
                         [@data.__send__(row_count), true]
                       else
                         [@data.fetch(row_count), true]
                       end

  return @result_driver.format_multiple_rows(raw) if multiple_rows

  @result_driver.format_single_row(raw) if raw

  # else nil -- :first, :last or #each at EOF
end

#finishObject

This call finishes the result, scheduling any unpreserved data for garbage collection.



286
287
288
289
290
291
292
293
# File 'lib/rdbi/result.rb', line 286

def finish
  @data.finish
  @data   = nil
  @sth    = nil
  @driver = nil
  @binds  = nil
  @schema = nil
end

#firstObject

Returns the first result in the set. Note that this may force an advance of the underlying cursor for non-rewindable ResultSets.



267
268
269
# File 'lib/rdbi/result.rb', line 267

def first
  fetch(:first)
end

#has_dataObject Also known as: has_data?

Does this result have data?



67
68
69
# File 'lib/rdbi/result.rb', line 67

def has_data
  @data.size > 0
end

#lastObject

Returns the last result in the set. Note that this may exhaust the underlying cursor for non-rewindable ResultSets, as the driver advances to the end of the results to fetch the last row.



276
277
278
# File 'lib/rdbi/result.rb', line 276

def last
  fetch(:last)
end

#reloadObject

Reload the result. This will:

  • Execute the statement that yielded this result again, with the original binds

  • Replace the results and other attributes with the new results.



113
114
115
116
117
118
119
120
121
122
# File 'lib/rdbi/result.rb', line 113

def reload
  @data.finish
  res = @sth.execute(*@binds)
  @data           = res.instance_variable_get(:@data)
  @type_hash      = res.instance_variable_get(:@type_hash)
  @schema         = res.instance_variable_get(:@schema)
  @affected_count = nil # recomputed on demand

  configure_rewindable
end

#result_countObject

The count of results (see RDBI::Result main documentation)



95
96
97
98
99
# File 'lib/rdbi/result.rb', line 95

def result_count
  # Non-rewindable cursors typically will give the number of rows
  # fetched so far...
  @data.size
end

#rewindObject

Reset the index.



137
138
139
# File 'lib/rdbi/result.rb', line 137

def rewind
  @data.rewind
end