Ieri sera sono andato a vedere Il Regno di Ga' Hoole in un cinema in provincia di Pavia e devo dire che il 3D è stato eccezionale!! Credo che la differenza la facciano gli occhiali "attivi" che ci hanno fornito all'ingresso : con gli occhiali passivi che avevo provato in altri cinema, la qualità 3D era nettamente inferiore e, a me, procuravano un leggero fastidio agli occhi mentre ,ad altri miei amici, dei noiosi mal di testa!
Comunque, oltre al 3D, il film presenta un'ottima animazione, paesaggi mozzafiato ed una buona trama : è un film che consiglio a tutti.
Ecco un link al trailer.
domenica 31 ottobre 2010
venerdì 29 ottobre 2010
Select * from C:\ ??? Ecco come farlo in Oracle
In questi giorni ho iniziato a sviluppare un nuovo processo che ha come database Oracle.
Prima di caricare alcuni file csv,l’utente deve avere la possibilità di vedere il contenuto di alcune directory non sempre visibili dalla macchina dell’utente quindi ho deciso che la soluzione ottimale fosse avere questo elenco all’interno di Oracle, ad esempio tramite una vista che interrogasse direttamente il filesystem. Ecco come fare tutto ciò :
CREATE OR REPLACE TYPE StringArray AS TABLE OF VARCHAR2(512);CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "FileHandler" AS
import java.io.*;
import java.sql.*;
import oracle.sql.*;
import oracle.jdbc.driver.*;
public class FileHandler {
public static oracle.sql.ARRAY list(String dirName) throws SQLException {
Connection conn = null;
int ret_code;
String[] files = null;
File file = new File(dirName);
if (file.exists()) {
if (file.isDirectory()) {
if (file.canRead()) {
files = file.list();
}
}
}
try {
conn = new OracleDriver().defaultConnection();
ArrayDescriptor x_ad = ArrayDescriptor.createDescriptor(
"STRINGARRAY", conn);
ARRAY x_array = new ARRAY(x_ad, conn, files);
return x_array;
} catch (SQLException e) {
ret_code = e.getErrorCode();
System.err.println(ret_code + e.getMessage());
conn.close();
return null;
} finally {
closeConnection(conn);
}
}private static void closeConnection(Connection connection) {
if (connection == null) {
return;
} else {
try {
connection.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
connection = null;
}}
};
/CREATE OR REPLACE FUNCTION list (p_path IN VARCHAR2) RETURN StringArray AS LANGUAGE JAVA NAME 'FileHandler.list (java.lang.String) return oracle.sql.ARRAY';EXEC DBMS_JAVA.grant_permission('SCHEMA-NAME', 'java.io.FilePermission', '<<ALL FILES>>', 'read ,write, execute, delete');
EXEC DBMS_JAVA.grant_permission('SCHEMA-NAME', 'SYS:java.lang.RuntimePermission', 'writeFileDescriptor', '');
EXEC DBMS_JAVA.grant_permission('SCHEMA-NAME', 'SYS:java.lang.RuntimePermission', 'readFileDescriptor', '');
GRANT JAVAUSERPRIV TO SCHEMA-NAME;
A questo punto sarà sufficiente fare una query come quella seguente per ottenere tutti i file e directory presenti sul disco C della macchina sulla quale c’è il database.
SELECT * FROM TABLE(LIST('C:\'))Naturalmente questo è solo un esempio che mostra la strada che ,secondo me, è meglio seguire; si potrebbero creare strutture più complesse che oltre al nome del file restituiscano la dimensione o la data dello stesso.
Lanciare un processo sincrono da Oracle tramite Java
Ecco una breve classe java che permette di lanciare un qualsiasi processo del sistema operativo da Oracle.
CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "FileHandler" AS
import java.io.*;
import java.sql.*;
import oracle.sql.*;
import oracle.jdbc.driver.*;
class StreamReader extends Thread {
InputStream inputStream;
String prefix;
StreamReader(InputStream inputStream, String prefix) {
this.inputStream = inputStream;
this.prefix = prefix;
}
public void run() {
try {
InputStreamReader inputStreamReader = new InputStreamReader(
inputStream);
BufferedReader bufferedReader = new BufferedReader(
inputStreamReader);
String line = null;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(prefix + " " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class StreamWriter extends Thread {
OutputStream outputStream;
String prefix;
StreamWriter(OutputStream outputStream) {
this.outputStream = outputStream;
}
public void run() {
try {
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
while (true) {
bufferedWriter.newLine();
}
} catch (IOException e) {
}
}
}
public class FileHandler {
public static int run (String command) {
Runtime runtime = Runtime.getRuntime();
Process process;
int exitValue = -1;
try {
process = runtime.exec(command);
StreamReader errorReader = new StreamReader(process.getErrorStream(), "<<ERROR>>");
StreamReader outputReader = new StreamReader(process.getInputStream(), "<<OUTPUT>>");
StreamWriter inputWriter = new StreamWriter(process.getOutputStream());
errorReader.start();
outputReader.start();
inputWriter.start();
exitValue = process.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return exitValue;
}
};La classe StreamReader serve per poter catturare lo standard output e lo standard error del processo lanciato e loggarli : in questo caso verranno scitti all’interno di trace file nella directory user dump di Oracle ma nulla ci vieta di inserire il tutto in una tabella per una consultazione più rapida. La classe StreamWriter invece impedisce che il processo si blocchi nel caso quest’ultimo sia in attesa sullo standard input. Nel mio caso ero solo interessato al fatto che il processo non si bloccasse quindi non faccio altro che simulare un invio da tastiera, però si potrebbero inviare delle informazioni tramite lo standard input se il processo lo richiedesse.
Il metodo run all’interno della classe FileHandler non fa altro che lanciare il processo e restituire il valore ritornato dal processo, solitamente 0 significa ‘terminato con successo’ e tutto il resto significa che si sono verificati degli errori.
Adesso creiamo una stored procedure che punti a questo metodo :
create or replace FUNCTION OSCommand_Run(Command IN STRING) RETURN NUMBER IS LANGUAGE JAVA NAME 'FileHandler.run(java.lang.String) return int';Diamo i permessi necessari
EXEC DBMS_JAVA.grant_permission('SCHEMA-NAME', 'java.io.FilePermission', '<<ALL FILES>>', 'read ,write, execute, delete');
EXEC DBMS_JAVA.grant_permission(’SCHEMA-NAME’, ‘SYS:java.lang.RuntimePermission’, ‘writeFileDescriptor’, ”);
EXEC DBMS_JAVA.grant_permission(’SCHEMA-NAME’, ‘SYS:java.lang.RuntimePermission’, ‘readFileDescriptor’, ”);A questo punto possiamo lanciare e monitorare un qualsiasi processo del sistema operativo da una stored procedure o package.
Linux : ruotare un video al volo
Ecco come ruotare un video in pochi secondi con mencoder :
mencoder -oac copy -ovc lavc -lavcopts vcodec=mjpeg -vf rotate=1 input.avi -o output.avi
Il parametro rotate puo assumere altri valori :
0 : ruota di 90° in senso orario e gira
1 : ruota di 90° in senso orario
2 : ruote di 90° in senso antiorario
3 : ruote di 90° in senso antiorario e gira
0 : ruota di 90° in senso orario e gira
1 : ruota di 90° in senso orario
2 : ruote di 90° in senso antiorario
3 : ruote di 90° in senso antiorario e gira
Analogamente valori dal 4 al 7 vengono utilizzati per quei video con geometria “ritratto” e non “paesaggio”.
Oracle : allocare/deallocare i CLOB
In una piattaforma che ho recentemente sviluppato in Oracle (PL/SQL) faccio un uso frequente di variabili CLOB che vanno correttamente deallocate soprattutto se utilizzate all'interno di un ciclo. Quindi mi sono creato due semplici procedure per creare e deallocare un CLOB.
PROCEDURE pr_Open_CLOB(pc_Clob IN OUT NOCOPY CLOB) AS
BEGIN
pc_Clob:=EMPTY_CLOB;
DBMS_LOB.CREATETEMPORARY(pc_Clob, TRUE);
DBMS_LOB.OPEN(pc_Clob, DBMS_LOB.LOB_READWRITE);
END pr_Open_CLOB;
PROCEDURE pr_Close_CLOB(pc_Clob IN OUT NOCOPY CLOB) AS
BEGIN
IF DBMS_LOB.ISOPEN(pc_Clob)=1 THEN
DBMS_LOB.CLOSE(pc_Clob);
END IF;
IF DBMS_LOB.ISTEMPORARY(pc_Clob)=1 THEN
DBMS_LOB.FREETEMPORARY(pc_Clob);
END IF;
END pr_Close_CLOB;
Ecco un esempietto su come utilizzarle :
PROCEDURE pr_Test AS
c_Temp CLOB;
BEGIN
pr_Open_Clob(c_Temp);
--use clob
pr_Close_Clob(c_Temp);
EXCEPTION
WHEN OTHERS THEN
pr_Close_Clob(c_Temp);
END pr_Test;
Utilizzo delle collezioni in Oracle-PL/SQL
Le collezioni di Oracle semplificano sicuramente la programmazione in PL/SQL e sono in grado di velocizzare notevolmente alcuni processi.
Mostrerò come utilizzarle e includerò qualche esempio di impieghi particolari.
Innanzitutto creiamo il tipo
Create or replace type t_varchar2_table as table of varchar2(4000);Operazioni base :
Create or replace procedure pr_base_test AS
--inizializzo la lista con zero elementi
x_Varchar2_Table t_varchar2_table := t_varchar2_table();
n_Step NUMBER;
BEGIN
--mettiamo 10 elementi nella lista
FOR n_Step IN 1..10 LOOP
x_Varchar2_Table.EXTEND;
x_Varchar2_Table(x_Varchar2_Table.LAST) := 'Row '||n_Step;
END LOOP;
--visualizzo quanti elementi ho nella lista
Dbms_Output.put_Line('Count = '||x_Varchar2_Table.COUNT);
--visualizzo ogni elemento in lista
FOR n_Step IN x_Varchar2_Table.FIRST..x_Varchar2_Table.LAST LOOP
Dbms_Output.put_Line(x_Varchar2_Table(n_Step);
END LOOP;
--controllo se un elemento è presente nella lista
IF 'Row 7' MEMBER OF x_Varchar2_Table THEN
Dbms_output.put_Line('OK');
END IF;
--Controllo se la lista è univoca, ovvero non ho duplicati
IF x_Varchar2_Table IS A SET THEN
Dbms_output.put_Line('IS A SET);
END IF;
--svuoto la lista
x_Varchar2_Table.DELETE;
END;
/A partire da Oracle 10g sono presenti una serie di operatori multiset che permettono operazioni sulle liste. Eccone una lista con accanto l’equivalente operatore SQL :
| MULTISET UNION DISTINCT | UNION |
| MULTISET UNION | UNION ALL |
| MULTISET UNION ALL | UNION ALL |
| MULTISET INTERSECT | INTERSECT |
| MULTISET INTERSECT DISTINCT | N/A |
| MULTISET EXCEPT | MINUS |
| MULTISET EXCEPT DISTINCT | N/A |
Per riempire una lista con dei valori provenienti da una query è semplicissimo :
declare
x_table_list t_varchar2_table := t_varchar2_table();
begin
select table_name BULK COLLECT INTO x_table_list
from user_tables
where table_name like 'A%';
--la lista ora contiene l'elenco delle tabelle che iniziano con A
end;Naturalmente non bisogna abusare dell’uso delle liste in quanto consumano parecchia memoria. Gli utilizzi principali che ho riscontrato sono :
- creo una lista di dati che poi andrò ad utilizzare parecchie volte : è inutile fare la join con la medesima tabella (magari di grandi dimensioni) molte volte quando è possibile caricare in memoria la lista di ID di cui necessito e unitilizzarla n volte.
- sono molto utili per trasferire dati da una procedura all’altra : ad esempio, se ho un package che mi gestisce gli utenti e all’interno di questo una procedure che mi restituisce la lista degli utenti è inutile implementare tale logica in un altro package. Quindi il package che avrà bisogno dell’elenco degli utenti invocherà tale procedura e ne utilizzerà la lista ottenuta.
- Ottimizzazione di insert/update/delete di tante righe grazie all’operatore FORALL
Ecco alcuni esempi :
Esempio 1
declare
x_table_list t_varchar2_table := t_varchar2_table();
n_count Number;
begin
select table_name bulk collect into x_table_list
from user_tables
where table_name like 'A%';
--use the list as a table
select count(*) into n_count
from user_tab_columns a, TABLE(x_table_list) b
where a.table_name = b.column_value;
dbms_output.put_line(n_count);
end;Esempio 2
create table temp
as select * from user_tables
where 1 = 2;declare
type t_user_tables is table of user_tables%ROWTYPE;
x_user_tables t_user_tables := t_user_tables();
n Number;
begin
select * bulk collect into x_user_tables
from user_tables;
FORALL n IN x_user_tables.FIRST..x_user_tables.LAST
Insert into temp values x_user_tables(n);
COMMIT;
End;E se invece avessi bisogno di utilizzare una collezione all’interno di una query dinamica ?? In questo caso è obbligatorio utilizzare collezioni basate su tipi creati all’interno dello schema tramite il comando Create Type. Nell’esempio utlizzerò il tipo
t_varchar2_table creato all’inizio del post.
create or replace PACKAGE pk_Test AS
PROCEDURE pr_Load;
PROCEDURE pr_ExecuteQuery;FUNCTION fn_List RETURN T_Varchar2_Table;
END pk_Test;
create or replace PACKAGE BODY pk_Test AS
kx_List T_Varchar2_Table := T_Varchar2_Table();
kx_List T_Varchar2_Table := T_Varchar2_Table();
PROCEDURE pr_Load IS
BEGIN
SELECT Table_Name BULK COLLECT INTO kx_List
FROM User_Tables
WHERE Table_Name LIKE ‘A%’;
BEGIN
SELECT Table_Name BULK COLLECT INTO kx_List
FROM User_Tables
WHERE Table_Name LIKE ‘A%’;
END pr_Test;
PROCEDURE pr_ExecuteQuery IS
v_Sql VARCHAR2(1000);
n_Count NUMBER;
BEGIN
v_Sql := ‘SELECT COUNT(*)
FROM User_Tables
WHERE Table_Name IN (
SELECT Column_Value FROM
TABLE ( pk_Test.fn_List )
)’;
v_Sql VARCHAR2(1000);
n_Count NUMBER;
BEGIN
v_Sql := ‘SELECT COUNT(*)
FROM User_Tables
WHERE Table_Name IN (
SELECT Column_Value FROM
TABLE ( pk_Test.fn_List )
)’;
EXECUTE IMMEDIATE v_Sql INTO n_Count;
Dbms_Output.put_line(n_Count);
END pr_ExecuteQuery;
Dbms_Output.put_line(n_Count);
END pr_ExecuteQuery;
FUNCTION fn_List RETURN T_Varchar2_Table IS
BEGIN
Return kx_List;
END fn_List;
BEGIN
Return kx_List;
END fn_List;
END pk_Test;
Nella procedura pr_Load non faccio altro che caricare in una collezione tutte le tabelle che iniziano con A.
Nella procedura pr_ExecuteQuery eseguo una query dinamica tramite il comando Execute Immediate utilizzando la lista appena caricata che mi viene restituita dalla funzione fn_List.
/bin/rm : Argument list too long
Se incappate in questo errore ecco alcune soluzioni :
1.
for x in *.trc
do
rm $x
done2.
find . -name '*.txt' -print0 | xargs -0 rm3.
find . -name '*.txt' -exec rm {} \; -print4.
find . -name '*.txt' -delete (presente dalla versione 4.2 di find)Riavvio pulito a sistema bloccato
Da quando uso linux, devo ammettere che mi è capitato raramente che il sistema non rispondesse completamente.
Se però dovesse capitarvi che anche la combinazione Ctrl-Alt-Del non riavvii la macchina allora potrete ricorrere alla seguente combinazione per evitare il tasto reset : tenere premuti Alt(destro) e SysRq e digitare R E I S U B
Ecco l’azione associata ad ogni lettera :
R : mette la tastiera in modalità XLATE
E : invia SIGTERM a tutti i processi tranne init
I : invia SIGKILL a tutti i processi tranne init
S : sincronizza tutti i filesystem montati
U : rimonta tutti i filesystem in sola lettura
B : riavvio immediato del sistema senza smontare i dischi
Perchè questa procedura funzioni bisogna assicurarsi che /proc/sys/kernel/sysrq contenga 1
Oracle : ricomprimere una sottopartizione
Recentemente mi è capitato di dover aggiornare dei dati in una tabella compressa sottopartizionata e di conseguenza la compressione è andata persa (la sottopartizione è passata da 950MB a 2.7 GB).
Ecco la procedura per ricomprimere la sottopartizione e ricostruire eventuali indici :
Ecco la procedura per ricomprimere la sottopartizione e ricostruire eventuali indici :
Alter table MYTABLE move subpartition P1_SP1 parallel 4 update global indexes Alter table MYTABLE modify subpartition P1_SP1 rebuild unusable local indexesCon il primo statement comprimiamo la sottopartizione e ricreiamo eventuali indici globali, col secondo ricreiamo eventuali indici locali.
Oracle 10.2.0.4 – 10.2.0.5 : DML parallele all’interno dello scheduler
l fine di velocizzare alcuni processi che girano di notte su un db Oracle ( 10.2.0.4) , ho deciso di eseguire alcune DML in parallelo. Sostanzialmente , le modifiche che apporto sono due :
- abilitare le DML parallele :
Alter session enable parallel DML; - Aggiungere l’hint alla query :
INSERT /*+ PARALLEL(a, 4) */ INTO mytable a ....
Prima di tutto eseguo la procedure da sqlplus e vedo che funziona tutto alla perfezione ma non appena viene inserita all’interno dello scheduler, la procedura va a buon fine ma senza alcun parallelismo e quindi senza alcuna riduzione dei tempi rispetto a quella originale.
Cerco il problema su google e scopro che facendo l’upgrade alla versione 10.2.0.5 di Oracle, si ha la possibilità di eseguire DML parallele anche ll’interno di “Transazioni autonome” e quindi anche all’interno dello scheduler.
Eseguo l’upgrade e riprovo ma senza successo. Facendo altri test, capisco che il problema è dato dal fatto che chiamo la procedura tramite Execute immediate.
Cerco il problema su google e scopro che facendo l’upgrade alla versione 10.2.0.5 di Oracle, si ha la possibilità di eseguire DML parallele anche ll’interno di “Transazioni autonome” e quindi anche all’interno dello scheduler.
Eseguo l’upgrade e riprovo ma senza successo. Facendo altri test, capisco che il problema è dato dal fatto che chiamo la procedura tramite Execute immediate.
Per riassumere : se uno procedura invocata dallo scheduler chiama un’altra procedura tramite il comando Execute Immediate, le query/dml parallele all’interno di quest’ultima procedura non verranno eseguite in parallelo. Se invece si chiamasse direttamente la procedura senza l’uso del comando execute immediate, tutto funzionerebbe come ci si aspetta.
Upgrade a Oracle 11.2
Come prima cosa ecco il link alla documentazione oracle per effettuare l’upgrade.
La documentazione mi sembra chiara e decido di aggiornare la mia istanza 10.2.0.2. Lo script di pre-upgrade non dà alcun errore, quindi procedo con
La mia conclusione è: lo script di pre-upgrade è veramente inutile se non è nemmeno in grado di capire se la versione di partenza sia direttamente aggiornabile alla 11.2 !! Questa volta Oracle mi ha veramente deluso: non lo cambierei mai come database ma a volte si perde in delle piccolezze molto fastidiose.
La documentazione mi sembra chiara e decido di aggiornare la mia istanza 10.2.0.2. Lo script di pre-upgrade non dà alcun errore, quindi procedo con
Startup upgrade e lancio lo script catupgrd.sql. Quest’ultimo termina con un errore che ora non ricordo ma che trovo in internet : in parole povere non è possibile aggiornare la mia versione direttamente alla 11.2 ma prima devo applicare il patchset 10.2.0.5. Ripristino i datafile, applico la patchset e rifaccio l’upgrade che adesso termina con successo.La mia conclusione è: lo script di pre-upgrade è veramente inutile se non è nemmeno in grado di capire se la versione di partenza sia direttamente aggiornabile alla 11.2 !! Questa volta Oracle mi ha veramente deluso: non lo cambierei mai come database ma a volte si perde in delle piccolezze molto fastidiose.
Iscriviti a:
Post (Atom)