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

Revision 22019, 37.9 KB checked in by Seasoft, 9 years ago (diff)

#1716 (Postgres Plus Advanced Server 9.1 対応)

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