def custom_execute(statement, *args, **kwargs):
     if not isinstance(statement, SQLALCHEMY_BASES) and strip(
             statement).lower().startswith("copy"):
         return execute_mock_s3_copy_command(statement, engine)
     if not isinstance(statement, SQLALCHEMY_BASES) and strip(
             statement).lower().startswith("unload"):
         return execute_mock_s3_unload_command(statement, engine)
     return default_execute(statement, *args, **kwargs)
Beispiel #2
0
        def execute(self, sql, args=None):
            if isinstance(sql, Executable):
                return super().execute(sql, args)

            if strip(sql).lower().startswith("copy"):
                mock_s3_copy_command(sql, self)
                sql = "commit"

            if strip(sql).lower().startswith("unload"):
                mock_s3_unload_command(sql, self)
                sql = "commit"

            return super().execute(sql, args)
 def execute(self, sql, args=None):
     dsn_params = self.connection.get_dsn_parameters()
     engine = create_engine(
         "postgresql+psycopg2://{user}:{password}@{hostname}:{port}/{dbname}"
         .format(
             user=dsn_params["user"],
             password=config["password"],
             hostname=dsn_params["host"],
             port=dsn_params["port"],
             dbname=dsn_params["dbname"],
         ))
     if not isinstance(
             sql, TextClause) and strip(sql).lower().startswith("copy"):
         return execute_mock_s3_copy_command(sql, engine)
     if not isinstance(
             sql, TextClause) and strip(sql).lower().startswith("unload"):
         return execute_mock_s3_unload_command(sql, engine)
     return super(CustomCursor, self).execute(sql, args)
def receive_before_cursor_execute(_, cursor, statement: str, parameters, context, executemany):
    """Handle the `before_cursor_execute` event.

    This is where we add support for custom features such as redshift COPY/UNLOAD because
    the query has already been rendered (into a string) at this point.

    Notably, COPY/UNLOAD need to perform extra non-sql behavior and potentially execute
    more than a single query and the interface requires that we return a query. Thus,
    we return a no-op query to be executed by sqlalchemy for certain kinds of supported
    extra features.
    """
    normalized_statement = strip(statement).lower()
    if normalized_statement.startswith("unload"):
        mock_s3_unload_command(statement, cursor)
        return "SELECT 1", {}

    if normalized_statement.startswith("copy"):
        mock_s3_copy_command(statement, cursor)
        context.should_autocommit = True
        return "SELECT 1", {}
    return statement, parameters
Beispiel #5
0
def _parse_s3_command(statement):
    """Format and Parse 'UNLOAD' command."""
    statement = strip(statement)
    params = dict()

    # deleting 'unload'
    tokens = statement.split()[1:]

    # Fetching select statement
    select_statement = ""
    error_flag = False
    for index, token in enumerate(tokens):
        if token.lower() == "to":
            tokens = tokens[index:]
            break
        select_statement += " " + token
    params["select_statement"] = select_statement
    if error_flag:
        raise ValueError((
            "Possibly malformed SELECT Statement. "
            "Statement = {statement}"
            "Redshift fixture only supports S3 Unload statments with the following syntax: "
            "UNLOAD ('select-statement') TO 's3://object-path/name-prefix'"
            "authorization 'aws_access_key_id=<aws_access_key_id>;"
            "aws_secret_access_key=<aws_secret_access_key>'"
            "[GZIP] [DELIMITER [ AS ] 'delimiter-char']").format(
                statement=statement))

    # Fetching s3_uri
    if tokens.pop(0).lower() != "to":
        raise ValueError((
            "Possibly malformed S3 URI Format. "
            "Statement = {statement}"
            "Redshift fixture only supports S3 Unload statments with the following syntax: "
            "UNLOAD ('select-statement') TO 's3://object-path/name-prefix'"
            "authorization 'aws_access_key_id=<aws_access_key_id>;"
            "aws_secret_access_key=<aws_secret_access_key>'"
            "[GZIP] [DELIMITER [ AS ] 'delimiter-char']").format(
                statement=statement))
    params["s3_uri"] = strip(tokens.pop(0))

    # Fetching authorization
    for token in tokens:
        if "aws_access_key_id" in token.lower(
        ) or "aws_secret_access_key" in token.lower():
            # This is because of the following possibiliteis:
            # ... [with ]authorization[ AS] 'aws_access_key_id=x;aws_secret_access_key=y'
            # OR
            # ... [with ]authorization[ AS] 'aws_secret_access_key=y;aws_access_key_id=x'
            # OR
            # ... [with ]authorization[ AS] 'aws_secret_access_key=y;\naws_access_key_id=x'
            # OR
            # ... [with ]authorization[ AS] 'aws_secret_access_key=y; aws_access_key_id=x'
            # Supportred AWS authorization format:
            # [with ]authorization[ AS] 'aws_secret_access_key=y; aws_access_key_id=x'
            # No Support for additional credential formats, eg IAM roles, etc, yet.
            credentials_list = token.split(";")
            for credentials in credentials_list:
                if "aws_access_key_id" in credentials:
                    params["aws_access_key_id"] = credentials.split("=")[-1]
                elif "aws_secret_access_key" in credentials:
                    params["aws_secret_access_key"] = credentials.split(
                        "=")[-1]
                else:
                    raise ValueError((
                        "Possibly malformed AWS Credentials Format. "
                        "Statement = {statement}"
                        "Redshift fixture only supports S3 Copy statments with the following "
                        "syntax: COPY <table_name> FROM [(column 1, [column2, [..]])] '"
                        "<file path on S3 bucket>' "
                        "credentials 'aws_access_key_id=<aws_access_key_id>;"
                        "aws_secret_access_key=<aws_secret_access_key>' "
                        "Supportred AWS credentials format: "
                        "[with ]credentials[ AS] 'aws_secret_access_key=y; aws_access_key_id=x'"
                        " No Support for additional credential formats, eg IAM roles, etc, yet."
                    ).format(statement=statement))

    # Fetching GZIP Flag
    params["gzip"] = False
    for token in tokens:
        if strip(token.lower()) == "gzip":
            params["gzip"] = True

    # Fetching delimiter
    for index, token in enumerate(tokens):
        if token.lower() == "delimiter":
            try:
                if tokens[index + 1].lower() != "as":
                    params["delimiter"] = strip(tokens[index + 1])
                else:
                    params["delimiter"] = strip(tokens[index + 2])
            except IndexError:
                raise ValueError((
                    "Possibly malformed Delimiter Format. "
                    "Statement = {statement}"
                    "Redshift fixture only supports S3 Unload statments with the following"
                    "syntax: UNLOAD ('select-statement') TO 's3://object-path/name-prefix'"
                    "authorization 'aws_access_key_id=<aws_access_key_id>;"
                    "aws_secret_access_key=<aws_secret_access_key>'"
                    "[GZIP] [DELIMITER [ AS ] 'delimiter-char']").format(
                        statement=statement))
    return params