Seite 1 von 1
DbAppend()
Verfasst: Mi, 13. Nov 2019 8:55
von Jan
Moin,
ab und an, recht selten, passiert etwas merkwürdiges:
Das ergibt einen Laufzeitfehler:
oError:args :
-> VALTYPE: N VALUE:1289
oError:canDefault : .F.
oError:canRetry : .F.
oError:canSubstitute: .T.
oError:cargo : NIL
oError:description : Datei oder Datensatz mu▀ f³r Operation gesperrt sein
oError:filename :
oError:genCode : 76
oError:operation : <FELDNAME>:=<1289>
oError:osCode : 0
oError:severity : 2
oError:subCode : 8043
oError:subSystem : BASE
oError:thread : 1
oError:tries : 0
Wie gesagt, das klappt fast immer. Aber manchmal hat der anscheinend beim appenden kein Locking gemacht. Obwohl ich dachte, das ich mit der IF-Abfrage ein sauberes appenden inkl. Locking ja abfange.
Hat jemand eine Idee, was da passiert?
Jan
Re: DbAppend()
Verfasst: Mi, 13. Nov 2019 11:47
von ramses
Hallo Jan
dbAppend() gibt immer NIL zurück.
Ob ein neuer Satz angefügt werden konnte musst du immer mit neterr() prüfen!
Sonst geht dein zugriff auf den aktuellen bestehenden (nicht gelockten) Datensatz und führt zum RunTime Fehler.
Re: DbAppend()
Verfasst: Mi, 13. Nov 2019 12:04
von Koverhage
Jan,
Ab und an haben unsere Kunden diesen Fehler auch.
Das Problem ist die Index (CDX) Datei.
Re: DbAppend()
Verfasst: Mi, 13. Nov 2019 12:34
von ramses
Jan's Code ist eigentlich falsch. Es müsste
Code: Alles auswählen
cAlias->(DbAppend())
IF !neterr()
cAlias->Feldname := wert
sein.
Re: DbAppend()
Verfasst: Mi, 13. Nov 2019 12:36
von Tom
Nee, Klaus, Jan hat das Problem, das Carlo skizziert hat. "IF NIL" läuft manchmal durch, und DbAppend() retourniert eben NIL. Wenn es fehlgeschlagen ist, bekommt Jan anschließend den Fehler, weil eben kein Locking vorliegt, da ja das DbAppend() fehlschlug.
Re: DbAppend()
Verfasst: Mi, 13. Nov 2019 13:08
von Jan
Hallo,
Danke für die vielen Antworten. Die mir zeigen, das mein Codebeispiel leider, nun ja, unvollständig ist. Ich mach nämlich an dieser Stelle kein DbAppend(). Sondern rufe eine eigene Funktion auf, die so aussieht:
Code: Alles auswählen
UNCTION MyDbAppend
LOCAL lReturn := .F.
LOCAL bOldError := NIL
LOCAL nRetry := 0
LOCAL lExit := .F.
LOCAL e := NIL
LOCAL cStack := ""
UnUsed(e)
bOldError := ErrorBlock( {|e| MyDbSeekErrorblock(e, @lExit)}) // Original-Fehler-Codeblock sichern, eigenen installieren
DO WHILE .T. // So lange laufen lassen, bis ein kontrolliertes "Exit" kommt
BEGIN SEQUENCE // Eigene Laufzeitfehler-Behandlung starten
DbAppend() // Den Lock versuchen, Ergebnis speichern
nRetry ++ // Gleich hochzählen, auch wenn gültiiger versuch. Später aufwändiger
RECOVER USING bOldError // Was tun bei einem Laufzeitfehler
lReturn := NIL
nRetry ++
END SEQUENCE
ErrorBlock(bOldError) // Original-Fehler-Codeblock wiederherstellen
IF ValType(lReturn) # "U" // Wenn Sperre erfolgreich ...
lReturn := .T.
Exit // ... raus aus der Schleife
ELSEIF nRetry > 10 // Nach 10 vergeblichen Sperrversuchen
cStack := "Funktion " + ProcName(1) + ; // Um die Codezeile und den Datensatz zu identifizieren
" Zeile " + Var2Char(ProcLine(1)) + crlf + ;
"Datenbank " + Alias() + ;
" RecNo " + Var2Char(RecNo())
IF ConfirmBox(MEMVAR->oXbp, ; // Weiteres Vorgehen abfragen
"Ein Datensatz konnte nicht angehängt werden." + crlf + crlf + ;
cStack + crlf + crlf + ;
"Noch einmal versuchen?", ;
"Neuer datensatz", ;
XBPMB_YESNO, ;
XBPMB_QUESTION, ;
XBPMB_DEFBUTTON1) == XBPMB_RET_NO
IF hatRechte(MEMVAR->aAktMitarbeiter, "Vertrieb") >= 7 // Nur Admins dürfen unkontrolliert abbrechen
EXIT // Wenn "Noch einmal versuchen" mit "Nein" beantwortet wurde -> unkontrolliertes Ende!
ELSE
MsgBox("Sie haben keine ausreichenden Rechte, ein unkontrolliertes Beenden auszuwählen." + crlf + crlf + ;
"Bitte einen Admin informieren", ;
"Datensatzsperre fehlgeschlagen")
ENDIF
ENDIF
nRetry := 0 // Zähler zurück setzeen
ENDIF
Sleep(10) // 10/100 Sekunden warten
ENDDO
RETURN lReturn
Damit bekomme ich dann eben kein NIL zurück. Sorry, das ich das erst verschwiegen hatte. Für mich war meine Funktion schon so selbstverständlich geworden das ich nicht mehr an den Unterschied in der Rückgabe
De Funktion hatte ich mal vor ein paar Jahren bei meinem Kunden geschrieben, als dort nach dem Einbau neuer schneller Server kaum noch eine Db-Funktion sauber lief. Ich weiß nicht ob das noch DBFNTX oder schon FOXCDX war. Auf jeden Fall gab es plötzlich stapelweise Laufzeitfehler und Datenmurks. Nachdem ich dann die relevanten Funktionen (DbSkip, DbAppend, DbRLock, DbRUnLock, DbClose, DbSeek) auf solch ein Muster umgeschrieben hab, lief alles wieder sauber und stabil.
Jan
Re: DbAppend()
Verfasst: Mi, 13. Nov 2019 13:54
von brandelh
dbAppend() und Append Blank, geben keine Laufzeitfehler, sondern setzen nur NETERR(), ähnlich dem USE der fehlschlägt, weil eben ein anderer schon den Satz hat.
Soweit ich deine Funktion überflogen habe nutzt du das Errorsystem um auf eventuelle Laufzeitfehler zu reagieren, aber hier gibt es keine !
Re: DbAppend()
Verfasst: Mi, 13. Nov 2019 14:15
von Werner_Bayern
Code: Alles auswählen
RECOVER USING bOldError // Was tun bei einem Laufzeitfehler
lReturn := NIL
nRetry ++
END SEQUENCE
ErrorBlock(bOldError) // Original-Fehler-Codeblock wiederherstellen
IF ValType(lReturn) # "U" // Wenn Sperre erfolgreich ...
lReturn := .T.
Exit
Macht das Sinn? Im Fehlerfall belegst Du lReturn doch mit NIL und danach prüfst Du auf ValType?
Re: DbAppend()
Verfasst: Mi, 13. Nov 2019 14:21
von Tom
Hubert hat recht.
Re: DbAppend()
Verfasst: Mi, 13. Nov 2019 15:45
von ramses
Es fehlt noch immer die Abfrage von netErr() als einzig mögliche Art zu erkennen ob dbAppend() erfolgreich war und ggf. ein neuer Versuch