2010-11-22 107 views
17

我試圖從SQL文件導入數據庫轉儲,並且在將字符串Mér插入定義爲varying(3)的字段時,插入失敗。我沒有捕獲確切的錯誤,但它指出具有varying(3)約束的具體值。Postgresql varchar使用unicode字符長度或ASCII字符長度計數嗎?

鑑於我認爲這不重要,我當時正在做什麼,我只是將其值改爲Mer,它的工作,我繼續前進。

是一個varying字段與其限制考慮到字節字符串的長度?我的腦海裏真是令人難以置信的是,這是從另一個PostgreSQL數據庫轉儲出來的。因此,約束如何允許最初寫入該值是沒有意義的。

+6

字符編碼是每個數據庫的東西。 PostgreSQL支持各種編碼,但對於給定的數據庫只能有一種編碼有效。也許你的源數據庫被設置爲與目標不同的編碼。 – Pointy 2010-11-22 20:14:44

回答

26

varchar(N)類型施加的長度限制和由length函數計算的長度限制是以字符而不是字節計算的。因此,'abcdef'::char(3)被截斷爲'abc',但即使在編碼爲UTF-8的數據庫的上下文中,'a€cdef'::char(3)也被截斷爲'a€c',其中'a€c'使用5個字節進行編碼。

如果還原轉儲文件抱怨'Mér'不會進入varchar(3)列,這表明您正在將UTF-8編碼的轉儲文件還原到SQL_ASCII數據庫中。

例如,我這樣做的UTF-8數據庫:

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

然後甩了這一點,並試圖將其加載到一個SQL_ASCII數據庫:

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

千真萬確:

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

相比之下,如果我創建數據庫enctest作爲編碼LATIN1或UTF8,它加載罰款。

此問題是由於將數據庫轉儲爲多字節字符編碼並試圖將其還原到SQL_ASCII數據庫中而產生的。使用SQL_ASCII基本上禁用客戶端數據到服務器數據的代碼轉換,並假定每個字符一個字節,並將其留給客戶端負責使用正確的字符映射。由於轉儲文件包含以UTF-8存儲的字符串,即四個字節,因此SQL_ASCII數據庫將其視爲四個字符,因此將其視爲違反約束。它打印出我的終端重新組裝爲三個字符的值。

4

這取決於您在創建數據庫時使用的值。 createdb -E UNICODE創建一個unicode數據庫,它也應該接受多字節字符並將它們作爲一個字符。

您可以使用

的psql -l

來查看使用了哪種編碼。 頁面http://www.postgresql.org/docs/8.4/interactive/multibyte.html有一個表格,其中包含每個字符使用多少個字節的信息。