Joins data to $dest from $source according to specification in $joins, or queries DB if data is not available in $source.
@param $source
@param $joins
@param $dest
@param $from
@param $contextAlias
@param bool $buildImpactIndex
557814
protected function doJoins($source, $joins, &$dest, $from, $contextAlias, $buildImpactIndex=true)
    {
        // expand sequences before doing any joins...
        $this->expandSequence($joins,$source);

        foreach ($joins as $predicate=>$ruleset) {
            if ($predicate=='followSequence') {
                continue;
            }

            if (isset($source[$predicate]))
            {
                // todo: perhaps we can get better performance by detecting whether or not
                // the uri to join on is already in the impact index, and if so not attempting
                // to join on it. However, we need to think about different combinations of
                // nested joins in different points of the view spec and see if this would
                // complicate things. Needs a unit test or two.
                $joinUris = [];
                if (isset($source[$predicate][VALUE_URI]))
                {
                    // single value for join
                    $joinUris[] = array(_ID_RESOURCE=>$source[$predicate][VALUE_URI],_ID_CONTEXT=>$contextAlias);
                }
                else
                {
                    // multiple values for join
                    $joinsPushed = 0;
                    foreach ($source[$predicate] as $v)
                    {
                        if (isset($ruleset['maxJoins']) && !$joinsPushed<$ruleset['maxJoins'])
                        {
                            break; // maxJoins reached
                        }
                        $joinUris[] = array(_ID_RESOURCE=>$v[VALUE_URI],_ID_CONTEXT=>$contextAlias);
                        $joinsPushed++;
                    }
                }

                $recursiveJoins = [];
                $collection = (
                isset($ruleset['from'])
                    ? $this->config->getCollectionForCBD($this->storeName, $ruleset['from'])
                    : $this->config->getCollectionForCBD($this->storeName, $from)
                );

                $cursor = $collection->find(array('_id'=>array('$in'=>$joinUris)), array(
                    'maxTimeMS' => $this->getConfigInstance()->getMongoCursorTimeout()
                ));

                $this->addIdToImpactIndex($joinUris, $dest, $buildImpactIndex);
                foreach($cursor as $linkMatch) {
                    // if there is a condition, check it...
                    if (isset($ruleset['condition']))
                    {
                        $ruleset['condition']['._id'] = $linkMatch['_id'];
                    }
                    if (!(isset($ruleset['condition']) && $collection->count($ruleset['condition'])==0))
                    {
                        // make sure any sequences are expanded before extracting properties
                        if (isset($ruleset['joins'])) $this->expandSequence($ruleset['joins'],$linkMatch);
                        if (isset($ruleset['filter']))
                        {
                            foreach($ruleset['filter'] as $filterPredicate => $filter)
                            {
                                foreach($filter as $filterType => $filterMatch)
                                {
                                    if (isset($linkMatch[$filterPredicate]))
                                    {
                                        foreach($linkMatch[$filterPredicate] as $linkMatchType => $linkMatchValues)
                                        {
                                            if (is_array($linkMatchValues) == false)
                                            {
                                                $linkMatchValues = array($linkMatchType => $linkMatchValues);
                                            }
                                            foreach($linkMatchValues as $linkMatchType => $linkMatchValue)
                                            {
                                                if ($this->matchesFilter($linkMatchType, $linkMatchValue, $filterType, $filterMatch))
                                                {
                                                    $dest[_GRAPHS][] = $this->extractProperties($linkMatch,$ruleset,$from);
                                                }
                                            }

                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            $dest[_GRAPHS][] = $this->extractProperties($linkMatch,$ruleset,$from);
                        }

                        if (isset($ruleset['joins']))
                        {
                            // recursive joins must be done after this cursor has completed, otherwise things get messy
                            $recursiveJoins[] = array('data'=>$linkMatch, 'ruleset'=>$ruleset['joins']);
                        }
                    }
                }
                if (count($recursiveJoins)>0)
                {
                    foreach ($recursiveJoins as $r)
                    {
                        $this->doJoins($r['data'],$r['ruleset'],$dest,$from,$contextAlias,$buildImpactIndex);
                    }
                }
            }
        }
        return;
    }
72296
public function createJoins(array $operations, array $joins, array $ons): string
    {
        $ret = [];
        for ($i = 0; $i < count($joins); $i++) {
            $ret[] = $this->createJoin($operations[$i], $joins[$i]) . $this->createOn($ons[$i]);
        }
        return implode(' ', $ret);
    }
151948
public function getJoins(&$parameters = array())
    {
        if (func_num_args() == 0) {
            Deprecation::notice(
                '4.0',
                'SQLConditionalExpression::getJoins() now may produce parameters which are necessary to
				execute this query'
            );
        }

        // Sort the joins
        $parameters = array();
        $joins = $this->getOrderedJoins($this->from);

        // Build from clauses
        foreach ($joins as $alias => $join) {
            // $join can be something like this array structure
            // array('type' => 'inner', 'table' => 'SiteTree', 'filter' => array("SiteTree.ID = 1",
            // "Status = 'approved'", 'order' => 20))
            if (!is_array($join)) {
                if (empty($alias) || is_numeric($alias)) {
                    continue;
                }

                if (preg_match('/AS\s+(?:"[^"]+"|[A-Z0-9_]+)\s*$/i', $join)) {
                    // custom aliases override the ones defined through array keys
                    // this is only meant to keep backward compatibility with SS <= 4.3,
                    // to be removed in SS5
                    continue;
                }

                $trimmedAlias = trim($alias, '"');

                if ($trimmedAlias !== trim($join, '"')) {
                    $joins[$alias] = "{$join} AS \"{$trimmedAlias}\"";
                }

                continue;
            }

            if (is_string($join['filter'])) {
                $filter = $join['filter'];
            } elseif (sizeof($join['filter']) == 1) {
                $filter = $join['filter'][0];
            } else {
                $filter = "(" . implode(") AND (", $join['filter']) . ")";
            }

            // Ensure tables are quoted, unless the table is actually a sub-select
            $table = preg_match('/\bSELECT\b/i', $join['table'])
                ? $join['table']
                : "\"{$join['table']}\"";
            $aliasClause = ($alias != $join['table'])
                ? " AS \"{$alias}\""
                : "";
            $joins[$alias] = strtoupper($join['type']) . " JOIN " . $table . "$aliasClause ON $filter";
            if (!empty($join['parameters'])) {
                $parameters = array_merge($parameters, $join['parameters']);
            }
        }

        return $joins;
    }
738381
protected function flattenArray(array $source, array &$dest)
    {
        foreach ($source as $elm) {
            if (is_array($elm)) {
                $this->flattenArray($elm, $dest);
            } else {
                $dest[] = $elm;
            }
        }
    }
763085
protected void appendTableWithJoins(TableAlias alias, StringBuffer where, StringBuffer buf)
    {
        int stmtFromPos = 0;
        byte joinSyntax = getJoinSyntaxType();

        if (joinSyntax == SQL92_JOIN_SYNTAX)
        {
            stmtFromPos = buf.length(); // store position of join (by: Terry Dexter)
        }

        if (alias == getRoot())
        {
            // BRJ: also add indirection table to FROM-clause for MtoNQuery 
            if (getQuery() instanceof MtoNQuery)
            {
                MtoNQuery mnQuery = (MtoNQuery)m_query; 
                buf.append(getTableAliasForPath(mnQuery.getIndirectionTable(), null).getTableAndAlias());
                buf.append(", ");
            }           
            buf.append(alias.getTableAndAlias());
        }
        else if (joinSyntax != SQL92_NOPAREN_JOIN_SYNTAX)
        {
            buf.append(alias.getTableAndAlias());
        }

        if (!alias.hasJoins())
        {
            return;
        }

        for (Iterator it = alias.iterateJoins(); it.hasNext();)
        {
            Join join = (Join) it.next();

            if (joinSyntax == SQL92_JOIN_SYNTAX)
            {
                appendJoinSQL92(join, where, buf);
                if (it.hasNext())
                {
                    buf.insert(stmtFromPos, "(");
                    buf.append(")");
                }
            }
            else if (joinSyntax == SQL92_NOPAREN_JOIN_SYNTAX)
            {
                appendJoinSQL92NoParen(join, where, buf);
            }
            else
            {
                appendJoin(where, buf, join);
            }

        }
    }
488409
protected static function merge_context_temp_table() {
        global $DB;

        /* MDL-11347:
         *  - mysql does not allow to use FROM in UPDATE statements
         *  - using two tables after UPDATE works in mysql, but might give unexpected
         *    results in pg 8 (depends on configuration)
         *  - using table alias in UPDATE does not work in pg < 8.2
         *
         * Different code for each database - mostly for performance reasons
         */

        $dbfamily = $DB->get_dbfamily();
        if ($dbfamily == 'mysql') {
            $updatesql = "UPDATE {context} ct, {context_temp} temp
                             SET ct.path     = temp.path,
                                 ct.depth    = temp.depth,
                                 ct.locked   = temp.locked
                           WHERE ct.id = temp.id";
        } else if ($dbfamily == 'oracle') {
            $updatesql = "UPDATE {context} ct
                             SET (ct.path, ct.depth, ct.locked) =
                                 (SELECT temp.path, temp.depth, temp.locked
                                    FROM {context_temp} temp
                                   WHERE temp.id=ct.id)
                           WHERE EXISTS (SELECT 'x'
                                           FROM {context_temp} temp
                                           WHERE temp.id = ct.id)";
        } else if ($dbfamily == 'postgres' or $dbfamily == 'mssql') {
            $updatesql = "UPDATE {context}
                             SET path     = temp.path,
                                 depth    = temp.depth,
                                 locked   = temp.locked
                            FROM {context_temp} temp
                           WHERE temp.id={context}.id";
        } else {
            // sqlite and others
            $updatesql = "UPDATE {context}
                             SET path     = (SELECT path FROM {context_temp} WHERE id = {context}.id),
                                 depth    = (SELECT depth FROM {context_temp} WHERE id = {context}.id),
                                 locked   = (SELECT locked FROM {context_temp} WHERE id = {context}.id)
                             WHERE id IN (SELECT id FROM {context_temp})";
        }

        $DB->execute($updatesql);
    }
1032922
public function selectFields($fields, $tableAlias, &$join)
    {
        //
        if (!$fields) {
            return '*';
        } elseif (is_string($fields)) {
            return $fields;
        } elseif (!is_array($fields)) {
            static::error('selectFields require array');
        }

        //
        $join = '';

        //
        $aliasTable = [];

        //
        $selectFields = [];

        //
        foreach ($fields as $field => $definition) {
            if (is_numeric($field)) {
                $field = $definition;
                $definition = null;
            }

            //
            if (!$definition) {
                $selectFields[] = $this->selectFieldsSingletoneField(
                    $field,
                    $tableAlias
                );
                continue;
            }

            //
            if (is_string($definition)) {
                $selectFields[] = $definition.' AS '.$field;
                continue;
            }

            //
            if (is_array($definition)) {
                $class = $definition['Class'];

                //
                $aliasTable[$class] = isset($aliasTable[$class])
                                    ? $aliasTable[$class] + 1
                                    : 1;

                //
                $joinAlias = $aliasTable[$class] > 1
                           ? $class.''.$aliasTable[$class]
                           : $class;

                //
                $joinTable = $definition['Table'];

                //
                $joinKey = $joinAlias.'.'.$definition['JoinKey'];

                //
                $fieldFrom = $definition['FieldFrom'];

                if ($fieldFrom == '__FIELD__') {
                    $fieldFrom = $field;
                }

                //
                $join .= " LEFT JOIN {$joinTable} AS {$joinAlias}"
                       ." ON {$joinKey} = {$fieldFrom}";

                if (is_array($definition['FieldTo'])) {
                    foreach ($definition['FieldTo'] as $nextFieldTo) {
                        $fieldAlias = $field.'__'.$nextFieldTo;
                        $fieldTo = $joinAlias.'.'.$nextFieldTo;
                        $selectFields[] = $fieldTo.' AS '.$fieldAlias;
                    }
                }

                continue;
            }
        }

        return implode(', ', $selectFields);
    }
331595
public function innerJoin($join, $alias)
    {
        if (!in_array($alias, $this->joins)) {
            $this->queryBuilder->innerJoin($join, $alias);
            array_push($this->joins, $alias);
        }

        return $this;
    }
829430
protected function visitSirelNodeJoinSource(Node\JoinSource $joinSource)
    {
        $right = $joinSource->getRight();

        return
            "FROM "
            . $this->visit($joinSource->getLeft())
            . ($right ? ' ' . join(' ', $this->visitEach($right)) : null);
    }
268340
public function alias($from, $to = null)
	{
		if (is_array($from))
		{
			$this->aliases = array_merge($this->aliases, $from);

			return $this;
		}

		$this->aliases[$from] = $to;
		$this->cache->set($from, $to);
	}
167080
public function leftJoin($fromAlias, $join, $alias, $condition = null)
    {
        $this->_dbPending[] = [
            'name' => 'leftJoin',
            'args' => [
                $fromAlias,
                $join,
                $alias,
                $condition
            ],
        ];

        return $this;
    }
870709
protected function compileJoins(Builder $query, $joins)
    {
        $sql = array();

        $query->setBindings(array(), 'join');

        foreach ($joins as $join) {
            $table = $this->wrapTable($join->table);

            $clauses = array();

            foreach ($join->clauses as $clause) {
                $clauses[] = $this->compileJoinConstraint($clause);
            }

            foreach ($join->bindings as $binding) {
                $query->addBinding($binding, 'join');
            }

            $clauses[0] = $this->removeLeadingBoolean($clauses[0]);

            $clauses = implode(' ', $clauses);

            $type = $join->type;

            //
            $sql[] = "$type join $table on $clauses";
        }

        return implode(' ', $sql);
    }
1064887
private function renameJoinFields(SelectQuery $query, $namesMap = null, $originalSource = null) {
        if($namesMap == null) {
            $namesMap = $this->getColumnNames();
        }
        $joins = &$query->getJoins();
        if(!empty($joins)) {
            foreach ($joins as &$join) {
                $this->renameConditionFields($join, $namesMap, $originalSource, $query->getTable());
            }
        }
    }
790249
function compileSelectStar(query, aliases, joinstar) {
	var sp = '',
		ss = [],
		columnIds = {};

	aliases.forEach(function(alias) {
		// console.log(query.aliases[alias]);
		//	console.log(query,alias);
		// console.log(query.aliases[alias].tableid);
		//	console.log(42,631,alias);
		//	console.log(query.aliases);
		//	if(!alias) {
		//		sp += 'for(var k1 in p) var w=p[k1];for(var k2 in w){r[k2]=w[k2]};';
		//	} else 	{

		// TODO move this out of this function
		query.ixsources = {};
		query.sources.forEach(function(source) {
			query.ixsources[source.alias] = source;
		});

		// Fixed
		var columns;
		if (query.ixsources[alias]) {
			var columns = query.ixsources[alias].columns;
		}

		//		if(columns.length == 0 && query.aliases[alias].tableid) {
		//			var columns = alasql.databases[query.aliases[alias].databaseid].tables[query.aliases[alias].tableid].columns;
		//		};

		// Check if this is a Table or other
		if (joinstar && alasql.options.joinstar == 'json') {
			sp += "r['" + alias + "']={};";
		}

		if (columns && columns.length > 0) {
			columns.forEach(function(tcol) {
				if (joinstar && alasql.options.joinstar == 'underscore') {
					ss.push(
						"'" +
							alias +
							'_' +
							tcol.columnid +
							"':p['" +
							alias +
							"']['" +
							tcol.columnid +
							"']"
					);
				} else if (joinstar && alasql.options.joinstar == 'json') {
					//				ss.push('\''+alias+'_'+tcol.columnid+'\':p[\''+alias+'\'][\''+tcol.columnid+'\']');
					sp +=
						"r['" +
						alias +
						"']['" +
						tcol.columnid +
						"']=p['" +
						alias +
						"']['" +
						tcol.columnid +
						"'];";
				} else {
					var value = "p['" + alias + "']['" + tcol.columnid + "']";
					if (!columnIds[tcol.columnid]) {
						var key = "'" + tcol.columnid + "':";
						ss.push(key + value);
						columnIds[tcol.columnid] = {
							id: ss.length - 1,
							value: value,
							key: key,
						};
					} else {
						var newValue =
							value +
							' !== undefined ? ' +
							value +
							' : ' +
							columnIds[tcol.columnid].value;
						ss[columnIds[tcol.columnid].id] = columnIds[tcol.columnid].key + newValue;
						columnIds[tcol.columnid].value = newValue;
					}
				}

				query.selectColumns[escapeq(tcol.columnid)] = true;

				//			console.log('ok',tcol);

				var coldef = {
					columnid: tcol.columnid,
					dbtypeid: tcol.dbtypeid,
					dbsize: tcol.dbsize,
					dbprecision: tcol.dbprecision,
					dbenum: tcol.dbenum,
				};
				query.columns.push(coldef);
				query.xcolumns[coldef.columnid] = coldef;
			});
			//console.log(999,columns);
		} else {
			//					console.log(60,alias,columns);

			// if column not exist, then copy all
			sp += 'var w=p["' + alias + '"];for(var k in w){r[k]=w[k]};';
			//console.log(777, sp);
			query.dirtyColumns = true;
		}
		//	}
		//console.log(87,{s:ss.join(','),sp:sp});
	});

	return {s: ss.join(','), sp: sp};
}
779549
public function joinSubSelect($join, $spec, $name, $cond = null)
    {
        if (!$this->from) {
            throw new \Exception('Cannot join() without from() first.');
        }

        $join                          = strtoupper(ltrim("$join JOIN"));
        $name                          = $this->quoteName($name);
        $cond                          = $this->fixJoinCondition($cond);
        $this->from[$this->from_key][] = rtrim("$join ($spec) AS $name $cond");
        return $this;
    }
1283727
private function getSQLForJoins($fromAlias, array &$knownAliases)
    {
        $sql = '';

        if (isset($this->sqlParts['join'][$fromAlias])) {
            foreach ($this->sqlParts['join'][$fromAlias] as $join) {
                if (array_key_exists($join['joinAlias'], $knownAliases)) {
                    throw QueryException::nonUniqueAlias($join['joinAlias'], array_keys($knownAliases));
                }
                $sql                             .= ' ' . strtoupper($join['joinType'])
                    . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias']
                    . ' ON ' . ((string) $join['joinCondition']);
                $knownAliases[$join['joinAlias']] = true;
            }

            foreach ($this->sqlParts['join'][$fromAlias] as $join) {
                $sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases);
            }
        }

        return $sql;
    }
756741
private function handleSubscriptionQuery(ConnectionInterface $from, $data) {
    $args = json_decode($data['query'], true);
    if (!class_exists($args[0]['class'])) {
      return;
    }
    $count = count($args);
    if ($count > 1) {
      for ($i = 1; $i < $count; $i++) {
        $newArg =
          \Nymph\REST::translateSelector($args[0]['class'], $args[$i]);
        if ($newArg === false) {
          return;
        }
        $args[$i] = $newArg;
      }
    }
    $etype = $args[0]['class']::ETYPE;
    $serialArgs = serialize($args);
    $this->prepareSelectors($args);

    if ($data['action'] === 'subscribe') {
      // Client is subscribing to a query.
      if (!key_exists($etype, $this->querySubs)) {
        $this->querySubs[$etype] = [];
      }
      if (!key_exists($serialArgs, $this->querySubs[$etype])) {
        $this->querySubs[$etype][$serialArgs] = new \SplObjectStorage();
      }
      $guidArgs = $args;
      $guidArgs[0]['return'] = 'guid';
      $guidArgs[0]['source'] = 'client';
      $token = null;
      if ($this->sessions->contains($from)) {
        $token = $this->sessions[$from];
      }
      if (class_exists('\Tilmeld\Tilmeld') && isset($token)) {
        $user = \Tilmeld\Tilmeld::extractToken($token);
        if ($user) {
          // Log in the user for access controls.
          \Tilmeld\Tilmeld::fillSession($user);
        }
      }
      $this->querySubs[$etype][$serialArgs][$from] = [
        'current' => call_user_func_array(
            "\Nymph\Nymph::getEntities",
            $guidArgs
        ),
        'query' => $data['query'],
        'count' => !!$data['count']
      ];
      if (class_exists('\Tilmeld\Tilmeld') && isset($token)) {
        // Clear the user that was temporarily logged in.
        \Tilmeld\Tilmeld::clearSession();
      }
      $this->logger->notice(
          "Client subscribed to a query! ".
            "($serialArgs, {$from->resourceId})"
      );

      if (Server::$config['broadcast_counts']) {
        // Notify clients of the subscription count.
        $count = count($this->querySubs[$etype][$serialArgs]);
        foreach ($this->querySubs[$etype][$serialArgs] as $key) {
          $curData = $this->querySubs[$etype][$serialArgs][$key];
          if ($curData['count']) {
            $key->send(json_encode([
              'query' => $curData['query'],
              'count' => $count
            ]));
          }
        }
      }
    }

    if ($data['action'] === 'unsubscribe') {
      // Client is unsubscribing from a query.
      if (!key_exists($etype, $this->querySubs)) {
        return;
      }
      if (!key_exists($serialArgs, $this->querySubs[$etype])) {
        return;
      }
      if (!$this->querySubs[$etype][$serialArgs]->contains($from)) {
        return;
      }
      unset($this->querySubs[$etype][$serialArgs][$from]);
      $this->logger->notice(
          "Client unsubscribed from a query! ".
            "($serialArgs, {$from->resourceId})"
      );

      $count = count($this->querySubs[$etype][$serialArgs]);

      if ($count === 0) {
        // No more subscribed clients.
        unset($this->querySubs[$etype][$serialArgs]);
        if (count($this->querySubs[$etype]) === 0) {
          unset($this->querySubs[$etype]);
        }
        return;
      }

      if (Server::$config['broadcast_counts']) {
        // Notify clients of the subscription count.
        foreach ($this->querySubs[$etype][$serialArgs] as $key) {
          $curData = $this->querySubs[$etype][$serialArgs][$key];
          if ($curData['count']) {
            $key->send(json_encode([
              'query' => $curData['query'],
              'count' => $count
            ]));
          }
        }
      }
    }
  }