Esempio n. 1
0
 def __init__(self, regexp, Result_Type=TFL.Record, **converters):
     self.__super.__init__(Result_Type=Result_Type)
     self.regexp = rex = TFL.Regexp(regexp)
     self._converters = conv = {}
     for match in self.field_pat.search_iter(rex._pattern.pattern):
         name = match.group("name")
         conv [name] = \
             (  converters.get (name)
             or converters.get ("default_converter", str)
             )
Esempio n. 2
0
class _Base_ (TFL.Meta.Object) :
    """Base class for SAW Q_Result classes"""

    _distinct            = False
    _order_by_cached     = None
    _sa_query_ob         = None

    def __init__ (self, session = None) :
        self.session     = session
        self.bvar_man    = Q.BVAR_Man ()
        self._joined     = set ()
        self._order_bys  = []
        self.polymorphic = False
    # end def __init__

    @TFL.Meta.Once_Property
    def QX (self) :
        from _MOM._DBW._SAW import QX
        return QX
    # end def QX

    @property
    def sa_query (self) :
        result = self._sa_query_ob
        if result is None :
            result = self._sa_query
            obs    = self._order_by
            if obs :
                result = result.order_by (* obs)
                if self._distinct :
                    ### For `SELECT DISTINCT`, PostgreSQL requires ORDER BY
                    ### expressions to appear in select list
                    ###
                    ### `_SAW_ORIGINAL` is the expression **without** `DESC`,
                    ### if any
                    for ob in obs :
                        cx = getattr (ob, "_SAW_ORIGINAL", ob)
                        result.append_column (cx)
            self._sa_query_ob = result
        return result
    # end def sa_query

    @property
    def sa_query_count (self) :
        result = SA.sql.select \
            ( [SA.func.count ("*").label ("count")]
            , from_obj = self._sa_query.alias ("__count__")
            )
        return result
    # end def sa_query_count

    @property
    def session (self) :
        return self.__dict__ ["session"]
    # end def session

    @session.setter
    def session (self, value) :
        old_value = self.__dict__.get ("session")
        if old_value is None or old_value is value :
            self.__dict__ ["session"] = value
        else :
            raise AttributeError \
                ("Cannot change session from %s to %s" % (old_value, value))
    # end def session

    @property
    def _order_by (self) :
        result = self._order_by_cached
        if result is None :
            result = self._order_by_cached = []
            for obs in reversed (self._order_bys) :
                result.extend (obs)
        return result
    # end def _order_by

    def all (self) :
        return list (self)
    # end def all

    def attr (self, getter, allow_duplicates = False) :
        return self._Attr_ (self, getter, allow_duplicates = allow_duplicates)
    # end def attr

    def attrs (self, * getters, ** kw) :
        if not getters :
            raise TypeError \
                ( "%s.attrs() requires at least one argument"
                % self.__class__.__name__
                )
        return self._Attrs_ (self, getters, ** kw)
    # end def attrs

    def bind (self, _session = None, ** bindings) :
        result = self._clone ()
        result.bvar_man.bind (** bindings)
        if _session is not None :
            result.session = _session
        return result
    # end def bind

    def count (self) :
        q_result = self.session and self.session.q_cache.get (self)
        if q_result is not None :
            return len (q_result)
        else :
            qr     = self._execute  (self.session, self.sa_query_count)
            result = int (qr.scalar ())
            return result
    # end def count

    def distinct (self) :
        result           = self._clone ()
        result._sa_query = result._sa_query.distinct ()
        result._distinct = True
        return result
    # end def distinct

    def filter (self, * criteria, ** kw) :
        if kw :
            crits = list (criteria)
            crits.extend (getattr (Q, k) == v for k, v in pyk.iteritems (kw))
            criteria = tuple (crits)
        assert criteria
        result           = self._clone ()
        sa_query         = result._sa_query
        where_exp, join  = result._get_filters  (criteria, sa_query)
        if join is not None :
            sa_query     = sa_query.select_from (join)
        result._sa_query = sa_query.where       (where_exp)
        return result
    # end def filter

    def first (self) :
        try :
            ### cache ??? `self.limit` creates a new Q_Result
            return TFL.first (self.limit (1))
        except IndexError :
            return None
    # end def first

    def formatted (self) :
        return formatted (self)
    # end def formatted

    def formatted_xqpi (self) :
        """Return formatted query with query parameters expanded inline."""
        return formatted_xqpi (self)
    # end def formatted_xqpi

    def group_by (self, * columns) :
        result           = self._clone ()
        result._sa_query = result._sa_query.group_by \
            (* self._get_group_by (columns))
        return result
    # end def group_by

    def limit (self, limit) :
        result           = self._clone ()
        result._sa_query = result._sa_query.limit (limit)
        return result
    # end def limit

    def offset (self, offset) :
        result           = self._clone ()
        result._sa_query = result._sa_query.offset (offset)
        return result
    # end def offset

    def one (self) :
        result = tuple (self.limit (2))
        count  = len (result)
        if count != 1 :
            raise IndexError \
                ( "Query result contains %s entries"
                % (self.count () if count else 0, )
                )
        return result [0]
    # end def one

    def order_by (self, * criteria) :
        result    = self._clone ()
        sa_query  = result._sa_query
        obs, join = result._get_order_by  (criteria, sa_query)
        if join is not None :
            result._sa_query = sa_query.select_from (join)
        ### `obs` is a list; use `append` here to be able to later reverse
        ### `_order_bys` while keeping the sequence of items in `obs`
        ### this allows later `order_by` calls to dominate earlier ones
        result._order_bys.append (obs)
        return result
    # end def order_by

    def row_iter (self, session, ** kw) :
        result = self._execute (session, self.sa_query, ** kw)
        try :
            yield from result 
        finally :
            result.close ()
    # end def row_iter

    def slice (self, start, stop = None) :
        result = self.offset (start)
        if stop is not None :
            result_sa_query = result._sa_query.limit (stop - start)
        return result
    # end def slice

    def _clone (self, ** kw) :
        cls    = self.__class__
        result = cls.__new__   (cls)
        result.__dict__.update \
            ( self.__dict__
            , bvar_man         = self.bvar_man.clone ()
            , _joined          = set (self._joined)
            , _order_bys       = list (self._order_bys)
            , _order_by_cached = None
            , _sa_query_ob     = None
            , ** kw
            )
        return result
    # end def _clone

    def _execute (self, session, q, ** kw) :
        qkw = dict (self.bvar_man.bindings, ** kw)
        return session.connection.execute (q, ** qkw)
    # end def _execute

    def _extend_join (self, join, joined, jxs) :
        for a_join in jxs :
            if a_join.key not in joined :
                joined.add (a_join.key)
                join = a_join (join)
        return join
    # end def _extend_join

    def _get_attr_exprs (self, axes, sa_query) :
        axes = list \
            (   (getattr (Q, ax) if isinstance (ax, pyk.string_types) else ax)
            for ax in axes
            )
        return self._get_xs (axes, sa_query, "XS_ATTR", TFL.Method.extend)
    # end def _get_attr_exprs

    def _get_filters (self, criteria, sa_query) :
        wxs, join = self._get_xs \
            (criteria, sa_query, "XS_FILTER", TFL.Method.append)
        wxs = wxs [0] if len (wxs) == 1 else SA.expression.and_ (* wxs)
        return wxs, join
    # end def _get_filters

    def _get_group_by (self, columns) :
        ETW = getattr (self, "ETW", None)
        if ETW is None :
            yield from columns 
        else :
            ETW_Q = ETW.QC
            for c in columns :
                if isinstance (c, pyk.string_types) :
                    c = getattr (Q, c)
                yield c (ETW_Q)
    # end def _get_group_by

    def _get_order_by (self, criteria, sa_query) :
        return self._get_xs \
            ( self.QX.fixed_order_by (criteria), sa_query
            , "XS_ORDER_BY", TFL.Method.extend
            )
    # end def _get_order_by

    def _get_xs (self, criteria, sa_query, xs_name, adder) :
        QX     = self.QX
        joined = self._joined
        join   = orig_join = sa_query.froms [-1]
        xs     = []
        for c in criteria :
            if isinstance (c, SA.Operators) :
                xs.append (c)
            elif c is not None :
                qx   = QX.Mapper (self) (c)
                x    = getattr (qx, xs_name)
                join = self._extend_join (join, joined, qx.JOINS)
                adder (xs, x)
        return xs, None if join is orig_join else join
    # end def _get_xs

    def __bool__ (self) :
        result = self.session.q_cache.get (self)
        ### XXX cache
        return bool (result or self.first () is not None)
    # end def __bool__

    def __getslice__ (self, start, stop) :
        return self.slice (start, stop)
    # end def __getslice__

    def __iter__ (self) :
        session = self.session
        result  = session.q_cache.get (self)
        if result is None :
            result = []
            for row in self.row_iter (session) :
                element = self._from_row (row)
                result.append (element)
                yield element
            session.q_cache [self] = result
        else :
            yield from result 
    # end def __iter__

    _fix_by     = TFL.Re_Replacer ("\s+((?:GROUP|ORDER) BY)", "\n     \\1")
    _fix_join   = TFL.Re_Replacer ("((?:[A-Z]+ )*JOIN)",      "\n       \\1")
    _fix_where  = TFL.Re_Replacer ("((?:\s+)*(?:AND|OR)\s+)", "\n       \\1")
    _select_pat = TFL.Regexp      ("(SELECT(?: DISTINCT)?) .*,")
    _select_sep = TFL.Regexp      ("(?<!\)), ")

    def __repr__ (self) :
        def q_parts (self) :
            _fix_by       = self._fix_by
            _fix_join     = self._fix_join
            _fix_where    = self._fix_where
            _select_pat   = self._select_pat
            _select_split = self._select_sep.split
            sep_1         = "\n       "
            sep_n         = "," + sep_1
            for p in str (self.sa_query).split ("\n") :
                p = p.strip ()
                if _select_pat.match (p) :
                    h  = _select_pat.group  (1)
                    t  = p [_select_pat.end (1):].strip ()
                    ts = _select_split (t)
                    p  = sep_1.join ((h, sep_n.join (sorted (ts))))
                elif "JOIN" in p :
                    px = _fix_join (p)
                    ps = list (x.rstrip () for x in px.split ("\n"))
                    p  = "\n".join (ps)
                elif "WHERE" in p :
                    px = _fix_where (p)
                    ps = list (x.rstrip () for x in px.split ("\n"))
                    p  = "\n".join (ps)
                p  = _fix_by (p)
                yield p
        sa_query = "\n     ".join (q_parts (self))
        return "SQL: %s" % (sa_query, )
Esempio n. 3
0
 def __init__(cls, name, bases, dct):
     cls.__m_super.__init__(name, bases, dct)
     cls.epk_split_pat = TFL.Regexp(cls.epk_split_characters)