¿Cómo funciona exactamente el tipo "char" de un byte en PostgreSQL?


9

A menudo veo gente hablando "char". Nunca lo he usado. Se define en los documentos como,

El tipo "char" (tenga en cuenta las comillas) es diferente de char (1) en que solo usa un byte de almacenamiento. Se utiliza internamente en los catálogos del sistema como un tipo de enumeración simplista.

Y además,

"char"  1 byte  single-byte internal type

Entonces, si es un byte, ¿cuál es el dominio y cómo lo utilizarías? ¿Está firmado o sin firmar? En esta publicación de @Erwin Brandstetter lo expone , pero todavía estoy confundido. Él está usando ascii()y chr(), y proporciona esto

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

Eso está haciendo algo realmente extraño entre 10 y 11.

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

También se pone realmente extraño aquí:

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

¿Por qué se decodifica todo al norte de 128 como 128? Pero para llevar el bizzare un poco, después de 192 hay un interruptor y se decodifican como 192 ..

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

Erwin dice

Hay varios caracteres que no se deben mostrar. Así que codifique antes de almacenar y decodifique antes de mostrar ...

Sin embargo, no estoy seguro de por qué deberíamos codificar si estamos haciendo exactamente lo que esa pregunta plantea

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

Eso funciona bien Podemos recuperar las entradas usando

SELECT i::int FROM foo;

En resumen,

  1. ¿Qué está haciendo el código de Erwin entre el 10 y el 11 cuando el i se anula?
  2. ¿Por qué se repiten 128 tantas veces?
  3. ¿Por qué se repite 192 tantas veces?
  4. ¿Cómo puedo activar la imposibilidad de almacenar 0, cuando Erwin dice que no puede codificar 0 de esta manera (no se permite el carácter nulo)

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

Respuestas:


11

1) chr(10)

... produce el carácter LINEFEED (también conocido como secuencia de escape \n) y psql muestra el carácter con una nueva línea (indicada por +). Todo correcto allí.

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

Comienza con un error que cometí. Yo asumí descuidadamente "char"cubriría el rango de un sin signo de 1 byte entero (0 a 255) en la respuesta referencia (ahora fija), pero es en realidad el alcance de una firmada número entero de 1 byte (-128 a 127) internamente.

ascii()toma un textparámetro, la conversión implícita de "char"a textproduce un carácter codificado en multibyte en unicode, y la función devuelve ( según la documentación enascii() ):

Código ASCII del primer carácter del argumento. Para UTF8 devuelve el punto de código Unicode del personaje. Para otras codificaciones multibyte, el argumento debe ser un carácter ASCII.

Entonces obtenemos muchos valores truncados. 128 y 192 son valores de byte para el byte inicial de caracteres multibyte.

4. El byte nulo

La incapacidad para almacenar bytes nulos sólo afecta a los tipos de caracteres regulares ( text, char, varchar), no "char". Se aplica a mi ejemplo con errores, porque lo lanzo textcomo trampolín. Mientras se lanza entre "char"y integerdirectamente, la limitación no se aplica. El manual sobre chr():

El carácter NULL (0) no está permitido porque los tipos de datos de texto no pueden almacenar dichos bytes.

No es así para "char", donde 0se asigna a la cadena vacía '':

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

Recuerde: "char"sigue siendo un tipo "interno" destinado a una enumeración simple y económica. No está oficialmente diseñado para lo que estamos haciendo aquí, y no es portátil para otros RDBMS. No hay garantías del proyecto Postgres para esto.


Todavía creo que el resultado de la visualización en \ r psqles un error o algo extraño. Se termina de la línea, y luego se salta una línea?
Evan Carroll

44
@Evan no, no 'salta una línea', la línea en blanco es la continuación de la fila anterior (que es multilínea). Si pudiera hacer que psql dibujara líneas horizontales entre las filas de salida, esto sería más obvio, pero debido a que no puede, la pista visual es el '+'.
Jack dice que intente topanswers.xyz

0

Para hacer el cambio al rango firmado, puede crear algunas funciones para ayudar a ayudar. Esta lista creará funciones que no se convertirán para ayudar en este proceso de pasar de un rango int de[0-255] un byte sin signo a un rango de un byte con signo que requiere el carácter[-128,127] .

Ejemplo

Un extracto de README

Ahora puede hacer, por ejemplo, almacenar los valores en el rango de [0-255]en la tabla.

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

Conviértalos a bit(8)

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

Tal vez desee borrar los dos bits de orden inferior, puede hacerlo 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)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.