インストーラのMySQL4.0系対応

プログラム(機能)関連の開発の話題
アバター
yama
管理人
記事: 3082
登録日時: 2009年7月29日(水) 02:50

インストーラのMySQL4.0系対応

投稿記事by yama » 2010年4月10日(土) 15:25

MySQL4.0環境でMODxのアップデートを試みたところ、チャンクやスニペットなどエレメンツまわりがアップデートされないことを確認しました。調べてみたところ、MySQL4.0系ではサポートされていないサブクエリを用いているのが原因のようです。

コード: 全て選択

$rs = mysql_query("REPLACE INTO $dbase.`" . $table_prefix . "categories` (`id`,`category`) ( SELECT MIN(`id`), '$category' FROM ( SELECT `id` FROM $dbase.`" . $table_prefix . "categories` WHERE `category` = '$category' UNION SELECT (CASE COUNT(*) WHEN 0 THEN 1 ELSE MAX(`id`)+1 END ) `id` FROM $dbase.`" . $table_prefix . "categories` ) AS _tmp )", $sqlParser->conn);

たとえばこの部分。これをサブクエリを用いない形式に書き換えるとよいのですが、SQL文は書き慣れないので苦戦中です。何をしようとしてるのか自体が理解できてないですが・・
sama55
メンバー
メンバー
記事: 816
登録日時: 2009年8月03日(月) 08:16

Re: インストーラのMySQL4.0系対応

投稿記事by sama55 » 2010年4月11日(日) 08:28

MySQL4.0と聞いて、MODxの保証環境が気になったので本家の下のページを見たところ、「MySQLは4.1.20以上」となってます。
General Requirements

何時からこの線引きが行われたのかは分かりませんが、4.0は保証範囲外という前提で組まれてるとすると、インストーラ以外でもV4.1依存の処理があると考えた方が良さそうなので、この件は対応する必要はないように感じますます。いかがでしょ?

また、公式サイトの下のページも要変更ですね。
MODx Evolution 1.0.3 日本語版
アバター
yama
管理人
記事: 3082
登録日時: 2009年7月29日(水) 02:50

Re: インストーラのMySQL4.0系対応

投稿記事by yama » 2010年4月11日(日) 09:00

sama55 さんが書きました:MySQL4.0と聞いて、MODxの保証環境が気になったので本家の下のページを見たところ、「MySQLは4.1.20以上」となってます。
General Requirements

何時からこの線引きが行われたのかは分かりませんが、4.0は保証範囲外という前提で組まれてるとすると、インストーラ以外でもV4.1依存の処理があると考えた方が良さそうなので、この件は対応する必要はないように感じますます。いかがでしょ?

また、公式サイトの下のページも要変更ですね。
MODx Evolution 1.0.3 日本語版

インストーラ以外では4.1依存の処理は存在しません。インストーラと一部のアドオンだけですね。インストーラのこの部分も、MODxの性能を発揮するためにこう書かなければならないというものでもないので、対応したほうがよいと思います。できればですが・・・
アバター
enogu
メンバー
メンバー
記事: 32
登録日時: 2009年9月02日(水) 23:56

Re: インストーラのMySQL4.0系対応

投稿記事by enogu » 2010年4月11日(日) 22:21

yamaさんに事情を伺って軽くインストーラーのSQLを拝見しました。

instprocessor.phpの316行目

コード: 全て選択

$rs = mysql_query(
"REPLACE INTO $dbase.`".$table_prefix."categories` (`id`,`category`) ( SELECT MIN(`id`), '$category' FROM ( SELECT `id` FROM $dbase.`" . $table_prefix . "categories` WHERE `category` = '$category' UNION SELECT (CASE COUNT(*) WHEN 0 THEN 1 ELSE MAX(`id`)+1 END ) `id` FROM $dbase.`" . $table_prefix . "categories` ) AS _tmp )", $sqlParser->conn);

変数だらけで読みづらいですが、実際のSQLに近づけるとこうなります。

コード: 全て選択

REPLACE INTO modx.`modx_categories`(`id`,`category`)
SELECT MIN(`id`), '$category'
FROM (SELECT `id` FROM modx.`modx_categories` WHERE `category` = '$category'
   UNION
   SELECT (CASE COUNT(*) WHEN 0 THEN 1 ELSE MAX(`id`) + 1 END) `id`
   FROM $dbase.`modx_categories`) AS _tmp

このクエリは指定されたカテゴリを強制的にmodx_categoriesテーブルに追加するものです。
INSERTのかわりにREPLACEを使っているのは既に存在している場合に対応するためです。UNIONを使って既存IDの検索結果と新しいID(CASE文で生成している)をくっつけており、小さい方の値を取ることで既存データがなければ新しいIDに書き込む、という動作になっていますね。

instprocessor.phpの316行目

コード: 全て選択

if (!@ mysql_query("INSERT INTO $dbase.`" . $table_prefix . "site_htmlsnippets` (name,description,snippet,category) VALUES('$name','$desc','$chunk',(SELECT (CASE COUNT(*) WHEN 0 THEN 0 ELSE `id` END) `id` FROM $dbase.`" . $table_prefix . "categories` WHERE `category` = '$category'));", $sqlParser->conn)) {

同じくクエリ部分だけ抜粋

コード: 全て選択

INSERT INTO modx.`modx_site_htmlsnippets` (name,description,snippet,category)
VALUES ( '$name', '$desc', 'chunk',
   (SELECT (CASE COUNT(*) WHEN 0 THEN 0 ELSE `id` END) `id`
   FROM modx.`modx_categories`
   WHERE `category` = '$category')
);

先ほどと似た要領で、新しいチャンクのカテゴリID値を検索しています。CASE文を使っているのは該当レコードがないときに0を返すためですね。(たぶんこれを指定しないと該当レコードがないときは0ではなくEmptyが返るはず)

で、問題になっている4.0の件ですが、MySQLのマニュアルに次のような記事がありました。
UNION の結果セット内のカラムの型と長さでは、すべての SELECT ステートメントで取り出された値が考慮されます。 MySQL 4.1.1 より前のバージョンの UNION では、最初の SELECT で使用された値のみに基づいて結果の型と長さが決まる、という制約がありました。 この場合、たとえば、最初の SELECT の値よりも長い値が 2 番目の SELECT で取り出されると、切り捨てが行われることがあります。
― 6.4.1.2. UNION 構文 / MySQL 4.1 リファレンスマニュアル(http://dev.mysql.com/doc/refman/4.1/ja/union.html) より
アバター
yama
管理人
記事: 3082
登録日時: 2009年7月29日(水) 02:50

Re: インストーラのMySQL4.0系対応

投稿記事by yama » 2010年4月12日(月) 09:47

ありがとうございます。おかげさまで理解できました。カテゴリーがすでに存在するかどうかチェックして、存在しなければinsert intoで追加する。という流れでよさそうですね。クエリがひとつ増えますが、今回はベタに対応しようと思います。