Seite 1 von 1

DbAppend()

Verfasst: Mi, 13. Nov 2019 8:55
von Jan
Moin,

ab und an, recht selten, passiert etwas merkwürdiges:

Code: Alles auswählen

IF cAlias->(DbAppend())
   cAlias->Feldname := wert
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