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
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
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
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)
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
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
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())
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
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 ), )