def get_connection(self, cluster_user: str, cluster_address: str, cluster_port: int, database: str, pwd: str, ssl: bool): self._logger.debug("Creating new Database Connection") self._logger.debug(locals()) if self._dialect == DIALECT_PG: # connect to the database conn = pg8000.connect(user=cluster_user, host=cluster_address, port=int(cluster_port), database=database, password=pwd, ssl_context=ssl.create_default_context() if ssl is True else None, timeout=None, tcp_keepalive=True, application_name=params.AWS_DATA_API_NAME) # Enable keepalives manually until pg8000 supports it # For future reference: https://github.com/mfenniak/pg8000/issues/149 # TCP keepalives still need to be configured appropriately on OS level as well conn._usock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) conn.autocommit = True return conn else: raise exceptions.UnimplementedFeatureException()
def json_to_pg(p_name: str, p_spec: dict, p_required: dict, pk_name: str) -> str: '''Convert a JSON type to a Postgres type with nullability spec ''' base = None if p_spec is not None: p_type = p_spec.get("type") if p_type.lower() == 'string': base = 'varchar' elif p_type.lower() == 'number': base = 'double precision' elif p_type.lower() == 'integer': base = 'integer' elif p_type.lower() == 'boolean': base = 'char(1) DEFAULT 0' return base else: raise exceptions.UnimplementedFeatureException( f"Type {p_type} not translatable to RDBMS types") # process null/not null if p_name.lower() == pk_name: base += ' NOT NULL PRIMARY KEY' elif p_name in p_required: base += ' NOT NULL' else: base += ' NULL' return base else: raise exceptions.DetailedException("Unable to render None Type Spec")
def who_column_insert(self, caller_identity: str): if self._dialect == DIALECT_PG: return [ f"0", f"'{params.ACTION_CREATE}'", f"CURRENT_TIMESTAMP", f"'{caller_identity}'" ] else: raise exceptions.UnimplementedFeatureException()
def verify_table(self, conn, table_ref: str, table_schema: dict, pk_name: str) -> None: if self._dialect == DIALECT_PG: try: cursor = conn.cursor() query = f'select count(9) from "{table_ref}"' self._logger.debug(query) cursor.execute(query) res = cursor.fetchone() self._logger.info(f"Bound to existing table {table_ref}") except ProgrammingError as pe: if "not exist" in str(pe): # table doesn't exist so create it based on the current schema self.create_table_from_schema(conn, table_ref, table_schema, pk_name) else: self._logger.error(pe) raise exceptions.DetailedException(pe) else: raise exceptions.UnimplementedFeatureException()
def get_streams(self): raise exceptions.UnimplementedFeatureException()
def delete(self, id: str, caller_identity: str, **kwargs): response = {} if params.METADATA in kwargs: if len(kwargs.get(params.METADATA)) == 0: # hard delete the metadata record - there is no soft delete response[params.METADATA] = { params.DATA_MODIFIED: self._delete_metadata(id) } else: # just delete the specified attributes response[params.METADATA] = { params.DATA_MODIFIED: self.remove_metadata_attributes( id=id, metadata_attributes=kwargs.get(params.METADATA), caller_identity=caller_identity) } if kwargs is None or kwargs == {} or params.RESOURCE in kwargs: if params.RESOURCE not in kwargs or len(kwargs.get( params.RESOURCE)) == 0: if self._delete_mode == params.DELETE_MODE_SOFT: # perform a soft delete and reflect that only the resource will have been deleted in the response§ update = self._create_update_statement( table_ref=self._resource_table_name, pk_name=self._pk_name, input={ self._engine_type.get_who(params.DELETED): True }, item_id=id, caller_identity=caller_identity) counts, records = self._engine_type.run_commands( self._db_conn, [update]) if counts is not None and counts[0] > 0: response[params.RESOURCE] = { params.DATA_MODIFIED: True } else: response[params.RESOURCE] = { params.DATA_MODIFIED: False } elif self._delete_mode == params.DELETE_MODE_HARD: # remove the metadata response[params.METADATA] = { params.DATA_MODIFIED: self._delete_metadata(id) } # delete the database record response[params.RESOURCE] = { params.DATA_MODIFIED: self._delete_record( table_name=self._resource_table_name, item_id=id) } else: # tombstone deletions not supported in rdbms due to nullability constraints raise exceptions.UnimplementedFeatureException( "Cannot Tombstone Delete in RDBMS") else: # remove resource attributes only response[params.RESOURCE] = { params.DATA_MODIFIED: self.remove_resource_attributes( id=id, resource_attributes=kwargs.get(params.RESOURCE), caller_identity=caller_identity) } return response