Example #1
0
    def table_fields(self, table_name: str, schema=None) -> List[FieldRecord]:
        """
        Get fields of table
        :param table_name:
        :param schema:
        :return:
        """
        if schema is None:
            schema = self.SCHEMA_DEFAULT

        columns = {
            'column_name': 'field',
            'data_type': 'type',
            Literal('false'): 'primary'
        }
        qry = Select(PgSqlDialect()) \
            .from_('columns', columns, schema='information_schema') \
            .where('table_schema', '=', schema) \
            .where('table_name', '=', table_name) \
            .order('ordinal_position')
        idx = self.table_pk(table_name, schema)
        with self._db.cursor() as c:
            fields = c.fetchall(*qry.assemble(),
                                cls=FieldRecord)  # type:list[FieldRecord]
            if idx is not None:
                for f in fields:
                    f.primary = f.field == idx.field
            return fields
Example #2
0
    def views(self, schema=None) -> List:
        if schema is None:
            schema = self.SCHEMA_DEFAULT

        result = []
        qry = Select(PgSqlDialect()).from_('pg_views', ['viewname']).where(
            'schemaname', '=', schema)
        with self._db.cursor() as c:
            for r in c.fetchall(*qry.assemble()):
                result.append(r['viewname'])
            return result
Example #3
0
 def view_exists(self, view_name: str, schema=None) -> bool:
     """
     Check if a given view exists
     :param view_name: table name
     :param schema: optional schema
     :return:
     """
     qry = Select(Sqlite3SqlDialect()) \
         .from_('sqlite_master', ['name']) \
         .where('name', '=', view_name) \
         .where('type', '=', 'view')
     with self._db.cursor() as c:
         return len(c.fetchall(*qry.assemble())) > 0
Example #4
0
 def views(self, schema=None) -> List:
     """
     List all available views on the indicated schema. If no schema is specified, assume public schema
     :param schema: optional schema name
     :return: list of tablenames
     """
     qry = Select(Sqlite3SqlDialect()).from_('sqlite_master').where(
         'type', '=', 'view')
     result = []
     with self._db.cursor() as c:
         for r in c.fetchall(*qry.assemble()):
             if not r['name'].startswith('sqlite_'):
                 result.append(r['name'])
     return result
Example #5
0
    def view_exists(self, view_name: str, schema=None) -> bool:
        """
        Check if a given view exists
        :param view_name: table name
        :param schema: optional schema
        :return:
        """
        if schema is None:
            schema = self.SCHEMA_DEFAULT

        qry = Select(PgSqlDialect()).from_('pg_views', ['viewname']) \
            .where('schemaname', '=', schema) \
            .where('viewname', '=', view_name)
        with self._db.cursor() as c:
            return len(c.fetchall(*qry.assemble())) > 0
Example #6
0
    def tables(self, schema=None) -> List:
        """
        List all available tables on the indicated schema. If no schema is specified, assume public schema
        :param schema: optional schema name
        :return: list of tablenames
        """
        if schema is None:
            schema = self.SCHEMA_DEFAULT

        result = []
        qry = Select(PgSqlDialect()).from_('pg_tables', ['tablename']).where(
            'schemaname', '=', schema)
        with self._db.cursor() as c:
            for r in c.fetchall(*qry.assemble()):
                result.append(r['tablename'])
            return result
Example #7
0
    def user_groups(self, user_name: str) -> List[str]:
        """
        List all groups associated with a given user
        :param user_name: user name to check
        :return: list of group names
        """
        qry = Select(PgSqlDialect()) \
            .from_('pg_user', {'rolname': 'name'}) \
            .join('pg_auth_members', 'member', 'pg_user', 'usesysid') \
            .join('pg_roles', 'oid', 'pg_auth_members', 'roleid') \
            .where('usename', '=', user_name)

        result = []
        with self._db.cursor() as c:
            for r in c.fetchall(*qry.assemble()):
                result.append(r['name'])
        return result
Example #8
0
 def select(self, cols=None) -> Select:
     """
     Return a Select() builder instance for the current table
     :param cols: optional columns
     :return: Select
     """
     return Select(self._dialect).from_(self._tablename,
                                        cols=cols,
                                        schema=self._schema)
Example #9
0
 def databases(self) -> List:
     """
     List all available databases
     :return: list of database names
     """
     with self._db.cursor() as c:
         result = []
         for r in c.fetchall(*Select(PgSqlDialect()).from_(
                 'pg_database', ['datname']).assemble()):
             result.append(r['datname'])
         return result
Example #10
0
    def fetch_one(self, qry: Select) -> Optional[object]:
        """
        Retrieve a single row
        :param qry: query to execute
        :return: record object of self._record or None

        Example:
            r.fetch_one(r.select().where('login', '=', 'gandalf@lotr'))     # fetch record if exists, else returns None
        """
        with self._db.cursor() as c:
            sql, values = qry.limit(1).assemble()
            return c.fetchone(sql, values, cls=self._record)
Example #11
0
 def schemas(self) -> List:
     """
     List all available schemas
     :return: list of schema names
     """
     with self._db.cursor() as c:
         result = []
         for r in c.fetchall(*Select(PgSqlDialect()).from_(
                 'schemata', ['schema_name'],
                 'information_schema').assemble()):
             result.append(r['schema_name'])
         return result
Example #12
0
    def list(self, qry: Select, limit=None, offset=None, cls=None):
        """
        Performs a query with offset and limit
        Returns a tuple with total record count for the query, and the rows returned by applying offset and limit

        The original query object is left intact

        :param qry: query to use
        :param limit:
        :param offset:
        :param cls: optional Record class to use for rows
        :return: (total_rows, selected_row_list)
        """
        total = 0
        qry = copy.deepcopy(qry)
        sql, values = qry.assemble()
        qry_count = Select(self._dialect).from_(
            {Literal(sql): "qry"}, cols={Literal('COUNT(*)'): 'total'})
        if limit:
            qry.limit(limit, offset)

        with self._db.cursor() as c:
            # count total rows
            sql, _ = qry_count.assemble()
            count_record = c.fetchone(sql, values)
            if count_record:
                total = count_record['total']

        # fetch rows
        rows = self.fetch(qry, cls=cls)
        return total, rows
Example #13
0
    def fetch_raw(self, qry: Select) -> Optional[list]:
        """
        Fetch a list of rows
        Result is not serialized to record, instead it returns a list of dict-like records directly from the DB driver

        :param qry: query to execute
        :return: list of dict-like result

        Example:
            r.fetch_raw(r.select().where('name', 'like', 'gandalf%'))     # fetch records that match query
        """
        with self._db.cursor() as c:
            sql, values = qry.assemble()
            return c.fetchall(sql, values)
Example #14
0
 def users(self) -> List[UserRecord]:
     """
     List all available users
     :return:
     """
     fields = {
         'usename': 'name',
         'usesuper': 'superuser',
         'usecreatedb': 'createdb'
     }
     with self._db.cursor() as c:
         return c.fetchall(
             *Select(PgSqlDialect()).from_('pg_user', fields,
                                           'pg_catalog').assemble(),
             UserRecord)
Example #15
0
    def fetch(self, qry: Select, cls=None) -> Optional[list]:
        """
        Fetch a list of rows

        :param qry: query to execute
        :param cls: optional record class (useful for joins that may return different record structures)
        :return: list of record object or empty list

        Example:
            r.fetch(r.select().where('name', 'like', 'gandalf%'))     # fetch records that match query
        """
        with self._db.cursor() as c:
            sql, values = qry.assemble()
            if cls is None:
                cls = self._record
            return c.fetchall(sql, values, cls=cls)
Example #16
0
    def run(self,
            qry: Select = None,
            search_text: str = None,
            match_fields: dict = None,
            limit: int = None,
            offset: int = None,
            sort_fields: dict = None) -> tuple:
        """
        Executes a query and returns the total row count matching the query, as well as the records within the specified
        range.

        If no query specified, a default query is built; If no sort dict specified, the default sort is used;
        If limit is omitted, all results are returned

        search_text is applied to all fields present in search_fields and are OR'ed;
        match_fields contains fieldnames and values to be matched for equality; they are AND'ed;

        :param qry: optional Select query
        :param search_text: optional search string
        :param match_fields: optional field filter
        :param limit: optional limit
        :param offset: optional offset (ignored if no limit)
        :param sort_fields: optional sort fields in the format {field_name: order}
        :return: tuple(total_row_count, filtered_rows)
        """

        if not qry:
            qry = self.default_query()
        if not sort_fields:
            sort_fields = self.default_sort()

        if match_fields:
            qry.where_and()
            for field, value in match_fields.items():
                if field not in self._fields:
                    raise ValueError(
                        "field '%s' used in match_field does not exist on Record"
                        % field)
                qry.where(field, '=', value)
            qry.where_end()

        if search_text:
            qry.where_and()
            if self._search_type == self.SEARCH_NONE:
                raise RuntimeError('search is not allowed')

            if len(self._search_fields) == 0:
                raise RuntimeError(
                    "no available fields are mapped as searchable")

            mask = self.search_map[self._search_type].format(str(search_text))

            operand = 'LIKE'
            psql_mode = False
            if not self._case_sensitive:
                if self._ilike:
                    operand = 'ILIKE'
                    psql_mode = True
            else:
                psql_mode = True

            if psql_mode:
                for field in self._search_fields:
                    qry.orwhere(field, operand, mask.format(str(search_text)))
            else:
                mask = mask.upper()
                for field in self._search_fields:
                    qry.orwhere(Literal('UPPER({})'.format(field)), operand,
                                mask)
            qry.where_end()

        if sort_fields:
            if not isinstance(sort_fields, Mapping):
                raise ValueError("'sort_fields' parameter must be a dict")
            for field, order in sort_fields.items():
                if field in self._fields:
                    qry.order(field, order.upper())
                else:
                    raise ValueError(
                        "field '%s' used for sorting does not exist on Record"
                        % field)

        # perform queries
        return self._repo.list(qry, limit=limit, offset=offset)
Example #17
0
from rick_db.sql import Update, Select, Literal


@fieldmapper(tablename='test_table')
class SomeTable:
    field = 'field'
    other = 'other_field'


@fieldmapper(tablename='other_table', schema='public')
class SchemaTestTable:
    field = 'field'
    other = 'other_field'


sample_query = Select().from_('test', ['id']).where('field', '=', 'abcd')
sample_record = SomeTable(field='value', other='other_value')

update_cases = [
    ['table1', {'field1': 'value1'}, None, None, 'UPDATE "table1" SET "field1"=?'],
    ['table1', {'f1': 'v1', 'f2': 2}, None, 'public', 'UPDATE "public"."table1" SET "f1"=?, "f2"=?'],
    ['table1', {'field1': 'value1'}, [('id', '=', '5')], None, 'UPDATE "table1" SET "field1"=? WHERE "id" = ?'],
    ['table1', {'f1': 'v1', 'f2': 2}, [('id', '=', '5')], 'public',
     'UPDATE "public"."table1" SET "f1"=?, "f2"=? WHERE "id" = ?'],
    ['table1', {'f1': 'v1', 'f2': 2}, [('id', 'in', sample_query)], None,
     'UPDATE "table1" SET "f1"=?, "f2"=? WHERE "id" in (SELECT "id" FROM "test" WHERE ("field" = ?))'],
    ['table1', {'field1': 'value1'}, [('id', 'is null', None), (Literal('LENGTH(name)'), '>', 3)], None,
     'UPDATE "table1" SET "field1"=? WHERE "id" is null AND LENGTH(name) > ?'],
    [SomeTable, sample_record, [('id', '=', '5')], None,
     'UPDATE "test_table" SET "field"=?, "other_field"=? WHERE "id" = ?'],
    [SomeTable, {SomeTable.field: 'value1'}, [('id', '=', '5')], None,