Sequel.require 'adapters/shared/postgres' module Sequel Postgres::CONVERTED_EXCEPTIONS << NativeException module JDBC # Adapter, Database, and Dataset support for accessing a PostgreSQL # database via JDBC. module Postgres # Methods to add to Database instances that access PostgreSQL via # JDBC. module DatabaseMethods extend Sequel::Database::ResetIdentifierMangling include Sequel::Postgres::DatabaseMethods # Add the primary_keys and primary_key_sequences instance variables, # so we can get the correct return values for inserted rows. def self.extended(db) super db.send(:initialize_postgres_adapter) end # See Sequel::Postgres::Adapter#copy_into def copy_into(table, opts=OPTS) data = opts[:data] data = Array(data) if data.is_a?(String) if block_given? && data raise Error, "Cannot provide both a :data option and a block to copy_into" elsif !block_given? && !data raise Error, "Must provide either a :data option or a block to copy_into" end synchronize(opts) do |conn| begin copy_manager = org.postgresql.copy.CopyManager.new(conn) copier = copy_manager.copy_in(copy_into_sql(table, opts)) if block_given? while buf = yield copier.writeToCopy(buf.to_java_bytes, 0, buf.length) end else data.each { |d| copier.writeToCopy(d.to_java_bytes, 0, d.length) } end rescue Exception => e copier.cancelCopy raise ensure unless e begin copier.endCopy rescue NativeException => e2 raise_error(e2) end end end end end # See Sequel::Postgres::Adapter#copy_table def copy_table(table, opts=OPTS) synchronize(opts[:server]) do |conn| copy_manager = org.postgresql.copy.CopyManager.new(conn) copier = copy_manager.copy_out(copy_table_sql(table, opts)) begin if block_given? while buf = copier.readFromCopy yield(String.from_java_bytes(buf)) end nil else b = '' while buf = copier.readFromCopy b << String.from_java_bytes(buf) end b end ensure raise DatabaseDisconnectError, "disconnecting as a partial COPY may leave the connection in an unusable state" if buf end end end private # Use setNull for nil arguments as the default behavior of setString # with nil doesn't appear to work correctly on PostgreSQL. def set_ps_arg_nil(cps, i) cps.setNull(i, JavaSQL::Types::NULL) end # Execute the connection configuration SQL queries on the connection. def setup_connection(conn) conn = super(conn) statement(conn) do |stmt| connection_configuration_sqls.each{|sql| log_yield(sql){stmt.execute(sql)}} end conn end end # Dataset subclass used for datasets that connect to PostgreSQL via JDBC. class Dataset < JDBC::Dataset include Sequel::Postgres::DatasetMethods APOS = Dataset::APOS class ::Sequel::JDBC::Dataset::TYPE_TRANSLATOR # Convert Java::OrgPostgresqlUtil::PGobject to ruby strings def pg_object(v) v.to_string end end # Handle conversions of PostgreSQL array instances class PGArrayConverter # Set the method that will return the correct conversion # proc for elements of this array. def initialize(meth) @conversion_proc_method = meth @conversion_proc = nil end # Convert Java::OrgPostgresqlJdbc4::Jdbc4Array to ruby arrays def call(v) _pg_array(v.array) end private # Handle multi-dimensional Java arrays by recursively mapping them # to ruby arrays of ruby values. def _pg_array(v) v.to_ary.map do |i| if i.respond_to?(:to_ary) _pg_array(i) elsif i if @conversion_proc.nil? @conversion_proc = @conversion_proc_method.call(i) end if @conversion_proc @conversion_proc.call(i) else i end else i end end end end PG_OBJECT_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:pg_object) # Add the shared PostgreSQL prepared statement methods def prepare(type, name=nil, *values) ps = to_prepared_statement(type, values) ps.extend(JDBC::Dataset::PreparedStatementMethods) ps.extend(::Sequel::Postgres::DatasetMethods::PreparedStatementMethods) if name ps.prepared_statement_name = name db.set_prepared_statement(name, ps) end ps end private # Handle PostgreSQL array and object types. Object types are just # turned into strings, similarly to how the native adapter treats # the types. def convert_type_proc(v, ctn=nil) case v when Java::OrgPostgresqlJdbc4::Jdbc4Array if pr = db.conversion_procs[ctn] lambda{|x| pr.call(PG_OBJECT_METHOD.call(x))} else PGArrayConverter.new(method(:convert_type_proc)) end when Java::OrgPostgresqlUtil::PGobject if pr = db.conversion_procs[ctn] lambda{|x| pr.call(PG_OBJECT_METHOD.call(x))} else PG_OBJECT_METHOD end when String if pr = db.conversion_procs[ctn] pr else false end when JAVA_HASH_MAP if Sequel.respond_to?(:hstore) lambda{|x| Sequel.hstore(HASH_MAP_METHOD.call(x))} else HASH_MAP_METHOD end else super end end # The jdbc/postgresql adapter uses column type oids when determining # conversion procs. def convert_type_proc_uses_column_info? true end # Use the column type oid as the database specific column info value. def convert_type_proc_column_info(meta, i) meta.field(i).oid end # Literalize strings similar to the native postgres adapter def literal_string_append(sql, v) sql << APOS << db.synchronize(@opts[:server]){|c| c.escape_string(v)} << APOS end end end end end