def test_get_cast_for_param(self): self.assertEqual(get_cast_for_param([], 'a'), '') self.assertEqual(get_cast_for_param({'a': True}, 'a'), '::boolean') self.assertEqual(get_cast_for_param({'a': datetime.datetime}, 'a'), '::timestamp') self.assertEqual(get_cast_for_param({'a': datetime.time}, 'a'), '::time') self.assertEqual(get_cast_for_param({'a': int}, 'a'), '::bigint') self.assertEqual(get_cast_for_param({'a': float}, 'a'), '::float8') from decimal import Decimal self.assertEqual(get_cast_for_param({'a': Decimal}, 'a'), '::numeric')
def as_postgresql(self, compiler, connection): lhs, lhs_params = self.process_lhs(compiler, connection) rhs, rhs_params = self.process_rhs(compiler, connection) if len(rhs_params) == 1 and isinstance(rhs_params[0], dict): param = rhs_params[0] sign = (self.lookup_name[0] == 'g' and '>%s' or '<%s') % (self.lookup_name[-1] == 'e' and '=' or '') param_keys = list(param.keys()) conditions = [] for key in param_keys: cast = get_cast_for_param(self.value_annot, key) conditions.append('(%s->\'%s\')%s %s %%s' % (lhs, key, cast, sign)) return (" AND ".join(conditions), param.values()) raise ValueError('invalid value')
def as_postgresql(self, qn, connection): lhs, lhs_params = self.process_lhs(qn, connection) rhs, rhs_params = self.process_rhs(qn, connection) if len(rhs_params) == 1 and isinstance(rhs_params[0], dict): param = rhs_params[0] sign = (self.lookup_name[0] == 'g' and '>%s' or '<%s') % (self.lookup_name[-1] == 'e' and '=' or '') param_keys = list(param.keys()) conditions = [] for key in param_keys: cast = get_cast_for_param(self.value_annot, key) conditions.append('(%s->\'%s\')%s %s %%s' % (lhs, key, cast, sign)) return (" AND ".join(conditions), param.values()) raise ValueError('invalid value')
def as_postgresql(self, compiler, connection): lhs, lhs_params = self.process_lhs(compiler, connection) # FIXME: ::text cast is added by ``django.db.backends.postgresql_psycopg2.DatabaseOperations.lookup_cast``; # maybe there's a cleaner way to fix the cast for hstore columns if lhs.endswith('::text'): lhs = '{0}{1}'.format(lhs[:-4], 'hstore') param = self.rhs if isinstance(param, dict): values = list(param.values()) keys = list(param.keys()) if len(values) == 1 and isinstance(values[0], (list, tuple)): # Can't cast here because the list could contain multiple types return '%s->\'%s\' = ANY(%%s)' % (lhs, keys[0]), [[ str(x) for x in values[0] ]] elif len(keys) == 1 and len(values) == 1: # Retrieve key and compare to param instead of using '@>' in order to cast hstore value cast = get_cast_for_param(self.value_annot, keys[0]) return ('(%s->\'%s\')%s = %%s' % (lhs, keys[0], cast), [values[0]]) return '%s @> %%s' % lhs, [param] elif isinstance(param, (list, tuple)): if len(param) == 0: raise ValueError('invalid value') if len(param) < 2: return '%s ? %%s' % lhs, [param[0]] if param: return '%s ?& %%s' % lhs, [param] elif isinstance(param, six.string_types): # if looking for a string perform the normal text lookup # that is: look for occurence of string in all the keys pass # needed for SerializedDictionaryField elif hasattr(self.lhs.target, 'serializer'): try: self.lhs.target._serialize_value(param) pass except Exception: raise ValueError('invalid value') else: raise ValueError('invalid value') return super(HStoreContains, self).as_sql(compiler, connection)
def as_postgresql(self, compiler, connection): lhs, lhs_params = self.process_lhs(compiler, connection) # FIXME: ::text cast is added by ``django.db.backends.postgresql_psycopg2.DatabaseOperations.lookup_cast``; # maybe there's a cleaner way to fix the cast for hstore columns if lhs.endswith('::text'): lhs = '{0}{1}'.format(lhs[:-4], 'hstore') param = self.rhs if isinstance(param, dict): values = list(param.values()) keys = list(param.keys()) if len(values) == 1 and isinstance(values[0], (list, tuple)): # Can't cast here because the list could contain multiple types return '%s->\'%s\' = ANY(%%s)' % (lhs, keys[0]), [[str(x) for x in values[0]]] elif len(keys) == 1 and len(values) == 1: # Retrieve key and compare to param instead of using '@>' in order to cast hstore value cast = get_cast_for_param(self.value_annot, keys[0]) return ('(%s->\'%s\')%s = %%s' % (lhs, keys[0], cast), [values[0]]) return '%s @> %%s' % lhs, [param] elif isinstance(param, (list, tuple)): if len(param) == 0: raise ValueError('invalid value') if len(param) < 2: return '%s ? %%s' % lhs, [param[0]] if param: return '%s ?& %%s' % lhs, [param] elif isinstance(param, six.string_types): # if looking for a string perform the normal text lookup # that is: look for occurence of string in all the keys pass # needed for SerializedDictionaryField elif hasattr(self.lhs.target, 'serializer'): try: self.lhs.target._serialize_value(param) pass except Exception: raise ValueError('invalid value') else: raise ValueError('invalid value') return super(HStoreContains, self).as_sql(compiler, connection)
def make_atom(self, child, qn, connection): lvalue, lookup_type, value_annot, param = child kwargs = {'connection': connection} if lvalue and lvalue.field and hasattr( lvalue.field, 'db_type') and lvalue.field.db_type(**kwargs) == 'hstore': try: lvalue, params = lvalue.process(lookup_type, param, connection) except EmptyShortCircuit: raise EmptyResultSet() field = self.sql_for_columns(lvalue, qn, connection) if lookup_type == 'exact': if isinstance(param, dict): return ('{0} = %s'.format(field), [param]) raise ValueError('invalid value') elif lookup_type in ('gt', 'gte', 'lt', 'lte'): if isinstance(param, dict): sign = (lookup_type[0] == 'g' and '>%s' or '<%s') % (lookup_type[-1] == 'e' and '=' or '') param_keys = list(param.keys()) conditions = [] for key in param_keys: cast = get_cast_for_param(value_annot, key) conditions.append('(%s->\'%s\')%s %s %%s' % (field, key, cast, sign)) return (" AND ".join(conditions), param.values()) raise ValueError('invalid value') elif lookup_type in ['contains', 'icontains']: if isinstance(param, dict): values = list(param.values()) keys = list(param.keys()) if len(values) == 1 and isinstance(values[0], (list, tuple)): # Can't cast here because the list could contain multiple types return ('%s->\'%s\' = ANY(%%s)' % (field, keys[0]), [[str(x) for x in values[0]]]) elif len(keys) == 1 and len(values) == 1: # Retrieve key and compare to param instead of using '@>' in order to cast hstore value cast = get_cast_for_param(value_annot, keys[0]) return ('(%s->\'%s\')%s = %%s' % (field, keys[0], cast), [values[0]]) return ('%s @> %%s' % field, [param]) elif isinstance(param, (list, tuple)): if len(param) == 0: raise ValueError('invalid value') if len(param) < 2: return ('%s ? %%s' % field, [param[0]]) if param: return ('%s ?& %%s' % field, [param]) raise ValueError('invalid value') elif isinstance(param, six.string_types): # if looking for a string perform the normal text lookup # that is: look for occurence of string in all the keys pass elif hasattr(child[0].field, 'serializer'): try: child[0].field._serialize_value(param) pass except Exception: raise ValueError('invalid value') else: raise ValueError('invalid value') elif lookup_type == 'isnull': if isinstance(param, dict): param_keys = list(param.keys()) conditions = [] for key in param_keys: op = 'IS NULL' if value_annot[key] else 'IS NOT NULL' conditions.append('(%s->\'%s\') %s' % (field, key, op)) return (" AND ".join(conditions), []) # do not perform any special format return super(HStoreWhereNode, self).make_atom(child, qn, connection) else: raise TypeError('invalid lookup type') return super(HStoreWhereNode, self).make_atom(child, qn, connection)
def make_atom(self, child, qn, connection): lvalue, lookup_type, value_annot, param = child kwargs = {'connection': connection} if VERSION[:2] >= (1, 3) else {} if lvalue and lvalue.field and hasattr(lvalue.field, 'db_type') and lvalue.field.db_type(**kwargs) == 'hstore': try: lvalue, params = lvalue.process(lookup_type, param, connection) except EmptyShortCircuit: raise EmptyResultSet() field = self.sql_for_columns(lvalue, qn, connection) if lookup_type == 'exact': if isinstance(param, dict): return ('{0} = %s'.format(field), [param]) raise ValueError('invalid value') elif lookup_type in ('gt', 'gte', 'lt', 'lte'): if isinstance(param, dict): sign = (lookup_type[0] == 'g' and '>%s' or '<%s') % (lookup_type[-1] == 'e' and '=' or '') param_keys = list(param.keys()) conditions = [] for key in param_keys: cast = get_cast_for_param(value_annot, key) conditions.append('(%s->\'%s\')%s %s %%s' % (field, key, cast, sign)) return (" AND ".join(conditions), param.values()) raise ValueError('invalid value') elif lookup_type in ['contains', 'icontains']: if isinstance(param, dict): values = list(param.values()) keys = list(param.keys()) if len(values) == 1 and isinstance(values[0], (list, tuple)): # Can't cast here because the list could contain multiple types return ('%s->\'%s\' = ANY(%%s)' % (field, keys[0]), [[str(x) for x in values[0]]]) elif len(keys) == 1 and len(values) == 1: # Retrieve key and compare to param instead of using '@>' in order to cast hstore value cast = get_cast_for_param(value_annot, keys[0]) return ('(%s->\'%s\')%s = %%s' % (field, keys[0], cast), [values[0]]) return ('%s @> %%s' % field, [param]) elif isinstance(param, (list, tuple)): if len(param) == 0: raise ValueError('invalid value') if len(param) < 2: return ('%s ? %%s' % field, [param[0]]) if param: return ('%s ?& %%s' % field, [param]) raise ValueError('invalid value') elif isinstance(param, six.string_types): # if looking for a string perform the normal text lookup # that is: look for occurence of string in all the keys pass elif hasattr(child[0].field, 'serializer'): try: child[0].field._serialize_value(param) pass except Exception: raise ValueError('invalid value') else: raise ValueError('invalid value') elif lookup_type == 'isnull': if isinstance(param, dict): param_keys = list(param.keys()) conditions = [] for key in param_keys: op = 'IS NULL' if value_annot[key] else 'IS NOT NULL' conditions.append('(%s->\'%s\') %s' % (field, key, op)) return (" AND ".join(conditions), []) # do not perform any special format return super(HStoreWhereNode, self).make_atom(child, qn, connection) else: raise TypeError('invalid lookup type') return super(HStoreWhereNode, self).make_atom(child, qn, connection)
def make_atom(self, child, qn, connection): lvalue, lookup_type, value_annot, param = child kwargs = {"connection": connection} if lvalue and lvalue.field and hasattr(lvalue.field, "db_type") and lvalue.field.db_type(**kwargs) == "hstore": try: lvalue, params = lvalue.process(lookup_type, param, connection) except EmptyShortCircuit: raise EmptyResultSet() field = self.sql_for_columns(lvalue, qn, connection) if lookup_type == "exact": if isinstance(param, dict): return ("{0} = %s".format(field), [param]) raise ValueError("invalid value") elif lookup_type in ("gt", "gte", "lt", "lte"): if isinstance(param, dict): sign = (lookup_type[0] == "g" and ">%s" or "<%s") % (lookup_type[-1] == "e" and "=" or "") param_keys = list(param.keys()) conditions = [] for key in param_keys: cast = get_cast_for_param(value_annot, key) conditions.append("(%s->'%s')%s %s %%s" % (field, key, cast, sign)) return (" AND ".join(conditions), param.values()) raise ValueError("invalid value") elif lookup_type in ["contains", "icontains"]: if isinstance(param, dict): values = list(param.values()) keys = list(param.keys()) if len(values) == 1 and isinstance(values[0], (list, tuple)): # Can't cast here because the list could contain multiple types return ("%s->'%s' = ANY(%%s)" % (field, keys[0]), [[str(x) for x in values[0]]]) elif len(keys) == 1 and len(values) == 1: # Retrieve key and compare to param instead of using '@>' in order to cast hstore value cast = get_cast_for_param(value_annot, keys[0]) return ("(%s->'%s')%s = %%s" % (field, keys[0], cast), [values[0]]) return ("%s @> %%s" % field, [param]) elif isinstance(param, (list, tuple)): if len(param) == 0: raise ValueError("invalid value") if len(param) < 2: return ("%s ? %%s" % field, [param[0]]) if param: return ("%s ?& %%s" % field, [param]) raise ValueError("invalid value") elif isinstance(param, six.string_types): # if looking for a string perform the normal text lookup # that is: look for occurence of string in all the keys pass elif hasattr(child[0].field, "serializer"): try: child[0].field._serialize_value(param) pass except Exception: raise ValueError("invalid value") else: raise ValueError("invalid value") elif lookup_type == "isnull": if isinstance(param, dict): param_keys = list(param.keys()) conditions = [] for key in param_keys: op = "IS NULL" if value_annot[key] else "IS NOT NULL" conditions.append("(%s->'%s') %s" % (field, key, op)) return (" AND ".join(conditions), []) # do not perform any special format return super(HStoreWhereNode, self).make_atom(child, qn, connection) else: raise TypeError("invalid lookup type") return super(HStoreWhereNode, self).make_atom(child, qn, connection)