Wie genau funktioniert der Ein-Byte-Typ "char" in PostgreSQL?

9

Ich sehe oft Leute darüber reden "char". Ich habe es nie benutzt. Es ist in den Dokumenten definiert als:

Der Typ "char" (beachten Sie die Anführungszeichen) unterscheidet sich von char (1) darin, dass nur ein Byte Speicher verwendet wird. Es wird intern in den Systemkatalogen als vereinfachter Aufzählungstyp verwendet.

Und weiter,

"char"  1 byte  single-byte internal type

Also, wenn es ein Byte ist, was ist die Domain und wie würden Sie sie nutzen? Ist es signiert oder nicht signiert? In diesem Beitrag von @Erwin Brandstetter legt er es dar , aber ich bin immer noch verwirrt. Er benutzt ascii()und chr()und stellt dies zur Verfügung

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

Das macht etwas wirklich Seltsames zwischen 10 und 11.

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

Hier wird es auch richtig komisch:

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

Warum wird alles nördlich von 128 als 128 dekodiert? Aber um das Bizzare ein wenig zu verbessern, gibt es nach 192 einen Schalter und sie werden als 192 dekodiert.

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

Erwin sagt

Es gibt mehrere Zeichen, die nicht zur Anzeige bestimmt sind. Codieren Sie also, bevor Sie speichern, und dekodieren Sie, bevor Sie ...

Ich bin mir nicht sicher, warum wir überhaupt codieren sollten, wenn wir genau das tun, was diese Fragen verlangen

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

Das funktioniert gut. Wir können die Ints mit wieder rausholen

SELECT i::int FROM foo;

Kurz gesagt,

  1. Was macht Erwins Code zwischen 10 und 11, wo das i null wird?
  2. Warum wird 128 so oft wiederholt?
  3. Warum wird 192 so oft wiederholt?
  4. Wie löse ich die Unfähigkeit aus, 0 zu speichern, wenn Erwin sagt, dass Sie 0 auf diese Weise nicht codieren können (Nullzeichen nicht zulässig)?

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

Antworten:

11

1. chr(10)

... erzeugt das LINEFEED- Zeichen (auch als Escape-Sequenz bezeichnet \n) und psql zeigt das Zeichen mit einer neuen Zeile an (angezeigt durch +). Dort stimmt alles.

2. & 3. ascii()produziert 128 oder 192?

Es beginnt mit einem Fehler, den ich gemacht habe. Ich habe unachtsam angenommen, "char"dass der Bereich einer vorzeichenlosen 1-Byte-Ganzzahl (0 bis 255) in der referenzierten Antwort (jetzt festgelegt) abgedeckt wird , aber es ist tatsächlich der Bereich einer vorzeichenbehafteten 1-Byte-Ganzzahl (-128 bis 127) intern.

ascii()Nimmt ein textParameter, erzeugt die implizite Umwandlung von "char"bis textein Multibyte-codiertes Zeichen in Unicode, und die Funktion gibt zurück ( gemäß Dokumentation zuascii() ):

ASCII-Code des ersten Zeichens des Arguments. Für UTF8 wird der Unicode-Codepunkt des Zeichens zurückgegeben. Bei anderen Multibyte-Codierungen muss das Argument ein ASCII-Zeichen sein.

Wir erhalten also viele abgeschnittene Werte. 128 und 192 sind Bytewerte für das führende Byte von Multibyte-Zeichen.

4. Das Null-Byte

Die Unfähigkeit zur Speicherung von Null - Bytes wirkt sich nur auf regulären Zeichentypen ( text, char, varchar), nicht "char". Es gilt für mein Buggy-Beispiel, weil ich textals Sprungbrett gegossen habe . Beim Casting zwischen "char"und integerdirekt gilt die Einschränkung nicht. Das Handbuch zu chr():

Das Zeichen NULL (0) ist nicht zulässig, da Textdatentypen solche Bytes nicht speichern können.

Nicht so bei "char", wo 0die leere Zeichenfolge zugeordnet ist '':

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

Denken Sie daran: "char"ist immer noch ein "interner" Typ, der für eine einfache und kostengünstige Aufzählung vorgesehen ist. Nicht offiziell für das konzipiert, was wir hier tun, und nicht auf andere RDBMS portierbar. Hierfür gibt es keine Garantien des Postgres-Projekts.

Erwin Brandstetter
quelle
Ich denke immer noch, dass das Ergebnis der \ r-Anzeige in psqlein Fehler oder etwas Seltsames ist. Es endet die Zeile und überspringt dann eine Zeile?
Evan Carroll
4
@Evan nein, es wird keine Zeile übersprungen, die leere Zeile ist die Fortsetzung der vorherigen Zeile (die mehrzeilig ist). Wenn Sie psql dazu bringen könnten, horizontale Linien zwischen Ausgabezeilen zu zeichnen, wäre dies offensichtlicher, aber da Sie dies nicht können, ist der visuelle Hinweis das '+'.
Jack sagt, versuchen Sie es mit topanswers.xyz am
0

Um zum vorzeichenbehafteten Bereich zu wechseln, können Sie einige unterstützende Funktionen erstellen. Diese Liste erstellt Funktionen, die nicht umgewandelt werden , um diesen Prozess beim Übergang von einem vorzeichenlosen 1-Byte-Int-Bereich[0-255] zu einem vorzeichenbehafteten 1-Byte-Bereich, den das Zeichen benötigt, zu unterstützen[-128,127] .

Beispiel

Ein Auszug aus der README

Jetzt können Sie beispielsweise die Werte im Bereich von [0-255]in der Tabelle speichern .

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

Konvertieren Sie sie in bit(8)

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

Vielleicht möchten Sie die zwei Bits niedrigerer Ordnung löschen, das können Sie mit BITWISE-AND tun,

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)
Evan Carroll
quelle