activerecord-redshift-adapter 0.9.12 → 8.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +5 -13
  2. data/LICENSE +25 -1
  3. data/README.md +28 -86
  4. data/lib/active_record/connection_adapters/redshift_7_0/array_parser.rb +92 -0
  5. data/lib/active_record/connection_adapters/redshift_7_0/column.rb +17 -0
  6. data/lib/active_record/connection_adapters/redshift_7_0/database_statements.rb +232 -0
  7. data/lib/active_record/connection_adapters/redshift_7_0/oid/date_time.rb +36 -0
  8. data/lib/active_record/connection_adapters/redshift_7_0/oid/decimal.rb +15 -0
  9. data/lib/active_record/connection_adapters/redshift_7_0/oid/json.rb +41 -0
  10. data/lib/active_record/connection_adapters/redshift_7_0/oid/jsonb.rb +25 -0
  11. data/lib/active_record/connection_adapters/redshift_7_0/oid/type_map_initializer.rb +62 -0
  12. data/lib/active_record/connection_adapters/redshift_7_0/oid.rb +17 -0
  13. data/lib/active_record/connection_adapters/redshift_7_0/quoting.rb +99 -0
  14. data/lib/active_record/connection_adapters/redshift_7_0/referential_integrity.rb +17 -0
  15. data/lib/active_record/connection_adapters/redshift_7_0/schema_definitions.rb +70 -0
  16. data/lib/active_record/connection_adapters/redshift_7_0/schema_dumper.rb +17 -0
  17. data/lib/active_record/connection_adapters/redshift_7_0/schema_statements.rb +421 -0
  18. data/lib/active_record/connection_adapters/redshift_7_0/type_metadata.rb +39 -0
  19. data/lib/active_record/connection_adapters/redshift_7_0/utils.rb +81 -0
  20. data/lib/active_record/connection_adapters/redshift_7_0_adapter.rb +768 -0
  21. data/lib/active_record/connection_adapters/redshift_7_1/array_parser.rb +92 -0
  22. data/lib/active_record/connection_adapters/redshift_7_1/column.rb +17 -0
  23. data/lib/active_record/connection_adapters/redshift_7_1/database_statements.rb +180 -0
  24. data/lib/active_record/connection_adapters/redshift_7_1/oid/date_time.rb +36 -0
  25. data/lib/active_record/connection_adapters/redshift_7_1/oid/decimal.rb +15 -0
  26. data/lib/active_record/connection_adapters/redshift_7_1/oid/json.rb +41 -0
  27. data/lib/active_record/connection_adapters/redshift_7_1/oid/jsonb.rb +25 -0
  28. data/lib/active_record/connection_adapters/redshift_7_1/oid/type_map_initializer.rb +62 -0
  29. data/lib/active_record/connection_adapters/redshift_7_1/oid.rb +17 -0
  30. data/lib/active_record/connection_adapters/redshift_7_1/quoting.rb +161 -0
  31. data/lib/active_record/connection_adapters/redshift_7_1/referential_integrity.rb +17 -0
  32. data/lib/active_record/connection_adapters/redshift_7_1/schema_definitions.rb +70 -0
  33. data/lib/active_record/connection_adapters/redshift_7_1/schema_dumper.rb +17 -0
  34. data/lib/active_record/connection_adapters/redshift_7_1/schema_statements.rb +422 -0
  35. data/lib/active_record/connection_adapters/redshift_7_1/type_metadata.rb +43 -0
  36. data/lib/active_record/connection_adapters/redshift_7_1/utils.rb +81 -0
  37. data/lib/active_record/connection_adapters/redshift_7_1_adapter.rb +847 -0
  38. data/lib/active_record/connection_adapters/redshift_7_2/array_parser.rb +92 -0
  39. data/lib/active_record/connection_adapters/redshift_7_2/column.rb +17 -0
  40. data/lib/active_record/connection_adapters/redshift_7_2/database_statements.rb +180 -0
  41. data/lib/active_record/connection_adapters/redshift_7_2/oid/date_time.rb +36 -0
  42. data/lib/active_record/connection_adapters/redshift_7_2/oid/decimal.rb +15 -0
  43. data/lib/active_record/connection_adapters/redshift_7_2/oid/json.rb +41 -0
  44. data/lib/active_record/connection_adapters/redshift_7_2/oid/jsonb.rb +25 -0
  45. data/lib/active_record/connection_adapters/redshift_7_2/oid/type_map_initializer.rb +62 -0
  46. data/lib/active_record/connection_adapters/redshift_7_2/oid.rb +17 -0
  47. data/lib/active_record/connection_adapters/redshift_7_2/quoting.rb +164 -0
  48. data/lib/active_record/connection_adapters/redshift_7_2/referential_integrity.rb +17 -0
  49. data/lib/active_record/connection_adapters/redshift_7_2/schema_definitions.rb +70 -0
  50. data/lib/active_record/connection_adapters/redshift_7_2/schema_dumper.rb +17 -0
  51. data/lib/active_record/connection_adapters/redshift_7_2/schema_statements.rb +422 -0
  52. data/lib/active_record/connection_adapters/redshift_7_2/type_metadata.rb +43 -0
  53. data/lib/active_record/connection_adapters/redshift_7_2/utils.rb +81 -0
  54. data/lib/active_record/connection_adapters/redshift_7_2_adapter.rb +847 -0
  55. data/lib/active_record/connection_adapters/redshift_8_0/array_parser.rb +92 -0
  56. data/lib/active_record/connection_adapters/redshift_8_0/column.rb +17 -0
  57. data/lib/active_record/connection_adapters/redshift_8_0/database_statements.rb +181 -0
  58. data/lib/active_record/connection_adapters/redshift_8_0/oid/date_time.rb +36 -0
  59. data/lib/active_record/connection_adapters/redshift_8_0/oid/decimal.rb +15 -0
  60. data/lib/active_record/connection_adapters/redshift_8_0/oid/json.rb +41 -0
  61. data/lib/active_record/connection_adapters/redshift_8_0/oid/jsonb.rb +25 -0
  62. data/lib/active_record/connection_adapters/redshift_8_0/oid/type_map_initializer.rb +62 -0
  63. data/lib/active_record/connection_adapters/redshift_8_0/oid.rb +17 -0
  64. data/lib/active_record/connection_adapters/redshift_8_0/quoting.rb +164 -0
  65. data/lib/active_record/connection_adapters/redshift_8_0/referential_integrity.rb +17 -0
  66. data/lib/active_record/connection_adapters/redshift_8_0/schema_definitions.rb +70 -0
  67. data/lib/active_record/connection_adapters/redshift_8_0/schema_dumper.rb +17 -0
  68. data/lib/active_record/connection_adapters/redshift_8_0/schema_statements.rb +422 -0
  69. data/lib/active_record/connection_adapters/redshift_8_0/type_metadata.rb +43 -0
  70. data/lib/active_record/connection_adapters/redshift_8_0/utils.rb +81 -0
  71. data/lib/active_record/connection_adapters/redshift_8_0_adapter.rb +846 -0
  72. data/lib/active_record/connection_adapters/redshift_adapter.rb +13 -1286
  73. data/lib/active_record/tasks/redshift_7_0_tasks.rb +148 -0
  74. data/lib/active_record/tasks/redshift_7_1_tasks.rb +151 -0
  75. data/lib/active_record/tasks/redshift_7_2_tasks.rb +151 -0
  76. data/lib/active_record/tasks/redshift_8_0_tasks.rb +151 -0
  77. data/lib/active_record/tasks/redshift_tasks.rb +13 -0
  78. data/lib/activerecord-redshift-adapter.rb +13 -0
  79. metadata +110 -84
  80. data/.gitignore +0 -26
  81. data/Gemfile +0 -14
  82. data/Rakefile +0 -26
  83. data/activerecord-redshift-adapter.gemspec +0 -24
  84. data/lib/activerecord_redshift/table_manager.rb +0 -230
  85. data/lib/activerecord_redshift_adapter/version.rb +0 -4
  86. data/lib/activerecord_redshift_adapter.rb +0 -4
  87. data/lib/monkeypatch_activerecord.rb +0 -195
  88. data/lib/monkeypatch_arel.rb +0 -96
  89. data/spec/active_record/base_spec.rb +0 -37
  90. data/spec/active_record/connection_adapters/redshift_adapter_spec.rb +0 -97
  91. data/spec/dummy/config/database.example.yml +0 -12
  92. data/spec/spec_helper.rb +0 -33
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- Y2JlOGRmZDQ2YjRmNTRkMTQwMWM0MjY3ODEyN2RjN2E4OTU2Zjk3ZA==
5
- data.tar.gz: !binary |-
6
- NDJjOGEyZGRmMTgzNWNiN2Q5MTAwOTM3MTFjM2NhMWEwMzdlOTQ0ZA==
2
+ SHA256:
3
+ metadata.gz: d981a65a30765fe36e5e58616007f33103f238596de10188107d5e7bbe677093
4
+ data.tar.gz: 78f40be0d77ab87f6e4b3b1f6857f84aae35d0b5184a6fe1b320b14c080bbce9
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- OTU2M2Y5MTg3ZjdlYTZmMjE2MmQzNDIzNmMxNTQyY2ZlZGMzYTFkZjhlNmM2
10
- ZWZjMmM1ZGZhYzBkYWQ3YmY1Yzk1MjY5NDg1MGE5ZjA0ZTk2MjEzZDNjYTg4
11
- OGFlNTJiZDc0M2IwZWQ0MDMwY2NmOTJjZjBlYTQ5YTZmMmRlZGE=
12
- data.tar.gz: !binary |-
13
- YWMzMWMyZmIwNGU2NWUyOTczZTU2NGU0YTYzNjA4NjU0YWQzZWQyMTM2ZmQ5
14
- NjdiNTg1YzlhNWE4NmRiMmU1MmQ4NjM3YzY5M2Y3NTZmOTMxOTNhYjkyZDcy
15
- MTUxNmRjYzMzMDQ5Y2E4OWI1ZWQ4NmViYzM0YzUxNzhhZThhNzQ=
6
+ metadata.gz: f8a9a3801675594bc2a3400c3a4d4657247614b250558c7c169c1f720c0b8f8ce333b29e6a1e621bd3557539c69c8a5ecb160d059049e80ab24d3c531350aecb
7
+ data.tar.gz: f75478c7a7df07526ad528e562978bae1918b8146562d42bab6fcf45c8f8eff749b67a3efa5dc08530285642d5ed1003247c2fa64bf3ce60f3482b9824a79580
data/LICENSE CHANGED
@@ -1,4 +1,28 @@
1
- Copyright (c) 2010-2014, Fiksu, Inc.
1
+ -----------------------------------------------------------------------------------
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2004-2013 David Heinemeier Hansson (original code author)
5
+ Copyright (c) 2013 Minero Aoki
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
8
+ this software and associated documentation files (the "Software"), to deal in
9
+ the Software without restriction, including without limitation the rights to
10
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11
+ the Software, and to permit persons to whom the Software is furnished to do so,
12
+ subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ -----------------------------------------------------------------------------------
25
+ Copyright (c) 2010-2013, Fiksu, Inc.
2
26
  All rights reserved.
3
27
 
4
28
  Redistribution and use in source and binary forms, with or without
data/README.md CHANGED
@@ -1,101 +1,43 @@
1
- # activerecord-redshift-adapter
1
+ activerecord-redshift-adapter
2
+ ==============================
2
3
 
3
- adapter for aws redshift for rails 3
4
+ Amazon Redshift adapter for ActiveRecord 7+ (Rails 7+).
5
+ This is a fork from https://github.com/pennylane-hq/activerecord-adapter-redshift, which is a fork from
6
+ https://rubygems.org/gems/activerecord7-redshift-adapter hosted on a private Gitlab instance.
7
+ It's itself forked the project from https://github.com/kwent/activerecord6-redshift-adapter
4
8
 
5
- Ripped from rails 3 postgresql.
9
+ Thanks to the auhors.
6
10
 
7
- ## example database.yml
8
- ```yml
9
- common: &common
10
- adapter: postgresql
11
- username: postgres
12
- encoding: SQL_ASCII
13
- template: template0
14
- pool: 5
15
- timeout: 5000
11
+ Usage
12
+ -------------------
16
13
 
17
- redshiftdb: &redshiftdb
18
- adapter: redshift
19
- host: clustername.something.us-east-1.redshift.amazonaws.com
20
- database: databasename
21
- port: 5439
22
- username: username
23
- password: password
24
-
25
- redshift_development:
26
- <<: *common
27
- <<: *redshiftdb
28
- database: databasename
29
- ```
14
+ For Rails, write following in Gemfile:
30
15
 
31
- ## options
32
- ```html
33
- <table>
34
- <tr>
35
- <th>option</th>
36
- <th>description</th>
37
- </tr>
38
- <tr>
39
- <th>schema_search_path</th>
40
- <td>set schema_search_path. use default value if not given.</td>
41
- </tr>
42
- <tr>
43
- <th>read_timezone</th>
44
- <td>force timezone for datetime when select values. ActiveRecord default timezone will set if not given.</td>
45
- </tr>
46
- </table>
16
+ ```ruby
17
+ gem 'activerecord-redshift-adapter'
47
18
  ```
48
19
 
49
- ## Have you considered using Partitioned gem? It works with redshift!
50
-
51
- https://github.com/fiksu/partitioned
20
+ Specify the adapter name in the `database.yml` file:
52
21
 
53
- ## TableManager
54
-
55
- Helpful code to clone redshift tables
56
-
57
- ```sql
58
- create table foos
59
- (
60
- id int not null primary key distkey,
61
- name varchar(255) unique sortkey
62
- );
22
+ ```YAML
23
+ development:
24
+ adapter: redshift
25
+ host: host
26
+ port: port
27
+ database: db
28
+ username: user
29
+ password: password
30
+ encoding: utf8
63
31
  ```
64
32
 
33
+ or use it directly in the URL when establishing a connection:
65
34
  ```ruby
66
- class Foo < ActiveRecord::Base
35
+ class SomeModel < ApplicationRecord
36
+ establish_connection('redshift://username:password@host/database')
67
37
  end
68
-
69
- require 'activerecord_redshift_adapter'
70
-
71
- table_manager = ActiverecordRedshift::TableManager.new(Foo.connection, :exemplar_table_name => Foo.table_name)
72
- table_manager.duplicate_table
73
38
  ```
74
39
 
75
- yields:
76
-
77
- ```sql
78
- select oid from pg_namespace where nspname = 'public' limit 1;
79
-
80
- select oid,reldiststyle from pg_class where relnamespace = 2200 and relname = 'foos' limit 1;
81
-
82
- select contype,conkey from pg_constraint where connamespace = 2200 and conrelid = 212591;
83
-
84
- select attname,attnum from pg_attribute where attrelid = 212591 and attnum in (2,1);
40
+ License
41
+ ---------
85
42
 
86
- show search_path;
87
-
88
- set search_path = 'public';
89
-
90
- select * from pg_table_def where tablename = 'foos' and schemaname = 'public';
91
-
92
- create temporary table temporary_events_25343
93
- (
94
- id integer not null distkey,
95
- name character varying(255),
96
- primary key (id),
97
- unique (name)
98
- ) sortkey (name);
99
-
100
- set search_path = '$user','public';
101
- ```
43
+ MIT license (same as ActiveRecord)
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Redshift
6
+ module ArrayParser # :nodoc:
7
+ DOUBLE_QUOTE = '"'
8
+ BACKSLASH = '\\'
9
+ COMMA = ','
10
+ BRACKET_OPEN = '{'
11
+ BRACKET_CLOSE = '}'
12
+
13
+ def parse_pg_array(string) # :nodoc:
14
+ local_index = 0
15
+ array = []
16
+ while local_index < string.length
17
+ case string[local_index]
18
+ when BRACKET_OPEN
19
+ local_index, array = parse_array_contents(array, string, local_index + 1)
20
+ when BRACKET_CLOSE
21
+ return array
22
+ end
23
+ local_index += 1
24
+ end
25
+
26
+ array
27
+ end
28
+
29
+ private
30
+
31
+ def parse_array_contents(array, string, index)
32
+ is_escaping = false
33
+ is_quoted = false
34
+ was_quoted = false
35
+ current_item = ''
36
+
37
+ local_index = index
38
+ while local_index
39
+ token = string[local_index]
40
+ if is_escaping
41
+ current_item << token
42
+ is_escaping = false
43
+ elsif is_quoted
44
+ case token
45
+ when DOUBLE_QUOTE
46
+ is_quoted = false
47
+ was_quoted = true
48
+ when BACKSLASH
49
+ is_escaping = true
50
+ else
51
+ current_item << token
52
+ end
53
+ else
54
+ case token
55
+ when BACKSLASH
56
+ is_escaping = true
57
+ when COMMA
58
+ add_item_to_array(array, current_item, was_quoted)
59
+ current_item = ''
60
+ was_quoted = false
61
+ when DOUBLE_QUOTE
62
+ is_quoted = true
63
+ when BRACKET_OPEN
64
+ internal_items = []
65
+ local_index, internal_items = parse_array_contents(internal_items, string, local_index + 1)
66
+ array.push(internal_items)
67
+ when BRACKET_CLOSE
68
+ add_item_to_array(array, current_item, was_quoted)
69
+ return local_index, array
70
+ else
71
+ current_item << token
72
+ end
73
+ end
74
+
75
+ local_index += 1
76
+ end
77
+ [local_index, array]
78
+ end
79
+
80
+ def add_item_to_array(array, current_item, quoted)
81
+ return if !quoted && current_item.empty?
82
+
83
+ if !quoted && current_item == 'NULL'
84
+ array.push nil
85
+ else
86
+ array.push current_item
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class RedshiftColumn < Column # :nodoc:
6
+ delegate :oid, :fmod, to: :sql_type_metadata
7
+
8
+ # Required for Rails 6.1, see https://github.com/rails/rails/pull/41756
9
+ mattr_reader :array, default: false
10
+ alias array? array
11
+
12
+ def initialize(name, default, sql_type_metadata, null = true, default_function = nil, **)
13
+ super name, default, sql_type_metadata, null, default_function
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Redshift
6
+ module DatabaseStatements
7
+ def explain(arel, binds = [])
8
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
9
+ ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
10
+ end
11
+
12
+ class ExplainPrettyPrinter # :nodoc:
13
+ # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
14
+ # PostgreSQL shell:
15
+ #
16
+ # QUERY PLAN
17
+ # ------------------------------------------------------------------------------
18
+ # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
19
+ # Join Filter: (posts.user_id = users.id)
20
+ # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
21
+ # Index Cond: (id = 1)
22
+ # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
23
+ # Filter: (posts.user_id = 1)
24
+ # (6 rows)
25
+ #
26
+ def pp(result)
27
+ header = result.columns.first
28
+ lines = result.rows.map(&:first)
29
+
30
+ # We add 2 because there's one char of padding at both sides, note
31
+ # the extra hyphens in the example above.
32
+ width = [header, *lines].map(&:length).max + 2
33
+
34
+ pp = []
35
+
36
+ pp << header.center(width).rstrip
37
+ pp << '-' * width
38
+
39
+ pp += lines.map { |line| " #{line}" }
40
+
41
+ nrows = result.rows.length
42
+ rows_label = nrows == 1 ? 'row' : 'rows'
43
+ pp << "(#{nrows} #{rows_label})"
44
+
45
+ "#{pp.join("\n")}\n"
46
+ end
47
+ end
48
+
49
+ def select_value(arel, name = nil, binds = [])
50
+ # In Rails 5.2, arel_from_relation replaced binds_from_relation,
51
+ # so we see which method exists to get the variables
52
+ #
53
+ # In Rails 6.0 to_sql_and_binds began only returning sql, with
54
+ # to_sql_and_binds serving as a replacement
55
+ if respond_to?(:arel_from_relation, true)
56
+ arel = arel_from_relation(arel)
57
+ sql, binds = to_sql_and_binds(arel, binds)
58
+ else
59
+ arel, binds = binds_from_relation arel, binds
60
+ sql = to_sql(arel, binds)
61
+ end
62
+ execute_and_clear(sql, name, binds) do |result|
63
+ result.getvalue(0, 0) if result.ntuples > 0 && result.nfields > 0
64
+ end
65
+ end
66
+
67
+ def select_values(arel, name = nil)
68
+ # In Rails 5.2, arel_from_relation replaced binds_from_relation,
69
+ # so we see which method exists to get the variables
70
+ #
71
+ # In Rails 6.0 to_sql_and_binds began only returning sql, with
72
+ # to_sql_and_binds serving as a replacement
73
+ if respond_to?(:arel_from_relation, true)
74
+ arel = arel_from_relation(arel)
75
+ sql, binds = to_sql_and_binds(arel, [])
76
+ else
77
+ arel, binds = binds_from_relation arel, []
78
+ sql = to_sql(arel, binds)
79
+ end
80
+
81
+ execute_and_clear(sql, name, binds) do |result|
82
+ if result.nfields > 0
83
+ result.column_values(0)
84
+ else
85
+ []
86
+ end
87
+ end
88
+ end
89
+
90
+ # Executes a SELECT query and returns an array of rows. Each row is an
91
+ # array of field values.
92
+ def select_rows(arel, name = nil, binds = [])
93
+ if respond_to?(:arel_from_relation, true)
94
+ arel = arel_from_relation(arel)
95
+ sql, binds = to_sql_and_binds(arel, [])
96
+ else
97
+ arel, binds = binds_from_relation arel, []
98
+ sql = to_sql(arel, binds)
99
+ end
100
+ execute_and_clear(sql, name, binds, &:values)
101
+ end
102
+
103
+ # The internal PostgreSQL identifier of the money data type.
104
+ MONEY_COLUMN_TYPE_OID = 790 # :nodoc:
105
+ # The internal PostgreSQL identifier of the BYTEA data type.
106
+ BYTEA_COLUMN_TYPE_OID = 17 # :nodoc:
107
+
108
+ # create a 2D array representing the result set
109
+ def result_as_array(res) # :nodoc:
110
+ # check if we have any binary column and if they need escaping
111
+ ftypes = Array.new(res.nfields) do |i|
112
+ [i, res.ftype(i)]
113
+ end
114
+
115
+ rows = res.values
116
+ return rows unless ftypes.any? do |_, x|
117
+ [BYTEA_COLUMN_TYPE_OID, MONEY_COLUMN_TYPE_OID].include?(x)
118
+ end
119
+
120
+ typehash = ftypes.group_by { |_, type| type }
121
+ binaries = typehash[BYTEA_COLUMN_TYPE_OID] || []
122
+ monies = typehash[MONEY_COLUMN_TYPE_OID] || []
123
+
124
+ rows.each do |row|
125
+ # unescape string passed BYTEA field (OID == 17)
126
+ binaries.each do |index, _|
127
+ row[index] = unescape_bytea(row[index])
128
+ end
129
+
130
+ # If this is a money type column and there are any currency symbols,
131
+ # then strip them off. Indeed it would be prettier to do this in
132
+ # PostgreSQLColumn.string_to_decimal but would break form input
133
+ # fields that call value_before_type_cast.
134
+ monies.each do |index, _|
135
+ data = row[index]
136
+ # Because money output is formatted according to the locale, there are two
137
+ # cases to consider (note the decimal separators):
138
+ # (1) $12,345,678.12
139
+ # (2) $12.345.678,12
140
+ case data
141
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
142
+ data.gsub!(/[^-\d.]/, '')
143
+ when /^-?\D+[\d.]+,\d{2}$/ # (2)
144
+ data.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ # Queries the database and returns the results in an Array-like object
151
+ def query(sql, name = nil) # :nodoc:
152
+ log(sql, name) do
153
+ result_as_array @connection.async_exec(sql)
154
+ end
155
+ end
156
+
157
+ # Executes an SQL statement, returning a PG::Result object on success
158
+ # or raising a PG::Error exception otherwise.
159
+ def execute(sql, name = nil)
160
+ log(sql, name) do
161
+ @connection.async_exec(sql)
162
+ end
163
+ end
164
+
165
+ def exec_query(sql, name = 'SQL', binds = [], prepare: false)
166
+ execute_and_clear(sql, name, binds, prepare: prepare) do |result|
167
+ types = {}
168
+ fields = result.fields
169
+ fields.each_with_index do |fname, i|
170
+ ftype = result.ftype i
171
+ fmod = result.fmod i
172
+ types[fname] = get_oid_type(ftype, fmod, fname)
173
+ end
174
+ ActiveRecord::Result.new(fields, result.values, types)
175
+ end
176
+ end
177
+ alias internal_exec_query exec_query
178
+
179
+ def exec_delete(sql, name = 'SQL', binds = [])
180
+ execute_and_clear(sql, name, binds, &:cmd_tuples)
181
+ end
182
+ alias exec_update exec_delete
183
+
184
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
185
+ if pk.nil?
186
+ # Extract the table from the insert sql. Yuck.
187
+ table_ref = extract_table_ref_from_insert_sql(sql)
188
+ pk = primary_key(table_ref) if table_ref
189
+ end
190
+
191
+ sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk && use_insert_returning?
192
+
193
+ super
194
+ end
195
+
196
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
197
+ val = exec_query(sql, name, binds)
198
+ if !use_insert_returning? && pk
199
+ unless sequence_name
200
+ table_ref = extract_table_ref_from_insert_sql(sql)
201
+ sequence_name = default_sequence_name(table_ref, pk)
202
+ return val unless sequence_name
203
+ end
204
+ last_insert_id_result(sequence_name)
205
+ else
206
+ val
207
+ end
208
+ end
209
+
210
+ # Begins a transaction.
211
+ def begin_db_transaction
212
+ execute 'BEGIN'
213
+ end
214
+
215
+ def begin_isolated_db_transaction(isolation)
216
+ begin_db_transaction
217
+ execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
218
+ end
219
+
220
+ # Commits a transaction.
221
+ def commit_db_transaction
222
+ execute 'COMMIT'
223
+ end
224
+
225
+ # Aborts a transaction.
226
+ def exec_rollback_db_transaction
227
+ execute 'ROLLBACK'
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Redshift
6
+ module OID # :nodoc:
7
+ class DateTime < Type::DateTime # :nodoc:
8
+ def type_cast_for_database(value)
9
+ if has_precision? && value.acts_like?(:time) && value.year <= 0
10
+ bce_year = format('%04d', -value.year + 1)
11
+ "#{super.sub(/^-?\d+/, bce_year)} BC"
12
+ else
13
+ super
14
+ end
15
+ end
16
+
17
+ def cast_value(value)
18
+ if value.is_a?(::String)
19
+ case value
20
+ when 'infinity' then ::Float::INFINITY
21
+ when '-infinity' then -::Float::INFINITY
22
+ when / BC$/
23
+ astronomical_year = format('%04d', -value[/^\d+/].to_i + 1)
24
+ super(value.sub(/ BC$/, '').sub(/^\d+/, astronomical_year))
25
+ else
26
+ super
27
+ end
28
+ else
29
+ value
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Redshift
6
+ module OID # :nodoc:
7
+ class Decimal < Type::Decimal # :nodoc:
8
+ def infinity(options = {})
9
+ BigDecimal('Infinity') * (options[:negative] ? -1 : 1)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Redshift
6
+ module OID # :nodoc:
7
+ class Json < Type::Value # :nodoc:
8
+ include ActiveModel::Type::Helpers::Mutable
9
+
10
+ def type
11
+ :json
12
+ end
13
+
14
+ def type_cast_from_database(value)
15
+ if value.is_a?(::String)
16
+ begin
17
+ ::ActiveSupport::JSON.decode(value)
18
+ rescue StandardError
19
+ nil
20
+ end
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ def type_cast_for_database(value)
27
+ if value.is_a?(::Array) || value.is_a?(::Hash)
28
+ ::ActiveSupport::JSON.encode(value)
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ def accessor
35
+ ActiveRecord::Store::StringKeyedHashAccessor
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Redshift
6
+ module OID # :nodoc:
7
+ class Jsonb < Json # :nodoc:
8
+ def type
9
+ :jsonb
10
+ end
11
+
12
+ def changed_in_place?(raw_old_value, new_value)
13
+ # Postgres does not preserve insignificant whitespaces when
14
+ # roundtripping jsonb columns. This causes some false positives for
15
+ # the comparison here. Therefore, we need to parse and re-dump the
16
+ # raw value here to ensure the insignificant whitespaces are
17
+ # consistent with our encoder's output.
18
+ raw_old_value = type_cast_for_database(type_cast_from_database(raw_old_value))
19
+ super(raw_old_value, new_value)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end