def columns(self, recurse=True, flags=0):
        """
        Returns the list of column instances that are defined
        for this table schema instance.
        
        :param      recurse | <bool>
                    flags   | <orb.Column.Flags>
                    kind    | <orb.Column.Kind>
        
        :return     {<str> column name: <orb.Column>, ..}
        """
        key = (recurse, flags)
        try:
            return self.__cache[key]
        except KeyError:

            output = odict()

            if recurse:
                inherits = self.inherits()
                if inherits:
                    schema = orb.system.schema(inherits)
                    if not schema:
                        raise orb.errors.ModelNotFound(schema=inherits)
                    else:
                        # lookup ancestor columns (don't care about virtual columns)
                        iflags = (flags & ~orb.Column.Flags.Virtual) if flags else ~orb.Column.Flags.Virtual
                        ancest_columns = schema.columns(recurse=recurse, flags=iflags)
                        dups = set(ancest_columns.keys()).intersection(output.keys())
                        if dups:
                            raise orb.errors.DuplicateColumnFound(schema=self, column=','.join(dups))
                        else:
                            output.update(ancest_columns)

            output.update(odict(
                (col.name(), col) for col
                in sorted(self.__columns.values(), key=lambda x: x.order())
                if (not flags or col.testFlag(flags))
            ))

            self.__cache[key] = output
            return output