<?php
class HardwareQuery
{
private $id = 0;
private $joins = [];
private $where = [];
private $args = [];
private $columns = [];
/**
* @param string $type hardware type form HardwareInfo
* @param ?string $uuid If set, only return data for specific client
*/
public function __construct(string $type, string $uuid = null, $connectedOnly = true)
{
if ($connectedOnly) {
$this->joins['mxhw_join'] = "INNER JOIN machine_x_hw mxhw ON (mxhw.hwid = shw.hwid AND mxhw.disconnecttime = 0)";
} else {
$this->joins['mxhw_join'] = "INNER JOIN machine_x_hw mxhw ON (mxhw.hwid = shw.hwid)";
}
if ($uuid !== null) {
$this->where[] = 'mxhw.machineuuid = :uuid';
$this->args['uuid'] = $uuid;
}
$this->where[] = 'shw.hwtype = :hwtype';
$this->args['hwtype'] = $type;
}
private function id(): string
{
return 'b' . (++$this->id);
}
/**
* Add join of a virtual column (hw property) to an arbitrary table and column.
* @param bool $global Is the virtual column global or local to machine?
* @param string $prop Name of property/virtual column
* @param string $jTable Table to join on
* @param string $jColumn Column to join on
* @param string $condColumn optionally, another column from the joined table to match against $condVal
* @param string|array $condVal optionally, a literal, or array of literals, to match foreign column against
* @return void
*/
public function addForeignJoin(bool $global, string $prop, string $jTable, string $jColumn, string $condColumn = '', $condVal = null)
{
if (isset($this->columns[$jTable]))
return;
if (!isset($this->columns[$prop])) {
if ($global) {
$srcTable = 'shw';
$table = 'statistic_hw_prop';
$column = 'hwid';
} else {
$srcTable = 'mxhw';
$table = 'machine_x_hw_prop';
$column = 'machinehwid';
}
$tid = $this->id();
$pid = $this->id();
$this->joins[$prop] = "INNER JOIN $table $tid ON ($srcTable.$column = $tid.$column
AND $tid.prop = :$pid)";
$this->args[$pid] = $prop;
$this->columns[$prop] = "$tid.`value` AS `$prop`";
}
$jtid = $this->id();
$cond = '';
if (!empty($condColumn)) {
$vid = $this->id();
if (is_array($condVal)) {
$cond = " AND $jtid.`$condColumn` IN (:$vid)";
} else {
$cond = " AND $jtid.`$condColumn` = :$vid";
}
$this->args[$vid] = $condVal;
}
$this->joins[$jTable] = "INNER JOIN $jTable $jtid ON ($jtid.$jColumn = $tid.`value` $cond)";
}
public function addMachineWhere(string $column, string $op, $value)
{
if (isset($this->columns[$column]))
return;
$vid = $this->id();
$this->joins['machine'] = 'INNER JOIN machine m USING (machineuuid)';
$this->where[] = "m.$column $op (:$vid)";
$this->args[$vid] = $value;
$this->columns[$column] = "m.$column";
}
public function addGlobalColumn(string $prop): HardwareQueryColumn
{
return $this->addColumn(true, $prop);
}
public function addLocalColumn(string $prop): HardwareQueryColumn
{
return $this->addColumn(false, $prop);
}
public function addColumn(bool $global, string $prop, string $alias = null): HardwareQueryColumn
{
return $this->columns[] = new HardwareQueryColumn($global, $prop, $alias);
}
/**
* Join the machine table and add the given column from it to the SELECT
* @param string $column
* @return void
*/
public function addMachineColumn(string $column)
{
if (isset($this->columns[$column]))
return;
$this->joins['machine'] = 'INNER JOIN machine m USING (machineuuid)';
$this->columns[$column] = "m.$column";
}
/**
* @return false|PDOStatement
*/
public function query($groupBy = '')
{
return Database::simpleQuery($this->buildQuery($groupBy), $this->args);
}
/**
* Build query string
* @param string[]|string $groupBy Column to group by
*/
public function buildQuery($groupBy = ''): string
{
if (empty($groupBy)) {
$groupBy = [];
} elseif (!is_array($groupBy)) {
$groupBy = [$groupBy];
}
foreach ($groupBy as &$gb) {
if ($gb[0] !== '`') {
$gb = "`$gb`";
}
}
$columns = [];
foreach ($this->columns as $column) {
if ($column instanceof HardwareQueryColumn) {
$column->generate($this->joins, $columns, $this->args, $groupBy);
} else {
$columns[] = $column;
}
}
$columns[] = 'mxhw.machineuuid';
$columns[] = 'shw.hwid';
// TODO: Untangle this implicit magic
if (empty($groupBy) || $groupBy[0] === 'mxhw.machinehwid') {
$columns[] = 'mxhw.disconnecttime';
} else {
$columns[] = 'Sum(If(mxhw.disconnecttime = 0, 1, 0)) AS connected_count';
}
if (!empty($groupBy)) {
$columns[] = 'Count(*) AS group_count';
$groupBy = " GROUP BY " . implode(', ', $groupBy);
} else {
$groupBy = '';
}
$query = 'SELECT ' . implode(', ', $columns)
. ' FROM statistic_hw shw '
. implode(' ', $this->joins)
. ' WHERE ' . implode(' AND ', $this->where)
. $groupBy;
//error_log($query);
return $query;
}
}