- ooperatii_comune: verific_partener nu mai construieste SQL NULL cand contul primit e NULL (EMPTY(.NULL.) e .F. in VFP) - utile\context_watch.ps1 si utile\docs_revizie_check.ps1: masurarea contextului sesiunii si cadenta reviziei de documentatie, prin hook-uri Claude Code (instalare in docs\monitorizare-context.md) - reguli_lucru: delegare la subagenti, modificari minime si scoped, scrierea si revizuirea documentatiei, changelog strictul necesar (regulile 3, 6, 9, 11, 12) - scripturi-migrare-db: continutul unui script (scoped, fara select, idempotent) - teste noi pentru cele doua erori din achizitia de import - restul documentatiei compactata, fara pierdere de reguli Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RYbiinqXxdEqXi53x4Ro7K
2.1 KiB
Capcană: ALTER TABLE pe cursorul întors de goExecutor.oExecute()
Partajat între proiectele ROA* — goExecutor (clasa oExecutor, definită în
COMUN/programe/oproceduri_comune.prg, metoda oExecute) e utilitarul standard pentru
interogări SQL pass-through (SQLExec) către Oracle, folosit peste tot în codul ROA*.
Ce nu funcționează
lnSucces = goExecutor.oExecute(lcSql, lcCursor)
...
ALTER TABLE (lcCursor) ADD COLUMN coloananoua L && <- pică
Cursorul întors de oExecute() poate refuza comenzi DDL ulterioare, cu erori în lanț
(descoperite empiric, în această ordine):
Function is not supported on remote tables.— peALTER TABLEdirect pe cursorul original.- Chiar și după rematerializare (
SELECT * FROM (cursor) INTO CURSOR (acelasi_nume) READWRITE) →Alias name is already in use.(sursa și ținta nu pot avea același nume). - Cu alias scratch diferit ca țintă → tot
Invalid operation for the cursor.peALTER TABLE, chiar dacă țintă ar trebui să fie deja un cursor local obișnuit.
Ce funcționează
Nu folosi ALTER TABLE deloc pe rezultatul unui oExecute(). Include coloanele
suplimentare direct în SELECT-ul de rematerializare, ca expresii literale alături de *
(VFP suportă SELECT *, expr AS coloana FROM ...):
lcCursorRaw = lcCursor + '_raw'
IF USED(lcCursorRaw)
USE IN (lcCursorRaw)
ENDIF
lnSucces = goExecutor.oExecute(lcSql, lcCursorRaw)
IF lnSucces < 0
* tratare eroare, RETURN
ENDIF
SELECT *, .F. AS coloananoua, SPACE(100) AS altacoloana ;
FROM (lcCursorRaw) INTO CURSOR (lcCursor) READWRITE
USE IN (lcCursorRaw)
* de-aici incolo doar REPLACE (date), NICIODATA ALTER TABLE (structura) pe (lcCursor)
După acest pas, cursorul (lcCursor) e un cursor local obișnuit — ALTER TABLE funcționează
normal pe el DUPĂ ACEEA (ex. într-o altă funcție care primește cursorul deja gata construit),
doar comanda imediat următoare unui oExecute() e restricționată.
Vezi și
Exemplu complet aplicat: ROAAUTO, Programe/oproceduri_autopass.prg,
autopass_cursor_comenzi()/autopass_cursor_operatii().