Skip to content
Permalink
Browse files

Add support for cross schema joins

  • Loading branch information...
Marlinc committed Nov 4, 2015
1 parent ca496e1 commit 93718a2380354449a1e9f080dd9bc8f759caba74
@@ -693,6 +693,38 @@ public function log($sql)
$this->logger()->log($query);
}
/**
* Check if cross talk is supported between two connections
*
* @param ConnectionInterface $target Connection to check cross talk with
*
* @return bool
*/
public function supportsCrossWith(ConnectionInterface $target)
{
$sourceConfig = $this->config();
$targetConfig = $target->config();
// No need to do report cross support in case the same connection is being used
if ($sourceConfig['name'] === $targetConfig['name']) {
return false;
}
$configToCheck = [
'driver',
'host',
'port'
];
foreach ($configToCheck as $config) {
if ((isset($sourceConfig[$config])) && ($sourceConfig[$config] !== $targetConfig[$config])) {
return false;
}
}
return true;
}
/**
* Returns a new statement object that will log the activity
* for the passed original statement instance.
@@ -264,6 +264,16 @@ public function schemaValue($value)
return $this->_connection->quote($value, PDO::PARAM_STR);
}
/**
* Returns the schema name that's being used
*
* @return string
*/
public function schema()
{
return $this->_config['schema'];
}
/**
* Returns last id generated for a table or sequence in database
*
@@ -126,6 +126,14 @@ public function prepare($query)
return $result;
}
/**
* {@inheritDoc}
*/
public function schema()
{
return $this->_config['database'];
}
/**
* {@inheritDoc}
*/
@@ -0,0 +1,129 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.3.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Database\Expression;
use Cake\Database\ExpressionInterface;
use Cake\Database\ValueBinder;
/**
* An expression object that represents a cross schema table name
*
* @internal
*/
class CrossSchemaTableExpression implements ExpressionInterface
{
/**
* Name of the schema
*
* @var \Cake\Database\ExpressionInterface|string
*/
protected $_schema;
/**
* Name of the table
*
* @var \Cake\Database\ExpressionInterface|string
*/
protected $_table;
/**
* @inheritDoc
*
* @param string\\Cake\Database\ExpressionInterface $schema Name of the schema
* @param string|\Cake\Database\ExpressionInterface $table Name of the table
*/
public function __construct($schema, $table)
{
$this->_schema = $schema;
$this->_table = $table;
}
/**
* Get or set the schema to use
*
* @param null|string|\Cake\Database\ExpressionInterface $schema The schema to set
* @return $this|string|\Cake\Database\ExpressionInterface The schema that has been set
*/
public function schema($schema = null)
{
if ($schema !== null) {
$this->_schema = $schema;
return $this;
}
return $this->_schema;
}
/**
* Get or set the schema to use
*
* @param null|string|\Cake\Database\ExpressionInterface $table The table to set
* @return $this|string|\Cake\Database\ExpressionInterface The table that has been set
*/
public function table($table = null)
{
if ($table !== null) {
$this->_table = $table;
return $this;
}
return $this->_table;
}
/**
* Converts the Node into a SQL string fragment.
*
* @param \Cake\Database\ValueBinder $generator Placeholder generator object
* @return string
*/
public function sql(ValueBinder $generator)
{
$schema = $this->_schema;
if ($schema instanceof ExpressionInterface) {
$schema = $schema->sql($generator);
}
$table = $this->_table;
if ($table instanceof ExpressionInterface) {
$table = $table->sql($generator);
}
return sprintf('%s.%s', $schema, $table);
}
/**
* Iterates over each part of the expression recursively for every
* level of the expressions tree and executes the $visitor callable
* passing as first parameter the instance of the expression currently
* being iterated.
*
* @param callable $visitor The callable to apply to all nodes.
* @return void
*/
public function traverse(callable $visitor)
{
if ($this->_schema instanceof ExpressionInterface) {
$visitor($this->_schema);
$this->_schema->traverse($visitor);
}
if ($this->_table instanceof ExpressionInterface) {
$visitor($this->_table);
$this->_table->traverse($visitor);
}
}
}
@@ -14,6 +14,7 @@
*/
namespace Cake\Database;
use Cake\Database\Expression\CrossSchemaTableExpression;
use Cake\Database\Expression\FieldInterface;
use Cake\Database\Expression\IdentifierExpression;
use Cake\Database\Expression\OrderByExpression;
@@ -90,6 +91,10 @@ public function quoteExpression($expression)
$this->_quoteIdentifierExpression($expression);
return;
}
if ($expression instanceof CrossSchemaTableExpression) {
$this->_quoteCrossSchemaTableExpression($expression);
return;
}
}
/**
@@ -254,4 +259,20 @@ protected function _quoteIdentifierExpression(IdentifierExpression $expression)
$this->_driver->quoteIdentifier($expression->getIdentifier())
);
}
/**
* Quotes the cross schema table identifier
*
* @param CrossSchemaTableExpression $expression The identifier to quote
* @return void
*/
protected function _quoteCrossSchemaTableExpression(CrossSchemaTableExpression $expression)
{
if (!$expression->schema() instanceof ExpressionInterface) {
$expression->schema($this->_driver->quoteIdentifier($expression->schema()));
}
if (!$expression->table() instanceof ExpressionInterface) {
$expression->table($this->_driver->quoteIdentifier($expression->table()));
}
}
}
@@ -14,6 +14,8 @@
*/
namespace Cake\Database;
use Cake\Database\Expression\QueryExpression;
/**
* Responsible for compiling a Query object into its SQL representation
*
@@ -211,9 +213,15 @@ protected function _buildJoinPart($parts, $query, $generator)
{
$joins = '';
foreach ($parts as $join) {
$subquery = $join['table'] instanceof Query || $join['table'] instanceof QueryExpression;
if ($join['table'] instanceof ExpressionInterface) {
$join['table'] = '(' . $join['table']->sql($generator) . ')';
$join['table'] = $join['table']->sql($generator);
}
if ($subquery) {
$join['table'] = '(' . $join['table'] . ')';
}
$joins .= sprintf(' %s JOIN %s %s', $join['type'], $join['table'], $join['alias']);
if (isset($join['conditions']) && count($join['conditions'])) {
$joins .= sprintf(' ON %s', $join['conditions']->sql($generator));
@@ -15,6 +15,7 @@
namespace Cake\ORM;
use Cake\Core\ConventionsTrait;
use Cake\Database\Expression\CrossSchemaTableExpression;
use Cake\Database\Expression\IdentifierExpression;
use Cake\Datasource\EntityInterface;
use Cake\Datasource\ResultSetDecorator;
@@ -524,13 +525,22 @@ public function attachTo(Query $query, array $options = [])
{
$target = $this->target();
$joinType = empty($options['joinType']) ? $this->joinType() : $options['joinType'];
$table = $target->table();
if ($this->source()->connection()->supportsCrossWith($target->connection())) {
$table = new CrossSchemaTableExpression(
$target->connection()->driver()->schema(),
$table
);
}
$options += [
'includeFields' => true,
'foreignKey' => $this->foreignKey(),
'conditions' => [],
'fields' => [],
'type' => $joinType,
'table' => $target->table(),
'table' => $table,
'finder' => $this->finder()
];
@@ -962,4 +962,32 @@ public function testSchemaCollection()
$connection->schemaCollection($schema);
$this->assertSame($schema, $connection->schemaCollection());
}
/**
* Tests supportsCrossWith
*
* @return void
*/
public function testSupportsCrossWith()
{
$connection = new Connection(ConnectionManager::config('test'));
$targetConnection = new Connection(ConnectionManager::config('test'));
$this->assertFalse($connection->supportsCrossWith($targetConnection), 'The same connection can\'t used in cross');
$connection = new Connection(ConnectionManager::config('test'));
$targetConnection = new Connection(['name' => 'test2'] + ConnectionManager::config('test'));
$this->assertTrue($connection->supportsCrossWith($targetConnection), 'Cross should be supported on databases on the same server');
$connection = new Connection(ConnectionManager::config('test'));
$targetConnection = new Connection(['port' => 999999] + ConnectionManager::config('test'));
$this->assertFalse($connection->supportsCrossWith($targetConnection), 'Cross is not supported across different server instances');
$connection = new Connection(ConnectionManager::config('test'));
$targetConnection = new Connection(['host' => 'db2.example.com'] + ConnectionManager::config('test'));
$this->assertFalse($connection->supportsCrossWith($targetConnection), 'Cross is not supported across different server instances');
}
}
Oops, something went wrong.

0 comments on commit 93718a2

Please sign in to comment.
You can’t perform that action at this time.