diff --git a/active_record/connection_adapters/postgresql/get_type_map.rb b/active_record/connection_adapters/postgresql/get_type_map.rb new file mode 100644 index 0000000..e313e44 --- /dev/null +++ b/active_record/connection_adapters/postgresql/get_type_map.rb @@ -0,0 +1,11 @@ +module PostgreSQLCursor + module ActiveRecord + module ConnectionAdapters + module PostgreSQL + def get_type_map + type_map + end + end + end + end +end diff --git a/lib/postgresql_cursor.rb b/lib/postgresql_cursor.rb index 22c9011..e265b8c 100644 --- a/lib/postgresql_cursor.rb +++ b/lib/postgresql_cursor.rb @@ -2,13 +2,16 @@ require 'postgresql_cursor/cursor' require 'postgresql_cursor/active_record/relation/cursor_iterators' require 'postgresql_cursor/active_record/sql_cursor' +require 'postgresql_cursor/active_record/connection_adapters/postgresql_type_map' # ActiveRecord 4.x require 'active_record' require 'active_record/connection_adapters/postgresql_adapter' ActiveRecord::Base.include(PostgreSQLCursor::ActiveRecord::SqlCursor) ActiveRecord::Relation.include(PostgreSQLCursor::ActiveRecord::Relation::CursorIterators) +ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.include(PostgreSQLCursor::ActiveRecord::ConnectionAdapters::PostgreSQLTypeMap) +# Temp test ActiveRecord::Base.establish_connection( "postgres://#{ENV['USER']}:@localhost/#{ENV['USER']}" ) @@ -17,5 +20,9 @@ class List < ActiveRecord::Base self.table_name = 'list' end -List.order("list_id").each_row {|r| p r } -List.order("list_id").each_instance {|r| p r } +List.order("list_id").each_hash {|r| p r } +List.order("list_id").each_instance {|r| + r.upd_ts + $r = r + p r +} diff --git a/lib/postgresql_cursor/active_record/connection_adapters/postgresql_type_map.rb b/lib/postgresql_cursor/active_record/connection_adapters/postgresql_type_map.rb new file mode 100644 index 0000000..6a198ff --- /dev/null +++ b/lib/postgresql_cursor/active_record/connection_adapters/postgresql_type_map.rb @@ -0,0 +1,12 @@ +# lib/postgresql_cursor/active_record/connection_adapters/postgresql_type_map +module PostgreSQLCursor + module ActiveRecord + module ConnectionAdapters + module PostgreSQLTypeMap + def get_type_map + type_map + end + end + end + end +end diff --git a/lib/postgresql_cursor/active_record/relation/cursor_iterators.rb b/lib/postgresql_cursor/active_record/relation/cursor_iterators.rb index 6234d4d..89f4a28 100644 --- a/lib/postgresql_cursor/active_record/relation/cursor_iterators.rb +++ b/lib/postgresql_cursor/active_record/relation/cursor_iterators.rb @@ -18,6 +18,7 @@ def each_row(options={}, &block) options = {:connection => self.connection}.merge(options) PostgreSQLCursor::Cursor.new(to_sql, options).each(&block) end + alias :each_hash :each_row # Public: Like each_row, but returns an instantiated model object to the block # @@ -26,9 +27,11 @@ def each_row(options={}, &block) # Returns the number of rows yielded to the block def each_instance(options={}, &block) options = {:connection => self.connection}.merge(options) - PostgreSQLCursor::Cursor.new(to_sql, options).each do |row| - model = instantiate(row) - block.call model + options[:symbolize_keys] = false # Must be strings to initiate + pgresult = nil + PostgreSQLCursor::Cursor.new(to_sql, options).each do |row, column_types| + model = instantiate(row, column_types) + yield model end end end diff --git a/lib/postgresql_cursor/cursor.rb b/lib/postgresql_cursor/cursor.rb index 66de3f4..5f9d4da 100644 --- a/lib/postgresql_cursor/cursor.rb +++ b/lib/postgresql_cursor/cursor.rb @@ -51,18 +51,19 @@ def initialize(sql, options={}) # # Returns the count of rows processed def each(&block) - has_do_until = @options.has_key?(:until) - has_do_while = @options.has_key?(:while) - @count = 0 + has_do_until = @options.has_key?(:until) + has_do_while = @options.has_key?(:while) + @count = 0 + @column_types = nil @connection.transaction do begin open while (row = fetch) do break if row.size==0 @count += 1 - row = row.symbolize_keys - rc = yield row - # TODO: Handle exceptions raised within block + row = cast_types(row, column_types) if options[:symbolize_keys] + row = row.symbolize_keys if options[:cast] + rc = yield(row, column_types) break if has_do_until && rc == @options[:until] break if has_do_while && rc != @options[:while] end @@ -75,6 +76,28 @@ def each(&block) @count end + def cast_types(row) + row + end + + def column_types + return @column_types if @column_types + + types = {} + fields = @result.fields + fields.each_with_index do |fname, i| + ftype = @result.ftype i + fmod = @result.fmod i + types[fname] = @connection.get_type_map.fetch(ftype, fmod) { |oid, mod| + warn "unknown OID: #{fname}(#{oid}) (#{sql})" + OID::Identity.new + } + + end + + @column_types = types + end + # Public: Opens (actually, "declares") the cursor. Call this before fetching def open set_cursor_tuple_fraction