支援的版本:目前 (17) / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

CREATE TYPE

CREATE TYPE — 定義新的資料類型

概要

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

CREATE TYPE name AS ENUM
    ( [ 'label' [, ... ] ] )

CREATE TYPE name AS RANGE (
    SUBTYPE = subtype
    [ , SUBTYPE_OPCLASS = subtype_operator_class ]
    [ , COLLATION = collation ]
    [ , CANONICAL = canonical_function ]
    [ , SUBTYPE_DIFF = subtype_diff_function ]
    [ , MULTIRANGE_TYPE_NAME = multirange_type_name ]
)

CREATE TYPE name (
    INPUT = input_function,
    OUTPUT = output_function
    [ , RECEIVE = receive_function ]
    [ , SEND = send_function ]
    [ , TYPMOD_IN = type_modifier_input_function ]
    [ , TYPMOD_OUT = type_modifier_output_function ]
    [ , ANALYZE = analyze_function ]
    [ , SUBSCRIPT = subscript_function ]
    [ , INTERNALLENGTH = { internallength | VARIABLE } ]
    [ , PASSEDBYVALUE ]
    [ , ALIGNMENT = alignment ]
    [ , STORAGE = storage ]
    [ , LIKE = like_type ]
    [ , CATEGORY = category ]
    [ , PREFERRED = preferred ]
    [ , DEFAULT = default ]
    [ , ELEMENT = element ]
    [ , DELIMITER = delimiter ]
    [ , COLLATABLE = collatable ]
)

CREATE TYPE name

描述

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

如果指定了綱要名稱,則該類型會在指定的綱要中建立。否則,它會在目前的綱要中建立。類型名稱必須與同一綱要中任何現有類型或網域的名稱不同。(因為資料表具有相關聯的資料類型,所以類型名稱也必須與同一綱要中任何現有資料表的名稱不同。)

如上面的語法概要所示,CREATE TYPE 有五種形式。它們分別建立一個複合類型、一個列舉類型、一個範圍類型、一個基本類型或一個殼類型。以下依次討論前四個。殼類型僅僅是用於稍後定義的類型之佔位符;它是透過發出不帶任何參數(除了類型名稱)的 CREATE TYPE 指令來建立的。在建立範圍類型和基本類型時需要殼類型作為前向參考,如那些章節中所述。

複合類型

CREATE TYPE 的第一種形式建立複合類型。複合類型由屬性名稱和資料類型清單指定。如果屬性的資料類型是可以定序的,也可以指定屬性的定序。複合類型本質上與資料表的列類型相同,但使用 CREATE TYPE 可避免在只需要定義類型時建立實際資料表的需求。例如,獨立的複合類型可用作函式的引數或傳回類型。

若要能夠建立複合類型,您必須擁有所有屬性類型的 USAGE 權限。

列舉類型

CREATE TYPE 的第二種形式建立一個列舉 (enum) 類型,如第 8.7 節中所述。列舉類型採用引號括起來的標籤清單,每個標籤的長度必須小於 NAMEDATALEN 個位元組(標準 PostgreSQL 組建中為 64 個位元組)。(可以建立具有零個標籤的列舉類型,但在使用 ALTER TYPE 新增至少一個標籤之前,此類類型無法用於儲存值。)

範圍類型

CREATE TYPE 的第三種形式建立一個新的範圍類型,如第 8.17 節中所述。

範圍類型的 subtype 可以是具有相關聯 b 樹運算子類別的任何類型(以確定範圍類型值的順序)。通常,子類型的預設 b 樹運算子類別用於確定順序;若要使用非預設運算子類別,請使用 subtype_opclass 指定其名稱。如果子類型是可以定序的,並且您想要在範圍的順序中使用非預設定序,請使用 collation 選項指定所需的定序。

可選的 canonical 函式必須採用所定義範圍類型的一個引數,並傳回相同類型的值。這用於在適用的情況下將範圍值轉換為標準形式。有關更多資訊,請參閱第 8.17.8 節。建立 canonical 函式有點棘手,因為它必須在宣告範圍類型之前定義。為此,您必須首先建立一個殼類型,這是一種佔位符類型,除了名稱和所有者之外沒有任何屬性。這是透過發出不帶額外參數的指令 CREATE TYPE name 來完成的。然後可以使用殼類型作為引數和結果來宣告函式,最後可以使用相同的名稱來宣告範圍類型。這會自動將殼類型條目替換為有效的範圍類型。

可選的 subtype_diff 函式必須採用 subtype 類型的兩個值作為引數,並傳回一個 double precision 值,表示給定兩個值之間的差異。雖然這是可選的,但提供它允許在範圍類型欄位上的 GiST 索引具有更高的效率。有關更多資訊,請參閱第 8.17.8 節

可選的 multirange_type_name 參數指定了對應的多重範圍類型名稱。如果未指定,則會按照以下方式自動選擇此名稱。如果範圍類型名稱包含子字串 range,則多重範圍類型名稱會將範圍類型名稱中的 range 子字串替換為 multirange 來形成。否則,多重範圍類型名稱會在範圍類型名稱後附加 _multirange 後綴來形成。

基礎類型

CREATE TYPE 的第四種形式會建立新的基礎類型(純量類型)。若要建立新的基礎類型,您必須是超級使用者。(做出此限制的原因是,錯誤的類型定義可能會混淆甚至崩潰伺服器。)

參數可以以任何順序出現,而不僅限於上面所示的順序,並且大多數參數都是可選的。在定義類型之前,您必須註冊兩個或多個函式(使用 CREATE FUNCTION)。支援函式 input_functionoutput_function 是必要的,而函式 receive_functionsend_functiontype_modifier_input_functiontype_modifier_output_functionanalyze_functionsubscript_function 則是可選的。通常,這些函式必須以 C 或其他低階語言編碼。

input_function 將類型的外部文字表示法轉換為該類型定義的運算子和函式所使用的內部表示法。output_function 執行相反的轉換。輸入函式可以宣告為採用一個 cstring 類型的參數,或採用三個 cstringoidinteger 類型的參數。第一個參數是作為 C 字串的輸入文字,第二個參數是類型本身的 OID(陣列類型除外,它會改為接收其元素類型的 OID),第三個參數是目標欄位的 typmod,如果已知(如果不知道,則會傳遞 -1)。輸入函式必須傳回資料類型本身的值。通常,輸入函式應宣告為 STRICT;如果不是,則在讀取 NULL 輸入值時,會使用 NULL 作為第一個參數來呼叫它。在這種情況下,該函式仍然必須傳回 NULL,除非它引發錯誤。(這種情況主要用於支援網域輸入函式,這些函式可能需要拒絕 NULL 輸入。)輸出函式必須宣告為採用新資料類型的一個參數。輸出函式必須傳回 cstring 類型。不會為 NULL 值呼叫輸出函式。

可選的 receive_function 將類型的外部二進位表示法轉換為內部表示法。如果未提供此函式,則該類型無法參與二進位輸入。應選擇二進位表示法,使其易於轉換為內部格式,同時具有合理的移植性。(例如,標準整數資料類型使用網路位元組順序作為外部二進位表示法,而內部表示法則為機器的本機位元組順序。)接收函式應執行足夠的檢查,以確保該值有效。接收函式可以宣告為採用一個 internal 類型的參數,或採用三個 internaloidinteger 類型的參數。第一個參數是指向包含收到的位元組字串的 StringInfo 緩衝區的指標;可選參數與文字輸入函式的參數相同。接收函式必須傳回資料類型本身的值。通常,接收函式應宣告為 STRICT;如果不是,則在讀取 NULL 輸入值時,會使用 NULL 作為第一個參數來呼叫它。在這種情況下,該函式仍然必須傳回 NULL,除非它引發錯誤。(這種情況主要用於支援網域接收函式,這些函式可能需要拒絕 NULL 輸入。)同樣地,可選的 send_function 從內部表示法轉換為外部二進位表示法。如果未提供此函式,則該類型無法參與二進位輸出。傳送函式必須宣告為採用新資料類型的一個參數。傳送函式必須傳回 bytea 類型。不會為 NULL 值呼叫傳送函式。

到目前為止,您應該會想知道輸入和輸出函式如何能夠宣告為具有新類型的結果或引數,而它們必須在新類型建立之前建立。答案是,該類型應首先定義為shell type,這是一種佔位符類型,除了名稱和所有者之外,沒有其他屬性。這是透過發出命令 CREATE TYPE name(沒有其他參數)來完成的。然後,可以定義引用 shell 類型的 C I/O 函式。最後,具有完整定義的 CREATE TYPE 會將 shell 項目替換為完整且有效的類型定義,之後可以正常使用新類型。

如果類型支援修飾符,則需要可選的 type_modifier_input_functiontype_modifier_output_function,修飾符是附加到類型宣告的可選限制,例如 char(5)numeric(30,2)PostgreSQL 允許使用者定義的類型將一個或多個簡單常數或識別符號作為修飾符。但是,此資訊必須能夠封裝到單個非負整數值中,以便儲存在系統目錄中。type_modifier_input_function 會以 cstring 陣列的形式傳遞宣告的修飾符。它必須檢查值的有效性(如果錯誤則拋出錯誤),如果它們正確,則傳回單個非負 integer 值,該值將儲存為欄位 typmod。如果該類型沒有 type_modifier_input_function,則類型修飾符將被拒絕。type_modifier_output_function 會將內部整數 typmod 值轉換回正確的格式以供使用者顯示。它必須傳回一個 cstring 值,該值是附加到類型名稱的確切字串;例如,numeric 的函式可能會傳回 (30,2)。允許省略 type_modifier_output_function,在這種情況下,預設顯示格式僅是括在括號中的儲存的 typmod 整數值。

可選的 analyze_function 針對資料型別的欄位執行特定型別的統計資訊收集。預設情況下,如果該型別有預設的 b-tree 運算子類別,ANALYZE 將嘗試使用該型別的「等於」和「小於」運算子來收集統計資訊。對於非純量型別,此行為可能不合適,因此可以透過指定自訂的分析函數來覆寫。分析函數必須宣告為採用單一 internal 型別的引數,並傳回 boolean 結果。分析函數的詳細 API 出現在 src/include/commands/vacuum.h 中。

可選的 subscript_function 允許資料型別在 SQL 命令中被下標。指定此函數並不會使該型別被視為「真實」陣列型別;例如,它不會是 ARRAY[] 結構的結果型別候選者。但是,如果對該型別的值進行下標是從中提取資料的自然表示法,則可以編寫 subscript_function 來定義其含義。下標函數必須宣告為採用單一 internal 型別的引數,並傳回 internal 結果,該結果是指向下標方法的結構(函數)的指標。下標函數的詳細 API 出現在 src/include/nodes/subscripting.h 中。閱讀 src/backend/utils/adt/arraysubs.c 中的陣列實作,或 contrib/hstore/hstore_subs.c 中更簡單的程式碼,可能也很有用。其他資訊顯示在下面的陣列型別中。

雖然新型別的內部表示法的詳細資訊僅為 I/O 函數和您建立的其他與該型別搭配使用的函數所知,但必須向 PostgreSQL 宣告內部表示法的幾個屬性。其中最重要的是 internallength。基本資料型別可以是固定長度,在這種情況下,internallength 是一個正整數,或可變長度,由將 internallength 設定為 VARIABLE 來指示。(在內部,這表示將 typlen 設定為 -1。)所有可變長度型別的內部表示法都必須以一個 4 位元組的整數開頭,該整數提供該型別值的總長度。(請注意,長度欄位通常是經過編碼的,如第 65.2 節中所述;直接存取它是不明智的。)

可選旗標 PASSEDBYVALUE 表示此資料型別的值是傳值而不是傳參考。傳值型別必須是固定長度,並且其內部表示法不能大於 Datum 型別的大小(在某些機器上為 4 個位元組,在其他機器上為 8 個位元組)。

alignment 參數指定資料型別所需的儲存體對齊方式。允許的值相當於在 1、2、4 或 8 位元組邊界上的對齊。請注意,可變長度型別必須至少具有 4 的對齊方式,因為它們必然包含一個 int4 作為其第一個元件。

storage 參數允許選擇可變長度資料型別的儲存策略。(固定長度型別僅允許 plain。)plain 指定該型別的資料將始終以內嵌方式儲存且不壓縮。extended 指定系統將首先嘗試壓縮長資料值,如果仍然太長,則將該值移出主表格列。external 允許將該值移出主表格,但系統不會嘗試壓縮它。main 允許壓縮,但不鼓勵將該值移出主表格。(如果沒有其他方法可以使列適合,則具有此儲存策略的資料項目仍然可能移出主表格,但它們將優先於 extendedexternal 項目保留在主表格中。)

除了 plain 之外的所有 storage 值都表示資料型別的函數可以處理已經被TOAST 的值,如第 65.2 節第 36.13.1 節中所述。給定的特定其他值僅決定了可 TOAST 資料型別的欄位的預設 TOAST 儲存策略;使用者可以使用 ALTER TABLE SET STORAGE 為個別欄位選擇其他策略。

like_type 參數提供了一種替代方法來指定資料型別的基本表示法屬性:從某些現有型別複製它們。internallengthpassedbyvaluealignmentstorage 的值是從指定的型別複製的。(可以,但通常不希望,透過與 LIKE 子句一起指定它們來覆寫其中一些值。)以這種方式指定表示法在新型別的低階實作以某種方式「依附」於現有型別時特別有用。

categorypreferred 參數可用於幫助控制在不明確的情況下將應用哪個隱式轉換。每個資料型別都屬於一個由單個 ASCII 字元命名的類別,並且每個型別在其類別中都是「首選」或不是。當此規則有助於解決多載函數或運算子時,剖析器將首選轉換為首選型別(但僅從同一類別中的其他型別轉換)。有關更多詳細資訊,請參閱第 10 章。對於沒有與任何其他型別進行隱式轉換的型別,將這些設定保留為預設值就足夠了。但是,對於具有隱式轉換的一組相關型別,通常有助於將它們全部標記為屬於一個類別,並選擇一個或兩個「最通用」的型別作為該類別中的首選型別。category 參數在將使用者定義的型別新增到現有的內建類別(例如數字或字串型別)時特別有用。但是,也可以建立全新的完全由使用者定義的型別類別。選擇任何不是大寫字母的 ASCII 字元來命名此類類別。

可以指定預設值,以防使用者希望資料型別的欄位預設為 null 值以外的值。使用 DEFAULT 關鍵字指定預設值。(此類預設值可以被附加到特定欄位的明確 DEFAULT 子句覆寫。)

要指示型別是固定長度的陣列型別,請使用 ELEMENT 關鍵字指定陣列元素的型別。例如,要定義一個 4 位元組整數 (int4) 的陣列,請指定 ELEMENT = int4。有關更多詳細資訊,請參閱下面的陣列型別

要指示在此型別的陣列的外部表示法中用於值之間的分隔符,可以將 delimiter 設定為特定字元。預設分隔符是逗號 (,)。請注意,分隔符與陣列元素型別關聯,而不是與陣列型別本身關聯。

如果可選的布林參數 collatable 為 true,則該型別的欄位定義和表示式可以透過使用 COLLATE 子句來攜帶定序資訊。實際上使用定序資訊取決於對該型別進行運算的函數的實作;僅透過將型別標記為可定序,這不會自動發生。

陣列類型

每當建立使用者定義類型時,PostgreSQL 會自動建立一個相關聯的陣列類型,其名稱由元素類型的名稱加上一個底線作為前綴組成,並在必要時截斷,以保持其長度小於 NAMEDATALEN 位元組。(如果產生的名稱與現有的類型名稱衝突,則會重複此過程,直到找到不衝突的名稱為止。)這個隱式建立的陣列類型是可變長度的,並使用內建的輸入和輸出函式 array_inarray_out。此外,系統會將此類型用於諸如使用者定義類型上的 ARRAY[] 之類的結構。陣列類型會追蹤其元素類型的所有者或綱要中的任何變更,如果元素類型被刪除,則該陣列類型也會被刪除。

您可能會合理地問,如果系統會自動建立正確的陣列類型,為什麼還要有 ELEMENT 選項。使用 ELEMENT 的主要情況是,當您建立一個固定長度的類型,而該類型在內部恰好是多個相同事物的陣列,並且除了您計劃為整個類型提供的任何操作之外,您還希望允許通過下標直接存取這些事物。例如,類型 point 僅表示為兩個浮點數,可以使用 point[0]point[1] 存取。請注意,此功能僅適用於固定長度類型,其內部形式恰好是一系列相同的固定長度欄位。由於歷史原因(也就是說,這顯然是錯誤的,但現在更改它為時已晚),固定長度陣列類型的下標從零開始,而不是像可變長度陣列那樣從一開始。

指定 SUBSCRIPT 選項允許對資料類型進行下標操作,即使系統不認為它是陣列類型。剛才描述的固定長度陣列的行為實際上是由 SUBSCRIPT 處理函式 raw_array_subscript_handler 實現的,如果您為固定長度類型指定 ELEMENT 但未編寫 SUBSCRIPT,則會自動使用該處理函式。

指定自訂的 SUBSCRIPT 函式時,除非 SUBSCRIPT 處理函式需要查詢 typelem 以找出要傳回的內容,否則不必指定 ELEMENT。請注意,指定 ELEMENT 會導致系統假定新類型包含元素類型,或者在物理上以某種方式依賴於元素類型;因此,例如,如果存在依賴類型的任何欄位,則不允許變更元素類型的屬性。

參數

name

要建立的類型名稱(可選擇使用綱要限定)。

attribute_name

複合類型的屬性(欄位)名稱。

data_type

要成為複合類型欄位的現有資料類型名稱。

collation

要與複合類型或範圍類型的欄位關聯的現有排序規則名稱。

label

表示與列舉類型的一個值相關聯的文字標籤的字串文字。

subtype

範圍類型將表示的元素類型名稱。

subtype_operator_class

子類型的 b-tree 運算子類別名稱。

canonical_function

範圍類型的正規化函式名稱。

subtype_diff_function

子類型的差異函式名稱。

multirange_type_name

相應的多範圍類型名稱。

input_function

將資料從類型的外部文字形式轉換為其內部形式的函式名稱。

output_function

將資料從類型的內部形式轉換為其外部文字形式的函式名稱。

receive_function

將資料從類型的外部二進位形式轉換為其內部形式的函式名稱。

send_function

將資料從類型的內部形式轉換為其外部二進位形式的函式名稱。

type_modifier_input_function

將類型的修飾詞陣列轉換為內部形式的函式名稱。

type_modifier_output_function

將類型的修飾詞的內部形式轉換為外部文字形式的函式名稱。

analyze_function

對資料類型執行統計分析的函式名稱。

subscript_function

定義資料類型的值的下標操作作用的函式名稱。

internallength

一個數值常數,指定新類型的內部表示形式的長度(以位元組為單位)。預設假設是它是可變長度的。

alignment

資料類型的儲存體對齊要求。如果指定,則必須是 charint2int4double;預設值為 int4

storage

資料類型的儲存策略。如果指定,則必須是 plainexternalextendedmain;預設值為 plain

like_type

現有資料類型的名稱,新類型將具有與之相同的表示形式。internallengthpassedbyvaluealignmentstorage 的值會從該類型複製,除非在此 CREATE TYPE 命令中的其他地方被明確指定覆蓋。

category

此類型的類別代碼(單個 ASCII 字元)。預設值為 使用者定義類型'U'。其他標準類別代碼可以在表 51.65中找到。您也可以選擇其他 ASCII 字元以建立自訂類別。

preferred

如果此類型在其類型類別中是首選類型,則為 True,否則為 false。預設值為 false。在現有的類型類別中建立新的首選類型時要非常小心,因為這可能會導致令人驚訝的行為變更。

default

資料類型的預設值。如果省略此值,則預設值為 null。

element

正在建立的類型是一個陣列;這指定了陣列元素的類型。

delimiter

在此類型陣列中的值之間使用的分隔符號。

collatable

如果此類型的操作可以使用排序規則資訊,則為 True。預設值為 false。

備註

由於資料類型一旦建立就沒有使用限制,因此建立基本類型或範圍類型相當於授予公眾對類型定義中提及的函式的執行權限。對於類型定義中有用的那些函式來說,這通常不是問題。但在以需要使用機密資訊的方式來設計類型時,在將其轉換為外部形式或從外部形式轉換過來時,您可能需要三思而後行。

PostgreSQL 8.3 版本之前,產生的陣列類型的名稱始終是元素類型的名稱,並以一個底線字元(_)作為前綴。(因此,類型名稱的長度限制為比其他名稱少一個字元。)雖然這通常仍然是這種情況,但在最大長度名稱或與以下劃線開頭的使用者類型名稱衝突的情況下,陣列類型名稱可能會有所不同。因此,不建議編寫依賴於此約定的程式碼。相反,請使用 pg_type.typarray 來尋找與給定類型相關聯的陣列類型。

建議避免使用底線開頭的類型和表名稱。雖然伺服器會修改產生的陣列類型名稱,以避免與使用者給定的名稱衝突,但仍然存在混淆的風險,特別是對於可能假設以底線開頭的類型名稱總是表示陣列的舊客戶端軟體而言。

PostgreSQL 8.2 版之前,shell 類型建立語法 CREATE TYPE name 並不存在。 建立新基本類型的方法是先建立其輸入函式。 在這種方法中,PostgreSQL 會首先將新資料類型的名稱視為輸入函式的傳回類型。 在這種情況下,會隱式建立 shell 類型,然後可以在其餘 I/O 函式的定義中引用它。 這種方法仍然有效,但已被棄用,並且在未來的版本中可能會被禁止。 此外,為了避免因函式定義中的簡單錯誤而意外地用 shell 類型混淆目錄,只有當輸入函式是用 C 語言編寫時,才會以這種方式建立 shell 類型。

PostgreSQL 16 及更高版本中,期望基本類型的輸入函數使用新的 errsave()/ereturn() 機制返回軟性錯誤,而不是像以前的版本那樣拋出 ereport() 異常。 有關更多信息,請參閱 src/backend/utils/fmgr/README

範例

此範例建立一個複合類型,並在函式定義中使用它

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

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

此範例建立一個列舉類型,並在表定義中使用它

CREATE TYPE bug_status AS ENUM ('new', 'open', 'closed');

CREATE TABLE bug (
    id serial,
    description text,
    status bug_status
);

此範例建立一個範圍類型

CREATE TYPE float8_range AS RANGE (subtype = float8, subtype_diff = float8mi);

此範例建立基本資料類型 box,然後在表定義中使用該類型

CREATE TYPE box;

CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;

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
);

更多範例,包括合適的輸入和輸出函式,請參閱第 36.13 節

相容性

CREATE TYPE 命令的第一種形式(建立複合類型)符合SQL標準。 其他形式是 PostgreSQL 的擴展。CREATE TYPE 語句在SQL標準中也定義了其他在 PostgreSQL 中未實作的形式。

建立具有零屬性的複合類型的能力是 PostgreSQL 特有的偏離標準的行為(類似於 CREATE TABLE 中的相同情況)。

提交更正

如果您在文件中發現任何不正確、與您使用特定功能的經驗不符或需要進一步說明的內容,請使用此表格來報告文件問題。