Skip to content

rg/pg_array #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 52 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
05f43b3
Fix redshift schema dump.
driv3r Oct 22, 2013
6561a7b
Some other fixes for redshift.
Nov 13, 2013
21d8556
Add default adapter options
MSeifert Nov 26, 2013
0d0d9d1
Merge pull request #2 from HitFox/add_default_options
Nov 26, 2013
8f070eb
Added DISTKEY and SORTKEY SQL fragments to column definition methods
MSeifert Nov 26, 2013
dcab34e
Merge pull request #3 from HitFox/dist_key_and_sort_key_sql
Nov 27, 2013
1da6c74
Redshift does not support DROP TABLE IF EXISTS
MSeifert Jan 7, 2014
8f2bbf8
Merge pull request #4 from HitFox/do_not_support_drop_table_if_exists
Jan 7, 2014
a6fc717
Remove hardcoded database connection from spec_helper.
knaveofdiamonds Aug 28, 2014
649b9b8
Ensure INSERT RETURNING is disabled in latest Sequel version.
knaveofdiamonds Aug 28, 2014
a9c0752
adding common table expressions support
troynt Apr 25, 2015
4885aa0
Redshift Supports Window Functions
samuelreh May 9, 2015
e3e2fde
Merge remote-tracking branch 'omniata/master'
troex Feb 13, 2017
0e7a53e
Merge remote-tracking branch 'knaveofdiamonds/master'
troex Mar 6, 2017
ad8ee56
code cleanup
troex Mar 6, 2017
0a04618
bump version
troex Mar 6, 2017
8193788
Each ALTER TABLE command has to be on its own line
Dec 12, 2014
a3c43cf
bump version
troex Sep 14, 2018
ef673ae
adds redshift string agg extension
Rustik Jun 24, 2019
4850b1a
support drop tabke if exists
troex Sep 6, 2019
0dbb8ad
Merge branch 'master' of github.com:attribution/sequel-redshift
troex Sep 6, 2019
7052fb5
bump version
troex Sep 6, 2019
8d28287
monkey patch sequel's string_agg to use redshift out of the box
Rustik Oct 27, 2019
91ff8bb
Bump Sequel dependency
mgz Nov 19, 2019
515e8aa
Bump bundler to v2
mgz Nov 21, 2019
15dd7ca
Drop test table if exists
mgz Nov 21, 2019
6d783b2
Allow logging with DEBUG_SQL env var
mgz Nov 21, 2019
aaadcdd
Add fixes for Sequel 5
mgz Nov 21, 2019
ece8f7e
Only Integer columns can be IDENTITY
mgz Nov 22, 2019
45afbc3
Do not support transactional DDL
mgz Nov 22, 2019
0905295
Merge pull request #1 from attribution/ms/sequel-5
troex Nov 26, 2019
c1d9cb9
add median function to extensions
Rustik Jan 14, 2020
57826d9
Merge branch 'master' of github.com:attribution/sequel-redshift
Rustik Jan 14, 2020
c379775
add spec for median
Rustik Jan 14, 2020
53a89a6
redshift doesn't support savepoints
troex Oct 31, 2020
7e4432a
Merge branch 'master' of github.com:attribution/sequel-redshift
troex Oct 31, 2020
259e6ce
transaction safe table_exits?
troex Nov 3, 2020
cdf88ab
fix table_exists? method
Rustik Nov 12, 2020
fd7791b
Merge pull request #2 from attribution/rg-fix-table-exists
Rustik Nov 12, 2020
2025507
add redshift? helper
Rustik Dec 1, 2020
bd710d6
add server version for redshift
Jan 21, 2022
c7aa904
add basic support for table diststyle/distkey/sortkey attributes
troex Aug 16, 2022
dfb761a
support attributes on create table as
troex Aug 16, 2022
a441453
Merge pull request #3 from attribution/table-attributes
troex Aug 24, 2022
cd8968e
add pg_array support
Rustik Aug 23, 2023
fbdf387
upd
Rustik Aug 23, 2023
1d64ad4
update
Rustik Aug 23, 2023
5a3485e
upd
Rustik Aug 23, 2023
7062389
upd
Rustik Aug 23, 2023
d6c1643
upd
Rustik Aug 23, 2023
6327244
upd
Rustik Aug 23, 2023
68f639f
add array ops
Rustik Aug 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
spec/database.yml
*.gem
*.rbc
.bundle
Expand Down
101 changes: 94 additions & 7 deletions lib/sequel/adapters/redshift.rb
Original file line number Diff line number Diff line change
@@ -1,31 +1,118 @@
require 'sequel/adapters/postgres'
require 'sequel/adapters/shared/redshift'

module Sequel
module Redshift
include Postgres

class Database < Postgres::Database
include Sequel::Redshift::DatabaseMethods

set_adapter_scheme :redshift

DIST_KEY = ' DISTKEY'.freeze
SORT_KEY = ' SORTKEY'.freeze

# The order of column modifiers to use when defining a column.
COLUMN_DEFINITION_ORDER = [:collate, :default, :primary_key, :dist_key, :sort_key, :null, :unique, :auto_increment, :references]

def dataset_class_default
Sequel::Redshift::Dataset
end

# We need to change these default settings because they correspond to
# Postgres configuration variables which do not exist in Redshift
def adapter_initialize
@opts.merge!(
force_standard_strings: false,
client_min_messages: false
)
super
end

def table_exists?(name)
sql = <<~SQL
SELECT EXISTS (
SELECT * FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public'
AND c.relname = '#{name}'
AND c.relkind = 'r'
);
SQL
fetch(sql).first.fetch(:"?column?")
end

def column_definition_primary_key_sql(sql, column)
result = super
result << ' IDENTITY' if result
result
if column[:primary_key] && column[:type] == Integer
sql << ' IDENTITY'
end
end

# Add DISTKEY SQL fragment to column creation SQL.
def column_definition_dist_key_sql(sql, column)
if column[:dist_key]
sql << DIST_KEY
end
end

# Add SORTKEY SQL fragment to column creation SQL.
def column_definition_sort_key_sql(sql, column)
if column[:sort_key]
sql << SORT_KEY
end
end

def serial_primary_key_options
# redshift doesn't support serial type
super.merge(serial: false)
end

# DROP TABLE IF EXISTS is now supported by Redshift
def supports_drop_table_if_exists?
true
end

# None of the alter table operation are combinable.
def combinable_alter_table_op?(op)
false
end

def supports_transactional_ddl?
false
end

def supports_savepoints?
false
end
end

class Dataset < Postgres::Dataset
Database::DatasetClass = self

# Redshift doesn't support RETURNING statement
def insert_returning_sql(sql)
# do nothing here
sql
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'with select distinct columns from join where group having compounds order limit lock']])

def initialize(db)
@db = db
@opts = {disable_insert_returning: true}.freeze
@cache = {}
freeze
end

def supports_cte?(type = :select)
true if type == :select
end

def supports_insert_select?
false
end

def supports_returning?(type)
false
end

def supports_window_functions?
true
end
end
end
Expand Down
78 changes: 78 additions & 0 deletions lib/sequel/adapters/shared/redshift.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
module Sequel
module Redshift
module DatabaseMethods

REDSHIFT_STRING_MAXIMUM_SIZE = 255

# Redshift does not support arrays (type of pg_index.indkey is INT2VECTOR),
# and because of that we can't determine the primary key - so we set it to false.
#
# The workaround for now is to use `set_primary_key` inside the sequel model.
def schema_parse_table(table_name, opts)
m = output_identifier_meth(opts[:dataset])

ds = metadata_dataset.select(:pg_attribute__attname___name,
SQL::Cast.new(:pg_attribute__atttypid, :integer).as(:oid),
SQL::Cast.new(:basetype__oid, :integer).as(:base_oid),
SQL::Function.new(:format_type, :basetype__oid, :pg_type__typtypmod).as(:db_base_type),
SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
SQL::BooleanExpression.new(:NOT, :pg_attribute__attnotnull).as(:allow_null)).
from(:pg_class).
join(:pg_attribute, :attrelid=>:oid).
join(:pg_type, :oid=>:atttypid).
left_outer_join(:pg_type___basetype, :oid=>:typbasetype).
left_outer_join(:pg_attrdef, :adrelid=>:pg_class__oid, :adnum=>:pg_attribute__attnum).
filter(:pg_attribute__attisdropped=>false).
filter{|o| o.pg_attribute__attnum > 0}.
filter(:pg_class__oid=>regclass_oid(table_name, opts)).
order(:pg_attribute__attnum)

ds.map do |row|
row[:default] = nil if blank_object?(row[:default])
if row[:base_oid]
row[:domain_oid] = row[:oid]
row[:oid] = row.delete(:base_oid)
row[:db_domain_type] = row[:db_type]
row[:db_type] = row.delete(:db_base_type)
else
row.delete(:base_oid)
row.delete(:db_base_type)
end
row[:type] = schema_column_type(row[:db_type])
row[:primary_key] = false
[m.call(row.delete(:name)), row]
end
end

# Redshift changes text to varchar with maximum size of 256, and it complains if you will give text column
def type_literal_generic_string(column)
"#{column[:fixed] ? 'CHAR' : 'VARCHAR'}(#{column[:size] || REDSHIFT_STRING_MAXIMUM_SIZE})"
end

# The version of the PostgreSQL server, used for determining capability.
def server_version(server=nil)
@server_version ||= 80002
end

def create_table_suffix_sql(name, options)
sql = create_table_attributes(name, options)
"#{super}#{sql}"
end

def create_table_attributes(name, options)
sql = String.new
sql << " DISTSTYLE #{options[:diststyle].upcase}" if options[:diststyle]
sql << " DISTKEY(#{options[:distkey]})" if options[:distkey]
sql << " SORTKEY(#{Array(options[:sortkey]).join(', ')})" if options[:sortkey]
sql
end

def create_table_as_sql(name, sql, options)
result = create_table_prefix_sql(name, options)
result += create_table_attributes(name, options)
result += " AS #{sql}"
end
end
end
end
55 changes: 55 additions & 0 deletions lib/sequel/extensions/median.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen-string-literal: true
#
module Sequel
module SQL
module Builders
# Return a median expression.
def median(*a)
Median.new(*a)
end
end

# The Median class represents a median function for Redshift and Postgresql databases.
class Median < GenericExpression
include OrderMethods

module DatasetMethods
# Append the SQL fragment for the Median expression to the SQL query.
def median_sql_append(sql, sa)
if defined?(super)
return super
end

expr = sa.expr

case db_type = db.adapter_scheme
when :postgres

literal_append(sql, Function.new(:percentile_disc, 0.5))
sql << " WITHIN GROUP (ORDER BY "
identifier_append(sql, expr)
sql << ")"

when :redshift

literal_append(sql, Function.new(:median, expr))

else
raise Error, "median is not implemented on #{db.database_type}"
end
end
end

attr_reader :expr

# Set the expression and separator
def initialize(expr)
@expr = expr
end

to_s_method :median_sql
end
end

Dataset.register_extension(:median, SQL::Median::DatasetMethods)
end
Loading