Ejemplo n.º 1
0
def _get_query_comparisons(query):
    """Search an orm.Query object for binary expressions.

    Returns expressions which match a Column against one or more
    literal values as a list of tuples of the form
    (column, operator, values).   "values" is a single value
    or tuple of values depending on the operator.

    """
    binds = {}
    clauses = set()
    comparisons = []

    def visit_bindparam(bind):
        # visit a bind parameter.

        # check in _params for it first
        if bind.key in query._params:
            value = query._params[bind.key]
        elif bind.callable:
            # some ORM functions (lazy loading)
            # place the bind's value as a
            # callable for deferred evaluation.
            value = bind.callable()
        else:
            # just use .value
            value = bind.value

        binds[bind] = value

    def visit_column(column):
        clauses.add(column)

    def visit_binary(binary):
        # special handling for "col IN (params)"
        if binary.left in clauses and \
                binary.operator == operators.in_op and \
                hasattr(binary.right, 'clauses'):
            comparisons.append(
                (binary.left, binary.operator,
                 tuple(binds[bind] for bind in binary.right.clauses)))
        elif binary.left in clauses and binary.right in binds:
            comparisons.append(
                (binary.left, binary.operator, binds[binary.right]))

        elif binary.left in binds and binary.right in clauses:
            comparisons.append(
                (binary.right, binary.operator, binds[binary.left]))

    # here we will traverse through the query's criterion, searching
    # for SQL constructs.  We will place simple column comparisons
    # into a list.
    if query._criterion is not None:
        visitors.traverse_depthfirst(query._criterion, {}, {
            'bindparam': visit_bindparam,
            'binary': visit_binary,
            'column': visit_column
        })
    return comparisons
Ejemplo n.º 2
0
def _get_query_comparisons(query):
    """Search an orm.Query object for binary expressions.
    
    Returns expressions which match a Column against one or more
    literal values as a list of tuples of the form 
    (column, operator, values).   "values" is a single value
    or tuple of values depending on the operator.
    
    """
    binds = {}
    clauses = set()
    comparisons = []

    def visit_bindparam(bind):
        # visit a bind parameter.   Below we ensure
        # that we get the value whether it was specified
        # as part of query.params(), or is directly embedded
        # in the bind's "value" attribute.
        value = query._params.get(bind.key, bind.value)

        # some ORM functions place the bind's value as a
        # callable for deferred evaulation.   Get that
        # actual value here.
        if callable(value):
            value = value()

        binds[bind] = value

    def visit_column(column):
        clauses.add(column)

    def visit_binary(binary):
        # special handling for "col IN (params)"
        if binary.left in clauses and \
                binary.operator == operators.in_op and \
                hasattr(binary.right, 'clauses'):
            comparisons.append(
                (binary.left, binary.operator,
                 tuple(binds[bind] for bind in binary.right.clauses)))
        elif binary.left in clauses and binary.right in binds:
            comparisons.append(
                (binary.left, binary.operator, binds[binary.right]))

        elif binary.left in binds and binary.right in clauses:
            comparisons.append(
                (binary.right, binary.operator, binds[binary.left]))

    # here we will traverse through the query's criterion, searching
    # for SQL constructs.  We will place simple column comparisons
    # into a list.
    if query._criterion is not None:
        visitors.traverse_depthfirst(query._criterion, {}, {
            'bindparam': visit_bindparam,
            'binary': visit_binary,
            'column': visit_column
        })
    return comparisons
Ejemplo n.º 3
0
        def _get_query_comparisons(query):
            binds = {}
            clauses = set()
            comparisons = []

            def visit_bindparam(bind):
                # visit a bind parameter.

                # check in _params for it first
                if bind.key in query._params:
                    value = query._params[bind.key]
                elif bind.callable:
                    # some ORM functions (lazy loading)
                    # place the bind's value as a
                    # callable for deferred evaulation.
                    value = bind.callable()
                else:
                    # just use .value
                    value = bind.value

                binds[bind] = value

            def visit_column(column):
                clauses.add(column)

            def visit_binary(binary):
                # special handling for "col IN (params)"
                if (binary.left in clauses and
                    binary.operator == operators.in_op and
                        hasattr(binary.right, 'clauses')):
                    comparisons.append(
                        (binary.left, binary.operator,
                         tuple(binds[bind] for bind in binary.right.clauses))
                    )
                elif binary.left in clauses and binary.right in binds:
                    comparisons.append(
                        (binary.left, binary.operator, binds[binary.right])
                    )

                elif binary.left in binds and binary.right in clauses:
                    comparisons.append(
                        (binary.right, binary.operator, binds[binary.left])
                    )

            # here we will traverse through the query's criterion, searching
            # for SQL constructs.  We will place simple column comparisons
            # into a list.
            if query._criterion is not None:
                visitors.traverse_depthfirst(query._criterion, {},
                                             {'bindparam': visit_bindparam,
                                              'binary': visit_binary,
                                              'column': visit_column
                                              }
                                             )
            return comparisons
Ejemplo n.º 4
0
    def test_cache_key_gather_bindparams(self):
        for fixture in self.fixtures:
            case_a = fixture()
            case_b = fixture()

            # in the "bindparams" case, the cache keys for bound parameters
            # with only different values will be the same, but the params
            # themselves are gathered into a collection.
            for a, b in itertools.combinations_with_replacement(
                range(len(case_a)), 2
            ):
                a_params = {"bindparams": []}
                b_params = {"bindparams": []}
                if a == b:
                    a_key = case_a[a]._cache_key(**a_params)
                    b_key = case_b[b]._cache_key(**b_params)
                    eq_(a_key, b_key)

                    if a_params["bindparams"]:
                        for a_param, b_param in zip(
                            a_params["bindparams"], b_params["bindparams"]
                        ):
                            assert a_param.compare(b_param)
                else:
                    a_key = case_a[a]._cache_key(**a_params)
                    b_key = case_b[b]._cache_key(**b_params)

                    if a_key == b_key:
                        for a_param, b_param in zip(
                            a_params["bindparams"], b_params["bindparams"]
                        ):
                            if not a_param.compare(b_param):
                                break
                        else:
                            assert False, "Bound parameters are all the same"
                    else:
                        ne_(a_key, b_key)

                assert_a_params = []
                assert_b_params = []
                visitors.traverse_depthfirst(
                    case_a[a], {}, {"bindparam": assert_a_params.append}
                )
                visitors.traverse_depthfirst(
                    case_b[b], {}, {"bindparam": assert_b_params.append}
                )

                # note we're asserting the order of the params as well as
                # if there are dupes or not.  ordering has to be deterministic
                # and matches what a traversal would provide.
                eq_(a_params["bindparams"], assert_a_params)
                eq_(b_params["bindparams"], assert_b_params)
Ejemplo n.º 5
0
    def test_cache_key_gather_bindparams(self):
        for fixture in self.fixtures:
            case_a = fixture()
            case_b = fixture()

            # in the "bindparams" case, the cache keys for bound parameters
            # with only different values will be the same, but the params
            # themselves are gathered into a collection.
            for a, b in itertools.combinations_with_replacement(
                range(len(case_a)), 2
            ):
                a_params = {"bindparams": []}
                b_params = {"bindparams": []}
                if a == b:
                    a_key = case_a[a]._cache_key(**a_params)
                    b_key = case_b[b]._cache_key(**b_params)
                    eq_(a_key, b_key)

                    if a_params["bindparams"]:
                        for a_param, b_param in zip(
                            a_params["bindparams"], b_params["bindparams"]
                        ):
                            assert a_param.compare(b_param)
                else:
                    a_key = case_a[a]._cache_key(**a_params)
                    b_key = case_b[b]._cache_key(**b_params)

                    if a_key == b_key:
                        for a_param, b_param in zip(
                            a_params["bindparams"], b_params["bindparams"]
                        ):
                            if not a_param.compare(b_param):
                                break
                        else:
                            assert False, "Bound parameters are all the same"
                    else:
                        ne_(a_key, b_key)

                assert_a_params = []
                assert_b_params = []
                visitors.traverse_depthfirst(
                    case_a[a], {}, {"bindparam": assert_a_params.append}
                )
                visitors.traverse_depthfirst(
                    case_b[b], {}, {"bindparam": assert_b_params.append}
                )

                # note we're asserting the order of the params as well as
                # if there are dupes or not.  ordering has to be deterministic
                # and matches what a traversal would provide.
                eq_(a_params["bindparams"], assert_a_params)
                eq_(b_params["bindparams"], assert_b_params)
Ejemplo n.º 6
0
def _get_query_comparisons(query):
    """Search an orm.Query object for binary expressions.

    Returns expressions which match a Column against one or more
    literal values as a list of tuples of the form
    (column, operator, values).   "values" is a single value
    or tuple of values depending on the operator.

    """
    binds = {}
    clauses = set()
    comparisons = []

    def visit_bindparam(bind):
        # visit a bind parameter.

        # check in _params for it first
        if bind.key in query._params:
            value = query._params[bind.key]
        elif bind.callable:
            # some ORM functions (lazy loading)
            # place the bind's value as a
            # callable for deferred evaluation.
            value = bind.callable()
        else:
            # just use .value
            value = bind.value

        binds[bind] = value

    def visit_column(column):
        clauses.add(column)

    def visit_binary(binary):
        # special handling for "col IN (params)"
        if binary.left in clauses and binary.operator == operators.in_op and hasattr(binary.right, "clauses"):
            comparisons.append((binary.left, binary.operator, tuple(binds[bind] for bind in binary.right.clauses)))
        elif binary.left in clauses and binary.right in binds:
            comparisons.append((binary.left, binary.operator, binds[binary.right]))

        elif binary.left in binds and binary.right in clauses:
            comparisons.append((binary.right, binary.operator, binds[binary.left]))

    # here we will traverse through the query's criterion, searching
    # for SQL constructs.  We will place simple column comparisons
    # into a list.
    if query._criterion is not None:
        visitors.traverse_depthfirst(
            query._criterion, {}, {"bindparam": visit_bindparam, "binary": visit_binary, "column": visit_column}
        )
    return comparisons
Ejemplo n.º 7
0
def _get_query_comparisons(query):
    """Search an orm.Query object for binary expressions.
    
    Returns expressions which match a Column against one or more
    literal values as a list of tuples of the form 
    (column, operator, values).   "values" is a single value
    or tuple of values depending on the operator.
    
    """
    binds = {}
    clauses = set()
    comparisons = []

    def visit_bindparam(bind):
        # visit a bind parameter.   Below we ensure
        # that we get the value whether it was specified
        # as part of query.params(), or is directly embedded
        # in the bind's "value" attribute.
        value = query._params.get(bind.key, bind.value)

        # some ORM functions place the bind's value as a
        # callable for deferred evaulation.   Get that
        # actual value here.
        if callable(value):
            value = value()

        binds[bind] = value

    def visit_column(column):
        clauses.add(column)

    def visit_binary(binary):
        # special handling for "col IN (params)"
        if binary.left in clauses and binary.operator == operators.in_op and hasattr(binary.right, "clauses"):
            comparisons.append((binary.left, binary.operator, tuple(binds[bind] for bind in binary.right.clauses)))
        elif binary.left in clauses and binary.right in binds:
            comparisons.append((binary.left, binary.operator, binds[binary.right]))

        elif binary.left in binds and binary.right in clauses:
            comparisons.append((binary.right, binary.operator, binds[binary.left]))

    # here we will traverse through the query's criterion, searching
    # for SQL constructs.  We will place simple column comparisons
    # into a list.
    if query._criterion is not None:
        visitors.traverse_depthfirst(
            query._criterion, {}, {"bindparam": visit_bindparam, "binary": visit_binary, "column": visit_column}
        )
    return comparisons
Ejemplo n.º 8
0
    def test_cache_key(self):
        def assert_params_append(assert_params):
            def append(param):
                if param._value_required_for_cache:
                    assert_params.append(param)
                else:
                    is_(param.value, None)

            return append

        for fixture in self.fixtures:
            case_a = fixture()
            case_b = fixture()

            for a, b in itertools.combinations_with_replacement(
                range(len(case_a)), 2
            ):

                assert_a_params = []
                assert_b_params = []

                visitors.traverse_depthfirst(
                    case_a[a],
                    {},
                    {"bindparam": assert_params_append(assert_a_params)},
                )
                visitors.traverse_depthfirst(
                    case_b[b],
                    {},
                    {"bindparam": assert_params_append(assert_b_params)},
                )
                if assert_a_params:
                    assert_raises_message(
                        NotImplementedError,
                        "bindparams collection argument required ",
                        case_a[a]._cache_key,
                    )
                if assert_b_params:
                    assert_raises_message(
                        NotImplementedError,
                        "bindparams collection argument required ",
                        case_b[b]._cache_key,
                    )

                if not assert_a_params and not assert_b_params:
                    if a == b:
                        eq_(case_a[a]._cache_key(), case_b[b]._cache_key())
                    else:
                        ne_(case_a[a]._cache_key(), case_b[b]._cache_key())
Ejemplo n.º 9
0
    def test_cache_key(self):
        def assert_params_append(assert_params):
            def append(param):
                if param._value_required_for_cache:
                    assert_params.append(param)
                else:
                    is_(param.value, None)

            return append

        for fixture in self.fixtures:
            case_a = fixture()
            case_b = fixture()

            for a, b in itertools.combinations_with_replacement(
                range(len(case_a)), 2
            ):

                assert_a_params = []
                assert_b_params = []

                visitors.traverse_depthfirst(
                    case_a[a],
                    {},
                    {"bindparam": assert_params_append(assert_a_params)},
                )
                visitors.traverse_depthfirst(
                    case_b[b],
                    {},
                    {"bindparam": assert_params_append(assert_b_params)},
                )
                if assert_a_params:
                    assert_raises_message(
                        NotImplementedError,
                        "bindparams collection argument required ",
                        case_a[a]._cache_key,
                    )
                if assert_b_params:
                    assert_raises_message(
                        NotImplementedError,
                        "bindparams collection argument required ",
                        case_b[b]._cache_key,
                    )

                if not assert_a_params and not assert_b_params:
                    if a == b:
                        eq_(case_a[a]._cache_key(), case_b[b]._cache_key())
                    else:
                        ne_(case_a[a]._cache_key(), case_b[b]._cache_key())
Ejemplo n.º 10
0
def queryComparisons(query):
    binds = {}
    cls = set()
    cmps = []

    # Visitors
    def visit_bindparam(bind):
        if bind.key in query._params:
            value = query._params[bind.key]
        elif bind.callable:
            value = bind.callable()
        else:
            value = bind.value
        binds[bind] = value

    def visit_column(column):
        cls.add(column)

    def visit_binary(binary):
        if binary.left in cls and binary.right in binds:
            cmps.append((binary.left, binary.operator, binds[binary.right]))
        elif binary.left in binds and binary.right in cls:
            cmps.append((binary.right, binary.operator, binds[binary.left]))

    # Traverse query's criterion
    if query._criterion is not None:
        visitors.traverse_depthfirst(
            query._criterion,
            {},
            {
                "bindparam": visit_bindparam,
                "binary": visit_binary,
                "column": visit_column,
            },
        )
    return cmps
Ejemplo n.º 11
0
    def _run_cache_key_fixture(self, fixture, compare_values):
        case_a = fixture()
        case_b = fixture()

        for a, b in itertools.combinations_with_replacement(
            range(len(case_a)), 2
        ):
            if a == b:
                a_key = case_a[a]._generate_cache_key()
                b_key = case_b[b]._generate_cache_key()
                is_not_(a_key, None)
                is_not_(b_key, None)

                eq_(a_key.key, b_key.key)
                eq_(hash(a_key), hash(b_key))

                for a_param, b_param in zip(
                    a_key.bindparams, b_key.bindparams
                ):
                    assert a_param.compare(
                        b_param, compare_values=compare_values
                    )
            else:
                a_key = case_a[a]._generate_cache_key()
                b_key = case_b[b]._generate_cache_key()

                if a_key.key == b_key.key:
                    for a_param, b_param in zip(
                        a_key.bindparams, b_key.bindparams
                    ):
                        if not a_param.compare(
                            b_param, compare_values=compare_values
                        ):
                            break
                    else:
                        # this fails unconditionally since we could not
                        # find bound parameter values that differed.
                        # Usually we intended to get two distinct keys here
                        # so the failure will be more descriptive using the
                        # ne_() assertion.
                        ne_(a_key.key, b_key.key)
                else:
                    ne_(a_key.key, b_key.key)

            # ClauseElement-specific test to ensure the cache key
            # collected all the bound parameters
            if isinstance(case_a[a], ClauseElement) and isinstance(
                case_b[b], ClauseElement
            ):
                assert_a_params = []
                assert_b_params = []
                visitors.traverse_depthfirst(
                    case_a[a], {}, {"bindparam": assert_a_params.append}
                )
                visitors.traverse_depthfirst(
                    case_b[b], {}, {"bindparam": assert_b_params.append}
                )

                # note we're asserting the order of the params as well as
                # if there are dupes or not.  ordering has to be
                # deterministic and matches what a traversal would provide.
                # regular traverse_depthfirst does produce dupes in cases
                # like
                # select([some_alias]).
                #    select_from(join(some_alias, other_table))
                # where a bound parameter is inside of some_alias.  the
                # cache key case is more minimalistic
                eq_(
                    sorted(a_key.bindparams, key=lambda b: b.key),
                    sorted(
                        util.unique_list(assert_a_params), key=lambda b: b.key
                    ),
                )
                eq_(
                    sorted(b_key.bindparams, key=lambda b: b.key),
                    sorted(
                        util.unique_list(assert_b_params), key=lambda b: b.key
                    ),
                )