~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/canonical/launchpad/webapp/batching.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-08-08 16:37:39 UTC
  • mfrom: (13432.6.13 bug-739052-4)
  • Revision ID: launchpad@pqm.canonical.com-20110808163739-4q8u9eull8dfuc11
[r=lifeless][no-qa] StormRangeFactory: group WHERE expressions needed
        for slicing by order direction and use clauses like (col1,
        col2...) < (memo1, memo2...)

Show diffs side-by-side

added added

removed removed

Lines of Context:
200
200
        """
201
201
        self.resultset = resultset
202
202
        if zope_isinstance(resultset, DecoratedResultSet):
203
 
            self.plain_resultset = resultset.getPlainResultSet()
 
203
            self.plain_resultset = resultset.get_plain_result_set()
204
204
        else:
205
205
            self.plain_resultset = resultset
206
206
        self.error_cb = error_cb
336
336
            invert_sort_expression(expression)
337
337
            for expression in self.getOrderBy()]
338
338
 
339
 
    def andClausesForLeadingColumns(self, limits):
 
339
    def limitsGroupedByOrderDirection(self, sort_expressions, memos):
 
340
        """Group sort expressions and memo values by order direction."""
 
341
        descending = isinstance(sort_expressions[0], Desc)
 
342
        grouped_limits = []
 
343
        expression_group = []
 
344
        memo_group = []
 
345
        for expression, memo in zip(sort_expressions, memos):
 
346
            if descending == isinstance(expression, Desc):
 
347
                expression_group.append(expression)
 
348
                memo_group.append(memo)
 
349
            else:
 
350
                grouped_limits.append((expression_group, memo_group))
 
351
                descending = isinstance(expression, Desc)
 
352
                expression_group = [expression]
 
353
                memo_group = [memo]
 
354
        grouped_limits.append((expression_group, memo_group))
 
355
        return grouped_limits
 
356
 
 
357
    def lessThanOrGreaterThanExpression(self, expressions, memos):
 
358
        """Return an SQL expression "(expressions) OP (memos)".
 
359
 
 
360
        OP is >, if the elements of expressions are PropertyColumns; else
 
361
        the elements of expressions are instances of Desc(PropertyColumn)
 
362
        and OP is <.
 
363
        """
 
364
        descending = isinstance(expressions[0], Desc)
 
365
        if descending:
 
366
            expressions = [expression.expr for expression in expressions]
 
367
        expressions = map(compile, expressions)
 
368
        expressions = ', '.join(expressions)
 
369
        memos = ', '.join(sqlvalues(*memos))
 
370
        if descending:
 
371
            return SQL('(%s) < (%s)' % (expressions, memos))
 
372
        else:
 
373
            return SQL('(%s) > (%s)' % (expressions, memos))
 
374
 
 
375
    def equalsExpressionsFromLimits(self, limits):
 
376
        """Return a list [expression == memo, ...] for the given limits."""
340
377
        def plain_expression(expression):
341
 
            # Strip a possible Desc from an expression.
342
378
            if isinstance(expression, Desc):
343
379
                return expression.expr
344
380
            else:
345
381
                return expression
346
 
        return [plain_expression(column) == memo for column, memo in limits]
347
 
 
348
 
    def genericWhereExpressions(self, limits):
349
 
        """Generate WHERE expressions for the given sort columns and
350
 
        memos values.
351
 
 
352
 
        :return: A list of where expressions which should be OR-ed
353
 
        :param limits: A sequence of (column, mem_value) tuples.
354
 
 
355
 
        Note that the result set may be ordered by more than one column.
356
 
        Given the sort columns c[1], c[2] ... c[N] and assuming ascending
357
 
        sorting, we must look for all rows where
358
 
          * c[1] == memo[1], c[2] == memo[2] ... c[N-1] == memo[N-1] and
359
 
            c[N] > memo[N]
360
 
          * c[1] == memo[1], c[2] == memo[2] ... c[N-2] == memo[N-2] and
361
 
            c[N-1] > memo[N-1]
362
 
          ...
363
 
          * c[1] == memo[1], c[2] > memo[2]
364
 
          * c[1] > memo[1]
 
382
 
 
383
        result = []
 
384
        for expressions, memos in limits:
 
385
            result.extend(
 
386
                plain_expression(expression) == memo
 
387
                for expression, memo in zip(expressions, memos))
 
388
        return result
 
389
 
 
390
    def whereExpressionsFromGroupedLimits(self, limits):
 
391
        """Build a sequence of WHERE expressions from the given limits.
 
392
 
 
393
        limits is a list of tuples (expressions, memos), where
 
394
        expressions is a list of PropertyColumn instances or of
 
395
        instances of Desc(PropertyColumn). Desc(PropertyColumn)
 
396
        and PropertyColumn instances must not appear in the same
 
397
        expressions list.
 
398
 
 
399
        memos are the memo values asociated with the columns in
 
400
        expressions.
 
401
 
 
402
        Given a limits value of
 
403
            [([c11, c12 ...], [m11, m12 ...]),
 
404
             ([c21, c22 ...], [m21, m22 ...]),
 
405
             ...
 
406
             ([cN1, cN2 ...], [mN1, mN2 ...])]
 
407
 
 
408
        this method returns a sequence of these Storm/SQL expressions:
 
409
 
 
410
            * (c11, c12 ...) = (m11, m12 ...) AND
 
411
              (c21, c22 ...) = (m21, m22 ...) AND
 
412
              ...
 
413
              (cN1, cN2 ...) < (mN1, mN2 ...)
 
414
            * (c11, c12 ...) = (m11, m12 ...) AND
 
415
              (c21, c22 ...) = (m21, m22 ...) AND
 
416
              ...
 
417
              (cM1, cM2 ...) < (mM1, mM2 ...)
 
418
 
 
419
              (where M = N - 1)
 
420
            ...
 
421
            * (c11, c12 ...) < (m11, m12 ...)
 
422
 
 
423
        The getSlice() should return rows matching any of these
 
424
        expressions. Note that the result sets returned by each
 
425
        expression are disjuct, hence they can be simply ORed,
 
426
        as well as used in a UNION ALL query.
365
427
        """
366
428
        start = limits[:-1]
367
 
        last_expression, last_memo = limits[-1]
368
 
        if isinstance(last_expression, Desc):
369
 
            last_expression = last_expression.expr
370
 
            last_limit = last_expression < last_memo
371
 
        else:
372
 
            last_limit = last_expression > last_memo
 
429
        last_expressions, last_memos = limits[-1]
 
430
        last_clause = self.lessThanOrGreaterThanExpression(
 
431
            last_expressions, last_memos)
373
432
        if len(start) > 0:
374
 
            clauses = self.andClausesForLeadingColumns(start)
375
 
            clauses.append(last_limit)
376
 
            clause_for_last_column = reduce(And, clauses)
377
 
            return (
378
 
                [clause_for_last_column]
379
 
                + self.genericWhereExpressions(start))
 
433
            clauses = self.equalsExpressionsFromLimits(start)
 
434
            clauses.append(last_clause)
 
435
            clauses = [And(*clauses)]
 
436
            return clauses + self.whereExpressionsFromGroupedLimits(start)
380
437
        else:
381
 
            return [last_limit]
 
438
            return [last_clause]
382
439
 
383
440
    def whereExpressions(self, sort_expressions, memos):
384
441
        """WHERE expressions for the given sort columns and memos values."""
385
 
        expression = sort_expressions[0]
386
 
        descending = isinstance(expression, Desc)
387
 
        consistent = True
388
 
        for expression in sort_expressions[1:]:
389
 
            if isinstance(expression, Desc) != descending:
390
 
                consistent = False
391
 
                break
392
 
        if not consistent or len(sort_expressions) == 1:
393
 
            return self.genericWhereExpressions(zip(sort_expressions, memos))
394
 
 
395
 
        # If the columns are sorted either only ascending or only
396
 
        # descending, we can specify a single WHERE condition
397
 
        # (col1, col2...) > (memo1, memo2...)
398
 
        if descending:
399
 
            sort_expressions = [
400
 
                expression.expr for expression in sort_expressions]
401
 
        sort_expressions = map(compile, sort_expressions)
402
 
        sort_expressions = ', '.join(sort_expressions)
403
 
        memos = sqlvalues(*memos)
404
 
        memos = ', '.join(memos)
405
 
        if descending:
406
 
            return [SQL('(%s) < (%s)' % (sort_expressions, memos))]
407
 
        else:
408
 
            return [SQL('(%s) > (%s)' % (sort_expressions, memos))]
 
442
        grouped_limits = self.limitsGroupedByOrderDirection(
 
443
            sort_expressions, memos)
 
444
        return self.whereExpressionsFromGroupedLimits(grouped_limits)
409
445
 
410
446
    def getSliceFromMemo(self, size, memo):
411
447
        """Return a result set for the given memo values.