From 5126585314c282c836c90695ef93768591f691d3 Mon Sep 17 00:00:00 2001 From: thomas-topway-it Date: Sun, 24 Aug 2025 03:15:51 +0400 Subject: [PATCH 1/4] use structured queries --- formats/datatables/SearchPanes.php | 224 +++++++++++++++++++++++------ 1 file changed, 181 insertions(+), 43 deletions(-) diff --git a/formats/datatables/SearchPanes.php b/formats/datatables/SearchPanes.php index 9c8b47c85..d4e6bb5fe 100644 --- a/formats/datatables/SearchPanes.php +++ b/formats/datatables/SearchPanes.php @@ -70,6 +70,92 @@ public function getLog(): array { return $this->searchPanesLog; } + private function parseQuerySegment( $querySegment ) { +/* +parse QuerySegment in this form: +( + [type] => 1 + [depth] => + [fingerprint] => + [null] => + [not] => + [joinType] => + [joinTable] => smw_object_ids + [joinfield] => t0.smw_id + [indexField] => + [from] => INNER JOIN (`smw_fpt_mdat` AS t2 INNER JOIN `smw_di_blob` AS t3 ON t2.s_id=t3.s_id) ON t0.smw_id=t2.s_id + [fromTables] => Array + ( + [nestedt2] => Array + ( + [t3] => smw_di_blob + [t2] => smw_fpt_mdat + ) + + ) + + [joinConditions] => Array + ( + [t3] => Array + ( + [0] => INNER JOIN + [1] => t2.s_id=t3.s_id + ) + + [nestedt2] => Array + ( + [0] => INNER JOIN + [1] => t0.smw_id=t2.s_id + ) + + ) + + [where] => ((t3.p_id=519)) + [sortIndexField] => + [components] => Array + ( + ) + + [alias] => t0 + [sortfields] => Array + ( + [] => t0.smw_sort + ) + + [queryNumber] => 0 +) +*/ + $tables = [ + $querySegment['alias'] => $querySegment['joinTable'] + ]; + $joins = []; + $conds = $querySegment['where'] ?: []; + + if ( !empty( $querySegment['joinConditions'] ) ) { + foreach ( $querySegment['joinConditions'] as $alias => $joinInfo ) { + $aliasFixed = preg_replace( '/^nested/', '', $alias ); + $joins[$aliasFixed] = [ $joinInfo[0], [$joinInfo[1]] ]; + } + } + + if ( !empty( $querySegment['fromTables'] ) ) { + foreach ( $querySegment['fromTables'] as $nestedAlias => $nested ) { + if ( is_array($nested ) ) { + foreach ( $nested as $alias => $table ) { + $aliasFixed = preg_replace('/^nested/', '', $alias); + $tables[$aliasFixed] = $table; + } + } else { + $tables[$nestedAlias] = $nested; + } + } + } + + ksort($tables); + + return [ $tables, $joins, $conds ]; + } + private function getPanesOptions( PrintRequest $printRequest, string $canonicalLabel, @@ -126,21 +212,29 @@ private function getPanesOptions( $qobj = $querySegmentList[$rootid]; + [ $tables, $joins, $conds ] = $this->parseQuerySegment((array)$qobj); + $property = new DIProperty( DIProperty::newFromUserLabel( $printRequest->getCanonicalLabel() ) ); $propTypeid = $property->findPropertyValueType(); if ( $isCategory ) { - // data-length without the GROUP BY clause - $sql_options = [ 'LIMIT' => 1 ]; - + $sql_options_ = [ 'LIMIT' => 1 ]; + + $tables_ = $tables; + $tables_['insts'] = 'smw_fpt_inst'; + $fields_ = [ 'count' => 'COUNT(*)' ]; + $conds_ = $conds; + $joins_ = $joins; + $joins_['insts'] = [ 'JOIN', [ "$qobj->alias.smw_id = insts.s_id" ] ]; + $dataLength = (int)$this->connection->selectField( - $this->connection->tableName( $qobj->joinTable ) . " AS $qobj->alias" . $qobj->from - . ' JOIN ' . $this->connection->tableName( 'smw_fpt_inst' ) . " AS insts ON $qobj->alias.smw_id = insts.s_id", - "COUNT(*) AS count", - $qobj->where, - __METHOD__, - $sql_options + $tables_, + $fields_, + $conds_, + __METHOD__, + $sql_options_, + $joins_ ); if ( !$dataLength ) { @@ -149,7 +243,7 @@ private function getPanesOptions( $groupBy = "i.smw_id"; $orderBy = "count DESC, $groupBy ASC"; - $sql_options = [ + $sql_options_ = [ 'GROUP BY' => $groupBy, // $this->query->getOption( 'count' ), 'LIMIT' => $dataLength, @@ -167,6 +261,7 @@ private function getPanesOptions( HAVING COUNT(i.smw_id) >= 1 ORDER BY COUNT(i.smw_id) DESC */ +/* $res = $this->connection->select( $this->connection->tableName( $qobj->joinTable ) . " AS $qobj->alias" . $qobj->from // @see https://github.com/SemanticMediaWiki/SemanticDrilldown/blob/master/includes/Sql/SqlProvider.php @@ -177,6 +272,24 @@ private function getPanesOptions( __METHOD__, $sql_options ); +*/ + $tables_ = $tables; + $tables_['insts'] = 'smw_fpt_inst'; + $tables_[SQLStore::ID_TABLE] = 'i'; + $joins_ = $joins; + $joins_['insts'] = [ 'JOIN', [ "$qobj->alias.smw_id = insts.s_id" ] ]; + $joins_['i'] = [ 'JOIN', [ 'i.smw_id = insts.o_id' ] ]; + $conds_ = $conds; + $fields_ = "COUNT($groupBy) AS count, i.smw_id, i.smw_title, i.smw_namespace, i.smw_iw, i.smw_sort, i.smw_subobject"; + + $res = $this->connection->select( + $tables_, + $fields_, + $conds_, + __METHOD__, + $sql_options_, + $joins_ + ); $isIdField = true; @@ -203,25 +316,31 @@ private function getPanesOptions( } // data-length without the GROUP BY clause - $sql_options = [ 'LIMIT' => 1 ]; + $sql_options_ = [ 'LIMIT' => 1 ]; // SELECT COUNT(*) as count FROM `smw_object_ids` AS t0 // INNER JOIN (`smw_fpt_mdat` AS t2 INNER JOIN `smw_di_wikipage` AS t3 ON t2.s_id=t3.s_id) ON t0.smw_id=t2.s_id // WHERE ((t3.p_id=517)) LIMIT 500 + $tables_ = $tables; + $fields_ = [ 'count' => 'COUNT(*)' ]; + $conds_ = $conds; + $joins_ = $joins; + $dataLength = (int)$this->connection->selectField( - $this->connection->tableName( $qobj->joinTable ) . " AS $qobj->alias" . $qobj->from, - "COUNT(*) as count", - $qobj->where, - __METHOD__, - $sql_options + $tables_, + $fields_, + $conds_, + __METHOD__, + $sql_options_, + $joins_ ); if ( !$dataLength ) { return []; } - [ $diType, $isIdField, $fields, $groupBy, $orderBy ] = $this->fetchValuesByGroup( $property, $p_alias, $propTypeid ); + [ $diType, $isIdField, $fields_, $groupBy, $orderBy ] = $this->fetchValuesByGroup( $property, $p_alias, $propTypeid ); /* ---GENERATED FROM DATATABLES @@ -231,7 +350,7 @@ private function getPanesOptions( SELECT i.smw_id,i.smw_title,i.smw_namespace,i.smw_iw,i.smw_subobject,i.smw_hash,i.smw_sort,COUNT( p.o_id ) as count FROM `smw_object_ids` `o` INNER JOIN `smw_di_wikipage` `p` ON ((p.s_id=o.smw_id)) JOIN `smw_object_ids` `i` ON ((p.o_id=i.smw_id)) WHERE o.smw_hash IN ('1_-_A','1_-_Ab','1_-_Abc','10_-_Abcd','11_-_Abc') AND (o.smw_iw!=':smw') AND (o.smw_iw!=':smw-delete') AND p.p_id = 517 GROUP BY p.o_id, i.smw_id ORDER BY count DESC, i.smw_sort ASC */ - $sql_options = [ + $sql_options_ = [ 'GROUP BY' => $groupBy, // the following implies that if the user sets a threshold // close or equal to 1, and there are too many unique values, @@ -243,18 +362,27 @@ private function getPanesOptions( ]; // @see QueryEngine + $tables_ = $tables; + $joins_ = $joins; + $conds_ = $conds; + + if ($isIdField) { + $tables_['i'] = SQLStore::ID_TABLE; + $joins_['i'] = [ 'JOIN', "$p_alias.o_id = i.smw_id" ]; + $conds_ .= !empty( $conds_ ) ? ' AND' : ''; + $conds_ .= ' i.smw_iw != ' . $this->connection->addQuotes(SMW_SQL3_SMWIW_OUTDATED); + $conds_ .= ' AND i.smw_iw != ' . $this->connection->addQuotes(SMW_SQL3_SMWDELETEIW); + } + + // perform the select $res = $this->connection->select( - $this->connection->tableName( $qobj->joinTable ) . " AS $qobj->alias" . $qobj->from - . ( !$isIdField ? '' - : " JOIN " . $this->connection->tableName( SQLStore::ID_TABLE ) . " AS `i` ON ($p_alias.o_id = i.smw_id)" ), - implode( ',', $fields ), - $qobj->where . ( !$isIdField ? '' : ( !empty( $qobj->where ) ? ' AND' : '' ) - . ' i.smw_iw!=' . $this->connection->addQuotes( SMW_SQL3_SMWIW_OUTDATED ) - . ' AND i.smw_iw!=' . $this->connection->addQuotes( SMW_SQL3_SMWDELETEIW ) ), + $tables_, + $fields_, + $conds_, __METHOD__, - $sql_options + $sql_options_, + $joins_ ); - } // verify uniqueRatio @@ -605,9 +733,9 @@ private function searchPanesMainlabel( PrintRequest $printRequest, array $search 'threshold' => $threshold, ]; - if ( $threshold < 1 ) { - return []; - } + // if ( $threshold < 1 ) { + // return []; + // } $query = $this->datatables->query; $queryDescription = $query->getDescription(); @@ -628,7 +756,9 @@ private function searchPanesMainlabel( PrintRequest $printRequest, array $search $qobj = $querySegmentList[$rootid]; - $sql_options = [ + [ $tables, $joins, $conds ] = $this->parseQuerySegment((array)$qobj); + + $sql_options_ = [ // *** should we set a limit here ? // it makes sense to show the pane for // mainlabel only when page titles are grouped @@ -639,21 +769,29 @@ private function searchPanesMainlabel( PrintRequest $printRequest, array $search // Selecting those is required in standard SQL (but MySQL does not require it). $sortfields = implode( ',', $qobj->sortfields ); - $sortfields = $sortfields ? ',' . $sortfields : ''; + // $sortfields = $sortfields ? ',' . $sortfields : ''; // @see QueryEngine + $tables_ = $tables; + $fields_ = []; + $fields_['id'] = "$qobj->alias.smw_id"; + $fields_['t'] = "$qobj->alias.smw_title"; + $fields_['ns'] = "$qobj->alias.smw_namespace"; + $fields_['iw'] = "$qobj->alias.smw_iw"; + $fields_['so'] = "$qobj->alias.smw_subobject"; + $fields_['sortkey'] = "$qobj->alias.smw_sortkey"; + $fields_[] = $sortfields; + + $conds_ = $conds; + $joins_ = $joins; + $res = $this->connection->select( - $this->connection->tableName( $qobj->joinTable ) . " AS $qobj->alias" . $qobj->from, - "$qobj->alias.smw_id AS id," . - "$qobj->alias.smw_title AS t," . - "$qobj->alias.smw_namespace AS ns," . - "$qobj->alias.smw_iw AS iw," . - "$qobj->alias.smw_subobject AS so," . - "$qobj->alias.smw_sortkey AS sortkey" . - "$sortfields", - $qobj->where, - __METHOD__, - $sql_options + $tables_, + $fields_, + $conds_, + __METHOD__, + $sql_options_, + $joins_ ); $diHandler = $this->datatables->store->getDataItemHandlerForDIType( From 84e6afbb16d973dc2a1318eb3b25554d55e242f0 Mon Sep 17 00:00:00 2001 From: thomas-topway-it Date: Sun, 24 Aug 2025 03:30:12 +0400 Subject: [PATCH 2/4] fix phpcs --- formats/datatables/SearchPanes.php | 72 +++++++++++++----------------- 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/formats/datatables/SearchPanes.php b/formats/datatables/SearchPanes.php index d4e6bb5fe..2135754c1 100644 --- a/formats/datatables/SearchPanes.php +++ b/formats/datatables/SearchPanes.php @@ -134,7 +134,7 @@ private function parseQuerySegment( $querySegment ) { if ( !empty( $querySegment['joinConditions'] ) ) { foreach ( $querySegment['joinConditions'] as $alias => $joinInfo ) { $aliasFixed = preg_replace( '/^nested/', '', $alias ); - $joins[$aliasFixed] = [ $joinInfo[0], [$joinInfo[1]] ]; + $joins[$aliasFixed] = [ $joinInfo[0], [ $joinInfo[1] ] ]; } } @@ -142,7 +142,7 @@ private function parseQuerySegment( $querySegment ) { foreach ( $querySegment['fromTables'] as $nestedAlias => $nested ) { if ( is_array($nested ) ) { foreach ( $nested as $alias => $table ) { - $aliasFixed = preg_replace('/^nested/', '', $alias); + $aliasFixed = preg_replace( '/^nested/', '', $alias ); $tables[$aliasFixed] = $table; } } else { @@ -151,7 +151,7 @@ private function parseQuerySegment( $querySegment ) { } } - ksort($tables); + ksort( $tables ); return [ $tables, $joins, $conds ]; } @@ -212,7 +212,7 @@ private function getPanesOptions( $qobj = $querySegmentList[$rootid]; - [ $tables, $joins, $conds ] = $this->parseQuerySegment((array)$qobj); + [ $tables, $joins, $conds ] = $this->parseQuerySegment( (array)$qobj ); $property = new DIProperty( DIProperty::newFromUserLabel( $printRequest->getCanonicalLabel() ) ); $propTypeid = $property->findPropertyValueType(); @@ -226,15 +226,15 @@ private function getPanesOptions( $fields_ = [ 'count' => 'COUNT(*)' ]; $conds_ = $conds; $joins_ = $joins; - $joins_['insts'] = [ 'JOIN', [ "$qobj->alias.smw_id = insts.s_id" ] ]; - + $joins_['insts'] = [ 'JOIN', [ "$qobj->alias.smw_id = insts.s_id" ] ]; + $dataLength = (int)$this->connection->selectField( - $tables_, - $fields_, - $conds_, - __METHOD__, - $sql_options_, - $joins_ + $tables_, + $fields_, + $conds_, + __METHOD__, + $sql_options_, + $joins_ ); if ( !$dataLength ) { @@ -261,24 +261,12 @@ private function getPanesOptions( HAVING COUNT(i.smw_id) >= 1 ORDER BY COUNT(i.smw_id) DESC */ -/* - $res = $this->connection->select( - $this->connection->tableName( $qobj->joinTable ) . " AS $qobj->alias" . $qobj->from - // @see https://github.com/SemanticMediaWiki/SemanticDrilldown/blob/master/includes/Sql/SqlProvider.php - . ' JOIN ' . $this->connection->tableName( 'smw_fpt_inst' ) . " AS insts ON $qobj->alias.smw_id = insts.s_id" - . ' JOIN ' . $this->connection->tableName( SQLStore::ID_TABLE ) . " AS i ON i.smw_id = insts.o_id", - "COUNT($groupBy) AS count, i.smw_id, i.smw_title, i.smw_namespace, i.smw_iw, i.smw_sort, i.smw_subobject", - $qobj->where, - __METHOD__, - $sql_options - ); -*/ $tables_ = $tables; $tables_['insts'] = 'smw_fpt_inst'; $tables_[SQLStore::ID_TABLE] = 'i'; $joins_ = $joins; - $joins_['insts'] = [ 'JOIN', [ "$qobj->alias.smw_id = insts.s_id" ] ]; - $joins_['i'] = [ 'JOIN', [ 'i.smw_id = insts.o_id' ] ]; + $joins_['insts'] = [ 'JOIN', [ "$qobj->alias.smw_id = insts.s_id" ] ]; + $joins_['i'] = [ 'JOIN', [ 'i.smw_id = insts.o_id' ] ]; $conds_ = $conds; $fields_ = "COUNT($groupBy) AS count, i.smw_id, i.smw_title, i.smw_namespace, i.smw_iw, i.smw_sort, i.smw_subobject"; @@ -328,12 +316,12 @@ private function getPanesOptions( $joins_ = $joins; $dataLength = (int)$this->connection->selectField( - $tables_, - $fields_, - $conds_, - __METHOD__, - $sql_options_, - $joins_ + $tables_, + $fields_, + $conds_, + __METHOD__, + $sql_options_, + $joins_ ); if ( !$dataLength ) { @@ -366,12 +354,12 @@ private function getPanesOptions( $joins_ = $joins; $conds_ = $conds; - if ($isIdField) { + if ( $isIdField ) { $tables_['i'] = SQLStore::ID_TABLE; $joins_['i'] = [ 'JOIN', "$p_alias.o_id = i.smw_id" ]; $conds_ .= !empty( $conds_ ) ? ' AND' : ''; - $conds_ .= ' i.smw_iw != ' . $this->connection->addQuotes(SMW_SQL3_SMWIW_OUTDATED); - $conds_ .= ' AND i.smw_iw != ' . $this->connection->addQuotes(SMW_SQL3_SMWDELETEIW); + $conds_ .= ' i.smw_iw != ' . $this->connection->addQuotes( SMW_SQL3_SMWIW_OUTDATED ); + $conds_ .= ' AND i.smw_iw != ' . $this->connection->addQuotes( SMW_SQL3_SMWDELETEIW ); } // perform the select @@ -756,7 +744,7 @@ private function searchPanesMainlabel( PrintRequest $printRequest, array $search $qobj = $querySegmentList[$rootid]; - [ $tables, $joins, $conds ] = $this->parseQuerySegment((array)$qobj); + [ $tables, $joins, $conds ] = $this->parseQuerySegment( (array)$qobj ); $sql_options_ = [ // *** should we set a limit here ? @@ -786,12 +774,12 @@ private function searchPanesMainlabel( PrintRequest $printRequest, array $search $joins_ = $joins; $res = $this->connection->select( - $tables_, - $fields_, - $conds_, - __METHOD__, - $sql_options_, - $joins_ + $tables_, + $fields_, + $conds_, + __METHOD__, + $sql_options_, + $joins_ ); $diHandler = $this->datatables->store->getDataItemHandlerForDIType( From a30867f95839336a084d0d7ca744c92dae765711 Mon Sep 17 00:00:00 2001 From: thomas-topway-it Date: Sun, 24 Aug 2025 03:36:24 +0400 Subject: [PATCH 3/4] fix phpcs --- formats/datatables/SearchPanes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/formats/datatables/SearchPanes.php b/formats/datatables/SearchPanes.php index 2135754c1..6c90654b0 100644 --- a/formats/datatables/SearchPanes.php +++ b/formats/datatables/SearchPanes.php @@ -140,7 +140,7 @@ private function parseQuerySegment( $querySegment ) { if ( !empty( $querySegment['fromTables'] ) ) { foreach ( $querySegment['fromTables'] as $nestedAlias => $nested ) { - if ( is_array($nested ) ) { + if ( is_array( $nested ) ) { foreach ( $nested as $alias => $table ) { $aliasFixed = preg_replace( '/^nested/', '', $alias ); $tables[$aliasFixed] = $table; From 775dde35991e07c534837c128a72a918a86256d2 Mon Sep 17 00:00:00 2001 From: thomas-topway-it Date: Sun, 24 Aug 2025 03:49:17 +0400 Subject: [PATCH 4/4] remove commented line --- formats/datatables/SearchPanes.php | 1 - 1 file changed, 1 deletion(-) diff --git a/formats/datatables/SearchPanes.php b/formats/datatables/SearchPanes.php index 6c90654b0..213573c65 100644 --- a/formats/datatables/SearchPanes.php +++ b/formats/datatables/SearchPanes.php @@ -757,7 +757,6 @@ private function searchPanesMainlabel( PrintRequest $printRequest, array $search // Selecting those is required in standard SQL (but MySQL does not require it). $sortfields = implode( ',', $qobj->sortfields ); - // $sortfields = $sortfields ? ',' . $sortfields : ''; // @see QueryEngine $tables_ = $tables;