1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-07 15:26:54 +02:00

Various minor updates. Plus add a $database->isStopword() method and improve error logging in WireDatabasePDOStatement

This commit is contained in:
Ryan Cramer
2020-06-19 12:35:21 -04:00
parent 32e031ab7d
commit d79d9286b1
10 changed files with 115 additions and 20 deletions

View File

@@ -120,8 +120,7 @@ abstract class DatabaseQuery extends WireData {
* Get or set a bind option
*
* @param string|bool $optionName One of 'prefix' or 'global', boolean true to get/set all
* @param null|int|string|array $optionValue Omit when getting, Specify option value to set, or array when setting
* all
* @param null|int|string|array $optionValue Omit when getting, Specify option value to set, or array when setting all
* @return string|int|array
* @since 3.0.157
*

View File

@@ -3,7 +3,7 @@
/**
* ProcessWire DatabaseStopwords
*
* MySQL stopwords, primarily for use with filtering fulltext queries
* MySQL stopwords, primarily for use with filtering fulltext queries (MyISAM only)
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
@@ -49,8 +49,8 @@ class DatabaseStopwords {
"various", "very", "via", "viz", "vs", "want", "wants", "was", "wasn't", "way", "we", "we'd", "we'll", "we're", "we've", "welcome", "well", "went", "were",
"weren't", "what", "what's", "whatever", "when", "whence", "whenever", "where", "where's", "whereafter", "whereas", "whereby", "wherein", "whereupon",
"wherever", "whether", "which", "while", "whither", "who", "who's", "whoever", "whole", "whom", "whose", "why", "will", "willing", "wish", "with", "within",
"without", "won't", "wonder", "would", "would", "wouldn't", "yes", "yet", "you", "you'd", "you'll", "you're", "you've", "your", "yours", "yourself",
"yourselves", "zero");
"without", "won't", "wonder", "would", "wouldn't", "yes", "yet", "you", "you'd", "you'll", "you're", "you've", "your", "yours", "yourself", "yourselves", "zero"
);
/**
* Get all stopwords

View File

@@ -711,9 +711,7 @@ abstract class Fieldtype extends WireData implements Module {
$table = $database->escapeTable($table);
$subfield = $database->escapeCol($subfield);
$quoteValue = $database->quote($value);
$query->where("{$table}.{$subfield}{$operator}$quoteValue"); // QA
$query->where("{$table}.{$subfield}{$operator}?", $value); // QA
return $query;
}

View File

@@ -99,6 +99,7 @@
* @property Page|null $_cloning Internal runtime use, contains Page being cloned (source), when this Page is the new copy (target). #pw-internal
* @property bool|null $_hasAutogenName Internal runtime use, set by Pages class when page as auto-generated name. #pw-internal
* @property bool|null $_forceSaveParents Internal runtime/debugging use, force a page to refresh its pages_parents DB entries on save(). #pw-internal
* @property float|null $_pfscore Internal PageFinder fulltext match score when page found/loaded from relevant query. #pw-internal
*
* Methods added by PageRender.module:
* -----------------------------------

View File

@@ -639,7 +639,7 @@ class PageFinder extends Wire {
$score += $v;
unset($row[$k]);
}
$row['score'] = $score; // @todo do we need this anymore?
$row['score'] = $score;
$matches[] = $row;
} else if($options['returnAllCols']) {
@@ -2298,7 +2298,9 @@ class PageFinder extends Wire {
// the following fields are defined in each iteration here because they may be modified in the loop
$table = "pages";
$operator = $selector->operator;
$isPartialOperator = in_array($operator, array('%=', '^=', '$=', '%^=', '%$=', '*='));
$compareType = $selectors::getSelectorByOperator($operator, 'compareType');
$isPartialOperator = ($compareType & Selector::compareTypeFind);
$subfield = '';
$IDs = array(); // populated in special cases where we can just match parent IDs
$sql = '';
@@ -3054,7 +3056,10 @@ class PageFinder extends Wire {
/** @var PageFinder $finder */
$finder = $this->wire(new PageFinder());
$ids = $finder->findIDs($ownerSelectors);
$ids = array();
foreach($finder->findIDs($ownerSelectors) as $id) {
$ids[] = (int) $id;
}
if($this->isRepeaterFieldtype($ownerField->type)) {
// Repeater

View File

@@ -346,7 +346,7 @@ class PagesLoader extends Wire {
// [ pageID => [ all pages columns ] ]
$pagesInfo = $pageFinder->findVerboseIDs($selectors, $options);
} else {
// [ [ 'id' => 3, 'templates_id' => 2, 'parent_id' => 1 ]
// [ [ 'id' => 3, 'templates_id' => 2, 'parent_id' => 1, 'score' => 1.123 ]
$pagesInfo = $pageFinder->find($selectors, $options);
}
@@ -398,13 +398,16 @@ class PagesLoader extends Wire {
$parent_id = $pageFinder->getParentID();
$idsSorted = array();
$idsByTemplate = array();
$scores = array();
// organize the pages by template ID
foreach($pagesInfo as $page) {
$tpl_id = $page['templates_id'];
$tpl_id = (int) $page['templates_id'];
$id = (int) $page['id'];
if(!isset($idsByTemplate[$tpl_id])) $idsByTemplate[$tpl_id] = array();
$idsByTemplate[$tpl_id][] = $page['id'];
$idsSorted[] = $page['id'];
$idsByTemplate[$tpl_id][] = $id;
$idsSorted[] = $id;
if(!empty($page['score'])) $scores[$id] = (float) $page['score'];
}
if(count($idsByTemplate) > 1) {
@@ -440,6 +443,12 @@ class PagesLoader extends Wire {
$sortsAfter = $pageFinder->getSortsAfter();
if(count($sortsAfter)) $pages->sort($sortsAfter);
if(count($scores)) {
foreach($pages as $page) {
if(isset($scores[$page->id])) $page->setQuietly('_pfscore', $scores[$page->id]);
}
}
} else {
$pages = $this->pages->newPageArray($loadOptions);

View File

@@ -495,7 +495,9 @@ class ProcessWire extends Wire {
/** @var WireCache $cache */
$cache = $this->wire('cache', new WireCache(), true);
$cache->preload($config->preloadCacheNames);
$cacheNames = $config->preloadCacheNames;
if($database->getEngine() === 'innodb') $cacheNames[] = 'InnoDB.stopwords';
$cache->preload($cacheNames);
$modules = null;
try {

View File

@@ -122,6 +122,14 @@ class WireDatabasePDO extends Wire implements WireDatabase {
*/
protected $variableCache = array();
/**
* Cached InnoDB stopwords (keys are the stopwords and values are irrelevant)
*
* @var array|null Becomes array once loaded
*
*/
protected $stopwordCache = null;
/**
* Create a new PDO instance from ProcessWire $config API variable
*
@@ -766,6 +774,42 @@ class WireDatabasePDO extends Wire implements WireDatabase {
return in_array($str, $operators, true);
}
/**
* Is given word a fulltext stopword to the current database engine?
*
* @param string $word
* @return bool
* @since 3.0.160
*
*/
public function isStopword($word) {
if($this->engine === 'myisam') {
return DatabaseStopwords::has($word);
}
if($this->stopwordCache === null && $this->engine === 'innodb') {
$cache = $this->wire()->cache;
$stopwords = null;
if($cache) {
$stopwords = $cache->get('InnoDB.stopwords');
if($stopwords) $stopwords = explode(',', $stopwords);
}
if(!$stopwords) {
$query = $this->prepare('SELECT value FROM INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD');
$query->execute();
$stopwords = $query->fetchAll(\PDO::FETCH_COLUMN, 0);
$query->closeCursor();
if($cache) $cache->save('InnoDB.stopwords', implode(',', $stopwords), WireCache::expireDaily);
}
$this->stopwordCache = array_flip($stopwords);
}
if(!$this->stopwordCache) return false;
return isset($this->stopwordCache[strtolower($word)]);
}
/**
* Sanitize a table name for _a-zA-Z0-9
*
@@ -930,6 +974,28 @@ class WireDatabasePDO extends Wire implements WireDatabase {
return $value;
}
/**
* Get current database engine (lowercase)
*
* @return string
* @since 3.0.160
*
*/
public function getEngine() {
return $this->engine;
}
/**
* Get current database charset (lowercase)
*
* @return string
* @since 3.0.160
*
*/
public function getCharset() {
return $this->charset;
}
/**
* Retrieve new instance of WireDatabaseBackups ready to use with this connection
*

View File

@@ -129,15 +129,27 @@ class WireDatabasePDOStatement extends \PDOStatement {
*
* @param array|null $input_parameters
* @return bool
* @throws \PDOException
*
*/
public function execute($input_parameters = NULL) {
$timer = Debug::startTimer();
$result = parent::execute($input_parameters);
$exception = null;
try {
$result = parent::execute($input_parameters);
} catch(\PDOException $e) {
$exception = $e;
$result = false;
}
$timer = Debug::stopTimer($timer, 'ms');
if(!$this->database) return $result;
if(!$this->database) {
if($exception) throw $exception;
return $result;
}
if(is_array($input_parameters)) {
foreach($input_parameters as $key => $value) {
@@ -146,6 +158,7 @@ class WireDatabasePDOStatement extends \PDOStatement {
}
$debugNote = trim("$this->debugNote [$timer]");
if($exception) $debugNote .= ' FAIL SQLSTATE[' . $exception->getCode() . ']';
if($this->debugParamsQty) {
$sql = $this->queryString;
@@ -168,6 +181,8 @@ class WireDatabasePDOStatement extends \PDOStatement {
$this->database->queryLog($this->queryString, $debugNote);
}
if($exception) throw $exception;
return $result;
}

View File

@@ -913,8 +913,8 @@ class FieldtypeFile extends FieldtypeMulti implements ConfigurableModule {
} else if($operator === '!=' && ctype_alnum("$value")) {
$operator = '!~='; // ok
} else if($operator === '~=' || $operator === '!~=' || $operator === '%=' || $operator === '*=') {
// ok
} else if(Selectors::getSelectorByOperator($operator, 'compareType') & Selector::compareTypeFind) {
// ok, text finding operators
} else {
throw new PageFinderSyntaxException("Operator $operator is not supported by $this in selector: $selector");