00001 <?php
00003
00004
00005
00007
00026 class FLEA_Db_Driver_Pgsql
00027 {
00031 var $NEXT_ID_SQL = "SELECT NEXTVAL('%s')";
00032 var $CREATE_SEQ_SQL = "CREATE SEQUENCE %s START %s";
00033 var $DROP_SEQ_SQL = "DROP SEQUENCE %s";
00034
00038 var $TRUE_VALUE = 1;
00039 var $FALSE_VALUE = 0;
00040 var $NULL_VALUE = 'NULL';
00041
00045 var $META_COLUMNS_SQL = "SELECT a.attname,t.typname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,a.attnum FROM pg_class c, pg_attribute a,pg_type t WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) and a.attname not like '....%%' AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum";
00046
00047
00048 var $META_COLUMNS_SQL1 = "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) and c.relnamespace=n.oid and n.nspname='%s' and a.attname not like '....%%' AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum";
00049
00050
00051 var $META_KEY_SQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a WHERE bc.oid = i.indrelid AND ic.oid = i.indexrelid AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum) AND a.attrelid = bc.oid AND bc.relname = '%s'";
00052
00053
00054 var $META_DEFAULT_SQL = "SELECT d.adnum as num, d.adsrc as def from pg_attrdef d, pg_class c where d.adrelid=c.oid and c.relname='%s' order by d.adnum";
00055
00061 var $dsn = null;
00062
00068 var $conn = null;
00069
00075 var $log = array();
00076
00082 var $enableLog = false;
00083
00089 var $lasterr = null;
00090
00096 var $lasterrcode = null;
00097
00103 var $_transCount = 0;
00104
00110 var $_transCommit = true;
00111
00117 var $_lastrs = null;
00118
00124 function FLEA_Db_Driver_Pgsql($dsn = false)
00125 {
00126 $tmp = (array)$dsn;
00127 unset($tmp['password']);
00128 $this->dsn = $dsn;
00129 $this->enableLog = !defined('DEPLOY_MODE') || DEPLOY_MODE != true;
00130 if (!function_exists('log_message')) {
00131 $this->enableLog = false;
00132 }
00133 }
00134
00142 function connect($dsn = false)
00143 {
00144 $this->lasterr = null;
00145 $this->lasterrcode = null;
00146
00147 if ($this->conn && $dsn == false) { return true; }
00148 if (!$dsn) {
00149 $dsn = $this->dsn;
00150 } else {
00151 $this->dsn = $dsn;
00152 }
00153 $dsnstring = '';
00154 if (isset($dsn['host'])) {
00155 $dsnstring = 'host=' . $this->_addslashes($dsn['host']);
00156 }
00157 if (isset($dsn['port'])) {
00158 $dsnstring .= ' port=' . $this->_addslashes($dsn['port']);
00159 }
00160 if (isset($dsn['login'])) {
00161 $dsnstring .= ' user=' . $this->_addslashes($dsn['login']);
00162 }
00163 if (isset($dsn['password'])) {
00164 $dsnstring .= ' password=' . $this->_addslashes($dsn['password']);
00165 }
00166 if (isset($dsn['database'])) {
00167 $dsnstring .= ' dbname=' . $this->_addslashes($dsn['database']);
00168 }
00169 $dsnstring .= ' ';
00170
00171 if (isset($dsn['options'])) {
00172 $this->conn = pg_connect($dsnstring, $dsn['options']);
00173 } else {
00174 $this->conn = pg_connect($dsnstring);
00175 }
00176
00177 if (!$this->conn) {
00178 FLEA::loadClass('FLEA_Db_Exception_SqlQuery');
00179 $pos = strpos($dsnstring, 'password=');
00180 if ($pos !== false) {
00181 $dsnstring = substr($dsnstring, 0, $pos - 1) . substr($dsnstring, strpos($dsnstring, ' ', $pos + 1));
00182 }
00183 __THROW(new FLEA_Db_Exception_SqlQuery("pg_connect(\"{$dsnstring}\") failed!"));
00184 return false;
00185 }
00186
00187 if (!$this->execute("set datestyle='ISO'")) { return false; }
00188
00189 if (isset($dsn['charset']) && $dsn['charset'] != '') {
00190 $charset = $dsn['charset'];
00191 } else {
00192 $charset = FLEA::getAppInf('databaseCharset');
00193 }
00194 if (strtoupper($charset) == 'GB2312') { $charset = 'GBK'; }
00195 if ($charset != '') {
00196 pg_set_client_encoding($this->conn, $charset);
00197 }
00198
00199 return true;
00200 }
00201
00205 function close()
00206 {
00207 if ($this->conn) {
00208 pg_close($this->conn);
00209 }
00210 $this->conn = null;
00211 $this->lasterr = null;
00212 $this->lasterrcode = null;
00213 $this->_transCount = 0;
00214 $this->_transCommit = true;
00215 }
00216
00226 function execute($sql, $inputarr = null, $throw = true)
00227 {
00228 if (is_array($inputarr)) {
00229 $sql = $this->_prepareSql($sql, $inputarr);
00230 }
00231 if ($this->enableLog) {
00232 $this->log[] = $sql;
00233 log_message("sql: {$sql}", 'debug');
00234 }
00235 $this->_lastrs = @pg_exec($this->conn, $sql);
00236 if ($this->_lastrs !== false) {
00237 $this->lasterr = null;
00238 $this->lasterrcode = null;
00239 return $this->_lastrs;
00240 }
00241 $this->lasterr = pg_errormessage($this->conn);
00242 $this->lasterrcode = null;
00243 if (!$throw) { return false; }
00244
00245 FLEA::loadClass('FLEA_Db_Exception_SqlQuery');
00246 __THROW(new FLEA_Db_Exception_SqlQuery($sql, $this->lasterr));
00247 return false;
00248 }
00249
00257 function qstr($value)
00258 {
00259 if (is_bool($value)) { return $value ? $this->TRUE_VALUE : $this->FALSE_VALUE; }
00260 if (is_null($value)) { return $this->NULL_VALUE; }
00261 return "'" . pg_escape_string($value) . "'";
00262 }
00263
00271 function qtable($tableName)
00272 {
00273 if (substr($tableName, 0, 1) == '"') { return $tableName; }
00274 return '"' . $tableName . '"';
00275 }
00276
00285 function qfield($fieldName, $tableName = null)
00286 {
00287 $pos = strpos($fieldName, '.');
00288 if ($pos !== false) {
00289 $tableName = substr($fieldName, 0, $pos);
00290 $fieldName = substr($fieldName, $pos + 1);
00291 }
00292 if ($tableName != "") {
00293 return "\"{$tableName}\".\"{$fieldName}\"";
00294 } else {
00295 return "\"{$fieldName}\"";
00296 }
00297 }
00298
00307 function qfields($fields, $tableName = null)
00308 {
00309 if (!is_array($fields)) {
00310 $fields = explode(',', $fields);
00311 }
00312 $return = array();
00313 foreach ($fields as $fieldName) {
00314 $fieldName = trim($fieldName);
00315 if ($fieldName == '') { continue; }
00316 $pos = strpos($fieldName, '.');
00317 if ($pos !== false) {
00318 $tableName = substr($fieldName, 0, $pos);
00319 $fieldName = substr($fieldName, $pos + 1);
00320 }
00321 if ($tableName != '') {
00322 $return[] = "\"{$tableName}\".\"{$fieldName}\"";
00323 } else {
00324 $return[] = "\"{$fieldName}\"";
00325 }
00326 }
00327 return implode(', ', $return);
00328 }
00329
00338 function nextId($seqName = 'sdbo_seq', $startValue = 1)
00339 {
00340 $getNextId = sprintf($this->NEXT_ID_SQL, $seqName);
00341 $result = $this->execute($getNextId, null, false);
00342 if ($result == false) {
00343
00344 if (!$this->createSeq($seqName, $startValue)) { return false; }
00345 if (!$result = $this->execute($getNextId)) { return false; }
00346 }
00347
00348 $row = $this->fetchRow($result);
00349 $this->freeRes($result);
00350 return reset($row);
00351 }
00352
00361 function createSeq($seqName = 'sdbo_seq', $startValue = 1)
00362 {
00363 return $this->execute(sprintf($this->CREATE_SEQ_SQL, $seqName, $startValue));
00364 }
00365
00371 function dropSeq($seqName = 'sdbo_seq')
00372 {
00373 return $this->execute(sprintf($this->DROP_SEQ_SQL, $seqName));
00374 }
00375
00383 function insertId()
00384 {
00385 require_once(FLEA_DIR . '/Exception/NotImplemented.php');
00386 __THROW(new FLEA_Exception_NotImplemented('insertId()', 'FLEA_Db_Driver_Pgsql'));
00387 return false;
00388 }
00389
00395 function affectedRows()
00396 {
00397 return pg_affected_rows($this->_lastrs);
00398 }
00399
00407 function fetchRow($res)
00408 {
00409 return pg_fetch_row($res);
00410 }
00411
00419 function fetchAssoc($res)
00420 {
00421 return pg_fetch_assoc($res);
00422 }
00423
00429 function freeRes($res)
00430 {
00431 pg_free_result($res);
00432 }
00433
00441 function selectLimit($sql, $length = 'ALL', $offset = 0)
00442 {
00443 if (strtoupper($length) != 'ALL') { $length = (int)$length; }
00444 $sql = sprintf('%s LIMIT %d OFFSET %d', $sql, $length, (int)$offset);
00445 return $this->execute($sql);
00446 }
00447
00455 function & getAll($sql)
00456 {
00457 if (is_resource($sql)) {
00458 $res = $sql;
00459 } else {
00460 $res = $this->execute($sql);
00461 }
00462 $data = array();
00463 while ($row = pg_fetch_assoc($res)) {
00464 $data[] = $row;
00465 }
00466 pg_free_result($res);
00467 return $data;
00468 }
00469
00481 function & getAllGroupBy($sql, & $groupBy)
00482 {
00483 if (is_resource($sql)) {
00484 $res = $sql;
00485 } else {
00486 $res = $this->execute($sql);
00487 }
00488 $data = array();
00489 $row = pg_fetch_assoc($res);
00490 if ($row != false) {
00491 if ($groupBy === true) {
00492 $groupBy = key($row);
00493 }
00494 do {
00495 $rkv = $row[$groupBy];
00496 unset($row[$groupBy]);
00497 $data[$rkv][] = $row;
00498 } while ($row = pg_fetch_assoc($res));
00499 }
00500 pg_free_result($res);
00501 return $data;
00502 }
00503
00514 function getAllWithFieldRefs($sql, $field, & $fieldValues, & $reference)
00515 {
00516 if (is_resource($sql)) {
00517 $res = $sql;
00518 } else {
00519 $res = $this->execute($sql);
00520 }
00521
00522 $fieldValues = array();
00523 $reference = array();
00524 $offset = 0;
00525 $data = array();
00526 while ($row = pg_fetch_assoc($res)) {
00527 $fieldValue = $row[$field];
00528 unset($row[$field]);
00529 $data[$offset] = $row;
00530 $fieldValues[$offset] = $fieldValue;
00531 $reference[$fieldValue] =& $data[$offset];
00532 $offset++;
00533 }
00534 pg_free_result($res);
00535 return $data;
00536 }
00537
00548 function assemble($sql, & $assocRowset, $mappingName, $oneToOne, $refKeyName, $limit = null)
00549 {
00550 if (is_resource($sql)) {
00551 $res = $sql;
00552 } else {
00553 if (!is_null($limit)) {
00554 if (is_array($limit)) {
00555 list($length, $offset) = $limit;
00556 } else {
00557 $length = $limit;
00558 $offset = 0;
00559 }
00560 $res = $this->selectLimit($sql, $length, $offset);
00561 } else {
00562 $res = $this->execute($sql);
00563 }
00564 }
00565 if ($oneToOne) {
00566
00567 while ($row = pg_fetch_assoc($res)) {
00568 $rkv = $row[$refKeyName];
00569 unset($row[$refKeyName]);
00570 $assocRowset[$rkv][$mappingName] = $row;
00571 }
00572 } else {
00573
00574 while ($row = pg_fetch_assoc($res)) {
00575 $rkv = $row[$refKeyName];
00576 unset($row[$refKeyName]);
00577 $assocRowset[$rkv][$mappingName][] = $row;
00578 }
00579 }
00580
00581 pg_free_result($res);
00582 }
00583
00591 function getOne($sql)
00592 {
00593 if (is_resource($sql)) {
00594 $res = $sql;
00595 } else {
00596 $res = $this->execute($sql);
00597 }
00598 $row = pg_fetch_row($res);
00599 pg_free_result($res);
00600 return isset($row[0]) ? $row[0] : null;
00601 }
00602
00610 function & getRow($sql)
00611 {
00612 if (is_resource($sql)) {
00613 $res = $sql;
00614 } else {
00615 $res = $this->execute($sql);
00616 }
00617 $row = pg_fetch_assoc($res);
00618 pg_free_result($res);
00619 return $row;
00620 }
00621
00630 function & getCol($sql, $col = 0)
00631 {
00632 if (is_resource($sql)) {
00633 $res = $sql;
00634 } else {
00635 $res = $this->execute($sql);
00636 }
00637 $data = array();
00638 while ($row = pg_fetch_row($res)) {
00639 $data[] = $row[$col];
00640 }
00641 pg_free_result($res);
00642 return $data;
00643 }
00644
00670 function & metaColumns($table, $normalize = true)
00671 {
00672 static $typeMap = array(
00673 'MONEY' => 'C',
00674 'INTERVAL' => 'C',
00675 'CHAR' => 'C',
00676 'CHARACTER' => 'C',
00677 'VARCHAR' => 'C',
00678 'NAME' => 'C',
00679 'BPCHAR' => 'C',
00680 '_VARCHAR' => 'C',
00681 'INET' => 'C',
00682 'MACADDR' => 'C',
00683
00684 'TEXT' => 'X',
00685 'IMAGE' => 'B',
00686 'BLOB' => 'B',
00687 'BIT' => 'B',
00688 'VARBIT' => 'B',
00689 'BYTEA' => 'B',
00690
00691 'BOOL' => 'L',
00692 'BOOLEAN' => 'L',
00693
00694 'DATE' => 'D',
00695
00696 'TIMESTAMP WITHOUT TIME ZONE' => 'T',
00697 'TIME' => 'T',
00698 'DATETIME' => 'T',
00699 'TIMESTAMP' => 'T',
00700 'TIMESTAMPTZ' => 'T',
00701
00702 'SMALLINT' => 'I',
00703 'BIGINT' => 'I',
00704 'INTEGER' => 'I',
00705 'INT8' => 'I',
00706 'INT4' => 'I',
00707 'INT2' => 'I',
00708
00709 'OID' => 'R',
00710 'SERIAL' => 'R',
00711 );
00712
00713 $schema = false;
00714 $this->_findschema($table, $schema);
00715
00716 if (!empty($this->META_KEY_SQL)) {
00717
00718
00719
00720
00721
00722 $keys = $this->getAll(sprintf($this->META_KEY_SQL, $table));
00723 }
00724
00725 $rsdefa = array();
00726 if (!empty($this->META_DEFAULT_SQL)) {
00727 $sql = sprintf($this->META_DEFAULT_SQL, $table);
00728 $rsdef = $this->execute($sql);
00729 if ($rsdef) {
00730 while ($row = pg_fetch_assoc($rsdef)) {
00731 $num = $row['num'];
00732 $s = $row['def'];
00733 if (strpos($s, '::') === false && strpos($s, "'") === 0) {
00734
00735 $s = substr($s, 1);
00736 $s = substr($s, 0, strlen($s) - 1);
00737 }
00738
00739 $rsdefa[$num] = $s;
00740 }
00741 pg_free_result($rsdef);
00742 }
00743 unset($rsdef);
00744 }
00745
00746 if ($schema) {
00747 $rs = $this->execute(sprintf($this->META_COLUMNS_SQL1, $table, $table, $schema));
00748 } else {
00749 $rs = $this->execute(sprintf($this->META_COLUMNS_SQL, $table, $table));
00750 }
00751 if (!$rs) { return false; }
00752
00753 $retarr = array();
00754 while (($row = pg_fetch_row($rs))) {
00755 $field = array();
00756 $field['name'] = $row[0];
00757 $field['type'] = $row[1];
00758 $field['maxLength'] = $row[2];
00759 $field['attnum'] = $row[6];
00760
00761 if ($field['maxLength'] <= 0) {
00762 $field['maxLength'] = $row[3] - 4;
00763 }
00764 if ($field['maxLength'] <= 0) {
00765 $field['maxLength'] = -1;
00766 }
00767
00768 if ($field['type'] == 'numeric') {
00769 $field['scale'] = $field['maxLength'] & 0xFFFF;
00770 $field['maxLength'] >>= 16;
00771 }
00772
00773
00774 $field['hasDefault'] = ($row[5] == 't');
00775 if ($field['hasDefault']) {
00776 $field['defaultValue'] = $rsdefa[$row[6]];
00777 }
00778
00779 $field['notNull'] = $row[4] == 't';
00780
00781 if (is_array($keys)) {
00782 foreach($keys as $key) {
00783 if ($field['name'] == $key['column_name'] && $key['primary_key'] == 't') {
00784 $field['primaryKey'] = true;
00785 } else {
00786 $field['primaryKey'] = false;
00787 }
00788 if ($field['name'] == $key['column_name'] && $key['unique_key'] == 't') {
00789 $field['unique'] = true;
00790 } else {
00791 $field['unique'] = false;
00792 }
00793 }
00794 }
00795
00796 $t = strtoupper($field['type']);
00797 if (isset($typeMap[$t])) {
00798 $field['simpleType'] = $typeMap[$t];
00799 } else {
00800 $field['simpleType'] = 'N';
00801 }
00802
00803 if ($field['simpleType'] == 'I' && ($field['primaryKey'] != false || $field['unique'] != false)) {
00804 $field['simpleType'] = 'R';
00805 }
00806
00807 $field['autoIncrement'] = false;
00808
00809 if ($normalize) {
00810 $retarr[strtoupper($field['name'])] = $field;
00811 } else {
00812 $retarr[$field['name']] = $field;
00813 }
00814 }
00815 pg_free_result($rs);
00816 return $retarr;
00817 }
00818
00824 function dbTimeStamp($timestamp)
00825 {
00826 return date('Y-m-d H:i:s', $timestamp);
00827 }
00828
00832 function startTrans()
00833 {
00834 $this->_transCount += 1;
00835 if ($this->_transCount == 1) {
00836 $this->execute('BEGIN');
00837 }
00838 }
00839
00848 function completeTrans($commitOnNoErrors = true)
00849 {
00850 if ($this->_transCount < 1) { return; }
00851 if ($this->_transCount > 1) {
00852 $this->_transCount -= 1;
00853 return;
00854 }
00855 $this->_transCount = 0;
00856
00857 if ($this->_transCommit && $commitOnNoErrors) {
00858 $this->execute('COMMIT');
00859 } else {
00860 $this->execute('ROLLBACK');
00861 }
00862 }
00863
00867 function failTrans()
00868 {
00869 $this->_transCommit = false;
00870 }
00871
00875 function hasFailedTrans()
00876 {
00877 if ($this->_transCount > 0) {
00878 return $this->_transCommit === false;
00879 }
00880 return false;
00881 }
00882
00891 function _prepareSql($sql, & $inputarr)
00892 {
00893 $sqlarr = explode('?', $sql);
00894 $sql = '';
00895 $ix = 0;
00896 foreach ($inputarr as $v) {
00897 $sql .= $sqlarr[$ix];
00898 $typ = gettype($v);
00899 if ($typ == 'string') {
00900 $sql .= $this->qstr($v);
00901 } else if ($typ == 'double') {
00902 $sql .= $this->qstr(str_replace(',', '.', $v));
00903 } else if ($typ == 'boolean') {
00904 $sql .= $v ? $this->TRUE_VALUE : $this->FALSE_VALUE;
00905 } else if (is_null($v)) {
00906 $sql .= 'NULL';
00907 } else {
00908 $sql .= $v;
00909 }
00910 $ix += 1;
00911 }
00912 if (isset($sqlarr[$ix])) {
00913 $sql .= $sqlarr[$ix];
00914 }
00915 return $sql;
00916 }
00917
00925 function _addslashes($s)
00926 {
00927 $len = strlen($s);
00928 if ($len == 0) return "''";
00929 if (strncmp($s,"'",1) === 0 && substr($s,$len-1) == "'") return $s;
00930 return "'".addslashes($s)."'";
00931 }
00932
00939 function _findschema(& $table, & $schema)
00940 {
00941 if (!$schema && ($at = strpos($table, '.')) !== false) {
00942 $schema = substr($table, 0, $at);
00943 $table = substr($table, $at + 1);
00944 }
00945 }
00946 }