Come funziona esattamente il tipo "char" a un byte in PostgreSQL?


9

Vedo spesso persone che parlano "char". Non l'ho mai usato. È definito nei documenti come

Il tipo "char" (notare le virgolette) è diverso da char (1) in quanto utilizza solo un byte di memoria. È utilizzato internamente nei cataloghi di sistema come tipo di enumerazione semplicistico.

E inoltre,

"char"  1 byte  single-byte internal type

Quindi, se è un byte, qual è il dominio e come lo useresti? È firmato o non firmato? In questo post di @Erwin Brandstetter lo espone , ma sono ancora confuso. Sta usando ascii()e chr()e fornisce questo

SELECT i
     , chr(i)::"char"        AS i_encoded
     , ascii(chr(i)::"char") AS i_decoded
FROM   generate_series(1,256) i;

Sta facendo qualcosa di veramente strano tra 10 e 11.

  i  | i_encoded | i_decoded 
-----+-----------+-----------
...
   8 | \x08      |         8
   9 |           |         9
  10 |          +|        10
     |           |           -- WTF is going on here.
  11 | \x0B      |        11
  12 | \x0C      |        12
...

Diventa anche molto strano qui:

 126 | ~         |       126
 127 | \x7F      |       127
 128 |           |       128
 129 |           |       128
 130 |           |       128
 131 |           |       128

Perché tutto il nord di 128 viene decodificato come 128? Ma per aumentare un po 'il bizzare, dopo 192 c'è un interruttore e vengono decodificati come 192 ..

 190 |           |       128
 191 |           |       128
 192 |           |       192
 193 |           |       192
 194 |           |       192
 195 |           |       192
 196 |           |       192
 197 |           |       192

Dice Erwin

Esistono diversi caratteri non destinati alla visualizzazione. Quindi codifica prima di archiviare e decodificare prima di visualizzare ...

Non sono sicuro del motivo per cui dovremmo codificare affatto se stiamo facendo esattamente quello che ci si pone

CREATE TABLE foo AS
SELECT i::"char"
FROM   generate_series(-128,127) i;

Funziona benissimo. Possiamo riavere indietro gli ints usando

SELECT i::int FROM foo;

Quindi in breve,

  1. Cosa sta facendo il codice di Erwin tra il 10 e l'11 in cui l'i diventa nullo?
  2. Perché 128 viene ripetuto così tante volte?
  3. Perché 192 è ripetuto così tante volte?
  4. Come posso innescare l'impossibilità di memorizzare 0, quando Erwin dice che non è possibile codificare 0 in questo modo (carattere null non consentito)

    CREATE TABLE foo AS SELECT 0::int::"char" AS x;
    SELECT x::int FROM foo;
     x 
    ---
    0

Risposte:


11

1. chr(10)

... produce il carattere LINEFEED (aka sequenza di escape \n) e psql visualizza il carattere con una nuova riga (indicata da +). Tutto corretto lì.

2. e 3. ascii()produce 128 o 192?

Inizia con un errore che ho fatto. I incautamente assunto "char"sarebbe coprire la gamma di un senza segno intero 1 byte (da 0 a 255) in risposta riferimento (ora fissa), ma è in realtà la portata di un firmata intero 1 byte (da -128 a 127) internamente.

ascii()accetta un textparametro, il cast implicito da "char"a textproduce un carattere con codifica multibyte in unicode e la funzione restituisce ( per documentazione suascii() ):

Codice ASCII del primo carattere dell'argomento. Per UTF8 restituisce il punto di codice Unicode del carattere. Per altre codifiche multibyte, l'argomento deve essere un carattere ASCII.

Quindi otteniamo molti valori troncati. 128 e 192 sono valori byte per il byte iniziale di caratteri multibyte.

4. Il byte null

L'incapacità di memorizzare byte nulli riguarda solo tipi di carattere regolari ( text, char, varchar), non "char". Si applica al mio esempio di buggy, perché ho scelto textcome trampolino di lancio. Durante il cast tra "char"e integerdirettamente, la limitazione non si applica. Il manuale su chr():

Il carattere NULL (0) non è consentito poiché i tipi di dati di testo non possono memorizzare tali byte.

Non così per "char", dove 0è mappato sulla stringa vuota '':

SELECT ''::"char"::int  -- 0
     , 0::"char" = '';  -- t

Ricorda: "char"è ancora un tipo "interno" destinato all'enumerazione semplice ed economica. Non progettato ufficialmente per quello che stiamo facendo qui, e non portatile per altri RDBMS. Per questo il progetto Postgres non ha garanzie.


Penso ancora che il risultato della visualizzazione psqlsia un bug o qualcosa di strano. Termina la linea e quindi salta una linea?
Evan Carroll

4
@Evan no non "salta una riga", la riga vuota è la continuazione della riga precedente (che è multilinea). Se si potesse ottenere psql per disegnare linee orizzontali tra le righe di output, questo sarebbe più ovvio, ma poiché non è possibile l'indizio visivo è il '+'.
Jack dice di provare topanswers.xyz il

0

Per eseguire il passaggio all'intervallo firmato, è possibile creare alcune funzioni per facilitare l'assistenza. Questo elenco creerà funzioni non cast per aiutare in questo processo di passare da un intervallo int di[0-255] un byte [-128,127]senza segno a un intervallo di un byte con segno di cui richiede il carattere .

Esempio

Un estratto dal README

Ora puoi ad esempio archiviare i valori nell'intervallo di [0-255]sulla tabella.

CREATE TABLE t(x) AS VALUES
  (to_uchar(255)),
  (to_uchar(0));

Convertili in bit(8)

SELECT to_bit8(x) FROM t;
 to_bit8  
----------
 11111111
 00000000
(2 rows)

Forse vuoi cancellare i due bit di ordine inferiore, puoi farlo con BITWISE-AND,

UPDATE t
  SET x = to_uchar( to_bit8(x) & (x'fc')::bit(8) );

SELECT to_bit8(x) FROM t;
 to_bit8  
----------
 11111100
 00000000
(2 rows)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.