[DATABASE] Re-introduce PostgreSQL support

This commit is contained in:
Alexei Sorokin 2019-09-11 14:14:40 +03:00 committed by Diogo Peralta Cordeiro
parent e807e3bf08
commit 16b5ddd230
22 changed files with 606 additions and 410 deletions

View File

@ -164,10 +164,9 @@ The ones that you may want to set are listed below for clarity.
* `database` (string, required, default null): a DSN (Data Source Name) for your * `database` (string, required, default null): a DSN (Data Source Name) for your
GNU social database. This is in the format GNU social database. This is in the format
'protocol://username:password@hostname/databasename', where 'protocol' is ' 'protocol://username:password@hostname/databasename', where 'protocol' is
mysql' or 'mysqli' (or possibly 'postgresql', if you really know what 'mysqli' or 'pgsql' or 'mysql', 'username' is the username, 'password' is
you're doing), 'username' is the username, 'password' is the password, the password, and etc.
and etc.
* `ini_yourdbname` (string, default null): if your database is not named 'statusnet', * `ini_yourdbname` (string, default null): if your database is not named 'statusnet',
you'll need to set this to point to the location of the statusnet.ini file. you'll need to set this to point to the location of the statusnet.ini file.
@ -178,9 +177,9 @@ The ones that you may want to set are listed below for clarity.
'MDB2' to use the other driver type for DB_DataObject, but note that it 'MDB2' to use the other driver type for DB_DataObject, but note that it
breaks the OpenID libraries, which only support PEAR::DB. breaks the OpenID libraries, which only support PEAR::DB.
* `type` (enum["mysql", "postgresql"], default 'mysql'): Used for certain * `type` (enum["mysql", "pgsql"], default 'mysql'): Used for certain
database-specific optimization code. Assumes mysql if not set. MySQL also database-specific optimization code. Assumes mysql if not set. "mysql"
covers MySQLi and MariaDB. covers MariaDB, Oracle MySQL, mysqli or otherwise.
* `mirror` (array, default null): you can set this to an array of DSNs, in the * `mirror` (array, default null): you can set this to an array of DSNs, in the
format of the above 'database' value. If it's set, certain read-only format of the above 'database' value. If it's set, certain read-only

View File

@ -68,9 +68,7 @@ class SupAction extends Action
$notice->query('SELECT profile_id, max(id) AS max_id ' . $notice->query('SELECT profile_id, max(id) AS max_id ' .
'FROM ( ' . 'FROM ( ' .
'SELECT profile_id, id FROM notice ' . 'SELECT profile_id, id FROM notice ' .
((common_config('db','type') == 'pgsql') ? "WHERE created > TIMESTAMP '" . $divider . "' " .
'WHERE extract(epoch from created) > (extract(epoch from now()) - ' . $seconds . ') ' :
"WHERE created > TIMESTAMP '" . $divider . "' ") .
') AS latest ' . ') AS latest ' .
'GROUP BY profile_id'); 'GROUP BY profile_id');

View File

@ -943,12 +943,20 @@ class File extends Managed_DataObject
$tablefix = new $classname; $tablefix = new $classname;
// urlhash is hash('sha256', $url) in the File table // urlhash is hash('sha256', $url) in the File table
echo "Updating urlhash fields in $table table..."; echo "Updating urlhash fields in $table table...";
// Maybe very MySQL specific :( switch (common_config('db', 'type')) {
case 'pgsql':
$url_sha256 = 'encode(sha256(CAST("url" AS bytea)), \'hex\')';
break;
case 'mysql':
$url_sha256 = 'sha2(`url`, 256)';
break;
default:
throw new ServerException('Unknown DB type selected.');
}
$tablefix->query(sprintf( $tablefix->query(sprintf(
'UPDATE %1$s SET urlhash = %2$s;', 'UPDATE %1$s SET urlhash = %2$s;',
$tablefix->escapedTableName(), $tablefix->escapedTableName(),
// The line below is "result of sha256 on column `url`" $url_sha256
'sha2(url, 256)'
)); ));
echo "DONE.\n"; echo "DONE.\n";
echo "Resuming core schema upgrade..."; echo "Resuming core schema upgrade...";

View File

@ -457,12 +457,20 @@ class File_redirection extends Managed_DataObject
$tablefix = new $classname; $tablefix = new $classname;
// urlhash is hash('sha256', $url) in the File table // urlhash is hash('sha256', $url) in the File table
echo "Updating urlhash fields in $table table..."; echo "Updating urlhash fields in $table table...";
// Maybe very MySQL specific :( switch (common_config('db', 'type')) {
case 'pgsql':
$url_sha256 = 'encode(sha256(CAST("url" AS bytea)), \'hex\')';
break;
case 'mysql':
$url_sha256 = 'sha2(`url`, 256)';
break;
default:
throw new ServerException('Unknown DB type selected.');
}
$tablefix->query(sprintf( $tablefix->query(sprintf(
'UPDATE %1$s SET urlhash = %2$s;', 'UPDATE %1$s SET urlhash = %2$s;',
$tablefix->escapedTableName(), $tablefix->escapedTableName(),
// The line below is "result of sha256 on column `url`" $url_sha256
'sha2(url, 256)'
)); ));
echo "DONE.\n"; echo "DONE.\n";
echo "Resuming core schema upgrade..."; echo "Resuming core schema upgrade...";

View File

@ -226,11 +226,12 @@ abstract class Managed_DataObject extends Memcached_DataObject
$type = $column['type']; $type = $column['type'];
// For quoting style... // For quoting style...
$intTypes = array('int', $intTypes = [
'integer', 'int',
'float', 'float',
'serial', 'serial',
'numeric'); 'numeric'
];
if (in_array($type, $intTypes)) { if (in_array($type, $intTypes)) {
$style = DB_DATAOBJECT_INT; $style = DB_DATAOBJECT_INT;
} else { } else {

View File

@ -1,23 +1,25 @@
<?php <?php
/* // This file is part of GNU social - https://www.gnu.org/software/social
* StatusNet - the distributed open-source microblogging tool //
* Copyright (C) 2008, 2009, StatusNet, Inc. // GNU social is free software: you can redistribute it and/or modify
* // it under the terms of the GNU Affero General Public License as published by
* This program is free software: you can redistribute it and/or modify // the Free Software Foundation, either version 3 of the License, or
* it under the terms of the GNU Affero General Public License as published by // (at your option) any later version.
* the Free Software Foundation, either version 3 of the License, or //
* (at your option) any later version. // GNU social is distributed in the hope that it will be useful,
* // but WITHOUT ANY WARRANTY; without even the implied warranty of
* This program is distributed in the hope that it will be useful, // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* but WITHOUT ANY WARRANTY; without even the implied warranty of // GNU Affero General Public License for more details.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
* GNU Affero General Public License for more details. // You should have received a copy of the GNU Affero General Public License
* // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. /**
* @copyright 2008, 2009 StatusNet, Inc.
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/ */
if (!defined('GNUSOCIAL')) { exit(1); } defined('GNUSOCIAL') || die();
class Memcached_DataObject extends Safe_DataObject class Memcached_DataObject extends Safe_DataObject
{ {
@ -30,7 +32,7 @@ class Memcached_DataObject extends Safe_DataObject
* @param mixed $v key field value, or leave out for primary key lookup * @param mixed $v key field value, or leave out for primary key lookup
* @return mixed Memcached_DataObject subtype or false * @return mixed Memcached_DataObject subtype or false
*/ */
static function getClassKV($cls, $k, $v=null) public static function getClassKV($cls, $k, $v = null)
{ {
if (is_null($v)) { if (is_null($v)) {
$v = $k; $v = $k;
@ -71,7 +73,7 @@ class Memcached_DataObject extends Safe_DataObject
* *
* @return array Array of objects, in order * @return array Array of objects, in order
*/ */
static function multiGetClass($cls, $keyCol, array $keyVals, $skipNulls=true) public static function multiGetClass($cls, $keyCol, array $keyVals, $skipNulls = true)
{ {
$obj = new $cls; $obj = new $cls;
@ -100,8 +102,27 @@ class Memcached_DataObject extends Safe_DataObject
$keyVals[$key] = $obj->escape($val); $keyVals[$key] = $obj->escape($val);
} }
// FIND_IN_SET will make sure we keep the desired order switch (common_config('db', 'type')) {
$obj->orderBy(sprintf("FIND_IN_SET(%s, '%s')", $keyCol, implode(',', $keyVals))); case 'pgsql':
// "position" will make sure we keep the desired order
$obj->orderBy(sprintf(
"position(',' || CAST(%s AS text) || ',' IN ',%s,')",
$keyCol,
implode(',', $keyVals)
));
break;
case 'mysql':
// "find_in_set" will make sure we keep the desired order
$obj->orderBy(sprintf(
"find_in_set(%s, '%s')",
$keyCol,
implode(',', $keyVals)
));
break;
default:
throw new ServerException('Unknown DB type selected.');
}
$obj->find(); $obj->find();
return $obj; return $obj;
@ -117,7 +138,7 @@ class Memcached_DataObject extends Safe_DataObject
* *
* @return array Array mapping $keyVals to objects, or null if not found * @return array Array mapping $keyVals to objects, or null if not found
*/ */
static function pivotGetClass($cls, $keyCol, array $keyVals, array $otherCols = array()) public static function pivotGetClass($cls, $keyCol, array $keyVals, array $otherCols = [])
{ {
if (is_array($keyCol)) { if (is_array($keyCol)) {
foreach ($keyVals as $keyVal) { foreach ($keyVals as $keyVal) {
@ -130,7 +151,6 @@ class Memcached_DataObject extends Safe_DataObject
$toFetch = array(); $toFetch = array();
foreach ($keyVals as $keyVal) { foreach ($keyVals as $keyVal) {
if (is_array($keyCol)) { if (is_array($keyCol)) {
$kv = array_combine($keyCol, $keyVal); $kv = array_combine($keyCol, $keyVal);
} else { } else {
@ -147,7 +167,7 @@ class Memcached_DataObject extends Safe_DataObject
} else { } else {
$result[$keyVal] = $i; $result[$keyVal] = $i;
} }
} else if (!empty($keyVal)) { } elseif (!empty($keyVal)) {
$toFetch[] = $keyVal; $toFetch[] = $keyVal;
} }
} }
@ -207,7 +227,7 @@ class Memcached_DataObject extends Safe_DataObject
return $result; return $result;
} }
static function _inMultiKey($i, $cols, $values) public static function _inMultiKey($i, $cols, $values)
{ {
$types = array(); $types = array();
@ -255,7 +275,7 @@ class Memcached_DataObject extends Safe_DataObject
return $query; return $query;
} }
static function pkeyColsClass($cls) public static function pkeyColsClass($cls)
{ {
$i = new $cls; $i = new $cls;
$types = $i->keyTypes(); $types = $i->keyTypes();
@ -272,7 +292,7 @@ class Memcached_DataObject extends Safe_DataObject
return $pkey; return $pkey;
} }
static function listFindClass($cls, $keyCol, array $keyVals) public static function listFindClass($cls, $keyCol, array $keyVals)
{ {
$i = new $cls; $i = new $cls;
$i->whereAddIn($keyCol, $keyVals, $i->columnType($keyCol)); $i->whereAddIn($keyCol, $keyVals, $i->columnType($keyCol));
@ -283,7 +303,7 @@ class Memcached_DataObject extends Safe_DataObject
return $i; return $i;
} }
static function listGetClass($cls, $keyCol, array $keyVals) public static function listGetClass($cls, $keyCol, array $keyVals)
{ {
$pkeyMap = array_fill_keys($keyVals, array()); $pkeyMap = array_fill_keys($keyVals, array());
$result = array_fill_keys($keyVals, array()); $result = array_fill_keys($keyVals, array());
@ -296,7 +316,7 @@ class Memcached_DataObject extends Safe_DataObject
// We only cache keys -- not objects! // We only cache keys -- not objects!
foreach ($keyVals as $keyVal) { foreach ($keyVals as $keyVal) {
$l = self::cacheGet(sprintf("%s:list-ids:%s:%s", strtolower($cls), $keyCol, $keyVal)); $l = self::cacheGet(sprintf('%s:list-ids:%s:%s', strtolower($cls), $keyCol, $keyVal));
if ($l !== false) { if ($l !== false) {
$pkeyMap[$keyVal] = $l; $pkeyMap[$keyVal] = $l;
foreach ($l as $pkey) { foreach ($l as $pkey) {
@ -312,7 +332,7 @@ class Memcached_DataObject extends Safe_DataObject
foreach ($pkeyMap as $keyVal => $pkeyList) { foreach ($pkeyMap as $keyVal => $pkeyList) {
foreach ($pkeyList as $pkeyVal) { foreach ($pkeyList as $pkeyVal) {
$i = $keyResults[implode(',',$pkeyVal)]; $i = $keyResults[implode(',', $pkeyVal)];
if (!empty($i)) { if (!empty($i)) {
$result[$keyVal][] = $i; $result[$keyVal][] = $i;
} }
@ -338,15 +358,17 @@ class Memcached_DataObject extends Safe_DataObject
// no results found for our keyVals, so we leave them as empty arrays // no results found for our keyVals, so we leave them as empty arrays
} }
foreach ($toFetch as $keyVal) { foreach ($toFetch as $keyVal) {
self::cacheSet(sprintf("%s:list-ids:%s:%s", strtolower($cls), $keyCol, $keyVal), self::cacheSet(
$pkeyMap[$keyVal]); sprintf("%s:list-ids:%s:%s", strtolower($cls), $keyCol, $keyVal),
$pkeyMap[$keyVal]
);
} }
} }
return $result; return $result;
} }
function columnType($columnName) public function columnType($columnName)
{ {
$keys = $this->table(); $keys = $this->table();
if (!array_key_exists($columnName, $keys)) { if (!array_key_exists($columnName, $keys)) {
@ -365,7 +387,7 @@ class Memcached_DataObject extends Safe_DataObject
/** /**
* @todo FIXME: Should this return false on lookup fail to match getKV? * @todo FIXME: Should this return false on lookup fail to match getKV?
*/ */
static function pkeyGetClass($cls, array $kv) public static function pkeyGetClass($cls, array $kv)
{ {
$i = self::multicache($cls, $kv); $i = self::multicache($cls, $kv);
if ($i !== false) { // false == cache miss if ($i !== false) { // false == cache miss
@ -395,7 +417,7 @@ class Memcached_DataObject extends Safe_DataObject
} }
} }
function insert() public function insert()
{ {
$result = parent::insert(); $result = parent::insert();
if ($result) { if ($result) {
@ -405,7 +427,7 @@ class Memcached_DataObject extends Safe_DataObject
return $result; return $result;
} }
function update($dataObject=false) public function update($dataObject = false)
{ {
if (is_object($dataObject) && $dataObject instanceof Memcached_DataObject) { if (is_object($dataObject) && $dataObject instanceof Memcached_DataObject) {
$dataObject->decache(); # might be different keys $dataObject->decache(); # might be different keys
@ -418,17 +440,19 @@ class Memcached_DataObject extends Safe_DataObject
return $result; return $result;
} }
function delete($useWhere=false) public function delete($useWhere = false)
{ {
$this->decache(); # while we still have the values! $this->decache(); # while we still have the values!
return parent::delete($useWhere); return parent::delete($useWhere);
} }
static function memcache() { public static function memcache()
{
return Cache::instance(); return Cache::instance();
} }
static function cacheKey($cls, $k, $v) { public static function cacheKey($cls, $k, $v)
{
if (is_object($cls) || is_object($k) || (is_object($v) && !($v instanceof DB_DataObject_Cast))) { if (is_object($cls) || is_object($k) || (is_object($v) && !($v instanceof DB_DataObject_Cast))) {
$e = new Exception(); $e = new Exception();
common_log(LOG_ERR, __METHOD__ . ' object in param: ' . common_log(LOG_ERR, __METHOD__ . ' object in param: ' .
@ -438,7 +462,8 @@ class Memcached_DataObject extends Safe_DataObject
return Cache::key(strtolower($cls).':'.$k.':'.$vstr); return Cache::key(strtolower($cls).':'.$k.':'.$vstr);
} }
static function getcached($cls, $k, $v) { public static function getcached($cls, $k, $v)
{
$c = self::memcache(); $c = self::memcache();
if (!$c) { if (!$c) {
return false; return false;
@ -456,7 +481,7 @@ class Memcached_DataObject extends Safe_DataObject
} }
} }
function keyTypes() public function keyTypes()
{ {
// ini-based classes return number-indexed arrays. handbuilt // ini-based classes return number-indexed arrays. handbuilt
// classes return column => keytype. Make this uniform. // classes return column => keytype. Make this uniform.
@ -472,18 +497,17 @@ class Memcached_DataObject extends Safe_DataObject
global $_DB_DATAOBJECT; global $_DB_DATAOBJECT;
if (!isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"])) { if (!isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"])) {
$this->databaseStructure(); $this->databaseStructure();
} }
return $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"]; return $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"];
} }
function encache() public function encache()
{ {
$c = self::memcache(); $c = self::memcache();
if (!$c) { if (!$c) {
return false; return false;
} else if ($this->tableName() == 'user' && is_object($this->id)) { } elseif ($this->tableName() === 'user' && is_object($this->id)) {
// Special case for User bug // Special case for User bug
$e = new Exception(); $e = new Exception();
common_log(LOG_ERR, __METHOD__ . ' caching user with User object as ID ' . common_log(LOG_ERR, __METHOD__ . ' caching user with User object as ID ' .
@ -498,7 +522,7 @@ class Memcached_DataObject extends Safe_DataObject
} }
} }
function decache() public function decache()
{ {
$c = self::memcache(); $c = self::memcache();
@ -513,7 +537,7 @@ class Memcached_DataObject extends Safe_DataObject
} }
} }
function _allCacheKeys() public function _allCacheKeys()
{ {
$ckeys = array(); $ckeys = array();
@ -524,7 +548,6 @@ class Memcached_DataObject extends Safe_DataObject
$pval = array(); $pval = array();
foreach ($types as $key => $type) { foreach ($types as $key => $type) {
assert(!empty($key)); assert(!empty($key));
if ($type == 'U') { if ($type == 'U') {
@ -532,7 +555,7 @@ class Memcached_DataObject extends Safe_DataObject
continue; continue;
} }
$ckeys[] = self::cacheKey($this->tableName(), $key, self::valueString($this->$key)); $ckeys[] = self::cacheKey($this->tableName(), $key, self::valueString($this->$key));
} else if ($type == 'K' || $type == 'N') { } elseif (in_array($type, ['K', 'N'])) {
$pkey[] = $key; $pkey[] = $key;
$pval[] = self::valueString($this->$key); $pval[] = self::valueString($this->$key);
} else { } else {
@ -552,7 +575,7 @@ class Memcached_DataObject extends Safe_DataObject
return $ckeys; return $ckeys;
} }
static function multicache($cls, $kv) public static function multicache($cls, $kv)
{ {
ksort($kv); ksort($kv);
$c = self::memcache(); $c = self::memcache();
@ -563,7 +586,7 @@ class Memcached_DataObject extends Safe_DataObject
} }
} }
static function multicacheKey($cls, $kv) public static function multicacheKey($cls, $kv)
{ {
ksort($kv); ksort($kv);
$pkeys = implode(',', array_keys($kv)); $pkeys = implode(',', array_keys($kv));
@ -571,30 +594,35 @@ class Memcached_DataObject extends Safe_DataObject
return self::cacheKey($cls, $pkeys, $pvals); return self::cacheKey($cls, $pkeys, $pvals);
} }
function getSearchEngine($table) public function getSearchEngine($table)
{ {
require_once INSTALLDIR . '/lib/search/search_engines.php'; require_once INSTALLDIR . '/lib/search/search_engines.php';
if (Event::handle('GetSearchEngine', array($this, $table, &$search_engine))) { if (Event::handle('GetSearchEngine', [$this, $table, &$search_engine])) {
if ('mysql' === common_config('db', 'type')) {
$type = common_config('search', 'type'); $type = common_config('search', 'type');
if ($type == 'like') { if ($type === 'like') {
$search_engine = new MySQLLikeSearch($this, $table); $search_engine = new SQLLikeSearch($this, $table);
} else if ($type == 'fulltext') { } elseif ($type === 'fulltext') {
switch (common_config('db', 'type')) {
case 'pgsql':
$search_engine = new PostgreSQLSearch($this, $table);
break;
case 'mysql':
$search_engine = new MySQLSearch($this, $table); $search_engine = new MySQLSearch($this, $table);
break;
default:
throw new ServerException('Unknown DB type selected.');
}
} else { } else {
// Low level exception. No need for i18n as discussed with Brion. // Low level exception. No need for i18n as discussed with Brion.
throw new ServerException('Unknown search type: ' . $type); throw new ServerException('Unknown search type: ' . $type);
} }
} else {
$search_engine = new PGSearch($this, $table);
}
} }
return $search_engine; return $search_engine;
} }
static function cachedQuery($cls, $qry, $expiry=3600) public static function cachedQuery($cls, $qry, $expiry = 3600)
{ {
$c = self::memcache(); $c = self::memcache();
if (!$c) { if (!$c) {
@ -631,7 +659,7 @@ class Memcached_DataObject extends Safe_DataObject
* @access private * @access private
* @return mixed none or PEAR_Error * @return mixed none or PEAR_Error
*/ */
function _query($string) public function _query($string)
{ {
if (common_config('db', 'annotate_queries')) { if (common_config('db', 'annotate_queries')) {
$string = $this->annotateQuery($string); $string = $this->annotateQuery($string);
@ -680,7 +708,7 @@ class Memcached_DataObject extends Safe_DataObject
* @param string $string SQL query string * @param string $string SQL query string
* @return string SQL query string, with a comment in it * @return string SQL query string, with a comment in it
*/ */
function annotateQuery($string) public function annotateQuery($string)
{ {
$ignore = array('annotateQuery', $ignore = array('annotateQuery',
'_query', '_query',
@ -707,7 +735,7 @@ class Memcached_DataObject extends Safe_DataObject
} }
$here = $frame['class'] . '::' . $func; $here = $frame['class'] . '::' . $func;
break; break;
} else if (isset($frame['type']) && $frame['type'] == '->') { } elseif (isset($frame['type']) && $frame['type'] === '->') {
if ($frame['object'] === $this && in_array($func, $ignore)) { if ($frame['object'] === $this && in_array($func, $ignore)) {
continue; continue;
} }
@ -736,7 +764,7 @@ class Memcached_DataObject extends Safe_DataObject
// Sanitize a query for logging // Sanitize a query for logging
// @fixme don't trim spaces in string literals // @fixme don't trim spaces in string literals
function sanitizeQuery($string) public function sanitizeQuery($string)
{ {
$string = preg_replace('/\s+/', ' ', $string); $string = preg_replace('/\s+/', ' ', $string);
$string = trim($string); $string = trim($string);
@ -746,7 +774,7 @@ class Memcached_DataObject extends Safe_DataObject
// We overload so that 'SET NAMES "utf8mb4"' is called for // We overload so that 'SET NAMES "utf8mb4"' is called for
// each connection // each connection
function _connect() public function _connect()
{ {
global $_DB_DATAOBJECT, $_PEAR; global $_DB_DATAOBJECT, $_PEAR;
@ -794,7 +822,7 @@ class Memcached_DataObject extends Safe_DataObject
if (!empty($conn)) { if (!empty($conn)) {
if ($DB instanceof DB_mysqli || $DB instanceof MDB2_Driver_mysqli) { if ($DB instanceof DB_mysqli || $DB instanceof MDB2_Driver_mysqli) {
mysqli_set_charset($conn, 'utf8mb4'); mysqli_set_charset($conn, 'utf8mb4');
} else if ($DB instanceof DB_mysql || $DB instanceof MDB2_Driver_mysql) { } elseif ($DB instanceof DB_mysql || $DB instanceof MDB2_Driver_mysql) {
mysql_set_charset('utf8mb4', $conn); mysql_set_charset('utf8mb4', $conn);
} }
} }
@ -810,7 +838,7 @@ class Memcached_DataObject extends Safe_DataObject
// XXX: largely cadged from DB_DataObject // XXX: largely cadged from DB_DataObject
function _getDbDsnMD5() public function _getDbDsnMD5()
{ {
if ($this->_database_dsn_md5) { if ($this->_database_dsn_md5) {
return $this->_database_dsn_md5; return $this->_database_dsn_md5;
@ -828,7 +856,7 @@ class Memcached_DataObject extends Safe_DataObject
return $sum; return $sum;
} }
function _getDbDsn() public function _getDbDsn()
{ {
global $_DB_DATAOBJECT; global $_DB_DATAOBJECT;
@ -843,14 +871,13 @@ class Memcached_DataObject extends Safe_DataObject
$dsn = isset($this->_database_dsn) ? $this->_database_dsn : null; $dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
if (!$dsn) { if (!$dsn) {
if (!$this->_database) { if (!$this->_database) {
$this->_database = isset($options["table_{$this->tableName()}"]) ? $options["table_{$this->tableName()}"] : null; $this->_database = isset($options["table_{$this->tableName()}"]) ? $options["table_{$this->tableName()}"] : null;
} }
if ($this->_database && !empty($options["database_{$this->_database}"])) { if ($this->_database && !empty($options["database_{$this->_database}"])) {
$dsn = $options["database_{$this->_database}"]; $dsn = $options["database_{$this->_database}"];
} else if (!empty($options['database'])) { } elseif (!empty($options['database'])) {
$dsn = $options['database']; $dsn = $options['database'];
} }
} }
@ -863,7 +890,7 @@ class Memcached_DataObject extends Safe_DataObject
return $dsn; return $dsn;
} }
static function blow() public static function blow()
{ {
$c = self::memcache(); $c = self::memcache();
@ -882,7 +909,7 @@ class Memcached_DataObject extends Safe_DataObject
return $c->delete($cacheKey); return $c->delete($cacheKey);
} }
function fixupTimestamps() public function fixupTimestamps()
{ {
// Fake up timestamp columns // Fake up timestamp columns
$columns = $this->table(); $columns = $this->table();
@ -893,12 +920,12 @@ class Memcached_DataObject extends Safe_DataObject
} }
} }
function debugDump() public function debugDump()
{ {
common_debug("debugDump: " . common_log_objstring($this)); common_debug("debugDump: " . common_log_objstring($this));
} }
function raiseError($message, $type = null, $behaviour = null) public function raiseError($message, $type = null, $behavior = null)
{ {
$id = get_class($this); $id = get_class($this);
if (!empty($this->id)) { if (!empty($this->id)) {
@ -911,7 +938,7 @@ class Memcached_DataObject extends Safe_DataObject
throw new ServerException("[$id] DB_DataObject error [$type]: $message"); throw new ServerException("[$id] DB_DataObject error [$type]: $message");
} }
static function cacheGet($keyPart) public static function cacheGet($keyPart)
{ {
$c = self::memcache(); $c = self::memcache();
@ -924,7 +951,7 @@ class Memcached_DataObject extends Safe_DataObject
return $c->get($cacheKey); return $c->get($cacheKey);
} }
static function cacheSet($keyPart, $value, $flag=null, $expiry=null) public static function cacheSet($keyPart, $value, $flag = null, $expiry = null)
{ {
$c = self::memcache(); $c = self::memcache();
@ -937,7 +964,7 @@ class Memcached_DataObject extends Safe_DataObject
return $c->set($cacheKey, $value, $flag, $expiry); return $c->set($cacheKey, $value, $flag, $expiry);
} }
static function valueString($v) public static function valueString($v)
{ {
$vstr = null; $vstr = null;
if (is_object($v) && $v instanceof DB_DataObject_Cast) { if (is_object($v) && $v instanceof DB_DataObject_Cast) {

View File

@ -1,32 +1,32 @@
<?php <?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
/** /**
* GNU social
*
* Data class for Notice preferences * Data class for Notice preferences
* *
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Data * @category Data
* @package GNUsocial * @package GNUsocial
* @author Mikael Nordfeldth <mmn@hethane.se> * @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2013 Free Software Foundation, Inc. * @author Diogo Cordeiro <diogo@fc.up.pt>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @copyright 2013 Free Software Foundation, Inc http://www.fsf.org
* @link http://www.gnu.org/software/social/ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/ */
defined('GNUSOCIAL') || die();
class Notice_prefs extends Managed_DataObject class Notice_prefs extends Managed_DataObject
{ {
public $__table = 'notice_prefs'; // table name public $__table = 'notice_prefs'; // table name
@ -58,7 +58,7 @@ class Notice_prefs extends Managed_DataObject
); );
} }
static function getNamespacePrefs(Notice $notice, $namespace, array $topic=array()) public static function getNamespacePrefs(Notice $notice, $namespace, array $topic = [])
{ {
if (empty($topic)) { if (empty($topic)) {
$prefs = new Notice_prefs(); $prefs = new Notice_prefs();
@ -76,13 +76,13 @@ class Notice_prefs extends Managed_DataObject
return $prefs; return $prefs;
} }
static function getNamespace(Notice $notice, $namespace, array $topic=array()) public static function getNamespace(Notice $notice, $namespace, array $topic = [])
{ {
$prefs = self::getNamespacePrefs($notice, $namespace, $topic); $prefs = self::getNamespacePrefs($notice, $namespace, $topic);
return $prefs->fetchAll(); return $prefs->fetchAll();
} }
static function getAll(Notice $notice) public static function getAll(Notice $notice)
{ {
try { try {
$prefs = self::listFind('notice_id', array($notice->getID())); $prefs = self::listFind('notice_id', array($notice->getID()));
@ -100,13 +100,17 @@ class Notice_prefs extends Managed_DataObject
return $list; return $list;
} }
static function getTopic(Notice $notice, $namespace, $topic) { public static function getTopic(Notice $notice, $namespace, $topic)
return self::getByPK(array('notice_id' => $notice->getID(), {
return self::getByPK([
'notice_id' => $notice->getID(),
'namespace' => $namespace, 'namespace' => $namespace,
'topic' => $topic)); 'topic' => $topic,
]);
} }
static function getData(Notice $notice, $namespace, $topic, $def=null) { public static function getData(Notice $notice, $namespace, $topic, $def = null)
{
try { try {
$pref = self::getTopic($notice, $namespace, $topic); $pref = self::getTopic($notice, $namespace, $topic);
} catch (NoResultException $e) { } catch (NoResultException $e) {
@ -120,7 +124,8 @@ class Notice_prefs extends Managed_DataObject
return $pref->data; return $pref->data;
} }
static function getConfigData(Notice $notice, $namespace, $topic) { public static function getConfigData(Notice $notice, $namespace, $topic)
{
try { try {
$data = self::getData($notice, $namespace, $topic); $data = self::getData($notice, $namespace, $topic);
} catch (NoResultException $e) { } catch (NoResultException $e) {
@ -140,14 +145,15 @@ class Notice_prefs extends Managed_DataObject
* @return true if changes are made, false if no action taken * @return true if changes are made, false if no action taken
* @throws ServerException if preference could not be saved * @throws ServerException if preference could not be saved
*/ */
static function setData(Notice $notice, $namespace, $topic, $data=null) { public static function setData(Notice $notice, $namespace, $topic, $data = null)
{
try { try {
$pref = self::getTopic($notice, $namespace, $topic); $pref = self::getTopic($notice, $namespace, $topic);
if (is_null($data)) { if (is_null($data)) {
$pref->delete(); $pref->delete();
} else { } else {
$orig = clone($pref); $orig = clone($pref);
$pref->data = $data; $pref->data = DB_DataObject_Cast::blob($data);
$pref->update($orig); $pref->update($orig);
} }
return true; return true;
@ -161,7 +167,7 @@ class Notice_prefs extends Managed_DataObject
$pref->notice_id = $notice->getID(); $pref->notice_id = $notice->getID();
$pref->namespace = $namespace; $pref->namespace = $namespace;
$pref->topic = $topic; $pref->topic = $topic;
$pref->data = $data; $pref->data = DB_DataObject_Cast::blob($data);
$pref->created = common_sql_now(); $pref->created = common_sql_now();
if ($pref->insert() === false) { if ($pref->insert() === false) {

View File

@ -478,13 +478,24 @@ class Profile extends Managed_DataObject
* @return Profile_list resulting lists * @return Profile_list resulting lists
*/ */
public function getOtherTags(Profile $scoped = null, $offset = 0, $limit = null, $since_id = 0, $max_id = 0) public function getOtherTags(Profile $scoped = null, int $offset = 0, ?int $limit = null, int $since = 0, int $upto = 0)
{ {
$list = new Profile_list(); $list = new Profile_list();
if (common_config('db', 'type') !== 'mysql') {
$cursor = sprintf(
'((EXTRACT(DAY %1$s) * 24 + EXTRACT(HOUR %1$s)) * 60 + ' .
'EXTRACT(MINUTE %1$s)) * 60 + FLOOR(EXTRACT(SECOND %1$s)) AS "cursor"',
"FROM (profile_tag.modified - TIMESTAMP '1970-01-01 00:00:00')"
);
} else {
// The SQL/Foundation conforming implementation above doesn't work on MariaDB/MySQL
$cursor = "timestampdiff(SECOND, '1970-01-01', profile_tag.modified) AS `cursor`";
}
$qry = sprintf( $qry = sprintf(
'SELECT profile_list.*, unix_timestamp(profile_tag.modified) AS "cursor" ' . 'SELECT profile_list.*, ' . $cursor . ' ' .
'FROM profile_tag JOIN profile_list '. 'FROM profile_tag INNER JOIN profile_list ' .
'ON (profile_tag.tagger = profile_list.tagger ' . 'ON (profile_tag.tagger = profile_list.tagger ' .
' AND profile_tag.tag = profile_list.tag) ' . ' AND profile_tag.tag = profile_list.tag) ' .
'WHERE profile_tag.tagged = %d ', 'WHERE profile_tag.tagged = %d ',
@ -502,12 +513,12 @@ class Profile extends Managed_DataObject
$qry .= 'AND profile_list.private = FALSE '; $qry .= 'AND profile_list.private = FALSE ';
} }
if ($since_id > 0) { if ($since > 0) {
$qry .= sprintf('AND (cursor > %d) ', $since_id); $qry .= 'AND cursor > ' . $since . ' ';
} }
if ($max_id > 0) { if ($upto > 0) {
$qry .= sprintf('AND (cursor < %d) ', $max_id); $qry .= 'AND cursor < ' . $upto . ' ';
} }
$qry .= 'ORDER BY profile_tag.modified DESC '; $qry .= 'ORDER BY profile_tag.modified DESC ';
@ -558,31 +569,38 @@ class Profile extends Managed_DataObject
return ($tags->N == 0) ? false : true; return ($tags->N == 0) ? false : true;
} }
public function getTagSubscriptions($offset = 0, $limit = null, $since_id = 0, $max_id = 0) public function getTagSubscriptions(int $offset = 0, ?int $limit = null, int $since = 0, int $upto = 0)
{ {
$lists = new Profile_list(); $lists = new Profile_list();
$subs = new Profile_tag_subscription(); $subs = new Profile_tag_subscription();
$lists->joinAdd(array('id', 'profile_tag_subscription:profile_tag_id')); $lists->joinAdd(['id', 'profile_tag_subscription:profile_tag_id']);
#@fixme: postgres (round(date_part('epoch', my_date))) if (common_config('db', 'type') !== 'mysql') {
$lists->selectAdd('unix_timestamp(profile_tag_subscription.created) as "cursor"'); $lists->selectAdd(sprintf(
'((EXTRACT(DAY %1$s) * 24 + EXTRACT(HOUR %1$s)) * 60 + ' .
'EXTRACT(MINUTE %1$s)) * 60 + FLOOR(EXTRACT(SECOND %1$s)) AS "cursor"',
"FROM (profile_tag_subscription.created - TIMESTAMP '1970-01-01 00:00:00')"
));
} else {
$lists->selectAdd("timestampdiff(SECOND, '1970-01-01', profile_tag_subscription.created) AS `cursor`");
}
$lists->whereAdd('profile_tag_subscription.profile_id = '.$this->id); $lists->whereAdd('profile_tag_subscription.profile_id = '.$this->id);
if ($since_id > 0) { if ($since > 0) {
$lists->whereAdd('cursor > ' . $since_id); $lists->whereAdd('cursor > ' . $since);
} }
if ($max_id > 0) { if ($upto > 0) {
$lists->whereAdd('cursor <= ' . $max_id); $lists->whereAdd('cursor <= ' . $upto);
} }
if ($offset >= 0 && !is_null($limit)) { if ($offset >= 0 && !is_null($limit)) {
$lists->limit($offset, $limit); $lists->limit($offset, $limit);
} }
$lists->orderBy('"cursor" DESC'); $lists->orderBy('profile_tag_subscription.created DESC');
$lists->find(); $lists->find();
return $lists; return $lists;

View File

@ -200,7 +200,7 @@ class Profile_list extends Managed_DataObject
* @return Profile results * @return Profile results
*/ */
public function getSubscribers($offset = 0, $limit = null, $since = 0, $upto = 0) public function getSubscribers(int $offset = 0, ?int $limit = null, int $since = 0, int $upto = 0)
{ {
$subs = new Profile(); $subs = new Profile();
@ -209,8 +209,15 @@ class Profile_list extends Managed_DataObject
); );
$subs->whereAdd('profile_tag_subscription.profile_tag_id = ' . $this->id); $subs->whereAdd('profile_tag_subscription.profile_tag_id = ' . $this->id);
$subs->selectAdd('unix_timestamp(profile_tag_subscription.' . if (common_config('db', 'type') !== 'mysql') {
'created) as "cursor"'); $subs->selectAdd(sprintf(
'((EXTRACT(DAY %1$s) * 24 + EXTRACT(HOUR %1$s)) * 60 + ' .
'EXTRACT(MINUTE %1$s)) * 60 + FLOOR(EXTRACT(SECOND %1$s)) AS "cursor"',
"FROM (profile_tag_subscription.created - TIMESTAMP '1970-01-01 00:00:00')"
));
} else {
$subs->selectAdd("timestampdiff(SECOND, '1970-01-01', profile_tag_subscription.created) AS `cursor`");
}
if ($since != 0) { if ($since != 0) {
$subs->whereAdd('cursor > ' . $since); $subs->whereAdd('cursor > ' . $since);
@ -296,13 +303,21 @@ class Profile_list extends Managed_DataObject
* @return Profile results * @return Profile results
*/ */
public function getTagged($offset = 0, $limit = null, $since = 0, $upto = 0) public function getTagged(int $offset = 0, ?int $limit = null, int $since = 0, int $upto = 0)
{ {
$tagged = new Profile(); $tagged = new Profile();
$tagged->joinAdd(array('id', 'profile_tag:tagged')); $tagged->joinAdd(['id', 'profile_tag:tagged']);
if (common_config('db', 'type') !== 'mysql') {
$tagged->selectAdd(sprintf(
'((EXTRACT(DAY %1$s) * 24 + EXTRACT(HOUR %1$s)) * 60 + ' .
'EXTRACT(MINUTE %1$s)) * 60 + FLOOR(EXTRACT(SECOND %1$s)) AS "cursor"',
"FROM (profile_tag.modified - TIMESTAMP '1970-01-01 00:00:00')"
));
} else {
$tagged->selectAdd("timestampdiff(SECOND, '1970-01-01', profile_tag.modified) AS `cursor`");
}
#@fixme: postgres
$tagged->selectAdd('unix_timestamp(profile_tag.modified) as "cursor"');
$tagged->whereAdd('profile_tag.tagger = '.$this->tagger); $tagged->whereAdd('profile_tag.tagger = '.$this->tagger);
$tagged->whereAdd("profile_tag.tag = '{$this->tag}'"); $tagged->whereAdd("profile_tag.tag = '{$this->tag}'");

View File

@ -1,32 +1,31 @@
<?php <?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
/** /**
* StatusNet, the distributed open-source microblogging tool
*
* Data class for Profile preferences * Data class for Profile preferences
* *
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Data * @category Data
* @package GNUsocial * @package GNUsocial
* @author Mikael Nordfeldth <mmn@hethane.se> * @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2013 Free Software Foundation, Inc. * @copyright 2013 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
* @link http://www.gnu.org/software/social/
*/ */
defined('GNUSOCIAL') || die();
class Profile_prefs extends Managed_DataObject class Profile_prefs extends Managed_DataObject
{ {
public $__table = 'profile_prefs'; // table name public $__table = 'profile_prefs'; // table name
@ -58,7 +57,7 @@ class Profile_prefs extends Managed_DataObject
); );
} }
static function getNamespacePrefs(Profile $profile, $namespace, array $topic=array()) public static function getNamespacePrefs(Profile $profile, $namespace, array $topic = [])
{ {
if (empty($topic)) { if (empty($topic)) {
$prefs = new Profile_prefs(); $prefs = new Profile_prefs();
@ -76,13 +75,13 @@ class Profile_prefs extends Managed_DataObject
return $prefs; return $prefs;
} }
static function getNamespace(Profile $profile, $namespace, array $topic=array()) public static function getNamespace(Profile $profile, $namespace, array $topic = [])
{ {
$prefs = self::getNamespacePrefs($profile, $namespace, $topic); $prefs = self::getNamespacePrefs($profile, $namespace, $topic);
return $prefs->fetchAll(); return $prefs->fetchAll();
} }
static function getAll(Profile $profile) public static function getAll(Profile $profile)
{ {
try { try {
$prefs = self::listFind('profile_id', array($profile->getID())); $prefs = self::listFind('profile_id', array($profile->getID()));
@ -100,13 +99,15 @@ class Profile_prefs extends Managed_DataObject
return $list; return $list;
} }
static function getTopic(Profile $profile, $namespace, $topic) { public static function getTopic(Profile $profile, $namespace, $topic)
{
return Profile_prefs::getByPK(array('profile_id' => $profile->getID(), return Profile_prefs::getByPK(array('profile_id' => $profile->getID(),
'namespace' => $namespace, 'namespace' => $namespace,
'topic' => $topic)); 'topic' => $topic));
} }
static function getData(Profile $profile, $namespace, $topic, $def=null) { public static function getData(Profile $profile, $namespace, $topic, $def = null)
{
try { try {
$pref = self::getTopic($profile, $namespace, $topic); $pref = self::getTopic($profile, $namespace, $topic);
} catch (NoResultException $e) { } catch (NoResultException $e) {
@ -120,7 +121,8 @@ class Profile_prefs extends Managed_DataObject
return $pref->data; return $pref->data;
} }
static function getConfigData(Profile $profile, $namespace, $topic) { public static function getConfigData(Profile $profile, $namespace, $topic)
{
try { try {
$data = self::getData($profile, $namespace, $topic); $data = self::getData($profile, $namespace, $topic);
} catch (NoResultException $e) { } catch (NoResultException $e) {
@ -140,14 +142,15 @@ class Profile_prefs extends Managed_DataObject
* @return true if changes are made, false if no action taken * @return true if changes are made, false if no action taken
* @throws ServerException if preference could not be saved * @throws ServerException if preference could not be saved
*/ */
static function setData(Profile $profile, $namespace, $topic, $data=null) { public static function setData(Profile $profile, $namespace, $topic, $data = null)
{
try { try {
$pref = self::getTopic($profile, $namespace, $topic); $pref = self::getTopic($profile, $namespace, $topic);
if (is_null($data)) { if (is_null($data)) {
$pref->delete(); $pref->delete();
} else { } else {
$orig = clone($pref); $orig = clone($pref);
$pref->data = $data; $pref->data = DB_DataObject_Cast::blob($data);
$pref->update($orig); $pref->update($orig);
} }
return true; return true;
@ -161,7 +164,7 @@ class Profile_prefs extends Managed_DataObject
$pref->profile_id = $profile->getID(); $pref->profile_id = $profile->getID();
$pref->namespace = $namespace; $pref->namespace = $namespace;
$pref->topic = $topic; $pref->topic = $topic;
$pref->data = $data; $pref->data = DB_DataObject_Cast::blob($data);
$pref->created = common_sql_now(); $pref->created = common_sql_now();
if ($pref->insert() === false) { if ($pref->insert() === false) {

View File

@ -1804,8 +1804,15 @@ class DB_DataObject extends DB_DataObject_Overload
$kk = (strpos($k, '.') === false && strpos($k, ' ') === false) ? $kk = (strpos($k, '.') === false && strpos($k, ' ') === false) ?
$k : str_replace($replace, '_', $k); $k : str_replace($replace, '_', $k);
if ($dbtype === 'pgsql' && $tableInfo[$i]['type'] == 'bool') { if ($dbtype === 'pgsql') {
switch ($tableInfo[$i]['type']) {
case 'bool':
$array[$k] = str_replace(['t', 'f'], ['1', '0'], $array[$k]); $array[$k] = str_replace(['t', 'f'], ['1', '0'], $array[$k]);
break;
case 'bytea':
$array[$k] = pg_unescape_bytea($array[$k]);
break;
}
} }
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
@ -2433,7 +2440,7 @@ class DB_DataObject extends DB_DataObject_Overload
case 'pgsql': case 'pgsql':
if (!$seq) { if (!$seq) {
$seq = $DB->getSequenceName(strtolower($this->tableName())); $seq = $DB->getSequenceName(strtolower($this->tableName() . '_' . $key));
} }
$db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver']; $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
$method = ($db_driver == 'DB') ? 'getOne' : 'queryOne'; $method = ($db_driver == 'DB') ? 'getOne' : 'queryOne';
@ -2981,8 +2988,15 @@ class DB_DataObject extends DB_DataObject_Overload
$kk = (strpos($k, '.') === false && strpos($k, ' ') === false) ? $kk = (strpos($k, '.') === false && strpos($k, ' ') === false) ?
$k : str_replace($replace, '_', $k); $k : str_replace($replace, '_', $k);
if ($dbtype === 'pgsql' && $tableInfo[$i]['type'] == 'bool') { if ($dbtype === 'pgsql') {
switch ($tableInfo[$i]['type']) {
case 'bool':
$array[$k] = str_replace(['t', 'f'], ['1', '0'], $array[$k]); $array[$k] = str_replace(['t', 'f'], ['1', '0'], $array[$k]);
break;
case 'bytea':
$array[$k] = pg_unescape_bytea($array[$k]);
break;
}
} }
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {

View File

@ -44,13 +44,14 @@ class MysqlSchema extends Schema
* Main public entry point. Use this to get * Main public entry point. Use this to get
* the singleton object. * the singleton object.
* *
* @param null $conn * @param object|null $conn
* @param string|null dummy param
* @return Schema the (single) Schema object * @return Schema the (single) Schema object
*/ */
public static function get($conn = null) public static function get($conn = null, $_ = 'mysql')
{ {
if (empty(self::$_single)) { if (empty(self::$_single)) {
self::$_single = new Schema($conn); self::$_single = new Schema($conn, 'mysql');
} }
return self::$_single; return self::$_single;
} }
@ -105,14 +106,16 @@ class MysqlSchema extends Schema
$field['not null'] = true; $field['not null'] = true;
} }
if ($row['COLUMN_DEFAULT'] !== null) { if ($row['COLUMN_DEFAULT'] !== null) {
// Hack for timestamp cols // Hack for timestamp columns
if ($type == 'timestamp' && $row['COLUMN_DEFAULT'] == 'CURRENT_TIMESTAMP') { if ($row['COLUMN_DEFAULT'] === 'current_timestamp()') {
// skip because timestamp is numerical, but it accepts datetime strings as well // skip timestamp columns as they get a CURRENT_TIMESTAMP default implicitly
if ($type !== 'timestamp') {
$field['default'] = 'CURRENT_TIMESTAMP';
}
} elseif ($this->isNumericType($type)) {
$field['default'] = intval($row['COLUMN_DEFAULT']);
} else { } else {
$field['default'] = $row['COLUMN_DEFAULT']; $field['default'] = $row['COLUMN_DEFAULT'];
if ($this->isNumericType($type)) {
$field['default'] = intval($field['default']);
}
} }
} }
if ($row['COLUMN_KEY'] !== null) { if ($row['COLUMN_KEY'] !== null) {
@ -305,7 +308,7 @@ class MysqlSchema extends Schema
* *
* @param array $phrase * @param array $phrase
*/ */
public function appendAlterDropPrimary(array &$phrase) public function appendAlterDropPrimary(array &$phrase, string $tableName)
{ {
$phrase[] = 'DROP PRIMARY KEY'; $phrase[] = 'DROP PRIMARY KEY';
} }
@ -406,6 +409,8 @@ class MysqlSchema extends Schema
if ($type == 'int' && if ($type == 'int' &&
in_array($size, ['tiny', 'small', 'medium', 'big'])) { in_array($size, ['tiny', 'small', 'medium', 'big'])) {
$type = $size . $type; $type = $size . $type;
} elseif ($type == 'float' && $size == 'big') {
$type = 'double';
} elseif (in_array($type, ['blob', 'text']) && } elseif (in_array($type, ['blob', 'text']) &&
in_array($size, ['tiny', 'medium', 'long'])) { in_array($size, ['tiny', 'medium', 'long'])) {
$type = $size . $type; $type = $size . $type;

View File

@ -40,6 +40,24 @@ defined('GNUSOCIAL') || die();
*/ */
class PgsqlSchema extends Schema class PgsqlSchema extends Schema
{ {
public static $_single = null;
/**
* Main public entry point. Use this to get
* the singleton object.
*
* @param object|null $conn
* @param string|null dummy param
* @return Schema the (single) Schema object
*/
public static function get($conn = null, $_ = 'pgsql')
{
if (empty(self::$_single)) {
self::$_single = new Schema($conn, 'pgsql');
}
return self::$_single;
}
/** /**
* Returns a table definition array for the table * Returns a table definition array for the table
* in the schema with the given name. * in the schema with the given name.
@ -72,7 +90,7 @@ class PgsqlSchema extends Schema
$field = []; $field = [];
$field['type'] = $type = $row['udt_name']; $field['type'] = $type = $row['udt_name'];
if ($type == 'char' || $type == 'varchar') { if (in_array($type, ['char', 'bpchar', 'varchar'])) {
if ($row['character_maximum_length'] !== null) { if ($row['character_maximum_length'] !== null) {
$field['length'] = intval($row['character_maximum_length']); $field['length'] = intval($row['character_maximum_length']);
} }
@ -103,7 +121,8 @@ class PgsqlSchema extends Schema
// Pulling index info from pg_class & pg_index // Pulling index info from pg_class & pg_index
// This can give us primary & unique key info, but not foreign key constraints // This can give us primary & unique key info, but not foreign key constraints
// so we exclude them and pick them up later. // so we exclude them and pick them up later.
$indexInfo = $this->getIndexInfo($table); $indexInfo = $this->fetchIndexInfo($table);
foreach ($indexInfo as $row) { foreach ($indexInfo as $row) {
$keyName = $row['key_name']; $keyName = $row['key_name'];
@ -144,7 +163,7 @@ class PgsqlSchema extends Schema
if ($keyName == "{$table}_pkey") { if ($keyName == "{$table}_pkey") {
$def['primary key'] = $cols; $def['primary key'] = $cols;
} elseif (preg_match("/^{$table}_(.*)_fkey$/", $keyName, $matches)) { } elseif (preg_match("/^{$table}_(.*)_fkey$/", $keyName, $matches)) {
$fkey = $this->getForeignKeyInfo($table, $keyName); $fkey = $this->fetchForeignKeyInfo($table, $keyName);
$colMap = array_combine($cols, $fkey['col_names']); $colMap = array_combine($cols, $fkey['col_names']);
$def['foreign keys'][$keyName] = [$fkey['table_name'], $colMap]; $def['foreign keys'][$keyName] = [$fkey['table_name'], $colMap];
} else { } else {
@ -180,47 +199,42 @@ class PgsqlSchema extends Schema
* @return array of arrays * @return array of arrays
* @throws PEAR_Exception * @throws PEAR_Exception
*/ */
public function getIndexInfo($table) public function fetchIndexInfo(string $table): array
{ {
$query = 'SELECT ' . $query = 'SELECT indexname AS key_name, indexdef AS key_def, pg_index.* ' .
'(SELECT relname FROM pg_class WHERE oid=indexrelid) AS key_name, ' . 'FROM pg_index INNER JOIN pg_indexes ON pg_index.indexrelid = CAST(pg_indexes.indexname AS regclass) ' .
'* FROM pg_index ' . 'WHERE tablename = \'%s\' AND indisprimary = FALSE AND indisunique = FALSE ' .
'WHERE indrelid=(SELECT oid FROM pg_class WHERE relname=\'%s\') ' .
'AND indisprimary=\'f\' AND indisunique=\'f\' ' .
'ORDER BY indrelid, indexrelid'; 'ORDER BY indrelid, indexrelid';
$sql = sprintf($query, $table); $sql = sprintf($query, $table);
return $this->fetchQueryData($sql); return $this->fetchQueryData($sql);
} }
/** /**
* Column names from the foreign table can be resolved with a call to getTableColumnNames()
* @param string $table * @param string $table
* @param $constraint_name * @param string $constraint_name
* @return array array of rows with keys: fkey_name, table_name, table_id, col_names (array of strings) * @return array array of rows with keys: table_name, col_names (array of strings)
* @throws PEAR_Exception * @throws PEAR_Exception
*/ */
public function getForeignKeyInfo($table, $constraint_name) public function fetchForeignKeyInfo(string $table, string $constraint_name): array
{ {
// In a sane world, it'd be easier to query the column names directly. // In a sane world, it'd be easier to query the column names directly.
// But it's pretty hard to work with arrays such as col_indexes in direct SQL here. // But it's pretty hard to work with arrays such as col_indexes in direct SQL here.
$query = 'SELECT ' . $query = 'SELECT ' .
'(SELECT relname FROM pg_class WHERE oid=confrelid) AS table_name, ' . '(SELECT relname FROM pg_class WHERE oid = confrelid) AS table_name, ' .
'confrelid AS table_id, ' . 'confrelid AS table_id, ' .
'(SELECT indkey FROM pg_index WHERE indexrelid=conindid) AS col_indexes ' . '(SELECT indkey FROM pg_index WHERE indexrelid = conindid) AS col_indices ' .
'FROM pg_constraint ' . 'FROM pg_constraint ' .
'WHERE conrelid=(SELECT oid FROM pg_class WHERE relname=\'%s\') ' . 'WHERE conrelid = CAST(\'%s\' AS regclass) AND conname = \'%s\' AND contype = \'f\'';
'AND conname=\'%s\' ' .
'AND contype=\'f\'';
$sql = sprintf($query, $table, $constraint_name); $sql = sprintf($query, $table, $constraint_name);
$data = $this->fetchQueryData($sql); $data = $this->fetchQueryData($sql);
if (count($data) < 1) { if (count($data) < 1) {
throw new Exception("Could not find foreign key " . $constraint_name . " on table " . $table); throw new Exception('Could not find foreign key ' . $constraint_name . ' on table ' . $table);
} }
$row = $data[0]; $row = $data[0];
return [ return [
'table_name' => $row['table_name'], 'table_name' => $row['table_name'],
'col_names' => $this->getTableColumnNames($row['table_id'], $row['col_indexes']) 'col_names' => $this->getTableColumnNames($row['table_id'], $row['col_indices'])
]; ];
} }
@ -252,23 +266,6 @@ class PgsqlSchema extends Schema
return $out; return $out;
} }
/**
* Translate the (mostly) mysql-ish column types into somethings more standard
* @param string column type
*
* @return string postgres happy column type
*/
private function _columnTypeTranslation($type)
{
$map = [
'datetime' => 'timestamp',
];
if (!empty($map[$type])) {
return $map[$type];
}
return $type;
}
/** /**
* Return the proper SQL for creating or * Return the proper SQL for creating or
* altering a column. * altering a column.
@ -296,8 +293,11 @@ class PgsqlSchema extends Schema
} }
*/ */
if (!empty($cd['enum'])) { // This'll have been added from our transform of 'serial' type
foreach($cd['enum'] as &$val) { if (!empty($cd['auto_increment'])) {
$line[] = 'GENERATED BY DEFAULT AS IDENTITY';
} elseif (!empty($cd['enum'])) {
foreach ($cd['enum'] as &$val) {
$vals[] = "'" . $val . "'"; $vals[] = "'" . $val . "'";
} }
$line[] = 'CHECK (' . $name . ' IN (' . implode(',', $vals) . '))'; $line[] = 'CHECK (' . $name . ' IN (' . implode(',', $vals) . '))';
@ -338,6 +338,12 @@ class PgsqlSchema extends Schema
} }
} }
public function appendAlterDropPrimary(array &$phrase, string $tableName)
{
// name hack -- is this reliable?
$phrase[] = 'DROP CONSTRAINT ' . $this->quoteIdentifier($tableName . '_pkey');
}
/** /**
* Append an SQL statement to drop an index from a table. * Append an SQL statement to drop an index from a table.
* Note that in PostgreSQL, index names are DB-unique. * Note that in PostgreSQL, index names are DB-unique.
@ -354,9 +360,8 @@ class PgsqlSchema extends Schema
public function mapType($column) public function mapType($column)
{ {
$map = [ $map = [
'serial' => 'bigserial', // FIXME: creates the wrong name for the sequence for some internal sequence-lookup function, so better fix this to do the real 'create sequence' dance. 'integer' => 'int',
'bool' => 'boolean', 'char' => 'bpchar',
'numeric' => 'decimal',
'datetime' => 'timestamp', 'datetime' => 'timestamp',
'blob' => 'bytea', 'blob' => 'bytea',
'enum' => 'text', 'enum' => 'text',
@ -367,16 +372,17 @@ class PgsqlSchema extends Schema
$type = $map[$type]; $type = $map[$type];
} }
if ($type == 'int') { $size = $column['size'] ?? null;
if (!empty($column['size'])) { if ($type === 'int') {
$size = $column['size']; if (in_array($size, ['tiny', 'small'])) {
if ($size == 'small') { $type = 'int2';
return 'int2'; } elseif ($size === 'big') {
} elseif ($size == 'big') { $type = 'int8';
return 'int8'; } else {
$type = 'int4';
} }
} } elseif ($type === 'float') {
return 'int4'; $type = ($size !== 'big') ? 'float4' : 'float8';
} }
return $type; return $type;
@ -398,17 +404,33 @@ class PgsqlSchema extends Schema
// No convenient support for field descriptions // No convenient support for field descriptions
unset($col['description']); unset($col['description']);
/* switch ($col['type']) {
if (isset($col['size'])) { case 'serial':
// Don't distinguish between tinyint and int. $col['type'] = 'int';
if ($col['size'] == 'tiny' && $col['type'] == 'int') { $col['auto_increment'] = true;
unset($col['size']); break;
case 'datetime':
// Replace archaic MySQL-specific zero-dates with NULL
if (($col['default'] ?? null) === '0000-00-00 00:00:00') {
$col['default'] = null;
$col['not null'] = false;
} }
break;
case 'timestamp':
// In MariaDB: If the column does not permit NULL values,
// assigning NULL (or not referencing the column at all
// when inserting) will set the column to CURRENT_TIMESTAMP
// FIXME: ON UPDATE CURRENT_TIMESTAMP
if ($col['not null'] && !isset($col['default'])) {
$col['default'] = 'CURRENT_TIMESTAMP';
} }
*/ break;
}
$col['type'] = $this->mapType($col); $col['type'] = $this->mapType($col);
unset($col['size']); unset($col['size']);
} }
if (!empty($tableDef['primary key'])) { if (!empty($tableDef['primary key'])) {
$tableDef['primary key'] = $this->filterKeyDef($tableDef['primary key']); $tableDef['primary key'] = $this->filterKeyDef($tableDef['primary key']);
} }

View File

@ -64,9 +64,10 @@ class Schema
* the schema object. * the schema object.
* *
* @param object|null $conn * @param object|null $conn
* @param string|null Force a database type (necessary for installation purposes in which we don't have a config.php)
* @return Schema the Schema object for the connection * @return Schema the Schema object for the connection
*/ */
public static function get($conn = null) public static function get($conn = null, $dbtype = null)
{ {
if (is_null($conn)) { if (is_null($conn)) {
$key = 'default'; $key = 'default';
@ -74,9 +75,11 @@ class Schema
$key = md5(serialize($conn->dsn)); $key = md5(serialize($conn->dsn));
} }
$type = common_config('db', 'type'); if (is_null($dbtype)) {
$dbtype = common_config('db', 'type');
}
if (empty(self::$_static[$key])) { if (empty(self::$_static[$key])) {
$schemaClass = ucfirst($type) . 'Schema'; $schemaClass = ucfirst($dbtype) . 'Schema';
self::$_static[$key] = new $schemaClass($conn); self::$_static[$key] = new $schemaClass($conn);
} }
return self::$_static[$key]; return self::$_static[$key];
@ -369,6 +372,8 @@ class Schema
{ {
global $_PEAR; global $_PEAR;
$qry = [];
if (!is_array($columnNames)) { if (!is_array($columnNames)) {
$columnNames = [$columnNames]; $columnNames = [$columnNames];
} }
@ -377,11 +382,9 @@ class Schema
$name = "{$table}_" . implode("_", $columnNames) . "_idx"; $name = "{$table}_" . implode("_", $columnNames) . "_idx";
} }
$res = $this->conn->query( $this->appendCreateIndex($qry, $table, $name, $columnNames);
'ALTER TABLE ' . $this->quoteIdentifier($table) .
' ADD INDEX ' . $name . ' (' . $res = $this->conn->query(implode('; ', $qry));
implode(',', $columnNames) . ')'
);
if ($_PEAR->isError($res)) { if ($_PEAR->isError($res)) {
PEAR_ErrorToPEAR_Exception($res); PEAR_ErrorToPEAR_Exception($res);
@ -602,7 +605,7 @@ class Schema
} }
if (isset($old['primary key']) && (!isset($def['primary key']) || $def['primary key'] != $old['primary key'])) { if (isset($old['primary key']) && (!isset($def['primary key']) || $def['primary key'] != $old['primary key'])) {
$this->appendAlterDropPrimary($phrase); $this->appendAlterDropPrimary($phrase, $tableName);
} }
foreach ($fields['add'] as $columnName) { foreach ($fields['add'] as $columnName) {
@ -765,7 +768,7 @@ class Schema
$phrase[] = implode(' ', $sql); $phrase[] = implode(' ', $sql);
} }
public function appendAlterDropPrimary(array &$phrase) public function appendAlterDropPrimary(array &$phrase, string $tableName)
{ {
$phrase[] = 'DROP CONSTRAINT PRIMARY KEY'; $phrase[] = 'DROP CONSTRAINT PRIMARY KEY';
} }

View File

@ -70,6 +70,43 @@ class SearchEngine
} }
} }
class PostgreSQLSearch extends SearchEngine
{
public function query($q)
{
if ($this->table === 'profile') {
$cols = implode(" || ' ' || ", array_map(
function ($col) {
return sprintf(
"COALESCE(%s.%s, '')",
common_database_tablename($this->table),
$col
);
},
['nickname', 'fullname', 'location', 'bio', 'homepage']
));
$this->target->whereAdd(sprintf(
'to_tsvector(\'english\', %2$s) @@ plainto_tsquery(\'%1$s\')',
$this->target->escape($q, true),
$cols
));
return true;
} elseif ($this->table === 'notice') {
// Don't show imported notices
$this->target->whereAdd('notice.is_local <> ' . Notice::GATEWAY);
$this->target->whereAdd(sprintf(
'to_tsvector(\'english\', content) @@ plainto_tsquery(\'%1$s\')',
$this->target->escape($q, true)
));
return true;
} else {
throw new ServerException('Unknown table: ' . $this->table);
}
}
}
class MySQLSearch extends SearchEngine class MySQLSearch extends SearchEngine
{ {
public function query($q) public function query($q)
@ -120,7 +157,7 @@ class MySQLSearch extends SearchEngine
} }
} }
class MySQLLikeSearch extends SearchEngine class SQLLikeSearch extends SearchEngine
{ {
public function query($q) public function query($q)
{ {
@ -145,18 +182,3 @@ class MySQLLikeSearch extends SearchEngine
return true; return true;
} }
} }
class PGSearch extends SearchEngine
{
public function query($q)
{
if ($this->table === 'profile') {
return $this->target->whereAdd('textsearch @@ plainto_tsquery(\'' . $this->target->escape($q) . '\')');
} elseif ($this->table === 'notice') {
// XXX: We need to filter out gateway notices (notice.is_local = -2) --Zach
return $this->target->whereAdd('to_tsvector(\'english\', content) @@ plainto_tsquery(\'' . $this->target->escape($q) . '\')');
} else {
throw new ServerException('Unknown table: ' . $this->table);
}
}
}

View File

@ -68,11 +68,11 @@ abstract class Installer
'check_module' => 'mysqli', 'check_module' => 'mysqli',
'scheme' => 'mysqli', // DSN prefix for PEAR::DB 'scheme' => 'mysqli', // DSN prefix for PEAR::DB
], ],
/*'pgsql' => [ 'pgsql' => [
'name' => 'PostgreSQL', 'name' => 'PostgreSQL 11+',
'check_module' => 'pgsql', 'check_module' => 'pgsql',
'scheme' => 'pgsql', // DSN prefix for PEAR::DB 'scheme' => 'pgsql', // DSN prefix for PEAR::DB
]*/ ]
]; ];
/** /**
@ -304,20 +304,34 @@ abstract class Installer
throw new Exception('Cannot connect to database: ' . $conn->getMessage()); throw new Exception('Cannot connect to database: ' . $conn->getMessage());
} }
// ensure database encoding is UTF8 switch ($this->dbtype) {
$conn->query('SET NAMES utf8mb4'); case 'pgsql':
if ($this->dbtype == 'mysql') { // ensure the database encoding is UTF8
$server_encoding = $conn->getRow("SHOW VARIABLES LIKE 'character_set_server'")[1]; $conn->query("SET NAMES 'UTF8'");
if ($server_encoding != 'utf8mb4') {
$this->updateStatus("GNU social requires UTF8 character encoding. Your database is " . htmlentities($server_encoding));
return false;
}
} elseif ($this->dbtype == 'pgsql') {
$server_encoding = $conn->getRow('SHOW server_encoding')[0]; $server_encoding = $conn->getRow('SHOW server_encoding')[0];
if ($server_encoding != 'UTF8') { if ($server_encoding !== 'UTF8') {
$this->updateStatus("GNU social requires UTF8 character encoding. Your database is " . htmlentities($server_encoding)); $this->updateStatus(
'GNU social requires the UTF8 character encoding. Yours is ' .
htmlentities($server_encoding)
);
return false; return false;
} }
break;
case 'mysql':
// ensure the database encoding is utf8mb4
$conn->query("SET NAMES 'utf8mb4'");
$server_encoding = $conn->getRow("SHOW VARIABLES LIKE 'character_set_server'")[1];
if ($server_encoding !== 'utf8mb4') {
$this->updateStatus(
'GNU social requires the utf8mb4 character encoding. Yours is ' .
htmlentities($server_encoding)
);
return false;
}
break;
default:
$this->updateStatus('Unknown DB type selected: ' . $this->dbtype);
return false;
} }
$res = $this->updateStatus("Creating database tables..."); $res = $this->updateStatus("Creating database tables...");
@ -362,7 +376,7 @@ abstract class Installer
*/ */
public function createCoreTables(DB_common $conn): bool public function createCoreTables(DB_common $conn): bool
{ {
$schema = Schema::get($conn); $schema = Schema::get($conn, $this->dbtype);
$tableDefs = $this->getCoreSchema(); $tableDefs = $this->getCoreSchema();
foreach ($tableDefs as $name => $def) { foreach ($tableDefs as $name => $def) {
if (defined('DEBUG_INSTALLER')) { if (defined('DEBUG_INSTALLER')) {

View File

@ -83,7 +83,8 @@ class Spam_score extends Managed_DataObject
'notice_id' => array('type' => 'int', 'notice_id' => array('type' => 'int',
'not null' => true, 'not null' => true,
'description' => 'notice getting scored'), 'description' => 'notice getting scored'),
'score' => array('type' => 'double', 'score' => array('type' => 'float',
'size' => 'big',
'not null' => true, 'not null' => true,
'description' => 'score for the notice (0.0, 1.0)'), 'description' => 'score for the notice (0.0, 1.0)'),
'scaled' => array('type' => 'int', 'scaled' => array('type' => 'int',

View File

@ -1,33 +1,32 @@
<?php <?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
/** /**
* StatusNet, the distributed open-source microblogging tool
*
* Simple-minded queue manager for storing items in the database * Simple-minded queue manager for storing items in the database
* *
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category QueueManager * @category QueueManager
* @package StatusNet * @package GNUsocial
* @author Evan Prodromou <evan@status.net> * @author Evan Prodromou <evan@status.net>
* @author Brion Vibber <brion@status.net> * @author Brion Vibber <brion@status.net>
* @copyright 2009-2010 StatusNet, Inc. * @copyright 2009-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
* @link http://status.net/
*/ */
defined('GNUSOCIAL') || die();
class DBQueueManager extends QueueManager class DBQueueManager extends QueueManager
{ {
/** /**
@ -39,7 +38,7 @@ class DBQueueManager extends QueueManager
{ {
$qi = new Queue_item(); $qi = new Queue_item();
$qi->frame = $this->encode($object); $qi->frame = DB_DataObject_Cast::blob($this->encode($object));
$qi->transport = $queue; $qi->transport = $queue;
$qi->created = common_sql_now(); $qi->created = common_sql_now();
$result = $qi->insert(); $result = $qi->insert();
@ -121,7 +120,8 @@ class DBQueueManager extends QueueManager
// What to do if no handler was found. For example, the OpportunisticQM // What to do if no handler was found. For example, the OpportunisticQM
// should avoid deleting items just because it can't reach XMPP queues etc. // should avoid deleting items just because it can't reach XMPP queues etc.
protected function noHandlerFound(Queue_item $qi, $rep=null) { protected function noHandlerFound(Queue_item $qi, $rep = null)
{
$this->_log(LOG_INFO, "[{$qi->transport}:{$rep}] No handler for queue {$qi->transport}; discarding."); $this->_log(LOG_INFO, "[{$qi->transport}:{$rep}] No handler for queue {$qi->transport}; discarding.");
$this->_done($qi); $this->_done($qi);
} }

View File

@ -1,21 +1,23 @@
#!/usr/bin/env php #!/usr/bin/env php
<?php <?php
/* // This file is part of GNU social - https://www.gnu.org/software/social
* StatusNet - a distributed open-source microblogging tool //
* Copyright (C) 2010, StatusNet, Inc. // GNU social is free software: you can redistribute it and/or modify
* // it under the terms of the GNU Affero General Public License as published by
* This program is free software: you can redistribute it and/or modify // the Free Software Foundation, either version 3 of the License, or
* it under the terms of the GNU Affero General Public License as published by // (at your option) any later version.
* the Free Software Foundation, either version 3 of the License, or //
* (at your option) any later version. // GNU social is distributed in the hope that it will be useful,
* // but WITHOUT ANY WARRANTY; without even the implied warranty of
* This program is distributed in the hope that it will be useful, // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* but WITHOUT ANY WARRANTY; without even the implied warranty of // GNU Affero General Public License for more details.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
* GNU Affero General Public License for more details. // You should have received a copy of the GNU Affero General Public License
* // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. /**
* @copyright 2010 StatusNet, Inc.
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/ */
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
@ -39,7 +41,8 @@ END_OF_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc'; require_once INSTALLDIR.'/scripts/commandline.inc';
function showProfileInfo(Ostatus_profile $oprofile) { function showProfileInfo(Ostatus_profile $oprofile)
{
if ($oprofile->isGroup()) { if ($oprofile->isGroup()) {
echo "group\n"; echo "group\n";
} else { } else {
@ -51,7 +54,8 @@ function showProfileInfo(Ostatus_profile $oprofile) {
echo "\n"; echo "\n";
} }
function fixProfile(Ostatus_profile $oprofile) { function fixProfile(Ostatus_profile $oprofile)
{
echo "Before:\n"; echo "Before:\n";
showProfileInfo($oprofile); showProfileInfo($oprofile);
@ -102,10 +106,10 @@ if (have_option('all')) {
echo "Failed on URI=="._ve($oprofile->uri).": {$e->getMessage()}\n"; echo "Failed on URI=="._ve($oprofile->uri).": {$e->getMessage()}\n";
} }
} }
} else if (have_option('suspicious')) { } elseif (have_option('suspicious')) {
$oprofile = new Ostatus_profile(); $oprofile = new Ostatus_profile();
$oprofile->joinAdd(array('profile_id', 'profile:id')); $oprofile->joinAdd(['profile_id', 'profile:id']);
$oprofile->whereAdd("nickname rlike '^[0-9]$'"); $oprofile->whereAdd("CHAR_LENGTH(nickname) = 1 AND nickname BETWEEN '0' AND '9'");
$oprofile->find(); $oprofile->find();
echo "Found $oprofile->N matching profiles:\n\n"; echo "Found $oprofile->N matching profiles:\n\n";
while ($oprofile->fetch()) { while ($oprofile->fetch()) {
@ -116,7 +120,7 @@ if (have_option('all')) {
echo "Failed on URI=="._ve($oprofile->uri).": {$e->getMessage()}\n"; echo "Failed on URI=="._ve($oprofile->uri).": {$e->getMessage()}\n";
} }
} }
} else if (!empty($args[0]) && $validate->uri($args[0])) { } elseif (!empty($args[0]) && $validate->uri($args[0])) {
$uri = $args[0]; $uri = $args[0];
$oprofile = Ostatus_profile::getKV('uri', $uri); $oprofile = Ostatus_profile::getKV('uri', $uri);

View File

@ -1,25 +1,25 @@
<?php <?php
/* // This file is part of GNU social - https://www.gnu.org/software/social
* StatusNet - the distributed open-source microblogging tool //
* Copyright (C) 2008, 2009, StatusNet, Inc. // GNU social is free software: you can redistribute it and/or modify
* // it under the terms of the GNU Affero General Public License as published by
* This program is free software: you can redistribute it and/or modify // the Free Software Foundation, either version 3 of the License, or
* it under the terms of the GNU Affero General Public License as published by // (at your option) any later version.
* the Free Software Foundation, either version 3 of the License, or //
* (at your option) any later version. // GNU social is distributed in the hope that it will be useful,
* // but WITHOUT ANY WARRANTY; without even the implied warranty of
* This program is distributed in the hope that it will be useful, // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* but WITHOUT ANY WARRANTY; without even the implied warranty of // GNU Affero General Public License for more details.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
* GNU Affero General Public License for more details. // You should have received a copy of the GNU Affero General Public License
* // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. /**
* @copyright 2008, 2009 StatusNet, Inc.
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/ */
if (!defined('STATUSNET')) { defined('GNUSOCIAL') || die();
exit(1);
}
require_once('Auth/OpenID.php'); require_once('Auth/OpenID.php');
require_once('Auth/OpenID/Consumer.php'); require_once('Auth/OpenID/Consumer.php');
@ -49,19 +49,19 @@ function oid_store()
} }
$db = DB::connect($dsn, $options); $db = DB::connect($dsn, $options);
if (PEAR::isError($db)) { if ((new PEAR)->isError($db)) {
throw new ServerException($db->getMessage()); throw new ServerException($db->getMessage());
} }
switch (common_config('db', 'type')) { switch (common_config('db', 'type')) {
case 'pgsql':
$store = new Auth_OpenID_PostgreSQLStore($db);
break;
case 'mysql': case 'mysql':
$store = new Auth_OpenID_MySQLStore($db); $store = new Auth_OpenID_MySQLStore($db);
break; break;
case 'postgresql':
$store = new Auth_OpenID_PostgreSQLStore($db);
break;
default: default:
throw new ServerException(_m('Unknown DB type for OpenID.')); throw new ServerException('Unknown DB type selected.');
} }
} }
return $store; return $store;
@ -90,9 +90,11 @@ function oid_clear_last()
function oid_set_last($openid_url) function oid_set_last($openid_url)
{ {
common_set_cookie(OPENID_COOKIE_KEY, common_set_cookie(
OPENID_COOKIE_KEY,
$openid_url, $openid_url,
time() + OPENID_COOKIE_EXPIRY); time() + OPENID_COOKIE_EXPIRY
);
} }
function oid_get_last() function oid_get_last()
@ -119,7 +121,7 @@ function oid_link_user($id, $canonical, $display)
$oid->created = common_sql_now(); $oid->created = common_sql_now();
if (!$oid->insert()) { if (!$oid->insert()) {
$err = &$_PEAR->getStaticProperty('DB_DataObject','lastError'); $err = &$_PEAR->getStaticProperty('DB_DataObject', 'lastError');
return false; return false;
} }
@ -149,9 +151,11 @@ function oid_check_immediate($openid_url, $backto=null)
$_SESSION['openid_immediate_backto'] = $backto; $_SESSION['openid_immediate_backto'] = $backto;
oid_authenticate($openid_url, oid_authenticate(
$openid_url,
'finishimmediate', 'finishimmediate',
true); true
);
} }
function oid_authenticate($openid_url, $returnto, $immediate=false) function oid_authenticate($openid_url, $returnto, $immediate=false)
@ -177,23 +181,27 @@ function oid_authenticate($openid_url, $returnto, $immediate=false)
common_log(LOG_ERR, __METHOD__ . ": mystery fail contacting $openid_url"); common_log(LOG_ERR, __METHOD__ . ": mystery fail contacting $openid_url");
// TRANS: OpenID plugin message. Given when an OpenID is not valid. // TRANS: OpenID plugin message. Given when an OpenID is not valid.
throw new ServerException(_m('Not a valid OpenID.')); throw new ServerException(_m('Not a valid OpenID.'));
} else if (Auth_OpenID::isFailure($auth_request)) { } elseif (Auth_OpenID::isFailure($auth_request)) {
common_log(LOG_ERR, __METHOD__ . ": OpenID fail to $openid_url: $auth_request->message"); common_log(LOG_ERR, __METHOD__ . ": OpenID fail to $openid_url: $auth_request->message");
// TRANS: OpenID plugin server error. Given when the OpenID authentication request fails. // TRANS: OpenID plugin server error. Given when the OpenID authentication request fails.
// TRANS: %s is the failure message. // TRANS: %s is the failure message.
throw new ServerException(sprintf(_m('OpenID failure: %s.'), $auth_request->message)); throw new ServerException(sprintf(_m('OpenID failure: %s.'), $auth_request->message));
} }
$sreg_request = Auth_OpenID_SRegRequest::build(// Required $sreg_request = Auth_OpenID_SRegRequest::build(
array(), // Required
[],
// Optional // Optional
array('nickname', [
'nickname',
'email', 'email',
'fullname', 'fullname',
'language', 'language',
'timezone', 'timezone',
'postcode', 'postcode',
'country')); 'country',
]
);
if ($sreg_request) { if ($sreg_request) {
$auth_request->addExtension($sreg_request); $auth_request->addExtension($sreg_request);
@ -224,9 +232,11 @@ function oid_authenticate($openid_url, $returnto, $immediate=false)
// autosubmitter for now. // autosubmitter for now.
// //
//if ($auth_request->shouldSendRedirect()) { //if ($auth_request->shouldSendRedirect()) {
$redirect_url = $auth_request->redirectURL($trust_root, $redirect_url = $auth_request->redirectURL(
$trust_root,
$process_url, $process_url,
$immediate); $immediate
);
if (Auth_OpenID::isFailure($redirect_url)) { if (Auth_OpenID::isFailure($redirect_url)) {
// TRANS: OpenID plugin server error. Given when the OpenID authentication request cannot be redirected. // TRANS: OpenID plugin server error. Given when the OpenID authentication request cannot be redirected.
// TRANS: %s is the failure message. // TRANS: %s is the failure message.
@ -266,11 +276,14 @@ function oid_authenticate($openid_url, $returnto, $immediate=false)
function _oid_print_instructions() function _oid_print_instructions()
{ {
common_element('div', 'instructions', common_element(
'div',
'instructions',
// TRANS: OpenID plugin user instructions. // TRANS: OpenID plugin user instructions.
_m('This form should automatically submit itself. '. _m('This form should automatically submit itself. '.
'If not, click the submit button to go to your '. 'If not, click the submit button to go to your '.
'OpenID provider.')); 'OpenID provider.')
);
} }
/** /**
@ -382,22 +395,22 @@ function oid_check_teams($response)
class AutosubmitAction extends Action class AutosubmitAction extends Action
{ {
var $form_html = null; public $form_html = null;
var $form_id = null; public $form_id = null;
function handle() public function handle()
{ {
parent::handle(); parent::handle();
$this->showPage(); $this->showPage();
} }
function title() public function title()
{ {
// TRANS: Title // TRANS: Title
return _m('OpenID Login Submission'); return _m('OpenID Login Submission');
} }
function showContent() public function showContent()
{ {
$this->raw('<p style="margin: 20px 80px">'); $this->raw('<p style="margin: 20px 80px">');
// @todo FIXME: This would be better using standard CSS class, but the present theme's a bit scary. // @todo FIXME: This would be better using standard CSS class, but the present theme's a bit scary.
@ -414,10 +427,13 @@ class AutosubmitAction extends Action
$this->raw($this->form_html); $this->raw($this->form_html);
} }
function showScripts() public function showScripts()
{ {
parent::showScripts(); parent::showScripts();
$this->element('script', null, $this->element(
'document.getElementById(\'' . $this->form_id . '\').submit();'); 'script',
null,
'document.getElementById(\'' . $this->form_id . '\').submit();'
);
} }
} }

View File

@ -22,7 +22,7 @@ client side, which itself depends on the sphinx development files.
"pecl install sphinx" should take care of that. Add "extension=sphinx.so" "pecl install sphinx" should take care of that. Add "extension=sphinx.so"
to your php.ini and reload apache to enable it. to your php.ini and reload apache to enable it.
You can update your MySQL or Postgresql databases to drop their fulltext You can update your MariaDB or PostgreSQL databases to drop their fulltext
search indexes, since they're now provided by sphinx. search indexes, since they're now provided by sphinx.

View File

@ -605,14 +605,26 @@ function fixupFileThumbnailUrlhash()
{ {
printfnq("Setting urlhash for File_thumbnail entries: "); printfnq("Setting urlhash for File_thumbnail entries: ");
switch (common_config('db', 'type')) {
case 'pgsql':
$url_sha256 = 'encode(sha256(CAST("url" AS bytea)), \'hex\')';
break;
case 'mysql':
$url_sha256 = 'sha2(`url`, 256)';
break;
default:
throw new Exception('Unknown DB type selected.');
}
$thumb = new File_thumbnail(); $thumb = new File_thumbnail();
$thumb->query(sprintf( $thumb->query(sprintf(
'UPDATE %1$s ' . 'UPDATE %1$s ' .
'SET urlhash = sha2(url, 256) ' . 'SET urlhash = %2$s ' .
'WHERE url IS NOT NULL ' . // find all entries with a url value 'WHERE url IS NOT NULL ' . // find all entries with a url value
"AND url <> '' " . // precaution against non-null empty strings "AND url <> '' " . // precaution against non-null empty strings
'AND urlhash IS NULL', // but don't touch those we've already calculated 'AND urlhash IS NULL', // but don't touch those we've already calculated
$thumb->escapedTableName() $thumb->escapedTableName(),
$url_sha256
)); ));
printfnq("DONE.\n"); printfnq("DONE.\n");