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
This commit is contained in:
@@ -1,65 +1,55 @@
|
||||
# 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ă
|
||||
|
||||
```foxpro
|
||||
lnSucces = goExecutor.oExecute(lcSql, lcCursor)
|
||||
...
|
||||
ALTER TABLE (lcCursor) ADD COLUMN coloananoua L && <- pică
|
||||
```
|
||||
|
||||
Cursorul întors de `oExecute()` (rezultatul unui `SQLExec`) poate refuza comenzi DDL
|
||||
ulterioare, cu erori în lanț (descoperite empiric, în această ordine, în ROAAUTO
|
||||
`autopass_cursor_comenzi`/`autopass_cursor_operatii`):
|
||||
|
||||
1. `Function is not supported on remote tables.` — pe `ALTER TABLE` direct pe cursorul
|
||||
original.
|
||||
2. Chiar și după o încercare de rematerializare (`SELECT * FROM (cursor) INTO CURSOR
|
||||
(acelasi_nume) READWRITE`) → `Alias name is already in use.` (sursa și ținta nu pot
|
||||
avea același nume într-un `SELECT...INTO CURSOR`).
|
||||
3. Cu un alias scratch diferit ca țintă (`SELECT * FROM (cursor) INTO CURSOR
|
||||
(nume_final_diferit_de_sursa) READWRITE`) → tot `Invalid operation for the cursor.` pe
|
||||
`ALTER TABLE`, chiar dacă acum cursorul final teoretic ar trebui să fie unul local
|
||||
obișnuit.
|
||||
|
||||
Nu există niciun alt loc în codul ROA* care să facă `ALTER TABLE` imediat după
|
||||
`goExecutor.oExecute()` — deci acest pattern nu avea niciun precedent testat înainte de
|
||||
funcțiile AUTOPASS.
|
||||
|
||||
## 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 ...`):
|
||||
|
||||
```foxpro
|
||||
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. dintr-o altă funcție care primește cursorul deja gata construit,
|
||||
nu direct rezultatul unui `oExecute`), 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()`.
|
||||
# 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ă
|
||||
|
||||
```foxpro
|
||||
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 ...`):
|
||||
|
||||
```foxpro
|
||||
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()`.
|
||||
|
||||
Reference in New Issue
Block a user