web-dev-qa-db-fra.com

Comment créer une chaîne aléatoire adaptée à un ID de session dans PostgreSQL?

J'aimerais créer une chaîne aléatoire à utiliser lors de la vérification de session à l'aide de PostgreSQL. Je sais que je peux obtenir un nombre aléatoire avec SELECT random(), alors j'ai essayé SELECT md5(random()), mais cela ne fonctionne pas. Comment puis-je faire ceci?

95
gersh

Je suggérerais cette solution simple:

C'est une fonction assez simple qui retourne une chaîne aléatoire de la longueur donnée:

Create or replace function random_string(length integer) returns text as
$$
declare
  chars text[] := '{0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}';
  result text := '';
  i integer := 0;
begin
  if length < 0 then
    raise exception 'Given length cannot be less than 0';
  end if;
  for i in 1..length loop
    result := result || chars[1+random()*(array_length(chars, 1)-1)];
  end loop;
  return result;
end;
$$ language plpgsql;

Et l'usage:

select random_string(15);

Exemple de sortie:

select random_string(15) from generate_series(1,15);

  random_string
-----------------
 5emZKMYUB9C2vT6
 3i4JfnKraWduR0J
 R5xEfIZEllNynJR
 tMAxfql0iMWMIxM
 aPSYd7pDLcyibl2
 3fPDd54P5llb84Z
 VeywDb53oQfn9GZ
 BJGaXtfaIkN4NV8
 w1mvxzX33NTiBby
 knI1Opt4QDonHCJ
 P9KC5IBcLE0owBQ
 vvEEwc4qfV4VJLg
 ckpwwuG8YbMYQJi
 rFf6TchXTO3XsLs
 axdQvaLBitm6SDP
(15 rows)
68
Szymon Lipiński

Vous pouvez réparer votre tentative initiale comme ceci:

SELECT md5(random()::text);

Beaucoup plus simple que certaines des autres suggestions. :-)

215
Peter Eisentraut

En vous appuyant sur la solution de Marcin, vous pouvez utiliser un alphabet quelconque (dans ce cas, tous les 62 caractères alphanumériques ASCII):

SELECT array_to_string(array 
       ( 
              select substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', trunc(random() * 62)::integer + 1, 1)
              FROM   generate_series(1, 12)), '');
28
grourk

Vous pouvez obtenir 128 bits de manière aléatoire à partir d'un UUID. C'est la méthode pour faire le travail dans PostgreSQL moderne.

CREATE EXTENSION pgcrypto;
SELECT gen_random_uuid();

           gen_random_uuid            
--------------------------------------
 202ed325-b8b1-477f-8494-02475973a28f

Peut-être mérite aussi de lire la documentation sur l'UUID

Le type de données uuid stocke les identificateurs universels uniques (UUID) définis par RFC 4122, ISO/IEC 9834-8: 2005 et les normes associées. (Certains systèmes se réfèrent à ce type de données sous la forme d'un identifiant global unique, ou GUID, à la place.) Cet identifiant est une quantité de 128 bits générée par un algorithme choisi pour rendre très improbable que le même identifiant soit généré par une autre personne de l'univers connu utilisant le même algorithme. Par conséquent, pour les systèmes distribués, ces identificateurs offrent une meilleure garantie d'unicité que les générateurs de séquence, qui ne sont uniques que dans une seule base de données.

Dans quelle mesure une collision avec l'UUID est-elle rare ou concevable? En supposant qu'ils soient aléatoires,

Environ 100 000 milliards d’UUID de version 4 devraient être générés pour avoir une chance sur un milliard d’un doublon unique ("collision"). La probabilité d'une collision n'atteint 50% seulement après que 261 UUID (2,3 x 10 ^ 18 ou 2,3 ​​quintillions) ont été générés. En associant ces nombres à des bases de données et en examinant la question de savoir si la probabilité d'une collision UUID de version 4 est négligeable, considérons un fichier contenant 2,3 UIID de version 4 de 2,3 milliards, avec 50% de chances de contenir une collision d'UUID. Elle aurait une taille de 36 exaoctets, en supposant l'absence d'autres données ou coûts, des milliers de fois plus volumineuses que les plus grandes bases de données existantes, qui sont de l'ordre du pétaoctet. Au rythme de 1 milliard d'UUID générés par seconde, il faudrait 73 ans pour générer les UUID du fichier. Il faudrait également environ 3,6 millions de disques durs ou de cartouches de bande de 10 téraoctets pour le stocker, en l'absence de sauvegarde ou de redondance. Lire le fichier à un taux de transfert "disque à tampon" typique de 1 gigabit par seconde nécessiterait plus de 3 000 ans pour un seul processeur. Etant donné que le taux d'erreur de lecture irrécupérable des lecteurs est de 1 bit pour 1018 bits lus, au mieux, alors que le fichier contient environ 1020 bits, une simple lecture du fichier une fois à la fois entraînerait au moins environ 100 fois plus d'erreur. lire les UUID que les doublons. Les erreurs de stockage, de réseau, d'alimentation et autres erreurs matérielles et logicielles seraient sans doute des milliers de fois plus fréquentes que les problèmes de duplication UUID.

source: wikipedia

En résumé,

  • UUID est normalisé.
  • gen_random_uuid() correspond à 128 bits de mémoire aléatoire stockés sur 128 bits (2 ** 128 combinaisons). 0-déchets.
  • random() ne génère que 52 bits de manière aléatoire dans PostgreSQL (2 ** 52 combinaisons).
  • md5() enregistrée en tant qu'UUID est de 128 bits, mais elle ne peut être aussi aléatoire que son entrée ( 52 bits si vous utilisez random() )
  • md5() stocké en tant que texte contient 288 bits, mais il ne peut être que de manière aussi aléatoire que son entrée ( 52 bits si vous utilisez random() ) - plus de deux fois la taille d'un UUID et une fraction de l'aléatoire)
  • md5() comme un hachage, peut être tellement optimisé qu'il ne fait pas grand-chose.
  • L'UUID est très efficace pour le stockage: PostgreSQL fournit un type de 128 bits exactement. Contrairement à text et varchar, etc., qui sont stockés sous la forme de varlena, ce qui entraîne une surcharge pour la longueur de la chaîne.
  • PostgreSQL Nifty UUID est livré avec des opérateurs, castings et fonctionnalités par défaut.
16
Evan Carroll

Je jouais avec PostgreSQL récemment et je pense avoir trouvé une solution un peu meilleure en utilisant uniquement les méthodes PostgreSQL intégrées - pas de pl/pgsql. La seule limitation est qu’il ne génère actuellement que des chaînes, des nombres ou des chaînes en minuscule UPCASE.

template1=> SELECT array_to_string(ARRAY(SELECT chr((65 + round(random() * 25)) :: integer) FROM generate_series(1,12)), '');
 array_to_string
-----------------
 TFBEGODDVTDM

template1=> SELECT array_to_string(ARRAY(SELECT chr((48 + round(random() * 9)) :: integer) FROM generate_series(1,12)), '');
 array_to_string
-----------------
 868778103681

Le deuxième argument à la generate_series La méthode dicte la longueur de la chaîne.

14
Marcin Raczkowski

Bien que non actif par défaut, vous pouvez activer l'une des extensions principales:

CREATE EXTENSION IF NOT EXISTS pgcrypto;

Votre déclaration devient alors un simple appel à gen_salt () qui génère une chaîne aléatoire:

select gen_salt('md5') from generate_series(1,4);

 gen_salt
-----------
$1$M.QRlF4U
$1$cv7bNJDM
$1$av34779p
$1$ZQkrCXHD

Le premier chiffre est un identifiant de hachage. Plusieurs algorithmes sont disponibles, chacun avec son propre identifiant:

  • md5: 1 $
  • bf: $ 2a $ 06 $
  • des: pas d'identifiant
  • xdes: _J9 ..

Plus d'informations sur les extensions:


[~ # ~] éditer [~ # ~]

Comme indiqué par Evan Carrol, à partir de la v9.4, vous pouvez utiliser gen_random_uuid()

http://www.postgresql.org/docs/9.4/static/pgcrypto.html

11
Jefferey Cave

Veuillez utiliser string_agg!

SELECT string_agg (substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', ceil (random() * 62)::integer, 1), '')
FROM   generate_series(1, 45);

J'utilise cela avec MD5 pour générer un UUID également. Je veux juste une valeur aléatoire avec plus de bits qu'un random () entier.

10
Andrew Wolfe

Je ne pense pas que vous cherchiez une chaîne aléatoire en soi. Ce dont vous auriez besoin pour la vérification de session est une chaîne qui est garantie d'être unique. Stockez-vous les informations de vérification de session pour l'audit? Dans ce cas, vous avez besoin que la chaîne soit unique entre les sessions. Je connais deux approches assez simples:

  1. Utilisez une séquence. Bon pour une utilisation sur une base de données unique.
  2. Utilisez un UUID. Universellement unique, donc excellent pour les environnements distribués.

Les UUID sont garantis d'être uniques en raison de leur algorithme de génération; effectivement il est extrêmement peu probable que vous généreriez deux nombres identiques sur une machine, à tout moment, jamais (notez que cela est beaucoup plus fort que sur des chaînes aléatoires, qui ont une périodicité beaucoup plus petite que les UUID).

Vous devez charger l'extension uuid-ossp pour utiliser les UUID. Une fois installé, appelez l’une des fonctions uuid_generate_vXXX () disponibles dans vos appels SELECT, INSERT ou UPDATE. Le type uuid est un nombre sur 16 octets, mais il possède également une représentation sous forme de chaîne.

6
Patrick

Le paramètre INTEGER définit la longueur de la chaîne. Garanti de couvrir les 62 caractères alphanum avec une probabilité égale (contrairement à certaines autres solutions flottant sur Internet).

CREATE OR REPLACE FUNCTION random_string(INTEGER)
RETURNS TEXT AS
$BODY$
SELECT array_to_string(
    ARRAY (
        SELECT substring(
            '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
            FROM (ceil(random()*62))::int FOR 1
        )
        FROM generate_series(1, $1)
    ), 
    ''
)
$BODY$
LANGUAGE sql VOLATILE;
4
Laryx Decidua

@Kavius ​​a recommandé d'utiliser pgcrypto , mais au lieu de gen_salt, qu'en est-il de gen_random_bytes? Et que diriez-vous sha512 au lieu de md5?

create extension if not exists pgcrypto;
select digest(gen_random_bytes(1024), 'sha512');

Docs:

F.25.5. Fonctions de données aléatoires

gen_random_bytes (count integer) renvoie le bytea

Renvoie le nombre d'octets aléatoires cryptographiquement forts. Au plus 1024 octets peuvent être extraits à la fois. Cela évite de vider le pool du générateur d’aléatoire.

4
Jared Beck

select * from md5(to_char(random(), '0.9999999999999999'));

4
user516487
select encode(decode(md5(random()::text), 'hex')||decode(md5(random()::text), 'hex'), 'base64')
1
user457226