summaryrefslogtreecommitdiffstats
path: root/modules-available/statistics/inc/hardwarequery.inc.php
blob: 00a9bbc910caa26a66b53590eecaf5a444ca7ea1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<?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(string $groupBy = '')
	{
		return Database::simpleQuery($this->buildQuery($groupBy), $this->args);
	}

	/**
	 * Build query string
	 * @param string $groupBy Column to group by
	 */
	public function buildQuery(string $groupBy = ''): string
	{
		$groupConcat = !empty($groupBy) && $groupBy !== 'mxhw.machinehwid';
		$columns = [];
		foreach ($this->columns as $column) {
			if ($column instanceof HardwareQueryColumn) {
				$column->generate($this->joins, $columns, $this->args, $groupConcat);
			} else {
				$columns[] = $column;
			}
		}
		$columns[] = 'mxhw.machineuuid';
		$columns[] = 'shw.hwid';
		// TODO: Untangle this implicit magic
		if (empty($groupBy) || $groupBy === '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 $groupBy";
		}
		return 'SELECT ' . implode(', ', $columns)
			. ' FROM statistic_hw shw '
			. implode(' ', $this->joins)
			. ' WHERE ' . implode(' AND ', $this->where)
			. $groupBy;
	}

}