Oracle & regexp_replace vs. ActiveRecord & utf8
In einem Projekt habe ich vor kurzem ein cooles Oracle-Feature einsetzen wollen: reguläre Ausdrücke. Wie ich jüngst lernte, unterstützt Oracle tatsächlich anständige reguläre Ausdrücke. Die Funktion, die ähnlich wie sed funktioniert, heißt "regexp_replace".
Doch das mühsam im SQL Developer zusammengebaute Statement wollte aber nicht aus der Rails-Anwendung funktionieren. Als Beispiel kann folgendes Statement dienen:
select regexp_replace(to_char(sysdate, 'dd.mm.yyyy'),
'^([0-9]+)\.([0-9]+)\.([0-9]+)$',
'Tag: \1, Monat: \2, Jahr: \3')
from dual;
Loading development environment (Rails 4.0.0)
2.0.0p247 :001 > stmt = "select regexp_replace(to_char(sysdate, 'dd.mm.yyyy'), '^([0-9]+)\.([0-9]+)\.([0-9]+)$', 'Tag: \1, Monat: \2, Jahr: \3') from dual"
=> "select regexp_replace(to_char(sysdate, 'dd.mm.yyyy'), '^([0-9]+).([0-9]+).([0-9]+)$', 'Tag: \u0001, Monat: \u0002, Jahr: \u0003') from dual"
Das Problem ist, dass die Referenzen auf die Klammer-Terme (gibt es dafür einen anständigen Ausdruck?) \1, \2, \3 als Escape-Sequenzen interpretiert werden. Führt man das Statement aus, kommt erwartungsgemäß Müll heraus:
2.0.0p247 :021 > result = ActiveRecord::Base.connection.select_one(stmt)
(18.7ms) select regexp_replace(to_char(sysdate, 'dd.mm.yyyy'), '^([0-9]+).([0-9]+).([0-9]+)$', 'Tag: , Monat: , Jahr: ') from dual
=> {"regexp_replace(to_char(sysdate"=>"Tag: \u0001, Monat: \u0002, Jahr: \u0003"}
Die Backslashes sind alle verschwunden, was sich für den regulären Ausdruck nicht so gut macht.
Erstaunlicherweise ist die Lösung tatsächlich die naheliegende:
2.0.0p247 :022 > stmt2 = "select regexp_replace(to_char(sysdate, 'dd.mm.yyyy'), '^([0-9]+)\\.([0-9]+)\\.([0-9]+)$', 'Tag: \\1, Monat: \\2, Jahr: \\3') from dual"
=> "select regexp_replace(to_char(sysdate, 'dd.mm.yyyy'), '^([0-9]+)\\.([0-9]+)\\.([0-9]+)$', 'Tag: \\1, Monat: \\2, Jahr: \\3') from dual"
2.0.0p247 :023 > result2 = ActiveRecord::Base.connection.select_one(stmt2) (5.6ms) select regexp_replace(to_char(sysdate, 'dd.mm.yyyy'), '^([0-9]+)\.([0-9]+)\.([0-9]+)$', 'Tag: \1, Monat: \2, Jahr: \3') from dual
=> {"regexp_replace(to_char(sysdate,'dd.mm.yyyy'),'^([0-9]+)\\.([0-9]+)\\.([0-9]+)$','tag:\\1,monat:\\2,jahr:\\3')"=>"Tag: 19, Monat: 12, Jahr: 2013"}
2.0.0p247 :024 > result2.values
=> ["Tag: 19, Monat: 12, Jahr: 2013"]
Obwohl die String-Darstellung das Statements doppelte \ zeigt, kommt in der Datenbank nur einer an und das erwartete Ergebnis kommt zurück.
Vermutlich ist es nicht der schönste Weg, den String zu erzeugen. Ich bin für Vorschläge offen...