def __init__(self, query_string, _app=None): """Ctor. Parses the input query into the class as a pre-compiled query, allowing for a later call to Bind() to bind arguments as defined in the documentation. Args: query_string: properly formatted GQL query string. Raises: datastore_errors.BadQueryError: if the query is not parsable. """ self._entity = '' self.__filters = {} self.__bound_filters = {} self.__has_ancestor = False self.__orderings = [] self.__offset = -1 self.__limit = -1 self.__hint = '' self.__app = _app self.__symbols = self.TOKENIZE_REGEX.findall(query_string) self.__next_symbol = 0 if not self.__Select(): raise datastore_errors.BadQueryError('Unable to parse query') else: pass
def __init__(self, query_string, _app=None, _auth_domain=None, namespace=None): """Ctor. Parses the input query into the class as a pre-compiled query, allowing for a later call to Bind() to bind arguments as defined in the documentation. Args: query_string: properly formatted GQL query string. namespace: the namespace to use for this query. Raises: datastore_errors.BadQueryError: if the query is not parsable. """ self.__filters = {} self.__orderings = [] self.__app = _app self.__namespace = namespace self.__auth_domain = _auth_domain self.__symbols = self.TOKENIZE_REGEX.findall(query_string) self.__next_symbol = 0 if not self.__Select(): raise datastore_errors.BadQueryError('Unable to parse query')
def __Error(self, error_message): """Generic query error. Args: error_message: string to emit as part of the 'Parse Error' string. Raises: BadQueryError and passes on an error message from the caller. Will raise BadQueryError on all calls to __Error() """ if self.__next_symbol >= len(self.__symbols): raise datastore_errors.BadQueryError( 'Parse Error: %s at end of string' % error_message) else: raise datastore_errors.BadQueryError( 'Parse Error: %s at symbol %s' % (error_message, self.__symbols[self.__next_symbol]))
def count_async(self, limit, options=None): conn = tasklets.get_context()._conn options = QueryOptions(offset=limit, limit=0, config=options) dsqry, post_filters = self._get_query(conn) if post_filters: raise datastore_errors.BadQueryError( 'Post-filters are not supported for count().') rpc = dsqry.run_async(conn, options) total = 0 while rpc is not None: batch = yield rpc rpc = batch.next_batch_async(options) total += batch.skipped_results raise tasklets.Return(total)
def __CastError(self, operator, values, error_message): """Query building error for type cast operations. Args: operator: the failed cast operation values: value list passed to the cast operator error_message: string to emit as part of the 'Cast Error' string. Raises: BadQueryError and passes on an error message from the caller. Will raise BadQueryError on all calls. """ raise datastore_errors.BadQueryError( 'Type Cast Error: unable to cast %r with operation %s (%s)' % (values, operator.upper(), error_message))
def _filter_func(self, value, entity): if isinstance(entity, Key): raise datastore_errors.BadQueryError( 'StructuredProperty filter cannot be used with keys_only query') subentities = getattr(entity, self._code_name, None) if subentities is None: return False if not isinstance(subentities, list): subentities = [subentities] for subentity in subentities: for name, prop in value._properties.iteritems(): val = prop.RetrieveValue(value) if val is not None: if prop.RetrieveValue(subentity) != val: break else: return True return False
def __AddMultiQuery(self, identifier, condition, value, enumerated_queries): """Helper function to add a muti-query to previously enumerated queries. Args: identifier: property being filtered by this condition condition: filter condition (e.g. !=,in) value: value being bound enumerated_queries: in/out list of already bound queries -> expanded list with the full enumeration required to satisfy the condition query Raises: BadArgumentError if the filter is invalid (namely non-list with IN) """ if condition.lower() in ('!=', 'in') and self._keys_only: raise datastore_errors.BadQueryError( 'Keys only queries do not support IN or != filters.') def CloneQueries(queries, n): """Do a full copy of the queries and append to the end of the queries. Does an in-place replication of the input list and sorts the result to put copies next to one-another. Args: queries: list of all filters to clone n: number of copies to make Returns: Number of iterations needed to fill the structure """ if not enumerated_queries: for i in xrange(n): queries.append({}) return 1 else: old_size = len(queries) tmp_queries = [] for i in xrange(n - 1): [ tmp_queries.append(filter_map.copy()) for filter_map in queries ] queries.extend(tmp_queries) queries.sort() return old_size if condition == '!=': if len(enumerated_queries) * 2 > self.MAX_ALLOWABLE_QUERIES: raise datastore_errors.BadArgumentError( 'Cannot satisfy query -- too many IN/!= values.') num_iterations = CloneQueries(enumerated_queries, 2) for i in xrange(num_iterations): enumerated_queries[2 * i]['%s <' % identifier] = value enumerated_queries[2 * i + 1]['%s >' % identifier] = value elif condition.lower() == 'in': if not isinstance(value, list): raise datastore_errors.BadArgumentError( 'List expected for "IN" filter') in_list_size = len(value) if len(enumerated_queries ) * in_list_size > self.MAX_ALLOWABLE_QUERIES: raise datastore_errors.BadArgumentError( 'Cannot satisfy query -- too many IN/!= values.') num_iterations = CloneQueries(enumerated_queries, in_list_size) for clone_num in xrange(num_iterations): for value_num in xrange(len(value)): list_val = value[value_num] query_num = in_list_size * clone_num + value_num filt = '%s =' % identifier enumerated_queries[query_num][filt] = list_val
def _to_filter(self, bindings): # Because there's no point submitting a query that will never # return anything. raise datastore_errors.BadQueryError( 'Cannot convert FalseNode to predicate')