activerecord6-redshift-adapter 1.1.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5830bb316b6a3021508d6542b09478a0b0c3ce7a
4
- data.tar.gz: eb66565b485ab9464f93fe5f2e392a37a64d2805
2
+ SHA256:
3
+ metadata.gz: 95fe63dabddcdce38d3cf4e84d8a891a682ddcbd5884ec19bd0c1907693d1773
4
+ data.tar.gz: 00c59c4c3f97f14c08a9f140399e9a698d19b7ad5cd18d65e6291c23f7fb9486
5
5
  SHA512:
6
- metadata.gz: 69d0323991a44986cef4b87d9268eaeff017c46dfd83cd6785753353c18900cc0dedde17539ef4c99e476d10f4b61e262aea755aaea8acae9070763d61f8aa79
7
- data.tar.gz: 819f6b941172c242926a3fc6229fe4b4d12742c89115178cfa4ae4d929e5eb2eea0cee773b852126074b2d26993db6e2faef0b99ecf6aafec75500bd0e49e41a
6
+ metadata.gz: 07a977cc1444f6e4e303b1b1aa884c736d0eed3b276e04cb706f9b89cad0e24dde2f9a4519fb1647238b80199ae51662d67a499299e0f9a4a44dff36b4598109
7
+ data.tar.gz: 1fb3715ef714636c9ae987e759b4a2501c87af1e721f32aedf400b2c7436a0582dba50c5a671ea6595d2bb9ad1633f2c8d444cf8be7ec2537d1a6a75723b3457
@@ -3,8 +3,18 @@ module ActiveRecord
3
3
  class RedshiftColumn < Column #:nodoc:
4
4
  delegate :oid, :fmod, to: :sql_type_metadata
5
5
 
6
- def initialize(name, default, sql_type_metadata, null = true, default_function = nil)
7
- super name, default, sql_type_metadata, null, default_function
6
+ if ActiveRecord::VERSION::MAJOR >= 6 && ActiveRecord::VERSION::MINOR >= 1
7
+ # Required for Rails 6.1, see https://github.com/rails/rails/pull/41756
8
+ mattr_reader :array, default: false
9
+ alias :array? :array
10
+
11
+ def initialize(name, default, sql_type_metadata, null = true, default_function = nil, **)
12
+ super name, default, sql_type_metadata, null, default_function
13
+ end
14
+ else
15
+ def initialize(name, default, sql_type_metadata, null = true, default_function = nil)
16
+ super name, default, sql_type_metadata, null, default_function
17
+ end
8
18
  end
9
19
  end
10
20
  end
@@ -47,9 +47,12 @@ module ActiveRecord
47
47
  def select_value(arel, name = nil, binds = [])
48
48
  # In Rails 5.2, arel_from_relation replaced binds_from_relation,
49
49
  # so we see which method exists to get the variables
50
+ #
51
+ # In Rails 6.0 to_sql_and_binds began only returning sql, with
52
+ # to_sql_and_binds serving as a replacement
50
53
  if respond_to?(:arel_from_relation, true)
51
54
  arel = arel_from_relation(arel)
52
- sql, binds = to_sql(arel, binds)
55
+ sql, binds = to_sql_and_binds(arel, binds)
53
56
  else
54
57
  arel, binds = binds_from_relation arel, binds
55
58
  sql = to_sql(arel, binds)
@@ -62,9 +65,12 @@ module ActiveRecord
62
65
  def select_values(arel, name = nil)
63
66
  # In Rails 5.2, arel_from_relation replaced binds_from_relation,
64
67
  # so we see which method exists to get the variables
68
+ #
69
+ # In Rails 6.0 to_sql_and_binds began only returning sql, with
70
+ # to_sql_and_binds serving as a replacement
65
71
  if respond_to?(:arel_from_relation, true)
66
72
  arel = arel_from_relation(arel)
67
- sql, binds = to_sql(arel, [])
73
+ sql, binds = to_sql_and_binds(arel, [])
68
74
  else
69
75
  arel, binds = binds_from_relation arel, []
70
76
  sql = to_sql(arel, binds)
@@ -81,7 +87,14 @@ module ActiveRecord
81
87
 
82
88
  # Executes a SELECT query and returns an array of rows. Each row is an
83
89
  # array of field values.
84
- def select_rows(sql, name = nil, binds = [])
90
+ def select_rows(arel, name = nil, binds = [])
91
+ if respond_to?(:arel_from_relation, true)
92
+ arel = arel_from_relation(arel)
93
+ sql, binds = to_sql_and_binds(arel, [])
94
+ else
95
+ arel, binds = binds_from_relation arel, []
96
+ sql = to_sql(arel, binds)
97
+ end
85
98
  execute_and_clear(sql, name, binds) do |result|
86
99
  result.values
87
100
  end
@@ -3,7 +3,7 @@ module ActiveRecord
3
3
  module Redshift
4
4
  module OID # :nodoc:
5
5
  class Decimal < Type::Decimal # :nodoc:
6
- def infinity(options = {})
6
+ def infinity(**options)
7
7
  BigDecimal.new("Infinity") * (options[:negative] ? -1 : 1)
8
8
  end
9
9
  end
@@ -30,18 +30,18 @@ module ActiveRecord
30
30
  # require you to assure that you always provide a UUID value before saving
31
31
  # a record (as primary keys cannot be +nil+). This might be done via the
32
32
  # +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
33
- def primary_key(name, type = :primary_key, options = {})
33
+ def primary_key(name, type = :primary_key, **options)
34
34
  return super unless type == :uuid
35
35
  options[:default] = options.fetch(:default, 'uuid_generate_v4()')
36
36
  options[:primary_key] = true
37
37
  column name, type, options
38
38
  end
39
39
 
40
- def json(name, options = {})
40
+ def json(name, **options)
41
41
  column(name, :json, options)
42
42
  end
43
43
 
44
- def jsonb(name, options = {})
44
+ def jsonb(name, **options)
45
45
  column(name, :jsonb, options)
46
46
  end
47
47
  end
@@ -54,8 +54,8 @@ module ActiveRecord
54
54
 
55
55
  private
56
56
 
57
- def create_column_definition(name, type)
58
- Redshift::ColumnDefinition.new name, type
57
+ def create_column_definition(*args)
58
+ Redshift::ColumnDefinition.new(*args)
59
59
  end
60
60
  end
61
61
 
@@ -1,28 +1,49 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  module Redshift
4
- class SchemaCreation < AbstractAdapter::SchemaCreation
5
- private
6
4
 
7
- def visit_ColumnDefinition(o)
8
- o.sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale)
9
- super
5
+ if ActiveRecord::VERSION::MAJOR >= 6 && ActiveRecord::VERSION::MINOR >= 1
6
+ class SchemaCreation < SchemaCreation
7
+ private
8
+
9
+ def visit_ColumnDefinition(o)
10
+ o.sql_type = type_to_sql(o.type, limit: o.limit, precision: o.precision, scale: o.scale)
11
+ super
12
+ end
13
+
14
+ def add_column_options!(sql, options)
15
+ column = options.fetch(:column) { return super }
16
+ if column.type == :uuid && options[:default] =~ /\(\)/
17
+ sql << " DEFAULT #{options[:default]}"
18
+ else
19
+ super
20
+ end
21
+ end
10
22
  end
23
+ else
24
+ class SchemaCreation < AbstractAdapter::SchemaCreation
25
+ private
11
26
 
12
- def add_column_options!(sql, options)
13
- column = options.fetch(:column) { return super }
14
- if column.type == :uuid && options[:default] =~ /\(\)/
15
- sql << " DEFAULT #{options[:default]}"
16
- else
27
+ def visit_ColumnDefinition(o)
28
+ o.sql_type = type_to_sql(o.type, limit: o.limit, precision: o.precision, scale: o.scale)
17
29
  super
18
30
  end
31
+
32
+ def add_column_options!(sql, options)
33
+ column = options.fetch(:column) { return super }
34
+ if column.type == :uuid && options[:default] =~ /\(\)/
35
+ sql << " DEFAULT #{options[:default]}"
36
+ else
37
+ super
38
+ end
39
+ end
19
40
  end
20
41
  end
21
42
 
22
43
  module SchemaStatements
23
44
  # Drops the database specified on the +name+ attribute
24
45
  # and creates it again using the provided +options+.
25
- def recreate_database(name, options = {}) #:nodoc:
46
+ def recreate_database(name, **options) #:nodoc:
26
47
  drop_database(name)
27
48
  create_database(name, options)
28
49
  end
@@ -35,7 +56,7 @@ module ActiveRecord
35
56
  # Example:
36
57
  # create_database config[:database], config
37
58
  # create_database 'foo_development', encoding: 'unicode'
38
- def create_database(name, options = {})
59
+ def create_database(name, **options)
39
60
  options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
40
61
 
41
62
  option_string = options.inject("") do |memo, (key, value)|
@@ -130,7 +151,7 @@ module ActiveRecord
130
151
  SQL
131
152
  end
132
153
 
133
- def drop_table(table_name, options = {})
154
+ def drop_table(table_name, **options)
134
155
  execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
135
156
  end
136
157
 
@@ -200,7 +221,7 @@ module ActiveRecord
200
221
  end
201
222
 
202
223
  # Drops the schema for the given schema name.
203
- def drop_schema(schema_name, options = {})
224
+ def drop_schema(schema_name, **options)
204
225
  execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
205
226
  end
206
227
 
@@ -268,20 +289,20 @@ module ActiveRecord
268
289
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
269
290
  end
270
291
 
271
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
292
+ def add_column(table_name, column_name, type, **options) #:nodoc:
272
293
  clear_cache!
273
294
  super
274
295
  end
275
296
 
276
297
  # Changes the column of a table.
277
- def change_column(table_name, column_name, type, options = {})
298
+ def change_column(table_name, column_name, type, **options)
278
299
  clear_cache!
279
300
  quoted_table_name = quote_table_name(table_name)
280
- sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
301
+ sql_type = type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])
281
302
  sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}"
282
303
  sql << " USING #{options[:using]}" if options[:using]
283
304
  if options[:cast_as]
284
- sql << " USING CAST(#{quote_column_name(column_name)} AS #{type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale])})"
305
+ sql << " USING CAST(#{quote_column_name(column_name)} AS #{type_to_sql(options[:cast_as], limit: options[:limit], precision: options[:precision], scale: options[:scale])})"
285
306
  end
286
307
  execute sql
287
308
 
@@ -321,7 +342,7 @@ module ActiveRecord
321
342
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
322
343
  end
323
344
 
324
- def add_index(table_name, column_name, options = {}) #:nodoc:
345
+ def add_index(table_name, column_name, **options) #:nodoc:
325
346
  end
326
347
 
327
348
  def remove_index!(table_name, index_name) #:nodoc:
@@ -372,7 +393,7 @@ module ActiveRecord
372
393
  end
373
394
 
374
395
  # Maps logical Rails types to PostgreSQL-specific data types.
375
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
396
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **)
376
397
  case type.to_s
377
398
  when 'integer'
378
399
  return 'integer' unless limit
@@ -12,10 +12,14 @@ require 'active_record/connection_adapters/redshift/schema_statements'
12
12
  require 'active_record/connection_adapters/redshift/type_metadata'
13
13
  require 'active_record/connection_adapters/redshift/database_statements'
14
14
 
15
+ require 'active_record/tasks/database_tasks'
16
+
15
17
  require 'pg'
16
18
 
17
19
  require 'ipaddr'
18
20
 
21
+ ActiveRecord::Tasks::DatabaseTasks.register_task(/redshift/, "ActiveRecord::Tasks::PostgreSQLDatabaseTasks")
22
+
19
23
  module ActiveRecord
20
24
  module ConnectionHandling # :nodoc:
21
25
  RS_VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
@@ -78,10 +82,10 @@ module ActiveRecord
78
82
  string: { name: "varchar" },
79
83
  text: { name: "varchar" },
80
84
  integer: { name: "integer" },
81
- float: { name: "float" },
85
+ float: { name: "decimal" },
82
86
  decimal: { name: "decimal" },
83
87
  datetime: { name: "timestamp" },
84
- time: { name: "time" },
88
+ time: { name: "timestamp" },
85
89
  date: { name: "date" },
86
90
  bigint: { name: "bigint" },
87
91
  boolean: { name: "boolean" },
@@ -122,55 +126,29 @@ module ActiveRecord
122
126
  { concurrently: 'CONCURRENTLY' }
123
127
  end
124
128
 
125
- class StatementPool < ConnectionAdapters::StatementPool
129
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
126
130
  def initialize(connection, max)
127
131
  super(max)
128
132
  @connection = connection
129
133
  @counter = 0
130
- @cache = Hash.new { |h,pid| h[pid] = {} }
131
134
  end
132
135
 
133
- def each(&block); cache.each(&block); end
134
- def key?(key); cache.key?(key); end
135
- def [](key); cache[key]; end
136
- def length; cache.length; end
137
-
138
136
  def next_key
139
137
  "a#{@counter + 1}"
140
138
  end
141
139
 
142
140
  def []=(sql, key)
143
- while @max <= cache.size
144
- dealloc(cache.shift.last)
145
- end
146
- @counter += 1
147
- cache[sql] = key
148
- end
149
-
150
- def clear
151
- cache.each_value do |stmt_key|
152
- dealloc stmt_key
153
- end
154
- cache.clear
155
- end
156
-
157
- def delete(sql_key)
158
- dealloc cache[sql_key]
159
- cache.delete sql_key
141
+ super.tap { @counter += 1 }
160
142
  end
161
143
 
162
144
  private
163
-
164
- def cache
165
- @cache[Process.pid]
166
- end
167
-
168
145
  def dealloc(key)
169
146
  @connection.query "DEALLOCATE #{key}" if connection_active?
147
+ rescue PG::Error
170
148
  end
171
149
 
172
150
  def connection_active?
173
- @connection.status == PG::Connection::CONNECTION_OK
151
+ @connection.status == PG::CONNECTION_OK
174
152
  rescue PG::Error
175
153
  false
176
154
  end
@@ -181,7 +159,7 @@ module ActiveRecord
181
159
  super(connection, logger, config)
182
160
 
183
161
  @visitor = Arel::Visitors::PostgreSQL.new self
184
- @visitor.extend(ConnectionAdapters::DetermineIfPreparableVisitor)
162
+ @visitor.extend(ConnectionAdapters::DetermineIfPreparableVisitor) if defined?(ConnectionAdapters::DetermineIfPreparableVisitor)
185
163
  @prepared_statements = false
186
164
 
187
165
  @connection_parameters = connection_parameters
@@ -255,14 +233,6 @@ module ActiveRecord
255
233
  true
256
234
  end
257
235
 
258
- # Enable standard-conforming strings if available.
259
- def set_standard_conforming_strings
260
- old, self.client_min_messages = client_min_messages, 'panic'
261
- execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
262
- ensure
263
- self.client_min_messages = old
264
- end
265
-
266
236
  def supports_ddl_transactions?
267
237
  true
268
238
  end
@@ -342,7 +312,7 @@ module ActiveRecord
342
312
  @connection.server_version
343
313
  end
344
314
 
345
- def translate_exception(exception, message)
315
+ def translate_exception(exception, message:, sql:, binds:)
346
316
  return exception unless exception.respond_to?(:result)
347
317
 
348
318
  case exception.message
@@ -496,39 +466,68 @@ module ActiveRecord
496
466
  ret
497
467
  end
498
468
 
469
+
499
470
  def exec_no_cache(sql, name, binds)
500
- log(sql, name, binds) { @connection.async_exec(sql, []) }
471
+ materialize_transactions
472
+
473
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
474
+ # made since we established the connection
475
+ update_typemap_for_default_timezone
476
+
477
+ type_casted_binds = type_casted_binds(binds)
478
+ log(sql, name, binds, type_casted_binds) do
479
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
480
+ @connection.exec_params(sql, type_casted_binds)
481
+ end
482
+ end
501
483
  end
502
484
 
503
485
  def exec_cache(sql, name, binds)
504
- stmt_key = prepare_statement(sql)
505
- type_casted_binds = binds.map { |col, val|
506
- [col, type_cast(val, col)]
507
- }
486
+ materialize_transactions
487
+ update_typemap_for_default_timezone
508
488
 
509
- log(sql, name, type_casted_binds, stmt_key) do
510
- @connection.exec_prepared(stmt_key, type_casted_binds.map { |_, val| val })
489
+ stmt_key = prepare_statement(sql, binds)
490
+ type_casted_binds = type_casted_binds(binds)
491
+
492
+ log(sql, name, binds, type_casted_binds, stmt_key) do
493
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
494
+ @connection.exec_prepared(stmt_key, type_casted_binds)
495
+ end
511
496
  end
512
497
  rescue ActiveRecord::StatementInvalid => e
513
- pgerror = e.original_exception
514
-
515
- # Get the PG code for the failure. Annoyingly, the code for
516
- # prepared statements whose return value may have changed is
517
- # FEATURE_NOT_SUPPORTED. Check here for more details:
518
- # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
519
- begin
520
- code = pgerror.result.result_error_field(PG::Result::PG_DIAG_SQLSTATE)
521
- rescue
522
- raise e
523
- end
524
- if FEATURE_NOT_SUPPORTED == code
525
- @statements.delete sql_key(sql)
526
- retry
498
+ raise unless is_cached_plan_failure?(e)
499
+
500
+ # Nothing we can do if we are in a transaction because all commands
501
+ # will raise InFailedSQLTransaction
502
+ if in_transaction?
503
+ raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
527
504
  else
528
- raise e
505
+ @lock.synchronize do
506
+ # outside of transactions we can simply flush this query and retry
507
+ @statements.delete sql_key(sql)
508
+ end
509
+ retry
529
510
  end
530
511
  end
531
512
 
513
+ # Annoyingly, the code for prepared statements whose return value may
514
+ # have changed is FEATURE_NOT_SUPPORTED.
515
+ #
516
+ # This covers various different error types so we need to do additional
517
+ # work to classify the exception definitively as a
518
+ # ActiveRecord::PreparedStatementCacheExpired
519
+ #
520
+ # Check here for more details:
521
+ # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
522
+ CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
523
+ def is_cached_plan_failure?(e)
524
+ pgerror = e.cause
525
+ code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
526
+ code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
527
+ rescue
528
+ false
529
+ end
530
+
532
531
  # Returns the statement identifier for the client side cache
533
532
  # of statements
534
533
  def sql_key(sql)
@@ -537,34 +536,31 @@ module ActiveRecord
537
536
 
538
537
  # Prepare the statement if it hasn't been prepared, return
539
538
  # the statement key.
540
- def prepare_statement(sql)
541
- sql_key = sql_key(sql)
542
- unless @statements.key? sql_key
543
- nextkey = @statements.next_key
544
- begin
545
- @connection.prepare nextkey, sql
546
- rescue => e
547
- raise translate_exception_class(e, sql)
539
+ def prepare_statement(sql, binds)
540
+ @lock.synchronize do
541
+ sql_key = sql_key(sql)
542
+ unless @statements.key? sql_key
543
+ nextkey = @statements.next_key
544
+ begin
545
+ @connection.prepare nextkey, sql
546
+ rescue => e
547
+ raise translate_exception_class(e, sql, binds)
548
+ end
549
+ # Clear the queue
550
+ @connection.get_last_result
551
+ @statements[sql_key] = nextkey
548
552
  end
549
- # Clear the queue
550
- @connection.get_last_result
551
- @statements[sql_key] = nextkey
553
+ @statements[sql_key]
552
554
  end
553
- @statements[sql_key]
554
555
  end
555
556
 
556
557
  # Connects to a PostgreSQL server and sets up the adapter depending on the
557
558
  # connected server's characteristics.
558
559
  def connect
559
- @connection = PG::Connection.connect(@connection_parameters)
560
-
560
+ @connection = PG.connect(@connection_parameters)
561
561
  configure_connection
562
- rescue ::PG::Error => error
563
- if error.message.include?("does not exist")
564
- raise ActiveRecord::NoDatabaseError.new(error.message, error)
565
- else
566
- raise
567
- end
562
+ add_pg_encoders
563
+ add_pg_decoders
568
564
  end
569
565
 
570
566
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -575,17 +571,29 @@ module ActiveRecord
575
571
  end
576
572
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
577
573
 
574
+ variables = @config.fetch(:variables, {}).stringify_keys
575
+
576
+ # If using Active Record's time zone support configure the connection to return
577
+ # TIMESTAMP WITH ZONE types in UTC.
578
+ unless variables["timezone"]
579
+ if ActiveRecord::Base.default_timezone == :utc
580
+ variables["timezone"] = "UTC"
581
+ elsif @local_tz
582
+ variables["timezone"] = @local_tz
583
+ end
584
+ end
585
+
578
586
  # SET statements from :variables config hash
579
- # http://www.postgresql.org/docs/8.3/static/sql-set.html
580
- variables = @config[:variables] || {}
587
+ # https://www.postgresql.org/docs/current/static/sql-set.html
581
588
  variables.map do |k, v|
582
- if v == ':default' || v == :default
589
+ if v == ":default" || v == :default
583
590
  # Sets the value to the global or compile default
584
- execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
591
+ execute("SET #{k} TO DEFAULT", "SCHEMA")
585
592
  elsif !v.nil?
586
- execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
593
+ execute("SET #{k} TO #{quote(v)}", "SCHEMA")
587
594
  end
588
595
  end
596
+
589
597
  end
590
598
 
591
599
  def last_insert_id_result(sequence_name) #:nodoc:
@@ -622,13 +630,111 @@ module ActiveRecord
622
630
  end_sql
623
631
  end
624
632
 
625
- def extract_table_ref_from_insert_sql(sql) # :nodoc:
633
+ def extract_table_ref_from_insert_sql(sql)
626
634
  sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
627
635
  $1.strip if $1
628
636
  end
629
637
 
638
+ def arel_visitor
639
+ Arel::Visitors::PostgreSQL.new(self)
640
+ end
641
+
642
+ def build_statement_pool
643
+ StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
644
+ end
645
+
646
+
647
+ def can_perform_case_insensitive_comparison_for?(column)
648
+ @case_insensitive_cache ||= {}
649
+ @case_insensitive_cache[column.sql_type] ||= begin
650
+ sql = <<~SQL
651
+ SELECT exists(
652
+ SELECT * FROM pg_proc
653
+ WHERE proname = 'lower'
654
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
655
+ ) OR exists(
656
+ SELECT * FROM pg_proc
657
+ INNER JOIN pg_cast
658
+ ON ARRAY[casttarget]::oidvector = proargtypes
659
+ WHERE proname = 'lower'
660
+ AND castsource = #{quote column.sql_type}::regtype
661
+ )
662
+ SQL
663
+ execute_and_clear(sql, "SCHEMA", []) do |result|
664
+ result.getvalue(0, 0)
665
+ end
666
+ end
667
+ end
668
+
669
+ def add_pg_encoders
670
+ map = PG::TypeMapByClass.new
671
+ map[Integer] = PG::TextEncoder::Integer.new
672
+ map[TrueClass] = PG::TextEncoder::Boolean.new
673
+ map[FalseClass] = PG::TextEncoder::Boolean.new
674
+ @connection.type_map_for_queries = map
675
+ end
676
+
677
+ def update_typemap_for_default_timezone
678
+ if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
679
+ decoder_class = ActiveRecord::Base.default_timezone == :utc ?
680
+ PG::TextDecoder::TimestampUtc :
681
+ PG::TextDecoder::TimestampWithoutTimeZone
682
+
683
+ @timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
684
+ @connection.type_map_for_results.add_coder(@timestamp_decoder)
685
+ @default_timezone = ActiveRecord::Base.default_timezone
686
+ end
687
+ end
688
+
689
+
690
+ def add_pg_decoders
691
+ @default_timezone = nil
692
+ @timestamp_decoder = nil
693
+
694
+ coders_by_name = {
695
+ "int2" => PG::TextDecoder::Integer,
696
+ "int4" => PG::TextDecoder::Integer,
697
+ "int8" => PG::TextDecoder::Integer,
698
+ "oid" => PG::TextDecoder::Integer,
699
+ "float4" => PG::TextDecoder::Float,
700
+ "float8" => PG::TextDecoder::Float,
701
+ "bool" => PG::TextDecoder::Boolean,
702
+ }
703
+
704
+ if defined?(PG::TextDecoder::TimestampUtc)
705
+ # Use native PG encoders available since pg-1.1
706
+ coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
707
+ coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
708
+ end
709
+
710
+ known_coder_types = coders_by_name.keys.map { |n| quote(n) }
711
+ query = <<~SQL % known_coder_types.join(", ")
712
+ SELECT t.oid, t.typname
713
+ FROM pg_type as t
714
+ WHERE t.typname IN (%s)
715
+ SQL
716
+ coders = execute_and_clear(query, "SCHEMA", []) do |result|
717
+ result
718
+ .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
719
+ .compact
720
+ end
721
+
722
+ map = PG::TypeMapByOid.new
723
+ coders.each { |coder| map.add_coder(coder) }
724
+ @connection.type_map_for_results = map
725
+
726
+ # extract timestamp decoder for use in update_typemap_for_default_timezone
727
+ @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
728
+ update_typemap_for_default_timezone
729
+ end
730
+
731
+ def construct_coder(row, coder_class)
732
+ return unless coder_class
733
+ coder_class.new(oid: row["oid"].to_i, name: row["typname"])
734
+ end
735
+
630
736
  def create_table_definition(*args) # :nodoc:
631
- Redshift::TableDefinition.new(*args)
737
+ Redshift::TableDefinition.new(self, *args)
632
738
  end
633
739
  end
634
740
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord6-redshift-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nancy Foen
@@ -11,22 +11,22 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2019-09-05 00:00:00.000000000 Z
14
+ date: 2022-02-04 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: pg
18
18
  requirement: !ruby/object:Gem::Requirement
19
19
  requirements:
20
- - - ">="
20
+ - - "~>"
21
21
  - !ruby/object:Gem::Version
22
- version: '0.18'
22
+ version: '1.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - ">="
27
+ - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: '0.18'
29
+ version: '1.0'
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: activerecord
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -34,9 +34,6 @@ dependencies:
34
34
  - - "~>"
35
35
  - !ruby/object:Gem::Version
36
36
  version: '6.0'
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- version: 6.0.0
40
37
  type: :runtime
41
38
  prerelease: false
42
39
  version_requirements: !ruby/object:Gem::Requirement
@@ -44,11 +41,8 @@ dependencies:
44
41
  - - "~>"
45
42
  - !ruby/object:Gem::Version
46
43
  version: '6.0'
47
- - - ">="
48
- - !ruby/object:Gem::Version
49
- version: 6.0.0
50
- description: Amazon Redshift _makeshift_ adapter for ActiveRecord 6.
51
- email: fantast.d@gmail.com
44
+ description: Amazon Redshift adapter for ActiveRecord 6.x.
45
+ email: contact@quent.in
52
46
  executables: []
53
47
  extensions: []
54
48
  extra_rdoc_files: []
@@ -84,15 +78,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
84
78
  requirements:
85
79
  - - ">="
86
80
  - !ruby/object:Gem::Version
87
- version: 2.2.2
81
+ version: '3.0'
88
82
  required_rubygems_version: !ruby/object:Gem::Requirement
89
83
  requirements:
90
84
  - - ">="
91
85
  - !ruby/object:Gem::Version
92
86
  version: '0'
93
87
  requirements: []
94
- rubyforge_project:
95
- rubygems_version: 2.5.2.3
88
+ rubygems_version: 3.0.3.1
96
89
  signing_key:
97
90
  specification_version: 4
98
91
  summary: Amazon Redshift adapter for ActiveRecord