home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 August / PCWorld_2001-08_cd.bin / Komunikace / phptriad / phptriadsetup2-11.exe / php / pear / DB / pgsql.php < prev    next >
PHP Script  |  2001-03-10  |  18KB  |  600 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4.0                                                      |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2001 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Rui Hirokawa <louis@cityfujisawa.ne.jp>                     |
  17. // |          Stig Bakken <ssb@fast.no>                                   |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // Database independent query interface definition for PHP's PostgreSQL
  21. // extension.
  22. //
  23.  
  24. //
  25. // XXX legend:
  26. //
  27. // XXX ERRORMSG: The error message from the pgsql function should
  28. //               be registered here.
  29. //
  30.  
  31. require_once 'DB/common.php';
  32.  
  33. class DB_pgsql extends DB_common
  34. {
  35.     // {{{ properties
  36.  
  37.     var $connection;
  38.     var $phptype, $dbsyntax;
  39.     var $prepare_tokens = array();
  40.     var $prepare_types = array();
  41.     var $transaction_opcount = 0;
  42.     var $numrows;
  43.     var $row;
  44.     var $affected;
  45.     var $autocommit = true;
  46.     var $dsn;
  47.  
  48.     // }}}
  49.     // {{{ constructor
  50.  
  51.     function DB_pgsql()
  52.     {
  53.         $this->DB_common();
  54.         $this->phptype = 'pgsql';
  55.         $this->dbsyntax = 'pgsql';
  56.         $this->features = array(
  57.             'prepare' => false,
  58.             'pconnect' => true,
  59.             'transactions' => true
  60.         );
  61.         $this->errorcode_map = array(
  62.         );
  63.         $this->numrows = array();
  64.         $this->row = array();
  65.         $this->affected = 0;
  66.     }
  67.  
  68.     // }}}
  69.     // {{{ connect()
  70.  
  71.     /**
  72.      * Connect to a database and log in as the specified user.
  73.      *
  74.      * @param $dsn the data source name (see DB::parseDSN for syntax)
  75.      * @param $persistent (optional) whether the connection should
  76.      *        be persistent
  77.      *
  78.      * @return int DB_OK on success, a DB error code on failure
  79.      */
  80.     function connect($dsn, $persistent = false)
  81.     {
  82.         if (is_array($dsn)) {
  83.             $dsninfo = &$dsn;
  84.         } else {
  85.             $dsninfo = DB::parseDSN($dsn);
  86.         }
  87.         if (!$dsninfo || !$dsninfo['phptype']) {
  88.             return $this->raiseError(); // XXX ERRORMSG
  89.         }
  90.         $this->dsn = $dsninfo;
  91.         $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'unix';
  92.         if ($dbhost == 'unix') {
  93.             $protocol = 'unix';
  94.         } else {
  95.             $protocol = $dsninfo['protocol'] ? $dsninfo['protocol'] : 'tcp';
  96.         }
  97.         $user = $dsninfo['username'];
  98.         $pw = $dsninfo['password'];
  99.         $dbname = $dsninfo['database'];
  100.         $options = (!empty($dsninfo['options'])) ? $dsninfo['options'] : NULL;
  101.         $tty = (!empty($dsninfo['tty'])) ? $dsninfo['tty'] : NULL;
  102.         $port = (!empty($dsninfo['port'])) ? $dsninfo['port'] : '5432';
  103.  
  104.         $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect';
  105.  
  106.         if (($protocol == 'unix') && $dbname) {
  107.             $connect_params = "dbname=$dbname";
  108.             if ($user) {
  109.                 $connect_params .= " user=$user";
  110.             }
  111.             if ($pw) {
  112.                 $connect_params .= " password=$pw";
  113.             }
  114.             $conn = @$connect_function($connect_params);
  115.         } elseif ($dbhost && $user && $pw && $dbname) {
  116.             $conn = @$connect_function(
  117.                 "host=$dbhost port=$port dbname=$dbname user=$user password=$pw");
  118.         } elseif ($dbhost && $dbname && $options && $tty) {
  119.             $conn = @$connect_function($dbhost, $port, $options, $tty, $dbname);
  120.         } elseif ($dbhost && $dbname) {
  121.             $conn = @$connect_function($dbhost, $port, $dbname);
  122.         } else {
  123.             $conn = false;
  124.         }
  125.         if ($conn == false) {
  126.             return $this->raiseError(); // XXX ERRORMSG
  127.         }
  128.         $this->connection = $conn;
  129.         return DB_OK;
  130.     }
  131.  
  132.     // }}}
  133.     // {{{ disconnect()
  134.  
  135.     /**
  136.      * Log out and disconnect from the database.
  137.      *
  138.      * @return bool TRUE on success, FALSE if not connected.
  139.      */
  140.     function disconnect()
  141.     {
  142.         return @pg_close($this->connection); // XXX ERRORMSG
  143.     }
  144.  
  145.     // }}}
  146.     // {{{ simpleQuery()
  147.  
  148.     /**
  149.      * Send a query to PostgreSQL and return the results as a
  150.      * PostgreSQL resource identifier.
  151.      *
  152.      * @param $query the SQL query
  153.      *
  154.      * @return int returns a valid PostgreSQL result for successful SELECT
  155.      * queries, DB_OK for other successful queries.  A DB error code
  156.      * is returned on failure.
  157.      */
  158.     function simpleQuery($query)
  159.     {
  160.         $ismanip = DB::isManip($query);
  161.         $this->last_query = $query;
  162.         $query = $this->modifyQuery($query);
  163.         if (!$this->autocommit && $ismanip) {
  164.             if ($this->transaction_opcount == 0) {
  165.                 $result = @pg_exec($this->connection, "begin;");
  166.                 if (!$result) {
  167.                     return $this->pgsqlRaiseError();
  168.                 }
  169.             }
  170.             $this->transaction_opcount++;
  171.         }
  172.         $result = @pg_exec($this->connection, $query);
  173.         if (!$result) {
  174.             return $this->pgsqlRaiseError();
  175.         }
  176.         // Determine which queries that should return data, and which
  177.         // should return an error code only.
  178.         if ($ismanip) {
  179.             $this->affected = @pg_cmdtuples($result);
  180.             return DB_OK;
  181.         } elseif (preg_match('/^\s*(SELECT)\s/i', $query) &&
  182.                   !preg_match('/^\s*(SELECT\s+INTO)\s/i', $query)) {
  183.             /* PostgreSQL commands:
  184.                ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY,
  185.                CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH,
  186.                GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET,
  187.                REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW,
  188.                UNLISTEN, UPDATE, VACUUM
  189.             */
  190.             $this->row[$result] = 0; // reset the row counter.
  191.             $this->numrows[$result] = @pg_numrows($result);  
  192.             $this->affected = 0;
  193.             return $result;
  194.         } else {
  195.             $this->affected = 0;
  196.             return DB_OK;
  197.         }
  198.     }
  199.  
  200.     // }}}
  201.     // {{{ errorCode()
  202.  
  203.     /**
  204.      * Map native error codes to DB's portable ones.  Requires that
  205.      * the DB implementation's constructor fills in the $errorcode_map
  206.      * property.
  207.      *
  208.      * @param $nativecode the native error code, as returned by the backend
  209.      * database extension (string or integer)
  210.      *
  211.      * @return int a portable DB error code, or FALSE if this DB
  212.      * implementation has no mapping for the given error code.
  213.      */
  214.  
  215.     function errorCode($errormsg)
  216.     {
  217.         static $error_regexps;
  218.         if (empty($error_regexps)) {
  219.             $error_regexps = array(
  220.                 '/(Table does not exist\.|Relation \'.*\' does not exist|sequence does not exist)$/' => DB_ERROR_NOSUCHTABLE,
  221.                 '/Relation \'.*\' already exists/' => DB_ERROR_ALREADY_EXISTS,
  222.                 '/divide by zero$/' => DB_ERROR_DIVZERO,
  223.                 '/pg_atoi: error in .*: can\'t parse /' => DB_ERROR_INVALID_NUMBER,
  224.                 '/attribute \'.*\' not found$/' => DB_ERROR_NOSUCHFIELD,
  225.                 '/parser: parse error at or near \"/' => DB_ERROR_SYNTAX
  226.             );
  227.         }
  228.         foreach ($error_regexps as $regexp => $code) {
  229.             if (preg_match($regexp, $errormsg)) {
  230.                 return $code;
  231.             }
  232.         }
  233.         //php_error(E_WARNING, get_class($this)."::errorCode: no mapping for $nativecode");
  234.         // Fall back to DB_ERROR if there was no mapping.
  235.         return DB_ERROR;
  236.         //return $errormsg;
  237.     }
  238.  
  239.     // }}}
  240.     // {{{ fetchRow()
  241.  
  242.     /**
  243.      * Fetch a row and return as array.
  244.      *
  245.      * @param $result PostgreSQL result identifier
  246.      * @param $fetchmode how the resulting array should be indexed
  247.      *
  248.      * @return int an array on success, a DB error code on failure, NULL
  249.      *             if there is no more data
  250.      */
  251.     function &fetchRow($result, $fetchmode = DB_FETCHMODE_DEFAULT)
  252.     {
  253.         if ($fetchmode == DB_FETCHMODE_DEFAULT) {
  254.             $fetchmode = $this->fetchmode;
  255.         }
  256.         if ($this->row[$result] >= $this->numrows[$result]) {
  257.             return NULL;
  258.         }
  259.         if ($fetchmode & DB_FETCHMODE_ASSOC) {
  260.             $row = @pg_fetch_array($result, $this->row[$result], PGSQL_ASSOC);
  261.         } else {
  262.             $row = @pg_fetch_row($result, $this->row[$result]);
  263.         }
  264.         if (!$row) {
  265.             $err = $this->pgsqlRaiseError();
  266.             if (!$err) {
  267.                 return NULL;
  268.             }
  269.             return $err;
  270.         }
  271.         $this->row[$result]++;
  272.         return $row;
  273.     }
  274.  
  275.     // }}}
  276.     // {{{ fetchInto()
  277.  
  278.     /**
  279.      * Fetch a row and insert the data into an existing array.
  280.      *
  281.      * @param $result PostgreSQL result identifier
  282.      * @param $arr (reference) array where data from the row is stored
  283.      * @param $fetchmode how the array data should be indexed
  284.      *
  285.      * @return int DB_OK on success, a DB error code on failure
  286.      */
  287.     function fetchInto($result, &$arr, $fetchmode = DB_FETCHMODE_DEFAULT)
  288.     {
  289.         if ($fetchmode == DB_FETCHMODE_DEFAULT) {
  290.             $fetchmode = $this->fetchmode;
  291.         }
  292.         if ($this->row[$result]>=$this->numrows[$result]){
  293.             return NULL;
  294.         }
  295.         if ($fetchmode & DB_FETCHMODE_ASSOC) {
  296.             $arr = @pg_fetch_array($result, $this->row[$result], PGSQL_ASSOC);
  297.         } else {
  298.             $arr = @pg_fetch_row($result, $this->row[$result]);
  299.         }
  300.         if (!$arr) {
  301.             /* 
  302.              $errno = pg_errormessage($this->connection);
  303.              if (!$errno) {
  304.                 return NULL;
  305.              }
  306.              return $errno;
  307.             */
  308.             // the error codes are not supported in pgsql. 
  309.             return $this->raiseError(DB_ERROR_NOT_CAPABLE); // XXX ERRORMSG
  310.         }
  311.         $this->row[$result]++;
  312.         return DB_OK;
  313.     }
  314.  
  315.     // }}}
  316.     // {{{ freeResult()
  317.  
  318.     /**
  319.      * Free the internal resources associated with $result.
  320.      *
  321.      * @param $result PostgreSQL result identifier or DB statement identifier
  322.      *
  323.      * @return bool TRUE on success, FALSE if $result is invalid
  324.      */
  325.     function freeResult($result)
  326.     {
  327.         if (is_resource($result)) {
  328.             return @pg_freeresult($result);
  329.         }
  330.         if (!isset($this->prepare_tokens[$result])) {
  331.             return false;
  332.         }
  333.         unset($this->prepare_tokens[$result]);
  334.         unset($this->prepare_types[$result]);
  335.         unset($this->row[$result]);
  336.         unset($this->numrows[$result]);
  337.         $this->affected = 0;
  338.         return true; 
  339.     }
  340.  
  341.     // }}}
  342.     // {{{ numCols()
  343.  
  344.     /**
  345.      * Get the number of columns in a result set.
  346.      *
  347.      * @param $result PostgreSQL result identifier
  348.      *
  349.      * @return int the number of columns per row in $result
  350.      */
  351.     function numCols($result)
  352.     {
  353.         $cols = @pg_numfields($result);
  354.         if (!$cols) {
  355.             return $this->pgsqlRaiseError();
  356.         }
  357.         return $cols;
  358.     }
  359.  
  360.     // }}}
  361.     // {{{ numRows()
  362.  
  363.     /**
  364.      * Get the number of rows in a result set.
  365.      *
  366.      * @param $result PostgreSQL result identifier
  367.      *
  368.      * @return int the number of rows in $result
  369.      */
  370.     function numRows($result)
  371.     {
  372.         $rows = @pg_numrows($result);
  373.         if ($rows === null) {
  374.             return $this->pgsqlRaiseError();
  375.         }
  376.         return $rows;
  377.     }
  378.  
  379.     // }}}
  380.     // {{{ errorNative()
  381.  
  382.     /**
  383.      * Get the native error code of the last error (if any) that
  384.      * occured on the current connection.
  385.      *
  386.      * @return int native PostgreSQL error code
  387.      */
  388.     function errorNative()
  389.     {
  390.         return pg_errormessage($this->connection);
  391.     }
  392.  
  393.     // }}}
  394.     // {{{ prepare()
  395.  
  396.     /**
  397.      * Prepares a query for multiple execution with execute().  With
  398.      * PostgreSQL, this is emulated.
  399.      */
  400.     function prepare($query)
  401.     {
  402.         $tokens = split('[\&\?]', $query);
  403.         $token = 0;
  404.         $types = array();
  405.         for ($i = 0; $i < strlen($query); $i++) {
  406.             switch ($query[$i]) {
  407.                 case '?':
  408.                     $types[$token++] = DB_PARAM_SCALAR;
  409.                     break;
  410.                 case '&':
  411.                     $types[$token++] = DB_PARAM_OPAQUE;
  412.                     break;
  413.             }
  414.         }
  415.         $this->prepare_tokens[] = &$tokens;
  416.         end($this->prepare_tokens);
  417.         $k = key($this->prepare_tokens);
  418.         $this->prepare_types[$k] = $types;
  419.         return $k;
  420.     }
  421.  
  422.     // }}}
  423.     // {{{ execute()
  424.  
  425.     /**
  426.      * @return mixed returns a DB result object for successful SELECT
  427.      *         queries, DB_OK for other successful queries.  A DB
  428.      *         error is returned on failure.
  429.      */
  430.     function execute($stmt, $data = false)
  431.     {
  432.         $realquery = $this->executeEmulateQuery($stmt, $data);
  433.         $result = $this->simpleQuery($realquery);
  434.         if (DB::isError($result) || $result === DB_OK) {
  435.             return $result;
  436.         } else {
  437.             return new DB_result($this, $result);
  438.         }
  439.     }
  440.  
  441.     // }}}
  442.     // {{{ autoCommit()
  443.  
  444.     /**
  445.      * Enable/disable automatic commits
  446.      */
  447.     function autoCommit($onoff = false)
  448.     {
  449.         // XXX if $this->transaction_opcount > 0, we should probably
  450.         // issue a warning here.
  451.         $this->autocommit = $onoff ? true : false;
  452.         return DB_OK;
  453.     }
  454.  
  455.     // }}}
  456.     // {{{ commit()
  457.  
  458.     /**
  459.      * Commit the current transaction.
  460.      */
  461.     function commit()
  462.     {
  463.         if ($this->transaction_opcount > 0) {
  464.             // (disabled) hack to shut up error messages from libpq.a
  465.             //@fclose(@fopen("php://stderr", "w"));
  466.             $result = @pg_exec($this->connection, "end;");
  467.             $this->transaction_opcount = 0;
  468.             if (!$result) {
  469.                 return $this->pgsqlRaiseError();
  470.             }
  471.         }
  472.         return DB_OK;
  473.     }
  474.  
  475.     // }}}
  476.     // {{{ rollback()
  477.  
  478.     /**
  479.      * Roll back (undo) the current transaction.
  480.      */
  481.     function rollback()
  482.     {
  483.         if ($this->transaction_opcount > 0) {
  484.             $result = @pg_exec($this->connection, "abort;");
  485.             $this->transaction_opcount = 0;
  486.             if (!$result) {
  487.                 return $this->pgsqlRaiseError();
  488.             }
  489.         }
  490.         return DB_OK;
  491.     }
  492.  
  493.     // }}}
  494.     // {{{ affectedRows()
  495.  
  496.     /**
  497.      * Gets the number of rows affected by the last query.
  498.      * if the last query was a select, returns 0.
  499.      *
  500.      * @return number of rows affected by the last query or DB_ERROR
  501.      */
  502.     function affectedRows()
  503.     {
  504.         $result = $this->affected;
  505.         if ($result === false) {
  506.             return $this->raiseError();
  507.         }
  508.         return $result;
  509.      }
  510.      // }}}
  511.     // {{{ nextId()
  512.  
  513.     /**
  514.      * Get the next value in a sequence.  
  515.      *
  516.      * We are using native PostgreSQL sequences. If a sequence does
  517.      * not exist, it will be created, unless $ondemand is false.
  518.      *
  519.      * @access public
  520.      * @param string $seq_name the name of the sequence
  521.      * @param bool $ondemand whether to create the sequence on demand
  522.      * @return a sequence integer, or a DB error
  523.      */
  524.     function nextId($seq_name, $ondemand = true)
  525.     {
  526.         $sqn = preg_replace('/[^a-z0-9_]/i', '_', $seq_name);
  527.         $repeat = 0;
  528.         do {
  529.             $result = $this->query("SELECT NEXTVAL('${sqn}_seq')");
  530.             if ($ondemand && DB::isError($result) &&
  531.                 $result->getCode() == DB_ERROR_NOSUCHTABLE) {
  532.                 $repeat = 1;
  533.                 $result = $this->createSequence($seq_name);
  534.                 if (DB::isError($result)) {
  535.                     return $result;
  536.                 }
  537.             } else {
  538.                 $repeat = 0;
  539.             }
  540.         } while ($repeat);
  541.         if (DB::isError($result)) {
  542.             return $result;
  543.         }
  544.         $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
  545.         $result->free();
  546.         return $arr[0];
  547.     }
  548.  
  549.     // }}}
  550.     // {{{ createSequence()
  551.  
  552.     /**
  553.      * Create the sequence
  554.      *
  555.      * @param string $seq_name the name of the sequence
  556.      * @return DB error
  557.      * @access public
  558.      */
  559.     function createSequence($seq_name)
  560.     {
  561.         $sqn = preg_replace('/[^a-z0-9_]/i', '_', $seq_name);
  562.         return $this->query("CREATE SEQUENCE ${sqn}_seq");
  563.     }
  564.  
  565.     // }}}
  566.     // {{{ dropSequence()
  567.  
  568.     /**
  569.      * Drop a sequence
  570.      *
  571.      * @param string $seq_name the name of the sequence
  572.      * @return DB error
  573.      * @access public
  574.      */
  575.     function dropSequence($seq_name)
  576.     {
  577.         $sqn = preg_replace('/[^a-z0-9_]/i', '_', $seq_name);
  578.         return $this->query("DROP SEQUENCE ${sqn}_seq");
  579.     }
  580.  
  581.     // }}}
  582.     // {{{ pgsqlRaiseError()
  583.  
  584.     function pgsqlRaiseError($errno = null)
  585.     {
  586.         if ($errno === null) {
  587.             return $this->raiseError($this->errorCode(pg_errormessage($this->connection)));
  588.         }
  589.         return $this->raiseError($this->errorCode($errno));
  590.     }
  591.  
  592.     // }}}
  593. }
  594.  
  595. // Local variables:
  596. // tab-width: 4
  597. // c-basic-offset: 4
  598. // End:
  599. ?>
  600.