예제 #1
0
    def records_where(
        self, where: Union[Mapping[str, Any], str,
                           List[str]]) -> List[Dict[str, Any]]:
        if not self.connected:
            raise ConnectionError(
                f'no connection to {self.username}@{self.hostname}:{self.port}/{self.database}/{self.name}'
            )

        where_clause, where_values = self.__where_clause(where)

        with self.connection as connection:
            with connection.cursor() as cursor:
                if where_clause is None:
                    cursor.execute(
                        f'SELECT {", ".join(self.fields.keys())} FROM {self.name};'
                    )
                else:
                    try:
                        cursor.execute(
                            f'SELECT * FROM {self.name} WHERE {where_clause};',
                            where_values)
                    except psycopg2.errors.UndefinedColumn as error:
                        raise KeyError(error)
                    except psycopg2.errors.SyntaxError as error:
                        raise SyntaxError(f'invalid SQL syntax - {error}')
                matching_records = cursor.fetchall()
        connection.close()

        matching_records = [
            parse_record_values(dict(zip(self.fields.keys(), record)),
                                self.fields) for record in matching_records
        ]

        return matching_records
예제 #2
0
 def __len__(self) -> int:
     with self.connection as connection:
         with connection.cursor() as cursor:
             cursor.execute(f'SELECT COUNT(*) FROM {self.name};')
             length = cursor.fetchone()[0]
     connection.close()
     return length
예제 #3
0
 def connected(self) -> bool:
     with self.connection as connection:
         try:
             with connection.cursor() as cursor:
                 cursor.execute('SELECT 1;')
                 cursor.fetchone()
             connected = True
         except:
             connected = False
     connection.close()
     return connected
예제 #4
0
    def remote_fields(self) -> Dict[str, type]:
        if not self.connected:
            raise ConnectionError(
                f'no connection to {self.username}@{self.hostname}:{self.port}/{self.database}/{self.name}'
            )

        fields = None
        with self.connection as connection:
            with connection.cursor() as cursor:
                if database_has_table(cursor, self.name):
                    fields = database_table_fields(cursor, self.name)

                    for field, field_type in fields.items():
                        dimensions = field_type.count('_')
                        field_type = field_type.strip('_')

                        field_type = field_type.lower()
                        if field_type == 'geometry':
                            if field in self.fields:
                                fields[field] = self.fields[field]
                                continue

                        for python_type, postgres_type in self.FIELD_TYPES.items(
                        ):
                            if postgres_type.lower() == field_type:
                                if field_type == 'geometry':
                                    if python_type not in globals():
                                        exec(
                                            f'from shapely.geometry import {python_type}'
                                        )
                                field_type = eval(python_type)
                                break
                        else:
                            for python_type, postgres_type in self.FIELD_TYPES.items(
                            ):
                                if python_type.lower() in field_type:
                                    field_type = eval(python_type)
                                    break
                            else:
                                field_type = str

                        for _ in range(dimensions):
                            field_type = [field_type]
                        fields[field] = field_type
                else:
                    fields = None
        connection.close()

        return fields
예제 #5
0
    def delete_where(self, where: Union[Mapping[str, Any], str, List[str]]):
        if not self.connected:
            raise ConnectionError(
                f'no connection to {self.username}@{self.hostname}:{self.port}/{self.database}/{self.name}'
            )

        where_clause, where_values = self.__where_clause(where)

        with self.connection as connection:
            with connection.cursor() as cursor:
                if where_clause is None:
                    cursor.execute(f'TRUNCATE {self.name};')
                else:
                    try:
                        cursor.execute(
                            f'DELETE FROM {self.name} WHERE {where_clause};',
                            where_values)
                    except psycopg2.errors.UndefinedColumn as error:
                        raise KeyError(error)
                    except psycopg2.errors.SyntaxError as error:
                        raise SyntaxError(f'invalid SQL syntax - {error}')
        connection.close()
예제 #6
0
    def records_intersecting(
            self,
            geometry: BaseGeometry,
            crs: CRS = None,
            geometry_fields: List[str] = None) -> List[Dict[str, Any]]:
        if crs is None:
            crs = self.crs

        if crs.to_epsg() is None:
            raise NotImplementedError(f'no EPSG code found for CRS "{crs}"')

        if geometry_fields is None or len(geometry_fields) == 0:
            geometry_fields = list(self.geometry_fields)

        where_clause = []
        where_values = []
        for field in geometry_fields:
            where_values.extend([geometry.wkt, crs.to_epsg()])
            geometry_string = f'ST_GeomFromText(%s, %s)'
            if crs != self.crs:
                geometry_string = f'ST_Transform({geometry_string}, %s)'
                where_values.append(self.crs.to_epsg())
            where_clause.append(f'ST_Intersects({field}, {geometry_string})')
        where_clause = ' OR '.join(where_clause)

        with self.connection as connection:
            with connection.cursor() as cursor:
                cursor.execute(
                    f'SELECT * FROM {self.name} WHERE {where_clause};',
                    where_values)
                records = cursor.fetchall()
        connection.close()

        return [
            parse_record_values(dict(zip(self.fields.keys(), record)),
                                self.fields) for record in records
        ]
    for row in user_record:
        user_id, first_name, last_name, user_type = row
        temp_dict = {
            'user_id': user_id,
            'first_name': first_name,
            'last_name': last_name,
            'user_type': user_type
        }
        my_list_of_users.append(temp_dict)

except (Exception, Error) as error:
    print("Error while connection to PostgreSQL", error)
finally:
    if connection:
        cursor.close()
        connection.close()

# this is the call that saw us create the room.py
# we included a possible solution in room.py
room_list = []
try:
    connection = connect(database=os.getenv('NIYON_CHAT_DATABASE'),
                         user=os.getenv('NIYON_CHAT_USER'),
                         host=os.getenv('NIYON_CHAT_HOST'),
                         password=os.getenv('NIYON_CHAT_PASSWORD'),
                         port=os.getenv('NIYON_PG_PORT'))
    cursor = connection.cursor()
    my_room_list = "SELECT id, roomname FROM public.roomname"

    cursor.execute(my_room_list)
    my_rooms = cursor.fetchall()
예제 #8
0
def close():
    if connection:
        connection.close()
        print("PostgreSQL connection is closed")
예제 #9
0
    def __where_clause(self, where: Dict[str, Union[Any,
                                                    List]]) -> (str, List):
        if (where is not None and not isinstance(where, Sequence)
                and not isinstance(where, dict)):
            raise NotImplementedError(
                f'unsupported query type "{type(where)}"')

        if where is None or len(where) == 0:
            where_clause = None
            where_values = None
        else:
            fields = None
            where_values = []
            if isinstance(where, str):
                where_clause = where
            elif isinstance(where, dict):
                where_clause = []
                for field, value in where.items():
                    field_type = self.fields[field]
                    if isinstance(value, BaseGeometry) or isinstance(
                            value, BaseMultipartGeometry):
                        where_clause.append(
                            f'{field} = ST_GeomFromText(%s, %s)')
                        where_values.extend([value.wkt, self.crs.to_epsg()])
                    else:
                        if isinstance(field_type, list):
                            if not isinstance(value, Sequence) or isinstance(
                                    value, str):
                                statement = f'%s = ANY({field})'
                            else:
                                if fields is None:
                                    with self.connection as connection:
                                        with connection.cursor() as cursor:
                                            fields = database_table_fields(
                                                cursor, self.name)
                                    connection.close()
                                field_type = fields[field]
                                dimensions = field_type.count('_')
                                field_type = field_type.strip('_')
                                statement = f'{field} = %s::{field_type}{"[]" * dimensions}'
                        elif value is None:
                            statement = f'{field} IS %s'
                        elif isinstance(value, Sequence) and not isinstance(
                                value, str):
                            statement = f'{field} IN %s'
                            value = tuple(value)
                        elif isinstance(value, str) and '%' in value:
                            statement = f'{field} ILIKE %s'
                        else:
                            if isinstance(value, datetime):
                                value = f'{value:%Y%m%d %H%M%S}'
                            elif isinstance(value, date):
                                value = f'{value:%Y%m%d}'
                            statement = f'{field} = %s'
                        where_values.append(value)
                        where_clause.append(statement)
                where_clause = ' AND '.join(where_clause)
            else:
                where_clause = ' AND '.join(where)

            if len(where_values) == 0:
                where_values = None

        return where_clause, where_values
예제 #10
0
 def delete_table(self):
     with self.connection as connection:
         with connection.cursor() as cursor:
             cursor.execute(f'DROP TABLE {self.name};')
     connection.close()
예제 #11
0
    def insert(self, records: List[Dict[str, Any]]):
        if isinstance(records, dict):
            records = [records]

        if not all(field in record for field in self.primary_key
                   for record in records):
            raise KeyError(
                f'one or more records does not contain primary key(s) "{self.primary_key}"'
            )

        if not self.connected:
            raise ConnectionError(
                f'no connection to {self.username}@{self.hostname}:{self.port}/{self.database}/{self.name}'
            )

        with self.connection as connection:
            with connection.cursor() as cursor:
                for record in records:
                    if len(self.primary_key) == 1:
                        primary_key_string = self.primary_key[0]
                        primary_key_value = record[self.primary_key[0]]
                    else:
                        primary_key_string = f'({", ".join(self.primary_key)})'
                        primary_key_value = tuple(
                            record[primary_key]
                            for primary_key in self.primary_key)

                    record_fields_not_in_local_table = [
                        field for field in record if field not in self.fields
                    ]
                    if len(record_fields_not_in_local_table) > 0:
                        self.logger.warning(
                            f'record has {len(record_fields_not_in_local_table)} fields not in the local table'
                            f' that will not be inserted: {record_fields_not_in_local_table}'
                        )

                    local_fields_in_record = [
                        field for field in self.fields if field in record
                    ]
                    geometry_fields = [
                        field for field in self.geometry_fields
                        if field in record
                    ]

                    columns = [
                        field for field in local_fields_in_record
                        if field not in geometry_fields
                    ]
                    values = [
                        record[field] for field in local_fields_in_record
                        if field not in geometry_fields
                    ]

                    for index, value in enumerate(values):
                        if isinstance(value, Collection) and not isinstance(
                                value, (str, list)):
                            values[index] = list(value)

                    if primary_key_value in self:
                        record_without_primary_key = {
                            column: value
                            for column, value in zip(columns, values)
                            if column not in self.primary_key
                        }
                        if len(record_without_primary_key) > 0:
                            if len(record_without_primary_key) > 1:
                                cursor.execute(
                                    f'UPDATE {self.name} SET ({", ".join(record_without_primary_key.keys())}) = %s '
                                    f'WHERE {primary_key_string} = %s;',
                                    [
                                        tuple(record_without_primary_key.
                                              values()),
                                        primary_key_value,
                                    ],
                                )
                            else:
                                cursor.execute(
                                    f'UPDATE {self.name} SET {tuple(record_without_primary_key.keys())[0]} = %s '
                                    f'WHERE {primary_key_string} = %s;',
                                    [
                                        tuple(record_without_primary_key.
                                              values())[0],
                                        primary_key_value,
                                    ],
                                )
                    else:
                        cursor.execute(
                            f'INSERT INTO {self.name} ({", ".join(columns)}) VALUES %s;',
                            [tuple(values)],
                        )

                    if len(geometry_fields) > 0:
                        geometries = {
                            field: record[field]
                            for field in geometry_fields
                            if record[field] is not None
                        }

                        for field, geometry in geometries.items():
                            cursor.execute(
                                f'UPDATE {self.name} SET {field} = ST_GeomFromText(%s, %s) '
                                f'WHERE {primary_key_string} = %s;',
                                [
                                    geometry.wkt,
                                    self.crs.to_epsg(), primary_key_value
                                ],
                            )
        connection.close()
예제 #12
0
    def __init__(
        self,
        hostname: str,
        table_name: str,
        database: str = None,
        fields: Dict[str, type] = None,
        primary_key: Union[str, List[str]] = None,
        crs: CRS = None,
        username: str = None,
        users: List[str] = None,
        logger: Logger = None,
        **kwargs,
    ):
        self.tunnel_credentials = {}

        if 'ssh_hostname' in kwargs and kwargs['ssh_hostname'] is not None:
            credentials = parse_hostname(kwargs['ssh_hostname'])
            ssh_hostname = credentials['hostname']
            ssh_port = credentials['port']
            if ssh_port is None:
                ssh_port = SSH_DEFAULT_PORT

            ssh_username = kwargs[
                'ssh_username'] if 'ssh_username' in kwargs else None
            ssh_password = kwargs[
                'ssh_password'] if 'ssh_password' in kwargs else None

            self.tunnel_credentials['ssh_hostname'] = ssh_hostname
            self.tunnel_credentials['ssh_port'] = ssh_port
            self.tunnel_credentials['ssh_username'] = ssh_username
            self.tunnel_credentials['ssh_password'] = ssh_password

        password = None
        if username is not None and ':' in username:
            username, password = username.split(':', 1)
        if 'password' in kwargs:
            password = kwargs['password']
        if password is None:
            password = getpass()
        self._DatabaseTable__password = password

        if database is None:
            database = 'postgres'

        super().__init__(
            resource=hostname,
            table_name=table_name,
            database=database,
            fields=fields,
            primary_key=primary_key,
            crs=crs,
            username=username,
            password=password,
            users=users,
            logger=logger,
        )

        if not self.connected:
            raise ConnectionError(
                f'no connection to {self.username}@{self.hostname}:{self.port}/{self.database}/{self.name}'
            )

        if self.fields is None:
            with self.connection as connection:
                with connection.cursor() as cursor:
                    self._DatabaseTable__fields = database_table_fields(
                        cursor, self.name)
            connection.close()

            if self.primary_key is None:
                self._DatabaseTable__primary_key = list(self.fields)[0]

        with self.connection as connection:
            with connection.cursor() as cursor:
                if database_has_table(cursor, self.name):
                    if database_table_is_inherited(cursor, self.name):
                        raise RuntimeError(
                            f'inheritance of table "{self.database}/{self.name}" will cause unexpected behaviour; aborting'
                        )

                    remote_fields = self.remote_fields
                    if list(remote_fields) != list(self.fields):
                        self.logger.warning(
                            f'schema of existing table "{self.database}/{self.name}" differs from given fields'
                        )

                        remote_fields_not_in_local_table = {
                            field: value
                            for field, value in remote_fields.items()
                            if field not in self.fields
                        }
                        if len(remote_fields_not_in_local_table) > 0:
                            self.logger.warning(
                                f'remote table has {len(remote_fields_not_in_local_table)} fields not in local table: {list(remote_fields_not_in_local_table)}'
                            )
                            self.logger.warning(
                                f'adding {len(remote_fields_not_in_local_table)} fields to local table: {list(remote_fields_not_in_local_table)}'
                            )

                            self._DatabaseTable__fields.update(
                                remote_fields_not_in_local_table)
                            self._DatabaseTable__fields = {
                                field: self._DatabaseTable__fields[field]
                                for field in remote_fields
                            }

                        local_fields_not_in_remote_table = {
                            field: value
                            for field, value in self.fields.items()
                            if field not in remote_fields
                        }
                        if len(local_fields_not_in_remote_table) > 0:
                            self.logger.warning(
                                f'local table has {len(local_fields_not_in_remote_table)} fields not in remote table: {list(local_fields_not_in_remote_table)}'
                            )
                            self.logger.warning(
                                f'adding {len(local_fields_not_in_remote_table)} fields to remote table: {list(local_fields_not_in_remote_table)}'
                            )

                        if list(remote_fields) != list(self.fields):
                            self.logger.warning(
                                f'altering schema of "{self.database}/{self.name}"'
                            )
                            self.logger.debug(self.remote_fields)
                            self.logger.debug(self.fields)

                            copy_table_name = f'old_{self.name}'

                            if database_has_table(cursor, copy_table_name):
                                cursor.execute(
                                    f'DROP TABLE {copy_table_name};')

                            cursor.execute(
                                f'ALTER TABLE {self.name} RENAME TO {copy_table_name};'
                            )

                            cursor.execute(
                                f'CREATE TABLE {self.name} ({self.schema});')
                            for user in self.users:
                                cursor.execute(
                                    f'GRANT INSERT, SELECT, UPDATE, DELETE ON TABLE public.{self.name} TO {user};'
                                )

                            copy_table_fields = list(
                                database_table_fields(cursor, copy_table_name))

                            cursor.execute(
                                f'INSERT INTO {self.name} ({", ".join(copy_table_fields)}) SELECT {", ".join(copy_table_fields)} FROM {copy_table_name};'
                            )

                            cursor.execute(f'DROP TABLE {copy_table_name};')
                else:
                    self.logger.debug(
                        f'creating remote table "{self.database}/{self.name}"')
                    cursor.execute(
                        f'CREATE TABLE {self.name} ({self.schema});')

                    for user in self.users:
                        cursor.execute(
                            f'GRANT INSERT, SELECT, UPDATE, DELETE ON TABLE public.{self.name} TO {user};'
                        )
        connection.close()
        if 'password' in kwargs:
            kwargs['password'] = '******'
        self.kwargs = kwargs
예제 #13
0
 def exists(self) -> bool:
     with self.connection as connection:
         with connection.cursor() as cursor:
             exists = database_has_table(cursor, self.name)
     connection.close()
     return exists