diff --git a/activerecord6-redshift-adapter.gemspec b/activerecord6-redshift-adapter.gemspec index f242929..6dcc22f 100644 --- a/activerecord6-redshift-adapter.gemspec +++ b/activerecord6-redshift-adapter.gemspec @@ -1,7 +1,7 @@ Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY s.name = 'activerecord6-redshift-adapter' - s.version = '1.1.4' + s.version = '1.2.0' s.summary = 'Amazon Redshift adapter for ActiveRecord ' s.description = 'Amazon Redshift _makeshift_ adapter for ActiveRecord 6.' s.license = 'MIT' diff --git a/lib/active_record/connection_adapters/redshift/column.rb b/lib/active_record/connection_adapters/redshift/column.rb index 6ec2b09..211009b 100644 --- a/lib/active_record/connection_adapters/redshift/column.rb +++ b/lib/active_record/connection_adapters/redshift/column.rb @@ -1,39 +1,47 @@ module ActiveRecord module ConnectionAdapters - class RedshiftColumn < Column #:nodoc: - delegate :oid, :fmod, to: :sql_type_metadata + module Redshift + class Column < ConnectionAdapters::Column #:nodoc: + delegate :oid, :fmod, to: :sql_type_metadata - def initialize(name, default, sql_type_metadata, null = true, table_name = nil, default_function = nil, encoding = nil, auto_increment = nil) - super name, default, sql_type_metadata, null, default_function - @null = null - @default_function = default_function - @encoding = encoding - @auto_increment = auto_increment - end + def initialize(name, default, sql_type_metadata, null = true, table_name = nil, default_function = nil, encoding = nil, auto_increment = nil, **) + super name, default, sql_type_metadata, null, default_function + @null = null + @default_function = default_function + @encoding = encoding + @auto_increment = auto_increment + end - def init_with(coder) - super coder - @encoding = coder["encoding"] - @auto_increment = coder["auto_increment"] - end + def init_with(coder) + super coder + @encoding = coder["encoding"] + @auto_increment = coder["auto_increment"] + end - def encode_with(coder) - super coder - coder["encoding"] = @encoding - coder["auto_increment"] = @auto_increment - end + def encode_with(coder) + super coder + coder["encoding"] = @encoding + coder["auto_increment"] = @auto_increment + end - def encoding - @encoding - end + def encoding + @encoding + end - def null - @null - end + def null + @null + end + + def auto_increment + @auto_increment + end - def auto_increment - @auto_increment + def array + sql_type_metadata.sql_type.end_with?("[]") + end + alias :array? :array end end + RedshiftColumn = Redshift::Column end end diff --git a/lib/active_record/connection_adapters/redshift/database_statements.rb b/lib/active_record/connection_adapters/redshift/database_statements.rb index b66cc54..836a642 100644 --- a/lib/active_record/connection_adapters/redshift/database_statements.rb +++ b/lib/active_record/connection_adapters/redshift/database_statements.rb @@ -44,53 +44,33 @@ def pp(result) end end - def select_value(arel, name = nil, binds = []) - # In Rails 5.2, arel_from_relation replaced binds_from_relation, - # so we see which method exists to get the variables - # - # In Rails 6.0 to_sql_and_binds began only returning sql, with - # to_sql_and_binds serving as a replacement - if respond_to?(:arel_from_relation, true) - arel = arel_from_relation(arel) - sql, binds = to_sql_and_binds(arel, binds) + # Returns an ActiveRecord::Result instance. + def select_all(arel, name = nil, binds = [], preparable: nil) + arel = arel_from_relation(arel) + sql, binds, preparable = to_sql_and_binds(arel, binds, preparable) + + if prepared_statements && preparable + select_prepared(sql, name, binds) else - arel, binds = binds_from_relation arel, binds - sql = to_sql(arel, binds) - end - execute_and_clear(sql, name, binds) do |result| - result.getvalue(0, 0) if result.ntuples > 0 && result.nfields > 0 + select(sql, name, binds) end + rescue ::RangeError + ActiveRecord::Result.new([], []) end - def select_values(arel, name = nil) - # In Rails 5.2, arel_from_relation replaced binds_from_relation, - # so we see which method exists to get the variables - # - # In Rails 6.0 to_sql_and_binds began only returning sql, with - # to_sql_and_binds serving as a replacement - if respond_to?(:arel_from_relation, true) - arel = arel_from_relation(arel) - sql, binds = to_sql_and_binds(arel, []) - else - arel, binds = binds_from_relation arel, [] - sql = to_sql(arel, binds) - end + # Returns a single value from a record + def select_value(arel, name = nil, binds = []) + single_value_from_rows(select_rows(arel, name, binds)) + end - execute_and_clear(sql, name, binds) do |result| - if result.nfields > 0 - result.column_values(0) - else - [] - end - end + # Returns an array of the values of the first column in a select: + # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3] + def select_values(arel, name = nil, binds = []) + select_rows(arel, name, binds).map(&:first) end - # Executes a SELECT query and returns an array of rows. Each row is an - # array of field values. - def select_rows(sql, name = nil, binds = []) - execute_and_clear(sql, name, binds) do |result| - result.values - end + def select_rows(arel, name = nil, binds = []) + select_all(arel, name, binds).rows end # The internal PostgreSQL identifier of the money data type. @@ -220,6 +200,11 @@ def commit_db_transaction def exec_rollback_db_transaction execute "ROLLBACK" end + + def single_value_from_rows(rows) + row = rows.first + row && row.first + end end end end diff --git a/lib/active_record/connection_adapters/redshift/schema_statements.rb b/lib/active_record/connection_adapters/redshift/schema_statements.rb index ac17868..62e5881 100644 --- a/lib/active_record/connection_adapters/redshift/schema_statements.rb +++ b/lib/active_record/connection_adapters/redshift/schema_statements.rb @@ -463,7 +463,7 @@ def fetch_type_metadata(column_name, sql_type, oid, fmod) precision: cast_type.precision, scale: cast_type.scale, ) - TypeMetadata.new(simple_type, oid: oid, fmod: fmod) + RedshiftTypeMetadata.new(simple_type, oid: oid, fmod: fmod) end end end diff --git a/lib/active_record/connection_adapters/redshift/type_metadata.rb b/lib/active_record/connection_adapters/redshift/type_metadata.rb index b08a71d..4ff3da0 100644 --- a/lib/active_record/connection_adapters/redshift/type_metadata.rb +++ b/lib/active_record/connection_adapters/redshift/type_metadata.rb @@ -2,6 +2,10 @@ module ActiveRecord module ConnectionAdapters module Redshift class TypeMetadata < DelegateClass(SqlTypeMetadata) + undef to_yaml if method_defined?(:to_yaml) + + include Deduplicable + attr_reader :oid, :fmod, :array def initialize(type_metadata, oid: nil, fmod: nil) @@ -33,5 +37,6 @@ def attributes_for_hash end end end + RedshiftTypeMetadata = Redshift::TypeMetadata end end diff --git a/lib/active_record/connection_adapters/redshift_adapter.rb b/lib/active_record/connection_adapters/redshift_adapter.rb index 120d659..fab74fd 100644 --- a/lib/active_record/connection_adapters/redshift_adapter.rb +++ b/lib/active_record/connection_adapters/redshift_adapter.rb @@ -165,9 +165,9 @@ def initialize(connection, logger, connection_parameters, config) super(connection, logger, config) @visitor = Arel::Visitors::PostgreSQL.new self - @visitor.extend(ConnectionAdapters::DetermineIfPreparableVisitor) - @prepared_statements = false - + @prepared_statements = self.class.type_cast_config_to_boolean( + config.fetch(:prepared_statements, true) + ) @connection_parameters = connection_parameters # @local_tz is initialized as nil to avoid warnings when connect tries to use it