Warning: Can't use blame annotator:
svn blame failed on branches/version-2_13-dev/data/class/SC_Query.php: バイナリファイル 'file:///home/svn/open/branches/version-2_13-dev/data/class/SC_Query.php' に対しては blame で各行の最終変更者を計算できません 195004

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

Revision 22771, 38.1 KB checked in by Seasoft, 11 years ago (diff)

#2213 (バックアップ管理 プラグインのテーブルをバックアップできずエラーとなる)

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