resultset.rb 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. require 'sqlite3/constants'
  2. require 'sqlite3/errors'
  3. module SQLite3
  4. # The ResultSet object encapsulates the enumerability of a query's output.
  5. # It is a simple cursor over the data that the query returns. It will
  6. # very rarely (if ever) be instantiated directly. Instead, client's should
  7. # obtain a ResultSet instance via Statement#execute.
  8. class ResultSet
  9. include Enumerable
  10. class ArrayWithTypes < Array # :nodoc:
  11. attr_accessor :types
  12. end
  13. class ArrayWithTypesAndFields < Array # :nodoc:
  14. attr_writer :types
  15. attr_writer :fields
  16. def types
  17. warn(<<-eowarn) if $VERBOSE
  18. #{caller[0]} is calling #{self.class}#types. This method will be removed in
  19. sqlite3 version 2.0.0, please call the `types` method on the SQLite3::ResultSet
  20. object that created this object
  21. eowarn
  22. @types
  23. end
  24. def fields
  25. warn(<<-eowarn) if $VERBOSE
  26. #{caller[0]} is calling #{self.class}#fields. This method will be removed in
  27. sqlite3 version 2.0.0, please call the `columns` method on the SQLite3::ResultSet
  28. object that created this object
  29. eowarn
  30. @fields
  31. end
  32. end
  33. # The class of which we return an object in case we want a Hash as
  34. # result.
  35. class HashWithTypesAndFields < Hash # :nodoc:
  36. attr_writer :types
  37. attr_writer :fields
  38. def types
  39. warn(<<-eowarn) if $VERBOSE
  40. #{caller[0]} is calling #{self.class}#types. This method will be removed in
  41. sqlite3 version 2.0.0, please call the `types` method on the SQLite3::ResultSet
  42. object that created this object
  43. eowarn
  44. @types
  45. end
  46. def fields
  47. warn(<<-eowarn) if $VERBOSE
  48. #{caller[0]} is calling #{self.class}#fields. This method will be removed in
  49. sqlite3 version 2.0.0, please call the `columns` method on the SQLite3::ResultSet
  50. object that created this object
  51. eowarn
  52. @fields
  53. end
  54. def [] key
  55. key = fields[key] if key.is_a? Numeric
  56. super key
  57. end
  58. end
  59. # Create a new ResultSet attached to the given database, using the
  60. # given sql text.
  61. def initialize db, stmt
  62. @db = db
  63. @stmt = stmt
  64. end
  65. # Reset the cursor, so that a result set which has reached end-of-file
  66. # can be rewound and reiterated.
  67. def reset( *bind_params )
  68. @stmt.reset!
  69. @stmt.bind_params( *bind_params )
  70. @eof = false
  71. end
  72. # Query whether the cursor has reached the end of the result set or not.
  73. def eof?
  74. @stmt.done?
  75. end
  76. # Obtain the next row from the cursor. If there are no more rows to be
  77. # had, this will return +nil+. If type translation is active on the
  78. # corresponding database, the values in the row will be translated
  79. # according to their types.
  80. #
  81. # The returned value will be an array, unless Database#results_as_hash has
  82. # been set to +true+, in which case the returned value will be a hash.
  83. #
  84. # For arrays, the column names are accessible via the +fields+ property,
  85. # and the column types are accessible via the +types+ property.
  86. #
  87. # For hashes, the column names are the keys of the hash, and the column
  88. # types are accessible via the +types+ property.
  89. def next
  90. if @db.results_as_hash
  91. return next_hash
  92. end
  93. row = @stmt.step
  94. return nil if @stmt.done?
  95. if @db.type_translation
  96. row = @stmt.types.zip(row).map do |type, value|
  97. @db.translator.translate( type, value )
  98. end
  99. end
  100. if row.respond_to?(:fields)
  101. # FIXME: this can only happen if the translator returns something
  102. # that responds to `fields`. Since we're removing the translator
  103. # in 2.0, we can remove this branch in 2.0.
  104. row = ArrayWithTypes.new(row)
  105. else
  106. # FIXME: the `fields` and `types` methods are deprecated on this
  107. # object for version 2.0, so we can safely remove this branch
  108. # as well.
  109. row = ArrayWithTypesAndFields.new(row)
  110. end
  111. row.fields = @stmt.columns
  112. row.types = @stmt.types
  113. row
  114. end
  115. # Required by the Enumerable mixin. Provides an internal iterator over the
  116. # rows of the result set.
  117. def each
  118. while node = self.next
  119. yield node
  120. end
  121. end
  122. # Provides an internal iterator over the rows of the result set where
  123. # each row is yielded as a hash.
  124. def each_hash
  125. while node = next_hash
  126. yield node
  127. end
  128. end
  129. # Closes the statement that spawned this result set.
  130. # <em>Use with caution!</em> Closing a result set will automatically
  131. # close any other result sets that were spawned from the same statement.
  132. def close
  133. @stmt.close
  134. end
  135. # Queries whether the underlying statement has been closed or not.
  136. def closed?
  137. @stmt.closed?
  138. end
  139. # Returns the types of the columns returned by this result set.
  140. def types
  141. @stmt.types
  142. end
  143. # Returns the names of the columns returned by this result set.
  144. def columns
  145. @stmt.columns
  146. end
  147. # Return the next row as a hash
  148. def next_hash
  149. row = @stmt.step
  150. return nil if @stmt.done?
  151. # FIXME: type translation is deprecated, so this can be removed
  152. # in 2.0
  153. if @db.type_translation
  154. row = @stmt.types.zip(row).map do |type, value|
  155. @db.translator.translate( type, value )
  156. end
  157. end
  158. # FIXME: this can be switched to a regular hash in 2.0
  159. row = HashWithTypesAndFields[*@stmt.columns.zip(row).flatten]
  160. # FIXME: these methods are deprecated for version 2.0, so we can remove
  161. # this code in 2.0
  162. row.fields = @stmt.columns
  163. row.types = @stmt.types
  164. row
  165. end
  166. end
  167. end