core.py :  » Web-Server » Porcupine-Web-Application-Server » porcupine-0.6-src » porcupine » core » oql » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Server » Porcupine Web Application Server 
Porcupine Web Application Server » porcupine 0.6 src » porcupine » core » oql » core.py
#===============================================================================
#    Copyright 2005-2009, Tassos Koutsovassilis
#
#    This file is part of Porcupine.
#    Porcupine is free software; you can redistribute it and/or modify
#    it under the terms of the GNU Lesser General Public License as published by
#    the Free Software Foundation; either version 2.1 of the License, or
#    (at your option) any later version.
#    Porcupine is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Lesser General Public License for more details.
#    You should have received a copy of the GNU Lesser General Public License
#    along with Porcupine; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#===============================================================================
"""
OQL Core Interpreter
"""
import types

from porcupine import datatypes
from porcupine import db
from porcupine.core.objectSet import ObjectSet
from porcupine.utils.date import Date
from porcupine.utils import misc

NEG         = 1

ARRAY       = 51

FUNCTION    = 60
BETWEEN     = 61
IN          = 62
HASATTR     = 63
SLICE       = 64
IF          = 65
INSTANCEOF  = 66
GETPARENT   = 67

CMD_ASSIGN  = 100
OQL_SELECT  = 200

# map operator symbols to corresponding operations
opn2 = { 
    '+' : lambda a,b: a + b,
    '-' : lambda a,b: a - b,
    '*' : lambda a,b: a * b,
    '/' : lambda a,b: a / b,
    '^' : lambda a,b: a ** b,
    
    '=' : lambda a,b: a == b,
    '<>' : lambda a,b: a != b,
    '<=' : lambda a,b: a <= b,
    '>=' : lambda a,b: a >= b,
    '>' : lambda a,b: a > b,
    '<' : lambda a,b: a < b,

    'or' : lambda a,b: a or b,
    'and' : lambda a,b: a and b,
}

opn1 = {
    "not": lambda a: not a
}

operators2 = frozenset(opn2.keys())
operators1 = frozenset(opn1.keys())

fn  = {
    'len' : len,
    'abs' : abs,
    'str' : str,
    'lower' : lambda a: a.lower(),
    'upper' : lambda a: a.upper(),
    'date' : lambda a: Date.from_iso_8601(a),
    'trunc' : lambda a: int(a),
    'round' : lambda a: int(a+0.5),
    'sgn' : lambda a: ( (a<0 and -1) or (a>0 and 1) or 0 ),
    'isnone' : lambda a,b: a or b,
    'getattr' : lambda a,b: get_attribute(a, [b])
}

def evaluate_stack(stack, variables, for_object=None):
    try:
        op = stack.pop()
    except AttributeError:
        op = stack

    if type(op) == list:
        cmd_code = op[0]
        handler = globals()['h_%s' % cmd_code]
        return handler(op[1], variables, for_object)

    elif type(op) == types.FunctionType:
        return op(for_object)

    elif type(op) == str:
        if op[0] == "'":
            # a string
            return op[1:-1]

        elif op[0] == "$":
            # a variable
            return variables[op[1:]]

        elif op in operators2:
            op1 = evaluate_stack(stack, variables, for_object)
            if op == 'and' and not op1:
                return False
            elif op == 'or' and op1:
                return True
            op2 = evaluate_stack(stack, variables, for_object)
            return opn2[op](op1,op2)

        elif op in operators1:
            op1 = evaluate_stack(stack, variables, for_object)
            return opn1[op](op1)

        else:
            if variables.has_key(op) and type(variables[op]) == tuple:
                # an alias or optimized attribute
                alias_stack, objectid, alias_value = variables[op]
                if objectid != for_object._id:
                    # compute alias
                    alias_value = evaluate_stack(alias_stack[:],
                                                 variables,
                                                 for_object)
                    # write cached value
                    variables[op] = (alias_stack,
                                     for_object._id,
                                     alias_value)
                    return alias_value
                else:
                    # get alias from cache
                    return alias_value
            else:
                # an attribute
                if op == '**':
                    return for_object
                else:
                    if for_object != None:
                        return get_attribute(for_object, op.split('.'))
    else:
        return op
        
def get_attribute(obj, name_list):
    try:
        attr_name = name_list.pop(0)
        attr = getattr(obj, attr_name)
        if attr.__class__.__module__ != '__builtin__':
            if isinstance(attr, datatypes.Reference1):
                obj = attr.get_item()
            elif isinstance(attr,
                            (datatypes.ReferenceN, datatypes.Composition)):
                obj = attr.get_items()
            elif isinstance(attr, datatypes.Date):
                obj = attr
            else:
                obj = attr.value
        elif attr_name in ('created', 'modified'):
            obj = Date(attr)
        else:
            obj = attr
        
        if len(name_list) > 0:
            if type(obj) == list:
                obj = [get_attribute(item, name_list[:]) for item in obj]
            else:
                obj = get_attribute(obj, name_list[:])
        
        return obj
    except AttributeError:
        return None
        
def sort_list(list1, list2):
    pairs = zip(list1, list2)
    pairs.sort()
    res = [x[1] for x in pairs]
    return res

def compute_aggregate(aggr, lst):
    #print aggr, list
    if aggr=='COUNT':
        return len(lst)
    elif aggr=='MAX':
        return max(lst)
    elif aggr=='MIN':
        return min(lst)
    elif aggr=='SUM':
        return sum(lst)
    elif aggr=='AVG':
        if lst:
            return sum(lst)/len(lst)
        else:
            return 0
    else:
        # check if list has constant values
        if lst:
            if lst != [lst[0]] * len(lst):
                raise TypeError, ('Non aggregate expressions should be ' +
                                  'constants or included in a GROUP BY clause')
            else:
                return(lst[0])
        return None

#===============================================================================
# unary minus command handler
#===============================================================================

def h_1(params, variables, for_object):
    return(-evaluate_stack(params[:], variables, for_object))

#===============================================================================
# array command handler
#===============================================================================
    
def h_51(params, variables, for_object):
    l = [evaluate_stack(x[:], variables, for_object) for x in params]
    return l

#===============================================================================
# function command handler
#===============================================================================

def h_60(params, variables, for_object):
    func = fn[params[0]]
    args = params[1]
    #print args
    f_args = [evaluate_stack(arg[:], variables, for_object) for arg in args]
    return func(*f_args)

#===============================================================================
# BETWEEN command handler
#===============================================================================

def h_61(params, variables, for_object):
    value, low, high = [evaluate_stack(expr[:], variables, for_object)
                        for expr in params]
    return(low < value < high)

#===============================================================================
# IN command handler
#===============================================================================

def h_62(params, variables, for_object):
    value, iterable = [evaluate_stack(expr[:], variables, for_object)
                       for expr in params]
    try:
        return value in iterable
    except TypeError:
        return False

#===============================================================================
# HASATTR command handler
#===============================================================================

def h_63(params, variables, for_object):
    attrName = evaluate_stack(params[0][:], variables, for_object)
    return hasattr(for_object, attrName)

#===============================================================================
# SLICE command handler
#===============================================================================

def h_64(params, variables, for_object):
    expression = evaluate_stack(params[0][:], variables, for_object)
    low = evaluate_stack(params[1][:], variables, for_object) or None
    high = evaluate_stack(params[2][:], variables, for_object) or None
    if type(expression)==str:
        return unicode(expression, 'utf-8')[low:high].encode('utf-8')
    else:
        return expression[low:high]

#===============================================================================
# IF command handler
#===============================================================================

def h_65(params, variables, for_object):
    test_expession = evaluate_stack(params[0][:], variables, for_object)
    if test_expession:
        return evaluate_stack(params[1][:], variables, for_object)
    else:
        return evaluate_stack(params[2][:], variables, for_object)
        
#===============================================================================
# INSTANCEOF command handler
#===============================================================================
def h_66(params, variables, for_object):
    className = evaluate_stack(params[0][:], variables, for_object)
    return isinstance(for_object, misc.get_rto_by_name(className))

#===============================================================================
# GETPARENT command handler
#===============================================================================
def h_67(params, variables, for_object):
    if (params):
        obj = evaluate_stack(params[0][:], variables, for_object)
    else:
        obj = for_object
    return obj.get_parent()

#===============================================================================
# assignment command handler
#===============================================================================
       
def h_100(params, variables):
    variables[params[0]] = evaluate_stack(params[1][:], variables)

#===============================================================================
# oql select command handler
#===============================================================================

def select(container_id, deep, iterable, fields, condition, variables):
    results = []
    if condition:
        results = [tuple(evaluate_stack(expr[1], variables, item)
                         for expr in fields)
                   for item in iterable
                   if evaluate_stack(condition[:], variables, item)]
    else:
        results = [tuple(evaluate_stack(expr[1], variables, item)
                         for expr in fields)
                   for item in iterable]

    if deep:
        subfolders = db._db.join((('_parentid', container_id),
                                  ('isCollection', True)))
        for folder in subfolders:
            cursor = iterable.duplicate()
            db._db.switch_cursor_scope(cursor, folder._id)
            results1 = select(folder._id, deep, cursor, fields, condition,
                              variables)
            cursor.close()
            results.extend(results1)
        subfolders.close()
    
    return results

def optimize_query(conditions, variables):
    indexes = []
    if len(conditions) == 3 \
            and db._db.has_index(conditions[1]) \
            and conditions[2] in ['=', '<', '>', '<=', '>=']:
        index_value = evaluate_stack(conditions[0], variables)
        if index_value != None:
            if conditions[2] == '=':
                indexes.append((conditions[1], index_value))
            elif conditions[2] == '<':
                indexes.append((conditions[1],
                               (None, (index_value, False))))
            elif conditions[2] == '<=':
                indexes.append((conditions[1],
                               (None, (index_value, True))))
            elif conditions[2] == '>':
                indexes.append((conditions[1],
                               ((index_value, False), None)))
            elif conditions[2] == '>=':
                indexes.append((conditions[1],
                               ((index_value, True), None)))
            conditions = []
    #print indexes
    return indexes, conditions

def h_200(params, variables, for_object=None):
    select_fields = params[0]
    
    # get aliases
    aliases = []
    for expr, alias, aggr in select_fields:
        if expr != alias:
            variables[alias] = (expr, None, None)
            aliases.append(alias)

    field_names = [x[1] for x in select_fields]
    expressions = [tuple(x[0::2]) for x in select_fields]
    
    select_from = params[1]
    where_condition = params[2]
    
    if params[3]:
        sort_order, order_by = params[3]
        
        for ind, order_field in enumerate(order_by):
            expr, alias, aggr = order_field
            if alias in aliases:
                order_by[ind] = select_fields[field_names.index(alias)]
            elif expr != alias:
                if (expr, aggr) in expressions:
                    order_by[ind] = select_fields[expressions.index((expr,
                                                                     aggr))]
                else:
                    variables[alias] = (expr, None, None)
                    aliases.append(alias)
    else:
        order_by = []

    group_by = params[4]
    if group_by:
        for ind, group_field in enumerate(group_by):
            expr, alias, aggr = group_field
            if alias in aliases:
                group_by[ind] = select_fields[field_names.index(alias)]
            elif expr != alias:
                if (expr, aggr) in expressions:
                    group_by[ind] = select_fields[expressions.index((expr,
                                                                     aggr))]
                else:
                    variables[alias] = (expr, None, None)
                    aliases.append(alias)

    if select_fields:
        all_fields = select_fields + order_by + group_by
    else:
        all_fields = [['**', '**', '']] + order_by + group_by

    # optimize field access
    for f in all_fields:
        if type(f[0]) == str and f[0][0]!= "'" \
                and f[0][0]!='$' and f[0] != '**':
            field_spec = f[0].split('.')
            variables[f[1]] = (
                eval('[lambda x: get_attribute(x, %s)]' % field_spec),
                None, None)
    
    aggregates = [x[2] for x in all_fields]
    results = []

    #print where_condition
    if for_object == None:
        # in case of not being a subquery, optimize query
        indexed_lookups, where_condition = \
            optimize_query(where_condition[:], variables)
    
    for deep, object_id in select_from:
        if deep==2:
            # this:attr
            if not for_object:
                raise TypeError, \
                    'Inner scopes using "this:" are valid only in sub-queries'
            if hasattr(for_object, object_id):
                attr = getattr(for_object, object_id)
                if isinstance(attr, (datatypes.ReferenceN,
                                     datatypes.Composition)):
                    ref_objects = attr.get_items()
                elif isinstance(attr, datatypes.Reference1):
                    ref_objects = [attr.get_item()]
                else:
                    raise TypeError, ('Inner scopes using "this:" are ' +
                                      'valid only ReferenceN, Reference1 ' +
                                      'and Composition data types')
                r = select(for_object._id, False, ref_objects, all_fields,
                           where_condition, variables)
                results.extend(r)
        else:
            # swallow-deep
            obj = db.get_item(object_id)
            if obj != None and obj.isCollection:
                if deep and obj._id == '':
                    deep = False
                    scope_condition = []
                else:
                    scope_condition = [('_parentid', obj._id)]
                
                if len(indexed_lookups) == 0:
                    #print 'not using index'
                    if scope_condition == []:
                        # deep traversal of root without index
                        scope_condition = [('displayName', (None, None))]
                    cursor = db._db.query_index(*scope_condition[0])
                else:
                    #print 'using index'
                    if len(indexed_lookups) == 1 and not scope_condition:
                        cursor = db._db.query_index(*indexed_lookups[0])
                    else:
                        cursor = db._db.join(indexed_lookups + scope_condition)

                r = select(obj._id, deep, cursor, all_fields, where_condition,
                           variables)
                cursor.close()
                results.extend(r)
    
    if results:
        if group_by:
            if select_fields:
                #construct groups
                group_dict = {}
                igrpstart = len(select_fields) + len(order_by)
                igrpend = igrpstart + len(group_by)
                for rec in results:
                    group_value = tuple(rec[igrpstart:igrpend])
                    group_dict[group_value] = \
                        group_dict.setdefault(group_value, [])
                    group_dict[group_value].append(rec)
                groups = [tuple(g) for g in group_dict.values()]
            else:
                raise TypeError, \
                    'GROUP BY clause is incompatible with SELECT *'
        else:
            groups = [results]
        
        #print len(groups)
        results = []
        
        if aggregates != [''] * len(aggregates) or group_by:
            for ind, group in enumerate(groups):
                group_sum = []
                for aggr_index, aggr_type in enumerate(aggregates):
                    # aggregates exclude None values
                    group_sum.append(compute_aggregate(
                        aggr_type,
                        [x[aggr_index]
                         for x in group
                         if x[aggr_index] != None]))
                groups[ind] = (tuple(group_sum),)
        
        for group in groups:
            results.extend(group)
        
        if order_by:
            # extract sort values
            istart = 1
            if select_fields:
                istart = len(select_fields)
            sortlist = tuple([x[istart:istart + len(order_by)]
                              for x in results])
            results = sort_list(sortlist, results)
            # if it is descending reverse the result list
            if not sort_order: results.reverse()
    
    # remove aliases
    for alias in aliases:
        del variables[alias]
    
    if select_fields:
        schema = field_names
        # truncate to keep only select fields
        iend = len(select_fields)
        results = tuple([x[:iend] for x in results])
    else:
        schema = None
        results = tuple([x[:1][0] for x in results])
    
    return ObjectSet(results, schema)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.