Repository: dbt-labs/dbt-core

Issue:
dbt docs generate error on Snowflake when `QUOTED_IDENTIFIERS_IGNORE_CASE` is set to `True`
`dbt docs generate` returns the following error on a Snowflake instance where the `QUOTED_IDENTIFIERS_IGNORE_CASE` parameter is set to True

 Encountered an error:
'table_schema'

That is because the following query issued by dbt to get the catalog will have upper case column names.
```
with tables as (

        select
            table_schema as "table_schema",
            table_name as "table_name",
            table_type as "table_type",

            -- note: this is the _role_ that owns the table
            table_owner as "table_owner",

            'Clustering Key' as "stats:clustering_key:label",
            clustering_key as "stats:clustering_key:value",
            'The key used to cluster this table' as "stats:clustering_key:description",
            (clustering_key is not null) as "stats:clustering_key:include",

            'Row Count' as "stats:row_count:label",
            row_count as "stats:row_count:value",
            'An approximate count of rows in this table' as "stats:row_count:description",
            (row_count is not null) as "stats:row_count:include",

            'Approximate Size' as "stats:bytes:label",
            bytes as "stats:bytes:value",
            'Approximate size of the table as reported by Snowflake' as "stats:bytes:description",
            (bytes is not null) as "stats:bytes:include"

        from information_schema.tables

    ),

    columns as (

        select

            table_schema as "table_schema",
            table_name as "table_name",
            null as "table_comment",

            column_name as "column_name",
            ordinal_position as "column_index",
            data_type as "column_type",
            null as "column_comment"

        from information_schema.columns

    )

    select *
    from tables
    join columns using ("table_schema", "table_name")
    where "table_schema" != 'INFORMATION_SCHEMA'
    order by "column_index"  
 ```

```
table_schema	table_name
ACCT	F_REGISTRATIONS
```


Discussion:

Resolving patch:
diff --git a/dbt/adapters/default/impl.py b/dbt/adapters/default/impl.py
--- a/dbt/adapters/default/impl.py
+++ b/dbt/adapters/default/impl.py
@@ -34,8 +34,15 @@ def _filter_schemas(manifest):
     })
 
     def test(row):
-        # the schema may be present but None
-        table_schema = row.get('table_schema')
+        if 'table_schema' not in row.keys():
+            # this means the get catalog operation is somehow not well formed!
+            raise dbt.exceptions.InternalException(
+                'Got a row without "table_schema" column, columns: {}'
+                .format(row.keys())
+            )
+        # the schema may be present but None, which is not an error and should
+        # be filtered out
+        table_schema = row['table_schema']
         if table_schema is None:
             return False
         return table_schema.lower() in schemas
@@ -840,6 +847,10 @@ def run_operation(cls, profile, project_cfg, manifest, operation_name):
     ###
     # Abstract methods involving the manifest
     ###
+    @classmethod
+    def _filter_table(cls, table, manifest):
+        return table.where(_filter_schemas(manifest))
+
     @classmethod
     def get_catalog(cls, profile, project_cfg, manifest):
         try:
@@ -848,5 +859,5 @@ def get_catalog(cls, profile, project_cfg, manifest):
         finally:
             cls.release_connection(profile, GET_CATALOG_OPERATION_NAME)
 
-        results = table.where(_filter_schemas(manifest))
+        results = cls._filter_table(table, manifest)
         return results
diff --git a/dbt/adapters/snowflake/impl.py b/dbt/adapters/snowflake/impl.py
--- a/dbt/adapters/snowflake/impl.py
+++ b/dbt/adapters/snowflake/impl.py
@@ -220,6 +220,15 @@ def add_query(cls, profile, sql, model_name=None, auto_begin=True,
 
         return connection, cursor
 
+    @classmethod
+    def _filter_table(cls, table, manifest):
+        # On snowflake, users can set QUOTED_IDENTIFIERS_IGNORE_CASE, so force
+        # the column names to their lowercased forms.
+        lowered = table.rename(
+            column_names=[c.lower() for c in table.column_names]
+        )
+        return super(SnowflakeAdapter, cls)._filter_table(lowered, manifest)
+
     @classmethod
     def _make_match_kwargs(cls, project_cfg, schema, identifier):
         if identifier is not None and \


Test patch:
diff --git a/test/integration/029_docs_generate_tests/test_docs_generate.py b/test/integration/029_docs_generate_tests/test_docs_generate.py
--- a/test/integration/029_docs_generate_tests/test_docs_generate.py
+++ b/test/integration/029_docs_generate_tests/test_docs_generate.py
@@ -3,10 +3,11 @@
 from numbers import Integral
 import os
 from datetime import datetime, timedelta
-from mock import ANY
+from mock import ANY, patch
 
 from test.integration.base import DBTIntegrationTest, use_profile
 from dbt.compat import basestring
+from dbt.adapters.snowflake import impl as snowflake_impl
 
 DATEFMT = '%Y-%m-%dT%H:%M:%S.%fZ'
 
@@ -177,7 +178,7 @@ def _snowflake_stats(self):
             'row_count': {
                 'id': 'row_count',
                 'label': 'Row Count',
-                'value': True,
+                'value': 1.0,
                 'description': 'An approximate count of rows in this table',
                 'include': True,
             },
@@ -1501,23 +1502,26 @@ def verify_manifest(self, expected_manifest):
         )
         self.assertEqual(manifest_without_extras, expected_manifest)
 
-    def expected_run_results(self):
+    def _quote(self, value):
+        quote_char = '`' if self.adapter_type == 'bigquery' else '"'
+        return '{0}{1}{0}'.format(quote_char, value)
+
+    def expected_run_results(self, quote_schema=True, quote_model=False):
         """
         The expected results of this run.
         """
         schema = self.unique_schema()
-        compiled_sql = '\n\nselect * from "{}".seed'.format(schema)
-        status = 'CREATE VIEW'
 
-        if self.adapter_type == 'snowflake':
-            status = 'SUCCESS 1'
-            compiled_sql = compiled_sql.replace('"', '')
+        compiled_schema = self._quote(schema) if quote_schema else schema
+        compiled_seed = self._quote('seed') if quote_model else 'seed'
+
         if self.adapter_type == 'bigquery':
-            status = 'OK'
-            compiled_sql = '\n\nselect * from `{}`.`{}`.seed'.format(
-                self._profile['project'], schema
+            compiled_sql = '\n\nselect * from `{}`.{}.{}'.format(
+                self._profile['project'], compiled_schema, compiled_seed
             )
-        status = None
+        else:
+            compiled_sql = '\n\nselect * from {}.{}'.format(compiled_schema,
+                                                            compiled_seed)
 
         return [
             {
@@ -1573,7 +1577,7 @@ def expected_run_results(self):
                     'wrapped_sql': 'None'
                 },
                 'skip': False,
-                'status': status,
+                'status': None,
             },
             {
                 'error': None,
@@ -2025,7 +2029,26 @@ def test__snowflake__run_and_generate(self):
 
         self.verify_catalog(self.expected_snowflake_catalog())
         self.verify_manifest(self.expected_seeded_manifest())
-        self.verify_run_results(self.expected_run_results())
+        self.verify_run_results(self.expected_run_results(quote_schema=False, quote_model=False))
+
+    @use_profile('snowflake')
+    def test__snowflake__run_and_generate_ignore_quoting_parameter(self):
+        old_connect = snowflake_impl.snowflake.connector.connect
+        def connect(*args, **kwargs):
+            kwargs['session_parameters'] = {
+                'QUOTED_IDENTIFIERS_IGNORE_CASE':True
+            }
+            return old_connect(*args, **kwargs)
+        with patch.object(snowflake_impl.snowflake.connector, 'connect', connect):
+            self.run_and_generate({
+                'quoting': {
+                    'identifier': True,
+                }
+            })
+
+        self.verify_catalog(self.expected_snowflake_catalog())
+        self.verify_manifest(self.expected_seeded_manifest())
+        self.verify_run_results(self.expected_run_results(quote_schema=False, quote_model=True))
 
     @use_profile('bigquery')
     def test__bigquery__run_and_generate(self):