カテゴリー別アーカイブ: データベース

WordPressの検索で検索結果が表示されなくて困った

WordPressの検索をいじっている最中に検索結果が出なくて困った。

pre_get_posts (にフックした関数)をいじっていたんだけれど、全然思ったような結果にならずに結構困っていた。

原因は色々あったんだけれど、二箇所で困ったのでメモしておく。

検索結果がでない、ということはSQLに問題がある。

なのでSQLの中身を確認してなぜ出ないかを検討することに。

以下のダンプをpre_get_postではなく、利用したいテンプレート側に記載。

$wp_query->requestは実行されたSQL文なんだけど、これはpre_get_postsの後に実行されたSQL文なので該当するテンプレートに記載しないと読み出せない。

<span style="line-height: 1.5;">var_dump($wp_query->request);</span>

まず一つはpost_typeが’post’にしかなっていない点。これはpre_get_postsの関数内で

$query->set('post_type', 'post');

を記載すること。これが実は結構やっかいで、他の条件文、例えばtax_queryなどと組み合わす際には無くても何とかなったりするので、条件文を組み立てている最中のデバッグと完成したカタチではpost_typeの指定が必要か不要か、結構迷うことになる。

post_typeに何も指定されていない場合に実際に中身を決定づけているのはwp-includes/query.phpのget_posts内でpost_type = ‘any’として処理していき、状況に応じていろんなのが入る。例えばattachmentとかpostなど。

このget_postsは結構大きなメソッドでこれまた結構読みづらいんだけど、詰まるたんびに読んでいればそのうち読めるようになるかも。

さて次いで検索結果がでない状況に当たったんだけど、これはSQL内に 0 = 1 なんていう文が。例えば下記のような感じに。

string(250) "SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND 0 = 1 AND wp_posts.post_type = 'custom_post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 5"

where句の二つ目にばっちり0 = 1の条件が。おかげさまで検索結果はゼロに。

wp-includes/query.phpのget_postsを丁寧に調べていくと下記の部分で付け加えられていた。

// Taxonomies
 if ( !$this->is_singular ) {
 $this->parse_tax_query( $q );

$clauses = $this->tax_query->get_sql( $wpdb->posts, 'ID' );

$join .= $clauses['join'];
 $where .= $clauses['where'];
 }

ここのtax_query->get_sql() の部分。

今回このpre_get_postsではtax_queryを使用していなかったのでどうやらそれが原因らしい。

結果としては今回対象となるページが http://www.example.com/search/ なんていう検索用のURLだったんだけど、searchを普通のpostのcategoryとして見做していたようで、get_posts内の$this->tax_queryという$qを元に作られたタクソノミクエリでは

object(WP_Tax_Query)#9 (2) {
 ["queries"]=>
 array(1) {
 [0]=>
 array(5) {
 ["taxonomy"]=>
 string(8) "category"
 ["terms"]=>
 array(1) {
 [0]=>
 string(6) "search"
 }
 ["include_children"]=>
 bool(true)
 ["field"]=>
 string(4) "slug"
 ["operator"]=>
 string(2) "IN"
 }
 }
 ["relation"]=>
 string(3) "AND"
}

という状態に。

$qや$parsed_query、$wp_queryを調べてみると’category_name’に’search’が設定されていた。

これはパーマリンク設定で記事名を/%category%/%postname%/のようにしていたことで、最初のスラッシュからスラッシュの間をカテゴリ名として認識していたから、のようだ。

なのでpre_get_postsの中で

$query->set('category_name', '');

としてやることで不必要なtax_queryを省くことができた。

複数人開発の時にデータベースの同期をうまく取る方法

最近「意外に若かったんだね」と言われるようになりました、その言葉の指すところについては考えないようにしています岡田洋一ですコンニチハ。

さて、僕はしばらくチームで開発を行っています。大人数というわけでも無く、2〜3人での小規模プロジェクトチームです。少人数制のチームではありがちで、僕以外のメンバーもプログラム〜インフラ周りも修正することが多く、データベースのテーブル構造を修正することが各自にありえます。

さてそんなときにデータベースの同期を取る方法について考えてみました。

現状はテーブルのCREATE分を出力させ、プロジェクトのwikiに書き込んでいます。

wikiは変更情報を保存しますので、ある程度のバージョン管理ができました。が、いかんせん「出力、編集、保存、確認、伝達、適用」と、修正内容をチーム内で共有するためのステップが多いのです。少人数チームでステップが多いのは良くないですね。素早さこそがウリですから。

次に「テーブルの差分になるALTERのみを書く」ということについて勘案しました。が、こちらも開発を兼ねているため、テーブルの変更となるALTER分はそこそこの数が出てしまい、すべてのALTERを記録して適用することは面倒となりました。素早さがなくなってしまいます。

 

というわけで、現在のテーブル情報を書き出しつつバージョン管理システムで共有、という手段にしました。

現在プロジェクトはSVNで管理されていたので、テーブル情報のみをファイルに書き出してコミット、それを手元へ適用という手段をとりました。ちなみにデータベースはMySQLです。

テーブル情報のみを書き出す為に必要なコードは

shell> mysqldump -h mysqlhost -u user -ppassword --no-data target_schema | sed 's/ AUTO_INCREMENT=[0-9]*//' > tables.sql

shell> svn commit tables.sql -m 'update table sql.'

となりました。mysqlのホスト名、ユーザ名、パスワード、対象スキーマは読み替えて下さい。パスワードはナシにするとプロンプトで聞いてきます。

パイプでsedに渡してAUTO_INCREMENTを消しています。

最後にこの出力をファイルに書き出して、コミットして共有します。

 

これだと差分をうまく見ることができるので便利ですね!

TeamSpeak3のserveradminのパスワードを忘れてしまった場合の対処法

TeamSpeak3のserveradminのパスワードを忘れてしまった場合の対処法について。

音声チャットソフトTeamSpeak3の管理用アカウントとしてserveradminがあるのだけれど、そのログインパスワードを忘れてしまったときの復旧方法について。管理用ログインを使うといろいろなことができるのだけれど、この管理用パスワードは恐らく初回インストール時、初回サーバログイン時に表示されてよくわすれちゃう。どこかのログに残っていそうなきがするのだけれど探しても見つからないし途方に暮れてしまっちゃう。で、そんなアナタに、そんなワタシにserveradminのパスワード復旧法。

まずは元になった記事の紹介。

A simple ts3 serveradmin password recovery script (win + linux)
というわけでパスワード復旧のスクリプトを配布してくれている人がいました。このスクリプトを使って復旧できます。

上記フォーラムの第一投稿にスクリプト(tar.gz)のリンクがあるのでダウンロードして解凍して実行。これでうまくいきます。中にはwindows用とlinux用のスクリプト、batファイルbashスクリプト、それからsqliteのバイナリが入っている。それぞれの環境で実行してやればうまくいくはず。

このスクリプトを実行すると管理者のパスワードをykN+zfqDに変えてくれる。これでこのパスワードを使ってログインする事ができる。管理者でのログイン方法については他の記事を参照してください

で、もちろん気をつけなければいけないことは、管理者用パスワードをログイン後に変えておくこと、が必要。これは絶対に必要。アナタのTeamSpeak3 serverを守るために必ず行っておいてください。

管理者用のパスワードの変え方については以下の通り。telnetでログインして上記のパスワードでログイン後、”clientsetserverquerylogin client_login_name=serveradmin”を実行する。これで新しいランダムなパスワードが発行される。このパスワードをメモしておきましょう。

login serveradmin ykN+zfqD
 error id=0 msg=ok</b>
 clientsetserverquerylogin client_login_name=serveradmin
 client_login_password=password
 error id=0 msg=ok

どんなスクリプトなの?

というわけで、管理者のパスワードを書き換えるという結構危ういスクリプトなので中身が気になりました。簡単なスクリプトなので中身を覗いてみることに。
中身はsqliteでのいくつかのSQLの発行している。clientsテーブルのclient_id=1な行をserveradminとしてそこのclient_login_passwordをupdateしている。

UPDATE clients
SET client_login_password = "r5oBZ3Z8s8IqjiEJ/k3o9dkSUgU="
WHERE client_id = "1";

更新している内容は恐らくmd5のハッシュだと思われる。ハッシュの元になっているのが先述の”ykN+zfqD”で、このハッシュにアップデートすることでパスワードを変更している。

なるほど、という感じでこれぐらいなら安心できることが確認できました。

FreeBSDとかだとsqliteのデータベースファイルの位置がわかりにくいのだけれど、そこへいって手動でsqliteを実行することでパスワードを更新できた。sudoで変更してあげれるようにしてやらないとダメだけど。

 

というわけで管理者で無事にログインできるようになります。

MySQL接続で起こった怪奇現象

今日も引き続いてzabbixの設定をしてました。そのなかでmysqlコマンドを利用してzabbixに接続をさせてあげたかったんです。で、その時のテスト時に不可解な現象が、理解不能な現象が起きました。

MySQL上でzabbixという名前のユーザを作り、ローカルホストのログインのみパスワード無しで接続可能にしたんですね。つまりTCP/IPとかの接続はダメでローカルのsock通信のみOK、ローカルからのmysqlコマンドでつなげばOKと。

ユーザを追加したので鼻歌でも歌いながら接続試験をしてみました。

mysqlhost> sudo -u zabbix mysql
ERROR 1045 (28000): Access denied for user 'hogetan'@'localhost' (using password: NO)

が、繋がらない。見てみるとaccess deniedしてる対象ユーザがsudo元のhogetanになってる!

これじゃ繋がらないよね。でもおかしい。通常sudoすればバイナリを実行するユーザは切り替わるはずなのに切り替わってないご様子。環境変数にその辺りが入ってるのかと思いチェックしてみるも…。

mysqlhost> sudo -u zabbix env| grep hogetan
SUDO_USER=hogetan

うーんむ、怪しい感じではないなぁ。USERとかはちゃんとzabbixになってたし。しょうがないのでnologinを解除してsuしてログインしてやってみることに。

mysqlhost> sudo su - zabbix; env | grep zabbix;
mysqlhost> mysql
ERROR 1045 (28000): Access denied for user 'hogetan'@'localhost' (using password: NO)
mysqlhost> env | grep hogetan

うーむ……。最後のenvはhogetanにヒットするものが無かった。一体ドコの情報を参照しながら元のsudoユーザ名でmysqlコマンドをつなごうとしてるのだろうか。

結局、結局結局、mysqlコマンドに-uでつながせることで落着はしそうなのですが、何とも腑に落ちない結末でした。

ちなみにですがsudoをするユーザをfugatanとかにすると確かに今度はfugatanでrejectされてるんですね。なのでmysqlコマンドがかならずhogetanユーザになる、というわけでもなさそうです。またmysqlホスト、sudoを行う場所をlinuxで試してみたらこれはちゃんと所望の結果が得られました。

FreeBSD特有の挙動なのかなぁと思いながら今夜も寝れなさそうです。

解決やお心当たりのある方は是非教えて下さい。

MySQLのvarchar型をintegerにcastしながらorderとかするとどうなるか

1. 始めに
MySQLを使っていてとある問題に当たった。文字列と数字列が混在するカラムで、数字のみの比較を取りたくなった。比較というか厳密にはmax()を取りたかった。
varchar型のカラムなのでmax()をするとそのカラムの中で9から始まる列が最も大きなカラムとして認識されてしまった。
例えば、1234 と 99 という二つの数字(カラムはvarcharなのでMySQL的には文字列)があり、そのカラムに対してmax()を取ると99がmaxとして出てきてしまった。
これは想像の通り、1234の1という文字と99の9という文字では9の方が文字として大きいとして認識されてしまったからだ。
そこでintegerにcastしたときにどんな動作になるか試してみた。

続きを読む MySQLのvarchar型をintegerにcastしながらorderとかするとどうなるか

wordpressでリモートにある環境にデータベースを転送したい

はめじに

wordpressでリモートにある環境にデータベースを転送したい。転送というか同期というか。

通常の環境であれば管理者メニューからエクスポート > インポートとすればすむ話なのだけれど、今回はちょっと変わった事情があった。

その条件ってのは下記の通り

  • 手元のサーバにあるワードプレスのデータベースの一部(コメントとかは上書きしないようにする)をリモートサーバに転送したい
  • リモートサーバにはsshでログイン可能
  • テータベースの一部のうち、post_metaにあるmeta_keyがviewsは上書きしたくない
  • 転送するのは一日に一回ぐらいのそこそこのペースで転送したい

リモートホストにsshでログインできるのでsshとパイプを使ったワンライナーでやってしまおう的な。

またpost_metaのテーブルはちょっとやっかい。今回利用しているプラグインでWP-PostViewsという閲覧数を保管するプラグインがある。これは閲覧数をpost_metaにmeta_key = viewsで管理している。だからそこをハズしたい。

キャプチャ4

というわけでさっくり

mysqldump –h localdbhost –u localuser –plocaldbpassword  --add-drop-table wpdbname wp_options wp_posts wp_postmeta wp_term_relationships wp_term_taxonomy wp_terms wp_users wp_usermeta | ssh [email protected] mysql -f -h remotedbhost -u remotedbuser –premotedbpassword  remotewpdb

これでひとまずpost_meta以外はいけた。

データがちょっと複雑なpost_meta

post_metaに関してはmysqldumpにwhere句が使えるらしいので使ってみる。

次にpost_metaをうまく通してやるためにオプションを調整してやる。

mysqldump –h localdbhost –u localuser –plocaldbpassword   --add-drop-table=false --extended-insert=false --where="meta_key!='views'" mysqldump –h localdbhost –u localuser –plocaldbpassword wp_postmeta  |  ssh [email protected] mysql -f -h remotedbhost -u remotedbuser –premotedbpassword remotewpdb

キャプチャ

--add-drop-table=false

これは対象テーブルのdrop table – if exists文を出力に含めないようにするためのオプション。

基本的にはテーブルがあるっていう前提なのでcreate-infoごといらないかもしれない。(create文ごとなくすには—no-create-infoを付けるといい)

今回は安全に動作(運用)するために付けておくことにする。ここで与えたオプションはlong形式の—add-drop-tableなんだけど、実は引数でtrue falseが与えられるらしい。よくありがちなのは—no-add-drop-tableみたいに対応してる気がしてたんだけどそうじゃないみたい。

公式には書いてないので気がつかなかった。っていうか引数ありって本当意外。

  • --add-drop-table

DROP TABLEステートメントをCREATE TABLEステートメントの前に追加します。
MySQL ::   MySQL 5.1 リファレンスマニュアル :: 7.12 mysqldump — データベースバックアッププログラム

--where=”meta_key!=’views'”

これはmeta_keyで上書きしたくない条件を指定。今回は手元のデータのうち閲覧数は上書きしたくなかった。sqlだったらすんなりwhere句書いちゃえば楽なのになーって思ったらmysqldumpでもwhereが書けるらしい。

  • --where='where_condition', -w 'where_condition'

あるWHERE状態に選択された行のみダンプします。ユーザのコマンドインタープリタにとって特別なキャラクタ、もしくはスペースを含んでいる場合、状態の周りをクオートで囲まなければいけません。

MySQL :: MySQL 5.1 リファレンスマニュアル :: 7.12 mysqldump — データベースバックアッププログラム

–extended-insert=false

キャプチャ2

このオプションも引数を取るらしい。このオプションは

  • --extended-insert, -e

複数のVALUESリストを含む、複数行INSERT構文を使用してください。これにより、ダンプファイルサイズを小さくし、ファイルが再ロードされる際の挿入スピードがあがります。

MySQL :: MySQL 5.1 リファレンスマニュアル :: 7.12 mysqldump — データベースバックアッププログラム

となっている。いわゆるbulk insertとかmultiple-row insertsとか言われてるあれにするようなやつですね。

insert hoge (id, hoge) values (1, hoge), (2, hoge1), (3, hoge), (4, hoge);

で、コレって結構早くする上では重要なものなのです。このオプションがonでないと、bulk insertが有効になっていないと、かなり遅くなります。なのですが後述するmysql -fと組み合わさって今回の運用上はoffにしてないとうまく目的を達成できません。

今回の運用上、というところはどういったところなのか。

さっきの例だとid, hogeという二つのカラムに対して四つの行を一つのinsert文でinsertしようとしてる。で、カラムの名前から分かるように、idというのはpkeyなのでunique制約がある。となるとき、上のinsertだとduplicateでinsertできないことがある。もともとid=1な行があるときとか。で、そういうのは無視しながら必要な行だけをinsertしたいので、これらのinsert文を分けて書く必要がある。なのでこのオプションを有効にしてやる。するとこんな感じのinsert文になる。

insert hoge (id, hoge) values (1, hoge);

insert hoge (id, hoge) values (2, hoge1);

insert hoge (id, hoge) values  (3, hoge);

insert hoge (id, hoge) values (4, hoge);

これでid=1は通らなくても他のinsert文は通ってくれるので期待通りの結果になるかな!

mysql –f

受け側のオプション。エラーがあっても処理を続行する。先のcreate文ですでにtableが存在しても続けたり、insert文でduplicateな行だったとしても続けてくれる。おかげさまでイケるようになります。

おわりに

これでcronに入れとけばテキトウに同期してくれそう。sshでパスワードのプロンプトをなくすために公開鍵をやりとりする、とかやる必要はあるけれど。

でもこれできっと便利。しらばらくは手でやってみるけど。

LDAPを利用したFreeBSDでMySQLが起動しない

LDAPを利用したFreeBSDでMySQLが起動しない。

shell> /usr/local/etc/rc.d/mysql-server start
Starting mysql.
UID must be greater than 1000
su: Sorry

エラーメッセージの通り、ldapに登録されているmysqlユーザがuidNumberが88で1000以下になっている。なのでうまくうごかないみたい。

shell> more /usr/local/etc/ldap.conf


pam_min_uid 1000

となっていたので pam_min_uid をコメントアウト。うまくいった。

Courierが利用するaliases.datの中身を確認する

1. Courierのaliasシステム

現在、メールのMTAとしてcourierを利用している。courierさんはメールを配送する際、aliasというメールアドレスに別名を付ける仕組みを持っている。

この仕組みがあると仮想的にメールアドレスを増やせたりして結構便利だったり。もともとsendmailにあったものをそのまま引き継いでいるような。

で、このaliasを利用するにはちょっと一癖ある。

続きを読む Courierが利用するaliases.datの中身を確認する

Debianでtritonnをインストールしようとしたらコケた

debianでtritonn, senna, mysqlをインストールしようとコンパイルしたらコケた。

バージョンは tritonn-1.0.12-mysql-5.0.87

このバージョンだとFreeBSDでもコンパイルが通らなくて、linuxならいけるでしょ!って思ったら無理だった。

しょんぼりしながら湯バード先生に泣きついて、ググってもらって情報が出てきたり。

ありがとう!湯バード先生!

続きを読む Debianでtritonnをインストールしようとしたらコケた

Ubuntu 11.10でmysql-workbenchがインストールできない

Ubuntu 11.10にmysql-workbench 5.2.35を入れようと思ったら11.04のパッケージしか無い。まぁ入るだろうと思って

sudo dpkg-i mysql-workbench-gpl-5.2.35-1ubu1104-amd64.deb

して、要求されるパッケージを入れたけど、ダメ。
ソースから入れようと思ってconfigureで要求されるライブラリを一つ一つ入れて行ったけど、Cの構文エラーは出るし宣言エラーは出るし全然通らない。

で、数時間の探し回ったら パッチ
コンパイル方法の解説を発見
手順どおりにソースを展開したディレクトリで

% sudo apt-get install build-essential autoconf automake libtool libzip-dev libxml2-dev libsigc++-2.0-dev libglade2-dev libgtkmm-2.4-dev libglu1-mesa-dev libmysqlclient15-dev uuid-dev liblua5.1-dev libpcre3-dev g++ libglade2-dev libgnome2-dev python-pexpect libboost-dev libsqlite3-dev python-dev libgnome-keyring-dev libctemplate-dev
% patch -p0 < ../5.2.35-ubuntu_11_10_compile_fix.patch
% ./autogen.sh -prefix=/usr/local
make

パッチを当てないとコンパイルが出来ても
起動スプラッシュでフリーズするらしい
makeはCore2Quad 2.4GHzのメモリ4GBで30-40分かかるらしい。