Import Ruty
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Database wrapper class that implements PHP PDO functions |
|
||||
| for MS SQL Server database |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/**
|
||||
* Database independent query interface
|
||||
* This is a wrapper for the PHP PDO
|
||||
*
|
||||
* @package Framework
|
||||
* @subpackage Database
|
||||
*/
|
||||
class rcube_db_mssql extends rcube_db
|
||||
{
|
||||
public $db_provider = 'mssql';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($db_dsnw, $db_dsnr = '', $pconn = false)
|
||||
{
|
||||
parent::__construct($db_dsnw, $db_dsnr, $pconn);
|
||||
|
||||
$this->options['identifier_start'] = '[';
|
||||
$this->options['identifier_end'] = ']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Driver-specific configuration of database connection
|
||||
*
|
||||
* @param array $dsn DSN for DB connections
|
||||
* @param PDO $dbh Connection handler
|
||||
*/
|
||||
protected function conn_configure($dsn, $dbh)
|
||||
{
|
||||
// Set date format in case of non-default language (#1488918)
|
||||
$dbh->query("SET DATEFORMAT ymd");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SQL function for current time and date
|
||||
*
|
||||
* @param int $interval Optional interval (in seconds) to add/subtract
|
||||
*
|
||||
* @return string SQL function to use in query
|
||||
*/
|
||||
public function now($interval = 0)
|
||||
{
|
||||
if ($interval) {
|
||||
$interval = intval($interval);
|
||||
return "dateadd(second, $interval, getdate())";
|
||||
}
|
||||
|
||||
return "getdate()";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SQL statement to convert a field value into a unix timestamp
|
||||
*
|
||||
* @param string $field Field name
|
||||
*
|
||||
* @return string SQL statement to use in query
|
||||
* @deprecated
|
||||
*/
|
||||
public function unixtimestamp($field)
|
||||
{
|
||||
return "DATEDIFF(second, '19700101', $field) + DATEDIFF(second, GETDATE(), GETUTCDATE())";
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract SQL statement for value concatenation
|
||||
*
|
||||
* @return string ...$args Values to concatenate
|
||||
*/
|
||||
public function concat(...$args)
|
||||
{
|
||||
if (count($args) == 1 && is_array($args[0])) {
|
||||
$args = $args[0];
|
||||
}
|
||||
|
||||
return '(' . implode('+', $args) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds TOP (LIMIT,OFFSET) clause to the query
|
||||
*
|
||||
* @param string $query SQL query
|
||||
* @param int $limit Number of rows
|
||||
* @param int $offset Offset
|
||||
*
|
||||
* @return string SQL query
|
||||
*/
|
||||
protected function set_limit($query, $limit = 0, $offset = 0)
|
||||
{
|
||||
$limit = intval($limit);
|
||||
$offset = intval($offset);
|
||||
$end = $offset + $limit;
|
||||
|
||||
// query without OFFSET
|
||||
if (!$offset) {
|
||||
$query = preg_replace('/^SELECT\s/i', "SELECT TOP $limit ", $query);
|
||||
return $query;
|
||||
}
|
||||
|
||||
$orderby = stristr($query, 'ORDER BY');
|
||||
$offset += 1;
|
||||
|
||||
if ($orderby !== false) {
|
||||
$query = trim(substr($query, 0, -1 * strlen($orderby)));
|
||||
}
|
||||
else {
|
||||
// it shouldn't happen, paging without sorting has not much sense
|
||||
// @FIXME: I don't know how to build paging query without ORDER BY
|
||||
$orderby = "ORDER BY 1";
|
||||
}
|
||||
|
||||
$query = preg_replace('/^SELECT\s/i', '', $query);
|
||||
$query = "WITH paging AS (SELECT ROW_NUMBER() OVER ($orderby) AS [RowNumber], $query)"
|
||||
. " SELECT * FROM paging WHERE [RowNumber] BETWEEN $offset AND $end ORDER BY [RowNumber]";
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns PDO DSN string from DSN array
|
||||
*/
|
||||
protected function dsn_string($dsn)
|
||||
{
|
||||
$params = [];
|
||||
$result = $dsn['phptype'] . ':';
|
||||
|
||||
if (isset($dsn['hostspec'])) {
|
||||
$host = $dsn['hostspec'];
|
||||
if (isset($dsn['port'])) {
|
||||
$host .= ',' . $dsn['port'];
|
||||
}
|
||||
$params[] = 'host=' . $host;
|
||||
}
|
||||
|
||||
if (isset($dsn['database'])) {
|
||||
$params[] = 'dbname=' . $dsn['database'];
|
||||
}
|
||||
|
||||
if (!empty($params)) {
|
||||
$result .= implode(';', $params);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse SQL file and fix table names according to table prefix
|
||||
*/
|
||||
protected function fix_table_names($sql)
|
||||
{
|
||||
if (!$this->options['table_prefix']) {
|
||||
return $sql;
|
||||
}
|
||||
|
||||
// replace sequence names, and other postgres-specific commands
|
||||
$sql = preg_replace_callback(
|
||||
'/((TABLE|(?<!ON )UPDATE|INSERT INTO|FROM(?! deleted)| ON(?! (DELETE|UPDATE|\[PRIMARY\]))'
|
||||
. '|REFERENCES|CONSTRAINT|TRIGGER|INDEX)\s+(\[dbo\]\.)?[\[\]]*)([^\[\]\( \r\n]+)/',
|
||||
[$this, 'fix_table_names_callback'],
|
||||
$sql
|
||||
);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Database wrapper class that implements PHP PDO functions |
|
||||
| for MySQL database |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/**
|
||||
* Database independent query interface
|
||||
*
|
||||
* This is a wrapper for the PHP PDO
|
||||
*
|
||||
* @package Framework
|
||||
* @subpackage Database
|
||||
*/
|
||||
class rcube_db_mysql extends rcube_db
|
||||
{
|
||||
public $db_provider = 'mysql';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($db_dsnw, $db_dsnr = '', $pconn = false)
|
||||
{
|
||||
parent::__construct($db_dsnw, $db_dsnr, $pconn);
|
||||
|
||||
// SQL identifiers quoting
|
||||
$this->options['identifier_start'] = '`';
|
||||
$this->options['identifier_end'] = '`';
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract SQL statement for value concatenation
|
||||
*
|
||||
* @return string ...$args Values to concatenate
|
||||
*/
|
||||
public function concat(...$args)
|
||||
{
|
||||
if (count($args) == 1 && is_array($args[0])) {
|
||||
$args = $args[0];
|
||||
}
|
||||
|
||||
return 'CONCAT(' . implode(', ', $args) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns PDO DSN string from DSN array
|
||||
*
|
||||
* @param array $dsn DSN parameters
|
||||
*
|
||||
* @return string Connection string
|
||||
*/
|
||||
protected function dsn_string($dsn)
|
||||
{
|
||||
$params = [];
|
||||
$result = 'mysql:';
|
||||
|
||||
if (isset($dsn['database'])) {
|
||||
$params[] = 'dbname=' . $dsn['database'];
|
||||
}
|
||||
|
||||
if (isset($dsn['hostspec'])) {
|
||||
$params[] = 'host=' . $dsn['hostspec'];
|
||||
}
|
||||
|
||||
if (isset($dsn['port'])) {
|
||||
$params[] = 'port=' . $dsn['port'];
|
||||
}
|
||||
|
||||
if (isset($dsn['socket'])) {
|
||||
$params[] = 'unix_socket=' . $dsn['socket'];
|
||||
}
|
||||
|
||||
$params[] = 'charset=' . (!empty($dsn['charset']) ? $dsn['charset'] : 'utf8mb4');
|
||||
|
||||
if (!empty($params)) {
|
||||
$result .= implode(';', $params);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns driver-specific connection options
|
||||
*
|
||||
* @param array $dsn DSN parameters
|
||||
*
|
||||
* @return array Connection options
|
||||
*/
|
||||
protected function dsn_options($dsn)
|
||||
{
|
||||
$result = parent::dsn_options($dsn);
|
||||
|
||||
if (!empty($dsn['key'])) {
|
||||
$result[PDO::MYSQL_ATTR_SSL_KEY] = $dsn['key'];
|
||||
}
|
||||
|
||||
if (!empty($dsn['cipher'])) {
|
||||
$result[PDO::MYSQL_ATTR_SSL_CIPHER] = $dsn['cipher'];
|
||||
}
|
||||
|
||||
if (!empty($dsn['cert'])) {
|
||||
$result[PDO::MYSQL_ATTR_SSL_CERT] = $dsn['cert'];
|
||||
}
|
||||
|
||||
if (!empty($dsn['capath'])) {
|
||||
$result[PDO::MYSQL_ATTR_SSL_CAPATH] = $dsn['capath'];
|
||||
}
|
||||
|
||||
if (!empty($dsn['ca'])) {
|
||||
$result[PDO::MYSQL_ATTR_SSL_CA] = $dsn['ca'];
|
||||
}
|
||||
|
||||
if (isset($dsn['verify_server_cert'])) {
|
||||
$result[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = rcube_utils::get_boolean($dsn['verify_server_cert']);
|
||||
}
|
||||
|
||||
// Always return matching (not affected only) rows count
|
||||
$result[PDO::MYSQL_ATTR_FOUND_ROWS] = true;
|
||||
|
||||
// Enable AUTOCOMMIT mode (#1488902)
|
||||
$result[PDO::ATTR_AUTOCOMMIT] = true;
|
||||
|
||||
// Disable emulating of prepared statements
|
||||
$result[PDO::ATTR_EMULATE_PREPARES] = false;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of tables in a database
|
||||
*
|
||||
* @return array List of all tables of the current database
|
||||
*/
|
||||
public function list_tables()
|
||||
{
|
||||
// get tables if not cached
|
||||
if ($this->tables === null) {
|
||||
$q = $this->query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES"
|
||||
. " WHERE TABLE_SCHEMA = ? AND TABLE_TYPE = 'BASE TABLE'"
|
||||
. " ORDER BY TABLE_NAME", $this->db_dsnw_array['database']);
|
||||
|
||||
$this->tables = $q ? $q->fetchAll(PDO::FETCH_COLUMN, 0) : [];
|
||||
}
|
||||
|
||||
return $this->tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of columns in database table
|
||||
*
|
||||
* @param string $table Table name
|
||||
*
|
||||
* @return array List of table cols
|
||||
*/
|
||||
public function list_cols($table)
|
||||
{
|
||||
$q = $this->query("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS"
|
||||
. " WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?",
|
||||
$this->db_dsnw_array['database'], $table);
|
||||
|
||||
if ($q) {
|
||||
return $q->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database runtime variables
|
||||
*
|
||||
* @param string $varname Variable name
|
||||
* @param mixed $default Default value if variable is not set
|
||||
*
|
||||
* @return mixed Variable value or default
|
||||
*/
|
||||
public function get_variable($varname, $default = null)
|
||||
{
|
||||
if (!isset($this->variables)) {
|
||||
$this->variables = [];
|
||||
}
|
||||
|
||||
if (array_key_exists($varname, $this->variables)) {
|
||||
return $this->variables[$varname];
|
||||
}
|
||||
|
||||
// configured value has higher prio
|
||||
$conf_value = rcube::get_instance()->config->get('db_' . $varname);
|
||||
if ($conf_value !== null) {
|
||||
return $this->variables[$varname] = $conf_value;
|
||||
}
|
||||
|
||||
$result = $this->query('SHOW VARIABLES LIKE ?', $varname);
|
||||
|
||||
while ($row = $this->fetch_array($result)) {
|
||||
$this->variables[$row[0]] = $row[1];
|
||||
}
|
||||
|
||||
// not found, use default
|
||||
if (!isset($this->variables[$varname])) {
|
||||
$this->variables[$varname] = $default;
|
||||
}
|
||||
|
||||
return $this->variables[$varname];
|
||||
}
|
||||
|
||||
/**
|
||||
* INSERT ... ON DUPLICATE KEY UPDATE (or equivalent).
|
||||
* When not supported by the engine we do UPDATE and INSERT.
|
||||
*
|
||||
* @param string $table Table name (should be already passed via table_name() with quoting)
|
||||
* @param array $keys Hash array (column => value) of the unique constraint
|
||||
* @param array $columns List of columns to update
|
||||
* @param array $values List of values to update (number of elements
|
||||
* should be the same as in $columns)
|
||||
*
|
||||
* @return PDOStatement|bool Query handle or False on error
|
||||
* @todo Multi-insert support
|
||||
*/
|
||||
public function insert_or_update($table, $keys, $columns, $values)
|
||||
{
|
||||
$columns = array_map(function($i) { return "`$i`"; }, $columns);
|
||||
$cols = implode(', ', array_map(function($i) { return "`$i`"; }, array_keys($keys)));
|
||||
$cols .= ', ' . implode(', ', $columns);
|
||||
$vals = implode(', ', array_map(function($i) { return $this->quote($i); }, $keys));
|
||||
$vals .= ', ' . rtrim(str_repeat('?, ', count($columns)), ', ');
|
||||
$update = implode(', ', array_map(function($i) { return "$i = VALUES($i)"; }, $columns));
|
||||
|
||||
return $this->query("INSERT INTO $table ($cols) VALUES ($vals)"
|
||||
. " ON DUPLICATE KEY UPDATE $update", $values);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,631 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Database wrapper class that implements database functions |
|
||||
| for Oracle database using OCI8 extension |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/**
|
||||
* Database independent query interface
|
||||
*
|
||||
* @package Framework
|
||||
* @subpackage Database
|
||||
*/
|
||||
class rcube_db_oracle extends rcube_db
|
||||
{
|
||||
public $db_provider = 'oracle';
|
||||
public $in_transaction = false;
|
||||
|
||||
/**
|
||||
* Create connection instance
|
||||
*/
|
||||
protected function conn_create($dsn)
|
||||
{
|
||||
// Get database specific connection options
|
||||
$dsn_options = $this->dsn_options($dsn);
|
||||
|
||||
$function = $this->db_pconn ? 'oci_pconnect' : 'oci_connect';
|
||||
|
||||
if (!function_exists($function)) {
|
||||
$this->db_error = true;
|
||||
$this->db_error_msg = 'OCI8 extension not loaded. See http://php.net/manual/en/book.oci8.php';
|
||||
|
||||
rcube::raise_error([
|
||||
'code' => 500, 'type' => 'db',
|
||||
'line' => __LINE__, 'file' => __FILE__,
|
||||
'message' => $this->db_error_msg
|
||||
], true, false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// connect
|
||||
$dbh = @$function($dsn['username'], $dsn['password'], $dsn_options['database'], $dsn_options['charset']);
|
||||
|
||||
if (!$dbh) {
|
||||
$error = oci_error();
|
||||
$this->db_error = true;
|
||||
$this->db_error_msg = $error['message'];
|
||||
|
||||
rcube::raise_error([
|
||||
'code' => 500, 'type' => 'db',
|
||||
'line' => __LINE__, 'file' => __FILE__,
|
||||
'message' => $this->db_error_msg
|
||||
], true, false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// configure session
|
||||
$this->conn_configure($dsn, $dbh);
|
||||
|
||||
return $dbh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Driver-specific configuration of database connection
|
||||
*
|
||||
* @param array $dsn DSN for DB connections
|
||||
* @param PDO $dbh Connection handler
|
||||
*/
|
||||
protected function conn_configure($dsn, $dbh)
|
||||
{
|
||||
$init_queries = [
|
||||
"ALTER SESSION SET nls_date_format = 'YYYY-MM-DD'",
|
||||
"ALTER SESSION SET nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'",
|
||||
];
|
||||
|
||||
foreach ($init_queries as $query) {
|
||||
$stmt = oci_parse($dbh, $query);
|
||||
oci_execute($stmt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection state checker
|
||||
*
|
||||
* @return bool True if in connected state
|
||||
*/
|
||||
public function is_connected()
|
||||
{
|
||||
return empty($this->dbh) ? false : $this->db_connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a SQL query with limits
|
||||
*
|
||||
* @param string $query SQL query to execute
|
||||
* @param int $offset Offset for LIMIT statement
|
||||
* @param int $numrows Number of rows for LIMIT statement
|
||||
* @param array $params Values to be inserted in query
|
||||
*
|
||||
* @return PDOStatement|bool Query handle or False on error
|
||||
*/
|
||||
protected function _query($query, $offset, $numrows, $params)
|
||||
{
|
||||
$query = ltrim($query);
|
||||
|
||||
$this->db_connect($this->dsn_select($query), true);
|
||||
|
||||
// check connection before proceeding
|
||||
if (!$this->is_connected()) {
|
||||
return $this->last_result = false;
|
||||
}
|
||||
|
||||
if ($numrows || $offset) {
|
||||
$query = $this->set_limit($query, $numrows, $offset);
|
||||
}
|
||||
|
||||
// replace self::DEFAULT_QUOTE with driver-specific quoting
|
||||
$query = $this->query_parse($query);
|
||||
|
||||
// Because in Roundcube we mostly use queries that are
|
||||
// executed only once, we will not use prepared queries
|
||||
$pos = 0;
|
||||
$idx = 0;
|
||||
$args = [];
|
||||
|
||||
if (!empty($params)) {
|
||||
while ($pos = strpos($query, '?', $pos)) {
|
||||
if ($query[$pos+1] == '?') { // skip escaped '?'
|
||||
$pos += 2;
|
||||
}
|
||||
else {
|
||||
$val = $this->quote($params[$idx++]);
|
||||
|
||||
// long strings are not allowed inline, need to be parametrized
|
||||
if (strlen($val) > 4000) {
|
||||
$key = ':param' . (count($args) + 1);
|
||||
$args[$key] = $params[$idx-1];
|
||||
$val = $key;
|
||||
}
|
||||
|
||||
unset($params[$idx-1]);
|
||||
$query = substr_replace($query, $val, $pos, 1);
|
||||
$pos += strlen($val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$query = rtrim($query, " \t\n\r\0\x0B;");
|
||||
|
||||
// replace escaped '?' and quotes back to normal, see self::quote()
|
||||
$query = str_replace(
|
||||
['??', self::DEFAULT_QUOTE.self::DEFAULT_QUOTE],
|
||||
['?', self::DEFAULT_QUOTE],
|
||||
$query
|
||||
);
|
||||
|
||||
// log query
|
||||
$this->debug($query);
|
||||
|
||||
// destroy reference to previous result
|
||||
$this->last_result = null;
|
||||
$this->db_error_msg = null;
|
||||
|
||||
// prepare query
|
||||
$result = @oci_parse($this->dbh, $query);
|
||||
$mode = $this->in_transaction ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS;
|
||||
|
||||
if ($result) {
|
||||
foreach (array_keys($args) as $param) {
|
||||
oci_bind_by_name($result, $param, $args[$param], -1, SQLT_LNG);
|
||||
}
|
||||
}
|
||||
|
||||
// execute query
|
||||
if (!$result || !@oci_execute($result, $mode)) {
|
||||
$result = $this->handle_error($query, $result);
|
||||
}
|
||||
|
||||
return $this->last_result = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to handle DB errors.
|
||||
* This by default logs the error but could be overridden by a driver implementation
|
||||
*
|
||||
* @param string Query that triggered the error
|
||||
* @param resource Query result
|
||||
*
|
||||
* @return bool Result to be stored and returned
|
||||
*/
|
||||
protected function handle_error($query, $result = null)
|
||||
{
|
||||
$error = oci_error($result ?: $this->dbh);
|
||||
|
||||
// @TODO: Find error codes for key errors
|
||||
if (empty($this->options['ignore_key_errors']) || !in_array($error['code'], ['23000', '23505'])) {
|
||||
$this->db_error = true;
|
||||
$this->db_error_msg = sprintf('[%s] %s', $error['code'], $error['message']);
|
||||
|
||||
rcube::raise_error([
|
||||
'code' => 500, 'type' => 'db',
|
||||
'line' => __LINE__, 'file' => __FILE__,
|
||||
'message' => $this->db_error_msg . " (SQL Query: $query)"
|
||||
], true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted record ID
|
||||
*
|
||||
* @param string $table Table name (to find the incremented sequence)
|
||||
*
|
||||
* @return mixed ID or false on failure
|
||||
*/
|
||||
public function insert_id($table = null)
|
||||
{
|
||||
if (!$this->db_connected || $this->db_mode == 'r' || empty($table)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sequence = $this->quote_identifier($this->sequence_name($table));
|
||||
$result = $this->query("SELECT $sequence.currval FROM dual");
|
||||
$result = $this->fetch_array($result);
|
||||
|
||||
return !empty($result[0]) ? $result[0] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of affected rows for the last query
|
||||
*
|
||||
* @param mixed $result Optional query handle
|
||||
*
|
||||
* @return int Number of (matching) rows
|
||||
*/
|
||||
public function affected_rows($result = null)
|
||||
{
|
||||
if ($result || ($result === null && ($result = $this->last_result))) {
|
||||
return oci_num_rows($result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of rows for a SQL query
|
||||
* If no query handle is specified, the last query will be taken as reference
|
||||
*
|
||||
* @param mixed $result Optional query handle
|
||||
*
|
||||
* @return mixed Number of rows or false on failure
|
||||
* @deprecated This method shows very poor performance and should be avoided.
|
||||
*/
|
||||
public function num_rows($result = null)
|
||||
{
|
||||
// not implemented
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an associative array for one row
|
||||
* If no query handle is specified, the last query will be taken as reference
|
||||
*
|
||||
* @param mixed $result Optional query handle
|
||||
*
|
||||
* @return mixed Array with col values or false on failure
|
||||
*/
|
||||
public function fetch_assoc($result = null)
|
||||
{
|
||||
return $this->_fetch_row($result, OCI_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an index array for one row
|
||||
* If no query handle is specified, the last query will be taken as reference
|
||||
*
|
||||
* @param mixed $result Optional query handle
|
||||
*
|
||||
* @return mixed Array with col values or false on failure
|
||||
*/
|
||||
public function fetch_array($result = null)
|
||||
{
|
||||
return $this->_fetch_row($result, OCI_NUM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get col values for a result row
|
||||
*
|
||||
* @param mixed $result Optional query handle
|
||||
* @param int $mode Fetch mode identifier
|
||||
*
|
||||
* @return array|false Array with col values or false on failure
|
||||
*/
|
||||
protected function _fetch_row($result, $mode)
|
||||
{
|
||||
if ($result || ($result === null && ($result = $this->last_result))) {
|
||||
return oci_fetch_array($result, $mode + OCI_RETURN_NULLS + OCI_RETURN_LOBS);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats input so it can be safely used in a query
|
||||
* PDO_OCI does not implement quote() method
|
||||
*
|
||||
* @param mixed $input Value to quote
|
||||
* @param string $type Type of data (integer, bool, ident)
|
||||
*
|
||||
* @return string Quoted/converted string for use in query
|
||||
*/
|
||||
public function quote($input, $type = null)
|
||||
{
|
||||
// handle int directly for better performance
|
||||
if ($type == 'integer' || $type == 'int') {
|
||||
return intval($input);
|
||||
}
|
||||
|
||||
if (is_null($input)) {
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
if ($input instanceof DateTime) {
|
||||
return $this->quote($input->format($this->options['datetime_format']));
|
||||
}
|
||||
|
||||
if ($type == 'ident') {
|
||||
return $this->quote_identifier($input);
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'bool':
|
||||
case 'integer':
|
||||
return intval($input);
|
||||
default:
|
||||
return "'" . strtr($input, [
|
||||
'?' => '??',
|
||||
"'" => "''",
|
||||
rcube_db::DEFAULT_QUOTE => rcube_db::DEFAULT_QUOTE . rcube_db::DEFAULT_QUOTE
|
||||
]) . "'";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return correct name for a specific database sequence
|
||||
*
|
||||
* @param string $table Table name
|
||||
*
|
||||
* @return string Translated sequence name
|
||||
*/
|
||||
protected function sequence_name($table)
|
||||
{
|
||||
// Note: we support only one sequence per table
|
||||
// Note: The sequence name must be <table_name>_seq
|
||||
$sequence = $table . '_seq';
|
||||
|
||||
// modify sequence name if prefix is configured
|
||||
if ($prefix = $this->options['table_prefix']) {
|
||||
return $prefix . $sequence;
|
||||
}
|
||||
|
||||
return $sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SQL statement for case insensitive LIKE
|
||||
*
|
||||
* @param string $column Field name
|
||||
* @param string $value Search value
|
||||
*
|
||||
* @return string SQL statement to use in query
|
||||
*/
|
||||
public function ilike($column, $value)
|
||||
{
|
||||
return 'UPPER(' . $this->quote_identifier($column) . ') LIKE UPPER(' . $this->quote($value) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SQL function for current time and date
|
||||
*
|
||||
* @param int $interval Optional interval (in seconds) to add/subtract
|
||||
*
|
||||
* @return string SQL function to use in query
|
||||
*/
|
||||
public function now($interval = 0)
|
||||
{
|
||||
if ($interval) {
|
||||
$interval = intval($interval);
|
||||
return "current_timestamp + INTERVAL '$interval' SECOND";
|
||||
}
|
||||
|
||||
return "current_timestamp";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SQL statement to convert a field value into a unix timestamp
|
||||
*
|
||||
* @param string $field Field name
|
||||
*
|
||||
* @return string SQL statement to use in query
|
||||
* @deprecated
|
||||
*/
|
||||
public function unixtimestamp($field)
|
||||
{
|
||||
return "(($field - to_date('1970-01-01','YYYY-MM-DD')) * 60 * 60 * 24)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds TOP (LIMIT,OFFSET) clause to the query
|
||||
*
|
||||
* @param string $query SQL query
|
||||
* @param int $limit Number of rows
|
||||
* @param int $offset Offset
|
||||
*
|
||||
* @return string SQL query
|
||||
*/
|
||||
protected function set_limit($query, $limit = 0, $offset = 0)
|
||||
{
|
||||
$limit = intval($limit);
|
||||
$offset = intval($offset);
|
||||
$end = $offset + $limit;
|
||||
|
||||
// @TODO: Oracle 12g has better OFFSET support
|
||||
|
||||
if (!$offset) {
|
||||
$query = "SELECT * FROM ($query) a WHERE rownum <= $end";
|
||||
}
|
||||
else {
|
||||
$query = "SELECT * FROM (SELECT a.*, rownum as rn FROM ($query) a WHERE rownum <= $end) b WHERE rn > $offset";
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse SQL file and fix table names according to table prefix
|
||||
*/
|
||||
protected function fix_table_names($sql)
|
||||
{
|
||||
if (!$this->options['table_prefix']) {
|
||||
return $sql;
|
||||
}
|
||||
|
||||
$sql = parent::fix_table_names($sql);
|
||||
|
||||
// replace sequence names, and other Oracle-specific commands
|
||||
$sql = preg_replace_callback('/(SEQUENCE ["]?)([^" \r\n]+)/',
|
||||
[$this, 'fix_table_names_callback'],
|
||||
$sql
|
||||
);
|
||||
|
||||
$sql = preg_replace_callback(
|
||||
'/([ \r\n]+["]?)([^"\' \r\n\.]+)(["]?\.nextval)/',
|
||||
[$this, 'fix_table_names_seq_callback'],
|
||||
$sql
|
||||
);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preg_replace callback for fix_table_names()
|
||||
*/
|
||||
protected function fix_table_names_seq_callback($matches)
|
||||
{
|
||||
return $matches[1] . $this->options['table_prefix'] . $matches[2] . $matches[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns connection options from DSN array
|
||||
*/
|
||||
protected function dsn_options($dsn)
|
||||
{
|
||||
$params = [];
|
||||
|
||||
if (isset($dsn['hostspec'])) {
|
||||
$host = $dsn['hostspec'];
|
||||
if (isset($dsn['port'])) {
|
||||
$host .= ':' . $dsn['port'];
|
||||
}
|
||||
|
||||
$params['database'] = $host . '/' . $dsn['database'];
|
||||
}
|
||||
|
||||
$params['charset'] = 'UTF8';
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given SQL script
|
||||
*
|
||||
* @param string $sql SQL queries to execute
|
||||
*
|
||||
* @return bool True on success, False on error
|
||||
*/
|
||||
public function exec_script($sql)
|
||||
{
|
||||
$sql = $this->fix_table_names($sql);
|
||||
$buff = '';
|
||||
$body = false;
|
||||
|
||||
foreach (explode("\n", $sql) as $line) {
|
||||
$tok = strtolower(trim($line));
|
||||
if (preg_match('/^--/', $line) || $tok == '' || $tok == '/') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$buff .= $line . "\n";
|
||||
|
||||
// detect PL/SQL function bodies, don't break on semicolon
|
||||
if ($body && $tok == 'end;') {
|
||||
$body = false;
|
||||
}
|
||||
else if (!$body && $tok == 'begin') {
|
||||
$body = true;
|
||||
}
|
||||
|
||||
if (!$body && substr($tok, -1) == ';') {
|
||||
$this->query($buff);
|
||||
$buff = '';
|
||||
if ($this->db_error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !$this->db_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start transaction
|
||||
*
|
||||
* @return bool True on success, False on failure
|
||||
*/
|
||||
public function startTransaction()
|
||||
{
|
||||
$this->db_connect('w', true);
|
||||
|
||||
// check connection before proceeding
|
||||
if (!$this->is_connected()) {
|
||||
return $this->last_result = false;
|
||||
}
|
||||
|
||||
$this->debug('BEGIN TRANSACTION');
|
||||
|
||||
return $this->last_result = $this->in_transaction = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit transaction
|
||||
*
|
||||
* @return bool True on success, False on failure
|
||||
*/
|
||||
public function endTransaction()
|
||||
{
|
||||
$this->db_connect('w', true);
|
||||
|
||||
// check connection before proceeding
|
||||
if (!$this->is_connected()) {
|
||||
return $this->last_result = false;
|
||||
}
|
||||
|
||||
$this->debug('COMMIT TRANSACTION');
|
||||
|
||||
if ($result = @oci_commit($this->dbh)) {
|
||||
$this->in_transaction = true;
|
||||
}
|
||||
else {
|
||||
$this->handle_error('COMMIT');
|
||||
}
|
||||
|
||||
return $this->last_result = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback transaction
|
||||
*
|
||||
* @return bool True on success, False on failure
|
||||
*/
|
||||
public function rollbackTransaction()
|
||||
{
|
||||
$this->db_connect('w', true);
|
||||
|
||||
// check connection before proceeding
|
||||
if (!$this->is_connected()) {
|
||||
return $this->last_result = false;
|
||||
}
|
||||
|
||||
$this->debug('ROLLBACK TRANSACTION');
|
||||
|
||||
if (@oci_rollback($this->dbh)) {
|
||||
$this->in_transaction = false;
|
||||
}
|
||||
else {
|
||||
$this->handle_error('ROLLBACK');
|
||||
}
|
||||
|
||||
return $this->last_result = $this->dbh->rollBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate database connection.
|
||||
*/
|
||||
public function closeConnection()
|
||||
{
|
||||
// release statement and close connection(s)
|
||||
$this->last_result = null;
|
||||
foreach ($this->dbhs as $dbh) {
|
||||
oci_close($dbh);
|
||||
}
|
||||
|
||||
parent::closeConnection();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Database wrapper class for query parameters |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/**
|
||||
* Database query parameter
|
||||
*
|
||||
* @package Framework
|
||||
* @subpackage Database
|
||||
*/
|
||||
class rcube_db_param
|
||||
{
|
||||
protected $db;
|
||||
protected $type;
|
||||
protected $value;
|
||||
|
||||
|
||||
/**
|
||||
* Object constructor
|
||||
*
|
||||
* @param rcube_db $db Database driver
|
||||
* @param mixed $value Parameter value
|
||||
* @param string $type Parameter type (One of rcube_db::TYPE_* constants)
|
||||
*/
|
||||
public function __construct($db, $value, $type = null)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->value = $value;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value as string for inlining into SQL query
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
if ($this->type === rcube_db::TYPE_SQL) {
|
||||
return (string) $this->value;
|
||||
}
|
||||
|
||||
return (string) $this->db->quote($this->value, $this->type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Database wrapper class that implements PHP PDO functions |
|
||||
| for PostgreSQL database |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/**
|
||||
* Database independent query interface
|
||||
* This is a wrapper for the PHP PDO
|
||||
*
|
||||
* @package Framework
|
||||
* @subpackage Database
|
||||
*/
|
||||
class rcube_db_pgsql extends rcube_db
|
||||
{
|
||||
public $db_provider = 'postgres';
|
||||
|
||||
// See https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS
|
||||
private static $libpq_connect_params = [
|
||||
'application_name',
|
||||
'sslmode',
|
||||
'sslcert',
|
||||
'sslkey',
|
||||
'sslrootcert',
|
||||
'sslcrl',
|
||||
'sslcompression',
|
||||
'service'
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($db_dsnw, $db_dsnr = '', $pconn = false)
|
||||
{
|
||||
parent::__construct($db_dsnw, $db_dsnr, $pconn);
|
||||
|
||||
// use date/time input format with timezone spec.
|
||||
$this->options['datetime_format'] = 'c';
|
||||
}
|
||||
|
||||
/**
|
||||
* Driver-specific configuration of database connection
|
||||
*
|
||||
* @param array $dsn DSN for DB connections
|
||||
* @param PDO $dbh Connection handler
|
||||
*/
|
||||
protected function conn_configure($dsn, $dbh)
|
||||
{
|
||||
$dbh->query("SET NAMES 'utf8'");
|
||||
$dbh->query("SET DATESTYLE TO ISO");
|
||||
|
||||
// if ?schema= is set in dsn, set the search_path
|
||||
if (!empty($dsn['schema'])) {
|
||||
$dbh->query("SET search_path TO " . $this->quote($dsn['schema']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted record ID
|
||||
*
|
||||
* @param string $table Table name (to find the incremented sequence)
|
||||
*
|
||||
* @return mixed ID or false on failure
|
||||
*/
|
||||
public function insert_id($table = null)
|
||||
{
|
||||
if (!$this->db_connected || $this->db_mode == 'r') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($table) {
|
||||
$table = $this->sequence_name($table);
|
||||
}
|
||||
|
||||
return $this->dbh->lastInsertId($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return correct name for a specific database sequence
|
||||
*
|
||||
* @param string $table Table name
|
||||
*
|
||||
* @return string Translated sequence name
|
||||
*/
|
||||
protected function sequence_name($table)
|
||||
{
|
||||
// Note: we support only one sequence per table
|
||||
// Note: The sequence name must be <table_name>_seq
|
||||
$sequence = $table . '_seq';
|
||||
|
||||
// modify sequence name if prefix is configured
|
||||
if ($prefix = $this->options['table_prefix']) {
|
||||
return $prefix . $sequence;
|
||||
}
|
||||
|
||||
return $sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SQL statement to convert a field value into a unix timestamp
|
||||
*
|
||||
* @param string $field Field name
|
||||
*
|
||||
* @return string SQL statement to use in query
|
||||
* @deprecated
|
||||
*/
|
||||
public function unixtimestamp($field)
|
||||
{
|
||||
return "EXTRACT (EPOCH FROM $field)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SQL function for current time and date
|
||||
*
|
||||
* @param int $interval Optional interval (in seconds) to add/subtract
|
||||
*
|
||||
* @return string SQL function to use in query
|
||||
*/
|
||||
public function now($interval = 0)
|
||||
{
|
||||
$result = 'now()';
|
||||
|
||||
if ($interval) {
|
||||
$result .= ' ' . ($interval > 0 ? '+' : '-') . " interval '"
|
||||
. ($interval > 0 ? intval($interval) : intval($interval) * -1)
|
||||
. " seconds'";
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SQL statement for case insensitive LIKE
|
||||
*
|
||||
* @param string $column Field name
|
||||
* @param string $value Search value
|
||||
*
|
||||
* @return string SQL statement to use in query
|
||||
*/
|
||||
public function ilike($column, $value)
|
||||
{
|
||||
return $this->quote_identifier($column) . ' ILIKE ' . $this->quote($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database runtime variables
|
||||
*
|
||||
* @param string $varname Variable name
|
||||
* @param mixed $default Default value if variable is not set
|
||||
*
|
||||
* @return mixed Variable value or default
|
||||
*/
|
||||
public function get_variable($varname, $default = null)
|
||||
{
|
||||
// There's a known case when max_allowed_packet is queried
|
||||
// PostgreSQL doesn't have such limit, return immediately
|
||||
if ($varname == 'max_allowed_packet') {
|
||||
return rcube::get_instance()->config->get('db_' . $varname, $default);
|
||||
}
|
||||
|
||||
$this->variables[$varname] = rcube::get_instance()->config->get('db_' . $varname);
|
||||
|
||||
if (!isset($this->variables)) {
|
||||
$this->variables = [];
|
||||
|
||||
$result = $this->query('SHOW ALL');
|
||||
|
||||
while ($row = $this->fetch_array($result)) {
|
||||
$this->variables[$row[0]] = $row[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->variables[$varname] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* INSERT ... ON CONFLICT DO UPDATE.
|
||||
* When not supported by the engine we do UPDATE and INSERT.
|
||||
*
|
||||
* @param string $table Table name (should be already passed via table_name() with quoting)
|
||||
* @param array $keys Hash array (column => value) of the unique constraint
|
||||
* @param array $columns List of columns to update
|
||||
* @param array $values List of values to update (number of elements
|
||||
* should be the same as in $columns)
|
||||
*
|
||||
* @return PDOStatement|bool Query handle or False on error
|
||||
* @todo Multi-insert support
|
||||
*/
|
||||
public function insert_or_update($table, $keys, $columns, $values)
|
||||
{
|
||||
// Check if version >= 9.5, otherwise use fallback
|
||||
if ($this->get_variable('server_version_num') < 90500) {
|
||||
return parent::insert_or_update($table, $keys, $columns, $values);
|
||||
}
|
||||
|
||||
$columns = array_map([$this, 'quote_identifier'], $columns);
|
||||
$target = implode(', ', array_map([$this, 'quote_identifier'], array_keys($keys)));
|
||||
$cols = $target . ', ' . implode(', ', $columns);
|
||||
$vals = implode(', ', array_map(function($i) { return $this->quote($i); }, $keys));
|
||||
$vals .= ', ' . rtrim(str_repeat('?, ', count($columns)), ', ');
|
||||
$update = implode(', ', array_map(function($i) { return "$i = EXCLUDED.$i"; }, $columns));
|
||||
|
||||
return $this->query("INSERT INTO $table ($cols) VALUES ($vals)"
|
||||
. " ON CONFLICT ($target) DO UPDATE SET $update", $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of tables in a database
|
||||
*
|
||||
* @return array List of all tables of the current database
|
||||
*/
|
||||
public function list_tables()
|
||||
{
|
||||
// get tables if not cached
|
||||
if ($this->tables === null) {
|
||||
if (($schema = $this->options['table_prefix']) && $schema[strlen($schema)-1] === '.') {
|
||||
$add = " AND TABLE_SCHEMA = " . $this->quote(substr($schema, 0, -1));
|
||||
}
|
||||
else {
|
||||
$add = " AND TABLE_SCHEMA NOT IN ('pg_catalog', 'information_schema')";
|
||||
}
|
||||
|
||||
$q = $this->query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES"
|
||||
. " WHERE TABLE_TYPE = 'BASE TABLE'" . $add
|
||||
. " ORDER BY TABLE_NAME");
|
||||
|
||||
$this->tables = $q ? $q->fetchAll(PDO::FETCH_COLUMN, 0) : [];
|
||||
}
|
||||
|
||||
return $this->tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of columns in database table
|
||||
*
|
||||
* @param string $table Table name
|
||||
*
|
||||
* @return array List of table cols
|
||||
*/
|
||||
public function list_cols($table)
|
||||
{
|
||||
$args = [$table];
|
||||
|
||||
if (($schema = $this->options['table_prefix']) && $schema[strlen($schema)-1] === '.') {
|
||||
$add = " AND TABLE_SCHEMA = ?";
|
||||
$args[] = substr($schema, 0, -1);
|
||||
}
|
||||
else {
|
||||
$add = " AND TABLE_SCHEMA NOT IN ('pg_catalog', 'information_schema')";
|
||||
}
|
||||
|
||||
$q = $this->query("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS"
|
||||
. " WHERE TABLE_NAME = ?" . $add, $args);
|
||||
|
||||
if ($q) {
|
||||
return $q->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns PDO DSN string from DSN array
|
||||
*
|
||||
* @param array $dsn DSN parameters
|
||||
*
|
||||
* @return string DSN string
|
||||
*/
|
||||
protected function dsn_string($dsn)
|
||||
{
|
||||
$params = [];
|
||||
$result = 'pgsql:';
|
||||
|
||||
if (isset($dsn['hostspec'])) {
|
||||
$params[] = 'host=' . $dsn['hostspec'];
|
||||
}
|
||||
else if (isset($dsn['socket'])) {
|
||||
$params[] = 'host=' . $dsn['socket'];
|
||||
}
|
||||
|
||||
if (isset($dsn['port'])) {
|
||||
$params[] = 'port=' . $dsn['port'];
|
||||
}
|
||||
|
||||
if (isset($dsn['database'])) {
|
||||
$params[] = 'dbname=' . $dsn['database'];
|
||||
}
|
||||
|
||||
foreach (self::$libpq_connect_params as $param) {
|
||||
if (isset($dsn[$param])) {
|
||||
$params[] = $param . '=' . $dsn[$param];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($params)) {
|
||||
$result .= implode(';', $params);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse SQL file and fix table names according to table prefix
|
||||
*/
|
||||
protected function fix_table_names($sql)
|
||||
{
|
||||
if (!$this->options['table_prefix']) {
|
||||
return $sql;
|
||||
}
|
||||
|
||||
$sql = parent::fix_table_names($sql);
|
||||
|
||||
// replace sequence names, and other postgres-specific commands
|
||||
$sql = preg_replace_callback(
|
||||
'/((SEQUENCE |RENAME TO |nextval\()["\']*)([^"\' \r\n]+)/',
|
||||
[$this, 'fix_table_names_callback'],
|
||||
$sql
|
||||
);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Database wrapper class that implements PHP PDO functions |
|
||||
| for SQLite database |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/**
|
||||
* Database independent query interface
|
||||
* This is a wrapper for the PHP PDO
|
||||
*
|
||||
* @package Framework
|
||||
* @subpackage Database
|
||||
*/
|
||||
class rcube_db_sqlite extends rcube_db
|
||||
{
|
||||
public $db_provider = 'sqlite';
|
||||
|
||||
/**
|
||||
* Prepare connection
|
||||
*/
|
||||
protected function conn_prepare($dsn)
|
||||
{
|
||||
// Create database file, required by PDO to exist on connection
|
||||
if (!empty($dsn['database']) && !file_exists($dsn['database'])) {
|
||||
$created = touch($dsn['database']);
|
||||
|
||||
// File mode setting, for compat. with MDB2
|
||||
if (!empty($dsn['mode']) && $created) {
|
||||
chmod($dsn['database'], octdec($dsn['mode']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure connection, create database if not exists
|
||||
*/
|
||||
protected function conn_configure($dsn, $dbh)
|
||||
{
|
||||
// Initialize database structure in file is empty
|
||||
if (!empty($dsn['database']) && !filesize($dsn['database'])) {
|
||||
$data = file_get_contents(RCUBE_INSTALL_PATH . 'SQL/sqlite.initial.sql');
|
||||
|
||||
if (strlen($data)) {
|
||||
$this->debug('INITIALIZE DATABASE');
|
||||
|
||||
$q = $dbh->exec($data);
|
||||
|
||||
if ($q === false) {
|
||||
$error = $dbh->errorInfo();
|
||||
$this->db_error = true;
|
||||
$this->db_error_msg = sprintf('[%s] %s', $error[1], $error[2]);
|
||||
|
||||
rcube::raise_error([
|
||||
'code' => 500, 'type' => 'db',
|
||||
'line' => __LINE__, 'file' => __FILE__,
|
||||
'message' => $this->db_error_msg
|
||||
],
|
||||
true, false
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enable WAL mode to fix locking issues like #8035.
|
||||
$dbh->query("PRAGMA journal_mode = WAL");
|
||||
|
||||
// Enable foreign keys (requires sqlite 3.6.19 compiled with FK support)
|
||||
$dbh->query("PRAGMA foreign_keys = ON");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SQL statement to convert a field value into a unix timestamp
|
||||
*
|
||||
* @param string $field Field name
|
||||
*
|
||||
* @return string SQL statement to use in query
|
||||
* @deprecated
|
||||
*/
|
||||
public function unixtimestamp($field)
|
||||
{
|
||||
return "strftime('%s', $field)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SQL function for current time and date
|
||||
*
|
||||
* @param int $interval Optional interval (in seconds) to add/subtract
|
||||
*
|
||||
* @return string SQL function to use in query
|
||||
*/
|
||||
public function now($interval = 0)
|
||||
{
|
||||
$add = '';
|
||||
|
||||
if ($interval) {
|
||||
$add = ($interval > 0 ? '+' : '') . intval($interval) . ' seconds';
|
||||
}
|
||||
|
||||
return "datetime('now'" . ($add ? ", '$add'" : "") . ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of tables in database
|
||||
*
|
||||
* @return array List of all tables of the current database
|
||||
*/
|
||||
public function list_tables()
|
||||
{
|
||||
if ($this->tables === null) {
|
||||
$q = $this->query('SELECT name FROM sqlite_master'
|
||||
.' WHERE type = \'table\' ORDER BY name');
|
||||
|
||||
$this->tables = $q ? $q->fetchAll(PDO::FETCH_COLUMN, 0) : [];
|
||||
}
|
||||
|
||||
return $this->tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of columns in database table
|
||||
*
|
||||
* @param string $table Table name
|
||||
*
|
||||
* @return array List of table cols
|
||||
*/
|
||||
public function list_cols($table)
|
||||
{
|
||||
$q = $this->query('PRAGMA table_info(?)', $table);
|
||||
|
||||
return $q ? $q->fetchAll(PDO::FETCH_COLUMN, 1) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build DSN string for PDO constructor
|
||||
*/
|
||||
protected function dsn_string($dsn)
|
||||
{
|
||||
return $dsn['phptype'] . ':' . $dsn['database'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns driver-specific connection options
|
||||
*
|
||||
* @param array $dsn DSN parameters
|
||||
*
|
||||
* @return array Connection options
|
||||
*/
|
||||
protected function dsn_options($dsn)
|
||||
{
|
||||
$result = parent::dsn_options($dsn);
|
||||
|
||||
// Change the default timeout (60) to a smaller value
|
||||
$result[PDO::ATTR_TIMEOUT] = isset($dsn['timeout']) ? intval($dsn['timeout']) : 10;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Database wrapper class that implements PHP PDO functions |
|
||||
| for MS SQL Server database |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/**
|
||||
* Database independent query interface
|
||||
* This is a wrapper for the PHP PDO
|
||||
*
|
||||
* @package Framework
|
||||
* @subpackage Database
|
||||
*/
|
||||
class rcube_db_sqlsrv extends rcube_db_mssql
|
||||
{
|
||||
|
||||
/**
|
||||
* Get last inserted record ID
|
||||
*
|
||||
* @param string $table Table name (to find the incremented sequence)
|
||||
*
|
||||
* @return string|false The ID or False on failure
|
||||
*/
|
||||
public function insert_id($table = '')
|
||||
{
|
||||
if (!$this->db_connected || $this->db_mode == 'r') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($table) {
|
||||
// For some unknown reason the constant described in the driver docs
|
||||
// might not exist, we'll fallback to PDO::ATTR_CLIENT_VERSION (#7564)
|
||||
if (defined('PDO::ATTR_DRIVER_VERSION')) {
|
||||
$driver_version = $this->dbh->getAttribute(PDO::ATTR_DRIVER_VERSION);
|
||||
}
|
||||
else if (defined('PDO::ATTR_CLIENT_VERSION')) {
|
||||
$client_version = $this->dbh->getAttribute(PDO::ATTR_CLIENT_VERSION);
|
||||
$driver_version = $client_version['ExtensionVer'];
|
||||
}
|
||||
else {
|
||||
$driver_version = 5;
|
||||
}
|
||||
|
||||
// Starting from version 5 of the driver lastInsertId() method expects
|
||||
// a sequence name instead of a table name. We'll unset the argument
|
||||
// to get the last insert sequence (#7564)
|
||||
if (version_compare($driver_version, '5', '>=')) {
|
||||
$table = null;
|
||||
}
|
||||
else {
|
||||
// resolve table name
|
||||
$table = $this->table_name($table);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->dbh->lastInsertId($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns PDO DSN string from DSN array
|
||||
*/
|
||||
protected function dsn_string($dsn)
|
||||
{
|
||||
$params = [];
|
||||
$result = 'sqlsrv:';
|
||||
|
||||
if (isset($dsn['hostspec'])) {
|
||||
$host = $dsn['hostspec'];
|
||||
|
||||
if (isset($dsn['port'])) {
|
||||
$host .= ',' . $dsn['port'];
|
||||
}
|
||||
|
||||
$params[] = 'Server=' . $host;
|
||||
}
|
||||
|
||||
if (isset($dsn['database'])) {
|
||||
$params[] = 'Database=' . $dsn['database'];
|
||||
}
|
||||
|
||||
if (!empty($params)) {
|
||||
$result .= implode(';', $params);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user