source: branches/version-2_13-dev/data/class/SC_Query.php @ 22856

Revision 22856, 38.2 KB checked in by Seasoft, 11 years ago (diff)

#2043 (typo修正・ソース整形・ソースコメントの改善 for 2.13.0)

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