Sempre falo, que depois que conheci as expressões regulares, minha vida mudou! Semana passada, tive que converter um backup de um banco de dados do Oracle para o PostgreSql, então, reparei algumas diferenças na sintaxe.

A ideia para solucionar a diferença, foi criar uma método simples que conseguia manipular o código sql do Oracle e transformar no código PostgreSql. Cada um dos casos acima não passa de:

{ /diferenca do oracle/ => "para postgresql" }

Ou diferenças envolvendo variáveis do contexto como:

{ /diferenca do (\w+)/ = > lambda do |nome_do_sgbd|
      "para #{nome_do_sgbd}"
  end }

Nesta idéia, foi simples de converter todos os códigos Oracle que havia diferença de sintaxe usando um Hash de casos:

CASES = { 
     /(ALTER TABLE .*) MODIFY +\((.*) NOT NULL ENABLE\);/im => lambda do |alter, column|
       "#{alter} ALTER COLUMN #{column} SET NOT NULL;"
     end,

     /ALTER TABLE "(.*)" 
       ADD PRIMARY KEY \((.*)\) ENABLE/im => lambda do |table_name, primary_keys| 
          "ALTER TABLE \"#{table_name}\" 
            ADD CONSTRAINT #{table_name}_PKEY 
            PRIMARY KEY (#{primary_keys});"
     end,

     /(.*) BIGINT\((.*)\)(,)?/im => lambda do |column_name, range, comma|
         "#{column_name} NUMERIC(#{range})#{comma}"
     end,
     / NUMBER/im         => lambda { " BIGINT" },
     /DEFAULT SYSDATE/im => lambda { "DEFAULT now()" },
     /VARCHAR2/im        => lambda { "VARCHAR" }
}

Desta forma, cada caso, já está mapeado com a sua devida solução, sendo apenas necessário criar o código que manipula o arquivo do Oracle de entrada, e gera o arquivo PostgreSql de saída.

Um exemplo simples de utilização dos casos acima, por sinal muito inspirado no cucumber, seria criar uma método que avalie os casos, e em cada expressão regular, verificaque a probabilidade de substituição pela nova sintaxe (no caso Oracle para PostgreSql).

def convert(sql)
  CASES.each do |regexp, replacer|
    while match = sql.match(regexp)
      if match.captures.empty?
        sql.gsub!(regexp, replacer.call)
      else
        sql = replacer.call *match.captures
      end
    end
  end
  sql
end

O método acima, avalia cada caso de expressão regular nova, e propõe uma solução apenas em string, ou faz capturas de resultados relevantes em meio a cada caso. Quando alguma captura é efetuada, é invocado um bloco de código (esperado) que manipule e devolva a nova sintaxe, manipulando os resultados.

Este exemplo, é muito semelhante as estórias usadas no Cucumber, as quais também lançam os parâmetros capturados como variáveis do bloco. Este é um exemplo muito simples, e que pode colaborar na formulação de muitas novas sintaxes e linguagens de domínio específicos.

Desta forma, o Hash, sempre demonstra ser a:

  { :chave => valor }

Neste caso, o exemplo aborda:

  { :sintaxe_antiga => nova_sintaxe }

E o mais divertido, é esta possibilidade de apontar qualquer tipo de objeto, para qualquer tipo de valor. Sem especificar o tipo, é possível esperar algo, de forma simples e objetiva, ou seja, sem muitas formalidades.

Para ler um arquivo do Oracle e criar um novo arquivo do PostgreSql, é simples como o exemplo abaixo:

File.open("PostgreSqlBkp.sql", "w+") do |postgresql_file|
  File.readlines("OracleBkp.sql").each do |line|
    postgresql_file.puts convert(line)
  end
end

O código acima, supõe que o nome do arquivo de bkp do banco Postgresql é PostgreSqlBkp.sql, e irá criar um novo arquivo OracleBkp.sql. A opção w+ sobrescreve o arquivo se já existente.


Share → Twitter Facebook Linkedin


Hello there, my name is Jônatas Davi Paganini and this is my personal blog.
I'm developer advocate at Timescale and I also have a few open source projects on github.

Check my talks or connect with me via linkedin / twitter / github / instagram / facebook / strava / meetup.