-- -- bytea conversion. A bytea is a raw byte string, so it reaches Ruby as a -- binary (ASCII-8BIT) String holding its exact bytes -- NUL-safe -- rather than -- as its hex text. Returning a Ruby String into a bytea takes the string's -- raw bytes verbatim (any encoding), with no hex/escape parsing. -- SET bytea_output = 'hex'; -- A bytea argument is a binary String; its bytes match the input exactly. CREATE FUNCTION b_form(bytea) RETURNS text LANGUAGE plruby AS $$ "#{args[0].class}:#{args[0].encoding}:#{args[0].bytesize}" $$; SELECT b_form('\xdeadbeef'::bytea); SELECT b_form('\x00ff01fe'::bytea); -- NUL and high bytes preserved SELECT b_form('\x'::bytea); -- empty -- The bytes are the raw content, so encode() of the echoed value round-trips. CREATE FUNCTION b_echo(bytea) RETURNS bytea LANGUAGE plruby AS $$ args[0] $$; SELECT encode(b_echo('\x00ff01fe'::bytea), 'hex'); -- The function can inspect and transform the raw bytes. CREATE FUNCTION b_first_byte(bytea) RETURNS int LANGUAGE plruby AS $$ args[0].bytes[0] $$; SELECT b_first_byte('\x41ff00'::bytea); -- 65 -- Building a bytea from raw Ruby bytes. CREATE FUNCTION b_make() RETURNS bytea LANGUAGE plruby AS $$ [0, 255, 16, 32].pack('C*') $$; SELECT encode(b_make(), 'hex'); -- A Ruby String with embedded NULs becomes those exact bytes. CREATE FUNCTION b_nul() RETURNS bytea LANGUAGE plruby AS $$ "a\x00b".b $$; SELECT encode(b_nul(), 'hex'); -- The empty bytea. CREATE FUNCTION b_empty() RETURNS bytea LANGUAGE plruby AS $$ "".b $$; SELECT encode(b_empty(), 'hex'); -- bytea inside a composite: the field is a binary String too. CREATE TYPE b_rec AS (tag text, blob bytea); CREATE FUNCTION b_rec_in(b_rec) RETURNS int LANGUAGE plruby AS $$ args[0]['blob'].bytesize $$; SELECT b_rec_in(ROW('x', '\x00ff01fe')::b_rec); -- 4 CREATE FUNCTION b_rec_out() RETURNS b_rec LANGUAGE plruby AS $$ {'tag' => 'y', 'blob' => [1, 2, 3].pack('C*')} $$; SELECT (b_rec_out()).tag, encode((b_rec_out()).blob, 'hex'); -- bytea read back through SPI is also a binary String. CREATE FUNCTION b_via_spi() RETURNS int LANGUAGE plruby AS $$ r = spi_exec("SELECT '\\x00ff01fe'::bytea AS b") spi_fetch_row(r)['b'].bytesize $$; SELECT b_via_spi(); -- 4 -- bytea[] arguments arrive as an Array of binary Strings, and a returned -- Array of binary Strings becomes a bytea[]. CREATE FUNCTION b_arr_in(bytea[]) RETURNS int LANGUAGE plruby AS $$ args[0].map(&:bytesize).inject(0, :+) $$; SELECT b_arr_in(ARRAY['\x0000'::bytea, '\xffffff'::bytea]); -- 5 CREATE FUNCTION b_arr_out() RETURNS bytea[] LANGUAGE plruby AS $$ [[0, 255].pack('C*'), [16].pack('C*')] $$; SELECT encode((b_arr_out())[1], 'hex'), encode((b_arr_out())[2], 'hex');