Mostrando postagens com marcador OLE DB. Mostrar todas as postagens
Mostrando postagens com marcador OLE DB. Mostrar todas as postagens

quarta-feira, 3 de dezembro de 2008

Operação de várias etapas gerou erro. Verifique cada valor de status.

Erro genérico da middleware OLE DB

Mensagem de exceção genérica, presumo (hehehhe...), da Ole Db. A mesma mensagem pode ser recebida ao inserir, alterar, excluir, ou até mesmo consultar, dados. Diversas vezes, em empresas diferentes ao dar manutenção em softwares (alguns muito imbicados, outros nem tanto) passei por essa situação. Há algum tempo imaginei postar a respeito. A princípio, não há nada neste problema que mereça destaque. Todavia, um pequeníssimo detalhe me chamou a atenção para tal.
A pior situação que vivenciei ao receber este erro foi com a seguinte configuração: Oracle e Delphi, usando ADO. O problema acontecia quando um determinado registro era carregado para tela e em decorrência era disparada a seguinte exceção: “Operação de várias etapas gerou erro.Verifique cada valor de status”. Obviamente, esse detalhe, de que era apenas um registro específico que causava o problema, só foi percebido depois de algum tempo perdido em função de estarmos concentrados em resolver um incident report, o qual descrevia que a determinada tela do sistema não estava funcionando. A tela do sistema não esta funcionando! Anexo ao incidente havia um screenshot com a mensagem de exceção e mais nada.
Apesar de estarmos certos de que o problema era o registro, foi bastante cansativo e demorado encontrar em que dado específico estava a causa do problema. A tela em questão envolvia pelo menos três tabelas tabelas. Sendo que duas delas (as principais, master/detail) possuíam, cada uma, mais de duzentas colunas. Contudo, num dos casos isso foi totalmente relevante porque por mais que observássemos os dados o problema era invisível. Como??? Pois é, isso mesmo, não dava pra perceber simplesmente usando um “select * from ... where ...”


Processo de identificação do dado defeituoso:


Primeiro, num ClientDataset de cada vez, excluímos todos os TFields. Em seguida fomos adicionando um TField de cada vez, então ativávamos o ClientDataset. e testávamos. Assim , encontramos o dado que apresentava problema. Não era possível ver o valor nem debugar. A exceção era disparada imediatamente ao método “Open” ser chamado. É claro, suponho que alguém possa sugerir, debuggar nas units do Delphi (a própria ADODb). Todavia, no nosso caso isso seria pouco eficiente porque nesta situação específica, os desenvolvedores originais do sistema haviam alterado a ADODb. Além de outras units importantes (é desnecessário dizer que eles haviam prejudicado bastante a biblioteca. Na minha opinião, destruíram as units). Além disso, haviam várias chamadas a centenas de funções bizonhas, desnecessárias e confusas (uma rara coletânea de bozosorts anhinhados). Era uma verdadeira "visão Dantesca" (Deus me livre e guarde!). Seria muito simplório também imaginar ser possível substituí-las pelas originais (Existem loucos que conseguem inventar problemas mais complicados que as soluções seriam, se fossem razoavelmente implementadas). Pode acreditar, eu tenho vergonha de mencionar essas coisas e trazer a luz fatos como esses...
Em fim, não era possível na aplicação Delphi identificar o valor do campo, tratava-se de um campo “Date”, ou “DateTime”. Tranqüilo, basta um select do PL/SQL Developer!! Que nada. "Select" feito, a data estava lá, perfeita. Sei lá, tipo “12/11/05”. Ué tudo certo com esse valor?!??!?! Parece que sim ... então ...
O Que fazer? Cara, faz um cast para string ... resultado: “12/11/00”. Só que, cansado, na tensão do problema, é bem provável que vc não consiga distinguir entre “12/11/00” e “12/11/05”. Mas, se usar a mascará “DD/MM/YYYY” fica mais fácil ver a diferença entre “12/11/0005” e “12/11/2005”.


Pois é amigo, isso acontece! Vi muita gente boa perder tempo e stress nessa sinuca de bico.
Hoje, ao socorrer um amigo, tive tempo para documentar e fiz alguns screenshots para demonstrar o problema.



Forçando a exceção em tempo de projeto: Atribuindo valor para o parâmetro, em seguida ativando o ClientDataset.




Exceção, mensagem, recebida no Delphi ...




No Oracle, o que víamos não apresenta falha. A data, aparentemente, era exibida como se estivesse correta ...




Não confiando no que os olhos viam superficialmente, procuramos um pouco mais....



Alteramos a data e tudo ficou bem. Problema resolvido! Só que, hoje, sem sofrimento.

update CadZnCotryDm set dat_nasc= to_date('25/03/2004', 'dd/mm/yyyy')
where cic='0618XXXXXX019X';

Isso também aconteceu, anteriormente com campos strings cujo valor possuía caracteres acentuados que de alguma forma ao serem persistidos eram substituídos por caracteres estranhos, tipo. “Maria das Gra¿as”.
Esse problema encontra-se dentro do escopo de problemas que descrevi no artigo “Banco de Dados ou Bando de Dados”. Esse é mais um exemplo de como poderá ser custoso no futuro para uma empresa manter um banco de dados o qual aceita que qualquer valor possa ser inserido nele. A qualquer momento caba entrando lixo, visto que não existem regras que determinem o que pode ou não ser persistido em suas tabelas.

Artigo completo (View Full Post)

terça-feira, 4 de novembro de 2008

VB6 & Oracle - Inserindo Dados

O Modelo Cliente/Servidor caracteriza-se por centralizar grande parte dos processos de um sistema de informação no SGBD. Esses processos são programados em forma de UDFs, Procedures e Triggers.
Neste artigo veremos como criar um procedure no Oracle, para em seguida desenvolvermos um aplicativo em VB6 o qual ira chamar a execução da mesma.

Iniciaremos então pelo banco de dados, veja abaixo o código da procedure:



Back-End Script de Banco de Dados

-- Criando a tabela para testar insert apartir de um front-end vb6
CREATE TABLE TESTEGM (
COD VARCHAR(3),
DESCRICAO VARCHAR(10)

);

-- Procedimento para insert - exemplo de arquitetura cliente/servidor
CREATE OR REPLACE PROCEDURE PROC_TESTE_GM (
PCOD IN VARCHAR,
PDESCRICAO IN VARCHAR,
PAUX_RETORNO OUT VARCHAR)
AS
BEGIN
INSERT INTO TESTEGM
(COD, DESCRICAO) VALUES (PCOD, PDESCRICAO);
COMMIT;
PAUX_retorno := 'OPERAÇÃO REALIZADA COM SUCESSO!';
EXCEPTION
WHEN OTHERS THEN
PAUX_retorno := SQLERRM || ' INSERT DISTRATO ERRO.' ; -- RETORNA A MENSAGEM COM CÓDIGO E DESCRIÇÃO DO ERRO ORACLE
ROLLBACK;

END;


Front-end Código do lado Cliente

Neste exemplo, aproveito para demostrar um pouco de modularização. Criei duas funcionalidades, uma que se preocupa com a conexão com o banco, "GetConnectionCRUD". Essa "Sub" retorna um "ADO Command" conectado e configurado como "procedure". Uma segunda rotina, "BuildParams", monta os parâmetros do "ADO Command" (o qual recebe como parâmetro) dinamicamente e atribui valor para os mesmos.


Private Sub BuildParams(ByRef obj As ADODB.Command)
'Cria o parâmetro de saida no commad
objInsertCommandAux.Parameters.Append objInsertCommandAux.CreateParameter("PAUX_RETORNO", adBSTR, adParamOutput)
'Cria os parâmetros de entrada nos commads já passando o valor para eles
obj.Parameters.Append objInsertCommandAux.CreateParameter("PCOD", adBSTR, adParamInput, , Text1.Text)
obj.Parameters.Append objInsertCommandAux.CreateParameter("PDESCRICAO", adBSTR, adParamInput, , Text2.Text)

End Sub

Private Function GetConnectionCRUD() As ADODB.Command
' Retorna um objeto (um activeX) ja conectado ao banco
' Dessa forma podemos de forma coesa, isolada definir uma conexão com o banco
Dim objCommandAux As New ADODB.Command
Dim MyConn As New ADODB.Connection

MyConn.Open "Provider=MSDAORA;Data Source=desvbancoteste;User ID=sistema;Password=landjah;"
objCommandAux.ActiveConnection = MyConn
objCommandAux.CommandType = adCmdStoredProc
Set GetConnectionCRUD = objCommandAux

End Function


Em seguinda codificaremos no evento OnClick de um botão a execução dos procedimentos, "Caixa Preta", descritos acima.


Dim objInsertCommandAux As New ADODB.Command
Dim AuxRetorno As String
' Para trabalar desconectado do banco ...
Dim rsZN As New ADODB.Recordset

Private Sub Command1_Click()

Set objInsertCommandAux = GetConnectionCRUD()
' Define que o command criado executará proc de Insert
objInsertCommandAux.CommandText = "PROC_TESTE_GM"


BuildParams objInsertCommandAux

'*** Executando a stored procedure
objInsertCommandAux.Execute

MsgBox (objInsertCommandAux("PAUX_RETORNO"))

'Destroi o objeto
Set objInsertCommandAux = Nothing

End Sub


Artigo completo (View Full Post)

 
BlogBlogs.Com.Br