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.