Aquest error es un bug de FoxPro i afecta a totes les versions desde la 7 a la 9, es produiex sempre que es dongui una excepció de la base de dades dins un stored procedure, pot ser un deadlock o una excepcio controlada per programa. L'error esta comprobat que falla conectant a altes base de dades (SQLSERVER) i fent servir altres controladors ODBC.
L'error es produeix unicament si criden la SP amb select. ( SELECT * FROM NOM_STORED_PROCEDURE() ) o si es crida directament amb EXECUTE BLOCK.
Com reproduir el bug:
Set Console On
gpconstring="DRIVER=Firebird/InterBase(r) driver;UID=sysdba;PWD=masterkey;DBNAME=roby:f:\bd\ADSGEST.FDB"
nconh= Sqlstringconnect(gpconstring)
Clear
? "Connectat"
m_codi=1
TEXT TO lcsql TEXTMERGE NOSHOW PRETEXT 7
execute block (
id integer =?m_codi)
returns (
codi integer,
nom varchar(40))
AS
begin
for select empresa,nom_emp from sys_empreses
into :codi ,:nom
do begin
exception ERR_DADES_BD 'Error provocat desde un SP';
suspend;
end
end
ENDTEXT
aux=sqlprepare(nconh, lcsql,"SQLRESULT")
If aux<>-1
If sqlexec(nconh)=-1
? "ERROR: "+Message()
Else
Browse
Endif
Else
? Message()
Endif
= sqldisconnect(nconh)
Un cop es produiex aquest error, FoxPro deixa de funcionar correctament i totes les consultes a la base de dades donen aquest error,i la única opcio per continuar treballant es sortir del programa i tornar a entrar.
Si comentem la linia 19 , així
/* exception ERR_DADES_BD 'Error provocat desde un SP'; */
i tornem a executar el programa ens hauria de tornar un cursor amb les dades de les empreses de l'aplicació.
Com solucionar-ho:
Degut a que les crides a objectes ADO no es veuen afectades per aquest bug, el que s'ha fet es simular una crida SQL ODBC fent servir els objectes ADO, s'ha creat una nova funcio SQLEXEC_ADO, que sera que la haurem de cridar en comptes de la propia de fox.
Afegir aquesta funció a LIBPROCS.PRG
Function SQLEXEC_ADO
Parameters m_con,m_sql,m_cursor
If Parameters()=2
m_cursor=''
Endif
sterr=1
LOCAL closeconection
IF VARTYPE(oconn)<>'O' && Si l'ojecte de la conexio no existeix el crea de nou
closeconection=.t.
Local oconn As adodb.Connection
oconn = Createobject("ADODB.Connection")
oconn.connectionstring = gpconstring
oconn.Open()
else
closeconection=.f.
ENDIF
Local ocommand As adodb.Command
ocommand = Createobject("ADODB.Command")
ocommand.activeconnection = oconn
Local ors As adodb.recordset
ors = Createobject("ADODB.RecordSet")
ors.activeconnection = oconn
Local oca As CursorAdapter
oca = Createobject("CursorAdapter")
oca.Alias = "CURSORADAPTER1" && m_cursor
oca.DataSourceType = "ADO"
oca.Datasource = ors
oca.SelectCmd = m_sql
If !oca.CursorFill(,,,ocommand)
= Aerror(aerrorarray)
IF aerrorarray(1)<>1463 && El cursor no retorna dades
sterr=-1
ELSE
sterr=1
endif
ELSE
SELECT CURSORADAPTER1
try
GO bottom
CATCH
ENDtry
SELECT * FROM CURSORADAPTER1 INTO CURSOR (M_CURSOR) readwrite
USE IN CURSORADAPTER1
oca.CursorDetach()
sterr=1
Endif
RELEASE OCA,ORS,OCOMMAND
IF closeconnection=.t.
RELEASE oconn
ENDIF
Return sterr
1. Afegir variable publica al programa ADSGEST.PRG i a INICI.PRG
PUBLIC gpconstring
2. Afegir la seguent linia despres de crear la cadena de conexión:
aux = goReg.GetIniEntry(lcROLE,"BaseDades","ROLE",lcNomINI)
lcCadCon = "DSN=" + lcDSN + ";UID=" + lcUID + ";PWD=" + lcPWD + ";DB=" + lcDB + ";DBNAME=" + lcDB + ";ROLE=" + lcROLE
gpconstring = lcCadCon
3. Afegir la seguent linia despres de crear la cadena de conexión a INICI.PRG:
** lcaux = "DSN=IB_FOX;UID=pere;PWD=pere;DB=ROBY:f:\BD\adsgest.fdb;ROLE=TOTS;"
lcaux = "DSN=IB_FOX;UID=pere;PWD=pere;DB=ROBY:f:\BD\adsgest.fdb;ROLE=TOTS;DBNAME=ROBY:f:\BD\adsgest.fdb;"
gpconstring= lcaux
Nota: En cas de tenir dues bases de dades obertes a l'hora, será necesari tenir dues variables gpconstring diferentes.
4. Modificar les crides a stored procedures de l'aplicació.
Unicament s'haurien de modificar les que cridem amb SELECT * FROM NOM_ STORED_PROCEDURE()
EXEMPLE:
aux = sqlexec(lnCon,"select * from V_LLISTAT(?gpEmpresa,?gpCanal,?lndatini,?lndatfin)","sqlresult")
cambiar a :
aux = sqlexec_ado(lnCon,"select * from V_LLISTAT(?gpEmpresa,?gpCanal,?lndatini,?lndatfin)","sqlresult")
Nota: La nova funcio sqlexec_ado, no utilitza el mateix identificador de conexió que fem servir (hcon, thisform.conexio) a l'aplicació, cada cop que es crida crea una nova conexio a la base de dades i un cop finalitzat el proces tanca la conexió. Per aquest motiu els comanments que es cridin d'aquesta menera no es veuran afectat per l'estat de les transaccions (auto o manual) en cas d'haverles canviat manualament en un proces anterior.
Si es volem crear un proces amb multiples crides a Stored procedures englobades en una única transacció, haurem de iniciar la conexio ADO manualment i tambe finalitzar-la amb un COMMIT O ROLLBACK.
Exemple:
gpconstring="DRIVER=Firebird/InterBase(r) driver;UID=sysdba;PWD=masterkey;DBNAME=roby:f:\bd\ADSGEST.FDB"
oconn = Createobject("ADODB.Connection")
oconn.connectionstring = gpconstring
oconn.Open() && Obre conexio
oconn.BeginTrans && Inicia transaccio
lcerr=.f.
aux = sqlexec_ado(lnCon,"select * from V_PROCES1(?gpEmpresa,?gpCanal,?lndatini,?lndatfin)","sqlresult")
if aux=-1
lcerr=.t. && Es produieix error
endif
aux = sqlexec_ado(lnCon,"select * from V_PROCES2(?gpEmpresa,?gpCanal,?lndatini,?lndatfin)","sqlresult")
if aux=-1
lcerr=.t. && Es produeix error
endif
aux = sqlexec_ado(lnCon,"select * from V_PROCES3(?gpEmpresa,?gpCanal,?lndatini,?lndatfin)","sqlresult")
if aux=-1
lcerr=.t. && Es produeix error
endif
if lcerr=.f.
oconn.Committrans && Tot correcte
else
oconn.Rollbacktrans && Desfa tots els procesos
endif
release oconn && Tanca conexió