¿Cómo puedo generar un bytea al azar?

18

Me gustaría poder generar byteacampos aleatorios de longitud arbitraria (<1Gb) para llenar datos de prueba.

Cual es la mejor manera de hacer esto?

Jack Douglas
fuente

Respuestas:

20

Mejorando la respuesta de Jack Douglas para evitar la necesidad de bucles PL / PgSQL y concatenación bytea, puede 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';

Es una SQLfunción simple que es más barata de llamar que PL / PgSQL.

La diferencia en el rendimiento debido al método de agregación modificado es inmensa para byteavalores más grandes . Aunque la función original es hasta 3 veces más rápida para tamaños <50 bytes, esta escala mucho mejor para valores más grandes.

O use una función de extensión C :

He implementado un generador bytea aleatorio como una simple función de extensión C. Está en mi repositorio de código de desecho en GitHub . Ver el archivo Léame allí.

Desnuda el rendimiento de la versión SQL anterior:

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
fuente
1
Bueno, se me ocurrió casi la misma solución, pero probé solo los valores más bajos. La solución de @ Jack fue un claro ganador. +1 para ti por no detenerte aquí :)
dezso
Gracias, esto es excelente y estimulante. Creo que FROM generate_series(0, $1);necesita ser FROM generate_series(1, $1);. ¿Has probado la recursividad? Mi prueba limitada implica que esto se escala mejor:
Jack Douglas
2
Probé enlaces simbólicos /dev/urandomen /var/lib/pgsql/datay leer con pg_read_file()de puntos de bonificación locos, pero por desgracia pg_read_file()lee textentrada a través de una conversión de codificación, por lo que no puede leer bytea. Si realmente desea una velocidad máxima, escriba una Cfunción de extensión que use un generador rápido de números pseudoaleatorios para producir datos binarios y ajuste un dato bytea alrededor del búfer :-)
Craig Ringer
1
@JackDouglas No pude evitarlo. Versión de extensión C de random_bytea. github.com/ringerc/scrapcode/tree/master/postgresql/…
Craig Ringer
1
Otra excelente respuesta! En realidad, uno de los mejores que he visto hasta ahora. No he probado la extensión, pero confío en que funcione como se anuncia.
Erwin Brandstetter
5

Me gustaría poder generar campos bytea aleatorios de longitud arbitraria

Esta función lo hará, pero 1Gb tomará mucho tiempo porque no se escala linealmente con la longitud de salida:

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;$$;

prueba de salida:

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 aquí

Jack Douglas
fuente
Buen uso de width_bucket. Práctico.
Craig Ringer
1
He mejorado su enfoque para evitar el PL / PgSQL y el costoso ciclo de concatenación; ver nueva respuesta Al usar string_agg sobre generate_series en lugar de un ciclo de concatenación PL / PgSQL en bytea, veo una mejora de 150 veces en el rendimiento.
Craig Ringer