FC2ブログ

Perl & Oracleでハマった件

   プログラミング [2019/12/06]
Perlをちゃんと使うのは初めてで、
わけのわかんない省略形?的な記述で四苦八苦はしたものの、
ちょっとは動き出して、DBアクセスもできそうな感じになってきたときにハマった話です。

相変わらず自分用の備忘録なんで、表現/用語が不正確な部分は勘弁してやってください。


◆Oracleに関する情報
.plを動かしてみるとHTTPステータス500で相変わらず動きません。

自.plで出してるエラーログで見ると、「SQLクエリ内に間違いがある。」と出てる。
具体的には、「DBD::Oracle::db prepare failed: ORA-00911: 文字が無効です。 (DBD ERROR: error possibly near <*> indicator at char 19 in 'SELECT ...
クエリ内に<*>と入っているのですが、カラム名の真ん中に「番<*>号」と入っている。
なんじゃこれは?
SQL文メッセージの表記もなんか変。

#そう!今回対象としているOracle DBのシステムでは、テーブル名のみならず、項目名にまで日本語の名前を付けるという暴挙をやってのけているDBだったのです。しかもテーブル仕様書もない。
(ボヤキ:素人かよ。はぁ~。普通しないだろ。アルファベット日本語交じりのネーミングが開発にどれだけ不効率的なことなのかはさるでも分かる。)

ともかく、メッセージも変なので、Oracleに関する情報集めをする必要がありそうだ。
まずは、バージョン。
とっても便利なsqlplusというツールが入っているらしい。
--------------------------------------------------------
C:\Perl64>sqlplus

SQL*Plus: Release 11.2.0.3.0 Production on 水 4月 17 16:27:13 2019

Copyright (c) 1982, 2011, Oracle. All rights reserved.

ユーザー名を入力してください: hogehoge
パスワードを入力してください:

Oracle Database 11g Release 11.2.0.3.0 - 64bit Production
に接続されました。
--------------------------------------------------------

この状態で、クエリを実行できます。

Oracleのバージョン確認は、以下。(上のメッセージにも出てますけど・・・)
--------------------------------------------------------
SQL> select * from v$version;
BANNER
-----------------------------------
Oracle Database 11g Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for 64-bit Windows: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
--------------------------------------------------------


DBがサポートしている文字コードを知りたい場合には、
--------------------------------------------------------
SQL> select value from NLS_DATABASE_PARAMETERS where PARAMETER='NLS_CHARACTERSET';
VALUE
-----------------------------------
AL32UTF8
--------------------------------------------------------


色々ググった結果、
・やっぱりDBのオブジェクト(テーブル、カラム)に日本語は付けない方がいいそうだ。
・でも禁止はされていない。そりゃそうね、現に実在できてるみたいだし。
・もし日本語付けちゃったら、正規には、非引用識別子指定(ダブルクォーテーションで挟む)をする必要があるらしい。
えーーーっ本当?
perlのダブルクォーテーションの扱いあいまってさらに面倒なことになりそう。

でも、sqlplusで、エラーの出たスクリプトをコピペして実行すると、エラーにならずに動く。
ということは、perl環境下特有ということではなかろうか。

調べを続けます・・・。


・perlでは、日本語というかマルチバイト文字列の扱いを「内部文字列」「内部表現」と呼んでいる形式にするそうだ。
・perl内で使用している形式ではutf8フラグなるものが付加されている。
・外とのやり取りの場合、入ってきたコードも入口で「decode」して、出口で「encode」するのが基本だ!とある。
・内部表現=「flagged utf8」に変換している?
じゃperlでクエリを作ってDBIに食わせるときはencodeする必要があるのか?・・・そこが良く分からない。


Eclipseで使ってたMySqlのときには「$dbh->do("set names utf8");」というのを入れていた。
Oracleでは無効だそうだ。

同じようなのはないか?と探したら以下が引っ掛かりました。
--------------------------------------------------------
$ENV{'nls_lang'} = "Japanese_Japan.AL32UTF8";
--------------------------------------------------------

後ろの、文字コード名はこの前の説明で調べたOracle自身の設定値からもってくるようです。
この1行をBDIのconnect前に実施するように説明されてました。

ひとまず、SELECTクエリが一切「ORA-00911: 文字が無効です。」になる現象は上記で回避できた。
結果、一歩前進。

でも、INSTERクエリは相変わらずORA-00911エラー。なんで?


◆CPANによるDBD::Oracleインストール
そもそも、既に入っているDBI::DBD::Oracleが信用ならない(古いかもしれないので)のでアップグレードしようとしたが、
前の記事「Perl環境でまたハマった件」でも説明したようにプロキシ越えを断念しているので、その記事の中の「◆Crypt::PBKDF2のインストール」と同じようにインストールし直した。

今回はCPANのみで行った。(PPMを使わなかった)
必要構成は以下だった。
※「cpan> install DBD」とやったら足りないと言われたものも含みます。一部(01mailrc.txt.gz)に関しては、「何か」を試しにインストールしたときに足りないと言われたもので、既に入ってた。よって、これまでのインストール作業の前提になっているかもしれない。

--------------------------------------------------------
DBD
 -Common? →authers/01mailrc.txt.gz
 -? →modules/02packages.details.txt.gz
 -? →modules/03modlist.data.gz

DBD::Oracle →authors/id/Z/ZA/ZARQUON/DBD-Oracle-1.76.tar.gz
-Test::NoWarnings →authors/id/A/AD/ADAMK/Test-NoWarnings-1.04.tar.gz
--------------------------------------------------------


上記をインストールしなおして再度.plを実行してみましたが、エラーは消えませんでした。

ここで、ちょっと話を変えます。


◆.plのデバッグ時に困ったこと(Windowsのキャッシュにハマる)
Perl-暗号化/DBアクセス環境が整った感、ありましたが、外見には分からない?DB側の癖が多くて、結構な対応に追われました。
そのせいで、コードも直すことしばしば。

そんなこんなしていると、.plを書き換えて再実行を繰り返していると、変更した部分が有効に動作していないことがあるのに気付きました。
ソースを開いてみると、書き換わっているのですが、実行したログを見ると、コード変更前の動きをしている。
どうやっても、変更内容が有効にならない。

Apache側のサーバキャッシュを疑って、httpd.conf内をチェックして臭い2文をコメントアウトから復活させました。
--------------------------------------------------------

#LoadModule cache_module modules/mod_cache.so

#LoadModule disk_cache_module modules/mod_disk_cache.so

#LoadModule file_cache_module modules/mod_file_cache.so

#LoadModule mem_cache_module modules/mod_mem_cache.so

EnableMMAP off
EnableSendfile off

--------------------------------------------------------

が、変化なし。治りません。

たまには、ソース内容が更新されるので我慢してたのですが、イライラが増します。

メディアの.plのバックアップとWinMergeでコンペアしたところ、最新の修正内容が反映されてない。
・・・・ってことはApacheは関係なくて、OS/ファイルシステムということですよね。

OSがWindows8で、Program Files (x86)の下にApacheのフォルダがあるのを見たとき、何やら嫌な気分になったのは、さるの危機察知能力だったのでしょう。
しかも、ログインを許されたのは、一般ユーザ権限のアカウント。

噂で聞いたことがあった、「一部のシステムフォルダは、その変更がキャッシュされる。」それだったようです。

キーワードは、「UACの仮想化フォルダ」・・・これは初耳でした。
どうも、使っているエディタでも動作が違ってくるらしいのです。
悪いことに、最初Notepadしかなかったので、後でsakuraさんを入れて使い出した途端ひどくなった。
(そもそも、Notepadでは「上書きできないよ」と出てた。)

対策方法です。全部必要かどうかはわかりません。

1).plの入っているCGIフォルダの[プロパティ]-[セキュリティ]で使っているユーザアカウントにフルコントロール権限を与えた。

2)sakuraさんに常に管理者権限で起動するプロパティを付けた。
-ここで、CGIフォルダの修正していたファイルをsakuraさんで開いたら、修正前のコードが表示された。

3)キャッシュされた実態は、以下にあるらしい。
 C:¥Users¥ユーザー名¥AppData¥Local¥VirtualStore¥Program Files (x86)¥ ・・・
 その対象のサブフォルダにあった最新と思われるファイルを本体にコピーした後、キャッシュフォルダは削除。

3つとも実行したら、現象はでなくなりました。
(ちょっと危険な環境 ということになるかもしれませんが、開発途中なので勘弁してもらおう。)


◆結局は内部表現か!(perlの内部表現でハマる)
前のSQLのエラー解決もここに繋がります。

内部表現?
なぜ、こんな厄介な仕組みがあるんだろう。まったく理解に苦しむ。
本当に必要なものなら、他の言語でも取り入れてそうなのだが、perlを使いだして初めて聞いた。

結局、DBD::Oracleを更新しても、ProgramFiles下のキャッシュを無効にしても、
INSTERクエリのORA-00911:エラーは消えません。
まだ、なんかあるの?
perlの内部表現とやらがよく理解できていないことに起因してました。

以下、解説してくれている色んなサイトの 「説明の要約」->ぼやき です。

・「外部(ファイル)等から取り込んだデータの文字コードが違っている場合があるから変換しないと化ける。」
 ->そりゃごもっとも。仕方ない。

・「perlは、だいたいはUTF-8でコードを書く。」
 ->否定はしません。何を標準とするかは自由。

・「コード上で定義した文字列は内部文字列/内部表現になる。」「内部文字列はflaged UTF8」
 ->なぜ別のコード系のようなものを使う?

・「外から来たデータはUTF-8コードを使ってたとしてもいちいちEncode::decodeしなきゃいけない。
 外に出すときもUTF-8でいいのにいちいちEncode::encodeしなきゃなんない。」
 ->決まりとあらば、仕方ないので仰せの通りにやってたが・・・

・「プラグマ「use utf8;」を入れて置くと、ソース内で定義した文字列もflaged UTF-8になる。」
 ->えっ?プラグマを入れない場合は、ただのUTF-8なの? それともflagedなの?
  これがあるとないとで、どう違う?

・「プラグマを入れると面倒だよ。」「入れたほうがいい。」
 ->どっちなんでしょうね。

・「flagedになっているかは、簡単には判別できない。」
 ->マジ? どっちになっているか簡単に見える方法ねーのはカスだよね。


「use utf8;」を入れてない前提で以下でした。

1)アップロードされて来るデータはShift-JISなので、これを Encode::decode('Shift_JIS', $updata)変換してた。
 そうすると、クエリ内でflagedとnot flagedが混在するようでORA-00911が出ます。
 上記の変換で外部から来たデータが内部表現になってます。
 Encode::from_to($updata, 'Shift_JIS', 'UTF-8')で内部表現にしないようにすると動きます。

2)その状態で、DBから読み込まれたデータは結局flagedになっていた。
 問題はここでした。つまり、クエリを作成するときのスカラ変数のほとんどはflagedじゃないUTF-8で、
 DBから取り出したデータ(半角数字)がflaged UTF-8だった。
 これを、連結してSQLテキストを作ったので、文字化けしていた模様。
 DB由来のデータをEncode::encode('utf-8', $(変数名))してから使ったら動作しました。

ちなみに、Devel::Peek::Dump($(変数名))を使うとそのデータ内容の解析結果がApacheのエラーログにダンプされます。
その中の「FLAGS」という項目にUTF8と記述があるとその文字列はflaged-utf8という意味だそうです。
上記の記述を.pl内に仕掛けてデバッグしました。

あーやれやれ。
もう、perlに関わるのはよほどのことでもない限りご遠慮させていただきたいですな。

では、この話はこの辺で。
m(__)m

スポンサーサイト





コメントの投稿

非公開コメント

カレンダー
01 | 2024/02 | 03
- - - - 1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 - -
プロフィール

さるもすなる

Author:さるもすなる
さるです。別HPサイト「さるもすなる」から侵食してきました。 山菜/きのこ、それとタイトルにしたPPバンド籠のことをメインに徒然に・・・・暇を持て余したさるの手仕事:男手芸のブログってことで。

最新記事
最新コメント
月別アーカイブ
カテゴリ
天気予報

-天気予報コム- -FC2-
本家のHPのトップ
山菜や茸の話です
PPバンドの籠作品と作り方です
投稿をお待ちしております



PVアクセスランキング にほんブログ村 にほんブログ村 ハンドメイドブログへ



マニュアルのお申し込み



検索フォーム
リンク
RSSリンクの表示
ブロとも申請フォーム

この人とブロともになる

QRコード
QR