2023年9月22日金曜日

コマンドプロンプトとCURLのエスケープ

コマンドプロンプトのエスケープは、\ と " がエスケープ処理の対象となります。

CURL のエスケープは、\ { } | がエスケープ処理の対象となります。

\ をエスケープすると、コマンドプロンプト用のエスケープとCURL用のエスケープが複合し、\\\\という形になります。

ふむふむ…
ふむふむ…
ふむふむふむふむ?…

コマンドプロンプトは \\\\ を " として取り扱います…

(`□′)╯┴┴

2023年6月23日金曜日

atldbcli.h のバグを踏んだ

atldbcli.h の CCommand<CDynamicStringAccessor> を利用していたコードが null pointer exception で落ちるようになった。
ほぇ?(#^ω^)ピキピキ

デバッガでトレースしていくと、m_pAccessor のメンバが nullptr で初期化されないまま放置されていた。
で、Accessor クラスは、どこに行った?
おそらく、class template refactoring により、自分自身に継承する形になったようです。

なので、CCommand<CDynamicStringAccessor>::SetAccessor 関数を使用して自分自身を設定する必要がありました。
せっかくなので、使い方のサンプル込みで、紹介です。
#include <iostream>
#include <objbase.h>
#include <atldbcli.h>

int main(int argc, char* argv[] )
{
  HRESULT hr = CoInitialize(NULL );
  if(FAILED(hr)) {
    std::cerr << "Fail to Initialize COM:" << hr << std::endl;
  }

  CDataSource ds;

  // OLEDBによるODBC接続文字列
  hr = ds.OpenFromInitializationString(
     L"Provider=MSDASQL.1;Password=password;"
     L"Persist Security Info=True;"
     L"User ID=username;"
     L"Data Source=odbc_data_source_name;"
     L"Extended Properties=\"DSN=data_source_name;UID=username;PWD=password\""
  );

  CSession ss;

  CCommand<CDynamicStringAccessor> rs;

  hr = ss.Open(ds );
  hr = rs.Open(ss, "select * from hoge" );

  // CCommand<typename T>::m_pAccessor を初期化するコードが欠落しているため
  // 自分自身を設定する必要がある(どこかのタイミングで混入したバグ対応)
  // 2023/7/13 修正されたような気がしますが、下記コードは正しくなくなった感じがします。
  // 仕様がよくわかりません。
  rs.SetAccessor(&rs);

  hr = rs.MoveNext();
  if( FAILED(hr) || hr == DB_S_ENDOFROWSET ) {
    std::cerr << "Fail to MoveNext: " << hr << std::endl;
  }
  // Get the column information
  ULONG ulColumns = rs.GetColumnCount();
  
  DBTYPE dbtype;
  rs.GetColumnType( 1, &dbtype );
  dbtype &= 0x3FFF;	// DBTYPE_BYREF を除去	
  if( 
       dbtype == DBTYPE_I4
    || dbtype == DBTYPE_UI4
    || dbtype == DBTYPE_R8
    || dbtype == DBTYPE_STR
    || dbtype == DBTYPE_WSTR
    || dbtype == DBTYPE_DECIMAL
    || dbtype == DBTYPE_VARNUMERIC
    || dbtype == DBTYPE_NUMERIC
  ) {
    // よくあるフィールド型
  } else {
    // 対応可能か検討が必要なフィールド型
    std::cerr << "Un supported dbtype: " << dbtype << std::endl;
  }

  int nMax = 10;
  while(nMax) {
    std::string strData;
    for( DBORDINAL col = 1; col <= ulColumns; col++ ) {
      DBSTATUS dStatus;
      DBLENGTH nLength;
      rs.GetStatus( col, &dStatus );
      rs.GetLength( col, &nLength );
      if( col > 1 ) {
        std::cout << ", ";
      }
      if( FAILED( dStatus ) ) {
        std::cout << "<<ERROR>>";
      } else if( dStatus == DBSTATUS_S_ISNULL ) {
        std::cout << "<<NULL>>";
      } else {
        std::cout << rs.GetString(col);
      }
    }
    std::cout << std::endl;
    hr = rs.MoveNext();
    if( FAILED(hr) || hr == DB_S_ENDOFROWSET )  break;
    --nMax;
  }

  rs.Close();
  ss.Close();
  ds.Close();
  CoUninitialize();

  return 0;
}
2023/07/13 追記: バグは修正されたのか、仕様が変わったのか、よくわからない状況になりました。