Como posso gerar um bytea aleatório

18

Eu gostaria de poder gerar byteacampos aleatórios de comprimento arbitrário (<1 GB) para preencher dados de teste.

Qual a melhor maneira para fazer isto?

Jack Douglas
fonte

Respostas:

20

Aprimorando a resposta de Jack Douglas para evitar a necessidade de loop PL / PgSQL e concatenação bytea, você pode usar:

CREATE OR REPLACE FUNCTION random_bytea(bytea_length integer)
RETURNS bytea AS $body$
    SELECT decode(string_agg(lpad(to_hex(width_bucket(random(), 0, 1, 256)-1),2,'0') ,''), 'hex')
    FROM generate_series(1, $1);
$body$
LANGUAGE 'sql'
VOLATILE
SET search_path = 'pg_catalog';

É uma SQLfunção simples , mais barata de chamar do que PL / PgSQL.

A diferença no desempenho devido ao método de agregação alterado é imensa para byteavalores maiores . Embora a função original seja até 3x mais rápida para tamanhos <50 bytes, esta é dimensionada muito melhor para valores maiores.

Ou use uma função de extensão C :

Eu implementei um gerador de bytea aleatório como uma função de extensão C simples. Está no meu repositório de scrapcode no GitHub . Veja o README lá.

Ele reduz o desempenho da versão SQL acima:

regress=# \a
regress=# \o /dev/null
regress=# \timing on
regress=# select random_bytea(2000000);
Time: 895.972 ms
regress=# drop function random_bytea(integer);
regress=# create extension random_bytea;
regress=# select random_bytea(2000000);
Time: 24.126 ms
Craig Ringer
fonte
11
Bem, eu vim com quase a mesma solução, mas testei apenas para valores mais baixos. A solução de @ Jack foi um vencedor claro. +1 para você por não parar aqui :)
Dezso
Obrigado - isso é excelente e instigante. Eu acho que FROM generate_series(0, $1);precisa ser FROM generate_series(1, $1);. Você já tentou recursão? Meu teste limitado implica que isso é melhor:
Jack Douglas
2
Eu tentei criar um link simbólico /dev/urandompara /var/lib/pgsql/datae lê-lo com pg_read_file()para o bônus pontos loucas, mas, infelizmente, pg_read_file()textentrada através de uma conversão de codificação, por isso não pode ler bytea. Se você realmente quer velocidade máxima, escrever uma Cfunção de extensão que utiliza um gerador de números pseudo-aleatório rápido para produzir dados binários e enrole um dado bytea ao redor do tampão :-)
Craig Ringer
11
@JackDouglas Eu não pude evitar. Versão de extensão C de random_bytea. github.com/ringerc/scrapcode/tree/master/postgresql/…
Craig Ringer
11
Outra excelente resposta! Na verdade, um dos melhores que eu já vi até agora. Não testei a extensão, mas confio que ela funcione conforme anunciado.
Erwin Brandstetter
5

Eu gostaria de poder gerar campos de bytea aleatórios de comprimento arbitrário

Esta função fará isso, mas 1Gb levará muito tempo porque não é dimensionado linearmente com o comprimento da saída:

create function random_bytea(p_length in integer) returns bytea language plpgsql as $$
declare
  o bytea := '';
begin 
  for i in 1..p_length loop
    o := o||decode(lpad(to_hex(width_bucket(random(), 0, 1, 256)-1),2,'0'), 'hex');
  end loop;
  return o;
end;$$;

teste de saída:

select random_bytea(2);

/*
|random_bytea|
|:-----------|
|\xcf99      |
*/

select random_bytea(10);

/*
|random_bytea          |
|:---------------------|
|\x781b462c3158db229b3c|
*/

select length(random_bytea(100000))
     , clock_timestamp()-statement_timestamp() time_taken;

/*
|length|time_taken     |
|-----:|:--------------|
|100000|00:00:00.654008|
*/

dbfiddle aqui

Jack Douglas
fonte
Bom uso de width_bucket. Handy.
Craig Ringer
11
Aprimorei sua abordagem para evitar o PL / PgSQL e o loop de concatenação caro; veja nova resposta. Usando string_agg sobre generate_series em vez de um loop de concatenação PL / PgSQL no bytea, estou vendo uma melhoria de 150 vezes no desempenho.
Craig Ringer