31.12. Operadores definidos pelo usuário

PostgreSQL 14.5: Operadores definidos pelo usuário

Todo operador é uma "suavização sintática" para a chamada a uma função subjacente que realiza o trabalho real; portanto, primeiro deve ser criada a função subjacente para depois ser criado o operador. Entretanto o operador não é meramente uma suavização sintática, porque possui informações adicionais para ajudar o otimizador de comandos a otimizar os comandos que utilizam o operador. A próxima seção se dedica a explicar estas informações adicionais.

O PostgreSQL dá suporte a operador unário esquerdo, unário direito e binário. Os operadores podem ser sobrecarregados, ou seja, o mesmo nome de operador pode ser utilizado por operadores diferentes que possuam número ou tipo diferente de operandos. Quando o comando é executado, o sistema determina o operador a ser chamado a partir do número e tipo dos operandos fornecidos.

Abaixo segue um exemplo da criação de um operador para adicionar dois números complexos. É assumido que já se tenha criado a definição do tipo complex (consulte a Seção 31.11). Primeiro é necessária uma função para fazer o trabalho, para depois ser definido o operador:

CREATE FUNCTION complex_add(complex, complex)
    RETURNS complex
    AS 'nome_do_arquivo', 'complex_add'
    LANGUAGE C IMMUTABLE STRICT;

CREATE OPERATOR + (
    leftarg = complex,
    rightarg = complex,
    procedure = complex_add,
    commutator = +
);

Agora é possível executar um comando como este:

SELECT (a + b) AS c FROM test_complex;

        c
-----------------
 (5.2,6.05)
 (133.42,144.95)

Aqui foi mostrado como criar um operador binário. Para criar um operador binário deve-se apenas omitir leftarg (para operadores unários esquerdo) ou rightarg (para operadores unários direito). A cláusula procedure e as cláusulas de argumento são os únicos itens requeridos pelo comando CREATE OPERATOR. A cláusula commutator mostrada no exemplo é uma dica opcional para o otimizador de comandos. Na próxima seção podem ser obtidos mais detalhes sobre a cláusula commutator e outras dicas para o otimizador.

31.12.1. Exemplos

Nota: Seção escrita pelo tradutor, não fazendo parte do manual original.

Os exemplos de operadores definidos pelo usuário mostrados nesta seção utilizam os dados da tabela tbl_santos mostrada abaixo:

CREATE TABLE tbl_santos (nome text);
INSERT INTO tbl_santos VALUES('São José');
INSERT INTO tbl_santos VALUES(' São Bento  ');
INSERT INTO tbl_santos VALUES('SAO BENEDITO');
INSERT INTO tbl_santos VALUES(NULL);
INSERT INTO tbl_santos VALUES('SAO BENTO');
INSERT INTO tbl_santos VALUES('Santa Clara');
\pset null '(nulo)'
\pset border 2
\pset title 'Tabela tbl_santos'
SELECT * FROM tbl_santos;
Tabela tbl_santos
+--------------+
|     nome     |
+--------------+
| São José     |
|  São Bento   |
| SAO BENEDITO |
| (nulo)       |
| SAO BENTO    |
| Santa Clara  |
+--------------+
(6 linhas)

Exemplo 31-10. Operadores para comparar texto sem diferenciar letras minúsculas, maiúsculas ou acentuadas

Os operadores binários definidos pelo usuário mostrados neste exemplo (<<<, >>>, !== e ===) têm por finalidade comparar dois textos sem diferenciar letras minúsculas, maiúsculas ou acentuadas, e também desprezando os espaços em branco no início e no fim dos textos. Além de serem utilizados para comparar dois textos diretamente, estes operadores também são utilizados em consultas para classificar a saída em ordem ascendente e descendente através da cláusula ORDER BY expressão USING operador do comando SELECT.

Abaixo está mostrado o arquivo operador.sql utilizado para criar e testar os operadores:

/*
 * Retorna verdade se os dois argumentos forem considerados iguais,
 * falso caso contrário.
 */
CREATE FUNCTION fun_igual_ci_ai(a text, b text) RETURNS boolean AS $$
BEGIN
   RETURN (lower(to_ascii(trim(a))) = lower(to_ascii(trim(b))));
END;
$$ LANGUAGE plpgsql STRICT;
/*
 * Retorna verdade se os dois argumentos forem considerados diferentes,
 * falso caso contrário.
 */
CREATE FUNCTION fun_difer_ci_ai(a text, b text) RETURNS boolean AS $$
BEGIN
   RETURN (lower(to_ascii(trim(a))) != lower(to_ascii(trim(b))));
END;
$$ LANGUAGE plpgsql STRICT;
/*
 * Retorna verdade se o primeiro argumento for considerado maior que o
 * segundo argumento, falso caso contrário.
 */
CREATE FUNCTION fun_maior_ci_ai(a text, b text) RETURNS boolean AS $$
BEGIN
   RETURN (lower(to_ascii(trim(a))) > lower(to_ascii(trim(b))));
END;
$$ LANGUAGE plpgsql STRICT;
/*
 * Retorna verdade se o primeiro argumento for considerado menor que o
 * segundo argumento, falso caso contrário.
 */
CREATE FUNCTION fun_menor_ci_ai(a text, b text) RETURNS boolean AS $$
BEGIN
   RETURN (lower(to_ascii(trim(a))) < lower(to_ascii(trim(b))));
END;
$$ LANGUAGE plpgsql STRICT;
/*
 * Criação dos operadores que implementam as funções de comparação.
 */
CREATE OPERATOR <<< (
    PROCEDURE = fun_menor_ci_ai,
    LEFTARG   = text,
    RIGHTARG  = text
);
CREATE OPERATOR >>> (
    PROCEDURE = fun_maior_ci_ai,
    LEFTARG   = text,
    RIGHTARG  = text
);
CREATE OPERATOR !== (
    PROCEDURE = fun_difer_ci_ai,
    LEFTARG   = text,
    RIGHTARG  = text
);
CREATE OPERATOR === (
    PROCEDURE = fun_igual_ci_ai,
    LEFTARG   = text,
    RIGHTARG  = text
);
\pset border 2
\pset null '(nulo)'
\pset title 'Resultados das expressões'
SELECT
    ''' São Bento  '' = ''SAO BENTO''' AS expressão,
    ' São Bento  ' = 'SAO BENTO' AS resultado
UNION
SELECT
    ''' São Bento  '' === ''SAO BENTO''',
    ' São Bento  ' === 'SAO BENTO'
UNION
SELECT
    ''' São Bento  '' === NULL',
    ' São Bento  ' === NULL
UNION
SELECT
    ''' São Bento  '' === ''  SAO BENEDITO''',
    ' São Bento  ' === '  SAO BENEDITO'
UNION
SELECT
    ''' São Bento  '' !== ''SAO BENTO''',
    ' São Bento  ' !== 'SAO BENTO'
UNION
SELECT
    ''' São Bento  '' >>> ''  SAO BENEDITO''',
    ' São Bento  ' >>> '  SAO BENEDITO'
UNION
SELECT
    ''' São Bento  '' <<< ''  SAO BENEDITO''',
    ' São Bento  ' <<< '  SAO BENEDITO';
\pset title 'Ascendente'
SELECT nome FROM tbl_santos ORDER BY nome USING <<<;
\pset title 'Descendente'
SELECT nome FROM tbl_santos ORDER BY nome USING >>>;

A seguir está mostrado o resultado do processamento do arquivo:

# psql -U teste -f operador.sql -o operador.out -q teste
# cat operador.out
             Resultados das expressões
+-------------------------------------+-----------+
|              expressão              | resultado |
+-------------------------------------+-----------+
| ' São Bento  ' !== 'SAO BENTO'      | f         |
| ' São Bento  ' <<< '  SAO BENEDITO' | f         |
| ' São Bento  ' = 'SAO BENTO'        | f         |
| ' São Bento  ' === '  SAO BENEDITO' | f         |
| ' São Bento  ' === 'SAO BENTO'      | t         |
| ' São Bento  ' === NULL             | (nulo)    |
| ' São Bento  ' >>> '  SAO BENEDITO' | t         |
+-------------------------------------+-----------+
(7 linhas)

   Ascendente
+--------------+
|     nome     |
+--------------+
| Santa Clara  |
| SAO BENEDITO |
|  São Bento   |
| SAO BENTO    |
| São José     |
| (nulo)       |
+--------------+
(6 linhas)

  Descendente
+--------------+
|     nome     |
+--------------+
| São José     |
| SAO BENTO    |
|  São Bento   |
| SAO BENEDITO |
| Santa Clara  |
| (nulo)       |
+--------------+
(6 linhas)
  

Exemplo 31-11. Operador para concatenar texto sem nunca retornar nulo

O operador binário definido pelo usuário mostrado neste exemplo (||+) têm por finalidade concatenar dois textos sem nunca retornar o valor nulo. Este exemplo foi baseado no artigo How to add custom operators in PostgreSQL, escrito por Josh Berkus.

Abaixo está mostrado o arquivo concatena.sql utilizado para criar e testar o operador:

/*
 * Concatena duas cadeias de caracteres sem nunca retornar nulo.
 */
CREATE FUNCTION fun_concatena_nunca_nulo(text, text) RETURNS text AS $$
    SELECT COALESCE($1,'') || COALESCE($2,'');
$$ LANGUAGE SQL IMMUTABLE;

CREATE OPERATOR ||+ (
    PROCEDURE = fun_concatena_nunca_nulo,
    LEFTARG   = text,
    RIGHTARG  = text
);
\pset border 2
\pset null '(nulo)'
\pset title 'Resultados das expressões'
SELECT
    '''Postgre'' || NULL' AS expressão,
    'Postgre' || NULL AS resultado
UNION
SELECT
    '''Postgre'' ||+ NULL',
    'Postgre' ||+ NULL
UNION
SELECT
    'NULL ||+ ''SQL''',
    NULL ||+ 'SQL'
UNION
SELECT
    '''Postgre'' ||+ ''SQL''',
    'Postgre' ||+ 'SQL'
UNION
SELECT
    'NULL || NULL',
    NULL || NULL
UNION
SELECT
    'NULL ||+ NULL',
    NULL ||+ NULL
;

A seguir está mostrado o resultado do processamento do arquivo:

# psql -U teste -f concatena.sql -o concatena.out -q teste
# cat concatena.out
     Resultados das expressões
+---------------------+------------+
|      expressão      | resultado  |
+---------------------+------------+
| 'Postgre' || NULL   | (nulo)     |
| 'Postgre' ||+ 'SQL' | PostgreSQL |
| 'Postgre' ||+ NULL  | Postgre    |
| NULL || NULL        | (nulo)     |
| NULL ||+ 'SQL'      | SQL        |
| NULL ||+ NULL       |            |
+---------------------+------------+
(6 linhas)
SourceForge.net Logo CSS válido!