Accesso a DB tramite prepared statement

Generalità

I prepared statement sono gestiti attraverso l'AgentData della libreria iaf/ia/iadata

I prepared statement riconosciuti dall'AgentData sono configurati in appositi file .json

Ogni file json può contenere più di un prepared statement

L'AgentData viene configurato andando ad indicare due parametri:

tpevodb: agente di database

path: path ove risiedono i file di configurazione

Es:

<agent lib="/usr/lib/libiadata.so.0.0.0" create="agentData" live="1" name="prepstat"> <param name="agdb" value="tpevodb"/> <param name="path" value="/code/cpp/uml/proled/config/iadata"/> </agent>

Key

Ogni prepared statement è identificata attraverso la seguente codifica:

{Main Key}.{Sub Key}

{Main Key}: identifica il nome del file senza estensione

{Sub Key}: identifica lo specifico prepared statement all'interno del file

L'univocità a livello di Main Key è assicurata dal fatto che non possono esistere nella medesima cartella due file con lo stesso nome

L'univocità a livello di chiave completa {Main Key}.{Sub Key} deve essere assicurata dal configuratore, che non deve permettere che per lo stesso Main Key esistano due Sub Key uguali

Es:

nel file topology.json è presente un prepared statement codificato con nodi

la chiave di tale prepared statement è: topology.nodi

JSON

Il file .json permette di configurare più di un prepared statement

Si possono aggregare, nello stesso file, prepared statement che hanno fini comuni ad esempio

insieme di tabelle di database associate a certe entità logicamente correlate

insieme di operazioni comuni (select, update, insert, delete)

etc...

Il file contiene un array di oggetti, ognuno dei quali ha i seguenti attributi:

subkey: chiave associata allo specifico prepared statement all'interno del file .json

sql: query sql che definisce il prepared statement

Es: file operalang.json, abbiamo 3 prepared statement identificati come segue:

operalang.insert

operalang.update

operalang.select

[ { "subkey":"insert", "sql":"insert into operalang (codice,descrizio) values ($[codice],$[descrizio]) returning *" }, { "subkey":"update", "sql":"update operalang set descrizio=$[descrizio] where codice=$[codice] returning *" }, { "subkey":"select", "sql":"select * from operalang where codice=$[codice]" } ]

Parametri e Query

I parametri di un prepared statement sono definiti in forma espressiva (attraverso nomi descrittivi) e non in forma numerata

Questo modello permette di poter gestire i parametri in modo più intuitivo e semplificato, dal punto di vista della programmazione della chiamata

L'AgentData si occupa di convertire la forma espressiva di ogni parametro nella corrispondente forma numerata (richiesta dal sistema di database)

La sintassi utilizzata per definire un parametro in un prepared statement è la seguente:

$[.] => il nome del parametro è inserito tra parentesi quadre, precedute dal simbolo $

Es: $[codice]

il nome del parametro, tra parentesi quadre, non deve essere necessariamente il nome di un campo di database, ma può essere un qualsiasi nome riconosciuto dall'utilizzatore.

Risulta però conveniente dal punto di vista progettuale e di uniformità nelle conoscenze dei sistemi, utilizzare i nomi dei campi di database

Di seguito un esempio di query in forma espressiva (da configurare nel sistema) e la corrispondente in forma numerata:

--Forma espressiva SELECT * FROM nodi WHERE id=$[idnodo] AND codprg=$[codice progetto]; --Forma numerata SELECT * FROM nodi WHERE id=$1 AND codprg=$2;

Se un parametro è ripetuto più volte con lo stesso nome, verrà connesso con lo stesso numero

Le query di insert, update, delete devono essere sempre seguite dall'espressione returning *, ad esempio:

INSERT INTO operalang (codice,descrizio) VALUES ($[codice], $[descrizio]) returning *

AgentData e funzionalità

L'AgentData si occupa di gestire i prepared statement sql del sistema; fonisce verso gli utilizzatori una interfaccia semplificata per l'accesso ai prepared statement

L'agente fornisce la possibilità di eseguire più prepared statement appartenenti al medesimo file, in una unica chiamata

Le query preconfezionate sono definite in appositi file json. Ogni file json e' un array di oggetti del tipo:

{"subkey":"xxx", "sql":"select ..."} ove subkey = una sotto-chiave della specifica prepared statement, sql = sql del prepared statement.

Esecuzione dei prepared statement

Al fine di eseguire un prepared statement si effettua una ask come segue:

request = KEY (chiave del prepared statement che si vuole eseguire, oppure Main Key)

p1 = rec(lista parametri) (es: rec(codice,descrizio)) ove lista parametri contiene la lista dei nomi dei parametri richiesti nel prepared statement ed i rispettivi valori; il rec può essere costituito da più righe ad esempio nel caso di inserimento o update di più righe per la stessa tabella; in tal caso è necessario attivare esplicitamente la transazione

nume = 0 valore di default => operazione semplice (per nume > 0 vedi paragrafo Funzioni integrative)

retval = risultato della query (nel caso di query insert, update, delete si inserisce in fondo alla query "returning *", in modo che il sistema ritorni un json contenente il dato aggiornato o il dato cancellato)

Es

file operacom.json

[ { "subkey":"select", "sql":"select * from operacom where codice=$[codice]" }, { "subkey":"selectkey", "sql":"select * from operacom where key=$[key]" } ]

Chiamata

agente: AgentData

tipo di chiamata: ask

request: operacom.selectkey

p1: {"key":"errormsg"}

Risposta

[ { "codice":3, "key":"errormsg", "tipo":"M" } ]

Se KEY (request) viene posta ad un valore di {Main Key} (solo parte relativa al nome del file), il sistema può restituire il risultato dell'esecuzione di più di un prepared statement associato a più di una {Sub Key} presente nel file

L'utilizzo diretto della Main Key nella request, permette di semplificare l'assegnazione dei permessi

infatti i permessi utente potrebbero essere associati all'intero file piuttosto che alle singole prepared statement

Esecuzione multi-query implicita

request = {Main Key}

p1 = (_subkey, lista parametri) (singolo oggetto json <=> rec con una sola riga) ove

_subkey = la lista delle Sub Key a cui siamo interessati, divise da virgola senza spazi (è possibile, naturalmente, inserire una unica Sub Key)

se _subkey non viene specificato (vuoto, oppure assente) => l'agente restituisce tutte le subkey della Main Key

lista parametri: lista completa di tutti i parametri richiesti da ogni Sub Key

se due Sub Key hanno un parametro in comune (stesso nome espressivo), questo va specificato una sola volta nella lista ed il corrispondente valore verrà utilizzato da entrambe

retval: rec(lista key) ove

lista key:

un attributo per ogni KEY richiesta, con il nome {Main Key}.{Sub Key}

il valore di ogni attributo è il JSON corrispondente all'esecuzione del prepared statement

Questo modello è particolarmente indicato per estrarre (select), con una unica chiamata, i dati da più tabelle in relazione tra loro

Es:

file topology.json

[ { "subkey":"nodiprogetto", "sql":"select codice,id,tipoarm,h,hftmax,css from nodi where codprg=$[codprg] and id=$[idnodo] order by id" }, { "subkey":"ramiprogetto", "sql":"select codice,id,nodosx,nododx,lc,leq from rami where codprg=$[codprg] and id=$[idramo] order by id" }, { "subkey":"prgmezzi", "sql":"select * from prgmezzi where codprg=$[codprg] order by codice" } ]

Chiamata

agente: AgentData

tipo chiamata: ask

request: topology

p1: {"_subkey":"ramiprogetto,nodiprogetto","codprg":1748,"idnodo":1,"idramo":1}

Risposta

[ { "topology.ramiprogetto":{ "codice": 53730, "id": 1, "nodosx": 49641, "nododx": 49642, "lc": 350.0, "leq": 350.0 }, "topology.nodiprogetto":{ "codice": 49641, "id": 1, "tipoarm": "A", "h": 27.0, "hftmax": 42.0, "css": 2826 } } ]

Esecuzione multi-query esplicita

request = {Main Key}

p1 = (_subkey, _params) (una riga per ogni Sub Key) ove

_subkey = codice di una specifica Sub Key nel file della Main Key

_params: oggetto JSon contenente i parametri richiesti dalla specifica Sub Key

ogni Sub Key è analizzata indipendentemente dalle altre

retval: rec(lista key) ove

lista key:

un attributo per ogni KEY richiesta, con il nome {Main Key}.{Sub Key}

il valore di ogni attributo è il JSON corrispondente all'esecuzione del prepared statement

Esempio

Consideriamo il seguente file operacomins.json

[ { "subkey": "operacom", "sql": "insert into operacom (codice,key,tipo) values ($[codice],$[key],$[tipo]) returning *" }, { "subkey": "operamsg", "sql": "insert into operamsg (codcom,codlang,msg) values ($[codcom],$[codlang],$[msg]) returning *" }, { "subkey": "operalang", "sql": "insert into operalang (codice,descrizio) values ($[codice],$[descrizio]) returning *" } ]

Vogliamo inserire un nuovo operacom ed i relativi messaggi in operamsg per i vari linguaggi operalang, per cui utilizzeremo due subkey:

operacom: una riga per il nuovo tipo di messaggio

operamsg: una riga per ogni traduzione prevista nella specifica lingua

Possiamo eseguire

ask

request = operacomins

nume = 2

transazione: assicuriamo l'integrità dei dati nell'inserimento nelle tabelle operacom e operamsg

pre-processing: vogliamo ottenere il numeratore per il campo codice di operacom e poi associare il codice rilevato alle righe di operamsg come valore di chiave esterna

{"_subkey":"operacom", "_params":[{ ... "codice" : "_getnume" ... },...]}

{"_subkey":"operamsg", "_params":[{ ... "codcom" : "$[operacom.codice]" ... },...]} (operacom di operacom.codice fa riferimento al nome della query e non al nome della tabella)

p1 è il seguente json

[ { "_subkey":"operacom", "_params":{ "codice":"_getnume", "tipo":"M", "key":"errmsg" } }, { "_subkey":"operamsg", "_params":[ { "codcom":"$[operacom.codice]", "codlang":"it", "msg":"E' tutto sbagliato" }, { "codcom":"$[operacom.codice]", "codlang":"en", "msg":"All is wrong" }, { "codcom":"$[operacom.codice]", "codlang":"es", "msg":"Todo es erato" } ] } ]

Applicazione Funzioni Avanzate

Indipendentemente dal tipo di query (monoquery, multiquery implicete o esplicite) è possibile richiedere al sistema una combinazione delle seguenti funzionalità:

Transazione

Preprocessing

Merge dei risultati

Per attivare una o tutte le funzioni indicate bisogna utilizzare il nume secondo la tabella seguente:

Nume	Bit	Descrizione	Transazione	Preprocessing	Merge dei risultati
0	000	Nessuna Funzione
1	001	Transazione	X
2	010	Preprocessing	X
3	011	Transazione + Preprocessing	X	X
4	100	Merge	X
5	101	Transazione + Merge	X	X
6	110	Preprocessing + Merge	X	X
7	111	Transazione + Preprocessing + Merge	X	X	X

Come si osserva dalla tabella le transazioni sono attivate con nume=1, il preprocessing con nume = 2, il merge con nume = 4; gli altri valori del nume combinano le tre funzioni in modo opportuno (vedi tabella delle funzioni avanzate sopra).

Transazioni

Questo modello è particolarmente indicato nel caso di applicazione di modifiche ai dati attraverso operazioni di insert, update, delete attuate su più tabelle in relazione tra loro

I dati vengono preparati inserendo, per ogni Sub Key, i valori rilevati, nei rispettivi parametri attesi dal prepared statement

Si costruisce un JSon per ogni Sub Key, contenente i parametri

Per assicurare l'integrità dei dati

è possibile eseguire le operazioni all'interno di una transazione

a tale scopo => nume = 1 esegue i prepared statement all'interno di una transazione

se anche una sola query non va a buon fine, il sistema esegue una rollback della transazione altrimenti esegue commit

Pre-Processing dei parametri

Impostando nume = 2 si richiede all'agente di eseguire la transazione con preprocessing dei parametri

L'agente interpreta il contenuto di ogni parametro e ricerca i seguenti valori:

_getnume => se un parametro ha il valore _getnume, l'utilizzatore intende sostituire il valore del parametro con un numertore richiesto al tpe_db

il numeratore richiesto sarà: getnume({MainKey}.{SubKey}, nomeparametro)

l'agente chiederà tanti numeratori quante sono le righe della tabella dei parametri e assegnerà ad ogni riga il rispettivo valore

_getnume(tabnume,fldnume) => si desidera estrarre il numeratore esattamente dalla tabnume, fldnume indicati tra parentesi (es: _getnume(rilievo,codice))

_uuidv4 => se un parametro ha il valore _uuid (v4), l'utilizzatore intende sostituire il valore del parametro con una chiave uuid v4 generata dal sistema

la uuid viene generata con la classe iaf stringutil::generate_uuid_v4()

viene generata una uuid per l'attributo relativo, per ogni riga del rec dei parametri

la uuid generata ha una dimensione fissa di 37 caratteri

_uuidv7 => se un parametro ha il valore _uuidv7, l'utilizzatore intende sostituire il valore del parametro con una chiave uuid v7 generata dal sistema (la chiave v7 rispetto a v4 permette un ordinamento cronologico in base al codice)

la uuid viene generata con la classe iaf stringutil::generate_uuid_v7()

viene generata una uuid per l'attributo relativo, per ogni riga del rec dei parametri

la uuid generata ha una dimensione fissa di 37 caratteri

_uuidshort => se un parametro ha il valore _uuidshort, si intende sostituire il parametro con una chiave uuid corta

_uuidveryshort => se un parametro ha il valore _uuidveryshort, si intende sostituire il parametro con una chiave molto corta della lunghezza di 10 caratteri

$[X.Y] => sostituisce il valore del parametro relativo, con il valore del parametro riferito dalla coppia

X=Sub Key della Main Key in elaborazione

Y=nome parametro della Sub Key

es: nodi.codice: X=nodi, Y=codice => sostituisce il valore del paramtro a cui è stato assegnato nodi.codice, con il valore del parametro codice specificato per la Sub Key nodi

Utile nei casi in cui si assegni una chiave esterna a RUN-TIME tramite la _getnume

_datanow, _oranow, _timenow, l'agente imposta rispettivamente

la data corrente (YYYYMMDD)

l'ora corrente (HH:MI:SE)

il time epoch corrente (secondi da 1 Gennaio 1970)

Merge dei risultati

Funzionalità applicata dall'agente solo in caso di multiquery e non in caso di monoquery

Esegue il merge di tutti i dataset di una multiquery in un unico dataset dove vengono accodati nella sequenza di esecuzione i dataset ottenuti

Il retval prende in questo caso una struttura tabella che rappresenta il merge di tutti i risultati

Particolarmente utile per fare il merge di dati provenienti da più database in una unica struttura tabellare

Altre funzioni di AgentData

Lista parametri

Tramite ask è possibile richiedere la lista dei parametri di una {Main Key}.{Sub Key}:

request = keyparams

p1 = rec(key,_subkey), possiamo avere i seguenti casi

key = chiave completa {Main Key}.{Sub Key} => il campo _subkey è trascurato

key = Main Key e _subkey = vuoto => restituisce i parametri di tutti i _subkey della Main Key

key = Main Key e _subkey = lista di subkey divisi da virgola => restituisce i parametri dei subkey specificati

nume = numero di righe nei parametri

retv = rec(parametri), per i tre casi

un json con una colonna per ogni parametro il cui nome e' proprio il nome del parametro, il valore e' vuoto

un json con una colonna per ogni subkey della Main Key identificata con {Main Key}.{Sub Key} contenente il json dei parametri della specifica key

un json con una colonna per ogni subkey della Main Key indicata in _subkey, identificata con {Main Key}.{Sub Key} contenente il json dei parametri della specifica key

request = keyparamsmqi (keyparams per multiquery implicite)

p1 = rec(key,_subkey) come keyparams

retv = rec(parametri) restituisce in una unica riga tutti i parametri del sottoinsieme di key richieste senza replicazioni; questo parametro può essere utilizzato per le multi-query implicite

Reload del repository

Tramite tell è possibile fare ricaricare dal repository di file json tutte le configurazioni, nel caso in cui vengano modificati:

request = reload

p1 = rec(ciao=ciao)

retv = true se l'operazione ha successo, false altrimenti

Costruzione automatica file JSon Insert / Update

Attraverso una chiamata tell all'agente sarà possibile creare automaticamente i prepared statement di:

insert, update, delete

di apposite liste di tabelle. Di seguito vediamo i 3 casi.

Insert

E' possibile costruire file json contenenti operazioni di insert per gruppi tabelle correlate, attraverso l'utilizzo delle seguenti chiamate tell (i file sono creati nel path definito in configurazione dell'agente):

request = tabinsert

p1 = rec(key,tabs) ove

key = Main Key (nome del file senza estensione da creare)

tabs = lista delle tabelle interessate separate da virgola senza spazi

nume = 1 => i nuovi dati vengono inseriti in append nel file (se esiste), altrimenti il file viene sovrascritto

retv = true se il file e' stato creato nel path di destinazione definito

Update

E' possibile costruire file json contenenti operazioni di update per gruppi tabelle correlate, attraverso l'utilizzo delle seguenti chiamate tell (i file sono creati nel path definito in configurazione dell'agente):

request = tabupdate

p1 = rec(key,tabs) ove

key = Main Key (nome del file senza estensione da creare)

tabs = lista delle tabelle interessate separate da virgola senza spazi

nume = 1 => i nuovi dati vengono inseriti in append nel file (se esiste), altrimenti il file viene sovrascritto

retv = true se il file e' stato creato nel path di destinazione definito

La condizione di update (where codice=...) viene applicata come segue:

se esiste un campo codice, viene utilizzato nella codizione di update come segue

nome espressivo: _codice (codice preceduto da underscore)

condizione aggiunta: where codice = $[_codice]

se non esiste un campo codice, l'agente ricerca tutti i campi che iniziano con cod e applica tutte le condizioni

nome espressivo: _codXXX (codice di chiave esterna preceduto da underscore)

condizione aggiunta: where codXXX = $[_codXXX] and codYYY = $[_codYYY] and ...

Delete

E' possibile costruire file json contenenti operazioni di delete per gruppi tabelle correlate, attraverso l'utilizzo delle seguenti chiamate tell (i file sono creati nel path definito in configurazione dell'agente):

request = tabdelete

p1 = rec(key,tabs) ove

key = Main Key (nome del file senza estensione da creare)

tabs = lista delle tabelle interessate separate da virgola senza spazi

nume = 1 => i nuovi dati vengono inseriti in append nel file (se esiste), altrimenti il file viene sovrascritto

retv = true se il file e' stato creato nel path di destinazione definito

La condizione di delete (where codice=...) viene applicata come segue:

se esiste un campo codice, viene utilizzato nella codizione di update come segue

nome espressivo: _codice (codice preceduto da underscore)

condizione aggiunta: where codice = $[_codice]

se non esiste un campo codice, l'agente ricerca tutti i campi che iniziano con cod e applica tutte le condizioni

nome espressivo: _codXXX (codice di chiave esterna preceduto da underscore)

condizione aggiunta: where codXXX = $[_codXXX] and codYYY = $[_codYYY] and ...

Tutte le operazioni

E' possibile creare un file json con tutte le operazioni (insert, update, delete) richiamando la seguente

request = taballop

p1 = rec(key,tabs) ove

key = Main Key (nome del file senza estensione da creare)

tabs = lista delle tabelle interessate separate da virgola senza spazi

nume = 1 => i nuovi dati vengono inseriti in append nel file (se esiste), altrimenti il file viene sovrascritto

retv = true se il file e' stato creato nel path di destinazione definito

Esempio Insert (stesso per update)

Chiamata

agente: AgentData

tipo chiamata: tell

request: tabinsert

p1: {"key":"nodimng","tabs":"nodi,nodisup,nodiatt"}

Utilizzo di più database

I file Json degli esempi precedenti non forniscono alcuna indicazione sull'agente di database da utilizzare

In questi casi l'agentdb utilizzato è quello specificato nell'AgentData

Tutti i prepared statement verranno quindi eseguiti attraverso tale agentdb

E' possibile associare ciascuna prepared statement ad un differente agentdb

A tal fine si può aggiungere, opzionalmente, l'attributo "db" nel relativo oggetto prepared statement

L'AgentData utilizzerà l'agentdb di default (quello assegnato all'agente) per i prepared statement che non forniscono indicazione esplicita dell'attributo db

mentre per i prepared statement che forniscono l'attributo db l'AgentData eseguirà il prepared statement utilizzando l'agentdb indicato dall'attributo stesso

Questa possibilità è utile per:

recuperare dati da differenti database

inserire dati in differenti database

replicare dati da un database ad un'altro

Esempio Json per più database

Ipotizziamo di:

dover inserire un nuovo utente in un sistema informativo

che il sistema informativo sia composto dai seguenti database:

userdb: database di gestione della protezione (nome dell'agentdb: userdb)

oeedb: database di gestione dell'oee (nome dell'agentdb: tpevooee)

mldb: database di gestione del machine ledger (nome dell'agentdb: tpevoml)

l'inserimento dell'utente deve avvenire su tutti i database indicati

Vediamo di seguito un esempio di file JSON opins.json:

[ { "db":"userdb", "subkey":"operato", "sql":"insert into operato (codice,pwd,nominati,email) values ($[codice],$[pwd],$[nominati],$[email]) returning *" }, { "db":"tpevooee", "subkey":"operato_oee", "sql":"insert into operato (codice,nominati,email) values ($[codice],$[nominati],$[email]) returning *" }, { "db":"tpevoml", "subkey":"operato_ml", "sql":"insert into operato (codice,nominati,email) values ($[codice],$[nominati],$[email]) returning *" } ]

Attraverso una unica richiesta di esecuzione di multi-query implicita è possibile inserire l'utente in tutte e tre i database

si assegnano gli attributi codice, pwd, nominati, email dell'utente

si esegue la ask con request = operains

si pone nume = 1 per attivare le transazioni

l'AgentData avvierà una transazione per ogni database, in modo da garantire che tutte le query vadano a buon fine, altrimenti annullerà la transazione su tutti i db assicurando cosi l'integrità dei dati

Esempio Chiamata

agente: AgentData

tipo chiamata: ask

request: opins

p1: {"codice":"n.berga","pwd":"aioaio","nominati":"Nicola Bergantino","email":"n.bergantino@youhh.com"}

nume: 1

Parameters Augmentation

L'AgentData implementa un algoritmo che permette di completare i parametri di una prepared statement, non forniti dal richiedente:

utilizzando valori default specificati in configurazione

andando a prelevare i dati dal database attraverso una prepared statement

impostando i dati a NULL

Di seguito la specifica dell'algoritmo:

Data una prepared S sia:

P l'insieme dei parametri richiesti da S

Q l'insieme dei parametri forniti dal richiedente

sia M = P - Q l'insieme dei parametri presenti in P ma non forniti in Q

se M = 0 significa che tutti i parametri P richiesti da S sono stati forniti in Q

Se Q ha una sola riga e M > 0, l'AgentData applica un algoritmo di parameters augmentation

tale algoritmo cerca di assegnare automaticamente i parametri in M, come segue:

applicazione dei parametri di default definiti nella subkey

impostazione dei parametri a partire dal risultato derivante dall'esecuzione di altre prepared statement (key)

impostazione dei parametri a valori NULL

Siano S(1), S(2), ..., S(n) le prepared statement da cui prelevare i parametri

Siano R(1), R(2), ..., R(n) i risultati dell'esecuzione delle n prepared statement

M = M - R(1) - R(2) - ... - R(n) è l'insieme dei parametri rimasti inassegnati dopo l'applicazione di R(1), ..., R(n)

Se M > 0 ed è richiesto il setting a NULL

l'algoritmo assegna tutti i parametri in M al valore NULL

Per poter applicare l'algoritmo di parameters augmentation è necessario utilizzare due attributi dell'oggetto json corrispondente alla prepared statement:

paramsdef: è un oggetto JSON con un attributo per ogni parametro con il relativo valore di default

paramsql: specifica la lista delle key da eseguire per recuperare i parametri (lista di elementi divisi da virgola senza spazi)

paramsnull: se posto a 1 indica che si richiede il settaggio a NULL dei parametri residui (dopo l'applicazione di P e di R(1), ..., R(n))

Esempio

vogliamo inserire una nuova linea di produzione

creiamo una mainkey = linea e il corrispondente file linea.json

per completare l'inserimento di una linea dobbiamo inserire:

ubicaz: tutti i dati richiesti, con setting automatico a NULL di quelli non forniti

lottim: solo il campo codice, non richiesto l'algoritmo di parameters augmentation

oeeconf (ORA): oeeconf orario, si preleva la configurazione da valori default

oeeconf (DAY): oeeconf giornaliero, si preleva la configurazione da oeeconf.codice="day"

di seguito linea.json

[ {"subkey":".","sql":".","paramsnull":0,"paramsql":".","paramsdef":{},"events":{}}, { "subkey":"ubicaz", "sql":"insert into ubicaz (codice,descrizio,identif,tiposet,tipocom,tipodepo,codriso,impianto,utente,datareg,stato,codcate,codoper,locked,codqua,nqua,codubib,capacita,occupazione,pos,lim1,lim2,curop,codturno,agpausesap,plmaster,plslave,updw,kbterm,blink,visnum,codfase,codazi,schema,costo,defincdec,oeew,opts,thmonoee,thmonsetup,thmondownt,thmonscrap,thdayoee,thdaysetup,thdaydownt,thdayscrap,laps,linubi,defnpop,deftcpol,saveoeeshift,tmstart,started,mancost,amcilr,amsop,storcompo,supname,locname,extcost,tmstartto,ystart,ystartto,codtunext,tmtunext,tmtu) values ($[codice],$[descrizio],$[identif],'P',$[tipocom],$[tipodepo],$[codriso],$[impianto],$[utente],$[datareg],$[stato],$[codcate],$[codoper],$[locked],$[codqua],$[nqua],$[codubib],$[capacita],$[occupazione],$[pos],$[lim1],$[lim2],$[curop],$[codturno],$[agpausesap],$[plmaster],$[plslave],$[updw],$[kbterm],$[blink],$[visnum],$[codfase],$[codazi],$[schema],$[costo],$[defincdec],$[oeew],$[opts],$[thmonoee],$[thmonsetup],$[thmondownt],$[thmonscrap],$[thdayoee],$[thdaysetup],$[thdaydownt],$[thdayscrap],$[laps],$[linubi],$[defnpop],$[deftcpol],$[saveoeeshift],$[tmstart],$[started],$[mancost],$[amcilr],$[amsop],$[storcompo],$[supname],$[locname],$[extcost],$[tmstartto],$[ystart],$[ystartto],$[codtunext],$[tmtunext],$[tmtu]) returning *", "paramsnull":1 }, { "subkey":"lottim", "sql":"insert into lottim (codice) values ($[codice]) returning *", }, { "subkey":"oeeconf_hourly", "sql":"insert into oeeconf (codice,codriso,codperiodo,durperiodo,nrperiodi,tmdisp,tpriso,tmlast,codmm,codrisodef,bgnper,availab,calcnop,kweek,lim1,lim2,mudoee,dircalcsca,disabled,nptots) values ($[codice] || $[codperiodo],$[codice],$[codperiodo],$[durperiodo],$[nrperiodi],$[tmdisp],$[tpriso],$[tmlast],$[codmm],$[codrisodef],$[bgnper],$[availab],$[calcnop],$[kweek],$[lim1],$[lim2],$[mudoee],$[dircalcsca],$[disabled],$[nptots]) returning *", "paramsdef":{ "codice":"ora","codperiodo":"ORA","durperiodo":3600,"nrperiodi":24,"tmdisp":3600,"tpriso":"M", "tmlast":"_timenow","codmm":0,"codrisodef":"","bgnper":0,"availab":1,"calcnop":1,"kweek":"", "lim1":0,"lim2":0,"mudoee":"","dircalcsca":1,"disabled":1,"nptots":0 } }, { "subkey":"oeeconf_daily", "sql":"insert into oeeconf (codice,codriso,codperiodo,durperiodo,nrperiodi,tmdisp,tpriso,tmlast,codmm,codrisodef,bgnper,availab,calcnop,kweek,lim1,lim2,mudoee,dircalcsca,disabled,nptots) values ($[codice] || $[codperiodo],$[codice],$[codperiodo],$[durperiodo],$[nrperiodi],$[tmdisp],$[tpriso],$[tmlast],$[codmm],$[codrisodef],$[bgnper],$[availab],$[calcnop],$[kweek],$[lim1],$[lim2],$[mudoee],$[dircalcsca],$[disabled],$[nptots]) returning *", "paramsql":"ubicazdef.oeeconfd" } ]

le key ubicazdef.oeeconfh e ubicazdef.oeeconfd sono presenti nel file ubicazdef.json, come segue

[ { "subkey":"oeeconfh", "sql":"select * from oeeconf where codice='ora'" }, { "subkey":"oeeconfd", "sql":"select * from oeeconf where codice='day'" } ]

L'inserimento di una linea di produzione può essere svolta attraverso la seguente chiamata

Chiamata

agente: AgentData

request: linea

p1: {"codice":"test1","descrizio":"test1","tiposet":"P"}

nume: 2

IMPORTANTE : i parametri aggiunti con parameters augmentation possono essere soggetti a pre-processing dei parametri; ad esempio in un parametro default potrei impostare il valore di preprocessing _getnume

Events

E' possibile associare ad ogni prepared statement un evento da inviare ad un agente qualora la prepared statement fosse stata eseguita con successo (es: un inserimento, una modifica, ...).

E' possibile configurare uno (singolo oggetto) o più agenti (array json) destinatari dell'evento.

A seguito dell'esecuzione con successo del prepared statement l'AgentData verifica se vi è associato un evento, in tal caso richiama la tell di ogni agente configurato, con la rispettiva request.

La tell eseguita fornirà nell'attributo "p1" della richiesta l'oggetto JSON (myrec) risultante dall'esecuzione del prepared statement (es: nel caso di isnert, delete, update sarà il valore di "returning *", mentre nel caso di select, sarà il risultato della select).

Per configurare gli eventi è sufficiente integrare nel JSON del prepared statement il seguente attributo:

events = oggetto json o array json, cosi composto:

to = nome dell'agente destinatario.

request = request.

param = l'attributo param definisce il valore di tipo stringa che viene inserito nel "p2" della richiesta all'agente destinatario. Se non specificato il "p2" viene impostato a NULL, altrimenti viene impostato al valore specificato.

Esempio di configurazione evento:

[ { "subkey":"operacom", "sql":"insert into operacom (codice,key,tipo) values ($[codice],$[key],$[tipo]) returning *", "paramsdef":{"codice":"_getnume"}, "events":{"to":"mngevent","request":"newopera","param":""} }, { "subkey":"operamsg", "sql":"insert into operamsg (codcom,codlang,msg) values ($[codcom],$[codlang],$[msg]) returning *", "paramsdef":{"codcom":"$[operacom.codice]"} } ]

Se nell'attributo to dell'oggetto events, si inserisce la seguente istruzione $me l'agente invierà la chiamata a se stesso (esempio per attivare certe operazioni su DB a seguito di certe query)

Riga dummy

Nel caso in cui, per il primo oggetto del file JSON, non si specifichino gli attributi opzionali (db, paramsql, paramsnull) e se questi attributi fossero poi forniti gli oggetti successivi nell'array, allora è necessario indrodurre una riga dummy

La riga dummy permette all'algoritmo di conversione da JSON a myrecson della IAF di intercettare tutti gli attributi degli oggetti presenti

La riga dummy va inserita come prima riga del json, di seguito un esempio:

[ {"subkey":".","sql":".","paramsnull":0,"paramsql":".","paramsdef":{},"events":{}}, {...} ]

Generazione di csv da Prepared Statement

Utilizzando la tell è possibile eseguire qualsiasi prepared statement come con la ask

In questo caso l'agente ritorna True se la chiamata multi-mono query restituisce un JSON altrimenti ritorna False

E' possible generare file CSV a partire da una prepared statement, basta aggiungere nel JSON della richiesta i seguenti attributi:

_csvfile: path completo del file csv

_decimalpoint: (opzionale) si puo' indicare il carattere separatore dei decimali

_separator: (opzionale) si può indicare il carattere separatore dei campi nel file csv (default: ;)

Se presente l'attributo _csvfile, l'agente crea il file csv

Attributi utili di una subkey

Rinominare nomi di campi

Con l'attributo renamemap del file JSON è possibile rinominare nomi dei campi della risposta di un prepared statement in modo arbitrario, ad esempio per stampare un CSV con nomi di campi significativi.
Vediamo il seguente esempio:

[ { "subkey":"stab03", "db":"terranuovadb", "sql":"with d as (select d.* from dimension d inner join oeeconf o on o.codriso=d.d1 where tm>$[tmbgn] and tm<$[tmend] and d2='ORA' and o.disabled='0' and o.codperiodo='ORA'),e as (select u.descrizio as linea,round(sum(m_rep_nptc-m_rep_stc)/60) as lavorato,round(sum(m_rep_d)/60) as previsto from d inner join fact f on f.codice=d.kfact inner join ubicaz u on u.codice=d.d1 group by u.descrizio) select 'Terranuova' as dplant,*,round((case when previsto>0 then lavorato/previsto else 0 end)::numeric, 2) as e from e order by linea", "renamemap":{"dplant":"Stabilimento","linea":"Centro di lavoro","lavorato":"Tempo Lavorato (min)", "previsto":"Tempo Previsto (min)", "e":"Efficienza (%)"} } ]

In questo caso renamemap rinomina i campi dbplant, linea, lavorato, previsto, e assegnando nomi esplicativi.

Normalizzare query lunghe

Per poter normalizzare query lunghe in cui si adoperano ritorni a capo e spazi per poterle leggere meglio nel file JSON si può attivare l'attributo normalize.
L'attributo normalize se posto a 1 attiva un algoritmo di normalizzazione della stringa SQL.
Questa operazione è particolarmente utile per la visualizzazione dei LOG delle stringhe lunghe.

{ "subkey":"ubigiac", "sql":"SELECT g.codubi, l.codart, ca.tipopal as codpal, ug.xabs as x, ug.yabs as y, ug.zabs as z, CASE WHEN tp.stackable = 1 then 2 else 1 END as npallet, u.tipocom, u.tiposet, 0.0 as a, 0.0 as b FROM giacese g JOIN lottim l on l.codice=g.codlot JOIN catego ca ON l.codart = ca.codice JOIN ubicaz u on u.codice=g.codubi JOIN tipopal tp on tp.codice = l.umext LEFT JOIN ubigeo ug on ug.codubi = g.codubi WHERE g.codubi=$[codubi] GROUP BY g.codubi, l.codart, u.tipocom, u.tiposet, ca.tipopal, ug.xabs, ug.yabs, ug.zabs, tp.stackable ORDER BY max(g.codice)", "paramsdef":{"codubi":"LGV"}, "normalize":1 }

La normalize riduce tutti gli spazi doppi o i ritorni a capo o i segni di tabulazione ad un unico spazio, in modo da avere la query su una unica riga.
IMPORTANTE: Utilizzare con attenzione nel caso in cui gli spazi servano per impostare valori o condizioni.

Q&A

E' possibile attivare la procedura di parameters augmentation per settare automaticamente attributi default indicati espressamente per valore oppure che utilizzano i valori default del campo nel db ? (tipo il null)

SI basta utilizzare l'attributo paramsdef dell'oggetto subkey specifico (vedi Parameters Augmentation)

Come va gestito nel prepared statement le query con condizione where che richiede che un parametro sia parte di un certo insieme di parametri ? (esempio: where codice in ('A', 'B', ...) and ... )

di seguito la risposta:

[ ... { "subkey":"ubiany", "sql":"select * from ubicaz WHERE codice = ANY(string_to_array($[lista codici], ','))" }, ... ]

Come gestire un prepared statement con una like ?

di seguito la risposta:

[ ... { "subkey":"ubilike", "sql":"select * from ubicaz WHERE codice like $[codice] || '%'" } ]

Nel caso in cui una richiesta multi-query non sia vincolata da una transazione è possibile eseguire le query in parallelo ?

Nella versione attuale le query parallele sono possibili con le multi-query implicite che non prevedono transazioni; per farlo sarebbe necessario installare le librerie omp per il threading implicito

Come potremo orientarci all'interno dei vari path di file json quando le query saranno davvero tante ? Come faremo a capire se una query è già presente ? come faremo a indiviuare dove si torva una mainkey o una subkey ? Come potremo testarle o inserirne di nuove ?

E' necessario costruire un agente che si occupa di:

tracciare tutte le key presenti nel sistema fornendo:

commento, sql, path

ricercare una key, subkey

ricercare in modo intelligente un commento, una sql

inserire / modificare / testare nuove key o nuove mainkey