6.6. Comparação com padrão

Existem três abordagens separadas para comparação com padrão fornecidas pelo PostgreSQL: o operador LIKE do SQL, o operador mais recente SIMILAR TO do SQL99, e as expressões regulares no estilo POSIX. Além destas, a função de comparação com padrão SUBSTRING também está disponível, utilizando tanto o estilo SQL99 quanto as expressões regulares POSIX.

Dica: Havendo necessidade de comparação com padrão não atendidas, deve ser considerado o desenvolvimento de uma função definida pelo usuário em Perl ou Tcl.

6.6.1. LIKE

cadeia_de_caracteres LIKE filtro [ESCAPE caractere_de_escape]
cadeia_de_caracteres NOT LIKE filtro [ESCAPE caractere_de_escape]

Cada filtro define um conjunto de cadeias de caracteres. A expressão LIKE retorna verdade se a cadeia_de_caracteres estiver contida no conjunto de cadeias de caracteres representado pelo filtro (Como esperado, a expressão NOT LIKE retorna falso quando LIKE retorna verdade, e vice-versa. A expressão equivalente seria NOT (cadeia_de_caracteres LIKE filtro)).

Quando o filtro não possui os caracteres percentagem ou sublinhado, o filtro representa apenas a própria cadeia de caracteres; neste caso LIKE atua como o operador igual. O sublinhado (_) no filtro significa (corresponde a) qualquer caractere único; o caractere percentagem (%) corresponde a qualquer cadeia com zero ou mais caracteres.

Alguns exemplos:

'abc' LIKE 'abc'    true
'abc' LIKE 'a%'     true
'abc' LIKE '_b_'    true
'abc' LIKE 'c'      false

A comparação com padrão LIKE sempre abrange toda a cadeia de caracteres. Para haver correspondência com o padrão em qualquer posição da cadeia de caracteres, o filtro precisa começar e terminar pelo caractere percentagem.

Para corresponder ao próprio caractere sublinhado ou percentagem, e não a outros caracteres, estes caracteres no filtro devem estar precedidos pelo caractere de escape. O padrão para caractere de escape é a contrabarra, mas um outro caractere pode ser definido através da cláusula ESCAPE. Para corresponder ao próprio caractere de escape, devem ser escritos dois caracteres de escape.

Deve ser observado que a contrabarra possui significado especial nas cadeias de caracteres e, portanto, para escrever um filtro contendo uma contrabarra devem ser escritas duas contrabarras na consulta. Assim sendo, escrever um filtro correspondente à contrabarra significa escrever quatro contrabarras na consulta, o que pode ser evitado escolhendo um caractere de escape diferente na cláusula ESCAPE; assim a contrabarra deixa de ser um caractere especial para o LIKE (Mas continua sendo especial para o analisador de cadeias de caracteres e, por isso, continuam sendo necessárias duas contrabarras).

Também é possível fazer nenhum caractere servir de escape declarando ESCAPE ''. Esta declaração tem como efeito desativar o mecanismo de escape, tornando impossível evitar o significado especial dos caracteres sublinhado e percentagem presentes no filtro.

A palavra chave ILIKE pode ser utilizada no lugar do LIKE para fazer a comparação não diferenciar letras maiúsculas de minúsculas, conforme a localização ativa. Isto não faz parte do padrão SQL, sendo extensão do PostgreSQL.

O operador ~~ equivale ao LIKE, enquanto ~~* equivale ao ILIKE. Também existem os operadores !~~ e !~~*, representando o NOT LIKE e o NOT ILIKE. Todos estes operadores são específicos do PostgreSQL.

6.6.2. SIMILAR TO e as expressões regulares do SQL99

cadeia_de_caracteres SIMILAR TO filtro [ESCAPE caractere_de_escape]
cadeia_de_caracteres NOT SIMILAR TO filtro [ESCAPE caractere_de_escape]

O operador SIMILAR TO retorna verdade ou falso conforme o filtro corresponda ou não à cadeia de caracteres fornecida. Este operador é muito semelhante ao LIKE, exceto por interpretar o filtro utilizando a definição de expressão regular do SQL99. As expressões regulares do SQL99 são um cruzamento curioso entre a notação do LIKE e a notação das expressões regulares comuns.

Da mesma forma que o LIKE, o operador SIMILAR TO somente tem êxito quando o filtro corresponde a toda cadeia de caracteres; é diferente da prática comum para expressões regulares, onde o filtro pode corresponder a qualquer parte da cadeia de caracteres. Também como o LIKE, o operador SIMILAR TO utiliza % e _ como caracteres curingas, representando qualquer cadeia de caracteres e qualquer caractere único, respectivamente (sendo semelhantes aos .* e . das expressões regulares POSIX).

Além destas facilidades tomadas emprestada do LIKE, o SIMILAR TO suporta os seguintes metacaracteres para comparação com padrão tomados emprestado das expressões regulares POSIX:

Observe que a repetição limitada (bounded repetition) (? e {...}) não está disponível, embora exista no POSIX. Além disso, o ponto (.) não é um metacaractere.

Da mesma forma que no LIKE, a contrabarra cancela o significado especial de qualquer um destes metacaracteres; ou um caractere de escape diferente pode ser especificado por meio do ESCAPE.

Alguns exemplos:

'abc' SIMILAR TO 'abc'      true
'abc' SIMILAR TO 'a'        false
'abc' SIMILAR TO '%(b|d)%'  true
'abc' SIMILAR TO '(b|c)%'   false

A função SUBSTRING com três parâmetros, SUBSTRING(cadeia_de_caracteres FROM filtro FOR escape), permite a extração da parte da cadeia de caracteres correspondente à expressão regular SQL99 no filtro. Assim como em SIMILAR TO, o filtro especificado deve corresponder a toda a cadeia de caracteres do dado, senão a função falha e retorna nulo. Para indicar a parte do filtro que deve ser retornada em caso de sucesso, o SQL99 especifica que o filtro deve conter duas ocorrências do caractere de escape seguido por aspas ("). O texto correspondente à parte do filtro entre estes marcadores é retornada.

Alguns exemplos:

SUBSTRING('foobar' FROM '%#"o_b#"%' FOR '#')   oob
SUBSTRING('foobar' FROM '#"o_b#"%' FOR '#')    NULL

6.6.3. Expressões regulares POSIX

A Tabela 6-11 lista os operadores disponíveis para comparação com padrão utilizando as expressões regulares POSIX.

Tabela 6-11. Operadores de comparação para expressões regulares

OperadorDescriçãoExemplo
~ Corresponde à expressão regular, diferenciando maiúsculas/minúsculas'thomas' ~ '.*thomas.*'
~* Corresponde à expressão regular, não diferenciando maiúsculas/minúsculas'thomas' ~* '.*Thomas.*'
!~ Não corresponde à expressão regular, diferenciando maiúsculas/minúsculas'thomas' !~ '.*Thomas.*'
!~* Não corresponde à expressão regular, não diferenciando maiúsculas/minúsculas'thomas' !~* '.*vadim.*'

As expressões regulares POSIX fornecem uma forma mais poderosa para comparação com padrão que os operadores LIKE e SIMILAR TO. Muitas ferramentas do Unix, como egrep, sed e awk, utilizam uma linguagem para comparação com padrão semelhante à descrita aqui.

Uma expressão regular é uma seqüência de caracteres que é uma definição abreviada de um conjunto de cadeias de caracteres (um conjunto regular). Uma cadeia de caracteres é dita correspondendo a uma expressão regular se for membro do conjunto regular descrito pela expressão regular. Assim como no LIKE, os caracteres do filtro correspondem exatamente aos caracteres da cadeia de caracteres, a não ser que sejam caracteres especiais da linguagem da expressão regular --- porém as expressões regulares utilizam caracteres especiais diferentes dos utilizados pelo LIKE. Ao contrário do LIKE, uma expressão regular pode corresponder a qualquer parte da cadeia de caracteres, a não ser que a expressão regular esteja explicitamente ancorada ao início ou ao fim da cadeia de caracteres.

Alguns exemplos:

'abc' ~ 'abc'    true
'abc' ~ '^a'     true
'abc' ~ '(b|d)'  true
'abc' ~ '^(b|c)' false

A função SUBSTRING com dois parâmetros, SUBSTRING(cadeia_de_caracteres FROM filtro), permite extrair a parte da cadeia de caracteres correspondente à expressão regular SQL99 no filtro. A função retorna nulo quando não há correspondência, senão retorna a parte do texto que corresponde ao filtro. Mas se o filtro contiver parênteses, a parte do texto correspondente à primeira subexpressão entre parênteses (aquela cujo abre parênteses vem primeiro) é retornada. Pode ser colocado parênteses envolvendo toda a expressão se for desejado utilizar parênteses em seu interior sem disparar esta exceção.

Alguns exemplos:

SUBSTRING('foobar' FROM 'o.b')     oob
SUBSTRING('foobar' FROM 'o(.)b')   o

As expressões regulares (REs), conforme definidas no POSIX 1003.2, estão presentes em duas formas: REs modernas (as do egrep aproximadamente; chamadas no 1003.2 de REs "estendidas"), e REs obsoletas (as do ed aproximadamente; REs "básicas" do 1003.2). O PostgreSQL implementa a forma moderna.

Uma expressão regular (moderna) é composta por uma ou mais subdivisões não vazias, separadas por |. A expressão corresponde a qualquer coisa que corresponda a uma das suas subdivisões.

Uma subdivisão é uma ou mais peças, concatenadas. Equivale a uma correspondência para a primeira, seguida por uma correspondência para a segunda, etc.

Uma peça é um átomo, possivelmente seguido por um único *, +, ? ou limite. Um átomo seguido por um * corresponde a uma série de 0 ou mais ocorrências do átomo. Um átomo seguido por um + corresponde a uma série de 1 ou mais ocorrências do átomo. Um átomo seguido por um ? corresponde a uma série de 0 ou 1 ocorrências do átomo.

Um limite é uma { seguida por um número decimal inteiro sem sinal, possivelmente seguido por uma , possivelmente seguida por um número decimal inteiro sem sinal, sempre seguido por uma }. Os inteiros devem estar entre 0 e RE_DUP_MAX (255) inclusive e, havendo dois deles, o primeiro não pode ser maior que o segundo. Um átomo seguido por um limite contendo um inteiro i e nenhuma vírgula, corresponde a uma série de exatamente i ocorrências do átomo. Um átomo seguido por um limite contendo um inteiro i e uma vírgula, corresponde a uma seqüência de i ou mais ocorrências do átomo. Um átomo seguido por um limite contendo dois inteiros i e j, corresponde a uma série de i até j (inclusive) ocorrências do átomo.

Nota: Um operador de repetição (?, *, + ou limites) não pode vir depois de outro operador de repetição. O operador de repetição não pode iniciar uma expressão, uma subexpressão, ou vir depois de ^ ou |.

Um átomo pode ser uma expressão regular entre () (representando a expressão regular), um conjunto vazio de () (representando a cadeia de caracteres nula), uma expressão de colchete (veja abaixo), . (representando qualquer caractere único), ^ (representando a cadeia de caracteres nula no início da cadeia de caracteres da entrada), $ (representando a cadeia de caracteres nula no final da cadeia de caracteres da entrada), uma \ seguida por um dos caracteres ^.[$()|*+?{\ (representando este caractere como sendo um caractere ordinário), uma \ seguida por qualquer outro caractere (representando este caractere como sendo um caractere ordinário, como se a \ não estivesse presente), ou um caractere único sem qualquer outro significado (representando o próprio caractere). Uma { seguida por um outro caractere que não seja um dígito é um caractere ordinário, e não o início de um limite. É ilegal terminar uma expressão regular por uma \.

Observe que a contrabarra (\) possui um significado especial nas cadeias de caracteres e, portanto, para escrever uma constante filtro contendo uma contrabarra devem ser escritas duas contrabarras na consulta.

Uma expressão entre colchetes é uma seqüência de caracteres entre []. Normalmente significa qualquer um dos caracteres da seqüência (mas veja abaixo). Se o conjunto começar por ^, significa qualquer caractere não presente no restante do conjunto (mas veja abaixo). Se dois caracteres do conjunto estiverem separados por -, isto representa a forma abreviada de todos os caracteres do intervalo delimitado por estes dois caracteres (inclusive) na seqüência de arrumação (collating sequence). Por exemplo, [0-9] em ASCII significa qualquer dígito decimal. É ilegal dois intervalos compartilharem uma mesma extremidade como, por exemplo, a-c-e. Os intervalos são dependentes da seqüência de arrumação dos caracteres, e os programas portáveis devem evitar esta dependência.

Para incluir o literal ] na seqüência, deve ser feito com que seja o primeiro caractere (possivelmente após o ^). Para incluir o literal -, deve ser feito com que seja o primeiro ou o último caractere, ou a segunda extremidade de um intervalo. Para utilizar o literal - como a primeira extremidade de um intervalo, deve-se colocá-lo entre [. e .] para torná-lo um elemento de arrumação (veja abaixo). Com estas excessões e algumas outras combinações utilizando [ (veja os próximos parágrafos), todos os outros caracteres especiais, incluindo a \, perdem seu significado especial dentro de uma expressão entre colchetes.

Dentro da expressão entre colchetes, o elemento de arrumação (um caractere, uma seqüência de múltiplos caracteres arrumada como se fosse um único caractere, ou o nome de uma seqüência de arrumação) entre [. e .] representa a seqüência de caracteres deste elemento de arrumação. A seqüência é um único elemento do conjunto da expressão entre colchetes. Uma expressão entre colchetes contendo um elemento de arrumação de múltiplos caracteres pode, portanto, corresponder a mais de um caractere. Por exemplo, se a seqüência de arrumação incluir um elemento de arrumação ch, então a expressão regular [[.ch.]]*c corresponde aos cinco primeiros caracteres de chchcc.

Dentro de uma expressão entre colchetes, um elemento de arrumação entre [= e =] é uma classe de equivalência, representando as seqüências de caracteres de todos os elementos de arrumação equivalentes a este elemento, incluindo o próprio (Se não existirem outros elementos de arrumação equivalentes, o tratamento é como se os delimitadores envoltórios fossem [. e .]). Por exemplo, se o e ^ são membros de uma classe de equivalência, então [[=o=]], [[=^=]] e [o^] são todos sinônimos. Uma classe de equivalência não pode ser a extremidade de um intervalo.

Dentro de uma expressão entre colchetes, o nome de uma classe de caracteres entre [: e :] representa o conjunto de todos os caracteres pertencentes a esta classe. Os nomes das classes de caracteres padrão são: alnum, alpha, blank, cntrl, digit, graph, lower, print, punct, space, upper, xdigit. Estes nomes representam as classes de caracteres definidas em ctype. Uma localização pode fornecer outras. Uma classe de caracteres não pode ser usada como uma extremidade de um intervalo.

Existem dois casos especiais para expressões entre colchetes: as expressões entre colchetes [[:<:]] e [[:>:]] correspondem à cadeia de caracteres nula no início e no fim de uma palavra, respectivamente. Uma palavra é definida como a seqüência de caracteres de palavra que não é nem precedida nem seguida por caracteres de palavra. Um caractere de palavra é um alnum caractere (conforme definido em ctype) ou um sublinhado. Isto é uma extensão, compatível mas não especificada pelo POSIX 1003.2, devendo ser utilizada com cautela em programas onde se deseja a portabilidade para outros sistemas.

Quando a expressão regular puder corresponder a mais de uma parte da cadeia de caracteres, a expressão regular corresponderá à parte que começar primeiro na cadeia de caracteres. Se a expressão regular puder corresponder a mais de uma parte começando no mesmo ponto, corresponderá à mais longa. As subexpressões também correspondem às partes mais longas possíveis, sujeitas à restrição que toda a correspondência seja a mais longa possível, com as subexpressões começando antes na expressão regular tendo prioridade em relação às começando depois. Observe que, deste modo, as subexpressões de maior nível têm prioridade em relação às suas subexpressões componentes de nível mais baixo.

Os comprimentos das correspondências são medidos em caracteres, e não em elementos de arrumação. Uma cadeia de caracteres nula é considerada como sendo mais longa que nenhuma correspondência. Por exemplo, bb* corresponde aos três caracteres do meio de abbbc; (wee|week)(knights|nights) corresponde a todos os dez caracteres de weeknights; quando (.*).* é comparado com abc a subexpressão entre parênteses corresponde a todos os três caracteres; e quando (a*)* é comparado com bc tanto a expressão regular completa quanto a subexpressão entre parênteses correspondem a cadeia de caracteres nula.

Se for especificada uma correspondência que não diferencie letras maiúsculas e minúsculas, o efeito é como se a distinção entre maiúsculas e minúsculas tivesse desaparecido do alfabeto. Quando um caractere alfabético que existe em maiúscula e minúscula aparece como caractere ordinário fora da expressão entre colchetes, este caractere é transformado em uma expressão entre colchetes contendo as duas representações, por exemplo, x se torna [xX]. Quando o caractere aparece dentro da expressão entre colchetes, a representação que falta é adicionada à expressão entre colchetes, portanto (por exemplo) [x] se torna [xX] e [^x] se torna [^xX].

Não existe nenhum limite determinado para o comprimento da expressão regular, a não ser o fato da memória ser limitada. A utilização de memória é aproximadamente linear em relação ao tamanho da expressão regular, e altamente independente da complexidade da expressão regular, exceto para repetições limitadas. As repetições limitadas são implementadas por meio de expansão de macro, gastando muito tempo e espaço quando os contadores são grandes ou as repetições limitadas são aninhadas. Uma expressão regular como, digamos, ((((a{1,100}){1,100}){1,100}){1,100}){1,100} irá (ao fim) fazer quase todas as máquinas existentes esgotarem suas áreas de permuta (swap). [1]

Notas

[1]

Tenha em mente que isto foi escrito em 1994. Os números provavelmente são outros, mas o problema continua.