includes/dbal.php
changeset 1165 ce8aaa2956d1
parent 1147 7ddd475bc661
child 1175 1e2c9819ede3
equal deleted inserted replaced
1163:1b90f6c41d9c 1165:ce8aaa2956d1
    40   var $row = array();
    40   var $row = array();
    41 	var $rowset = array();
    41 	var $rowset = array();
    42   var $errhandler;
    42   var $errhandler;
    43   var $dbms_name = 'MySQL';
    43   var $dbms_name = 'MySQL';
    44   
    44   
    45   function enable_errorhandler()
    45   /**
    46   {
    46    * Get a flat textual list of queries that have been made.
    47     if ( !defined('ENANO_DEBUG') )
    47    */
    48       return true;
    48   
    49     // echo "DBAL: enabling error handler<br />";
    49   function sql_backtrace()
    50     if ( function_exists('debug_backtrace') )
    50   {
    51     {
    51     return implode("\n-------------------------------------------------------------------\n", $this->query_backtrace);
    52       $this->errhandler = set_error_handler('db_error_handler');
    52   }
    53     }
    53   
    54   }
    54   /**
    55   
    55    * Connect to the database, but only if a connection isn't already up.
    56   function disable_errorhandler()
    56    */
    57   {
    57   
    58     if ( !defined('ENANO_DEBUG') )
    58   function ensure_connection()
    59       return true;
    59   {
    60     // echo "DBAL: disabling error handler<br />";
    60     if(!$this->_conn)
    61     if ( $this->errhandler )
    61     {
    62     {
    62       $this->connect();
    63       set_error_handler($this->errhandler);
    63     }
    64     }
    64   }
       
    65   
       
    66   /**
       
    67    * Exit Enano, dumping out a friendly error message indicating a database error on the way out.
       
    68    * @param string Description or location of error; defaults to none
       
    69    */
       
    70  
       
    71   function _die($t = '')
       
    72   {
       
    73     if ( defined('ENANO_HEADERS_SENT') )
       
    74       ob_clean();
       
    75     
       
    76     $internal_text = $this->get_error($t);
       
    77     
       
    78     if ( defined('ENANO_CONFIG_FETCHED') )
       
    79       // config is in, we can show a slightly nicer looking error page
       
    80       die_semicritical('Database error', $internal_text);
    65     else
    81     else
    66     {
    82       // no config, display using no-DB template engine
    67       restore_error_handler();
    83       grinding_halt('Database error', $internal_text);
    68     }
    84     
    69   }
    85     exit;
    70   
    86   }
    71   function sql_backtrace()
    87   
    72   {
    88   /**
    73     return implode("\n-------------------------------------------------------------------\n", $this->query_backtrace);
    89    * Get the internal text used for a database error message.
    74   }
    90    * @param string Description or location of error; defaults to none
    75   
    91    */
    76   function ensure_connection()
    92   
    77   {
    93   function get_error($t = '')
    78     if(!$this->_conn)
    94   {
    79     {
    95     @header('HTTP/1.1 500 Internal Server Error');
    80       $this->connect();
    96     
    81     }
    97     $bt = $this->latest_query;
    82   }
    98     $e = htmlspecialchars($this->sql_error());
    83   
    99     if ( empty($e) )
    84   function _die($t = '') {
   100       $e = '&lt;none&gt;';
    85     if ( defined('ENANO_HEADERS_SENT') )
   101     
    86     {
       
    87       ob_clean();
       
    88     }
       
    89     
       
    90     if ( !headers_sent() )
       
    91       header('HTTP/1.1 500 Internal Server Error');
       
    92     
       
    93     $bt = $this->latest_query; // $this->sql_backtrace();
       
    94     $e = htmlspecialchars(mysql_error());
       
    95     if($e=='') $e='&lt;none&gt;';
       
    96     $t = ( !empty($t) ) ? $t : '&lt;No error description provided&gt;';
       
    97     global $email;
   102     global $email;
    98     $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) ) ? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;' : '';
   103     
    99     $internal_text = '<h3>The site was unable to finish serving your request.</h3>
   104     // As long as the admin's e-mail is accessible, display it.
   100                       <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site' . $email_info . '.</p>
   105     $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) )
   101                       <p>Description or location of error: '.$t.'<br />
   106                     ? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;'
   102                       Error returned by MySQL extension: ' . $e . '<br />
   107                     : '';
   103                       Most recent SQL query:</p>
   108     
   104                       <pre>'.$bt.'</pre>';
   109     $internal_text = "<h3>The site was unable to finish serving your request.</h3>
   105     if ( defined('IN_ENANO_INSTALL') && is_object(@$GLOBALS['ui']) )
   110                       <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site{$email_info}.</p>
   106     {
   111                       <p>Description or location of error: $t<br />
   107       global $ui;
   112                       Error returned by $this->dbms_name extension: $e</p>
   108       echo '<h2>Database error!</h2>';
   113                       <p>Most recent SQL query:</p>
   109       echo $internal_text;
   114                       <pre>$bt</pre>";
   110       $ui->show_footer();
   115     return $internal_text;
   111       
   116   }
   112       exit;
   117   
   113     }
   118   /**
   114     if(defined('ENANO_CONFIG_FETCHED')) die_semicritical('Database error', $internal_text);
   119    * Exit Enano and output a JSON format datbase error.
   115     else                                   grinding_halt('Database error', $internal_text);
   120    * @param string Description or location of error; defaults to none
   116     exit;
   121    */
   117   }
       
   118   
   122   
   119   function die_json($loc = false)
   123   function die_json($loc = false)
   120   {
   124   {
   121     $e = str_replace("\n", "\\n", addslashes(htmlspecialchars(mysql_error())));
   125     $e = str_replace("\n", "\\n", addslashes(htmlspecialchars($this->sql_error())));
   122     $q = str_replace("\n", "\\n", addslashes($this->latest_query));
   126     $q = str_replace("\n", "\\n", addslashes($this->latest_query));
   123     $loc = ( $loc ) ? addslashes("\n\nDescription or location of error: $loc") : "";
   127     $loc = ( $loc ) ? addslashes("\n\nDescription or location of error: $loc") : "";
   124     $loc .= "\n\nPlease report the full text of this error to the administrator of the site. If you believe that this is a bug with the software, please contact support@enanocms.org.";
   128     $loc .= "\n\nPlease report the full text of this error to the administrator of the site. If you believe that this is a bug with the software, please contact support@enanocms.org.";
   125     $loc = str_replace("\n", "\\n", $loc);
   129     $loc = str_replace("\n", "\\n", $loc);
   126     $t = "{\"mode\":\"error\",\"error\":\"An error occurred during database query.\\nQuery was:\\n  $q\\n\\nError returned by MySQL: $e$loc\"}";
   130     $t = "{\"mode\":\"error\",\"error\":\"An error occurred during database query.\\nQuery was:\\n  $q\\n\\nError returned by {$this->dbms_name}: $e$loc\"}";
   127     die($t);
   131     die($t);
   128   }
   132   }
   129   
   133   
   130   function get_error($t = '') {
   134   /**
   131     header('HTTP/1.1 500 Internal Server Error');
   135    * Connect to the database.
   132     $bt = $this->sql_backtrace();
   136    * @param bool If true, enables all other parameters. Defaults to false, which emans that you can call this function with no arguments and it will fetch information from the config file.
   133     $e = htmlspecialchars(mysql_error());
   137    * @param string Database server hostname
   134     if($e=='') $e='&lt;none&gt;';
   138    * @param string Database server username
   135     global $email;
   139    * @param string Database server password
   136     $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) ) ? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;' : '';
   140    * @param string Name of the database
   137     $internal_text = '<h3>The site was unable to finish serving your request.</h3>
   141    * @param int Optional port number to connect over
   138                       <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site' . $email_info . '.</p>
   142    */
   139                       <p>Description or location of error: '.$t.'<br />
       
   140                       Error returned by MySQL extension: ' . $e . '<br />
       
   141                       Most recent SQL query:</p>
       
   142                       <pre>'.$bt.'</pre>';
       
   143     return $internal_text;
       
   144   }
       
   145   
   143   
   146   function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false, $dbport = false)
   144   function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false, $dbport = false)
   147   {
   145   {
   148     if ( !defined('ENANO_SQL_CONSTANTS') )
   146     if ( !defined('ENANO_SQL_CONSTANTS') )
   149     {
   147     {
   242     
   240     
   243     // We're in!
   241     // We're in!
   244     return true;
   242     return true;
   245   }
   243   }
   246   
   244   
       
   245   /**
       
   246    * Make a SQL query.
       
   247    * @param string Query
       
   248    * @param bool If false, skips all checks and logging stages. If you're doing a ton of queries, set this to true; in all other cases, leave at the default of false.
       
   249    * @return resource or false on failure
       
   250    */
       
   251   
   247   function sql_query($q, $log_query = true)
   252   function sql_query($q, $log_query = true)
   248   {
   253   {
   249     if ( $this->debug && function_exists('debug_backtrace') )
   254     if ( $this->debug && function_exists('debug_backtrace') )
   250     {
   255     {
   251       $backtrace = @debug_backtrace();
   256       $backtrace = @debug_backtrace();
   297     $this->latest_result = $r;
   302     $this->latest_result = $r;
   298     
   303     
   299     return $r;
   304     return $r;
   300   }
   305   }
   301   
   306   
       
   307   /**
       
   308    * Make a SQL query, but do not have PHP buffer all the results. Useful for queries that are expected to return a huge number of results.
       
   309    * @param string Query
       
   310    * @param bool If false, skips all checks and logging stages. If you're doing a ton of queries, set this to true; in all other cases, leave at the default of false.
       
   311    * @return resource or false on failure
       
   312    */
       
   313   
   302   function sql_unbuffered_query($q, $log_query = true)
   314   function sql_unbuffered_query($q, $log_query = true)
   303   {
   315   {
   304     $this->num_queries++;
   316     $this->num_queries++;
   305     if ( $log_query || defined('ENANO_DEBUG') )
   317     if ( $log_query || defined('ENANO_DEBUG') )
   306       $this->query_backtrace[] = '(UNBUFFERED) ' . $q;
   318       $this->query_backtrace[] = '(UNBUFFERED) ' . $q;
   379    * @return true on success, false on failure
   391    * @return true on success, false on failure
   380    */
   392    */
   381    
   393    
   382   function sql_data_seek($pos, $result = false)
   394   function sql_data_seek($pos, $result = false)
   383   {
   395   {
   384     if(!$result)
   396     if ( !$result )
   385       $result = $this->latest_result;
   397       $result = $this->latest_result;
   386     if(!$result)
   398     if ( !$result )
   387     {
       
   388       return false;
   399       return false;
   389     }
   400     
   390     if(mysql_data_seek($result, $pos))
   401     return mysql_data_seek($result, $pos) ? true : false;
   391     {
       
   392       return true;
       
   393     }
       
   394     else
       
   395     {
       
   396       return false;
       
   397     }
       
   398   }
   402   }
   399   
   403   
   400   /**
   404   /**
   401    * Reports a bad query to the admin
   405    * Reports a bad query to the admin
   402    * @param string $query the naughty query
   406    * @param string $query the naughty query
   404    */
   408    */
   405    
   409    
   406   function report_query($query)
   410   function report_query($query)
   407   {
   411   {
   408     global $session;
   412     global $session;
   409     if(is_object($session) && defined('ENANO_MAINSTREAM'))
   413     if ( is_object($session) && defined('ENANO_MAINSTREAM') )
   410       $username = $session->username;
   414       $username = $session->username;
   411     else
   415     else
   412       $username = 'Unavailable';
   416       $username = 'Unavailable';
       
   417     
   413     $query = $this->escape($query);
   418     $query = $this->escape($query);
   414     $q = $this->sql_query('INSERT INTO '.table_prefix.'logs(log_type,     action,         time_id,    date_string, page_text,      author,            edit_summary)
   419     $q = $this->sql_query('INSERT INTO '.table_prefix.'logs(log_type,     action,         time_id,    date_string, page_text,      author,            edit_summary)
   415                                                      VALUES(\'security\', \'sql_inject\', '.time().', \'\',        \''.$query.'\', \''.$username.'\', \''.$_SERVER['REMOTE_ADDR'].'\');');
   420                                                      VALUES(\'security\', \'sql_inject\', '.time().', \'\',        \''.$query.'\', \''.$username.'\', \''.$_SERVER['REMOTE_ADDR'].'\');');
   416   }
   421   }
   417   
   422   
   423   function insert_id()
   428   function insert_id()
   424   {
   429   {
   425     return @mysql_insert_id();
   430     return @mysql_insert_id();
   426   }
   431   }
   427   
   432   
   428   function fetchrow($r = false) {
   433   /**
       
   434    * Fetch one row from the given query as an associative array.
       
   435    * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
       
   436    * @return array
       
   437    */
       
   438   
       
   439   function fetchrow($r = false)
       
   440   {
   429     if ( !$this->_conn )
   441     if ( !$this->_conn )
   430       return false;
   442       return false;
   431     
   443     
   432     if ( !$r )
   444     if ( !$r )
   433       $r = $this->latest_result;
   445       $r = $this->latest_result;
   434     
   446     
   435     if ( !$r )
   447     if ( !$r )
   436       $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
   448       $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
   437     
   449     
   438     $row = mysql_fetch_assoc($r);
   450     $row = mysql_fetch_assoc($r);
   439     /*
       
   440     if ( empty($row) )
       
   441     {
       
   442       $GLOBALS['do_gzip'] = false;
       
   443       echo '<pre>' . enano_debug_print_backtrace(true) . '</pre>';
       
   444     }
       
   445     */
       
   446     
   451     
   447     return integerize_array($row);
   452     return integerize_array($row);
   448   }
   453   }
   449   
   454   
   450   function fetchrow_num($r = false) {
   455   /**
   451     if(!$r) $r = $this->latest_result;
   456    * Fetch one row from the given query as a numeric array.
   452     if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
   457    * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
       
   458    * @return array
       
   459    */
       
   460   
       
   461   function fetchrow_num($r = false)
       
   462   {
       
   463     if ( !$r )
       
   464       $r = $this->latest_result;
       
   465     if ( !$r )
       
   466       $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
       
   467     
   453     $row = mysql_fetch_row($r);
   468     $row = mysql_fetch_row($r);
   454     return integerize_array($row);
   469     return integerize_array($row);
   455   }
   470   }
   456   
   471   
   457   function numrows($r = false) {
   472   /**
   458     if(!$r) $r = $this->latest_result;
   473    * Get the number of results for a given query.
   459     if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
   474    * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
   460     $n = mysql_num_rows($r);
   475    * @return array
   461     return $n;
   476    */
   462   }
   477   
       
   478   function numrows($r = false)
       
   479   {
       
   480     if ( !$r )
       
   481       $r = $this->latest_result;
       
   482     if ( !$r )
       
   483       $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
       
   484     
       
   485     return mysql_num_rows($r);
       
   486   }
       
   487   
       
   488   /**
       
   489    * Escape a string so that it may safely be included in a SQL query.
       
   490    * @param string String to escape
       
   491    * @return string Escaped string
       
   492    */
   463   
   493   
   464   function escape($str)
   494   function escape($str)
   465   {
   495   {
   466     $str = mysql_real_escape_string($str);
   496     $str = mysql_real_escape_string($str);
   467     return $str;
   497     return $str;
   468   }
   498   }
   469   
   499   
       
   500   /**
       
   501    * Free the given result from memory. Use this when completely finished with a result resource.
       
   502    * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
       
   503    * @return null
       
   504    */
       
   505   
   470   function free_result($result = false)
   506   function free_result($result = false)
   471   {
   507   {
   472     if(!$result)
   508     if ( !$result )
   473       $result = $this->latest_result;
   509       $result = $this->latest_result;
   474     if(!$result)
   510     if ( !$result )
   475     {
       
   476       return null;
   511       return null;
   477     }
   512     
   478     @mysql_free_result($result);
   513     @mysql_free_result($result);
   479     return null;
   514     return null;
   480   }
   515   }
   481   
   516   
   482   function close() {
   517   /**
       
   518    * Close the database connection
       
   519    */
       
   520   
       
   521   function close()
       
   522   {
   483     @mysql_close($this->_conn);
   523     @mysql_close($this->_conn);
   484     unset($this->_conn);
   524     unset($this->_conn);
   485   }
       
   486   
       
   487   // phpBB DBAL compatibility
       
   488   function sql_fetchrow($r = false)
       
   489   {
       
   490     return $this->fetchrow($r);
       
   491   }
       
   492   function sql_freeresult($r = false)
       
   493   {
       
   494     if(!$this->_conn) return false;
       
   495     if(!$r) $r = $this->latest_result;
       
   496     if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
       
   497     @mysql_free_result($r);
       
   498   }
       
   499   function sql_numrows($r = false)
       
   500   {
       
   501     if(!$this->_conn) return false;
       
   502     if(!$r) $r = $this->latest_result;
       
   503     if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
       
   504     return mysql_num_rows($r);
       
   505   }
       
   506   function sql_affectedrows($r = false, $f = false, $n = false)
       
   507   {
       
   508     if(!$this->_conn) return false;
       
   509     if(!$r) $r = $this->latest_result;
       
   510     if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
       
   511     return mysql_affected_rows();
       
   512   }
   525   }
   513   
   526   
   514   /**
   527   /**
   515    * Get a list of columns in the given table
   528    * Get a list of columns in the given table
   516    * @param string Table
   529    * @param string Table
   531       $columns[] = $row[0];
   544       $columns[] = $row[0];
   532     }
   545     }
   533     return $columns;
   546     return $columns;
   534   }
   547   }
   535   
   548   
   536   function sql_type_cast(&$value)
   549   /**
   537 	{
   550    * Get the text of the most recent error.
   538 		if ( is_float($value) )
   551    * @return string
   539 		{
   552    */
   540 			return doubleval($value);
   553   
   541 		}
   554   function sql_error()
   542 		if ( is_integer($value) || is_bool($value) )
       
   543 		{
       
   544 			return intval($value);
       
   545 		}
       
   546 		if ( is_string($value) || empty($value) )
       
   547 		{
       
   548 			return '\'' . $this->sql_escape_string($value) . '\'';
       
   549 		}
       
   550 		// uncastable var : let's do a basic protection on it to prevent sql injection attempt
       
   551 		return '\'' . $this->sql_escape_string(htmlspecialchars($value)) . '\'';
       
   552 	}
       
   553 
       
   554 	function sql_statement(&$fields, $fields_inc='')
       
   555 	{
       
   556 		// init result
       
   557 		$this->sql_fields = $this->sql_values = $this->sql_update = '';
       
   558 		if ( empty($fields) && empty($fields_inc) )
       
   559 		{
       
   560 			return;
       
   561 		}
       
   562 
       
   563 		// process
       
   564 		if ( !empty($fields) )
       
   565 		{
       
   566 			$first = true;
       
   567 			foreach ( $fields as $field => $value )
       
   568 			{
       
   569 				// field must contain a field name
       
   570 				if ( !empty($field) && is_string($field) )
       
   571 				{
       
   572 					$value = $this->sql_type_cast($value);
       
   573 					$this->sql_fields .= ( $first ? '' : ', ' ) . $field;
       
   574 					$this->sql_values .= ( $first ? '' : ', ' ) . $value;
       
   575 					$this->sql_update .= ( $first ? '' : ', ' ) . $field . ' = ' . $value;
       
   576 					$first = false;
       
   577 				}
       
   578 			}
       
   579 		}
       
   580 		if ( !empty($fields_inc) )
       
   581 		{
       
   582 			foreach ( $fields_inc as $field => $indent )
       
   583 			{
       
   584 				if ( $indent != 0 )
       
   585 				{
       
   586 					$this->sql_update .= (empty($this->sql_update) ? '' : ', ') . $field . ' = ' . $field . ($indent < 0 ? ' - ' : ' + ') . abs($indent);
       
   587 				}
       
   588 			}
       
   589 		}
       
   590 	}
       
   591 
       
   592 	function sql_stack_reset($id='')
       
   593 	{
       
   594 		if ( empty($id) )
       
   595 		{
       
   596 			$this->sql_stack_fields = array();
       
   597 			$this->sql_stack_values = array();
       
   598 		}
       
   599 		else
       
   600 		{
       
   601 			$this->sql_stack_fields[$id] = array();
       
   602 			$this->sql_stack_values[$id] = array();
       
   603 		}
       
   604 	}
       
   605 
       
   606 	function sql_stack_statement(&$fields, $id='')
       
   607 	{
       
   608 		$this->sql_statement($fields);
       
   609 		if ( empty($id) )
       
   610 		{
       
   611 			$this->sql_stack_fields = $this->sql_fields;
       
   612 			$this->sql_stack_values[] = '(' . $this->sql_values . ')';
       
   613 		}
       
   614 		else
       
   615 		{
       
   616 			$this->sql_stack_fields[$id] = $this->sql_fields;
       
   617 			$this->sql_stack_values[$id][] = '(' . $this->sql_values . ')';
       
   618 		}
       
   619 	}
       
   620 
       
   621 	function sql_stack_insert($table, $transaction=false, $line='', $file='', $break_on_error=true, $id='')
       
   622 	{
       
   623 		if ( (empty($id) && empty($this->sql_stack_values)) || (!empty($id) && empty($this->sql_stack_values[$id])) )
       
   624 		{
       
   625 			return false;
       
   626 		}
       
   627 		switch( SQL_LAYER )
       
   628 		{
       
   629 			case 'mysql':
       
   630 			case 'mysql4':
       
   631 				if ( empty($id) )
       
   632 				{
       
   633 					$sql = 'INSERT INTO ' . $table . '
       
   634 								(' . $this->sql_stack_fields . ') VALUES ' . implode(",\n", $this->sql_stack_values);
       
   635 				}
       
   636 				else
       
   637 				{
       
   638 					$sql = 'INSERT INTO ' . $table . '
       
   639 								(' . $this->sql_stack_fields[$id] . ') VALUES ' . implode(",\n", $this->sql_stack_values[$id]);
       
   640 				}
       
   641 				$this->sql_stack_reset($id);
       
   642 				return $this->sql_query($sql, $transaction, $line, $file, $break_on_error);
       
   643 				break;
       
   644 			default:
       
   645 				$count_sql_stack_values = empty($id) ? count($this->sql_stack_values) : count($this->sql_stack_values[$id]);
       
   646 				$result = !empty($count_sql_stack_values);
       
   647 				for ( $i = 0; $i < $count_sql_stack_values; $i++ )
       
   648 				{
       
   649 					if ( empty($id) )
       
   650 					{
       
   651 						$sql = 'INSERT INTO ' . $table . '
       
   652 									(' . $this->sql_stack_fields . ') VALUES ' . $this->sql_stack_values[$i];
       
   653 					}
       
   654 					else
       
   655 					{
       
   656 						$sql = 'INSERT INTO ' . $table . '
       
   657 									(' . $this->sql_stack_fields[$id] . ') VALUES ' . $this->sql_stack_values[$id][$i];
       
   658 					}
       
   659 					$result &= $this->sql_query($sql, $transaction, $line, $file, $break_on_error);
       
   660 				}
       
   661 				$this->sql_stack_reset($id);
       
   662 				return $result;
       
   663 				break;
       
   664 		}
       
   665 	}
       
   666 
       
   667 	function sql_subquery($field, $sql, $line='', $file='', $break_on_error=true, $type=TYPE_INT)
       
   668 	{
       
   669 		// sub-queries doable
       
   670 		$this->sql_get_version();
       
   671 		if ( !in_array(SQL_LAYER, array('mysql', 'mysql4')) || (($this->sql_version[0] + ($this->sql_version[1] / 100)) >= 4.01) )
       
   672 		{
       
   673 			return $sql;
       
   674 		}
       
   675 
       
   676 		// no sub-queries
       
   677 		$ids = array();
       
   678 		$result = $this->sql_query(trim($sql), false, $line, $file, $break_on_error);
       
   679 		while ( $row = $this->sql_fetchrow($result) )
       
   680 		{
       
   681 			$ids[] = $type == TYPE_INT ? intval($row[$field]) : '\'' . $this->sql_escape_string($row[$field]) . '\'';
       
   682 		}
       
   683 		$this->sql_freeresult($result);
       
   684 		return empty($ids) ? 'NULL' : implode(', ', $ids);
       
   685 	}
       
   686 
       
   687 	function sql_col_id($expr, $alias)
       
   688 	{
       
   689 		$this->sql_get_version();
       
   690 		return in_array(SQL_LAYER, array('mysql', 'mysql4')) && (($this->sql_version[0] + ($this->sql_version[1] / 100)) <= 4.01) ? $alias : $expr;
       
   691 	}
       
   692 
       
   693 	function sql_get_version()
       
   694 	{
       
   695 		if ( empty($this->sql_version) )
       
   696 		{
       
   697 			$this->sql_version = array(0, 0, 0);
       
   698 			switch ( SQL_LAYER )
       
   699 			{
       
   700 				case 'mysql':
       
   701 				case 'mysql4':
       
   702 					if ( function_exists('mysql_get_server_info') )
       
   703 					{
       
   704 						$lo_version = explode('-', mysql_get_server_info());
       
   705 						$this->sql_version = explode('.', $lo_version[0]);
       
   706 						$this->sql_version = array(intval($this->sql_version[0]), intval($this->sql_version[1]), intval($this->sql_version[2]), $lo_version[1]);
       
   707 					}
       
   708 					break;
       
   709 
       
   710 				case 'postgresql':
       
   711 				case 'mssql':
       
   712 				case 'mssql-odbc':
       
   713 				default:
       
   714 					break;
       
   715 			}
       
   716 		}
       
   717 		return $this->sql_version;
       
   718 	}
       
   719 
       
   720 	function sql_error()
       
   721 	{
   555 	{
   722     return mysql_error();
   556     return mysql_error();
   723 	}
   557 	}
   724   function sql_escape_string($t) 
   558   
   725   {
       
   726     return mysql_real_escape_string($t);
       
   727   }
       
   728   function sql_close()
       
   729   {
       
   730     $this->close();
       
   731   }
       
   732   function sql_fetchrowset($query_id = 0)
       
   733 	{
       
   734 		if( !$query_id )
       
   735 		{
       
   736 			$query_id = $this->query_result;
       
   737 		}
       
   738 
       
   739 		if( $query_id )
       
   740 		{
       
   741 			unset($this->rowset[$query_id]);
       
   742 			unset($this->row[$query_id]);
       
   743 
       
   744 			while($this->rowset[$query_id] = mysql_fetch_array($query_id, MYSQL_ASSOC))
       
   745 			{
       
   746 				$result[] = $this->rowset[$query_id];
       
   747 			}
       
   748 
       
   749 			return $result;
       
   750 		}
       
   751 		else
       
   752 		{
       
   753 			return false;
       
   754 		}
       
   755 	}
       
   756   /**
   559   /**
   757    * Generates and outputs a report of all the SQL queries made during execution. Should only be called after everything's over with.
   560    * Generates and outputs a report of all the SQL queries made during execution. Should only be called after everything's over with.
   758    */
   561    */
   759   
   562   
   760   function sql_report()
   563   function sql_report()
   826           </div>';
   629           </div>';
   827     $template->footer();
   630     $template->footer();
   828   }
   631   }
   829 }
   632 }
   830 
   633 
   831 class postgresql {
   634 class postgresql
       
   635 {
   832   var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug;
   636   var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug;
   833   var $row = array();
   637   var $row = array();
   834 	var $rowset = array();
   638 	var $rowset = array();
   835   var $errhandler;
   639   var $errhandler;
   836   var $dbms_name = 'PostgreSQL';
   640   var $dbms_name = 'PostgreSQL';
   837   
   641   
       
   642   /**
       
   643    * Get a flat textual list of queries that have been made.
       
   644    */
       
   645   
   838   function sql_backtrace()
   646   function sql_backtrace()
   839   {
   647   {
   840     return implode("\n-------------------------------------------------------------------\n", $this->query_backtrace);
   648     return implode("\n-------------------------------------------------------------------\n", $this->query_backtrace);
   841   }
   649   }
   842   
   650   
       
   651   /**
       
   652    * Connect to the database, but only if a connection isn't already up.
       
   653    */
       
   654   
   843   function ensure_connection()
   655   function ensure_connection()
   844   {
   656   {
   845     if(!$this->_conn)
   657     if(!$this->_conn)
   846     {
   658     {
   847       $this->connect();
   659       $this->connect();
   848     }
   660     }
   849   }
   661   }
   850   
   662   
   851   function _die($t = '') {
   663   /**
   852     if(defined('ENANO_HEADERS_SENT')) {
   664    * Exit Enano, dumping out a friendly error message indicating a database error on the way out.
       
   665    * @param string Description or location of error; defaults to none
       
   666    */
       
   667  
       
   668   function _die($t = '')
       
   669   {
       
   670     if ( defined('ENANO_HEADERS_SENT') )
   853       ob_clean();
   671       ob_clean();
   854     }
   672     
   855     header('HTTP/1.1 500 Internal Server Error');
   673     $internal_text = $this->get_error($t);
   856     $bt = $this->latest_query; // $this->sql_backtrace();
   674     
   857     $e = htmlspecialchars(pg_last_error());
   675     if ( defined('ENANO_CONFIG_FETCHED') )
   858     if($e=='') $e='&lt;none&gt;';
   676       // config is in, we can show a slightly nicer looking error page
   859     $t = ( !empty($t) ) ? $t : '&lt;No error description provided&gt;';
   677       die_semicritical('Database error', $internal_text);
       
   678     else
       
   679       // no config, display using no-DB template engine
       
   680       grinding_halt('Database error', $internal_text);
       
   681     
       
   682     exit;
       
   683   }
       
   684   
       
   685   /**
       
   686    * Get the internal text used for a database error message.
       
   687    * @param string Description or location of error; defaults to none
       
   688    */
       
   689   
       
   690   function get_error($t = '')
       
   691   {
       
   692     @header('HTTP/1.1 500 Internal Server Error');
       
   693     
       
   694     $bt = $this->latest_query;
       
   695     $e = htmlspecialchars($this->sql_error());
       
   696     if ( empty($e) )
       
   697       $e = '&lt;none&gt;';
       
   698     
   860     global $email;
   699     global $email;
   861     $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) ) ? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;' : '';
   700     
   862     $internal_text = '<h3>The site was unable to finish serving your request.</h3>
   701     // As long as the admin's e-mail is accessible, display it.
   863                       <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site' . $email_info . '.</p>
   702     $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) )
   864                       <p>Description or location of error: '.$t.'<br />
   703                     ? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;'
   865                       Error returned by PostgreSQL extension: ' . $e . '<br />
   704                     : '';
   866                       Most recent SQL query:</p>
   705     
   867                       <pre>'.$bt.'</pre>';
   706     $internal_text = "<h3>The site was unable to finish serving your request.</h3>
   868     if(defined('ENANO_CONFIG_FETCHED')) die_semicritical('Database error', $internal_text);
   707                       <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site{$email_info}.</p>
   869     else                                   grinding_halt('Database error', $internal_text);
   708                       <p>Description or location of error: $t<br />
   870     exit;
   709                       Error returned by $this->dbms_name extension: $e</p>
   871   }
   710                       <p>Most recent SQL query:</p>
       
   711                       <pre>$bt</pre>";
       
   712     return $internal_text;
       
   713   }
       
   714   
       
   715   /**
       
   716    * Exit Enano and output a JSON format datbase error.
       
   717    * @param string Description or location of error; defaults to none
       
   718    */
   872   
   719   
   873   function die_json($loc = false)
   720   function die_json($loc = false)
   874   {
   721   {
   875     $e = str_replace("\n", "\\n", addslashes(htmlspecialchars(pg_last_error())));
   722     $e = str_replace("\n", "\\n", addslashes(htmlspecialchars($this->sql_error())));
   876     $q = str_replace("\n", "\\n", addslashes($this->latest_query));
   723     $q = str_replace("\n", "\\n", addslashes($this->latest_query));
   877     $loc = ( $loc ) ? addslashes("\n\nDescription or location of error: $loc") : "";
   724     $loc = ( $loc ) ? addslashes("\n\nDescription or location of error: $loc") : "";
   878     $loc .= "\n\nPlease report the full text of this error to the administrator of the site. If you believe that this is a bug with the software, please contact support@enanocms.org.";
   725     $loc .= "\n\nPlease report the full text of this error to the administrator of the site. If you believe that this is a bug with the software, please contact support@enanocms.org.";
   879     $loc = str_replace("\n", "\\n", $loc);
   726     $loc = str_replace("\n", "\\n", $loc);
   880     $t = "{\"mode\":\"error\",\"error\":\"An error occurred during database query.\\nQuery was:\\n  $q\\n\\nError returned by {$this->dbms_name}: $e$loc\"}";
   727     $t = "{\"mode\":\"error\",\"error\":\"An error occurred during database query.\\nQuery was:\\n  $q\\n\\nError returned by {$this->dbms_name}: $e$loc\"}";
   881     die($t);
   728     die($t);
   882   }
   729   }
   883   
   730   
   884   function get_error($t = '') {
   731   /**
   885     @header('HTTP/1.1 500 Internal Server Error');
   732    * Connect to the database.
   886     $bt = $this->sql_backtrace();
   733    * @param bool If true, enables all other parameters. Defaults to false, which emans that you can call this function with no arguments and it will fetch information from the config file.
   887     $e = htmlspecialchars(pg_last_error());
   734    * @param string Database server hostname
   888     if($e=='') $e='&lt;none&gt;';
   735    * @param string Database server username
   889     global $email;
   736    * @param string Database server password
   890     $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) ) ? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;' : '';
   737    * @param string Name of the database
   891     $internal_text = '<h3>The site was unable to finish serving your request.</h3>
   738    * @param int Optional port number to connect over
   892                       <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site' . $email_info . '.</p>
   739    */
   893                       <p>Description or location of error: '.$t.'<br />
       
   894                       Error returned by ' . $this->dbms_name . ' extension: ' . $e . '<br />
       
   895                       Most recent SQL query:</p>
       
   896                       <pre>'.$bt.'</pre>';
       
   897     return $internal_text;
       
   898   }
       
   899   
   740   
   900   function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false, $dbport = false)
   741   function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false, $dbport = false)
   901   {
   742   {
   902     if ( !defined('ENANO_SQL_CONSTANTS') )
   743     if ( !defined('ENANO_SQL_CONSTANTS') )
   903     {
   744     {
   977     
   818     
   978     // We're in!
   819     // We're in!
   979     return true;
   820     return true;
   980   }
   821   }
   981   
   822   
       
   823   /**
       
   824    * Make a SQL query.
       
   825    * @param string Query
       
   826    * @param bool If false, skips all checks and logging stages. If you're doing a ton of queries, set this to true; in all other cases, leave at the default of false.
       
   827    * @return resource or false on failure
       
   828    */
       
   829   
   982   function sql_query($q)
   830   function sql_query($q)
   983   {
   831   {
   984     if ( $this->debug && function_exists('debug_backtrace') )
   832     if ( $this->debug && function_exists('debug_backtrace') )
   985     {
   833     {
   986       $backtrace = @debug_backtrace();
   834       $backtrace = @debug_backtrace();
  1019     $this->query_times[$q] = microtime_float() - $time_start;
   867     $this->query_times[$q] = microtime_float() - $time_start;
  1020     $this->latest_result = $r;
   868     $this->latest_result = $r;
  1021     return $r;
   869     return $r;
  1022   }
   870   }
  1023   
   871   
       
   872   /**
       
   873    * Make a SQL query, but do not have PHP buffer all the results. Useful for queries that are expected to return a huge number of results.
       
   874    * @param string Query
       
   875    * @param bool If false, skips all checks and logging stages. If you're doing a ton of queries, set this to true; in all other cases, leave at the default of false.
       
   876    * @return resource or false on failure
       
   877    */
       
   878   
  1024   function sql_unbuffered_query($q)
   879   function sql_unbuffered_query($q)
  1025   {
   880   {
  1026     return $this->sql_query($q);
   881     return $this->sql_query($q);
  1027   }
   882   }
  1028   
   883   
  1077    * @return true on success, false on failure
   932    * @return true on success, false on failure
  1078    */
   933    */
  1079    
   934    
  1080   function sql_data_seek($pos, $result = false)
   935   function sql_data_seek($pos, $result = false)
  1081   {
   936   {
  1082     if(!$result)
   937     if ( !$result )
  1083       $result = $this->latest_result;
   938       $result = $this->latest_result;
  1084     if(!$result)
   939     if ( !$result )
  1085     {
       
  1086       return false;
   940       return false;
  1087     }
   941     
  1088     if(pg_result_seek($result, $pos))
   942     return pg_result_seek($result, $pos) ? true : false;
  1089     {
       
  1090       return true;
       
  1091     }
       
  1092     else
       
  1093     {
       
  1094       return false;
       
  1095     }
       
  1096   }
   943   }
  1097   
   944   
  1098   /**
   945   /**
  1099    * Reports a bad query to the admin
   946    * Reports a bad query to the admin
  1100    * @param string $query the naughty query
   947    * @param string $query the naughty query
  1170       }
  1017       }
  1171     }
  1018     }
  1172     return false;
  1019     return false;
  1173   }
  1020   }
  1174   
  1021   
  1175   function fetchrow($r = false) {
  1022   /**
  1176     if(!$this->_conn) return false;
  1023    * Fetch one row from the given query as an associative array.
  1177     if(!$r) $r = $this->latest_result;
  1024    * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
  1178     if(!$r) $this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
  1025    * @return array
       
  1026    */
       
  1027   
       
  1028   function fetchrow($r = false)
       
  1029   {
       
  1030     if ( !$this->_conn )
       
  1031       return false;
       
  1032     if ( !$r )
       
  1033       $r = $this->latest_result;
       
  1034     if ( !$r )
       
  1035       $this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
       
  1036     
  1179     $row = pg_fetch_assoc($r);
  1037     $row = pg_fetch_assoc($r);
  1180     return integerize_array($row);
  1038     return integerize_array($row);
  1181   }
  1039   }
  1182   
  1040   
  1183   function fetchrow_num($r = false) {
  1041   /**
  1184     if(!$r) $r = $this->latest_result;
  1042    * Fetch one row from the given query as a numeric array.
  1185     if(!$r) $this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
  1043    * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
       
  1044    * @return array
       
  1045    */
       
  1046   
       
  1047   function fetchrow_num($r = false)
       
  1048   {
       
  1049     if ( !$r )
       
  1050       $r = $this->latest_result;
       
  1051     if ( !$r )
       
  1052       $this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
       
  1053     
  1186     $row = pg_fetch_row($r);
  1054     $row = pg_fetch_row($r);
  1187     return integerize_array($row);
  1055     return integerize_array($row);
  1188   }
  1056   }
  1189   
  1057   
  1190   function numrows($r = false) {
  1058   /**
  1191     if(!$r) $r = $this->latest_result;
  1059    * Get the number of results for a given query.
  1192     if(!$r) $this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
  1060    * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
       
  1061    * @return array
       
  1062    */
       
  1063   
       
  1064   function numrows($r = false)
       
  1065   {
       
  1066     if ( !$r )
       
  1067       $r = $this->latest_result;
       
  1068     if ( !$r )
       
  1069       $this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
       
  1070     
  1193     $n = pg_num_rows($r);
  1071     $n = pg_num_rows($r);
  1194     return $n;
  1072     return $n;
  1195   }
  1073   }
  1196   
  1074   
       
  1075   /**
       
  1076    * Escape a string so that it may safely be included in a SQL query.
       
  1077    * @param string String to escape
       
  1078    * @return string Escaped string
       
  1079    */
       
  1080   
  1197   function escape($str)
  1081   function escape($str)
  1198   {
  1082   {
  1199     $str = pg_escape_string($this->_conn, $str);
  1083     $str = pg_escape_string($this->_conn, $str);
  1200     return $str;
  1084     return $str;
  1201   }
  1085   }
  1202   
  1086   
       
  1087   /**
       
  1088    * Free the given result from memory. Use this when completely finished with a result resource.
       
  1089    * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
       
  1090    * @return null
       
  1091    */
       
  1092   
  1203   function free_result($result = false)
  1093   function free_result($result = false)
  1204   {
  1094   {
  1205     if(!$result)
  1095     if ( !$result )
  1206       $result = $this->latest_result;
  1096       $result = $this->latest_result;
  1207     if(!$result)
  1097     
  1208     {
  1098     if ( !$result )
  1209       return null;
  1099       return null;
  1210     }
  1100     
  1211     @pg_free_result($result);
  1101     @pg_free_result($result);
  1212     return null;
  1102     return null;
  1213   }
  1103   }
  1214   
  1104   
  1215   function close() {
  1105   /**
       
  1106    * Close the database connection
       
  1107    */
       
  1108   
       
  1109   function close()
       
  1110   {
  1216     @pg_close($this->_conn);
  1111     @pg_close($this->_conn);
  1217     unset($this->_conn);
  1112     unset($this->_conn);
  1218   }
       
  1219   
       
  1220   // phpBB DBAL compatibility
       
  1221   function sql_fetchrow($r = false)
       
  1222   {
       
  1223     return $this->fetchrow($r);
       
  1224   }
       
  1225   function sql_freeresult($r = false)
       
  1226   {
       
  1227     if(!$this->_conn) return false;
       
  1228     if(!$r) $r = $this->latest_result;
       
  1229     if(!$r) $this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
       
  1230     $this->free_result($r);
       
  1231   }
       
  1232   function sql_numrows($r = false)
       
  1233   {
       
  1234     return $this->numrows();
       
  1235   }
       
  1236   function sql_affectedrows($r = false, $f = false, $n = false)
       
  1237   {
       
  1238     if(!$this->_conn) return false;
       
  1239     if(!$r) $r = $this->latest_result;
       
  1240     if(!$r) $this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
       
  1241     return pg_affected_rows();
       
  1242   }
  1113   }
  1243   
  1114   
  1244   /**
  1115   /**
  1245    * Get a list of columns in the given table
  1116    * Get a list of columns in the given table
  1246    * @param string Table
  1117    * @param string Table
  1263     $row = $this->fetchrow();
  1134     $row = $this->fetchrow();
  1264     $this->free_result();
  1135     $this->free_result();
  1265     return array_keys($row);
  1136     return array_keys($row);
  1266   }
  1137   }
  1267   
  1138   
  1268   function sql_type_cast(&$value)
       
  1269 	{
       
  1270 		if ( is_float($value) )
       
  1271 		{
       
  1272 			return doubleval($value);
       
  1273 		}
       
  1274 		if ( is_integer($value) || is_bool($value) )
       
  1275 		{
       
  1276 			return intval($value);
       
  1277 		}
       
  1278 		if ( is_string($value) || empty($value) )
       
  1279 		{
       
  1280 			return '\'' . $this->sql_escape_string($value) . '\'';
       
  1281 		}
       
  1282 		// uncastable var : let's do a basic protection on it to prevent sql injection attempt
       
  1283 		return '\'' . $this->sql_escape_string(htmlspecialchars($value)) . '\'';
       
  1284 	}
       
  1285 
       
  1286 	function sql_statement(&$fields, $fields_inc='')
       
  1287 	{
       
  1288 		// init result
       
  1289 		$this->sql_fields = $this->sql_values = $this->sql_update = '';
       
  1290 		if ( empty($fields) && empty($fields_inc) )
       
  1291 		{
       
  1292 			return;
       
  1293 		}
       
  1294 
       
  1295 		// process
       
  1296 		if ( !empty($fields) )
       
  1297 		{
       
  1298 			$first = true;
       
  1299 			foreach ( $fields as $field => $value )
       
  1300 			{
       
  1301 				// field must contain a field name
       
  1302 				if ( !empty($field) && is_string($field) )
       
  1303 				{
       
  1304 					$value = $this->sql_type_cast($value);
       
  1305 					$this->sql_fields .= ( $first ? '' : ', ' ) . $field;
       
  1306 					$this->sql_values .= ( $first ? '' : ', ' ) . $value;
       
  1307 					$this->sql_update .= ( $first ? '' : ', ' ) . $field . ' = ' . $value;
       
  1308 					$first = false;
       
  1309 				}
       
  1310 			}
       
  1311 		}
       
  1312 		if ( !empty($fields_inc) )
       
  1313 		{
       
  1314 			foreach ( $fields_inc as $field => $indent )
       
  1315 			{
       
  1316 				if ( $indent != 0 )
       
  1317 				{
       
  1318 					$this->sql_update .= (empty($this->sql_update) ? '' : ', ') . $field . ' = ' . $field . ($indent < 0 ? ' - ' : ' + ') . abs($indent);
       
  1319 				}
       
  1320 			}
       
  1321 		}
       
  1322 	}
       
  1323 
       
  1324 	function sql_stack_reset($id='')
       
  1325 	{
       
  1326 		if ( empty($id) )
       
  1327 		{
       
  1328 			$this->sql_stack_fields = array();
       
  1329 			$this->sql_stack_values = array();
       
  1330 		}
       
  1331 		else
       
  1332 		{
       
  1333 			$this->sql_stack_fields[$id] = array();
       
  1334 			$this->sql_stack_values[$id] = array();
       
  1335 		}
       
  1336 	}
       
  1337 
       
  1338 	function sql_stack_statement(&$fields, $id='')
       
  1339 	{
       
  1340 		$this->sql_statement($fields);
       
  1341 		if ( empty($id) )
       
  1342 		{
       
  1343 			$this->sql_stack_fields = $this->sql_fields;
       
  1344 			$this->sql_stack_values[] = '(' . $this->sql_values . ')';
       
  1345 		}
       
  1346 		else
       
  1347 		{
       
  1348 			$this->sql_stack_fields[$id] = $this->sql_fields;
       
  1349 			$this->sql_stack_values[$id][] = '(' . $this->sql_values . ')';
       
  1350 		}
       
  1351 	}
       
  1352 
       
  1353 	function sql_stack_insert($table, $transaction=false, $line='', $file='', $break_on_error=true, $id='')
       
  1354 	{
       
  1355 		if ( (empty($id) && empty($this->sql_stack_values)) || (!empty($id) && empty($this->sql_stack_values[$id])) )
       
  1356 		{
       
  1357 			return false;
       
  1358 		}
       
  1359 		switch( SQL_LAYER )
       
  1360 		{
       
  1361 			case 'mysql':
       
  1362 			case 'mysql4':
       
  1363 				if ( empty($id) )
       
  1364 				{
       
  1365 					$sql = 'INSERT INTO ' . $table . '
       
  1366 								(' . $this->sql_stack_fields . ') VALUES ' . implode(",\n", $this->sql_stack_values);
       
  1367 				}
       
  1368 				else
       
  1369 				{
       
  1370 					$sql = 'INSERT INTO ' . $table . '
       
  1371 								(' . $this->sql_stack_fields[$id] . ') VALUES ' . implode(",\n", $this->sql_stack_values[$id]);
       
  1372 				}
       
  1373 				$this->sql_stack_reset($id);
       
  1374 				return $this->sql_query($sql, $transaction, $line, $file, $break_on_error);
       
  1375 				break;
       
  1376 			default:
       
  1377 				$count_sql_stack_values = empty($id) ? count($this->sql_stack_values) : count($this->sql_stack_values[$id]);
       
  1378 				$result = !empty($count_sql_stack_values);
       
  1379 				for ( $i = 0; $i < $count_sql_stack_values; $i++ )
       
  1380 				{
       
  1381 					if ( empty($id) )
       
  1382 					{
       
  1383 						$sql = 'INSERT INTO ' . $table . '
       
  1384 									(' . $this->sql_stack_fields . ') VALUES ' . $this->sql_stack_values[$i];
       
  1385 					}
       
  1386 					else
       
  1387 					{
       
  1388 						$sql = 'INSERT INTO ' . $table . '
       
  1389 									(' . $this->sql_stack_fields[$id] . ') VALUES ' . $this->sql_stack_values[$id][$i];
       
  1390 					}
       
  1391 					$result &= $this->sql_query($sql, $transaction, $line, $file, $break_on_error);
       
  1392 				}
       
  1393 				$this->sql_stack_reset($id);
       
  1394 				return $result;
       
  1395 				break;
       
  1396 		}
       
  1397 	}
       
  1398 
       
  1399 	function sql_subquery($field, $sql, $line='', $file='', $break_on_error=true, $type=TYPE_INT)
       
  1400 	{
       
  1401 		// sub-queries doable
       
  1402 		$this->sql_get_version();
       
  1403 		if ( !in_array(SQL_LAYER, array('mysql', 'mysql4')) || (($this->sql_version[0] + ($this->sql_version[1] / 100)) >= 4.01) )
       
  1404 		{
       
  1405 			return $sql;
       
  1406 		}
       
  1407 
       
  1408 		// no sub-queries
       
  1409 		$ids = array();
       
  1410 		$result = $this->sql_query(trim($sql), false, $line, $file, $break_on_error);
       
  1411 		while ( $row = $this->sql_fetchrow($result) )
       
  1412 		{
       
  1413 			$ids[] = $type == TYPE_INT ? intval($row[$field]) : '\'' . $this->sql_escape_string($row[$field]) . '\'';
       
  1414 		}
       
  1415 		$this->sql_freeresult($result);
       
  1416 		return empty($ids) ? 'NULL' : implode(', ', $ids);
       
  1417 	}
       
  1418 
       
  1419 	function sql_col_id($expr, $alias)
       
  1420 	{
       
  1421 		$this->sql_get_version();
       
  1422 		return in_array(SQL_LAYER, array('mysql', 'mysql4')) && (($this->sql_version[0] + ($this->sql_version[1] / 100)) <= 4.01) ? $alias : $expr;
       
  1423 	}
       
  1424 
       
  1425 	function sql_get_version()
       
  1426 	{
       
  1427 		if ( empty($this->sql_version) )
       
  1428 		{
       
  1429 			$this->sql_version = array(0, 0, 0);
       
  1430 			switch ( SQL_LAYER )
       
  1431 			{
       
  1432 				case 'mysql':
       
  1433 				case 'mysql4':
       
  1434 					if ( function_exists('mysql_get_server_info') )
       
  1435 					{
       
  1436 						$lo_version = explode('-', mysql_get_server_info());
       
  1437 						$this->sql_version = explode('.', $lo_version[0]);
       
  1438 						$this->sql_version = array(intval($this->sql_version[0]), intval($this->sql_version[1]), intval($this->sql_version[2]), $lo_version[1]);
       
  1439 					}
       
  1440 					break;
       
  1441 
       
  1442 				case 'postgresql':
       
  1443 				case 'mssql':
       
  1444 				case 'mssql-odbc':
       
  1445 				default:
       
  1446 					break;
       
  1447 			}
       
  1448 		}
       
  1449 		return $this->sql_version;
       
  1450 	}
       
  1451 
       
  1452 	function sql_error()
  1139 	function sql_error()
  1453 	{
  1140 	{
  1454 		if ( $this->_conn )
  1141 		if ( $this->_conn )
  1455 		{
  1142 		{
  1456 			return pg_last_error();
  1143 			return pg_last_error();
  1458 		else
  1145 		else
  1459 		{
  1146 		{
  1460 			return ( defined('IN_ENANO_INSTALL') ) ? $GLOBALS["lang"]->get('dbpgsql_msg_err_auth') : 'Access to the database was denied. Ensure that your database exists and that your username and password are correct.';
  1147 			return ( defined('IN_ENANO_INSTALL') ) ? $GLOBALS["lang"]->get('dbpgsql_msg_err_auth') : 'Access to the database was denied. Ensure that your database exists and that your username and password are correct.';
  1461 		}
  1148 		}
  1462 	}
  1149 	}
  1463   function sql_escape_string($t) 
       
  1464   {
       
  1465     return mysql_real_escape_string($t);
       
  1466   }
       
  1467   function sql_close()
       
  1468   {
       
  1469     $this->close();
       
  1470   }
       
  1471   function sql_fetchrowset($query_id = 0)
       
  1472 	{
       
  1473 		if( !$query_id )
       
  1474 		{
       
  1475 			$query_id = $this->query_result;
       
  1476 		}
       
  1477 
  1150 
  1478 		if( $query_id )
       
  1479 		{
       
  1480 			unset($this->rowset[$query_id]);
       
  1481 			unset($this->row[$query_id]);
       
  1482 
       
  1483 			while($this->rowset[$query_id] = mysql_fetch_array($query_id, MYSQL_ASSOC))
       
  1484 			{
       
  1485 				$result[] = $this->rowset[$query_id];
       
  1486 			}
       
  1487 
       
  1488 			return $result;
       
  1489 		}
       
  1490 		else
       
  1491 		{
       
  1492 			return false;
       
  1493 		}
       
  1494 	}
       
  1495   /**
  1151   /**
  1496    * Generates and outputs a report of all the SQL queries made during execution. Should only be called after everything's over with.
  1152    * Generates and outputs a report of all the SQL queries made during execution. Should only be called after everything's over with.
  1497    */
  1153    */
  1498   
  1154   
  1499   function sql_report()
  1155   function sql_report()