CREATE TYPE

Name

CREATE TYPE -- 定義一個新的資料類型

Synopsis

CREATE TYPE name AS
    ( attribute_name data_type [, ... ] )

CREATE TYPE name (
    INPUT = input_function,
    OUTPUT = output_function
    [ , RECEIVE = receive_function ]
    [ , SEND = send_function ]
    [ , ANALYZE = analyze_function ]
    [ , INTERNALLENGTH = { internallength | VARIABLE } ]
    [ , PASSEDBYVALUE ]
    [ , ALIGNMENT = alignment ]
    [ , STORAGE = storage ]
    [ , DEFAULT = default ]
    [ , ELEMENT = element ]
    [ , DELIMITER = delimiter ]
)

描述

CREATE TYPE 為目前資料庫註冊一個新的資料類型。 定義該類型的用戶成為其所有者。

如果給出模式名,那麼該類型是在指定模式中建立。 否則它是在目前模式中建立。類型名必需和同一模式中任何現有的類型或者域不同。 (因為資料表和資料類型有聯繫,類型名不能和同模式中的資料表名字衝突。)

復合類型

第一種形式的 CREATE TYPE 建立一個復合類型。 復合類型是透過一列屬性名和資料類型聲明的。這樣實際上和一個資料表的行類型一樣, 但是如果我們只是想定義一個類型,那麼使用 CREATE TYPE 避免了直接建立實際的資料表。 一個獨立的復合類型做一個函數的參數或者返回類型是非常有用的。

基本類型

第二種形式的CREATE TYPE建立一種新的基本類型(標量類型)。 參數可以以任意的順序出現,而不是上面顯示的那樣。並且大多數都是可選的。 它要求要在定義類型之前先註冊兩個函數(用CREATE FUNCTION命令)。 支援函數 input_functionoutput_function 是必須的, 而函數 receive_functionsend_functionanalyze_function 是可選的。 通常,這些函數必須用 C 或者其它低層語言編寫。

函數 input_function 將該類型的外部文本形式轉換成可以被對該類型操作的操作符和函數識別的內部形式。 output_function 用做相反用途。 輸入函數可以聲明為接受一個類型為 c_string 的參數,或者接受三個類型分別為 c_stringoidinteger 的參數。 第一個參數是 C 字串形式的輸入文本,第二個是在該類型為數組類型時其元素的類型 OID, (如果類型是復合類型,就是自己的 OID) 第三個是目標字串的typmod,如果已知的話(如果不是,將傳遞 -1)。 它應該返回一個該資料類型本身的數值。 輸出函數可以聲明為接受一個類型為新資料類型的參數, 或者接受兩個類型,第二個參數的類型是 oid。 第二個參數也是用於數組類型的數組元素類型 OID 或者是復合類型自己的 OID。輸出函數應該返回類型 cstring

可選的 receive_function 把該類型的外部二進製資料表現形式轉換成內部資料表現形式。 如果沒有提供這個函數,那麼該類型不能用二進制輸入。二進制格式應該選取那種轉換成內部格式比較容易的,同時還有一定移植性的。 (比如,標準的整數資料類型使用網絡字元序作為外部的二進製資料表現形式,而內部資料表現形式是機器的本機字元序。) 接收函數應該聲明為接受一個類型為 internal 的參數,或者兩個類型分別為 internaloid 的參數。 它必須返回一個資料類型自身的數值。(第一個參數是一個指向一個 StringInfo 緩衝區的,保存接受字元串的指針; 可選的第二個參數是元素類型 OID ——如果類型是一個數組類型的話,如果是復合類型,則是類型自己的 OID。) 類似的,可選的 send_function 把類型轉換為外部二進製資料表現形式。 如果沒有提供這些函數,那麼類型就不能用二進制方式輸出。發送函數可以聲明為接收一個新資料類型, 或者接收兩個參數,第二個參數的類型是 oid。第二個參數仍然是數組類型的數組元素 OID 或者復合類型裡該類型自己的OID。 發送函數必須返回 bytea

這個時候您應該覺得奇怪,就是輸入和輸出函數怎麼可以聲明為返回新類型的結果或者是接受新類型的參數, 而且是在新類型建立之前就需要建立它們。 答案是輸入函數必須先建立,然後是輸出函數,最後是資料類型。 PostgreSQL 將首先把新資料類型的名字看作輸入函數的返回類型。 它將建立一個"殼"類型,這個類型只是在 pg_type裡面的一個佔位符,然後把輸入函數定義和這個殼類型連接起來。 類似的是輸出函數將連接到(現在已經存在)的殼類型。最後, CREATE TYPE 把這個殼類型替換成完整的類型定義,這樣就可以使用新類型了。

可選的 analyze_function 為該資料類型的字串執行與該類型相關的統計訊息手機。 預設的時候,如果該類型有個預設的 b-tree 操作符資料表,那麼ANALYZE 將視圖使用該類型的"等於""小於"操作符。 對於非標量類型,這種行為很可能不合適,因此我們可以透過提供一個客戶化的分析函數覆蓋它。 分析函數必須聲明為接收一個類型為 internal 的一個參數,並且返回一個 boolean 值。 分析函數的詳細 API 在 src/include/commands/vacuum.h 裡。

儘管新類型的內部資料表現形式只有 I/O 函數和其它您建立來使用該類型的函數瞭解, 但內部資料表現還是有幾個屬性必須為 PostgreSQL 聲明。 這些中最重要的是 internallength。 基本資料類型可定義成為定長,這時 internallength 是一個正整數,也可以是變長的,透過把 internallength 設置為 VARIABLE 資料表示。(在內部,這個狀態 是透過將typlen設置為 -1 實現的。)所有變長類型的內部形式都必須以一個四字元整數開頭,這個整數給出此類型這個數值的全長。

可選的標記 PASSEDBYVALUE 資料表明該類型的數值是用值傳遞的, 而不是用引用。您不能傳遞那些內部形式大於 Datum (大多數機器上是 4 字元,有些是 8 字元)類型的尺寸的資料類型的值。

alignment 參數聲明該資料類型要求的對齊儲存方式。 允許的數值等效於按照 1,2,4,或者 8 字元邊界對齊。請注意變長類型必須有至少 4 字元的對齊, 因為它們必須包含一個 int4 作為它們的第一個成份。

storage 參數允許為變長資料類型選擇儲存策略。 (定長類型只允許使用 plain)。 plain 聲明該資料類型總是用內聯的方式而不是壓縮的方式儲存。 extended 聲明系統將首先試圖壓縮一個長的資料值,然後如果它仍然太長的話就將它的值移出主資料表的行, 但系統將不會壓縮它。 main 允許壓縮,但是不贊成把數值移動出主資料表。 (用這種儲存策略的資料項可能仍將移動出主資料表,如果不能放在一行裡的話, 但是它們將比 extendedexternal 項更願意呆在主資料表裡。)

如果用戶希望字串的資料類型預設時不是 NULL,而是其它什麼東西, 那麼您可以聲明一個預設值。 在 DEFAULT 關鍵字裡面聲明預設值。 (這樣的預設可以被附著在特定字串上的明確的 DEFAULT 子句覆蓋。)

要資料表示一個類型是數組,用 ELEMENT 關鍵字聲明數組元素的類型。 比如,要定義一個 4 字元整數(int4)的數組,聲明

ELEMENT = int4

。 有關數組類型的更多細節在下面描述。

要聲明用於這種類型數組的外部形式的數值之間的分隔符,可用 delimiter 聲明指定分隔符。預設的分隔符是逗號(,)。 請注意分隔符是和數組元素類型相關聯,而不是數組類型本身。

數組類型

在建立用戶定義資料類型的時候,PostgreSQL 自動建立一個與之關聯的數組類型,其名字由該基本類型的名字前綴一個下劃線組成。 分析器理解這個命名傳統,並且把對類型為 foo[] 的字串的請求轉換成對類型為 _foo 的字串的請求。這個隱含建立的數組類型是變長並且使用內建的輸入和輸出函數 array_inarray_out

您很可能會問如果系統自動製作正確的數組類型,那為什麼有個 ELEMENT選項?使用 ELEMENT 有用的唯一的場合是在您製作的定長類型碰巧在內部是一個一定數目相同事物的數組, 而您又想允許這 N 個事物可以透過腳標直接關聯,以及那些您準備把該類型當做整體進行的操作。 比如,類型 name 就允許其構成 char 用這種方法關聯。 一個二維的 point 類型也可以允許其兩個構成浮點型按照類似 point[0]point[1] 的方法關聯。 請注意這個功能只適用與那些內部形式是一個相同的定長域的序列的類型。 一個可以腳標化的變長類型必須有被 array_inarray_out 使用的一般化的內部資料表現形式。 出於歷史原因(也就是說,那些明顯錯誤但補救來得太遲的問題),定長數組類型的腳標從零開始,而不是象變長類型那樣的從一開始。

參數

name

將要建立的類型名(可以有模式修飾)。

attribute_name

復合類型的一個屬性(字串)的名字。

data_type

一個要成為一個復合類型的字串的現有資料類型的名字。

input_function

一個函數的名稱, 將資料從外部類型轉換成內部類型。

output_function

一個函數的名稱, 將資料從內部格式轉換成適於顯示的形式。

receive_function

把資料從類型的外部二進制形式轉換成其內部形式的函數的名字。

send_function

把資料從類型的內部形式轉換成其外部二進制形式的函數名。

analyze_function

為該資料類型執行統計分析的函數名。

internallength

一個數值常量,說明新類型的內部資料表現形式的長度。預設的假設是它是變長的。

alignment

該資料類型的儲存對齊要求。如果聲明了,必須是 charint2int4double; 預設是 int4

storage

該資料類型的儲存策略。如果聲明了,必須是 plainexternalextended,或 main; 預設是 plain

default

該類型的預設值。通常是省略它的,所以預設是 NULL。

element

被建立的類型是數組;這個聲明數組元素的類型。

delimiter

將用做數組的資料元素之間分隔符的字元。

注意

用戶定義類型名不能以下劃線(_) 開頭而且只能有 62 個字元長。(或者通常是 NAMEDATALEN-2, 而不是其它名字那樣的可以有 NAMEDATALEN-1 個字元)。 以下劃線開頭的類型名被解析成內部建立的數組類型名。

PostgreSQL 版本 7.3 以前,我們要透過使用佔位偽類型 opaque 代替函數的前向引用來避免建立殼類型。 7.3 之前 cstring 參數和結果同樣需要聲明偽 opaque。 要支援裝載舊的轉儲外那間,CREATE TYPE 將接受那些用 opaque聲明的函數, 但是它回發出一條通知並且用正確的類型改變函數的聲明。

例子

這個例子建立一個復合類型並且在一個函數定義中使用它:

CREATE TYPE compfoo AS (f1 int, f2 text);

CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS $$
  SELECT fooid, fooname FROM foo
$$ LANGUAGE SQL;

這個命令建立box資料類型,並且將這種類型用於一個資料表定義:

CREATE TYPE box (
    INTERNALLENGTH = 16,
    INPUT = my_box_in_function,
    OUTPUT = my_box_out_function
);

CREATE TABLE myboxes (
    id integer,
    description box
);

如果 box 的內部結構是一個四個 float4 的數組,我們可以說

CREATE TYPE box (
    INTERNALLENGTH = 16,
    INPUT = my_box_in_function,
    OUTPUT = my_box_out_function,
    ELEMENT = float4
);

它允許一個 box 的數值成分成員可以用腳標訪問。 否則該類型和前面的行為一樣。

這條命令建立一個大對像類型並將其用於一個資料表定義:

CREATE TYPE bigobj (
    INPUT = lo_filein, OUTPUT = lo_fileout,
    INTERNALLENGTH = VARIABLE
);
CREATE TABLE big_objs (
    id integer,
    obj bigobj
);

更多的例子,包括合適的輸入和輸出函數,在 Section 31.11

相容性

CREATE TYPE 命令是 PostgreSQL 擴展。在 SQL:1999 裡有一個 CREATE TYPE 語句,但是細節上和 PostgreSQL 的有比較大區別。

又見

CREATE FUNCTION, DROP TYPE, ALTER TYPE