--- name: roa-oracle-sqlexec-no-data description: Prinde si elimina pierderea tacuta de randuri intre VFP si Oracle - o procedura PL/SQL cu SELECT ... INTO care nu gaseste niciun rand arunca NO_DATA_FOUND (ORA-01403), iar prin SQLEXEC driver-ul il raporteaza ca SUCCES: SQLExec() intoarce 1, bucla din VFP nu vede nicio eroare si randurile lipsesc fara urma in log. Foloseste-l OBLIGATORIU cand auzi "SQLEXEC nu intoarce nimic", "lipsesc randuri", "procedura intoarce gol dar fara eroare", "documentul se salveaza cu mai putine linii", "totalurile ies mai mici dar consistente", "SELECT INTO", "NO_DATA_FOUND", "ORA-01403", sau cand scrii ori modifici o procedura apelata din VFP si trebuie sa stii de ce intoarce mai putine randuri decat ar trebui. Nu accepta "a mers" fara numaratoare de randuri. --- # Randuri pierdute tacit intre VFP si Oracle (SQLEXEC + NO_DATA_FOUND) O eroare reala in Oracle devine succes raportat in VFP. Mecanismul care ascunde defectul ramane activ pana cand e inchis explicit, iar orice reparatie ulterioara ramane neverificabila. ## Cand se foloseste - Un apel VFP catre o procedura PL/SQL intoarce mai putine randuri sau zero, fara eroare. - Triggeri textuali: "SQLEXEC nu intoarce nimic", "lipsesc randuri", "procedura intoarce gol dar fara eroare", "documentul are mai putine linii decat pe ecran", "SELECT INTO". - Scrii sau modifici o procedura care contine `SELECT ... INTO` si e apelata din VFP. - `SQLExec()` intoarce 1 (succes), dar cursorul rezultat e gol sau incomplet. ## Regula 1. **Garda intai, cauza dupa.** Infasoara FIECARE `SELECT ... INTO` in `BEGIN ... EXCEPTION WHEN NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR(-20000, '...')`. Codul de eroare (`FACT-0xx`) se cauta liber in `USER_SOURCE` inainte de a-l folosi. 2. Daca absenta randului e legitima, nu o lasa sa cada: foloseste un agregat (`SELECT COUNT(*)/MAX(...) INTO`) sau un cursor care intoarce garantat un rand. 3. Simetrizeaza predicatul pe coloana SI pe parametru - sirul gol din VFP este NULL in Oracle: -- gresit: 'XXXX' = NULL da UNKNOWN, deci zero randuri AND NVL(A.CONT, 'XXXX') = V_CONT -- corect: AND NVL(A.CONT, 'XXXX') = NVL(V_CONT, 'XXXX') 4. **Partea VFP e obligatorie.** `SQLExec()` nu e sursa de adevar. Testeaza numarul de randuri: lnRez = SQLEXEC(lnH, "begin PACK_FACTURARE.adauga_articol_factura(...); end;", "curRez") IF lnRez < 0 OR RECCOUNT("curRez") = 0 * trateaza ca esec, nu ca succes ENDIF Nu raporta "functioneaza" pe baza lui `lnSucces`. ## Capcane - **`SQLExec()` intoarce 1 la `SQL_NO_DATA`** - `If lnSucces < 0` nu prinde nimic, linia dispare fara urma. Verificarea de succes trebuie sa includa si numarul de randuri. - **Sirul gol este NULL in Oracle**: `'XXXX' = NULL` da UNKNOWN, deci predicatul cade pe zero randuri. Santinela doar pe coloana nu e suficienta. - **Camp lipsa din lista de insert** a cursorului, pe o coloana numerica fara `NULL`: ramane `0` tacit, nu NULL si nu eroare, iar predicatul cade pe `0` - acelasi simptom, alta cauza. - **Fara asertiune pe numarul de linii**, toate testele existente trec: identitatea documentului si legaturile lui se pastreaza corect, doar liniile lipsesc. - Nu presupune ca un cod `FACT-0xx` e liber - verifica-l in `USER_SOURCE`. ## Criterii de acceptare si dovada Fara dovada da/nu de mai jos, defectul NU se raporteaza ca reparat. 1. Numaratoarea de randuri e asertata intr-un test headless pe fluxul real: acelasi document, acelasi input, `RECCOUNT()` / `SELECT COUNT(*)` egal cu numarul de linii din sursa. Un total mai mic nu e acceptabil, chiar daca nu exista eroare. 2. Handler-ul exista pe disc, nu doar in intentie - interogare pe `USER_SOURCE` care arata `EXCEPTION WHEN NO_DATA_FOUND` in ramura procedurii vizate. 3. Test negativ: forteaza un predicat care nu potriveste nimic si confirma ca primesti eroare `-20000` in VFP, nu succes cu cursor gol. 4. "Arata bine" / "nu a dat eroare" nu e dovada. Dovada e numarul de randuri. ## Referinta (doar la nevoie) - `COMUN\docs\capcana_sqlexec_no_data_found.md` - mecanismul complet, precedentul `PACK_FACTURARE.adauga_articol_factura` si modul de cautare preventiva in `USER_SOURCE`.