2013年8月29日木曜日

SQL Trigger ではまった

今回のバグは難解でした。Sybase SQL Anywhere のデータベースのバージョンをアップしたら、なんか、変なところでエラーになりました。

伝票 テーブル名 SLIP のレコード新規作成で insert 文を発行しているのですが、別の帳票を印刷すると、レコードの挿入に失敗してしまいます。
帳票のコード中で、現象を引き起こす部分を特定したところ、全く関係のない 日別伝票集計 テーブル名 DSLIP を SELECT * FROM DSLIP WHERE RDATE='2013/8/29'; という極めてシンプルなクエリーをオープンして、クローズするだけで発生していました。
訳がわからなくて、コンポーネントを別にしてみたり、接続をクローズしてオープンしてみたり、SELECT文末に FOR READ ONLY を加えてみたりしましたが、エラーは直りません。もうお手上げに近い感じですが、ふとトリガー?というキーワードが浮かびました。それでも原因がわからなくて、助っ人を頼んで原因を特定できました。
よくある事ですが、日別で伝票番号をシーケンシャルで発生させたいという要望から、DSLIP 中に伝票番号の最大値を持たせて SLIP の INSERT TRIGGER で伝票番号を発生させていました。
問題のトリガーは、こんな感じ
ALTER TRIGGER "insert_slip" before insert order 1
on "DBA".SLIP
referencing new as new_val
for each row
begin
  declare AINC smallint;
  declare thisDSlip cursor for select SCNT from DSLIP where RDATE=new_val.RDATE;
  -- %IF CURRENT REMOTE USER IS NULL THEN
  if new_val.SLIPCD is null then
    if not exists(select* from DSLIP where RDATE=new_val.RDATE) then
      insert into DSLIP(RDATE,LDATE) values(new_val.RDATE,TODAY(*))
    end if
    ;
    open thisDSlip;
    fetch first thisDSlip into AINC for update;
    set new_val.SLIPCD=AINC;
    update DSLIP set SCNT=AINC+1,LDATE=TODAY(*) where current of thisDSlip;
    close thisDSlip
  else
    if not exists(select* from DSLIP where RDATE=new_val.RDATE) then
      insert into DSLIP(RDATE,LDATE,SCNT) values(new_val.RDATE,TODAY(*),new_val.SLIPCD)
    end if
    ;
    open thisDSlip;
    fetch first thisDSlip into AINC for update;
    if AINC<new_val.SLIPCD then
      set AINC=new_val.SLIPCD;
      update DSLIP set SCNT=AINC+1,LDATE=TODAY(*) where current of thisDSlip
    end if
    ;
    close thisDSlip
  end if
  ;
  if(ISNULL(new_val.RENT,0)<>0) or(ISNULL(new_val.EMFLG,0)<>0) then
    set new_val.ACFLG=5
  end if
  -- %END IF;
end;

はい、cursor 宣言がまずくて
  declare thisDSlip cursor for select SCNT from DSLIP where RDATE=new_val.RDATE FOR UPDATE;
というように更新をかけるタイプのカーソルですよと、FOR UPDATE 句が必要でした。これが抜けていたために、データベースの状況により読み取り専用カーソルに対して更新をかけるという事態が発生し、エラーを引き押していたと。

2013年8月8日木曜日

「工場夜景・美の祭典」フォトコンテスト2013に応募しますた

「工場夜景・美の祭典」フォトコンテスト2013に応募しました。 3作品まで登録でき、2作品エントリーしました。
1作品目
2作品目
もう1作品、枠があるので、また撮影に行きたいと思います。
コンテストも視野に入れつつ、最初に大雨の中撮影してきたのが、こちらになります。ゴアテックスの雨合羽を着て自転車で移動してましたが、全身びしょ濡れになって、これ以上の撮影は無理!帰ろうと思ったら、大雨でJRが全面ストップw。帰れないけど、自転車での移動は、もう完全に無理。どうしよう?と悩んだ末、歩いて往復22km移動して撮りました。歩いてる途中も雨が激しく降ってきたり、トラックに水をぶっかけられたりして、精神的にも肉体的にも、かなり疲弊しました。いざ撮影してみると、三脚を使っているにもかかわらず、風が強くてブラケット撮影している明るい画像のシャッター速度が長すぎて、ブレブレの写真しか撮影できません。雨の中のレンズ交換では、ローパス・フィルターが汚れる可能性が高いので、やりたくなかったのですが、このままでは勝負にならないと思い、F値が低い単焦点レンズに交換する決意をしました。さすがに、こちらのレンズだとシャッター速度も短い!神経を研ぎ澄ませて、一番低いF値のコンディションでマニュアル・フォーカスで撮影しました。後から考えれば、シャッター速度も余裕があったので、F値を上げて(もう少し絞りを入れて)撮影すれば良かった。これらの写真ではパープル・フリンジが盛大に発生しています。それに、ISOを上げて捨て写真を撮りピントのレベルをチェックする余裕も欲しかったかなぁと。まぁ、パソコンで写真を確認するまで、どんな風に仕上がってるのか、カメラのモニターだけでは、わからないんですけどね(^^;






エントリーした写真は、リベンジで夕景を狙いに行ったものです。撮り終えて、白鳥大橋の反対側に自転車で移動しよう(東室蘭経由)と思ったら、自転車の後輪がパンクしてしまい、ゲームセットとなりました。予定では、この後、深夜の夜景を撮影して漫画喫茶で仮眠して、朝焼けを狙う予定だったのに…。そんなわけで、残り1作品も撮りに、リベンジしたいと考えております。

2013年8月1日木曜日

boost::spirit grammar 書くのが、めんどくなってきたのでテスト

はい、boost::spirit も コマンドによって、引数となる構文構造が異なってくると、いちいち grammar 作成するのも大変ですよね? てなわけで、ちまちま phrase_parse にかけながら、コードで処理を分岐しても良いんじゃないかと思い、実験くんしてみました。
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>

namespace qi = boost::spirit::qi;

int main() {
  std::string test = "put 123 aioue";
  
  auto i = test.begin();
  bool res = qi::phrase_parse( i, test.end(), qi::lit("put"), qi::ascii::space );
  std::cout << res << std::endl;
  std::string rest;
  rest.assign(i,test.end());
  std::cout << rest << std::endl;
  
  return 0;
}
出力は
1
123 aioue
いちおう 構文を qi::lit("get") に変えてみたところ
0
put 123 aioue
となりまして、ちまちまとパースしても行けそうですよね?
ちょっとしたものならば、こちらの方がササッと書けそうです。