Revisione di progetto · per il team

Asqueel 0.6 e GenroPy SQL

Questa pagina confronta asqueel 0.6 con il layer SQL di GenroPy, funzione per funzione. Ogni riga viene dall'inventario docs/design/112-legacy-inventory.md. Le funzioni mancanti mostrano le alternative affiancate: sono punti aperti per la discussione.

Questa pagina sostituisce la pagina docs/review/asqueel-vs-genropy-it.html, datata 4 ottobre 2026 e pubblicata il 5 ottobre 2026. Quella pagina resta invariata.

Data 17 ottobre 2026 GenroPy 95da66ee64b0c789dbb6b35254011d83ce1911c5 Asqueel plan/write-cycle a 4361e769a53148c83a527fa1afa02dd2ac796668 (aree 5-8 a 2ba61df)
presente stessa funzione, stesso comportamento diverso funzione disponibile con comportamento diverso mancante funzione non disponibile nuovo funzione senza controparte legacy
Areapresentediversomancantenuovorighe
1 Modello21410127
2 Query979126
3 Formule928120
4 Relazioni675321
5 Scritture51112230
6 Transazioni31310329
7 Migrazione8116429
8 Dialetti3118123
Totale45766816205

1 Modello

presente 2 funzioni identiche al legacy
IDFunzioneLegacyAsqueel
A1.8validate_*Kwargs di colonna memorizzati (gnrpy/gnr/sql/gnrsqlmodel/model.py:938); in gnrpy/gnr/sql nessun codice li esegueMemorizzati piatti; vista validations src/asqueel/object_tree.py:261-269; figlio validation src/asqueel/semantic_elements.py:10, che blocca la scrittura (src/asqueel/model_source.py:247)
A1.21columns / relationsgnrpy/gnr/sql/gnrsqlmodel/table.py:265, :277 (Bag delle relazioni)src/asqueel/object_tree.py:188, :221 (Bag dal RelationTreeResolver)

diverso Differenze e motivazione

IDFunzioneLegacyAsqueelMotivazione
A1.1package() / sqlschema / sqlprefix / sqlnamegnrpy/gnr/sql/gnrsqlmodel/model.py:602 package(name, sqlschema, ...); gnrpy/gnr/sql/gnrsqlmodel/obj.py:200 prefisso = nome package; gnrpy/gnr/sql/gnrsqlmodel/table.py:151 sqlschema con tenant_schema (riga 154)src/asqueel/model_elements.py:41 package(name, sqlschema, sqlprefix, ...) sotto db.packages(); src/asqueel/object_tree.py:146-154 schema e prefisso; src/asqueel/object_tree.py:207-213 sqlschema/sqlname della tabella; nessun tenant_schemaRegole di schema e prefisso come nel legacy: docs/guide/models.md:77-78. Un modello con tag schema non imposta il prefisso: #25. tenant_schema non applicato a sqlschema: nessuna motivazione registrata
A1.2table(name, pkey=, ..., **kwargs)gnrpy/gnr/sql/gnrsqlmodel/model.py:643 accetta lastTS, rowcaption, sqlname, sqlschema e qualsiasi attributo in kwargssrc/asqueel/elements.py:164 insieme dichiarato (caption_field, lookup, order_by, newrecord_caption, deletable, ...); gli extra passano solo se contengono '_' (src/asqueel/validators.py:121), altrimenti errore (src/asqueel/validators.py:131). Prova: lastTS, logicalDeletionField, draftField, hierarchical, xtd rifiutatidocs/guide/release-notes.md:20-21 (namespace piatti x_*). Il docstring src/asqueel/elements.py:32-35 chiede il prefisso x_; il codice accetta qualsiasi chiave con '_'. Vale il codice
A1.3lastTS / logicalDeletionField / draftFieldgnrpy/gnr/sql/gnrsqlmodel/table.py:216, :222, :228 proprietà lette dagli attributi omonimi della tabellasrc/asqueel/model.py:126-128 legge x_draft_field / x_logical_deletion_field (oppure x_legacy_*); proprietà in src/asqueel/table_triggers.py:50, :54; lastTS solo come x_legacy_lastTS da sys_fields (src/asqueel/model_expansion.py:147), nessuna proprietà lastTSdocs/guide/row-policies.md:53-54 e :14. Il filtro di lettura ignora il timestamp di cancellazione: #16
A1.4partition_<campo>='path'gnrpy/gnr/sql/gnrsqltable/columns.py:104-105 partitionParameters estratti dagli attributi partition_*src/asqueel/option_elements.py:78 options().partition(name, field=, value=) oppure mappa x_partition (src/asqueel/model.py:103-111). Un partition_x legacy passa il validatore (src/asqueel/validators.py:121) e viene ignorato: nessun filtrodocs/guide/row-policies.md:18-29. Partizioni non applicate alle scritture: #15
A1.5column(): dtype, size, notnull, unique, indexed, sqlname, comment, group, sql_valuegnrpy/gnr/sql/gnrsqlmodel/model.py:855; dtype di default A con size, altrimenti T (gnrpy/gnr/sql/gnrsqlmodel/columns.py:119); indexed/unique anche come stringa (gnrpy/gnr/sql/gnrsqlmodel/model.py:910-913)src/asqueel/elements.py:246; stesso default di dtype (src/asqueel/object_tree.py:258); DTYPE src/asqueel/elements.py:59; indice da indexed (src/asqueel/object_tree.py:173-181); sql_value scritto (src/asqueel/write_compiler.py:64). indexed='y' rifiutato: il tipo è bool (src/asqueel/elements.py:249)Diverso solo per indexed/unique passati come stringa. La firma booleana è in docs/design/42-configuration-grammar.md:217; nessuna motivazione registrata per il rifiuto della stringa
A1.9Attributi UI senza '_' (format, values, print_width, encrypted, ...)gnrpy/gnr/sql/gnrsqlmodel/model.py:938 qualsiasi kwarg viene memorizzatoRifiutati come attributo sconosciuto (src/asqueel/validators.py:131); prova con format. values diventa validate_values; encrypted diventa il figlio encryption (src/asqueel/semantic_elements.py:20)docs/guide/release-notes.md:19-21 (validate_values, x_ui_*)
A1.11colgroup()gnrpy/gnr/sql/gnrsqlmodel/model.py:809; group 'nome.NNN' più colgroup_label e colgroup_name_long (:843-848); col_* qualsiasi; attributo di tabella group_<nome> (:831)src/asqueel/model_elements.py:29; group 'nome.NNN' (src/asqueel/builder.py:443); col_* solo per i parametri dichiarati di column (src/asqueel/builder.py:468); prova: colgroup_label, colgroup_name_long e group_<nome> assentiFirma in docs/design/42-configuration-grammar.md:457 (default col_* dalle firme delle colonne); nessuna motivazione registrata per gli attributi mancanti
A1.12compositeColumn()gnrpy/gnr/sql/gnrsqlmodel/model.py:1078 virtual_column con composed_of, dtype JS, static=True; pkeys espanse da gnrpy/gnr/sql/gnrsqlmodel/table.py:191src/asqueel/elements.py:374 nella collezione composites, con unique=; dtype JS (src/asqueel/object_tree.py:274-276); pkey risolta sui membri (src/asqueel/model_source.py:130-132); SELECT e record() falliscono#26; docs/guide/limitations.md:53
A1.13index()gnrpy/gnr/sql/gnrsqlmodel/model.py:1326 index(columns, name, unique); accetta una lista (:1343); nome di default '<tabella>_<colonne>_key' (:1346)src/asqueel/elements.py:412 index(name, columns, unique, method, where, ...): il primo posizionale è il nome; lista rifiutata (src/asqueel/validators.py:194); nome di default index_<hash> (src/asqueel/builder.py:410). Prova: index('id') dà "index requires 'columns'"Firma in docs/design/42-configuration-grammar.md:640; nessuna motivazione registrata per l'ordine degli argomenti
A1.17config_db / config_db_<pkg> / config_db_customgnrpy/gnr/sql/gnrsqlmodel/model.py:127, :130, :140 eseguiti da build() sui mixin di tabellaRicetta main(self, root) con funzioni per tabella (docs/guide/models.md:42-48, :66-71); override con builder.contribution (src/asqueel/builder.py:152)docs/guide/legacy.md:57-66 (adapter legacy non implementato); docs/guide/models.md:91
A1.19model.table('pkg.tbl') / model.column() / tblobj.column(name)gnrpy/gnr/sql/gnrsqlmodel/model.py:545 package sconosciuto: None; gnrpy/gnr/sql/gnrsqlmodel/table.py:514 colonna semplice sconosciuta: Nonesrc/asqueel/object_tree.py:372-384 e :226; package sconosciuto: ValueError (:379); colonna sconosciuta: ValueError (:239); percorsi @ e AliasColumnWrapper come nel legacyL'errore 'Unknown column' è documentato in docs/guide/troubleshooting.md:30; nessuna motivazione registrata per il passaggio da None a errore
A1.20pkey / pkeysgnrpy/gnr/sql/gnrsqlmodel/table.py:210 una sola colonna; gnrpy/gnr/sql/gnrsqlmodel/table.py:191 pkeys espande composed_ofsrc/asqueel/object_tree.py:204-205 restituisce x_logical_pkey oppure pkey; prova: pkey='a,b' accettato; proprietà pkeys assente#23 (pkey='a,b'). pkeys assente: nessuna issue
A1.22name_long / name_short / name_fullgnrpy/gnr/sql/gnrsqlmodel/obj.py:109-125: name_long ricade su name_short; name_full ricade su name_long; ci sono i settersrc/asqueel/object_tree.py:87-93: name_long ricade sul nome; name_full assente; nessun setterAttributi descrittivi della firma in docs/design/42-configuration-grammar.md:190-192 (name_full non previsto); nessuna motivazione registrata per i fallback
A1.26Tipi custom del package (custom_type_<dtype>)gnrpy/gnr/sql/gnrsqlmodel/columns.py:126-133 hook del packagesrc/asqueel/model_elements.py:83 options().type(name, dtype, size, ...); risolto in src/asqueel/model_source.py:98-119; dtype sconosciuto: ValueErrorFirma type() in docs/design/42-configuration-grammar.md:130: dichiarazione al posto dell'hook

mancante Funzioni mancanti: punti aperti

A1.6 · Abbreviazione 'nome::dtype' (colonne fisiche e virtuali)

Legacy gnrpy/gnr/sql/gnrsqlmodel/model.py:914-915 per column; gnrpy/gnr/sql/gnrsqlmodel/model.py:1003-1004 per le virtuali: divide il nome e imposta dtype

Asqueel src/asqueel/elements.py:246 non divide il nome. Prova: column('n::N') registra il nome letterale con dtype T; formulaColumn('dbl::N') crea la colonna 'dbl::n' senza errore

Issue nessuna issue

(a)

dividere il nome nel builder come nel legacy

(b)

convertirla solo nell'adapter o nell'importer legacy

(c)

rifiutare '::' con un errore esplicito e usare dtype=

A1.7 · column(default=)

Legacy gnrpy/gnr/sql/gnrsqlmodel/model.py:938 attributo memorizzato; usato da defaultValues in gnrpy/gnr/sql/gnrsqltable/record.py:600

Asqueel src/asqueel/elements.py:256 dichiarato; la scrittura viene rifiutata se default è presente (src/asqueel/model_source.py:249)

Issue #28

(a)

newrecord con default come nel legacy

(b)

tradurlo in sqldefault lato database

(c)

lasciarlo fuori: valorizzare in trigger_onInserting

A1.10 · localized / variant / ext_<pkg>

Legacy gnrpy/gnr/sql/gnrsqlmodel/model.py:919 e :958 (handleLocalizedColumn), :935 variant, :948 ext_config

Asqueel localized e variant rifiutati (src/asqueel/validators.py:131, prova). ext_* accettato da src/asqueel/validators.py:121 ma non eseguito

Issue nessuna issue

(a)

eseguire gli hook del package come nel legacy

(b)

tradurli in opzioni dichiarative nel livello applicativo

(c)

lasciarli fuori: dichiarare a mano le colonne generate

A1.14 · sysFields(tbl, ...)

Legacy gnrpy/gnr/app/gnrdbo.py:324 metodo chiamato in config_db

Asqueel src/asqueel/model_elements.py:89 options().sys_fields(...); espansione src/asqueel/model_expansion.py:113 (prova: __ins_ts, __mod_ts, __del_ts, __ins_user, __mod_user); scrittura rifiutata (src/asqueel/model_source.py:243)

Issue #29; docs/guide/limitations.md:40

(a)

eseguire i trigger di campo come nel legacy

(b)

default e trigger lato database

(c)

lasciarlo fuori: dichiarare le colonne a mano e valorizzarle negli hook

A1.15 · sysFields(hierarchical=...)

Legacy gnrpy/gnr/app/gnrdbo.py:326 parametro; attributo hierarchical impostato in gnrpy/gnr/app/gnrdbo.py:437

Asqueel src/asqueel/option_elements.py:18 options().hierarchical; espansione src/asqueel/model_expansion.py:228, richiede pkey id di size 22 (:234); scrittura rifiutata

Issue #30; JSON del modello: #18

(a)

trigger hierarchical come nel legacy

(b)

calcolo dei percorsi lato database

(c)

lasciarlo fuori: hook applicativi su parent_id

A1.16 · sysFields(counter=) / sysFields_counter

Legacy gnrpy/gnr/app/gnrdbo.py:326; gnrpy/gnr/app/gnrdbo.py:556

Asqueel src/asqueel/option_elements.py:26 options().auto_counter; espansione src/asqueel/model_expansion.py:201-209 (colonna _row_count, x_legacy_counter); scrittura rifiutata

Issue #31

(a)

setRowCounter come nel legacy

(b)

sequence o serial nel database

(c)

lasciarlo fuori: valorizzare in trigger_onInserting

A1.18 · Mixin di tabella e di package (tableMixin, packageMixin, _plugins, mixin=)

Legacy gnrpy/gnr/sql/gnrsqlmodel/model.py:516, :525; gnrpy/gnr/sql/gnrsqlmodel/obj.py:58-63

Asqueel Nessun mixin installato (src/asqueel/object_tree.py:160-161). Esiste solo x_table_class (src/asqueel/object_tree.py:162-165; docs/guide/hooks.md:3)

Issue nessuna issue

(a)

comporre le classi mixin come nel legacy

(b)

una sola sottoclasse SqlTable per tabella più contributi

(c)

lasciarli fuori: x_table_class

A1.23 · rowcaption / newrecord_caption (proprietà)

Legacy gnrpy/gnr/sql/gnrsqlmodel/table.py:240 ricade su caption_field e poi su pkey; gnrpy/gnr/sql/gnrsqlmodel/table.py:255

Asqueel Attributi dichiarati (src/asqueel/elements.py:169-170). Prova: DbTableObj non ha la proprietà rowcaption

Issue nessuna issue

(a)

proprietà con lo stesso fallback del legacy

(b)

lettura da attributes con un helper unico

(c)

lasciarle fuori: attributes.get('rowcaption')

A1.24 · virtual_columns / starColumns()

Legacy gnrpy/gnr/sql/gnrsqlmodel/table.py:359 include colonne locali, formulaColumn_* (:405) e variant (:440); gnrpy/gnr/sql/gnrsqlmodel/table.py:479 starColumns pubblico

Asqueel src/asqueel/object_tree.py:192 solo le colonne dichiarate; nessun formulaColumn_* né variantColumn_*; star_columns è interno al compilatore (src/asqueel/compiler.py:663) e serve a '*' (docs/guide/limitations.md:234)

Issue nessuna issue

(a)

proprietà complete come nel legacy

(b)

solo colonne dichiarate più starColumns pubblico

(c)

lasciarli fuori: dichiarare le colonne nella ricetta

A1.25 · subtable()

Legacy gnrpy/gnr/sql/gnrsqlmodel/model.py:685; formula subtable_<nome> (:800); variante di package con maintable (:704)

Asqueel src/asqueel/semantic_elements.py:39 solo su tabella; formula subtable_<nome> (src/asqueel/model_expansion.py:168-181); opzione di query subtable rifiutata (src/asqueel/application_table.py:36)

Issue nessuna issue (mancano la variante di package e il filtro in query)

(a)

variante di package e filtro come nel legacy

(b)

solo filtro in query su subtable dichiarata

(c)

lasciarlo fuori: where con $subtable_<nome>

nuovo Funzioni nuove di asqueel

IDFunzioneAsqueelMotivazione
A1.27column(sqldefault=, extra_sql=, generated_expression=)src/asqueel/elements.py:249-251docs/guide/writes.md:47; docs/guide/limitations.md:74

2 Query

presente 9 funzioni identiche al legacy
IDFunzioneLegacyAsqueel
A2.2espansione '*' e bagFieldsgnrpy/gnr/sql/gnrsqldata/compiler.py:934 espande '*' con expandMultipleColumns; bagFields or for_update in gnrpy/gnr/sql/gnrsqldata/query.py:176src/asqueel/compiler.py:663 star_columns: colonne fisiche, poi virtuali static; X solo con bagFields o for_update
A2.5nome automatico (colToAs)gnrpy/gnr/sql/gnrsqldata/compiler.py:1017 as_ = colToAs(col); gnrpy/gnr/sql/gnrsql/query.py:361src/asqueel/_legacy_sql.py:17 col_to_as uguale al legacy; @customer_id.name diventa '_customer_id_name' (prova)
A2.6$col, $pkey, @rel verso uno in columns/where/order_bygnrpy/gnr/sql/gnrsqldata/compiler.py:915 relationDict['pkey']; updateFieldDict e getFieldAliassrc/asqueel/compiler.py:571 paths['pkey']; src/asqueel/_legacy_sql.py:65 FieldReferences
A2.7sqlparams e parametri simbolici ($col/@rel nel valore)gnrpy/gnr/sql/gnrsqldata/compiler.py:998 embedFieldPars sulla WHEREsrc/asqueel/_legacy_sql.py:40 embed_field_parameters; src/asqueel/compiler.py:614
A2.9parametro :env_nomegnrpy/gnr/sql/gnrsql/execute.py:94-100 legge :env_* da currentEnv; un sqlargs esplicito ha la precedenzasrc/asqueel/compiler.py:647-650 stessa regex; un parametro esplicito ha la precedenza; workdate di default da src/asqueel/environment.py:121
A2.11distinct, group_by, having, limit, offsetgnrpy/gnr/sql/gnrsqldata/compiler.py:900-912 aggregate; :1098 limit/offsetsrc/asqueel/compiler.py:552-563, :638, :644; il codice non rifiuta having senza group_by né ORDER BY fuori proiezione con DISTINCT (prova)
A2.13excludeLogicalDeleted (anche 'mark'), excludeDraftgnrpy/gnr/sql/gnrsqldata/compiler.py:982-990 $del IS NULL, _isdeleted, draft IS NOT TRUEsrc/asqueel/compiler.py:781-805 stessi filtri e stesso ordine; alias camelCase in src/asqueel/application_table.py:28
A2.16fetch()gnrpy/gnr/sql/gnrsqldata/query.py:241 cursor.fetchall, poi pyColumns, decrittazione e #BAGsrc/asqueel/application_table.py:99 righe NamedList di Toolbox, query ricompilata a ogni chiamata; un pyColumn fallisce già in compilazione (riga A3.12)
A2.18count()gnrpy/gnr/sql/gnrsqldata/query.py:557 compileQuery(count=True); legge gnr_row_count oppure conta le righe; gnrpy/gnr/sql/gnrsqldata/compiler.py:948src/asqueel/application_table.py:131-137 e src/asqueel/compiler.py:592 stessa logica; LIMIT/OFFSET mantenuti come nel legacy (prova)

diverso Differenze e motivazione

IDFunzioneLegacyAsqueelMotivazione
A2.3'*@rel', '*@rel1.@rel2', '*prefix_', '*nome', '*@rel.(a,b)'gnrpy/gnr/sql/gnrsqldata/compiler.py:746 expandMultipleColumns espande le forme con prefisso e relazione; :776 *@rel1.@rel2src/asqueel/compiler.py:675 UnsupportedFeatureError 'Wildcard prefixes/paths are not connected'docs/adattamenti-legacy.md:176-183 (forme rimosse; genropy#623 e genropy#1504); docs/design/41-deferred-features.md:226 (decisione del 15 ottobre 2026: solo * e colonne esplicite)
A2.4addPkeyColumn / colonna 'pkey' automaticagnrpy/gnr/sql/gnrsqltable/query.py:130 default True; gnrpy/gnr/sql/gnrsqldata/compiler.py:949 aggiunge $pkey AS pkey se la query non aggregasrc/asqueel/compiler.py:593 aggiunge 'pkey' solo con addPkeyColumn=True; default False (src/asqueel/compiler.py:555)docs/adattamenti-legacy.md:118-126 (decisione del 2 ottobre 2026: la colonna la aggiunge l'adattatore legacy)
A2.8kwargs come parametri di querygnrpy/gnr/sql/gnrsqldata/query.py:162 sqlparams.update(kwargs): una chiave non usata viene ignoratasrc/asqueel/application_table.py:85-89 una kwarg non usata dalla query solleva UnsupportedFeatureError (anche jc_*)docs/guide/queries.md:253-255 (scelto per intercettare opzioni scritte male)
A2.12for_updategnrpy/gnr/sql/gnrsqldata/query.py:176 forza bagFields; FOR UPDATE nella SQLsrc/asqueel/compiler.py:563 come il legacy, ma rifiuta for_update con distinct o group_bydocs/guide/queries.md:459-461 (rifiuto anticipato)
A2.14ignorePartition e filtro di partizionegnrpy/gnr/sql/gnrsqldata/compiler.py:971 getPartitionConditionsrc/asqueel/compiler.py:736 partition_conditions: 0/False sono valori validi, allowed vuoto non restituisce righe, contesto mancante solleva erroredocs/guide/legacy.md:46-52 (differenza concordata)
A2.19compiled, sqltext, test()gnrpy/gnr/sql/gnrsqldata/query.py:215-217 compila una volta e mette in cache; :401 test() restituisce (sqltext, sqlparams)src/asqueel/application_table.py:78 e :93 ricompilano a ogni accesso nell'ambiente corrente; test() non esistedocs/guide/queries.md:55-56 e :88-90 (ricompilazione voluta); cache dei compilati in discussione: #3
A2.21record(pkey= / where=, for_update, ignoreMissing)gnrpy/gnr/sql/gnrsqltable/record.py:309; gnrpy/gnr/sql/gnrsqldata/record.py:224 pkey composta come stringa '[...]'; :353 Bag vuota se ignoreMissingsrc/asqueel/application_table.py:274; pkey composta come mapping o sequenza (:244); ignoreMissing restituisce {} (:152); filtri come compiledRecordQuery (:291)docs/guide/queries.md:494-496 (forma della pkey composta); #26 (pkey compositeColumn fallisce)

mancante Funzioni mancanti: punti aperti

A2.1 · columns di default / columns=None

Legacy gnrpy/gnr/sql/gnrsqldata/query.py:157 columns None o vuoto diventa '*'

Asqueel src/asqueel/application_table.py:227 default '*'; columns=None arriva a src/asqueel/compiler.py:573 e solleva TypeError (prova)

Issue nessuna issue; nessun documento lo registra

(a)

trattare None e '' come '*', come il legacy

(b)

rifiutare None con un errore esplicito e documentarlo

(c)

lasciarlo fuori: il chiamante omette columns

A2.10 · condizioni env_<tabella>_condition_*

Legacy gnrpy/gnr/sql/gnrsqldata/compiler.py:966 condizioni lette da currentEnv e aggiunte alla WHERE

Asqueel Nessuna lettura di env_*_condition_; i filtri aggiunti alla WHERE sono solo le policy di riga (src/asqueel/compiler.py:781)

Issue nessuna issue

(a)

leggere le condizioni dall'ambiente come nel legacy

(b)

condizioni dichiarate nel modello come policy di riga

(c)

lasciarlo fuori: where esplicita nel chiamante

A2.15 · ignoreTableOrderBy, _storename, locale, mode, checkPermissions, aliasPrefix

Legacy gnrpy/gnr/sql/gnrsqltable/query.py:130-133 (mode è accettato ma non passato a SqlQuery); gnrpy/gnr/sql/gnrsqldata/compiler.py:912 order_by della tabella

Asqueel src/asqueel/application_table.py:36-37 in _UNSUPPORTED: UnsupportedFeatureError. L'order_by della tabella è applicato (src/asqueel/compiler.py:569) ma non si può disattivare. subtable: riga A1.25

Issue nessuna issue. Vale il codice su docs/guide/queries.md:124 e docs/guide/limitations.md:248 ('nessun ordinamento di default')

(a)

accettare tutte le opzioni con la semantica legacy

(b)

accettare solo ignoreTableOrderBy, ignorare mode come fa il legacy

(c)

lasciarle fuori: order_by esplicito, filtro nella where

A2.17 · fetchAsDict, fetchAsBag, fetchGrouped, fetchPkeys, fetchAsJson, cursor, servercursor, iterfetch

Legacy gnrpy/gnr/sql/gnrsqldata/query.py:235, :320, :330, :351, :371, :379, :504, :527

Asqueel Nessun metodo su SqlQuery (src/asqueel/application_table.py:68-137); cursor dà AttributeError (prova)

Issue nessuna issue; docs/guide/limitations.md:22 (nessun cursore in streaming)

(a)

aggiungere i terminali con le firme legacy

(b)

aggiungere solo fetchAsDict, fetchPkeys, fetchGrouped, senza Bag né cursore server

(c)

lasciarli fuori: comprehension su fetch()

A2.20 · selection() e API SqlSelection (output, sort, totalize, freeze, frozenSelection)

Legacy gnrpy/gnr/sql/gnrsqldata/query.py:446; gnrpy/gnr/sql/gnrsqldata/selection.py:256 output, :493 freeze, :558 sort, :731 totalize; gnrpy/gnr/sql/gnrsqltable/query.py:107

Asqueel src/asqueel/application_table.py:128 solleva UnsupportedFeatureError

Issue nessuna issue; docs/guide/limitations.md:186-188 e :196

(a)

portare SqlSelection con i suoi output

(b)

un oggetto risultato ridotto (sort, filter, totalize) senza i modi Bag e griglia

(c)

lasciarlo fuori: fetch() ed elaborazione in Python

A2.22 · record: selettori kwargs, ignoreDuplicate, virtual_columns, lazy/eager

Legacy gnrpy/gnr/sql/gnrsqldata/record.py:305 where dai kwargs colonna=valore; :360-366 scarta i duplicati cancellati, poi ignoreDuplicate; gnrpy/gnr/sql/gnrsqldata/compiler.py:1105 virtual_columns

Asqueel src/asqueel/application_table.py:287 senza pkey né where solleva ValueError; :158 errore su più righe; :38 virtual_columns, lazy, eager, ignoreDuplicate in _UNSUPPORTED

Issue nessuna issue

(a)

portare tutti i parametri con la semantica legacy, compreso il filtro sui duplicati cancellati

(b)

solo selettori kwargs e virtual_columns, senza ignoreDuplicate

(c)

lasciarli fuori: where esplicita e query() per le formule

A2.23 · record mode / output(mode)

Legacy gnrpy/gnr/sql/gnrsqldata/record.py:263 out_<mode>: bag (con resolver), dict, record, json, template, sample, newrecord

Asqueel src/asqueel/application_table.py:162-173 solo 'dict' e 'record'; 'bag' solleva errore (prova)

Issue nessuna issue (newrecord: #28); docs/guide/queries.md:502-505

(a)

aggiungere bag con resolver e gli altri modi legacy

(b)

aggiungere bag senza resolver e json

(c)

lasciarli fuori: output('record') o output('dict')

A2.25 · readColumns, countRecords, recordAs, cachedRecord/tableCachedData, db.query(table)

Legacy gnrpy/gnr/sql/gnrsqltable/crud.py:447, :476; gnrpy/gnr/sql/gnrsqltable/record.py:346, :183, :241; gnrpy/gnr/sql/gnrsql/query.py:253

Asqueel Nessun corrispondente in src/asqueel; esistono solo db.table(...).query e record

Issue nessuna issue

(a)

portare gli helper con firme e cache legacy

(b)

solo readColumns e recordAs, senza cache

(c)

lasciarli fuori: query(...).fetch() e record(...)

A2.26 · sqlWhereFromBag / whereTranslator / opTranslate

Legacy gnrpy/gnr/sql/gnrsqltable/query.py:91, :95, :85 WHERE costruita da una Bag tramite l'adapter

Asqueel Nessun corrispondente in src/asqueel

Issue nessuna issue

(a)

portare il whereTranslator dell'adapter

(b)

un traduttore ridotto da mapping a where con parametri legati

(c)

lasciarlo fuori: where scritta dal chiamante

nuovo Funzioni nuove di asqueel

IDFunzioneAsqueelMotivazione
A2.24SqlRecord.refresh()src/asqueel/application_table.py:175 refresh() scarta il risultato e rilegge nell'ambiente correntedocs/guide/queries.md:500-501

3 Formule

presente 9 funzioni identiche al legacy
IDFunzioneLegacyAsqueel
A3.2sql_formula=True con sql_formula_<nome>(attr); formule sysFields __protecting_reasons / __invalid_reasonsgnrpy/gnr/sql/gnrsqldata/compiler.py:396-397 chiama il metodo sulla tabella proprietaria; gnrpy/gnr/app/gnrdbo.py:505, :657, :675src/asqueel/compiler.py:117-124 (errore esplicito se il metodo manca o restituisce SQL vuoto); src/asqueel/model_expansion.py:159-162; src/asqueel/application_table.py:205, :211
A3.3select= / exists= / select_<nome> con #<nome>, cast, subquery_<nome>()gnrpy/gnr/sql/gnrsqldata/compiler.py:402-419: '#default' o 'EXISTS(#default)'; una stringa chiama subquery_<nome>() sulla tabella radice (:406); cast (:408); default ignorePartition True, excludeDraft ed excludeLogicalDeleted Falsesrc/asqueel/compiler.py:138-151 e :161-220: stessi default (:202-204), subquery_ sulla tabella radice (:164-172), opzioni sconosciute rifiutate (:186-190)
A3.4var_* nelle formulegnrpy/gnr/sql/gnrsqldata/compiler.py:429-436: valori copiati in currentEnv, :k riscritto in :env_<prefisso>_<k>src/asqueel/model.py:74-86 e src/asqueel/compiler.py:134-137: parametri legati locali alla formula, con nomi isolati
A3.6aliasColumn (relation_path, dtype e metadati propri)gnrpy/gnr/sql/gnrsqlmodel/model.py:1055; gnrpy/gnr/sql/gnrsqldata/compiler.py:373-375 ricorsione su relation_path; gnrpy/gnr/sql/gnrsqlmodel/columns.py:340-366 AliasColumnWrapper unisce gli attributisrc/asqueel/elements.py:292; src/asqueel/compiler.py:93-95; src/asqueel/object_tree.py:292-309 (i default di firma non nascondono il dtype del target)
A3.7Ridefinizione di una virtuale con tipo diverso (alias, formula, subQuery, py, bagItem)gnrpy/gnr/sql/gnrsqlmodel/model.py:1030-1036: il nodo cambia tipo, gli attributi del tipo precedente vengono rimossisrc/asqueel/builder.py:89-95, :164-185; tests/test_virtual_column_redefinition.py:32
A3.9static=True e '*'gnrpy/gnr/sql/gnrsqlmodel/table.py:396-402 static_virtual_columns; :490 starColumns aggiunge le staticsrc/asqueel/compiler.py:663-666 star_columns: fisiche, poi virtuali static
A3.11bagItemColumn (bagcolumn, itempath, dtype)gnrpy/gnr/sql/gnrsqlmodel/model.py:1159-1192; gnrpy/gnr/sql/gnrsqlmodel/helpers.py:34 bagItemFormula (XPath)src/asqueel/semantic_elements.py:32; src/asqueel/compiler.py:105-108; src/asqueel/dialects/postgres.py:31; SQLite rifiuta (src/asqueel/dialects/sqlite.py:13-14)
A3.16Riferimenti nelle formule: $col, @rel.col, :param, :env_*gnrpy/gnr/sql/gnrsqldata/compiler.py:62-63 (COLFINDER, RELFINDER), :425 updateFieldDict; gnrpy/gnr/sql/gnrsql/execute.py:94-95 :env_* da currentEnvsrc/asqueel/compiler.py:143-158 (parametri legati e FieldReferences), :645-651 :env_* dallo snapshot d'ambiente; relazioni verso molti rifiutate (src/asqueel/compiler.py:249-250, riga A4.9)
A3.17#THIS, #IN_RANGE, #PERIODgnrpy/gnr/sql/gnrsqldata/compiler.py:74, :417, :424 (#THIS); :426, :962 (#IN_RANGE); :963 (#PERIOD solo nel WHERE)src/asqueel/compiler.py:153, :195 (#THIS espanso prima dei path); :156, :601 (#IN_RANGE); :602, :684-715 (#PERIOD, richiede workdate e locale)

diverso Differenze e motivazione

IDFunzioneLegacyAsqueelMotivazione
A3.1formulaColumn (sql_formula, dtype)gnrpy/gnr/sql/gnrsqlmodel/model.py:1265 (dtype di default 'A', :1271); gnrpy/gnr/sql/gnrsqldata/compiler.py:400-401: sql_formula prevale, select/exists usati solo se sql_formula mancasrc/asqueel/elements.py:312; src/asqueel/model.py:59-60 vuole esattamente uno fra sql_formula, select, exists; :64-67 un select_<nome> non usato dà errore; senza dtype vale 'T' (src/asqueel/object_tree.py:258)Combinazioni ambigue rifiutate invece di scegliere una definizione: docs/guide/formulas.md:176-181. dtype esplicito richiesto: docs/guide/formulas.md:21. Nessuna motivazione registrata per il default 'T' al posto di 'A'
A3.10subQueryColumn (query=, mode=)gnrpy/gnr/sql/gnrsqlmodel/model.py:1213-1262: json (:1233) jsonb_agg, xml (:1247) xmlagg; altri mode: SELECT semplicesrc/asqueel/compiler.py:110-113, :146, :207-220; json/xml solo PostgreSQL (src/asqueel/dialects/postgres.py:62). mode='join', 'to_json', 'to_xml' producono aggregazioni (src/asqueel/compiler.py:207-212); prova: mode='join' dà group_concatjson/xml su SQLite rifiutati: docs/guide/formulas.md:242-246. Vale il codice su docs/guide/formulas.md:239-240 ('other modes retain plain SELECT') per i mode join, to_json, to_xml

mancante Funzioni mancanti: punti aperti

A3.8 · _override (virtuale che sostituisce una colonna fisica omonima)

Legacy gnrpy/gnr/sql/gnrsqlmodel/model.py:1007-1019: errore salvo _override=True, che toglie la colonna fisica

Asqueel Rifiutato: SqlModelValidationError 'unknown attribute _override' (prova); fisica contro virtuale dà 'conflicting element kinds' (tests/test_virtual_column_redefinition.py:90-102)

Issue nessuna issue. docs/design/33-structure-rectification-plan.md:305-309 (decisione D4 del 2 ottobre 2026): rinviato alla composizione fra package

(a)

accettare _override come nel legacy

(b)

solo nella composizione fra package, come prevede D4

(c)

lasciarlo fuori: rinominare la virtuale

A3.12 · pyColumn (py_method, default pyColumn_<nome>)

Legacy gnrpy/gnr/sql/gnrsqlmodel/model.py:1291-1303; gnrpy/gnr/sql/gnrsqldata/compiler.py:442-446 NULL in SQL e registrazione; gnrpy/gnr/sql/gnrsqldata/query.py:257-280 calcolo Python sulle righe

Asqueel src/asqueel/elements.py:354 solo dichiarazione; selezionarla dà 'pyColumn is not connected yet' (src/asqueel/compiler.py:127, prova)

Issue nessuna issue. docs/guide/limitations.md:254, docs/guide/formulas.md:213-214

(a)

NULL in SQL e callback dopo il fetch, come nel legacy

(b)

callback dichiarata solo sul record e non nelle query

(c)

lasciarla fuori: calcolo applicativo dopo fetch()

A3.13 · compositeColumn letta come valore

Legacy gnrpy/gnr/sql/gnrsqlmodel/model.py:1078-1118, :1120-1157: virtuale static dtype JS, formula stringa '[v1, v2]'

Asqueel src/asqueel/elements.py:374 chiave multi-colonna; selezionare $nome dà 'virtual/composite columns are not connected yet' (src/asqueel/compiler.py:63-66, prova)

Issue #26 (copre solo il caso della pkey compositeColumn)

(a)

proiettare la stringa JSON come nel legacy

(b)

proiettare un valore strutturato del dialetto (array o json)

(c)

lasciarla fuori: selezionare le colonne membro

A3.14 · toolColumn

Legacy gnrpy/gnr/sql/gnrsqlmodel/model.py:1194-1211 (toolFormula)

Asqueel Assente da src/asqueel/builder.py:65-68

Issue nessuna issue. Esclusa: docs/design/44-core-release-plan.md:113

(a)

portarla come nel legacy

(b)

spostarla nel livello applicazione o UI

(c)

lasciarla fuori: formulaColumn con l'espressione esplicita

A3.15 · Colonne dinamiche: metodi formulaColumn_<x>(), variant= con variantColumn_<v>(), sqlparams[nome]={field:...}

Legacy gnrpy/gnr/sql/gnrsqlmodel/table.py:405-427 (formulaColumn_*), :440-471 (variantColumn_*), :343-356 (variante creata dai sqlparams)

Asqueel variant= rifiutato in validazione (prova); i metodi formulaColumn_* non vengono letti; un nome non dichiarato dà 'Unknown column' (src/asqueel/compiler.py:67)

Issue nessuna issue. docs/guide/limitations.md:254 ('Formula variants ... not implemented')

(a)

ricalcolare le virtuali per ambiente come nel legacy

(b)

espanderle una volta al render del modello

(c)

lasciarle fuori: dichiarare formulaColumn esplicite

A3.18 · #ENV(nome[,fallback]) e #PREF(path[,default])

Legacy gnrpy/gnr/sql/gnrsqldata/compiler.py:322-350 (env diretto, fallback, env_<nome>() della tabella), :316-320 (preferenza del package), applicati in :427-428 come testo letterale

Asqueel Rifiutati da _check_macros (src/asqueel/compiler.py:157, :610, :517-519) con il messaggio 'Hierarchical macro expansion is not connected yet', che non nomina la macro reale; tests/object_tree/test_phase4_macros.py:75-78

Issue nessuna issue (#30 tratta solo le macro hierarchical). docs/design/32-expression-resolver-plan.md:247-248 (proposta): #ENV sostituito da :env_*

(a)

espandere come nel legacy, testo letterale

(b)

tradurle in parametri legati :env_* e in un provider di preferenze

(c)

lasciarle fuori: usare :env_nome e var_*

A3.19 · #BAG / #BAGCOLS nelle colonne

Legacy gnrpy/gnr/sql/gnrsqldata/compiler.py:69-70, :1003-1004, :1283-1316; decodifica Bag dopo il fetch in gnrpy/gnr/sql/gnrsqldata/query.py:282-300

Asqueel Rifiutate da _check_macros (src/asqueel/compiler.py:610, :517-519); tests/object_tree/test_phase4_macros.py:75

Issue nessuna issue. docs/design/95-develop-reference-audit.md:95-97

(a)

decodificare la Bag dopo il fetch come nel legacy

(b)

un tipo di risultato dichiarato sulla colonna X

(c)

lasciarle fuori: bagItemColumn o decodifica applicativa

A3.20 · Macro del dialetto #TSQUERY, #TSRANK, #TSHEADLINE, #VECQUERY, #VECRANK

Legacy gnrpy/gnr/sql/gnrsqldata/compiler.py:421 (formule), :964 (WHERE); gnrpy/gnr/sql/adapters/_gnrbasepostgresadapter.py:13-46

Asqueel Rifiutate da _check_macros (src/asqueel/compiler.py:157, :610, :517-519); tests/object_tree/test_phase4_macros.py:75 (#TSQUERY)

Issue nessuna issue. docs/design/58-compiler-checklist.md:163

(a)

portare il MacroExpander del dialetto come nel legacy

(b)

funzioni esplicite del dialetto PostgreSQL con parametri legati

(c)

lasciarle fuori: SQL full-text esplicito in sql_formula o where

nuovo Funzioni nuove di asqueel

IDFunzioneAsqueelMotivazione
A3.5parameters= (mappa al posto di var_*) ed expression= (sinonimo di sql_formula)src/asqueel/model.py:49-53 (expression), :75-83 (parameters; un duplicato con var_* è rifiutato)parameters è documentato in docs/guide/formulas.md:84-85. expression non ha documentazione pubblica: esiste solo nel codice

4 Relazioni

presente 6 funzioni identiche al legacy
IDFunzioneLegacyAsqueel
A4.1relation(): destinazione 'pkg.tbl.col' / 'tbl.col', anche verso un altro packagegnrpy/gnr/sql/gnrsqlmodel/columns.py:153 _fillRelatedColumn completa 'tbl.col' con il package corrente; gnrpy/gnr/sql/gnrsqlmodel/model.py:256 richiede pkg.table.columnsrc/asqueel/object_tree_relations.py:72-75 aggiunge il package corrente e, con 'schema.table', la pkey del target; src/asqueel/validators.py:271 rifiuta 'tbl' da solo
A4.5one_name, many_name, one_group, many_group, many_order_bygnrpy/gnr/sql/gnrsqlmodel/model.py:1358-1365, :214; il group della FK passa a one_group in gnrpy/gnr/sql/gnrsqlmodel/model.py:1403-1407src/asqueel/object_tree_relations.py:31-32, :39 nel joiner; src/asqueel/model_source.py:145-149 sposta group su one_group; many_order_by passa come extra (src/asqueel/validators.py:121)
A4.7Percorso verso uno @fkey_col.col in columns, where, order_bygnrpy/gnr/sql/gnrsqldata/compiler.py:273 getFieldAlias, :509 _getRelationAlias, LEFT JOIN a :662src/asqueel/compiler.py:74 get_field_alias, :246 _get_relation_alias, LEFT JOIN a :311 con riuso dell'alias per path
A4.8Percorso multi-hop @a.@b.cgnrpy/gnr/sql/gnrsqldata/compiler.py:458 _findRelationAlias ricorsivo; la forma @a.b.c fallisce con KeyError 'joiner' a gnrpy/gnr/sql/gnrsqldata/compiler.py:541src/asqueel/compiler.py:222 _find_relation_alias ricorsivo; @a.b.c fallisce con KeyError 'joiner' a src/asqueel/compiler.py:248
A4.10Relazione su chiave composta (compositeColumn)gnrpy/gnr/sql/gnrsqldata/compiler.py:597: chiave multipla solo fra compositeColumnsrc/asqueel/object_tree_relations.py:82-86 controlla che le chiavi abbiano la stessa lunghezza; src/asqueel/compiler.py:268-276 genera la condizione AND sulle coppie
A4.15Bag relations (table.relations, model.relations)gnrpy/gnr/sql/gnrsqlmodel/table.py:277-295 relations; gnrpy/gnr/sql/gnrsqlmodel/resolvers.py:38 RelationTreeResolver; gnrpy/gnr/sql/gnrsqlmodel/model.py:195 addRelationsrc/asqueel/object_tree.py:220-224 relations, :336 model.relations; src/asqueel/object_tree_relations.py:66-113 registra le voci O e M, :174 RelationTreeResolver

diverso Differenze e motivazione

IDFunzioneLegacyAsqueelMotivazione
A4.2relation(mode='foreignkey'/'relation'/'insensitive')gnrpy/gnr/sql/gnrsqlmodel/model.py:1357 mode='relation' di default; gnrpy/gnr/sql/gnrsqlmodel/model.py:268-269 foreignkey e case_insensitive derivati da modesrc/asqueel/elements.py:435 relation(to, foreign_key=, case_insensitive=); in un package foreign_key vale True di default (src/asqueel/builder.py:396-397); weak_relation senza FK (src/asqueel/semantic_elements.py:24, src/asqueel/builder.py:387); mode= rifiutato dal validatore (prova); l'adapter legacy traduce mode (src/asqueel/legacy_source.py:113-117)docs/design/42-configuration-grammar.md:526-528: relation crea la FK, weak_relation no. Nel legacy il default mode='relation' non crea FK; in asqueel il default dentro un package la crea
A4.3relation(relation_name=...), nome dell'inversagnrpy/gnr/sql/gnrsqlmodel/model.py:1371; gnrpy/gnr/sql/gnrsqlmodel/model.py:266, :303 registra '@relation_name' sul lato onesrc/asqueel/elements.py:444 inverse= (alias back_reference, src/asqueel/object_tree_relations.py:27). relation_name= è accettato in silenzio perché contiene '_' (src/asqueel/validators.py:121) e viene ignorato: l'inversa resta col nome di default (prova)docs/design/42-configuration-grammar.md:516: inverse corrisponde al legacy relation_name. Lo scarto silenzioso di relation_name= non ha motivazione registrata
A4.4Nome inverso di default e one_one='*'gnrpy/gnr/sql/gnrsqlmodel/model.py:265-266: senza relation_name il nome è pkg_tbl_col; con one_one='*' è il nome della tabella many e la relazione non è privatasrc/asqueel/object_tree_relations.py:45 nome pkg_tbl_col, :38 private_relation; one_one è bool (src/asqueel/elements.py:443): one_one='*' rifiutato con 'expected bool' (prova); il nome di default legato a '*' non esistedocs/design/42-configuration-grammar.md:542: one_one=True dichiara cardinalità al massimo uno. Il default legato a '*' non ha motivazione registrata. Vale il codice su docs/guide/models.md:105, che dice le inverse non generate: il codice le registra sempre (src/asqueel/object_tree_relations.py:98)
A4.6onDelete, onUpdate, onDelete_sql, onUpdate_sql, deferred (solo dichiarazione)gnrpy/gnr/sql/gnrsqlmodel/model.py:1366-1370, onUpdate_sql='cascade' di default; deferred automatico con setnull in gnrpy/gnr/sql/gnrsqlmodel/model.py:276src/asqueel/elements.py:436-445 on_delete, on_update, on_delete_sql, on_update_sql, deferred, deferrable, initially_deferred; src/asqueel/model_source.py:150-176 normalizza nel joiner, cascade di default, deferred con set_null. onDelete= rifiutato; onDelete_sql= accettato e ignorato (prova)docs/design/42-configuration-grammar.md:534-540: nomi snake_case e azioni applicative raise, cascade, set_null, ignore. Lo scarto silenzioso di onDelete_sql= non ha motivazione registrata. Gli effetti in scrittura sono dell'area 5
A4.9Percorso verso molti @relation_name.col (JOIN che moltiplica le righe)gnrpy/gnr/sql/gnrsqldata/compiler.py:571 manyrelation, :665 registra la join, :1059 DISTINCT automaticosrc/asqueel/compiler.py:249-250 UnsupportedFeatureError 'many relation not yet supported'; con one_one=True diventa LEFT JOIN (src/asqueel/compiler.py:235-236, prova)docs/design/41-deferred-features.md:192 (decisione del 16 ottobre 2026): niente JOIN verso molti, niente righe moltiplicate, niente DISTINCT implicito; al loro posto aggr (riga A4.19). Vale il codice su docs/guide/queries.md:356-363 e docs/guide/limitations.md:23, che descrivono una LEFT JOIN (tests/object_tree/test_phase2_relations.py:115-117)
A4.12Condizioni di join della relazione: cnd, join_on, betweengnrpy/gnr/sql/gnrsqlmodel/model.py:222-224; gnrpy/gnr/sql/gnrsqldata/compiler.py:609-616 sostituiscono la condizione di chiavesrc/asqueel/compiler.py:281-292 le esegue nella join verso uno. join_on passa perché contiene '_' (src/asqueel/validators.py:121); cnd e between rifiutati dal validatore (prova); aggr rifiuta queste relazioni (src/asqueel/compiler.py:475). Vale il codice sul commento in src/asqueel/object_tree_relations.py:41, che le dice non eseguitedocs/design/42-configuration-grammar.md:530: le condizioni arbitrarie appartengono a virtualRelation. L'accettazione di join_on non ha motivazione registrata
A4.18aliasTable / table_alias (alias di percorso)gnrpy/gnr/sql/gnrsqlmodel/model.py:1305; gnrpy/gnr/sql/gnrsqldata/compiler.py:490 espande table_aliases dentro il percorsosrc/asqueel/configuration.py:123-125 toglie aliasTable dalla grammatica. virtualRelation(relation=...) esiste ma funziona solo in aggr (src/asqueel/compiler.py:406, :226)docs/design/41-deferred-features.md:73 (decisione del 13 ottobre 2026) e docs/design/42-configuration-grammar.md:554: virtualRelation solo verso molti. Un alias verso uno multi-hop non è esprimibile

mancante Funzioni mancanti: punti aperti

A4.11 · joinColumn e relazione su colonna virtuale

Legacy gnrpy/gnr/sql/gnrsqlmodel/model.py:1067 joinColumn; gnrpy/gnr/sql/gnrsqlmodel/columns.py:273-279 relazione virtuale con relation_path '@nome.col'

Asqueel La relazione su colonna virtuale esiste: src/asqueel/builder.py:390-395 (weak di default), src/asqueel/object_tree_relations.py:68-70, src/asqueel/compiler.py:298-299. La firma joinColumn manca. L'adapter la traduce in virtualRelation più aliasColumn (src/asqueel/legacy_source.py:70-78), ma una virtualRelation non si risolve come percorso verso uno (src/asqueel/compiler.py:226, prova)

Issue nessuna issue

(a)

aggiungere joinColumn come nel legacy: colonna virtuale, relazione e relation_path automatico

(b)

far tradurre joinColumn all'adapter in formulaColumn(...).relation(...) più aliasColumn

(c)

lasciarla fuori: il progetto scrive a mano formulaColumn con relation e aliasColumn

A4.13 · joinConditions, setJoinCondition, kwargs jc_* di query

Legacy gnrpy/gnr/sql/gnrsqldata/query.py:191 setJoinCondition; gnrpy/gnr/sql/gnrsqltable/query.py:134, :157 jc_kwargs; gnrpy/gnr/sql/gnrsqldata/compiler.py:653, :702 aggiungono la condizione, one_one rende verso uno una join verso molti

Asqueel src/asqueel/application_table.py:35 mette joinConditions in _UNSUPPORTED: query() solleva UnsupportedFeatureError (prova). setJoinCondition non esiste; jc_* rifiutati come kwarg non usate (src/asqueel/application_table.py:85-89)

Issue nessuna issue

(a)

implementarlo come nel legacy: dict per relazione con '$tbl', parametri e one_one

(b)

esprimere la condizione con una virtualRelation filtrata, usabile solo in aggr

(c)

lasciarlo fuori: condizione nella WHERE o in una subQueryColumn

A4.14 · relationDict (nomi simbolici delle colonne in query)

Legacy gnrpy/gnr/sql/gnrsqldata/query.py:146 parametro, :491 uso

Asqueel src/asqueel/application_table.py:35: query() lo rifiuta (prova). src/asqueel/compiler.py:53 cpl.relationDict contiene solo i path risolti in uscita

Issue nessuna issue

(a)

accettare relationDict come nel legacy, con i nomi simbolici precompilati

(b)

usare alias AS nelle columns

(c)

lasciarlo fuori: aliasColumn nel modello

A4.16 · getRelation, getRelationBlock, getJoiner, resolveRelationPath, relations_one, relations_many, manyRelationsList, oneRelationsList

Legacy gnrpy/gnr/sql/gnrsqlmodel/table.py:648, :696, :709, :717, :730, :755, :859, :881

Asqueel Assenti su DbTableObj e su db.table(): hasattr restituisce False (prova). Esiste solo il metodo privato src/asqueel/table_crud.py:137 _relations_many

Issue nessuna issue

(a)

portarli come nel legacy su DbTableObj, sopra la Bag relations

(b)

definire una sola API di introspezione nuova sopra relations

(c)

lasciarli fuori: il progetto legge table.relations e il joiner

A4.17 · relationExplorer

Legacy gnrpy/gnr/sql/gnrsqltable/utils.py:456; gnrpy/gnr/sql/gnrsql/query.py:378

Asqueel Nessun equivalente: hasattr restituisce False (prova)

Issue nessuna issue

(a)

portarlo come nel legacy, sopra RelationTreeResolver

(b)

esporre solo l'albero relations e lasciare l'attraversamento al chiamante

(c)

lasciarlo fuori, come funzione della UI e non del layer SQL

nuovo Funzioni nuove di asqueel

IDFunzioneAsqueelMotivazione
A4.19@rel.aggr(...) e @rel.order_by('...').aggr(...)src/asqueel/compiler.py:323 expand_many_operations, :393 many_aggr, :495 subquery correlata; src/asqueel/_many_aggregates.py:12-21 count, sum, avg, min, max, join, to_json, to_xml; solo in proiezione: where e order_by rifiutati (src/asqueel/compiler.py:396, prova)docs/guide/queries.md:389-427; docs/design/101-many-aggregation-decisions.md; to_xml solo su PostgreSQL
A4.20virtualRelation (relation= derivata, table= diretta, var_*, ask)src/asqueel/semantic_elements.py:45; src/asqueel/compiler.py:406, :436-466 derivazioni e forma table=; ask rifiutato (src/asqueel/compiler.py:447); usabile solo dentro aggrdocs/design/109-derived-virtual-relations.md; docs/design/110-direct-virtual-relations.md; docs/design/108-deferred-virtual-relation-ask.md (ask rinviato)
A4.21Relazione ereditata dall'aliasColumn (@alias.col senza .relation() sull'alias)src/asqueel/object_tree_relations.py:116-171 registra una relazione weak sull'alias; src/asqueel/elements.py:299-300docs/design/100-alias-relation-inheritance.md:15-18; docs/guide/models.md:166-185

5 Scritture

presente 5 funzioni identiche al legacy
IDFunzioneLegacyAsqueel
A5.4trigger_onInserting / onInserted / onUpdating / onUpdated / onDeleting / onDeleted; trigger_assignCounters / trigger_releaseCountersgnrpy/gnr/sql/gnrsqltable/triggers.py:112-133 e :174-179 metodi vuoti; posizioni in gnrpy/gnr/sql/gnrsql/write.py:156, :158, :167, :254, :259, :264, :308, :317, :319src/asqueel/table_triggers.py:16-47 metodi vuoti con le stesse firme; stesse posizioni in src/asqueel/db_write.py:27-28, :33, :42, :45, :50, :62, :68-69; tests/write_compiler/test_write_cycle.py:127. Gli hook si definiscono nella classe x_table_class (riga A1.18)
A5.5Trigger di campo: onInserting, onUpdating, onDeleting, onInserted, onUpdated, onDeleted, trigger_table sulla colonnagnrpy/gnr/sql/gnrsqlmodel/columns.py:237-244 registrazione; gnrpy/gnr/sql/gnrsqltable/crud.py:680-694 nome stringa: trigger_<nome>(record, fldname=, tblname=, **kwargs) sulla tabella o su trigger_table; callable: f(record, fldname)src/asqueel/elements.py:259-260 parametri di column; src/asqueel/table_crud.py:120-131 stessa chiamata, in ordine di dichiarazione delle colonne; tests/write_compiler/test_write_cycle.py:127 (trigger_upper). Prova: callable eseguita con (record, 'n')
A5.6protect_update / protect_delete / protect_validategnrpy/gnr/sql/gnrsqltable/triggers.py:140-150; chiamati in gnrpy/gnr/sql/gnrsql/write.py:154, :251-252, :306src/asqueel/table_triggers.py:34-41; chiamati in src/asqueel/db_write.py:25, :39-40, :60; tests/write_compiler/test_write_cycle.py:153
A5.14Dati del record in scrittura: chiavi '@', 'pkey', virtuali e sconosciute scartate; None omesso in INSERT; UPDATE di tutte le chiavi presenti; sql_value sempre emessognrpy/gnr/sql/adapters/_gnrbaseadapter.py:728 prepareRecordData, :755-756 sql_value; :797 insert omette i None; :843 update di ogni chiave presentesrc/asqueel/write_compiler.py:48-66 stesse regole; :103 None omesso; :112-126 SET di ogni colonna presente, None compreso; tests/write_compiler/test_write_compiler.py:61, :79, :94
A5.28Policy di riga in scrittura: delete fisico, cancellazione logica con update, partizione e bozza non applicategnrpy/gnr/sql/gnrsql/write.py:314 DELETE fisico; gnrpy/gnr/sql/gnrsql/write.py:134-168 nessun valore né controllo di partizione; filtri solo in lettura (gnrpy/gnr/sql/gnrsqldata/compiler.py:971)src/asqueel/db_write.py:64 DELETE fisico; nessun filtro di partizione, bozza o cancellazione logica in src/asqueel/write_compiler.py:72-147

diverso Differenze e motivazione

IDFunzioneLegacyAsqueelMotivazione
A5.1insert(record)gnrpy/gnr/sql/gnrsqltable/crud.py:57 delega a db.insert e restituisce il record; gnrpy/gnr/sql/gnrsql/write.py:134-168 ciclo: checkPkey, protect_validate, trigger di campo, trigger di tabella, trigger di package (:157), contatori, dbo_onInserting (:159-160), protect_draft, INSERT, _onDbChange (:165), trigger aftersrc/asqueel/table_crud.py:33-36; src/asqueel/db_write.py:21-33 stesso ordine senza trigger di package, dbo_onInserting e _onDbChange; tabella con dichiarazioni non eseguite rifiutata prima degli hook (src/asqueel/model_source.py:236); tests/write_compiler/test_write_cycle.py:127docs/guide/writes.md:11-14 e :34-36; docs/guide/hooks.md:123-128 (niente _onDbChange, trigger di package non eseguiti); docs/guide/limitations.md:31-47 (rifiuti). Dettagli nelle righe A5.7, A5.8, A5.16, A5.26
A5.2update(record, old_record=None, pkey=None)gnrpy/gnr/sql/gnrsqltable/crud.py:118-148, storename del package in tempEnv (:129-136); gnrpy/gnr/sql/gnrsql/write.py:223-265; pkey composta come stringa serializzata (gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:867)src/asqueel/table_crud.py:38-49; src/asqueel/db_write.py:35-50; chiave da old_record, poi pkey, poi record (src/asqueel/write_compiler.py:130-135); pkey composta come mapping o sequenza (src/asqueel/write_compiler.py:187-194); nessuno storename; kwargs accettati e ignorati; riga mancante: nessun errore, come il legacy (prova); tests/write_compiler/test_write_cycle.py:143docs/guide/writes.md:66-82. Forma della pkey composta: docs/guide/queries.md:494-496 (stessa scelta di record, riga A2.21). Storename del package: nessuna motivazione registrata
A5.3delete(record) e attributo deletablegnrpy/gnr/sql/gnrsqltable/crud.py:103-115 stringa letta con lock e ignoreMissing, dict passato com'è; gnrpy/gnr/sql/gnrsql/write.py:294-305 deletable anche come tag di permesso; WHERE col nome di modello (gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:890); nessun controllo delle righe cancellatesrc/asqueel/table_crud.py:51-65 stringa mancante: ritorno senza errore; dict incompleto riletto con lock; src/asqueel/db_write.py:64-66 il DELETE deve togliere una riga, altrimenti RecordNotFoundError; WHERE col sqlname (src/asqueel/write_compiler.py:141-147); deletable solo bool (src/asqueel/elements.py:174), una stringa è rifiutata dal validatore (prova); tests/write_compiler/test_write_cycle.py:168, :173, :186, :191docs/guide/writes.md:96-100; decisione utente 2026-10-15 (provvisoria): rilettura solo del dict incompleto, errore se il DELETE non toglie esattamente una riga. deletable come tag di permesso: nessuna motivazione registrata
A5.7protect_draft e dbo_onInserting / dbo_onUpdating / dbo_onDeletinggnrpy/gnr/sql/gnrsql/write.py:159-163 (dbo_onInserting, poi protect_draft), :256-257 dbo_onUpdating, :311-312 dbo_onDeleting; in update protect_draft è chiamato da gnrpy/gnr/app/gnrdbo.py:1079 dbo_onUpdatingsrc/asqueel/db_write.py:29 e :43-44 protect_draft chiamato direttamente, in update prima dei contatori; src/asqueel/db_write.py:71-76 solo se draftField e il metodo esistono; nessun dbo_* chiamatodecisione utente 2026-10-16: il concetto dbo sparisce; del suo contenuto resta protect_draft nelle posizioni legacy. Vale il codice su docs/adattamenti-legacy.md:90 ('protect_draft ... Non portato')
A5.9currentTrigger e trigger stack (event, table, record, old_record, parent)gnrpy/gnr/sql/gnrsqltable/triggers.py:107-110; gnrpy/gnr/sql/gnrsql/helpers.py:80-82 push e pop senza try/finally: dopo un'eccezione l'elemento resta; elemento in gnrpy/gnr/sql/gnrsql/helpers.py:280src/asqueel/triggers.py:9-17 stessi campi, nessun level; pop in finally (src/asqueel/triggers.py:52-57); src/asqueel/runtime.py:213-223; src/asqueel/application_table.py:305-307; tests/write_compiler/test_write_cycle.py:136. I comandi sql_* non entrano nello stack, come i raw legacydecisione utente 2026-10-15: stack come il legacy, pop anche su eccezione come chiede genropy#1688. Vale il codice su docs/guide/hooks.md:70-71, che documenta un campo level inesistente (prova: hasattr False)
A5.10self.exception(nome, record=, msg=)gnrpy/gnr/sql/gnrsqltable/table.py:92-118: nome cercato in EXCEPTIONS, rowcaption da recordCaption (:109), localizer del db (:116)src/asqueel/table_triggers.py:57-67: stesso EXCEPTIONS; rowcaption è il valore della pkey (:64-66); nessun localizer. Prova: 'The record 5 in table s.t is not updatable: ...'docs/guide/hooks.md:49-52. Caption dalla pkey al posto di recordCaption: nessuna motivazione registrata (la proprietà rowcaption manca, riga A1.23)
A5.11deleteRelated: onDelete raise / cascade / setnull / ignore, 'azione:messaggio'gnrpy/gnr/sql/gnrsqltable/crud.py:182-229: forme brevi r, c, n, i; default 'raise' per mode foreignkey (:189-191) mai raggiunto, perché relations_many contiene solo joiner con mode 'M' (gnrpy/gnr/sql/gnrsqlmodel/table.py:702): default effettivo ignore; query con subtable='*' e salto dei dbstores (:201, :208); messaggio '!!Record referenced...'src/asqueel/table_crud.py:155-177 stesse azioni, default ignore, messaggio dopo ':' (tests/write_compiler/test_write_cycle.py:197); forme brevi rifiutate con 'invalid application action' (src/asqueel/model_source.py:159, prova); nessun subtable né dbstore; tests/write_compiler/test_write_cycle.py:173. Senza on_delete decide la FK fisica (prova: IntegrityError), come nel legacydocs/guide/writes.md:102-105; decisione utente 2026-10-16: grafie legacy raise, cascade, setnull, ignore (le forme brevi non hanno usi nei repo locali). Vale il codice su docs/adattamenti-legacy.md:91 ('Nessun ciclo Python equivalente'). Dichiarazione: riga A4.6
A5.12updateRelated: onUpdate raise / cascadegnrpy/gnr/sql/gnrsqltable/crud.py:150-180: con chiave invariata esce dal ciclo con return (:156-157); raise usa il nome 'update', che non è in EXCEPTIONS (:172); setnull ignorato; old_record None dà AttributeErrorsrc/asqueel/table_crud.py:179-200: continue alla relazione successiva (:188-189); raise con 'save' (:194, prova SaveException); setnull ignorato come nel legacy (prova); senza old_record ValueError esplicito (:185-186, tests/write_compiler/test_write_cycle.py:204); cascade in tests/write_compiler/test_write_cycle.py:160decisione utente 2026-10-16 (provvisoria): correggere i difetti legacy segnalati in genropy#1686 e genropy#1687. docs/guide/writes.md:74-77
A5.13checkPkey / newPkeyValue / pkeyValue (generazione della chiave)gnrpy/gnr/sql/gnrsqltable/record.py:620; gnrpy/gnr/sql/gnrsqltable/utils.py:75 e :92: pkey composta con toTypedJSON (:88), max+1 per L/I/R (:106), getUuid (:110, :134), pkey_columns, __syscode con sysRecord_<codice> (:120)src/asqueel/table_crud.py:76-114 stesso ordine delle strategie; composta con json.dumps (:95); UUID da genro_toolbox get_uuid (:102, :113); __syscode senza sysRecord_<codice> (:108-111). Prova: chiave 1, 2 su intero, 'Z0G3...' di 22 caratteri su testoAlgoritmo UUID: docs/design/40-model-and-operation-recipes.md:207-212 (decisione del 16 ottobre: id ordinabile nel tempo); docs/guide/writes.md:39-43. sysRecord_<codice> e formato JSON della composta: nessuna motivazione registrata
A5.17raw_insert / raw_update / raw_delete e insertManygnrpy/gnr/sql/gnrsqltable/crud.py:67, :77, :86, :91; gnrpy/gnr/sql/gnrsql/write.py:180-220 senza trigger ma con _onDbChange(_raw=True); insertMany (gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:821) assegna newPkeyValue alle chiavi mancanti (:829) e usa le colonne del primo recordsrc/asqueel/application_table.py:309-328 sql_insert (record o lista), sql_update, sql_delete: solo SQL, nessun _onDbChange, nessun controllo require_write_capabilities (prova: sql_insert su tabella sys_fields riuscito); lista: un executemany per gruppo di record consecutivi con le stesse colonne, senza assegnare chiavi (src/asqueel/write_compiler.py:79-93); raw_*, insertMany assenti (prova); tests/write_compiler/test_write_compiler.py:246, :253docs/guide/writes.md:111-136; decisione utente 2026-10-15: i raw servono a non gestire i trigger, insertMany sostituito da sql_insert con una lista, solo SQL. Vale il codice su docs/adattamenti-legacy.md:48-51, :55-56, :75-78 e :93 (raw_* con hook DB)
A5.18sql_deleteSelection(where, _pkeys)gnrpy/gnr/sql/gnrsqltable/crud.py:280-295 legge le pkey con lock, poi DELETE ... IN :pkeyList senza triggerAssente (prova). Al suo posto query(...).sql_delete(): src/asqueel/application_table.py:103-108, un solo DELETE ... WHERE (pk) IN (SELECT della query) con filtri, ordine e limit della query; restituisce il numero di righe (src/asqueel/write_compiler.py:170-174); tests/write_compiler/test_write_compiler.py:214docs/guide/writes.md:138-155; decisione utente 2026-10-15: sql_deleteSelection tolto dal core, semmai rientra nell'adattatore GenroPy

mancante Funzioni mancanti: punti aperti

A5.8 · Trigger di package trigger_<evento>_<pkg> e avoid_trigger_<pkg>

Legacy gnrpy/gnr/sql/gnrsqltable/crud.py:696-710 per ogni package dell'applicazione, saltati con avoid_trigger_<pkg> nell'env; chiamati in gnrpy/gnr/sql/gnrsql/write.py:157, :168, :255, :265, :309, :318

Asqueel Assenti: il ciclo non li chiama (src/asqueel/db_write.py:6 nel docstring); un metodo trigger_onInserted_<pkg> resta inerte

Issue nessuna issue. decisione utente 2026-10-15: personalizzazioni rinviate, la struttura deve permetterle dopo. docs/guide/hooks.md:124-128

(a)

chiamarli come nel legacy, nell'ordine dei package, con avoid_trigger_<pkg>

(b)

comporre i trigger dei foglietti di personalizzazione con ereditarietà e super()

(c)

lasciarli fuori: la logica va nel trigger di tabella

A5.15 · Valore Bag in una colonna X

Legacy gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:742-747 la Bag diventa XML (toXml) prima del binding

Asqueel Nessuna conversione in src/asqueel/write_compiler.py:48-66. Prova su SQLite: insert con Bag dà ProgrammingError 'type Bag is not supported'

Issue nessuna issue in asqueel; dipende da genro-bag#72 (formato bagfield). decisione utente 2026-10-16: bagfield mode di genro-bag, senza conversione finché manca

(a)

scrivere l'XML legacy GenRoBag con i tipi _T, come il legacy

(b)

scrivere un formato tipizzato diverso (TYTX JSON)

(c)

lasciarlo fuori: il chiamante converte la Bag in testo prima della scrittura

A5.20 · deleteSelection (con trigger)

Legacy gnrpy/gnr/sql/gnrsqltable/crud.py:297-324 query con lock, poi delete(r) per ogni riga; restituisce la selezione

Asqueel Assente (prova); query(...).sql_delete() non esegue i trigger (riga A5.18)

Issue nessuna issue

(a)

portarlo come nel legacy

(b)

un terminale della query che cancella riga per riga con il ciclo

(c)

lasciarlo fuori: fetch con for_update e delete() per ogni riga

A5.21 · batchUpdate(updater, where/pkey/_pkeys, _raw_update, autocommit, ...)

Legacy gnrpy/gnr/sql/gnrsqltable/crud.py:349-387 e :389-436 query con lock, updater dict o callable, update() per riga, autocommit e deferredTotalize

Asqueel Assente (prova); query(...).sql_update() scrive senza hook (riga A5.19)

Issue #32

(a)

portarlo come nel legacy

(b)

un terminale della query che aggiorna riga per riga con il ciclo

(c)

lasciarlo fuori: fetch con for_update e update() per ogni riga

A5.22 · recordToUpdate(pkey, ...) (RecordUpdater)

Legacy gnrpy/gnr/sql/gnrsqltable/crud.py:345; gnrpy/gnr/sql/gnrsqltable/helpers.py:190 context manager: legge con lock, poi update, insert (insertMissing) o delete (pkey False), anche raw

Asqueel Assente (prova)

Issue #17

(a)

portare RecordUpdater come nel legacy

(b)

un context manager ridotto: solo update e delete di una riga esatta

(c)

lasciarlo fuori: record(..., for_update=True, mode='dict') e update(record, old_record)

A5.23 · insertOrUpdate(record)

Legacy gnrpy/gnr/sql/gnrsqltable/crud.py:252-263 cerca la pkey con lock; insert se manca, altrimenti update con la prima riga

Asqueel Assente (prova)

Issue nessuna issue

(a)

portarlo come nel legacy

(b)

un upsert SQL del dialetto (ON CONFLICT) senza trigger

(c)

lasciarlo fuori: record con ignoreMissing, poi insert o update

A5.24 · newrecord(assignId, _fromRecord, **kwargs), defaultValues, extendDefaultValues, recordCopy

Legacy gnrpy/gnr/sql/gnrsqltable/record.py:161-177; :600 defaultValues; :543 extendDefaultValues (defaultFrom); :141 recordCopy

Asqueel Assenti (prova); una colonna con default o default_from blocca la scrittura (src/asqueel/model_source.py:249)

Issue #28. Dichiarazione default: riga A1.7

(a)

portarli come nel legacy, con Bag e defaultFrom

(b)

newrecord che restituisce un dict con i soli default dichiarati

(c)

lasciarli fuori: valori iniziali nel chiamante o in trigger_onInserting

A5.25 · touchRecords(_pkeys, method, ...)

Legacy gnrpy/gnr/sql/gnrsqltable/triggers.py:52-100 riscrive o applica un metodo a una selezione

Asqueel Assente (prova)

Issue nessuna issue. decisione utente 2026-10-15: per ora non si fa

(a)

portarlo come nel legacy, con _notUserChange

(b)

un terminale della query che riesegue il ciclo update sulle righe

(c)

lasciarlo fuori: fetch e update() per ogni riga

A5.26 · Effetti di _onDbChange: totalizer_<x>, logChanges, notify

Legacy gnrpy/gnr/sql/gnrsql/write.py:50-85 updateTotalizers, onLogChange e change log; :87-131 NOTIFY 'dbevent'

Asqueel Nessun _onDbChange. logChanges e notify rifiutati dal validatore come attributi sconosciuti; totalizer_<x> accettato e ignorato (prova)

Issue nessuna issue. decisione utente 2026-10-15: asqueel per ora solo database, totalizer esclusi. docs/guide/hooks.md:123-128

(a)

eseguirli dopo ogni scrittura come nel legacy

(b)

solo NOTIFY e change log, senza totalizer

(c)

lasciarli fuori: trigger after dell'applicazione o trigger del database

A5.27 · Cascata dell'archiviazione logica: onArchivingRecord / archiveRelatedRecords

Legacy gnrpy/gnr/sql/gnrsqltable/copy.py:70-100 batchUpdate del campo di cancellazione logica sulle figlie con onDelete cascade; avviata da gnrpy/gnr/app/gnrdbo.py:1088 quando cambia __del_ts

Asqueel Assente: l'update del campo di cancellazione logica non tocca le figlie (src/asqueel/db_write.py:35-50, nessun dbo_onUpdating)

Issue nessuna issue. Collegata a #32 (batchUpdate)

(a)

eseguirla in update come nel legacy

(b)

un comando dedicato di cancellazione logica che applica la cascata

(c)

lasciarla fuori: aggiornare le figlie in trigger_onUpdating

A5.29 · check_updatable / check_deletable / isDraft / islocked_write / islocked_delete / hasProtectionColumns

Legacy gnrpy/gnr/sql/gnrsqltable/triggers.py:152, :186-231 protect_* senza eccezione, readOnly della tabella, campo bozza

Asqueel Assenti (prova). La proprietà draftField esiste (src/asqueel/table_triggers.py:50)

Issue nessuna issue

(a)

portarli come nel legacy

(b)

solo check_updatable e check_deletable sopra protect_*

(c)

lasciarli fuori: il chiamante intercetta le eccezioni di protect_*

A5.30 · Helper di tabella: setColumns, changePrimaryKeyValue, empty, fillFromSqlTable, duplicateRecord e copia/incolla

Legacy gnrpy/gnr/sql/gnrsqltable/crud.py:438, :96, :269, :273; gnrpy/gnr/sql/gnrsqltable/copy.py:251 duplicateRecord con onDuplicating, :107 onCopyRecord, :163 onPasteRecord

Asqueel Assenti (prova)

Issue nessuna issue (recordCopy: #28)

(a)

portarli come nel legacy

(b)

solo setColumns ed empty, senza copia e incolla

(c)

lasciarli fuori: record, update, insert e sql_delete sulla query

nuovo Funzioni nuove di asqueel

IDFunzioneAsqueelMotivazione
A5.16RETURNING dei valori decisi dal databasesrc/asqueel/write_compiler.py:215-238 colonne con sql_value, generated_expression, sqldefault omessa o dtype serial; src/asqueel/db_write.py:78-81 scritte nel record prima dei trigger after; anche sql_insert (src/asqueel/application_table.py:316-318); solo con la capability returning_insert, che SqliteDialect eredita da PostgresDialect (src/asqueel/dialects/sqlite.py:8, src/asqueel/dialects/postgres.py:18-22): RETURNING su PostgreSQL e su SQLite (prova); tests/write_compiler/test_write_compiler.py:70docs/guide/writes.md:45-47; decisione utente 2026-10-15 (provvisoria). Vale il codice su docs/guide/writes.md:48-49 e docs/guide/limitations.md:74-75, che dicono SQLite senza RETURNING. Su un dialetto senza capability il RETURNING cade senza errore: #21
A5.19query(...).sql_update(values)src/asqueel/application_table.py:110-126 un solo UPDATE ... WHERE (pk) IN (SELECT della query); group_by e having rifiutati; parametri condivisi rifiutati (src/asqueel/write_compiler.py:205-208); tests/write_compiler/test_write_compiler.py:214, :233docs/guide/writes.md:138-155; decisione utente 2026-10-16: scritture su più righe come terminali della query

6 Transazioni

presente 3 funzioni identiche al legacy
IDFunzioneLegacyAsqueel
A6.1tempEnv(**kwargs) / temp_envgnrpy/gnr/sql/gnrsql/env.py:46 restituisce TempEnv; gnrpy/gnr/sql/gnrsql/helpers.py:196-218: all'uscita ripristina le chiavi esistenti e toglie una chiave aggiunta solo se vale ancora il valore temporaneosrc/asqueel/runtime.py:178-185 (temp_env e tempEnv restituiscono il db); src/asqueel/environment.py:125-138 stessa regola di ripristino. Prova: chiave aggiunta e poi cambiata nel blocco resta dopo l'uscita, come nel legacy
A6.5connectionName in ambiente, currentConnectionName, usingMainConnection, '_main_connection'gnrpy/gnr/sql/gnrsql/env.py:209-211, :193-195; gnrpy/gnr/sql/gnrsql/helpers.py:47 MAIN_CONNECTION_NAMEsrc/asqueel/runtime.py:94-103 stesso default '_main_connection'; un nome non stringa solleva TypeError (:98-99)
A6.19Parametri :env_* e preparazione degli argomenti in executegnrpy/gnr/sql/gnrsql/execute.py:94-113: :env_* da currentEnv (mancante vale None), env_workdate di default, env_storename, decodifica bytes, escape \$ e \@src/asqueel/drivers/sql.py:28-44 stessa preparazione; prova: :env_user mancante vale None

diverso Differenze e motivazione

IDFunzioneLegacyAsqueelMotivazione
A6.2currentEnv (lettura e assegnazione), updateEnv(_excludeNoneValues), clearCurrentEnvgnrpy/gnr/sql/gnrsql/env.py:63-71 dict per thread, il setter accetta qualsiasi valore; :105 updateEnv; :59 clearCurrentEnvsrc/asqueel/runtime.py:130-145; src/asqueel/environment.py:91-101 dict per thread, il setter solleva TypeError se il valore non è un dict; current_env in più restituisce una copia in sola lettura (src/asqueel/environment.py:87-89)docs/guide/environment.md:59-63 e :109-114 (current_env come snapshot). Nessuna motivazione registrata per il TypeError del setter
A6.3workdate / localegnrpy/gnr/sql/gnrsql/env.py:75-86 workdate con fallback a date.today(); :90-101 locale con fallback a defaultLocale(), che valida l'identificativo e ricade su en_GB (gnrpy/gnr/core/gnrlocale.py:308-313)src/asqueel/runtime.py:160-176; src/asqueel/environment.py:103-117 stesso fallback per workdate; locale da GNR_LOCALE o locale.getlocale() senza validazione. Prova: con locale di sistema 'C' asqueel restituisce 'C'Mancata validazione dichiarata in docs/guide/environment.md:69-72 e docs/guide/limitations.md:211-212; nessuna motivazione registrata
A6.6Connessioni per thread e per nome, aperte alla prima istruzionegnrpy/gnr/sql/gnrsql/connections.py:79-102 chiave (storename, connectionName) per thread; ogni nome usa gli stessi parametri fisici dello store (adapter.connect(storename), :97)src/asqueel/runtime.py:35-40 e :110-123 stato per thread e per nome; apertura a :393-398. Un nome può avere una connessione dichiarata con un altro database fisico (src/asqueel/configuration.py:308-322); con dichiarazioni esplicite un nome non dichiarato dà ValueError 'Unknown named connection' (:321, prova). Le operazioni sulla connessione devono avvenire nel thread che l'ha creata (src/asqueel/runtime.py:360-362)Dichiarazione per nome descritta in docs/guide/cli.md:81-90; nessuna motivazione registrata
A6.8db.commit()gnrpy/gnr/sql/gnrsql/transactions.py:47-93: per ogni connessione non committata con lo stesso connectionName (tutti gli store) esegue deferToCommit, eccezioni pendenti, commit, deferAfterCommit, poi onDbCommitted; consentito anche dentro un triggersrc/asqueel/runtime.py:245-247 e :454-485 stesso ordine sulla sola connessione selezionata. Dentro una scrittura o un hook, commit sulla connessione in uso solleva TransactionStateError (:240-243, prova); su un'altra connessione con nome è consentito (prova). Nessun onDbCommitted (riga A6.15)decisione utente 2026-10-15: commit e rollback rifiutati sulla connessione in uso dentro un trigger e consentiti su un'altra, per il deadlock segnalato in genropy#1621 (provvisoria)
A6.9db.rollback() / rollbackAll()gnrpy/gnr/sql/gnrsql/transactions.py:252-264 rollback della sola connessione, code differite ed eccezioni pendenti restano in currentEnv e partono al commit successivo; :266-287 rollbackAll le svuotasrc/asqueel/runtime.py:249-251, :493-496; _finish svuota code ed eccezioni pendenti (:427-452, :355-358). rollbackAll assente: rollback ha già il suo effetto sull'unico store. Rifiutato dentro una scrittura come il commit (:240-243). Prova: callback registrata e poi rollback non parte al commit successivodecisione utente 2026-10-16: correzione di genropy#1689 (rollback e callback differite); docs/guide/transactions.md:123-125
A6.10Rollback automatico su errore SQLgnrpy/gnr/sql/gnrsql/execute.py:154-159 registra l'errore, chiama self.rollback() e rilanciasrc/asqueel/runtime.py:404-413 rollback e rilancio dell'errore originale; se il rollback fallisce la connessione viene scartata e outcome vale 'unknown' (:440-447). Svuota anche le code differite (riga A6.9)docs/guide/transactions.md:52-53 e :60-61
A6.11Errore Python in un trigger o hook di scritturaLa transazione resta valida: insert/update/delete sono solo avvolti da in_triggerstack (gnrpy/gnr/sql/gnrsql/helpers.py:56-85; gnrpy/gnr/sql/gnrsql/write.py:133-134); il commit successivo salva la scrittura interrottasrc/asqueel/runtime.py:253-271 _write_operation marca la connessione rollback-only; commit rifiutato finché non si chiama rollback (:379-382; prova: 'Connection is rollback-only; call rollback before reuse')decisione utente 2026-10-15 (provvisoria): rollback-only, con il possibile difetto legacy segnalato in genropy#1696; docs/guide/legacy.md:76-80
A6.12deferToCommit / deferAfterCommit (_deferredBlock, _deferredId, deferredCommitRecursion, onCommittingStep)gnrpy/gnr/sql/gnrsql/transactions.py:139, :155, :171-208 blocchi e deduplica su (id(cb), deferredId); :112-129 ricorsione; :75 e :90 onCommittingStep. Le code stanno in currentEnv con chiave connectionKey (:192)src/asqueel/runtime.py:225-233, :314-325 stessi blocchi, deduplica e kwargs restituiti; :333-353 onCommittingStep e connectionName selezionato, blocchi in ordine di etichetta minuscola, deferredCommitRecursion (:351). Le code stanno nello stato della connessione: clearCurrentEnv e acquire() non le cancellanodocs/guide/transactions.md:69-73 (code per thread e nome); nessuna motivazione registrata per lo spostamento fuori da currentEnv
A6.13deferredRaise(exception)gnrpy/gnr/sql/gnrsql/transactions.py:131-137 lista in currentEnv; :84-87 commit solleva GnrException e non svuota la lista: ogni nuovo commit la rilanciasrc/asqueel/runtime.py:235-238, :327-331 (TypeError se non è un'eccezione); :464-470 DeferredCommitError e connessione rollback-only. Prova: secondo commit dà TransactionStateError, rollback la svuotadecisione utente 2026-10-16: correzione di genropy#1690; docs/guide/transactions.md:67 e :123-125
A6.17db.execute(sql, sqlargs, cursor, cursorname, dbtable, storename, _adaptArguments)gnrpy/gnr/sql/gnrsql/execute.py:47-56 firma; restituisce il cursore (:164); cursorname '*' crea un cursore server (:128-131)src/asqueel/runtime.py:194-201 execute(query, sqlargs): stringa SQL o CompiledQuery; restituisce QueryResult con rows, rowcount, columns (src/asqueel/drivers/result.py:7-19). cursor, cursorname, dbtable, storename danno TypeError (prova). executemany in più (src/asqueel/runtime.py:203-206)docs/guide/transactions.md:3-7 (un solo percorso di esecuzione); nessuna motivazione registrata per la firma ridotta
A6.18autocommit (execute(autocommit=True), connessione autocommit)gnrpy/gnr/sql/gnrsql/execute.py:161-162 commit dopo l'istruzione; connessione con autoCommit in gnrpy/gnr/sql/adapters/gnrpostgres3.py:41 e :54execute non ha il parametro (TypeError, prova); connect_kwargs autocommit rifiutato con ValueError (src/asqueel/runtime.py:56-57, :395-396; src/asqueel/drivers/psycopg.py:40-41)docs/guide/transactions.md:22-23 e docs/guide/limitations.md:137 ('execute() never commits automatically'); nessuna motivazione registrata
A6.21db.closeConnection()gnrpy/gnr/sql/gnrsql/connections.py:41-57 rollback e chiusura di tutte le connessioni del thread; gli errori sono ignoratisrc/asqueel/runtime.py:273-284 rollback e chiusura di tutte le connessioni del thread; dentro una scrittura solleva TransactionStateError (prova); il primo errore di pulizia viene rilanciato dopo aver chiuso le altre (:286-297); azzera lo stato di acquire() (:284)docs/guide/transactions.md:110-114; nessuna motivazione registrata per la propagazione degli errori
A6.27Transazioni e lock su SQLitegnrpy/gnr/sql/adapters/gnrsqlite.py:80 connessione sqlite3 con transazione implicita di default; for_update non produce nulla (:140-141); lockTable vuoto (:390-392)src/asqueel/drivers/sqlite.py:40 isolation_level=None; :64-65 BEGIN IMMEDIATE alla prima istruzione, letture comprese; :66-70 for_update in transazione aperta prende il lock con un UPDATE senza effetto; FK attive (:44)decisione utente 2026-10-16: for_update su SQLite prende il lock di scrittura del database; docs/guide/sqlite.md:45-50

mancante Funzioni mancanti: punti aperti

A6.4 · setLocale() (locale applicato all'adapter)

Legacy gnrpy/gnr/sql/gnrsql/env.py:213-216 chiama adapter.setLocale; su PostgreSQL è vuoto (gnrpy/gnr/sql/adapters/_gnrbasepostgresadapter.py:241-246)

Asqueel Assente: hasattr(db, 'setLocale') è False (prova)

Issue nessuna issue

(a)

metodo con lo stesso effetto del legacy, vuoto su PostgreSQL

(b)

impostare la locale di sessione nel driver alla connessione

(c)

lasciarlo fuori: la locale resta solo valore d'ambiente (:env_locale)

A6.7 · storename, use_store, store multipli ('*' o lista), currentStorename, connection, connectionKey, get_dbname

Legacy gnrpy/gnr/sql/gnrsql/env.py:131, :139, :204; gnrpy/gnr/sql/gnrsql/connections.py:59, :104-121; esecuzione parallela su più store in gnrpy/gnr/sql/gnrsql/execute.py:137-145

Asqueel Uno storename diverso da '_main_db' solleva UnsupportedFeatureError 'Database stores are not implemented in this profile' (src/asqueel/runtime.py:105-108; prova con 'other' e '*'). use_store, currentStorename, connection, connectionKey, get_dbname assenti (prova)

Issue nessuna issue. docs/guide/transactions.md:48; docs/adattamenti-legacy.md:154-157

(a)

store come nel legacy: connessione per (storename, connectionName) ed esecuzione su più store

(b)

solo selezione di un singolo store, senza esecuzione multi-store

(c)

lasciarlo fuori: un AsqueelDb per database fisico o una connessione con nome dichiarata

A6.14 · autoCommit, dbevents, systemDbEvent, GnrMissedCommitException, tblobj.dbevents / notifyDbUpdate

Legacy gnrpy/gnr/sql/gnrsql/transactions.py:210-233; gnrpy/gnr/sql/gnrsql/helpers.py:152; gnrpy/gnr/sql/gnrsqltable/triggers.py:45-50

Asqueel Assenti su db e su tabella: hasattr restituisce False (prova)

Issue nessuna issue. decisione utente 2026-10-15: asqueel solo database; dbevents e notifyDbEvent si riprendono con l'applicazione

(a)

portarli come nel legacy, con dbevents in currentEnv per connessione

(b)

metodi vuoti nel core, riempiti dal livello applicativo

(c)

lasciarli fuori: deferAfterCommit per le notifiche applicative

A6.15 · onDbCommitted()

Legacy gnrpy/gnr/sql/gnrsql/transactions.py:93 chiamato alla fine di commit; :235-241 vuoto, ridefinito dal db applicativo

Asqueel Assente (prova); nessun punto equivalente dopo il commit oltre a deferAfterCommit

Issue nessuna issue. decisione utente 2026-10-15 (solo database); docs/adattamenti-legacy.md:96

(a)

hook vuoto chiamato a fine commit come nel legacy

(b)

hook fornito dal livello applicativo sopra AsqueelDb

(c)

lasciarlo fuori: deferAfterCommit registrato dal chiamante

A6.16 · NOTIFY: attributo di tabella notify, db.notify, db.listen

Legacy gnrpy/gnr/sql/gnrsql/write.py:87-131 _dbNotify invia NOTIFY 'dbevent' con il payload; gnrpy/gnr/sql/gnrsql/transactions.py:289-305 listen e notify delegati all'adapter

Asqueel Assenti (prova). src/asqueel/db_write.py:5 esclude _onDbChange e NOTIFY dal ciclo di scrittura

Issue nessuna issue. decisione utente 2026-10-15: i comandi eseguono solo lo SQL; notify (2 usi) va tra le divergenze

(a)

_dbNotify e listen/notify come nel legacy, solo PostgreSQL

(b)

NOTIFY registrato con deferAfterCommit nel livello applicativo

(c)

lasciarlo fuori: il progetto chiama NOTIFY con db.execute

A6.20 · Commento SQL con l'utente, sql_audit, debugger

Legacy gnrpy/gnr/sql/gnrsql/execute.py:105 e :120 antepone '-- <sql_comment o user>'; gnrpy/gnr/sql/gnrsql/helpers.py:88-117 log di audit con il tempo; debugger a :149-152

Asqueel Nessun corrispondente: grep di sql_comment, sql_details, debugger in src/asqueel non trova nulla

Issue nessuna issue

(a)

commento e audit come nel legacy

(b)

un solo hook di tracciamento sul driver, senza modificare il testo SQL

(c)

lasciarli fuori: logging del driver (psycopg, sqlite3)

A6.25 · tblobj.lock(mode, nowait) / adapter.lockTable

Legacy gnrpy/gnr/sql/gnrsqltable/crud.py:481-487; PostgreSQL scrive 'NO WAIT' (gnrpy/gnr/sql/adapters/_gnrbasepostgresadapter.py:319-325); SQLite non fa nulla (gnrpy/gnr/sql/adapters/gnrsqlite.py:390-392)

Asqueel tblobj.lock assente (prova). Esiste solo WriteCompiler.lock_table con NOWAIT corretto e None su SQLite (src/asqueel/write_compiler.py:176-181), senza chiamanti in src

Issue nessuna issue (decisione utente 2026-10-16: correzione di genropy#1692 per NOWAIT, già nel compilatore)

(a)

tblobj.lock che esegue lock_table, come nel legacy

(b)

metodo di db con nome di tabella

(c)

lasciarlo fuori: db.execute('LOCK ...')

A6.26 · for_update con modo stringa ('NOWAIT', 'SKIP LOCKED')

Legacy gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:717-723 una stringa viene aggiunta dopo FOR UPDATE OF; SQLite restituisce '' (gnrpy/gnr/sql/adapters/gnrsqlite.py:140-141)

Asqueel src/asqueel/compiler.py:557-559 for_update deve essere booleano: ValueError (prova). for_update=True: riga A2.12

Issue nessuna issue. docs/guide/limitations.md:248

(a)

accettare la stringa del modo come nel legacy

(b)

parametri espliciti nowait e skip_locked

(c)

lasciarlo fuori: for_update booleano

A6.28 · setConstraintsDeferred()

Legacy gnrpy/gnr/sql/gnrsql/transactions.py:243-250; gnrpy/gnr/sql/adapters/gnrpostgres3.py:347

Asqueel Assente (prova)

Issue nessuna issue

(a)

portarlo come nel legacy, SET CONSTRAINTS ALL DEFERRED

(b)

solo l'attributo deferred della relazione (riga A4.6)

(c)

lasciarlo fuori: db.execute('SET CONSTRAINTS ALL DEFERRED')

A6.29 · analyze() / vacuum(table, full)

Legacy gnrpy/gnr/sql/gnrsql/transactions.py:307-318 delegati all'adapter

Asqueel Assenti (prova)

Issue nessuna issue

(a)

portarli come nel legacy

(b)

spostarli negli strumenti di migrazione

(c)

lasciarli fuori: db.execute('ANALYZE'); VACUUM non può passare da db.execute, che apre sempre una transazione (riga A6.18)

nuovo Funzioni nuove di asqueel

IDFunzioneAsqueelMotivazione
A6.22db.close(), with db:, acquire()src/asqueel/runtime.py:299-312 close per il thread corrente; :524-535 with db: chiude all'uscita; :147-158 acquire() svuota currentEnv alla prima presa del thread; src/asqueel/hosting.py:100docs/guide/transactions.md:110-112; docs/guide/release-notes.md:65 (acquire)
A6.23db.outcomesrc/asqueel/runtime.py:187-192 not_started, active, committed, rolled_back, unknowndocs/guide/transactions.md:88-90
A6.24Controllo dell'ambiente dei compilati (EnvironmentMismatchError)src/asqueel/drivers/sql.py:113-115 registra le chiavi env usate; src/asqueel/runtime.py:387-388 rifiuta l'esecuzione se i valori sono cambiati. Prova: CompiledQuery con :env_x preparata in tempEnv(x=1) ed eseguita fuori dà EnvironmentMismatchErrordocs/guide/environment.md:78-82

7 Migrazione

presente 8 funzioni identiche al legacy
IDFunzioneLegacyAsqueel
A7.5Creazione del database mancante (CREATE DATABASE)added_db in gnrpy/gnr/sql/gnrsqlmigration/command_builder.py:134; eseguito con la connessione di servizio (gnrpy/gnr/sql/gnrsqlmigration/executor.py:237)Il nome del database viene dalle impostazioni di connessione (src/asqueel/migration.py:349-351); il motore crea il database mancante; test tests/application_integration/test_cli_postgres.py:37-40
A7.6Schemi applicativi e CREATE SCHEMA dal sqlschema del packagegetApplicationSchemas restituisce il sqlname dei package (gnrpy/gnr/sql/gnrsql/env.py:175); added_schema in gnrpy/gnr/sql/gnrsqlmigration/command_builder.py:149Schemi proiettati (src/asqueel/migration.py:96-97) passati come application_schemas (src/asqueel/migration.py:352-356); su SQLite uno schema è un file separato (tests/sqlite/test_migration_api.py:22-23); evoluzione senza ricreare lo schema (tests/test_wf_phase5_integration.py:95)
A7.10Attributi di colonna migrati: dtype, size, notnull, sqldefault, unique, extra_sql, generated_expression; normalizzazione X/Z/P→T, size fissa→C, ':n'→'0:n', N 'n'→'n,0'COL_JSON_KEYS in gnrpy/gnr/sql/gnrsqlmigration/structures.py:67; convert_colattr in gnrpy/gnr/sql/gnrsqlmigration/orm_extractor.py:496-508; minimo di size azzerato (:205)src/asqueel/migration.py:132-149 con le chiavi COL_JSON_KEYS del motore e lo stesso dtype di default (:139). Prova: X, Z, P diventano T; A con size '10' diventa C; '5:10' diventa '0:10'; N '10' diventa '10,0'; extra_sql e generated_expression proiettati
A7.11Chiave primaria nella migrazione: pkeys, notnull '_auto_', pkey composta, cambio di pkeygnrpy/gnr/sql/gnrsqlmigration/orm_extractor.py:217 notnull '_auto_' e unique tolto sulla pkey singola; changed_table ricrea la PK (gnrpy/gnr/sql/gnrsqlmigration/command_builder.py:297)src/asqueel/migration.py:110-113 e :140-143; pkey su compositeColumn risolta nei membri (src/asqueel/model_source.py:130-132); test tests/object_tree/test_migration.py:140
A7.14Politica di rimozione (removeDisabled)gnrpy/gnr/sql/gnrsqlmigration/migrator.py:144 default True, eventi removed saltati (:188); solo removed_column produce DROP COLUMN (gnrpy/gnr/sql/gnrsqlmigration/command_builder.py:759); tabelle, indici, relazioni, vincoli ed estensioni no-op (:755, :772-784)allow_removals=False di default (src/asqueel/migration.py:297, :357); --allow-removals (src/asqueel/cli.py:42-43); test tests/application_integration/test_cli_postgres.py:65-77 (DROP COLUMN sì, DROP TABLE no)
A7.15FK da relation con foreign_key: azioni SQL, deferred, colonne composte, schema di destinazionegnrpy/gnr/sql/gnrsqlmigration/orm_extractor.py:263 solo se il joiner ha foreignkey; azioni da onDelete_sql/onUpdate_sql (:368); deferred diventa deferrable e initially_deferred; nome hash (:410); FK eseguite per ultime (gnrpy/gnr/sql/gnrsqlmigration/executor.py:134)src/asqueel/migration.py:151-185 (azioni :170-172, deferred :173-177, constraint_name :180); test tests/object_tree/test_migration.py:135-146 (FK composta, CASCADE, deferred, schema di destinazione). Dichiarazione: riga A4.6
A7.20unique su colonna e compositeColumn(unique=True)unique in COL_JSON_KEYS (gnrpy/gnr/sql/gnrsqlmigration/structures.py:67); vincolo UNIQUE multicolonna (gnrpy/gnr/sql/gnrsqlmigration/orm_extractor.py:282-302)src/asqueel/migration.py:218-224; test tests/test_wf_phase4_renderer.py:199
A7.22Nomi di vincoli e indici (ignore_constraint_name)gnrpy/gnr/sql/gnrsqlmigration/migrator.py:142 default True; changed_index, changed_relation e changed_constraint ignorano il cambio di nome (gnrpy/gnr/sql/gnrsqlmigration/command_builder.py:644, :673, :723)SqlMigrator(..., ignore_constraint_name=True) (src/asqueel/migration.py:357); constraint_name della relation e name dell'index proiettati (src/asqueel/migration.py:180, :238)

diverso Differenze e motivazione

IDFunzioneLegacyAsqueelMotivazione
A7.1db.checkDb(applyChanges=) / model.check() / modelChanges / applyModelChanges()gnrpy/gnr/sql/gnrsql/schema.py:99 delega a model.check (gnrpy/gnr/sql/gnrsqlmodel/model.py:454), che usa SqlModelChecker (gnrpy/gnr/sql/gnrsqlutils.py:146), salva la lista modelChanges e restituisce un booleano; applyModelChanges (gnrpy/gnr/sql/gnrsqlmodel/model.py:487) esegue la lista e fa commitNessun checkDb, nessun modelChanges. db.migration_plan() (src/asqueel/application.py:114) con plan.empty (src/asqueel/migration.py:292-294) al posto del booleano; db.migrate() (src/asqueel/application.py:123) al posto di applyChanges=True. Un solo motore (asqueel-migration): SqlModelChecker non ha controparteI due metodi sono descritti in docs/guide/migrations.md:14-35; nessuna motivazione registrata per l'assenza di checkDb
A7.2db.diffOrmToSql() / SqlMigrator.getChanges(): anteprima SQL senza applicaregnrpy/gnr/sql/gnrsql/schema.py:110; gnrpy/gnr/sql/gnrsqlmigration/executor.py:75 restituisce una stringa SQL con le FK in coda (:134)db.migration_plan() (src/asqueel/migration.py:297-303) non cambia nulla e restituisce MigrationPlan(commands, warnings, skipped) (src/asqueel/migration.py:281-290); commands è la stringa di getChanges (:330); test tests/sqlite/test_migration_api.py:30Oggetto piano con warnings e skipped: docs/guide/migrations.md:24-27; nessuna motivazione registrata per il cambio del valore restituito
A7.3db.syncOrmToSql() / SqlMigrator.applyChanges()gnrpy/gnr/sql/gnrsql/schema.py:119 applica quando getChanges non è vuoto; gnrpy/gnr/sql/gnrsqlmigration/executor.py:218 esegue CREATE DATABASE con manager=True (:237), poi i comandi di struttura e le estensioni (:243-245)db.migrate() (src/asqueel/migration.py:306-324): rifiuta prima di ogni DDL se plan.skipped non è vuoto (:315-316); dopo l'esecuzione riconfronta e solleva MigrationError se restano differenze (:321-323). Rifiuta anche SQLite in memoria (:339-340), connessione senza nome di database (:347-348) e modello senza tabelle (:353-354); test tests/sqlite/test_migration_api.py:48, :69, :88, :97, :111docs/guide/migrations.md:32-35 (rifiuto dei cambi saltati e verifica dopo l'esecuzione)
A7.9Estensioni: SqlMigrator(extensions=) e unaccent automaticagnrpy/gnr/sql/gnrsqlmigration/migrator.py:141 parametro extensions; una colonna con unaccent aggiunge l'estensione (gnrpy/gnr/sql/gnrsqlmigration/orm_extractor.py:194-196; gnrpy/gnr/sql/adapters/_gnrbasepostgresadapter.py:666); il vecchio checker la rimuove quando nessuna colonna la usa (gnrpy/gnr/sql/gnrsqlutils.py:189-190)Dichiarazione esplicita db.extensions().extension(name) (src/asqueel/elements.py:114), proiettata (src/asqueel/migration.py:102-103), mai rimossa (src/asqueel/elements.py:119); test tests/object_tree/test_migration.py:122, :139. column(unaccent=True) rifiutato come attributo sconosciuto (prova)Nome esplicito in docs/design/42-configuration-grammar.md:110; nessuna motivazione registrata per la rimozione dell'estensione automatica
A7.12Cambio di tipo di colonna_handle_dtype_change (gnrpy/gnr/sql/gnrsqlmigration/command_builder.py:459): verso testo sempre, TYPE_CONVERSIONS dell'adapter, colonna vuota DROP+ADD (:515), colonna piena GnrSqlException (:524). Per SQLite struct_alter_column_sql non è implementato (gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:604-606, nessun override in gnrsqlite)Stesso motore su PostgreSQL (src/asqueel/migration.py:357). Su SQLite il cambio è saltato: migration_plan lo elenca in skipped e migrate() rifiuta prima di ogni DDL (src/asqueel/migration.py:315-316; tests/sqlite/test_migration_api.py:69-85; CLI tests/sqlite/test_migration_api.py:138)docs/guide/migrations.md:32-33
A7.16Valori abbreviati delle azioni SQL: 'C', 'R', 'N', 'SN', 'SD', 'SETNULL'statement_converter in gnrpy/gnr/sql/gnrsqlmigration/orm_extractor.py:327-336 accetta abbreviazioni e nomi completisrc/asqueel/model_source.py:126-127 accetta cascade, restrict, no_action, set_null, set_default, con maiuscole e spazi normalizzati (:167). Prova: 'C', 'SN', 'R' e 'setnull' danno ValueError "invalid SQL action"; 'SET NULL' è accettatoElenco delle azioni SQL in docs/design/42-configuration-grammar.md:534; nessuna motivazione registrata per il rifiuto delle abbreviazioni
A7.17Indice sulle colonne di relazioneOgni colonna con relazione riceve un indice, anche senza FK e senza indexed (gnrpy/gnr/sql/gnrsqlmigration/orm_extractor.py:257); indice differito sulla colonna di destinazione quando non è la pkey (:270-275, :540)relation con FK: indice sulla sorgente salvo relation(indexed=False) (src/asqueel/migration.py:183-184); relation(foreign_key=False): nessun indice (prova); weak_relation: indice su sorgente e destinazione (src/asqueel/model_source.py:201-232); relation con FK verso una colonna non pkey: nessun indice sulla destinazione (prova)docs/design/42-configuration-grammar.md:531-532 (indice di destinazione solo per weak) e docs/guide/migrations.md:193-196; nessuna motivazione registrata per la destinazione delle FK e per relation(foreign_key=False)
A7.18column(indexed=True) e indexed={opzioni}fill_json_column_index (gnrpy/gnr/sql/gnrsqlmigration/orm_extractor.py:440) accetta True o un dict con sorting, with_*, method, unique, where (:456-457); unique sulla colonna salta l'indice (:444); indexed con unique su colonna semplice diventa l'attributo unique (:453)indexed solo booleano (riga A1.5; prova: indexed={'method': 'gin'} dà "expected bool, got dict"); indice materializzato da src/asqueel/migration.py:124-129 e :242-264, saltato sulla pkey e sulle colonne unique (:252); un index() parziale sulle stesse colonne è rifiutato (src/asqueel/validators.py:247, prova; src/asqueel/migration.py:259-263); test tests/test_wf_phase4_renderer.py:221Firma booleana in docs/design/42-configuration-grammar.md:216; le opzioni passano da index() (riga A7.19); nessuna motivazione registrata
A7.19index() del modello nella migrazioneSqlMigrator non legge tblobj.indexes: get_json_struct (gnrpy/gnr/sql/gnrsqlmigration/orm_extractor.py:519-548) proietta solo colonne e colonne composte; il vecchio checker crea gli indici dichiarati (gnrpy/gnr/sql/gnrsqlutils.py:338-357)index() proiettato con unique, method, where, tablespace, with_options e ordinamento per colonna (src/asqueel/migration.py:226-240; src/asqueel/common.py:11); test tests/object_tree/test_migration.py:136. Dichiarazione: riga A1.13docs/guide/migrations.md:97-98 (indici supportati nella proiezione); nessuna motivazione registrata
A7.28model.importFromDb(): modello dal databasegnrpy/gnr/sql/gnrsqlmodel/model.py:439 usa ModelExtractor (gnrpy/gnr/sql/gnrsqlutils.py:14) e aggiorna la source del modelloinspect_postgres (src/asqueel/importers.py:26) tramite PostgresCatalogProvider (src/asqueel/catalog_provider.py:21) restituisce ImportResult(model, warnings) e non tocca db.model; SqlModelReader (src/asqueel/reader.py:97) ricava una ricetta dal JSON normalizzato (tests/test_wf_phase16_genropy_oracles.py:126)docs/guide/importing.md:3-6. Il modello importato non si interroga: #22
A7.29Struttura JSON del modello: extractOrm / ormStructure / jsonModelWithoutMetagnrpy/gnr/sql/gnrsqlmigration/migrator.py:232 extractOrm legge db.packages; :244 jsonModelWithoutMeta restituisce una Bag con i rami orm e sqlSqlMigrationRenderer(to_physical_builder(db.model)).render() (src/asqueel/migration.py:68-104) controllato da StructureValidator (:104) e assegnato a ormStructure (:358); nessuna vista Bag orm/sql; test tests/object_tree/test_migration.py:43docs/guide/migrations.md:48-64; nessuna motivazione registrata per l'assenza della vista Bag

mancante Funzioni mancanti: punti aperti

A7.7 · Package readOnly / storename esclusi dalla migrazione (excludeReadOnly)

Legacy gnrpy/gnr/sql/gnrsqlmigration/migrator.py:208-213 toglie readOnlySchemas (gnrpy/gnr/sql/gnrsql/env.py:179); gnrpy/gnr/sql/gnrsqlmigration/orm_extractor.py:536 salta il package; il vecchio checker salta anche i package con storename (gnrpy/gnr/sql/gnrsqlutils.py:176)

Asqueel Assente. Prova: package(..., readOnly=True) dà ValueError "package: does not accept ['readOnly']". Ogni schema con tabelle proiettate è gestito (src/asqueel/migration.py:352-356)

Issue nessuna issue

(a)

attributo readOnly sul package come nel legacy, escluso dalla migrazione

(b)

elenco degli schemi esclusi come parametro di migrate()

(c)

lasciarlo fuori: non dichiarare quelle tabelle nel modello che si migra

A7.8 · Schemi tenant: tabelle multi_tenant replicate in ogni schema di tenant_table

Legacy gnrpy/gnr/sql/gnrsqlmigration/migrator.py:216 legge getTenantSchemas (gnrpy/gnr/sql/gnrsql/env.py:156); add_tenant_schemas replica le tabelle multi_tenant (gnrpy/gnr/sql/gnrsqlmigration/orm_extractor.py:560); le FK verso tabelle multi_tenant puntano allo schema tenant (:382)

Asqueel Assente. options().tenant() è accettato (src/asqueel/option_elements.py:84) ma la proiezione lo ignora (prova: nessuna differenza nel JSON); il motore riceve solo application_schemas (src/asqueel/migration.py:356)

Issue nessuna issue

(a)

replica per schema tenant come nel legacy

(b)

migrazione separata per schema tenant con un modello per tenant

(c)

lasciarlo fuori: un database per tenant

A7.13 · force / backup / verifyConversionBackups (conversione di colonne piene)

Legacy gnrpy/gnr/sql/gnrsqlmigration/migrator.py:145-146; senza force la conversione di una colonna piena solleva errore (gnrpy/gnr/sql/gnrsqlmigration/command_builder.py:575); backup crea la colonna col__dtype (:585); verifica dei dati persi in gnrpy/gnr/sql/gnrsqlmigration/executor.py:247

Asqueel Assente. SqlMigrator è costruito senza force e backup (src/asqueel/migration.py:357); migrate() e la CLI non hanno opzioni equivalenti (src/asqueel/cli.py:42). Una conversione con USING su colonna piena fa fallire la preparazione del piano

Issue nessuna issue

(a)

parametri force e backup su migrate() e sulla CLI come nel legacy

(b)

conversione dei dati come passo esplicito dell'applicazione prima della migrazione

(c)

lasciarlo fuori: usare SqlMigrator di asqueel-migration direttamente

A7.21 · dtype TSV: indice gin obbligatorio

Legacy DTYPE_INDEX_CONFIG in gnrpy/gnr/sql/gnrsqlmigration/structures.py:84-85; indexed forzato (gnrpy/gnr/sql/gnrsqlmigration/orm_extractor.py:246-248) e method gin (:441-443)

Asqueel TSV ammesso (src/asqueel/elements.py:61) ma nessun indice automatico; con indexed=True l'indice non ha method (prova)

Issue nessuna issue

(a)

indice gin automatico come nel legacy

(b)

method gin di default solo quando indexed=True su TSV

(c)

lasciarlo fuori: dichiarare index(columns=..., method='gin')

A7.26 · model.enableForeignKeys(enable=False)

Legacy gnrpy/gnr/sql/gnrsqlmodel/model.py:471; il checker crea le FK solo se enableForeignKeys (gnrpy/gnr/sql/gnrsqlutils.py:389)

Asqueel Assente. Ogni relation con foreign_key produce la FK (src/asqueel/migration.py:156)

Issue nessuna issue

(a)

opzione che crea le tabelle senza FK e le aggiunge dopo, come nel legacy

(b)

caricamento dei dati con vincoli deferred

(c)

lasciarlo fuori: weak_relation dove la FK non serve

A7.27 · DDL diretto: createDb, dropDb, createSchema, dropSchema, dropTable, dropColumn

Legacy gnrpy/gnr/sql/gnrsql/schema.py:266, :275, :283, :292, :338, :346

Asqueel Assente: AsqueelDb non ha metodi DDL (src/asqueel/application.py:114-131 offre solo migration_plan e migrate)

Issue nessuna issue

(a)

metodi DDL su AsqueelDb come nel legacy

(b)

solo creazione via migrate() e rimozione via allow_removals

(c)

lasciarlo fuori: SQL eseguito dall'applicazione

nuovo Funzioni nuove di asqueel

IDFunzioneAsqueelMotivazione
A7.4Comando asqueel db plan / db apply [--allow-removals] e asqueel checksrc/asqueel/cli.py:37-43 sottocomandi; src/asqueel/cli.py:52-64 stampa avvisi e piano; src/asqueel/cli.py:86-90 check senza connessione; test tests/application_integration/test_cli_postgres.py:35-40docs/guide/cli.md:99-113
A7.23comment di tabella e di colonna nella migrazionesrc/asqueel/migration.py:114-116 (tabella); comment di colonna proiettato (prova); test tests/test_wf_phase4_renderer.py:29, :79docs/design/42-configuration-grammar.md:658-659
A7.24Vincolo CHECK dichiarato: constraints().constraint(constraint_type='CHECK')src/asqueel/elements.py:397; proiezione src/asqueel/migration.py:206-211; test tests/test_wf_phase5_integration.py:45-46nessuna motivazione registrata; docs/design/42-configuration-grammar.md:671 dice che i CHECK arbitrari richiedono un contratto successivo: vale il codice
A7.25dtype serial4 (SERIAL a 32 bit)src/asqueel/elements.py:61; test tests/test_serial4_migration.py:22, :30docs/guide/migrations.md:219-224

8 Dialetti

presente 3 funzioni identiche al legacy
IDFunzioneLegacyAsqueel
A8.3Collezioni dopo IN :param (adaptTupleListSet)gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:208 SQLite: nome0, nome1; gnrpy/gnr/sql/adapters/gnrpostgres3.py:65 PostgreSQL: nome_0, nome_1, lista vuota sostituita da TRUE o FALSEsrc/asqueel/drivers/sql.py:47-55 e :58-83 stessi algoritmi e stesse collisioni di nomi; confronto con gli originali in tests/adapter_driver/test_legacy_preparation.py:44 e :153. Nel compilatore la collezione vuota diventa = ANY('{}') o IN (SELECT 1 WHERE 0) (src/asqueel/dialects/postgres.py:92-94, src/asqueel/dialects/sqlite.py:50-52), con lo stesso risultato
A8.4REGEXP / ~* e funzione regexp su SQLitegnrpy/gnr/sql/adapters/gnrpostgres.py:166 e gnrpy/gnr/sql/adapters/gnrpostgres3.py:108 REGEXP diventa ~*; gnrpy/gnr/sql/adapters/gnrsqlite.py:115 ~* diventa REGEXP; gnrpy/gnr/sql/adapters/gnrsqlite.py:81 registra regexp, che usa re.match (gnrpy/gnr/sql/adapters/gnrsqlite.py:66)src/asqueel/drivers/sql.py:111 e :106 stesse sostituzioni; src/asqueel/drivers/sqlite.py:43 e :58-60 regexp con re.match. Prova: $name ~* :p su SQLite restituisce la riga; su PostgreSQL REGEXP diventa ~*
A8.6IS TRUE / IS FALSE su SQLite (_booleanSubCb)gnrpy/gnr/sql/adapters/gnrsqlite.py:116 e :119 riscrive in confronto con 1/0 e semantica NULLsrc/asqueel/drivers/sql.py:86-92 e :107-108 stessa riscrittura. Prova: $b IS TRUE diventa ("t0"."b" IS NOT NULL AND "t0"."b" =1)

diverso Differenze e motivazione

IDFunzioneLegacyAsqueelMotivazione
A8.1Scelta del backend: implementation ('sqlite', 'postgres', 'postgres3', 'nome:modulo'), currentImplementationgnrpy/gnr/sql/gnrsql/db.py:97 default 'sqlite'; gnrpy/gnr/sql/gnrsql/db.py:151-156 carica gnr.sql.adapters.gnr<implementation> oppure il modulo dopo ':'; gnrpy/gnr/sql/gnrsql/db.py:272 l'ambiente può cambiare adapter con currentImplementationsrc/asqueel/configuration.py:56 default 'postgresql'; src/asqueel/application.py:34-43 accetta solo 'postgresql' e 'sqlite', altrimenti UnsupportedFeatureError (prova: 'postgres', 'postgres3', 'mssql' rifiutati); driver= e dialect= iniettabili ma con lo stesso nome di implementazione (src/asqueel/application.py:44-47); le connessioni nominate usano il dialetto del db (src/asqueel/configuration.py:375-376); nessun cambio per ambienteImplementazioni supportate: docs/guide/configuration.md:53. Nessuna motivazione registrata per i nomi diversi, per il default PostgreSQL e per l'assenza del modulo custom
A8.2Parametri :nome nel testo SQL e driver PostgreSQL (prepareSqlText)gnrpy/gnr/sql/adapters/gnrpostgres.py:166 :nome diventa %(nome)s, binding di psycopg2; gnrpy/gnr/sql/adapters/gnrpostgres3.py:107 :nome diventa {nome} e gnrpy/gnr/sql/adapters/gnrpostgres3.py:337 interpola i valori nel testo con sql.SQL().format; SQLite paramstyle named (gnrpy/gnr/sql/adapters/gnrsqlite.py:60)Solo psycopg 3. src/asqueel/drivers/sql.py:111 %(nome)s come gnrpostgres, binding lato server (src/asqueel/drivers/psycopg.py:49); SQLite lascia :nome (src/asqueel/drivers/sql.py:104-108); \: protetto (src/asqueel/drivers/sql.py:98); parametro mancante a None (src/asqueel/drivers/sql.py:102-103)docs/design/83-legacy-realignment-r3.md:50-53: algoritmo postgres3 per le collezioni, senza l'interpolazione, perché SQL e mapping restano separati. Effetto registrato in docs/design/84-legacy-realignment-r4.md:54-61 (':id' dentro una stringa dà '$1'). '%' letterale: docs/guide/queries.md:305 e #19
A8.5ILIKE su SQLitegnrpy/gnr/sql/adapters/gnrsqlite.py:115 replace('ILIKE','LIKE') e replace('ilike','like') su tutto il testo: cambia anche nomi e stringhe che contengono 'ilike'; 'IlIkE' resta invariatosrc/asqueel/drivers/sql.py:15-25 sostituisce solo l'operatore, in qualsiasi grafia, fuori da stringhe, identificatori quotati e commenti; tests/sqlite/test_ilike.py:30 e :37docs/guide/queries.md:242-245 descrive la sostituzione limitata all'operatore; nessuna motivazione registrata. Vale il codice su docs/guide/sqlite.md:65, che dice ILIKE non tradotto
A8.7Schemi SQLite come file allegati (ATTACH) e schema maingnrpy/gnr/sql/adapters/gnrsqlite.py:84-91 allega <cartella>/<sqlschema>.db per ogni package con sqlschema; gnrpy/gnr/sql/adapters/gnrsqlite.py:98 _attach crea il file; gnrpy/gnr/sql/adapters/gnrsqlite.py:76-79 crea la cartella se manca; schema main (gnrpy/gnr/sql/adapters/gnrsqlite.py:63)src/asqueel/drivers/sqlite.py:45-52 allega <nome>_<schema>.db per gli schemi delle tabelle (src/asqueel/application.py:94-95); nomi semplici, temp vietato (src/asqueel/drivers/sqlite.py:48-49); schema di default main (src/asqueel/object_tree.py:147); la cartella deve esistere (docs/guide/sqlite.md:40)docs/guide/sqlite.md:25-29 (stesso layout dell'adapter di migrazione); nessuna motivazione registrata per il cambio di nome dei file rispetto al legacy
A8.8Apertura della connessione SQLite: PRAGMA foreign_keys, timeout, opzionignrpy/gnr/sql/adapters/gnrsqlite.py:80 timeout=20.0 e detect_types; nessun PRAGMA foreign_keys: le FK non valgonosrc/asqueel/drivers/sqlite.py:44 PRAGMA foreign_keys = ON; src/asqueel/drivers/sqlite.py:37-39 solo timeout e cached_statements, default di sqlite3; src/asqueel/drivers/sqlite.py:30-31 richiede SQLite 3.35FK attive: decisione utente 2026-10-16, perché il legacy dovrebbe attivarle (genropy#1695). Timeout: docs/guide/sqlite.md:21-23; nessuna motivazione registrata per il default diverso
A8.9Tipi dei valori su SQLite (converter e adapter registrati)gnrpy/gnr/sql/adapters/gnrsqlite.py:80 PARSE_DECLTYPES; gnrpy/gnr/sql/adapters/gnrsqlite.py:543-544 Decimal scritto come float, numeric letto come Decimal; :552 date letta come date; :579 bool; :596 timestampsrc/asqueel/drivers/sqlite.py:91-97 date e datetime scritti come stringa ISO, Decimal come stringa; nessun converter in lettura. Prova: D letta come '2020-01-02' (str), N come float, B come intdocs/guide/sqlite.md:67-70 descrive i tipi nativi restituiti; nessuna motivazione registrata per l'abbandono dei converter legacy
A8.10Transazione e for_update su SQLitegnrpy/gnr/sql/adapters/gnrsqlite.py:140-141 _selectForUpdate restituisce '': nessun lock; transazione implicita differita del modulo sqlite3 (gnrpy/gnr/sql/adapters/gnrsqlite.py:80)src/asqueel/drivers/sqlite.py:64-65 BEGIN IMMEDIATE all'inizio di ogni unità di lavoro, letture comprese; src/asqueel/drivers/sqlite.py:66-70 con for_update in transazione aperta esegue un UPDATE senza effetto sulla tabella; src/asqueel/dialects/sqlite.py:66-71 nessun FOR UPDATE nel testodocs/guide/sqlite.md:45-56 (protegge il ciclo degli hook, lock sull'intero database). La decisione utente 2026-10-16 prevede transazione differita come nel legacy e lock di scrittura solo con for_update: il codice apre BEGIN IMMEDIATE anche per le letture
A8.14Inserimento multiplo (insertMany, executemany)gnrpy/gnr/sql/gnrsqltable/crud.py:86 insertMany; gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:821 assegna la pkey mancante con newPkeyValue (:827-829) ed esegue cursor.executemany fuori da db.executesrc/asqueel/application_table.py:309-315 sql_insert con una lista: un executemany per gruppo di record con le stesse colonne (src/asqueel/write_compiler.py:79-93), senza RETURNING e senza generare la pkey (prova); driver: src/asqueel/drivers/psycopg.py:53-58, src/asqueel/drivers/sqlite.py:79-89; insertMany assente (prova)decisione utente 2026-10-15: insertMany sostituito da sql_insert con una lista; docs/guide/writes.md:132-134. Nessuna motivazione registrata per la pkey non generata
A8.20Capability dell'adapter (CAPABILITIES, has_capability)gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:87 e :343; gnrpy/gnr/sql/adapters/_gnrbasepostgresadapter.py:114 MIGRATIONS, VECTOR, SCHEMAS; gnrpy/gnr/sql/adapters/gnrsqlite.py:56 SCHEMAS; ADMINISTER se ci sono gli eseguibili (gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:164-165); letta da gnrpy/gnr/sql/gnrsqlmodel/table.py:176src/asqueel/dialects/postgres.py:18-22 capability di SQL (select, insert, update, delete, returning_*, left_join, limit, offset, default_values, for_update), riducibili nel costruttore (src/asqueel/dialects/postgres.py:24-29) e verificate da _require (src/asqueel/dialects/postgres.py:102-104); nessun has_capabilitydocs/guide/limitations.md:180-182 (dichiarare una capability non la implementa). Il compilatore di scrittura non le verifica tutte: #21
A8.21compileSql: LIMIT e OFFSET per dialettognrpy/gnr/sql/adapters/_gnrbaseadapter.py:715-716 LIMIT e OFFSET aggiunti solo se valorizzati: limit=0 viene omesso; su SQLite un OFFSET senza LIMIT dà errore di sintassi (prova con sqlite3)src/asqueel/dialects/postgres.py:113-117 interi non negativi; src/asqueel/dialects/postgres.py:130-133 LIMIT 0 emesso; src/asqueel/dialects/sqlite.py:68-69 OFFSET senza LIMIT aggiunge LIMIT 9223372036854775807 (prova)limit=0: docs/guide/queries.md:125 e docs/design/09b-legacy-query-record-lifecycle.md:492 (non importare le anomalie di truthiness del legacy). OFFSET su SQLite: nessuna motivazione registrata
A8.22Tipi SQL dei dtype (revTypesDict, columnSqlType) e lettura inversa (typesDict)gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:1133 columnSqlType (size senza ':' dà C, 'n:m' dà A); gnrpy/gnr/sql/adapters/_gnrbasepostgresadapter.py:146 tipi PostgreSQL; gnrpy/gnr/sql/adapters/gnrsqlite.py:50 tipi SQLite (X blob, DH timestamp, H time, O assente); gnrpy/gnr/sql/adapters/_gnrbasepostgresadapter.py:123 typesDictIl DDL lo produce il pacchetto asqueel-migration (docs/guide/sqlite.md:38), con la regola di columnSqlType e i tipi PostgreSQL del legacy. src/asqueel/projection.py:14-30 nei modelli a package trasforma X, Z, P e A senza size in T. Prova SQLite: A senza size text, X text, DH timestamp without time zone, O blob. Lettura inversa PostgreSQL uguale a typesDict (src/asqueel/importers.py:210-225); codici dtype in src/asqueel/elements.py:59-62Codici dtype: docs/guide/models.md:63. Nessuna motivazione registrata per A senza size come text e per i tipi SQLite diversi dal legacy

mancante Funzioni mancanti: punti aperti

A8.11 · for_update con modo ('NOWAIT', 'SKIP LOCKED')

Legacy gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:721-723 FOR UPDATE OF t0 seguito dalla stringa del modo; usato con 'SKIP LOCKED' in gnrpy/gnr/web/gnrtask.py:99

Asqueel src/asqueel/compiler.py:557-559 for_update deve essere bool: ValueError 'for_update must be a boolean' (prova con 'SKIP LOCKED'); src/asqueel/dialects/postgres.py:134-135 emette solo FOR UPDATE OF t0

Issue nessuna issue; docs/guide/limitations.md:248 (nessuna opzione NOWAIT/SKIP LOCKED)

(a)

accettare la stringa del modo come nel legacy

(b)

parametri separati per nowait e skip locked, validati dal dialetto

(c)

lasciarlo fuori: SQL esplicito con db.execute

A8.12 · lock() di tabella (lockTable)

Legacy gnrpy/gnr/sql/gnrsqltable/crud.py:481 lock(mode, nowait); gnrpy/gnr/sql/adapters/_gnrbasepostgresadapter.py:319 LOCK ... IN <mode> MODE, con 'NO WAIT' non valido (:321); SQLite nessuna istruzione (gnrpy/gnr/sql/adapters/gnrsqlite.py:390)

Asqueel Nessun metodo pubblico: hasattr(table, 'lock') è False (prova). Esiste lock_table nel compilatore di scrittura (src/asqueel/write_compiler.py:176-181) con NOWAIT corretto e nessuna istruzione su SQLite, chiamato solo dai test (tests/write_compiler/test_write_compiler.py:139)

Issue nessuna issue (correzione di NO WAIT: genropy#1692)

(a)

table.lock(mode, nowait) come nel legacy, sopra lock_table

(b)

un metodo del db con i modi di lock validati

(c)

lasciarlo fuori: db.execute('LOCK ...')

A8.15 · db.adapter.string_agg(campo, separatore) nelle formule del progetto

Legacy gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:950 string_agg(); gnrpy/gnr/sql/adapters/gnrsqlite.py:394 group_concat(); chiamato nei modelli come self.db.adapter.string_agg(...) (projects/gnrcore/packages/adm/model/user.py:51)

Asqueel Nessun oggetto db.adapter (prova). string_agg e group_concat esistono solo dentro aggr(join) (src/asqueel/dialects/postgres.py:47-51, src/asqueel/dialects/sqlite.py:17-21; riga A4.19). string_join corrisponde a concat_non_null (riga A3.2)

Issue nessuna issue

(a)

esporre un oggetto adapter con string_agg come nel legacy

(b)

un metodo pubblico del dialetto con la stessa firma

(c)

lasciarlo fuori: @rel.aggr(join(...)) o SQL del dialetto scritto nella formula

A8.16 · unaccentFormula, mask_field_sql, ageAtDate (espressioni SQL del dialetto)

Legacy gnrpy/gnr/sql/adapters/_gnrbasepostgresadapter.py:1291 unaccent($campo); :1333 unaccent nel whereTranslator; gnrpy/gnr/sql/adapters/_gnrbasepostgresadapter.py:248 e gnrpy/gnr/sql/adapters/gnrsqlite.py:401 mask_field_sql; gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:681 ageAtDate; usate da gnrpy/gnr/sql/gnrsqltable/columns.py:297, :344, :381

Asqueel Nessuna funzione corrispondente. Si può solo dichiarare l'estensione unaccent (src/asqueel/elements.py:117-122). Colonne variante: riga A3.15; whereTranslator: riga A2.26

Issue nessuna issue

(a)

portarle nel dialetto con le stesse espressioni del legacy

(b)

documentarle come espressioni da scrivere in sql_formula, per dialetto

(c)

lasciarle fuori: sql_formula con unaccent() o con l'espressione esplicita

A8.17 · db.listen / db.notify (LISTEN e NOTIFY)

Legacy gnrpy/gnr/sql/gnrsql/transactions.py:289 e :298; gnrpy/gnr/sql/adapters/gnrpostgres3.py:155 e :180; gnrpy/gnr/sql/adapters/_gnrbasepostgresadapter.py:1295 listen_connection, :1311 poll_notifications; SQLite: notify vuoto e listen che attende soltanto (gnrpy/gnr/sql/adapters/gnrsqlite.py:299, :315)

Asqueel Assenti: hasattr(db, 'listen') e hasattr(db, 'notify') sono False (prova). src/asqueel/db_write.py:5 esclude NOTIFY dal ciclo di scrittura

Issue nessuna issue. decisione utente 2026-10-15: notify fuori dal primo livello, asqueel solo database

(a)

db.listen e db.notify come nel legacy

(b)

solo notify nella transazione corrente, listen nel livello applicativo

(c)

lasciarli fuori: db.execute('NOTIFY canale, :payload') e una connessione psycopg dedicata

A8.18 · db.analyze() / db.vacuum(table, full)

Legacy gnrpy/gnr/sql/gnrsql/transactions.py:307 e :311; gnrpy/gnr/sql/adapters/_gnrbaseadapter.py:929 ANALYZE, :933 VACUUM su una connessione autocommit dedicata

Asqueel Metodi assenti (prova). db.execute('ANALYZE') funziona. VACUUM fallisce: su PostgreSQL 'VACUUM cannot run inside a transaction block', su SQLite 'cannot VACUUM from within a transaction' (prove); il driver rifiuta autocommit (src/asqueel/drivers/psycopg.py:40-41)

Issue nessuna issue

(a)

analyze e vacuum sul db con connessione autocommit dedicata, come nel legacy

(b)

solo come comandi della CLI asqueel

(c)

lasciarli fuori: vacuumdb o psql

A8.19 · createDb / dropDb / dbExists / dump / restore / importRemoteDb

Legacy gnrpy/gnr/sql/gnrsql/schema.py:266, :275, :302, :315; gnrpy/gnr/sql/adapters/_gnrbasepostgresadapter.py:417, :435, :486, :335 (pg_dump), :439; gnrpy/gnr/sql/adapters/gnrpostgres3.py:146 (pg_restore o psql); SQLite crea e cancella il file (gnrpy/gnr/sql/adapters/gnrsqlite.py:319, :329); il modello chiama createDb in gnrpy/gnr/sql/gnrsqlmodel/model.py:496

Asqueel Metodi assenti (prova). db.migrate() crea e altera le tabelle, non il database (docs/guide/limitations.md:21); su SQLite la connessione crea il file ma non le tabelle (docs/guide/sqlite.md:40-41)

Issue nessuna issue

(a)

portarli sul db come nel legacy

(b)

solo come comandi della CLI asqueel accanto a db plan e db apply

(c)

lasciarli fuori: createdb, dropdb, pg_dump, pg_restore

A8.23 · Altri adapter legacy: mssql, mysql, db2_400, fourd, postgres8000

Legacy gnrpy/gnr/sql/adapters/gnrmssql.py:102, gnrpy/gnr/sql/adapters/gnrmysql.py:48, gnrpy/gnr/sql/adapters/gnrdb2_400.py:113, gnrpy/gnr/sql/adapters/gnrfourd.py:84, gnrpy/gnr/sql/adapters/gnrpostgres8000.py:91

Asqueel Implementazione rifiutata con UnsupportedFeatureError (src/asqueel/application.py:42-43; prova con 'mssql'). Il pacchetto di migrazione ha adapter MySQL e SQL Server solo per il DDL (docs/guide/cli.md:137-140)

Issue nessuna issue; docs/guide/cli.md:140 (esecuzione MySQL e SQL Server lavoro separato)

(a)

portare gli adapter legacy come dialetto più driver

(b)

solo MySQL e SQL Server, già coperti dalla migrazione

(c)

lasciarli fuori: PostgreSQL o SQLite

nuovo Funzioni nuove di asqueel

IDFunzioneAsqueelMotivazione
A8.13Versione minima di SQLitesrc/asqueel/drivers/sqlite.py:30-31 connect() solleva RuntimeError sotto SQLite 3.35, la versione che introduce RETURNING; RETURNING delle scritture: riga A5.16decisione utente 2026-10-15 (rileggere i valori decisi dal database, provvisoria; riga A5.16); docs/guide/sqlite.md:5