lib/sequel/extensions/pg_row.rb in sequel-4.47.0 vs lib/sequel/extensions/pg_row.rb in sequel-4.48.0
- old
+ new
@@ -16,15 +16,18 @@
#
# In addition to the parsers, this extension comes with literalizers
# for HashRow and ArrayRow using the standard Sequel literalization callbacks, so
# they work with on all adapters.
#
-# The first thing you are going to want to do is to load the extension into
-# your Database object. Make sure you load the :pg_array extension first
-# if you plan to use composite types in bound variables:
+# To use this extension, first load it into the Database instance:
+#
+# DB.extension :pg_row
+#
+# If you plan to use arrays of composite types, make sure you load the
+# pg_array extension first:
#
-# DB.extension(:pg_array, :pg_row)
+# DB.extension :pg_array, :pg_row
#
# You can create an anonymous row type by calling the Sequel.pg_row with
# an array:
#
# Sequel.pg_row(array)
@@ -83,17 +86,19 @@
#
# Related module: Sequel::Postgres::PGRow
require 'delegate'
require 'strscan'
-Sequel.require 'adapters/utils/pg_types'
+Sequel.require 'adapters/shared/postgres'
module Sequel
module Postgres
module PGRow
ROW = 'ROW'.freeze
+ Sequel::Deprecation.deprecate_constant(self, :ROW)
CAST = '::'.freeze
+ Sequel::Deprecation.deprecate_constant(self, :CAST)
# Class for row-valued/composite types that are treated as arrays. By default,
# this is only used for generic PostgreSQL record types, as registered
# types use HashRow by default.
class ArrayRow < DelegateClass(Array)
@@ -128,14 +133,14 @@
@db_type || self.class.db_type
end
# Append SQL fragment related to this object to the sql.
def sql_literal_append(ds, sql)
- sql << ROW
+ sql << 'ROW'
ds.literal_append(sql, to_a)
if db_type
- sql << CAST
+ sql << '::'
ds.quote_schema_table_append(sql, db_type)
end
end
end
@@ -200,55 +205,64 @@
end
# Append SQL fragment related to this object to the sql.
def sql_literal_append(ds, sql)
check_columns!
- sql << ROW
+ sql << 'ROW'
ds.literal_append(sql, values_at(*columns))
if db_type
- sql << CAST
+ sql << '::'
ds.quote_schema_table_append(sql, db_type)
end
end
end
- ROW_TYPE_CLASSES = [HashRow, ArrayRow]
+ ROW_TYPE_CLASSES = [HashRow, ArrayRow]#.freeze # SEQUEL5
# This parser-like class splits the PostgreSQL
# row-valued/composite type output string format
# into an array of strings. Note this class makes
# no attempt to handle all input formats that PostgreSQL
# will accept, it only handles the output format that
# PostgreSQL uses.
class Splitter < StringScanner
OPEN_PAREN = /\(/.freeze
+ Sequel::Deprecation.deprecate_constant(self, :OPEN_PAREN)
CLOSE_PAREN = /\)/.freeze
+ Sequel::Deprecation.deprecate_constant(self, :CLOSE_PAREN)
UNQUOTED_RE = /[^,)]*/.freeze
+ Sequel::Deprecation.deprecate_constant(self, :UNQUOTED_RE)
SEP_RE = /[,)]/.freeze
+ Sequel::Deprecation.deprecate_constant(self, :SEP_RE)
QUOTE_RE = /"/.freeze
+ Sequel::Deprecation.deprecate_constant(self, :QUOTE_RE)
QUOTE_SEP_RE = /"[,)]/.freeze
+ Sequel::Deprecation.deprecate_constant(self, :QUOTE_SEP_RE)
QUOTED_RE = /(\\.|""|[^"])*/.freeze
+ Sequel::Deprecation.deprecate_constant(self, :QUOTED_RE)
REPLACE_RE = /\\(.)|"(")/.freeze
+ Sequel::Deprecation.deprecate_constant(self, :REPLACE_RE)
REPLACE_WITH = '\1\2'.freeze
+ Sequel::Deprecation.deprecate_constant(self, :REPLACE_WITH)
# Split the stored string into an array of strings, handling
# the different types of quoting.
def parse
return @result if @result
values = []
- skip(OPEN_PAREN)
- if skip(CLOSE_PAREN)
+ skip(/\(/)
+ if skip(/\)/)
values << nil
else
until eos?
- if skip(QUOTE_RE)
- values << scan(QUOTED_RE).gsub(REPLACE_RE, REPLACE_WITH)
- skip(QUOTE_SEP_RE)
+ if skip(/"/)
+ values << scan(/(\\.|""|[^"])*/).gsub(/\\(.)|"(")/, '\1\2')
+ skip(/"[,)]/)
else
- v = scan(UNQUOTED_RE)
+ v = scan(/[^,)]*/)
values << (v unless v.empty?)
- skip(SEP_RE)
+ skip(/[,)]/)
end
end
end
values
end
@@ -372,42 +386,42 @@
end
end
module DatabaseMethods
ESCAPE_RE = /("|\\)/.freeze
+ Sequel::Deprecation.deprecate_constant(self, :ESCAPE_RE)
ESCAPE_REPLACEMENT = '\\\\\1'.freeze
+ Sequel::Deprecation.deprecate_constant(self, :ESCAPE_REPLACEMENT)
COMMA = ','.freeze
+ Sequel::Deprecation.deprecate_constant(self, :COMMA)
# A hash mapping row type keys (usually symbols), to option
# hashes. At the least, the values will contain the :parser
# option for the Parser instance that the type will use.
attr_reader :row_types
# Do some setup for the data structures the module uses.
def self.extended(db)
- # Return right away if row_types has already been set. This
- # makes things not break if a user extends the database with
- # this module more than once (since extended is called every
- # time).
- return if db.row_types
-
db.instance_eval do
@row_types = {}
@row_schema_types = {}
extend(@row_type_method_module = Module.new)
- copy_conversion_procs([2249, 2287])
+ add_conversion_proc(2249, PGRow::Parser.new(:converter=>PGRow::ArrayRow))
+ if respond_to?(:register_array_type)
+ register_array_type('record', :oid=>2287, :scalar_oid=>2249)
+ end
end
end
# Handle ArrayRow and HashRow values in bound variables.
def bound_variable_arg(arg, conn)
case arg
when ArrayRow
- "(#{arg.map{|v| bound_variable_array(v) if v}.join(COMMA)})"
+ "(#{arg.map{|v| bound_variable_array(v) if v}.join(',')})"
when HashRow
arg.check_columns!
- "(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(COMMA)})"
+ "(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(',')})"
else
super
end
end
@@ -417,10 +431,12 @@
@row_schema_types.freeze
@row_type_method_module.freeze
super
end
+ STRING_TYPES = [18, 19, 25, 1042, 1043].freeze
+
# Register a new row type for the Database instance. db_type should be the type
# symbol. This parses the PostgreSQL system tables to get information the
# composite type, and by default has the type return instances of a subclass
# of HashRow.
#
@@ -469,33 +485,43 @@
parser_opts[:columns] = res.map{|r| r[0].to_sym}
parser_opts[:column_oids] = res.map{|r| r[1].to_i}
# Using the conversion_procs, lookup converters for each member of the composite type
parser_opts[:column_converters] = parser_opts[:column_oids].map do |oid|
+ # procs[oid] # SEQUEL5
+
+ # SEQUEL5: Remove
if pr = procs[oid]
pr
- elsif !Sequel::Postgres::STRING_TYPES.include?(oid)
+ elsif !STRING_TYPES.include?(oid)
# It's not a string type, and it's possible a conversion proc for this
# oid will be added later, so do a runtime check for it.
- lambda{|s| (pr = procs[oid]) ? pr.call(s) : s}
+ lambda do |s|
+ if (pr = procs[oid])
+ Sequel::Deprecation.deprecate("Calling conversion proc for subtype (oid: #{oid}) of composite type (oid: #{parser_opts[:oid]}) not added until after composite type registration", "Register subtype conversion procs before registering composite type")
+ pr.call(s)
+ else
+ s
+ end
+ end
end
end
# Setup the converter and typecaster
parser_opts[:converter] = opts.fetch(:converter){HashRow.subclass(db_type, parser_opts[:columns])}
parser_opts[:typecaster] = opts.fetch(:typecaster, parser_opts[:converter])
parser = Parser.new(parser_opts)
- @conversion_procs[parser.oid] = parser
+ add_conversion_proc(parser.oid, parser)
- if defined?(PGArray) && PGArray.respond_to?(:register) && array_oid && array_oid > 0
+ if respond_to?(:register_array_type) && array_oid && array_oid > 0
array_type_name = if type_schema
"#{type_schema}.#{type_name}"
else
type_name
end
- PGArray.register(array_type_name, :oid=>array_oid, :converter=>parser, :type_procs=>@conversion_procs, :scalar_typecast=>schema_type_symbol)
+ register_array_type(array_type_name, :oid=>array_oid, :converter=>parser, :scalar_typecast=>schema_type_symbol)
end
@row_types[literal(db_type)] = opts.merge(:parser=>parser, :type=>db_type)
@row_schema_types[schema_type_string] = schema_type_symbol
@schema_type_classes[schema_type_symbol] = ROW_TYPE_CLASSES
@@ -505,16 +531,15 @@
row_type(db_type, v)
end
private meth
end
- conversion_procs_updated
+ conversion_procs_updated # SEQUEL5: Remove
nil
end
- # When reseting conversion procs, reregister all the row types so that
- # the system tables are introspected again, picking up database changes.
+ # SEQUEL5: Remove
def reset_conversion_procs
procs = super
row_types.values.each do |opts|
register_row_type(opts[:type], opts)
@@ -556,14 +581,14 @@
# Format composite types used in bound variable arrays.
def bound_variable_array(arg)
case arg
when ArrayRow
- "\"(#{arg.map{|v| bound_variable_array(v) if v}.join(COMMA).gsub(ESCAPE_RE, ESCAPE_REPLACEMENT)})\""
+ "\"(#{arg.map{|v| bound_variable_array(v) if v}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
when HashRow
arg.check_columns!
- "\"(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(COMMA).gsub(ESCAPE_RE, ESCAPE_REPLACEMENT)})\""
+ "\"(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
else
super
end
end
@@ -576,13 +601,17 @@
end
end
end
end
- # Register the default anonymous record type
- PG_TYPES[2249] = PGRow::Parser.new(:converter=>PGRow::ArrayRow)
+ # SEQUEL5: Remove
+ parser = PGRow::Parser.new(:converter=>PGRow::ArrayRow)
+ PG__TYPES[2249] = lambda do |s|
+ Sequel::Deprecation.deprecate("Conversion proc for record added globally by pg_row extension", "Load the pg_row extension into the Database instance")
+ parser.call(s)
+ end
if defined?(PGArray) && PGArray.respond_to?(:register)
- PGArray.register('record', :oid=>2287, :scalar_oid=>2249)
+ PGArray.register('record', :oid=>2287, :scalar_oid=>2249, :skip_deprecation_warning=>true)
end
end
module SQL::Builders
# Wraps the expr array in an anonymous Postgres::PGRow::ArrayRow instance.