activerecord-redshift-adapter 8.0.0.beta2 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/active_record/connection_adapters/redshift_7_0/database_statements.rb +5 -0
- data/lib/active_record/connection_adapters/redshift_7_0/schema_statements.rb +4 -4
- data/lib/active_record/connection_adapters/redshift_7_0_adapter.rb +3 -6
- data/lib/active_record/connection_adapters/redshift_7_1/schema_statements.rb +4 -4
- data/lib/active_record/connection_adapters/redshift_7_1_adapter.rb +0 -10
- data/lib/active_record/connection_adapters/redshift_7_2/schema_statements.rb +4 -4
- data/lib/active_record/connection_adapters/redshift_7_2_adapter.rb +0 -10
- data/lib/active_record/connection_adapters/redshift_8_0/schema_statements.rb +8 -7
- data/lib/active_record/connection_adapters/redshift_8_0_adapter.rb +17 -10
- data/lib/active_record/connection_adapters/redshift_8_1/array_parser.rb +92 -0
- data/lib/active_record/connection_adapters/redshift_8_1/column.rb +20 -0
- data/lib/active_record/connection_adapters/redshift_8_1/database_statements.rb +181 -0
- data/lib/active_record/connection_adapters/redshift_8_1/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/redshift_8_1/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/redshift_8_1/oid/json.rb +41 -0
- data/lib/active_record/connection_adapters/redshift_8_1/oid/jsonb.rb +25 -0
- data/lib/active_record/connection_adapters/redshift_8_1/oid/type_map_initializer.rb +62 -0
- data/lib/active_record/connection_adapters/redshift_8_1/oid.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_8_1/quoting.rb +164 -0
- data/lib/active_record/connection_adapters/redshift_8_1/referential_integrity.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_8_1/schema_definitions.rb +70 -0
- data/lib/active_record/connection_adapters/redshift_8_1/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_8_1/schema_statements.rb +423 -0
- data/lib/active_record/connection_adapters/redshift_8_1/type_metadata.rb +43 -0
- data/lib/active_record/connection_adapters/redshift_8_1/utils.rb +81 -0
- data/lib/active_record/connection_adapters/redshift_8_1_adapter.rb +858 -0
- data/lib/active_record/connection_adapters/redshift_adapter.rb +3 -1
- metadata +22 -7
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters
|
|
5
|
+
module Redshift
|
|
6
|
+
module OID # :nodoc:
|
|
7
|
+
# This class uses the data from PostgreSQL pg_type table to build
|
|
8
|
+
# the OID -> Type mapping.
|
|
9
|
+
# - OID is an integer representing the type.
|
|
10
|
+
# - Type is an OID::Type object.
|
|
11
|
+
# This class has side effects on the +store+ passed during initialization.
|
|
12
|
+
class TypeMapInitializer # :nodoc:
|
|
13
|
+
def initialize(store, run_complex_types = true)
|
|
14
|
+
@store = store
|
|
15
|
+
@run_complex_types = run_complex_types
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run(records)
|
|
19
|
+
records
|
|
20
|
+
.reject { |row| @store.key? row['oid'].to_i }
|
|
21
|
+
.select { |row| @store.key? row['typname'] }
|
|
22
|
+
.each { |row| register_mapped_type(row) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def register_mapped_type(row)
|
|
28
|
+
alias_type row['oid'], row['typname']
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def register(oid, oid_type = nil, &block)
|
|
32
|
+
oid = assert_valid_registration(oid, oid_type || block)
|
|
33
|
+
if block_given?
|
|
34
|
+
@store.register_type(oid, &block)
|
|
35
|
+
else
|
|
36
|
+
@store.register_type(oid, oid_type)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def alias_type(oid, target)
|
|
41
|
+
oid = assert_valid_registration(oid, target)
|
|
42
|
+
@store.alias_type(oid, target)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def register_with_subtype(oid, target_oid)
|
|
46
|
+
return unless @store.key?(target_oid)
|
|
47
|
+
|
|
48
|
+
register(oid) do |_, *args|
|
|
49
|
+
yield @store.lookup(target_oid, *args)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def assert_valid_registration(oid, oid_type)
|
|
54
|
+
raise ArgumentError, "can't register nil type for OID #{oid}" if oid_type.nil?
|
|
55
|
+
|
|
56
|
+
oid.to_i
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'oid/date_time'
|
|
4
|
+
require_relative 'oid/decimal'
|
|
5
|
+
require_relative 'oid/json'
|
|
6
|
+
require_relative 'oid/jsonb'
|
|
7
|
+
|
|
8
|
+
require_relative 'oid/type_map_initializer'
|
|
9
|
+
|
|
10
|
+
module ActiveRecord
|
|
11
|
+
module ConnectionAdapters
|
|
12
|
+
module Redshift
|
|
13
|
+
module OID # :nodoc:
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters
|
|
5
|
+
module Redshift
|
|
6
|
+
module Quoting
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
|
11
|
+
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
|
12
|
+
|
|
13
|
+
# Checks the following cases:
|
|
14
|
+
#
|
|
15
|
+
# - table_name
|
|
16
|
+
# - "table.name"
|
|
17
|
+
# - schema_name.table_name
|
|
18
|
+
# - schema_name."table.name"
|
|
19
|
+
# - "schema.name".table_name
|
|
20
|
+
# - "schema.name"."table.name"
|
|
21
|
+
def quote_table_name(name)
|
|
22
|
+
QUOTED_TABLE_NAMES[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Quotes column names for use in SQL queries.
|
|
26
|
+
def quote_column_name(name) # :nodoc:
|
|
27
|
+
QUOTED_COLUMN_NAMES[name] ||= PG::Connection.quote_ident(name.to_s).freeze
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def column_name_matcher
|
|
31
|
+
COLUMN_NAME
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def column_name_with_order_matcher
|
|
35
|
+
COLUMN_NAME_WITH_ORDER
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
COLUMN_NAME = /
|
|
39
|
+
\A
|
|
40
|
+
(
|
|
41
|
+
(?:
|
|
42
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
|
43
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
|
44
|
+
)
|
|
45
|
+
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
|
46
|
+
)
|
|
47
|
+
(?:\s*,\s*\g<1>)*
|
|
48
|
+
\z
|
|
49
|
+
/ix
|
|
50
|
+
|
|
51
|
+
COLUMN_NAME_WITH_ORDER = /
|
|
52
|
+
\A
|
|
53
|
+
(
|
|
54
|
+
(?:
|
|
55
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
|
56
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
|
57
|
+
)
|
|
58
|
+
(?:\s+COLLATE\s+"\w+")?
|
|
59
|
+
(?:\s+ASC|\s+DESC)?
|
|
60
|
+
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
|
61
|
+
)
|
|
62
|
+
(?:\s*,\s*\g<1>)*
|
|
63
|
+
\z
|
|
64
|
+
/ix
|
|
65
|
+
|
|
66
|
+
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Escapes binary strings for bytea input to the database.
|
|
70
|
+
def escape_bytea(value)
|
|
71
|
+
valid_raw_connection.escape_bytea(value) if value
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Unescapes bytea output from a database to the binary string it represents.
|
|
75
|
+
# NOTE: This is NOT an inverse of escape_bytea! This is only to be used
|
|
76
|
+
# on escaped binary output from database drive.
|
|
77
|
+
def unescape_bytea(value)
|
|
78
|
+
valid_raw_connection.unescape_bytea(value) if value
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Quotes strings for use in SQL input.
|
|
82
|
+
def quote_string(s) # :nodoc:
|
|
83
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
|
84
|
+
connection.escape(s)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def quote_table_name_for_assignment(_table, attr)
|
|
89
|
+
quote_column_name(attr)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Quotes schema names for use in SQL queries.
|
|
93
|
+
def quote_schema_name(name)
|
|
94
|
+
PG::Connection.quote_ident(name)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Quote date/time values for use in SQL input.
|
|
98
|
+
def quoted_date(value) # :nodoc:
|
|
99
|
+
if value.year <= 0
|
|
100
|
+
bce_year = format("%04d", -value.year + 1)
|
|
101
|
+
super.sub(/^-?\d+/, bce_year) + " BC"
|
|
102
|
+
else
|
|
103
|
+
super
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def quoted_binary(value) # :nodoc:
|
|
108
|
+
"'#{escape_bytea(value.to_s)}'"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Does not quote function default values for UUID columns
|
|
112
|
+
def quote_default_value(value, column) # :nodoc:
|
|
113
|
+
if column.type == :uuid && value =~ /\(\)/
|
|
114
|
+
value
|
|
115
|
+
else
|
|
116
|
+
quote(value, column)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def quote(value)
|
|
121
|
+
case value
|
|
122
|
+
when Numeric
|
|
123
|
+
if value.finite?
|
|
124
|
+
super
|
|
125
|
+
else
|
|
126
|
+
"'#{value}'"
|
|
127
|
+
end
|
|
128
|
+
when Type::Binary::Data
|
|
129
|
+
"'#{escape_bytea(value.to_s)}'"
|
|
130
|
+
else
|
|
131
|
+
super
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def quote_default_expression(value, column) # :nodoc:
|
|
136
|
+
if value.is_a?(Proc)
|
|
137
|
+
value.call
|
|
138
|
+
elsif column.type == :uuid && value.is_a?(String) && value.include?("()")
|
|
139
|
+
value # Does not quote function default values for UUID columns
|
|
140
|
+
else
|
|
141
|
+
super
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def type_cast(value)
|
|
146
|
+
case value
|
|
147
|
+
when Type::Binary::Data
|
|
148
|
+
# Return a bind param hash with format as binary.
|
|
149
|
+
# See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
|
|
150
|
+
# for more information
|
|
151
|
+
{ value: value.to_s, format: 1 }
|
|
152
|
+
else
|
|
153
|
+
super
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def lookup_cast_type_from_column(column) # :nodoc:
|
|
158
|
+
verify! if type_map.nil?
|
|
159
|
+
type_map.lookup(column.oid, column.fmod, column.sql_type)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters
|
|
5
|
+
module Redshift
|
|
6
|
+
module ReferentialIntegrity # :nodoc:
|
|
7
|
+
def supports_disable_referential_integrity? # :nodoc:
|
|
8
|
+
true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def disable_referential_integrity # :nodoc:
|
|
12
|
+
yield
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters
|
|
5
|
+
module Redshift
|
|
6
|
+
module ColumnMethods
|
|
7
|
+
# Defines the primary key field.
|
|
8
|
+
# Use of the native PostgreSQL UUID type is supported, and can be used
|
|
9
|
+
# by defining your tables as such:
|
|
10
|
+
#
|
|
11
|
+
# create_table :stuffs, id: :uuid do |t|
|
|
12
|
+
# t.string :content
|
|
13
|
+
# t.timestamps
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# By default, this will use the +uuid_generate_v4()+ function from the
|
|
17
|
+
# +uuid-ossp+ extension, which MUST be enabled on your database. To enable
|
|
18
|
+
# the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
|
|
19
|
+
# migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
|
|
20
|
+
# set the +:default+ option to +nil+:
|
|
21
|
+
#
|
|
22
|
+
# create_table :stuffs, id: false do |t|
|
|
23
|
+
# t.primary_key :id, :uuid, default: nil
|
|
24
|
+
# t.uuid :foo_id
|
|
25
|
+
# t.timestamps
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# You may also pass a different UUID generation function from +uuid-ossp+
|
|
29
|
+
# or another library.
|
|
30
|
+
#
|
|
31
|
+
# Note that setting the UUID primary key default value to +nil+ will
|
|
32
|
+
# require you to assure that you always provide a UUID value before saving
|
|
33
|
+
# a record (as primary keys cannot be +nil+). This might be done via the
|
|
34
|
+
# +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
|
|
35
|
+
def primary_key(name, type = :primary_key, **options)
|
|
36
|
+
return super unless type == :uuid
|
|
37
|
+
|
|
38
|
+
options[:default] = options.fetch(:default, 'uuid_generate_v4()')
|
|
39
|
+
options[:primary_key] = true
|
|
40
|
+
column name, type, options
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def json(name, **options)
|
|
44
|
+
column(name, :json, options)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def jsonb(name, **options)
|
|
48
|
+
column(name, :jsonb, options)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
|
56
|
+
include ColumnMethods
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def create_column_definition(*args)
|
|
61
|
+
Redshift::ColumnDefinition.new(*args)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class Table < ActiveRecord::ConnectionAdapters::Table
|
|
66
|
+
include ColumnMethods
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters
|
|
5
|
+
module Redshift
|
|
6
|
+
module ColumnDumper
|
|
7
|
+
# Adds +:array+ option to the default set provided by the
|
|
8
|
+
# AbstractAdapter
|
|
9
|
+
def prepare_column_options(column) # :nodoc:
|
|
10
|
+
spec = super
|
|
11
|
+
spec[:default] = "\"#{column.default_function}\"" if column.default_function
|
|
12
|
+
spec
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|