2 # -*- coding: utf-8 -*-
6 # Copyright (C) UPMC Paris Universitas
8 # Jordan Augé <jordan.auge@lip6.fr>
9 # Marc-Olivier Buob <marc-olivier.buob@lip6.fr>
10 # Thierry Parmentelat <thierry.parmentelat@inria.fr>
12 from types import StringTypes
13 from manifold.core.filter import Filter, Predicate
14 from manifold.util.frozendict import frozendict
15 from manifold.util.type import returns, accepts
22 return uuid.uuid4().hex
27 class ParameterError(StandardError): pass
31 Implements a TopHat query.
33 We assume this is a correct DAG specification.
35 1/ A field designates several tables = OR specification.
36 2/ The set of fields specifies a AND between OR clauses.
39 #---------------------------------------------------------------------------
41 #---------------------------------------------------------------------------
43 def __init__(self, *args, **kwargs):
45 self.query_uuid = uniqid()
47 # Initialize optional parameters
50 #l = len(kwargs.keys())
54 if isinstance(args[0], dict):
58 # Initialization from a tuple
60 if len_args in range(2, 7) and type(args) == tuple:
61 # Note: range(x,y) <=> [x, y[
67 self.timestamp = 'now'
68 self.object, self.filters, self.fields = args
70 self.object, self.filters, self.params, self.fields = args
72 self.timestamp = 'now'
74 self.action, self.object, self.filters, self.params, self.fields, self.timestamp = args
76 # Initialization from a dict
77 elif "object" in kwargs:
78 if "action" in kwargs:
79 self.action = kwargs["action"]
82 print "W: defaulting to get action"
86 self.object = kwargs["object"]
89 if "filters" in kwargs:
90 self.filters = kwargs["filters"]
93 self.filters = Filter([])
95 if "fields" in kwargs:
96 self.fields = set(kwargs["fields"])
101 # "update table set x = 3" => params == set
102 if "params" in kwargs:
103 self.params = kwargs["params"]
108 if "timestamp" in kwargs:
109 self.timestamp = kwargs["timestamp"]
110 del kwargs["timestamp"]
112 self.timestamp = "now"
115 raise ParameterError, "Invalid parameter(s) : %r" % kwargs.keys()
117 # raise ParameterError, "No valid constructor found for %s : args = %r" % (self.__class__.__name__, args)
119 if not self.filters: self.filters = Filter([])
120 if not self.params: self.params = {}
121 if not self.fields: self.fields = set([])
122 if not self.timestamp: self.timestamp = "now"
124 if isinstance(self.filters, list):
126 self.filters = Filter([])
129 self.filters.add(pred)
131 if isinstance(self.fields, list):
132 self.fields = set(self.fields)
134 for field in self.fields:
135 if not isinstance(field, StringTypes):
136 raise TypeError("Invalid field name %s (string expected, got %s)" % (field, type(field)))
138 #---------------------------------------------------------------------------
140 #---------------------------------------------------------------------------
143 return copy.deepcopy(self)
148 self.filters = Filter([])
150 self.fields = set([])
151 self.timestamp = "now"
152 self.timestamp = 'now' # ignored for now
154 @returns(StringTypes)
156 return "SELECT %(select)s%(from)s%(where)s%(at)s" % {
157 "select": ", ".join(self.get_select()) if self.get_select() else "*",
158 "from" : "\n FROM %s" % self.get_from(),
159 "where" : "\n WHERE %s" % self.get_where() if self.get_where() else "",
160 "at" : "\n AT %s" % self.get_timestamp() if self.get_timestamp() else ""
163 @returns(StringTypes)
165 return "SELECT %s FROM %s WHERE %s" % (
166 ", ".join(self.get_select()) if self.get_select() else '*',
172 return (self.action, self.object, self.filters, frozendict(self.params), frozenset(self.fields))
175 return hash(self.__key())
177 #---------------------------------------------------------------------------
179 #---------------------------------------------------------------------------
183 'action': self.action,
184 'object': self.object,
185 'timestamp': self.timestamp,
186 'filters': self.filters,
187 'params': self.params,
188 'fields': list(self.fields)
191 def to_json (self, analyzed_query=None):
192 query_uuid=self.query_uuid
196 f=json.dumps (self.filters.to_list())
197 p=json.dumps (self.params)
198 c=json.dumps (list(self.fields))
199 # xxx unique can be removed, but for now we pad the js structure
202 if not analyzed_query:
205 aq = analyzed_query.to_json()
208 result= """ new ManifoldQuery('%(a)s', '%(o)s', '%(t)s', %(f)s, %(p)s, %(c)s, %(unique)s, '%(query_uuid)s', %(aq)s, %(sq)s)"""%locals()
209 if debug: print 'ManifoldQuery.to_json:',result
212 # this builds a ManifoldQuery object from a dict as received from javascript through its ajax request
213 # we use a json-encoded string - see manifold.js for the sender part
214 # e.g. here's what I captured from the server's output
215 # manifoldproxy.proxy: request.POST <QueryDict: {u'json': [u'{"action":"get","object":"resource","timestamp":"latest","filters":[["slice_hrn","=","ple.inria.omftest"]],"params":[],"fields":["hrn","hostname"],"unique":0,"query_uuid":"436aae70a48141cc826f88e08fbd74b1","analyzed_query":null,"subqueries":{}}']}>
216 def fill_from_POST (self, POST_dict):
218 json_string=POST_dict['json']
219 dict=json.loads(json_string)
220 for (k,v) in dict.iteritems():
223 print "Could not decode incoming ajax request as a Query, POST=",POST_dict
226 traceback.print_exc()
228 #---------------------------------------------------------------------------
230 #---------------------------------------------------------------------------
232 @returns(StringTypes)
233 def get_action(self):
237 def get_select(self):
238 return frozenset(self.fields)
240 @returns(StringTypes)
249 def get_params(self):
252 @returns(StringTypes)
253 def get_timestamp(self):
254 return self.timestamp
257 #DEPRECATED# def make_filters(self, filters):
258 #DEPRECATED# return Filter(filters)
260 #DEPRECATED# def make_fields(self, fields):
261 #DEPRECATED# if isinstance(fields, (list, tuple)):
262 #DEPRECATED# return set(fields)
264 #DEPRECATED# raise Exception, "Invalid field specification"
266 #---------------------------------------------------------------------------
268 #---------------------------------------------------------------------------
271 def action(self, action, object):
273 query.action = action
274 query.object = object
278 def get(self, object): return self.action('get', object)
281 def update(self, object): return self.action('update', object)
284 def create(self, object): return self.action('create', object)
287 def delete(self, object): return self.action('delete', object)
290 def execute(self, object): return self.action('execute', object)
292 def at(self, timestamp):
293 self.timestamp = timestamp
296 def filter_by(self, *args):
299 if not isinstance(filters, (set, list, tuple, Filter)):
301 for predicate in filters:
302 self.filters.add(predicate)
304 predicate = Predicate(*args)
305 self.filters.add(predicate)
307 raise Exception, 'Invalid expression for filter'
310 def select(self, fields=None):
315 if not isinstance(fields, (set, list, tuple)):
318 self.fields.add(field)
321 def set(self, params):
322 self.params.update(params)
325 class AnalyzedQuery(Query):
327 # XXX we might need to propagate special parameters sur as DEBUG, etc.
329 def __init__(self, query=None, metadata=None):
331 self.metadata = metadata
333 self.query_uuid = query.query_uuid
336 self.query_uuid = uniqid()
338 @returns(StringTypes)
341 fields = self.get_select()
342 fields = ", ".join(fields) if fields else '*'
343 out.append("SELECT %s FROM %s WHERE %s" % (
349 for method, subquery in self.subqueries():
350 out.append(' [SQ #%d : %s] %s' % (cpt, method, str(subquery)))
353 return "\n".join(out)
356 super(AnalyzedQuery, self).clear()
357 self._subqueries = {}
359 def subquery(self, method):
360 # Allows for the construction of a subquery
361 if not method in self._subqueries:
362 analyzed_query = AnalyzedQuery(metadata=self.metadata)
363 analyzed_query.action = self.action
366 type = self.metadata.get_field_type(self.object, method)
367 except ValueError ,e: # backwards 1..N
371 analyzed_query.object = type
372 self._subqueries[method] = analyzed_query
373 return self._subqueries[method]
375 def get_subquery(self, method):
376 return self._subqueries.get(method, None)
378 def remove_subquery(self, method):
379 del self._subqueries[method]
381 def get_subquery_names(self):
382 return set(self._subqueries.keys())
384 def subqueries(self):
385 for method, subquery in self._subqueries.iteritems():
386 yield (method, subquery)
388 def filter_by(self, filters):
389 if not filters: return self
390 if not isinstance(filters, (set, list, tuple, Filter)):
392 for predicate in filters:
393 if '.' in predicate.key:
394 method, subkey = pred.key.split('.', 1)
395 # Method contains the name of the subquery, we need the type
396 # XXX type = self.metadata.get_field_type(self.object, method)
397 sub_pred = Predicate(subkey, pred.op, pred.value)
398 self.subquery(method).filter_by(sub_pred)
400 super(AnalyzedQuery, self).filter_by(predicate)
403 def select(self, fields):
404 if not isinstance(fields, (set, list, tuple)):
408 method, subfield = field.split('.', 1)
409 # Method contains the name of the subquery, we need the type
410 # XXX type = self.metadata.get_field_type(self.object, method)
411 self.subquery(method).select(subfield)
413 super(AnalyzedQuery, self).select(field)
416 def set(self, params):
417 for param, value in self.params.items():
419 method, subparam = param.split('.', 1)
420 # Method contains the name of the subquery, we need the type
421 # XXX type = self.metadata.get_field_type(self.object, method)
422 self.subquery(method).set({subparam: value})
424 super(AnalyzedQuery, self).set({param: value})
427 def analyze(self, query):
429 self.action = query.action
430 self.object = query.object
431 self.filter_by(query.filters)
432 self.set(query.params)
433 self.select(query.fields)
436 query_uuid=self.query_uuid
440 f=json.dumps (self.filters.to_list())
441 p=json.dumps (self.params)
442 c=json.dumps (list(self.fields))
443 # xxx unique can be removed, but for now we pad the js structure
447 sq=", ".join ( [ "'%s':%s" % (object, subquery.to_json())
448 for (object, subquery) in self._subqueries.iteritems()])
451 result= """ new ManifoldQuery('%(a)s', '%(o)s', '%(t)s', %(f)s, %(p)s, %(c)s, %(unique)s, '%(query_uuid)s', %(aq)s, %(sq)s)"""%locals()
452 if debug: print 'ManifoldQuery.to_json:',result