source: branches/version-2_12-dev/data/class/SC_Query.php @ 21903

Revision 21903, 37.0 KB checked in by Seasoft, 12 years ago (diff)

#1467 (SC_Query#getSingletonInstance DB問い合わせ用パラメータの保持・初期化が不適当)
#1858 (SC_Query#getSingletonInstance グローバル変数の使用を避ける)
#1859 (無駄な処理を改善する)

  • SC_Query#getSingletonInstance isset() と is_null() の AND 実行は無意味

#1860 (SC_Query#getSingletonInstance 第1引数($dsn)の差を適切に処理できない)

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-httpd-php; charset=UTF-8
Line 
1<?php
2/*
3 * This file is part of EC-CUBE
4 *
5 * Copyright(c) 2000-2012 LOCKON CO.,LTD. All Rights Reserved.
6 *
7 * http://www.lockon.co.jp/
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 */
23
24/**
25 * SQLの構築・実行を行う
26 *
27 * TODO エラーハンドリング, ロギング方法を見直す
28 *
29 * @author LOCKON CO.,LTD.
30 * @version $Id$
31 */
32class SC_Query {
33
34    var $option = '';
35    var $where = '';
36    var $arrWhereVal = array();
37    var $conn;
38    var $groupby = '';
39    var $order = '';
40    var $force_run = false;
41    static $arrInstance = array();
42
43    /**
44     * コンストラクタ.
45     *
46     * @param string $dsn データソース名
47     * @param boolean $force_run エラーが発生しても処理を続行する場合 true
48     * @param boolean $new 新規に接続を行うかどうか
49     */
50    function __construct($dsn = '', $force_run = false, $new = false) {
51
52        if ($dsn == "") {
53            $dsn = array('phptype'  => DB_TYPE,
54                         'username' => DB_USER,
55                         'password' => DB_PASSWORD,
56                         'protocol' => 'tcp',
57                         'hostspec' => DB_SERVER,
58                         'port'     => DB_PORT,
59                         'database' => DB_NAME
60                         );
61        }
62
63        // オプション
64        $options = array(
65            // 持続的接続
66            'persistent' => PEAR_DB_PERSISTENT,
67            // Debugモード
68            'debug' => PEAR_DB_DEBUG,
69        );
70
71        // バッファリング trueにするとメモリが解放されない。
72        // 連続クエリ実行時に問題が生じる。
73        $options['result_buffering'] = false;
74
75        if ($new) {
76            $this->conn = MDB2::connect($dsn, $options);
77        } else {
78            $this->conn = MDB2::singleton($dsn, $options);
79        }
80        if (!PEAR::isError($this->conn)) {
81            $this->conn->setCharset('utf8');
82            $this->conn->setFetchMode(MDB2_FETCHMODE_ASSOC);
83        }
84
85        // XXX 上書きインストール時にDBを変更するケースを想定し第1引数を与えている。
86        $this->dbFactory = SC_DB_DBFactory_Ex::getInstance($this->conn->dsn['phptype']);
87        $this->dbFactory->initObjQuery($this);
88
89        $this->force_run = $force_run;
90    }
91
92    /**
93     * シングルトンの SC_Query インスタンスを取得する.
94     *
95     * @param string $dsn データソース名
96     * @param boolean $force_run エラーが発生しても処理を続行する場合 true
97     * @param boolean $new 新規に接続を行うかどうか
98     * @return SC_Query シングルトンの SC_Query インスタンス
99     */
100    function getSingletonInstance($dsn = '', $force_run = false, $new = false) {
101        if (!isset(SC_Query_Ex::$arrInstance[$dsn])) {
102            SC_Query_Ex::$arrInstance[$dsn] =& new SC_Query_Ex($dsn, $force_run, $new);
103        }
104        /*
105         * 歴史的な事情で、このメソッドの呼び出し元は参照で受け取る確率がある。
106         * 退避しているインスタンスをそのまま返すと、退避している SC_Query の
107         * プロパティを直接書き換えることになる。これを回避するため、クローンを返す。
108         * 厳密な意味でのシングルトンではないが、パフォーマンス的に大差は無い。
109         */
110        return clone SC_Query_Ex::$arrInstance[$dsn];
111    }
112
113    /**
114     * エラー判定を行う.
115     *
116     * @deprecated PEAR::isError() を使用して下さい
117     * @return boolean
118     */
119    function isError() {
120        if (PEAR::isError($this->conn)) {
121            return true;
122        }
123        return false;
124    }
125
126    /**
127     * COUNT文を実行する.
128     *
129     * @param string $table テーブル名
130     * @param string $where where句
131     * @param array $arrWhereVal プレースホルダ
132     * @return integer 件数
133     */
134    function count($table, $where = '', $arrWhereVal = array()) {
135        return $this->get('COUNT(*)', $table, $where, $arrWhereVal);
136    }
137
138    /**
139     * EXISTS文を実行する.
140     *
141     * @param string $table テーブル名
142     * @param string $where where句
143     * @param array $arrWhereVal プレースホルダ
144     * @return boolean 有無
145     */
146    function exists($table, $where = '', $arrWhereVal = array()) {
147        $sql_inner = $this->getSql('*', $table, $where, $arrWhereVal);
148        $sql = "SELECT CASE WHEN EXISTS($sql_inner) THEN 1 ELSE 0 END";
149        $res = $this->getOne($sql, $arrWhereVal);
150        return (bool)$res;
151    }
152
153    /**
154     * SELECT文を実行する.
155     *
156     * @param string $cols カラム名. 複数カラムの場合はカンマ区切りで書く
157     * @param string $from テーブル名
158     * @param string $where WHERE句
159     * @param array $arrWhereVal プレースホルダ
160     * @param integer $fetchmode 使用するフェッチモード。デフォルトは MDB2_FETCHMODE_ASSOC。
161     * @return array|null
162     */
163    function select($cols, $from = '', $where = '', $arrWhereVal = array(), $fetchmode = MDB2_FETCHMODE_ASSOC) {
164        $sqlse = $this->getSql($cols, $from, $where, $arrWhereVal);
165        return $this->getAll($sqlse, $arrWhereVal, $fetchmode);
166    }
167
168    /**
169     * 直前に実行されたSQL文を取得する.
170     *
171     * @param boolean $disp trueの場合、画面出力を行う.
172     * @return string SQL文
173     */
174    function getLastQuery($disp = true) {
175        $sql = $this->conn->last_query;
176        if ($disp) {
177            echo $sql . ";<br />\n";
178        }
179        return $sql;
180    }
181
182    /**
183     * トランザクションをコミットする.
184     *
185     * @return MDB2_OK 成功した場合は MDB2_OK;
186     *         失敗した場合は PEAR::Error オブジェクト
187     */
188    function commit() {
189        return $this->conn->commit();
190    }
191
192    /**
193     * トランザクションを開始する.
194     *
195     * @return MDB2_OK 成功した場合は MDB2_OK;
196     *         失敗した場合は PEAR::Error オブジェクト
197     */
198    function begin() {
199        return $this->conn->beginTransaction();
200    }
201
202    /**
203     * トランザクションをロールバックする.
204     *
205     * @return MDB2_OK 成功した場合は MDB2_OK;
206     *         失敗した場合は PEAR::Error オブジェクト
207     */
208    function rollback() {
209        return $this->conn->rollback();
210    }
211
212    /**
213     * トランザクションが開始されているかチェックする.
214     *
215     * @return boolean トランザクションが開始されている場合 true
216     */
217    function inTransaction() {
218        return $this->conn->inTransaction();
219    }
220
221    /**
222     * 更新系の SQL を実行する.
223     *
224     * この関数は SC_Query::query() のエイリアスです.
225     *
226     * FIXME MDB2::exec() の実装であるべき
227     */
228    function exec($str, $arrVal = array()) {
229        return $this->query($str, $arrVal);
230    }
231
232    /**
233     * クエリを実行し、結果行毎にコールバック関数を適用する
234     *
235     * @param callback $function コールバック先
236     * @param string $sql SQL クエリ
237     * @param array $arrVal プリペアドステートメントの実行時に使用される配列。配列の要素数は、クエリ内のプレースホルダの数と同じでなければなりません。
238     * @param integer $fetchmode 使用するフェッチモード。デフォルトは DB_FETCHMODE_ASSOC。
239     * @return boolean 結果
240     */
241    function doCallbackAll($cbFunc, $sql, $arrVal = array(), $fetchmode = MDB2_FETCHMODE_ASSOC) {
242
243        $sql = $this->dbFactory->sfChangeMySQL($sql);
244
245        $sth =& $this->prepare($sql);
246        if (PEAR::isError($sth) && $this->force_run) {
247            return;
248        }
249
250        $affected =& $this->execute($sth, $arrVal);
251        if (PEAR::isError($affected) && $this->force_run) {
252            return;
253        }
254
255        while ($data = $affected->fetchRow($fetchmode)) {
256            $result = call_user_func($cbFunc, $data);
257            if ($result === false) {
258                break;
259            }
260        }
261        $sth->free();
262        return $result;
263    }
264
265    /**
266     * クエリを実行し、全ての行を返す
267     *
268     * @param string $sql SQL クエリ
269     * @param array $arrVal プリペアドステートメントの実行時に使用される配列。配列の要素数は、クエリ内のプレースホルダの数と同じでなければなりません。
270     * @param integer $fetchmode 使用するフェッチモード。デフォルトは DB_FETCHMODE_ASSOC。
271     * @return array データを含む2次元配列。失敗した場合に 0 または DB_Error オブジェクトを返します。
272     */
273    function getAll($sql, $arrVal = array(), $fetchmode = MDB2_FETCHMODE_ASSOC) {
274
275        $sql = $this->dbFactory->sfChangeMySQL($sql);
276
277        $sth =& $this->prepare($sql);
278        if (PEAR::isError($sth) && $this->force_run) {
279            return;
280        }
281
282        $affected =& $this->execute($sth, $arrVal);
283        if (PEAR::isError($affected) && $this->force_run) {
284            return;
285        }
286
287        // MySQL での不具合対応のため、一旦変数に退避
288        $arrRet = $affected->fetchAll($fetchmode);
289
290        // PREPAREの解放
291        $sth->free();
292
293        return $arrRet;
294    }
295
296    /**
297     * 構築した SELECT 文を取得する.
298     *
299     * クラス変数から WHERE 句を組み立てる場合、$arrWhereVal を経由してプレースホルダもクラス変数のもので上書きする。
300     * @param string $cols SELECT 文に含めるカラム名
301     * @param string $from SELECT 文に含めるテーブル名
302     * @param string $where SELECT 文に含める WHERE 句
303     * @param mixed $arrWhereVal プレースホルダ(参照)
304     * @return string 構築済みの SELECT 文
305     */
306    function getSql($cols, $from = '', $where = '', &$arrWhereVal = null) {
307        $dbFactory = SC_DB_DBFactory_Ex::getInstance();
308
309        $sqlse = "SELECT $cols";
310
311        if (strlen($from) === 0) {
312            $sqlse .= ' ' . $dbFactory->getDummyFromClauseSql();
313        } else {
314            $sqlse .= " FROM $from";
315        }
316
317        // 引数の$whereを優先する。
318        if (strlen($where) >= 1) {
319            $sqlse .= " WHERE $where";
320        } elseif (strlen($this->where) >= 1) {
321            $sqlse .= ' WHERE ' . $this->where;
322            // 実行時と同じくキャストしてから評価する (空文字を要素1の配列と評価させる意図)
323            $arrWhereValForEval = (array)$arrWhereVal;
324            if (empty($arrWhereValForEval)) {
325                $arrWhereVal = $this->arrWhereVal;
326            }
327        }
328
329        $sqlse .= ' ' . $this->groupby . ' ' . $this->order . ' ' . $this->option;
330
331        return $sqlse;
332    }
333
334    /**
335     * SELECT 文の末尾に付与する SQL を設定する.
336     *
337     * この関数で設定した値は SC_Query::getSql() で使用されます.
338     *
339     * @param string $str 付与する SQL 文
340     * @return SC_Query 自分自身のインスタンス
341     */
342    function setOption($str) {
343        $this->option = $str;
344        return $this;
345    }
346
347    /**
348     * SELECT 文に付与する LIMIT, OFFSET 句を設定する.
349     *
350     * この関数で設定した値は SC_Query::getSql() で使用されます.
351     *
352     * @param integer $limit LIMIT 句に付与する値
353     * @param integer $offset OFFSET 句に付与する値
354     * @return SC_Query 自分自身のインスタンス
355     */
356    function setLimitOffset($limit, $offset = 0) {
357        if (is_numeric($limit) && is_numeric($offset)) {
358            $this->conn->setLimit($limit, $offset);
359        }
360        return $this;
361    }
362
363    /**
364     * SELECT 文に付与する GROUP BY 句を設定する.
365     *
366     * この関数で設定した値は SC_Query::getSql() で使用されます.
367     *
368     * @param string $str GROUP BY 句に付与する文字列
369     * @return SC_Query 自分自身のインスタンス
370     */
371    function setGroupBy($str) {
372        if (strlen($str) == 0) {
373            $this->groupby = '';
374        } else {
375            $this->groupby = 'GROUP BY ' . $str;
376        }
377        return $this;
378    }
379
380    /**
381     * SELECT 文の WHERE 句に付与する AND 条件を設定する.
382     *
383     * この関数で設定した値は SC_Query::getSql() で使用されます.
384     *
385     * @param string $str WHERE 句に付与する AND 条件の文字列
386     * @return SC_Query 自分自身のインスタンス
387     */
388    function andWhere($str) {
389        if ($this->where != '') {
390            $this->where .= ' AND ' . $str;
391        } else {
392            $this->where = $str;
393        }
394        return $this;
395    }
396
397    /**
398     * SELECT 文の WHERE 句に付与する OR 条件を設定する.
399     *
400     * この関数で設定した値は SC_Query::getSql() で使用されます.
401     *
402     * @param string $str WHERE 句に付与する OR 条件の文字列
403     * @return SC_Query 自分自身のインスタンス
404     */
405    function orWhere($str) {
406        if ($this->where != '') {
407            $this->where .= ' OR ' . $str;
408        } else {
409            $this->where = $str;
410        }
411        return $this;
412    }
413
414    /**
415     * SELECT 文に付与する WHERE 句を設定する.
416     *
417     * この関数で設定した値は SC_Query::getSql() で使用されます.
418     *
419     * @param string $where WHERE 句に付与する文字列
420     * @param mixed $arrWhereVal プレースホルダ
421     * @return SC_Query 自分自身のインスタンス
422     */
423    function setWhere($where = '', $arrWhereVal = array()) {
424        $this->where = $where;
425        $this->arrWhereVal = $arrWhereVal;
426        return $this;
427    }
428
429    /**
430     * SELECT 文に付与する ORDER BY 句を設定する.
431     *
432     * この関数で設定した値は SC_Query::getSql() で使用されます.
433     *
434     * @param string $str ORDER BY 句に付与する文字列
435     * @return SC_Query 自分自身のインスタンス
436     */
437    function setOrder($str) {
438        if (strlen($str) == 0) {
439            $this->order = '';
440        } else {
441            $this->order = 'ORDER BY ' . $str;
442        }
443        return $this;
444    }
445
446    /**
447     * SELECT 文に付与する LIMIT 句を設定する.
448     *
449     * この関数で設定した値は SC_Query::getSql() で使用されます.
450     *
451     * @param integer $limit LIMIT 句に設定する値
452     * @return SC_Query 自分自身のインスタンス
453     */
454    function setLimit($limit) {
455        if (is_numeric($limit)) {
456            $this->conn->setLimit($limit);
457        }
458        return $this;
459    }
460
461    /**
462     * SELECT 文に付与する OFFSET 句を設定する.
463     *
464     * この関数で設定した値は SC_Query::getSql() で使用されます.
465     *
466     * @param integer $offset OFFSET 句に設定する値
467     * @return SC_Query 自分自身のインスタンス
468     */
469    function setOffset($offset) {
470        if (is_numeric($offset)) {
471            $this->conn->setLimit($this->conn->limit, $offset);
472        }
473        return $this;
474    }
475
476    /**
477     * INSERT文を実行する.
478     *
479     * @param string $table テーブル名
480     * @param array $arrVal array('カラム名' => '値', ...)の連想配列
481     * @param array $arrSql array('カラム名' => 'SQL文', ...)の連想配列
482     * @param array $arrSqlVal SQL文の中で使用するプレースホルダ配列
483     * @param string $from FROM 句・WHERE 句
484     * @param string $arrFromVal FROM 句・WHERE 句で使用するプレースホルダ配列
485     * @return integer|DB_Error|boolean 挿入件数またはエラー(DB_Error, false)
486     */
487    function insert($table, $arrVal, $arrSql = array(), $arrSqlVal = array(), $from = '', $arrFromVal = array()) {
488        $strcol = '';
489        $strval = '';
490        $find = false;
491        $arrValForQuery = array();
492
493        foreach ($arrVal as $key => $val) {
494            $strcol .= $key . ',';
495            if (strcasecmp('Now()', $val) === 0) {
496                $strval .= 'Now(),';
497            } else if (strcasecmp('CURRENT_TIMESTAMP', $val) === 0) {
498                $strval .= 'CURRENT_TIMESTAMP,';
499            } else {
500                $strval .= '?,';
501                $arrValForQuery[] = $val;
502            }
503            $find = true;
504        }
505
506        foreach ($arrSql as $key => $val) {
507            $strcol .= $key . ',';
508            $strval .= $val . ',';
509            $find = true;
510        }
511
512        $arrValForQuery = array_merge($arrValForQuery, $arrSqlVal);
513
514        if (!$find) {
515            return false;
516        }
517        // 文末の','を削除
518        $strcol = rtrim($strcol, ',');
519        $strval = rtrim($strval, ',');
520        $sqlin = "INSERT INTO $table($strcol) SELECT $strval";
521
522        if (strlen($from) >= 1) {
523            $sqlin .= ' ' . $from;
524            $arrValForQuery = array_merge($arrValForQuery, $arrFromVal);
525        }
526
527        // INSERT文の実行
528        $ret = $this->query($sqlin, $arrValForQuery, false, null, MDB2_PREPARE_MANIP);
529
530        return $ret;
531    }
532
533    /**
534     * UPDATE文を実行する.
535     *
536     * @param string $table テーブル名
537     * @param array $arrVal array('カラム名' => '値', ...)の連想配列
538     * @param string $where WHERE句
539     * @param array $arrWhereVal WHERE句用のプレースホルダ配列 (従来は追加カラム用も兼ねていた)
540     * @param array $arrRawSql 追加カラム
541     * @param array $arrRawSqlVal 追加カラム用のプレースホルダ配列
542     * @return
543     */
544    function update($table, $arrVal, $where = '', $arrWhereVal = array(), $arrRawSql = array(), $arrRawSqlVal = array()) {
545        $arrCol = array();
546        $arrValForQuery = array();
547        $find = false;
548
549        foreach ($arrVal as $key => $val) {
550            if (strcasecmp('Now()', $val) === 0) {
551                $arrCol[] = $key . '= Now()';
552            } else if (strcasecmp('CURRENT_TIMESTAMP', $val) === 0) {
553                $arrCol[] = $key . '= CURRENT_TIMESTAMP';
554            } else {
555                $arrCol[] = $key . '= ?';
556                $arrValForQuery[] = $val;
557            }
558            $find = true;
559        }
560
561        if ($arrRawSql != '') {
562            foreach ($arrRawSql as $key => $val) {
563                $arrCol[] = "$key = $val";
564            }
565        }
566
567        $arrValForQuery = array_merge($arrValForQuery, $arrRawSqlVal);
568
569        if (empty($arrCol)) {
570            return false;
571        }
572
573        // 文末の','を削除
574        $strcol = implode(', ', $arrCol);
575
576        if (is_array($arrWhereVal)) { // 旧版との互換用
577            // プレースホルダー用に配列を追加
578            $arrValForQuery = array_merge($arrValForQuery, $arrWhereVal);
579        }
580
581        $sqlup = "UPDATE $table SET $strcol";
582        if (strlen($where) >= 1) {
583            $sqlup .= " WHERE $where";
584        }
585
586        // UPDATE文の実行
587        return $this->query($sqlup, $arrValForQuery, false, null, MDB2_PREPARE_MANIP);
588    }
589
590    /**
591     * MAX文を実行する.
592     *
593     * @param string $table テーブル名
594     * @param string $col カラム名
595     * @param string $where 付与する WHERE 句
596     * @param array $arrWhereVal プレースホルダに挿入する値
597     * @return integer MAX文の実行結果
598     */
599    function max($col, $table, $where = '', $arrWhereVal = array()) {
600        $ret = $this->get("MAX($col)", $table, $where, $arrWhereVal);
601        return $ret;
602    }
603
604    /**
605     * MIN文を実行する.
606     *
607     * @param string $table テーブル名
608     * @param string $col カラム名
609     * @param string $where 付与する WHERE 句
610     * @param array $arrWhereVal プレースホルダに挿入する値
611     * @return integer MIN文の実行結果
612     */
613    function min($col, $table, $where = '', $arrWhereVal = array()) {
614        $ret = $this->get("MIN($col)", $table, $where, $arrWhereVal);
615        return $ret;
616    }
617
618    /**
619     * SQL を構築して, 特定のカラムの値を取得する.
620     *
621     * @param string $table テーブル名
622     * @param string $col カラム名
623     * @param string $where 付与する WHERE 句
624     * @param array $arrWhereVal プレースホルダに挿入する値
625     * @return mixed SQL の実行結果
626     */
627    function get($col, $table = '', $where = '', $arrWhereVal = array()) {
628        $sqlse = $this->getSql($col, $table, $where, $arrWhereVal);
629        // SQL文の実行
630        $ret = $this->getOne($sqlse, $arrWhereVal);
631        return $ret;
632    }
633
634    /**
635     * SQL を指定して, 特定のカラムの値を取得する.
636     *
637     * @param string $sql 実行する SQL
638     * @param array $arrVal プレースホルダに挿入する値
639     * @return mixed SQL の実行結果
640     */
641    function getOne($sql, $arrVal = array()) {
642
643        $sql = $this->dbFactory->sfChangeMySQL($sql);
644
645        $sth =& $this->prepare($sql);
646        if (PEAR::isError($sth) && $this->force_run) {
647            return;
648        }
649
650        $affected =& $this->execute($sth, $arrVal);
651        if (PEAR::isError($affected) && $this->force_run) {
652            return;
653        }
654
655        // MySQL での不具合対応のため、一旦変数に退避
656        $arrRet = $affected->fetchOne();
657
658        // PREPAREの解放
659        $sth->free();
660
661        return $arrRet;
662    }
663
664    /**
665     * 一行をカラム名をキーとした連想配列として取得
666     *
667     * @param string $table テーブル名
668     * @param string $col カラム名
669     * @param string $where WHERE句
670     * @param array $arrWhereVal プレースホルダ配列
671     * @param integer $fetchmode 使用するフェッチモード。デフォルトは MDB2_FETCHMODE_ASSOC。
672     * @return array array('カラム名' => '値', ...)の連想配列
673     */
674    function getRow($col, $table = '', $where = '', $arrWhereVal = array(), $fetchmode = MDB2_FETCHMODE_ASSOC) {
675
676        $sql = $this->getSql($col, $table, $where, $arrWhereVal);
677        $sql = $this->dbFactory->sfChangeMySQL($sql);
678
679        $sth =& $this->prepare($sql);
680        if (PEAR::isError($sth) && $this->force_run) {
681            return;
682        }
683
684        $affected =& $this->execute($sth, $arrWhereVal);
685        if (PEAR::isError($affected) && $this->force_run) {
686            return;
687        }
688
689        // MySQL での不具合対応のため、一旦変数に退避
690        $arrRet = $affected->fetchRow($fetchmode);
691
692        // PREPAREの解放
693        $sth->free();
694
695        return $arrRet;
696    }
697
698    /**
699     * SELECT 文の実行結果を 1列のみ取得する.
700     *
701     * @param string $table テーブル名
702     * @param string $col カラム名
703     * @param string $where 付与する WHERE 句
704     * @param array $arrWhereVal プレースホルダに挿入する値
705     * @return array SQL の実行結果の配列
706     */
707    function getCol($col, $table = '', $where = '', $arrWhereVal = array()) {
708        $sql = $this->getSql($col, $table, $where, $arrWhereVal);
709        $sql = $this->dbFactory->sfChangeMySQL($sql);
710
711        $sth =& $this->prepare($sql);
712        if (PEAR::isError($sth) && $this->force_run) {
713            return;
714        }
715
716        $affected =& $this->execute($sth, $arrWhereVal);
717        if (PEAR::isError($affected) && $this->force_run) {
718            return;
719        }
720
721        // MySQL での不具合対応のため、一旦変数に退避
722        $arrRet = $affected->fetchCol();
723
724        // PREPAREの解放
725        $sth->free();
726
727        return $arrRet;
728    }
729
730    /**
731     * レコードの削除
732     *
733     * @param string $table テーブル名
734     * @param string $where WHERE句
735     * @param array $arrWhereVal プレースホルダ
736     * @return
737     */
738    function delete($table, $where = '', $arrWhereVal = array()) {
739        if (strlen($where) <= 0) {
740            $sqlde = "DELETE FROM $table";
741        } else {
742            $sqlde = "DELETE FROM $table WHERE $where";
743        }
744        $ret = $this->query($sqlde, $arrWhereVal, false, null, MDB2_PREPARE_MANIP);
745        return $ret;
746    }
747
748    /**
749     * 次のシーケンス値を取得する.
750     *
751     * @param string $seq_name 取得するシーケンス名
752     * @param integer 次のシーケンス値
753     */
754    function nextVal($seq_name) {
755        return $this->conn->nextID($seq_name);
756    }
757
758    /**
759     * 現在のシーケンス値を取得する.
760     *
761     * @param string $seq_name 取得するシーケンス名
762     * @return integer 現在のシーケンス値
763     */
764    function currVal($seq_name) {
765        return $this->conn->currID($seq_name);
766    }
767
768    /**
769     * シーケンス値を設定する.
770     *
771     * @param string $seq_name シーケンス名
772     * @param integer $start 設定するシーケンス値
773     * @return MDB2_OK
774     */
775    function setVal($seq_name, $start) {
776        $objManager =& $this->conn->loadModule('Manager');
777
778        // XXX 値変更の役割のため、存在チェックは行なわない。存在しない場合、ここでエラーとなる。
779        $ret = $objManager->dropSequence($seq_name);
780        if (PEAR::isError($ret)) {
781            $this->error("setVal -> dropSequence [$seq_name]");
782        }
783
784        $ret = $objManager->createSequence($seq_name, $start);
785        if (PEAR::isError($ret)) {
786            $this->error("setVal -> createSequence [$seq_name] [$start]");
787        }
788        return $ret;
789    }
790
791    /**
792     * SQL を実行する.
793     *
794     * FIXME $ignore_errが無視されるようになっているが互換性として問題が無いか確認が必要
795     *
796     * @param string $n 実行する SQL 文
797     * @param array $arr プレースホルダに挿入する値
798     * @param boolean $ignore_err MDB2切替で無効化されている (エラーが発生しても処理を続行する場合 true)
799     * @param mixed $types プレースホルダの型指定 デフォルトnull = string
800     * @param mixed $result_types 返値の型指定またはDML実行(MDB2_PREPARE_MANIP)
801     * @return array SQL の実行結果の配列
802     */
803    function query($n ,$arr = array(), $ignore_err = false, $types = null, $result_types = MDB2_PREPARE_RESULT) {
804
805        $n = $this->dbFactory->sfChangeMySQL($n);
806
807        $sth =& $this->prepare($n, $types, $result_types);
808        if (PEAR::isError($sth) && $this->force_run) {
809            return $sth;
810        }
811
812        $result = $this->execute($sth, $arr);
813        if (PEAR::isError($result) && $this->force_run) {
814            return $sth;
815        }
816
817        // PREPAREの解放
818        $sth->free();
819
820        return $result;
821    }
822
823    /**
824     * シーケンスの一覧を取得する.
825     *
826     * @return array シーケンス名の配列
827     */
828    function listSequences() {
829        $objManager =& $this->conn->loadModule('Manager');
830        return $objManager->listSequences();
831    }
832
833    /**
834     * テーブル一覧を取得する.
835     *
836     * @return array テーブル名の配列
837     */
838    function listTables() {
839        $objManager =& $this->conn->loadModule('Manager');
840        return $objManager->listTables();
841    }
842
843    /**
844     * テーブルのカラム一覧を取得する.
845     *
846     * @param string $table テーブル名
847     * @return array 指定のテーブルのカラム名の配列
848     */
849    function listTableFields($table) {
850        $objManager =& $this->conn->loadModule('Manager');
851        return $objManager->listTableFields($table);
852    }
853
854    /**
855     * テーブルのインデックス一覧を取得する.
856     *
857     * @param string $table テーブル名
858     * @return array 指定のテーブルのインデックス一覧
859     */
860    function listTableIndexes($table) {
861        $objManager =& $this->conn->loadModule('Manager');
862        return $objManager->listTableIndexes($table);
863    }
864
865    /**
866     * テーブルにインデックスを付与する
867     *
868     * @param string $table テーブル名
869     * @param string $name インデックス名
870     * @param array $definition フィールド名など 通常のフィールド指定時は、$definition=array('fields' => array('フィールド名' => array()));
871     *               MySQLのtext型フィールドを指定する場合は $definition['length'] = 'text_field(NNN)' が必要
872     */
873    function createIndex($table, $name, $definition) {
874        $definition = $this->dbFactory->sfGetCreateIndexDefinition($table, $name, $definition);
875        $objManager =& $this->conn->loadModule('Manager');
876        return $objManager->createIndex($table, $name, $definition);
877    }
878
879    /**
880     * テーブルにインデックスを破棄する
881     *
882     * @param string $table テーブル名
883     * @param string $name インデックス名
884     */
885    function dropIndex($table, $name) {
886        $objManager =& $this->conn->loadModule('Manager');
887        return $objManager->dropIndex($table, $name);
888    }
889
890    /**
891     * テーブルの詳細情報を取得する。
892     *
893     * @param string $table テーブル名
894     * @return array テーブル情報の配列
895     */
896    function getTableInfo($table) {
897        $objManager =& $this->conn->loadModule('Reverse');
898        return $objManager->tableInfo($table, NULL);
899    }
900
901    /**
902     * 値を適切にクォートする.
903     *
904     * TODO MDB2 に対応するための暫定的な措置.
905     *      プレースホルダが使用できない実装があるため.
906     *      本来であれば, MDB2::prepare() を適切に使用するべき
907     *
908     * @see MDB2::quote()
909     * @param string $val クォートを行う文字列
910     * @return string クォートされた文字列
911     */
912    function quote($val) {
913        return $this->conn->quote($val);
914    }
915
916    /**
917     * パラメーターの連想配列から, テーブルに存在する列のみを取得する.
918     *
919     * @param string $table テーブル名
920     * @param array プレースホルダの連想配列
921     * @return array テーブルに存在する列のみ抽出した連想配列
922     */
923    function extractOnlyColsOf($table, $arrParams) {
924        $arrCols = $this->listTableFields($table);
925        $arrResults = array();
926        foreach ($arrParams as $key => $val) {
927            if (in_array($key, $arrCols)) {
928                $arrResults[$key] = $val;
929            }
930        }
931        return $arrResults;
932    }
933
934    /**
935     * プリペアドステートメントを構築する.
936     *
937     * @access private
938     * @param string $sql プリペアドステートメントを構築する SQL
939     * @param mixed $types プレースホルダの型指定 デフォルト null
940     * @param mixed $result_types 返値の型指定またはDML実行(MDB2_PREPARE_MANIP)、nullは指定無し
941     * @return MDB2_Statement_Common プリペアドステートメントインスタンス
942     */
943    function prepare($sql, $types = null, $result_types = MDB2_PREPARE_RESULT) {
944        $sth =& $this->conn->prepare($sql, $types, $result_types);
945        if (PEAR::isError($sth)) {
946            $msg = $this->traceError($sth, $sql);
947            $this->error($msg);
948        }
949        return $sth;
950    }
951
952    /**
953     * プリペアドクエリを実行する.
954     *
955     * @access private
956     * @param MDB2_Statement_Common プリペアドステートメントインスタンス
957     * @param array $arrVal プレースホルダに挿入する配列
958     * @return MDB2_Result 結果セットのインスタンス
959     */
960    function execute(&$sth, $arrVal = array()) {
961
962        $arrStartInfo =& $this->lfStartDbTraceLog($sth, $arrVal);
963        $affected =& $sth->execute((array)$arrVal);
964        $this->lfEndDbTraceLog($arrStartInfo, $sth, $arrVal);
965
966        if (PEAR::isError($affected)) {
967            $sql = isset($sth->query) ? $sth->query : '';
968            $msg = $this->traceError($affected, $sql, $arrVal);
969            $this->error($msg);
970        }
971        $this->conn->last_query = stripslashes($sth->query);
972        return $affected;
973    }
974
975    /**
976     * エラーの内容をトレースする.
977     *
978     * XXX trigger_error で処理する場合、1024文字以内に抑える必要がある。
979     * XXX 重要な情報を先頭に置き、冗長になりすぎないように留意する。
980     * @access private
981     * @param PEAR::Error $error PEAR::Error インスタンス
982     * @param string $sql エラーの発生した SQL 文
983     * @param array $arrVal プレースホルダ
984     * @return string トレースしたエラー文字列
985     */
986    function traceError($error, $sql = '', $arrVal = false) {
987        $err = "SQL: [$sql]\n";
988        if ($arrVal !== false) {
989            $err .= 'PlaceHolder: [' . var_export($arrVal, true) . "]\n";
990        }
991        $err .= $error->getMessage() . "\n";
992        $err .= rtrim($error->getUserInfo()) . "\n";
993
994        // PEAR::MDB2 内部のスタックトレースを出力する場合、下記のコメントを外す。
995        // $err .= GC_Utils_Ex::toStringBacktrace($error->getBackTrace());
996
997        return $err;
998    }
999
1000    /**
1001     * エラー処理
1002     */
1003    function error($msg) {
1004        $msg = "DB処理でエラーが発生しました。\n" . $msg;
1005        if (!$this->force_run) {
1006            trigger_error($msg, E_USER_ERROR);
1007        } else {
1008            GC_Utils_Ex::gfPrintLog($msg, ERROR_LOG_REALFILE, true);
1009        }
1010    }
1011
1012    /**
1013     * SQLクエリの結果セットのカラム名だけを取得する
1014     *
1015     * @param string $n 実行する SQL 文
1016     * @param array $arr プレースホルダに挿入する値
1017     * @param boolean エラーが発生しても処理を続行する場合 true
1018     * @param mixed $types プレースホルダの型指定 デフォルトnull = string
1019     * @param mixed $result_types 返値の型指定またはDML実行(MDB2_PREPARE_MANIP)
1020     * @return array 実行結果の配列
1021     */
1022    function getQueryDefsFields($n ,$arr = array(), $ignore_err = false, $types = null, $result_types = MDB2_PREPARE_RESULT) {
1023
1024        $n = $this->dbFactory->sfChangeMySQL($n);
1025
1026        $sth =& $this->prepare($n, $types, $result_types);
1027        if (PEAR::isError($sth) && ($this->force_run || $ignore_err)) {
1028            return;
1029        }
1030
1031        $result = $this->execute($sth, $arr);
1032        if (PEAR::isError($result) && ($this->force_run || $ignore_err)) {
1033            return;
1034        }
1035        $arrRet = $result->getColumnNames();
1036        // PREPAREの解放
1037        $sth->free();
1038
1039        return $arrRet;
1040    }
1041
1042    /**
1043     * SQL の実行ログ (トレースログ) を書き出す
1044     *
1045     * @param string 実行するSQL文
1046     * @param array $arrVal プレースホルダに挿入する配列
1047     * @return void
1048     */
1049    private function lfStartDbTraceLog(&$objSth, &$arrVal) {
1050        if (!defined('SQL_QUERY_LOG_MODE') || SQL_QUERY_LOG_MODE === 0) {
1051            return;
1052        }
1053        $arrInfo =& $GLOBALS['_SC_Query_TraceLogInfo'];
1054        if (!isset($arrInfo['http_request_id'])) {
1055            $arrInfo['http_request_id'] = uniqid();
1056        }
1057
1058        $arrStartInfo = array(
1059            'http_request_id'   => $arrInfo['http_request_id'],
1060            'time_start'        => microtime(true),
1061            'count'             => ++$arrInfo['count'],
1062        );
1063
1064        // ログモード1の場合、開始はログに出力しない
1065        if (SQL_QUERY_LOG_MODE === 1) {
1066            return $arrStartInfo;
1067        }
1068
1069        $msg = "[execute start {$arrStartInfo['http_request_id']}#{$arrStartInfo['count']}]\n"
1070             . 'SQL: ' . $objSth->query . "\n"
1071             . 'PlaceHolder: ' . var_export($arrVal, true) . "\n";
1072        GC_Utils_Ex::gfPrintLog($msg, DB_LOG_REALFILE);
1073
1074        return $arrStartInfo;
1075    }
1076
1077    /**
1078     * SQL の実行ログ (トレースログ) を書き出す
1079     *
1080     * @param string 実行するSQL文
1081     * @param array $arrVal プレースホルダに挿入する配列
1082     * @return void
1083     */
1084    private function lfEndDbTraceLog(&$arrStartInfo, &$objSth, &$arrVal) {
1085        if (!defined('SQL_QUERY_LOG_MODE') || SQL_QUERY_LOG_MODE === 0) {
1086            return;
1087        }
1088        $msg = "[execute end {$arrStartInfo['http_request_id']}#{$arrStartInfo['count']}]\n";
1089
1090        $timeEnd = microtime(true);
1091        $timeExecTime = $timeEnd - $arrStartInfo['time_start'];
1092
1093        // ログモード1の場合、
1094        if (SQL_QUERY_LOG_MODE === 1) {
1095            // 規定時間より速い場合、ログに出力しない
1096            if (!defined('SQL_QUERY_LOG_MIN_EXEC_TIME') || $timeExecTime < (float)SQL_QUERY_LOG_MIN_EXEC_TIME) {
1097                return;
1098            }
1099            // 開始時にログ出力していないため、ここで実行内容を出力する
1100            $msg .= 'SQL: ' . $objSth->query . "\n";
1101            $msg .= 'PlaceHolder: ' . var_export($arrVal, true) . "\n";
1102        }
1103
1104        $msg .= 'execution time: ' . sprintf('%.2f sec', $timeExecTime) . "\n";
1105        GC_Utils_Ex::gfPrintLog($msg, DB_LOG_REALFILE);
1106    }
1107}
Note: See TracBrowser for help on using the repository browser.