Skip to content

Commit

Permalink
Support column aliases with data types on PostgreSQL, useful for sele…
Browse files Browse the repository at this point in the history
…cting from functions returning records

Previously, you had to use Sequel.lit to handle this type of
derived column list.  While this is useful when selecting from
any database functions that return records, in terms of working
with other parts of Sequel, this makes usage of to_recordset in
the pg_json extension simpler, as shown by the spec change.
  • Loading branch information
jeremyevans committed Dec 18, 2024
1 parent 31d28f2 commit cc4a0ff
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
=== master

* Support column aliases with data types on PostgreSQL, useful for selecting from functions returning records (jeremyevans)

* Break ties in timestamp migrator version handling using lexicographic sort of rest of migration filename (jeremyevans)

* Fix strict_unused_block warnings when running specs on Ruby 3.4 (jeremyevans)
Expand Down
19 changes: 19 additions & 0 deletions lib/sequel/adapters/shared/postgres.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2388,6 +2388,25 @@ def delete_using_sql(sql)
join_from_sql(:USING, sql)
end

# Handle column aliases containing data types, useful for selecting from functions
# that return the record data type.
def derived_column_list_sql_append(sql, column_aliases)
c = false
comma = ', '
column_aliases.each do |a|
sql << comma if c
if a.is_a?(Array)
raise Error, "column aliases specified as arrays must have only 2 elements, the first is alias name and the second is data type" unless a.length == 2
a, type = a
identifier_append(sql, a)
sql << " " << db.cast_type_literal(type).to_s
else
identifier_append(sql, a)
end
c ||= true
end
end

# Add ON CONFLICT clause if it should be used
def insert_conflict_sql(sql)
if opts = @opts[:insert_conflict]
Expand Down
7 changes: 6 additions & 1 deletion lib/sequel/dataset/sql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1032,7 +1032,7 @@ def as_sql_append(sql, aliaz, column_aliases=nil)
if column_aliases
raise Error, "#{db.database_type} does not support derived column lists" unless supports_derived_column_lists?
sql << '('
identifier_list_append(sql, column_aliases)
derived_column_list_sql_append(sql, column_aliases)
sql << ')'
end
end
Expand Down Expand Up @@ -1165,6 +1165,11 @@ def delete_from_sql(sql)
end
end

# Append the column aliases to the SQL.
def derived_column_list_sql_append(sql, column_aliases)
identifier_list_append(sql, column_aliases)
end

# Disable caching of SQL for the current dataset
def disable_sql_caching!
cache_set(:_no_cache_sql, true)
Expand Down
2 changes: 1 addition & 1 deletion spec/adapters/postgres_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4116,7 +4116,7 @@ def left_item_id
@db.get(ja.typeof).must_equal 'array'
@db.from(ja.array_elements_text.as(:v)).select_map(:v).map{|s| s.gsub(' ', '')}.must_equal ['2', '3', '["a","b"]']
@db.from(jo.to_record.as(:v, [Sequel.lit('a integer'), Sequel.lit('b text')])).select_map(:a).must_equal [1]
@db.from(pg_json.call([{'a'=>1, 'b'=>1}]).op.to_recordset.as(:v, [Sequel.lit('a integer'), Sequel.lit('b integer')])).select_map(:a).must_equal [1]
@db.from(pg_json.call([{'a'=>1, 'b'=>1}]).op.to_recordset.as(:v, [[:a, Integer], [:b, Integer]])).select_map(:a).must_equal [1]

if json_type == :jsonb
@db.get(jo.has_key?('a')).must_equal true
Expand Down
6 changes: 6 additions & 0 deletions spec/core/mock_adapter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,12 @@ def @db.schema(x) [[:id, {:primary_key=>false, :auto_increment=>false}]] end
db.immediate_constraints(:server=>:test)
db.sqls.must_equal ['SET CONSTRAINTS ALL IMMEDIATE -- test']
end

it "should raise if an invalid number of elements is provided when using derived column lists with an array" do
@db.from{c.function.as(:d, [[:a, :b], [:e, :f]])}.sql.must_equal 'SELECT * FROM c() AS "d"("a" b, "e" f)'
proc{@db.from{c.function.as(:d, [[:a], [:e, :f]])}.sql}.must_raise Sequel::Error
proc{@db.from{c.function.as(:d, [[:a, :b, :c], [:e, :f]])}.sql}.must_raise Sequel::Error
end
end

describe "MySQL support" do
Expand Down

0 comments on commit cc4a0ff

Please sign in to comment.