web-dev-qa-db-fra.com

Inverser les octets d'un champ de bytea postgres

Je travaille actuellement sur une table contenant des hachages, stockée au format BYTEA. La conversion des hachages aux chaînes hexagonales donne cependant le mauvais ordre des octets. Exemple:

SELECT encode(hash, 'hex') FROM mytable LIMIT 1;

Output: 1a6ee4de86143e81
Expected: 813e1486dee46e1a

Existe-t-il un moyen d'inverser l'ordre des octets pour toutes les entrées?

6
R. Martin

Voici une méthode pour le faire, cependant Je ne ferais jamais cela. Il n'y a rien de mal à stocker des octets dans une base de données bytea . Mais, je ne me trompais pas dans la base de données, et si je le faisais, j'utilisais,

  • a fonction de langage c , ou
  • quelques langues procédurales de fantaisie qui n'avaient pas besoin d'exploser les intrants dans un ensemble d'octets.

C'est SQL-ESQ et devrait travailler - voici ce que nous faisons,

  1. Générer un ensemble consistant en une série de décalages 0 bytelgth-1).
  2. Planez les décalages des octets représentés comme des chaînes d'hex.
  3. String les agrégez dans l'ordre inverse.

Voici un exemple,

CREATE TABLE foo AS SELECT '\x813e1486dee46e1a'::bytea AS bar;

SELECT bar, string_agg(to_hex(byte), '') AS hash
FROM foo
CROSS JOIN LATERAL (
  SELECT get_byte(bar,"offset") AS byte
  FROM generate_series(0,octet_length(bar)-1) AS x("offset")
  ORDER BY "offset" DESC
) AS x
GROUP BY bar;

Deux notes,

  1. Nous ne pourrions probablement pas utiliser offset parce que --- (c'est réservé Mais vous obtenez le point.
  2. Cela suppose que votre hachage (bar dans ce qui précède) est unique.
5
Evan Carroll

Vous pouvez traiter une représentation codée en tant que texte et utiliser REGEXP pour inverser l'octet d'octet.

SELECT string_agg(reverse(b[1]),'')
FROM regexp_matches(reverse(encode('STUFF','hex')),'..','g')b;

Une autre méthode (plus verbose):

WITH bytes AS (
  SELECT row_number() over() AS n, byte[1]
  FROM regexp_matches( encode( 'STUFF', 'hex' ), '..', 'g' ) AS byte
), revbytes AS (
  SELECT * FROM bytes ORDER BY n DESC
)
SELECT array_to_string(array_agg(byte),'')
FROM revbytes;

Utilisation des échantillons:

(filip@[local:/var/run/postgresql]:5432) filip=# SELECT encode( 'STUFF', 'hex' );
   encode   
------------
 5354554646
(1 row)

(filip@[local:/var/run/postgresql]:5432) filip=# SELECT string_agg(reverse(b[1]),'')FROM regexp_matches(reverse(encode('STUFF','hex')),'..','g')b;
 string_agg 
------------
 4646555453
(1 row)
3
filiprem

Si vous avez besoin de renverser les octets de la valeur bytea, la valeur (relativement) simple et rapide utilise plpythonu :

create or replace function reverse_bytea(p_inp bytea) returns bytea stable language plpythonu as $$
  b = bytearray()
  b.extend(p_inp)
  b.reverse()
  return b
$$;

select encode(reverse_bytea('\x1a6ee4de86143e81'), 'hex');
----
813e1486dee46e1a

Cependant, je suppose que quelque chose ne va pas avec les données elles-mêmes (la voie de stockage, l'interprétation des données ...)

3
Abelisto

Solutions avec outils à Vanilla Postgres:

J'ai ajouté une colonne bytea_reverse Aux deux solutions. Supprimez-le si vous n'en avez pas besoin.

Avec get_byte() :

SELECT t.b, text_reverse, decode(text_reverse, 'hex') AS bytea_reverse
FROM   tbl t
LEFT   JOIN LATERAL (
   SELECT string_agg(to_hex(get_byte(b, x)), '') AS text_reverse
   FROM   generate_series(octet_length(t.b) - 1, 0, -1) x
   ) x ON true;

Ceci est similaire à ce que @ evan fourni . La majeure partie de son excellente explication s'applique. Mais:

  • Utilisez LEFT JOIN LATERAL ... ON true Ou vous perdez des lignes avec des valeurs null.
  • generate_series() peut fournir des chiffres en sens inverse, nous n'avons donc pas besoin d'un autre ORDER BY.
  • Tout en utilisant un LATERAL jointure, agrégat dans la sous-requête. Moins d'erreur sujette, plus facile à intégrer avec des requêtes plus complexes et pas besoin de GROUP BY Dans la requête extérieure.

Avec regexp_matches():

SELECT t.b, text_reverse, decode(text_reverse, 'hex') AS bytea_reverse
FROM   tbl t
LEFT   JOIN LATERAL (
   SELECT string_agg(byte[1], '' ORDER  BY ord DESC) AS text_reverse
   FROM   regexp_matches(encode(t.b, 'hex' ), '..', 'g' ) WITH ORDINALITY AS x(byte, ord)
   ) x ON true;

Ceci est similaire à la variante "Verbose" Variante @FiliPrem fournie . Mais:

  • Utilisez LEFT JOIN LATERAL ... ON true Ou vous perdez des lignes avec des valeurs null.
  • Utilisez WITH ORDINALITY Pour obtenir des numéros de ligne "gratuitement". Donc, nous n'avons pas besoin d'une autre sous-requête avec row_number() ni un double reverse(). Détails:
  • La commande inverse peut être effectuée dans la fonction d'agrégation. (Mais cela pourrait être un peu plus rapide de commander dans la sous-requête et ajoutez une autre couche de sous-requête à l'agrégation des lignes pré-commandées.)
  • Une sous-requête (ou deux) au lieu de deux CTE est généralement plus rapide.

Question similaire sur SO:

3
Erwin Brandstetter

Grâce à toutes les suggestions, j'ai écrit cette fonction c-linguistique qui fonctionne au besoin:

#include "postgres.h"
#include "fmgr.h"

#ifdef PG_MODULE_MAGIC
    PG_MODULE_MAGIC;
#endif

Datum bytea_custom_reverse(PG_FUNCTION_ARGS);

PG_FUNCTION_INFO_V1(bytea_custom_reverse);
Datum
bytea_custom_reverse(PG_FUNCTION_ARGS) {
  bytea *data = PG_GETARG_BYTEA_P_COPY(0);
  unsigned char *ptr = (unsigned char *) VARDATA(data);

  int32 dataLen = VARSIZE(data) - VARHDRSZ;

  unsigned char *start, *end;

  for ( start = ptr, end = ptr + dataLen - 1; start < end; ++start, --end ) {
    unsigned char swap = *start;
    *start = *end;
    *end = swap;
  }


  PG_RETURN_BYTEA_P(data);
}
1
R. Martin

Merci d'avoir aidé ce fil. Et ceci est mon choix de convertir Bigint en bytea en peu souvent en C # en utilisant BitConverter.getBytes () Selon les réponses:

with mycte as (
select int8send(394112768534335::bigint) as conversionValue
)
SELECT decode(string_agg (
  (case when get_byte(conversionValue, x)<= 15 then ('0')  else  '' end) ||
  to_hex(get_byte(conversionValue, x))
  , ''), 'hex') AS nativeId_reverse  
   FROM mycte,  generate_series(octet_length(conversionValue) - 1, 0, -1) as x;

Pour la valeur de recherche placée dans PostgreSQL comme Petitendian Bytea par IT BIBINT PRESENTATION:

with mycte as (
select int8send(394112768534335::bigint) as conversionValue
)
Select * FROM mycte, *SomeByteaFieldTable*
where *SomeByteaId* =                                                                         
(SELECT decode(string_agg (
  (case when get_byte(conversionValue, x)<= 15 then ('0')  else  '' end) ||
  to_hex(get_byte(conversionValue, x))
  , ''), 'hex') AS nativeId_reverse  
   FROM   generate_series(octet_length(conversionValue) - 1, 1, -1) x);   
0