EDB
[ class tree: EDB ] [ index: EDB ] [ all elements ]

Source for file EDB_PGSQL.php

Documentation is available at EDB_PGSQL.php

  1. <?php
  2. /**
  3.  * Project: EDB_PGSQL :: PostgreSQL abstraction layer<br>
  4.  * File:    EDB/EDB_PGSQL.php
  5.  *
  6.  * EDB_PGSQL class는 EDB 패키지가 PosgreSQL을 처리하기 위한
  7.  * 추상 계층을 제공한다.
  8.  *
  9.  * @category    Database
  10.  * @package     EDB
  11.  * @subpackage  EDB_ABSTRACT
  12.  * @author      JoungKyun.Kim <http://oops.org>
  13.  * @copyright   (c) 2018, JoungKyun.Kim
  14.  * @license     BSD License
  15.  * @version     $Id$
  16.  * @link        http://pear.oops.org/package/EDB
  17.  * @filesource
  18.  */
  19.  
  20. /**
  21.  * PosgreSQL engine for EDB API
  22.  *
  23.  * PostgreSQL 엔진을 위한 DB 추상 계층을 제공
  24.  *
  25.  * @package     EDB
  26.  */
  27. Class EDB_PGSQL extends EDB_Common {
  28.     // {{{ properties
  29.     /**#@+
  30.      * @access private
  31.      */
  32.     /**
  33.      * db handler of EDB_PGSQL class
  34.      * @var    object 
  35.      */
  36.     private $db;
  37.     /**
  38.      * The number of query parameter
  39.      * @var    integer 
  40.      */
  41.     private $pno 0;
  42.     /**
  43.      * The number of query parameter
  44.      * @var    integer 
  45.      */
  46.     private $field array ();
  47.     /**
  48.      * Bytea information
  49.      * @var    array 
  50.      */
  51.     private $lob array ();
  52.     /**#@-*/
  53.     // }}}
  54.  
  55.     // {{{ (object) EDB_PGSQL::__construct ($host, $user, $pass, $db)
  56.     /** 
  57.      * EDB_PGSQL 객체를 인스턴스화 하고 PostgreSQL 데이터베이스를
  58.      * 연결한다.
  59.      *
  60.      * For examples:
  61.      * <code>
  62.      * $db = new EDB_PGSQL ('pgsql://localhost', 'user', 'host', 'database');
  63.      * $db = new EDB_PGSQL ('pgsql://localhost:3306', 'user', 'host', 'database');
  64.      * $db = new EDB_PGSQL (
  65.      *           'pgsql:///var/run/postgresql',
  66.      *           'user', 'host', 'database'
  67.      * );
  68.      * $db = new EDB_PGSQL (
  69.      *           'pgsql:///var/run/postgresql',
  70.      *           'user', 'host', 'database', 'options'
  71.      * );
  72.      * </code>
  73.      *
  74.      * options parameter는 다음의 객체로 지정한다.
  75.      *
  76.      * <code>
  77.      * $o = (object) array (
  78.      *     'connect_timeout' => 2,
  79.      *     'options' => '--client_encoding=UTF8',
  80.      *     'sslmode' => 'prefer',
  81.      *     'requiressl' => 0,
  82.      *     'service' => ''
  83.      * }
  84.      * </code>
  85.      *
  86.      * options 객체 멤버에 대해서는
  87.      * {@link http://www.postgresql.org/docs/8.3/static/libpq-connect.html}
  88.      * 참조하도록 한다.
  89.      *
  90.      * 만약 persistent connection을 사용하고 싶다면 host 앞에 'p~' prefix를
  91.      * 붙이면 된다.
  92.      *
  93.      * For Examples:
  94.      * <code>
  95.      * $db = new EDB_PGSQL ('pgsql://p~localhost', 'user', 'host', 'database');
  96.      * </code>
  97.      *
  98.      * @access public
  99.      * @return EDB_PGSQL 
  100.      * @param  string  $hostname pgsql host[:port] 또는 unix socket 경로
  101.      * @param  string  $user     pgsql DB 계정
  102.      * @param  string  $password pgsql DB 암호
  103.      * @param  string  $database pgsql DB 이름
  104.      * @param  object  $options  pgsql 옵션
  105.      */
  106.     function __construct ({
  107.         $_argv func_get_args ();
  108.         $argv is_array ($_argv[0]$_argv[0$_argv;;
  109.  
  110.         if extension_loaded ('pgsql') )
  111.             throw new myException ('pgsql extension is not loaded on PHP!'E_USER_ERROR);
  112.  
  113.         $o = (object) array (
  114.             'host' => preg_replace ('!^pgsql://!'''$argv[0]),
  115.             'user' => $argv[1],
  116.             'pass' => $argv[2],
  117.             'dbname'   => $argv[3],
  118.             'options' => $argv[4]
  119.         );
  120.  
  121.         foreach $o as $key => $val {
  122.             if $key == 'host' {
  123.                 if preg_match ('/^p~/'$val) ) {
  124.                     $func 'pg_pconnect';
  125.                     $val preg_replace ('/^p~/'''$val);
  126.                 else
  127.                     $func 'pg_connect';
  128.  
  129.                 // 파일이 존재하면 host를 unix socket으로 지정
  130.                 if file_exists ($val) ) {
  131.                     $cstring sprintf ('host=%s'$val);
  132.                     continue;
  133.                 }
  134.  
  135.                 if preg_match ('/([^:]+):(.*)/'$val$m) ) {
  136.                     $port is_numeric ($m[2]$m[25432;
  137.                     $cstring sprintf ('hostaddr=%s port=%s'gethostbyname ($m[1])$port);
  138.                 else
  139.                     $cstring sprintf ('hostaddr=%s port=5432'gethostbyname ($val));
  140.             }
  141.  
  142.             if $key == 'options' && is_object ($val) ) {
  143.                 foreach $val as $k => $v {
  144.                     if trim ($v) )
  145.                         $cstring .= sprintf (' %s=%s'$ktrim ($v));
  146.                 }
  147.             }
  148.  
  149.             if trim ($val) )
  150.                 $cstring .= sprintf (' %s=%s'$keytrim ($val));
  151.         }
  152.  
  153.         try {
  154.             $this->db $func ($cstringPGSQL_CONNECT_FORCE_NEW);
  155.         catch Exception $e {
  156.             throw new myException ($e->getMessage ()$e->getCode()$e);
  157.         }
  158.     }
  159.     // }}}
  160.  
  161.     // {{{ (string) EDB_PGSQL::get_charset (void)
  162.     /** 
  163.      * Get character set of current database
  164.      *
  165.      * @access public
  166.      * @return string Current character set name on DB
  167.      */
  168.     function get_charset ({
  169.         try {
  170.             return pg_client_encoding ($this->db);
  171.         catch Exception $e {
  172.             throw new myException ($e->getMessage ()$e->getCode()$e);
  173.             return false;
  174.         }
  175.     }
  176.     // }}}
  177.  
  178.     // {{{ (bool) EDB_PGSQL::set_charset ($charset)
  179.     /** 
  180.      * Set character set of current database
  181.      *
  182.      * Postgresql don't support set characterset and always returns true
  183.      *
  184.      * @access public
  185.      * @return bool    always returns true
  186.      * @param  string  name of character set that supported from database
  187.      */
  188.     function set_charset ($char{
  189.         $r pg_set_client_encoding ($this->db$char);
  190.         return $r false true;
  191.     }
  192.     // }}}
  193.  
  194.     // {{{ (string) EDB_PGSQL::escape ($string)
  195.     /** 
  196.      * Escape special characters in a string for use in an SQL statement
  197.      *
  198.      * Attention! This method always returns original string.
  199.      *
  200.      * @access public
  201.      * @return string 
  202.      * @param  string  The string that is to be escaped.
  203.      */
  204.     function escape ($buf$type 's'{
  205.         switch ($type{
  206.             case 'b' :
  207.                 return base64_encode ($buf);
  208.                 #return pg_escape_bytea ($buf);
  209.             case 'i' :
  210.                 return pg_escape_identifier ($buf);
  211.             case 'u' :
  212.                 /*
  213.                  * bind query시에 ::bytea가 먹지를 않는다 --;
  214.                  * 그리고 base64가 용량이 가장 작다
  215.                 if ( preg_match ('/::bytea$/', $buf) ) {
  216.                     $buf = preg_replace (
  217.                         array ('/\'\'/', '/::bytea/'),
  218.                         array ('\'', ''),
  219.                         $buf
  220.                     );
  221.                     return pg_unescape_bytea (stripslashes ($buf));
  222.                 }
  223.                 return pg_unescape_bytea ($buf);
  224.                  */
  225.                 return base64_decode ($buf);
  226.             default:
  227.                 /*
  228.                 $pgver = pg_version ($this->db);
  229.                 if ( version_compare ('8.3', $pgver['client'], '<') )
  230.                     return pg_escape_literal ($buf);
  231.                 else
  232.                  */
  233.                 return pg_escape_string ($buf);
  234.         }
  235.     }
  236.     // }}}
  237.  
  238.     // {{{ (int) EDB_PGSQL::query ($query, $param_type, $param1, $param2 ...)
  239.     /** 
  240.      * Performs a query on the database
  241.      *
  242.      * @access public
  243.      * @return integer|falseThe number of affected rows
  244.      * @param  string  $query The query strings
  245.      * @param  string  $type  (optional) Bind parameter type. See also
  246.      *  {@link http://php.net/manual/en/mysqli-stmt.bind-param.php mysqli_stmt::bind_param}.
  247.      *  <code>
  248.      *  i => integer
  249.      *  d => double
  250.      *  s => string
  251.      *  b => blob
  252.      *  </code>
  253.      * @param  mixed   $param1 (optional) Bind parameter 1
  254.      * @param  mixed   $param2,... (optional) Bind parameter 2 ..
  255.      */
  256.     function query ({
  257.         $_argv func_get_args ();
  258.         $argv is_array ($_argv[0]$_argv[0$_argv;;
  259.  
  260.         $this->error null;
  261.  
  262.         try {
  263.             $sql array_shift ($argv);
  264.             $this->pno count ($argv$this->get_param_number ($sql'pgsql'0;
  265.  
  266.             if $this->free )
  267.                 $this->free_result ();
  268.  
  269.             // store query in log variable
  270.             $this->queryLog[$sql;
  271.  
  272.             if $this->pno++ == )
  273.                 $r $this->no_bind_query ($sql);
  274.             else
  275.                 $r $this->bind_query ($sql$argv);
  276.  
  277.             if $r === false )
  278.                 return false;
  279.  
  280.             if preg_match ('/^(update|insert|delete|replace)/i'trim ($sql)) ) {
  281.                 /* Insert or update, or delete query */
  282.                 return pg_affected_rows ($this->result);
  283.             else if preg_match ('/create|drop/i'trim ($sql)) ) {
  284.                 return 1;
  285.             }
  286.  
  287.             # Only select
  288.             if preg_match ('/^select/i'trim ($sql)) ) {
  289.                 $fno $this->num_fields ();
  290.                 for $i=0$i<$fno$i++ {
  291.                     $type $this->field_type ($i);
  292.                     if $type == 'bytea' )
  293.                         $lob .= ':' $this->field_name ($i);
  294.                 }
  295.  
  296.                 $lob substr ($lob1);
  297.                 if preg_match ('/:/'$lob) )
  298.                     $this->lob preg_split ('/:/'$lob);
  299.                 else
  300.                     $this->lob array ($lob);
  301.             }
  302.  
  303.             return pg_num_rows ($this->result);
  304.         catch Exception $e {
  305.             throw new myException ($e->getMessage ()$e->getCode()$e);
  306.             return false;
  307.         }
  308.     }
  309.     // }}}
  310.  
  311.     // {{{ (string) EDB_PGSQL::lastId (void)
  312.     /**
  313.      * 가장 마지막 입력 row의 OID를 반환한다.
  314.      *
  315.      * @since  2.0.4
  316.      * @access public
  317.      * @return string|false
  318.      */
  319.     function lastId ({
  320.         return pg_last_oid ($this->db);
  321.     }
  322.     // }}}
  323.  
  324.     // {{{ (bool) EDB_PGSQL::seek ($offset)
  325.     /**
  326.      * Adjusts the result pointer to an arbitrary row in the result
  327.      *
  328.      * @access public
  329.      * @return boolean 
  330.      * @param  integer Must be between zero and the total number of rows minus one
  331.      */
  332.     function seek ($offset{
  333.         if is_resource ($this->result) )
  334.             return false;
  335.  
  336.         try {
  337.             return pg_result_seek ($this->result$offset);
  338.         catch Exception $e {
  339.             throw new myException ($e->getMessage ()$e->getCode()$e);
  340.             return false;
  341.         }
  342.     }
  343.     // }}}
  344.  
  345.     // {{{ (object) EDB_PGSQL::fetch (void)
  346.     /**
  347.      * Fetch a result row as an associative object
  348.      *
  349.      * @access public
  350.      * @return object The object of fetched a result row or false
  351.      * @param  boolean (optional) fetch 수행 후 result를 free한다.
  352.      *                  (기본값: false) EDB >= 2.0.3
  353.      */
  354.     function fetch ($free false{
  355.         if is_resource ($this->result ) )
  356.             return false;
  357.  
  358.         try {
  359.             $r pg_fetch_object ($this->result);
  360.             if is_object ($r) )
  361.                 return false;
  362.  
  363.             foreach $this->lob as $keyname )
  364.                 $r->$keyname $this->escape ($r->$keyname'u');
  365.  
  366.             if $free )
  367.                 $this->free_result ();
  368.  
  369.             return $r;
  370.         catch Exception $e {
  371.             throw new myException ($e->getMessage ()$e->getCode()$e);
  372.             return false;
  373.         }
  374.     }
  375.     // }}}
  376.  
  377.     // {{{ (array) EDB_PGSQL::fetch_all ($free = true)
  378.     /**
  379.      * Fetch all result rows as an associative object
  380.      *
  381.      * @access public
  382.      * @return array The fetched result rows
  383.      * @param  boolean (optional) free result set after fetch.
  384.      *                  Defaluts is true.
  385.      */
  386.     function fetch_all ($free true{
  387.         $r array ();
  388.         while ( ($row $this->fetch ()) !== false )
  389.             $r[$row
  390.  
  391.         if $free )
  392.             $this->free_result ();
  393.  
  394.         return $r;
  395.     }
  396.     // }}}
  397.  
  398.     // {{{ (bool) EDB_PGSQL::free_result (void)
  399.     /**
  400.      * Frees stored result memory for the given statement handle
  401.      *
  402.      * @access public
  403.      * @return boolean 
  404.      * @param  void 
  405.      */
  406.     function free_result ({
  407.         if $this->free return true;
  408.         $this->free = false;
  409.         $this->lob array ();
  410.  
  411.         try {
  412.             if is_resource ($this->result) )
  413.                 return true;
  414.  
  415.             return pg_free_result ($this->result);
  416.         catch Exception $e {
  417.             throw new myException ($e->getMessage ()$e->getCode()$e);
  418.             return false;
  419.         }
  420.     }
  421.     // }}}
  422.  
  423.     // {{{ (string) EDB_PGSQL::field_name ($index)
  424.     /**
  425.      * Get the name of the specified field in a result
  426.      *
  427.      * @access public
  428.      * @return string|false
  429.      * @param  integer Field number, starting from 0.
  430.      * @see http://php.net/manual/en/function.pg-field-name.php pg_field_name()
  431.      */
  432.     function field_name ($index{
  433.         try {
  434.             if is_resource ($this->result) )
  435.                 return false;
  436.  
  437.             return pg_field_name ($this->result$index);
  438.         catch Exception $e {
  439.             throw new myException ($e->getMessage ()$e->getCode()$e);
  440.             return false;
  441.         }
  442.  
  443.         return false;
  444.     }
  445.     // }}}
  446.  
  447.     // {{{ (string) EDB_PGSQL::field_type ($index)
  448.     /**
  449.      * Returns the type name for the corresponding field number
  450.      *
  451.      * returns a string containing the base type name of the given
  452.      * field_number in the given PostgreSQL result resource.
  453.      *
  454.      * @access public
  455.      * @return string|false
  456.      * @param  integer Field number, starting from 0.
  457.      */
  458.     function field_type ($index{
  459.         try {
  460.             if is_resource ($this->result) )
  461.                 return false;
  462.  
  463.             return pg_field_type ($this->result$index);
  464.         catch Exception $e {
  465.             throw new myException ($e->getMessage ()$e->getCode()$e);
  466.             return false;
  467.         }
  468.     }
  469.     // }}}
  470.  
  471.     // {{{ (int) EDB_PGSQL::num_fields (void)
  472.     /**
  473.      * Returns the number of fields in a result
  474.      *
  475.      * @access public
  476.      * @return integer|false
  477.      * @see http://php.net/manual/en/function.pg-num-fields.php pg_num_fields()
  478.      */
  479.     function num_fields ({
  480.         try {
  481.             if is_resource ($this->result) )
  482.                 return false;
  483.             $r pg_num_fields ($this->result);
  484.         catch Exception $e {
  485.             throw new myException ($e->getMessage ()$e->getCode()$e);
  486.             return false;
  487.         }
  488.  
  489.         return ($r != -1$r false;
  490.     }
  491.     // }}}
  492.  
  493.     // {{{ (void) EDB_PGSQL::trstart (void)
  494.     /**
  495.      * DB transaction 을 시작한다.
  496.      *
  497.      * @access public
  498.      * @return void 
  499.      */
  500.     function trstart ({
  501.         $this->db->query ('BEGIN');
  502.     }
  503.     // }}}
  504.  
  505.     // {{{ (void) EDB_PGSQL::trend ($v)
  506.     /**
  507.      * DB transaction 을 종료한다.
  508.      *
  509.      * @access public
  510.      * @return void 
  511.      * @param bool false일경우 rollback을 수행한다.
  512.      */
  513.     function trend ($v true{
  514.         $sql ($v === false'ROLLBACK' 'COMMIT';
  515.         $this->db->query ($sql);
  516.     }
  517.     // }}}
  518.  
  519.     // {{{ (void) EDB_PGSQL::close (void)
  520.     /**
  521.      * Close the db handle
  522.      *
  523.      * @access public
  524.      * @return void 
  525.      * @param  void 
  526.      */
  527.     function close ({
  528.         if is_resource ($this->db) ) {
  529.             pg_close ($this->db);
  530.             unset ($this->db);
  531.         }
  532.     }
  533.     // }}}
  534.  
  535.     /*
  536.      * Priavte functions
  537.      */
  538.     // {{{ private (bool) EDB_PGSQL::no_bind_query ($sql)
  539.     /** 
  540.      * Performs a query on the database
  541.      *
  542.      * @access private
  543.      * @return boolean 
  544.      * @param  string  The query strings
  545.      */
  546.     private function no_bind_query ($sql{
  547.         try {
  548.             if ( ($this->result = pg_query ($this->db$sql)) === false {
  549.                 $this->free = false;
  550.                 throw new myException (pg_last_error ($this->db)E_USER_WARNING);
  551.                 return false;
  552.             }
  553.         catch Exception $e {
  554.             $this->free = false;
  555.             throw new myException ($e->getMessage ()$e->getCode ()$e);
  556.             return false;
  557.         }
  558.  
  559.         $this->switch_freemark ();
  560.         return true;
  561.     }
  562.     // }}}
  563.  
  564.     // {{{ private (bool) EDB_PGSQL::bind_query ($sql, $parameters)
  565.     /** 
  566.      * Performs a bind query on the database
  567.      *
  568.      * @access private
  569.      * @return boolean 
  570.      * @param  string  The query strings
  571.      * @param  array   (optional) Bind parameter type
  572.      */
  573.     private function bind_query ($sql$params{
  574.         if isset ($param) )
  575.             unset ($param);
  576.  
  577.         if $this->pno != count ($params|| $this->check_param ($params=== false {
  578.             $this->free = false;
  579.             throw new myExeption (
  580.                 'Number of elements in query doesn\'t match number of bind variables',
  581.                 E_USER_WARNING
  582.             );
  583.             return false;
  584.         }
  585.  
  586.         try {
  587.             $type array_shift ($params);
  588.             foreach ($params as $key => $v{
  589.                 if is_object ($v) )
  590.                     $params[$key$v->data;
  591.  
  592.                 if $type[$key== 'b' || $type[$key== 'c' )
  593.                     $params[$key$this->escape ($params[$key]'b');
  594.             }
  595.  
  596.             $this->result = pg_query_params ($this->db$sql$params);
  597.             if is_resource ($this->result) ) {
  598.                 $this->free = false;
  599.                 throw new myExeption (pg_last_error ($this->db)E_USER_WARNING);
  600.                 return false;
  601.             }
  602.         catch Exception $e {
  603.             $this->free = false;
  604.             throw new myException ($e->getMessage ()$e->getCode ()$e);
  605.             return false;
  606.         }
  607.  
  608.         $this->switch_freemark ();
  609.         return true;
  610.     }
  611.     // }}}
  612.  
  613.     function __destruct ({
  614.         try {
  615.             $this->free_result ();
  616.             $this->close ();
  617.         catch Exception $e }
  618.     }
  619. }
  620.  
  621. /*
  622.  * Local variables:
  623.  * tab-width: 4
  624.  * c-basic-offset: 4
  625.  * End:
  626.  * vim: set filetype=php noet sw=4 ts=4 fdm=marker:
  627.  * vim600: noet sw=4 ts=4 fdm=marker
  628.  * vim<600: noet sw=4 ts=4
  629.  */
  630. ?>

Documentation generated on Tue, 14 May 2019 01:59:54 +0900 by phpDocumentor 1.4.4