Exemple #1
0
 def test_expressions_in_union_in_union_order_by(self):
     column = SQLRaw("1")
     alias = Alias(column, "id")
     expr = Union(Select(alias),
                  Select(column),
                  order_by=alias + 1,
                  limit=1,
                  offset=1,
                  all=True)
     expr = Union(expr, expr, order_by=alias + 1, all=True)
     result = self.connection.execute(expr)
     self.assertEquals(result.get_all(), [(1, ), (1, )])
Exemple #2
0
    def getSpecifications(self, user):
        """See `IMilestoneData`"""
        from lp.registry.model.person import Person
        origin = [Specification]
        product_origin, clauses = get_specification_active_product_filter(self)
        origin.extend(product_origin)
        clauses.extend(get_specification_privacy_filter(user))
        origin.append(LeftJoin(Person, Specification._assigneeID == Person.id))
        milestones = self._milestone_ids_expr(user)

        results = Store.of(self.target).using(*origin).find(
            (Specification, Person),
            Specification.id.is_in(
                Union(Select(
                    Specification.id,
                    tables=[Specification],
                    where=(Specification.milestoneID.is_in(milestones))),
                      Select(SpecificationWorkItem.specification_id,
                             tables=[SpecificationWorkItem],
                             where=And(
                                 SpecificationWorkItem.milestone_id.is_in(
                                     milestones),
                                 SpecificationWorkItem.deleted == False)),
                      all=True)), *clauses)
        ordered_results = results.order_by(Desc(Specification.priority),
                                           Specification.definition_status,
                                           Specification.implementation_status,
                                           Specification.title)
        ordered_results.config(distinct=True)
        return DecoratedResultSet(ordered_results, itemgetter(0))
Exemple #3
0
    def test_expressions_in_union_order_by(self):
        # The following statement breaks in postgres:
        #     SELECT 1 AS id UNION SELECT 1 ORDER BY id+1;
        # With the error:
        #     ORDER BY on a UNION/INTERSECT/EXCEPT result must
        #     be on one of the result columns
        column = SQLRaw("1")
        Alias.auto_counter = 0
        alias = Alias(column, "id")
        expr = Union(Select(alias),
                     Select(column),
                     order_by=alias + 1,
                     limit=1,
                     offset=1,
                     all=True)

        state = State()
        statement = compile(expr, state)
        self.assertEquals(
            statement, 'SELECT * FROM '
            '((SELECT 1 AS id) UNION ALL (SELECT 1)) AS "_1" '
            'ORDER BY id+? LIMIT 1 OFFSET 1')
        self.assertVariablesEqual(state.parameters, [Variable(1)])

        result = self.connection.execute(expr)
        self.assertEquals(result.get_one(), (1, ))
    def findRecipes(branch_or_repository, revspecs=None):
        """Find recipes for a given branch or repository.

        :param branch_or_repository: The branch or repository to search for.
        :param revspecs: If not None, return only recipes whose `revspec` is
            in this sequence.
        :return: a collection of `ISourcePackageRecipe`s.
        """
        from lp.code.model.sourcepackagerecipe import SourcePackageRecipe
        store = Store.of(branch_or_repository)
        if IGitRepository.providedBy(branch_or_repository):
            data_clause = (SourcePackageRecipeData.base_git_repository ==
                           branch_or_repository)
            insn_clause = (_SourcePackageRecipeDataInstruction.git_repository
                           == branch_or_repository)
        elif IBranch.providedBy(branch_or_repository):
            data_clause = (
                SourcePackageRecipeData.base_branch == branch_or_repository)
            insn_clause = (_SourcePackageRecipeDataInstruction.branch ==
                           branch_or_repository)
        else:
            raise AssertionError("Unsupported source: %r" %
                                 (branch_or_repository, ))
        if revspecs is not None:
            concrete_revspecs = [
                revspec for revspec in revspecs if revspec is not None
            ]
            data_revspec_clause = In(SourcePackageRecipeData.revspec,
                                     concrete_revspecs)
            insn_revspec_clause = In(
                _SourcePackageRecipeDataInstruction.revspec, concrete_revspecs)
            if None in revspecs:
                data_revspec_clause = Or(
                    data_revspec_clause,
                    SourcePackageRecipeData.revspec == None)
                insn_revspec_clause = Or(
                    insn_revspec_clause,
                    _SourcePackageRecipeDataInstruction.revspec == None)
            data_clause = And(data_clause, data_revspec_clause)
            insn_clause = And(insn_clause, insn_revspec_clause)
        return store.find(
            SourcePackageRecipe,
            SourcePackageRecipe.id.is_in(
                Union(
                    Select(SourcePackageRecipeData.sourcepackage_recipe_id,
                           data_clause),
                    Select(
                        SourcePackageRecipeData.sourcepackage_recipe_id,
                        And(
                            _SourcePackageRecipeDataInstruction.recipe_data_id
                            == SourcePackageRecipeData.id, insn_clause)))))
Exemple #5
0
 def findRecipes(branch):
     from lp.code.model.sourcepackagerecipe import SourcePackageRecipe
     store = Store.of(branch)
     return store.find(
         SourcePackageRecipe,
         SourcePackageRecipe.id.is_in(Union(
             Select(
                 SourcePackageRecipeData.sourcepackage_recipe_id,
                 SourcePackageRecipeData.base_branch == branch),
             Select(
                 SourcePackageRecipeData.sourcepackage_recipe_id,
                 And(
                     _SourcePackageRecipeDataInstruction.recipe_data_id ==
                     SourcePackageRecipeData.id,
                     _SourcePackageRecipeDataInstruction.branch == branch)
                 )
         ))
     )
def calculate_bugsummary_rows(target):
    """Calculate BugSummary row fragments for the given `IBugTarget`.

    The data is re-aggregated from BugTaskFlat, BugTag and BugSubscription.
    """
    # Use a CTE to prepare a subset of BugTaskFlat, filtered to the
    # relevant target and to exclude duplicates, and with has_patch
    # calculated.
    relevant_tasks = With(
        'relevant_task',
        Select((BugTaskFlat.bug_id, BugTaskFlat.information_type,
                BugTaskFlat.status, BugTaskFlat.milestone_id,
                BugTaskFlat.importance,
                Alias(BugTaskFlat.latest_patch_uploaded != None, 'has_patch'),
                BugTaskFlat.access_grants, BugTaskFlat.access_policies),
               tables=[BugTaskFlat],
               where=And(BugTaskFlat.duplicateof_id == None,
                         *get_bugtaskflat_constraint(target))))

    # Storm class to reference the CTE.
    class RelevantTask(BugTaskFlat):
        __storm_table__ = 'relevant_task'

        has_patch = Bool()

    # Storm class to reference the union.
    class BugSummaryPrototype(RawBugSummary):
        __storm_table__ = 'bugsummary_prototype'

    # Prepare a union for all combination of privacy and taggedness.
    # It'll return a full set of
    # (status, milestone, importance, has_patch, tag, viewed_by, access_policy)
    # rows.
    common_cols = (RelevantTask.status, RelevantTask.milestone_id,
                   RelevantTask.importance, RelevantTask.has_patch)
    null_tag = Alias(Cast(None, 'text'), 'tag')
    null_viewed_by = Alias(Cast(None, 'integer'), 'viewed_by')
    null_policy = Alias(Cast(None, 'integer'), 'access_policy')

    tag_join = Join(BugTag, BugTag.bugID == RelevantTask.bug_id)

    public_constraint = RelevantTask.information_type.is_in(
        PUBLIC_INFORMATION_TYPES)
    private_constraint = RelevantTask.information_type.is_in(
        PRIVATE_INFORMATION_TYPES)

    unions = Union(
        # Public, tagless
        Select(common_cols + (null_tag, null_viewed_by, null_policy),
               tables=[RelevantTask],
               where=public_constraint),
        # Public, tagged
        Select(common_cols + (BugTag.tag, null_viewed_by, null_policy),
               tables=[RelevantTask, tag_join],
               where=public_constraint),
        # Private, access grant, tagless
        Select(common_cols +
               (null_tag, Unnest(RelevantTask.access_grants), null_policy),
               tables=[RelevantTask],
               where=private_constraint),
        # Private, access grant, tagged
        Select(common_cols +
               (BugTag.tag, Unnest(RelevantTask.access_grants), null_policy),
               tables=[RelevantTask, tag_join],
               where=private_constraint),
        # Private, access policy, tagless
        Select(
            common_cols +
            (null_tag, null_viewed_by, Unnest(RelevantTask.access_policies)),
            tables=[RelevantTask],
            where=private_constraint),
        # Private, access policy, tagged
        Select(
            common_cols +
            (BugTag.tag, null_viewed_by, Unnest(RelevantTask.access_policies)),
            tables=[RelevantTask, tag_join],
            where=private_constraint),
        all=True)

    # Select the relevant bits of the prototype rows and aggregate them.
    proto_key_cols = (BugSummaryPrototype.status,
                      BugSummaryPrototype.milestone_id,
                      BugSummaryPrototype.importance,
                      BugSummaryPrototype.has_patch, BugSummaryPrototype.tag,
                      BugSummaryPrototype.viewed_by_id,
                      BugSummaryPrototype.access_policy_id)
    origin = IStore(BugTaskFlat).with_(relevant_tasks).using(
        Alias(unions, 'bugsummary_prototype'))
    results = origin.find(proto_key_cols + (Count(), ))
    results = results.group_by(*proto_key_cols).order_by(*proto_key_cols)
    return results
Exemple #7
0
def _calculate_tag_query(conditions, tags):
    """Determine tag-related conditions and assemble a query.

    :param conditions: the other conditions that constrain the query.
    :param tags: the list of tags that the bug has.
    """
    # These are tables and joins we will want.  We leave out the tag join
    # because that needs to be added conditionally.
    tables = [
        StructuralSubscription,
        Join(BugSubscriptionFilter,
             BugSubscriptionFilter.structural_subscription_id ==
             StructuralSubscription.id),
        LeftJoin(BugSubscriptionFilterStatus,
                 BugSubscriptionFilterStatus.filter_id ==
                 BugSubscriptionFilter.id),
        LeftJoin(BugSubscriptionFilterImportance,
                 BugSubscriptionFilterImportance.filter_id ==
                 BugSubscriptionFilter.id),
        LeftJoin(BugSubscriptionFilterInformationType,
                 BugSubscriptionFilterInformationType.filter_id ==
                 BugSubscriptionFilter.id)]
    tag_join = LeftJoin(
        BugSubscriptionFilterTag,
        BugSubscriptionFilterTag.filter_id == BugSubscriptionFilter.id)
    # If the bug has no tags, this is relatively easy. Otherwise, not so
    # much.
    if len(tags) == 0:
        # The bug has no tags.  We should leave out filters that
        # require any generic non-empty set of tags
        # (BugSubscriptionFilter.include_any_tags), which we do with
        # the conditions.
        conditions.append(Not(BugSubscriptionFilter.include_any_tags))
        tables.append(tag_join)
        return Select(
            BugSubscriptionFilter.id,
            tables=tables,
            where=And(*conditions),
            # We have to make sure that the filter does not require
            # any *specific* tags. We do that with a GROUP BY on the
            # filters, and then a HAVING clause that aggregates the
            # BugSubscriptionFilterTags that are set to "include" the
            # tag.  (If it is not an include, that is an exclude, and a
            # bug without tags will not have a particular tag, so we can
            # ignore those in this case.)  This requires a CASE
            # statement within the COUNT.
            group_by=(BugSubscriptionFilter.id,),
            having=Count(
                SQL('CASE WHEN BugSubscriptionFilterTag.include '
                    'THEN BugSubscriptionFilterTag.tag END')) == 0)
    else:
        # The bug has some tags.  This will require a bit of fancy
        # footwork. First, though, we will simply want to leave out
        # filters that should only match bugs without tags.
        conditions.append(Not(BugSubscriptionFilter.exclude_any_tags))
        # We're going to have to do a union with another query.  One
        # query will handle filters that are marked to include *any*
        # of the filter's selected tags, and the other query will
        # handle filters that include *all* of the filter's selected
        # tags (as determined by BugSubscriptionFilter.find_all_tags).
        # Every aspect of the unioned queries' WHERE clauses *other
        # than tags* will need to be the same, and so we perform that
        # separately, first.  When Storm supports the WITH statement
        # (bug 729134), we can consider folding this back into a single
        # query.
        candidates = list(
            IStore(BugSubscriptionFilter).using(*tables).find(
                BugSubscriptionFilter.id, *conditions))
        if not candidates:
            return None
        # As mentioned, in this first SELECT we handle filters that
        # match any of the filter's tags.  This can be a relatively
        # straightforward query--we just need a bit more added to
        # our WHERE clause, and we don't need a GROUP BY/HAVING.
        first_select = Select(
            BugSubscriptionFilter.id,
            tables=[BugSubscriptionFilter, tag_join],
            where=And(
                Or(  # We want filters that proclaim they simply want any tags.
                   BugSubscriptionFilter.include_any_tags,
                   # Also include filters that match any tag...
                   And(Not(BugSubscriptionFilter.find_all_tags),
                       Or(  # ...with a positive match...
                          And(BugSubscriptionFilterTag.include,
                              In(BugSubscriptionFilterTag.tag, tags)),
                          # ...or with a negative match...
                          And(Not(BugSubscriptionFilterTag.include),
                              Not(In(BugSubscriptionFilterTag.tag, tags))),
                          # ...or if the filter does not specify any tags.
                          BugSubscriptionFilterTag.tag == None))),
                In(BugSubscriptionFilter.id, candidates)))
        # We have our first clause.  Now we start on the second one:
        # handling filters that match *all* tags.
        # This second query will have a HAVING clause, which is where some
        # tricky bits happen. We first make a SQL snippet that
        # represents the tags on this bug.  It is straightforward
        # except for one subtle hack: the addition of the empty
        # space in the array.  This is because we are going to be
        # aggregating the tags on the filters using ARRAY_AGG, which
        # includes NULLs (unlike most other aggregators).  That
        # is an issue here because we use CASE statements to divide
        # up the set of tags that are supposed to be included and
        # supposed to be excluded.  This means that if we aggregate
        # "CASE WHEN BugSubscriptionFilterTag.include THEN
        # BugSubscriptionFilterTag.tag END" then that array will
        # include NULL.  SQL treats NULLs as unknowns that can never
        # be matched, so the array of ['foo', 'bar', NULL] does not
        # contain the array of ['foo', NULL] ("SELECT
        # ARRAY['foo','bar',NULL]::TEXT[] @>
        # ARRAY['foo',NULL]::TEXT[];" is false).  Therefore, so we
        # can make the HAVING statement we want to make without
        # defining a custom Postgres aggregator, we use a single
        # space as, effectively, NULL.  This is safe because a
        # single space is not an acceptable tag.  Again, the
        # clearest alternative is defining a custom Postgres aggregator.
        tags_array = "ARRAY[%s,' ']::TEXT[]" % ",".join(
            quote(tag) for tag in tags)
        # Now let's build the select itself.
        second_select = Select(
            BugSubscriptionFilter.id,
            tables=[BugSubscriptionFilter, tag_join],
            # Our WHERE clause is straightforward. We are simply
            # focusing on BugSubscriptionFilter.find_all_tags, when the
            # first SELECT did not consider it.
            where=And(BugSubscriptionFilter.find_all_tags,
                      In(BugSubscriptionFilter.id, candidates)),
            # The GROUP BY collects the filters together.
            group_by=(BugSubscriptionFilter.id,),
            having=And(
                # The list of tags should be a superset of the filter tags to
                # be included.
                ArrayContains(
                    SQL(tags_array),
                    # This next line gives us an array of the tags that the
                    # filter wants to include.  Notice that it includes the
                    # empty string when the condition does not match, per the
                    # discussion above.
                    ArrayAgg(
                       SQL("CASE WHEN BugSubscriptionFilterTag.include "
                           "THEN BugSubscriptionFilterTag.tag "
                           "ELSE ' '::TEXT END"))),
                # The list of tags should also not intersect with the
                # tags that the filter wants to exclude.
                Not(
                    ArrayIntersects(
                        SQL(tags_array),
                        # This next line gives us an array of the tags
                        # that the filter wants to exclude.  We do not bother
                        # with the empty string, and therefore allow NULLs
                        # into the array, because in this case we are
                        # determining whether the sets intersect, not if the
                        # first set subsumes the second.
                        ArrayAgg(
                           SQL('CASE WHEN '
                               'NOT BugSubscriptionFilterTag.include '
                               'THEN BugSubscriptionFilterTag.tag END'))))))
        # Everything is ready.  Return the union.
        return Union(first_select, second_select)