Files
comun/docs/conventie_goexecutor_alter_table.md
Marius Mutu c4d869921d verificare partener: garda pe cont NULL, hook-uri de disciplina, docs compactate
- 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
2026-08-02 22:30:44 +03:00

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):

  1. Function is not supported on remote tables. — pe ALTER TABLE direct pe cursorul original.
  2. 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).
  3. Cu alias scratch diferit ca țintă → tot Invalid operation for the cursor. pe ALTER 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().