Artigo
· Maio 12, 2023 5min de leitura

Turbinando Consultas com Custom Class Queries

Quem nunca deve ter passado pela seguinte situação:

Tenho uma aplicação/global de configuração que não está e não pode ser mapeada para uma classe, porém é necessário fornecer uma procedure específica para que uma ferramenta de relatório usando ODBC ou JDBC ou ainda utilização de resultset do prório IRIS, possa ter acesso aos dados e gerar o relatório.

No IRIS temos uma funcionalidade que nos permite criar uma query que pode ser acessada internamente e também ser exposta como uma stored procedure, com nossa própria lógica. Essa funcionalidade é Custom Class Query.

Para definir uma Custom Class Query devemos serguir os seguintes passos (que podemos fazer uma analogia a implementar uma interface):

  1. Criar a assinatura da Custom Query na Classe com a dfinição dos parâmetros da da especificação das colunas a serem retornadas;
  2. Implementar o método de classe nomeQueryExecute;
  3. Implementar o método de classe nomeQueryFetch;
  4. Implementar o método de classe nomeQueryClose.

Para o nosso exemplo vamos criar uma global hipotética com um nó e 4 pieces. O nó será autoincrementado e os pieces serão alfanuméricos. Abaixo o código para gerar a nossa global de exemplo com  1000 linhas.

PopularGlobal() Public
{
	#Dim contadorLinha As %Integer = 0
	For contadorLinha = 1 : 1 : 1000
	{
		#Dim linha			As %String	= ""
		#Dim contatorColuna	As %Integer	= 0
		For contatorColuna = 1 : 1 : 4
		{
			Set linha = $Get(linha) _ "^" _  $Replace($Replace("coluna Y linha X", "Y", contatorColuna), "X", contadorLinha)
		}
		Set ^CustomQueryTest(contadorLinha) = $Piece(linha, "^", 2, *)
	}
}

Assinatura da Custom Class Query

Query ListarDados() As %Query(ROWSPEC = "ID:%Integer,Coluna1:%String,Coluna2:%String,Coluna3:%String,Coluna4:%String") [ SqlProc ]
{
}

O corpo deve ser deixado em branco. O tipo de retorno deve ser %Query e não %SQLQuery que é utilizada com comandos SQL. O parâmetro ROWSPEC contém os metadados da linha que será retornada no formato NomeColuna:TipoDeDado.

Método ListarDadosExecute

ClassMethod ListarDadosExecute(ByRef qHandle As %Binary) As %Status
{
  // Define a global que contém os dados
  Set qHandle		= "^CustomQueryTest"
  // Define o último nó acessado
  Set qHandle(1)	= ""
  //
  Return $System.Status.OK()
}

Neste método devemos executar a inicialização da nossa Custom Class Query, podemos fazer chamadas à código interno, abrir conexões externas, etc. O parâmetro qHandle é o ponteiro para o "cursor".

Método ListarDadosExecute

ClassMethod ListarDadosFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = ListarDadosExecute ]
{
	#Dim statusCode	As %Status	= $System.Status.OK()
	// Recupera o próximo nó a ser acessado
	#Dim indice		As %Integer	= $Order(@qHandle@(qHandle(1)))
	// Caso não tenha mais dados set aflag indicando que os dados acabaram e retorna
	If (indice = "")
	{
		Set AtEnd	= 1
		Set Row		= ""
		//
		Return statusCode
	}
	// Recupera os dados da global
	#Dim linha As %String = $Get(@qHandle@(indice))
	// Define a linha a ser retorna
	Set Row			= $ListBuild(indice, $Piece(linha, "^", 1), $Piece(linha, "^", 2), $Piece(linha, "^", 2), $Piece(linha, "^", 2))
	// Definie último nó acessado
	Set qHandle(1)	= indice
	//
	Return statusCode
}

Este é o principal método onde conterá toda a lógica de montagem, transformação, adaptação dos dados a serem retornados. Quando se usa ResultSet por exmplo, este método é chamado toda vez que o método %Next é executado.

  • O parâmetro qHandle é o ponteiro para o "cursor" que foi inicializado no método ListarDadosExecute;
  • O parâmetro Row é a referência para a linha contendo os dados;
  • O parâmetro AtEnd indica se existe ou não mais dados a serem retornados.
Importante quando não houverem mais dados a serem retornados além de atribuir 0 para AtEnd, deve-se atribuir vazio a Row, sem isso  as chamadas via ODBC ou JDBC podem se comportar de forma inesperada.

Método ListarDadosClose

ClassMethod ListarDadosClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = ListarDadosExecute ]
{
  // Limpa o handle
  Kill qHandle
  //
  Return $System.Status.OK()
}

Neste método fazemos a liberação de todos os recursos utilizados como por exemplo: fechar conexões, limpar globais temporárias,  liberar arquivos abertos, etc.

Abaixo a definição completa da classe com todos métodos

Class cjs.concurso.CustomQuery
{

Query ListarDados() As %Query(ROWSPEC = "ID:%Integer,Coluna1:%String,Coluna2:%String,Coluna3:%String,Coluna4:%String") [ SqlProc ]
{
}

ClassMethod ListarDadosExecute(ByRef qHandle As %Binary) As %Status
{
	// Define a global que contém os dados
	Set qHandle		= "^CustomQueryTest"
	// Define o último nó acessado
	Set qHandle(1)	= ""
	//
	Return $System.Status.OK()
}

ClassMethod ListarDadosClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = ListarDadosExecute ]
{
	// Limpa o handle
	Kill qHandle
	//
	Return $System.Status.OK()
}

ClassMethod ListarDadosFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = ListarDadosExecute ]
{
	#Dim statusCode	As %Status	= $System.Status.OK()
	// Recupera o próximo nó a ser acessado
	#Dim indice		As %Integer	= $Order(@qHandle@(qHandle(1)))
	// Caso não tenha mais dados set aflag indicando que os dados acabaram e retorna
	If (indice = "")
	{
		Set AtEnd	= 1
		Set Row		= ""
		//
		Return statusCode
	}
	// Recupera os dados da global
	#Dim linha As %String = $Get(@qHandle@(indice))
	// Define a linha a ser retorna
	Set Row			= $ListBuild(indice, $Piece(linha, "^", 1), $Piece(linha, "^", 2), $Piece(linha, "^", 2), $Piece(linha, "^", 2))
	// Definie último nó acessado
	Set qHandle(1)	= indice
	//
	Return statusCode
}

}

Exemplo de excução utilizando conexção JDBC com o DBeaver

Custom Class Query é uma ferramenta extremamente poderosa, podemos utilizá-la em diversas outras situações como:

  • Queries com lógica extremamamente complexas;
  • Acesso à fontes de dados externas como arquivos, chamadas à serviços REST, SOAP, etc;
  • Requisitos especifícos de seguraça;
  • Data mushup; 
  • Entre outros.

Qualquer dúvida ou sugestão por favor não exitem em interagir.

Para mais informações consulte a documentação Defining Custom Class Queries

Discussão (0)1
Entre ou crie uma conta para continuar