Comandos DTML para gerar SQL
Os comandos dtml-sql são usados dentro de objetos SQL Method. O contexto de execução dos SQL
Methods é especial: Dentro de um SQL Method, os argumentos declarados no campo Arguments do
próprio método estão no topo do namespace. Eles são preenchidos automaticamente a partir de valores
presentes no REQUEST. Por exemplo, se existe uma variável quantidade no REQUEST, seu valor será
atribuido ao parâmetro declarado de nome idêntico, podendo ser acessado da seguinte maneira:
<dtml-sqlvar name="quantidade" type="int">
Outros comandos DTML também podem ser usadas em SQL Methods, porém eles raramente são
necessários, uma vez que os comandos dtml-sqlgroup e dtml-sqltest, são bastante poderosos e evitam
muitos dos transtornos que encontramos ao lidar com SQL em outras linguagens.
dtml-sqlgroup
<dtml-sqlgroup parâmetros>testes</dtml-sqlgroup>Produz cláusulas condicionais complexas, formadas por múltiplos testes que podem ser opcionais ou
obrigatórios. O uso deste comando combinado ao dtml-sqltest simplifica muito a geração de código SQL
com cláusulas configuradas dinamicamente em função dos argumentos fornecidos ou omitidos pelo
usuário. Geralmente, o bloco contido em um dtml-sqlgroup é composto exclusivamente por comandos
dtml-sqltest.
Variantes
<dtml-sqlgroup parâmetros>bloco1<dtml-and>bloco2</dtml-sqlgroup><dtml-sqlgroup parâmetros>bloco1<dtml-or>bloco2</dtml-sqlgroup>
<dtml-sqlgroup params1>bloco1<dtml-or>bloco2<dtml-and>bloco3</dtml-sqlgroup>
Dentro de um dtml-sqlgroup, vários comandos dtml-and e dtml-or podem ser utilizados para conectar os
blocos, renderizando opcionalmente os operadores lógicos AND e OR da linguagem SQL. Estes
operadores só são renderizados caso os blocos adjacentes também sejam, evitando erros sintáticos no SQL.
Parâmetros
| Parâmetro | Descrição |
| required | se presente, este parâmetro define um grupo obrigatório, gerando uma exceção caso bloco renderizado fique vazio |
| where | se presente, este parâmetro provoca a renderização condicional da string WHERE; se o bloco renderizado ficar vazio e não for opcional, a palavra WHERE é omitida, evitando erro sintáico |
Exemplos
O exemplo abaixo produz uma lista de candidatos de um estado e/ou partido. Para funcionar, este códigodeve ser colocado em um SQL Method com dois argumentos declarados: uf e partido. O usuário pode
fornecer ambos, apenas um, ou nenhum dos argumentos.
select uf, partido, nome from candidatos
<dtml-sqlgroup where>
<dtml-sqltest name="uf" type="nb" optional>
<dtml-and>
<dtml-sqltest name="partido" type="nb" optional>
</dtml-sqlgroup>
order by uf, partido, nome
Supondo os argumentos uf='AM' e partido='PT', o código resultante será:
select uf, partido, nome from candidatos
where
(uf = 'AM'
and partido = 'PT'
)
order by uf, partido, nome
Se apenas o argumento uf='RS' for indicado, o SQL ficará assim (note a omissão dos parênteses e do
operador and):
select uf, partido, nome from candidatos
where
uf = 'AM'
order by uf, partido, nome
Deixando em branco os dois argumentos, o resultado será uma listagem de todos os candidatos,
indepentente de estado ou partido. O código SQL gerado será assim:
select uf, partido, nome from candidatos
order by uf, partido, nome
Uso do parâmetro required para evitar um desastre
No código abaixo, o parâmetro required é usado para impedir a omissão da cláusula where. Isto é útilespecialmente com o comando SQL DELETE, capaz de aniquilar uma tabela inteira de uma vez só. Neste
exemplo, os dois argumentos são opcionais, mas o usuário será obrigado a fornecer um deles. Se ambos
forem omitidos, uma execeção será levantada e o SQL não será executado. Assim, só é possível remover os
candidatos de um partido, de um estado, ou de um partido e estado, e não todos de uma vez.
delete from candidatos
<dtml-sqlgroup where required>
<dtml-sqltest name="uf" type="nb" optional>
<dtml-and>
<dtml-sqltest name="partido" type="nb" optional>
</dtml-sqlgroup>
Grupos de condições aninhadas e festas com muito uísque
O código a seguir poderia ser usado para criar listas de convidados para festas de políticos. Dados osargumentos uf, partido e votos este método seleciona todos os candidatos que são de um determinado
estado e de um determinado partido, ou que tiveram mais do que um certo número de votos, independente
do estado ou partido.
select uf, partido, nome, votos from candidatos
<dtml-sqlgroup where>
<dtml-sqltest votos op="gt" type="int">
<dtml-or>
<dtml-sqlgroup>
<dtml-sqltest uf type="nb">
<dtml-and>
<dtml-sqltest partido type="nb">
</dtml-sqlgroup>
</dtml-sqlgroup>
Para formar uma mesa de pôquer, os argumentos poderiam ser uf='BA', partido='PV' e votos=40000.
Tais argumentos resultariam no seguinte SQL:
select uf, partido, nome, votos from candidatos
where
(votos > 400000
or (uf = 'BA'
and partido = 'PV'
)
)
dtml-sqltest
<dtml-sqltest parâmetros>Gera uma expressão de teste condicional em SQL, comparando um argumento declarado a uma coluna da
tabela. O parâmetro name= é usado para especificar o argumento, e parâmetro column= define a coluna da
tabela contra a qual o argumento deve ser comparado. Se o nome da coluna é idêntico ao nome do
argumento, o parâmetro column= pode ser omitido.
Parâmetros
| parâmetro | descrição |
| name=arg | nome do argumento a ser comparado (é obrigatório usar name= ou expr=; proibido usar ambos) |
| expr=expressão | expressão a ser comparada (é obrigatório usar name= ou expr=; proibido usar ambos) |
| column=coluna | nome do coluna a ser comparada (o default é usar o mesmo nome definido no parâmetro name=); este parâmetro é obrigatório quando o parâmetro name= não é utilizado |
| type=tipo | parâmetro obrigatório; valores válidos: nb, string, int, float; para strings, utilize tipo nb a menos que necessite testar campos vazios e não nulos (ex.: complemento = '') |
| optional | quando presente, este parâmetro evita evita a renderização do teste como um todo, em vez de levantar uma exceção se o argumento é nulo ou inexistente |
| multiple=bool | se verdadeiro, assume que o argumento é uma lista e gera um teste usando o operador SQL IN para verificar se o valor da coluna é igual a um dos valores do argumento (veja exemplo abaixo) |
| op=operador | operador
a ser utilizado na comparação (default é eq); valores reconhecidos: eq
(= igual), ne (<> diferente) gt (> maior), ge (>= maior ou
igual), lt(< menor), le (<= menor ou igual); se um valor não
reconhecido for utilizado, ele é renderizado sem modificação,
permitindo o uso de operadores como LIKE, % etc. |
Exemplos
Abaixo, alguns usos simples do comando dtml-sqltest. Outros exemplos podem ser encontrados nocomando dtml-sqlgroup.
select * from bares where <dtml-sqltest nome type="nb">
Se o valor do argumento nome é "Moe's", o comando acima será renderizado assim:
select * from bares where nome = 'Moe''s'
Note que a aspa simples usada como apóstrofe foi duplicada na string renderizada, evitando um erro de
sintaxe em SQL.
Para pesquisar todos os bares diferentes do argumento:
select * from bares where <dtml-sqltest nome type="nb" op="ne">
Neste caso o resultado seria:
select * from bares where nome <> 'Moe''s'
Nos exemplos acima, utilizamos type="nb" obrigando o argumento nome a ser uma string não vazia.
Supondo que o coluna nome pode conter strings vazias, podemos dar ao usuário a opção de buscar registros
nestas condições usando type="string":
select * from bares where <dtml-sqltest nome type="string">
Agora, se o argumento nome é uma string vazia, o usuário não verá uma mensagem de erro, mas sim uma
lista de bares sem nome, resultante deste comando SQL:
select * from bares where nome = ''
Uso do parâmetro muliple
O exemplo abaixo produz uma lista de candidatos de uma região. Para funcionar, este código deve sercolocado em um SQL Method com um argumento declarado de nome regiao. O valor esperado para este
argumento é uma lista de siglas de estados separadas por espaços (por exemplo: "RS PR SC"). Uma
expressão Python é usada para transformar o argumento em uma lista através do uso do método
String.split().
select uf, nome, partido from candidatos
where <dtml-sqltest expr="regiao.split()" column="uf" type="nb" multiple>
order by uf, nome
Caso o argumento região receba o valor "RS PR SC", o SQL resultante é:
select uf, nome, partido from candidatos
where uf in ('RS', 'SC', 'PR')
order by uf, nome
Se apenas uma sigla é fornecida (por exemplo, regiao="MG"), o SQL gerado é:
select uf, nome, partido from candidatos
where uf = 'MG'
order by uf, nome
dtml-sqlvar
<dtml-sqlvar parâmetros>Insere o valor da variável ou expressão no código SQL. Normalmente o parâmetro name= corresponde a
um argumento declarado no SQL Method. O parâmetro obrigatório type= define como o argumento deve
ser renderizado (por exemplo, strings são renderizadas entre aspas simples, de acordo com a sintaxe da
linguagem SQL, e aspas simples dentro de strings são duplicadas automaticamente). Este comando é útil
principalmente para produzir valores para os comandos SQL INSERT e UPDATE.
Os parâmetros optional e type=nb modificam bastante o comportamento deste comando. O tipo nb (non-
blank) serve para renderizar strings em situações em que uma string vazia não faz sentido. Se o argumento
é uma string vazia, a opção type=nb gera um erro. Porém, se o parâmetro optional for especificado, a
palavra null (sem aspas) é renderizada no lugar do argumento (em muitos dialetos de SQL, null é a
palavra reservada usada para designar um valor nulo). No caso de type=string, uma string vazia é sempre
renderizada como '' (duas aspas simples). Na maioria dos casos é melhor usar type=nb do que type=string.
Os exemplos abaixo demonstram todos os possíveis resultados da renderização de uma string vazia com
diferentes parâmetros. Para entender estes exemplos, suponha que o código está em um SQL Method com
um argumento declarado chamado cod. Os resultados mostram o que acontece quando o argumento cod
recebe uma string vazia.
<dtml-sqlvar name="cod" type="nb"> #> Erro: string vazia inválida em cod
<dtml-sqlvar name="cod" type="nb" optional> #> null (palava reservada de SQL)
<dtml-sqlvar name="cod" type="string"> #> '' (uma string vazia entre aspas
simples)
Parâmetros
| parâmetro | descrição |
| name=arg | nome do argumento a ser renderizado (é obrigatório usar name= ou expr=; proibido usar ambos) |
| expr=expressão | expressão a ser renderizada (é obrigatório usar name= ou expr=; proibido usar ambos) |
| type=tipo | parâmetro obrigatório; valores válidos: nb, string, int, float; para strings, utilize preferencialmente o tipo nb (veja explicação acima) |
| optional | quando presente, este parâmetro evita levantar uma exceção se o argumento é nulo ou inexistente |
Exemplo
O código abaixo poderia ser usado para cadastrar itens em um estoque. Supondo que os campos cod,descricao e cor são todos do tipo VARCHAR no banco de dados, este exemplo dá um tratamento diferente
para seus respectivos argumentos. O argumento cod é obrigatório; se um valor nulo ou string vazia é
fornecido, o comando dtml-sqlvar levanta uma exceção. O argumento descricao é uma string
obrigatória, mas pode ser uma string vazia (''). Finalmente o argumento cor é uma string opcional; se um
valor nulo ou vazio é fornecido, o comando dtml-sqlvar produzirá a palavra null.
INSERT INTO ESTOQUE (
cod, descricao, cor, preco, quantidade
)
VALUES (
<dtml-sqlvar cod type="nb">,
<dtml-sqlvar descricao type="string">,
<dtml-sqlvar cor type="nb" optional>,
<dtml-sqlvar preco type="float">,
<dtml-sqlvar quantidade type="int">
)
Desenvolvido por: Luciano Ramalho <ramalho@hiper.com.br>