Mondi su mondi, sistemi di sistemi.

PostgreSQL 8.4: window functions

Tuesday, May 12th, 2009

Le win­dow func­tions per­met­tono di usare valori aggre­gati diret­ta­mente sulle sin­gole righe e senza dover uti­liz­zare la clau­sola GROUP BY.

Prima della ver­sione 8.4 avrei dovuto scri­vere una cosa del genere (usando l’esempio in documentazione):


SELECT
    depname,
    empno,
    salary, avg_salary
FROM
    empsalary
    JOIN (
        SELECT
            depname,
            avg(salary) as avg_salarys
        FROM empsalary
        GROUP BY depname
    ) AS avg_salaries using(depname);


Oltre a sem­pli­fi­care le query, pos­siamo anche andare oltre e, con la fun­zione rank() “clas­si­fi­care” i record della tabella in base al loro sala­rio o cal­co­lare dei totali pro­gres­sivi; il tutto espresso in modo molto naturale.

Se penso alle con­tor­sioni da fare in SQL “nor­male” (o comun­que una ver­sione suf­fi­cien­te­mente vec­chia) per fare cose del genere…

PostgreSQL 8.4: query ricorsive con WITH

Sunday, May 10th, 2009

Finora abbiamo visto delle novità non par­ti­co­lar­mente ecla­tanti(1, 2, 3). Con l’introduzione delle “WITH que­ries” pas­siamo a qual­cosa di più sostanzioso.

WITH (non ricorsivo)

Nella ver­sione “liscia” WITH è un aiuto per scri­vere le query in modo più chiaro, senza biso­gno di sub-SELECT nidi­fi­cate. Al posto di:

WITH regional_sales AS (
        SELECT
            region,
            SUM(amount) AS total_sales
        FROM orders
        GROUP BY region
     ), top_regions AS (
        SELECT region
        FROM regional_sales
        WHERE total_sales > (
            SELECT SUM(total_sales)/10
            FROM regional_sales
        )
     )
SELECT region,
       product,
       SUM(quantity) AS product_units,
       SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;

Avremmo dovuto scrivere:

SELECT region,
       product,
       SUM(quantity) AS product_units,
       SUM(amount) AS product_sales
FROM orders
WHERE region IN (
    SELECT region FROM (
        SELECT region
        FROM regional_sales
        WHERE total_sales > (
            SELECT SUM(total_sales)/10 FROM (
                SELECT
                    region,
                    SUM(amount) AS total_sales
                FROM orders
                GROUP BY region
            )
       )
    )
)
GROUP BY region, product;

Oltre a essere più chiara, notiamo che la stessa espres­sione — in que­sto caso, regional_sales — può essere richia­mata già all’interno del blocco di WITH; inol­tre, secondo la docu­men­ta­zione, l’espressione viene valu­tata una volta sola, anche se viene in seguito richiamata.

WITH RECURSIVE

Se la clau­sola WITH ha prin­ci­pal­mente il van­tag­gio di ren­dere la query più chiara ed effi­ciente, la ver­sione ricor­siva rende pos­si­bile l’espressione in SQL di pro­blemi che avreb­bero prima richie­sto una solu­zione procedurale.

Ad esem­pio diventa pos­si­bile for­mu­lare query che attra­ver­sano strut­ture gerar­chi­che (il clas­sico pro­blema del “bill of mate­rials”). Eccone uno dalla documentazione:

WITH RECURSIVE included_parts(sub_part, part, quantity) AS (
    SELECT
        sub_part,
        part,
        quantity
   FROM parts
   WHERE part = 'our_product'
  UNION ALL
    SELECT p.sub_part, p.part, p.quantity
    FROM included_parts pr, parts p
    WHERE p.part = pr.sub_part
  )
SELECT sub_part, SUM(quantity) as total_quantity
FROM included_parts
GROUP BY sub_part;

Come tutte le strut­ture ricor­sive biso­gnerà fare atten­zione ad evi­tare cicli infi­niti ma, in linea gene­rale, l’uso di WITH RECURSIVE non riserva grosse sorprese.

PostgreSQL 8.4: argomenti delle funzioni

Thursday, May 7th, 2009

Con la ver­sione 8.4 ven­gono intro­dotte due novità di grande uti­lità: la pos­si­bi­lità di avere un numero varia­bile di argo­menti e la pos­si­bi­lità di spe­ci­fi­care dei valori di default.

Non mi dilungo sul fun­zio­na­mento, piut­to­sto ovvio, riman­dando alla docu­men­ta­zione. Vorrei invece spie­gare il motivo del mio inte­resse, in par­ti­co­lare per le fun­zioni varia­dic.

Funzioni varia­dic

Con que­ste fun­zioni diventa ora sem­pli­cis­simo defi­nirne una gene­rica di inse­ri­mento, uti­liz­za­bile senza che sia quasi neces­sa­rio “mas­sag­giare” i dati che arri­vano da un POST.

Basta creare una fun­zione che abbia come argo­mento VARIADIC text[]. L’array avrà una strut­tura del tipo: nome tabella, chiave, valore, … Usando il pro­prio lin­guag­gio di fidu­cia è facile poi costruire lo sta­te­ment SQL adatto.

Funzioni con default

La pos­si­bi­lità di spe­ci­fi­care dei valori di default può essere molto utile, soprat­tutto con le fun­zioni SQL e non PL/pgSQL, dato che non c’è la pos­si­bi­lità di dichia­rare varia­bili. Tuttavia, più che segna­lare i pos­si­bili usi mi limito a far pre­sente un’ambiguità. 

PostgreSQL (esat­ta­mente come Java) sup­porta l’overloading degli argo­menti: due fun­zioni con lo stesso nome sono disam­bi­guate in base agli argo­menti. Ora, intro­du­cendo i defaults pos­siamo avere situa­zioni come questa:


create or replace function fn (a int)
returns int as $$
    select $1;
$$ language sql;

create or replace function fn (
    a int, b int default 2
) returns int as $$
    select $1 + $2;
$$ language sql;

Se fac­cio: select fn(10, 20) tutto bene, ma con:


test=# select fn(10);
ERROR:  function fn(integer) is not unique
LINE 1: select fn(10);
               ^
HINT:  Could not choose a best candidate function.
You might need to add explicit type casts.
test=#

So che è un caso limite ma è meglio tener pre­sente la cosa.

PostgreSQL 8.4: privilegi a livello di colonna

Tuesday, May 5th, 2009

In que­sto post esa­mi­ne­remo un po’ più da vicino le novità della ver­sione 8.4 per quanto riguarda i privilegi.

In PostgreSQL — come anche in altri sistemi — si usa il comando GRANT per asse­gnare i pri­vi­legi per usare gli oggetti del data­base e gestire i ruoli di utenza.

Rispetto alla ver­sione pre­ce­dente, la novità più impor­tante è appunto quella di poter scen­dere fino alla sin­gola colonna nell’assegnazione dei pri­vi­legi. In que­sto modo si sem­pli­fi­cano note­vol­mente le solu­zioni per imple­men­tare sce­nari in cui, ad esem­pio, due gruppi di utenti pote­vano inse­rire dati ma solo uno dei due poteva poi modi­fi­care i dati di una data colonna.

Soprattutto que­sta miglio­ria con­sente di risol­vere que­sti pro­blemi in modo dichia­ra­tivo e non pro­ce­du­rale, che è molto più pulito e potente. Ad esem­pio, pos­siamo pen­sare a sistemi in cui alcuni campi nei form ven­gano mostrato o meno in base al pro­filo dell’utente e così via.

PostgreSQL 8.4: impostazioni del locale

Monday, May 4th, 2009

Le impo­sta­zioni del locale sono un aspetto spesso tra­scu­rato — vuoi per­ché even­tuali defi­cienze ven­gono com­pen­sate nell’applicazione, o per­ché con gli alfa­beti roman il default, anche se non per­fetto, non è visto­sa­mente inadeguato.

Fino alla ver­sione 8.3, PostgresSQL non con­sen­tiva di spe­ci­fi­care il locale per un sin­golo data­base, ma solo a livello di clu­ster, durante la sua crea­zione con initdb. Con la nuova ver­sione, i para­me­tri impo­stati nell’initdb val­gono solo come default, con­sen­tendo di cam­biare il tipo di col­la­zione e la clas­si­fi­ca­zione dei caratteri.

La col­la­zione sta­bi­li­sce l’ordinamento alfa­be­tico, men­tre la clas­si­fi­ca­zione sta­bi­li­sce quali carat­teri sono maiu­scoli, quali minu­scoli e quali sono le cifre.

In que­sto modo è pos­si­bile usare il locale più adatto senza dover creare un clu­ster ad hoc, con tutte le com­pli­ca­zioni che comporta.

PostgreSQL 8.4

Thursday, April 30th, 2009

So che sono in ritardo ma credo valga ancora la pena di ricor­dare che da qual­che giorno è dispo­ni­bile la prima beta di PostegreSQL 8.4.

Ci sono diverse novità e quelle che mi inte­res­sano di più sono:

  • le win­do­wing functions;
  • sup­porto di WITH e WITH RECURSIVE;
  • valori di default e numero di argo­menti varia­bile per le funzioni;
  • pri­vi­legi a livello della sin­gola colonna;
  • locale spe­ci­fi­ca­bile per ogni sin­golo database.

Nei pros­simi giorni, tempo per­met­tendo, comin­cerò a pren­dere in esame que­ste novità più nel dettaglio.

OR Mappers: verso altre direzioni?

Friday, April 3rd, 2009

In uno dei post pre­ce­denti accen­navo al fatto che il giu­di­zio sui domain model — e di con­se­guenza sugli ORM — tiene conto di cri­teri di ordine diverso: ci sono ragioni teo­ri­che — di prin­ci­pio, direi — e ragioni pratiche.

Per quanto riguarda le ragioni teo­ri­che credo di essermi già dilun­gato abba­stanza, men­tre sulle ragioni pra­ti­che abbiamo comin­ciato a par­larne in rela­zione al caching. Oggi vor­rei discu­terne un’altra di tipo pratico. 

Uno dei modi di ana­liz­zare la strut­tura di un’applicazione è quella di ragio­nare per livelli.  Prendendo spunto da Fowler, abbiamo: un livello di pre­sen­ta­zione, uno di sor­gente dei dati e uno con la cosid­detta domain logic. Grosso modo, pos­siamo dire che il primo livello cor­ri­sponde al bro­w­ser, il secondo all’application ser­ver, il terzo al database.

Sorvoliamo su molti det­ta­gli (ad es. se con­si­de­rare il JavaScript che gira nel bro­w­ser o la sto­red pro­ce­dure nel data­base come facenti parte della domain logic o meno ecc.) e con­cen­tria­moci su quello che potremmo chia­mare il tasso di muta­zione nei vari livelli.

Sul lato del bro­w­ser abbiamo la com­bi­na­zione HTML+CSS+JavaScript da tempo imme­more. Da qual­che anno, con AJAX, la por­zione di JavaScript è cre­sciuta a dismi­sura ed è cam­biato il modo di con­si­de­rare JavaScript come lin­guag­gio; l’HTML è pas­sato attra­verso varie revi­sioni e rami­fi­ca­zioni. Tuttavia, pos­siamo dire che nel com­plesso è cam­biato rela­ti­va­mente poco.

All’estremo oppo­sto abbiamo uno sce­na­rio simile. L’SQL, con i suoi limiti, le sue imper­fe­zioni e i suoi diversi dia­letti è cer­ta­mente un qual­cosa in con­ti­nua evo­lu­zione, ma senza scos­soni e in un qua­dro con­cet­tuale molto sta­bile. Altrettanto sta­bili sono le instal­la­zioni: una volta messo in piedi, è dif­fi­cile che si passi ad un altro sistema.

Se guar­diamo al livello inter­me­dio tro­viamo invece una situa­zione com­ple­ta­mente diversa. Abbiamo a dispo­si­zione tutti i lin­guaggi pos­si­bili, con tutti gli approcci pos­si­bili (ho perso il conto dei fra­mework web in Java), che mutano a velo­cità sostenuta.

Non solo. L’avvento di AJAX ha spo­stato il bari­cen­tro verso il bro­w­ser, sot­traendo fun­zioni al livello inter­me­dio, sino ad arri­vare ad archi­tet­ture come CouchDB, in cui que­sto livello non c’è pro­prio più.

E quindi mi sono fatto la domanda ine­vi­ta­bile: per­ché inve­stirci ancora risorse? Ha ancora senso?

OR Mappers: il caching

Wednesday, April 1st, 2009

Fra i van­taggi che sono soli­ta­mente ascritti agli ORM c’è il caching. Si sfrutta la pos­si­bi­lità di creare in memo­ria una rap­pre­sen­ta­zione per­si­stente di righe nel data­base, sotto forma di un grafo di oggetti, per evi­tare di dover andare a ripe­scare dal data­base mede­simo i dati ogni volta che serve.

È un’idea sen­sata, ovvia­mente, ma credo che abbia un uso più limi­tato di quanto si pensi, soprat­tutto in tempi come que­sti fatti di archi­tet­ture distri­buite, dove la cache può diven­tare un’arma a dop­pio taglio.

Ad esem­pio, quando seguivo con più atten­zione la mai­ling list di WebObjects c’era spesso una domanda ricor­rente “Ho n istanze della stessa appli­ca­zione, col­le­gate allo stesso data­base; come fac­cio a ‘far vedere’ le modi­fi­che dell’istanza A all’istanza B?”

Per risol­vere que­sto pro­blema nel tempo sono state svi­lup­pate diverse solu­zioni, l’ultima di cui sono a cono­scenza è com­po­sta da er.extensions.remoteSynchronizer e er.jgroups (che usa, appunto, JGroups).

La com­ples­sità aumenta con­si­de­re­vol­mente e abbiamo comun­que risolto solo una parte del pro­blema, visto che se un pro­cesso aggiorna il data­base al di fuori del con­te­sto delle appli­ca­zioni A e B le loro cache risul­tano comun­que invalidate.

Inoltre, il pro­blema potrebbe essere con­si­de­rato risolto solo se ci limi­tiamo a sistemi tra­di­zio­nali client/server, ma su web cam­bia tutto: è un’architettura fon­da­men­tal­mente sta­te­less, dove le cache inter­me­die non vanno molto d’accordo con que­sta carat­te­ri­stica (un’altra mani­fe­sta­zione dell’end-to-end argu­ment).

Se aggiun­giamo que­sto para­me­tro al cal­colo dei costi/benefici ci accor­giamo che il van­tag­gio del caching risulta molto più ambiguo.

OR Mappers: l’importanza degli strumenti

Monday, March 30th, 2009

Qualche anno fa, Charles Petzold si è doman­dato se l’uso di Visual Studio possa rimbambirci:

Life without Visual Studio is uni­ma­gi­na­ble, and yet, no less than PowerPoint, Visual Studio cau­ses us to do our jobs in various pre­de­fi­ned ways, and I, for one, would be much hap­pier if Visual Studio did much less than what it does. Certain fea­tu­res in Visual Studio are sup­po­sed to make us more pro­duc­tive, and yet for me, they seem to deni­grate and degrade the pro­gram­ming expe­rience. #

Cito que­sto paper per sug­ge­rire che gli stru­menti che usiamo pos­sono avere veri e pro­pri effetti col­la­te­rali. Bene, ma che c’entra con gli ORM?

C’entra in que­sto senso: se gli stru­menti che uso non mi con­sen­tono di avere una visione d’insieme di tutti gli aspetti dell’applicazione, ho più dif­fi­coltà a rico­struire men­tal­mente que­sta visione. Più il pro­getto è com­plesso e più que­sto pro­blema diventa sen­si­bile. Se potessi usare un solo lin­guag­gio la situa­zione sarebbe sem­pli­fi­cata e gesti­bile com­ple­ta­mente all’interno di un IDE.

Ecco quindi che suona come una buona idea quella di evi­tare l’uso dell’SQL attra­verso una map­pa­tura verso un qual­che lin­guag­gio a oggetti. È in fondo la stessa idea diGWT, appli­cata que­sta volta a JavaScript.

Ovviamente gli IDE si sono evo­luti e quindi pos­sono gestire molto meglio di prima più lin­guaggi in modo inte­grato, ma quando è nato l’EOF tutto que­sto era ancora di là da venire: dove oggi si usa solo Eclipse una volta c’erano il Project Builder, il WebObjects Builder, l’EOModeler (e poi suc­ces­si­va­mente anche il Rule Editor). Ma, appunto, era un limite degli stru­menti, non una neces­sità intrin­seca per poter pro­gram­mare pro­dut­ti­va­mente con i database.

OR Mappers: pro e contro i domain models

Monday, March 23rd, 2009

Una obie­zione che viene subito in mente con­tro l’idea di ridurre i domain objects a sem­plici EOGenericRecord è quello dell’antipattern (o code smell, fate voi) del cosi­detto Anemic Domain Model:

The basic symp­tom of an Anemic Domain Model is that at first blush it looks like the real thing. There are objects, many named after the nouns in the domain space, and these objects are con­nec­ted with the rich rela­tion­ships and struc­ture that true domain models have.

The catch comes when you look at the beha­vior, and you rea­lize that there is hardly any beha­vior on these objects, making them lit­tle more than bags of get­ters and set­ters. Indeed often these models come with design rules that say that you are not to put any domain logic in the the domain objects. Instead there are a set of ser­vice objects which cap­ture all the domain logic. These ser­vi­ces live on top of the domain model and use the domain model for data.

Sembra una descri­zione esatta di quello a cui porta il mio approc­cio. Ci sono però delle ulte­riori con­si­de­ra­zioni da fare.

Prima di tutto, l’EOGenericRecord non è pro­prio una sem­plice Data Class e for­ni­sce già un bel po’ di logica pronta all’uso e se non basta c’è la ver­sione pom­pata di Wonder. Di con­se­guenza il mio approc­cio sem­bra più a quello che viene defi­nito un Service Layer:

Application Layer [his name for Service Layer]: Defines the jobs the soft­ware is sup­po­sed to do and directs the expres­sive domain objects to work out pro­blems. The tasks this layer is respon­si­ble for are mea­ning­ful to the busi­ness or neces­sary for inte­rac­tion with the appli­ca­tion layers of other systems. This layer is kept thin. It does not con­tain busi­ness rules or kno­w­ledge, but only coor­di­na­tes tasks and dele­ga­tes work to col­la­bo­ra­tions of domain objects in the next layer down. It does not have state reflec­ting the busi­ness situa­tion, but it can have state that reflects the pro­gress of a task for the user or the program.

Domain Layer (or Model Layer): Responsible for repre­sen­ting con­cepts of the busi­ness, infor­ma­tion about the busi­ness situa­tion, and busi­ness rules. State that reflects the busi­ness situa­tion is con­trol­led and used here, even though the tech­ni­cal details of sto­ring it are dele­ga­ted to the infra­struc­ture. This layer is the heart of busi­ness software.

In secondo luogo non mi sem­bra che le argo­men­ta­zioni di Fowler siano molto con­vin­centi. È vero che l’accoppiamento fra dati e pro­ce­dure è la ragion d’essere dell’OOP ma que­sto non dimo­stra che sia sem­pre l’approccio migliore (cosa che anche Fowler rico­no­sce). Inoltre non viene por­tato un sin­golo esem­pio con­creto di una situa­zione in cui un Domani Model è chia­ra­mente supe­riore, a parte gli esempi ovvi.

Distinguere chia­ra­mente i pro e i con­tro è dif­fi­cile anche per­ché i cri­teri di valu­ta­zione sono in parte teo­rici e in parte pra­tici (almeno nel mio caso). Ci torneremo.

« Voci Precedenti Prossime Voci »