segunda-feira, 28 de novembro de 2016

Transporte de um Tablespace para outro Diskgroup no Oracle

Um diskgroup de minha base (11g) estava perigosamente cheio, então resolvi transportar um tablespace pouco usado para um diskgroup mais lento.

Suponha que os datafiles do tablespace TSX estejam no diskgroup +DG1 e que serão transportados ao diskgroup +DG2. A tarefa é simples e envolve os seguintes passos para cada datafile (executados no RMAN):
  1. Fazer uma cópia no diskgroup destino: backup as copy datafile file# format '+DG2';
  2. Colocar o datafile atual offline: sql 'alter database datafile file# offline';
  3. Fazer a troca para o backup: switch datafile file# to copy;
  4. Fazer recover do novo datafile: recover datafile file#;
  5. Colocar o datafile online: sql 'alter database datafile file# online';
  6. Apagar datafile original: delete datafilecopy 'filename';
Se o tablespace tiver muitos arquivos, ou a tarefa tiver que ser executada em vários ambientes, vale a pena escrever uma consulta (ou uma função) para gerar o script:

select case step.n
           when 1 then
             'backup as copy datafile '||file#||' format ''+DG2'';'
           when 2 then
             'sql ''alter database datafile '||file#||' offline'';'
           when 3 then
             'switch datafile '||file#||' to copy;'
           when 4 then
             'recover datafile '||file#||';'
           when 5 then
             'sql ''alter database datafile '||file#||' online'';'
           when 6 then
             'delete datafilecopy '''||df.name||''';'
          end
       esac
from v$tablespace ts
     inner join v$datafile df on ts.ts#=df.ts#,
    (select rownum n from all_tables where rownum<7) step
where ts.name=:TABLESPACE_NAME
order by df.file#, step.n
E o resultado será algo assim:

backup as copy datafile 106 format '+DG2';
sql 'alter database datafile 106 offline';
switch datafile 106 to copy;
recover datafile 106;
sql 'alter database datafile 106 online';
delete datafilecopy '+DG1/mydb/datafile/tsx_data.265.122421389';
backup as copy datafile 138 format '+DG2';
sql 'alter database datafile 138 offline';
switch datafile 138 to copy;
recover datafile 138;
sql 'alter database datafile 138 online';
delete datafilecopy '+DG1/mydb/datafile/tsx_data.263.183291830';

sábado, 19 de novembro de 2016

O Caos Imposto pela Ordem

Em 2050, os carros autônomos começaram a dominar o mercado de transporte individual no Brasil. Após desistir de impor a ordem ao trânsito, o país estava esperançoso: os novos veículos inteligentes trariam a paz às ruas e o ano terminaria com uma contagem de fatalidades de apenas quatro dígitos.

O típico pedestre foi o primeiro a perceber o seu novo poder. Já que ninguém o atropelaria, ele podia atravessar qualquer avenida sem medo. Ao mesmo tempo, os passageiros dessas maravilhas tecnológicas ficavam cada vez mais irritados: não podiam viajar na velocidade que desejavam e tampouco podiam estacionar onde queriam. Era um inferno.

O trânsito das grandes cidades simplesmente parou nos horários de pico. Pedestres e ciclistas retomaram as ruas. Os proprietários destes novos carros tornaram-se violentos. Primeiro, com ameaças:

- Você sabe quem eu sou?! Eu paguei caro por este carro!!

Isso não resolveu. Afinal, os pedestres estavam acostumadas a carros avançando; uns gritos nervosos não assustavam a ninguém.

Os ex-motoristas estavam deseperados. Haviam pagado caro na esperança de finalmente livrarem-se dos engarrafamentos. O resultado é que estavam perdendo tempo no trânsito como nunca.

Nas cercanias das favelas do Rio de Janeiro, alguns desocupados logo perceberam que podiam improvisar um pedágio sem grande esforço ou risco. Três pessoas paravam os carros e outros recolhiam o dinheiro. Alguns adolescentes dos Jardins, em São Paulo, também adotaram essa tática. Logo surgiu um aplicativo de celular para coordenar os pedágios e evitar a polícia.

Foi quando um delegado atirou no primeiro paisano em plena Avenida Paulista que o verdadeiro caos iniciou. O juiz do caso considerou o homicídio como uma morte de trânsito. Afinal, se fosse um carro comum, o pedestre teria sido atropelado e o caso seria apenas mais uma fatalidade do terrível trânsito brasileiro. Então, não seria justo mudar agora a jurisprudência, quando tantos motoristas já tinham sido inocentados. Além disso, não havia como determinar se o pedestre não tinha a intenção de assaltar ou simplesmente cobrar pelo direito de passagem.

Em pouco tempo, os pedestres estavam devolvendo as balas. O Congresso Nacional rapidamente reconheceu a necessidade de nova legislação e no tempo recorde de sete anos foram criados os conceitos de autodefesa veicular e autodefesa peatonal.

A Em 2065, tudo tinha voltado à normalidade. Neste ano, morreram 50 mil brasileiros no trânsito (vítimas de atropelamentos e choques devidos a carros com firmware modificado para ignorar as regras de circulação). Os brasileiros estavam aliviados que os tiroteios nas ruas eram coisa do passado.

segunda-feira, 7 de novembro de 2016

Mandelbrot com expoentes complexos

Depois de explorar o mandelbrot com expoentes maiores de 2, com números reais, e com números negativos, eu tinha que verificar o que acontece com expoentes complexos.

O cálculo é um pouco mais complicado e também mais demorado.

var pr=2;
var pi=-0.009;    
    
function mandel(x,y) {
  var c=1;
  var r=x;
  var i=y;
  while(r*r+i*i<4 && c<MAXITER) {
    var a=Math.atan2(i,r);
    var b=r*r+i*i;
    var p=Math.pow(b,pr/2)*Math.pow(Math.E,-1*pi*a);
    var nr=p*Math.cos(pr*a+0.5*pi*Math.log(b));
    var ni=p*Math.sin(pr*a+0.5*pi*Math.log(b));
    r=nr+x;
    i=ni+y;
    c+=1;
  }
  return c;
}

O expoente tem uma parte real (pr) e uma parte imaginária (pi).

Valores grandes (maiores de 1) de pi distorcem muito a imagem, então usei valores pequenos. De longe, percebe-se que algo não está certo. A imagem abaixo foi gerada com expoente=2-0.009i.


De perto aparecem detalhes interessantes.


Com 2+0.1i, a distorção já é grave.


Eis algunas detalhes curiosos de 1/pi + i/e.




Clique nas imagens para vê-las no tamanho original.
E i/e merece um lugar num museu de arte moderna.





terça-feira, 1 de novembro de 2016

Porta-Retratos Digital Caseiro III (A Mágica)

O CPAN nunca decepciona: há um módulo para rotacionar as imagens conforme os metadados do JPEG. As máquinas fotográficas mais modernas gravam nos arquivos JPEG a orientação das fotos e o módulo Imager::ExifOrientation permite carregar a imagem já virada no sentido correto para exibir na tela.

use Imager::ExifOrientation;

sub next_photo {
  my $index=int(rand($#file_list));
  my $filename=$file_list[$index];
  
  my $image = Imager::ExifOrientation->rotate(path => $filename);
  scale($photo_width, $photo_height, $image);
  $photo->configure(-file=>'tmp.jpg');
  
  $filename=$file_list[($index+1)%($#file_list+1)];
  
  $image = Imager::ExifOrientation->rotate(path => $filename);
  scale($thumb_width, $thumb_height, $image);
  $thumb->configure(-file=>'tmp.jpg');
  
  $main->update();
}
A mágica tem seus limites: se o arquivo não contiver a informação de orientação, a imagem será carregada assim como está no arquivo.

segunda-feira, 31 de outubro de 2016

Porta-Retratos Digital Caseiro II (O Bug)

O porta-retratos digital funcionou bem por cerca de 8 horas e então parou com um erro de malloc(). Após algumas simulações e pesquisa dos bugs do Tk, descobri que o defeito está na classe Tk::Photo. Quando a imagem é carregada pela memória (com -data), os bits ficam guardados para sempre. Quando são lidos do disco, os dados não persistem além da próxima leitura.

Então, reescrevi alguns trechos e o código ficou um pouco menor. A função scale() não retorna mais os dados; agora ela escreve um arquivo.

sub scale {
  my ($width, $height, $image)=@_;
  my $scaled=$image->scale(xpixels=>$width,ypixels=>$height,type=>'min');
  $scaled->write(file=>'tmp.jpg', type=>'jpeg');  
}

Por sua vez, a função next_photo() lê este arquivo:

sub next_photo {
  my $index=int(rand($#file_list));
  my $filename=$file_list[$index];
  
  $image->open(file=>$filename);
  scale($photo_width, $photo_height, $image);
  $photo->configure(-file=>'tmp.jpg');
  
  $filename=$file_list[($index+1)%($#file_list+1)];
  
  $image->open(file=>$filename);
  scale($thumb_width, $thumb_height, $image);
  $thumb->configure(-file=>'tmp.jpg');
  
  $main->update();
}

Evidentemente, não quero escrever este arquivo no cartão de memória para não apressar o seu fim. Então, a solução é criar um disco em RAM e escrever lá. Ele não precisa ser muito grande, os JPEGs temporários não passam de 100KB (por isso, o script original demorou a ocupar toda a memória).

quinta-feira, 27 de outubro de 2016

Porta-Retratos Digital Caseiro

Os preços dos porta-retratos digitais estão começando a ficar interessantes, mas construir o próprio é mais divertido.

Usei um Raspberry Pi A+ (256MB de RAM e um ARM11 de 700MHz) com a última versão do Raspbian Jessie. Eu queria mesmo era usar um Raspberry Pi Zero (que custa apenas US$5), mas são muito procurados e ainda não apareceram na minha loja preferida.

Comecei muito otimista (como sempre) e tentei montar uma solução com HTML, Javascript, jQuery, transições, etc. Ela resultou ser muito pesada. Então, tentei em Tcl/Tk, mas manipular os tamanhos das imagens resultou ser muito trabalhoso (possível, mas trabalhoso).

Então, só me restou usar Perl. Foi preciso instalar dois pacotes: Tk e Imager.


sudo apt-get install perl-tk
sudo cpanm Imager


A interface é muito simples. Há um quadro grande com uma foto qualquer e uma foto pequena com a foto seguinte. A foto grande é escolhida aleatoriamente, mas a pequena é sempre a que segue (para haver uma relação entre elas). Além disso, exibo as horas e a data.

O script recebe um único parâmetro que indica o diretório onde as fotos são armazenadas. Ctrl+C suavemente encerra o programa.

Futuramente, espero exibir algo mais interessante no quadro das horas (a previsão do tempo ou as notícias).

#!/usr/bin/perl
use Tk;
use Tk::JPEG;
use Tk::Photo;
use Imager;
use File::Find;
use MIME::Base64;
use POSIX 'strftime';
use strict;

my @file_list;

find(\&wanted, $ARGV[0]);

sub wanted {
    return unless -f;
    return unless /\.jpe?g$/i;
    push @file_list, $File::Find::name;
}

my $main = MainWindow->new (
  -title => 'PiFrame',
  -background => 'black'
);

my $w=$main->screenwidth();
my $h=$main->screenheight();
$main->overrideredirect(1);
$main->MoveToplevelWindow(0,0);
$main->geometry(join('x',$w,$h));

my $photo_width=int($w*3/4);
my $photo_height=$h;
my $thumb_width=$w-$photo_width;
my $thumb_height=int($h/3);

my $canvas=$main->Canvas(
  -width=>$photo_width,
  -height=>$h,
  -background=>'black',
  -highlightthickness => 0);
$canvas->pack(-side=>'left');

my $small_canvas=$main->Canvas(
  -width=>$thumb_width,
  -height=>$thumb_height,
  -background=>'black',
  -highlightthickness => 0);
$small_canvas->pack(-side=>'bottom');

my $time;

my $clock=$main->Label(
  -textvariable=>\$time,
  -width=>100,
  -height=>100,
  -background=>'black',
  -foreground=>'white',
  -highlightthickness => 0,
  -font=>'courier 40 bold');
$clock->pack(-side=>'top');

my $photo=$canvas->Photo();
$canvas->createImage(0,0,-image=>$photo,-anchor=>'nw');
my $thumb=$small_canvas->Photo();
$small_canvas->createImage(0,0,-image=>$thumb,-anchor=>'nw');

sub scale {
  my ($width, $height, $image)=@_;
  my $scaled=$image->scale(xpixels=>$width,ypixels=>$height,type=>'min');
  my $data;
  $scaled->write(data=>\$data, type=>'jpeg');
  return encode_base64($data)
}

my $image=Imager->new();

sub next_photo {
  my $index=int(rand($#file_list));
  my $filename=$file_list[$index];
  
  $image->open(file=>$filename);
  $photo->configure(-data=>scale($photo_width, $photo_height, $image));
  
  $filename=$file_list[($index+1)%($#file_list+1)];
  $image->open(file=>$filename);
  $thumb->configure(-data=>scale($thumb_width, $thumb_height, $image));
  
  $main->update();
}

$canvas->repeat(60000, sub {eval {next_photo()}});
$canvas->repeat(1000, sub {$time=strftime("%H:%M\n%A\n%d/%m", localtime)});

next_photo();

MainLoop;

O sistema operacional que usei para este projeto é o Raspbian com Pixel. As fotos estão armazenadas na pasta /data01/fotos. Então, adicionei a seguinte linha ao arquivo  /home/pi/.config/lxsession/LXDE-pi/autostart para que o sistema inicie automaticamente:

@perl /home/pi/piframe.pl /data01/fotos/

O gasto de memória oscila entre 25MB e 50MB, o que cabe confortavelmente no espaço que o A+ oferece. Um Pi 3 talvez possa executar isso ao mesmo tempo que ofereça o serviço do Amazon Echo.

quinta-feira, 15 de setembro de 2016

Melhorias ao SQL

Lendo um relato sobre um pobre programador que apagou uma tabela inteira, porque esqueceu o where num delete, fiquei a pensar que o SQL deveria ter sintaxe diferente para alguns casos.

Os updates e os deletes de tabelas inteiras deveriam indicar a intenção explicitamente:

update my_table set my_column=0 all rows;
delete other_table all rows;

E se alguém esquecesse do all rows ou do where, seria um erro de sintaxe. Aposto que essa mudança evitaria muito sofrimento.