[DATABASE] Re-introduce PostgreSQL support
This commit is contained in:
parent
e807e3bf08
commit
16b5ddd230
|
@ -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
|
||||||
|
|
|
@ -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');
|
||||||
|
|
||||||
|
|
|
@ -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...";
|
||||||
|
|
|
@ -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...";
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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,7 +871,6 @@ 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;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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}'");
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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'])) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,7 +293,10 @@ class PgsqlSchema extends Schema
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!empty($cd['enum'])) {
|
// This'll have been added from our transform of 'serial' type
|
||||||
|
if (!empty($cd['auto_increment'])) {
|
||||||
|
$line[] = 'GENERATED BY DEFAULT AS IDENTITY';
|
||||||
|
} elseif (!empty($cd['enum'])) {
|
||||||
foreach ($cd['enum'] as &$val) {
|
foreach ($cd['enum'] as &$val) {
|
||||||
$vals[] = "'" . $val . "'";
|
$vals[] = "'" . $val . "'";
|
||||||
}
|
}
|
||||||
|
@ -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']);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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';
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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')) {
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
@ -104,8 +108,8 @@ if (have_option('all')) {
|
||||||
}
|
}
|
||||||
} elseif (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()) {
|
||||||
|
|
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -184,16 +188,20 @@ function oid_authenticate($openid_url, $returnto, $immediate=false)
|
||||||
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();'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Reference in New Issue
Block a user