Basi di crittografia

Come ho spiegato nel mio post precedente sto cercando di mettere un po’ in sicurezza degli script in python che uso per l’automazione di alcune operazioni su database. Da questa esigenza mi è venuta voglia di aggiornarmi su quella che è attualmente il livello tecnologico della crittografia. Si tratta di un campo che deve evolvere velocemente perché da una parte i sistemi di elaborazione diventano più potenti rendendo deboli soluzioni che anni fa erano robustissime e dall’altro, il fatto che sempre più servizi informatici siano accessibili a tutto il mondo sulla rete rende necessario sistemi più sicuri. Facendo un po’ di ricerche ho trovato come base di partenza una ottima fonte, il libro di Svetlin Nakov “Practical Cryptography for developers”, un libro abbastanza aggiornato e che fa una buona panoramica, senza voler entrare troppo nei dettagli della matematica necessaria per comprendere il funzionamento degli algoritmi di crittografia. Per qualche motivo l’autore sembra non abbia portato a compimento le sue intenzioni e il libro sembra fermo al 2018 con delle parti rimaste incompiute. Ciò nonostante mi è piaciuto e mi è stato utile. Ho integrato la mia ricerca con delle verifiche che di solito finiscono sempre su wikipedia. Il mio intento non era entrare nei dettagli dei vari algoritmi ma avere una panoramica e senz’altro questo libro mi è stato molto d’aiuto. Ho raccolto un po’ di appunti sugli argomenti principali e li voglio riportare qui, come ho sempre cercato di fare.

Criptazione e chiavi

La criptazione ha l’obiettivo di modificare o meglio nascondere il testo di un messaggio affinché solo il destinatario del messaggio possa decodificarlo e comprenderlo. Per fare ciò è fondamentale il concetto di chiave, che permette di codificare (criptare) il messaggio in modo da renderlo incomprensibile a chi non ha la chiave per poterlo decodificare. Esistono due tipologie di crittografia: 

  1. simmetrica, in cui mittente e destinatario condividono la stessa chiave per codificare e decodificare i messaggi
  2. Asimmetrica, in cui il mittente usa una chiave per codificare e il destinatario usa un’altra chiave per decodificare. Di solito si parla di chiave pubblica e chiave privata, il destinatario condivide la chiave pubblica con cui il mittente codifica il messaggio che può essere decodificato solo con la chiave privata e quindi solo dal destinatario

Uno dei principi base della crittografia è che la codifica è tanto più sicura (quindi il messaggio non può essere decodificato da estranei) quanto più la chiave è lunga e casuale, per questo solitamente si usano algoritmi di derivazione delle chiavi.

Funzioni Hash (Hash Functions)

Sono funzioni deterministiche che effettuano una codifica unidirezionale, data una stringa di input generano un output a lunghezza fissa pseudocasuale dal quale non è teoricamente fattibile risalire alla stringa di partenza. Il risultato di questa funzione viene proprio chiamato “hash” ed una delle caratteristiche di queste funzioni è che la probabilità che due input diversi producano lo stesso hash è pressoché zero. Uno degli utilizzi di questo tipo di funzioni è quindi la generazione di codici per validare il contenuto di un messaggio o file. Avendo a disposizione l’hash del messaggio o file originale è possibile, riapplicando la stessa funzione di hash, verificare facilmente l’uguaglianza dell’hash calcolato con quello che accompagna il messaggio o file. Un esempio ormai obsoleto di tali funzioni è MD5 (dove se non ricordo male MD sta per message digest), essa viene ancora utilizzata, seppur sempre meno, per il controllo di integrità ovvero la verifica che un messaggio (per semplicità d’ora in poi parlerò solo di messaggi e non di file) arrivato a destinazione sia uguale a quello partito. 

Le funzioni di hash sono state una delle prime funzionalità per mettere in sicurezza le password salvate, tipicamente anziché salvare le password in chiaro si salva il loro hash e in fase di login si confronta l’hash calcolato sulla password fornita con l’hash memorizzato. Questo fa si che se un malintenzionato riesce ad accedere all’archivio delle password non è in grado di risalire alla password originale, per via di una una delle caratteristiche delle funzioni di hash  che quella di non essere “invertibili” e quindi di non permettere di  risalire agevolmente alle password originale dagli hash.

Altri funzioni di hash più comunemente utilizzate sono MD5, SHA-1, SHA-2, SHA-3, BLAKE2 e BLAKE3.  MD5, SHA-1 sono oramai considerati insicuri e da non usare crittografia ma ancora usati in altri ambiti quale il controllo di integrità, oppure, come nel caso di sha-1 come generatore di hash univoci (vedi id commit in git).  La prima versione di SHA (SHA-1) genera hash di 160 bit, le versioni 2 e 3 (SHA-2 e SHA-3) hanno varianti con hash a 256 e 512 per cui si trova la notazione SHA-256 o SHA3-512 (ci sono poi anche ulteriori varianti a 224 384 bit che sono in realtà derivate rispettivamente dalla variante a 256 bit e 512 bit) quindi se si trova la notazione SHA-XXX si tratta implicitamente di SHA-2 mentre per SHA-3 si trova la notazione SHA3-XXX. Lo esplicito perché io sinceramente quanto ho iniziato a studiare questo argomento ho fatto un po’ di fatica all’inizio a districarmi tra queste notazioni per capire esattamente a che versioni ci si riferisse. SHA-1 non ha la specifica del numero di bit dell’hash prodotto perché ha una unica variante a 160 bit. Per completezza esiste anche un SHA-0, obsoleto e un RIPEMD-160. Ce ne sono ancora altri ma non intendo fare una rassegna esaustiva, rimando all’ottimo libro di Nakov per gli approfondimenti.

E’ abbastanza evidente che con il passare del tempo, il progredire della ricerca e l’aumento delle prestazioni di elaborazione dei calcolatori gli algoritmi devono essere aggiornati e per questo osserviamo l’esistenza di diverse versioni o il nascere di nuove, se si vuole essere sicuri occorre aggiornare gli algoritmi utilizzati dai propri sistemi. Wikipedia al solito è una fonte di informazione ottima, segnalo solo che almeno un paio di pagine in italiano sono poco aggiornate, quindi consiglio di andare direttamente alla versione inglese. Qui si trova uno schemino di confronto dei vari algoritmi di hash usati in crittografia

Message Authentication Codes (MAC) e hash based MAC (HMAC)

Un MAC è un codice calcolato da una chiave e da un messaggio che serve a stabilire l’autenticità di un messaggio. Il principio è che mittente e destinatario in qualche modo condividono una chiave nota solo a loro. Il mittente che deve inviare un messaggio genera un codice (il MAC) usando il testo del messaggio e la chiave, invia il messaggio e il MAC al destinatario e quest’ultimo potrà verificare l’autenticità del messaggio confrontando il MAC ricevuto con quello calcolato da lui con il messaggio e la chiave che solo lui e il mittente conoscono.  I MAC hanno le stesse caratteristiche degli hash crittografici, sono irreversibili e generano codici praticamente unici. I MAC basati su algoritmi di hashing vengono chiamati hash based MAC e quindi identificato con la sigla HMAC. Gli utilizzi tipici degli HMAC sono l’autenticazione di messaggi, la verifica di integrità di messaggi e la derivazione di chiavi

Algoritmi di derivazione delle chiavi 

Si tratta di algoritmi utilizzati per derivare chiavi di criptazione (con determinate caratteristiche) da semplici password generate solitamente da umani. Esempi di questi algoritmi sono PBKDF2, bcrypt, argon2 e scrypt. Questi algoritmi servono a generare (o meglio derivare) delle chiavi che abbiano requisiti necessari a garantire la necessaria efficacia e sicurezza di criptazione nel caso di criptazione a chiave unica (simmetrica). La caratteristica più importante che deve avere una chiave, come abbiamo detto sopra, è quella di essere quanto più “casuale” possibile, questo permette di prevenire approcci basati sulla statistica. Per rendere difficile l’uso di dizionari di hash (chiamati “rainbow table”) queste funzioni aggiungono alla password di partenza un “salt”, un codice generato anche esso con qualche funzione “casuale” da aggiungere alla chiave di partenza, quindi generando hash diversi per le stesse password se si usano salt diversi. Per rendere poi infattibili i cosiddetti attacchi di forza bruta (brute force attacks) ovvero il calcolo di tutti i possibili hash, gli algoritmi di derivazione introducono un certo costo computazionale reiterando migliaia o centinaia di migliaia di volte la stessa operazione. Il numero di iterazioni è un compromesso fra la necessità di una autenticazione veloce (perché nei sistemi di autenticazione questi algoritmi hanno preso il posto delle semplici funzioni di hash per il salvataggio delle password) e la necessità di garantirne  la non decodificabilità. Infatti uno dei limiti delle semplici funzioni di hash è che moderni sistemi dedicati possono calcolare milioni di hash al secondo rendendo quindi un attacco di forza bruta fattibile, usando algoritmi più complessi questo diventa molto più complesso.

L’algoritmo PBKDF2 è oramai ritenuto non molto sicuro perché debole contro attacchi di forza bruta basati sull’uso di GPU o ASIC (circuiti di elaborazione dedicati); Bcrypt, Scrypt e Argon2 sono algoritmi più recenti e più robusti contro attacchi perché richiedono molte più risorse computazionali. Questi algoritmi prevedono esplicitamente dei parametri di input che incidono sull’uso di memoria e cpu mentre PBKDF2 prevede solo il numero di iterazioni che può incidere sull’uso di CPU.

Gestione script e password

Da un po’ di tempo a questa parte uso script python per gestire piccole attività ripetitive su database. Un tempo avevamo database in locale o presso i clienti, protetti da reti locali per cui non ci si poneva grandi problemi sulla gestione delle password e della sicurezza dell’accesso, salvo qualche rara eccezione. Ora i tempi sono cambiati, la mia azienda è cresciuta e sempre di più lavoriamo sul “cloud”. Questo ha portato a una moltiplicazione di destinazioni e alla complicazione di piccole attività come fare dump da un database all’altro. Da questa aumentata complessità è nato il mio bisogno di utilizzare script di automazione e un paio d’anni fa (forse anche prima…) ho iniziato a lavorare con python e devo dire che per questo tipo di cose è molto comodo, facile da imparare e usare e al tempo stesso potente e completo.

Siccome ancora non ho a disposizione uno strumento “comodo” di gestione delle password ho deciso di fare un passo ulteriore, rispetto al dover andare a recuperare la password e copiarla come parametro ogni volta che devo eseguire uno script. Un primo passo è stato usare un file di testo in chiaro sul pc, d’altra parte il pc è già protetto di suo da un accesso controllato, il che garantisce già un minimo livello di sicurezza.

L’argomento sicurezza è serio e complesso, secondo me in molti lo prendono seriamente, alcuni sono al limite della patologia, molti hanno un approccio serio ma pragmatico, altri secondo me non ne capiscono molto. Poi ci sono quelli che invece non se ne preoccupano, però sono sempre meno perché l’evoluzione dell’informatica, la diffusione dell’uso del cloud e dei servizi accessibili su internet costringe a fare attenzione. Io confesso di essere molto critico rispetto ai molti che ritengono ed esibiscono esperienza di sicurezza, sono spesso gli stessi che ti fanno mettere simboli del dollaro, punti esclamativi e maiuscole nelle password che però possono essere lunghe otto caratteri. Certamente, se uso come password la parola “password” mi espongo a un grosso rischio, ma non è che usando “Pa$sword1” possa essere molto più sicuro.

Premesso rapidamente il pensiero sull’argomento (era tanto che volevo esporlo), come dicevo che mi sono posto il problema di come gestire in sicurezza le password di accesso a sistemi sul mio pc per poter lanciare gli script in modo quanto più automatico e veloce. Si tratta di un piccolo pezzo di gestione della sicurezza su cui mi sono chiesto quali sono le buone pratiche standard (“best practice” come direbbero quelli che ne sanno) per gestire questa cosa. Alla fin fine queste password devono essere salvate da qualche parte, anche sia un password manager in piena regola è sempre un sistema a cui io devo poter accedere tramite una password. Il fatto di averle sul mio pc limita la gestione in quanto non è condivisa, ma questo è un passo ulteriore che per ora tralascio, altrimenti non si fa nulla.

Facendo una rapida ricerca su google qualche cosa di trova, la cosa più interessante che ho trovato è stata questa, da cui ho preso spunto. La prima cosa che ho voluto fare è quindi informarmi un po’ sulla libreria usata: criptography. Il link dell’articolo manda a una versione piuttosto vecchia ma in realtà la libreria pare essere ancora aggiornata ed attiva e ne ho trovato vari riferimenti in giro. Per la verità non ho trovato altre librerie analoghe per python…. Il caso descritto nell’articolo è un po’ basilare perché gestisce una sola password, la mia ambizione è gestire diverse, non tantissime, parliamo di una decina al momento. In ogni caso, andando sul sito della documentazione si trovano anche un po’ di esempi interessanti. La mia idea è di usare un sistema con una password più o meno mnemonica, altrimenti sono al punto di partenza e la c’è un esempio che si basa sull’uso di PBKDF2 che io sinceramente non conosco e su cui quindi mi devo informare, anche perché sulla documentazione stessa consigliano di usare “Scrypt“. Quindi mi rimane da fare qualche prova e trovare qualche informazione chiara sul livello di sicurezza di queste soluzioni, a quel punto spero di poter fare un nuovo post con i risultati di queste mie ricerche

Oracle ed Espressioni Regolari, introduzione

Le espressioni regolari sono delle “funzioni” che permettono di identificare delle stringhe tramite semplici regole. Ho difficoltà a spiegarlo bene in italiano quindi farò una piccola deroga alla mia regola di evitare quanto possibile terminologia mista inglese/italiano; regola stabilita quando iniziai a scrivere su questo blog ma che per la verità sopratutto negli ultimi anni ho violato spesso. Dicevo, una espressione regolare serve a definire un “pattern” ovvero una caratteristica particolare di una stringa. Tramite poi questo pattern solitamente poi si va a filtrare un sottoinsieme di stringhe che corrispondono (“match”) a questo pattern da un insieme più grande.

Volendo fare una analogia di quanto si impara già dalle basi dell’SQL le espressioni regolari possono estendere le possibilità fornite dalla clausola LIKE con i caratteri speciali “%” e “_”.

Le espressioni regolari esistono da che esistono i computer e forse da prima; sistemi evoluti (*nix) forniscono da decenni programmi di utilità che implementano ricerche basate su espressioni regolari (grep, sed, ecc). Sono molto potenti ma possono essere anche molto complesse e quindi non è banale conoscerle e utilizzarle.

Oracle ha introdotto, già con la versione 10g, il supporto all’uso di espressioni regolari tramite l’introduzione di specifiche funzioni (che mi sembra siano standard ISO SQL) che sono:

  • REGEXP_LIKE
  • REGEXP_INSTR
  • REGEXP_REPLACE
  • REGEXP_SUBSTR

Non può sfuggire che sono l’analogo di funzioni/operatori già esistenti ma più semplici.

Ciclicamente mi capita di dover affrontare problemi in cui una buona conoscenza delle espressioni regolari aiuta molto; ogni volta mi trovo in difficoltà perché non riesco a memorizzare e capire a fondo il loro funzionamento. Il fatto di non usarle con costanza e regolarità non aiuta. Ecco perché a credo almeno 15 anni dalla loro introduzione in Oracle io mi trovo adesso a scrivere un post per spiegarne un po’ di basi, nella speranza che questo mi aiuti la prossima volta che mi serviranno.

Una espressione regolare è formata da uno o più “caratteri” e “metacaratteri”. Nella forma più semplice una espressione regolare può essere formata da da una o più lettere e/o cifre, ad esempio “a”, “a1”, “pappa”. I metacaratteri servono a indicare delle regole con cui processare e interpretare i caratteri della espressione regolare.

Vediamo le regole di base per definire una espressione regolare partendo da esempi semplici.

Il metacarattere “.” Si utilizza per indicare un qualunque carattere (ad esclusione dell’”accapo”). L’espressione “c.t” equivale a una stringa che contenga la lettera “c”, seguita da un qualunque carattere a sua volta seguito dalla lettera “t”. Quindi può corrispondere alla parola “cat”, ma anche “cut”, o “cutter” o “incitare”. Se vogliamo identificare solo parole che iniziano con “c”, seguita da un carattere qualunque seguito a sua volta dalla lettera “t” allora spossiamo usare i metacaratteri “^”, per indicare che il carattere che si trova dopo deve essere all’inizio della stringa e il metacarattere “$” per indicare che il carattere prima si deve trovare alla fine della stringa. Quindi ad esempio l’espressione regolare “^c.t$” corrisponde solo a stringhe di tre caratteri in cui il primo è la lettera “c”, l’ultimo è la lettera “t” e in mezzo va bene un carattere qualunque, come ad esempio “cit”, “c1t”, “cat” ma non “catalogo”.
Per indicare più occorrenze di un carattere si possono usare dei “quantificatori”; si possono usare i metacaratteri “?”, “+” e “*” o la notazione {n,m}, {n}, {n,}. “?” si usa per indicare zero o una occorrenze; “+” per indicare una o più occorrenze; “*” per indicare zero o più occorrenze. {n,m} dove n ed m sono due numeri, indica un numero di occorrenze compreso tra n ed m (estremi inclusi); {n} indica n occorrenze esatte; {n,} indica n o più occorrenze.
Quindi, l’espressione “^c.*t$” indica ora una stringa che inizia con la lettera “c” e finisce con la lettera “t”, in mezzo ci può essere un numero qualunque di qualunque carattere, ad esempio: “ct”, “caat”, “cavallot”, “cavolo t”. All’interno di una espressione regolare possiamo raggruppare sottoespressioni racchiudendole tra parentesi tonde (che sono quindi altri metacaratteri) Possiamo quindi definire l’espressione regolare “^c(as)*t$” che corrisponde ad esempio a “ct”, “cast”, “casast”.
Per definire una lista di caratteri si possono usare le parentesi quadre ([]) per delimitarli, ad esempio: “^c[auoei]t$” corrisponde a “cat”, “cit”,”cot” ma non “cyt”. Questa espressione può essere anche scritta usando le parentesi tonde e il metacarattere “|” che indica alternative, quindi: “^c(a|u|o|e|i)t$”. All’interno di parentesi quadre, all’inizio, il metacarattere “^” assume il significato di negazione, quindi l’espressione “^c[^hjk]t$” indica una stringa che inizia con c e finisce con t e in mezzo ha un qualunque carattere che non sia “h”, “j” o “k”. Fra le parentesi quadre si possono usare “liste” di caratteri usando il metacarattere “-“, ad esempio: “^c[d-z]t$” indica che tra “c” e “t” ci può essere un qualunque carattere compreso tra “d” e “z”. Se il carattere “-“ è all’inizio, come ad esempio “^c[-dys]t$” viene interpretato come carattere e non come metacarattere, quindi l’espressione in questo caso corrisponde ad esempio alla stringa “c-t”.

Esistono dei gruppi di caratteri definiti classi POSIX che vanno inseriti fra parentesi quadre e sono a loro definiti con la notazione di questo tipo: [:class:] , riport una tabellin di quelli più comuni:

Character ClassDescription
[:alpha:]Alphabetic characters
[:lower:]Lowercase alphabetic characters
[:upper:]Uppercase alphabetic characters
[:digit:]Numeric digits
[:alnum:]Alphanumeric characters
[:space:]Space characters (nonprinting), such as carriage return, newline, vertical tab, and form feed
[:punct:]Punctuation characters
[:cntrl:]Control characters (nonprinting)
[:print:]Printable characters
Classi di caratteri predefinite POSIX

Su wikipedia ce ne sono anche altre ma non ho verificato se Oracle le supporta tutte.

Il manuale Oracle dove vengono spiegate un po’ le espressioni regolari e il loro supporto e uso sul database è “Database Development Guide” a cui rimando per approfondimenti sulle espressioni regolari in genere ma soprattutto per le specificità. Spero di fare ulteriori post per approfondire meglio l’argomento e arrivare ad un uso più avanzato e proficuo di questo potente strumento.

SQLcl 19.4 e break on report

Scrivo un breve post per segnalare una cosa bizzarra che mi è capitata, un bug su SQLcl che mi perseguitava da diverso tempo ma che solo recentemente ho analizzato e individuato. Già qualche settimana fa, dopo che ero convintamente passato a utilizzare SQLcl al posto di SQLPlus mi sono accorto che alcuni miei script non funzionavano bene. Alla fine ho trovato un attimo per approfondire e cercare di riprodurre il problema, ho fatto un test su un database interno e fatto un trace della sessione per vedere cosa accadeva. Sono così riuscito a circoscrivere il problema e creare un banale “test case” che ho sottoposto via twitteral gruppo di sviluppo di SQLcl. Il codice è molto semplice:

create table t1(a number);
insert into t1 values (1);
insert into t1 values (2);
break on report
compute sum of uno on report
select t1a.a uno from t1 t1a join t1 t1b on (t1a.a=t1b.a);

Il problema si verifica quando si usa la direttiva “break on report” in associazione a una query di join. Per gestire questa direttiva a quanto pare la query viene riscritta e qui nasce il problema, perché dalla query riscritta spariscono le condizioni di join. Infatti uno dei miei script andava su viste di sistema, ci metteva una vita a dare il risultato che poi era anche sbagliato. La mia query veniva trasformata in un prodotto cartesiano….

Nel mio caso di test anche facendo un prodotto la query restituirebbe solo due record, quindi non sarebbe un problema.  In realtà l’unica query che vedo eseguita risulta essere:

SELECT 
sum(t1a.a) AS uno, 'sum' uno_CMPFN, COUNT(*) AS ROW_CNT, 'report' AS BRKCOLKEY FROM t1 t1a, t1 t1b;

che restituisce un solo record ma qualcosa su SQLcl sembra vada in loop.

Sono ansioso di vedere se dopo la mia segnalazione al prossimo rilascio il problema sarà corretto

Oracle SQLcl

Anche se la mia diffidenza mi spinge ad essere ancora prudente e a tenermi pronto per un piano di ritorno, sto passando da SQL*Plus a SQLcl. Il client a linea di comando alternativo a SQL*Plus facente parte di una suite che comprende tra gli altri il client grafico SQLDeveloper e SQL Data Modeler, sembra adesso abbastanza maturo ed compatibile quasi al 100% con SQL*Plus, del quale però estende le funzionalità e l’usabilità. Fra l’altro il pacchetto è anche più leggero. Rimane qualche minuscolo dettaglio che mi crea qualche turbamento ma direi che è ora di fare questo salto. Una delle cose che mi ha turbato è stata il problema del set autotrace di cui ho parlato nel post precedente, problema però che è comune a SQL*Plus, quindi è un falso problema. Trovo poi comodo la modalità di output “ansiconsole”, attivabile con il comando “set sqlformat ansiconsole”. Questa modalità ha il difetto che non fa funzionare le direttive “compute” o “break on”. Volevo aggirare il problema aggiungendo negli script il comando di salvataggio delle impostazioni su file (con il comando “store”), il cambiamento di impostazione per sqlformat e infine il caricamento delle impostazioni salvate. Purtroppo però con il comando STORE non mi viene salvata l’impostazione di sqlformat, quindi il trucco non funziona.

Per il resto il prodotto sviluppato da Jeff Smith e dai suoi collaboratori sembra proprio valido

Riferimenti:

SET AUTOTRACE

Si vede che sono un po’ fuori allenamento e dimentico le cose, quelle che imparai anni fa con tanto impegno e che registrai su questo blog proprio per evitare che ciò accadesse. E’ il caso di quanto scrissi in questo post,  l’unica scusante è che si tratta di un post di 12 anni fa; non posso ammetterla come scusante ma non posso neppure accettare di dimenticare cose che mi servono ancora nel mio lavoro.

Nei giorni scorsi stavo analizzando una query sottopostami da un collega, per mia sventura ho pensato di analizzare il piano di esecuzione usando l’opzione “set autotrace” di sqlplus, per fare più velocemente. Poi in realtà ho provato un passaggio a SQLcl che sembra un prodotto decisamente maturo e in grado di superare molti limiti di SQL*Plus anche se ho trovato qualche problemino con alcuni vecchi script. Premetto che il db su cui stavo facendo i test è un Oracle 12.1. A un certo punto noto una anomalia, il piano di esecuzione rimaneva lo stesso ma le statistiche, in particolare i “consistent gets” diminuivano molto dopo la prima esecuzione. A quel punto sono tornato sui miei passi ed ho utilizzato esplicitamente la cara vecchia “dbms_xplan.display_cursor” e mi sono accorto che in effetti la query aveva la particolarità di ottenere, grazie alle funzionalità di “adaptive query optimization”, un piano diverso alla seconda esecuzione.  Chiaramente il fatto di utilizzare un “explain plan” per calcolare il piano di esecuzione tagliava fuori questa casistica confondendomi. Per la verità quando mi sono accorto dell’inghippo stavo utilizzando SQLcl e quindi ho fatto una breve ricerca, imbattendomi in questo “twitt“; a questo punto ho fatto un test, anche se su un database su cui non c’erano gli stessi dati e la particolarità del cambio dati non si ripresenta. Ho abilitato il trace della sessione, abilitato il set autotrace, ho eseguito la query, fermato il trace e analizzato il trace. Dentro il trace ci ho trovato la solita “explain plan for” per cui ho chiesto informazioni commentando il twitt. Anche con l’ultima versione, 19.2 di SQLcl io registro lo stesso comportamento, quindi continuerò ad utilizzare la cara vecchia “dbms_xplan.display_cursor”

External Table Preprocessor e l’importanza di leggere bene

In questi giorni ho avuto la necessità di fare un sistema semplice di monitoraggio di un database server. Una delle cose che volevo verificare era lo spazio su disco e il server era con sistema operativo winzoz. Da una rapida ricerca su come fare un monitoraggio direttamente via SQL sono capitato sul solito AskTom con questa discusssione.  La fortuna mi aveva sorriso perché li c’erano due tecniche che mi permettevano di risolvere il problema in maniera brillante; la prima è quella di usare la direttiva “PREPROCESSOR” delle External Tables; la seconda era una script per powershell che estrae i dati sui dischi della macchina.  Mi sono gingillato un po’ con powershell, che è sì una strumento utile e potente ma continuo a pensare sia un po’ complesso per le normali esigenze; forse è solo perché io non sono un gran programmatore e non mi sono mai impegnato per studiare bene l’argomento.

Il punto però è che mi sono incagliato per un paio d’ore perché non riuscivo a far funzionare l’interrogazione sulla external table con la direttiva PREPROCESSOR. Ottenevo sempre un misterioso errore di permessi sul file di testo su cui la external table doveva accedere. Alla fine sono capitato sulla nota del supporto Oracle “Database External Tables Fails with KUP-04001 On Windows (Doc ID 2046669.1)”. La nota spiega come l’errore che io continuavo ad avere era causato da un baco della versione 12.1.0.2 su windows e come sia sufficiente aggiungere la direttiva “DISABLE_DIRECTORY_LINK_CHECK”. A quel punto mi è venuta voglia di sbattere la testa sul muro. In effetti sulla discussione di AskTom avevo visto quella strana direttiva, avevo però letto molto in fretta. Poi mi ero perso con l’adattamento dello script per powershell e così mi ero dimenticato di quella particolarità. Aggiunta la direttiva il trucco della external table che chiama lo script funziona benissimo. Se avessi letto con maggiore attenzione la discussione originale avrei risparmiato tempo.

Export datapump ORA-12899 con parametro VERSION

Facendo un export datapump  su un Oracle 12.1.0.2 con il parametro VERSION=11.2 ho avuto questo errore:

ORA-39126: Error fatal inesperado de worker en KUPW$WORKER.FIXUP_MASTER_TABLE_EXPORT [TABLE_DATA:”SCHEMANAME”.”SYS_EXPORT_SCHEMA_02″]
ORA-12899: el valor es demasiado grande para la columna “SYS”.”KU$_DATAPUMP_MASTER_11_2″.”PROCESSING_STATUS” (real: 3, maximo: 1)

Una rapida ricerca mi ha portato alla nota del supporto Oracle “ORA-12899 During Expdp With VERSION parameter In 12.1.0.2 (Doc ID 2051535.1)”

La nota riporta come il problema sia riconducibile a un bug identificato e per il quale è disponibile una patch. Il fattore scatenante pare siano le impostazioni internazionali, infatti nel caso riportato nella nota il linguaggio è tedesco. Nel mio caso è spagnolo ma l’errore è quasi identico. La cosa che mi ha lasciato veramente perplesso è che nella nota riportano tre possibili soluzioni:

  1. installazione della patch;
  2. upgrade a oracle 12.2;
  3. impostazione del parametro VERSION=12.1

Ora, avrei da ridire sulla 3. nel mio caso ho utilizzato il parametro version=11.2 non per vezzo ma per necessità di importare il dump su una versione 11.2

Il fatto però è che in altri casi non ho avuto questo problema; se la causa è il settaggio di NLS_LANG, ho pensato, proviamo a impostarla a un valore comune, quindi ho provato a impostare NLS_LANG=AMERICAN_AMERICA, ho rilanciato l’export e l’operazione è andata a buon fine. Non mi risulta che la cosa possa creare problemi sul dump e quindi mi sfugge come mai questa soluzione non venga proposta come “work-around”

 

JSON in Oracle: Introduzione

JSON è la sigla per JavaScript Object Notation. Si tratta di un formato per lo scambio dati molto semplice, basato su un sottoinsieme del linguaggio di programmazione JavaScript. E’ un formato facile da leggere e capire per gli umani e per le macchine. Oserei dire che in questo aspetto è un ottimo concorrente di XML. Qui viene riportato un confronto tra le due rappresentazioni: https://docs.oracle.com/en/database/oracle/oracle-database/18/adjsn/json-data.html#GUID-C347AC02-31E4-49CE-9F74-C7C0F339D68E.
L’idea che al momento mi sono fatto è che diventa un’ottima alternativa a XML in caso di dati con struttura molto semplice. La definizione del formato JSON è tanto semplice da stare in una paginetta abbastanza corta: https://www.json.org/
Negli anni JSON pare essere diventanto sempre più popolare e diffusamente usato, assieme ai cosiddetti database NoSQL. Questo formato è talmente diffuso che anche Oracle ne ha gradualmente introdotta la gestione sul suo database relazionale. L’introduzione è avvenuta con la versione 12cR1, penso come estensione delle funzionalità XML DB, o perlomeno ne ha aggiunto la documentazione al relativo manuale. (https://docs.oracle.com/database/121/ADXDB/json.htm#ADXDB6246). Un’ottima sintesi del manuale Oracle viene fornita da Tim Hall: https://oracle-base.com/articles/12c/json-support-in-oracle-database-12cr1

Apro una piccola parentesi con le impressioni e l’idea che mi sono fatto al momento, da novizio di JSON. Presumo che JSON si sia diffuso molto in contesti particolari, dove c’era l’esigenza di gestire dati con strutture molto semplici, assieme a questo formato mi sembra si sia diffuso l’uso di database NoSQL che hanno la capacità di gestire in modo più efficente questi dati e che in alcuni casi al costo di non soddisfare i requisiti di un database relazionali (ad esempio transazioni ACID) riescono ad avere migliori prestazioni e migliore “scalabilità”. Si tratta quindi di sistemi che sono in grado di gestire un carico crescente di lavoro senza andare in sofferenza. Ora, quello che si fa con JSON si può benissimo fare con un database relazionale, anzi, si tratta di implementare schemi molto semplici, per fare interrogazioni analoghe a quelle tipiche che si fanno su dati in formato JSON, su una analoga struttura relazionale bastano query SQL molto banali, non parliamo di join, raggruppamenti ecc. Il fatto di essere “schemaless” è un falso vantaggio o problema, anche in un database relazionale posso aggiungere a piacere colonne al volo a una tabella, senza problemi; è tutto nel come si scrive le applicazioni, se la mia applicazione è scritta come una volta si raccomandava, il fatto che in una tabella da un momento all’altro ci sia un campo in più non fa nessuna differenza. Ciò che un database come Oracle forse non può fare bene è gestire questi dati con prestazioni elavate, perché Oracle è appunto un database relazionale vero, ne rispetta tutte le regole, la visione dei dati è sempre consistente e in più Oracle ha una infrastruttura di monitoraggio e manutenzione. Per andare incontro al mercato e all’esigenza di interfacciarsi con il mondo anche Oracle si è adeguata; certo, trovo buffo dover fare su un database relazionale manovre per estrarre e manipolare dei dati quanto si possono usare dei semplici comandi sql, alla portata di qualunque programmatore degno di questo nome. E’ normale che Oracle abbia aggiunto il supporto a questo formato, per facilitare lo sviluppo di funzionalità di scambio dati con sistemi esterni senza dover forzare continue trasformazioni da un modello JSON a un modello relazionale e viceversa. Chiusa per ora la parentesi.

Alla base del modello dati JSON ci sono insiemi di coppie “chiave”-“valore”, dove la chiave è un’etichetta, il nome di una variabile, un identificativo a cui è associato appunto un valore. Il valore può essere un tipo base e in JSON sono previsti solo numeri, stringhe, valori booleani (true o false) o null. Altrimenti il valore può essere ricorsivamente un altro insieme di coppie chiave-valore oppure un array che si identifica usando come delimitatori dei suoi elementi le parentesi quadre. Un oggetto base, invece è delimitato dalle parentesi graffe. Quindi ecco un primo esempio di oggetto JSON:

{“nome” : “cristian”}

Se ci sono più coppie vengono separate da virgole:

{“nome” : “cristian”, “matricola” : 1234}

L’etichetta, come tutte le stringhe, anche quando sono valori, vanno racchiuse tra apici doppi. Ampliando e applicando ricorsivamente la struttura possiamo arrivare a un esempio più complesso (tratto dal manuale Oracle):

{“PONumber” : 1600,
“Reference” : “ABULL-20140421”,
“Requestor” : “Alexis Bull”,
“User” : “ABULL”,
“CostCenter” : “A50”,
“ShippingInstructions” : {…},
“Special Instructions” : null,
“AllowPartialShipment” : true,
“LineItems” : […]}

Per gestire il tipo dato “JSON” in Oracle non hanno introdotto un nuovo tipo dato, gli oggetti JSON possono essere messi in campi con tipo dato VARCHAR2, CLOB o BLOB. E’ stato introdotto un vincolo “IS JSON” applicabile alla colonna destinata a contenere i documenti JSON (https://docs.oracle.com/en/database/oracle/oracle-database/18/adjsn/json-in-oracle-database.html#GUID-F6282E67-CBDF-442E-946F-5F781BC14F33). Il vincolo fa si che oracle verifichi che all’interno della colonna venga inserito un documento di tipo JSON valido. Il vincolo può essere rafforzato con la specifica “STRICT” che fa rende il vincolo più rigoroso (https://docs.oracle.com/database/121/ADXDB/json.htm#GUID-951A61D5-EDC2-4E30-A20C-AE2AE7605C77)

Con questo concludo questo post introduttivo, ne seguiranno altri con esempi e approfondimenti.

Oracle Instant Client e SQL*Loader

Sarò breve, volevo solo condividere la soddisfazione che ho provato nel vedere che finalmente all'”instant client“, il client leggero e portatibile per database Oracle è stato aggiunto il pacchetto che include gli eseguibili per import export, vecchi e datapump ma soprattutto, quello che trovo più utile, è stato aggiunto l’eseguibile di SQL Loader, il programma per caricare file di testo su database. In passato confesso di aver fatto qualche tentativo di utilizzarlo senza dover installare il client completo, però non ci ero mai riuscito. Mentre trovo poco utile la disponibilità degli eseguibili exp,imp, expdp,impdp, ritengo che il caricamento di file CSV o simili dal proprio pc sia un’operazione che capita abbastanza di frequente. Viceversa devo ancora capire l’utilità degli eseguibili di export e import data pump che richiedono sempre che il file di dump si trovi sul database server su cui deve essere creato anche un oggetto “directory”.