We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Athena query runner doesn't paginate schemas
<!--
#####################################################################
#
# Need support? USE THE FORUM! https://discuss.redash.io/c/support.
#
# Don't have steps to reproduce and actually not sure it's a bug?
# Use the forum! https://discuss.redash.io/c/support.
#
#####################################################################

**Got an idea for a new feature?** Check if it isn't on the roadmap already: http://bit.ly/redash-roadmap and start a new discussion in the features category: https://discuss.redash.io/c/feature-requests 🌟.

Found a bug? Please fill out the sections below... thank you 👍
-->

### Issue Summary
Hi,

AWS Athena query runner paginates only tables but not schemas.
We have ~170 schemas, however, only the first 100 are shown in schema browser.
 
### Steps to Reproduce

1. Connect to Athena with Glue metastore which has more than 100 schemas
2. In the new query window, only 100 schemas are shown.

I've looked into the code and looks like you paginate only on tables but not on schemas (https://github.com/getredash/redash/blob/v4.0.0/redash/query_runner/athena.py#L130).
### Technical details:

* Redash Version: 4.0.0
* Browser/OS:
* How did you install Redash: docker


Kind regards,
Ievgen.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `redash/query_runner/athena.py`
Content:
```
1 import logging
2 import os
3 
4 from redash.query_runner import *
5 from redash.settings import parse_boolean
6 from redash.utils import json_dumps, json_loads
7 
8 logger = logging.getLogger(__name__)
9 ANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true'))
10 SHOW_EXTRA_SETTINGS = parse_boolean(os.environ.get('ATHENA_SHOW_EXTRA_SETTINGS', 'true'))
11 OPTIONAL_CREDENTIALS = parse_boolean(os.environ.get('ATHENA_OPTIONAL_CREDENTIALS', 'true'))
12 
13 try:
14     import pyathena
15     import boto3
16     enabled = True
17 except ImportError:
18     enabled = False
19 
20 
21 _TYPE_MAPPINGS = {
22     'boolean': TYPE_BOOLEAN,
23     'tinyint': TYPE_INTEGER,
24     'smallint': TYPE_INTEGER,
25     'integer': TYPE_INTEGER,
26     'bigint': TYPE_INTEGER,
27     'double': TYPE_FLOAT,
28     'varchar': TYPE_STRING,
29     'timestamp': TYPE_DATETIME,
30     'date': TYPE_DATE,
31     'varbinary': TYPE_STRING,
32     'array': TYPE_STRING,
33     'map': TYPE_STRING,
34     'row': TYPE_STRING,
35     'decimal': TYPE_FLOAT,
36 }
37 
38 
39 class SimpleFormatter(object):
40     def format(self, operation, parameters=None):
41         return operation
42 
43 
44 class Athena(BaseQueryRunner):
45     noop_query = 'SELECT 1'
46 
47     @classmethod
48     def name(cls):
49         return "Amazon Athena"
50 
51     @classmethod
52     def configuration_schema(cls):
53         schema = {
54             'type': 'object',
55             'properties': {
56                 'region': {
57                     'type': 'string',
58                     'title': 'AWS Region'
59                 },
60                 'aws_access_key': {
61                     'type': 'string',
62                     'title': 'AWS Access Key'
63                 },
64                 'aws_secret_key': {
65                     'type': 'string',
66                     'title': 'AWS Secret Key'
67                 },
68                 's3_staging_dir': {
69                     'type': 'string',
70                     'title': 'S3 Staging (Query Results) Bucket Path'
71                 },
72                 'schema': {
73                     'type': 'string',
74                     'title': 'Schema Name',
75                     'default': 'default'
76                 },
77                 'glue': {
78                     'type': 'boolean',
79                     'title': 'Use Glue Data Catalog',
80                 },
81             },
82             'required': ['region', 's3_staging_dir'],
83             'order': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir', 'schema'],
84             'secret': ['aws_secret_key']
85         }
86 
87         if SHOW_EXTRA_SETTINGS:
88             schema['properties'].update({
89                 'encryption_option': {
90                     'type': 'string',
91                     'title': 'Encryption Option',
92                 },
93                 'kms_key': {
94                     'type': 'string',
95                     'title': 'KMS Key',
96                 },
97             })
98 
99         if not OPTIONAL_CREDENTIALS:
100             schema['required'] += ['aws_access_key', 'aws_secret_key']
101 
102         return schema
103 
104     @classmethod
105     def enabled(cls):
106         return enabled
107 
108     @classmethod
109     def annotate_query(cls):
110         return ANNOTATE_QUERY
111 
112     @classmethod
113     def type(cls):
114         return "athena"
115 
116     def __get_schema_from_glue(self):
117         client = boto3.client(
118                 'glue',
119                 aws_access_key_id=self.configuration.get('aws_access_key', None),
120                 aws_secret_access_key=self.configuration.get('aws_secret_key', None),
121                 region_name=self.configuration['region']
122                 )
123         schema = {}
124         paginator = client.get_paginator('get_tables')
125 
126         for database in client.get_databases()['DatabaseList']:
127             iterator = paginator.paginate(DatabaseName=database['Name'])
128             for table in iterator.search('TableList[]'):
129                 table_name = '%s.%s' % (database['Name'], table['Name'])
130                 if table_name not in schema:
131                     column = [columns['Name'] for columns in table['StorageDescriptor']['Columns']]
132                     schema[table_name] = {'name': table_name, 'columns': column}
133                     for partition in table.get('PartitionKeys', []):
134                         schema[table_name]['columns'].append(partition['Name'])
135 
136         return schema.values()
137 
138     def get_schema(self, get_stats=False):
139         if self.configuration.get('glue', False):
140             return self.__get_schema_from_glue()
141 
142         schema = {}
143         query = """
144         SELECT table_schema, table_name, column_name
145         FROM information_schema.columns
146         WHERE table_schema NOT IN ('information_schema')
147         """
148 
149         results, error = self.run_query(query, None)
150         if error is not None:
151             raise Exception("Failed getting schema.")
152 
153         results = json_loads(results)
154         for row in results['rows']:
155             table_name = '{0}.{1}'.format(row['table_schema'], row['table_name'])
156             if table_name not in schema:
157                 schema[table_name] = {'name': table_name, 'columns': []}
158             schema[table_name]['columns'].append(row['column_name'])
159 
160         return schema.values()
161 
162     def run_query(self, query, user):
163         cursor = pyathena.connect(
164             s3_staging_dir=self.configuration['s3_staging_dir'],
165             region_name=self.configuration['region'],
166             aws_access_key_id=self.configuration.get('aws_access_key', None),
167             aws_secret_access_key=self.configuration.get('aws_secret_key', None),
168             schema_name=self.configuration.get('schema', 'default'),
169             encryption_option=self.configuration.get('encryption_option', None),
170             kms_key=self.configuration.get('kms_key', None),
171             formatter=SimpleFormatter()).cursor()
172 
173         try:
174             cursor.execute(query)
175             column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description]
176             columns = self.fetch_columns(column_tuples)
177             rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())]
178             qbytes = None
179             athena_query_id = None
180             try:
181                 qbytes = cursor.data_scanned_in_bytes
182             except AttributeError as e:
183                 logger.debug("Athena Upstream can't get data_scanned_in_bytes: %s", e)
184             try:
185                 athena_query_id = cursor.query_id
186             except AttributeError as e:
187                 logger.debug("Athena Upstream can't get query_id: %s", e)
188             data = {
189                 'columns': columns,
190                 'rows': rows,
191                 'metadata': {
192                     'data_scanned': qbytes,
193                     'athena_query_id': athena_query_id
194                 }
195             }
196             json_data = json_dumps(data, ignore_nan=True)
197             error = None
198         except KeyboardInterrupt:
199             if cursor.query_id:
200                 cursor.cancel()
201             error = "Query cancelled by user."
202             json_data = None
203         except Exception as ex:
204             if cursor.query_id:
205                 cursor.cancel()
206             error = ex.message
207             json_data = None
208 
209         return json_data, error
210 
211 
212 register(Athena)
213
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py
--- a/redash/query_runner/athena.py
+++ b/redash/query_runner/athena.py
@@ -121,18 +121,20 @@
                 region_name=self.configuration['region']
                 )
         schema = {}
-        paginator = client.get_paginator('get_tables')
-
-        for database in client.get_databases()['DatabaseList']:
-            iterator = paginator.paginate(DatabaseName=database['Name'])
-            for table in iterator.search('TableList[]'):
-                table_name = '%s.%s' % (database['Name'], table['Name'])
-                if table_name not in schema:
-                    column = [columns['Name'] for columns in table['StorageDescriptor']['Columns']]
-                    schema[table_name] = {'name': table_name, 'columns': column}
-                    for partition in table.get('PartitionKeys', []):
-                        schema[table_name]['columns'].append(partition['Name'])
 
+        database_paginator = client.get_paginator('get_databases')
+        table_paginator = client.get_paginator('get_tables')
+
+        for databases in database_paginator.paginate():
+            for database in databases['DatabaseList']:
+                iterator = table_paginator.paginate(DatabaseName=database['Name'])
+                for table in iterator.search('TableList[]'):
+                    table_name = '%s.%s' % (database['Name'], table['Name'])
+                    if table_name not in schema:
+                        column = [columns['Name'] for columns in table['StorageDescriptor']['Columns']]
+                        schema[table_name] = {'name': table_name, 'columns': column}
+                        for partition in table.get('PartitionKeys', []):
+                            schema[table_name]['columns'].append(partition['Name'])
         return schema.values()
 
     def get_schema(self, get_stats=False):

{"golden_diff": "diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py\n--- a/redash/query_runner/athena.py\n+++ b/redash/query_runner/athena.py\n@@ -121,18 +121,20 @@\n                 region_name=self.configuration['region']\n                 )\n         schema = {}\n-        paginator = client.get_paginator('get_tables')\n-\n-        for database in client.get_databases()['DatabaseList']:\n-            iterator = paginator.paginate(DatabaseName=database['Name'])\n-            for table in iterator.search('TableList[]'):\n-                table_name = '%s.%s' % (database['Name'], table['Name'])\n-                if table_name not in schema:\n-                    column = [columns['Name'] for columns in table['StorageDescriptor']['Columns']]\n-                    schema[table_name] = {'name': table_name, 'columns': column}\n-                    for partition in table.get('PartitionKeys', []):\n-                        schema[table_name]['columns'].append(partition['Name'])\n \n+        database_paginator = client.get_paginator('get_databases')\n+        table_paginator = client.get_paginator('get_tables')\n+\n+        for databases in database_paginator.paginate():\n+            for database in databases['DatabaseList']:\n+                iterator = table_paginator.paginate(DatabaseName=database['Name'])\n+                for table in iterator.search('TableList[]'):\n+                    table_name = '%s.%s' % (database['Name'], table['Name'])\n+                    if table_name not in schema:\n+                        column = [columns['Name'] for columns in table['StorageDescriptor']['Columns']]\n+                        schema[table_name] = {'name': table_name, 'columns': column}\n+                        for partition in table.get('PartitionKeys', []):\n+                            schema[table_name]['columns'].append(partition['Name'])\n         return schema.values()\n \n     def get_schema(self, get_stats=False):\n", "issue": "Athena query runner doesn't paginate schemas\n<!--\r\n#####################################################################\r\n#\r\n# Need support? USE THE FORUM! https://discuss.redash.io/c/support.\r\n#\r\n# Don't have steps to reproduce and actually not sure it's a bug?\r\n# Use the forum! https://discuss.redash.io/c/support.\r\n#\r\n#####################################################################\r\n\r\n**Got an idea for a new feature?** Check if it isn't on the roadmap already: http://bit.ly/redash-roadmap and start a new discussion in the features category: https://discuss.redash.io/c/feature-requests \ud83c\udf1f.\r\n\r\nFound a bug? Please fill out the sections below... thank you \ud83d\udc4d\r\n-->\r\n\r\n### Issue Summary\r\nHi,\r\n\r\nAWS Athena query runner paginates only tables but not schemas.\r\nWe have ~170 schemas, however, only the first 100 are shown in schema browser.\r\n \r\n### Steps to Reproduce\r\n\r\n1. Connect to Athena with Glue metastore which has more than 100 schemas\r\n2. In the new query window, only 100 schemas are shown.\r\n\r\nI've looked into the code and looks like you paginate only on tables but not on schemas (https://github.com/getredash/redash/blob/v4.0.0/redash/query_runner/athena.py#L130).\r\n### Technical details:\r\n\r\n* Redash Version: 4.0.0\r\n* Browser/OS:\r\n* How did you install Redash: docker\r\n\r\n\r\nKind regards,\r\nIevgen.\n", "before_files": [{"content": "import logging\nimport os\n\nfrom redash.query_runner import *\nfrom redash.settings import parse_boolean\nfrom redash.utils import json_dumps, json_loads\n\nlogger = logging.getLogger(__name__)\nANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true'))\nSHOW_EXTRA_SETTINGS = parse_boolean(os.environ.get('ATHENA_SHOW_EXTRA_SETTINGS', 'true'))\nOPTIONAL_CREDENTIALS = parse_boolean(os.environ.get('ATHENA_OPTIONAL_CREDENTIALS', 'true'))\n\ntry:\n    import pyathena\n    import boto3\n    enabled = True\nexcept ImportError:\n    enabled = False\n\n\n_TYPE_MAPPINGS = {\n    'boolean': TYPE_BOOLEAN,\n    'tinyint': TYPE_INTEGER,\n    'smallint': TYPE_INTEGER,\n    'integer': TYPE_INTEGER,\n    'bigint': TYPE_INTEGER,\n    'double': TYPE_FLOAT,\n    'varchar': TYPE_STRING,\n    'timestamp': TYPE_DATETIME,\n    'date': TYPE_DATE,\n    'varbinary': TYPE_STRING,\n    'array': TYPE_STRING,\n    'map': TYPE_STRING,\n    'row': TYPE_STRING,\n    'decimal': TYPE_FLOAT,\n}\n\n\nclass SimpleFormatter(object):\n    def format(self, operation, parameters=None):\n        return operation\n\n\nclass Athena(BaseQueryRunner):\n    noop_query = 'SELECT 1'\n\n    @classmethod\n    def name(cls):\n        return \"Amazon Athena\"\n\n    @classmethod\n    def configuration_schema(cls):\n        schema = {\n            'type': 'object',\n            'properties': {\n                'region': {\n                    'type': 'string',\n                    'title': 'AWS Region'\n                },\n                'aws_access_key': {\n                    'type': 'string',\n                    'title': 'AWS Access Key'\n                },\n                'aws_secret_key': {\n                    'type': 'string',\n                    'title': 'AWS Secret Key'\n                },\n                's3_staging_dir': {\n                    'type': 'string',\n                    'title': 'S3 Staging (Query Results) Bucket Path'\n                },\n                'schema': {\n                    'type': 'string',\n                    'title': 'Schema Name',\n                    'default': 'default'\n                },\n                'glue': {\n                    'type': 'boolean',\n                    'title': 'Use Glue Data Catalog',\n                },\n            },\n            'required': ['region', 's3_staging_dir'],\n            'order': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir', 'schema'],\n            'secret': ['aws_secret_key']\n        }\n\n        if SHOW_EXTRA_SETTINGS:\n            schema['properties'].update({\n                'encryption_option': {\n                    'type': 'string',\n                    'title': 'Encryption Option',\n                },\n                'kms_key': {\n                    'type': 'string',\n                    'title': 'KMS Key',\n                },\n            })\n\n        if not OPTIONAL_CREDENTIALS:\n            schema['required'] += ['aws_access_key', 'aws_secret_key']\n\n        return schema\n\n    @classmethod\n    def enabled(cls):\n        return enabled\n\n    @classmethod\n    def annotate_query(cls):\n        return ANNOTATE_QUERY\n\n    @classmethod\n    def type(cls):\n        return \"athena\"\n\n    def __get_schema_from_glue(self):\n        client = boto3.client(\n                'glue',\n                aws_access_key_id=self.configuration.get('aws_access_key', None),\n                aws_secret_access_key=self.configuration.get('aws_secret_key', None),\n                region_name=self.configuration['region']\n                )\n        schema = {}\n        paginator = client.get_paginator('get_tables')\n\n        for database in client.get_databases()['DatabaseList']:\n            iterator = paginator.paginate(DatabaseName=database['Name'])\n            for table in iterator.search('TableList[]'):\n                table_name = '%s.%s' % (database['Name'], table['Name'])\n                if table_name not in schema:\n                    column = [columns['Name'] for columns in table['StorageDescriptor']['Columns']]\n                    schema[table_name] = {'name': table_name, 'columns': column}\n                    for partition in table.get('PartitionKeys', []):\n                        schema[table_name]['columns'].append(partition['Name'])\n\n        return schema.values()\n\n    def get_schema(self, get_stats=False):\n        if self.configuration.get('glue', False):\n            return self.__get_schema_from_glue()\n\n        schema = {}\n        query = \"\"\"\n        SELECT table_schema, table_name, column_name\n        FROM information_schema.columns\n        WHERE table_schema NOT IN ('information_schema')\n        \"\"\"\n\n        results, error = self.run_query(query, None)\n        if error is not None:\n            raise Exception(\"Failed getting schema.\")\n\n        results = json_loads(results)\n        for row in results['rows']:\n            table_name = '{0}.{1}'.format(row['table_schema'], row['table_name'])\n            if table_name not in schema:\n                schema[table_name] = {'name': table_name, 'columns': []}\n            schema[table_name]['columns'].append(row['column_name'])\n\n        return schema.values()\n\n    def run_query(self, query, user):\n        cursor = pyathena.connect(\n            s3_staging_dir=self.configuration['s3_staging_dir'],\n            region_name=self.configuration['region'],\n            aws_access_key_id=self.configuration.get('aws_access_key', None),\n            aws_secret_access_key=self.configuration.get('aws_secret_key', None),\n            schema_name=self.configuration.get('schema', 'default'),\n            encryption_option=self.configuration.get('encryption_option', None),\n            kms_key=self.configuration.get('kms_key', None),\n            formatter=SimpleFormatter()).cursor()\n\n        try:\n            cursor.execute(query)\n            column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description]\n            columns = self.fetch_columns(column_tuples)\n            rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())]\n            qbytes = None\n            athena_query_id = None\n            try:\n                qbytes = cursor.data_scanned_in_bytes\n            except AttributeError as e:\n                logger.debug(\"Athena Upstream can't get data_scanned_in_bytes: %s\", e)\n            try:\n                athena_query_id = cursor.query_id\n            except AttributeError as e:\n                logger.debug(\"Athena Upstream can't get query_id: %s\", e)\n            data = {\n                'columns': columns,\n                'rows': rows,\n                'metadata': {\n                    'data_scanned': qbytes,\n                    'athena_query_id': athena_query_id\n                }\n            }\n            json_data = json_dumps(data, ignore_nan=True)\n            error = None\n        except KeyboardInterrupt:\n            if cursor.query_id:\n                cursor.cancel()\n            error = \"Query cancelled by user.\"\n            json_data = None\n        except Exception as ex:\n            if cursor.query_id:\n                cursor.cancel()\n            error = ex.message\n            json_data = None\n\n        return json_data, error\n\n\nregister(Athena)\n", "path": "redash/query_runner/athena.py"}], "after_files": [{"content": "import logging\nimport os\n\nfrom redash.query_runner import *\nfrom redash.settings import parse_boolean\nfrom redash.utils import json_dumps, json_loads\n\nlogger = logging.getLogger(__name__)\nANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true'))\nSHOW_EXTRA_SETTINGS = parse_boolean(os.environ.get('ATHENA_SHOW_EXTRA_SETTINGS', 'true'))\nOPTIONAL_CREDENTIALS = parse_boolean(os.environ.get('ATHENA_OPTIONAL_CREDENTIALS', 'true'))\n\ntry:\n    import pyathena\n    import boto3\n    enabled = True\nexcept ImportError:\n    enabled = False\n\n\n_TYPE_MAPPINGS = {\n    'boolean': TYPE_BOOLEAN,\n    'tinyint': TYPE_INTEGER,\n    'smallint': TYPE_INTEGER,\n    'integer': TYPE_INTEGER,\n    'bigint': TYPE_INTEGER,\n    'double': TYPE_FLOAT,\n    'varchar': TYPE_STRING,\n    'timestamp': TYPE_DATETIME,\n    'date': TYPE_DATE,\n    'varbinary': TYPE_STRING,\n    'array': TYPE_STRING,\n    'map': TYPE_STRING,\n    'row': TYPE_STRING,\n    'decimal': TYPE_FLOAT,\n}\n\n\nclass SimpleFormatter(object):\n    def format(self, operation, parameters=None):\n        return operation\n\n\nclass Athena(BaseQueryRunner):\n    noop_query = 'SELECT 1'\n\n    @classmethod\n    def name(cls):\n        return \"Amazon Athena\"\n\n    @classmethod\n    def configuration_schema(cls):\n        schema = {\n            'type': 'object',\n            'properties': {\n                'region': {\n                    'type': 'string',\n                    'title': 'AWS Region'\n                },\n                'aws_access_key': {\n                    'type': 'string',\n                    'title': 'AWS Access Key'\n                },\n                'aws_secret_key': {\n                    'type': 'string',\n                    'title': 'AWS Secret Key'\n                },\n                's3_staging_dir': {\n                    'type': 'string',\n                    'title': 'S3 Staging (Query Results) Bucket Path'\n                },\n                'schema': {\n                    'type': 'string',\n                    'title': 'Schema Name',\n                    'default': 'default'\n                },\n                'glue': {\n                    'type': 'boolean',\n                    'title': 'Use Glue Data Catalog',\n                },\n            },\n            'required': ['region', 's3_staging_dir'],\n            'order': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir', 'schema'],\n            'secret': ['aws_secret_key']\n        }\n\n        if SHOW_EXTRA_SETTINGS:\n            schema['properties'].update({\n                'encryption_option': {\n                    'type': 'string',\n                    'title': 'Encryption Option',\n                },\n                'kms_key': {\n                    'type': 'string',\n                    'title': 'KMS Key',\n                },\n            })\n\n        if not OPTIONAL_CREDENTIALS:\n            schema['required'] += ['aws_access_key', 'aws_secret_key']\n\n        return schema\n\n    @classmethod\n    def enabled(cls):\n        return enabled\n\n    @classmethod\n    def annotate_query(cls):\n        return ANNOTATE_QUERY\n\n    @classmethod\n    def type(cls):\n        return \"athena\"\n\n    def __get_schema_from_glue(self):\n        client = boto3.client(\n                'glue',\n                aws_access_key_id=self.configuration.get('aws_access_key', None),\n                aws_secret_access_key=self.configuration.get('aws_secret_key', None),\n                region_name=self.configuration['region']\n                )\n        schema = {}\n\n        database_paginator = client.get_paginator('get_databases')\n        table_paginator = client.get_paginator('get_tables')\n\n        for databases in database_paginator.paginate():\n            for database in databases['DatabaseList']:\n                iterator = table_paginator.paginate(DatabaseName=database['Name'])\n                for table in iterator.search('TableList[]'):\n                    table_name = '%s.%s' % (database['Name'], table['Name'])\n                    if table_name not in schema:\n                        column = [columns['Name'] for columns in table['StorageDescriptor']['Columns']]\n                        schema[table_name] = {'name': table_name, 'columns': column}\n                        for partition in table.get('PartitionKeys', []):\n                            schema[table_name]['columns'].append(partition['Name'])\n        return schema.values()\n\n    def get_schema(self, get_stats=False):\n        if self.configuration.get('glue', False):\n            return self.__get_schema_from_glue()\n\n        schema = {}\n        query = \"\"\"\n        SELECT table_schema, table_name, column_name\n        FROM information_schema.columns\n        WHERE table_schema NOT IN ('information_schema')\n        \"\"\"\n\n        results, error = self.run_query(query, None)\n        if error is not None:\n            raise Exception(\"Failed getting schema.\")\n\n        results = json_loads(results)\n        for row in results['rows']:\n            table_name = '{0}.{1}'.format(row['table_schema'], row['table_name'])\n            if table_name not in schema:\n                schema[table_name] = {'name': table_name, 'columns': []}\n            schema[table_name]['columns'].append(row['column_name'])\n\n        return schema.values()\n\n    def run_query(self, query, user):\n        cursor = pyathena.connect(\n            s3_staging_dir=self.configuration['s3_staging_dir'],\n            region_name=self.configuration['region'],\n            aws_access_key_id=self.configuration.get('aws_access_key', None),\n            aws_secret_access_key=self.configuration.get('aws_secret_key', None),\n            schema_name=self.configuration.get('schema', 'default'),\n            encryption_option=self.configuration.get('encryption_option', None),\n            kms_key=self.configuration.get('kms_key', None),\n            formatter=SimpleFormatter()).cursor()\n\n        try:\n            cursor.execute(query)\n            column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description]\n            columns = self.fetch_columns(column_tuples)\n            rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())]\n            qbytes = None\n            athena_query_id = None\n            try:\n                qbytes = cursor.data_scanned_in_bytes\n            except AttributeError as e:\n                logger.debug(\"Athena Upstream can't get data_scanned_in_bytes: %s\", e)\n            try:\n                athena_query_id = cursor.query_id\n            except AttributeError as e:\n                logger.debug(\"Athena Upstream can't get query_id: %s\", e)\n            data = {\n                'columns': columns,\n                'rows': rows,\n                'metadata': {\n                    'data_scanned': qbytes,\n                    'athena_query_id': athena_query_id\n                }\n            }\n            json_data = json_dumps(data, ignore_nan=True)\n            error = None\n        except KeyboardInterrupt:\n            if cursor.query_id:\n                cursor.cancel()\n            error = \"Query cancelled by user.\"\n            json_data = None\n        except Exception as ex:\n            if cursor.query_id:\n                cursor.cancel()\n            error = ex.message\n            json_data = None\n\n        return json_data, error\n\n\nregister(Athena)\n", "path": "redash/query_runner/athena.py"}]}