8.11. Tipos compostos

PostgreSQL 14.5: Tipos compostos

O tipo composto descreve a estrutura de uma linha ou registro; essencialmente, é apenas uma lista de nomes de campos com seus tipos de dado. O PostgreSQL permite que os valores de tipo composto sejam utilizados de muitas maneiras idênticas às que os tipos simples podem ser ser utilizados. Por exemplo, uma coluna de uma tabela pode ser declarada como sendo de um tipo composto.

8.11.1. Declaração de tipos compostos

Abaixo seguem dois exemplos simples definindo tipos compostos:

CREATE TYPE complexo AS (
    r       double precision,
    i       double precision
);

CREATE TYPE catalogo AS (
    nome            text,
    id_fornecedor   integer,
    preco           numeric
);

A sintaxe pode ser comparada a do comando CREATE TABLE, exceto que somente podem ser especificados os nomes e tipos dos campos; atualmente não pode ser incluída nenhuma restrição (como NOT NULL). Deve ser observado que a palavra chave AS é essencial; sem esta, o sistema imagina que está lidando com um tipo bem diferente de comando CREATE TYPE, e mostra erros de sintaxe bem estranhos.

Após definir os tipos, estes podem ser utilizados para criar tabelas:

CREATE TABLE estoque (
    item     catalogo,
    contador integer
);

INSERT INTO estoque VALUES (ROW('dados de pano', 42, 1.99), 1000);

ou funções:

CREATE FUNCTION preco_quantidade(catalogo, integer) RETURNS numeric
AS 'SELECT $1.preco * $2' LANGUAGE SQL;

SELECT preco_quantidade(item, 10) FROM estoque;

Sempre que uma tabela é criada também é criado, automaticamente, um tipo composto com o mesmo nome da tabela para representar o tipo linha da tabela. [1] Por exemplo, se tivéssemos declarado

CREATE TABLE catalogo (
    nome            text,
    id_fornecedor   integer REFERENCES fornecedores,
    preco           numeric CHECK (preco > 0)
);

teria sido criado como subproduto o mesmo tipo composto catalogo mostrado acima, podendo ser utilizado conforme mostrado anteriormente. Entretanto, deve ser observada uma restrição importante da implementação corrente: uma vez que não há nenhuma restrição associada ao tipo composto, a restrição mostrada na definição da tabela não se aplica aos valores do tipo composto fora da tabela (Uma forma parcial de evitar este problema é utilizar tipos domínios como membros dos tipos compostos).

8.11.2. Entrada de valor composto

Para escrever um valor composto como uma constante literal, os valores do campo devem ser envoltos por parênteses e separados por vírgulas. Podem ser colocadas aspas em torno de qualquer valor do campo, sendo obrigatório se o valor contiver vírgulas ou parênteses (Abaixo são mostrados mais detalhes). Portanto, o formato geral de uma constante composta é o seguinte:

'( val1 , val2 , ... )'

Por exemplo,

'("dados de pano",42,1.99)'

é um valor válido para o tipo catalogo definido acima. Para tornar o campo nulo, não deve ser escrito nenhum caractere na sua posição na lista. Por exemplo, esta constante especifica um terceiro campo nulo:

'("dados de pano",42,)'

Se, em vez de nulo, for desejada uma cadeia de caracteres vazia, devem ser escritas duas aspas:

'("",42,)'

Neste caso, o primeiro campo é uma cadeia de caracteres vazia não-nula, e o terceiro campo é nulo.

(Estas constantes são, na verdade, apenas um caso especial do tipo genérico de constantes mostrado na Seção 4.1.2.5. Inicialmente, a constante é tratada como uma cadeia de caracteres e passada para a rotina de conversão de entrada de tipo composto (Pode ser necessária uma especificação explícita do tipo).

Também pode ser utilizada a sintaxe da expressão ROW para construir valores compostos. Na maioria dos casos, esta sintaxe é bem mais simples que a sintaxe do literal cadeia de caracteres, uma vez que não é necessário se preocupar com várias camadas de aspas. Este método já foi utilizado acima:

ROW('dados de pano', 42, 1.99)
ROW('', 42, NULL)

Desde que haja mais de um campo na expressão, a palavra chave ROW se torna opcional, permitindo simplificar como:

('dados de pano', 42, 1.99)
('', 42, NULL)

A sintaxe da expressão ROW é mostrada com mais detalhes na Seção 4.2.11.

8.11.3. Acesso aos tipos compostos

Para acessar um campo de uma coluna composta deve ser escrito um ponto e o nome do campo, como se faz ao selecionar um campo de uma tabela. Na verdade, é tão parecido com selecionar um campo de uma tabela que, geralmente, é necessário utilizar parênteses para não confundir o analisador. Por exemplo, selecionar alguns subcampos da tabela exemplo estoque usando algo como

SELECT item.nome FROM estoque WHERE item.preco > 9.99;

não funciona, porque o nome item é assumido como sendo o nome da tabela, e não o nome do campo, pelas regras de sintaxe do SQL. Devendo, então, ser escrito como mostrado abaixo

SELECT (item).nome FROM estoque WHERE (item).preco > 9.99;

ou desta forma, se também for necessário utilizar o nome da tabela (por exemplo, numa consulta com várias tabela):

SELECT (estoque.item).nome FROM estoque WHERE (estoque.item).preco > 9.99;

Agora, como o objeto entre parênteses é interpretado corretamente como uma referência à coluna item, é possível selecionar um subcampo da mesma.

Ocorrem problemas de sintaxe semelhantes sempre que é selecionado um campo de um valor composto. Por exemplo, para selecionar apenas um campo do resultado de uma função que retorna um valor composto, é necessário escrever algo como:

SELECT (minha_funcao(...)).campo FROM ...

Sem os parênteses extra, provoca um erro de sintaxe.

8.11.4. Modificação de tipos compostos

Abaixo estão mostrados alguns exemplos da sintaxe apropriada para inserir e atualizar colunas compostas. Primeiro, são inseridas e atualizadas colunas inteiras:

INSERT INTO minha_tabela (coluna_complexa) VALUES((1.1,2.2));

UPDATE minha_tabela SET coluna_complexa = ROW(1.1,2.2) WHERE ...;

O primeiro exemplo omite ROW, enquanto o segundo exemplo não; pode ser feito de qualquer uma destas maneiras.

Os subcampos de uma coluna composta podem ser atualizados individualmente:

UPDATE minha_tabela SET coluna_complexa.r = (coluna_complexa).r + 1 WHERE ...;

Deve ser observado que não é necessário (e, na verdade, não se pode) colocar parênteses em torno do nome da coluna que aparece logo após a cláusula SET, mas são necessários parênteses ao se fazer referência à mesma coluna na expressão à direita do sinal de igual.

Também podem ser especificados subcampos como destino do INSERT:

INSERT INTO minha_tabela (coluna_complexa.r, coluna_complexa.i) VALUES(1.1, 2.2);

Caso não tivéssemos fornecido valores para todos os subcampos da coluna, os demais subcampos seriam preenchidos com o valor nulo.

8.11.5. Sintaxe de entrada e saída dos tipos compostos

A representação textual externa do valor composto é formada por itens que são interpretados de acordo com as regras individuais de conversão de entrada e saída do tipo de dado do campo, mais os adornos que indicam a estrutura composta. Os adornos são formados por parênteses (( e )) em torno de todo o valor, mais vírgulas (,) entre itens adjacentes. Os espaços em branco fora dos parênteses são ignorados, mas dentro dos parênteses são considerados parte do valor do campo, podendo ou não serem significativos dependendo das regras de conversão de entrada para o tipo de dado do campo. Por exemplo, em

'(  42)'

o espaço em branco é ignorado se o tipo do campo for inteiro, mas não é ignorado se o tipo do campo for texto.

Como mostrado anteriormente, ao se escrever um valor composto podem ser escritas aspas envolvendo qualquer valor individual de campo. Isto deve ser feito se o valor do campo puder, de alguma forma, confundir o analisador de valores compostos. Em particular, os campos contendo parênteses, vírgulas, aspas ou contrabarras devem estar entre aspas (Além disso, um par de aspas dentro de um valor de campo envolto por aspas é assumido como representando o caractere aspas, de maneira análoga à regra para os apóstrofos nas cadeias de caracteres literais do SQL). Como alternativa, pode ser utilizado o escape de contrabarra para proteger todos os caracteres dos dados que, de outra forma, seriam assumidos como fazendo parte da sintaxe do tipo composto.

Um valor de campo inteiramente vazio (nenhum caractere entre as vírgulas ou parênteses) representa o valor nulo. Para escrever um valor que seja uma cadeia de caracteres vazia, e não o valor nulo, deve ser escrito "".

A rotina de saída do tipo composto coloca aspas em torno dos valores dos campos caso estes sejam cadeias de caracteres vazias, ou contenham parêntese, vírgulas, aspas, contrabarras ou espaços em branco (Fazer isto para os espaços em branco não é essencial, mas melhora a legibilidade). Aspas e contrabarras incorporadas aos valores dos campos são duplicadas.

Nota: Deve ser lembrado que, o que se escreve em um comando SQL, é interpretado primeiro como um literal cadeia de caracteres e, depois, como um tipo composto. Isto duplica o número de contrabarras necessárias. Por exemplo, para inserir um campo do tipo text contendo uma contrabarra e uma aspa em um valor composto, deve ser escrito:

INSERT ... VALUES ('("\\"\\\\")');

O processador de literais cadeias de caracteres remove um nível de contrabarras, portanto o que chega ao analisador de valor composto se parece com ("\"\\"). Por sua vez, a cadeia de caracteres introduzida na rotina de entrada do tipo de dado text se torna "\ (Se estivéssemos trabalhando com um tipo de dado cuja rotina de entrada também tratasse as contrabarras de forma especial como, por exemplo, bytea, seriam necessárias oito contrabarras no comando para obter uma contrabarra armazenada no campo composto). Pode ser utilizada a delimitação por cifrão (dollar quoting) (consulte a Seção 4.1.2.2) para evitar a necessidade de duplicar as contrabarras.

Dica: Ao se escrever valores compostos nos comandos SQL, normalmente é mais fácil trabalhar com a sintaxe do construtor ROW do que com a sintaxe do literal composto. Usando ROW os valores individuais dos campos são escritos da mesma maneira como seriam escritos se não fossem membros de um valor composto.

Notas

[1]

As linhas da tabela possuem um tipo, chamado "tipo linha"; todas as linhas da tabela possuem o mesmo tipo linha, que também é o tipo linha da tabela.

Um tipo linha é uma seqüência de um ou mais pares (nome de campo, tipo de dado), conhecido como campos. O valor do tipo linha consiste de um valor para cada um de seus campos.

(ISO-ANSI Working Draft) Framework (SQL/Framework), August 2003, ISO/IEC JTC 1/SC 32, 25-jul-2003, ISO/IEC 9075-1:2003 (E) (N. do T.)

SourceForge.net Logo CSS válido!