Turn database drivers into classes. Keep db.inc.php as a Shim.
This commit is contained in:
parent
5a32071cb1
commit
d5318922b9
@ -1,186 +1,167 @@
|
||||
<?php
|
||||
# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
|
||||
# All rights reserved. See LICENSE file for licensing details
|
||||
|
||||
// PHP 5.5 compat, no longer use deprecated mysql
|
||||
if (($serendipity['dbType'] ?? '') == 'mysql') {
|
||||
$serendipity['dbType'] = 'mysqli';
|
||||
// Serendipity
|
||||
// See LICENSE file for license information.
|
||||
|
||||
// FIXME: THIS IS A SHIM FOR BACKWARDS COMPATIBILITY - REMOVE WHEN NO LONGER NEEDED
|
||||
|
||||
use Serendipity\Database\DbFactory;
|
||||
|
||||
// SQLite3 only fetches by assoc, we will emulate the other result types
|
||||
define(SQLITE3_ASSOC, 0);
|
||||
define(SQLITE3_NUM, 1);
|
||||
define(SQLITE3_BOTH, 2);
|
||||
|
||||
function serendipity_db_connect()
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->connect();
|
||||
}
|
||||
|
||||
if (@include(S9Y_INCLUDE_PATH . "include/db/{$serendipity['dbType']}.inc.php")) {
|
||||
@define('S9Y_DB_INCLUDED', TRUE);
|
||||
function serendipity_db_reconnect()
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->reconnect();
|
||||
}
|
||||
#include_once(S9Y_INCLUDE_PATH . "include/db/generic.inc.php");
|
||||
#define('S9Y_DB_INCLUDED', TRUE);
|
||||
|
||||
/**
|
||||
* Perform a query to update the data of a certain table row
|
||||
*
|
||||
* You can pass the tablename and an array of keys to select the row,
|
||||
* and an array of values to UPDATE in the DB table.
|
||||
*
|
||||
* @access public
|
||||
* @param string Name of the DB table
|
||||
* @param array Input array that controls the "WHERE" condition part. Pass it an associative array like array('key1' => 'value1', 'key2' => 'value2') to get a statement like "WHERE key1 = value1 AND key2 = value2". Escaping is done automatically in this function.
|
||||
* @param array Input array that controls the "SET" condition part. Pass it an associative array like array('key1' => 'value1', 'key2' => 'value2') to get a statement like "SET key1 = value1, key2 = value2". Escaping is done automatically in this function.
|
||||
* @param string What do do with the SQL query (execute, display)
|
||||
* @return array Returns the result of the SQL query
|
||||
*/
|
||||
function serendipity_db_update($table, $keys, $values, $action = 'execute')
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
$set = '';
|
||||
|
||||
foreach ($values as $k => $v) {
|
||||
if (strlen($set))
|
||||
$set .= ', ';
|
||||
$set .= $k . '=\'' . serendipity_db_escape_string($v) . '\'';
|
||||
}
|
||||
|
||||
$where = '';
|
||||
foreach ($keys as $k => $v) {
|
||||
if (strlen($where))
|
||||
$where .= ' AND ';
|
||||
$where .= $k . '=\'' . serendipity_db_escape_string($v) . '\'';
|
||||
}
|
||||
|
||||
if (strlen($where)) {
|
||||
$where = " WHERE $where";
|
||||
}
|
||||
|
||||
$q = "UPDATE {$serendipity['dbPrefix']}$table SET $set $where";
|
||||
if ($action == 'execute') {
|
||||
return serendipity_db_query($q);
|
||||
} else {
|
||||
return $q;
|
||||
}
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->update($table, $keys, $values, $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a query to insert an associative array into a specific SQL table
|
||||
*
|
||||
* You can pass a tablename and an array of input data to insert into an array.
|
||||
*
|
||||
* @access public
|
||||
* @param string Name of the SQL table
|
||||
* @param array Associative array of keys/values to insert into the table. Escaping is done automatically.
|
||||
* @param string What do do with the SQL query (execute, display)
|
||||
* @return array Returns the result of the SQL query
|
||||
*/
|
||||
function serendipity_db_insert($table, $values, $action = 'execute')
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
$names = implode(',', array_keys($values));
|
||||
|
||||
$vals = '';
|
||||
foreach ($values as $k => $v) {
|
||||
if (strlen($vals))
|
||||
$vals .= ', ';
|
||||
$vals .= '\'' . serendipity_db_escape_string($v) . '\'';
|
||||
}
|
||||
|
||||
$q = "INSERT INTO {$serendipity['dbPrefix']}$table ($names) values ($vals)";
|
||||
|
||||
if ($action == 'execute') {
|
||||
return serendipity_db_query($q);
|
||||
} else {
|
||||
return $q;
|
||||
}
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->insert($table, $values, $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether an input value corresponds to a TRUE/FALSE option in the SQL database.
|
||||
*
|
||||
* Because older DBs could not store TRUE/FALSE values to be restored into a PHP variable,
|
||||
* this function tries to detect what the return code of a SQL column is, and convert it
|
||||
* to a PHP native boolean.
|
||||
*
|
||||
* Values that will be recognized as TRUE are 'true', 't' and '1'.
|
||||
*
|
||||
* @access public
|
||||
* @param string input value to compare
|
||||
* @return boolean boolean conversion of the input value
|
||||
*/
|
||||
function serendipity_db_bool($val)
|
||||
{
|
||||
if(($val === true) || ($val == 'true') || ($val == 't') || ($val == '1'))
|
||||
return true;
|
||||
#elseif (($val === false || $val == 'false' || $val == 'f'))
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a SQL statement for a time interval or timestamp, specific to certain SQL backends
|
||||
*
|
||||
* @access public
|
||||
* @param string Indicate whether to return a timestamp, or an Interval
|
||||
* @param int The interval one might want to use, if Interval return was selected
|
||||
* @return string SQL statement
|
||||
*/
|
||||
function serendipity_db_get_interval($val, $ival = 900) {
|
||||
global $serendipity;
|
||||
|
||||
switch($serendipity['dbType']) {
|
||||
case 'sqlite':
|
||||
case 'sqlite3':
|
||||
case 'sqlite3oo':
|
||||
case 'pdo-sqlite':
|
||||
$interval = $ival;
|
||||
$ts = time();
|
||||
break;
|
||||
|
||||
case 'pdo-postgres':
|
||||
case 'postgres':
|
||||
$interval = "interval '$ival'";
|
||||
$ts = 'NOW()';
|
||||
break;
|
||||
|
||||
case 'mysql':
|
||||
case 'mysqli':
|
||||
default:
|
||||
$interval = $ival;
|
||||
$ts = 'NOW()';
|
||||
break;
|
||||
}
|
||||
|
||||
switch($val) {
|
||||
case 'interval':
|
||||
return $interval;
|
||||
|
||||
default:
|
||||
case 'ts':
|
||||
return $ts;
|
||||
}
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->bool($val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operates on an array to prepare it for SQL usage.
|
||||
*
|
||||
* @access public
|
||||
* @param string Concatenation character
|
||||
* @param array Input array
|
||||
* @param string How to convert (int: Only numbers, string: serendipity_db_escape_String)
|
||||
* @return string Imploded string
|
||||
*/
|
||||
function serendipity_db_implode($string, &$array, $type = 'int') {
|
||||
$new_array = array();
|
||||
if (!is_array($array)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
foreach($array AS $idx => $key) {
|
||||
if ($type == 'int') {
|
||||
$new_array[$idx] = (int)$key;
|
||||
} else {
|
||||
$new_array[$idx] = serendipity_db_escape_string($key);
|
||||
}
|
||||
}
|
||||
|
||||
$string = implode($string, $new_array);
|
||||
return $string;
|
||||
function serendipity_db_get_interval($val, $ival = 900)
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->getInterval($val, $ival);
|
||||
}
|
||||
|
||||
/* vim: set sts=4 ts=4 expandtab : */
|
||||
function serendipity_db_implode($string, &$array, $type = 'int')
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->implode($string, $array, $type);
|
||||
}
|
||||
|
||||
function serendipity_db_concat($string)
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->concat($string);
|
||||
}
|
||||
|
||||
function serendipity_db_logmsg($msgstr)
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->logmsg($msgstr);
|
||||
}
|
||||
|
||||
function serendipity_db_begin_transaction()
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->beginTransaction();
|
||||
}
|
||||
|
||||
function serendipity_db_end_transaction($commit)
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->endTransaction($commit);
|
||||
}
|
||||
|
||||
function serendipity_db_in_sql($col, &$search_ids, $type = ' OR ')
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->inSql($col, $search_ids, $type);
|
||||
}
|
||||
|
||||
function serendipity_db_escape_string($string)
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->escapeString($string);
|
||||
}
|
||||
|
||||
function serendipity_db_limit($start, $offset)
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->limit($start, $offset);
|
||||
}
|
||||
|
||||
function serendipity_db_limit_sql($limitstring)
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->limitSql($limitstring);
|
||||
}
|
||||
|
||||
function serendipity_db_affected_rows()
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->affectedRows();
|
||||
}
|
||||
|
||||
function serendipity_db_updated_rows()
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->updatedRows();
|
||||
}
|
||||
|
||||
function serendipity_db_matched_rows()
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->matchedRows();
|
||||
}
|
||||
|
||||
function serendipity_db_insert_id($table = '', $id = '')
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->insertId($table, $id);
|
||||
}
|
||||
|
||||
function &serendipity_db_query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->query($sql, $single, $result_type, $reportErr, $assocKey, $assocVal, $expectError);
|
||||
}
|
||||
|
||||
function serendipity_db_schema_import($query)
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->schemaImport($query);
|
||||
}
|
||||
|
||||
function serendipity_db_probe($hash, &$errs)
|
||||
{
|
||||
global $serendipity;
|
||||
$db = DbFactory::createFromConfig($serendipity);
|
||||
return $db->probe($hash, $errs);
|
||||
}
|
||||
|
@ -1,486 +0,0 @@
|
||||
<?php
|
||||
# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
|
||||
# All rights reserved. See LICENSE file for licensing details
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function serendipity_db_begin_transaction(){
|
||||
serendipity_db_query('start transaction');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
function serendipity_db_end_transaction($commit){
|
||||
if ($commit){
|
||||
serendipity_db_query('commit');
|
||||
}else{
|
||||
serendipity_db_query('rollback');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
function serendipity_db_in_sql($col, &$search_ids, $type = ' OR ') {
|
||||
return $col . " IN (" . implode(', ', $search_ids) . ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignored on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
function &serendipity_db_query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false) {
|
||||
global $serendipity;
|
||||
$type_map = array('assoc' => MYSQLI_ASSOC, 'num' => MYSQLI_NUM, 'both' => MYSQLI_BOTH, 'true' => true, 'false' => false);
|
||||
|
||||
if ($expectError) {
|
||||
$c = @mysqli_query($serendipity['dbConn'], $sql);
|
||||
} else {
|
||||
$c = mysqli_query($serendipity['dbConn'], $sql);
|
||||
}
|
||||
|
||||
if (!$expectError && mysqli_error($serendipity['dbConn']) != '') {
|
||||
$msg = mysqli_error($serendipity['dbConn']);
|
||||
return $msg;
|
||||
}
|
||||
|
||||
if (!$c) {
|
||||
if (!$expectError && !$serendipity['production']) {
|
||||
print mysqli_error($serendipity['dbConn']);
|
||||
if (function_exists('debug_backtrace') && $reportErr == true) {
|
||||
highlight_string(var_export(debug_backtrace(), 1));
|
||||
}
|
||||
}
|
||||
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
if ($c === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
$result_type = $type_map[$result_type];
|
||||
|
||||
switch(mysqli_num_rows($c)) {
|
||||
case 0:
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
case 1:
|
||||
if ($single) {
|
||||
return mysqli_fetch_array($c, $result_type);
|
||||
}
|
||||
default:
|
||||
if ($single) {
|
||||
return mysqli_fetch_array($c, $result_type);
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
while ($row = mysqli_fetch_array($c, $result_type)) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
function serendipity_db_insert_id() {
|
||||
global $serendipity;
|
||||
return mysqli_insert_id($serendipity['dbConn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
function serendipity_db_affected_rows() {
|
||||
global $serendipity;
|
||||
return mysqli_affected_rows($serendipity['dbConn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
function serendipity_db_updated_rows() {
|
||||
global $serendipity;
|
||||
|
||||
preg_match(
|
||||
"/^[^0-9]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)/",
|
||||
mysqli_info($serendipity['dbConn']),
|
||||
$arr);
|
||||
// mysqli_affected_rows returns 0 if rows were matched but not changed.
|
||||
// mysqli_info returns rows matched
|
||||
return $arr[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
function serendipity_db_matched_rows() {
|
||||
global $serendipity;
|
||||
|
||||
preg_match(
|
||||
"/^[^0-9]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)/",
|
||||
mysqli_info($serendipity['dbConn']),
|
||||
$arr);
|
||||
// mysqli_affected_rows returns 0 if rows were matched but not changed.
|
||||
// mysqli_info returns rows matched
|
||||
return $arr[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
function serendipity_db_escape_string($string) {
|
||||
global $serendipity;
|
||||
return mysqli_escape_string($serendipity['dbConn'], $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit($start, $offset) {
|
||||
return $start . ', ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit_sql($limitstring) {
|
||||
return ' LIMIT ' . $limitstring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
function serendipity_db_connect() {
|
||||
global $serendipity;
|
||||
|
||||
if (isset($serendipity['dbConn'])) {
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
$function = 'mysqli_connect';
|
||||
|
||||
$connparts = explode(':', $serendipity['dbHost']);
|
||||
if (!empty($connparts[1])) {
|
||||
// A "hostname:port" connection was specified
|
||||
$serendipity['dbConn'] = $function($connparts[0], $serendipity['dbUser'], $serendipity['dbPass'], $serendipity['dbName'], $connparts[1]);
|
||||
} else {
|
||||
// Connect with default ports
|
||||
$serendipity['dbConn'] = $function($connparts[0], $serendipity['dbUser'], $serendipity['dbPass']);
|
||||
}
|
||||
mysqli_select_db($serendipity['dbConn'], $serendipity['dbName']);
|
||||
serendipity_db_reconnect();
|
||||
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
function serendipity_db_reconnect() {
|
||||
global $serendipity;
|
||||
|
||||
$use_charset = '';
|
||||
if (isset($serendipity['dbCharset']) && !empty($serendipity['dbCharset'])) {
|
||||
$use_charset = $serendipity['dbCharset'];
|
||||
if (!defined('SQL_CHARSET_INIT')) { define('SQL_CHARSET_INIT', true); }
|
||||
} elseif (defined('SQL_CHARSET') && $serendipity['dbNames'] && !defined('SQL_CHARSET_INIT')) {
|
||||
$use_charset = SQL_CHARSET;
|
||||
}
|
||||
|
||||
if ($use_charset != '') {
|
||||
mysqli_set_charset($serendipity['dbConn'], $use_charset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipty query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
function serendipity_db_schema_import($query) {
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}',
|
||||
'{UNSIGNED}', '{FULLTEXT}', '{FULLTEXT_MYSQL}', '{BOOLEAN}', '{TEXT}');
|
||||
static $replace = array('int(11) not null auto_increment', 'primary key',
|
||||
'unsigned' , 'FULLTEXT', 'FULLTEXT', 'enum (\'true\', \'false\') NOT NULL default \'true\'', 'LONGTEXT');
|
||||
global $serendipity;
|
||||
|
||||
$search[] = '{UTF_8}';
|
||||
if ( $_POST['charset'] == 'UTF-8/' ||
|
||||
$serendipity['charset'] == 'UTF-8/' ||
|
||||
$serendipity['POST']['charset'] == 'UTF-8/' ||
|
||||
LANG_CHARSET == 'UTF-8' ) {
|
||||
if (serendipity_utf8mb4_ready()) {
|
||||
$replace[] = 'ROW_FORMAT=DYNAMIC /*!40100 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */';
|
||||
} else {
|
||||
# in old versions we stick to the three byte pseudo utf8 to not trip
|
||||
# over the max index restriction of 1000 bytes
|
||||
$replace[] = '/*!40100 CHARACTER SET utf8 COLLATE utf8_unicode_ci */';
|
||||
}
|
||||
} else {
|
||||
$replace[] = '';
|
||||
}
|
||||
|
||||
|
||||
if (serendipity_utf8mb4_ready()) {
|
||||
# InnoDB enables us to use utf8mb4 with the higher max index size
|
||||
serendipity_db_query("SET storage_engine=INNODB");
|
||||
} else {
|
||||
# Before 5.6.4/10.0.5 InnoDB did not support fulltext indexes, which we use,
|
||||
# thus we stay with MyISAM here
|
||||
serendipity_db_query("SET storage_engine=MYISAM");
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return serendipity_db_query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return serendipity_db_query($query);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if we think that it is safe to ugprade to utf8mb4. This checks version numbers and applied settings.
|
||||
* Depending on the version of mariadb/mysql we need to check either one or three settings. We check for
|
||||
* innodb being available with fulltext index and large index support, so that our database scheme can work
|
||||
*
|
||||
* @return boolean Whether the database could support utf8mb4
|
||||
*/
|
||||
function serendipity_utf8mb4_ready() {
|
||||
global $serendipity;
|
||||
|
||||
$mysql_version = mysqli_get_server_info($serendipity['dbConn']);
|
||||
$maria = false;
|
||||
if (strpos($mysql_version, 'MariaDB') !== false) {
|
||||
$maria = true;
|
||||
}
|
||||
if ($maria) {
|
||||
# maria trips up our version detection scheme by prepending 5.5.5 to newer versions
|
||||
$mysql_version = str_replace('5.5.5-', '', $mysql_version);
|
||||
}
|
||||
|
||||
if ($maria) {
|
||||
# see https://mariadb.com/kb/en/innodb-file-format/ for when barracuda is available, and when it's the only option
|
||||
# see https://docs.nextcloud.com/server/15/admin_manual/configuration_database/mysql_4byte_support.html for which
|
||||
# variables we have to check to assume utf8mb4 it can work (with the large indexes we need)
|
||||
|
||||
if (version_compare($mysql_version, '10.3.1', '>=')) {
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_file_per_table
|
||||
$rows = serendipity_db_query("SHOW VARIABLES LIKE 'innodb_file_per_table'");
|
||||
try {
|
||||
return $rows[0][1] == 'ON';
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
# see https://mariadb.com/kb/en/full-text-index-overview/. We need 10.0.5 to have fulltext indexes with innodb
|
||||
if (version_compare($mysql_version, '10.0.5', '>=')) {
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_file_per_table
|
||||
$rows = serendipity_db_query("SHOW VARIABLES LIKE 'innodb_file_per_table'");
|
||||
try {
|
||||
if ($rows[0][1] != 'ON') {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_file_format
|
||||
$rows = serendipity_db_query("SHOW VARIABLES LIKE 'innodb_file_format'");
|
||||
try {
|
||||
if ($rows[0][1] != 'barracuda') {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_large_prefix
|
||||
$rows = serendipity_db_query("SHOW VARIABLES LIKE 'innodb_large_prefix'");
|
||||
try {
|
||||
if ($rows[0][1] != 'ON') {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false; # version too old
|
||||
}
|
||||
|
||||
# now we know it is not mariadb, but "real" mysql
|
||||
|
||||
# These versions might need to be changed based on testing feedback
|
||||
if (version_compare($mysql_version, '8.0.0', '>=')) {
|
||||
$rows = serendipity_db_query("SHOW VARIABLES LIKE 'innodb_file_per_table'");
|
||||
try {
|
||||
return $rows[0][1] == 'ON';
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
# see https://dev.mysql.com/doc/refman/5.6/en/innodb-fulltext-index.html. We need 5.6 for fulltext indexes
|
||||
if (version_compare($mysql_version, '5.6', '>=')) {
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_file_per_table
|
||||
$rows = serendipity_db_query("SHOW VARIABLES LIKE 'innodb_file_per_table'");
|
||||
try {
|
||||
if ($rows[0][1] != 'ON') {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_file_format
|
||||
$rows = serendipity_db_query("SHOW VARIABLES LIKE 'innodb_file_format'");
|
||||
try {
|
||||
if ($rows[0][1] != 'barracuda') {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_large_prefix
|
||||
$rows = serendipity_db_query("SHOW VARIABLES LIKE 'innodb_large_prefix'");
|
||||
try {
|
||||
if ($rows[0][1] != 'ON') {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; # version too old
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
function serendipity_db_probe($hash, &$errs) {
|
||||
global $serendipity;
|
||||
|
||||
if (!function_exists('mysqli_connect')) {
|
||||
$errs[] = 'No mySQLi extension found. Please check your webserver installation or contact your systems administrator regarding this problem.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$function = 'mysqli_connect';
|
||||
$connparts = explode(':', $hash['dbHost']);
|
||||
if (!empty($connparts[1])) {
|
||||
// A "hostname:port" connection was specified
|
||||
$c = @$function($connparts[0], $hash['dbUser'], $hash['dbPass'], $hash['dbName'], $connparts[1]);
|
||||
} else {
|
||||
// Connect with default ports
|
||||
$c = @$function($connparts[0], $hash['dbUser'], $hash['dbPass']);
|
||||
}
|
||||
|
||||
if (!$c) {
|
||||
$errs[] = 'Could not connect to database; check your settings.';
|
||||
$errs[] = 'The mySQL error was: ' . serendipity_specialchars(mysqli_connect_error());
|
||||
return false;
|
||||
}
|
||||
|
||||
$serendipity['dbConn'] = $c;
|
||||
|
||||
if ( !@mysqli_select_db($c, $hash['dbName']) ) {
|
||||
$errs[] = 'The database you specified does not exist.';
|
||||
$errs[] = 'The mySQL error was: ' . serendipity_specialchars(mysqli_error($c));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
function serendipity_db_concat($string) {
|
||||
return 'concat(' . $string . ')';
|
||||
}
|
||||
|
||||
/* vim: set sts=4 ts=4 expandtab : */
|
@ -1,336 +0,0 @@
|
||||
<?php
|
||||
# $Id: postgres.inc.php 1178 2006-05-01 13:53:40Z garvinhicking $
|
||||
# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
|
||||
# All rights reserved. See LICENSE file for licensing details
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function serendipity_db_begin_transaction(){
|
||||
global $serendipity;
|
||||
$serendipity['dbConn']->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
function serendipity_db_end_transaction($commit){
|
||||
global $serendipity;
|
||||
if ($commit){
|
||||
$serendipity['dbConn']->commit();
|
||||
}else{
|
||||
$serendipity['dbConn']->rollback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
function serendipity_db_in_sql($col, &$search_ids, $type = ' OR ') {
|
||||
return $col . " IN (" . implode(', ', $search_ids) . ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
function serendipity_db_connect() {
|
||||
global $serendipity;
|
||||
|
||||
$host = $port = '';
|
||||
if (strlen($serendipity['dbHost'])) {
|
||||
if (false !== strstr($serendipity['dbHost'], ':')) {
|
||||
$tmp = explode(':', $serendipity['dbHost']);
|
||||
$host = "host={$tmp[0]};";
|
||||
$port = "port={$tmp[1]};";
|
||||
} else {
|
||||
$host = "host={$serendipity['dbHost']};";
|
||||
}
|
||||
}
|
||||
|
||||
$serendipity['dbConn'] = new PDO(
|
||||
sprintf(
|
||||
'pgsql:%sdbname=%s',
|
||||
"$host$port",
|
||||
$serendipity['dbName']
|
||||
),
|
||||
$serendipity['dbUser'],
|
||||
$serendipity['dbPass']
|
||||
);
|
||||
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
function serendipity_db_reconnect() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
function serendipity_db_escape_string($string) {
|
||||
global $serendipity;
|
||||
return substr($serendipity['dbConn']->quote($string), 1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit($start, $offset) {
|
||||
return $offset . ', ' . $start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit_sql($limitstring) {
|
||||
$limit_split = explode(',', $limitstring);
|
||||
if (count($limit_split) > 1) {
|
||||
$limit = ' LIMIT ' . $limit_split[0] . ' OFFSET ' . $limit_split[1];
|
||||
} else {
|
||||
$limit = ' LIMIT ' . $limit_split[0];
|
||||
}
|
||||
return $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
function serendipity_db_affected_rows() {
|
||||
global $serendipity;
|
||||
return $serendipity['dbSth']->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
function serendipity_db_updated_rows() {
|
||||
global $serendipity;
|
||||
// it is unknown whether pg_affected_rows returns number of rows
|
||||
// UPDATED or MATCHED on an UPDATE statement.
|
||||
return $serendipity['dbSth']->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
function serendipity_db_matched_rows() {
|
||||
global $serendipity;
|
||||
// it is unknown whether pg_affected_rows returns number of rows
|
||||
// UPDATED or MATCHED on an UPDATE statement.
|
||||
return $serendipity['dbSth']->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @param string Name of the table to get a INSERT ID for
|
||||
* @param string Name of the column to get a INSERT ID for
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
function serendipity_db_insert_id($table = '', $id = '') {
|
||||
global $serendipity;
|
||||
if (empty($table) || empty($id)) {
|
||||
// BC - will/should never be called with empty parameters!
|
||||
return $serendipity['dbConn']->lastInsertId();
|
||||
} else {
|
||||
$query = "SELECT currval('{$serendipity['dbPrefix']}{$table}_{$id}_seq'::text) AS {$id}";
|
||||
$res = $serendipity['dbConn']->prepare($query);
|
||||
$res->execute();
|
||||
foreach($res->fetchAll(PDO::FETCH_ASSOC) AS $row) {
|
||||
return $row[$id];
|
||||
}
|
||||
return $serendipity['dbConn']->lastInsertId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
function &serendipity_db_query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false) {
|
||||
global $serendipity;
|
||||
$type_map = array(
|
||||
'assoc' => PDO::FETCH_ASSOC,
|
||||
'num' => PDO::FETCH_NUM,
|
||||
'both' => PDO::FETCH_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
if (!$expectError && ($reportErr || !$serendipity['production'])) {
|
||||
$serendipity['dbSth'] = $serendipity['dbConn']->prepare($sql);
|
||||
} else {
|
||||
$serendipity['dbSth'] = $serendipity['dbConn']->prepare($sql);
|
||||
}
|
||||
|
||||
if (!$serendipity['dbSth']) {
|
||||
if (!$expectError && !$serendipity['production']) {
|
||||
print "<span class='msg_error'>Error in $sql</span>";
|
||||
print $serendipity['dbConn']->errorInfo() . "<BR/>\n";
|
||||
if (function_exists('debug_backtrace')) {
|
||||
highlight_string(var_export(debug_backtrace(), 1));
|
||||
}
|
||||
print "<pre>$sql</pre>\n";
|
||||
}
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
$serendipity['dbSth']->execute();
|
||||
|
||||
if ($serendipity['dbSth'] === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
$result_type = $type_map[$result_type];
|
||||
|
||||
$n = 0;
|
||||
|
||||
$rows = array();
|
||||
foreach($serendipity['dbSth']->fetchAll($result_type) AS $row) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
if(count($rows) == 0) {
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
}
|
||||
if(count($rows) == 1 && $single) {
|
||||
return $rows[0];
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipty query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
function serendipity_db_schema_import($query) {
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}',
|
||||
'{FULLTEXT}', '{BOOLEAN}', 'int(1)', 'int(10)', 'int(11)', 'int(4)', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('SERIAL', 'primary key', '', '', 'BOOLEAN NOT NULL', 'int2',
|
||||
'int4', 'int4', 'int4', '', 'text');
|
||||
|
||||
if (stristr($query, '{FULLTEXT_MYSQL}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return serendipity_db_query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return serendipity_db_query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
function serendipity_db_probe($hash, &$errs) {
|
||||
global $serendipity;
|
||||
|
||||
if(!in_array('pgsql', PDO::getAvailableDrivers())) {
|
||||
$errs[] = 'PDO_PGSQL driver not avialable';
|
||||
return false;
|
||||
}
|
||||
|
||||
$serendipity['dbConn'] = new PDO(
|
||||
sprintf(
|
||||
'pgsql:%sdbname=%s',
|
||||
strlen($hash['dbHost']) ? ('host=' . $hash['dbHost'] . ';') : '',
|
||||
$hash['dbName']
|
||||
),
|
||||
$hash['dbUser'],
|
||||
$hash['dbPass']
|
||||
);
|
||||
|
||||
if (!$serendipity['dbConn']) {
|
||||
$errs[] = 'Could not connect to database; check your settings.';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
function serendipity_db_concat($string) {
|
||||
return '(' . str_replace(', ', '||', $string) . ')';
|
||||
}
|
||||
|
||||
/* vim: set sts=4 ts=4 expandtab : */
|
@ -1,356 +0,0 @@
|
||||
<?php
|
||||
|
||||
function serendipity_db_logmsg($msgstr) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] ' . $msgstr . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse result arrays into expected format for further operations
|
||||
*
|
||||
* SQLite does not support to return "e.entryid" within a $row['entryid'] return.
|
||||
* So this function manually iteratse through all result rows and rewrites 'X.yyyy' to 'yyyy'.
|
||||
* Yeah. This sucks. Don't tell me!
|
||||
*
|
||||
* @access private
|
||||
* @param resource The row resource handle
|
||||
* @param int Bitmask to tell whether to fetch numerical/associative arrays
|
||||
* @return array Propper array containing the resource results
|
||||
*/
|
||||
function serendipity_db_sqlite_fetch_array($row, $type = PDO::FETCH_ASSOC)
|
||||
{
|
||||
static $search = array('%00', '%25');
|
||||
static $replace = array("\x00", '%');
|
||||
|
||||
if (!is_array($row)) {
|
||||
return $row;
|
||||
}
|
||||
|
||||
/* strip any slashes, correct fieldname */
|
||||
$newrow = array();
|
||||
foreach ($row AS $i => $v) {
|
||||
// TODO: If a query of the format 'SELECT a.id, b.text FROM table' is used,
|
||||
// the sqlite extension will give us key indizes 'a.id' and 'b.text'
|
||||
// instead of just 'id' and 'text' like in mysql/postgresql extension.
|
||||
// To fix that, we use a preg-regex; but that is quite performance costy.
|
||||
// Either we always need to use 'SELECT a.id AS id, b.text AS text' in query,
|
||||
// or the sqlite extension may get fixed. :-)
|
||||
$newrow[preg_replace('@^.+\.(.*)@', '\1', $i)] = str_replace($search, $replace, $v);
|
||||
}
|
||||
|
||||
return $newrow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
|
||||
function serendipity_db_begin_transaction(){
|
||||
global $serendipity;
|
||||
$serendipity['dbConn']->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
function serendipity_db_end_transaction($commit){
|
||||
global $serendipity;
|
||||
if ($commit){
|
||||
$serendipity['dbConn']->commit();
|
||||
}else{
|
||||
$serendipity['dbConn']->rollback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
function serendipity_db_in_sql($col, &$search_ids, $type = ' OR ') {
|
||||
$sql = array();
|
||||
if (!is_array($search_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($search_ids AS $id) {
|
||||
$sql[] = $col . ' = ' . $id;
|
||||
}
|
||||
|
||||
$cond = '(' . implode($type, $sql) . ')';
|
||||
return $cond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
function serendipity_db_connect() {
|
||||
global $serendipity;
|
||||
|
||||
$serendipity['dbConn'] = new PDO(
|
||||
'sqlite:' . (defined('S9Y_DATA_PATH') ? S9Y_DATA_PATH : $serendipity['serendipityPath']) . $serendipity['dbName'] . '.db'
|
||||
);
|
||||
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
function serendipity_db_reconnect() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
function serendipity_db_escape_string($string) {
|
||||
global $serendipity;
|
||||
return substr($serendipity['dbConn']->quote($string), 1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit($start, $offset) {
|
||||
return $start . ', ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit_sql($limitstring) {
|
||||
return ' LIMIT ' . $limitstring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
function serendipity_db_affected_rows() {
|
||||
global $serendipity;
|
||||
return $serendipity['dbSth']->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
function serendipity_db_updated_rows() {
|
||||
global $serendipity;
|
||||
// it is unknown whether pg_affected_rows returns number of rows
|
||||
// UPDATED or MATCHED on an UPDATE statement.
|
||||
return $serendipity['dbSth']->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
function serendipity_db_matched_rows() {
|
||||
global $serendipity;
|
||||
// it is unknown whether pg_affected_rows returns number of rows
|
||||
// UPDATED or MATCHED on an UPDATE statement.
|
||||
return $serendipity['dbSth']->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @param string Name of the table to get a INSERT ID for
|
||||
* @param string Name of the column to get a INSERT ID for
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
function serendipity_db_insert_id($table = '', $id = '') {
|
||||
global $serendipity;
|
||||
return $serendipity['dbConn']->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
function &serendipity_db_query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false) {
|
||||
global $serendipity;
|
||||
$type_map = array(
|
||||
'assoc' => PDO::FETCH_ASSOC,
|
||||
'num' => PDO::FETCH_NUM,
|
||||
'both' => PDO::FETCH_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
//serendipity_db_logmsg('SQLQUERY: ' . $sql);
|
||||
if (!$expectError && ($reportErr || !$serendipity['production'])) {
|
||||
$serendipity['dbSth'] = $serendipity['dbConn']->prepare($sql);
|
||||
} else {
|
||||
$serendipity['dbSth'] = $serendipity['dbConn']->prepare($sql);
|
||||
}
|
||||
|
||||
if (!$serendipity['dbSth']) {
|
||||
if (!$expectError && !$serendipity['production']) {
|
||||
print "<span class='msg_error'>Error in $sql</span>";
|
||||
print $serendipity['dbConn']->errorInfo() . "<BR/>\n";
|
||||
if (function_exists('debug_backtrace')) {
|
||||
highlight_string(var_export(debug_backtrace(), 1));
|
||||
}
|
||||
print "<pre>$sql</pre>";
|
||||
}
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
$serendipity['dbSth']->execute();
|
||||
|
||||
if ($serendipity['dbSth'] === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
$result_type = $type_map[$result_type];
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach($serendipity['dbSth']->fetchAll($result_type) AS $row) {
|
||||
$row = serendipity_db_sqlite_fetch_array($row, $result_type);
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
//serendipity_db_logmsg('SQLRESULT: ' . print_r($rows,true));
|
||||
|
||||
if(count($rows) == 0) {
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
}
|
||||
if(count($rows) == 1 && $single) {
|
||||
return $rows[0];
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipty query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
function serendipity_db_schema_import($query) {
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('INTEGER AUTOINCREMENT', 'PRIMARY KEY', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
|
||||
|
||||
if (stristr($query, '{FULLTEXT_MYSQL}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
$query = str_replace('INTEGER AUTOINCREMENT PRIMARY KEY', 'INTEGER PRIMARY KEY AUTOINCREMENT', $query);
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return serendipity_db_query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return serendipity_db_query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
function serendipity_db_probe($hash, &$errs) {
|
||||
global $serendipity;
|
||||
|
||||
if(!in_array('sqlite', PDO::getAvailableDrivers())) {
|
||||
$errs[] = 'PDO_SQLITE driver not available';
|
||||
return false;
|
||||
}
|
||||
|
||||
$dbName = (isset($hash['sqlitedbName']) ? $hash['sqlitedbName'] : $hash['dbName']);
|
||||
if (defined('S9Y_DATA_PATH')) {
|
||||
// Shared installations!
|
||||
$dbfile = S9Y_DATA_PATH . $dbName . '.db';
|
||||
} else {
|
||||
$dbfile = $serendipity['serendipityPath'] . $dbName . '.db';
|
||||
}
|
||||
|
||||
$serendipity['dbConn'] = new PDO(
|
||||
'sqlite:' . $dbfile
|
||||
);
|
||||
|
||||
if (!$serendipity['dbConn']) {
|
||||
$errs[] = "Unable to open \"$dbfile\" - check permissions (directory needs to be writeable for webserver)!";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
function serendipity_db_concat($string) {
|
||||
return 'concat(' . $string . ')';
|
||||
}
|
||||
|
||||
/* vim: set sts=4 ts=4 expandtab : */
|
@ -1,348 +0,0 @@
|
||||
<?php
|
||||
# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
|
||||
# All rights reserved. See LICENSE file for licensing details
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function serendipity_db_begin_transaction(){
|
||||
serendipity_db_query('begin work');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
function serendipity_db_end_transaction($commit){
|
||||
if ($commit){
|
||||
serendipity_db_query('commit');
|
||||
}else{
|
||||
serendipity_db_query('rollback');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
function serendipity_db_in_sql($col, &$search_ids, $type = ' OR ') {
|
||||
return $col . " IN (" . implode(', ', $search_ids) . ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
function serendipity_db_connect() {
|
||||
global $serendipity;
|
||||
|
||||
if (isset($serendipity['dbPersistent']) && $serendipity['dbPersistent']) {
|
||||
$function = 'pg_pconnect';
|
||||
} else {
|
||||
$function = 'pg_connect';
|
||||
}
|
||||
|
||||
$host = $port = '';
|
||||
if (strlen($serendipity['dbHost'])) {
|
||||
if (false !== strstr($serendipity['dbHost'], ':')) {
|
||||
$tmp = explode(':', $serendipity['dbHost']);
|
||||
$host = "host={$tmp[0]} ";
|
||||
$port = "port={$tmp[1]} ";
|
||||
} else {
|
||||
$host = "host={$serendipity['dbHost']} ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$serendipity['dbConn'] = $function(
|
||||
sprintf(
|
||||
'%sdbname=%s user=%s password=%s',
|
||||
"$host$port",
|
||||
$serendipity['dbName'],
|
||||
$serendipity['dbUser'],
|
||||
$serendipity['dbPass']
|
||||
)
|
||||
);
|
||||
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
function serendipity_db_reconnect() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
function serendipity_db_escape_string($string) {
|
||||
return pg_escape_string($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit($start, $offset) {
|
||||
return $offset . ', ' . $start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit_sql($limitstring) {
|
||||
$limit_split = explode(',', $limitstring);
|
||||
if (count($limit_split) > 1) {
|
||||
$limit = ' LIMIT ' . $limit_split[0] . ' OFFSET ' . $limit_split[1];
|
||||
} else {
|
||||
$limit = ' LIMIT ' . $limit_split[0];
|
||||
}
|
||||
return $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
function serendipity_db_affected_rows() {
|
||||
global $serendipity;
|
||||
return pg_affected_rows($serendipity['dbLastResult']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
function serendipity_db_updated_rows() {
|
||||
global $serendipity;
|
||||
// it is unknown whether pg_affected_rows returns number of rows
|
||||
// UPDATED or MATCHED on an UPDATE statement.
|
||||
return pg_affected_rows($serendipity['dbLastResult']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
function serendipity_db_matched_rows() {
|
||||
global $serendipity;
|
||||
// it is unknown whether pg_affected_rows returns number of rows
|
||||
// UPDATED or MATCHED on an UPDATE statement.
|
||||
return pg_affected_rows($serendipity['dbLastResult']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @param string Name of the table to get a INSERT ID for
|
||||
* @param string Name of the column to get a INSERT ID for
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
function serendipity_db_insert_id($table = '', $id = '') {
|
||||
global $serendipity;
|
||||
if (empty($table) || empty($id)) {
|
||||
// BC - will/should never be called with empty parameters!
|
||||
return pg_last_oid($serendipity['dbLastResult']);
|
||||
} else {
|
||||
$query = "SELECT currval('{$serendipity['dbPrefix']}{$table}_{$id}_seq'::text) AS {$id}";
|
||||
$res = pg_query($serendipity['dbConn'], $query);
|
||||
if (pg_num_rows($res)) {
|
||||
$insert_id = pg_fetch_array($res, 0, PGSQL_ASSOC);
|
||||
return $insert_id[$id];
|
||||
} else {
|
||||
return pg_last_oid($serendipity['dbLastResult']); // BC - should not happen!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
function &serendipity_db_query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false) {
|
||||
global $serendipity;
|
||||
$type_map = array(
|
||||
'assoc' => PGSQL_ASSOC,
|
||||
'num' => PGSQL_NUM,
|
||||
'both' => PGSQL_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
if (!isset($serendipity['dbPgsqlOIDS'])) {
|
||||
$serendipity['dbPgsqlOIDS'] = true;
|
||||
@serendipity_db_query('SET default_with_oids = true', true, 'both', false, false, false, true);
|
||||
}
|
||||
|
||||
if (!$expectError && ($reportErr || !$serendipity['production'])) {
|
||||
$serendipity['dbLastResult'] = pg_query($serendipity['dbConn'], $sql);
|
||||
} else {
|
||||
$serendipity['dbLastResult'] = @pg_query($serendipity['dbConn'], $sql);
|
||||
}
|
||||
|
||||
if (!$serendipity['dbLastResult']) {
|
||||
if (!$expectError && !$serendipity['production']) {
|
||||
print "<span class='msg_error'>Error in $sql</span>";
|
||||
print pg_last_error($serendipity['dbConn']) . "<BR/>\n";
|
||||
if (function_exists('debug_backtrace')) {
|
||||
highlight_string(var_export(debug_backtrace(), 1));
|
||||
}
|
||||
print "<pre>$sql</pre>";
|
||||
}
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
if ($serendipity['dbLastResult'] === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
$result_type = $type_map[$result_type];
|
||||
|
||||
$n = pg_num_rows($serendipity['dbLastResult']);
|
||||
|
||||
switch ($n) {
|
||||
case 0:
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
case 1:
|
||||
if ($single) {
|
||||
return pg_fetch_array($serendipity['dbLastResult'], 0, $result_type);
|
||||
}
|
||||
default:
|
||||
$rows = array();
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
$row = pg_fetch_array($serendipity['dbLastResult'], $i, $result_type);
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = pg_fetch_array($serendipity['dbLastResult'], $i, $result_type);
|
||||
}
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipty query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
function serendipity_db_schema_import($query) {
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}',
|
||||
'{FULLTEXT}', '{BOOLEAN}', 'int(1)', 'int(10)', 'int(11)', 'int(4)', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('SERIAL', 'primary key', '', '', 'BOOLEAN NOT NULL', 'int2',
|
||||
'int4', 'int4', 'int4', '', 'text');
|
||||
|
||||
if (stristr($query, '{FULLTEXT_MYSQL}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return serendipity_db_query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return serendipity_db_query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
function serendipity_db_probe($hash, &$errs) {
|
||||
global $serendipity;
|
||||
|
||||
if (!function_exists('pg_connect')) {
|
||||
$errs[] = 'No PostgreSQL extension found. Please check your webserver installation or contact your systems administrator regarding this problem.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$serendipity['dbConn'] = pg_connect(
|
||||
sprintf(
|
||||
'%sdbname=%s user=%s password=%s',
|
||||
|
||||
strlen($hash['dbHost']) ? ('host=' . $hash['dbHost'] . ' ') : '',
|
||||
$hash['dbName'],
|
||||
$hash['dbUser'],
|
||||
$hash['dbPass']
|
||||
)
|
||||
);
|
||||
|
||||
if (!$serendipity['dbConn']) {
|
||||
$errs[] = 'Could not connect to database; check your settings.';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
function serendipity_db_concat($string) {
|
||||
return '(' . str_replace(', ', '||', $string) . ')';
|
||||
}
|
||||
|
||||
/* vim: set sts=4 ts=4 expandtab : */
|
@ -1,388 +0,0 @@
|
||||
<?php
|
||||
# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
|
||||
# All rights reserved. See LICENSE file for licensing details
|
||||
|
||||
if (!function_exists('sqlite_open')) {
|
||||
@dl('sqlite.so');
|
||||
@dl('sqlite.dll');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function serendipity_db_begin_transaction(){
|
||||
serendipity_db_query('begin transaction');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
function serendipity_db_end_transaction($commit){
|
||||
if ($commit){
|
||||
serendipity_db_query('commit transaction');
|
||||
}else{
|
||||
serendipity_db_query('rollback transaction');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
function serendipity_db_connect()
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
if (isset($serendipity['dbConn'])) {
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
if (isset($serendipity['dbPersistent']) && $serendipity['dbPersistent']) {
|
||||
$function = 'sqlite_popen';
|
||||
} else {
|
||||
$function = 'sqlite_open';
|
||||
}
|
||||
|
||||
|
||||
if (defined('S9Y_DATA_PATH')) {
|
||||
$serendipity['dbConn'] = $function(S9Y_DATA_PATH . $serendipity['dbName'] . '.db');
|
||||
} else {
|
||||
$serendipity['dbConn'] = $function($serendipity['dbName'] . '.db');
|
||||
}
|
||||
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
function serendipity_db_reconnect() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
function serendipity_db_escape_string($string)
|
||||
{
|
||||
static $search = array("\x00", '%', "'", '\"');
|
||||
static $replace = array('%00', '%25', "''", '\\\"');
|
||||
|
||||
return str_replace($search, $replace, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
function serendipity_db_affected_rows()
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
return sqlite_changes($serendipity['dbConn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
function serendipity_db_updated_rows()
|
||||
{
|
||||
global $serendipity;
|
||||
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
|
||||
return sqlite_changes($serendipity['dbConn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
function serendipity_db_matched_rows()
|
||||
{
|
||||
global $serendipity;
|
||||
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
|
||||
return sqlite_changes($serendipity['dbConn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
function serendipity_db_insert_id()
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
return sqlite_last_insert_rowid($serendipity['dbConn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse result arrays into expected format for further operations
|
||||
*
|
||||
* SQLite does not support to return "e.entryid" within a $row['entryid'] return.
|
||||
* So this function manually iteratse through all result rows and rewrites 'X.yyyy' to 'yyyy'.
|
||||
* Yeah. This sucks. Don't tell me!
|
||||
*
|
||||
* @access private
|
||||
* @param resource The row resource handle
|
||||
* @param int Bitmask to tell whether to fetch numerical/associative arrays
|
||||
* @return array Propper array containing the resource results
|
||||
*/
|
||||
function serendipity_db_sqlite_fetch_array($res, $type = SQLITE_BOTH)
|
||||
{
|
||||
static $search = array('%00', '%25');
|
||||
static $replace = array("\x00", '%');
|
||||
|
||||
$row = sqlite_fetch_array($res, $type);
|
||||
if (!is_array($row)) {
|
||||
return $row;
|
||||
}
|
||||
|
||||
/* strip any slashes, correct fieldname */
|
||||
foreach ($row AS $i => $v) {
|
||||
// TODO: If a query of the format 'SELECT a.id, b.text FROM table' is used,
|
||||
// the sqlite extension will give us key indizes 'a.id' and 'b.text'
|
||||
// instead of just 'id' and 'text' like in mysql/postgresql extension.
|
||||
// To fix that, we use a preg-regex; but that is quite performance costy.
|
||||
// Either we always need to use 'SELECT a.id AS id, b.text AS text' in query,
|
||||
// or the sqlite extension may get fixed. :-)
|
||||
$row[preg_replace('@^.+\.(.*)@', '\1', $i)] = str_replace($search, $replace, $v);
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
function serendipity_db_in_sql($col, &$search_ids, $type = ' OR ') {
|
||||
$sql = array();
|
||||
if (!is_array($search_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($search_ids AS $id) {
|
||||
$sql[] = $col . ' = ' . $id;
|
||||
}
|
||||
|
||||
$cond = '(' . implode($type, $sql) . ')';
|
||||
return $cond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignored on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
function &serendipity_db_query($sql, $single = false, $result_type = "both", $reportErr = true, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
global $serendipity;
|
||||
$type_map = array(
|
||||
'assoc' => SQLITE_ASSOC,
|
||||
'num' => SQLITE_NUM,
|
||||
'both' => SQLITE_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
static $debug = false;
|
||||
|
||||
if ($debug) {
|
||||
// Open file and write directly. In case of crashes, the pointer needs to be killed.
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE QUERY: ' . $sql . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
if ($reportErr && !$expectError) {
|
||||
$res = sqlite_query($sql, $serendipity['dbConn']);
|
||||
} else {
|
||||
$res = @sqlite_query($sql, $serendipity['dbConn']);
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
if (!$expectError && !$serendipity['production']) {
|
||||
var_dump($res);
|
||||
var_dump($sql);
|
||||
$msg = "problem with query";
|
||||
return $msg;
|
||||
}
|
||||
if ($debug) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] [ERROR] ' . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
if ($res === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
if (sqlite_num_rows($res) == 0) {
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
} else {
|
||||
$rows = array();
|
||||
|
||||
while (($row = serendipity_db_sqlite_fetch_array($res, $type_map[$result_type]))) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE RESULT: ' . print_r($rows, true). "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
if ($single && count($rows) == 1) {
|
||||
return $rows[0];
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
function serendipity_db_probe($hash, &$errs)
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
$dbName = (isset($hash['sqlitedbName']) ? $hash['sqlitedbName'] : $hash['dbName']);
|
||||
|
||||
if (!function_exists('sqlite_open')) {
|
||||
$errs[] = 'SQLite extension not installed. Run "pear install sqlite" on your webserver or contact your systems administrator regarding this problem.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (defined('S9Y_DATA_PATH')) {
|
||||
// Shared installations!
|
||||
$dbfile = S9Y_DATA_PATH . $dbName . '.db';
|
||||
} else {
|
||||
$dbfile = $serendipity['serendipityPath'] . $dbName . '.db';
|
||||
}
|
||||
|
||||
$serendipity['dbConn'] = sqlite_open($dbfile);
|
||||
|
||||
if ($serendipity['dbConn']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$errs[] = "Unable to open \"$dbfile\" - check permissions (directory needs to be writeable for webserver)!";
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipty query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
function serendipity_db_schema_import($query)
|
||||
{
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('INTEGER', 'PRIMARY KEY', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
|
||||
|
||||
if (stristr($query, '{FULLTEXT_MYSQL}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return serendipity_db_query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return serendipity_db_query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit($start, $offset) {
|
||||
return $start . ', ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit_sql($limitstring) {
|
||||
return ' LIMIT ' . $limitstring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
function serendipity_db_concat($string) {
|
||||
return 'concat(' . $string . ')';
|
||||
}
|
||||
|
||||
/* vim: set sts=4 ts=4 expandtab : */
|
@ -1,397 +0,0 @@
|
||||
<?php
|
||||
# $Id: sqlite.inc.php 1670 2007-04-10 13:23:34Z garvinhicking $
|
||||
# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
|
||||
# All rights reserved. See LICENSE file for licensing details
|
||||
|
||||
// SQLite3 only fetches by assoc, we will emulate the other result types
|
||||
define(SQLITE3_ASSOC, 0);
|
||||
define(SQLITE3_NUM, 1);
|
||||
define(SQLITE3_BOTH, 2);
|
||||
|
||||
if (!function_exists('sqlite3_open')) {
|
||||
@dl('sqlite.so');
|
||||
@dl('sqlite.dll');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function serendipity_db_begin_transaction(){
|
||||
serendipity_db_query('begin transaction');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
function serendipity_db_end_transaction($commit){
|
||||
if ($commit){
|
||||
serendipity_db_query('commit transaction');
|
||||
}else{
|
||||
serendipity_db_query('rollback transaction');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
function serendipity_db_connect()
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
if (isset($serendipity['dbConn'])) {
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
// SQLite3 doesn't support persistent connections
|
||||
$serendipity['dbConn'] = sqlite3_open((defined('S9Y_DATA_PATH') ? S9Y_DATA_PATH : $serendipity['serendipityPath']) . $serendipity['dbName'] . '.db');
|
||||
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
function serendipity_db_reconnect() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
function serendipity_db_escape_string($string)
|
||||
{
|
||||
static $search = array("\x00", '%', "'", '\"');
|
||||
static $replace = array('%00', '%25', "''", '\\\"');
|
||||
|
||||
return str_replace($search, $replace, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
function serendipity_db_affected_rows()
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
return sqlite3_changes($serendipity['dbConn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
function serendipity_db_updated_rows()
|
||||
{
|
||||
global $serendipity;
|
||||
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
|
||||
return sqlite3_changes($serendipity['dbConn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
function serendipity_db_matched_rows()
|
||||
{
|
||||
global $serendipity;
|
||||
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
|
||||
return sqlite3_changes($serendipity['dbConn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
function serendipity_db_insert_id()
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
return sqlite3_last_insert_rowid($serendipity['dbConn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse result arrays into expected format for further operations
|
||||
*
|
||||
* SQLite does not support to return "e.entryid" within a $row['entryid'] return.
|
||||
* So this function manually iteratse through all result rows and rewrites 'X.yyyy' to 'yyyy'.
|
||||
* Yeah. This sucks. Don't tell me!
|
||||
*
|
||||
* @access private
|
||||
* @param resource The row resource handle
|
||||
* @param int Bitmask to tell whether to fetch numerical/associative arrays
|
||||
* @return array Propper array containing the resource results
|
||||
*/
|
||||
function serendipity_db_sqlite_fetch_array($res, $type = SQLITE3_BOTH)
|
||||
{
|
||||
static $search = array('%00', '%25');
|
||||
static $replace = array("\x00", '%');
|
||||
|
||||
$row = sqlite3_fetch_array($res);
|
||||
if (!is_array($row)) {
|
||||
return $row;
|
||||
}
|
||||
|
||||
/* strip any slashes, correct fieldname */
|
||||
foreach ($row AS $i => $v) {
|
||||
// TODO: If a query of the format 'SELECT a.id, b.text FROM table' is used,
|
||||
// the sqlite extension will give us key indizes 'a.id' and 'b.text'
|
||||
// instead of just 'id' and 'text' like in mysql/postgresql extension.
|
||||
// To fix that, we use a preg-regex; but that is quite performance costy.
|
||||
// Either we always need to use 'SELECT a.id AS id, b.text AS text' in query,
|
||||
// or the sqlite extension may get fixed. :-)
|
||||
$row[preg_replace('@^.+\.(.*)@', '\1', $i)] = str_replace($search, $replace, $v);
|
||||
}
|
||||
|
||||
if ($type == SQLITE3_NUM)
|
||||
$frow = array();
|
||||
else
|
||||
$frow = $row;
|
||||
|
||||
if ($type != SQLITE3_ASSOC) {
|
||||
$i = 0;
|
||||
foreach($row AS $k => $v) {
|
||||
$frow[$i] = $v;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
return $frow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
function serendipity_db_in_sql($col, &$search_ids, $type = ' OR ') {
|
||||
$sql = array();
|
||||
if (!is_array($search_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($search_ids AS $id) {
|
||||
$sql[] = $col . ' = ' . $id;
|
||||
}
|
||||
|
||||
$cond = '(' . implode($type, $sql) . ')';
|
||||
return $cond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
function &serendipity_db_query($sql, $single = false, $result_type = "both", $reportErr = true, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
global $serendipity;
|
||||
$type_map = array(
|
||||
'assoc' => SQLITE3_ASSOC,
|
||||
'num' => SQLITE3_NUM,
|
||||
'both' => SQLITE3_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
static $debug = false;
|
||||
|
||||
if ($debug) {
|
||||
// Open file and write directly. In case of crashes, the pointer needs to be killed.
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE QUERY: ' . $sql . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
if ($reportErr && !$expectError) {
|
||||
$res = sqlite3_query($serendipity['dbConn'], $sql);
|
||||
} else {
|
||||
$res = @sqlite3_query($serendipity['dbConn'], $sql);
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
if (!$expectError && !$serendipity['production']) {
|
||||
var_dump($res);
|
||||
var_dump($sql);
|
||||
$msg = "problem with query";
|
||||
return $msg;
|
||||
}
|
||||
if ($debug) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] [ERROR] ' . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
if ($res === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
|
||||
while (($row = serendipity_db_sqlite_fetch_array($res, $type_map[$result_type]))) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE RESULT: ' . print_r($rows, true). "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
if ($single && count($rows) == 1) {
|
||||
return $rows[0];
|
||||
}
|
||||
|
||||
if (count($rows) == 0) {
|
||||
if ($single)
|
||||
return $type_map['false'];
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
function serendipity_db_probe($hash, &$errs)
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
$dbName = (isset($hash['sqlitedbName']) ? $hash['sqlitedbName'] : $hash['dbName']);
|
||||
|
||||
if (!function_exists('sqlite3_open')) {
|
||||
$errs[] = 'SQLite extension not installed. Run "pear install sqlite" on your webserver or contact your systems administrator regarding this problem.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (defined('S9Y_DATA_PATH')) {
|
||||
// Shared installations!
|
||||
$dbfile = S9Y_DATA_PATH . $dbName . '.db';
|
||||
} else {
|
||||
$dbfile = $serendipity['serendipityPath'] . $dbName . '.db';
|
||||
}
|
||||
|
||||
$serendipity['dbConn'] = sqlite3_open($dbfile);
|
||||
|
||||
if ($serendipity['dbConn']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$errs[] = "Unable to open \"$dbfile\" - check permissions (directory needs to be writeable for webserver)!";
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipty query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
function serendipity_db_schema_import($query)
|
||||
{
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('INTEGER AUTOINCREMENT', 'PRIMARY KEY', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
|
||||
|
||||
if (stristr($query, '{FULLTEXT_MYSQL}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
$query = str_replace('INTEGER AUTOINCREMENT PRIMARY KEY', 'INTEGER PRIMARY KEY AUTOINCREMENT', $query);
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return serendipity_db_query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return serendipity_db_query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit($start, $offset) {
|
||||
return $start . ', ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit_sql($limitstring) {
|
||||
return ' LIMIT ' . $limitstring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
function serendipity_db_concat($string) {
|
||||
return 'concat(' . $string . ')';
|
||||
}
|
||||
|
||||
/* vim: set sts=4 ts=4 expandtab : */
|
@ -1,412 +0,0 @@
|
||||
<?php
|
||||
# $Id: sqlite.inc.php 1670 2007-04-10 13:23:34Z garvinhicking $
|
||||
# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
|
||||
# All rights reserved. See LICENSE file for licensing details
|
||||
|
||||
// SQLite3 only fetches by assoc, we will emulate the other result types
|
||||
define(SQLITE3_ASSOC, 0);
|
||||
define(SQLITE3_NUM, 1);
|
||||
define(SQLITE3_BOTH, 2);
|
||||
|
||||
if (!class_exists('SQLite3')) {
|
||||
@dl('sqlite3.so');
|
||||
@dl('sqlite3.dll');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function serendipity_db_begin_transaction(){
|
||||
serendipity_db_query('begin transaction');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
function serendipity_db_end_transaction($commit){
|
||||
if ($commit){
|
||||
serendipity_db_query('commit transaction');
|
||||
}else{
|
||||
serendipity_db_query('rollback transaction');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
function serendipity_db_connect()
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
if (isset($serendipity['dbConn'])) {
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
// SQLite3 doesn't support persistent connections
|
||||
$serendipity['dbConn'] = new SQLite3((defined('S9Y_DATA_PATH') ? S9Y_DATA_PATH : $serendipity['serendipityPath']) . $serendipity['dbName'] . '.db');
|
||||
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
function serendipity_db_reconnect() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
function serendipity_db_escape_string($string)
|
||||
{
|
||||
static $search = array("\x00", '%', "'", '\"');
|
||||
static $replace = array('%00', '%25', "''", '\\\"');
|
||||
|
||||
return str_replace($search, $replace, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
function serendipity_db_affected_rows()
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
return $serendipity['dbConn']->changes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
function serendipity_db_updated_rows()
|
||||
{
|
||||
global $serendipity;
|
||||
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
|
||||
return $serendipity['dbConn']->changes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
function serendipity_db_matched_rows()
|
||||
{
|
||||
global $serendipity;
|
||||
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
|
||||
return $serendipity['dbConn']->changes($serendipity['dbConn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
function serendipity_db_insert_id()
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
return $serendipity['dbConn']->lastInsertRowID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse result arrays into expected format for further operations
|
||||
*
|
||||
* SQLite does not support to return "e.entryid" within a $row['entryid'] return.
|
||||
* So this function manually iteratse through all result rows and rewrites 'X.yyyy' to 'yyyy'.
|
||||
* Yeah. This sucks. Don't tell me!
|
||||
*
|
||||
* @access private
|
||||
* @param resource The row resource handle
|
||||
* @param int Bitmask to tell whether to fetch numerical/associative arrays
|
||||
* @return array Propper array containing the resource results
|
||||
*/
|
||||
function serendipity_db_sqlite_fetch_array($res, $type = SQLITE3_BOTH)
|
||||
{
|
||||
static $search = array('%00', '%25');
|
||||
static $replace = array("\x00", '%');
|
||||
|
||||
try {
|
||||
$row = $res->fetchArray();
|
||||
} catch (Exception $e) {
|
||||
$row = false;
|
||||
echo "SQLITE-EXCEPTION: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
if (!is_array($row)) {
|
||||
return $row;
|
||||
}
|
||||
|
||||
/* strip any slashes, correct fieldname */
|
||||
foreach ($row AS $i => $v) {
|
||||
// TODO: If a query of the format 'SELECT a.id, b.text FROM table' is used,
|
||||
// the sqlite extension will give us key indizes 'a.id' and 'b.text'
|
||||
// instead of just 'id' and 'text' like in mysql/postgresql extension.
|
||||
// To fix that, we use a preg-regex; but that is quite performance costy.
|
||||
// Either we always need to use 'SELECT a.id AS id, b.text AS text' in query,
|
||||
// or the sqlite extension may get fixed. :-)
|
||||
$row[preg_replace('@^.+\.(.*)@', '\1', $i)] = str_replace($search, $replace, $v);
|
||||
}
|
||||
|
||||
if ($type == SQLITE3_NUM)
|
||||
$frow = array();
|
||||
else
|
||||
$frow = $row;
|
||||
|
||||
if ($type != SQLITE3_ASSOC) {
|
||||
$i = 0;
|
||||
foreach($row AS $k => $v) {
|
||||
$frow[$i] = $v;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
return $frow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
function serendipity_db_in_sql($col, &$search_ids, $type = ' OR ') {
|
||||
$sql = array();
|
||||
if (!is_array($search_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($search_ids AS $id) {
|
||||
$sql[] = $col . ' = ' . $id;
|
||||
}
|
||||
|
||||
$cond = '(' . implode($type, $sql) . ')';
|
||||
return $cond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
function &serendipity_db_query($sql, $single = false, $result_type = "both", $reportErr = true, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
global $serendipity;
|
||||
$type_map = array(
|
||||
'assoc' => SQLITE3_ASSOC,
|
||||
'num' => SQLITE3_NUM,
|
||||
'both' => SQLITE3_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
static $debug = false;
|
||||
|
||||
if ($debug) {
|
||||
// Open file and write directly. In case of crashes, the pointer needs to be killed.
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE QUERY: ' . $sql . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
if ($reportErr && !$expectError) {
|
||||
$res = $serendipity['dbConn']->query($sql);
|
||||
} else {
|
||||
$res = @$serendipity['dbConn']->query($sql);
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
if (!$expectError && !$serendipity['production']) {
|
||||
var_dump($res);
|
||||
var_dump($sql);
|
||||
$msg = "problem with query";
|
||||
return $msg;
|
||||
}
|
||||
if ($debug) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] [ERROR] ' . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
|
||||
if (!preg_match('@^SELECT@imsU', trim($sql))) {
|
||||
// Everything that is not SELECT will not return rows.
|
||||
// SQLite3 OO will always return an object though.
|
||||
if ($serendipity['dbConn']->lastErrorCode() > 0) {
|
||||
echo "SQLITE-ERROR: " . $serendipity['dbConn']->lastErrorMsg() . "<br />\n";
|
||||
return $type_map['false'];
|
||||
} else {
|
||||
return $type_map['true'];
|
||||
}
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
|
||||
while (($row = serendipity_db_sqlite_fetch_array($res, $type_map[$result_type]))) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE RESULT: ' . print_r($rows, true). "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
if ($single && count($rows) == 1) {
|
||||
return $rows[0];
|
||||
}
|
||||
|
||||
if (count($rows) == 0) {
|
||||
if ($single)
|
||||
return $type_map['false'];
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
function serendipity_db_probe($hash, &$errs)
|
||||
{
|
||||
global $serendipity;
|
||||
|
||||
$dbName = (isset($hash['sqlitedbName']) ? $hash['sqlitedbName'] : $hash['dbName']);
|
||||
|
||||
if (!class_exists('SQLite3')) {
|
||||
$errs[] = 'SQLite extension not installed. Available on PHP 5.4+.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (defined('S9Y_DATA_PATH')) {
|
||||
// Shared installations!
|
||||
$dbfile = S9Y_DATA_PATH . $dbName . '.db';
|
||||
} else {
|
||||
$dbfile = $serendipity['serendipityPath'] . $dbName . '.db';
|
||||
}
|
||||
|
||||
|
||||
$serendipity['dbConn'] = new SQLite3($dbfile);
|
||||
|
||||
if ($serendipity['dbConn']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$errs[] = "Unable to open \"$dbfile\" - check permissions (directory needs to be writeable for webserver)!";
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipty query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
function serendipity_db_schema_import($query)
|
||||
{
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('INTEGER AUTOINCREMENT', 'PRIMARY KEY', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
|
||||
|
||||
if (stristr($query, '{FULLTEXT_MYSQL}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
$query = str_replace('INTEGER AUTOINCREMENT PRIMARY KEY', 'INTEGER PRIMARY KEY AUTOINCREMENT', $query);
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return serendipity_db_query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return serendipity_db_query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit($start, $offset) {
|
||||
return $start . ', ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit_sql($limitstring) {
|
||||
return ' LIMIT ' . $limitstring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
function serendipity_db_concat($string) {
|
||||
return 'concat(' . $string . ')';
|
||||
}
|
||||
|
||||
/* vim: set sts=4 ts=4 expandtab : */
|
@ -1,558 +0,0 @@
|
||||
<?php
|
||||
# mysql_sqlrelay.inc.php modified from mysql.inc.php 2007-10-06
|
||||
# dantemason@gmail.com
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function serendipity_db_begin_transaction() {
|
||||
global $serendipity;
|
||||
|
||||
$type_of_database = sqlrcon_identify($serendipity['dbConn']);
|
||||
switch($type_of_database) {
|
||||
case 'mysql':
|
||||
$sqlstr = 'start transaction';
|
||||
break;
|
||||
|
||||
case 'postgresql':
|
||||
$sqlstr = 'begin work';
|
||||
break;
|
||||
|
||||
case 'sqlite':
|
||||
$sqlstr = 'begin transaction';
|
||||
break;
|
||||
|
||||
default:
|
||||
$sqlstr = 'start transaction';
|
||||
break;
|
||||
}
|
||||
serendipity_db_query($sqlstr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
function serendipity_db_end_transaction($commit) {
|
||||
global $serendipity;
|
||||
|
||||
if ($commit) {
|
||||
sqlrcon_commit($serendipity['dbConn']);
|
||||
} else {
|
||||
sqlrcon_rollback($serendipity['dbConn']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
function serendipity_db_in_sql($col, &$search_ids, $type = ' OR ') {
|
||||
global $serendipity;
|
||||
|
||||
if (sqlrcon_identify($serendipity['dbConn']) == "sqlite") {
|
||||
$sql = array();
|
||||
if (!is_array($search_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($search_ids AS $id) {
|
||||
$sql[] = $col . ' = ' . $id;
|
||||
}
|
||||
|
||||
$cond = '(' . implode($type, $sql) . ')';
|
||||
return $cond;
|
||||
} else {
|
||||
return $col . " IN (" . implode(', ', $search_ids) . ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
|
||||
function generate_resultset($cursor, $result_type = 'sqlr_BOTH') {
|
||||
$return_row = array();
|
||||
|
||||
for ($r_row = 0, $max_r = sqlrcur_rowCount($cursor) ; $r_row < $max_r ; $r_row++) {
|
||||
for ($r_col=0, $max_rc = sqlrcur_colCount($cursor); $r_col < $max_rc ; $r_col++) {
|
||||
if ($result_type == 'sqlr_ASSOC') {
|
||||
$return_row[sqlrcur_getColumnName($cursor, $r_col)] = sqlrcur_getField($cursor, $r_row, $r_col);
|
||||
}else{
|
||||
$return_row[$r_row][$r_col] = sqlrcur_getField($cursor, $r_row,$r_col);
|
||||
$return_row[$r_row][sqlrcur_getColumnName($cursor, $r_col)] = $return_row[$r_row][$r_col];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $return_row;
|
||||
}
|
||||
|
||||
function &serendipity_db_query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false) {
|
||||
global $serendipity;
|
||||
$type_map = array(
|
||||
'assoc' => sqlr_ASSOC,
|
||||
'num' => sqlr_NUM,
|
||||
'both' => sqlr_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
static $benchmark = false;
|
||||
|
||||
// highlight_string(var_export($sql, 1));
|
||||
|
||||
if (!is_resource($serendipity['dbConn'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cur = sqlrcur_alloc($serendipity['dbConn']);
|
||||
$serendipity['dbCursor'] = $cur;
|
||||
|
||||
if ($benchmark) {
|
||||
$start = microtime_float();
|
||||
}
|
||||
|
||||
if ($expectError) {
|
||||
$c = sqlrcur_sendQuery($cur, $sql);
|
||||
} else {
|
||||
$c = sqlrcur_sendQuery($cur, $sql);
|
||||
}
|
||||
|
||||
if ($benchmark) {
|
||||
$end = microtime_float();
|
||||
|
||||
$cur = sqlrcur_alloc($serendipity['dbConn']);
|
||||
$sql_b="INSERT INTO BLOGLOG (request, timestamp, sql, exec_time, ip) VALUES ('" . serendipity_db_escape_string($_SERVER['REQUEST_URI']) . "', NOW(), '" . serendipity_db_escape_string($sql) . "', '" . (number_format($end-$start, 10)) . "', '" . serendipity_db_escape_string($_SERVER['REMOTE_ADDR']) . "')";
|
||||
$c = sqlrcur_sendQuery($cur, $sql_b);
|
||||
|
||||
$psql = $sql;
|
||||
$psql = preg_replace('@[0-9]{10}@', 'TIMESTAMP', $psql);
|
||||
$sql_U="UPDATE BLOGLOG_TOTAL SET counter = counter + 1 WHERE sql = '" . serendipity_db_escape_string($psql) . "'";
|
||||
$c = sqlrcur_sendQuery($cur, $sql_U);
|
||||
if (sqlrcur_affectedRows() < 1) {
|
||||
$sql_i="INSERT INTO BLOGLOG_TOTAL (sql, counter) VALUES ('" . serendipity_db_escape_string($psql) . "', 1)";
|
||||
$c = sqlrcur_sendQuery($cur,$sql_i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$expectError && sqlrcur_errorMessage($cur) != '') {
|
||||
$msg = '<pre>' . serendipity_specialchars($sql) . '</pre> / ' . serendipity_specialchars(sqlrcur_errorMessage($cur));
|
||||
return $msg;
|
||||
}
|
||||
|
||||
if (!$c) {
|
||||
if (!$expectError && !$serendipity['production']) {
|
||||
print '<pre>' . serendipity_specialchars($sql) . '</pre> / ' . serendipity_specialchars(sqlrcur_errorMessage($cur));
|
||||
if (function_exists('debug_backtrace') && $reportErr == true) {
|
||||
highlight_string(var_export(debug_backtrace(), 1));
|
||||
}
|
||||
}
|
||||
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
if ($c === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
$result_type = $type_map[$result_type];
|
||||
|
||||
switch(sqlrcur_rowCount($cur)) {
|
||||
case 0:
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
|
||||
case 1:
|
||||
if ($single) {
|
||||
$row = generate_resultset($cur, $result_type);
|
||||
if ($result_type != 'sqlr_ASSOC') {
|
||||
$row=$row[0];
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
default:
|
||||
if ($single) {
|
||||
return generate_resultset($cur, $result_type);
|
||||
}
|
||||
|
||||
$row=generate_resultset($cur);
|
||||
$rows=array();
|
||||
|
||||
for($idx=0, $idxc = count($row); $idx < $idxc ; $idx++) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$assocKey] = $row[$idx][$assocKey];
|
||||
} else {
|
||||
$rows[$row[$idx][$assocKey]] = $row[$idx][$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $rows;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @return int Value of the auto-increment column
|
||||
*
|
||||
* If you insert multiple rows using a single INSERT statement,
|
||||
* LAST_INSERT_ID() returns the value generated for the first inserted row only.
|
||||
*/
|
||||
function serendipity_db_insert_id($table = '', $id = '') {
|
||||
global $serendipity;
|
||||
|
||||
$type_of_database = sqlrcon_identify($serendipity['dbConn']);
|
||||
switch($type_of_database) {
|
||||
case 'mysql':
|
||||
$sqlstr='SELECT LAST_INSERT_ID()';
|
||||
break;
|
||||
|
||||
case 'postgresql':
|
||||
$sqlstr = "SELECT currval('{$serendipity['dbPrefix']}{$table}_{$id}_seq'::text) AS {$id}";
|
||||
break;
|
||||
|
||||
case 'sqlite':
|
||||
$sqlstr = 'SELECT last_insert_rowid()';
|
||||
break;
|
||||
|
||||
default:
|
||||
$sqlstr = 'SELECT LAST_INSERT_ID()';
|
||||
}
|
||||
|
||||
$row = serendipity_db_query($sqlstr, true);
|
||||
|
||||
return $row[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
function serendipity_db_affected_rows() {
|
||||
global $serendipity;
|
||||
|
||||
/* int sqlrcur_affectedRows(int sqlrcurref)
|
||||
* Returns the number of rows that were updated, inserted or deleted by the query.
|
||||
* Not all databases support this call.
|
||||
* Don't use it for applications which are designed to be portable across databases.
|
||||
* -1 is returned by databases which don't support this option.
|
||||
*/
|
||||
|
||||
return sqlrcur_affectedRows($serendipity['dbCursor']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
function serendipity_db_updated_rows() {
|
||||
/*
|
||||
|
||||
preg_match(
|
||||
"/^[^0-9]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)/",
|
||||
mysql_info(),
|
||||
$arr);
|
||||
// mysql_affected_rows returns 0 if rows were matched but not changed.
|
||||
// mysql_info returns rows matched AND rows changed
|
||||
return $arr[2];
|
||||
*/
|
||||
return serendipity_db_affected_rows();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
function serendipity_db_matched_rows() {
|
||||
/*
|
||||
|
||||
preg_match(
|
||||
"/^[^0-9]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)/",
|
||||
mysql_info(),
|
||||
$arr);
|
||||
// mysql_affected_rows returns 0 if rows were matched but not changed.
|
||||
// mysql_info returns rows matched AND rows changed
|
||||
return $arr[1];
|
||||
*/
|
||||
return serendipity_db_affected_rows();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
|
||||
function serendipity_db_escape_string($str) {
|
||||
global $serendipity;
|
||||
static $search = array("\x00", '%', "'", '\"');
|
||||
static $replace = array('%00', '%25', "''", '\\\"');
|
||||
|
||||
$type_of_database = sqlrcon_identify($serendipity['dbConn']);
|
||||
switch($type_of_database) {
|
||||
case 'mysql':
|
||||
if (function_exists('mysql_real_escape_string')) {
|
||||
$rtnstr = mysql_real_escape_string($str, $serendipity['dbConn']);
|
||||
} else {
|
||||
$rtnstr = addslashes($str);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'postgresql':
|
||||
if (function_exists('pg_escape_string')) {
|
||||
$rtnstr = pg_escape_string($str);
|
||||
} else {
|
||||
$rtnstr = addslashes($str);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'sqlite':
|
||||
$rtnstr = str_replace($search, $replace, $str);
|
||||
break;
|
||||
|
||||
default:
|
||||
$rtnstr = addslashes($str);
|
||||
}
|
||||
|
||||
return $rtnstr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit($start, $offset) {
|
||||
return $start . ', ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
function serendipity_db_limit_sql($limitstring) {
|
||||
global $serendipity;
|
||||
|
||||
$type_of_database = sqlrcon_identify($serendipity['dbConn']);
|
||||
switch($type_of_database) {
|
||||
case "mysql":
|
||||
return ' LIMIT ' . $limitstring;
|
||||
case "postgresql":
|
||||
$limit_split = explode(',', $limitstring);
|
||||
if (count($limit_split) > 1) {
|
||||
$limit = ' LIMIT ' . $limit_split[0] . ' OFFSET ' . $limit_split[1];
|
||||
} else {
|
||||
$limit = ' LIMIT ' . $limit_split[0];
|
||||
}
|
||||
return $limit;
|
||||
default:
|
||||
return ' LIMIT ' . $limitstring;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
function serendipity_db_connect() {
|
||||
global $serendipity;
|
||||
|
||||
if (isset($serendipity['dbConn'])) {
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
if (isset($serendipity['dbPersistent']) && $serendipity['dbPersistent']) {
|
||||
$function = 'sqlrcon_alloc';
|
||||
} else {
|
||||
$function = 'sqlrcon_alloc';
|
||||
}
|
||||
|
||||
#$serendipity['dbHost']="localhost:9000"
|
||||
$dbHostPort=explode(":", $serendipity['dbHost']);
|
||||
$serendipity['dbConn'] = $function($dbHostPort[0], $dbHostPort[1], "", $serendipity['dbUser'], $serendipity['dbPass'], 0, 1);
|
||||
sqlrcon_debugOff($serendipity['dbConn']);
|
||||
|
||||
if( sqlrcon_identify($serendipity['dbConn']) == "mysql") {
|
||||
serendipity_db_reconnect();
|
||||
}
|
||||
|
||||
return $serendipity['dbConn'];
|
||||
}
|
||||
|
||||
function serendipity_db_reconnect() {
|
||||
global $serendipity;
|
||||
|
||||
if (isset($serendipity['dbCharset'])) {
|
||||
serendipity_db_query("SET NAMES " . $serendipity['dbCharset']);
|
||||
@define('SQL_CHARSET_INIT', true);
|
||||
} elseif (defined('SQL_CHARSET') && $serendipity['dbNames'] && !defined('SQL_CHARSET_INIT')) {
|
||||
serendipity_db_query("SET NAMES " . SQL_CHARSET);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipty query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
function serendipity_db_schema_import($query) {
|
||||
global $serendipity;
|
||||
|
||||
$type_of_database = sqlrcon_identify($serendipity['dbConn']);
|
||||
switch($type_of_database) {
|
||||
case "mysql":
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}',
|
||||
'{UNSIGNED}', '{FULLTEXT}', '{FULLTEXT_MYSQL}', '{BOOLEAN}', '{TEXT}');
|
||||
static $replace = array('int(11) not null auto_increment', 'primary key',
|
||||
'unsigned' , 'FULLTEXT', 'FULLTEXT', 'enum (\'true\', \'false\') NOT NULL default \'true\'', 'LONGTEXT');
|
||||
static $is_utf8 = null;
|
||||
|
||||
if ($is_utf8 === null) {
|
||||
$search[] = '{UTF_8}';
|
||||
if ((isset($_POST['charset']) && $_POST['charset'] == 'UTF-8/') ||
|
||||
$serendipity['charset'] == 'UTF-8/' ||
|
||||
$serendipity['POST']['charset'] == 'UTF-8/' ||
|
||||
LANG_CHARSET == 'UTF-8' ) {
|
||||
$replace[] = '/*!40100 CHARACTER SET utf8 COLLATE utf8_unicode_ci */';
|
||||
} else {
|
||||
$replace[] = '';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "postgresql":
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}',
|
||||
'{FULLTEXT}', '{FULLTEXT_MYSQL}', '{BOOLEAN}', 'int(1)', 'int(10)', 'int(11)', 'int(4)', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('SERIAL', 'primary key', '',
|
||||
'', '', 'BOOLEAN NOT NULL', 'int2', 'int4', 'int4', 'int4', '', 'text');
|
||||
break;
|
||||
|
||||
case "sqlite":
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{FULLTEXT_MYSQL}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('INTEGER', 'PRIMARY KEY', '', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
|
||||
break;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
|
||||
if ($query{0} == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return serendipity_db_query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return serendipity_db_query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
function serendipity_db_probe($hash, &$errs) {
|
||||
global $serendipity;
|
||||
$c = null;
|
||||
|
||||
if (!function_exists('sqlrcon_alloc')) {
|
||||
$errs[] = 'No sql_relay extension found. Please check your webserver installation or contact your systems administrator regarding this problem.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!($c=@sqlrcon_alloc($hash['dbHost'], '', '', $hash['dbUser'], $hash['dbPass'], 0, 5))) {
|
||||
$errs[] = 'Could not connect to database; check your settings.';
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$serendipity['dbConn'] = $c;
|
||||
|
||||
/* which db should be used is defined in sqlrelay.conf */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
function serendipity_db_concat($string) {
|
||||
global $serendipity;
|
||||
|
||||
$type_of_database = sqlrcon_identify($serendipity['dbConn']);
|
||||
switch($type_of_database) {
|
||||
case 'mysql':
|
||||
return 'concat(' . $string . ')';
|
||||
|
||||
case 'postgresql':
|
||||
return '(' . str_replace(', ', '||', $string) . ')';
|
||||
|
||||
case 'sqlite':
|
||||
return 'concat(' . $string . ')';
|
||||
|
||||
default:
|
||||
return 'concat(' . $string . ')';
|
||||
}
|
||||
}
|
||||
|
||||
/* vim: set sts=4 ts=4 expandtab : */
|
362
lib/Serendipity/Database/DbAbstract.php
Normal file
362
lib/Serendipity/Database/DbAbstract.php
Normal file
@ -0,0 +1,362 @@
|
||||
<?php
|
||||
|
||||
// Serendipity
|
||||
// See LICENSE file for license information.
|
||||
|
||||
namespace Serendipity\Database;
|
||||
|
||||
abstract class DbAbstract
|
||||
{
|
||||
protected $serendipity;
|
||||
protected $db_conn;
|
||||
protected $db_hostname;
|
||||
protected $db_username;
|
||||
protected $db_password;
|
||||
protected $db_name;
|
||||
protected $db_prefix;
|
||||
protected $db_type;
|
||||
|
||||
public function __construct(&$serendipity)
|
||||
{
|
||||
$this->serendipity =& $serendipity;
|
||||
$this->db_type = $serendipity['dbType'];
|
||||
$this->db_hostname = $serendipity['dbHost'];
|
||||
$this->db_username = $serendipity['dbUser'];
|
||||
$this->db_password = $serendipity['dbPass'];
|
||||
$this->db_name = $serendipity['dbName'];
|
||||
$this->db_prefix = $serendipity['dbPrefix'];
|
||||
}
|
||||
|
||||
public function connect()
|
||||
{
|
||||
}
|
||||
|
||||
public function reconnect()
|
||||
{
|
||||
}
|
||||
|
||||
public function beginTransaction()
|
||||
{
|
||||
}
|
||||
|
||||
public function endTransaction(bool $commit)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
public function escapeString($string)
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
public function inSql($col, &$searchIds, $type = ' OR ')
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
public function limit($start, $offset)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
public function limitSql($limitstring)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
public function affectedRows()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
public function updatedRows()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
public function matchedRows()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @param string Name of the table to get a INSERT ID for
|
||||
* @param string Name of the column to get a INSERT ID for
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
public function insertId($table = '', $id = '')
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a query to update the data of a certain table row
|
||||
*
|
||||
* You can pass the tablename and an array of keys to select the row,
|
||||
* and an array of values to UPDATE in the DB table.
|
||||
*
|
||||
* @access public
|
||||
* @param string Name of the DB table
|
||||
* @param array Input array that controls the "WHERE" condition part. Pass it an associative array like array('key1' => 'value1', 'key2' => 'value2') to get a statement like "WHERE key1 = value1 AND key2 = value2". Escaping is done automatically in this function.
|
||||
* @param array Input array that controls the "SET" condition part. Pass it an associative array like array('key1' => 'value1', 'key2' => 'value2') to get a statement like "SET key1 = value1, key2 = value2". Escaping is done automatically in this function.
|
||||
* @param string What do do with the SQL query (execute, display)
|
||||
* @return array Returns the result of the SQL query
|
||||
*/
|
||||
public function update($table, $keys, $values, $action = 'execute')
|
||||
{
|
||||
$set = '';
|
||||
|
||||
foreach ($values as $k => $v) {
|
||||
if (strlen($set)) {
|
||||
$set .= ', ';
|
||||
}
|
||||
$set .= $k . '=\'' . $this->escapeString($v) . '\'';
|
||||
}
|
||||
|
||||
$where = '';
|
||||
foreach ($keys as $k => $v) {
|
||||
if (strlen($where)) {
|
||||
$where .= ' AND ';
|
||||
}
|
||||
$where .= $k . '=\'' . $this->escapeString($v) . '\'';
|
||||
}
|
||||
|
||||
if (strlen($where)) {
|
||||
$where = " WHERE $where";
|
||||
}
|
||||
|
||||
$q = "UPDATE {$this->db_prefix}$table SET $set $where";
|
||||
if ($action == 'execute') {
|
||||
return $this->query($q);
|
||||
} else {
|
||||
return $q;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values depending on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param bool Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param bool If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param bool If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignored on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
public function &query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a query to insert an associative array into a specific SQL table
|
||||
*
|
||||
* You can pass a tablename and an array of input data to insert into an array.
|
||||
*
|
||||
* @access public
|
||||
* @param string Name of the SQL table
|
||||
* @param array Associative array of keys/values to insert into the table. Escaping is done automatically.
|
||||
* @param string What do do with the SQL query (execute, display)
|
||||
* @return array Returns the result of the SQL query
|
||||
*/
|
||||
public function insert($table, $values, $action = 'execute')
|
||||
{
|
||||
$names = implode(',', array_keys($values));
|
||||
|
||||
$vals = '';
|
||||
foreach ($values as $k => $v) {
|
||||
if (strlen($vals)) {
|
||||
$vals .= ', ';
|
||||
}
|
||||
$vals .= '\'' . $this->escapeString($v) . '\'';
|
||||
}
|
||||
|
||||
$q = "INSERT INTO {$this->db_prefix}$table ($names) values ($vals)";
|
||||
|
||||
if ($action == 'execute') {
|
||||
return $this->query($q);
|
||||
} else {
|
||||
return $q;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether an input value corresponds to a TRUE/FALSE option in the SQL database.
|
||||
*
|
||||
* Because older DBs could not store TRUE/FALSE values to be restored into a PHP variable,
|
||||
* this function tries to detect what the return code of a SQL column is, and convert it
|
||||
* to a PHP native boolean.
|
||||
*
|
||||
* Values that will be recognized as TRUE are 'true', 't' and '1'.
|
||||
*
|
||||
* @access public
|
||||
* @param string input value to compare
|
||||
* @return boolean boolean conversion of the input value
|
||||
*/
|
||||
public function bool($val)
|
||||
{
|
||||
if (($val === true) || ($val == 'true') || ($val == 't') || ($val == '1')) {
|
||||
return true;
|
||||
}
|
||||
#elseif (($val === false || $val == 'false' || $val == 'f'))
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipity query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
public function schemaImport($query)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a SQL statement for a time interval or timestamp, specific to certain SQL backends
|
||||
*
|
||||
* @access public
|
||||
* @param string Indicate whether to return a timestamp, or an Interval
|
||||
* @param int The interval one might want to use, if Interval return was selected
|
||||
* @return string SQL statement
|
||||
*/
|
||||
public function getInterval($val, $ival = 900)
|
||||
{
|
||||
switch ($this->db_type) {
|
||||
case 'sqlite':
|
||||
case 'sqlite3':
|
||||
case 'sqlite3oo':
|
||||
case 'pdo-sqlite':
|
||||
$interval = $ival;
|
||||
$ts = time();
|
||||
break;
|
||||
|
||||
case 'pdo-postgres':
|
||||
case 'postgres':
|
||||
$interval = "interval '$ival'";
|
||||
$ts = 'NOW()';
|
||||
break;
|
||||
|
||||
case 'mysql':
|
||||
case 'mysqli':
|
||||
default:
|
||||
$interval = $ival;
|
||||
$ts = 'NOW()';
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($val) {
|
||||
case 'interval':
|
||||
return $interval;
|
||||
|
||||
default:
|
||||
case 'ts':
|
||||
return $ts;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Operates on an array to prepare it for SQL usage.
|
||||
*
|
||||
* @access public
|
||||
* @param string Concatenation character
|
||||
* @param array Input array
|
||||
* @param string How to convert (int: Only numbers, string: serendipity_db_escape_String)
|
||||
* @return string Imploded string
|
||||
*/
|
||||
public function implode($string, &$array, $type = 'int')
|
||||
{
|
||||
$new_array = array();
|
||||
if (!is_array($array)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
foreach ($array as $idx => $key) {
|
||||
if ($type == 'int') {
|
||||
$new_array[$idx] = (int)$key;
|
||||
} else {
|
||||
$new_array[$idx] = $this->escapeString($key);
|
||||
}
|
||||
}
|
||||
|
||||
$string = implode($string, $new_array);
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
public function concat($string)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
public function probe($hash, &$errs)
|
||||
{
|
||||
}
|
||||
}
|
43
lib/Serendipity/Database/DbFactory.php
Normal file
43
lib/Serendipity/Database/DbFactory.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
// Serendipity
|
||||
// See LICENSE file for license information.
|
||||
|
||||
namespace Serendipity\Database;
|
||||
|
||||
use Exception;
|
||||
|
||||
class DbFactory
|
||||
{
|
||||
private static $db_instance = null;
|
||||
|
||||
public static function createFromConfig(&$serendipity)
|
||||
{
|
||||
if (self::$db_instance !== null) {
|
||||
// Already instantiated - return it
|
||||
return self::$db_instance;
|
||||
}
|
||||
|
||||
$config2class = [
|
||||
'mysql' => 'Mysqli',
|
||||
'mysqli' => 'Mysqli',
|
||||
'pdo-postgres' => 'PdoPostgres',
|
||||
'pdo-sqlite' => 'PdoSqlite',
|
||||
'postgres' => 'Postgres',
|
||||
'sqlite' => 'Sqlite',
|
||||
'sqlite3' => 'Sqlite3',
|
||||
'sqlite3oo' => 'Sqlite3oo',
|
||||
'sqlrelay' => 'SqlRelay',
|
||||
];
|
||||
|
||||
if (!array_key_exists($serendipity['dbType'], $config2class)) {
|
||||
throw new Exception('Database type "' . $serendipity['dbType'] . '" not supported!');
|
||||
}
|
||||
|
||||
// Name of database class
|
||||
$dbClass = '\\Serendipity\\Database\\' . $config2class[$serendipity['dbType']] . 'Database';
|
||||
|
||||
self::$db_instance = new $dbClass($serendipity);
|
||||
return self::$db_instance;
|
||||
}
|
||||
}
|
499
lib/Serendipity/Database/MysqliDatabase.php
Normal file
499
lib/Serendipity/Database/MysqliDatabase.php
Normal file
@ -0,0 +1,499 @@
|
||||
<?php
|
||||
|
||||
// Serendipity
|
||||
// See LICENSE file for license information.
|
||||
|
||||
namespace Serendipity\Database;
|
||||
|
||||
use Serendipity\Database\DbAbstract;
|
||||
use Exception;
|
||||
|
||||
class MysqliDatabase extends DbAbstract
|
||||
{
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->query('start transaction');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
public function endTransaction($commit)
|
||||
{
|
||||
if ($commit) {
|
||||
$this->query('commit');
|
||||
}else{
|
||||
$this->query('rollback');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
public function inSql($col, &$search_ids, $type = ' OR ')
|
||||
{
|
||||
return $col . " IN (" . implode(', ', $search_ids) . ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignored on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
public function &query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
$type_map = array(
|
||||
'assoc' => MYSQLI_ASSOC,
|
||||
'num' => MYSQLI_NUM,
|
||||
'both' => MYSQLI_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
if ($expectError) {
|
||||
$c = @mysqli_query($this->db_conn, $sql);
|
||||
} else {
|
||||
$c = mysqli_query($this->db_conn, $sql);
|
||||
}
|
||||
|
||||
if (!$expectError && mysqli_error($this->db_conn) != '') {
|
||||
$msg = mysqli_error($this->db_conn);
|
||||
return $msg;
|
||||
}
|
||||
|
||||
if (!$c) {
|
||||
if (!$expectError && !$this->serendipity['production']) {
|
||||
print mysqli_error($this->db_conn);
|
||||
if (function_exists('debug_backtrace') && $reportErr == true) {
|
||||
highlight_string(var_export(debug_backtrace(), 1));
|
||||
}
|
||||
}
|
||||
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
if ($c === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
$result_type = $type_map[$result_type];
|
||||
|
||||
switch(mysqli_num_rows($c)) {
|
||||
case 0:
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
case 1:
|
||||
if ($single) {
|
||||
return mysqli_fetch_array($c, $result_type);
|
||||
}
|
||||
default:
|
||||
if ($single) {
|
||||
return mysqli_fetch_array($c, $result_type);
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
while ($row = mysqli_fetch_array($c, $result_type)) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
public function insertId()
|
||||
{
|
||||
return mysqli_insert_id($this->db_conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
public function affectedRows()
|
||||
{
|
||||
return mysqli_affected_rows($this->db_conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
public function updatedRows()
|
||||
{
|
||||
preg_match(
|
||||
"/^[^0-9]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)/",
|
||||
mysqli_info($this->db_conn),
|
||||
$arr
|
||||
);
|
||||
// mysqli_affected_rows returns 0 if rows were matched but not changed.
|
||||
// mysqli_info returns rows matched
|
||||
return $arr[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
public function matchedRows()
|
||||
{
|
||||
preg_match(
|
||||
"/^[^0-9]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)/",
|
||||
mysqli_info($this->db_conn),
|
||||
$arr
|
||||
);
|
||||
// mysqli_affected_rows returns 0 if rows were matched but not changed.
|
||||
// mysqli_info returns rows matched
|
||||
return $arr[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
public function escapeString($string)
|
||||
{
|
||||
return mysqli_escape_string($this->db_conn, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
public function limit($start, $offset)
|
||||
{
|
||||
return $start . ', ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
public function limitSql($limitstring)
|
||||
{
|
||||
return ' LIMIT ' . $limitstring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if (isset($this->db_conn)) {
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
$function = 'mysqli_connect';
|
||||
|
||||
$connparts = explode(':', $this->db_hostname);
|
||||
if (!empty($connparts[1])) {
|
||||
// A "hostname:port" connection was specified
|
||||
$this->db_conn = $function($connparts[0], $this->db_username, $this->db_password, $this->db_name, $connparts[1]);
|
||||
} else {
|
||||
// Connect with default ports
|
||||
$this->db_conn = $function($connparts[0], $this->db_username, $this->db_password);
|
||||
}
|
||||
mysqli_select_db($this->db_conn, $this->db_name);
|
||||
$this->reconnect();
|
||||
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
public function reconnect()
|
||||
{
|
||||
$use_charset = '';
|
||||
if (isset($this->serendipity['dbCharset']) && !empty($this->serendipity['dbCharset'])) {
|
||||
$use_charset = $this->serendipity['dbCharset'];
|
||||
if (!defined('SQL_CHARSET_INIT')) { define('SQL_CHARSET_INIT', true); }
|
||||
} elseif (defined('SQL_CHARSET') && $this->serendipity['dbNames'] && !defined('SQL_CHARSET_INIT')) {
|
||||
$use_charset = SQL_CHARSET;
|
||||
}
|
||||
|
||||
if ($use_charset != '') {
|
||||
mysqli_set_charset($this->db_conn, $use_charset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipity query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
public function schemaImport($query)
|
||||
{
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}',
|
||||
'{UNSIGNED}', '{FULLTEXT}', '{FULLTEXT_MYSQL}', '{BOOLEAN}', '{TEXT}');
|
||||
static $replace = array('int(11) not null auto_increment', 'primary key',
|
||||
'unsigned' , 'FULLTEXT', 'FULLTEXT', 'enum (\'true\', \'false\') NOT NULL default \'true\'', 'LONGTEXT');
|
||||
|
||||
$search[] = '{UTF_8}';
|
||||
if ( $_POST['charset'] == 'UTF-8/' ||
|
||||
$this->serendipity['charset'] == 'UTF-8/' ||
|
||||
$this->serendipity['POST']['charset'] == 'UTF-8/' ||
|
||||
LANG_CHARSET == 'UTF-8' ) {
|
||||
if ($this->isUtf8mb4Ready()) {
|
||||
$replace[] = 'ROW_FORMAT=DYNAMIC /*!40100 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */';
|
||||
} else {
|
||||
# in old versions we stick to the three byte pseudo utf8 to not trip
|
||||
# over the max index restriction of 1000 bytes
|
||||
$replace[] = '/*!40100 CHARACTER SET utf8 COLLATE utf8_unicode_ci */';
|
||||
}
|
||||
} else {
|
||||
$replace[] = '';
|
||||
}
|
||||
|
||||
if ($this->isUtf8mb4Ready()) {
|
||||
# InnoDB enables us to use utf8mb4 with the higher max index size
|
||||
$this->query("SET storage_engine=INNODB");
|
||||
} else {
|
||||
# Before 5.6.4/10.0.5 InnoDB did not support fulltext indexes, which we use,
|
||||
# thus we stay with MyISAM here
|
||||
$this->query("SET storage_engine=MYISAM");
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return $this->query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return $this->query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we think that it is safe to ugprade to utf8mb4. This checks version numbers and applied settings.
|
||||
* Depending on the version of mariadb/mysql we need to check either one or three settings. We check for
|
||||
* innodb being available with fulltext index and large index support, so that our database scheme can work
|
||||
*
|
||||
* @return boolean Whether the database could support utf8mb4
|
||||
*/
|
||||
protected function isUtf8mb4Ready()
|
||||
{
|
||||
$mysql_version = mysqli_get_server_info($this->db_conn);
|
||||
$maria = false;
|
||||
if (strpos($mysql_version, 'MariaDB') !== false) {
|
||||
$maria = true;
|
||||
}
|
||||
if ($maria) {
|
||||
# maria trips up our version detection scheme by prepending 5.5.5 to newer versions
|
||||
$mysql_version = str_replace('5.5.5-', '', $mysql_version);
|
||||
}
|
||||
|
||||
if ($maria) {
|
||||
# see https://mariadb.com/kb/en/innodb-file-format/ for when barracuda is available, and when it's the only option
|
||||
# see https://docs.nextcloud.com/server/15/admin_manual/configuration_database/mysql_4byte_support.html for which
|
||||
# variables we have to check to assume utf8mb4 it can work (with the large indexes we need)
|
||||
|
||||
if (version_compare($mysql_version, '10.3.1', '>=')) {
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_file_per_table
|
||||
$rows = $this->query("SHOW VARIABLES LIKE 'innodb_file_per_table'");
|
||||
try {
|
||||
return $rows[0][1] == 'ON';
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
# see https://mariadb.com/kb/en/full-text-index-overview/. We need 10.0.5 to have fulltext indexes with innodb
|
||||
if (version_compare($mysql_version, '10.0.5', '>=')) {
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_file_per_table
|
||||
$rows = $this->query("SHOW VARIABLES LIKE 'innodb_file_per_table'");
|
||||
try {
|
||||
if ($rows[0][1] != 'ON') {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_file_format
|
||||
$rows = $this->query("SHOW VARIABLES LIKE 'innodb_file_format'");
|
||||
try {
|
||||
if ($rows[0][1] != 'barracuda') {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_large_prefix
|
||||
$rows = $this->query("SHOW VARIABLES LIKE 'innodb_large_prefix'");
|
||||
try {
|
||||
if ($rows[0][1] != 'ON') {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false; # version too old
|
||||
}
|
||||
|
||||
# now we know it is not mariadb, but "real" mysql
|
||||
|
||||
# These versions might need to be changed based on testing feedback
|
||||
if (version_compare($mysql_version, '8.0.0', '>=')) {
|
||||
$rows = $this->query("SHOW VARIABLES LIKE 'innodb_file_per_table'");
|
||||
try {
|
||||
return $rows[0][1] == 'ON';
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
# see https://dev.mysql.com/doc/refman/5.6/en/innodb-fulltext-index.html. We need 5.6 for fulltext indexes
|
||||
if (version_compare($mysql_version, '5.6', '>=')) {
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_file_per_table
|
||||
$rows = $this->query("SHOW VARIABLES LIKE 'innodb_file_per_table'");
|
||||
try {
|
||||
if ($rows[0][1] != 'ON') {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_file_format
|
||||
$rows = $this->query("SHOW VARIABLES LIKE 'innodb_file_format'");
|
||||
try {
|
||||
if ($rows[0][1] != 'barracuda') {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# see https://mariadb.com/kb/en/innodb-system-variables/#innodb_large_prefix
|
||||
$rows = $this->query("SHOW VARIABLES LIKE 'innodb_large_prefix'");
|
||||
try {
|
||||
if ($rows[0][1] != 'ON') {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; # version too old
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
public function probe($hash, &$errs)
|
||||
{
|
||||
if (!function_exists('mysqli_connect')) {
|
||||
$errs[] = 'No mySQLi extension found. Please check your webserver installation or contact your systems administrator regarding this problem.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$function = 'mysqli_connect';
|
||||
$connparts = explode(':', $hash['dbHost']);
|
||||
if (!empty($connparts[1])) {
|
||||
// A "hostname:port" connection was specified
|
||||
$c = @$function($connparts[0], $hash['dbUser'], $hash['dbPass'], $hash['dbName'], $connparts[1]);
|
||||
} else {
|
||||
// Connect with default ports
|
||||
$c = @$function($connparts[0], $hash['dbUser'], $hash['dbPass']);
|
||||
}
|
||||
|
||||
if (!$c) {
|
||||
$errs[] = 'Could not connect to database; check your settings.';
|
||||
$errs[] = 'The mySQL error was: ' . serendipity_specialchars(mysqli_connect_error());
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db_conn = $c;
|
||||
|
||||
if ( !@mysqli_select_db($c, $hash['dbName']) ) {
|
||||
$errs[] = 'The database you specified does not exist.';
|
||||
$errs[] = 'The mySQL error was: ' . serendipity_specialchars(mysqli_error($c));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
public function concat($string)
|
||||
{
|
||||
return 'concat(' . $string . ')';
|
||||
}
|
||||
}
|
342
lib/Serendipity/Database/PdoPostgresDatabase.php
Normal file
342
lib/Serendipity/Database/PdoPostgresDatabase.php
Normal file
@ -0,0 +1,342 @@
|
||||
<?php
|
||||
|
||||
// Serendipity
|
||||
// See LICENSE file for license information.
|
||||
|
||||
namespace Serendipity\Database;
|
||||
|
||||
use Serendipity\Database\DbAbstract;
|
||||
use PDO;
|
||||
|
||||
class PdoPostgresDatabase extends DbAbstract
|
||||
{
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->db_conn->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
public function endTransaction($commit)
|
||||
{
|
||||
if ($commit) {
|
||||
$this->db_conn->commit();
|
||||
} else {
|
||||
$this->db_conn->rollback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
public function inSql($col, &$search_ids, $type = ' OR ')
|
||||
{
|
||||
return $col . " IN (" . implode(', ', $search_ids) . ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
$host = $port = '';
|
||||
if (strlen($this->db_hostname)) {
|
||||
if (false !== strstr($this->db_hostname, ':')) {
|
||||
$tmp = explode(':', $this->db_hostname);
|
||||
$host = "host={$tmp[0]};";
|
||||
$port = "port={$tmp[1]};";
|
||||
} else {
|
||||
$host = "host={$this->db_hostname};";
|
||||
}
|
||||
}
|
||||
|
||||
$this->db_conn = new \PDO(
|
||||
sprintf(
|
||||
'pgsql:%sdbname=%s',
|
||||
"$host$port",
|
||||
$this->db_name
|
||||
),
|
||||
$this->db_username,
|
||||
$this->db_password
|
||||
);
|
||||
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
public function escapeString($string)
|
||||
{
|
||||
return substr($this->db_conn->quote($string), 1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
public function limit($start, $offset)
|
||||
{
|
||||
return $offset . ', ' . $start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
public function limitSql($limitstring)
|
||||
{
|
||||
$limit_split = explode(',', $limitstring);
|
||||
if (count($limit_split) > 1) {
|
||||
$limit = ' LIMIT ' . $limit_split[0] . ' OFFSET ' . $limit_split[1];
|
||||
} else {
|
||||
$limit = ' LIMIT ' . $limit_split[0];
|
||||
}
|
||||
return $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
public function affectedRows()
|
||||
{
|
||||
return $this->serendipity['dbSth']->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
public function updatedRows()
|
||||
{
|
||||
// it is unknown whether pg_affected_rows returns number of rows
|
||||
// UPDATED or MATCHED on an UPDATE statement.
|
||||
return $this->serendipity['dbSth']->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
public function matchedRows()
|
||||
{
|
||||
// it is unknown whether pg_affected_rows returns number of rows
|
||||
// UPDATED or MATCHED on an UPDATE statement.
|
||||
return $this->serendipity['dbSth']->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @param string Name of the table to get a INSERT ID for
|
||||
* @param string Name of the column to get a INSERT ID for
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
public function insertId($table = '', $id = '')
|
||||
{
|
||||
if (empty($table) || empty($id)) {
|
||||
// BC - will/should never be called with empty parameters!
|
||||
return $this->db_conn->lastInsertId();
|
||||
} else {
|
||||
$query = "SELECT currval('{$this->db_prefix}{$table}_{$id}_seq'::text) AS {$id}";
|
||||
$res = $this->db_conn->prepare($query);
|
||||
$res->execute();
|
||||
foreach ($res->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
return $row[$id];
|
||||
}
|
||||
return $this->db_conn->lastInsertId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
public function &query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
$type_map = array(
|
||||
'assoc' => PDO::FETCH_ASSOC,
|
||||
'num' => PDO::FETCH_NUM,
|
||||
'both' => PDO::FETCH_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
if (!$expectError && ($reportErr || !$this->serendipity['production'])) {
|
||||
$this->serendipity['dbSth'] = $this->db_conn->prepare($sql);
|
||||
} else {
|
||||
$this->serendipity['dbSth'] = $this->db_conn->prepare($sql);
|
||||
}
|
||||
|
||||
if (!$this->serendipity['dbSth']) {
|
||||
if (!$expectError && !$this->serendipity['production']) {
|
||||
print "<span class='msg_error'>Error in $sql</span>";
|
||||
print $this->db_conn->errorInfo() . "<BR/>\n";
|
||||
if (function_exists('debug_backtrace')) {
|
||||
highlight_string(var_export(debug_backtrace(), 1));
|
||||
}
|
||||
print "<pre>$sql</pre>\n";
|
||||
}
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
$this->serendipity['dbSth']->execute();
|
||||
|
||||
if ($this->serendipity['dbSth'] === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
$result_type = $type_map[$result_type];
|
||||
|
||||
$n = 0;
|
||||
|
||||
$rows = array();
|
||||
foreach ($this->serendipity['dbSth']->fetchAll($result_type) as $row) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
if (count($rows) == 0) {
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
}
|
||||
if (count($rows) == 1 && $single) {
|
||||
return $rows[0];
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipity query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
public function schemaImport($query)
|
||||
{
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}',
|
||||
'{FULLTEXT}', '{BOOLEAN}', 'int(1)', 'int(10)', 'int(11)', 'int(4)', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('SERIAL', 'primary key', '', '', 'BOOLEAN NOT NULL', 'int2',
|
||||
'int4', 'int4', 'int4', '', 'text');
|
||||
|
||||
if (stristr($query, '{FULLTEXT_MYSQL}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return $this->query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return $this->query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
public function probe($hash, &$errs)
|
||||
{
|
||||
if (!in_array('pgsql', PDO::getAvailableDrivers())) {
|
||||
$errs[] = 'PDO_PGSQL driver not avialable';
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db_conn = new PDO(
|
||||
sprintf(
|
||||
'pgsql:%sdbname=%s',
|
||||
strlen($hash['dbHost']) ? ('host=' . $hash['dbHost'] . ';') : '',
|
||||
$hash['dbName']
|
||||
),
|
||||
$hash['dbUser'],
|
||||
$hash['dbPass']
|
||||
);
|
||||
|
||||
if (!$this->db_conn) {
|
||||
$errs[] = 'Could not connect to database; check your settings.';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
public function concat($string)
|
||||
{
|
||||
return '(' . str_replace(', ', '||', $string) . ')';
|
||||
}
|
||||
}
|
368
lib/Serendipity/Database/PdoSqliteDatabase.php
Normal file
368
lib/Serendipity/Database/PdoSqliteDatabase.php
Normal file
@ -0,0 +1,368 @@
|
||||
<?php
|
||||
|
||||
// Serendipity
|
||||
// See LICENSE file for license information.
|
||||
|
||||
namespace Serendipity\Database;
|
||||
|
||||
use Serendipity\Database\DbAbstract;
|
||||
use PDO;
|
||||
|
||||
class PdoSqliteDatabase extends DbAbstract
|
||||
{
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
$this->db_conn = new PDO(
|
||||
'sqlite:' . (defined('S9Y_DATA_PATH') ? S9Y_DATA_PATH : $this->serendipity['serendipityPath']) . $this->db_name . '.db'
|
||||
);
|
||||
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
// TODO: Replace with proper Logging object
|
||||
public function logmsg($msgstr)
|
||||
{
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] ' . $msgstr . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse result arrays into expected format for further operations
|
||||
*
|
||||
* SQLite does not support to return "e.entryid" within a $row['entryid'] return.
|
||||
* So this function manually iterate through all result rows and rewrites 'X.yyyy' to 'yyyy'.
|
||||
* Yeah. This sucks. Don't tell me!
|
||||
*
|
||||
* @access private
|
||||
* @param resource The row resource handle
|
||||
* @param int Bitmask to tell whether to fetch numerical/associative arrays
|
||||
* @return array Propper array containing the resource results
|
||||
*/
|
||||
protected function fetchArray($row, $type = PDO::FETCH_ASSOC)
|
||||
{
|
||||
static $search = array('%00', '%25');
|
||||
static $replace = array("\x00", '%');
|
||||
|
||||
if (!is_array($row)) {
|
||||
return $row;
|
||||
}
|
||||
|
||||
/* strip any slashes, correct fieldname */
|
||||
$newrow = array();
|
||||
foreach ($row as $i => $v) {
|
||||
// TODO: If a query of the format 'SELECT a.id, b.text FROM table' is used,
|
||||
// the sqlite extension will give us key indizes 'a.id' and 'b.text'
|
||||
// instead of just 'id' and 'text' like in mysql/postgresql extension.
|
||||
// To fix that, we use a preg-regex; but that is quite performance costy.
|
||||
// Either we always need to use 'SELECT a.id AS id, b.text AS text' in query,
|
||||
// or the sqlite extension may get fixed. :-)
|
||||
$newrow[preg_replace('@^.+\.(.*)@', '\1', $i)] = str_replace($search, $replace, $v);
|
||||
}
|
||||
|
||||
return $newrow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function beginTransaction(){
|
||||
$this->db_conn->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
public function endTransaction(bool $commit)
|
||||
{
|
||||
if ($commit){
|
||||
$this->db_conn->commit();
|
||||
} else {
|
||||
$this->db_conn->rollback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
public function inSql($col, &$searchIds, $type = ' OR ')
|
||||
{
|
||||
$sql = array();
|
||||
if (!is_array($searchIds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($searchIds as $id) {
|
||||
$sql[] = $col . ' = ' . $id;
|
||||
}
|
||||
|
||||
$cond = '(' . implode($type, $sql) . ')';
|
||||
return $cond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
public function escapeString($string)
|
||||
{
|
||||
return substr($this->db_conn->quote($string), 1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
public function limit($start, $offset)
|
||||
{
|
||||
return $start . ', ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
public function limitSql($limitstring)
|
||||
{
|
||||
return ' LIMIT ' . $limitstring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
public function affectedRows()
|
||||
{
|
||||
// FIXME: Cache statement handler in this class?
|
||||
return $this->serendipity['dbSth']->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
public function updatedRows()
|
||||
{
|
||||
// it is unknown whether pg_affected_rows returns number of rows
|
||||
// UPDATED or MATCHED on an UPDATE statement.
|
||||
// FIXME: Cache statement handler in this class?
|
||||
return $this->serendipity['dbSth']->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
public function matchedRows()
|
||||
{
|
||||
// it is unknown whether pg_affected_rows returns number of rows
|
||||
// UPDATED or MATCHED on an UPDATE statement.
|
||||
// FIXME: Cache statement handler in this class?
|
||||
return $this->serendipity['dbSth']->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @param string Name of the table to get a INSERT ID for
|
||||
* @param string Name of the column to get a INSERT ID for
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
public function insertId($table = '', $id = '')
|
||||
{
|
||||
return $this->db_conn->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
public function &query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
$type_map = array(
|
||||
'assoc' => PDO::FETCH_ASSOC,
|
||||
'num' => PDO::FETCH_NUM,
|
||||
'both' => PDO::FETCH_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
//serendipity_db_logmsg('SQLQUERY: ' . $sql);
|
||||
if (!$expectError && ($reportErr || !$this->serendipity['production'])) {
|
||||
$this->serendipity['dbSth'] = $this->db_conn->prepare($sql);
|
||||
} else {
|
||||
$this->serendipity['dbSth'] = $this->db_conn->prepare($sql);
|
||||
}
|
||||
|
||||
if (!$this->serendipity['dbSth']) {
|
||||
if (!$expectError && !$this->serendipity['production']) {
|
||||
print "<span class='msg_error'>Error in $sql</span>";
|
||||
print $this->db_conn->errorInfo() . "<BR/>\n";
|
||||
if (function_exists('debug_backtrace')) {
|
||||
highlight_string(var_export(debug_backtrace(), 1));
|
||||
}
|
||||
print "<pre>$sql</pre>";
|
||||
}
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
$this->serendipity['dbSth']->execute();
|
||||
|
||||
if ($this->serendipity['dbSth'] === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
$result_type = $type_map[$result_type];
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($this->serendipity['dbSth']->fetchAll($result_type) as $row) {
|
||||
$row = $this->fetchArray($row, $result_type);
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
//$this->logmsg('SQLRESULT: ' . print_r($rows,true));
|
||||
|
||||
if (count($rows) == 0) {
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
}
|
||||
if (count($rows) == 1 && $single) {
|
||||
return $rows[0];
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipity query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
public function schemaImport($query)
|
||||
{
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('INTEGER AUTOINCREMENT', 'PRIMARY KEY', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
|
||||
|
||||
if (stristr($query, '{FULLTEXT_MYSQL}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
$query = str_replace('INTEGER AUTOINCREMENT PRIMARY KEY', 'INTEGER PRIMARY KEY AUTOINCREMENT', $query);
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return $this->query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return $this->query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
public function probe($hash, &$errs)
|
||||
{
|
||||
if (!in_array('sqlite', PDO::getAvailableDrivers())) {
|
||||
$errs[] = 'PDO_SQLITE driver not available';
|
||||
return false;
|
||||
}
|
||||
|
||||
$dbName = (isset($hash['sqlitedbName']) ? $hash['sqlitedbName'] : $hash['dbName']);
|
||||
if (defined('S9Y_DATA_PATH')) {
|
||||
// Shared installations!
|
||||
$dbfile = S9Y_DATA_PATH . $dbName . '.db';
|
||||
} else {
|
||||
$dbfile = $this->serendipity['serendipityPath'] . $dbName . '.db';
|
||||
}
|
||||
|
||||
$this->db_conn = new PDO(
|
||||
'sqlite:' . $dbfile
|
||||
);
|
||||
|
||||
if (!$this->db_conn) {
|
||||
$errs[] = "Unable to open \"$dbfile\" - check permissions (directory needs to be writeable for webserver)!";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
public function concat($string)
|
||||
{
|
||||
return 'concat(' . $string . ')';
|
||||
}
|
||||
}
|
356
lib/Serendipity/Database/PostgresDatabase.php
Normal file
356
lib/Serendipity/Database/PostgresDatabase.php
Normal file
@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
// Serendipity
|
||||
// See LICENSE file for license information.
|
||||
|
||||
namespace Serendipity\Database;
|
||||
|
||||
use Serendipity\Database\DbAbstract;
|
||||
|
||||
class PostgresDatabase extends DbAbstract
|
||||
{
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->query('begin work');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
public function endTransaction($commit)
|
||||
{
|
||||
if ($commit) {
|
||||
$this->query('commit');
|
||||
} else {
|
||||
$this->query('rollback');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
public function inSql($col, &$search_ids, $type = ' OR ')
|
||||
{
|
||||
return $col . " IN (" . implode(', ', $search_ids) . ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if (isset($this->serendipity['dbPersistent']) && $this->serendipity['dbPersistent']) {
|
||||
$function = 'pg_pconnect';
|
||||
} else {
|
||||
$function = 'pg_connect';
|
||||
}
|
||||
|
||||
$host = $port = '';
|
||||
if (strlen($this->db_hostname)) {
|
||||
if (false !== strstr($this->db_hostname, ':')) {
|
||||
$tmp = explode(':', $this->db_hostname);
|
||||
$host = "host={$tmp[0]} ";
|
||||
$port = "port={$tmp[1]} ";
|
||||
} else {
|
||||
$host = "host={$this->db_hostname} ";
|
||||
}
|
||||
}
|
||||
|
||||
$this->db_conn = $function(
|
||||
sprintf(
|
||||
'%sdbname=%s user=%s password=%s',
|
||||
"$host$port",
|
||||
$this->db_name,
|
||||
$this->db_username,
|
||||
$this->db_password
|
||||
)
|
||||
);
|
||||
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
public function escapeString($string)
|
||||
{
|
||||
return pg_escape_string($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
public function limit($start, $offset)
|
||||
{
|
||||
return $offset . ', ' . $start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
public function limitSql($limitstring)
|
||||
{
|
||||
$limit_split = explode(',', $limitstring);
|
||||
if (count($limit_split) > 1) {
|
||||
$limit = ' LIMIT ' . $limit_split[0] . ' OFFSET ' . $limit_split[1];
|
||||
} else {
|
||||
$limit = ' LIMIT ' . $limit_split[0];
|
||||
}
|
||||
return $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
public function affectedRows()
|
||||
{
|
||||
return pg_affected_rows($this->serendipity['dbLastResult']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
public function updatedRows()
|
||||
{
|
||||
// it is unknown whether pg_affected_rows returns number of rows
|
||||
// UPDATED or MATCHED on an UPDATE statement.
|
||||
return pg_affected_rows($this->serendipity['dbLastResult']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
public function matchedRows()
|
||||
{
|
||||
// it is unknown whether pg_affected_rows returns number of rows
|
||||
// UPDATED or MATCHED on an UPDATE statement.
|
||||
return pg_affected_rows($this->serendipity['dbLastResult']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @param string Name of the table to get a INSERT ID for
|
||||
* @param string Name of the column to get a INSERT ID for
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
public function insertId($table = '', $id = '')
|
||||
{
|
||||
if (empty($table) || empty($id)) {
|
||||
// BC - will/should never be called with empty parameters!
|
||||
return pg_last_oid($this->serendipity['dbLastResult']);
|
||||
} else {
|
||||
$query = "SELECT currval('{$this->db_prefix}{$table}_{$id}_seq'::text) AS {$id}";
|
||||
$res = pg_query($this->db_conn, $query);
|
||||
if (pg_num_rows($res)) {
|
||||
$insertId = pg_fetch_array($res, 0, PGSQL_ASSOC);
|
||||
return $insertId[$id];
|
||||
} else {
|
||||
return pg_last_oid($this->serendipity['dbLastResult']); // BC - should not happen!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
public function &query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
$type_map = array(
|
||||
'assoc' => PGSQL_ASSOC,
|
||||
'num' => PGSQL_NUM,
|
||||
'both' => PGSQL_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
if (!isset($this->serendipity['dbPgsqlOIDS'])) {
|
||||
$this->serendipity['dbPgsqlOIDS'] = true;
|
||||
@$this->query('SET default_with_oids = true', true, 'both', false, false, false, true);
|
||||
}
|
||||
|
||||
if (!$expectError && ($reportErr || !$this->serendipity['production'])) {
|
||||
$this->serendipity['dbLastResult'] = pg_query($this->db_conn, $sql);
|
||||
} else {
|
||||
$this->serendipity['dbLastResult'] = @pg_query($this->db_conn, $sql);
|
||||
}
|
||||
|
||||
if (!$this->serendipity['dbLastResult']) {
|
||||
if (!$expectError && !$this->serendipity['production']) {
|
||||
print "<span class='msg_error'>Error in $sql</span>";
|
||||
print pg_last_error($this->db_conn) . "<BR/>\n";
|
||||
if (function_exists('debug_backtrace')) {
|
||||
highlight_string(var_export(debug_backtrace(), 1));
|
||||
}
|
||||
print "<pre>$sql</pre>";
|
||||
}
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
if ($this->serendipity['dbLastResult'] === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
$result_type = $type_map[$result_type];
|
||||
|
||||
$n = pg_num_rows($this->serendipity['dbLastResult']);
|
||||
|
||||
switch ($n) {
|
||||
case 0:
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
|
||||
case 1:
|
||||
if ($single) {
|
||||
return pg_fetch_array($this->serendipity['dbLastResult'], 0, $result_type);
|
||||
}
|
||||
default:
|
||||
$rows = array();
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
$row = pg_fetch_array($this->serendipity['dbLastResult'], $i, $result_type);
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = pg_fetch_array($this->serendipity['dbLastResult'], $i, $result_type);
|
||||
}
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipty query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
public function schemaImport($query)
|
||||
{
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}',
|
||||
'{FULLTEXT}', '{BOOLEAN}', 'int(1)', 'int(10)', 'int(11)', 'int(4)', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('SERIAL', 'primary key', '', '', 'BOOLEAN NOT NULL', 'int2',
|
||||
'int4', 'int4', 'int4', '', 'text');
|
||||
|
||||
if (stristr($query, '{FULLTEXT_MYSQL}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return $this->query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return $this->query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
public function probe($hash, &$errs)
|
||||
{
|
||||
if (!function_exists('pg_connect')) {
|
||||
$errs[] = 'No PostgreSQL extension found. Please check your webserver installation or contact your systems administrator regarding this problem.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db_conn = pg_connect(
|
||||
sprintf(
|
||||
'%sdbname=%s user=%s password=%s',
|
||||
strlen($hash['dbHost']) ? ('host=' . $hash['dbHost'] . ' ') : '',
|
||||
$hash['dbName'],
|
||||
$hash['dbUser'],
|
||||
$hash['dbPass']
|
||||
)
|
||||
);
|
||||
|
||||
if (!$this->db_conn) {
|
||||
$errs[] = 'Could not connect to database; check your settings.';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
public function concat($string)
|
||||
{
|
||||
return '(' . str_replace(', ', '||', $string) . ')';
|
||||
}
|
||||
}
|
552
lib/Serendipity/Database/SqlRelayDatabase.php
Normal file
552
lib/Serendipity/Database/SqlRelayDatabase.php
Normal file
@ -0,0 +1,552 @@
|
||||
<?php
|
||||
|
||||
// Serendipity
|
||||
// See LICENSE file for license information.
|
||||
|
||||
namespace Serendipity\Database;
|
||||
|
||||
use Serendipity\Database\DbAbstract;
|
||||
|
||||
class SqlRelayDatabase extends DbAbstract
|
||||
{
|
||||
protected $db_cursor;
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$databaseType = sqlrcon_identify($this->db_conn);
|
||||
switch ($databaseType) {
|
||||
case 'mysql':
|
||||
$sqlstr = 'start transaction';
|
||||
break;
|
||||
|
||||
case 'postgresql':
|
||||
$sqlstr = 'begin work';
|
||||
break;
|
||||
|
||||
case 'sqlite':
|
||||
$sqlstr = 'begin transaction';
|
||||
break;
|
||||
|
||||
default:
|
||||
$sqlstr = 'start transaction';
|
||||
break;
|
||||
}
|
||||
$this->query($sqlstr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
public function endTransaction($commit)
|
||||
{
|
||||
if ($commit) {
|
||||
sqlrcon_commit($this->db_conn);
|
||||
} else {
|
||||
sqlrcon_rollback($this->db_conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
public function inSql($col, &$search_ids, $type = ' OR ')
|
||||
{
|
||||
if (sqlrcon_identify($this->db_conn) == "sqlite") {
|
||||
$sql = array();
|
||||
if (!is_array($search_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($search_ids as $id) {
|
||||
$sql[] = $col . ' = ' . $id;
|
||||
}
|
||||
|
||||
$cond = '(' . implode($type, $sql) . ')';
|
||||
return $cond;
|
||||
} else {
|
||||
return $col . " IN (" . implode(', ', $search_ids) . ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
protected function generateResultset($cursor, $result_type = 'sqlr_BOTH')
|
||||
{
|
||||
$return_row = array();
|
||||
|
||||
for ($r_row = 0, $max_r = sqlrcur_rowCount($cursor) ; $r_row < $max_r ; $r_row++) {
|
||||
for ($r_col=0, $max_rc = sqlrcur_colCount($cursor); $r_col < $max_rc ; $r_col++) {
|
||||
if ($result_type == 'sqlr_ASSOC') {
|
||||
$return_row[sqlrcur_getColumnName($cursor, $r_col)] = sqlrcur_getField($cursor, $r_row, $r_col);
|
||||
} else {
|
||||
$return_row[$r_row][$r_col] = sqlrcur_getField($cursor, $r_row,$r_col);
|
||||
$return_row[$r_row][sqlrcur_getColumnName($cursor, $r_col)] = $return_row[$r_row][$r_col];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $return_row;
|
||||
}
|
||||
|
||||
public function &query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
$type_map = array(
|
||||
'assoc' => sqlr_ASSOC,
|
||||
'num' => sqlr_NUM,
|
||||
'both' => sqlr_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
static $benchmark = false;
|
||||
|
||||
// highlight_string(var_export($sql, 1));
|
||||
|
||||
if (!is_resource($this->db_conn)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cur = sqlrcur_alloc($this->db_conn);
|
||||
$this->db_cursor = $cur;
|
||||
|
||||
if ($benchmark) {
|
||||
$start = microtime_float();
|
||||
}
|
||||
|
||||
if ($expectError) {
|
||||
$c = sqlrcur_sendQuery($cur, $sql);
|
||||
} else {
|
||||
$c = sqlrcur_sendQuery($cur, $sql);
|
||||
}
|
||||
|
||||
if ($benchmark) {
|
||||
$end = microtime_float();
|
||||
|
||||
$cur = sqlrcur_alloc($this->db_conn);
|
||||
$sql_b = "INSERT INTO BLOGLOG (request, timestamp, sql, exec_time, ip) VALUES ('" . $this->escapeString($_SERVER['REQUEST_URI']) . "', NOW(), '" . $this->escapeString($sql) . "', '" . (number_format($end-$start, 10)) . "', '" . $this->escapeString($_SERVER['REMOTE_ADDR']) . "')";
|
||||
$c = sqlrcur_sendQuery($cur, $sql_b);
|
||||
|
||||
$psql = $sql;
|
||||
$psql = preg_replace('@[0-9]{10}@', 'TIMESTAMP', $psql);
|
||||
$sql_U = "UPDATE BLOGLOG_TOTAL SET counter = counter + 1 WHERE sql = '" . $this->escapeString($psql) . "'";
|
||||
$c = sqlrcur_sendQuery($cur, $sql_U);
|
||||
if (sqlrcur_affectedRows() < 1) {
|
||||
$sql_i = "INSERT INTO BLOGLOG_TOTAL (sql, counter) VALUES ('" . $this->escapeString($psql) . "', 1)";
|
||||
$c = sqlrcur_sendQuery($cur,$sql_i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$expectError && sqlrcur_errorMessage($cur) != '') {
|
||||
$msg = '<pre>' . serendipity_specialchars($sql) . '</pre> / ' . serendipity_specialchars(sqlrcur_errorMessage($cur));
|
||||
return $msg;
|
||||
}
|
||||
|
||||
if (!$c) {
|
||||
if (!$expectError && !$this->serendipity['production']) {
|
||||
print '<pre>' . serendipity_specialchars($sql) . '</pre> / ' . serendipity_specialchars(sqlrcur_errorMessage($cur));
|
||||
if (function_exists('debug_backtrace') && $reportErr == true) {
|
||||
highlight_string(var_export(debug_backtrace(), 1));
|
||||
}
|
||||
}
|
||||
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
if ($c === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
$result_type = $type_map[$result_type];
|
||||
|
||||
switch (sqlrcur_rowCount($cur)) {
|
||||
case 0:
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
|
||||
case 1:
|
||||
if ($single) {
|
||||
$row = $this->generateResultset($cur, $result_type);
|
||||
if ($result_type != 'sqlr_ASSOC') {
|
||||
$row=$row[0];
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
default:
|
||||
if ($single) {
|
||||
return $this->generateResultset($cur, $result_type);
|
||||
}
|
||||
|
||||
$row = $this->generateResultset($cur);
|
||||
$rows = array();
|
||||
|
||||
for ($idx=0, $idxc = count($row); $idx < $idxc ; $idx++) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$assocKey] = $row[$idx][$assocKey];
|
||||
} else {
|
||||
$rows[$row[$idx][$assocKey]] = $row[$idx][$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows = $row;
|
||||
}
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @return int Value of the auto-increment column
|
||||
*
|
||||
* If you insert multiple rows using a single INSERT statement,
|
||||
* LAST_INSERT_ID() returns the value generated for the first inserted row only.
|
||||
*/
|
||||
public function insertId($table = '', $id = '')
|
||||
{
|
||||
$databaseType = sqlrcon_identify($this->db_conn);
|
||||
switch ($databaseType) {
|
||||
case 'mysql':
|
||||
$sqlstr='SELECT LAST_INSERT_ID()';
|
||||
break;
|
||||
|
||||
case 'postgresql':
|
||||
$sqlstr = "SELECT currval('{$this->db_prefix}{$table}_{$id}_seq'::text) AS {$id}";
|
||||
break;
|
||||
|
||||
case 'sqlite':
|
||||
$sqlstr = 'SELECT last_insert_rowid()';
|
||||
break;
|
||||
|
||||
default:
|
||||
$sqlstr = 'SELECT LAST_INSERT_ID()';
|
||||
}
|
||||
|
||||
$row = $this->query($sqlstr, true);
|
||||
|
||||
return $row[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
public function affectedRows()
|
||||
{
|
||||
/* int sqlrcur_affectedRows(int sqlrcurref)
|
||||
* Returns the number of rows that were updated, inserted or deleted by the query.
|
||||
* Not all databases support this call.
|
||||
* Don't use it for applications which are designed to be portable across databases.
|
||||
* -1 is returned by databases which don't support this option.
|
||||
*/
|
||||
return sqlrcur_affectedRows($this->db_cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
public function updatedRows()
|
||||
{
|
||||
/*
|
||||
preg_match(
|
||||
"/^[^0-9]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)/",
|
||||
mysql_info(),
|
||||
$arr);
|
||||
// mysql_affected_rows returns 0 if rows were matched but not changed.
|
||||
// mysql_info returns rows matched AND rows changed
|
||||
return $arr[2];
|
||||
*/
|
||||
return $this->affectedRows();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
public function matchedRows()
|
||||
{
|
||||
/*
|
||||
preg_match(
|
||||
"/^[^0-9]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)/",
|
||||
mysql_info(),
|
||||
$arr);
|
||||
// mysql_affected_rows returns 0 if rows were matched but not changed.
|
||||
// mysql_info returns rows matched AND rows changed
|
||||
return $arr[1];
|
||||
*/
|
||||
return $this->affectedRows();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
public function escapeString($str)
|
||||
{
|
||||
static $search = array("\x00", '%', "'", '\"');
|
||||
static $replace = array('%00', '%25', "''", '\\\"');
|
||||
|
||||
$databaseType = sqlrcon_identify($this->db_conn);
|
||||
switch ($databaseType) {
|
||||
case 'mysql':
|
||||
if (function_exists('mysql_real_escape_string')) {
|
||||
$rtnstr = mysql_real_escape_string($str, $this->db_conn);
|
||||
} else {
|
||||
$rtnstr = addslashes($str);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'postgresql':
|
||||
if (function_exists('pg_escape_string')) {
|
||||
$rtnstr = pg_escape_string($str);
|
||||
} else {
|
||||
$rtnstr = addslashes($str);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'sqlite':
|
||||
$rtnstr = str_replace($search, $replace, $str);
|
||||
break;
|
||||
|
||||
default:
|
||||
$rtnstr = addslashes($str);
|
||||
}
|
||||
|
||||
return $rtnstr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
public function limit($start, $offset)
|
||||
{
|
||||
return $start . ', ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
public function limitSql($limitstring)
|
||||
{
|
||||
$databaseType = sqlrcon_identify($this->db_conn);
|
||||
switch ($databaseType) {
|
||||
case "mysql":
|
||||
return ' LIMIT ' . $limitstring;
|
||||
case "postgresql":
|
||||
$limit_split = explode(',', $limitstring);
|
||||
if (count($limit_split) > 1) {
|
||||
$limit = ' LIMIT ' . $limit_split[0] . ' OFFSET ' . $limit_split[1];
|
||||
} else {
|
||||
$limit = ' LIMIT ' . $limit_split[0];
|
||||
}
|
||||
return $limit;
|
||||
default:
|
||||
return ' LIMIT ' . $limitstring;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if (isset($this->db_conn)) {
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
if (isset($this->serendipity['dbPersistent']) && $this->serendipity['dbPersistent']) {
|
||||
$function = 'sqlrcon_alloc';
|
||||
} else {
|
||||
$function = 'sqlrcon_alloc';
|
||||
}
|
||||
|
||||
#$this->db_hostname="localhost:9000"
|
||||
$dbHostPort = explode(":", $this->db_hostname);
|
||||
$this->db_conn = $function($dbHostPort[0], $dbHostPort[1], "", $this->db_username, $this->db_password, 0, 1);
|
||||
sqlrcon_debugOff($this->db_conn);
|
||||
|
||||
if( sqlrcon_identify($this->db_conn) == "mysql") {
|
||||
$this->reconnect();
|
||||
}
|
||||
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
public function reconnect()
|
||||
{
|
||||
if (isset($this->serendipity['dbCharset'])) {
|
||||
$this->query("SET NAMES " . $this->serendipity['dbCharset']);
|
||||
@define('SQL_CHARSET_INIT', true);
|
||||
} elseif (defined('SQL_CHARSET') && $this->serendipity['dbNames'] && !defined('SQL_CHARSET_INIT')) {
|
||||
$this->query("SET NAMES " . SQL_CHARSET);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipity query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
public function schemaImport($query)
|
||||
{
|
||||
$databaseType = sqlrcon_identify($this->db_conn);
|
||||
switch ($databaseType) {
|
||||
case "mysql":
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}',
|
||||
'{UNSIGNED}', '{FULLTEXT}', '{FULLTEXT_MYSQL}', '{BOOLEAN}', '{TEXT}');
|
||||
static $replace = array('int(11) not null auto_increment', 'primary key',
|
||||
'unsigned' , 'FULLTEXT', 'FULLTEXT', 'enum (\'true\', \'false\') NOT NULL default \'true\'', 'LONGTEXT');
|
||||
static $is_utf8 = null;
|
||||
|
||||
if ($is_utf8 === null) {
|
||||
$search[] = '{UTF_8}';
|
||||
if ((isset($_POST['charset']) && $_POST['charset'] == 'UTF-8/') ||
|
||||
$this->serendipity['charset'] == 'UTF-8/' ||
|
||||
$this->serendipity['POST']['charset'] == 'UTF-8/' ||
|
||||
LANG_CHARSET == 'UTF-8' ) {
|
||||
$replace[] = '/*!40100 CHARACTER SET utf8 COLLATE utf8_unicode_ci */';
|
||||
} else {
|
||||
$replace[] = '';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "postgresql":
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}',
|
||||
'{FULLTEXT}', '{FULLTEXT_MYSQL}', '{BOOLEAN}', 'int(1)', 'int(10)', 'int(11)', 'int(4)', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('SERIAL', 'primary key', '',
|
||||
'', '', 'BOOLEAN NOT NULL', 'int2', 'int4', 'int4', 'int4', '', 'text');
|
||||
break;
|
||||
|
||||
case "sqlite":
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{FULLTEXT_MYSQL}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('INTEGER', 'PRIMARY KEY', '', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
|
||||
break;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
|
||||
if ($query{0} == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return $this->query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return $this->query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
public function probe($hash, &$errs)
|
||||
{
|
||||
$c = null;
|
||||
|
||||
if (!function_exists('sqlrcon_alloc')) {
|
||||
$errs[] = 'No sql_relay extension found. Please check your webserver installation or contact your systems administrator regarding this problem.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!($c=@sqlrcon_alloc($hash['dbHost'], '', '', $hash['dbUser'], $hash['dbPass'], 0, 5))) {
|
||||
$errs[] = 'Could not connect to database; check your settings.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db_conn = $c;
|
||||
|
||||
/* which db should be used is defined in sqlrelay.conf */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
public function concat($string)
|
||||
{
|
||||
$databaseType = sqlrcon_identify($this->db_conn);
|
||||
switch ($databaseType) {
|
||||
case 'mysql':
|
||||
return 'concat(' . $string . ')';
|
||||
|
||||
case 'postgresql':
|
||||
return '(' . str_replace(', ', '||', $string) . ')';
|
||||
|
||||
case 'sqlite':
|
||||
return 'concat(' . $string . ')';
|
||||
|
||||
default:
|
||||
return 'concat(' . $string . ')';
|
||||
}
|
||||
}
|
||||
}
|
390
lib/Serendipity/Database/Sqlite3Database.php
Normal file
390
lib/Serendipity/Database/Sqlite3Database.php
Normal file
@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
// Serendipity
|
||||
// See LICENSE file for license information.
|
||||
|
||||
namespace Serendipity\Database;
|
||||
|
||||
use Serendipity\Database\DbAbstract;
|
||||
|
||||
class Sqlite3Database extends DbAbstract
|
||||
{
|
||||
// SQLite3 only fetches by assoc, we will emulate the other result types
|
||||
public const SQLITE3_ASSOC = 0;
|
||||
public const SQLITE3_NUM = 1;
|
||||
public const SQLITE3_BOTH = 2;
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->query('begin transaction');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
public function endTransaction($commit)
|
||||
{
|
||||
if ($commit) {
|
||||
$this->query('commit transaction');
|
||||
} else {
|
||||
$this->query('rollback transaction');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if (isset($this->db_conn)) {
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
// SQLite3 doesn't support persistent connections
|
||||
$this->db_conn = sqlite3_open((defined('S9Y_DATA_PATH') ? S9Y_DATA_PATH : $this->serendipity['serendipityPath']) . $this->db_name . '.db');
|
||||
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
public function escapeString($string)
|
||||
{
|
||||
static $search = array("\x00", '%', "'", '\"');
|
||||
static $replace = array('%00', '%25', "''", '\\\"');
|
||||
|
||||
return str_replace($search, $replace, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
public function affectedRows()
|
||||
{
|
||||
return sqlite3_changes($this->db_conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
public function updatedRows()
|
||||
{
|
||||
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
|
||||
return sqlite3_changes($this->db_conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
public function matchedRows()
|
||||
{
|
||||
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
|
||||
return sqlite3_changes($this->db_conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
public function insertId()
|
||||
{
|
||||
return sqlite3_last_insert_rowid($this->db_conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse result arrays into expected format for further operations
|
||||
*
|
||||
* SQLite does not support to return "e.entryid" within a $row['entryid'] return.
|
||||
* So this function manually iteratse through all result rows and rewrites 'X.yyyy' to 'yyyy'.
|
||||
* Yeah. This sucks. Don't tell me!
|
||||
*
|
||||
* @access private
|
||||
* @param resource The row resource handle
|
||||
* @param int Bitmask to tell whether to fetch numerical/associative arrays
|
||||
* @return array Propper array containing the resource results
|
||||
*/
|
||||
protected function fetchArray($res, $type = self::SQLITE3_BOTH)
|
||||
{
|
||||
static $search = array('%00', '%25');
|
||||
static $replace = array("\x00", '%');
|
||||
|
||||
$row = sqlite3_fetch_array($res);
|
||||
if (!is_array($row)) {
|
||||
return $row;
|
||||
}
|
||||
|
||||
/* strip any slashes, correct fieldname */
|
||||
foreach ($row as $i => $v) {
|
||||
// TODO: If a query of the format 'SELECT a.id, b.text FROM table' is used,
|
||||
// the sqlite extension will give us key indizes 'a.id' and 'b.text'
|
||||
// instead of just 'id' and 'text' like in mysql/postgresql extension.
|
||||
// To fix that, we use a preg-regex; but that is quite performance costy.
|
||||
// Either we always need to use 'SELECT a.id AS id, b.text AS text' in query,
|
||||
// or the sqlite extension may get fixed. :-)
|
||||
$row[preg_replace('@^.+\.(.*)@', '\1', $i)] = str_replace($search, $replace, $v);
|
||||
}
|
||||
|
||||
if ($type == self::SQLITE3_NUM) {
|
||||
$frow = array();
|
||||
} else {
|
||||
$frow = $row;
|
||||
}
|
||||
if ($type != self::SQLITE3_ASSOC) {
|
||||
$i = 0;
|
||||
foreach ($row as $k => $v) {
|
||||
$frow[$i] = $v;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
return $frow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
public function inSql($col, &$search_ids, $type = ' OR ')
|
||||
{
|
||||
$sql = array();
|
||||
if (!is_array($search_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($search_ids as $id) {
|
||||
$sql[] = $col . ' = ' . $id;
|
||||
}
|
||||
|
||||
$cond = '(' . implode($type, $sql) . ')';
|
||||
return $cond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
public function &query($sql, $single = false, $result_type = "both", $reportErr = true, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
$type_map = array(
|
||||
'assoc' => self::SQLITE3_ASSOC,
|
||||
'num' => self::SQLITE3_NUM,
|
||||
'both' => self::SQLITE3_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
static $debug = false;
|
||||
|
||||
if ($debug) {
|
||||
// Open file and write directly. In case of crashes, the pointer needs to be killed.
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE QUERY: ' . $sql . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
if ($reportErr && !$expectError) {
|
||||
$res = sqlite3_query($this->db_conn, $sql);
|
||||
} else {
|
||||
$res = @sqlite3_query($this->db_conn, $sql);
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
if (!$expectError && !$this->serendipity['production']) {
|
||||
var_dump($res);
|
||||
var_dump($sql);
|
||||
$msg = "problem with query";
|
||||
return $msg;
|
||||
}
|
||||
if ($debug) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] [ERROR] ' . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
if ($res === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
|
||||
while (($row = $this->fetchArray($res, $type_map[$result_type]))) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE RESULT: ' . print_r($rows, true). "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
if ($single && count($rows) == 1) {
|
||||
return $rows[0];
|
||||
}
|
||||
|
||||
if (count($rows) == 0) {
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
public function probe($hash, &$errs)
|
||||
{
|
||||
$dbName = (isset($hash['sqlitedbName']) ? $hash['sqlitedbName'] : $hash['dbName']);
|
||||
|
||||
if (!function_exists('sqlite3_open')) {
|
||||
$errs[] = 'SQLite extension not installed. Run "pear install sqlite" on your webserver or contact your systems administrator regarding this problem.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (defined('S9Y_DATA_PATH')) {
|
||||
// Shared installations!
|
||||
$dbfile = S9Y_DATA_PATH . $dbName . '.db';
|
||||
} else {
|
||||
$dbfile = $this->serendipity['serendipityPath'] . $dbName . '.db';
|
||||
}
|
||||
|
||||
$this->db_conn = sqlite3_open($dbfile);
|
||||
|
||||
if ($this->db_conn) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$errs[] = "Unable to open \"$dbfile\" - check permissions (directory needs to be writeable for webserver)!";
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipty query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
public function schemaImport($query)
|
||||
{
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('INTEGER AUTOINCREMENT', 'PRIMARY KEY', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
|
||||
|
||||
if (stristr($query, '{FULLTEXT_MYSQL}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
$query = str_replace('INTEGER AUTOINCREMENT PRIMARY KEY', 'INTEGER PRIMARY KEY AUTOINCREMENT', $query);
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return $this->query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return $this->query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
public function limit($start, $offset)
|
||||
{
|
||||
return $start . ', ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
public function limitSql($limitstring)
|
||||
{
|
||||
return ' LIMIT ' . $limitstring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
public function concat($string)
|
||||
{
|
||||
return 'concat(' . $string . ')';
|
||||
}
|
||||
}
|
404
lib/Serendipity/Database/Sqlite3ooDatabase.php
Normal file
404
lib/Serendipity/Database/Sqlite3ooDatabase.php
Normal file
@ -0,0 +1,404 @@
|
||||
<?php
|
||||
|
||||
// Serendipity
|
||||
// See LICENSE file for license information.
|
||||
|
||||
namespace Serendipity\Database;
|
||||
|
||||
use Serendipity\Database\DbAbstract;
|
||||
|
||||
class Sqlite3ooDatabase extends DbAbstract
|
||||
{
|
||||
// SQLite3 only fetches by assoc, we will emulate the other result types
|
||||
public const SQLITE3_ASSOC = 0;
|
||||
public const SQLITE3_NUM = 1;
|
||||
public const SQLITE3_BOTH = 2;
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->query('begin transaction');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
public function endTransaction($commit)
|
||||
{
|
||||
if ($commit) {
|
||||
$this->query('commit transaction');
|
||||
} else {
|
||||
$this->query('rollback transaction');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if (isset($this->db_conn)) {
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
// SQLite3 doesn't support persistent connections
|
||||
$this->db_conn = new SQLite3((defined('S9Y_DATA_PATH') ? S9Y_DATA_PATH : $this->serendipity['serendipityPath']) . $this->db_name . '.db');
|
||||
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
public function escapeString($string)
|
||||
{
|
||||
static $search = array("\x00", '%', "'", '\"');
|
||||
static $replace = array('%00', '%25', "''", '\\\"');
|
||||
|
||||
return str_replace($search, $replace, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
public function affectedRows()
|
||||
{
|
||||
return $this->db_conn->changes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
public function updatedRows()
|
||||
{
|
||||
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
|
||||
return $this->db_conn->changes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
public function matchedRows()
|
||||
{
|
||||
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
|
||||
return $this->db_conn->changes($this->db_conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
public function insertId()
|
||||
{
|
||||
return $this->db_conn->lastInsertRowID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse result arrays into expected format for further operations
|
||||
*
|
||||
* SQLite does not support to return "e.entryid" within a $row['entryid'] return.
|
||||
* So this function manually iteratse through all result rows and rewrites 'X.yyyy' to 'yyyy'.
|
||||
* Yeah. This sucks. Don't tell me!
|
||||
*
|
||||
* @access private
|
||||
* @param resource The row resource handle
|
||||
* @param int Bitmask to tell whether to fetch numerical/associative arrays
|
||||
* @return array Propper array containing the resource results
|
||||
*/
|
||||
public function sqlite_fetch_array($res, $type = self::SQLITE3_BOTH)
|
||||
{
|
||||
static $search = array('%00', '%25');
|
||||
static $replace = array("\x00", '%');
|
||||
|
||||
try {
|
||||
$row = $res->fetchArray();
|
||||
} catch (Exception $e) {
|
||||
$row = false;
|
||||
echo "SQLITE-EXCEPTION: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
if (!is_array($row)) {
|
||||
return $row;
|
||||
}
|
||||
|
||||
/* strip any slashes, correct fieldname */
|
||||
foreach ($row as $i => $v) {
|
||||
// TODO: If a query of the format 'SELECT a.id, b.text FROM table' is used,
|
||||
// the sqlite extension will give us key indizes 'a.id' and 'b.text'
|
||||
// instead of just 'id' and 'text' like in mysql/postgresql extension.
|
||||
// To fix that, we use a preg-regex; but that is quite performance costy.
|
||||
// Either we always need to use 'SELECT a.id AS id, b.text AS text' in query,
|
||||
// or the sqlite extension may get fixed. :-)
|
||||
$row[preg_replace('@^.+\.(.*)@', '\1', $i)] = str_replace($search, $replace, $v);
|
||||
}
|
||||
|
||||
if ($type == self::SQLITE3_NUM) {
|
||||
$frow = array();
|
||||
} else {
|
||||
$frow = $row;
|
||||
}
|
||||
|
||||
if ($type != self::SQLITE3_ASSOC) {
|
||||
$i = 0;
|
||||
foreach ($row as $k => $v) {
|
||||
$frow[$i] = $v;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
return $frow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
public function inSql($col, &$search_ids, $type = ' OR ')
|
||||
{
|
||||
$sql = array();
|
||||
if (!is_array($search_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($search_ids as $id) {
|
||||
$sql[] = $col . ' = ' . $id;
|
||||
}
|
||||
|
||||
$cond = '(' . implode($type, $sql) . ')';
|
||||
return $cond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
public function &query($sql, $single = false, $result_type = "both", $reportErr = true, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
$type_map = array(
|
||||
'assoc' => self::SQLITE3_ASSOC,
|
||||
'num' => self::SQLITE3_NUM,
|
||||
'both' => self::SQLITE3_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
static $debug = false;
|
||||
|
||||
if ($debug) {
|
||||
// Open file and write directly. In case of crashes, the pointer needs to be killed.
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE QUERY: ' . $sql . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
if ($reportErr && !$expectError) {
|
||||
$res = $this->db_conn->query($sql);
|
||||
} else {
|
||||
$res = @$this->db_conn->query($sql);
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
if (!$expectError && !$this->serendipity['production']) {
|
||||
var_dump($res);
|
||||
var_dump($sql);
|
||||
$msg = "problem with query";
|
||||
return $msg;
|
||||
}
|
||||
if ($debug) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] [ERROR] ' . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
if (!preg_match('@^SELECT@imsU', trim($sql))) {
|
||||
// Everything that is not SELECT will not return rows.
|
||||
// SQLite3 OO will always return an object though.
|
||||
if ($this->db_conn->lastErrorCode() > 0) {
|
||||
echo "SQLITE-ERROR: " . $this->db_conn->lastErrorMsg() . "<br />\n";
|
||||
return $type_map['false'];
|
||||
} else {
|
||||
return $type_map['true'];
|
||||
}
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
|
||||
while (($row = $this->fetchArray($res, $type_map[$result_type]))) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE RESULT: ' . print_r($rows, true). "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
if ($single && count($rows) == 1) {
|
||||
return $rows[0];
|
||||
}
|
||||
|
||||
if (count($rows) == 0) {
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
public function probe($hash, &$errs)
|
||||
{
|
||||
$dbName = (isset($hash['sqlitedbName']) ? $hash['sqlitedbName'] : $hash['dbName']);
|
||||
|
||||
if (!class_exists('SQLite3')) {
|
||||
$errs[] = 'SQLite extension not installed. Available on PHP 5.4+.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (defined('S9Y_DATA_PATH')) {
|
||||
// Shared installations!
|
||||
$dbfile = S9Y_DATA_PATH . $dbName . '.db';
|
||||
} else {
|
||||
$dbfile = $this->serendipity['serendipityPath'] . $dbName . '.db';
|
||||
}
|
||||
|
||||
$this->db_conn = new SQLite3($dbfile);
|
||||
|
||||
if ($this->db_conn) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$errs[] = "Unable to open \"$dbfile\" - check permissions (directory needs to be writeable for webserver)!";
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipty query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
public function schemaImport($query)
|
||||
{
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('INTEGER AUTOINCREMENT', 'PRIMARY KEY', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
|
||||
|
||||
if (stristr($query, '{FULLTEXT_MYSQL}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
$query = str_replace('INTEGER AUTOINCREMENT PRIMARY KEY', 'INTEGER PRIMARY KEY AUTOINCREMENT', $query);
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return $this->query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return $this->query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
public function limit($start, $offset)
|
||||
{
|
||||
return $start . ', ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
public function limitSql($limitstring)
|
||||
{
|
||||
return ' LIMIT ' . $limitstring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
public function concat($string)
|
||||
{
|
||||
return 'concat(' . $string . ')';
|
||||
}
|
||||
}
|
380
lib/Serendipity/Database/SqliteDatabase.php
Normal file
380
lib/Serendipity/Database/SqliteDatabase.php
Normal file
@ -0,0 +1,380 @@
|
||||
<?php
|
||||
|
||||
// Serendipity
|
||||
// See LICENSE file for license information.
|
||||
|
||||
namespace Serendipity\Database;
|
||||
|
||||
use Serendipity\Database\DbAbstract;
|
||||
|
||||
class SqliteDatabase extends DbAbstract
|
||||
{
|
||||
/**
|
||||
* Tells the DB Layer to start a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->query('begin transaction');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the DB Layer to end a DB transaction.
|
||||
*
|
||||
* @access public
|
||||
* @param boolean If true, perform the query. If false, rollback.
|
||||
*/
|
||||
public function endTransaction($commit)
|
||||
{
|
||||
if ($commit) {
|
||||
$this->query('commit transaction');
|
||||
} else {
|
||||
$this->query('rollback transaction');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured Database
|
||||
*
|
||||
* @access public
|
||||
* @return resource connection handle
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if (isset($this->db_conn)) {
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
if (isset($this->serendipity['dbPersistent']) && $this->serendipity['dbPersistent']) {
|
||||
$function = 'sqlite_popen';
|
||||
} else {
|
||||
$function = 'sqlite_open';
|
||||
}
|
||||
|
||||
if (defined('S9Y_DATA_PATH')) {
|
||||
$this->db_conn = $function(S9Y_DATA_PATH . $this->db_name . '.db');
|
||||
} else {
|
||||
$this->db_conn = $function($this->db_name . '.db');
|
||||
}
|
||||
|
||||
return $this->db_conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
|
||||
*
|
||||
* @access public
|
||||
* @param string input string
|
||||
* @return string output string
|
||||
*/
|
||||
public function escapeString($string)
|
||||
{
|
||||
static $search = array("\x00", '%', "'", '\"');
|
||||
static $replace = array('%00', '%25', "''", '\\\"');
|
||||
|
||||
return str_replace($search, $replace, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of affected rows of a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of affected rows
|
||||
*/
|
||||
public function affectedRows()
|
||||
{
|
||||
return sqlite_changes($this->db_conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of updated rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of updated rows
|
||||
*/
|
||||
public function updatedRows()
|
||||
{
|
||||
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
|
||||
return sqlite_changes($this->db_conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matched rows in a SQL query
|
||||
*
|
||||
* @access public
|
||||
* @return int Number of matched rows
|
||||
*/
|
||||
public function matchedRows()
|
||||
{
|
||||
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
|
||||
return sqlite_changes($this->db_conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
|
||||
*
|
||||
* @access public
|
||||
* @return int Value of the auto-increment column
|
||||
*/
|
||||
public function insertId()
|
||||
{
|
||||
return sqlite_last_insert_rowid($this->db_conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse result arrays into expected format for further operations
|
||||
*
|
||||
* SQLite does not support to return "e.entryid" within a $row['entryid'] return.
|
||||
* So this function manually iteratse through all result rows and rewrites 'X.yyyy' to 'yyyy'.
|
||||
* Yeah. This sucks. Don't tell me!
|
||||
*
|
||||
* @access private
|
||||
* @param resource The row resource handle
|
||||
* @param int Bitmask to tell whether to fetch numerical/associative arrays
|
||||
* @return array Propper array containing the resource results
|
||||
*/
|
||||
protected function fetchArray($res, $type = SQLITE_BOTH)
|
||||
{
|
||||
static $search = array('%00', '%25');
|
||||
static $replace = array("\x00", '%');
|
||||
|
||||
$row = sqlite_fetch_array($res, $type);
|
||||
if (!is_array($row)) {
|
||||
return $row;
|
||||
}
|
||||
|
||||
/* strip any slashes, correct fieldname */
|
||||
foreach ($row as $i => $v) {
|
||||
// TODO: If a query of the format 'SELECT a.id, b.text FROM table' is used,
|
||||
// the sqlite extension will give us key indizes 'a.id' and 'b.text'
|
||||
// instead of just 'id' and 'text' like in mysql/postgresql extension.
|
||||
// To fix that, we use a preg-regex; but that is quite performance costy.
|
||||
// Either we always need to use 'SELECT a.id AS id, b.text AS text' in query,
|
||||
// or the sqlite extension may get fixed. :-)
|
||||
$row[preg_replace('@^.+\.(.*)@', '\1', $i)] = str_replace($search, $replace, $v);
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble and return SQL condition for a "IN (...)" clause
|
||||
*
|
||||
* @access public
|
||||
* @param string table column name
|
||||
* @param array referenced array of values to search for in the "IN (...)" clause
|
||||
* @param string condition of how to associate the different input values of the $search_ids parameter
|
||||
* @return string resulting SQL string
|
||||
*/
|
||||
public function inSql($col, &$search_ids, $type = ' OR ')
|
||||
{
|
||||
$sql = array();
|
||||
if (!is_array($search_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($search_ids AS $id) {
|
||||
$sql[] = $col . ' = ' . $id;
|
||||
}
|
||||
|
||||
$cond = '(' . implode($type, $sql) . ')';
|
||||
return $cond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a DB Layer SQL query.
|
||||
*
|
||||
* This function returns values dependin on the input parameters and the result of the query.
|
||||
* It can return:
|
||||
* false or a string if there was an error (depends on $expectError),
|
||||
* true if the query succeeded but did not generate any rows
|
||||
* array of field values if it returned a single row and $single is true
|
||||
* array of array of field values if it returned row(s) [stacked array]
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query to execute
|
||||
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
|
||||
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
|
||||
* @param boolean If true, errors will be reported. If false, errors will be ignored.
|
||||
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
|
||||
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
|
||||
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignored on DUPLICATE INDEX queries and the likes)
|
||||
* @return mixed Returns the result of the SQL query, depending on the input parameters
|
||||
*/
|
||||
public function &query($sql, $single = false, $result_type = "both", $reportErr = true, $assocKey = false, $assocVal = false, $expectError = false)
|
||||
{
|
||||
$type_map = array(
|
||||
'assoc' => SQLITE_ASSOC,
|
||||
'num' => SQLITE_NUM,
|
||||
'both' => SQLITE_BOTH,
|
||||
'true' => true,
|
||||
'false' => false
|
||||
);
|
||||
|
||||
static $debug = false;
|
||||
|
||||
if ($debug) {
|
||||
// Open file and write directly. In case of crashes, the pointer needs to be killed.
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE QUERY: ' . $sql . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
if ($reportErr && !$expectError) {
|
||||
$res = sqlite_query($sql, $this->db_conn);
|
||||
} else {
|
||||
$res = @sqlite_query($sql, $this->db_conn);
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
if (!$expectError && !$this->serendipity['production']) {
|
||||
var_dump($res);
|
||||
var_dump($sql);
|
||||
$msg = "problem with query";
|
||||
return $msg;
|
||||
}
|
||||
if ($debug) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] [ERROR] ' . "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
return $type_map['false'];
|
||||
}
|
||||
|
||||
if ($res === true) {
|
||||
return $type_map['true'];
|
||||
}
|
||||
|
||||
if (sqlite_num_rows($res) == 0) {
|
||||
if ($single) {
|
||||
return $type_map['false'];
|
||||
}
|
||||
return $type_map['true'];
|
||||
} else {
|
||||
$rows = array();
|
||||
|
||||
while (($row = $this->fetchArray($res, $type_map[$result_type]))) {
|
||||
if (!empty($assocKey)) {
|
||||
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
|
||||
if (empty($assocVal)) {
|
||||
$rows[$row[$assocKey]] = $row;
|
||||
} else {
|
||||
$rows[$row[$assocKey]] = $row[$assocVal];
|
||||
}
|
||||
} else {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
$fp = @fopen('sqlite.log', 'a');
|
||||
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE RESULT: ' . print_r($rows, true). "\n\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
if ($single && count($rows) == 1) {
|
||||
return $rows[0];
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the configured Database (during installation)
|
||||
*
|
||||
* @access public
|
||||
* @param array input configuration array, holding the connection info
|
||||
* @param array referenced array which holds the errors that might be encountered
|
||||
* @return boolean return true on success, false on error
|
||||
*/
|
||||
public function probe($hash, &$errs)
|
||||
{
|
||||
$dbName = (isset($hash['sqlitedbName']) ? $hash['sqlitedbName'] : $hash['dbName']);
|
||||
|
||||
if (!function_exists('sqlite_open')) {
|
||||
$errs[] = 'SQLite extension not installed. Run "pear install sqlite" on your webserver or contact your systems administrator regarding this problem.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (defined('S9Y_DATA_PATH')) {
|
||||
// Shared installations!
|
||||
$dbfile = S9Y_DATA_PATH . $dbName . '.db';
|
||||
} else {
|
||||
$dbfile = $this->serendipity['serendipityPath'] . $dbName . '.db';
|
||||
}
|
||||
|
||||
$this->db_conn = sqlite_open($dbfile);
|
||||
|
||||
if ($this->db_conn) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$errs[] = "Unable to open \"$dbfile\" - check permissions (directory needs to be writeable for webserver)!";
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a Serendipity query input to fully valid SQL. Replaces certain "template" variables.
|
||||
*
|
||||
* @access public
|
||||
* @param string SQL query with template variables to convert
|
||||
* @return resource SQL resource handle of the executed query
|
||||
*/
|
||||
public function schemaImport($query)
|
||||
{
|
||||
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
|
||||
static $replace = array('INTEGER', 'PRIMARY KEY', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
|
||||
|
||||
if (stristr($query, '{FULLTEXT_MYSQL}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = trim(str_replace($search, $replace, $query));
|
||||
if ($query[0] == '@') {
|
||||
// Errors are expected to happen (like duplicate index creation)
|
||||
return $this->query(substr($query, 1), false, 'both', false, false, false, true);
|
||||
} else {
|
||||
return $this->query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
|
||||
*
|
||||
* @access public
|
||||
* @param int Number of the first row to return data from
|
||||
* @param int Number of rows to return
|
||||
* @return string SQL string to pass to a LIMIT statement
|
||||
*/
|
||||
public function limit($start, $offset)
|
||||
{
|
||||
return $start . ', ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
|
||||
*
|
||||
* @access public
|
||||
* @param SQL string of a LIMIT option
|
||||
* @return SQL string containing a full LIMIT statement
|
||||
*/
|
||||
public function limitSql($limitstring)
|
||||
{
|
||||
return ' LIMIT ' . $limitstring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL code used for concatenating strings
|
||||
*
|
||||
* @access public
|
||||
* @param string Input string/column to concatenate
|
||||
* @return string SQL parameter
|
||||
*/
|
||||
public function concat($string)
|
||||
{
|
||||
return 'concat(' . $string . ')';
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user