public function assemble(AbstractSQLDataSourceQueryHandler $datasourceHandler, AbstractQueryRequest $request, DatasetMetaData $dataset, array $columnNames = NULL) {
        $statement = new Statement();

        $TABLE_ALIAS__FACTS = 'dsf';
        $TABLE_ALIAS__COLUMN_JOIN = 'cj';

        $metamodel = data_controller_get_metamodel();

        $cubeName = $dataset->name;
        $cube = $metamodel->getCube($cubeName);

        $factsDataset = $metamodel->getDataset($cube->factsDatasetName);

        self::$TABLE_ALIAS_SUFFIX__SEQUENCE++;
        $factsTable = new DatasetSection($factsDataset, $TABLE_ALIAS__FACTS . self::$TABLE_ALIAS_SUFFIX__SEQUENCE);
        $statement->tables[] = $factsTable;

        foreach ($dataset->getColumns() as $column) {
            $columnName = $column->name;

            $columnDefaultAlias = NULL;
            if (isset($columnNames)) {
                $columnDefaultAlias = array_search($columnName, $columnNames);
                if ($columnDefaultAlias === FALSE) {
                    continue;
                }

                // fixing the alias if the list of columns is not associative array
                if (is_int($columnDefaultAlias)) {
                    $columnDefaultAlias = NULL;
                }
            }

            // FIXME the following code does not work in the following situation:
            //   1) we need to query a dataset which is defined in GovDashboard using file upload
            //   2) column names are not provided
            //   3) for columns which are references to lookup, value from the primary key from lookup table is returned
            //      even if in definition of the dataset we have columns with the following application type: name@lookup, code @lookup and etc

            $isReferenceUsed = FALSE;
            $handler = DimensionLookupFactory::getInstance()->findHandler($column->type->getLogicalApplicationType());
            if (isset($handler)) {
                // FIXME remove this implementation when we implement 'Executable Tree' functionality
                list($connectedDatasetName, $connectedColumnName) = $handler->adjustReferencePointColumn($metamodel, $factsDataset->name, $columnName);
                if (($connectedDatasetName != $factsDataset->name) || ($connectedColumnName != $columnName)) {
                    self::$TABLE_ALIAS_SUFFIX__SEQUENCE++;
                    $columnJoinAliasPrefix = $TABLE_ALIAS__COLUMN_JOIN . self::$TABLE_ALIAS_SUFFIX__SEQUENCE;

                    $connectedDataset = $metamodel->getDataset($connectedDatasetName);
                    $connectedDatasetKeyColumnName = $connectedDataset->getKeyColumn()->name;

                    // preparing list of columns we want to get from connected dataset
                    $connectedDatasetColumnNames = NULL;
                    ArrayHelper::addUniqueValue($connectedDatasetColumnNames, $connectedDatasetKeyColumnName);
                    ArrayHelper::addUniqueValue($connectedDatasetColumnNames, $connectedColumnName);

                    $connectedStatement = $datasourceHandler->assembleDatasetSourceStatement($request, $connectedDataset, $connectedDatasetColumnNames);
                    $connectedStatement->addTableAliasPrefix($columnJoinAliasPrefix);

                    $connectedTable = $connectedStatement->getColumnTable($connectedColumnName);
                    // registering the column for facts table
                    $factsTableColumn = new ColumnSection($columnName, 'fact_' . $columnName);
                    $factsTableColumn->visible = FALSE;
                    $factsTable->columns[] = $factsTableColumn;
                    // adjusting key column from lookup
                    $connectedDatasetKeyColumn = $connectedTable->getColumn($connectedDatasetKeyColumnName);
                    $connectedDatasetKeyColumn->alias = 'lookup_' . $connectedDatasetKeyColumnName;
                    $connectedDatasetKeyColumn->visible = FALSE;
                    // adjusting value column from lookup
                    $connectedDatasetValueColumn = $connectedTable->getColumn($connectedColumnName);
                    $connectedDatasetValueColumn->alias = $columnName;
                    // the key column could be the same as value column
                    $connectedDatasetValueColumn->visible = TRUE;
                    // new value column which uses composite name as column alias
                    if (isset($columnDefaultAlias)) {
                        $connectedDatasetValueColumn2 = new ColumnSection(
                            $connectedColumnName,
                            DataSourceColumnNameHelper::generateFromParameterElements($datasourceHandler->getMaximumEntityNameLength(), $columnDefaultAlias));
                        $connectedDatasetValueColumn2->visible = FALSE;
                        $connectedTable->columns[] = $connectedDatasetValueColumn2;
                    }

                    // linking the lookup table with the facts table
                    $connectedTable->conditions[] = new JoinConditionSection(
                        $connectedDatasetKeyColumn->alias, new TableColumnConditionSectionValue($factsTable->alias, $columnName));

                    $statement->merge($connectedStatement);

                    $isReferenceUsed = TRUE;
                }
            }

            if (!$isReferenceUsed) {
                $factsTable->columns[] = new ColumnSection($columnName);
            }
        }

        return $statement;
    }
    protected function assembleConnectedDatasetSourceStatement(DataControllerCallContext $callcontext, AbstractQueryRequest $request, ReferenceLink $link, array $columnNames, array $linkExecutionStack = NULL) {
        $TABLE_ALIAS__LINK = 'l';

        $nestedLinkExecutionStack = $linkExecutionStack;
        $nestedLinkExecutionStack[] = $link;

        $selectedColumnNames = ReferenceLinkBuilder::selectReferencedColumnNames4ReferenceLink($nestedLinkExecutionStack, $columnNames);

        // some columns could be mapped to the same column in a dataset. Removing duplicates
        $datasetColumnNames = NULL;
        if (isset($selectedColumnNames)) {
            foreach ($selectedColumnNames as $selectedColumnName) {
                if (isset($datasetColumnNames) && in_array($selectedColumnName, $datasetColumnNames)) {
                    continue;
                }

                $datasetColumnNames[] = $selectedColumnName;
            }
        }

        $statement = $this->assembleDatasetSourceStatement($request, $link->dataset, $datasetColumnNames);

        $linkTableAliasPrefix = $TABLE_ALIAS__LINK . $link->linkId;
        $statement->addTableAliasPrefix($linkTableAliasPrefix);

        // adding columns which we use to join with parent dataset
        if (!$link->isRoot()) {
            foreach ($link->columnNames as $columnName) {
                $joinColumnAlias = DataSourceColumnNameHelper::generateFromParameterElements(
                    $this->getMaximumEntityNameLength(),
                    ReferencePathHelper::assembleReference($link->dataset->name, $columnName));
                $joinTable = $statement->findColumnTable($columnName);
                if (!isset($joinTable)) {
                    $joinTable = $statement->tables[0];
                }
                $joinColumn = $joinTable->findColumnByAlias($joinColumnAlias);
                if (!isset($joinColumn)) {
                    $joinTable->columns[] = new ColumnSection($columnName, $joinColumnAlias);
                }
            }
        }

        // adding columns which we use to join with nested datasets
        if (isset($link->nestedLinks)) {
            foreach ($link->nestedLinks as $nestedLink) {
                foreach ($nestedLink->parentColumnNames as $parentColumnName) {
                    $parentColumnAlias = DataSourceColumnNameHelper::generateFromParameterElements(
                        $this->getMaximumEntityNameLength(),
                        ReferencePathHelper::assembleReference($link->dataset->name, $parentColumnName));
                    $joinTable = $statement->getColumnTable($parentColumnName);
                    $joinColumn = $joinTable->findColumnByAlias($parentColumnAlias);
                    if (!isset($joinColumn)) {
                        $joinTable->columns[] = new ColumnSection($parentColumnName, $parentColumnAlias);
                    }
                }
            }
        }

        // collecting columns which we need to mark as invisible
        $shouldBeInvisibleColumns = NULL;
        foreach ($statement->tables as $table) {
            if (isset($table->columns)) {
                foreach ($table->columns as $column) {
                    $shouldBeInvisibleColumns[$table->alias][$column->alias] = $column;
                }
            }
            else {
                $table->columns = array(); // We do not need any columns
            }
        }

        // adding or making as visible columns which we need to return
        if (isset($selectedColumnNames)) {
            foreach ($selectedColumnNames as $originalColumnName => $selectedColumnName) {
                // we need to show only those columns which are requested.
                // All intermediate columns (which are used to link with nested datasets) will not be shown
                if (in_array($originalColumnName, $columnNames)) {
                    $databaseColumnName = DataSourceColumnNameHelper::generateFromParameterElements($this->getMaximumEntityNameLength(), $originalColumnName);
                    $table = $statement->getColumnTable($selectedColumnName, TRUE);
                    $column = $table->findColumnByAlias($databaseColumnName);
                    if (isset($column)) {
                        $column->visible = TRUE;
                        unset($shouldBeInvisibleColumns[$table->alias][$column->alias]);
                    }
                    else {
                        $column = $table->findColumnByAlias($selectedColumnName);
                        if (isset($column)) {
                            // adding clone of the same column with another alias
                            $column = clone $column;
                            $column->visible = TRUE;
                            $column->alias = $databaseColumnName;
                        }
                        else {
                            $column = new ColumnSection($selectedColumnName, $databaseColumnName);
                        }

                        $table->columns[] = $column;
                    }

                    $callcontext->columnMapping[$databaseColumnName] = $originalColumnName;
                }
            }
        }

        if (isset($shouldBeInvisibleColumns)) {
            foreach ($shouldBeInvisibleColumns as $tableColumns) {
                foreach ($tableColumns as $column) {
                    $column->visible = FALSE;
                }
            }
        }

        // supporting nested links
        if (isset($link->nestedLinks)) {
            foreach ($link->nestedLinks as $nestedLink) {
                $nestedStatement = $this->assembleConnectedDatasetSourceStatement($callcontext, $request, $nestedLink, $columnNames, $nestedLinkExecutionStack);

                foreach ($nestedLink->parentColumnNames as $referencePointColumnIndex => $parentColumnName) {
                    // preparing parent table alias
                    $parentColumnAlias = DataSourceColumnNameHelper::generateFromParameterElements(
                        $this->getMaximumEntityNameLength(),
                        ReferencePathHelper::assembleReference($link->dataset->name, $parentColumnName));
                    $parentTableAlias = $statement->getColumnTable($parentColumnAlias)->alias;

                    // linking with parent
                    $nestedColumnName = $nestedLink->columnNames[$referencePointColumnIndex];
                    $nestedColumnAlias = DataSourceColumnNameHelper::generateFromParameterElements(
                        $this->getMaximumEntityNameLength(),
                        ReferencePathHelper::assembleReference($nestedLink->dataset->name, $nestedColumnName));
                    $nestedStatement->getColumnTable($nestedColumnAlias)->conditions[] = new JoinConditionSection(
                        $nestedColumnName,
                        new TableColumnConditionSectionValue($parentTableAlias, $parentColumnName));
                }

                $statement->merge($nestedStatement);
            }
        }

        return $statement;
    }
    protected function prepareReferencedCubeQueryStatement(
        AbstractSQLDataSourceQueryHandler $datasourceHandler, DataControllerCallContext $callcontext,
            Statement $combinedStatement, array $datasetMappedCubeRequests, ReferenceLink $link) {

        if (!isset($link->nestedLinks)) {
            return;
        }

        $metamodel = data_controller_get_metamodel();

        foreach ($link->nestedLinks as $referencedLink) {
            $referencedRequest = NULL;
            if (isset($datasetMappedCubeRequests[$referencedLink->dataset->name])) {
                $referencedRequest = clone $datasetMappedCubeRequests[$referencedLink->dataset->name];
            }
            else {
                // checking if there is corresponding cube for the referenced dataset
                $possibleReferencedCube = $metamodel->findCubeByDatasetName($referencedLink->dataset->name);
                if (isset($possibleReferencedCube)) {
                    $referencedRequest = new CubeQueryRequest($possibleReferencedCube->name);
                    $referencedRequest->referenced = TRUE;

                    $datasetMappedCubeRequests[$possibleReferencedCube->factsDatasetName] = $referencedRequest;
                }
            }

            if (isset($referencedRequest)) {
                // preparing parent cube
                $parentRequest = $datasetMappedCubeRequests[$link->dataset->name];
                $parentCubeName = $parentRequest->getCubeName();
                $parentCube = $metamodel->getCube($parentCubeName);

                // preparing referenced cube
                $referencedCubeName = $referencedRequest->getCubeName();
                $referencedCube = $metamodel->getCube($referencedCubeName);

                // adding required dimensions
                $joinConditions = NULL;
                foreach ($referencedLink->parentColumnNames as $columnIndex => $parentColumnName) {
                    // looking for a dimension in parent cube
                    $parentDimension = $parentCube->getDimensionByAttributeColumnName($parentColumnName);

                    // looking for a dimension in referenced cube
                    $referencedColumnName = $referencedLink->columnNames[$columnIndex];
                    $referencedDimension = $referencedCube->getDimensionByAttributeColumnName($referencedColumnName);

                    // checking if this dimension is part of query portion of parent request
                    $parentRequestDimensionQuery = $parentRequest->findDimensionQuery($parentDimension->name);
                    if (isset($parentRequestDimensionQuery)) {
                        // copying the query request to referenced cube
                        $referencedRequestDimensionQuery = new __AbstractCubeQueryRequest_DimensionQuery($referencedDimension->name);
                        $referencedRequestDimensionQuery->columns = $parentRequestDimensionQuery->columns;
                        $referencedRequest->importDimensionQueryFrom($referencedRequestDimensionQuery);
                    }

                    // checking if there is a related query for parent column name
                    $parentRequestFactsDatasetColumnQuery = $parentRequest->findFactsDatasetColumnQuery($parentColumnName);
                    if (isset($parentRequestFactsDatasetColumnQuery)) {
                        // copying the query request to referenced cube
                        $referencedRequest->addFactsDatasetColumnQueryValues($referencedColumnName, $parentRequestFactsDatasetColumnQuery->values);
                    }

                    // checking if this dimension is part of parent request
                    $parentRequestDimension = $parentRequest->findDimension($parentDimension->name);
                    if (!isset($parentRequestDimension)) {
                        // because this dimension is not in list of returned columns we should not use it to link with referenced cube
                        continue;
                    }

                    $referencedRequestDimension = $referencedRequest->addDimension(
                        NULL, // TODO support requestColumnIndex here
                        $referencedDimension->name);

                    $columnNames = $parentRequestDimension->getColumnNames();
                    if (isset($columnNames)) {
                        foreach ($columnNames as $columnName) {
                            $referencedRequestDimension->registerColumnName(
                                NULL, // TODO support requestColumnIndex here
                                $columnName);
                        }
                    }
                    else {
                        // to help the following loop to link cubes by dimension
                        $columnNames = array(NULL);
                    }

                    foreach ($columnNames as $columnName) {
                        $parentDatabaseColumnName = DataSourceColumnNameHelper::generateFromParameterElements(
                            $datasourceHandler->getMaximumEntityNameLength(),
                            ($parentRequest->referenced ? ReferencePathHelper::assembleReference($parentCube->factsDatasetName, $parentDimension->name) : $parentDimension->name),
                            $columnName);

                        $referencedDatabaseColumnName = DataSourceColumnNameHelper::generateFromParameterElements(
                            $datasourceHandler->getMaximumEntityNameLength(),
                            ReferencePathHelper::assembleReference($referencedCube->factsDatasetName, $referencedDimension->name),
                            $columnName);

                        $joinConditions[] = new JoinConditionSection(
                            $referencedDatabaseColumnName, new TableColumnConditionSectionValue(self::$TABLE_ALIAS__REFERENCED . $link->linkId, $parentDatabaseColumnName));
                    }
                }
                if (!isset($joinConditions)) {
                    throw new IllegalArgumentException(t(
                        'There is no common columns to join %datasetNameA and %datasetNameB datasets',
                        array('%datasetNameA' => $parentCube->publicName, '%datasetNameB' => $referencedCube->publicName)));
                }

                // preparing aggregation statement for referenced cube
                $referencedAggregationStatement = $this->prepareSelectedCubeQueryStatement($datasourceHandler, $callcontext, $referencedRequest);
                list($isSubqueryRequired, $assembledReferencedCubeSections) = $referencedAggregationStatement->prepareSections(NULL);
                $referencedCubeSubquerySection = new SubquerySection(
                    Statement::assemble($isSubqueryRequired, NULL, $assembledReferencedCubeSections, SelectStatementPrint::INDENT__LEFT_OUTER_JOIN__SUBQUERY, FALSE),
                    self::$TABLE_ALIAS__REFERENCED . $referencedLink->linkId);

                // preparing columns which are returned by referenced aggregation
                foreach ($referencedAggregationStatement->tables as $table) {
                    if (!isset($table->columns)) {
                        continue;
                    }

                    foreach ($table->columns as $column) {
                        if (!$column->visible) {
                            continue;
                        }

                        $referencedCubeSubquerySection->columns[] = new ColumnSection($column->alias);
                    }
                }

                // linking with parent cube
                foreach ($joinConditions as $joinCondition) {
                    // we do not need to return columns which are used to join with parent cube
                    $referencedCubeSubquerySection->getColumn($joinCondition->subjectColumnName)->visible = FALSE;

                    $referencedCubeSubquerySection->conditions[] = $joinCondition;
                }

                // adding to resulting statement
                $combinedStatement->tables[] = $referencedCubeSubquerySection;

                // applying referenced cubes measure conditions on resulting statement as well
                $measureQueries = $referencedRequest->findMeasureQueries();
                if (isset($measureQueries)) {
                    foreach ($measureQueries as $measureQuery) {
                        $measureName = ReferencePathHelper::assembleReference($referencedCube->factsDatasetName, $measureQuery->name);
                        $measureDatabaseColumnName = DataSourceColumnNameHelper::generateFromParameterElements(
                            $datasourceHandler->getMaximumEntityNameLength(), $measureName);

                        foreach ($measureQuery->values as $value) {
                            $combinedStatement->conditions[] = new WhereConditionSection(
                                self::$TABLE_ALIAS__REFERENCED . $referencedLink->linkId,
                                $measureDatabaseColumnName,
                                    new ExactConditionSectionValue(
                                        $datasourceHandler->formatOperatorValue($callcontext, $referencedRequest, $referencedCube->factsDatasetName, NULL, $value)));
                        }
                    }
                }
            }
            else {
                throw new UnsupportedOperationException(t('Cube joins using intermediate dataset is not supported yet'));

                // preparing statement for intermediate dataset
                $requiredColumnNames = $referencedLink->columnNames;
                $referencedIntermediateDatasetStatement = $datasourceHandler->prepareDatasetSourceStatement($callcontext, NULL, $referencedLink->dataset, $requiredColumnNames);

                // adding condition to join with parent statement
                $referencedIntermediateDatasetTableSection = $referencedIntermediateDatasetStatement->tables[0];
                foreach ($referencedLink->columnNames as $columnIndex => $referencedColumnName) {
                    $referencedDatabaseColumnName = $referencedColumnName;

                    $parentColumnName = $referencedLink->parentColumnNames[$columnIndex];
                    $parentDatabaseColumnName = $parentColumnName;

                    $referencedIntermediateDatasetTableSection->conditions[] = new JoinConditionSection(
                        $referencedDatabaseColumnName, new TableColumnConditionSectionValue(self::$TABLE_ALIAS__REFERENCED . $link->linkId, $parentDatabaseColumnName));
                }

                $combinedStatement->merge($referencedIntermediateDatasetStatement);
            }

            // recursively check nested levels
            $this->prepareReferencedCubeQueryStatement($datasourceHandler, $callcontext, $combinedStatement, $datasetMappedCubeRequests, $referencedLink);
        }
    }