Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 51
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
JoinGroupBase
0.00% covered (danger)
0.00%
0 / 51
0.00% covered (danger)
0.00%
0 / 5
380
0.00% covered (danger)
0.00%
0 / 1
 table
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
56
 leftJoin
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 join
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 straightJoin
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 addJoin
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
90
 getAutoAlias
n/a
0 / 0
n/a
0 / 0
0
1<?php
2
3namespace Wikimedia\Rdbms;
4
5use InvalidArgumentException;
6
7/**
8 * Shared code between SelectQueryBuilder and JoinGroup to represent tables and join conditions.
9 *
10 * @internal
11 */
12abstract class JoinGroupBase {
13    /** @var array */
14    protected $tables = [];
15
16    /** @var array */
17    protected $joinConds = [];
18
19    /** @var string|null */
20    protected $lastAlias;
21
22    /**
23     * Add a single table or a single parenthesized group.
24     *
25     * @param string|JoinGroup|Subquery|SelectQueryBuilder $table The unqualified name of a table,
26     *   a table name of the form "information_schema.<unquoted identifier>", a JoinGroup
27     *   containing multiple tables, a Subquery instance, or a SelectQueryBuilder representing a subquery.
28     * @param-taint $table exec_sql
29     * @param string|null $alias The table alias, or null for no alias
30     * @param-taint $alias exec_sql
31     * @return $this
32     */
33    public function table( $table, $alias = null ) {
34        if ( $table instanceof JoinGroup ) {
35            $alias ??= $table->getAlias();
36            $table = $table->getRawTables();
37        } elseif ( $table instanceof SelectQueryBuilder ) {
38            $alias ??= $this->getAutoAlias();
39            $table = new Subquery( $table->getSQL() );
40        } elseif ( $table instanceof Subquery ) {
41            if ( $alias === null ) {
42                throw new InvalidArgumentException( __METHOD__ .
43                    ': Subquery as table must provide an alias.' );
44            }
45        } elseif ( !is_string( $table ) ) {
46            throw new InvalidArgumentException( __METHOD__ .
47                ': $table must be either string, JoinGroup or SelectQueryBuilder' );
48        }
49        if ( $alias === null ) {
50            $this->tables[] = $table;
51            $this->lastAlias = $table;
52        } else {
53            $this->tables[$alias] = $table;
54            $this->lastAlias = $alias;
55        }
56        return $this;
57    }
58
59    /**
60     * Left join a table or group of tables. This should be called after table().
61     *
62     * @param string|JoinGroup|SelectQueryBuilder $table The unqualified name of a table,
63     *   a table name of the form "information_schema.<unquoted identifier>", a JoinGroup
64     *   containing multiple tables, or a SelectQueryBuilder representing a subquery.
65     * @param string|null $alias The alias name, or null to automatically
66     *   generate an alias which will be unique to this builder
67     * @param string|array $conds The conditions for the ON clause
68     * @return $this
69     */
70    public function leftJoin( $table, $alias = null, $conds = [] ) {
71        $this->addJoin( 'LEFT JOIN', $table, $alias, $conds );
72        return $this;
73    }
74
75    /**
76     * Inner join a table or group of tables. This should be called after table().
77     *
78     * @param string|JoinGroup|SelectQueryBuilder $table The unqualified name of a table,
79     *   a table name of the form "information_schema.<unquoted identifier>", a JoinGroup
80     *   containing multiple tables, or a SelectQueryBuilder representing a subquery.
81     * @param string|null $alias The alias name, or null to automatically
82     *   generate an alias which will be unique to this builder
83     * @param string|array $conds The conditions for the ON clause
84     * @return $this
85     */
86    public function join( $table, $alias = null, $conds = [] ) {
87        $this->addJoin( 'JOIN', $table, $alias, $conds );
88        return $this;
89    }
90
91    /**
92     * Straight join a table or group of tables. This should be called after table().
93     *
94     * @param string|JoinGroup|SelectQueryBuilder $table The unqualified name of a table,
95     *   a table name of the form "information_schema.<unquoted identifier>", a JoinGroup
96     *   containing multiple tables, or a SelectQueryBuilder representing a subquery.
97     * @param string|null $alias The alias name, or null to automatically
98     *   generate an alias which will be unique to this builder
99     * @param string|array $conds The conditions for the ON clause
100     * @return $this
101     */
102    public function straightJoin( $table, $alias = null, $conds = [] ) {
103        $this->addJoin( 'STRAIGHT_JOIN', $table, $alias, $conds );
104        return $this;
105    }
106
107    /**
108     * Private helper for functions that add joins
109     * @param string $type
110     * @param string|JoinGroup|SelectQueryBuilder $table
111     * @param string|null $alias
112     * @param string|array $joinConds
113     */
114    private function addJoin( $type, $table, $alias, $joinConds ) {
115        if ( !$this->tables ) {
116            throw new \LogicException( __METHOD__ .
117                ': cannot add a join unless a regular table is added first' );
118        }
119        if ( $alias === null ) {
120            if ( is_string( $table ) ) {
121                $alias = $table;
122            } else {
123                $alias = $this->getAutoAlias();
124            }
125        }
126        if ( isset( $this->joinConds[$alias] ) ) {
127            throw new \LogicException( __METHOD__ .
128                ": a join with alias \"$alias\" has already been added" );
129        }
130        if ( $table instanceof JoinGroup ) {
131            $conflicts = array_intersect_key( $this->joinConds, $table->getRawJoinConds() );
132            if ( $conflicts ) {
133                $conflict = reset( $conflicts );
134                throw new \LogicException( __METHOD__ .
135                    ": a join with alias \"$conflict\" has already been added" );
136            }
137            $this->tables[$alias] = $table->getRawTables();
138            $this->joinConds += $table->getRawJoinConds();
139        } elseif ( $table instanceof SelectQueryBuilder ) {
140            $this->tables[$alias] = new Subquery( $table->getSQL() );
141        } elseif ( is_string( $table ) ) {
142            $this->tables[$alias] = $table;
143        } else {
144            throw new InvalidArgumentException( __METHOD__ .
145                ': $table must be either string, JoinGroup or SelectQueryBuilder' );
146        }
147        $this->joinConds[$alias] = [ $type, $joinConds ];
148        $this->lastAlias = $alias;
149    }
150
151    abstract protected function getAutoAlias();
152}