Skip to content

Commit 1a56234

Browse files
committed
Merge pull request #1 from jasonamyers/master
Added UnloadFromSelect and associated tests. Also added build/ to .gitignore
2 parents 1986edc + 180fbea commit 1a56234

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
.DS_Store
44
dist
55
*.egg-info
6+
build

redshift_sqlalchemy/dialect.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
from sqlalchemy.engine import reflection
33
from sqlalchemy import util, exc
44
from sqlalchemy.types import VARCHAR, NullType
5+
from sqlalchemy.ext.compiler import compiles
6+
from sqlalchemy.sql.expression import Executable, ClauseElement
7+
58

69
class RedshiftDialect(PGDialect_psycopg2):
710
@reflection.cache
@@ -51,3 +54,36 @@ def _get_column_info(self, name, format_type, default,
5154
if isinstance(column_info['type'], VARCHAR) and column_info['type'].length is None:
5255
column_info['type'] = NullType()
5356
return column_info
57+
58+
59+
class UnloadFromSelect(Executable, ClauseElement):
60+
''' Prepares a RedShift unload statement to drop a query to Amazon S3
61+
http://docs.aws.amazon.com/redshift/latest/dg/r_UNLOAD_command_examples.html
62+
'''
63+
def __init__(self, select, bucket, access_key, secret_key):
64+
''' Initializes an UnloadFromSelect instance
65+
66+
Args:
67+
self: An instance of UnloadFromSelect
68+
select: The select statement to be unloaded
69+
bucket: The Amazon S3 bucket where the result will be stored
70+
access_key: The Amazon Access Key ID
71+
secret_key: The Amazon Secret Access Key
72+
'''
73+
self.select = select
74+
self.bucket = bucket
75+
self.access_key = access_key
76+
self.secret_key = secret_key
77+
78+
@compiles(UnloadFromSelect)
79+
def visit_unload_from_select(element, compiler, **kw):
80+
''' Returns the actual sql query for the UnloadFromSelect class
81+
'''
82+
return "unload ('%(query)s') to '%(bucket)s' credentials 'aws_access_key_id=%(access_key)s;aws_secret_access_key=%(secret_key)s'" % {
83+
'query': element.select,
84+
'bucket': element.bucket,
85+
'access_key': element.access_key,
86+
'secret_key': element.secret_key,
87+
}
88+
89+

tests/test_unload_from_select.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from unittest import TestCase
2+
from sqlalchemy import Table, Column, Integer, String, MetaData
3+
from sqlalchemy.sql import select, func
4+
from redshift_sqlalchemy.dialect import UnloadFromSelect
5+
6+
7+
class TestUnloadFromSelect(TestCase):
8+
def setUp(self):
9+
''' Sets up a table and associate meta data for the test queries to build against
10+
'''
11+
self.metadata = MetaData()
12+
self.t1 = Table('t1', self.metadata, Column('id', Integer, primary_key=True), Column('name', String))
13+
14+
def test_basic_unload_case(self):
15+
''' Tests that the simplest type of UnloadFromSelect works
16+
'''
17+
expected_result = "unload ('SELECT count(t1.id) AS count_1 \nFROM t1') to 'cookies' credentials 'aws_access_key_id=cookies;aws_secret_access_key=cookies'"
18+
insert = UnloadFromSelect(select([func.count(self.t1.c.id)]), 'cookies', 'cookies', 'cookies')
19+
self.assertEqual(expected_result, str(insert))

0 commit comments

Comments
 (0)