Página 1 de 2

ADO: Perdendo conexão

Enviado: 03 Dez 2021 23:11
por cjp
Pessoal, a linha oRs:requery(), da qual tratamos aqui, eu acresci em uma outra função do meu sistema. E, nessa outra função, está dando o seguinte erro:
Error WINOLE/1007 [ma-3.1.6][5.6.49-log]MySQL server has gone away (0x80004005): Microsoft OLE DB Provider for ODBC Drivers
Não é uma vez ou outra. Nessa função, sempre ocorre este erro.

Estranho, porque na função de que tratamos aqui neste post, a requery() funciona bem.

A função onde está dando erro está assim:

Código: Selecionar todos

	  elseif nkey == -18
	         nVez++
		     chmfunc("veronus",,"S")
			 if nVez>5
		        keyb chr(27)
			 else
		        oRs:requery()
			 endif
Pensei ser erro na função veronus(), mas não achei nenhum problema nela. Posto aqui para quem quiser conferir:

Código: Selecionar todos

function veronus
         local nrserv :=3
		 local cProc, nronus
		 local tpon :=0
		 private conexao2
		 
         cadativ("Veronus","Veronus: início"," ",0," ",0,0," ")
		 
         IF ADOconecta( nrserv, 3, 2 )
            cProc := conexao2:Execute( "SELECT numero,tempo FROM onus WHERE usuario='"+us+"' AND resolvido<>'S' AND data='"+dtsql(date())+"'" )
			if cProc:recordcount()=0
               cadativ("Veronus","Não tem ônus"," ",0," ",0,0," ")
			   return .f.
			endif
	        tpon=cProc:Fields("tempo"):Value
		    nronus=cProc:Fields("numero"):Value
            cProc:Close()
            conexao2:Close()
			
            hrintar=hrintar+tpon
			
            exqado("UPDATE onus SET resolvido='S' WHERE numero="+alltrim(str(nronus)),nrserv)
			
            cadativ("Veronus","Veronus pegou ônus de "+alltrim(str(tpon))+" e aumentou o hrintar para "+alltrim(str(hrintar))," ",0," ",0,0," ")
		 endif
		 
		 nHrInicAtiv=nHrInicAtiv+tpon+5
return .t.


Alguém saberia me dizer qual a razão desse erro?

tbrowse: atualização da tela

Enviado: 04 Dez 2021 03:21
por alxsts
Olá!

Na função veronus você fecha a conexão. Quando ela retorna você faz um requery com a conexão fechada. Como quer que funcione?

tbrowse: atualização da tela

Enviado: 04 Dez 2021 10:53
por cjp
Mas eu fecho conexao2, não oRs. São conexões diferentes, e inclusive com tabelas diferentes. Não está certo assim? Ou não dá pra fazer duas conexões diferentes?

tbrowse: atualização da tela

Enviado: 04 Dez 2021 16:35
por alxsts
Olá!

O código não está completo e é confuso. Então, não é possível ver muita coisa...
cjp escreveu:MySQL server has gone away (0x80004005):
Uma coisa é certa: o banco de dados está desconectado. A mensagem é clara. Só não sabemos em que linha o erro ocorreu.
cjp escreveu:Mas eu fecho conexao2, não oRs
Você fecha conexao2 mas, em seguida executa

Código: Selecionar todos

exqado("UPDATE onus SET resolvido='S' WHERE numero="+alltrim(str(nronus)),nrserv)
Não se sabe em que conexão é feito isto mas, se for conexao2, ela está fechada. Verifique. Pode estar aí o problema...
cjp escreveu:Ou não dá pra fazer duas conexões diferentes?
Sim, é possível. Normalmente se usa esta técnica para conectar a dois servidores diferentes. Em um mesmo servidor, normalmente não é necessário.

tbrowse: atualização da tela

Enviado: 04 Dez 2021 21:53
por cjp
É o mesmo servidor. Só são tabelas diferentes.
Realmente eu poderia fazer na mesma conexão sem fechar, não tinha pensado nisso.
A linha que vc questionou (exqado("UPDATE onus SET resolvido='S' WHERE numero="+alltrim(str(nronus)),nrserv)) executa outra conexão. Vou verificar se por acaso não é ela que está fechando a conexão anterior, mas acho que não. Já posto.

tbrowse: atualização da tela

Enviado: 06 Dez 2021 16:36
por cjp
Confirmei que a exqado executa outra conexão, e não fecha a conexão que está dando problema.

tbrowse: atualização da tela

Enviado: 06 Dez 2021 23:16
por alxsts
Olá!
cjp escreveu:Confirmei que a exqado executa outra conexão, e não fecha a conexão que está dando problema.
Que bom! O código completo está em tuas mãos. Revise tudo e certamente vai encontrar o problema. Agora é com você. Boa sorte!

tbrowse: atualização da tela

Enviado: 16 Dez 2021 03:35
por JoséQuintas
ado.png
NÃO É PRA COPIAR.

Com certeza, cnSQL é uma conexão, e cnInternet é outra.
No seu fonte, o que é o que? Dá pra ver? Como dizer se está certo ou errado?

Qual o problema?
Pode ser que caiu a conexão, pode ser que está usando ODBC errado, pode ser que o Windows está derrubando, pode ser fonte errado que não se vê, pode ser qualquer coisa.
Vai ter que olhar com muita atenção o que suas rotinas fazem, porque o trecho postado não diz nada.

o browse genérico da outra vez deveria ser um ponto de referência pra fazer outras coisas.

Variáveis PRIVATE?
VIXE... piorou.

Código: Selecionar todos

       private conexao2
         cadativ("Veronus","Veronus: início"," ",0," ",0,0," ")
         IF ADOconecta( nrserv, 3, 2 )
            cProc := conexao2:Execute( "SELECT numero,tempo FROM onus WHERE usuario='"+us+"' AND resolvido<>'S' AND data='"+dtsql(date())+"'" )
É criada a variável conexao2, e é executado um comando nessa variável vazia.
Não é vazia?
Ahhh tá... não sei o que, não sei aonde, faz não sei o que, mas não funciona. qual é o problema? deve ser não sei o que não sei aonde.

tbrowse: atualização da tela

Enviado: 16 Dez 2021 10:26
por alxsts
Olá!
JoséQuintas escreveu:Com certeza, cnSQL é uma conexão, e cnInternet é outra.
Neste caso, cnInternet parece ser um ADO RecordSet. O Objeto connection não tem a propriedade Eof...

tbrowse: atualização da tela

Enviado: 16 Dez 2021 12:58
por JoséQuintas
Foi só pra exemplo.
Minha classe junta tudo numa coisa só, pra facilitar e acrescentar extras.

tbrowse: atualização da tela

Enviado: 17 Dez 2021 01:51
por cjp
A minha função genérica está assim:

Código: Selecionar todos

FUNCTION TBrowseADO( oRsBrAdo, aCamposList, bExecutaRotinaUsuario )
   LOCAL oBrowse, nKey
   
   SuperADO( oRsBrAdo )
   
   oBrowse := CriaBrowse( oRsBrAdo, aCamposList )

   DO WHILE .T.
      oBrowse:refreshCurrent()
      DO WHILE ! oBrowse:Stable()
         oBrowse:Stabilize()
      ENDDO
      // Paint TBrowse current line...
      //oBrowse:ColorRect( { oBrowse:RowPos, oBrowse:LeftVisible, oBrowse:RowPos, oBrowse:RightVisible }, { 2, 1 } )


      if us="I"
         nKey := Inkey(300)
	  else
	     nKey := Inkey(100)
	  endif
	  
      IF nKey == 0
	     if us="I"
            KEYBOARD Chr( K_ESC )
            nKey := Inkey(0)
		 else
            KEYBOARD -18
		 endif
      ENDIF    
	  
      IF oBrowse:applyKey( nKey ) == TBR_EXIT
         EXIT
      ENDIF
      IF bExecutaRotinaUsuario != NIL
         DO WHILE ! oBrowse:Stable
            oBrowse:Stabilize()
         ENDDO
		 
         Eval( bExecutaRotinaUsuario, oBrowse, nKey )
         oBrowse:RefreshAll()
      ENDIF
   ENDDO
RETURN Nil

STATIC FUNCTION CriaBrowse( oRsBrAdo, aCamposList )
   LOCAL oBrowse, oColumn, aItem, nLen, nCont

   if procname(2)="BROWSEITEM"
      oBrowse := TBrowse():new( 07, 1, MaxRow() - 5, MaxCol() - 1 )
   else
      oBrowse := TBrowse():new( 02, 3, MaxRow() - 3, MaxCol() - 3 )
   endif
   
   oBrowse:headSep       := Chr(196) + Chr(194) + Chr(196)
   oBrowse:colSep        := " " + Chr(179) + " "
   oBrowse:footSep       := Chr(196) + Chr(193) + Chr(196)
   oBrowse:goTopBlock    := { || oRsBrAdo:moveFirst() }
   oBrowse:goBottomBlock := { || oRsBrAdo:moveLast() }
   oBrowse:skipBlock     := { |n| ADORecordSetSkipper( oRsBrAdo,n ) }
   
   if us#"I" .and. (procname(1)="ENTERITEM" .or. procname(2)="ENTERITEM" .or. procname(3)="ENTERITEM" .or. procname(4)="ENTERITEM" .or. procname(5)="ENTERITEM" .or. procname(6)="ENTERITEM" .or. procname(7)="ENTERITEM")
	  keysec(-18,115,-1,.t.)
   else
	  keysec(27,1200,-1,.t.)
   endif
		 
   IF aCamposList == Nil
      nLen := oRsBrAdo:fields():count() - 1
      FOR nCont := 0 TO nLen
         oColumn := TBColumnNew( oRsBrAdo:fields( nCont ):name(), ADORecordSetFieldBlock( oRsBrAdo, nCont ) )
         oColumn:width := Max( Min( oRsBrAdo:Fields( nCont ):definedSize,50), Len( oRsBrAdo:fields( nCont ):name ) ) + 5
         oBrowse:addColumn( oColumn )
      NEXT
   ELSE
      FOR EACH aItem IN aCamposList
         oColumn := TBColumnNew( aItem[1], aItem[2] )
         IF Len( aItem ) > 2
            oColumn:ColorBlock := aItem[3]
         ENDIF
         oBrowse:AddColumn( oColumn )
      NEXT
   ENDIF
RETURN oBrowse

FUNCTION SuperADO( oRsBrAdo )
   __ObjAddMethod( oRsBrAdo, "TOSTRING", @ADOToString() )
   __ObjAddMethod( oRsBrAdo, "TONUMBER", @ADOToNumber() )
   __ObjAddMethod( oRsBrAdo, "TODATE",   @ADOToDate() )
   __ObjAddMethod( oRsBrAdo, "TOSTR",    @ADOToStr() )

RETURN Nil

STATIC FUNCTION ADOToDate( cField )
   LOCAL x, Self := QSelf()

   x := ::Fields( cField ):Value
   IF ValType( x ) != "D"
      x := Ctod("")
   ENDIF
RETURN x

STATIC FUNCTION ADOToString( cField, nLen )
   LOCAL x, Self := QSelf()

   x := ::Fields( cField ):Value
   IF ValType( x ) != "C"
      x := ""
   ENDIF
   IF nLen != Nil
      x := Pad( x, nLen )
   ENDIF
RETURN x

STATIC FUNCTION ADOToNumber( cField )
   LOCAL x, Self := QSelf()

   x := ::Fields( cField ):Value
   IF ValType( x ) != "N"
      x := 0
   ENDIF
RETURN x

STATIC FUNCTION ADOToStr( cField, nLen, nDec )
   LOCAL x, Self := QSelf()

   x := ::Fields( cField ):Value
   IF ValType( x ) != "N"
      x := 0
   ENDIF
   IF nLen == Nil
      x := Str( x )
   ELSEIF nDec == Nil
      x := Str( x, nLen )
   ELSE
      x := Str( x, nLen, nDec )
   ENDIF
RETURN x

FUNCTION ConexaoMySQL( cServer, cDatabase, cUser, cPassword )
       LOCAL oCn := win_OleCreateObject("ADODB.Connection")

       oCn:ConnectionString := "DRIVER={MariaDB ODBC 3.1 Driver};TCPIP=1;SERVER=" + ;
        cServer + ";Database=" + cDatabase + ";UID=" + cUser + ";PWD=" + cPassword + ";PORT=3306"
       oCn:CursorLocation   := 3
RETURN oCn

Alterei oRs para oRsBrAdo para não ter a menor chance de ela ser alterada em nenhuma outra parte do sistema.

Na função de usuário tem também:
LOCAL oCnBrAdo :=ConexaoMySQL( "mysql.meuprovedor.com.br", "xxx", "xxx", "senha" )
A variável conexao2 está como private porque a Adoconecta usa ela, assim:

Código: Selecionar todos

function ADOconecta(nProvedor,nTentativas,nConexao)
         local vez :=0
		 local cRet :="F"
		 
		 nTentativas=2  // para diminuir tentativas quando não tiver conexão
		 
         bError := ErrorBlock( {|e| Break(e) } )
         begin sequence
		       if nProvedor=NIL
		          nProvedor=3
		       endif
		 
		       if nTentativas=NIL
		          nTentativas=3
		       endif
		 
         recover using e
                 cMessage := ErrorMessage(e)
                 logerro()
                 mandmail1("error.log","Erro na ADOconnecta1 contornado com o recover do begin sequence")
         endsequence
         ErrorBlock( bError )
		 
         do while .t.
		 
            bError := ErrorBlock( {|e| Break(e) } )
            begin sequence
		 
		          vez++
			      if vez>1
			         @ maxrow()-1,1 say "Tentando conexão com o servidor "+alltrim(str(nProvedor))+" ("+alltrim(str(vez))+"/"+alltrim(str(nTentativas+1))+")"
			         inkey(5.1)
			      endif
			
            recover using e
                    cMessage := ErrorMessage(e)
                    logerro()
                    mandmail1("error.log","Erro na ADOconnecta2 contornado com o recover do begin sequence")
            endsequence
            ErrorBlock( bError )
		 
			if vez>nTentativas
			   if nProvedor=12 .or. nProvedor=1 //.and. ("AGENDA"$Hb_CmdArgArgV() .or. "PROCESSO"$Hb_CmdArgArgV())
				  mandmail1("","Mudou nProvAtiv para 3 na ADOconecta porque não funcionou no "+alltrim(str(nProvedor)))
			      nProvAtiv=3
			   endif
			   return .f.
			endif
			
            bError := ErrorBlock( {|e| Break(e) } )
            begin sequence
		 
                  if nConexao=2
				     conexao2 := conexADO(nProvedor)
				  else
				     conexao := conexADO(nProvedor)
			      endif
			
            recover using e
                    cMessage := ErrorMessage(e)
                    logerro()
                    mandmail1("error.log","Erro na ADOconnecta3 contornado com o recover do begin sequence")
            endsequence
            ErrorBlock( bError )
			
            bError := ErrorBlock( {|e| Break(e) } )
            begin sequence
                  IF (nConexao=2 .and. AbreADO(conexao2)) .or. AbreADO( conexao )
			         if vez>1
			            @ maxrow()-1,1 clear to maxrow()-1,40
			         endif
			         cRet="T"
			      Endif
            recover using e
                    cMessage := ErrorMessage(e)
                    logerro()
                    mandmail1("error.log","Erro na ADOconnecta4 contornado com o recover do begin sequence")
            endsequence
            ErrorBlock( bError )
			
			if cRet="T"
			   return .t.
			endif
			
		 enddo
return .t.
Talvez dê para melhorar isso.
Mas, ao que me parece (me corrija se eu estiver errado), nem a veronus() nem a adoconecta() está encerrando a conexão do browse.
Eu não creio que o problema seja queda de conexão, ou ODBC errado, ou Windows derrubando, pois o problema tem ocorrido somente neste ponto do sistema, e sempre no mesmo ponto. Além disso, só passou a dar este problema depois que eu mudei para este browse genérico (quando eu uso o meu browse anterior, aquele confuso, não ocorre o problema).
Consegue dar uma luz de onde eu poderia procurar o problema? Aqui não estou enxergando nada.

tbrowse: atualização da tela

Enviado: 17 Dez 2021 16:06
por JoséQuintas
Compilação -w3 -es2, evitar private.....

Código: Selecionar todos

   private conexao2
   cadativ("Veronus","Veronus: início"," ",0," ",0,0," ")
   IF ADOconecta( nrserv, 3, 2 )
...
function ADOconecta(nProvedor,nTentativas,nConexao)
que tal assim?

Código: Selecionar todos

LOCAL conexao2
IF ADOConecta( nServ, 3, 2, @Conexao2 )
...
function ADOconecta(nProvedor,nTentativas,nConexao, @oQualquerNome)
E lá voltamos a falar sobre usar variável PRIVATE, sobre deixar função presa com variável que não é dela, etc. etc. etc.
Mesmo assunto, mesma coisa, em outra função.

Função é... função. Tem que ser independente.

Usa no aplicativo inteiro... o aplicativo inteiro depende disso... e isso usa não se sabe o que, que vém e vai não se sabe onde.

tbrowse: atualização da tela

Enviado: 17 Dez 2021 16:14
por JoséQuintas
Antes que digam..... parece o mesmo.... mas é totalmente diferente.

Na primeira rotina Conexao2 nasce ali, e morre ali, e só é visível ali.

Na função, ela recebe uma variável e retorna preenchida. sabe-se que veio como parâmetro e volta exatamente para o mesmo lugar de onde veio.
Ainda não é o ideal, mas já melhora muuuito.

tbrowse: atualização da tela

Enviado: 17 Dez 2021 16:21
por JoséQuintas
cjp escreveu:Alterei oRs para oRsBrAdo para não ter a menor chance de ela ser alterada em nenhuma outra parte do sistema.
Só veio na cabeça: Put. que par....

ISSO É VARIÁVEL LOCAL.

Pode alterar os raios que o partam no aplicativo inteiro, que isso NUNCA vai mudar.

Faça a comprovação:

Código: Selecionar todos

PROCEDURE Main

   LOCAL A

   a := "5"
   ? "main " + a
   Funcao( a )
   ? "main " + a
   Inkey(0)
   RETURN

FUNCTION FUNCAO( a )

   a := "10"
   ? "funcao " + a
   RETURN Nil
É justamente essa a diferença entre variável PRIVATE e LOCAL, e é por isso que variável LOCAL é o que tem de melhor.

Se está pensando que vai mostrar 5, 10, 10..... está ERRADO.

tbrowse: atualização da tela

Enviado: 17 Dez 2021 16:28
por JoséQuintas
Vamos um pouco mais:

Quer que mostre 5, 10, 5 é do jeito que está aí.

Quer que mostre 5, 10, 10 então é funcao( @a )

E tanto faz o nome da variável dentro da função, o resultado vai ser sempre o mesmo acima.