Perl para DBAs – Primeiro Script

Ao invés de colocar um script inteiro da Perl, vou exibi-lo passo a passo, para que o leitor possa entender os fragmentos da linguagem conforme eles são necessários.

Para testarmos se o DBD-Oracle está instalado corretamente, podemos fazer isso com um script teste.

Na Perl as livrarias são chamadas com o comando “use”. Além da DBI e da DBD::Oracle, eu invoquei antes duas livrarias que fazem parte das boas práticas de programação desta linguagem (“warnings” e “strict”).

Além da chamadas às livrarias, eu coloco o comando “exit”, para terminar o script. Este comando nem é necessário na Perl, se o script acabar, sua execução seria interrompida naturalmente.

A Perl possui este comportamento em diversos outros pontos: ela não te obriga a seguir muitas regras, mas se você quiser seguir, não tem problema (programadores Java ficam doidos com isso).

[oracle@OraclePRD ~]$ cat Teste.pl
use warnings;
use strict;
use DBI;
use DBD::Oracle;

exit;

[oracle@OraclePRD ~]$ perl Teste.pl
[oracle@OraclePRD ~]$

Se não tivemos nenhum erro, está tudo ok com as livrarias que utilizamos. Este comportamento também faz parte da cultura Perl. Se ela não tem nada importante para dizer, nada é dito.

Agora, vamos adicionar uma conexão a uma instância Oracle, e fazer um SELECT simples.

A variável $dbh contém o “Database Handle”, ele é a conexão e si ao Oracle Database.
A variável $sth contém o “Statement Handle”, necessário para utilização de um comando no “Database Handle”.
E a variável $sql contém apenas o texto do SQL que será executado pelo “Statement Handle”.

A execução de “$dbh->prepare($sql_01)” prepara (compila) o comando SQL, e o “$sth_01->execute()” o executa. Poderíamos parar por aqui, no caso de um UPDATE, por exemplo. Mas como é um SELECT, eu quero saber o que el retornou.

Isto é feito pelo LOOP em WHILE: para cada conjunto de valores (ou seja, para cada linha com colunas) retornado pelo “Statement Handle”, execute o que estiver dentro do LOOP. E dentro do LOOP, eu pego a coluna retornada TABLE_NAME, a atribuo à variváel $table_name, e a imprimo na tela. O “n” significa para pular uma linha depois da impressão.

E por último, eu executo “commit” e “disconnect” do “Database Handle”. Adivinhe, o “disconnect” também é desnecessário, ele aconteceria de qualquer forma.

use warnings;
use strict;
use DBI;
use DBD::Oracle;

my $hostname = 'localhost';
my $database = 'ORCL';
my $username = 'SCOTT';
my $password = 'TIGER';
my $dbh = DBI->connect("dbi:Oracle:host=$hostname;sid=$database", $username, $password, {RaiseError => 1, AutoCommit => 0});

my $sql_01 = 'SELECT TABLE_NAME FROM USER_TABLES';
my $sth_01 = $dbh->prepare($sql_01) or die $DBI::errstr;
$sth_01->execute() or die $DBI::errstr;

while (my $ref_01 = $sth_01->fetchrow_hashref())
        {
        my $table_name = $ref_01->{TABLE_NAME};
        print "TABLE: $table_namen";
        }

$dbh->commit;
$dbh->disconnect;

exit;

Ok, agora vamos executá-lo e ver o resultado.

[oracle@OraclePRD ~]$ perl Teste.pl
TABLE: EMP
TABLE: BONUS
TABLE: SALGRADE
TABLE: DEPT

Ok, vamos complicar mais agora.
Dentro do LOOP WHILE, eu vou chamar uma nova execução (um novo “Statement Handle”, o $sth_02), executando um novo comando SQL (o $sql_02). Este novo comando irá fazer um MOVE de cada tabela encontrada.

Como eu não preciso ver o resultado, eu não preciso fazer um novo LOOP WHILE. Só executo (“$sth_02->execute()”), e pronto.

Se houver algum erro, será impresso na tela. Isto é feito pelos comandos “or die $DBI::errstr”. Este comando significa: se der errado, imprima o erro na tela.

Para incrementar o script, eu coloco uma nova impressão após o MOVE, para informar o que foi feito.

use warnings;
use strict;
use DBI;
use DBD::Oracle;

my $hostname = 'localhost';
my $database = 'ORCL';
my $username = 'SCOTT';
my $password = 'TIGER';
my $dbh = DBI->connect("dbi:Oracle:host=$hostname;sid=$database", $username, $password, {RaiseError => 1, AutoCommit => 0});

my $sql_01 = 'SELECT TABLE_NAME FROM USER_TABLES';
my $sth_01 = $dbh->prepare($sql_01) or die $DBI::errstr;
$sth_01->execute() or die $DBI::errstr;
while (my $ref_01 = $sth_01->fetchrow_hashref())
        {
        my $table_name = $ref_01->{TABLE_NAME};
        print "Table found: $table_namen";

        my $sql_02 = "ALTER TABLE $table_name MOVE";
        my $sth_02 = $dbh->prepare($sql_02) or die $DBI::errstr;
        $sth_02->execute() or die $DBI::errstr;
        print "Table moved: $table_namenn"
        }

$dbh->commit;
$dbh->disconnect;

Vamos testar, e ver o resultado.

[oracle@OraclePRD ~]$ perl Teste.pl
Table found: DEPT
Table moved: DEPT

Table found: BONUS
Table moved: BONUS

Table found: EMP
Table moved: EMP

Table found: SALGRADE
Table moved: SALGRADE

[oracle@OraclePRD ~]$

Pronto, temos um desfragmentador automático para o SCOTT.

Mas vamos complicar ainda mais…

Depois do MOVE da tabela, os índices ficaram inválidos. Então vamos adicionar mais um SELECT, após o MOVE, para verificar quais os índices desta tabela.

E como uma tabela pode ter vários índices, eu preciso recuperar o nome deste índices com um novo LOOP WHILE, que também já executa o REBUILD.

[oracle@OraclePRD ~]$ cat Teste.pl
use warnings;
use strict;
use DBI;
use DBD::Oracle;

my $hostname = 'localhost';
my $database = 'ORCL';
my $username = 'SCOTT';
my $password = 'TIGER';
my $dbh = DBI->connect("dbi:Oracle:host=$hostname;sid=$database", $username, $password, {RaiseError => 1, AutoCommit => 0});

my $sql_01 = 'SELECT TABLE_NAME FROM USER_TABLES';
my $sth_01 = $dbh->prepare($sql_01) or die $DBI::errstr;
$sth_01->execute() or die $DBI::errstr;
while (my $ref_01 = $sth_01->fetchrow_hashref())
        {
        my $table_name = $ref_01->{TABLE_NAME};
        print "Table found: $table_namen";

        my $sql_02 = "ALTER TABLE $table_name MOVE";
        my $sth_02 = $dbh->prepare($sql_02) or die $DBI::errstr;
        $sth_02->execute() or die $DBI::errstr;
        print "Table moved: $table_namen";

        my $sql_03 = "SELECT INDEX_NAME FROM USER_INDEXES WHERE TABLE_NAME = '$table_name'";
        my $sth_03 = $dbh->prepare($sql_03) or die $DBI::errstr;
        $sth_03->execute() or die $DBI::errstr;
        while (my $ref_03 = $sth_03->fetchrow_hashref())
                {
                my $index_name = $ref_03->{INDEX_NAME};
                print "Index found: $index_namen";

                my $sql_04 = "ALTER INDEX $index_name REBUILD";
                my $sth_04 = $dbh->prepare($sql_04) or die $DBI::errstr;
                $sth_04->execute() or die $DBI::errstr;
                print "Index rebuild: $index_namen";
                }
        print "n";
        }

$dbh->commit;
$dbh->disconnect;

exit;

Vamos testar nosso desfragmentador…

[oracle@OraclePRD ~]$ perl Teste.pl
Table found: EMP
Table moved: EMP
Index found: PK_EMP
Index rebuild: PK_EMP

Table found: DEPT
Table moved: DEPT
Index found: PK_DEPT
Index rebuild: PK_DEPT

Table found: SALGRADE
Table moved: SALGRADE

Table found: BONUS
Table moved: BONUS

[oracle@OraclePRD ~]$

Ok, mas por que fazer isso em Perl, e não em…

Shell: Shell Script tem portabilidade limitada. Não funcionará em Windows, por exemplo, ou você pode se deparar com um Unix que não tem o sabor de Shell que você utilizou.

Java: Certo, Java é portável. Mas fazer isso em Java levaria quantas linhas?

PL/SQL: PL/SQL é portável… para Oracle. Você pode adaptar este script facilmente para funcionar em MySQL, SQL Server… Pode até fazer um script onde você informa qual é o banco de dados por uma variável, e dependendo do banco, o comando será diferente para capturar o TABLE_NAME, por exemplo.

4 comments

  1. Oi Portilho, tem sido muito boas essas dicas sobre Perl!

    Uma alternativa que tem sido interessante quando não há acesso de root é usar o Jython, que é o Python rodando sobre a JVM. Com ele é possível ter a agilidade na programação do Python e fazer conexão com bancos usando os drivers JDBC normais.

    Abraço,

    Lucas

Leave a Reply

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.