Rechercher sur arkzoyd.com

25 janvier 2009

L'Inquiétante Étrangeté d'Oracle Cost Based Optimizer

Je croyais avoir déjà illustré cet exemple dans ce blog; il semble que non! Alors voici un cas où le plan choisi par l'optimiser Oracle n'est pas celui avec le coût le plus faible; Si vous voulez savoir pourquoi, vous pouvez regarder la trace 10053. C'est au delà du sujet de ce post. Ce que vous devez retenir c'est que tout ne fonctionne pas toujours comme attendu.

Pour commencer vous allez créer la table exemple pour notre exemple:
create table demo(id number, text varchar2(4000));

begin
for i in 1..5000 loop
insert into demo values (i, rpad('X',1000,'X'));
end loop;
end;
/

create index demo_idx on demo(id) reverse;

exec dbms_stats.gather_table_stats(user,'DEMO');
Ensuite, vous pouvez regarder le coût et les statistiques du plan qui utilise l'index crée sur la table.
set autotrace on

select /*+ index(demo demo_idx) */ count(text)
from demo
where id between 1 and 2;

COUNT(TEXT)
-----------
2

Execution Plan
----------------------------------------------------------
Plan hash value: 3190157379

---------------------------------------------------------
| Id | Operation | Name | Cost |
---------------------------------------------------------
| 0 | SELECT STATEMENT | | 26 |
| 1 | SORT AGGREGATE | | |
| 2 | TABLE ACCESS BY INDEX ROWID| DEMO | 26 |
|* 3 | INDEX FULL SCAN | DEMO_IDX | 13 |
---------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("ID"<=2 AND "ID">=1)


Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
13 consistent gets
0 physical reads
0 redo size
421 bytes sent via SQL*Net to client
416 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
Maintenant si vous regardez le plan et les statistiques de la requête sans le hint, vous remarquerez que le résultat est différent de celui auquel on s'attend a priori:
set autotrace on

select count(text)
from demo
where id between 1 and 2;


COUNT(TEXT)
-----------
2

Execution Plan
---------------------------
Plan hash value: 2180342005

--------------------------------------------------
| Id | Operation | Name | Rows | Cost |
--------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 206 |
| 1 | SORT AGGREGATE | | 1 | |
|* 2 | TABLE ACCESS FULL| DEMO | 2 | 206 |
--------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("ID"<=2 AND "ID">=1)


Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
753 consistent gets
0 physical reads
0 redo size
421 bytes sent via SQL*Net to client
416 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
Bon, vous me direz, il s'agit d'un cas très particulier! Mais au final le métier du DBA n'est-il pas un peu de gérer les cas particuliers? Sinon, on aurait plus besoin de nous!

Et si vous voulez en voir un autre de ces cas particuliers, avec l'explication, cette fois, regardez le dernier post d'Alex.

24 janvier 2009

BTRFS, FileSystem pour Linux par Oracle, et Ubuntu Hardy-Heron

BTRFS sans doute un des "next big things" de Linux. Le système de fichiers développé initialement par Chris Mason (cf podcast) d'Oracle (http://oss.oracle.com/projects/btrfs) est maintenant Mainline dans le noyau Linux et sera disponible à partir de 2.6.29, encore RC2 aujourd'hui. Il fait déjà des petits puisque Coherent Remote FileSystem s'appuie sur BTRFS. Pour tout savoir, connectez-vous sur le site associé : http://btrfs.wiki.kernel.org.

Parmi ces nombreuses fonctionnalités, les snapshots permettent de créer différentes versions du même systèmes de fichiers, en lecture ET écriture; Si vous connaissez les technologies de Netapp, ces snapshots sont un peu comme autant de FlexClone, sur un système de fichier Open-Source. En substance: "BTRFS rocks!"

Il reste sans doute encore du chemin pour que BTRFS remplace EXT3 ou EXT4. C'est d'autant plus vrai que EXT4 est tout juste stable depuis 2.6.28 qui ne sera disponible qu'avec Ubuntu Jaunty Jackalope... Enfin, si vous voulez un avant goût de BTRFS, vous pouvez l'installer sur les versions précédentes, comme Hardy-Heron ; voici comment!

1. Installer BTRFS
Note:
Les versions au delà de 0.16 ne sont pas, pour l'instant, utilisables avec les noyaux autre que 2.6.29.
Installer BTRFS est plutôt simple d'autant que le noyau de Hardy Heron est normalement compilé avec CONFIG_LIBCRC32C=m. Pour commencer, installer uuid-dev comme ci-dessous:
apt-get install uuid-dev
Ensuite téléchargez BTRFS à l'URL qui suit, compilez-le et installez le btrfs.ko comme ci-dessous:
mkdir btrfs
cd btrfs

wget http://www.kernel.org/pub/linux/kernel/people/mason/btrfs/btrfs-0.16.tar.bz2
tar -jxvf btrfs-0.16.tar.bz2
cd btrfs-0.16
make
sudo make modules_install
Vous pouvez le charger avec les modules du noyau:
modprobe libcrc32c
modprobe zlib_deflate
insmod btrfs.ko

lsmod |grep btrfs
Ensuite, installez les outils nécessaires pour utiliser BTRFS :
wget http://www.kernel.org/pub/linux/kernel/people/mason/btrfs/btrfs-progs-0.16.tar.gz
tar -jxvf btrfs-progs-0.16.tar.gz
btrfs-progs-0.16
make
sudo make install
2. Créer un système de fichiers

Si vous n'avez pas de volumes pour créer un système de fichiers btrfs, vous pouvez créer un fichier et le monter sur un device comme loop0 avec losetup :
cd /
mkdir /disks
cd /disks
dd if=/dev/zero of=/disks/disk.btrfs bs=1024k count=512

losetup /dev/loop0 /disks/disk.btrfs
losetup -a
Une fois que vous avez un device, vous pouvez le formater pour btrfs et monter le système de fichier comme ci-dessous; Cette première section permet d'effacer le volume si vous vous en êtes déjà servi:
cd /
umount /btrfs
dd if=/dev/zero of=/dev/loop0 bs=1024k count=512
Pour formater le système de fichier et le monter, voici les commandes à utiliser:
mkfs -t btrfs /dev/loop0

mkdir /btrfs
mount -t btrfs -o compress,subvol=. /dev/loop0 /btrfs

df -h
Vous noterez que dans le cas de BTRFS, les snapshots sont publiés sous le forme de sous-volumes; l'option subvol=. indique donc de monter tous les snapshots sur le même point de montage. On peut également choisir de monter les snapshots séparément.

3. Illustrer l'utilisation des snapshots

Le snapshot default est le sous-volume par défaut; une fois le volume monté, vous pouvez écrire dans ce sous-volume:
cd /btrfs

find
.
./default

echo 1 > default/1

find
.
./default
./default/1
Vous pouvez créer un premier snapshot sur le sous-volume par défaut; vous constaterez que le snapshot est accessible en lecture et écriture:
btrfsctl -s snap1 /btrfs/default

find
.
./default
./default/1
./snap1
./snap1/1

echo 2 > default/2

echo 3 > snap1/3

find
.
./default
./default/1
./default/2
./snap1
./snap1/1
./snap1/3
Vous pouvez également créer un snapshot à partir d'un autre snapshot comme ci-dessous:
btrfsctl -s snap2 /btrfs/snap1

find
.
./default
./default/1
./default/2
./snap1
./snap1/1
./snap1/3
./snap2
./snap2/1
./snap2/3

rm default/1

echo 4 > snap2/4

find
.
./snap2
./snap2/1
./snap2/3
./snap2/4
./default
./default/2
./snap1
./snap1/1
./snap1/3
Je vous laisse imaginer ce qu'on peut faire avec ce genre de fonctionnalités et une base de données. Et imaginez que vous puissiez y accéder par un réseau; depuis des machines virtuelles. Et si (quand ?) BTRFS était plus performant que les systèmes de fichiers actuels?

19 janvier 2009

"Insert /*+Append */ Values", épisode 3 : PL/SQL

Vous pouvez aussi remplacer l'étape 2 du post précédent par un code en PL/SQL comme celui qui suit:
connect / as sysdba

grant select on sys.v_$mystat
to demo

connect demo/demo

set serveroutput on
declare
id dbms_utility.number_array;
text dbms_utility.lname_array;
dpw number;
begin
-- Get Direct Path Write Statistic
select value into dpw
from v$mystat
where statistic# = 73;
-- Fill Arrays of Variables
for i in 1..10000 loop
id(i):=i;
text(i):=to_char(i);
end loop;
-- Insert the Arrays With Direct
-- Path Write, The Append Hint
-- and the Values clause
forall i in 1..10000
insert /*+ append */
into insertappend(id,text)
values (id(i),text(i));
-- Get the Session Direct Path Write
-- Statistic in
select value-dpw
into dpw
from v$mystat
where statistic# = 73;
dbms_output.put_line('Direct Path Write: '||
to_char(dpw));
end;
/
rollback;

Direct Path Write: 20
A noter que la clause SAVE EXCEPTIONS ne fonctionne pas dans ce cas:
set serveroutput on
declare
id dbms_utility.number_array;
text dbms_utility.lname_array;
dpw number;
begin
-- Get Direct Path Write Statistic
select value into dpw
from v$mystat
where statistic# = 73;
-- Fill Arrays of Variables
for i in 1..10000 loop
id(i):=i;
text(i):=to_char(i);
end loop;
-- Insert the Arrays With Direct
-- Path Write, The Append Hint
-- and the Values clause
forall i in 1..10000
save exceptions
insert /*+ append */
into insertappend(id,text)
values (id(i),text(i));
-- Get the Session Direct Path Write
-- Statistic in
select value-dpw
into dpw
from v$mystat
where statistic# = 73;
dbms_output.put_line('Direct Path Write: '||
to_char(dpw));
end;
/
ERROR at line 1:
ORA-38910: BATCH ERROR mode is not supported for this operation
ORA-06512: at line 18
Merci Ghassan pour la syntaxe!

"Insert /*+Append */ Values", épisode 2 : le Pro*C

Pour faire suite au post précédent à propos du changement de comportement, non documenté de la commande "Insert /*+Append */ Values" en 11.1, quelqu'un, qui se reconnaitra, a pointé qu'il est possible d'effectuer des INSERT par tableau en Pro*C comme documenté dans le Pro*C/C++ Developer's Guide. Ça expliquerait donc l'intérêt de cette nouvelle fonctionnalités de 11g !

Dans l'exemple qui suit, nous allons donc combiner Pro*C et "Insert /*+ Append */ Values" par tableau sur Linux.
Note:
Compiler un programme en Pro*C est différent d'une plate-forme à une autre. Dans cette exemple, nous avons utiliser une version 11.1.0.7 sur Linux x86. La distribution que j'utilise est Ubuntu 8.04 Hardy Heron
1. Créez une table dans votre base de données :

Nous allons commencer par créer une table INSERTAPPEND dans un schéma DEMO pour notre test. Le script SQL*Plus ci-dessous illustre cette opération:
sqlplus / as sysdba

create user demo
identified by demo
default tablespace users
temporary tablespace temp;

grant connect, resource to demo;

connect demo/demo

create table insertappend(id number,
text varchar2(100)) ;

exit;
2. Écrivez un Programme en Pro*C pour le test

Je passerai sur les détails de l'écriture d'un programme en Pro*C; Pour faire simple ajoutez le SQL qui convient dans votre programme en C. Voici un exemple qui fonctionne; ce programme s'appelle insertappend.pc :
$ cat insertappend.pc
#include <stdio.h>
#include <string.h>
#include <sqlca.h>

/* Define constants for VARCHAR lengths. */
#define UNAME_LEN 20
#define PWD_LEN 40
#define ARRAY_LEN 50
#define TEXT_LEN 100

/* Declare variables */
VARCHAR username[UNAME_LEN];
VARCHAR password[PWD_LEN];
float id[ARRAY_LEN];
char text[ARRAY_LEN][TEXT_LEN];

/* Error handling function. */
void sql_error();

main()
{
/*****************************************************
* Connect to ORACLE--
****************************************************/

/* Copy the username into the VARCHAR. */
strncpy((char *) username.arr, "demo", UNAME_LEN);
username.len = strlen((char *) username.arr);
/* Copy the password. */
strncpy((char *) password.arr, "demo", PWD_LEN);
password.len = strlen((char *) password.arr);
/* Register sql_error() as the error handler. */
EXEC SQL WHENEVER SQLERROR DO
sql_error("ORACLE error--\n");
/* Connect */
EXEC SQL CONNECT :username IDENTIFIED BY :password;
printf("\nConnected to ORACLE as user: %s\n",
username.arr);

/*****************************************************
* Create a C Table to insert in the sample table
****************************************************/
int i;
for (i=0; i<ARRAY_LEN; i++) {
id[i]= (float) i;
strncpy((char *) text[i], "Text", 4);
}

/*****************************************************
* Perform an Insert by Array
****************************************************/
printf("Starting Insert Append...\n");

EXEC SQL INSERT /*+ APPEND */
INTO INSERTAPPEND (ID, TEXT)
VALUES (:id, :text);

printf("Insert Append Done...\n");

/*****************************************************
* Disconnect from Oracke
****************************************************/
EXEC SQL COMMIT WORK RELEASE;
exit(0);
}

void sql_error(msg)
char *msg;
{
char err_msg[128];
int buf_len, msg_len;

EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\n%s\n", msg);
buf_len = sizeof (err_msg);
sqlglm(err_msg, &buf_len, &msg_len);
if (msg_len > buf_len)
msg_len = buf_len;
printf("%.*s\n", msg_len, err_msg);
EXEC SQL ROLLBACK RELEASE;
exit(1);
}
3. Compilez votre programme.

Sur Linux, cette opération est assez simple; Utilisez le programme "proc" dans ORACLE_HOME/bin pour pre-compiler le programme du Pro*C en C; commencez par modifier si nécessaire le fichier de configuration du Pro*C dans $ORACLE_HOME/precomp/admin/pcscfg.cfg. Dans le cas de Ubuntu Hardy-Heron, le fichier ressemble à ce qui suit:
$cat $ORACLE_HOME/precomp/admin/pcscfg.cfg
sys_include=(/u01/app/oracle/product/11.1.0/db_1/precomp/public,
/usr/include,/usr/lib/gcc/i486-linux-gnu/4.2/include)
ltype=short
Une fois le fichier pcscfg.cfg à jour, utilisez le pré-compilateur puis le compilateur comme ceci:
proc DYNAMIC=ANSI iname=insertappend.pc

Pro*C/C++: Release 11.1.0.7.0 - Production on Mon Jan 19 15:09:51 2009

Copyright (c) 1982, 2007, Oracle. All rights reserved.

System default option values taken from:
/u01/app/oracle/product/11.1.0/db_1/precomp/admin/pcscfg.cfg

gcc -I$ORACLE_HOME/precomp/public \
-L$ORACLE_HOME/lib -lclntsh \
insertappend.c -o insertappend

insertappend.c: In function ‘main’:
insertappend.c:325: warning: incompatible implicit declaration
of built-in function ‘exit’
insertappend.c: In function ‘sql_error’:
insertappend.c:360: warning: incompatible implicit declaration
of built-in function ‘exit’
4. De nouveaux Tests:

Nous voila prêt à refaire nos tests avec cette fois un INSERT /*+APPEND */ VALUES par tableau. Pour cela, lancez simplement le programme insertappend que vous avez créé a l'etape précédente. Vous pouvez vérifier que, comme dans le post précédent, ce type d'Insert effectue un "direct-path write", ce qui apparaît dans ce contexte comme une fonctionnalité plutot cool!

5. Nettoyez

Comme d'habitude, supprimez table et schéma de la démonstration.
sqlplus / as sysdba

drop user demo cascade;

exit;

17 janvier 2009

11.1 et "Insert /*+ APPEND */ Values"

Alex, (encore lui!) pointe sur son blog un étonnant changement entre 10.2 et 11.1; INSERT /*+ APPEND */ VALUES fait désormais un INSERT /*+ APPEND */ ;-). Autrement dit, testez en 10g, dans une première session, exécutez :
SQL> create table g1 (id number);

Table created.

SQL> insert into g1 values (1);

1 row created.
Laissez la transaction en cours et dans une seconde session, lancez
SQL> insert /*+append*/ into g1 values (2);
En 10g, le prompt est retourné immédiatement; en 11g... on attend! La raison, ce type d'insert pose un lock exclusif sur la table. Pour vous en persuader, exécutez :
SQL>select sid,
type,
id1,
lmode,
request
from v$lock
where lmode!=request
and type='TM';

SID TY ID1 LMODE REQUEST
--- -- --------- ---------- ---------
155 TM 74741 0 6
170 TM 74741 3 0
Il est facile de se rendre compte de ce qu'il se passe, de ce qu'il faut et surtout de ce qu'il ne pas faire en 11g. Cependant, les vraies questions sont "pourquoi changer cette fonctionnalité ?" et "pourquoi maintenant ?". Une idée ?

16 janvier 2009

La subtilité de RMAN contre la conviction de SQL*Plus

Comme tous le monde le sait, RMAN est beaucoup plus intelligent que SQL*Plus... Et bien l'exemple ci-dessous, montre qu'on a pas toujours intérêt à être trop intelligent. Vous ferez sans doute rapidement le lien avec une situation réelle mais en gros, la séquence de commandes ne restore qu'un sous-ensemble d'une base de données tente d'en faire le recover après avoir mis le datafile 5 offline:
RMAN> restore datafile 1,2,3,4;

Starting restore at 16-JAN-09
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=154 device type=DISK

channel ORA_DISK_1: starting datafile backup set restore
channel ORA_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_DISK_1: restoring datafile 00001 to /u01/app/oracle/oradata/BLACK/system01.dbf
channel ORA_DISK_1: restoring datafile 00002 to /u01/app/oracle/oradata/BLACK/sysaux01.dbf
channel ORA_DISK_1: restoring datafile 00003 to /u01/app/oracle/oradata/BLACK/undotbs01.dbf
channel ORA_DISK_1: restoring datafile 00004 to /u01/app/oracle/oradata/BLACK/users01.dbf
channel ORA_DISK_1: reading from backup piece /u01/app/oracle/backup/BLACK/7hk4utqi_1_1

channel ORA_DISK_1: piece handle=/u01/app/oracle/backup/BLACK/7hk4utqi_1_1 tag=TAG20090116T120849
channel ORA_DISK_1: restored backup piece 1
channel ORA_DISK_1: restore complete, elapsed time: 00:02:55
Finished restore at 16-JAN-09

RMAN> sql 'alter database datafile 5 offline drop';
sql statement: alter database datafile 5 offline drop

RMAN> recover database;

Starting recover at 16-JAN-09
using channel ORA_DISK_1
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of recover command at 01/16/2009 12:25:18
RMAN-06094: datafile 5 must be restored
Et c'est ici que RMAN est peut-être trop intelligent ou moi trop bête pour ne pas trouver la correcte syntaxe, choisissez ! Toujours est-il que la seule façon de faire le recover que j'ai trouvée pour l'instant dans la doc, c'est RECOVER DATABASE SKIP TABLESPACE qui oblige à lister tous les tablespaces que vous ne voulez pas restaurer. D'un autre côté, si vous utilisez SQL*Plus à partir de cette étape:
SQL> recover database until cancel;
ORA-00279: change 49042462037 generated at 01/16/2009 12:08:50 needed for
thread 1
ORA-00289: suggestion : /u01/app/oracle/oradata/BLACK/1_426_666110942.dbf
ORA-00280: change 49042462037 for thread 1 is in sequence #426


Specify log: {=suggested | filename | AUTO | CANCEL}

ORA-00279: change 49042462166 generated at 01/16/2009 12:12:05 needed for
thread 1
ORA-00289: suggestion : /u01/app/oracle/oradata/BLACK/1_427_666110942.dbf
ORA-00280: change 49042462166 for thread 1 is in sequence #427
ORA-00278: log file '/u01/app/oracle/oradata/BLACK/1_426_666110942.dbf' no
longer needed for this recovery

WooHoo!
Remarquez, la conviction mène souvent plus loin que l'intelligence, comme dirait Leeroy Jenkins ;-).

13 janvier 2009

2 nouveaux blogs (y compris le mien !)

Juste un petit mot pour mentionner 2 nouveaux et "very interesting" blogs :
  • Le premier dont je suis l'auteur anonyme s'intitule "We Do Streams"; Il est entièrement dédié, comme vous pouvez l'imaginer, aux technologies Streams et associées... J'attends vos réactions !
  • Le deuxième est un "must" dans la liste de vos souscriptions RSS. Il est intitulé "the QUADRO Blog" et contient déjà plusieurs articles intéressants dont le bientôt célèbre 11G adaptive direct path reads -- what is the cached/dirty blocks threshold?. C'est mon post préféré pour l'instant, mais je fais confiance à Alex pour faire encore plus intéressant.
Enjoy!

08 janvier 2009

A propos d'Oracle Flexible Architecture (OFA) et du Clusterware

Dans les annexes "Oracle Flexible Architecture de guides d'Installation d'Oracle" comme celui-ci, Oracle propose un chemin d'installation pour le clusterware qui n'est pas compatible avec le reste de la documentation et le bon sens. Explications!

Le clusterware est mis à jour d'une version à l'autre "In Place", c'est à dire sans changer d'ORACLE_HOME. Si vous voulez changer d'ORACLE_HOME pour le clusterware, c'est possible; il vous faut l'arrêter et donc avec lui toutes les instances de bases de données RAC ou ASM du cluster. C'est donc très contraignant ! En général, vous vous choisirez la solution par défaut du "rolling upgrade" et vous retrouverez donc avec le même ORACLE_HOME après mise à jour du clusterware. Evidemment, si le numéro de version est dans le ORACLE_HOME, comme suggéré par OFA, il restera le même après la mise à jour et vous vous retrouverez avec un clusterware 11.1 dans un répertoire qui inclut 10.2.

Alors quel ORACLE_HOME pour le clusterware?
La documentation n'est pas explicite sur le sujet ou au moins je n'ai jamais rien trouvé. Il y a quelques exemple comme que vous pouvez en extraire :
La deuxième solution est sans doute plus flexible puisqu'elle permet d'avoir un volume séparé pour tous les logiciels Oracle et de toutefois partager le même volume pour tous les logiciels; c'est d'ailleurs celle de mon post Oracle Silent Mode, Part 7: Installing an 11.1 RAC Database. Est-elle la plus logique ? Sans doute pas, mais toujours plus logique que de mettre le numéro de version dans le chemin du clusterware. Alors, s'il vous plait, arrêtez ça !

06 janvier 2009

La documentation du connecteur SAP d'Oracle Warehouse Builder

La documentation du connecteur SAP d'Oracle Warehouse Builder a été énormément améliorée entre 10.2 et 11.1!

04 janvier 2009

Détecter une erreur dans un script RMAN

Pour vérifier que votre script RMAN s'est exécuté correctement, vous pouvez tester le code retour de la commande, comme ci-dessous:
cat >test_rman.sh
#!/bin/sh

rman <<EOF
connect target /
list backup summary;
exit;
EOF

RETVAL=$?
echo "Return Code is :$RETVAL"

Sélectionnez les touches "Ctrl+D"

chmod +x test_rman.sh

./test_rman.sh

...
Return Code is :0
Si RMAN rencontre une erreur quelconque, même si la dernière commande réussie, le code retour n'est plus 0 mais 1; voici un exemple:
cat >test_rman.sh
#!/bin/sh

rman <<EOF
connect target /
list;
list backup summary;
exit;
EOF

RETVAL=$?
echo "Return Code is :$RETVAL"

Sélectionnez les touches "Ctrl+D"

chmod +x test_rman.sh

./test_rman.sh

...
Return Code is :1
Si vous voulez afficher une erreur après coup, vous pouvez utilisez les vues V$RMAN_BACKUP_JOB_DETAILS, V$RMAN_BACKUP_TYPE et V$RMAN_OUTPUT d'Oracle 10g. A ce propos, regardez ce blog post

Pourquoi chanter réduit les perfomances des IO?

02 janvier 2009

ORA-0*(600?|7445|4[0-9][0-9][0-9])[^0-9]

Le seuil "Warning" par défaut de la "Generic Alert Log Error" metric d'Oracle GridControl est "ORA-0*(600?|7445|4[0-9][0-9][0-9])[^0-9]". Mais qu'est-ce que ca veut dire?
  • "ORA-" signifie que le début de la chaîne que l'on cherche est "ORA-"
  • "0*" (zéro suivi d'un asterisk) signifie qu'ensuite vous avez "0" un certain nombre de fois; ce nombre allant d'aucune à un nombre illimité d'occurences. Un 0 suivit de * n'est donc pas forcément présent !
  • Les parenthèses "(" avec le pipe "|" signifient plusieurs alternatives, i.e. l'un des choix possibles
  • "0?" (zero suivi d'un point d'interrogation) signifie que vous avez "0" 1 fois ou zéro fois. Autrement dit "600?" correspond à 600 ou à 60 !
  • [0-9] signifie tous les caractères compris entre 0 est 9, soit tous les chiffres. Autrement dit "4[0-9][0-9][0-9]" signifie tous les nombre entre 4000 et 4999.
  • Enfin ^ signifie "différent de"; [^0-9] signifie donc n'importe quel caractère en dehors des chiffres compris entre 0 et 9. Pour tester une exception, ajoutez un caractère comme par exemple un deux-points ":" ou un espace " ".
Pour tester l'expression, vous pouvez utiliser quelque chose comme ce qui suit avec Oracle 10g :
select 1
from dual
where regexp_like('ORA-07445:',
'ORA-0*(600?|7445|4[0-9][0-9][0-9])[^0-9]');
ou si vous préférez, utilisez Perl:
perl -e "if ( 'ORA-07445:'
=~ /ORA-0*(600?|7445|4[0-9][0-9][0-9])[^0-9]/ )
{print \"match \n\"}
else {print \"no match \n\"}"