更新状況

4月 26

MySQLで外部SQLファイルを読み込ませてデータベースやテーブルを作成するときによく使うのが、下記のコマンド。

mysql> SOURCE <外部ファイル名>;

この「SOURCE」がくせ者で、MySQLクライアントのビルドインツールなんですぅ。(ようは、MySQLクライアントの構文内でのみ有効ってこと)
MySQL Connector/C++なんかでプログラムを組むときやMySQL Query Browserで試験的にクエリを実行したいとき、この「SOURCE」をクエリとして使えると勘違いしやすいんです。
mysql クライアント上ではさもクエリのように振る舞うのに、プログラム内では「シンタックスエラー」となり、思考がスパイラル状態になります。

どうしても、プログラム内から外部ファイルで管理されているSQLを実行したい場合は、

    • 外部ファイルを読み込んでクエリをぶん投げる。
      [難点] 外部SQLファイルを同時配布したくない場合にどうするか。
    • mysql 構文をシェル(コンソール)プロセスに直接ぶん投げる。
      [難点] 外部プロセス発動になるので管理が面倒。
    • (時と場合によっては)ストアドプロシージャを使う。
      [難点] 後からアタッチするようなサブプログラムの場合、組み込むために外部からの供給が必要となり、堂々巡り。

だと思う。(他にもあると思うが手っ取り早い順で・・・)

開発にあたっていろいろ制約があり、外部SQLファイルは同時配布しないことになっていたので、外部のSQLファイルをプログラム内のリソースに取り込み、読み込んだリソーステキストをサーバにぶん投げることで「SOURCE」と同じような処理を実現したでやんす。まだ未検証だけど、読み込む外部ファイル(今回はリソースとして組み込んだファイル)のコメント等は無い方がいいか、もしくは読み込んだ先で余分なものを取っ払ってからクエリを投げた方が無難じゃないかなぁ~と思われ。

今回特殊な事情で、MySQLの「CREATE~FEDERATED」を使用しなくちゃいけなかったんだが、その話はまた後で出来ればと。

written by hiro \\ tags: , ,

5月 20

興味本位でWindowsでOpenLDAPのデータでも操作してみようかな~とプログラミングを開始してみた。基のソースはMicrosoft MSDNのライブラリからパクってきたのだが。(興味がある方はリンクにアクセスしてね)

で、いきなりこのソースの注意点ですが、構造体:SEC_WINNT_AUTH_IDENTITYのPasswordは素のパスワードでは通用しないと思われ、HASH化した文字列が必要と思われます。パスワードのHASH化はslappasswdで得ることが可能ですので調べてくださいな。素のパスワードでアクセスしたい場合は、ldap_simple_bind_s()もしくはldap_simple_bind()を利用してくださいな。

さて、順調にこのソースを咀嚼していき、何とか情報を引き出し表示させるまでに。そこで、表示されているデータをジッと見つめていると、アトリビュート(属性)数は可変だし、配下のデータ数も可変。
さて、どうやってローカルに取り込もうか思いあぐねた結果、構造体+std::vectorにすることに。理由は、vectorを利用することで可変データに対応するため。(無駄なメモリ使いたくないし、解放操作も面倒だしw)
構造体は下記のような入れ子。(データ構造は本文最後の方で図を掲載してあります)

struct LDAP_ITEM
{
	string					attr;		// 属性名
	vector<string>		item;	// 属性配下のアイテム(vectorで可変に)
};

struct LDAP_DATA
{
	int						entrynum;	// エントリ番号
	vector<LDAP_ITEM>	data;		// エントリデータ内容(上記入れ子構造体をvectorで可変に)
};

で、次にどうやってこの構造体にデータをぶち込んでいるかは以下のソース。Microsoftのソースコードを基にしているので何を追加・変更しているかは大体察しが付くと思いますが、簡単にコメントも入れてみました。
ソースが長いので、「ソースを表示」で伸張してくださいな。

	//----------------------------------------------------------
	// Get the number of entries returned.
	//----------------------------------------------------------
	ULONG numberOfEntries;

	numberOfEntries = ldap_count_entries(pLdapConnection, pSearchResult);

	if(numberOfEntries == NULL)
	{
		printf("ldap_count_entries failed with 0x%0lx \n",errorCode);
		ldap_unbind_s(pLdapConnection);
		if(pSearchResult != NULL)
			ldap_msgfree(pSearchResult);
			return -1;
	}
	else
		printf("ldap_count_entries succeeded \n");

	printf("The number of entries is: %d \n", numberOfEntries);

	//----------------------------------------------------------
	// Loop through the search entries, get, and output the
	// requested list of attributes and values.
	//----------------------------------------------------------
	LDAPMessage* pEntry = NULL;
	PCHAR pEntryDN = NULL;
	ULONG iCnt = 0;
	char* sMsg;
	BerElement* pBer = NULL;
	PCHAR pAttribute = NULL;
	PCHAR* ppValue = NULL;
	ULONG iValue = 0;

	// コメント:入れ子構造体データの一時的な保管場所。
	// これをあとで下の元データの一時的な構造体にpush_back。
	LDAP_ITEM		tmp_item;
	// コメント:元データの一時的な保管場所。あとで実データにpash_back。
	LDAP_DATA		tmp_ldap;

	for( iCnt=0; iCnt < numberOfEntries; iCnt++ )
	{
		// Get the first/next entry.
		if( !iCnt )
			pEntry = ldap_first_entry(pLdapConnection, pSearchResult);
		else
			pEntry = ldap_next_entry(pLdapConnection, pEntry);

		// Output a status message.
		sMsg = (!iCnt ? "ldap_first_entry" : "ldap_next_entry");
		if( pEntry == NULL )
		{
			printf("%s failed with 0x%0lx \n", sMsg, LdapGetLastError());
			ldap_unbind_s(pLdapConnection);
			ldap_msgfree(pSearchResult);
			return -1;
		}

		tmp_ldap.entrynum = iCnt;    // コメント:エントリ番号を保存。

		// Get the first attribute name.
		pAttribute = ldap_first_attribute(pLdapConnection, pEntry, &pBer);

		// Output the attribute names for the current object
		// and output values.

		while(pAttribute != NULL)
		{

			// コメント:一時的なアイテム保管場所。
			//                   アイテムがいくつあるか分からないのでvector使用。
			vector<string>		tmpItem;
			// コメント:属性を保存。
			tmp_item.attr = pAttribute;    

			// Get the string values.
			ppValue = ldap_get_values(pLdapConnection, pEntry, pAttribute);

			// Print status if no values are returned (NULL ptr)
			if(ppValue == NULL)
			{
				printf(": [NO ATTRIBUTE VALUE RETURNED]");
			}

			// Output the attribute values
			else
			{
				iValue = ldap_count_values(ppValue);
				if(!iValue)
				{
					printf(": [BAD VALUE LIST]");
				}
				else
				{
					tmpItem.push_back(*ppValue);    // コメント:最初のアイテムを保存。

					// Output more values if available
					ULONG z;
					for(z=1; z<iValue; z++)
					{
						// コメント:残りのアイテムがあれば保存。
						tmpItem.push_back(ppValue[z]);    
					}
					// コメント:push_backされてきたアイテムを入れ子構造体の
					//                   一時保管場所構造体データに。
					tmp_item.item = tmpItem;    
					// コメント:一時的なアイテム保管場所のvector要素を消去。
					//                 (解放ではないですよ。かつ<vector>.clear()でもない)
					tmpItem.erase(tmpItem.begin(), tmpItem.end());    
				}
			}
			// コメント:直前で入れ子の一時データができているので、
			//                   一時的な本営データ保管場所にpush_back。
			//                   なぜここで本営データに入れないかは本文で。
			tmp_ldap.data.push_back(tmp_item);    

			// Free memory.
			if(ppValue != NULL)
				ldap_value_free(ppValue);
			ppValue = NULL;
			ldap_memfree(pAttribute);

			// Get next attribute name.
			pAttribute = ldap_next_attribute(pLdapConnection, pEntry, pBer);
		}
		// コメント:本営データにpush_back。
		ldap_data->push_back(tmp_ldap);    
		// コメント:本営データに入れたので一時データの要素を消去し、for続きへ。
		tmp_ldap.data.erase(tmp_ldap.data.begin(), tmp_ldap.data.end());    

		if( pBer != NULL )
			ber_free(pBer,0);
		pBer = NULL;
	}

	//----------------------------------------------------------
	// Normal cleanup and exit.
	//----------------------------------------------------------
	ldap_unbind(pLdapConnection);
	ldap_msgfree(pSearchResult);
	ldap_value_free(ppValue);
	return 0;

上のソースで、アイテムが全て揃ったところで、なぜ本営データにぶち込んでいないかと言えば、まだ属性データがあった場合、属性ごとに本営データができあがってしまい、意図する動作にならないためです。つまり、「1つのエントリに対し、複数の属性、1つの属性に対し複数のアイテム」といった可変データ(1xNxN)構造と微妙に異なるからなんです。

で、ここまできたところで、「ldap_data」ってどこで宣言してるんじゃ~ってお怒りかも知れませんが、本営データはこの関数に引数として渡ってきます。

int	GetLDAPData(const char* pHost, std::vector<LDAP_DATA> *ldap_data)

では、この関数の呼び元と出力事例のソースは以下の通りでやんす。
ソースが長いので、「ソースを表示」で伸張してくださいな。

	// 本営データ作成(ここではメモリ確保もせずほったらかし)
	vector<LDAP_DATA>	ldap;
	// 本営データのアドレスを引数で渡してあげます
	GetLDAPData(ldap_host.c_str(), &ldap);

	// データがあったら表示を開始します
	if(!ldap.empty())
	{
		// 本営データに対するイテレータを作成しポインターもどきにします
		for(vector<LDAP_DATA>::iterator iarray = ldap.begin(); iarray<ldap.end() ; iarray++)
		{
			cout << "ENTRY NUMBER: " << iarray->entrynum << endl;
			// 入れ子構造体データに対するイテレータを作成しポインターもどきにします
			for(vector<LDAP_ITEM>::iterator iattr = iarray->data.begin(); iattr<iarray->data.end() ; iattr++)
			{
				cout << "	ATTR: " << iattr->attr << ":";
				// アイテムデータに対しイテレータを作成しポインターもどきにします
				for(vector<string>::iterator iitem = iattr->item.begin(); iitem<iattr->item.end() ; iitem++)
				{
					cout << " " << *iitem;
					if(iitem<iattr->item.end()-1)
						cout << ",";
				}
				cout << endl;
			}
		}
	}

データ表示部分で、本営、入れ子構造体、アイテムに対しそれぞれイテレータを作成していますが、こうしておけば独立した形(表現は厳密ではないけれど)で各データにアクセス出来ます。配列カウンターで悩むことがないので便利なんです。まぁ、配列ポインタと変わりはないんですけど、プログラムを書く好みでdata[i][j][k]とか好きな方ではないので。OpenLDAPのデータ構造はこんな感じ。

OpenLDAPデータ構造

足早にざっと書いては見たけれど、OpenLDAPのアクセスなんて誰もやろうなんて考えないよね。と言うことで備忘録ということで。

written by hiro \\ tags: , , , ,

6月 26

このサイトは、当方の自作サーバーで動いているので、レンタルサーバーのような制約が無い。
レンタルサーバを借りたことがないので、どこまでレンタルサーバーでどこまで制約があるか分からないが・・・
以下は、レンタルサーバーでも出来るかもしれないが、基本的に自鯖の方用ということで。
また、WordPress Database Backupプラグインを入れている方も不要と思われるんですが、データベースが大きくなると、ブラウザでの処理中にタイムアウトが発生する可能性があります。(タイムアウトするほどポストしていないので何とも言えませんが・・・)

日々のデータベースバックアップをどうするかというと、mysqldumpコマンドを使ってごっそり引っこ抜いてしまうという手法を使います。このコマンドで得られたダンプファイルというのは単なるテキストファイルなんですが、このフォーマットはリストアするときにMySQLが分かるような書式になっています。ということなので、このテキストファイルを圧縮し、メールで送れれば、ほぼWordPress Database Backupプラグインと同じことをブラウザのタイムアウトを気にせずバックアップを取ることが可能でやんす。
(当方はこのダンプファイルがテキストファイルであるということで、ブログの引っ越しにこの性質を利用して手間を掛けずに数時間で引っ越しを終わらせました。このことは後で記事にでもしてみます)

さて、では順を追って簡単に説明します。

まずは、mysqldumpコマンド例は以下の通りです。

# mysqldump [DB name] --host=[hostname] -u [DB user name] --password=[DB password] > [backup file name]

で、このままだと手動でコマンドを叩くしかないので、cronに任せたくなりますが、そこをちょっと我慢してメールの送信まで一気に処理しちゃうスクリプトを書いちゃいましょ。

基本的にダンプファイルを作るまではシェルスクリプト(bash)で、メール送信だけPerlスクリプトで書きました。Perl部分は[Bird-Soft]さんのコードを丸写しなので合体させました・・・orz

とりあえず、スクリプト全体のコードはこれ。(エラー処理は端折ってます)

««««« 続きはこちら »»»»»

written by hiro \\ tags: , , , , , , ,