Files
comun/docs/scripturi-migrare-db.md
2026-09-11 11:43:46 +03:00

204 lines
13 KiB
Markdown

# Scripturi migrare baza de date Oracle
Scripturile de migrare a schemei (si modelele pentru scripturi noi) sunt in
`DATABASE\SCRIPTURI_CLAR` de sub radacina suitei ROA (`gcDirMare`/`dirgen` — ex.
`D:\ROA\DATABASE\SCRIPTURI_CLAR`). Sursa SVN: `http://svnroa:3001/svn/ROA/DATABASE/Branches/RB-1.00`.
Pentru un script nou, urmeaza formatul/conventiile celor recente de acolo. Publicarea catre clienti
(import in `UPD_DATABASE` + arhiva lunara): `publicare-scripturi-db.md`.
Reguli confirmate de Marius (24.07.2026):
- **Line-endings CRLF obligatoriu** in scripturile `.sql` — tool-urile agentului scriu implicit
LF; dupa orice scriere, verifica si converteste byte-safe LF -> CRLF (fara decodare/reincodare).
Parsarea pe fluxul ROA (ex. `ALINES` pe `CHR(13)+CHR(10)`) esueaza silentios pe LF: tot fisierul
devine un singur rand.
- `versiune_db.txt` (marker-ul `YYYY_MM_DD_NN` din radacina aplicatiei) se scrie fara newline la
final (conventia existenta).
- Aplicarea prin ODBC/`goExecutor`: sintaxa SQL*Plus `exec pachet.procedura(...)` nu functioneaza
— foloseste `begin pachet.procedura(...); end;`.
- **Niciun `;` la capat de linie intr-un comentariu `--` din interiorul unei instructiuni.** SQL*Plus
termina instructiunea acolo, chiar daca `;`-ul e in comentariu: restul `CREATE VIEW`-ului ajunge sa
fie interpretat linie cu linie ca si comenzi, cu zeci de `SP2-0734` si un `ORA-00936` derutant care
arata spre comentariu. Pus la sfarsit de fraza, `.` in loc de `;`. Comentariile de **dinaintea**
instructiunii nu sunt afectate.
- **Fiecare instructiune se incheie explicit cu `;`** (sau `/` pentru blocurile PL/SQL). Fara el,
prima linie goala inchide bufferul SQL*Plus si arunca instructiunea **fara nicio eroare** —
scriptul continua pana la `UpdateVersiune`, se marcheaza aplicat, iar obiectul ramane cel vechi.
`WHENEVER SQLERROR EXIT` nu prinde cazul, pentru ca nu exista eroare. Verificare inainte de
publicare: ultima linie a fiecarui `CREATE`/`ALTER`/`MERGE` se termina cu `;`.
- **Fiecare script se incheie cu `exec pack_migrare.UpdateVersiune('<nume_script>');` urmat de
`commit;`.** Numele se da **fara extensia `.sql`** — o adauga `UpdateVersiune`; trecuta si in
argument, ajunge in `VERSIUNE` ca `..._.sql.sql` si scriptul apare ca neaplicat la comparatia de
mai jos. Toate scriptele din `SCRIPTURI_CLAR` o omit, iar in `VERSIUNE` nu exista niciun rand cu
dubla extensie.
DDL-ul comite implicit, dar `UpdateVersiune` e DML: fara `commit` explicit, un script
se poate aplica fara ca versiunea sa se inregistreze, iar `VERSIUNE` ajunge sa minta despre ce s-a
aplicat.
- **Rularea manuala se face conectat pe schema tinta** (`CONTAFIN_ORACLE` pentru `co_`, schema firmei
pentru `ff_`), niciodata cu un user de lucru. DDL-ul neprefixat (`create or replace package ...`)
merge pe schema conexiunii: pe alt user iese `PLS-00304` si ramane un obiect orfan, iar pachetul
tinta ramane neschimbat. In acelasi script, DML-ul pe tabele cu sinonim public nimereste tabela
reala si pare ca totul a mers — verifica intotdeauna iesirea sqlplus si `all_objects`.
`PACK_UPDATE` face `CONNECT <schema>/<parola>@ROA` real per schema (`UpdateSchemaSQLPLUS`, parola
din `SERVER_INFO`), deci scriptul neprefixat e corect pentru livrare.
## Sursa de referinta pentru DDL: MARIUSM_AUTO, nu productia
Regula lui Marius (06.08.2026): **orice modificare de tabela, view, procedura, functie sau pachet se
scrie plecand de la sursa din schema de dezvoltare `MARIUSM_AUTO` (`ROA_CENTRAL`), niciodata de la
sursa citita dintr-o schema de client** (`VENDING`, `ACN`, ...). Schemele de client raman in urma:
pot avea scripturi neaplicate sau variante livrate separat (ex. pachetele `_10G_ROMCONSTRUCT`), iar o
modificare scrisa peste o sursa veche sterge corectii deja livrate.
Schemele de client raman utile doar pentru **masuratori pe date reale**, in citire.
### Verifica intai ca MARIUSM_AUTO e la zi
"Ultima versiune din dev" e ultima versiune doar daca toate scripturile din `SCRIPTURI_CLAR` chiar au
fost aplicate acolo. `pack_migrare.UpdateVersiune` inregistreaza fiecare script aplicat in tabela
`VERSIUNE` (`script_final` = numele fisierului, cu tot cu `.sql`), deci diferenta se vede direct:
```sql
select script_final, data_final from versiune order by data_script desc, seq_script desc;
```
```powershell
Get-ChildItem D:\ROA\DATABASE\SCRIPTURI_CLAR -Recurse -Filter *.sql |
Select-Object -ExpandProperty Name | Sort-Object
```
Daca lipsesc scripturi din `VERSIUNE`, **nu porni modificarea** pe sursa de acolo — semnaleaza-i lui
Marius ce nu e aplicat. Exportul sursei de referinta: `oracle_export.md`.
## Continutul unui script
- **Minimul necesar si intotdeauna SCOPED**: `update`/`insert` doar pe randurile cazului tratat
(setul, codul, firma anume), niciodata pe toate randurile care "seamana" cu el.
- **Fara `select` de raportare in script** — nu-l citeste nimeni la aplicare si poate da eroare.
Verificarile se fac inainte, separat, pe schema de lucru.
- **Idempotent**: rulat de doua ori nu mai schimba nimic (`merge`, `where <coloana> is null`).
- **Un pachet sta singur in scriptul lui**, fara alt DDL sau DML alaturi: modificarile de tabele si
curateniile de date merg in scripturi separate, cu numar propriu.
- **Comentarii strict necesare**, ca in cod: antet de 4-5 randuri (data + autor, ce e obiectul, ce
contract are), fara referinte la planuri, stories, propuneri, decizii sau erori, fara trimiteri la
rapoartele din `docs/`, fara justificarea alegerilor si fara istoricul modificarii. Regula completa
si lista de interdictii: `reguli_lucru.md`, punctul 2.
## Optiuni de meniu si drepturi (`co_..._OBIECTE.sql`)
O optiune noua de meniu inseamna un rand in `DEF_OBIECTE` (catalogul de obiecte al programului,
arbore prin `ID_TATA`, cu `COD` de doua cifre pe fiecare nivel) plus cate un rand in
`DEF_GRUP_DREPT_OBIECTE` pentru fiecare grup care primeste dreptul. Ambele stau in
`CONTAFIN_ORACLE`, deci scriptul are prefix `co_`.
**ID_OBIECT se aloca in dezvoltare si se trece ca literal in script.** Se insereaza intai obiectul
pe `CONTAFIN_ORACLE@ROA_CENTRAL`, unde trigger-ul `TRG_DEF_OBIECTE_BEFOINS` ii da un ID din
`SEQ_DEF_OBIECTE`; ID-ul obtinut se scrie apoi in script si ajunge identic la toti clientii, cu
`merge ... on (a.id_obiect = <literal>)`. Catalogul ramane astfel acelasi peste tot, iar `ID_TATA`
poate fi si el literal.
**Niciodata insert fara `ID_OBIECT`.** Trigger-ul completeaza din secventa doar cand coloana e
`NULL` sau `0`, iar la client `SEQ_DEF_OBIECTE` e mult in urma fata de `MAX(ID_OBIECT)` (nu avanseaza
la insert-urile cu ID explicit): `NEXTVAL` intoarce un ID deja folosit si scriptul pica pe `PK_DO`
cu `ORA-00001`. Secventa nu se resincronizeaza — pe schemele de client e inerta prin design.
**Grupurile nu se dau niciodata ca ID-uri fixe.** Fiecare client isi defineste propriile grupuri,
deci acelasi `ID_GRUP` inseamna altceva la fiecare (`42` e `VIZUALIZARE` pe dev si
`ROMFAST - CONTRACTE` in productie). Drepturile se copiaza de la o optiune sora din acelasi nod:
```sql
merge into def_grup_drept_obiecte a using
(select distinct <id_obiect_nou> as id_obiect, g.id_grup, -3 as id_utilop, sysdate as dataora, 0 as sters
from def_obiecte s
join def_grup_drept_obiecte g on g.id_obiect = s.id_obiect and g.sters = 0
where s.id_tata = <id_nod> and s.id_obiect <> <id_obiect_nou> and nvl(s.sters, 0) = 0) b
on (a.id_obiect = b.id_obiect and a.id_grup = b.id_grup)
when not matched then
insert (id_obiect, id_grup, id_utilop, dataora, sters) values (b.id_obiect, b.id_grup, b.id_utilop, b.dataora, b.sters);
```
`DEF_OBIECTE.STERS` accepta `NULL`, deci filtrul pe el se scrie `nvl(s.sters, 0) = 0` — `s.sters = 0`
sare tacut peste randurile cu `NULL`, fara nicio eroare. In `DEF_GRUP_DREPT_OBIECTE` si `DEF_GRUP`
coloana e `NOT NULL`.
`ID_UTILOP = -3` e utilizatorul de migrare, folosit de toate scripturile `co_..._OBIECTE.sql`.
Optiunea devine vizibila prin `COMUN\programe\acces_meniu.prg`, care citeste
`contafin_oracle.vdef_util_obiecte` pentru utilizatorul si programul curent si activeaza obiectele
de clasa `cw` a caror cheie (literele nodurilor + `COD`) se regaseste acolo.
Pentru **barele din meniul propriu-zis** (nu butoanele `cw`) cheia porneste din nodul `Z` ("Meniu"):
`dezactiveaza_meniuri(90)` parcurge lista `laPad`, **hardcodata per `gnIdProgram`** in acelasi
`acces_meniu.prg`, si construieste `Z` + `CHR(64+j)` pentru pad-ul j, plus `01`, `02`... pentru
barele lui; ce nu se regaseste in `vdef_util_obiecte` primeste `SET SKIP OF BAR`. Deci o optiune de
meniu noua cere trei lucruri, nu doua: randul in `DEF_OBIECTE`, intrarea in `laPad` si numele
popup-ului din `.mnx` **identic** cu textul din `laPad` (max 10 caractere). Nepotrivirea nu da
eroare: `CNTBAR` intoarce 0, `TRY/CATCH` inghite, si drepturile nu se aplica deloc - asa e azi in
ROACONT, unde `laPad[5] = [eFactura]` nu corespunde popup-ului real `SPVeFactur`.
Optiunile de meniu sunt frunze: spre deosebire de butoanele `cw`, nu au sub ele obiecte de acces
(`1`=adaugare ... `4`=vizualizare), deci dreptul se acorda direct pe frunza.
## Compatibilitate cu serverele clientilor
Serverul de dezvoltare e mai nou decat serverele clientilor, deci un script care trece local poate
pica la client si opri actualizarea.
Parcul de servere (03.08.2026): **un client pe Oracle 10.2** (ROMCONSTRUCT), cativa pe **XE 11 si
11g** standard, restul pe **XE 18/19/21 si 18/21**.
**Regula: orice script care ajunge in comune se scrie la nivelul Oracle 10.2 si trebuie sa ruleze pe
10.2 si pe 11.x.** Numitorul comun e 10.2 — nu se folosesc facilitati 11g si cu atat mai putin 12c+,
oricat de comod ar fi pe dev. Proba inainte de publicare se face pe serverul cel mai vechi
(ROMCONSTRUCT), nu pe dev.
Constructii care merg pe dev si pica mai jos:
| Constructie | Disponibila de la | Ce iese sub ea |
|---|---|---|
| identificator peste 30 de caractere (tabela, index, constrangere, coloana) | 12.2 | `ORA-00972` |
| `REGEXP_COUNT` | 11.1 | `ORA-00904` in SQL / `PLS-00201` in PL/SQL |
| `CONTINUE` (instructiune PL/SQL) | 11.1 | `PLS-00201: identificatorul 'CONTINUE' trebuie declarat` |
| `LISTAGG` | 11.2 | `ORA-00904` |
| `PIVOT` / `UNPIVOT` | 11.1 | eroare de sintaxa |
| trigger compus (`COMPOUND TRIGGER`) | 11.1 | eroare de compilare |
| `secventa.NEXTVAL` direct intr-o atribuire PL/SQL | 11.1 | `PLS-00357` (pe 10g: `select ... into` din `dual`) |
| `FETCH FIRST n ROWS`, `CROSS APPLY`, `LATERAL` | 12.1 | eroare de sintaxa (pe 10g/11g: `rownum`) |
| `IDENTITY`, `DEFAULT ON NULL`, coloane invizibile | 12.1 | eroare de sintaxa |
| `VALIDATE_CONVERSION`, `CAST ... DEFAULT ON CONVERSION ERROR` | 12.2 | `ORA-00904` |
| `FORALL` cu campuri de record din colectie (`S(i).camp`) | 11.1 | `PLS-00436` / `PLS-00382` |
Editiile **XE** adauga limite proprii, independent de sintaxa: XE 11.2 nu are partitionare, executie
paralela si nici masina virtuala Java in baza; XE 18/19/21 au setul de facilitati al editiei mari,
dar plafonate pe resurse (12 GB de date, 2 GB RAM, 2 fire de executie). Deci nimic care sa depinda de
partitionare sau de Java in baza, si nicio operatie care sa presupuna spatiu/memorie de server mare.
Plus, independent de versiune: **un obiect nu se poate referi la ceva creat de un script ulterior.**
Pe dev obiectul exista deja, deci pachetul compileaza; la client scriptele se aplica in ordine si
iese `ORA-00942` / corp invalid. Cand un script creeaza o tabela si altul un pachet care o foloseste,
tabela trebuie sa fie prima in ordinea `YYYY_MM_DD_NN`.
Un pachet care nu are echivalent 10g se livreaza ca varianta separata pentru serverul acela
(ex. `ff_..._PACK_CONTAFIN_10G_ROMCONSTRUCT.pck`). La aplicarea manuala a unei astfel de variante se
compileaza **doar PACKAGE BODY-ul**: recrearea specificatiei invalideaza dependentele si da
`ORA-04068` utilizatorilor conectati. Inainte, verifica ca specificatia din fisier e identica cu cea
de pe server.
Verificarea erorii se face in `C:\DMPDIR\script_master.log` de pe serverul clientului
(`depanare-pack-update.md`) — nu apare in `UPD_LOG`.
## Numerotare si versiune_db.txt
Numele scriptului: `<prefix>_YYYY_MM_DD_NN_<subiect>.sql`. Prefixe in uz: `ff` (schema fiecarei
firme), `co` (`CONTAFIN_ORACLE`), `sys`, `ris`, `rf`.
- **`NN` e o secventa unica pe zi, comuna tuturor prefixelor** — un numar consumat de un `co_`
nu se reia intr-un `ff_` din aceeasi zi si invers.
- **Un script marcat in `VERSIUNE` nu se mai aplica a doua oara.** Clientul ia doar scripturile cu
`data||NN` peste marcajul din `SERVER_INFO.VERSIUNE_<SCHEMA>`, iar marcajul avanseaza la maximul
atins. Un script gresit nu se repara prin republicarea aceluiasi fisier: corectia se livreaza ca
script nou, cu data si `NN` noi.
- In `versiune_db.txt` (radacina aplicatiei) se trece **doar versiunea ultimului script `ff_`**:
programele se conecteaza pe schema firmei, nu pe `CONTAFIN_ORACLE`, deci markerul urmareste
numai migrarile aplicate acolo.