O operador específico referenciado por uma expressão de operador é determinado usando o procedimento abaixo. Note que este procedimento é afetado indiretamente pela precedência dos operadores envolvidos, porque isto determina quais subexpressões são consideradas entradas de quais operadores. Veja a Seção 4.1.6 para obter mais informações.
Resolução de tipo de dados para operador
Selecione os operadores a serem considerados no catálogo do sistema
pg_operator.
Se foi usado um nome de operador não qualificado pelo esquema
(o caso usual), os operadores considerados são aqueles com nome
e número de argumentos correspondentes visíveis no caminho
de procura atual (veja a Seção 5.10.3).
Se foi fornecido um nome de operador qualificado, serão
considerados somente os operadores no esquema especificado.
Se forem encontrados vários operadores com argumentos de tipos de dados idênticos no caminho de procura, apenas o operador que aparecer primeiro no caminho será considerado. Operadores com argumentos de tipos de dados diferentes são considerados em pé de igualdade, independentemente da posição no caminho de procura.
Verifique se há um operador que aceita exatamente os mesmos tipos de dados dos argumentos de entrada. Se existir um (só pode haver uma correspondência exata no conjunto de operadores considerado), use-o. A falta de uma correspondência exata cria um risco de segurança ao se chamar, por meio de um nome qualificado [92] (não típico), qualquer operador encontrado em um esquema que permita que usuários não confiáveis criem objetos. Em tais situações, os argumentos devem ser convertidos para forçar uma correspondência exata.
Se um argumento de uma chamada de operador binário for do tipo de dados
unknown, suponha que seja do mesmo tipo de dados que o outro
argumento para esta verificação.
Chamadas envolvendo duas entradas do tipo de dados unknown,
ou um operador de prefixo com uma entrada do tipo de dados
unknown, nunca encontrarão correspondência nesta etapa.
Se um argumento de uma chamada de operador binário for do tipo de dados
unknown, e o outro for do tipo de dados de domínio,
verifique se existe um operador aceitando exatamente o tipo de dados
base do domínio nos dois lados; se existir, use-o.
Procure a melhor correspondência.
Descarte os operadores candidatos para os quais os tipos de dados de entrada não
correspondem, e não podem ser convertidos (usando uma conversão implícita)
para corresponder.
Literais do tipo de dados unknown são considerados
conversíveis para qualquer tipo de dados para este propósito.
Se restar apenas um candidato, use-o; senão, siga para a próxima etapa.
Se algum argumento de entrada for de um tipo de dados de domínio, trate-o como sendo do tipo de dados base do domínio para todas as etapas subsequentes. Isto garante que os domínios ajam como seus tipoa de dados base para fins de resolução de operador ambíguo.
Percorra todos os candidatos e mantenha aqueles com as correspondências mais exatas nos tipos de dados de entrada. Mantenha todos os candidatos se nenhum tiver correspondências exatas. Se restar apenas um candidato, use-o; senão, siga para a próxima etapa.
Percorra todos os candidatos e mantenha aqueles que aceitam os tipos de dados preferidos (da categoria de tipo de dados do tipo de dados de entrada) na maioria das posições onde a conversão de tipo de dados será necessária. Mantenha todos os candidatos se nenhum aceitar os tipos de dados preferidos. Se restar apenas um candidato, use-o; senão, siga para a próxima etapa.
Se algum argumento de entrada for do tipo de dados unknown,
verifique as categorias de tipo de dados aceitas nestas posições de
argumento pelos candidatos restantes.
Em cada posição, selecione a categoria string,
se algum candidato aceitar essa categoria.
(Esse viés para cadeia de caracteres é apropriado, porque um literal de
tipo de dados desconhecido se parece com uma cadeia de caracteres.)
Senão, se todos os candidatos restantes aceitarem a mesma categoria de
tipo de dados, selecione essa categoria;
senão, falha porque a escolha correta não pode ser deduzida sem mais pistas.
Agora descarte os candidatos que não aceitam a categoria de tipo de dados
selecionada.
Além disso, se algum candidato aceitar um tipo de dados preferido nesta
categoria, descarte os candidatos que aceitam tipos de dados não
preferidos para este argumento.
Mantenha todos os candidatos se nenhum sobreviver a esses testes.
Se restar apenas um candidato, use-o; senão, siga para a próxima etapa.
Se houver argumentos do tipo de dados unknown e de
tipo de dados conhecido, e todos os argumentos de tipo de dados
conhecido tiverem o mesmo tipo de dados, suponha
que os argumentos unknown também sejam desse tipo de dados,
e verifique quais candidatos podem aceitar este tipo de dados nas
posições do argumento unknown.
Se restar apenas um candidato, use-o.
Senão, falhe.
Seguem alguns exemplos.
Exemplo 10.1. Resolução do tipo de dados do operador de raiz quadrada
Existe apenas um operador de raiz quadrada (prefixo |/)
definido no catálogo padrão, e recebe um argumento do tipo de dados
double precision.
O rastreador atribui um tipo de dados inicial integer
ao argumento nesta expressão de consulta:
SELECT |/ 40 AS "raiz quadrada de 40";
raiz quadrada de 40 --------------------- 6.324555320336759 (1 linha)
Assim, o analisador faz uma conversão de tipo de dados no operando, e a consulta fica equivalente a:
SELECT |/ CAST(40 AS double precision) AS "raiz quadrada de 40";
Exemplo 10.2. Resolução do tipo de dados do operador de concatenação de cadeia de caracteres
Uma sintaxe semelhante a cadeia de caracteres é usada para trabalhar com tipos de dados de cadeia de caracteres e para trabalhar com tipos de dados de extensão complexos. Cadeias de caracteres com tipo de dados não especificado são correspondidas com prováveis candidatos a operador.
Exemplo com um argumento não especificado:
SELECT text 'abc' || 'def' AS "texto e desconhecido";
texto e desconhecido ---------------------- abcdef (1 linha)
Nesse caso, o analisador analisa se existe um operador aceitando o
tipo de dados text para os dois argumentos.
Como existe, assume que o segundo argumento deve ser interpretado como
sendo do tipo de dados text.
Aqui está uma concatenação de dois valores de tipos de dados não especificados:
SELECT 'abc' || 'def' AS "não especificado";
não especificado ------------------ abcdef (1 linha)
Neste caso, não existe dica inicial para o tipo de dados a ser usado,
porque nenhum tipo de dados é especificado na consulta.
Assim, o analisador procura todos os operadores candidatos, e descobre
haver candidatos aceitando entradas na categoria cadeia de caracteres
e na categoria cadeia de bits.
Como a categoria cadeia de caracteres é a preferida quando está
disponível, essa categoria é selecionada e, em seguida, o tipo de dados
preferido para cadeias de caracteres, text, é usado como
o tipo de dados específico para resolver os literais com tipo de dados
desconhecido.
Exemplo 10.3. Resolução do tipo de dados do operador de valor absoluto e de negação
O catálogo de operadores do PostgreSQL possui
várias entradas para o operador de prefixo @,
todas implementando operações de valor absoluto para vários tipos de
dados numéricos.
Uma dessas entradas é para o tipo de dados float8,
o tipo de dados preferido na categoria numérica.
Portanto, o PostgreSQL usará essa entrada
quando for confrontado com uma entrada do tipo de dados unknown:
SELECT @ '-4.5' AS "abs";
abs ----- 4.5 (1 linha)
Aqui o sistema resolveu implicitamente o literal de tipo de dados
desconhecido como tipo de dados float8 antes de aplicar
o operador escolhido.
Podemos verificar se foi usado float8, e não algum outro
tipo de dados, pela mensagem de erro:
SELECT @ '-4.5e500' AS "abs";
ERRO: "-4.5e500" está fora do intervalo para o tipo de dados double precision
LINHA 1: SELECT @ '-4.5e500' AS "abs";
^
Por outro lado, o operador de prefixo ~
(negação bit a bit) é definido apenas para os tipos de dados inteiros,
e não para float8. Então, se tentarmos um algo semelhante
com ~, obteremos:
SELECT ~ '20' AS "negação";
ERRO: o operador não é único: ~ unknown
LINHA 1: SELECT ~ '20' AS "negação";
^
DICA: Não foi possível escolher o operador que se enquadra melhor.
Você precisa adicionar conversões de tipo de dados explícitas.
Isso acontece, porque o sistema não pode decidir qual dos vários
operadores ~ possíveis deve ser preferido.
Podemos ajudar com uma conversão explícita:
SELECT ~ CAST('20' AS int8) AS "negação";
negação
---------
-21
(1 linha)
Exemplo 10.4. Resolução do tipo de dados de operador de inclusão de matriz
Aqui está outro exemplo de resolução de um operador com uma entrada conhecida e uma desconhecida:
SELECT array[1,2] <@ '{1,2,3}' AS "é subconjunto";
é subconjunto --------------- t (1 linha)
O catálogo de operadores do PostgreSQL possui
várias entradas para o operador infixo <@,
mas os únicos dois que podem aceitar uma matriz de inteiros no lado
esquerdo são inclusão de matriz
(anyarray <@ anyarray)
e inclusão de intervalo
(anyelement <@ anyrange).
Como nenhum destes pseudotipos polimórficos
(veja a Seção 8.21) é considerado preferido,
o analisador não pode resolver a ambiguidade baseado nisso.
No entanto, o Passo 3.f diz para assumir
que o literal de tipo de dados desconhecido é do mesmo tipo de dados
que a outra entrada, ou seja, uma matriz de inteiros.
Agora, apenas um dos dois operadores pode corresponder, portanto,
é selecionado inclusão de matriz.
(Se tivesse sido selecionado inclusão de intervalo, teríamos recebido
um erro, porque a cadeia de caracteres não tem o formato correto para
ser um literal de intervalo.)
Exemplo 10.5. Operador personalizado em um tipo de dados de domínio
Às vezes, os usuários tentam declarar operadores que se aplicam apenas a um tipo de dados de domínio. Isto é possível, mas não é tão útil quanto parece, porque as regras de resolução de operadores são projetadas para selecionar operadores que se aplicam ao tipo de dados base do domínio. Como exemplo considere
CREATE DOMAIN mytext AS text CHECK(...); CREATE FUNCTION mytext_eq_text (mytext, text) RETURNS boolean AS ...; CREATE OPERATOR = (procedure=mytext_eq_text, leftarg=mytext, rightarg=text); CREATE TABLE mytable (val mytext); SELECT * FROM mytable WHERE val = 'foo';
Esta consulta não usará o operador personalizado.
O analisador primeiro verá se existe o operador
mytext = mytext
(Passo 2.a), que não existe;
depois irá considerar o tipo de dados base do domínio text,
e verificar se existe o operador
text = text
(Passo 2.b), que existe;
então resolve o tipo de dados unknown como text
e usa o operador text = text.
A única maneira de fazer com que o operador personalizado seja usado é
convertendo o literal explicitamente
SELECT * FROM mytable WHERE val = text 'foo';
para que o operador
mytext = text
seja encontrado imediatamente segundo a regra de correspondência exata.
Se as regras de melhor correspondência forem alcançadas, elas discriminarão
inteiramente os operadores nos domínios.
Caso contrário, tal operador criaria muitas falhas de operador ambíguo,
porque as regras de conversão sempre consideram um domínio como sendo
passível de conversão de ou para o seu tipo de dados base, portanto o
operador de domínio seria considerado utilizável nos mesmos casos onde
um operador do tipo de dados base de mesmo nome seria.
[92] O perigo não surge com nome não qualificado pelo esquema, porque um caminho de procura contendo esquemas que permitem usuários não confiáveis criarem objetos não é um padrão de uso de esquema seguro.