Chamando procedure SQL

Forum sobre SQL.

Moderador: Moderadores

Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Chamando procedure SQL

Mensagem por JoséQuintas »

Tava aqui pensando.... como integrar melhor as stored procedures com o Harbour?

Código: Selecionar todos

   :ExecuteCmd( "call ze_jptabcategoriaLicencaExclui(" + NumberSQL( nIdCategoria ) + "," + NumberSQL( nIdLicenca ) + ")" )
Depois pensei.... porque não uma função intermediária?

Código: Selecionar todos

ze_TabCategoriaLicencaExclui( nIdCategoria, nIdLicenca )
...
FUNCTION ze_TabCategoriaLicencaExclui( nIdCategoria, nIdLicenca )
   :ExecuteCmd( "call ze_jptabcategoriaLicencaExclui(" + NumberSQL( nIdCategoria ) + "," + NumberSQL( nIdLicenca ) + ")" )
   RETURN Nil
Mas vai ter muito criar conexão ou algo parecido.

Código: Selecionar todos

FUNCTION ExecuteSQLProcedure( ... )

   LOCAL cnSQL := ADOClass():New( AppConexao() )
   LOCAL cSQL, aList

   aList := hb_AParams()
   // monta a chamada de SQL e execute
   ...
Não valida o nome da procedure? mas... também não valida o texto do comando SQL, então dá no mesmo
Pode até servir para casos com e sem retorno.

Apesar de parecer quase a mesma coisa, fica melhor para o fonte.

Código: Selecionar todos

   :ExecuteCmd( "call ze_jptabcategoriaLicencaExclui(" + NumberSQL( nIdCategoria ) + "," + NumberSQL( nIdLicenca ) + ")" )

Código: Selecionar todos

ExecuteSQLProcedure( "ze_jptabcategoriaLicencaExclui", nIdCategoria, nIdLicenca )
A função de executar procedure faria a conversão dos parâmetros, conforme o tipo.
Fonte mais limpo.

Vou amadurecer essa idéia.
Dá até pra validar o nome das procedures, pra ver se existem mesmo
Um campo array STATIC, assim numa checagem seguinte basta olhar se está no array.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Chamando procedure SQL

Mensagem por JoséQuintas »

Nessas horas dá um nó na cabeça.

Seria melhor se o banco de dados tivesse uma API de acesso direto as procedures/functions?
Mas aí o SQL cai por terra.... seria melhor um objeto.

Mas o objeto cai por terra, quando lembramos que as IDEs para o Harbour não validam objetos.

É de ficar doido, mas são possibilidades... é disso que nascem novas opções.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Chamando procedure SQL

Mensagem por JoséQuintas »

Código: Selecionar todos

   cnSQL:ExecuteProcedure( "ze_PedidoCalculo", nIdPedido )

   //WITH OBJECT cnSQL
      //cnSQL:ExecuteCmd( "CALL ze_PedidoCalculo(" + NumberSQL( nIdPedido ) + ")" )
   //ENDWITH
Coloquei na classe ADO e vamos em frente.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Chamando procedure SQL

Mensagem por JoséQuintas »

Esta com mais parâmetros;

Código: Selecionar todos

   cnSQL:ExecuteProcedure( "ze_PedidoProdutoBaixaEstoque", nIdItPed, dData, ;
         nNumNotFis, cDeposito, nSomaTira, LogInfo() )

   WITH OBJECT cnSQL
      :Execute( "CALL ze_PedidoProdutoBaixaEstoque( " + ;
         NumberSQL( nIdItPed ) + ", " + DateSQL( dData ) + ", " + ;
         NumberSQL( nNumNotFis ) + ", " + StringSQL( cDeposito ) + ", " + ;
         NumberSQL( nSomaTira ) + ", " + StringSQL( LogInfo() ) + ")" )
   ENDWITH
Deixei execute, porque no resto está execute, mas pensei em CallProcedure, ou somente :Call
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Chamando procedure SQL

Mensagem por JoséQuintas »

Acabei alterando, pra ter os mesmos recursos que já tenho.

Código: Selecionar todos

METHOD ExecuteCmdProcedure( ... ) CLASS ADOClass

   LOCAL cSQL, aItem, aList := hb_AParams()

   cSQL := "CALL " + aList[ 1 ]
   hb_ADel( aList, 1, .T. )

   cSQL += "("
   FOR EACH aItem IN aList
      cSQL += ValueSQL( aItem )
      cSQL += iif( aItem:__ENumIsLast(), "", "," )
   NEXT
   cSQL += ")"
   ::ExecuteCmd( cSQL )

   RETURN Nil

METHOD ExecuteProcedure( ... ) CLASS ADOClass

   LOCAL cSQL, aItem, aList := hb_AParams()

   cSQL := "CALL " + aList[ 1 ]
   hb_ADel( aList, 1, .T. )

   cSQL += "("
   FOR EACH aItem IN aList
      cSQL += ValueSQL( aItem )
      cSQL += iif( aItem:__ENumIsLast(), "", "," )
   NEXT
   cSQL += ")"
   ::Execute( cSQL )

   RETURN Nil
Hoje tenho Execute() e ExecuteCmd()
Uma considera o recordset principal, e a outra cria um alternativo.
Então fiz igual pra procedure, porque em alguns casos, ela não deve interferir no recordset principal.

Tipo....

Código: Selecionar todos

WITH OBJECT cnSQL
   :Execute( "comando" )
   DO WHILE ! :Eof()
      :ExecuteCmd( "ddddd" )
      :MoveNext()
   ENDDO
   :CloseRecordset()
END WITH
No caso acima, o :ExecuteCmd() não causa interferência no recordset que já está aberto.
Apesar do nome, ela pode retornar recordset, mas independente do que já está em uso.

Lembrando: eu misturo conexão e recordset, numa única classe, pra facilitar.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Chamando procedure SQL

Mensagem por JoséQuintas »

stored.png
Parece até que o MySQL ficou integrado ao aplicativo, dispensando conversões.
Mas só parece, continua tudo igual antes.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Chamando procedure SQL

Mensagem por JoséQuintas »

O :Execute() da imagem anterior, esqueci de apagar.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Chamando procedure SQL

Mensagem por JoséQuintas »

Aqui mais interessante.

Fonte anterior Harbour:

Código: Selecionar todos

      WITH OBJECT cnSQL
         :cSQL := "SELECT * FROM JPITPED WHERE IPPEDIDO = " + NumberSQL( nIdPedidoJuntar )
         :Execute()
         DO WHILE ! :Eof()
            IF ADORecCount( "JPITPED", "IPPEDIDO = " + NumberSQL( nIdPedido ) + " AND IPPRODUTO = " + NumberSQL( :Number( "IPPRODUTO" ) ) ) == 0
               :QueryCreate()
               :QueryAdd( "IPPEDIDO", StrZero( nIdPedido, 6 ) )
               :QueryAdd( "IPPRODUTO", StrZero( :Number( "IPPRODUTO" ), 6 ) )
               :QueryAdd( "IPQTDE", :Number( "IPQTDE" ) )
               :QueryExecuteInsert( "JPITPED" )
            ELSE
               :ExecuteCmd( "UPDATE JPITPED SET IPQTDE = IPQTDE + " + NumberSQL( :Number( "IPQTDE" ) ) + ;
                  " WHERE IPPEDIDO = " + NumberSQL( nIdPedido ) )
            ENDIF
            :MoveNext()
         ENDDO
         :CloseRecordset()
      ENDWITH
fonte atual:

Código: Selecionar todos

      cnSQL:ExecuteCmdProcedure( "ze_PedidoJuntar", nIdPedido, nIdPedidoJuntar )
procedure:

Código: Selecionar todos

CREATE PROCEDURE ze_PedidoJuntar( nIdPedido INT(11), nIdPedidoJuntar INT(11) )

BEGIN

DECLARE nIdProduto INT(11);
DECLARE nQtde, nPrePed DECIMAL(16,4);

DECLARE nCursorEOF INT(11) DEFAULT 0;
DECLARE SP_CURSOR CURSOR FOR
   SELECT IPPRODUTO, IPQTDE, IPPREPED
   FROM JPITPED
   WHERE IPPEDIDO = nIdPedidoJuntar;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET nCursorEOF = 1;

OPEN SP_CURSOR;

THIS:WHILE nCursorEOF <> 1 DO
   FETCH SP_CURSOR INTO nIdProduto, nQtde, nPrePed;
   IF nCursorEOF = 1 THEN
      LEAVE THIS;
   END IF;
   IF ( SELECT COUNT(*) FROM JPITPED WHERE IPPEDIDO = nIdPedido AND IPPRODUTO = nIdProduto ) != 0 THEN
      UPDATE JPITPED SET IPQTDE = IPQTDE + nQtde WHERE IPPEDIDO = nIdPedido AND IPPRODUTO = nIdProduto;
   ELSE
      INSERT
      INTO JPITPED ( IPPEDIDO, IPPRODUTO, IPQTDE, IPPREPED )
      VALUES ( nIdPedido, nIdProduto, nQtde, nPrePed );
   END IF;
END WHILE;

CLOSE SP_CURSOR;

END
Coloquei pra gravar o valor, mas vou repensar.
O problema seria permitir um preço não autorizado.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Chamando procedure SQL

Mensagem por JoséQuintas »

Mais outro.

Era assim em Harbour:

Código: Selecionar todos

         WITH OBJECT cnSQL
            :ExecuteCmdProcedure( "ze_PedidoRelaciona", nIdPedido, nIdPedidoRel, ::cOpc )
/*
            IF nIdPedidoRel == 0
               :QueryCreate()
               :QueryAdd( "PDPEDREL", StrZero( 0, 6 ) )
               :QueryAdd( "PDNOTREL", StrZero( 0, 9 ) )
               :QueryExecuteUpdate( "JPPEDIDO", "IDPEDIDO = " + NumberSQL( nIdPedido ) )
            ELSE
               :cSQL := "SELECT JPPEDIDO.*, JPITPED.*, JPNOTFIS.NFNOTFIS" + ;
                  " FROM JPPEDIDO" + ;
                  " LEFT JOIN JPITPED ON JPITPED.IPPEDIDO = JPPEDIDO.IDPEDIDO" + ;
                  " LEFT JOIN JPNOTFIS ON JPNOTFIS.NFPEDIDO = JPPEDIDO.IDPEDIDO" + ;
                  " WHERE IDPEDIDO = " + NumberSQL( nIdPedidoRel )
               :Execute()
               IF :Eof()
                  :QueryCreate()
                  :QueryAdd( "PDPEDREL", StrZero( 0, 6 ) )
                  :QueryAdd( "PDNOTREL", StrZero( 0, 9 ) )
                  :QueryExecuteUpdate( "JPPEDIDO", "IDPEDIDO = " + NumberSQL( nIdPedido ) )
               ELSE
                  :QueryCreate()
                  :QueryAdd( "PDPEDREL", StrZero( nIdPedidoRel, 6 ) )
                  :QueryAdd( "PDNOTREL", StrZero( ADOField( "NFNOTFIS", "N", "JPNOTFIS", "NFPEDIDO = " + NumberSQL( nIdPedidoRel ) ), 9 ) )
                  :QueryExecuteUpdate( "JPPEDIDO", "IDPEDIDO=" + NumberSQL( nIdPedido ) )
                  IF ::cOpc == "I"
                     :QueryCreate()
                     :QueryAdd( "PDVALSEG", :Number( "PDVALSEG" ) )
                     :QueryAdd( "PDVALOUT", :Number( "PDVALOUT" ) )
                     :QueryAdd( "PDVALEXT", :Number( "PDVALEXT" ) )
                     :QueryAdd( "PDVALFRE", :Number( "PDVALFRE" ) )
                     :QueryAdd( "PDVALDES", :Number( "PDVALDES" ) )
                     :QueryExecuteUpdate( "JPPEDIDO", "IDPEDIDO = " + NumberSQL( nIdPedido ) )
                     DO WHILE ! :Eof()
                        :QueryCreate()
                        :QueryAdd( "IPPEDIDO", StrZero( nIdPedido, 6 ) )
                        FOR EACH cField IN { "IPPRODUTO", "IPTRIBUT" }
                           :QueryAdd( cField, StrZero( :Number( cField ), 6 ) )
                        NEXT
                        FOR EACH cField IN { "IPCFOP", "IPPEDCOM", "IPLEIS", "IPORIGEM", "IPIPIICM", ;
                              "IPIPICST", "IPIPIENQ", "IPICMCST", "IPDIFCAL", "IPPISCST", "IPPISENQ", ;
                              "IPCOFCST", "IPCOFENQ" }
                           :QueryAdd( cField, :String( cField ) )
                        NEXT
                        FOR EACH cField IN { "IPPRECUS", "IPPREPED", "IPQTDE", "IPVALCUS", "IPGARANTIA", ;
                              "IPPRENOT", "IPVALADI", "IPVALFRE", "IPVALSEG", "IPVALOUT", "IPVALEXT", ;
                              "IPVALADU", "IPVALIOF", "IPVALDES", "IPVALPRO", "IPVALNOT", "IPIIBAS", ;
                              "IPIIALI", "IPIIVAL", "IPIPIBAS", "IPIPIALI", "IPIPIVAL", "IPICMBAS", ;
                              "IPICMALI", "IPICMRED", "IPICMVAL", "IPFCPALI", "IPFCPVAL", "IPICSBAS", ;
                              "IPICSALI", "IPICSVAL", "IPSUBIVA", "IPSUBBAS", "IPSUBRED", "IPSUBALI", ;
                              "IPSUBVAL", "IPDIFBAS", "IPDIFALIF", "IPDIFALIU", "IPDIFALII", "IPDIFVALI", ;
                              "IPDIFVALF", "IPPISBAS", "IPPISALI", "IPPISVAL", "IPCOFBAS", "IPCOFALI", ;
                              "IPCOFVAL", "IPISSBAS", "IPISSALI", "IPISSVAL" }
                           :QueryAdd( cField, :Number( cField ) )
                        NEXT
                        :QueryExecuteInsert( "JPITPED" )
                        :MoveNext()
                     ENDDO
                  ENDIF
               ENDIF
               :CloseRecordset()
            ENDIF
            SubPedidoClass():CalculaValores( nIdPedido )
*/
            :ExecuteCmdProcedure( "ze_PedidoCalculo", nIdPedido )
         ENDWITH
Agora fica no servidor, assim em SQL

Código: Selecionar todos

CREATE PROCEDURE ze_PedidoRelaciona( nIdPedido INT(11), nIdPedidoRel INT(11), cOpcao VARCHAR(5) )

THIS:BEGIN

IF nIdPedidoRel = 0 THEN
   UPDATE
   JPPEDIDO SET
   PDPEDREL = '000000', PDNOTREL = '000000000'
   WHERE IDPEDIDO = nIdPedido;
   LEAVE THIS;
END IF;

IF ( SELECT IDPEDIDO FROM JPPEDIDO WHERE IDPEDIDO = nIdPedidoRel ) = 0 THEN
   UPDATE
   JPPEDIDO SET
   PDPEDREL = '000000', PDNOTREL = '000000000'
   WHERE IDPEDIDO = nIdPedido;
   LEAVE THIS;
END IF;

UPDATE
JPPEDIDO SET
PDPEDREL = LPAD( nIdPedidoRel, 6, '0' ),
PDNOTFIS = LPAD( COALESCE( ( SELECT NFPEDIDO FROM JPNOTFIS WHERE NFPEDIDO = nIdPedidoRel ), 0 ), 9, '0' )
WHERE IDPEDIDO = nIdPedido;

IF cOpcao != "I" THEN
   LEAVE THIS;
END IF;

UPDATE
JPPEDIDO
INNER JOIN JPPEDIDO AS B ON B.IDPEDIDO = nIdPedidoRel
SET
JPPEDIDO.PDVALSEG = COALESCE( B.PDVALSEG, 0 ),
JPPEDIDO.PDVALOUT = COALESCE( B.PDVALOUT, 0 ),
JPPEDIDO.PDVALEXT = COALESCE( B.PDVALEXT, 0 ),
JPPEDIDO.PDVALFRE = COALESCE( B.PDVALFRE, 0 ),
JPPEDIDO.PDVALDES = COALESCE( B.PDVALDES, 0 )
WHERE JPPEDIDO.IDPEDIDO = nIdPedido;

INSERT
INTO JPITPED
( IPPEDIDO, IPPRODUTO, IPTRIBUT,
IPCFOP, IPPEDCOM, IPLEIS, IPORIGEM, IPIPIICM,
IPIPICST, IPIPIENQ, IPICMCST, IPDIFCAL, IPPISCST, IPPISENQ,
IPCOFCST, IPCOFENQ,
IPPRECUS, IPPREPED, IPQTDE, IPVALCUS, IPGARANTIA,
IPPRENOT, IPVALADI, IPVALFRE, IPVALSEG, IPVALOUT, IPVALEXT,
IPVALADU, IPVALIOF, IPVALDES, IPVALPRO, IPVALNOT, IPIIBAS,
IPIIALI, IPIIVAL, IPIPIBAS, IPIPIALI, IPIPIVAL, IPICMBAS,
IPICMALI, IPICMRED, IPICMVAL, IPFCPALI, IPFCPVAL, IPICSBAS,
IPICSALI, IPICSVAL, IPSUBIVA, IPSUBBAS, IPSUBRED, IPSUBALI,
IPSUBVAL, IPDIFBAS, IPDIFALIF, IPDIFALIU, IPDIFALII, IPDIFVALI,
IPDIFVALF, IPPISBAS, IPPISALI, IPPISVAL, IPCOFBAS, IPCOFALI,
IPCOFVAL, IPISSBAS, IPISSALI, IPISSVAL )
SELECT
nIdPedido, IPPRODUTO, IPTRIBUT,
IPCFOP, IPPEDCOM, IPLEIS, IPORIGEM, IPIPIICM,
IPIPICST, IPIPIENQ, IPICMCST, IPDIFCAL, IPPISCST, IPPISENQ,
IPCOFCST, IPCOFENQ,
IPPRECUS, IPPREPED, IPQTDE, IPVALCUS, IPGARANTIA,
IPPRENOT, IPVALADI, IPVALFRE, IPVALSEG, IPVALOUT, IPVALEXT,
IPVALADU, IPVALIOF, IPVALDES, IPVALPRO, IPVALNOT, IPIIBAS,
IPIIALI, IPIIVAL, IPIPIBAS, IPIPIALI, IPIPIVAL, IPICMBAS,
IPICMALI, IPICMRED, IPICMVAL, IPFCPALI, IPFCPVAL, IPICSBAS,
IPICSALI, IPICSVAL, IPSUBIVA, IPSUBBAS, IPSUBRED, IPSUBALI,
IPSUBVAL, IPDIFBAS, IPDIFALIF, IPDIFALIU, IPDIFALII, IPDIFVALI,
IPDIFVALF, IPPISBAS, IPPISALI, IPPISVAL, IPCOFBAS, IPCOFALI,
IPCOFVAL, IPISSBAS, IPISSALI, IPISSVAL
FROM JPITPED
WHERE IPPEDIDO = nIdPedidoRel;

END
Fonte final Harbour:

Código: Selecionar todos

         WITH OBJECT cnSQL
            :ExecuteCmdProcedure( "ze_PedidoRelaciona", nIdPedido, nIdPedidoRel, ::cOpc )
            :ExecuteCmdProcedure( "ze_PedidoCalculo", nIdPedido )
         ENDWITH
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Responder