PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Postgresql varchar conta usando la lunghezza dei caratteri Unicode o la lunghezza dei caratteri ASCII?

Il limite di lunghezza imposto da varchar(N) tipi e calcolati dalla length la funzione è in caratteri, non in byte. Quindi 'abcdef'::char(3) viene troncato in 'abc' ma 'a€cdef'::char(3) viene troncato in 'a€c' , anche nel contesto di un database codificato come UTF-8, dove 'a€c' è codificato utilizzando 5 byte.

Se il ripristino di un file dump si è lamentato che 'Mér' non entrerebbe in un varchar(3) colonna, che suggerisce che stavi ripristinando un file di dump codificato UTF-8 in un database SQL_ASCII.

Ad esempio, l'ho fatto in un database UTF-8:

create schema so4249745;
create table so4249745.t(key varchar(3) primary key);
insert into so4249745.t values('Mér');

E poi l'ho scaricato e ho provato a caricarlo in un database SQL_ASCII:

pg_dump -f dump.sql --schema=so4249745 --table=t
createdb -E SQL_ASCII -T template0 enctest
psql -f dump.sql enctest

E abbastanza sicuro:

psql:dump.sql:34: ERROR:  value too long for type character varying(3)
CONTEXT:  COPY t, line 1, column key: "Mér"

Al contrario, se creo il database enctest come codifica LATIN1 o UTF8, viene caricato correttamente.

Questo problema si verifica a causa di una combinazione di dumping di un database con una codifica di caratteri multibyte e del tentativo di ripristinarlo in un database SQL_ASCII. L'utilizzo di SQL_ASCII disabilita sostanzialmente la transcodifica dei dati del client in dati del server e assume un byte per carattere, lasciando ai client la responsabilità dell'utilizzo della corretta mappa dei caratteri. Poiché il file di dump contiene la stringa archiviata come UTF-8, ovvero quattro byte, un database SQL_ASCII lo vede come quattro caratteri e quindi lo considera una violazione del vincolo. E stampa il valore, che poi il mio terminale riassembla come tre caratteri.